[vlc-commits] doc: add a Qt app to showcase the OpenGL surface callbacks
Steve Lhomme
git at videolan.org
Mon Jan 28 10:22:10 CET 2019
vlc | branch: master | Steve Lhomme <robux4 at ycbcr.xyz> | Wed Jan 23 13:13:40 2019 +0100| [d5ccdffa6594397c2320f55783b865e8af775902] | committer: Steve Lhomme
doc: add a Qt app to showcase the OpenGL surface callbacks
> http://git.videolan.org/gitweb.cgi/vlc.git/?a=commit;h=d5ccdffa6594397c2320f55783b865e8af775902
---
doc/libvlc/QtGL/QtGl.pro | 10 ++
doc/libvlc/QtGL/main.cpp | 40 +++++
doc/libvlc/QtGL/qtvlcwidget.cpp | 313 ++++++++++++++++++++++++++++++++++++++++
doc/libvlc/QtGL/qtvlcwidget.h | 46 ++++++
4 files changed, 409 insertions(+)
diff --git a/doc/libvlc/QtGL/QtGl.pro b/doc/libvlc/QtGL/QtGl.pro
new file mode 100644
index 0000000000..0d87c88727
--- /dev/null
+++ b/doc/libvlc/QtGL/QtGl.pro
@@ -0,0 +1,10 @@
+TEMPLATE = app
+TARGET = qtglvlc
+DEPENDPATH += .
+INCLUDEPATH += . ../../../include
+LIBS += -lvlc
+#-L../../../../build/git64/lib/.libs
+QT += widgets
+
+SOURCES += main.cpp qtvlcwidget.cpp
+HEADERS += qtvlcwidget.h
diff --git a/doc/libvlc/QtGL/main.cpp b/doc/libvlc/QtGL/main.cpp
new file mode 100644
index 0000000000..eb15b92fa5
--- /dev/null
+++ b/doc/libvlc/QtGL/main.cpp
@@ -0,0 +1,40 @@
+#include <QApplication>
+#include <QDesktopWidget>
+#include <QSurfaceFormat>
+#include <QMainWindow>
+
+#include <QtPlugin>
+Q_IMPORT_PLUGIN(QWindowsIntegrationPlugin)
+
+
+#include "qtvlcwidget.h"
+
+int main(int argc, char *argv[])
+{
+ QApplication app(argc, argv);
+
+ // this important so we can call makeCurrent from our rendering thread
+ QCoreApplication::setAttribute(Qt::AA_DontCheckOpenGLContextThreadAffinity);
+
+ QSurfaceFormat fmt;
+ fmt.setDepthBufferSize(24);
+ QSurfaceFormat::setDefaultFormat(fmt);
+
+ QMainWindow mainWindow;
+
+ QtVLCWidget *glWidget = new QtVLCWidget;
+ mainWindow.setCentralWidget(glWidget);
+
+ mainWindow.resize(mainWindow.sizeHint());
+ int desktopArea = QApplication::desktop()->width() *
+ QApplication::desktop()->height();
+ int widgetArea = mainWindow.width() * mainWindow.height();
+ if (((float)widgetArea / (float)desktopArea) < 0.75f)
+ mainWindow.show();
+ else
+ mainWindow.showMaximized();
+
+ glWidget->playMedia(argv[1]);
+
+ return app.exec();
+}
diff --git a/doc/libvlc/QtGL/qtvlcwidget.cpp b/doc/libvlc/QtGL/qtvlcwidget.cpp
new file mode 100644
index 0000000000..a9abeccc16
--- /dev/null
+++ b/doc/libvlc/QtGL/qtvlcwidget.cpp
@@ -0,0 +1,313 @@
+#include "qtvlcwidget.h"
+#include <QMouseEvent>
+#include <QOpenGLShaderProgram>
+#include <QCoreApplication>
+#include <QOpenGLFramebufferObject>
+#include <QThread>
+#include <cmath>
+
+#include <mutex>
+
+#include <vlc/vlc.h>
+
+class VLCVideo
+{
+public:
+ VLCVideo(QtVLCWidget *widget)
+ :mWidget(widget)
+ {
+ mBuffers[0] = NULL;
+ mBuffers[1] = NULL;
+ mBuffers[2] = NULL;
+ }
+
+ ~VLCVideo()
+ {
+ cleanup(this);
+ }
+
+ /// return the texture to be displayed
+ QOpenGLFramebufferObject *getVideoFrame()
+ {
+ std::lock_guard<std::mutex> lock(m_text_lock);
+ if (m_updated) {
+ std::swap(m_idx_swap, m_idx_display);
+ m_updated = false;
+ }
+ return mBuffers[m_idx_display];
+ }
+
+ /// this callback will create the surfaces and FBO used by VLC to perform its rendering
+ static void resizeRenderTextures(void* data, unsigned width, unsigned height)
+ {
+ VLCVideo* that = static_cast<VLCVideo*>(data);
+ if (width != that->m_width || height != that->m_height)
+ cleanup(data);
+
+ that->mBuffers[0] = new QOpenGLFramebufferObject(width, height);
+ that->mBuffers[1] = new QOpenGLFramebufferObject(width, height);
+ that->mBuffers[2] = new QOpenGLFramebufferObject(width, height);
+
+ that->m_width = width;
+ that->m_height = height;
+
+ that->mBuffers[that->m_idx_render]->bind();
+ }
+
+ // This callback is called during initialisation.
+ static bool setup(void* data)
+ {
+ if (!QOpenGLContext::supportsThreadedOpenGL())
+ return false;
+
+ VLCVideo* that = static_cast<VLCVideo*>(data);
+ that->m_width = 0;
+ that->m_height = 0;
+ return true;
+ }
+
+
+ // This callback is called to release the texture and FBO created in resize
+ static void cleanup(void* data)
+ {
+ VLCVideo* that = static_cast<VLCVideo*>(data);
+ if (that->m_width == 0 && that->m_height == 0)
+ return;
+ delete that->mBuffers[0];
+ that->mBuffers[0] = NULL;
+ delete that->mBuffers[1];
+ that->mBuffers[1] = NULL;
+ delete that->mBuffers[2];
+ that->mBuffers[2] = NULL;
+ }
+
+ //This callback is called after VLC performs drawing calls
+ static void swap(void* data)
+ {
+ VLCVideo* that = static_cast<VLCVideo*>(data);
+ std::lock_guard<std::mutex> lock(that->m_text_lock);
+ that->m_updated = true;
+ that->mWidget->update();
+ std::swap(that->m_idx_swap, that->m_idx_render);
+ that->mBuffers[that->m_idx_render]->bind();
+ }
+
+ // This callback is called to set the OpenGL context
+ static bool make_current(void* data, bool current)
+ {
+ VLCVideo* that = static_cast<VLCVideo*>(data);
+ if (current)
+ that->mWidget->makeCurrent();
+ else
+ that->mWidget->doneCurrent();
+ return true;
+ }
+
+ // This callback is called by VLC to get OpenGL functions.
+ static void* get_proc_address(void* data, const char* current)
+ {
+ VLCVideo* that = static_cast<VLCVideo*>(data);
+ QOpenGLContext *ctx = that->mWidget->context();
+ return reinterpret_cast<void*>(ctx->getProcAddress(current));
+ }
+
+private:
+ QtVLCWidget *mWidget;
+
+ //FBO data
+ unsigned m_width = 0;
+ unsigned m_height = 0;
+ std::mutex m_text_lock;
+ QOpenGLFramebufferObject *mBuffers[3];
+ GLuint m_tex[3];
+ GLuint m_fbo[3];
+ size_t m_idx_render = 0;
+ size_t m_idx_swap = 1;
+ size_t m_idx_display = 2;
+ bool m_updated = false;
+};
+
+
+QtVLCWidget::QtVLCWidget(QWidget *parent)
+ : QOpenGLWidget(parent),
+ m_program(nullptr),
+ vertexBuffer(QOpenGLBuffer::VertexBuffer),
+ vertexIndexBuffer(QOpenGLBuffer::IndexBuffer)
+{
+ // --transparent causes the clear color to be transparent. Therefore, on systems that
+ // support it, the widget will become transparent apart from the logo.
+
+ const char *args[] = {
+ "--verbose=4"
+ };
+ m_vlc = libvlc_new(sizeof(args) / sizeof(*args), args);
+
+ mVLC = new VLCVideo(this);
+}
+
+bool QtVLCWidget::playMedia(const char* url)
+{
+ m_media = libvlc_media_new_location (m_vlc, url);
+ if (m_media == nullptr) {
+ fprintf(stderr, "unable to create media %s", url);
+ return false;
+ }
+ m_mp = libvlc_media_player_new_from_media (m_media);
+ if (m_mp == nullptr) {
+ fprintf(stderr, "unable to create media player");
+ libvlc_media_release(m_media);
+ return false;
+ }
+
+ // Define the opengl rendering callbacks
+ libvlc_video_set_output_callbacks(m_mp, libvlc_video_engine_opengl,
+ VLCVideo::setup, VLCVideo::cleanup, VLCVideo::resizeRenderTextures, VLCVideo::swap,
+ VLCVideo::make_current, VLCVideo::get_proc_address,
+ mVLC);
+
+ // Play the video
+ libvlc_media_player_play (m_mp);
+
+ return true;
+}
+
+QtVLCWidget::~QtVLCWidget()
+{
+ cleanup();
+}
+
+QSize QtVLCWidget::minimumSizeHint() const
+{
+ return QSize(50, 50);
+}
+
+QSize QtVLCWidget::sizeHint() const
+{
+ return QSize(400, 400);
+}
+
+void QtVLCWidget::cleanup()
+{
+ stop();
+ if (m_vlc)
+ libvlc_release(m_vlc);
+ if (m_program == nullptr)
+ return;
+ makeCurrent();
+ vertexBuffer.destroy();
+ vertexIndexBuffer.destroy();
+ delete m_program;
+ m_program = 0;
+ doneCurrent();
+}
+
+void QtVLCWidget::stop()
+{
+ if (m_mp) {
+ libvlc_media_player_release(m_mp);
+ m_mp = nullptr;
+ }
+ if (m_media) {
+ libvlc_media_release(m_media);
+ m_media = nullptr;
+ }
+}
+
+static const char *vertexShaderSource =
+ "attribute vec2 position;\n"
+ "varying vec2 texcoord;\n"
+ "void main()\n"
+ "{\n"
+ " gl_Position = vec4(position, 0.0, 1.0);\n"
+ " texcoord = position * vec2(0.5) + vec2(0.5);\n"
+ "}\n";
+
+static const char *fragmentShaderSource =
+ "uniform sampler2D texture;\n"
+ "\n"
+ "varying vec2 texcoord;\n"
+ "\n"
+ "void main()\n"
+ "{\n"
+ " gl_FragColor = texture2D(texture, texcoord);\n"
+ "};\n";
+
+/*
+ * Data used to seed our vertex array and element array buffers:
+ */
+static const GLfloat g_vertex_buffer_data[] = {
+ -1.0f, -1.0f,
+ 1.0f, -1.0f,
+ -1.0f, 1.0f,
+ 1.0f, 1.0f
+};
+static const GLushort g_element_buffer_data[] = { 0, 1, 2, 3 };
+
+void QtVLCWidget::initializeGL()
+{
+ // In this example the widget's corresponding top-level window can change
+ // several times during the widget's lifetime. Whenever this happens, the
+ // QOpenGLWidget's associated context is destroyed and a new one is created.
+ // Therefore we have to be prepared to clean up the resources on the
+ // aboutToBeDestroyed() signal, instead of the destructor. The emission of
+ // the signal will be followed by an invocation of initializeGL() where we
+ // can recreate all resources.
+ connect(context(), &QOpenGLContext::aboutToBeDestroyed, this, &QtVLCWidget::cleanup);
+
+ initializeOpenGLFunctions();
+
+ vertexBuffer.create();
+ vertexBuffer.bind();
+ vertexBuffer.allocate(g_vertex_buffer_data, sizeof(g_vertex_buffer_data));
+
+ vertexIndexBuffer.create();
+ vertexIndexBuffer.bind();
+ vertexIndexBuffer.allocate(g_element_buffer_data, sizeof(g_element_buffer_data));
+
+ m_program = new QOpenGLShaderProgram;
+ m_program->addShaderFromSourceCode(QOpenGLShader::Vertex, vertexShaderSource);
+ m_program->addShaderFromSourceCode(QOpenGLShader::Fragment, fragmentShaderSource);
+ m_program->link();
+
+ m_program->setUniformValue("texture", 0);
+
+ m_program->bindAttributeLocation("position", 0);
+}
+
+void QtVLCWidget::paintGL()
+{
+ QOpenGLFramebufferObject *fbo = mVLC->getVideoFrame();
+ if (fbo != NULL)
+ {
+ m_program->bind();
+
+ glClearColor(1.0, 0.5, 0.0, 1.0);
+
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, fbo->takeTexture());
+
+ vertexBuffer.bind();
+ m_program->setAttributeArray("position", (const QVector2D *)nullptr, sizeof(GLfloat)*2);
+ //vertexBuffer.release();
+
+ m_program->enableAttributeArray("position");
+
+ vertexIndexBuffer.bind();
+ glDrawElements(
+ GL_TRIANGLE_STRIP, /* mode */
+ 4, /* count */
+ GL_UNSIGNED_SHORT, /* type */
+ (void*)0 /* element array buffer offset */
+ );
+ //vertexIndexBuffer.release();
+
+ m_program->disableAttributeArray("position");
+
+ //m_program->release();
+ }
+}
+
+void QtVLCWidget::resizeGL(int w, int h)
+{
+ /* TODO */
+}
diff --git a/doc/libvlc/QtGL/qtvlcwidget.h b/doc/libvlc/QtGL/qtvlcwidget.h
new file mode 100644
index 0000000000..febbc6252f
--- /dev/null
+++ b/doc/libvlc/QtGL/qtvlcwidget.h
@@ -0,0 +1,46 @@
+#ifndef GLWIDGET_H
+#define GLWIDGET_H
+
+#include <QOpenGLWidget>
+#include <QOpenGLFunctions>
+#include <QOpenGLVertexArrayObject>
+#include <QOpenGLBuffer>
+
+QT_FORWARD_DECLARE_CLASS(QOpenGLShaderProgram)
+
+class QtVLCWidget : public QOpenGLWidget, protected QOpenGLFunctions
+{
+ Q_OBJECT
+
+public:
+ QtVLCWidget(QWidget *parent = 0);
+ ~QtVLCWidget();
+
+ QSize minimumSizeHint() const override;
+ QSize sizeHint() const override;
+
+ bool playMedia(const char* url);
+
+public slots:
+ void cleanup();
+
+protected:
+ void initializeGL() override;
+ void paintGL() override;
+ void resizeGL(int width, int height) override;
+
+private:
+ QOpenGLVertexArrayObject m_vao;
+ QOpenGLShaderProgram *m_program;
+
+ class VLCVideo *mVLC;
+
+ void stop();
+ struct libvlc_instance_t* m_vlc = nullptr;
+ struct libvlc_media_player_t* m_mp = nullptr;
+ struct libvlc_media_t* m_media = nullptr;
+
+ QOpenGLBuffer vertexBuffer, vertexIndexBuffer;
+};
+
+#endif /* GLWIDGET_H */
More information about the vlc-commits
mailing list