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 // Limit frame rate to 30 fps for performance reasons,
32 // to be reviewed at the next optimization round
33 setFrameRate(std::min(screen->refreshRate(), qreal(30.0)));
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(), QVideoFrameFormat::pixelFormatFromImageFormat(image.format()) };
62 m_format.setStreamFrameRate(frameRate());
63 }
64
65 return QVideoFramePrivate::createFrame(std::move(videoBuffer), m_format);
66 }
67
69};
70
71#ifndef QT_NO_QUICK
72class QEglfsScreenCapture::QuickGrabber : public Grabber
73{
74public:
75 QuickGrabber(QEglfsScreenCapture &screenCapture, QScreen *screen, QQuickWindow *quickWindow)
76 : Grabber(screenCapture, screen), m_quickWindow(quickWindow)
77 {
78 Q_ASSERT(m_quickWindow);
79 }
80
81protected:
83 {
84 if (!m_quickWindow) {
85 updateError(Error::InternalError, QLatin1String("Window deleted"));
86 return {};
87 }
88
89 QImage image = m_quickWindow->grabWindow();
90
91 if (image.isNull()) {
92 updateError(Error::InternalError, QLatin1String("Image invalid"));
93 return {};
94 }
95
96 if (!m_format.isValid()) {
97 m_format = { image.size(),
98 QVideoFrameFormat::pixelFormatFromImageFormat(image.format()) };
99 m_format.setStreamFrameRate(frameRate());
100 }
101
102 return QVideoFramePrivate::createFrame(
103 std::make_unique<QImageVideoBuffer>(std::move(image)), m_format);
104 }
105
106private:
107 QPointer<QQuickWindow> m_quickWindow;
108};
109#endif // QT_NO_QUICK
110
111QEglfsScreenCapture::QEglfsScreenCapture() : QPlatformSurfaceCapture(ScreenSource{}) { }
112
113QEglfsScreenCapture::~QEglfsScreenCapture() = default;
114
115QVideoFrameFormat QEglfsScreenCapture::frameFormat() const
116{
117 return m_grabber ? m_grabber->format() : QVideoFrameFormat();
118}
119
120bool QEglfsScreenCapture::setActiveInternal(bool active)
121{
122 if (static_cast<bool>(m_grabber) == active)
123 return true;
124
125 if (m_grabber)
126 m_grabber.reset();
127
128 if (!active)
129 return true;
130
131 m_grabber = createGrabber();
132
133 if (!m_grabber) {
134 // TODO: This could mean that the UI is not started yet, so we should wait and try again,
135 // and then give error if still not started. Might not be possible here.
136 return false;
137 }
138
139 m_grabber->start();
140 return true;
141}
142
143bool QEglfsScreenCapture::isSupported()
144{
145 return QGuiApplication::platformName() == QLatin1String("eglfs");
146}
147
148std::unique_ptr<QEglfsScreenCapture::Grabber> QEglfsScreenCapture::createGrabber()
149{
150 auto screen = source<ScreenSource>();
151 if (!checkScreenWithError(screen))
152 return nullptr;
153
154 QOpenGLCompositor *compositor = QOpenGLCompositor::instance();
155
156 if (compositor->context()) {
157 // Create OpenGL grabber
158 if (!compositor->targetWindow()) {
159 updateError(Error::CaptureFailed,
160 QLatin1String("Target window is not set for OpenGL compositor"));
161 return nullptr;
162 }
163
164 return std::make_unique<Grabber>(*this, screen);
165 }
166
167#ifndef QT_NO_QUICK
168 // Check for QQuickWindow
169 auto windows = QGuiApplication::topLevelWindows();
170 auto it = ranges::find_if(windows, [screen](QWindow *window) {
171 auto quickWindow = qobject_cast<QQuickWindow *>(window);
172 if (!quickWindow)
173 return false;
174
175 return quickWindow->screen() == screen;
176 });
177
178 if (it != windows.end()) {
179 // Create grabber that calls QQuickWindow::grabWindow
180 return std::make_unique<QuickGrabber>(*this, screen, qobject_cast<QQuickWindow *>(*it));
181 }
182#endif // QT_NO_QUICK
183
184 updateError(Error::CaptureFailed, QLatin1String("No existing OpenGL context or QQuickWindow"));
185 return nullptr;
186}
187
188QT_END_NAMESPACE
Grabber(QEglfsScreenCapture &screenCapture, QScreen *screen)
QuickGrabber(QEglfsScreenCapture &screenCapture, QScreen *screen, QQuickWindow *quickWindow)
Combined button and popup list for selecting options.