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