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
qgrabwindowsurfacecapture.cpp
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 "qvideoframe.h"
8
9#include "private/qimagevideobuffer_p.h"
10#include "private/qcapturablewindow_p.h"
11#include "private/qvideoframe_p.h"
12
13#include "qscreen.h"
14#include "qmutex.h"
15#include "qwaitcondition.h"
16#include "qpixmap.h"
18#include "qwindow.h"
19#include "qpointer.h"
20
21#include <QtCore/qloggingcategory.h>
22
23#include <utility>
24
25QT_BEGIN_NAMESPACE
26
27using namespace Qt::StringLiterals;
28
29namespace {
30
31using WindowUPtr = std::unique_ptr<QWindow>;
32
33} // namespace
34
35class QGrabWindowSurfaceCapture::Grabber : public QFFmpegSurfaceCaptureGrabber
36{
37public:
38 Grabber(QGrabWindowSurfaceCapture &capture, QScreen *screen) : Grabber(capture, screen, nullptr)
39 {
40 Q_ASSERT(screen);
41 }
42
43 Grabber(QGrabWindowSurfaceCapture &capture, WindowUPtr window)
44 : Grabber(capture, nullptr, std::move(window))
45 {
46 Q_ASSERT(m_window);
47 }
48
50 stop();
51
52 Q_ASSERT(!m_screenRemovingLocked);
53 }
54
56 {
57 QMutexLocker locker(&m_formatMutex);
58 while (!m_format)
59 m_waitForFormat.wait(&m_formatMutex);
60 return *m_format;
61 }
62
63private:
64 Grabber(QGrabWindowSurfaceCapture &capture, QScreen *screen, WindowUPtr window)
65 : QFFmpegSurfaceCaptureGrabber(
66 QGuiApplication::platformName() == QLatin1String("eglfs")
67 ? QFFmpegSurfaceCaptureGrabber::UseCurrentThread
68 : QFFmpegSurfaceCaptureGrabber::CreateGrabbingThread),
69 m_screen(screen),
70 m_window(std::move(window))
71 {
72 connect(qApp, &QGuiApplication::screenRemoved, this, &Grabber::onScreenRemoved);
73 addFrameCallback(&capture, &QGrabWindowSurfaceCapture::newVideoFrame);
74 connect(this, &Grabber::errorUpdated, &capture, &QGrabWindowSurfaceCapture::updateError);
75
76 const auto defaultRate = screen ? qMin(screen->refreshRate(), DefaultScreenCaptureFrameRate)
77 : DefaultScreenCaptureFrameRate;
78 setFrameRate(capture.frameRate().value_or(defaultRate));
79 }
80
81 void onScreenRemoved(QScreen *screen)
82 {
83 /* The hack allows to lock screens removing while QScreen::grabWindow is in progress.
84 * The current solution works since QGuiApplication::screenRemoved is emitted from
85 * the destructor of QScreen before destruction members of the object.
86 * Note, QGuiApplication works with screens in the main thread, and any removing of a screen
87 * must be synchronized with grabbing thread.
88 */
89 QMutexLocker locker(&m_screenRemovingMutex);
90
91 if (m_screenRemovingLocked) {
92 qDebug() << "Screen" << screen->name()
93 << "is removed while screen window grabbing lock is active";
94 }
95
96 while (m_screenRemovingLocked)
97 m_screenRemovingWc.wait(&m_screenRemovingMutex);
98 }
99
100 void setScreenRemovingLocked(bool locked)
101 {
102 Q_ASSERT(locked != m_screenRemovingLocked);
103
104 {
105 QMutexLocker locker(&m_screenRemovingMutex);
106 m_screenRemovingLocked = locked;
107 }
108
109 if (!locked)
110 m_screenRemovingWc.wakeAll();
111 }
112
113 void updateFormat(const QVideoFrameFormat &format)
114 {
115 if (m_format && m_format->isValid())
116 return;
117
118 {
119 QMutexLocker locker(&m_formatMutex);
120 m_format = format;
121 }
122
123 m_waitForFormat.wakeAll();
124 }
125
127 {
128 setScreenRemovingLocked(true);
129 auto screenGuard = qScopeGuard([this] {
130 setScreenRemovingLocked(false);
131 });
132
133 WId wid = m_window ? m_window->winId() : 0;
134 QScreen *screen = m_window ? m_window->screen() : m_screen ? m_screen.data() : nullptr;
135
136 if (!screen) {
137 updateError(QPlatformSurfaceCapture::CaptureFailed, u"Screen not found"_s);
138 return {};
139 }
140
141 QPixmap p = screen->grabWindow(wid);
142 auto buffer = std::make_unique<QImageVideoBuffer>(p.toImage());
143 const auto img = buffer->underlyingImage();
144
145 QVideoFrameFormat format(img.size(),
146 QVideoFrameFormat::pixelFormatFromImageFormat(img.format()));
147 format.setStreamFrameRate(frameRate());
148 updateFormat(format);
149
150 if (!format.isValid()) {
151 updateError(QPlatformSurfaceCapture::CaptureFailed,
152 u"Failed to grab the screen content"_s);
153 return {};
154 }
155
156 return QVideoFramePrivate::createFrame(std::move(buffer), std::move(format));
157 }
158
159private:
160 QPointer<QScreen> m_screen;
161 WindowUPtr m_window;
162
163 QMutex m_formatMutex;
164 QWaitCondition m_waitForFormat;
165 std::optional<QVideoFrameFormat> m_format;
166
167 QMutex m_screenRemovingMutex;
168 bool m_screenRemovingLocked = false;
169 QWaitCondition m_screenRemovingWc;
170};
171
172QGrabWindowSurfaceCapture::QGrabWindowSurfaceCapture(Source initialSource)
173 : QPlatformSurfaceCapture(std::move(initialSource))
174{
175}
176
177QGrabWindowSurfaceCapture::~QGrabWindowSurfaceCapture() = default;
178
179QVideoFrameFormat QGrabWindowSurfaceCapture::frameFormat() const
180{
181 if (m_grabber)
182 return m_grabber->format();
183 else
184 return {};
185}
186
187bool QGrabWindowSurfaceCapture::setActiveInternal(bool active)
188{
189 if (active == static_cast<bool>(m_grabber))
190 return true;
191
192 if (m_grabber)
193 m_grabber.reset();
194 else
195 std::visit([this](auto source) { activate(std::move(source)); }, source());
196
197 return static_cast<bool>(m_grabber) == active;
198}
199
200void QGrabWindowSurfaceCapture::activate(ScreenSource screen)
201{
202 if (!checkScreenWithError(screen))
203 return;
204
205 m_grabber = std::make_unique<Grabber>(*this, screen);
206 m_grabber->start();
207}
208
209void QGrabWindowSurfaceCapture::activate(const WindowSource& window)
210{
211 auto handle = QCapturableWindowPrivate::handle(window);
212 auto wid = handle ? handle->id : 0;
213 if (auto wnd = WindowUPtr(QWindow::fromWinId(wid))) {
214 if (!wnd->screen()) {
215 updateError(InternalError,
216 u"Window " + QString::number(wid) + u" doesn't belong to any screen");
217 } else {
218 m_grabber = std::make_unique<Grabber>(*this, std::move(wnd));
219 m_grabber->start();
220 }
221 } else {
222 updateError(NotFound,
223 u"Window " + QString::number(wid) + u"doesn't exist or permissions denied");
224 }
225}
226
227QT_END_NAMESPACE
Grabber(QGrabWindowSurfaceCapture &capture, WindowUPtr window)
Grabber(QGrabWindowSurfaceCapture &capture, QScreen *screen)
\inmodule QtCore
Definition qmutex.h:346
#define qApp