4#include <QtFFmpegMediaPluginImpl/private/qavfscreencapture_p.h>
6#include <QtGui/qscreen.h>
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>
14#define AVMediaType XAVMediaType
16#include <libavutil/hwcontext_videotoolbox.h>
17#include <libavutil/hwcontext.h>
21#import <AppKit/NSScreen.h>
23#include <dispatch/dispatch.h>
27const auto DefaultCVPixelFormat = kCVPixelFormatType_32BGRA;
29CGDirectDisplayID findDisplayByName(
const QString &name)
31 for (NSScreen *screen in NSScreen.screens) {
32 if (name == QString::fromNSString(screen.localizedName))
33 return [screen.deviceDescription[@
"NSScreenNumber"] unsignedIntValue];
35 return kCGNullDirectDisplay;
41class QAVFScreenCapture::Grabber
44 Grabber(QAVFScreenCapture &capture, QScreen *screen, CGDirectDisplayID screenID,
45 std::unique_ptr<QFFmpeg::HWAccel> hwAccel)
47 m_captureSession = [[AVCaptureSession alloc] init];
49 m_sampleBufferDelegate = [[QAVFSampleBufferDelegate alloc]
50 initWithFrameHandler:[&capture](
const QVideoFrame &frame) {
51 capture.onNewFrame(frame);
54 m_videoDataOutput = [[AVCaptureVideoDataOutput alloc] init];
56 NSDictionary *videoSettings = [NSDictionary
57 dictionaryWithObjectsAndKeys:[NSNumber numberWithUnsignedInt:DefaultCVPixelFormat],
58 kCVPixelBufferPixelFormatTypeKey, nil];
60 [m_videoDataOutput setVideoSettings:videoSettings];
61 [m_videoDataOutput setAlwaysDiscardsLateVideoFrames:
true];
64 m_dispatchQueue = dispatch_queue_create(
"vf_queue",
nullptr);
65 [m_videoDataOutput setSampleBufferDelegate:m_sampleBufferDelegate queue:m_dispatchQueue];
67 [m_captureSession addOutput:m_videoDataOutput];
69 [m_sampleBufferDelegate setHWAccel:std::move(hwAccel)];
71 const auto frameRate = std::min(screen->refreshRate(), MaxScreenCaptureFrameRate);
72 [m_sampleBufferDelegate setVideoFormatFrameRate:frameRate];
74 m_screenInput = [[AVCaptureScreenInput alloc] initWithDisplayID:screenID];
75 [m_screenInput setMinFrameDuration:CMTimeMake(1,
static_cast<int32_t>(frameRate))];
76 [m_captureSession addInput:m_screenInput];
78 [m_captureSession startRunning];
84 [m_captureSession stopRunning];
86 if (m_dispatchQueue) {
94 [m_sampleBufferDelegate discardFutureSamples];
97 dispatch_release(m_dispatchQueue);
100 [m_sampleBufferDelegate release];
101 [m_screenInput release];
102 [m_videoDataOutput release];
103 [m_captureSession release];
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;
114QAVFScreenCapture::QAVFScreenCapture() : QPlatformSurfaceCapture(ScreenSource{})
116 CGRequestScreenCaptureAccess();
119QAVFScreenCapture::~QAVFScreenCapture()
124bool QAVFScreenCapture::setActiveInternal(
bool active)
127 if (!CGPreflightScreenCaptureAccess()) {
128 updateError(CaptureFailed, QLatin1String(
"Permissions denied"));
132 auto screen = source<ScreenSource>();
134 if (!checkScreenWithError(screen))
137 return initScreenCapture(screen);
145void QAVFScreenCapture::onNewFrame(
const QVideoFrame &frame)
149 if (!m_format || m_format != frame.surfaceFormat()) {
150 QMutexLocker<QMutex> locker(&m_formatMutex);
152 m_format = frame.surfaceFormat();
156 m_waitForFormat.notify_one();
159 emit newVideoFrame(frame);
162QVideoFrameFormat QAVFScreenCapture::frameFormat()
const
167 QMutexLocker<QMutex> locker(&m_formatMutex);
169 m_waitForFormat.wait(&m_formatMutex);
173std::optional<
int> QAVFScreenCapture::ffmpegHWPixelFormat()
const
175 return AV_PIX_FMT_VIDEOTOOLBOX;
178bool QAVFScreenCapture::initScreenCapture(QScreen *screen)
180 const auto screenID = findDisplayByName(screen->name());
182 if (screenID == kCGNullDirectDisplay) {
183 updateError(InternalError, QLatin1String(
"Screen exists but couldn't been found by name"));
187 auto hwAccel = QFFmpeg::HWAccel::create(AV_HWDEVICE_TYPE_VIDEOTOOLBOX);
190 updateError(CaptureFailed, QLatin1String(
"Couldn't create videotoolbox hw acceleration"));
194 hwAccel->createFramesContext(av_map_videotoolbox_format_to_pixfmt(DefaultCVPixelFormat),
195 screen->size() * screen->devicePixelRatio());
197 if (!hwAccel->hwFramesContextAsBuffer()) {
198 updateError(CaptureFailed, QLatin1String(
"Couldn't create hw frames context"));
202 m_grabber = std::make_unique<Grabber>(*
this, screen, screenID, std::move(hwAccel));
206void QAVFScreenCapture::resetCapture()
214#include "moc_qavfscreencapture_p.cpp"