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
qeglfsscreencapture.cpp
Go to the documentation of this file.
1// Copyright (C) 2023 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
5
9#include "private/qimagevideobuffer_p.h"
10#include "private/qvideoframe_p.h"
11
12#include <QtMultimedia/private/qmultimedia_ranges_p.h>
13#include <QtOpenGL/private/qopenglcompositor_p.h>
14#include <QtOpenGL/private/qopenglframebufferobject_p.h>
15
16#ifndef QT_NO_QUICK
17#include <QtQuick/qquickwindow.h>
18#endif
19
21namespace ranges = QtMultimediaPrivate::ranges;
22
23class QEglfsScreenCapture::Grabber : public QFFmpegSurfaceCaptureGrabber
24{
25public:
26 Grabber(QEglfsScreenCapture &screenCapture, QScreen *screen)
28 {
29 addFrameCallback(&screenCapture, &QEglfsScreenCapture::newVideoFrame);
30 connect(this, &Grabber::errorUpdated, &screenCapture, &QEglfsScreenCapture::updateError);
31
32 Q_ASSERT(screen);
33 setFrameRate(screenCapture.frameRate().value_or(qMin(screen->refreshRate(), 30.f)));
34 }
35
37
38 QVideoFrameFormat format() { return m_format; }
39
40protected:
42 {
43 auto nativeSize = QOpenGLCompositor::instance()->nativeTargetGeometry().size();
44 auto fbo = std::make_unique<QOpenGLFramebufferObject>(nativeSize);
45
46 if (!QOpenGLCompositor::instance()->grabToFrameBufferObject(
47 fbo.get(), QOpenGLCompositor::NotFlipped)) {
48 updateError(Error::InternalError, QLatin1String("Couldn't grab to framebuffer object"));
49 return {};
50 }
51
52 if (!fbo->isValid()) {
53 updateError(Error::InternalError, QLatin1String("Framebuffer object invalid"));
54 return {};
55 }
56
57 auto videoBuffer = std::make_unique<QOpenGLVideoBuffer>(std::move(fbo));
58
59 if (!m_format.isValid()) {
60 auto image = videoBuffer->ensureImageBuffer().underlyingImage();
61 m_format = { image.size(),
62 QVideoFrameFormat::pixelFormatFromImageFormat(image.format()) };
63 }
64
65 if (m_format.streamFrameRate() != frameRate())
66 m_format.setStreamFrameRate(frameRate());
67
68 return QVideoFramePrivate::createFrame(std::move(videoBuffer), m_format);
69 }
70
72};
73
74#ifndef QT_NO_QUICK
75class QEglfsScreenCapture::QuickGrabber : public Grabber
76{
77public:
78 QuickGrabber(QEglfsScreenCapture &screenCapture, QScreen *screen, QQuickWindow *quickWindow)
79 : Grabber(screenCapture, screen), m_quickWindow(quickWindow)
80 {
81 Q_ASSERT(m_quickWindow);
82 }
83
84protected:
86 {
87 if (!m_quickWindow) {
88 updateError(Error::InternalError, QLatin1String("Window deleted"));
89 return {};
90 }
91
92 QImage image = m_quickWindow->grabWindow();
93
94 if (image.isNull()) {
95 updateError(Error::InternalError, QLatin1String("Image invalid"));
96 return {};
97 }
98
99 if (!m_format.isValid()) {
100 m_format = { image.size(),
101 QVideoFrameFormat::pixelFormatFromImageFormat(image.format()) };
102 }
103
104 if (m_format.streamFrameRate() != frameRate())
105 m_format.setStreamFrameRate(frameRate());
106
107 return QVideoFramePrivate::createFrame(
108 std::make_unique<QImageVideoBuffer>(std::move(image)), m_format);
109 }
110
111private:
112 QPointer<QQuickWindow> m_quickWindow;
113};
114#endif // QT_NO_QUICK
115
116QEglfsScreenCapture::QEglfsScreenCapture() : QPlatformSurfaceCapture(ScreenSource{}) { }
117
118QEglfsScreenCapture::~QEglfsScreenCapture() = default;
119
120QVideoFrameFormat QEglfsScreenCapture::frameFormat() const
121{
122 return m_grabber ? m_grabber->format() : QVideoFrameFormat();
123}
124
125bool QEglfsScreenCapture::setActiveInternal(bool active)
126{
127 if (static_cast<bool>(m_grabber) == active)
128 return true;
129
130 if (m_grabber)
131 m_grabber.reset();
132
133 if (!active)
134 return true;
135
136 m_grabber = createGrabber();
137
138 if (!m_grabber) {
139 // TODO: This could mean that the UI is not started yet, so we should wait and try again,
140 // and then give error if still not started. Might not be possible here.
141 return false;
142 }
143
144 m_grabber->start();
145 return true;
146}
147
148bool QEglfsScreenCapture::isSupported()
149{
150 return QGuiApplication::platformName() == QLatin1String("eglfs");
151}
152
153std::unique_ptr<QEglfsScreenCapture::Grabber> QEglfsScreenCapture::createGrabber()
154{
155 auto screen = source<ScreenSource>();
156 if (!checkScreenWithError(screen))
157 return nullptr;
158
159 QOpenGLCompositor *compositor = QOpenGLCompositor::instance();
160
161 if (compositor->context() && QOpenGLContext::currentContext()) {
162 // Create OpenGL grabber
163 if (!compositor->targetWindow()) {
164 updateError(Error::CaptureFailed,
165 QLatin1String("Target window is not set for OpenGL compositor"));
166 return nullptr;
167 }
168
169 return std::make_unique<Grabber>(*this, screen);
170 }
171
172#ifndef QT_NO_QUICK
173 // Check for QQuickWindow
174 auto windows = QGuiApplication::topLevelWindows();
175 auto it = ranges::find_if(windows, [screen](QWindow *window) {
176 auto quickWindow = qobject_cast<QQuickWindow *>(window);
177 if (!quickWindow)
178 return false;
179
180 return quickWindow->screen() == screen;
181 });
182
183 if (it != windows.end()) {
184 // Create grabber that calls QQuickWindow::grabWindow
185 return std::make_unique<QuickGrabber>(*this, screen, qobject_cast<QQuickWindow *>(*it));
186 }
187#endif // QT_NO_QUICK
188
189 updateError(Error::CaptureFailed, QLatin1String("No existing OpenGL context or QQuickWindow"));
190 return nullptr;
191}
192
193QT_END_NAMESPACE
Grabber(QEglfsScreenCapture &screenCapture, QScreen *screen)
QuickGrabber(QEglfsScreenCapture &screenCapture, QScreen *screen, QQuickWindow *quickWindow)
Combined button and popup list for selecting options.