#include "main.h" #include #include #include #include #include #include #include #include #include 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 std::runtime_error("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(); window->resetOpenGLState(); mpv_opengl_cb_draw(mpv_gl, fbo->handle(), fbo->width(), fbo->height()); window->resetOpenGLState(); } }; MpvObject::MpvObject(QQuickItem * parent) : QQuickFramebufferObject(parent), mpv_gl(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"); // 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); } 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::command(const QVariant& params) { mpv::qt::command_variant(mpv, params); } void MpvObject::setProperty(const QString& name, const QVariant& value) { mpv::qt::set_property_variant(mpv, name, value); } QQuickFramebufferObject::Renderer *MpvObject::createRenderer() const { window()->setPersistentOpenGLContext(true); window()->setPersistentSceneGraph(true); return new MpvRenderer(this); } int main(int argc, char **argv) { QGuiApplication app(argc, argv); // Qt sets the locale in the QGuiApplication constructor, but libmpv // requires the LC_NUMERIC category to be set to "C", so change it back. std::setlocale(LC_NUMERIC, "C"); qmlRegisterType("mpvtest", 1, 0, "MpvObject"); QQuickView view; view.setResizeMode(QQuickView::SizeRootObjectToView); view.setSource(QUrl("qrc:///mpvtest/main.qml")); view.show(); return app.exec(); }