Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
mpvwidget implemented with PyQt5 and QOpenGLWidget (currently used in http://github.com/cosven/feeluown)
"""
Mainly learned from these two example.
- https://github.com/ozmartian/vidcutter/blob/793127c521b18f0bab19b67bc42e8da16a667afd/vidcutter/libs/mpvwidget.py
- https://github.com/mpv-player/mpv-examples/blob/master/libmpv/qt_opengl/mpvwidget.cpp
"""
from PyQt5.QtCore import Qt, QMetaObject, pyqtSlot
from PyQt5.QtWidgets import QOpenGLWidget, QApplication
from PyQt5.QtOpenGL import QGLContext
# HELP: currently, we need import GL module,otherwise it will raise seg fault on Linux(Ubuntu 18.04)
from OpenGL import GL # noqa
from mpv import MPV, _mpv_get_sub_api, _mpv_opengl_cb_set_update_callback, \
_mpv_opengl_cb_init_gl, OpenGlCbGetProcAddrFn, _mpv_opengl_cb_draw, \
_mpv_opengl_cb_report_flip, MpvSubApi, OpenGlCbUpdateFn, _mpv_opengl_cb_uninit_gl
def get_proc_addr(_, name):
glctx = QGLContext.currentContext()
if glctx is None:
return 0
addr = int(glctx.getProcAddress(name.decode('utf-8')))
return addr
class MpvWidget(QOpenGLWidget):
def __init__(self, parent=None):
super().__init__(parent=parent)
self.mpv = MPV(vo='opengl-cb', ytdl=True)
self.mpv_gl = _mpv_get_sub_api(self.mpv.handle, MpvSubApi.MPV_SUB_API_OPENGL_CB)
self.on_update_c = OpenGlCbUpdateFn(self.on_update)
self.on_update_fake_c = OpenGlCbUpdateFn(self.on_update_fake)
self.get_proc_addr_c = OpenGlCbGetProcAddrFn(get_proc_addr)
_mpv_opengl_cb_set_update_callback(self.mpv_gl, self.on_update_c, None)
self.frameSwapped.connect(self.swapped, Qt.DirectConnection)
def initializeGL(self):
_mpv_opengl_cb_init_gl(self.mpv_gl, None, self.get_proc_addr_c, None)
def paintGL(self):
# compatible with HiDPI display
ratio = self.windowHandle().devicePixelRatio()
w = int(self.width() * ratio)
h = int(self.height() * ratio)
_mpv_opengl_cb_draw(self.mpv_gl, self.defaultFramebufferObject(), w, -h)
@pyqtSlot()
def maybe_update(self):
if self.window().isMinimized():
self.makeCurrent()
self.paintGL()
self.context().swapBuffers(self.context().surface())
self.swapped()
self.doneCurrent()
else:
self.update()
def on_update(self, ctx=None):
# maybe_update method should run on the thread that creates the OpenGLContext,
# which in general is the main thread. QMetaObject.invokeMethod can
# do this trick.
QMetaObject.invokeMethod(self, 'maybe_update')
def on_update_fake(self, ctx=None):
pass
def swapped(self):
_mpv_opengl_cb_report_flip(self.mpv_gl, 0)
def closeEvent(self, _):
self.makeCurrent()
if self.mpv_gl:
_mpv_opengl_cb_set_update_callback(self.mpv_gl, self.on_update_fake_c, None)
_mpv_opengl_cb_uninit_gl(self.mpv_gl)
self.mpv.terminate()
if __name__ == '__main__':
import locale
locale.setlocale(locale.LC_NUMERIC, 'C')
app = QApplication([])
widget = MpvWidget()
widget.show()
url = 'data/test.webm'
widget.mpv.play(url)
app.exec()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.