From ea84bfbb96f3c9d4857ac9558e1a9de913d43df1 Mon Sep 17 00:00:00 2001 From: Michael Hasselmann Date: Sat, 5 May 2018 13:58:23 +0200 Subject: Convert QML example from opengl-cb to render API Mimics the approach in SDL example. --- libmpv/qml/main.cpp | 114 +++++++++++++++++++++++++++++++++++----------------- libmpv/qml/main.h | 13 +++--- 2 files changed, 85 insertions(+), 42 deletions(-) diff --git a/libmpv/qml/main.cpp b/libmpv/qml/main.cpp index 2122cd8..033c1af 100644 --- a/libmpv/qml/main.cpp +++ b/libmpv/qml/main.cpp @@ -13,49 +13,95 @@ #include #include +namespace +{ +void on_mpv_events(void *ctx) +{ + Q_UNUSED(ctx) +} + +void on_mpv_redraw(void *ctx) +{ + MpvObject::on_update(ctx); +} + +static void *get_proc_address_mpv(void *ctx, const char *name) +{ + Q_UNUSED(ctx) + + QOpenGLContext *glctx = QOpenGLContext::currentContext(); + if (!glctx) return nullptr; + + return reinterpret_cast(glctx->getProcAddress(QByteArray(name))); +} + +} + 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)); - } + MpvObject *obj; - mpv::qt::Handle mpv; - QQuickWindow *window; - mpv_opengl_cb_context *mpv_gl; public: - MpvRenderer(const MpvObject *obj) - : mpv(obj->mpv), window(obj->window()), mpv_gl(obj->mpv_gl) + MpvRenderer(MpvObject *new_obj) + : obj{new_obj} { - 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"); + mpv_set_wakeup_callback(obj->mpv, on_mpv_events, nullptr); } virtual ~MpvRenderer() + {} + + // This function is called when a new FBO is needed. + // This happens on the initial frame. + QOpenGLFramebufferObject * createFramebufferObject(const QSize &size) { - // 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); + // init mpv_gl: + if (!obj->mpv_gl) + { + mpv_opengl_init_params gl_init_params{get_proc_address_mpv, nullptr, nullptr}; + mpv_render_param params[]{ + {MPV_RENDER_PARAM_API_TYPE, const_cast(MPV_RENDER_API_TYPE_OPENGL)}, + {MPV_RENDER_PARAM_OPENGL_INIT_PARAMS, &gl_init_params}, + {MPV_RENDER_PARAM_INVALID, nullptr} + }; + + if (mpv_render_context_create(&obj->mpv_gl, obj->mpv, params) < 0) + throw std::runtime_error("failed to initialize mpv GL context"); + mpv_render_context_set_update_callback(obj->mpv_gl, on_mpv_redraw, obj); + } + + return QQuickFramebufferObject::Renderer::createFramebufferObject(size); } void render() { + obj->window()->resetOpenGLState(); + QOpenGLFramebufferObject *fbo = framebufferObject(); - window->resetOpenGLState(); - mpv_opengl_cb_draw(mpv_gl, fbo->handle(), fbo->width(), fbo->height()); - window->resetOpenGLState(); + mpv_opengl_fbo mpfbo{.fbo = static_cast(fbo->handle()), .w = fbo->width(), .h = fbo->height(), .internal_format = 0}; + int flip_y{0}; + + mpv_render_param params[] = { + // Specify the default framebuffer (0) as target. This will + // render onto the entire screen. If you want to show the video + // in a smaller rectangle or apply fancy transformations, you'll + // need to render into a separate FBO and draw it manually. + {MPV_RENDER_PARAM_OPENGL_FBO, &mpfbo}, + // Flip rendering (needed due to flipped GL coordinate system). + {MPV_RENDER_PARAM_FLIP_Y, &flip_y}, + {MPV_RENDER_PARAM_INVALID, nullptr} + }; + // See render_gl.h on what OpenGL environment mpv expects, and + // other API details. + mpv_render_context_render(obj->mpv_gl, params); + + obj->window()->resetOpenGLState(); } }; MpvObject::MpvObject(QQuickItem * parent) - : QQuickFramebufferObject(parent), mpv_gl(0) + : QQuickFramebufferObject(parent), mpv{mpv_create()}, mpv_gl(nullptr) { - mpv = mpv::qt::Handle::FromRawHandle(mpv_create()); if (!mpv) throw std::runtime_error("could not create mpv context"); @@ -65,27 +111,21 @@ MpvObject::MpvObject(QQuickItem * parent) 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"); - // Request hw decoding, just for testing. mpv::qt::set_option_variant(mpv, "hwdec", "auto"); - // 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); } MpvObject::~MpvObject() { - if (mpv_gl) - mpv_opengl_cb_set_update_callback(mpv_gl, NULL, NULL); + if (mpv_gl) // only initialized if something got drawn + { + mpv_render_context_free(mpv_gl); + } + + mpv_terminate_destroy(mpv); } void MpvObject::on_update(void *ctx) @@ -114,7 +154,7 @@ QQuickFramebufferObject::Renderer *MpvObject::createRenderer() const { window()->setPersistentOpenGLContext(true); window()->setPersistentSceneGraph(true); - return new MpvRenderer(this); + return new MpvRenderer(const_cast(this)); } int main(int argc, char **argv) diff --git a/libmpv/qml/main.h b/libmpv/qml/main.h index 9a65ae5..448707c 100644 --- a/libmpv/qml/main.h +++ b/libmpv/qml/main.h @@ -4,7 +4,7 @@ #include #include -#include +#include #include class MpvRenderer; @@ -13,24 +13,27 @@ class MpvObject : public QQuickFramebufferObject { Q_OBJECT - mpv::qt::Handle mpv; - mpv_opengl_cb_context *mpv_gl; + mpv_handle *mpv; + mpv_render_context *mpv_gl; friend class MpvRenderer; public: + static void on_update(void *ctx); + MpvObject(QQuickItem * parent = 0); virtual ~MpvObject(); virtual Renderer *createRenderer() const; + public slots: void command(const QVariant& params); void setProperty(const QString& name, const QVariant& value); + signals: void onUpdate(); + private slots: void doUpdate(); -private: - static void on_update(void *ctx); }; #endif -- cgit v1.2.3