Skip to content

Commit

Permalink
[MpvWidget] use render api instead of opengl_cb #259
Browse files Browse the repository at this point in the history
  • Loading branch information
easymodo committed Sep 25, 2020
1 parent 7b7f9c1 commit a1058ef
Show file tree
Hide file tree
Showing 4 changed files with 57 additions and 84 deletions.
87 changes: 44 additions & 43 deletions plugins/player_mpv/src/mpvwidget.cpp
Original file line number Diff line number Diff line change
@@ -1,52 +1,44 @@
#include "mpvwidget.h"
#include <stdexcept>
#include <QtGui/QOpenGLContext>
#include <QtCore/QMetaObject>

static void wakeup(void *ctx) {
QMetaObject::invokeMethod(reinterpret_cast<MpvWidget*>(ctx), "on_mpv_events", Qt::QueuedConnection);
QMetaObject::invokeMethod((MpvWidget*)ctx, "on_mpv_events", Qt::QueuedConnection);
}

static void *get_proc_address(void *ctx, const char *name) {
Q_UNUSED(ctx)
Q_UNUSED(ctx);
QOpenGLContext *glctx = QOpenGLContext::currentContext();
if (!glctx)
return nullptr;
return reinterpret_cast<void*>(glctx->getProcAddress(QByteArray(name)));
return reinterpret_cast<void *>(glctx->getProcAddress(QByteArray(name)));
}

MpvWidget::MpvWidget(QWidget *parent, Qt::WindowFlags f)
: QOpenGLWidget(parent, f)
{
this->setAttribute(Qt::WA_TransparentForMouseEvents, true);
mpv = mpv::qt::Handle::FromRawHandle(mpv_create());
if (!mpv)
mpv = mpv_create();
if(!mpv)
throw std::runtime_error("could not create mpv context");

this->setAttribute(Qt::WA_TransparentForMouseEvents, true);

//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");
mpv::qt::set_option_variant(mpv, "video-unscaled", "downscale-big");
// Request hw decoding, just for testing.
mpv::qt::set_option_variant(mpv, "hwdec", "auto");

//mpv::qt::set_option_variant(mpv, "video-pan-x", "0.5");
//mpv::qt::set_option_variant(mpv, "video-unscaled", "downscale-big");

// Loop video
setRepeat(true);

// Unmute
setMuted(false);

mpv_gl = reinterpret_cast<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, MpvWidget::on_update, reinterpret_cast<void*>(this));
connect(this, SIGNAL(frameSwapped()), SLOT(swapped()));

mpv_observe_property(mpv, 0, "duration", MPV_FORMAT_DOUBLE);
mpv_observe_property(mpv, 0, "time-pos", MPV_FORMAT_DOUBLE);
mpv_observe_property(mpv, 0, "pause", MPV_FORMAT_FLAG);
Expand All @@ -56,41 +48,51 @@ MpvWidget::MpvWidget(QWidget *parent, Qt::WindowFlags f)
MpvWidget::~MpvWidget() {
makeCurrent();
if (mpv_gl)
mpv_opengl_cb_set_update_callback(mpv_gl, nullptr, nullptr);
// 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);
mpv_render_context_free(mpv_gl);
mpv_terminate_destroy(mpv);
}

void MpvWidget::command(const QVariant& params) {
mpv::qt::command(mpv, params);
}

void MpvWidget::setOption(const QString& name, const QVariant& value) {
mpv::qt::set_option_variant(mpv, name, value);
mpv::qt::command_variant(mpv, params);
}

void MpvWidget::setProperty(const QString& name, const QVariant& value) {
mpv::qt::set_property(mpv, name, value);
mpv::qt::set_property_variant(mpv, name, value);
}

QVariant MpvWidget::getProperty(const QString &name) const {
return mpv::qt::get_property(mpv, name);
}

void MpvWidget::setOption(const QString& name, const QVariant& value) {
mpv::qt::set_option_variant(mpv, name, value);
}

void MpvWidget::initializeGL() {
int r = mpv_opengl_cb_init_gl(mpv_gl, nullptr, get_proc_address, nullptr);
if (r < 0)
throw std::runtime_error("could not initialize OpenGL");
mpv_opengl_init_params gl_init_params{get_proc_address, nullptr, nullptr};
mpv_render_param params[]{
{MPV_RENDER_PARAM_API_TYPE, const_cast<char *>(MPV_RENDER_API_TYPE_OPENGL)},
{MPV_RENDER_PARAM_OPENGL_INIT_PARAMS, &gl_init_params},
{MPV_RENDER_PARAM_INVALID, nullptr}
};

if(mpv_render_context_create(&mpv_gl, mpv, params) < 0)
throw std::runtime_error("failed to initialize mpv GL context");
mpv_render_context_set_update_callback(mpv_gl, MpvWidget::on_update, reinterpret_cast<void *>(this));
}

void MpvWidget::paintGL() {
mpv_opengl_cb_draw(mpv_gl, static_cast<int>(defaultFramebufferObject()), width(), -height());
}
mpv_opengl_fbo mpfbo{static_cast<int>(defaultFramebufferObject()), width(), height(), 0};
int flip_y{1};

