Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
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
4#include <QtFFmpegMediaPluginImpl/private/qavfscreencapture_p.h>
5
6#include <QtGui/qscreen.h>
7
8#include <QtFFmpegMediaPluginImpl/private/qavfsamplebufferdelegate_p.h>
9#include <QtFFmpegMediaPluginImpl/private/qffmpegsurfacecapturegrabber_p.h>
10#define AVMediaType XAVMediaType
11#include <QtFFmpegMediaPluginImpl/private/qffmpeghwaccel_p.h>
12#undef AVMediaType
13
14#define AVMediaType XAVMediaType
15extern "C" {
16#include <libavutil/hwcontext_videotoolbox.h>
17#include <libavutil/hwcontext.h>
18}
19#undef AVMediaType
20
21#import <AppKit/NSScreen.h>
22
23#include <dispatch/dispatch.h>
24
25namespace {
26
27const auto DefaultCVPixelFormat = kCVPixelFormatType_32BGRA;
28
29CGDirectDisplayID findDisplayByName(const QString &name)
30{
31 for (NSScreen *screen in NSScreen.screens) {
32 if (name == QString::fromNSString(screen.localizedName))
33 return [screen.deviceDescription[@"NSScreenNumber"] unsignedIntValue];
34 }
35 return kCGNullDirectDisplay;
36}
37}
38
39QT_BEGIN_NAMESPACE
40
41class QAVFScreenCapture::Grabber
42{
43public:
44 Grabber(QAVFScreenCapture &capture, QScreen *screen, CGDirectDisplayID screenID,
45 std::unique_ptr<QFFmpeg::HWAccel> hwAccel)
46 {
47 m_captureSession = [[AVCaptureSession alloc] init];
48
49 m_sampleBufferDelegate = [[QAVFSampleBufferDelegate alloc]
50 initWithFrameHandler:[&capture](const QVideoFrame &frame) {
51 capture.onNewFrame(frame);
52 }];
53
54 m_videoDataOutput = [[AVCaptureVideoDataOutput alloc] init];
55
56 NSDictionary *videoSettings = [NSDictionary
57 dictionaryWithObjectsAndKeys:[NSNumber numberWithUnsignedInt:DefaultCVPixelFormat],
58 kCVPixelBufferPixelFormatTypeKey, nil];
59
60 [m_videoDataOutput setVideoSettings:videoSettings];
61 [m_videoDataOutput setAlwaysDiscardsLateVideoFrames:true];
62
63 // Configure video output
64 m_dispatchQueue = dispatch_queue_create("vf_queue", nullptr);
65 [m_videoDataOutput setSampleBufferDelegate:m_sampleBufferDelegate queue:m_dispatchQueue];
66
67 [m_captureSession addOutput:m_videoDataOutput];
68
69 [m_sampleBufferDelegate setHWAccel:std::move(hwAccel)];
70
71 const auto frameRate = std::min(screen->refreshRate(), MaxScreenCaptureFrameRate);
72 [m_sampleBufferDelegate setVideoFormatFrameRate:frameRate];
73
74 m_screenInput = [[AVCaptureScreenInput alloc] initWithDisplayID:screenID];
75 [m_screenInput setMinFrameDuration:CMTimeMake(1, static_cast<int32_t>(frameRate))];
76 [m_captureSession addInput:m_screenInput];
77
78 [m_captureSession startRunning];
79 }
80
81 ~Grabber()
82 {
83 if (m_captureSession)
84 [m_captureSession stopRunning];
85
86 if (m_dispatchQueue) {
87 // Push a blocking job to the background frame thread,
88 // so we guarantee future frames are discarded. This
89 // causes the frameHandler to be destroyed, and the reference
90 // to this QAVFScreenCapture is cleared.
91 dispatch_sync(
92 m_dispatchQueue,
93 [this]() {
94 [m_sampleBufferDelegate discardFutureSamples];
95 });
96
97 dispatch_release(m_dispatchQueue);
98 }
99
100 [m_sampleBufferDelegate release];
101 [m_screenInput release];
102 [m_videoDataOutput release];
103 [m_captureSession release];
104 }
105
106private:
107 AVCaptureSession *m_captureSession = nullptr;
108 AVCaptureScreenInput *m_screenInput = nullptr;
109 AVCaptureVideoDataOutput *m_videoDataOutput = nullptr;
110 QAVFSampleBufferDelegate *m_sampleBufferDelegate = nullptr;
111 dispatch_queue_t m_dispatchQueue = nullptr;
112};
113
114QAVFScreenCapture::QAVFScreenCapture() : QPlatformSurfaceCapture(ScreenSource{})
115{
116 CGRequestScreenCaptureAccess();
117}
118
119QAVFScreenCapture::~QAVFScreenCapture()
120{
121 resetCapture();
122}
123
124bool QAVFScreenCapture::setActiveInternal(bool active)
125{
126 if (active) {
127 if (!CGPreflightScreenCaptureAccess()) {
128 updateError(CaptureFailed, QLatin1String("Permissions denied"));
129 return false;
130 }
131
132 auto screen = source<ScreenSource>();
133
134 if (!checkScreenWithError(screen))
135 return false;
136
137 return initScreenCapture(screen);
138 } else {
139 resetCapture();
140 }
141
142 return true;
143}
144
145void QAVFScreenCapture::onNewFrame(const QVideoFrame &frame)
146{
147 // Since writing of the format is supposed to be only from one thread,
148 // the read-only comparison without a mutex is thread-safe
149 if (!m_format || m_format != frame.surfaceFormat()) {
150 QMutexLocker<QMutex> locker(&m_formatMutex);
151
152 m_format = frame.surfaceFormat();
153
154 locker.unlock();
155
156 m_waitForFormat.notify_one();
157 }
158
159 emit newVideoFrame(frame);
160}
161
162QVideoFrameFormat QAVFScreenCapture::frameFormat() const
163{
164 if (!m_grabber)
165 return {};
166
167 QMutexLocker<QMutex> locker(&m_formatMutex);
168 while (!m_format)
169 m_waitForFormat.wait(&m_formatMutex);
170 return *m_format;
171}
172
173std::optional<int> QAVFScreenCapture::ffmpegHWPixelFormat() const
174{
175 return AV_PIX_FMT_VIDEOTOOLBOX;
176}
177
178bool QAVFScreenCapture::initScreenCapture(QScreen *screen)
179{
180 const auto screenID = findDisplayByName(screen->name());
181
182 if (screenID == kCGNullDirectDisplay) {
183 updateError(InternalError, QLatin1String("Screen exists but couldn't been found by name"));
184 return false;
185 }
186
187 auto hwAccel = QFFmpeg::HWAccel::create(AV_HWDEVICE_TYPE_VIDEOTOOLBOX);
188
189 if (!hwAccel) {
190 updateError(CaptureFailed, QLatin1String("Couldn't create videotoolbox hw acceleration"));
191 return false;
192 }
193
194 hwAccel->createFramesContext(av_map_videotoolbox_format_to_pixfmt(DefaultCVPixelFormat),
195 screen->size() * screen->devicePixelRatio());
196
197 if (!hwAccel->hwFramesContextAsBuffer()) {
198 updateError(CaptureFailed, QLatin1String("Couldn't create hw frames context"));
199 return false;
200 }
201
202 m_grabber = std::make_unique<Grabber>(*this, screen, screenID, std::move(hwAccel));
203 return true;
204}
205
206void QAVFScreenCapture::resetCapture()
207{
208 m_grabber.reset();
209 m_format = {};
210}
211
212QT_END_NAMESPACE
213
214#include "moc_qavfscreencapture_p.cpp"