Revision control

Copy as Markdown

// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qeglfsscreencapture_p.h"
#include <QtMultimedia/private/qsurfacecapturegrabber_p.h>
#include "qguiapplication.h"
#include "qopenglvideobuffer_p.h"
#include "private/qimagevideobuffer_p.h"
#include "private/qvideoframe_p.h"
#include <QtMultimedia/private/qmultimedia_ranges_p.h>
#include <QtOpenGL/private/qopenglcompositor_p.h>
#include <QtOpenGL/private/qopenglframebufferobject_p.h>
#ifndef QT_NO_QUICK
#include <QtQuick/qquickwindow.h>
#endif
QT_BEGIN_NAMESPACE
namespace ranges = QtMultimediaPrivate::ranges;
class QEglfsScreenCapture::Grabber : public QSurfaceCaptureGrabber
{
public:
Grabber(QEglfsScreenCapture &screenCapture, QScreen *screen)
: QSurfaceCaptureGrabber(QSurfaceCaptureGrabber::UseCurrentThread)
{
addFrameCallback(&screenCapture, &QEglfsScreenCapture::newVideoFrame);
connect(this, &Grabber::errorUpdated, &screenCapture, &QEglfsScreenCapture::updateError);
Q_ASSERT(screen);
setFrameRate(screenCapture.frameRate().value_or(qMin(screen->refreshRate(), 30.f)));
}
~Grabber() override { stop(); }
QVideoFrameFormat format() { return m_format; }
protected:
QVideoFrame grabFrame() override
{
auto nativeSize = QOpenGLCompositor::instance()->nativeTargetGeometry().size();
auto fbo = std::make_unique<QOpenGLFramebufferObject>(nativeSize);
if (!QOpenGLCompositor::instance()->grabToFrameBufferObject(
fbo.get(), QOpenGLCompositor::NotFlipped)) {
updateError(Error::InternalError, QLatin1String("Couldn't grab to framebuffer object"));
return {};
}
if (!fbo->isValid()) {
updateError(Error::InternalError, QLatin1String("Framebuffer object invalid"));
return {};
}
auto videoBuffer = std::make_unique<QOpenGLVideoBuffer>(std::move(fbo));
if (!m_format.isValid()) {
auto image = videoBuffer->ensureImageBuffer().underlyingImage();
m_format = { image.size(),
QVideoFrameFormat::pixelFormatFromImageFormat(image.format()) };
}
if (m_format.streamFrameRate() != activeFrameRate())
m_format.setStreamFrameRate(activeFrameRate());
return QVideoFramePrivate::createFrame(std::move(videoBuffer), m_format);
}
QVideoFrameFormat m_format;
};
#ifndef QT_NO_QUICK
class QEglfsScreenCapture::QuickGrabber : public Grabber
{
public:
QuickGrabber(QEglfsScreenCapture &screenCapture, QScreen *screen, QQuickWindow *quickWindow)
: Grabber(screenCapture, screen), m_quickWindow(quickWindow)
{
Q_ASSERT(m_quickWindow);
}
protected:
QVideoFrame grabFrame() override
{
if (!m_quickWindow) {
updateError(Error::InternalError, QLatin1String("Window deleted"));
return {};
}
QImage image = m_quickWindow->grabWindow();
if (image.isNull()) {
updateError(Error::InternalError, QLatin1String("Image invalid"));
return {};
}
if (!m_format.isValid()) {
m_format = { image.size(),
QVideoFrameFormat::pixelFormatFromImageFormat(image.format()) };
}
if (m_format.streamFrameRate() != activeFrameRate())
m_format.setStreamFrameRate(activeFrameRate());
return QVideoFramePrivate::createFrame(
std::make_unique<QImageVideoBuffer>(std::move(image)), m_format);
}
private:
QPointer<QQuickWindow> m_quickWindow;
};
#endif // QT_NO_QUICK
QEglfsScreenCapture::QEglfsScreenCapture() : QPlatformSurfaceCapture(ScreenSource{}) { }
QEglfsScreenCapture::~QEglfsScreenCapture() = default;
QVideoFrameFormat QEglfsScreenCapture::frameFormat() const
{
return m_grabber ? m_grabber->format() : QVideoFrameFormat();
}
bool QEglfsScreenCapture::setActiveInternal(bool active)
{
if (static_cast<bool>(m_grabber) == active)
return true;
if (m_grabber)
m_grabber.reset();
if (!active)
return true;
m_grabber = createGrabber();
if (!m_grabber) {
// TODO: This could mean that the UI is not started yet, so we should wait and try again,
// and then give error if still not started. Might not be possible here.
return false;
}
m_grabber->start();
return true;
}
bool QEglfsScreenCapture::isSupported()
{
return QGuiApplication::platformName() == QLatin1String("eglfs");
}
std::unique_ptr<QEglfsScreenCapture::Grabber> QEglfsScreenCapture::createGrabber()
{
auto screen = source<ScreenSource>();
if (!checkScreenWithError(screen))
return nullptr;
QOpenGLCompositor *compositor = QOpenGLCompositor::instance();
if (compositor->context() && QOpenGLContext::currentContext()) {
// Create OpenGL grabber
if (!compositor->targetWindow()) {
updateError(Error::CaptureFailed,
QLatin1String("Target window is not set for OpenGL compositor"));
return nullptr;
}
return std::make_unique<Grabber>(*this, screen);
}
#ifndef QT_NO_QUICK
// Check for QQuickWindow
auto windows = QGuiApplication::topLevelWindows();
auto it = ranges::find_if(windows, [screen](QWindow *window) {
auto quickWindow = qobject_cast<QQuickWindow *>(window);
if (!quickWindow)
return false;
return quickWindow->screen() == screen;
});
if (it != windows.end()) {
// Create grabber that calls QQuickWindow::grabWindow
return std::make_unique<QuickGrabber>(*this, screen, qobject_cast<QQuickWindow *>(*it));
}
#endif // QT_NO_QUICK
updateError(Error::CaptureFailed, QLatin1String("No existing OpenGL context or QQuickWindow"));
return nullptr;
}
QT_END_NAMESPACE