diff options
-rw-r--r-- | DOCS/client_api_examples/README.md | 30 | ||||
-rw-r--r-- | DOCS/client_api_examples/qml_direct/main.cpp | 149 | ||||
-rw-r--r-- | DOCS/client_api_examples/qml_direct/main.h | 49 | ||||
-rw-r--r-- | DOCS/client_api_examples/qml_direct/main.qml | 49 | ||||
-rw-r--r-- | DOCS/client_api_examples/qml_direct/mpvtest.pro | 11 | ||||
-rw-r--r-- | DOCS/client_api_examples/qml_direct/mpvtest.qrc | 5 |
6 files changed, 293 insertions, 0 deletions
diff --git a/DOCS/client_api_examples/README.md b/DOCS/client_api_examples/README.md new file mode 100644 index 0000000000..9de90a6748 --- /dev/null +++ b/DOCS/client_api_examples/README.md @@ -0,0 +1,30 @@ +Client API examples +=================== + +All these examples use the mpv client API through libmpv. + +cocoa +----- + +Shows how to embed the mpv video window in Objective-C/Cocoa. + +qml +--- + +Shows how to use mpv's OpenGL video renderer in QtQuick2 with QML. + +qml_direct +---------- + +Alternative example, which typically avoids a FBO indirection. Might be +slightly faster, but is less flexible and harder to use. + +qt +-- + +Shows how to embed the mpv video window in Qt (using normal desktop widgets). + +simple +------ + +Very primitive terminal-only example. Shows some most basic API usage. 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(); +} diff --git a/DOCS/client_api_examples/qml_direct/main.h b/DOCS/client_api_examples/qml_direct/main.h new file mode 100644 index 0000000000..90acdeedd2 --- /dev/null +++ b/DOCS/client_api_examples/qml_direct/main.h @@ -0,0 +1,49 @@ +#ifndef MPVRENDERER_H_ +#define MPVRENDERER_H_ + +#include <QtQuick/QQuickItem> + +#include <mpv/client.h> +#include <mpv/opengl_cb.h> +#include <mpv/qthelper.hpp> + +class MpvRenderer : public QObject +{ + Q_OBJECT + mpv::qt::Handle mpv; + mpv_opengl_cb_context *mpv_gl; + QQuickWindow *window; + + friend class MpvObject; +public: + MpvRenderer(mpv::qt::Handle a_mpv, mpv_opengl_cb_context *a_mpv_gl); + virtual ~MpvRenderer(); +public slots: + void paint(); +}; + +class MpvObject : public QQuickItem +{ + Q_OBJECT + + mpv::qt::Handle mpv; + mpv_opengl_cb_context *mpv_gl; + MpvRenderer *renderer; + +public: + MpvObject(QQuickItem * parent = 0); + virtual ~MpvObject(); +public slots: + void command(const QVariant& params); + void sync(); + void cleanup(); +signals: + void onUpdate(); +private slots: + void doUpdate(); + void handleWindowChanged(QQuickWindow *win); +private: + static void on_update(void *ctx); +}; + +#endif diff --git a/DOCS/client_api_examples/qml_direct/main.qml b/DOCS/client_api_examples/qml_direct/main.qml new file mode 100644 index 0000000000..92be9bc405 --- /dev/null +++ b/DOCS/client_api_examples/qml_direct/main.qml @@ -0,0 +1,49 @@ +import QtQuick 2.0 +import QtQuick.Controls 1.0 + +import mpvtest 1.0 + +Item { + width: 1280 + height: 720 + + MpvObject { + id: renderer + + // This object isn't real and not visible; it just renders into the + // background of the containing Window. + width: 0 + height: 0 + } + + MouseArea { + anchors.fill: parent + onClicked: renderer.command(["loadfile", "../../../test.mkv"]) + } + + Rectangle { + id: labelFrame + anchors.margins: -50 + radius: 5 + color: "white" + border.color: "black" + opacity: 0.8 + anchors.fill: box + } + + Row { + id: box + anchors.bottom: parent.bottom + anchors.left: parent.left + anchors.right: parent.right + anchors.margins: 100 + + Text { + anchors.margins: 10 + wrapMode: Text.WordWrap + text: "QtQuick and mpv are both rendering stuff.\n + In this example, mpv is always in the background.\n + Click to load ../../../test.mkv" + } + } +} diff --git a/DOCS/client_api_examples/qml_direct/mpvtest.pro b/DOCS/client_api_examples/qml_direct/mpvtest.pro new file mode 100644 index 0000000000..323b8bef71 --- /dev/null +++ b/DOCS/client_api_examples/qml_direct/mpvtest.pro @@ -0,0 +1,11 @@ +QT += qml quick + +HEADERS += main.h +SOURCES += main.cpp + +CONFIG += link_pkgconfig debug +PKGCONFIG += mpv + +RESOURCES += mpvtest.qrc + +OTHER_FILES += main.qml diff --git a/DOCS/client_api_examples/qml_direct/mpvtest.qrc b/DOCS/client_api_examples/qml_direct/mpvtest.qrc new file mode 100644 index 0000000000..bb672657e5 --- /dev/null +++ b/DOCS/client_api_examples/qml_direct/mpvtest.qrc @@ -0,0 +1,5 @@ +<RCC> + <qresource prefix="/mpvtest"> + <file>main.qml</file> + </qresource> +</RCC> |