mirror of
https://github.com/mpv-player/mpv
synced 2025-04-11 04:01:31 +00:00
Until now, calling mpv_opengl_cb_uninit_gl() at a "bad moment" could make the whole thing to explode. The API user was asked to avoid such situations by calling it only in "good moments". But this was probably a bit too subtle and could easily be overlooked. Integrate the approach the qml example uses directly into the implementation. If the OpenGL context is to be unitialized, forcefully disable video, and block until this is done.
113 lines
3.2 KiB
C++
113 lines
3.2 KiB
C++
#include "mpvrenderer.h"
|
|
|
|
#include <QObject>
|
|
#include <QtGlobal>
|
|
#include <QOpenGLContext>
|
|
|
|
#include <QtGui/QOpenGLFramebufferObject>
|
|
|
|
#include <QtQuick/QQuickWindow>
|
|
|
|
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::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)
|
|
{
|
|
int r = mpv_opengl_cb_init_gl(mpv_gl, NULL, get_proc_address, NULL);
|
|
if (r < 0)
|
|
throw "could not initialize OpenGL";
|
|
}
|
|
|
|
virtual ~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 render()
|
|
{
|
|
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();
|
|
}
|
|
};
|
|
|
|
MpvObject::MpvObject(QQuickItem * parent)
|
|
: QQuickFramebufferObject(parent), mpv_gl(0)
|
|
{
|
|
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)
|
|
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");
|
|
|
|
// 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 "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);
|
|
}
|
|
|
|
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(this);
|
|
}
|