vlc | branch: master | Steve Lhomme <[email protected]> | 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 */ _______________________________________________ vlc-commits mailing list [email protected] https://mailman.videolan.org/listinfo/vlc-commits
