summaryrefslogtreecommitdiffstats
path: root/DOCS/client_api_examples/qml_direct/main.cpp
diff options
context:
space:
mode:
authorwm4 <wm4@nowhere>2015-01-08 02:40:30 +0100
committerwm4 <wm4@nowhere>2015-01-08 02:40:30 +0100
commitf551fbaa25ac9930c6e5a3df372a4bd7acdd2811 (patch)
tree2bad3f3c5ced87b3e446f11c18f474414aef3af7 /DOCS/client_api_examples/qml_direct/main.cpp
parentf52ec079b22a4dae732f759ac29746d1d7ca3076 (diff)
downloadmpv-f551fbaa25ac9930c6e5a3df372a4bd7acdd2811.tar.bz2
mpv-f551fbaa25ac9930c6e5a3df372a4bd7acdd2811.tar.xz
DOCS/client_api_examples: add an alternative qml example
This one avoids use of a FBO. It's less flexible, because it uses works around the whole QML rendering API. It seems to be the only way to get OpenGL rendering without any indirections, though. Parts of this example were insipired by Qt's "Squircle" example. Also add a README file with a short description of each example, to reduce the initial confusing.
Diffstat (limited to 'DOCS/client_api_examples/qml_direct/main.cpp')
-rw-r--r--DOCS/client_api_examples/qml_direct/main.cpp149
1 files changed, 149 insertions, 0 deletions
diff --git a/DOCS/client_api_examples/qml_direct/main.cpp b/DOCS/client_api_examples/qml_direct/main.cpp
new file mode 100644
index 0000000000..b999d10ff4
--- /dev/null
+++ b/DOCS/client_api_examples/qml_direct/main.cpp
@@ -0,0 +1,149 @@
+#include "main.h"
+
+#include <stdexcept>
+
+#include <QObject>
+#include <QtGlobal>
+#include <QOpenGLContext>
+
+#include <QGuiApplication>
+#include <QtQuick/QQuickWindow>
+#include <QtQuick/QQuickView>
+
+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));
+}
+
+MpvRenderer::MpvRenderer(mpv::qt::Handle a_mpv, mpv_opengl_cb_context *a_mpv_gl)
+ : mpv(a_mpv), mpv_gl(a_mpv_gl), window(0)
+{
+ int r = mpv_opengl_cb_init_gl(mpv_gl, NULL, get_proc_address, NULL);
+ if (r < 0)
+ throw std::runtime_error("could not initialize OpenGL");
+}
+
+MpvRenderer::~MpvRenderer()
+{
+ // Until this call is done, we need to make sure the player remains
+ // alive. This is done implicitly with the mpv::qt::Handle instance
+ // in this class.
+ mpv_opengl_cb_uninit_gl(mpv_gl);
+}
+
+void MpvRenderer::paint()
+{
+ window->resetOpenGLState();
+
+ // Render to the whole window.
+ QSize s = window->size() * window->devicePixelRatio();
+ int vp[4] = {0, 0, s.width(), -s.height()};
+
+ // This uses 0 as framebuffer, which indicates that mpv will render directly
+ // to the frontbuffer. Note that mpv will always switch framebuffers
+ // explicitly. Some QWindow setups (such as using QQuickWidget) actually
+ // want you to render into a FBO in the beforeRendering() signal, and this
+ // code won't work there.
+ mpv_opengl_cb_render(mpv_gl, 0, vp);
+
+ window->resetOpenGLState();
+}
+
+MpvObject::MpvObject(QQuickItem * parent)
+ : QQuickItem(parent), mpv_gl(0), renderer(0)
+{
+ mpv = mpv::qt::Handle::FromRawHandle(mpv_create());
+ if (!mpv)
+ throw std::runtime_error("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)
+ throw std::runtime_error("could not initialize mpv context");
+
+ // Make use of the MPV_SUB_API_OPENGL_CB API.
+ mpv::qt::set_option_variant(mpv, "vo", "opengl-cb");
+
+ // Setup the callback that will make QtQuick update and redraw if there
+ // is a new video frame. Use a queued connection: this makes sure the
+ // doUpdate() function is run on the GUI thread.
+ mpv_gl = (mpv_opengl_cb_context *)mpv_get_sub_api(mpv, MPV_SUB_API_OPENGL_CB);
+ if (!mpv_gl)
+ throw std::runtime_error("OpenGL not compiled in");
+ mpv_opengl_cb_set_update_callback(mpv_gl, MpvObject::on_update, (void *)this);
+ connect(this, &MpvObject::onUpdate, this, &MpvObject::doUpdate,
+ Qt::QueuedConnection);
+
+ connect(this, &QQuickItem::windowChanged,
+ this, &MpvObject::handleWindowChanged);
+}
+
+MpvObject::~MpvObject()
+{
+ if (mpv_gl)
+ mpv_opengl_cb_set_update_callback(mpv_gl, NULL, NULL);
+}
+
+void MpvObject::handleWindowChanged(QQuickWindow *win)
+{
+ if (!win)
+ return;
+ connect(win, &QQuickWindow::beforeSynchronizing,
+ this, &MpvObject::sync, Qt::DirectConnection);
+ connect(win, &QQuickWindow::sceneGraphInvalidated,
+ this, &MpvObject::cleanup, Qt::DirectConnection);
+ win->setClearBeforeRendering(false);
+}
+
+void MpvObject::sync()
+{
+ if (!renderer) {
+ renderer = new MpvRenderer(mpv, mpv_gl);
+ connect(window(), &QQuickWindow::beforeRendering,
+ renderer, &MpvRenderer::paint, Qt::DirectConnection);
+ }
+ renderer->window = window();
+}
+
+void MpvObject::cleanup()
+{
+ if (renderer) {
+ delete renderer;
+ renderer = 0;
+ }
+}
+
+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()
+{
+ window()->update();
+}
+
+void MpvObject::command(const QVariant& params)
+{
+ mpv::qt::command_variant(mpv, params);
+}
+
+int main(int argc, char **argv)
+{
+ QGuiApplication app(argc, argv);
+
+ qmlRegisterType<MpvObject>("mpvtest", 1, 0, "MpvObject");
+
+ QQuickView view;
+ view.setResizeMode(QQuickView::SizeRootObjectToView);
+ view.setSource(QUrl("qrc:///mpvtest/main.qml"));
+ view.show();
+
+ return app.exec();
+}