Revision control
Copy as Markdown
// Copyright (C) 2024 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 "qopenglvideobuffer_p.h"
#include <qoffscreensurface.h>
#include <qthread.h>
#include <private/qimagevideobuffer_p.h>
#include <QtOpenGL/private/qopenglcompositor_p.h>
#include <QtOpenGL/private/qopenglframebufferobject_p.h>
QT_BEGIN_NAMESPACE
static QOpenGLContext *createContext(QOpenGLContext *shareContext)
{
// Create an OpenGL context for the current thread. The lifetime of the context is tied to the
// lifetime of the current thread.
auto context = std::make_unique<QOpenGLContext>();
context->setShareContext(shareContext);
if (!context->create()) {
qWarning() << "Couldn't create an OpenGL context for QOpenGLVideoBuffer";
return nullptr;
}
QObject::connect(QThread::currentThread(), &QThread::finished,
context.get(), &QOpenGLContext::deleteLater);
return context.release();
}
static bool setCurrentOpenGLContext()
{
auto compositorContext = QOpenGLCompositor::instance()->context();
// A thread-local variable is used to avoid creating a new context if we're called on the same
// thread. The context lifetime is tied to the current thread lifetime (see createContext()).
static thread_local QOpenGLContext *context = nullptr;
static thread_local QOffscreenSurface *surface = nullptr;
if (!context) {
context = compositorContext->thread()->isCurrentThread() ? compositorContext
: createContext(compositorContext);
if (!context)
return false;
surface = new QOffscreenSurface(nullptr, context);
surface->setFormat(context->format());
surface->create();
}
return context->makeCurrent(surface);
}
QOpenGLVideoBuffer::QOpenGLVideoBuffer(std::unique_ptr<QOpenGLFramebufferObject> fbo)
: m_fbo(std::move(fbo))
{
Q_ASSERT(m_fbo);
}
QOpenGLVideoBuffer::~QOpenGLVideoBuffer() = default;
QAbstractVideoBuffer::MapData QOpenGLVideoBuffer::map(QVideoFrame::MapMode mode)
{
return ensureImageBuffer().map(mode);
}
void QOpenGLVideoBuffer::unmap()
{
if (m_imageBuffer)
m_imageBuffer->unmap();
}
quint64 QOpenGLVideoBuffer::textureHandle(QRhi &, int plane)
{
Q_UNUSED(plane);
return m_fbo->texture();
}
QImageVideoBuffer &QOpenGLVideoBuffer::ensureImageBuffer()
{
// Create image buffer if not yet created.
// This is protected by mapMutex in QVideoFrame::map.
if (!m_imageBuffer) {
if (!setCurrentOpenGLContext())
qWarning() << "Failed to set current OpenGL context";
m_imageBuffer = std::make_unique<QImageVideoBuffer>(m_fbo->toImage(false));
}
return *m_imageBuffer;
}
QT_END_NAMESPACE