[vlc-devel] [RFC 46/82] qt: provide OpenGL video renderer

Pierre Lamot pierre at videolabs.io
Fri Feb 1 14:01:50 CET 2019


---
 modules/gui/qt/Makefile.am                    |   3 +
 .../video_renderer/videorenderer.cpp          |   7 +
 .../video_renderer/videorenderer.hpp          |   2 +-
 .../video_renderer/videorenderergl.cpp        | 227 ++++++++++++++++++
 .../video_renderer/videorenderergl.hpp        | 101 ++++++++
 5 files changed, 339 insertions(+), 1 deletion(-)
 create mode 100644 modules/gui/qt/components/video_renderer/videorenderergl.cpp
 create mode 100644 modules/gui/qt/components/video_renderer/videorenderergl.hpp

diff --git a/modules/gui/qt/Makefile.am b/modules/gui/qt/Makefile.am
index 4fef55cfad..7d0132c6a1 100644
--- a/modules/gui/qt/Makefile.am
+++ b/modules/gui/qt/Makefile.am
@@ -104,6 +104,8 @@ libqt_plugin_la_SOURCES = \
 	gui/qt/components/video_renderer/videorenderer.hpp \
 	gui/qt/components/video_renderer/videorendererdummy.cpp \
 	gui/qt/components/video_renderer/videorendererdummy.hpp \
+	gui/qt/components/video_renderer/videorenderergl.cpp \
+	gui/qt/components/video_renderer/videorenderergl.hpp \
 	gui/qt/components/custom_menus.cpp \
 	gui/qt/components/custom_menus.hpp \
 	gui/qt/components/epg/EPGChannels.cpp \
@@ -225,6 +227,7 @@ nodist_libqt_plugin_la_SOURCES = \
 	gui/qt/components/video_renderer/videosurface.moc.cpp \
 	gui/qt/components/video_renderer/videorenderer.moc.cpp \
 	gui/qt/components/video_renderer/videorendererdummy.moc.cpp \
+	gui/qt/components/video_renderer/videorenderergl.moc.cpp \
 	gui/qt/components/epg/EPGChannels.moc.cpp \
 	gui/qt/components/epg/EPGProgram.moc.cpp \
 	gui/qt/components/epg/EPGRuler.moc.cpp \
diff --git a/modules/gui/qt/components/video_renderer/videorenderer.cpp b/modules/gui/qt/components/video_renderer/videorenderer.cpp
index 09150f372d..d27987f7a9 100644
--- a/modules/gui/qt/components/video_renderer/videorenderer.cpp
+++ b/modules/gui/qt/components/video_renderer/videorenderer.cpp
@@ -5,6 +5,13 @@ VideoRenderer::VideoRenderer(QObject* parent)
 {
 }
 
