1
0
mirror of https://github.com/mpv-player/mpv synced 2025-04-07 01:53:06 +00:00

DOCS/client_api_examples: qtquick: fix destruction

Destruction (e.g. when closing the window) was a bit broken. This commit
fixes some possible crashes, and should make lifetime management
relatively sane. (Still a bit complex, though. Maybe this code should be
moved into a tiny library.)

QtQuick runs the renderer on a separate thread. This thread is rather
loosely connected to the main thread. The loose separation is enforced
by the API, which also makes coordination of initialization and
destruction harder. Throw refcounting at the problem, which fixes it.
The refcounting wrapper introduced in the previous commit is used for
this.

Also contains some general cleanups.
This commit is contained in:
wm4 2014-12-30 22:34:34 +01:00
parent 50c40b6ded
commit 10f51a8c57
4 changed files with 35 additions and 38 deletions

View File

@ -34,6 +34,7 @@ Item {
anchors.right: renderer.right
anchors.margins: 100
wrapMode: Text.WordWrap
text: "QtQuick and mpv are both rendering stuff."
text: "QtQuick and mpv are both rendering stuff.\n
Click to load ../../../test.mkv"
}
}

View File

@ -7,7 +7,6 @@
#include <QtGui/QOpenGLFramebufferObject>
#include <QtQuick/QQuickWindow>
#include <qsgsimpletexturenode.h>
class MpvRenderer : public QQuickFramebufferObject::Renderer
{
@ -19,11 +18,12 @@ class MpvRenderer : public QQuickFramebufferObject::Renderer
return (void *)glctx->getProcAddress(QByteArray(name));
}
mpv_opengl_cb_context *mpv_gl;
mpv::qt::Handle mpv;
QQuickWindow *window;
mpv_opengl_cb_context *mpv_gl;
public:
MpvRenderer(mpv_opengl_cb_context *a_mpv_gl)
: mpv_gl(a_mpv_gl), window(NULL)
MpvRenderer(const MpvObject *obj)
: mpv(obj->mpv), window(obj->window()), mpv_gl(obj->mpv_gl)
{
int r = mpv_opengl_cb_init_gl(mpv_gl, NULL, get_proc_address, NULL);
if (r < 0)
@ -32,47 +32,40 @@ public:
virtual ~MpvRenderer()
{
// Before we can really destroy the OpenGL state, we must make sure
// that the video output is destroyed. This is important for some
// forms of hardware decoding, where the decoder shares some state
// with the video output and the OpenGL context.
// Deselecting the video track is the easiest way to achieve this in
// a synchronous way. If no file is playing, setting the property
// will fail and do nothing.
mpv::qt::set_property_variant(mpv, "vid", "no");
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;
return new QOpenGLFramebufferObject(size, format);
}
protected:
virtual void synchronize(QQuickFramebufferObject *item)
{
window = item->window();
}
};
MpvObject::MpvObject(QQuickItem * parent)
: QQuickFramebufferObject(parent)
: QQuickFramebufferObject(parent), mpv_gl(0)
{
mpv = mpv_create();
mpv = mpv::qt::Handle::FromRawHandle(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);
if (mpv_initialize(mpv) < 0)
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");
@ -80,20 +73,21 @@ MpvObject::MpvObject(QQuickItem * parent)
// 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) {
mpv_terminate_destroy(mpv);
if (!mpv_gl)
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);
mpv_opengl_cb_set_update_callback(mpv_gl, MpvObject::on_update, (void *)this);
connect(this, &MpvObject::onUpdate, this, &MpvObject::doUpdate,
Qt::QueuedConnection);
}
MpvObject::~MpvObject()
{
mpv_terminate_destroy(mpv);
if (mpv_gl)
mpv_opengl_cb_set_update_callback(mpv_gl, NULL, NULL);
}
void MpvObject::on_update(void *ctx)
@ -120,5 +114,5 @@ QQuickFramebufferObject::Renderer *MpvObject::createRenderer() const
{
window()->setPersistentOpenGLContext(true);
window()->setPersistentSceneGraph(true);
return new MpvRenderer(mpv_gl);
return new MpvRenderer(this);
}

View File

@ -1,25 +1,27 @@
#ifndef MPVRENDERER_H_
#define MPVRENDERER_H_
#include <assert.h>
#include <QtQuick/QQuickFramebufferObject>
#include <mpv/client.h>
#include <mpv/opengl_cb.h>
#include <mpv/qthelper.hpp>
class MpvRenderer;
class MpvObject : public QQuickFramebufferObject
{
Q_OBJECT
mpv_handle *mpv;
mpv::qt::Handle mpv;
mpv_opengl_cb_context *mpv_gl;
friend class MpvRenderer;
public:
MpvObject(QQuickItem * parent = 0);
virtual ~MpvObject();
Renderer *createRenderer() const;
virtual Renderer *createRenderer() const;
public slots:
void loadfile(const QString& filename);
signals:

View File

@ -4,7 +4,7 @@ HEADERS += mpvrenderer.h
SOURCES += mpvrenderer.cpp main.cpp
CONFIG += link_pkgconfig debug
PKGCONFIG = mpv
PKGCONFIG += mpv
RESOURCES += mpvtest.qrc