From fb855b86593ad2a9db71cce3aa652ace93af38b5 Mon Sep 17 00:00:00 2001 From: wm4 Date: Tue, 9 Dec 2014 17:47:02 +0100 Subject: client API: expose OpenGL renderer This adds API to libmpv that lets host applications use the mpv opengl renderer. This is a more flexible (and possibly more portable) option to foreign window embedding (via --wid). This assumes that methods like context sharing and multithreaded OpenGL rendering are infeasible, and that a way is needed to integrate it with an application that uses a single thread to render everything. Add an example that does this with QtQuick/qml. The example is relatively lazy, but still shows how relatively simple the integration is. The FBO indirection could probably be avoided, but would require more work (and would probably lead to worse QtQuick integration, because it would have to ignore transformations like rotation). Because this makes mpv directly use the host application's OpenGL context, there is no platform specific code involved in mpv, except for hw decoding interop. main.qml is derived from some Qt example. The following things are still missing: - a way to do better video timing - expose GL renderer options, allow changing them at runtime - support for color equalizer controls - support for screenshots --- DOCS/client-api-changes.rst | 4 + DOCS/client_api_examples/qml/main.cpp | 19 ++++ DOCS/client_api_examples/qml/main.qml | 39 +++++++++ DOCS/client_api_examples/qml/mpvrenderer.cpp | 125 +++++++++++++++++++++++++++ DOCS/client_api_examples/qml/mpvrenderer.h | 33 +++++++ DOCS/client_api_examples/qml/mpvtest.pro | 11 +++ DOCS/client_api_examples/qml/mpvtest.qrc | 5 ++ 7 files changed, 236 insertions(+) create mode 100644 DOCS/client_api_examples/qml/main.cpp create mode 100644 DOCS/client_api_examples/qml/main.qml create mode 100644 DOCS/client_api_examples/qml/mpvrenderer.cpp create mode 100644 DOCS/client_api_examples/qml/mpvrenderer.h create mode 100644 DOCS/client_api_examples/qml/mpvtest.pro create mode 100644 DOCS/client_api_examples/qml/mpvtest.qrc (limited to 'DOCS') diff --git a/DOCS/client-api-changes.rst b/DOCS/client-api-changes.rst index 918d986ee6..69458d0d49 100644 --- a/DOCS/client-api-changes.rst +++ b/DOCS/client-api-changes.rst @@ -25,6 +25,10 @@ API changes :: + 1.11 - add OpenGL rendering interop API - allows an application to combine + its own and mpv's OpenGL rendering + Warning: this API is not stable yet - anything in opengl_cb.h might + be changed in completely incompatible ways in minor API bumps --- mpv 0.7.0 is released --- 1.10 - deprecate/disable everything directly related to script_dispatch (most likely affects nobody) diff --git a/DOCS/client_api_examples/qml/main.cpp b/DOCS/client_api_examples/qml/main.cpp new file mode 100644 index 0000000000..cc86302be6 --- /dev/null +++ b/DOCS/client_api_examples/qml/main.cpp @@ -0,0 +1,19 @@ +#include + +#include + +#include "mpvrenderer.h" + +int main(int argc, char **argv) +{ + QGuiApplication app(argc, argv); + + qmlRegisterType("mpvtest", 1, 0, "MpvObject"); + + QQuickView view; + view.setResizeMode(QQuickView::SizeRootObjectToView); + view.setSource(QUrl("qrc:///mpvtest/main.qml")); + view.show(); + + return app.exec(); +} diff --git a/DOCS/client_api_examples/qml/main.qml b/DOCS/client_api_examples/qml/main.qml new file mode 100644 index 0000000000..d921e2a86c --- /dev/null +++ b/DOCS/client_api_examples/qml/main.qml @@ -0,0 +1,39 @@ +import QtQuick 2.0 +import QtQuick.Controls 1.0 + +import mpvtest 1.0 + +Item { + width: 1280 + height: 720 + + MpvObject { + id: renderer + anchors.fill: parent + + MouseArea { + anchors.fill: parent + onClicked: renderer.loadfile("../../../test.mkv") + } + } + + Rectangle { + id: labelFrame + anchors.margins: -50 + radius: 5 + color: "white" + border.color: "black" + opacity: 0.8 + anchors.fill: label + } + + Text { + id: label + anchors.bottom: renderer.bottom + anchors.left: renderer.left + anchors.right: renderer.right + anchors.margins: 100 + wrapMode: Text.WordWrap + text: "QtQuick and mpv are both rendering stuff." + } +} diff --git a/DOCS/client_api_examples/qml/mpvrenderer.cpp b/DOCS/client_api_examples/qml/mpvrenderer.cpp new file mode 100644 index 0000000000..af62fd2762 --- /dev/null +++ b/DOCS/client_api_examples/qml/mpvrenderer.cpp @@ -0,0 +1,125 @@ +#include "mpvrenderer.h" + +#include +#include +#include + +#include + +#include +#include + +class MpvRenderer : public QQuickFramebufferObject::Renderer +{ + static void *get_proc_address(void *ctx, const char *name) { + (void)ctx; + QOpenGLContext *glctx = QOpenGLContext::currentContext(); + if (!glctx) + return NULL; + return (void *)glctx->getProcAddress(QByteArray(name)); + } + + mpv_opengl_cb_context *mpv_gl; + QQuickWindow *window; +public: + MpvRenderer(mpv_opengl_cb_context *a_mpv_gl) + : mpv_gl(a_mpv_gl), window(NULL) + { + int r = mpv_opengl_cb_init_gl(mpv_gl, NULL, get_proc_address, NULL); + if (r < 0) + throw "could not initialize OpenGL"; + } + + virtual ~MpvRenderer() + { + mpv_opengl_cb_uninit_gl(mpv_gl); + } + + void render() + { + assert(window); // must have been set by synchronize() + + QOpenGLFramebufferObject *fbo = framebufferObject(); + int vp[4] = {0, 0, fbo->width(), fbo->height()}; + window->resetOpenGLState(); + mpv_opengl_cb_render(mpv_gl, fbo->handle(), vp); + window->resetOpenGLState(); + } + + QOpenGLFramebufferObject *createFramebufferObject(const QSize &size) + { + QOpenGLFramebufferObjectFormat format; + format.setSamples(4); + return new QOpenGLFramebufferObject(size, format); + } + +protected: + virtual void synchronize(QQuickFramebufferObject *item) + { + window = item->window(); + } +}; + +MpvObject::MpvObject(QQuickItem * parent) + : QQuickFramebufferObject(parent) +{ + mpv = mpv_create(); + if (!mpv) + throw "could not create mpv context"; + + mpv_set_option_string(mpv, "terminal", "yes"); + mpv_set_option_string(mpv, "msg-level", "all=v"); + + if (mpv_initialize(mpv) < 0) { + mpv_terminate_destroy(mpv); + throw "could not initialize mpv context"; + } + + // Make use of the MPV_SUB_API_OPENGL_CB API. + mpv::qt::set_option_variant(mpv, "vo", "opengl-cb"); + + // Request hw decoding, just for testing. + mpv::qt::set_option_variant(mpv, "hwdec", "auto"); + + mpv_gl = (mpv_opengl_cb_context *)mpv_get_sub_api(mpv, MPV_SUB_API_OPENGL_CB); + if (!mpv_gl) { + mpv_terminate_destroy(mpv); + throw "OpenGL not compiled in"; + } + + mpv_opengl_cb_set_update_callback(mpv_gl, on_update, (void *)this); + + connect(this, &MpvObject::onUpdate, this, &MpvObject::doUpdate, Qt::QueuedConnection); +} + +MpvObject::~MpvObject() +{ + mpv_terminate_destroy(mpv); +} + +void MpvObject::on_update(void *ctx) +{ + MpvObject *self = (MpvObject *)ctx; + emit self->onUpdate(); +} + +// connected to onUpdate(); signal makes sure it runs on the GUI thread +void MpvObject::doUpdate() +{ + update(); +} + +void MpvObject::loadfile(const QString& filename) +{ + QVariantList cmd; + cmd.append("loadfile"); + cmd.append(filename); + mpv::qt::command_variant(mpv, cmd); +} + +QQuickFramebufferObject::Renderer *MpvObject::createRenderer() const +{ + window()->setPersistentOpenGLContext(true); + window()->setPersistentSceneGraph(true); + return new MpvRenderer(mpv_gl); +} diff --git a/DOCS/client_api_examples/qml/mpvrenderer.h b/DOCS/client_api_examples/qml/mpvrenderer.h new file mode 100644 index 0000000000..d0bedf720b --- /dev/null +++ b/DOCS/client_api_examples/qml/mpvrenderer.h @@ -0,0 +1,33 @@ +#ifndef MPVRENDERER_H_ +#define MPVRENDERER_H_ + +#include + +#include + +#include "libmpv/client.h" +#include "libmpv/opengl_cb.h" +#include "libmpv/qthelper.hpp" + +class MpvObject : public QQuickFramebufferObject +{ + Q_OBJECT + + mpv_handle *mpv; + mpv_opengl_cb_context *mpv_gl; + +public: + MpvObject(QQuickItem * parent = 0); + virtual ~MpvObject(); + Renderer *createRenderer() const; +public slots: + void loadfile(const QString& filename); +signals: + void onUpdate(); +private slots: + void doUpdate(); +private: + static void on_update(void *ctx); +}; + +#endif diff --git a/DOCS/client_api_examples/qml/mpvtest.pro b/DOCS/client_api_examples/qml/mpvtest.pro new file mode 100644 index 0000000000..6681fa1884 --- /dev/null +++ b/DOCS/client_api_examples/qml/mpvtest.pro @@ -0,0 +1,11 @@ +QT += qml quick + +HEADERS += mpvrenderer.h +SOURCES += mpvrenderer.cpp main.cpp + +CONFIG += link_pkgconfig debug +PKGCONFIG = mpv + +RESOURCES += mpvtest.qrc + +OTHER_FILES += main.qml diff --git a/DOCS/client_api_examples/qml/mpvtest.qrc b/DOCS/client_api_examples/qml/mpvtest.qrc new file mode 100644 index 0000000000..bb672657e5 --- /dev/null +++ b/DOCS/client_api_examples/qml/mpvtest.qrc @@ -0,0 +1,5 @@ + + + main.qml + + -- cgit v1.2.3