4 #if defined(Q_OS_UNIX) && !defined(Q_OS_DARWIN)
5 #include <QtX11Extras/QX11Info>
8 static void *get_proc_address(void *ctx, const char *name) {
10 QOpenGLContext *glctx = QOpenGLContext::currentContext();
11 if (!glctx) return nullptr;
12 return reinterpret_cast<void *>(glctx->getProcAddress(QByteArray(name)));
15 MpvWidget::MpvWidget(mpv_handle *mpv, QWidget *parent, Qt::WindowFlags f)
16 : QOpenGLWidget(parent, f), mpv(mpv), mpvContext(nullptr) {
17 moveToThread(qApp->thread());
20 MpvWidget::~MpvWidget() {
22 if (mpvContext) mpv_render_context_free(mpvContext);
23 mpv_terminate_destroy(mpv);
26 void MpvWidget::initializeGL() {
27 if (mpvContext) qFatal("Already initialized");
29 QWidget *nativeParent = nativeParentWidget();
30 qDebug() << "initializeGL" << nativeParent;
31 if (nativeParent == nullptr) qFatal("No native parent");
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}};
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";
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);
54 connect(this, &QOpenGLWidget::frameSwapped, this, &MpvWidget::onFrameSwapped);
57 void MpvWidget::resizeGL(int w, int h) {
58 qreal r = devicePixelRatioF();
60 glHeight = int(h * r);
63 void MpvWidget::paintGL() {
64 mpv_opengl_fbo fbo{static_cast<int>(defaultFramebufferObject()), glWidth, glHeight, 0};
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
72 mpv_render_context_render(mpvContext, params);
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()) {
87 QOpenGLContext *c = context();
88 c->swapBuffers(c->surface());
90 mpv_render_context_report_swap(mpvContext);
96 void MpvWidget::onFrameSwapped() {
97 mpv_render_context_report_swap(mpvContext);
100 void MpvWidget::onUpdate(void *ctx) {
101 QMetaObject::invokeMethod((MpvWidget *)ctx, "maybeUpdate");