6#include <QtCore/qthread.h>
7#include <QtCore/private/qcore_mac_p.h>
9#include <QtFFmpegMediaPluginImpl/private/qffmpegmediacapturesession_p.h>
10#include <QtFFmpegMediaPluginImpl/private/qmacscreencapturekit_p.h>
12#include <QtMultimedia/private/qmultimedia_ranges_p.h>
13#include <QtMultimedia/private/qcapturablewindow_p.h>
14#include <QtMultimedia/private/qvideoframe_p.h>
16#define AVMediaType XAVMediaType
18#include <libavutil/hwcontext_videotoolbox.h>
22namespace ranges = QtMultimediaPrivate::ranges;
23using namespace Qt::Literals::StringLiterals;
31[[nodiscard]] q23::expected<AVFScopedPointer<SCWindow>, QString> findScWindow(CGWindowID input)
34 q23::expected<QMacScreenCaptureKit::CapturableItems, QString> enumerateResult =
35 QMacScreenCaptureKit::enumerateCapturableItems()
39 return q23::unexpected(enumerateResult.error());
41 const std::vector<AVFScopedPointer<SCWindow>> &windows = enumerateResult->windows;
42 auto it = ranges::find_if(
44 [&](
const AVFScopedPointer<SCWindow> &item) {
45 return item.data().windowID == input;
48 if (it == windows.end())
49 return q23::unexpected(u"Window not found"_s);
52 return AVFScopedPointer<SCWindow>{ [it->data() retain] };
56[[nodiscard]] q23::expected<AVFScopedPointer<SCWindow>, QPlatformSurfaceCapture::ErrorDetails>
57findScWindowForSource(
const QCapturableWindow &capturableWindow)
59 using Error = QPlatformSurfaceCapture::Error;
60 using ErrorDetails = QPlatformSurfaceCapture::ErrorDetails;
62 const QCapturableWindowPrivate *handle = QCapturableWindowPrivate::handle(capturableWindow);
64 return q23::unexpected{ ErrorDetails{
66 u"No window has been selected for capture."_s } };
67 const auto cgWindowId =
static_cast<CGWindowID>(handle->id);
70 q23::expected<AVFScopedPointer<SCWindow>, QString> scWindowResult = findScWindow(cgWindowId);
71 if (!scWindowResult) {
72 qCWarning(qLcMacScreenCapture)
73 <<
"Could not find associated SCWindow:"
74 << scWindowResult.error();
75 return q23::unexpected{ ErrorDetails{
77 u"The selected window could not be found."_s } };
80 return std::move(*scWindowResult);
83void setupQMacScreenCaptureKitConnections(
85 const QMacScreenCaptureKit &macScreenCaptureKit)
94 &QMacScreenCaptureKit::newVideoFrameGenerated,
96 [&windowCapture](QMacScreenCaptureKit::StreamId, QVideoFrame videoFrame) {
97 Q_EMIT windowCapture.newVideoFrame(videoFrame);
99 Qt::DirectConnection);
102 &macScreenCaptureKit,
103 &QMacScreenCaptureKit::newVideoFrameGenerated,
105 [&windowCapture](QMacScreenCaptureKit::StreamId streamId, QVideoFrame newFrame) {
106 windowCapture.onNewFrameFormatReceived(streamId, newFrame.surfaceFormat());
108 Qt::QueuedConnection);
111 &macScreenCaptureKit,
112 &QMacScreenCaptureKit::streamStoppedWithError,
114 &QSckWindowCapture::onStreamStoppedWithErrorEvent,
115 Qt::QueuedConnection);
138 using TryStartResult = q23::expected<std::unique_ptr<ActiveData>, ErrorDetails>;
140 auto tryStartStream = [&]() -> TryStartResult {
142 q23::expected<AVFScopedPointer<SCWindow>, ErrorDetails> scWindowResult =
143 findScWindowForSource(source<WindowSource>());
145 return q23::unexpected{ std::move(scWindowResult.error()) };
147 QMacScreenCaptureKit::StreamId newStreamId = allocateStreamId();
149 AVFScopedPointer<SCWindow> &scWindow = *scWindowResult;
154 m_session->onSourceActivating(*
this);
156 QMacScreenCaptureKit::CreateStreamInfo createInfo = {};
157 createInfo.streamId = newStreamId;
158 createInfo.connectionSetupFn = [&](QMacScreenCaptureKit &newObject) {
159 setupQMacScreenCaptureKitConnections(*
this, newObject);
161 createInfo.streamSettings.frameRate = frameRate();
162 createInfo.streamSettings.overrideIgnoreCursor = ignoreCursor();
165 using ResultType = q23::expected<std::unique_ptr<QMacScreenCaptureKit>, QString>;
166 std::future<ResultType> streamResultFuture = QMacScreenCaptureKit::createStreamFromWindow(
170 ResultType streamResult = streamResultFuture.get();
174 m_session->onSourceActivationFailure(*
this);
176 qCWarning(qLcMacScreenCapture)
177 <<
"Failed to start screen capture stream:"
178 << streamResult.error();
179 return q23::unexpected{ ErrorDetails{
180 QPlatformSurfaceCapture::Error::CaptureFailed,
181 u"Window capture could not be started."_s } };
184 std::unique_ptr<QMacScreenCaptureKit> &macScreenCaptureKit = *streamResult;
186 auto newActiveData = std::make_unique<ActiveData>();
187 newActiveData->macScreenCaptureKit = std::move(macScreenCaptureKit);
188 newActiveData->scWindow = std::move(scWindow);
189 newActiveData->streamId = newStreamId;
190 return newActiveData;
194 Q_ASSERT(!m_activeData);
196 TryStartResult result = tryStartStream();
198 const ErrorDetails &error = result.error();
199 QPlatformSurfaceCapture::updateError(error.error, error.description);
203 m_activeData = std::move(*result);
205 m_activeData.reset();
214 Q_ASSERT(m_activeData);
216 q23::expected<AVFScopedPointer<SCWindow>, ErrorDetails> scWindowResult =
217 findScWindowForSource(std::get<WindowSource>(newSource));
219 return SourceSwitchResult{ q23::unexpected{ std::move(scWindowResult.error()) } };
221 std::future<q23::expected<
void, QString>> switchFuture =
222 m_activeData->macScreenCaptureKit->switchToWindow(scWindowResult->data());
224 const q23::expected<
void, QString> result = switchFuture.get();
226 qCWarning(qLcMacScreenCapture)
227 <<
"Failed to switch screen capture stream to new window:"
229 return SourceSwitchResult{ q23::unexpected{ ErrorDetails{
230 Error::CaptureFailed,
231 u"Could not switch capture to the selected window."_s } } };
234 m_activeData->scWindow = std::move(*scWindowResult);
235 return SourceSwitchResult{};
239 QMacScreenCaptureKit::StreamId incomingStreamId,
240 QVideoFrameFormat
const &format)
242 Q_ASSERT(thread()->isCurrentThread());
243 if (activeStreamId() == incomingStreamId)
244 m_videoFrameFormat = format;
248 QMacScreenCaptureKit::StreamId incomingStreamId,
251 Q_ASSERT(thread()->isCurrentThread());
253 qCDebug(qLcMacScreenCapture)
255 <<
static_cast<int64_t>(incomingStreamId)
256 <<
"stopped with error:"
259 if (activeStreamId() != incomingStreamId)
264 m_activeData.reset();
265 m_videoFrameFormat.reset();
267 QPlatformSurfaceCapture::updateError(
268 Error::CaptureFailed,
269 u"The capture stream was closed by the system"_s);
274 Q_ASSERT(thread()->isCurrentThread());
276 if (m_videoFrameFormat)
277 return *m_videoFrameFormat;
283 Q_ASSERT(thread()->isCurrentThread());
284 return AV_PIX_FMT_VIDEOTOOLBOX;
296#include "moc_qsckwindowcapture_p.cpp"
void onStreamStoppedWithErrorEvent(QMacScreenCaptureKit::StreamId streamId, const QString &)
void setCaptureSession(QPlatformMediaCaptureSession *) override
std::optional< int > ffmpegHWPixelFormat() const override
QVideoFrameFormat frameFormat() const override
std::optional< SourceSwitchResult > switchActiveSource(const Source &newSource) override
void onNewFrameFormatReceived(QMacScreenCaptureKit::StreamId streamId, const QVideoFrameFormat &)
bool setActiveInternal(bool active) override
std::unique_ptr< QPlatformSurfaceCapture > makeQSckWindowCapture()