void MpvWidget::swapped() {
mpv_opengl_cb_report_flip(mpv_gl, 0);
mpv_render_param params[] = {
{MPV_RENDER_PARAM_OPENGL_FBO, &mpfbo},
{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(mpv_gl, params);
}

void MpvWidget::on_mpv_events() {
Expand Down Expand Up @@ -131,10 +133,10 @@ void MpvWidget::handle_mpv_event(mpv_event *event) {
}
}

// Make Qt invoke mpv_opengl_cb_draw() to draw a new/updated video frame.
// Make Qt invoke mpv_render_context_render() to draw a new/updated video frame.
void MpvWidget::maybeUpdate() {
// If the Qt window is not visible, Qt's update() will just skip rendering.
// This confuses mpv's opengl-cb API, and may lead to small occasional
// This confuses mpv's render API, and may lead to small occasional
// freezes due to video rendering timing out.
// Handle this by manually redrawing.
// Note: Qt doesn't seem to provide a way to query whether update() will
Expand All @@ -144,13 +146,16 @@ void MpvWidget::maybeUpdate() {
makeCurrent();
paintGL();
context()->swapBuffers(context()->surface());
swapped();
doneCurrent();
} else {
update();
}
}

void MpvWidget::on_update(void *ctx) {
QMetaObject::invokeMethod((MpvWidget*)ctx, "maybeUpdate");
}

void MpvWidget::setMuted(bool mode) {
if(mode)
mpv::qt::set_option_variant(mpv, "mute", "yes");
Expand Down Expand Up @@ -180,7 +185,3 @@ void MpvWidget::setRepeat(bool mode) {
else
mpv::qt::set_option_variant(mpv, "loop-file", "no");
}

void MpvWidget::on_update(void *ctx) {
QMetaObject::invokeMethod(reinterpret_cast<MpvWidget*>(ctx), "maybeUpdate");
}
16 changes: 11 additions & 5 deletions plugins/player_mpv/src/mpvwidget.h
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
#pragma once

#include <QtCore/QMetaObject>
#include <QtWidgets/QOpenGLWidget>
#include <QtGui/QOpenGLContext>
#include <QtGui/QOpenGLFramebufferObject>

#include <mpv/client.h>
#include <mpv/opengl_cb.h>
#include <mpv/render_gl.h>
#include "qthelper.hpp"

#include <QDebug>
#include <ctime>
#include <QSurfaceFormat>
Expand All @@ -13,7 +18,8 @@ class MpvWidget Q_DECL_FINAL: public QOpenGLWidget {
Q_OBJECT
public:
MpvWidget(QWidget *parent = nullptr, Qt::WindowFlags f = Qt::Widget);
~MpvWidget() Q_DECL_OVERRIDE;
~MpvWidget() override;

void command(const QVariant& params);
void setOption(const QString &name, const QVariant &value);
void setProperty(const QString& name, const QVariant& value);
Expand All @@ -32,6 +38,7 @@ class MpvWidget Q_DECL_FINAL: public QOpenGLWidget {
bool muted();
int volume();
void setVolume(int vol);

signals:
void durationChanged(int value);
void positionChanged(int value);
Expand All @@ -43,14 +50,13 @@ class MpvWidget Q_DECL_FINAL: public QOpenGLWidget {
void paintGL() Q_DECL_OVERRIDE;

private slots:
void swapped();
void on_mpv_events();
void maybeUpdate();

private:
void handle_mpv_event(mpv_event *event);
static void on_update(void *ctx);

mpv::qt::Handle mpv;
mpv_opengl_cb_context *mpv_gl;
mpv_handle *mpv;
mpv_render_context *mpv_gl;
};
37 changes: 2 additions & 35 deletions plugins/player_mpv/src/qthelper.hpp
Original file line number Diff line number Diff line change
@@ -1,35 +1,8 @@
/* Copyright (C) 2017 the mpv developers
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

#ifndef MPV_CLIENT_API_QTHELPER_H_
#define MPV_CLIENT_API_QTHELPER_H_
#ifndef LIBMPV_QTHELPER_H_
#define LIBMPV_QTHELPER_H_

#include <mpv/client.h>

#if !MPV_ENABLE_DEPRECATED
#error "This helper is deprecated. Copy it into your project instead."
#else

/**
* Note: these helpers are provided for convenience for C++/Qt applications.
* This is based on the public API in client.h, and it does not encode any
* knowledge that is not known or guaranteed outside of the C client API. You
* can even copy and modify this code as you like, or implement similar things
* for other languages.
*/

#include <cstring>

#include <QVariant>
Expand Down Expand Up @@ -230,8 +203,6 @@ struct node_autofree {
~node_autofree() { mpv_free_node_contents(ptr); }
};

#if MPV_ENABLE_DEPRECATED

/**
* Return the given property as mpv_node converted to QVariant, or QVariant()
* on error.
Expand Down Expand Up @@ -289,8 +260,6 @@ static inline QVariant command_variant(mpv_handle *ctx, const QVariant &args)
return node_to_variant(&res);
}

#endif

/**
* This is used to return error codes wrapped in QVariant for functions which
* return QVariant.
Expand Down Expand Up @@ -381,6 +350,4 @@ static inline QVariant command(mpv_handle *ctx, const QVariant &args)

Q_DECLARE_METATYPE(mpv::qt::ErrorReturn)

#endif /* else #if MPV_ENABLE_DEPRECATED */

#endif
1 change: 0 additions & 1 deletion plugins/player_mpv/src/videoplayermpv.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ VideoPlayerMpv::VideoPlayerMpv(QWidget *parent) : VideoPlayer(parent) {
setAttribute(Qt::WA_TranslucentBackground, true);
setMouseTracking(true);


m_mpv = new MpvWidget(this);
QVBoxLayout *vl = new QVBoxLayout();
vl->setContentsMargins(0,0,0,0);
Expand Down

0 comments on commit a1058ef

Please sign in to comment.