I'm in the process of updating a media browser application so that it will work with both X11 and Wayland. It currently uses the python-mpv library, and uses the X Window ID which obviously doesn't work in Wayland: self.player = mpv.MPV(wid=str(int(self.winId())), log_handler=print, loglevel='info', input_default_bindings=False, input_vo_keyboard=False, keep_open='yes', volume=self.current_volume, osc=init_window_options.show_controls) I found the following examples for getting python-mpv working using OpenGL rendering for Qt5/6. I verified the PySide6 example works with both X11 and Wayland. However, the example uses QtQuick and QML rendering. My application uses traditional QWidgets and layouts. I don't want to rewrite my application in QtQuick/QML. From what I've found, it's not advised or supported to mix QtQuick and QtWidget components. I found an example that shows the QOpenGLWidget. I've been digging through the documentation to see how I can adapt the renderer from the first example into a QOpenGLWidget, but the APIs seem very different. I took the approach of trying to render the mpv frames within the paintGL() function of a QOpenGLWidget import sys from OpenGL.GL import glClearColor from PySide6.QtGui import QOpenGLContext from PySide6.QtWidgets import QPushButton, QHBoxLayout from PySide6.QtCore import Qt, Signal, Slot from PySide6.QtOpenGLWidgets import QOpenGLWidget from PySide6.QtWidgets import QApplication, QWidget from mpv import MPV, MpvRenderContext, MpvGlGetProcAddressFn def get_process_address(_, name): print("get_process_address", name.decode('utf-8')) glctx = QOpenGLContext.currentContext() if glctx is None: return 0 return int(glctx.getProcAddress(name)) class MPVWidget(QOpenGLWidget): onUpdate = Signal() def __init__(self): super().__init__() self.setWindowTitle("OpenGL ES 2.0, PySide6, Python") self.resize(350, 350) print("MpvObject.init") self.mpv = MPV(ytdl=True, vo='libmpv', terminal="yes", msg_level="all=v") self._get_proc_address_resolver = MpvGlGetProcAddressFn(get_process_address) self._ctx = MpvRenderContext( self.mpv, api_type='opengl', opengl_init_params={'get_proc_address': self._get_proc_address_resolver} ) self._ctx.update_cb = self._parent.onUpdate.emit self.onUpdate.connect(self.doUpdate) def on_update(self): self.onUpdate.emit() @Slot() def doUpdate(self): self.paintGL() @Slot(str) def play(self, url): print("MpvObject.play") self.mpv.play(url) def initializeGL(self): glClearColor(48 / 255, 56 / 255, 65 / 255, 1) def resizeGL(self, w, h): pass def paintGL(self): if self._ctx: factor = self._parent.scale() rect = self._parent.size() width = int(rect.width() * factor) height = int(rect.height() * factor) fbo = int(self.defaultFramebufferObject()) self._ctx.render(flip_y=False, opengl_fbo={'w': width, 'h': height, 'fbo': fbo}) class MainWidget(QWidget): def __init__(self): super().__init__() self. play_button = QPushButton("Play") self.play_button.clicked.connect(self.click_play) import locale locale.setlocale(locale.LC_NUMERIC, 'C') mpv_widget = MPVWidget() layout = QHBoxLayout() layout.addWidget(self.play_button) layout.addWidget(mpv_widget) self.setLayout(layout.layout()) @Slot() def click_play(self): print("click") self.mpv_widget.play("demo.mp4") if __name__ == "__main__": QApplication.setAttribute(Qt.ApplicationAttribute.AA_UseDesktopOpenGL) app = QApplication(sys.argv) main_widget = MainWidget() main_widget.show() sys.exit(app.exec()) However, I get an error that the OpenGL context can't be initialized: [libmpv_render] Can't load OpenGL functions. [libmpv_render] OpenGL not initialized. Traceback (most recent call last): File "/home/cassius/src/qtquick-mpv/opengl-example.py", line 92, in <module> main_widget = MainWidget() File "/home/cassius/src/qtquick-mpv/opengl-example.py", line 77, in __init__ mpv_widget = MPVWidget() File "/home/cassius/src/qtquick-mpv/opengl-example.py", line 32, in __init__ self._ctx = MpvRenderContext( ~~~~~~~~~~~~~~~~^ self.mpv, ^^^^^^^^^ api_type='opengl', ^^^^^^^^^^^^^^^^^^ opengl_init_params={'get_proc_address': self._get_proc_address_resolver} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ) ^ File "/home/cassius/src/qtquick-mpv/.venv/lib/python3.13/site-packages/mpv.py", line 2121, in __init__ _mpv_render_context_create(buf, mpv.handle, kwargs_to_render_param_array(kwargs)) ~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/home/cassius/src/qtquick-mpv/.venv/lib/python3.13/site-packages/mpv.py", line 142, in raise_for_ec raise ex ValueError: Generic error for signaling that certain system requirements are not fulfilled I realize one big issue is right here: fbo = int(self.defaultFramebufferObject()) In the QQuick example that uses the QQuickFramebufferObject.Renderer, the author of that example calls handle() to get an int representing the OpenGL frame buffer. I can't seem to figure out how to do that using a QtOpenGLWidget default frame buffer. The Problem I'm trying to Solve I'm not sure if this is even the right approach. The end goal is to get python-mpv outputting in a widget I can hold in a traditional layout that I can use on both X11 or Wayland. It seems like the way this needs to be accomplished is using either the older/legacy Qt5 QQuickFramebufferObject, or the newer QRhi (which can support OpenGL, Vulkn or Metal). However, the only examples I can find that work use QQuick and my existing application is already built around QWidgets. How do I get python-mpv working on both X11/Wayland in PyQt6 or PySlide6 (I made a branch of my project that's converted to PySlide6, so either solution would work), presumably using some type of direct rendering context? Continue reading...