]> git.sur5r.net Git - minitube/blob - lib/media/src/mpv/mpvwidget.cpp
New upstream version 3.1
[minitube] / lib / media / src / mpv / mpvwidget.cpp
1 #include "mpvwidget.h"
2 #include <stdexcept>
3
4 #if defined(Q_OS_UNIX) && !defined(Q_OS_DARWIN)
5 #include <QtX11Extras/QX11Info>
6 #endif
7
8 static void *get_proc_address(void *ctx, const char *name) {
9     Q_UNUSED(ctx);
10     QOpenGLContext *glctx = QOpenGLContext::currentContext();
11     if (!glctx) return nullptr;
12     return reinterpret_cast<void *>(glctx->getProcAddress(QByteArray(name)));
13 }
14
15 MpvWidget::MpvWidget(mpv_handle *mpv, QWidget *parent, Qt::WindowFlags f)
16     : QOpenGLWidget(parent, f), mpv(mpv), mpvContext(nullptr) {
17     moveToThread(qApp->thread());
18 }
19
20 MpvWidget::~MpvWidget() {
21     makeCurrent();
22     if (mpvContext) mpv_render_context_free(mpvContext);
23     mpv_terminate_destroy(mpv);
24 }
25
26 void MpvWidget::initializeGL() {
27     if (mpvContext) qFatal("Already initialized");
28
29     QWidget *nativeParent = nativeParentWidget();
30     qDebug() << "initializeGL" << nativeParent;
31     if (nativeParent == nullptr) qFatal("No native parent");
32
33     mpv_opengl_init_params gl_init_params{get_proc_address, this, nullptr};
34     mpv_render_param params[]{{MPV_RENDER_PARAM_API_TYPE, (void *)MPV_RENDER_API_TYPE_OPENGL},
35                               {MPV_RENDER_PARAM_OPENGL_INIT_PARAMS, &gl_init_params},
36                               {MPV_RENDER_PARAM_INVALID, nullptr},
37                               {MPV_RENDER_PARAM_INVALID, nullptr}};
38
39 #if defined(Q_OS_UNIX) && !defined(Q_OS_DARWIN)
40     const QString platformName = QGuiApplication::platformName();
41     if (platformName.contains("xcb")) {
42         params[2].type = MPV_RENDER_PARAM_X11_DISPLAY;
43         params[2].data = (void *)QX11Info::display();
44         qDebug() << platformName << params[2].data;
45     } else if (platformName.contains("wayland")) {
46         qWarning() << "Wayland not supported";
47     }
48 #endif
49
50     if (mpv_render_context_create(&mpvContext, mpv, params) < 0)
51         qFatal("failed to initialize mpv GL context");
52     mpv_render_context_set_update_callback(mpvContext, MpvWidget::onUpdate, (void *)this);
53
54     connect(this, &QOpenGLWidget::frameSwapped, this, &MpvWidget::onFrameSwapped);
55 }
56
57 void MpvWidget::resizeGL(int w, int h) {
58     qreal r = devicePixelRatioF();
59     glWidth = int(w * r);
60     glHeight = int(h * r);
61 }
62
63 void MpvWidget::paintGL() {
64     mpv_opengl_fbo fbo{static_cast<int>(defaultFramebufferObject()), glWidth, glHeight, 0};
65
66     static bool yes = true;
67     mpv_render_param params[] = {{MPV_RENDER_PARAM_OPENGL_FBO, &fbo},
68                                  {MPV_RENDER_PARAM_FLIP_Y, &yes},
69                                  {MPV_RENDER_PARAM_INVALID, nullptr}};
70     // See render_gl.h on what OpenGL environment mpv expects, and
71     // other API details.
72     mpv_render_context_render(mpvContext, params);
73 }
74
75 // Make Qt invoke mpv_opengl_cb_draw() to draw a new/updated video frame.
76 void MpvWidget::maybeUpdate() {
77     // If the Qt window is not visible, Qt's update() will just skip rendering.
78     // This confuses mpv's opengl-cb API, and may lead to small occasional
79     // freezes due to video rendering timing out.
80     // Handle this by manually redrawing.
81     // Note: Qt doesn't seem to provide a way to query whether update() will
82     //       be skipped, and the following code still fails when e.g. switching
83     //       to a different workspace with a reparenting window manager.
84     if (!updatesEnabled() || isHidden() || window()->isHidden() || window()->isMinimized()) {
85         makeCurrent();
86         paintGL();
87         QOpenGLContext *c = context();
88         c->swapBuffers(c->surface());
89         doneCurrent();
90         mpv_render_context_report_swap(mpvContext);
91     } else {
92         update();
93     }
94 }
95
96 void MpvWidget::onFrameSwapped() {
97     mpv_render_context_report_swap(mpvContext);
98 }
99
100 void MpvWidget::onUpdate(void *ctx) {
101     QMetaObject::invokeMethod((MpvWidget *)ctx, "maybeUpdate");
102 }