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
qavfscreencapture.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
7
8#include <qscreen.h>
9
10#define AVMediaType XAVMediaType
11#include "qffmpeghwaccel_p.h"
12
13extern "C" {
14#include <libavutil/hwcontext_videotoolbox.h>
15#include <libavutil/hwcontext.h>
16}
17#undef AVMediaType
18
19#include <AppKit/NSScreen.h>
20
21#include <dispatch/dispatch.h>
22
23namespace {
24
25const auto DefaultCVPixelFormat = kCVPixelFormatType_32BGRA;
26
27CGDirectDisplayID findDisplayByName(const QString &name)
28{
29 for (NSScreen *screen in NSScreen.screens) {
30 if (name == QString::fromNSString(screen.localizedName))
31 return [screen.deviceDescription[@"NSScreenNumber"] unsignedIntValue];
32 }
33 return kCGNullDirectDisplay;
34}
35}
36
38
40{
41 CGRequestScreenCaptureAccess();
42}
43
45{
46 resetCapture();
47}
48
50{
51 if (active) {
52 if (!CGPreflightScreenCaptureAccess()) {
53 updateError(CaptureFailed, QLatin1String("Permissions denied"));
54 return false;
55 }
56
57 auto screen = source<ScreenSource>();
58
60 return false;
61
62 return initScreenCapture(screen);
63 } else {
64 resetCapture();
65 }
66
67 return true;
68}
69
70void QAVFScreenCapture::onNewFrame(const QVideoFrame &frame)
71{
72 // Since writing of the format is supposed to be only from one thread,
73 // the read-only comparison without a mutex is thread-safe
74 if (!m_format || m_format != frame.surfaceFormat()) {
75 QMutexLocker<QMutex> locker(&m_formatMutex);
76
77 m_format = frame.surfaceFormat();
78
79 locker.unlock();
80
81 m_waitForFormat.notify_one();
82 }
83
85}
86
88{
89 if (!m_grabber)
90 return {};
91
92 QMutexLocker<QMutex> locker(&m_formatMutex);
93 while (!m_format)
94 m_waitForFormat.wait(&m_formatMutex);
95 return *m_format;
96}
97
99{
100 return AV_PIX_FMT_VIDEOTOOLBOX;
101}
102
104{
105public:
106 Grabber(QAVFScreenCapture &capture, QScreen *screen, CGDirectDisplayID screenID,
107 std::unique_ptr<QFFmpeg::HWAccel> hwAccel)
108 {
109 m_captureSession = [[AVCaptureSession alloc] init];
110
111 m_sampleBufferDelegate = [[QAVFSampleBufferDelegate alloc]
112 initWithFrameHandler:[&capture](const QVideoFrame &frame) {
113 capture.onNewFrame(frame);
114 }];
115
116 m_videoDataOutput = [[AVCaptureVideoDataOutput alloc] init];
117
118 NSDictionary *videoSettings = [NSDictionary
119 dictionaryWithObjectsAndKeys:[NSNumber numberWithUnsignedInt:DefaultCVPixelFormat],
120 kCVPixelBufferPixelFormatTypeKey, nil];
121
122 [m_videoDataOutput setVideoSettings:videoSettings];
123 [m_videoDataOutput setAlwaysDiscardsLateVideoFrames:true];
124
125 // Configure video output
126 m_dispatchQueue = dispatch_queue_create("vf_queue", nullptr);
127 [m_videoDataOutput setSampleBufferDelegate:m_sampleBufferDelegate queue:m_dispatchQueue];
128
129 [m_captureSession addOutput:m_videoDataOutput];
130
131 [m_sampleBufferDelegate setHWAccel:std::move(hwAccel)];
132
133 const auto frameRate = std::min(screen->refreshRate(), MaxScreenCaptureFrameRate);
134 [m_sampleBufferDelegate setVideoFormatFrameRate:frameRate];
135
136 m_screenInput = [[AVCaptureScreenInput alloc] initWithDisplayID:screenID];
137 [m_screenInput setMinFrameDuration:CMTimeMake(1, static_cast<int32_t>(frameRate))];
138 [m_captureSession addInput:m_screenInput];
139
140 [m_captureSession startRunning];
141 }
142
144 {
145 if (m_captureSession)
146 [m_captureSession stopRunning];
147
148 if (m_dispatchQueue)
149 dispatch_release(m_dispatchQueue);
150
151 [m_sampleBufferDelegate release];
152 [m_screenInput release];
153 [m_videoDataOutput release];
154 [m_captureSession release];
155 }
156
157private:
158 AVCaptureSession *m_captureSession = nullptr;
159 AVCaptureScreenInput *m_screenInput = nullptr;
160 AVCaptureVideoDataOutput *m_videoDataOutput = nullptr;
161 QAVFSampleBufferDelegate *m_sampleBufferDelegate = nullptr;
162 dispatch_queue_t m_dispatchQueue = nullptr;
163};
164
165bool QAVFScreenCapture::initScreenCapture(QScreen *screen)
166{
167 const auto screenID = findDisplayByName(screen->name());
168
169 if (screenID == kCGNullDirectDisplay) {
170 updateError(InternalError, QLatin1String("Screen exists but couldn't been found by name"));
171 return false;
172 }
173
174 auto hwAccel = QFFmpeg::HWAccel::create(AV_HWDEVICE_TYPE_VIDEOTOOLBOX);
175
176 if (!hwAccel) {
177 updateError(CaptureFailed, QLatin1String("Couldn't create videotoolbox hw acceleration"));
178 return false;
179 }
180
181 hwAccel->createFramesContext(av_map_videotoolbox_format_to_pixfmt(DefaultCVPixelFormat),
183
184 if (!hwAccel->hwFramesContextAsBuffer()) {
185 updateError(CaptureFailed, QLatin1String("Couldn't create hw frames context"));
186 return false;
187 }
188
189 m_grabber = std::make_unique<Grabber>(*this, screen, screenID, std::move(hwAccel));
190 return true;
191}
192
193void QAVFScreenCapture::resetCapture()
194{
195 m_grabber.reset();
196 m_format = {};
197}
198
200
201#include "moc_qavfscreencapture_p.cpp"
Grabber(QAVFScreenCapture &capture, QScreen *screen, CGDirectDisplayID screenID, std::unique_ptr< QFFmpeg::HWAccel > hwAccel)
bool setActiveInternal(bool active) override
~QAVFScreenCapture() override
QVideoFrameFormat frameFormat() const override
std::optional< int > ffmpegHWPixelFormat() const override
static std::unique_ptr< HWAccel > create(AVHWDeviceType deviceType)
void updateError(Error error, const QString &errorString)
bool checkScreenWithError(ScreenSource &screen)
void newVideoFrame(const QVideoFrame &)
The QScreen class is used to query screen properties. \inmodule QtGui.
Definition qscreen.h:32
qreal devicePixelRatio
the screen's ratio between physical pixels and device-independent pixels
Definition qscreen.h:59
qreal refreshRate
the approximate vertical refresh rate of the screen in Hz
Definition qscreen.h:64
QSize size
the pixel resolution of the screen
Definition qscreen.h:41
QString name
a user presentable string representing the screen
Definition qscreen.h:36
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
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
bool wait(QMutex *, QDeadlineTimer=QDeadlineTimer(QDeadlineTimer::Forever))
Combined button and popup list for selecting options.
static constexpr qreal MaxScreenCaptureFrameRate
GLuint name
GLuint in
QLatin1StringView QLatin1String
Definition qstringfwd.h:31
QScreen * screen
[1]
Definition main.cpp:29
static QT_BEGIN_NAMESPACE void init(QTextBoundaryFinder::BoundaryType type, QStringView str, QCharAttributes *attributes)
#define emit
sem release()
QQueue< int > queue
[0]
QFrame frame
[0]