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_capture(capture),
70 m_screen(screen),
71 m_window(std::move(window))
72 {
73 connect(qApp, &QGuiApplication::screenRemoved, this, &Grabber::onScreenRemoved);
74 addFrameCallback(m_capture, &QGrabWindowSurfaceCapture::newVideoFrame);
75 connect(this, &Grabber::errorUpdated, &m_capture, &QGrabWindowSurfaceCapture::updateError);
76 }
77
78 void onScreenRemoved(QScreen *screen)
79 {
80 /* The hack allows to lock screens removing while QScreen::grabWindow is in progress.
81 * The current solution works since QGuiApplication::screenRemoved is emitted from
82 * the destructor of QScreen before destruction members of the object.
83 * Note, QGuiApplication works with screens in the main thread, and any removing of a screen
84 * must be synchronized with grabbing thread.
85 */
86 QMutexLocker locker(&m_screenRemovingMutex);
87
88 if (m_screenRemovingLocked) {
89 qDebug() << "Screen" << screen->name()
90 << "is removed while screen window grabbing lock is active";
91 }
92
93 while (m_screenRemovingLocked)
94 m_screenRemovingWc.wait(&m_screenRemovingMutex);
95 }
96
97 void setScreenRemovingLocked(bool locked)
98 {
99 Q_ASSERT(locked != m_screenRemovingLocked);
100
101 {
102 QMutexLocker locker(&m_screenRemovingMutex);
103 m_screenRemovingLocked = locked;
104 }
105
106 if (!locked)
107 m_screenRemovingWc.wakeAll();
108 }
109
110 void updateFormat(const QVideoFrameFormat &format)
111 {
112 if (m_format && m_format->isValid())
113 return;
114
115 {
116 QMutexLocker locker(&m_formatMutex);
117 m_format = format;
118 }
119
120 m_waitForFormat.wakeAll();
121 }
122
124 {
125 setScreenRemovingLocked(true);
126 auto screenGuard = qScopeGuard([this] {
127 setScreenRemovingLocked(false);
128 });
129
130 WId wid = m_window ? m_window->winId() : 0;
131 QScreen *screen = m_window ? m_window->screen() : m_screen ? m_screen.data() : nullptr;
132
133 if (!screen) {
134 updateError(QPlatformSurfaceCapture::CaptureFailed, u"Screen not found"_s);
135 return {};
136 }
137
138 setFrameRate(screen->refreshRate());
139
140 QPixmap p = screen->grabWindow(wid);
141 auto buffer = std::make_unique<QImageVideoBuffer>(p.toImage());
142 const auto img = buffer->underlyingImage();
143
144 QVideoFrameFormat format(img.size(),
145 QVideoFrameFormat::pixelFormatFromImageFormat(img.format()));
146 format.setStreamFrameRate(screen->refreshRate());
147 updateFormat(format);
148
149 if (!format.isValid()) {
150 updateError(QPlatformSurfaceCapture::CaptureFailed,
151 u"Failed to grab the screen content"_s);
152 return {};
153 }
154
155 return QVideoFramePrivate::createFrame(std::move(buffer), std::move(format));
156 }
157
158private:
159 QGrabWindowSurfaceCapture &m_capture;
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