+VideoRenderer::~VideoRenderer()
+{
+    QMutexLocker lock(&m_voutlock);
+    if (m_voutWindow)
+        vout_window_ReportClose(m_voutWindow);
+}
+
 void VideoRenderer::setupVoutWindow(vout_window_t* window)
 {
     QMutexLocker lock(&m_voutlock);
diff --git a/modules/gui/qt/components/video_renderer/videorenderer.hpp b/modules/gui/qt/components/video_renderer/videorenderer.hpp
index 98e3ea26c2..d94ec5e1aa 100644
--- a/modules/gui/qt/components/video_renderer/videorenderer.hpp
+++ b/modules/gui/qt/components/video_renderer/videorenderer.hpp
@@ -12,7 +12,7 @@ class VideoRenderer : public QObject
     Q_OBJECT
 public:
     VideoRenderer(QObject* parent = nullptr);
-    virtual ~VideoRenderer() {}
+    virtual ~VideoRenderer();
 
     virtual void setupVoutWindow(vout_window_t* window);
     virtual void enableVideo(unsigned width, unsigned height, bool fullscreen);
diff --git a/modules/gui/qt/components/video_renderer/videorenderergl.cpp b/modules/gui/qt/components/video_renderer/videorenderergl.cpp
new file mode 100644
index 0000000000..7e01f8d60b
--- /dev/null
+++ b/modules/gui/qt/components/video_renderer/videorenderergl.cpp
@@ -0,0 +1,227 @@
+#include "videorenderergl.hpp"
+#include <QtQuick/QSGImageNode>
+#include <QtQuick/QSGRectangleNode>
+#include <QtQuick/QQuickWindow>
+#include <vlc_vout_window.h>
+#include "main_interface.hpp"
+
+VideoRendererGL::VideoRendererGL(MainInterface* p_mi, QObject* parent)
+    : VideoRenderer(parent)
+    , m_mainInterface(p_mi)
+{
+    assert(m_mainInterface);
+    m_surfaceProvider = new VideoSurfaceGL(this);
+    for (int i = 0; i < 3; i++)
+    {
+        m_fbo[i] = nullptr;
+        m_textures[i] = nullptr;
+    }
+}
+
+QSharedPointer<QSGTexture> VideoRendererGL::getDisplayTexture()
+{
+    QMutexLocker lock(&m_lock);
+    if (!m_hasTextures)
+        return nullptr;
+    if (m_updated)
+    {
+        qSwap(m_displayIdx, m_bufferIdx);
+        m_updated = false;
+    }
+    return m_textures[m_displayIdx];
+}
+
+bool VideoRendererGL::make_current_cb(void* data, bool current)
+{
+    VideoRendererGL* that = static_cast<VideoRendererGL*>(data);
+    QMutexLocker lock(&that->m_lock);
+    if (!that->m_ctx || !that->m_surface)
+    {
+        return false;
+    }
+
+    if (current)
+        return that->m_ctx->makeCurrent(that->m_surface);
+    else
+        that->m_ctx->doneCurrent();
+    return true;
+}
+
+void*VideoRendererGL::get_proc_address_cb(void* data, const char* procName)
+{
+    VideoRendererGL* that = static_cast<VideoRendererGL*>(data);
+    return (void*)that->m_ctx->getProcAddress(procName);
+}
+
+void VideoRendererGL::swap_cb(void* data)
+{
+    VideoRendererGL* that = static_cast<VideoRendererGL*>(data);
+    {
+        QMutexLocker lock(&that->m_lock);
+        qSwap(that->m_renderIdx, that->m_bufferIdx);
+        that->m_updated = true;
+        that->m_hasTextures = true;
+    }
+    that->m_fbo[that->m_renderIdx]->bind();
+    emit that->updated();
+}
+
+bool VideoRendererGL::setup_cb(void* data)
+{
+    VideoRendererGL* that = static_cast<VideoRendererGL*>(data);
+
+    QMutexLocker lock(&that->m_lock);
+    that->m_window = that->m_mainInterface->getRootQuickWindow();
+    if (! that->m_window)
+        return false;
+
+    QOpenGLContext *current = that->m_window->openglContext();
+
+    that->m_ctx = new QOpenGLContext();
+    if (!that->m_ctx)
+    {
+        that->m_window = nullptr;
+        return false;
+    }
+    QSurfaceFormat format = current->format();
+
+    that->m_ctx->setFormat(format);
+    that->m_ctx->setShareContext(current);
+    that->m_ctx->create();
+
+    that->m_surface = new QOffscreenSurface();
+    if (!that->m_surface)
+    {
+        that->m_window = nullptr;
+        delete that->m_ctx;
+        that->m_ctx = nullptr;
+        return false;
+    }
+    that->m_surface->setFormat(that->m_ctx->format());
+    that->m_surface->create();
+
+    that->m_glViewport = (GlViewportPF)that->m_ctx->getProcAddress("glViewport");
+
+    return true;
+}
+
+void VideoRendererGL::cleanup_cb(void* data)
+{
+    VideoRendererGL* that = static_cast<VideoRendererGL*>(data);
+
+    QMutexLocker lock(&that->m_lock);
+    for (int i =0; i < 3; i++)
+    {
+        if (that->m_fbo[i])
+        {
+            delete that->m_fbo[i];
+            that->m_fbo[i] = nullptr;
+        }
+        if (that->m_textures[i])
+        {
+            that->m_textures[i] = nullptr;
+        }
+    }
+    that->m_size = QSize();
+    that->m_window = nullptr;
+    that->m_hasTextures = false;
+}
+
+void VideoRendererGL::resize_cb(void* data, unsigned width, unsigned height)
+{
+    VideoRendererGL* that = static_cast<VideoRendererGL*>(data);
+
+    QMutexLocker lock(&that->m_lock);
+    QSize newsize(width, height);
+    if (that->m_size != newsize)
+    {
+        that->m_size = newsize;
+        for (int i =0; i < 3; i++)
+        {
+            if (that->m_fbo[i])
+                delete that->m_fbo[i];
+            that->m_fbo[i] = new QOpenGLFramebufferObject(newsize);
+            that->m_textures[i] = QSharedPointer<QSGTexture>(that->m_window->createTextureFromId(that->m_fbo[i]->texture(), newsize));
+            that->m_hasTextures = false;
+        }
+        emit that->sizeChanged(newsize);
+    }
+    that->m_fbo[that->m_renderIdx]->bind();
+    //set the initial viewport
+    that->m_glViewport(0, 0, width, height);
+}
+
+void VideoRendererGL::setupVoutWindow(vout_window_t* voutWindow)
+{
+    VideoRenderer::setupVoutWindow(voutWindow);
+    var_Create( voutWindow, "vout", VLC_VAR_STRING );
+    var_Create( voutWindow, "gl", VLC_VAR_STRING );
+
+    var_SetString ( voutWindow, "vout", "gl" );
+    var_SetString ( voutWindow, "gl", "vgl");
+
+    var_Create( voutWindow, "vout-cb-opaque", VLC_VAR_ADDRESS );
+    var_Create( voutWindow, "vout-cb-setup", VLC_VAR_ADDRESS );
+    var_Create( voutWindow, "vout-cb-cleanup", VLC_VAR_ADDRESS );
+    var_Create( voutWindow, "vout-cb-update-output", VLC_VAR_ADDRESS );
+    var_Create( voutWindow, "vout-cb-swap", VLC_VAR_ADDRESS );
+    var_Create( voutWindow, "vout-cb-make-current", VLC_VAR_ADDRESS );
+    var_Create( voutWindow, "vout-cb-get-proc-address", VLC_VAR_ADDRESS );
+
+    var_SetAddress( voutWindow, "vout-cb-opaque", this );
+    var_SetAddress( voutWindow, "vout-cb-setup", (void*)&VideoRendererGL::setup_cb );
+    var_SetAddress( voutWindow, "vout-cb-cleanup", (void*)&VideoRendererGL::cleanup_cb );
+    var_SetAddress( voutWindow, "vout-cb-update-output", (void*)&VideoRendererGL::resize_cb );
+    var_SetAddress( voutWindow, "vout-cb-swap", (void*)&VideoRendererGL::swap_cb );
+    var_SetAddress( voutWindow, "vout-cb-make-current", (void*)&VideoRendererGL::make_current_cb );
+    var_SetAddress( voutWindow, "vout-cb-get-proc-address", (void*)&VideoRendererGL::get_proc_address_cb );
+}
+
+VideoSurfaceProvider*VideoRendererGL::getVideoSurfaceProvider()
+{
+    return m_surfaceProvider;
+}
+
+////////
+
+
+VideoSurfaceGL::VideoSurfaceGL(VideoRendererGL* renderer, QObject* parent)
+    : VideoSurfaceProvider(parent)
+    , m_renderer(renderer)
+{
+    connect(this, &VideoSurfaceGL::mouseMoved, m_renderer, &VideoRenderer::onMouseMoved);
+    connect(this, &VideoSurfaceGL::mousePressed, m_renderer, &VideoRenderer::onMousePressed);
+    connect(this, &VideoSurfaceGL::mouseDblClicked, m_renderer, &VideoRenderer::onMouseDoubleClick);
+    connect(this, &VideoSurfaceGL::mouseReleased, m_renderer, &VideoRenderer::onMouseReleased);
+
+    connect(this, &VideoSurfaceGL::surfaceSizeChanged, m_renderer, &VideoRenderer::onSurfaceSizeChanged);
+
+    connect(m_renderer, &VideoRendererGL::updated, this, &VideoSurfaceGL::update, Qt::QueuedConnection);
+    connect(m_renderer, &VideoRendererGL::sizeChanged, this, &VideoSurfaceGL::sourceSizeChanged, Qt::QueuedConnection);
+}
+
+QSGNode* VideoSurfaceGL::updatePaintNode(QQuickItem* item, QSGNode* oldNode, QQuickItem::UpdatePaintNodeData*)
+{
+    QSGSimpleTextureNode* node = static_cast<QSGSimpleTextureNode*>(oldNode);
+
+    QSharedPointer<QSGTexture> newdisplayTexture = m_renderer->getDisplayTexture();
+    if (!newdisplayTexture)
+    {
+        if (node)
+            node->setRect(item->boundingRect());
+        return node;
+    }
+    m_displayTexture = newdisplayTexture;
+
+    if (!node)
+    {
+        node = new QSGSimpleTextureNode();
+        node->setTextureCoordinatesTransform(QSGSimpleTextureNode::MirrorVertically);
+    }
+
+    node->setTexture(m_displayTexture.data());
+    node->setRect(item->boundingRect());
+    node->markDirty(QSGNode::DirtyMaterial);
+    return node;
+}
+
diff --git a/modules/gui/qt/components/video_renderer/videorenderergl.hpp b/modules/gui/qt/components/video_renderer/videorenderergl.hpp
new file mode 100644
index 0000000000..b7aeda3f07
--- /dev/null
+++ b/modules/gui/qt/components/video_renderer/videorenderergl.hpp
@@ -0,0 +1,101 @@
+/*****************************************************************************
+ * Copyright (C) 2019 VLC authors and VideoLAN
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+#ifndef VIDEORENDERERGL_HPP
+#define VIDEORENDERERGL_HPP
+
+#include <QtQuick/QQuickItem>
+#include <QOpenGLContext>
+#include <QOpenGLFramebufferObject>
+#include <QOffscreenSurface>
+#include <QMutex>
+#include <QtQuick/QSGSimpleTextureNode>
+#include <QtQuick/QSGRectangleNode>
+#include <components/qml_main_context.hpp>
+#include "qt.hpp"
+#include "main_interface.hpp"
+#include "videosurface.hpp"
+#include "videorenderer.hpp"
+
+class VideoSurfaceGL;
+
+class VideoRendererGL : public VideoRenderer
+{
+    Q_OBJECT
+public:
+    VideoRendererGL(MainInterface* p_mi,  QObject *parent = nullptr);
+
+    QSharedPointer<QSGTexture> getDisplayTexture();
+
+    void setupVoutWindow(vout_window_t* window) override;
+
+    VideoSurfaceProvider* getVideoSurfaceProvider() override;
+
+private:
+    //openGL callbacks
+    static bool make_current_cb(void* data, bool current);
+    static void* get_proc_address_cb(void* data, const char* procName);
+    static void swap_cb(void* data);
+    static bool setup_cb(void* data);
+    static void cleanup_cb(void* data);
+    static void resize_cb(void* data, unsigned width, unsigned height);
+
+signals:
+    void updated();
+    void sizeChanged(QSize);
+
+private:
+    QMutex  m_lock;
+
+    QOpenGLContext* m_ctx = nullptr;
+    QOffscreenSurface* m_surface = nullptr;
+
+    MainInterface* m_mainInterface = nullptr;
+    QQuickWindow* m_window = nullptr;
+    QSize m_size;
+
+    QSharedPointer<QSGTexture> m_textures[3] ;
+    QOpenGLFramebufferObject* m_fbo[3];
+
+    bool m_hasTextures = false;
+    bool m_updated = false;
+    int m_renderIdx = 0;
+    int m_bufferIdx = 0;
+    int m_displayIdx = 0;
+
+    VideoSurfaceGL* m_surfaceProvider = nullptr;
+
+    typedef void (*GlViewportPF)(GLint, GLint, GLsizei, GLsizei);
+    GlViewportPF m_glViewport = nullptr;
+};
+
+class VideoSurfaceGL : public VideoSurfaceProvider
+{
+public:
+    VideoSurfaceGL(VideoRendererGL* renderer, QObject* parent = nullptr);
+
+private:
+    QSGNode *updatePaintNode(QQuickItem* item, QSGNode *, QQuickItem::UpdatePaintNodeData *) override;
+
+private:
+    VideoRendererGL* m_renderer = nullptr;
+    QSharedPointer<QSGTexture> m_displayTexture;
+};
+
+
+
+#endif // VIDEORENDERERGL_HPP
-- 
2.19.1



More information about the vlc-devel mailing list