Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qcgwindowcapture.mm
Go to the documentation of this file.
1// Copyright (C) 2022 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
5#include "private/qcapturablewindow_p.h"
7#include "private/qabstractvideobuffer_p.h"
8
9#include "qscreen.h"
10#include "qguiapplication.h"
11#include <qmutex.h>
12#include <qwaitcondition.h>
13
14#include <ApplicationServices/ApplicationServices.h>
15#include <IOKit/graphics/IOGraphicsLib.h>
16#include <AppKit/NSScreen.h>
17#include <AppKit/NSApplication.h>
18#include <AppKit/NSWindow.h>
19
20namespace {
21
22std::optional<qreal> frameRateForWindow(CGWindowID /*wid*/)
23{
24 // TODO: detect the frame rate
25 // if (window && window.screen) {
26 // CGDirectDisplayID displayID = [window.screen.deviceDescription[@"NSScreenNumber"]
27 // unsignedIntValue]; const auto displayRefreshRate =
28 // CGDisplayModeGetRefreshRate(CGDisplayCopyDisplayMode(displayID)); if (displayRefreshRate
29 // > 0 && displayRefreshRate < frameRate) frameRate = displayRefreshRate;
30 // }
31
32 return {};
33}
34
35}
36
38
40{
41public:
43 {
44 auto provider = CGImageGetDataProvider(image);
45 m_data = CGDataProviderCopyData(provider);
46 m_bytesPerLine = CGImageGetBytesPerRow(image);
47 }
48
49 ~QCGImageVideoBuffer() override { CFRelease(m_data); }
50
51 QVideoFrame::MapMode mapMode() const override { return m_mapMode; }
52
54 {
56 if (m_mapMode == QVideoFrame::NotMapped) {
57 m_mapMode = mode;
58
59 mapData.nPlanes = 1;
60 mapData.bytesPerLine[0] = static_cast<int>(m_bytesPerLine);
61 mapData.data[0] = (uchar *)CFDataGetBytePtr(m_data);
62 mapData.size[0] = static_cast<int>(CFDataGetLength(m_data));
63 }
64
65 return mapData;
66 }
67
68 void unmap() override { m_mapMode = QVideoFrame::NotMapped; }
69
70private:
72 CFDataRef m_data;
73 size_t m_bytesPerLine = 0;
74};
75
77{
78public:
79 Grabber(QCGWindowCapture &capture, CGWindowID wid) : m_capture(capture), m_wid(wid)
80 {
83
86
87 start();
88 }
89
90 ~Grabber() override { stop(); }
91
93 {
94 QMutexLocker<QMutex> locker(&m_formatMutex);
95 while (!m_format)
96 m_waitForFormat.wait(&m_formatMutex);
97 return *m_format;
98 }
99
100protected:
102 {
103 if (auto rate = frameRateForWindow(m_wid))
105
106 auto imageRef = CGWindowListCreateImage(CGRectNull, kCGWindowListOptionIncludingWindow,
107 m_wid, kCGWindowImageBoundsIgnoreFraming);
108 if (!imageRef) {
110 QLatin1String("Cannot create image by window"));
111 return {};
112 }
113
114 auto imageDeleter = qScopeGuard([imageRef]() { CGImageRelease(imageRef); });
115
116 if (CGImageGetBitsPerPixel(imageRef) != 32
117 || CGImageGetPixelFormatInfo(imageRef) != kCGImagePixelFormatPacked
118 || CGImageGetByteOrderInfo(imageRef) != kCGImageByteOrder32Little) {
119 qWarning() << "Unexpected image format. PixelFormatInfo:"
120 << CGImageGetPixelFormatInfo(imageRef)
121 << "BitsPerPixel:" << CGImageGetBitsPerPixel(imageRef) << "AlphaInfo"
122 << CGImageGetAlphaInfo(imageRef)
123 << "ByteOrderInfo:" << CGImageGetByteOrderInfo(imageRef);
124
126 QLatin1String("Not supported pixel format"));
127 return {};
128 }
129
130 QVideoFrameFormat format(QSize(CGImageGetWidth(imageRef), CGImageGetHeight(imageRef)),
132 format.setStreamFrameRate(frameRate());
133
134 return QVideoFrame(new QCGImageVideoBuffer(imageRef), format);
135 }
136
138 {
139 // Since writing of the format is supposed to be only from one thread,
140 // the read-only comparison without a mutex is thread-safe
141 if (!m_format || m_format != frame.surfaceFormat()) {
142 QMutexLocker<QMutex> locker(&m_formatMutex);
143
144 m_format = frame.surfaceFormat();
145
146 locker.unlock();
147
148 m_waitForFormat.notify_one();
149 }
150
151 emit m_capture.newVideoFrame(frame);
152 }
153
154private:
155 QCGWindowCapture &m_capture;
156 std::optional<QVideoFrameFormat> m_format;
157 mutable QMutex m_formatMutex;
158 mutable QWaitCondition m_waitForFormat;
159 CGWindowID m_wid;
160};
161
163{
164 CGRequestScreenCaptureAccess();
165}
166
168
170{
171 if (active) {
172 if (!CGPreflightScreenCaptureAccess()) {
174 QLatin1String("Permissions denied"));
175 return false;
176 }
177
178 auto window = source<WindowSource>();
179
181 if (!handle || !handle->id)
183 else
184 m_grabber = std::make_unique<Grabber>(*this, handle->id);
185
186 } else {
187 m_grabber.reset();
188 }
189
190 return active == static_cast<bool>(m_grabber);
191}
192
194{
195 return m_grabber ? m_grabber->frameFormat() : QVideoFrameFormat();
196}
197
199
200#include "moc_qcgwindowcapture_p.cpp"
The QAbstractVideoBuffer class is an abstraction for video data. \inmodule QtMultimedia.
void unmap() override
Releases the memory mapped by the map() function.
~QCGImageVideoBuffer() override
MapData map(QVideoFrame::MapMode mode) override
Independently maps the planes of a video buffer to memory.
QCGImageVideoBuffer(CGImageRef image)
QVideoFrame::MapMode mapMode() const override
QVideoFrameFormat frameFormat() const
void onNewFrame(QVideoFrame frame)
QVideoFrame grabFrame() override
Grabber(QCGWindowCapture &capture, CGWindowID wid)
QVideoFrameFormat frameFormat() const override
bool setActiveInternal(bool active) override
~QCGWindowCapture() override
static const QCapturableWindowPrivate * handle(const QCapturableWindow &window)
\inmodule QtMultimedia
void errorUpdated(QPlatformSurfaceCapture::Error error, const QString &description)
void addFrameCallback(Object &object, Method method)
void updateError(QPlatformSurfaceCapture::Error error, const QString &description={})
QScreen * primaryScreen
the primary (or default) screen of the application.
\inmodule QtCore
Definition qmutex.h:281
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
\threadsafe
Definition qobject.cpp:2960
void updateError(Error error, const QString &errorString)
void newVideoFrame(const QVideoFrame &)
qreal refreshRate
the approximate vertical refresh rate of the screen in Hz
Definition qscreen.h:64
\inmodule QtCore
Definition qsize.h:25
The QVideoFrameFormat class specifies the stream format of a video presentation surface.
The QVideoFrame class represents a frame of video data.
Definition qvideoframe.h:27
MapMode
Enumerates how a video buffer's data is mapped to system memory.
Definition qvideoframe.h:37
bool wait(QMutex *, QDeadlineTimer=QDeadlineTimer(QDeadlineTimer::Forever))
Combined button and popup list for selecting options.
Definition image.cpp:4
#define qWarning
Definition qlogging.h:166
GLuint64 GLenum void * handle
GLenum mode
GLint GLsizei GLsizei GLenum format
GLuint GLenum * rate
static QAbstractVideoBuffer::MapData mapData(const camera_frame_nv12_t &frame, unsigned char *baseAddress)
QScopeGuard< typename std::decay< F >::type > qScopeGuard(F &&f)
[qScopeGuard]
Definition qscopeguard.h:60
QLatin1StringView QLatin1String
Definition qstringfwd.h:31
QScreen * screen
[1]
Definition main.cpp:29
#define emit
unsigned char uchar
Definition qtypes.h:32
aWidget window() -> setWindowTitle("New Window Title")
[2]
QFrame frame
[0]