93ab33eb56
Widget playback was broken under QQmlApplicationEngine based workflows. Refactoring MpvRenderContext creation to happen in the constructor fixed playback in this workflow.
89 lines
3.1 KiB
Python
89 lines
3.1 KiB
Python
from PyQt5.QtCore import QUrl, QSize, pyqtSignal, pyqtSlot
|
|
from PyQt5.QtGui import QOpenGLFramebufferObject
|
|
from PyQt5.QtOpenGL import QGLContext
|
|
from PyQt5.QtQuick import QQuickFramebufferObject
|
|
|
|
from mpv import MPV, MpvRenderContext, OpenGlCbGetProcAddrFn
|
|
|
|
|
|
def get_process_address(_, name):
|
|
glctx = QGLContext.currentContext()
|
|
if glctx is None:
|
|
return 0
|
|
return int(glctx.getProcAddress(name.decode("utf-8")))
|
|
|
|
|
|
class MpvObject(QQuickFramebufferObject):
|
|
"""MpvObject:
|
|
This is a QML widget that can be used to embed the output of a mpv instance.
|
|
It extends the QQuickFramebufferObject class to implement this functionality."""
|
|
|
|
# This signal allows triggers the update function to run on the correct thread
|
|
onUpdate = pyqtSignal()
|
|
|
|
def __init__(self, parent=None):
|
|
print("Creating MpvObject")
|
|
super(MpvObject, self).__init__(parent)
|
|
self.mpv = MPV(ytdl=True)
|
|
self.mpv_gl = None
|
|
self._proc_addr_wrapper = OpenGlCbGetProcAddrFn(get_process_address)
|
|
self.onUpdate.connect(self.doUpdate)
|
|
|
|
def on_update(self):
|
|
"""Function for mpv to call to trigger a framebuffer update"""
|
|
self.onUpdate.emit()
|
|
|
|
@pyqtSlot()
|
|
def doUpdate(self):
|
|
"""Slot for receiving the update event on the correct thread"""
|
|
self.update()
|
|
|
|
def createRenderer(self) -> "QQuickFramebufferObject.Renderer":
|
|
"""Overrides the default createRenderer function to create a
|
|
MpvRenderer instance"""
|
|
print("Calling overridden createRenderer")
|
|
return MpvRenderer(self)
|
|
|
|
@pyqtSlot(str)
|
|
def play(self, url):
|
|
"""Temporary adapter fuction that allowing playing media from QML"""
|
|
self.mpv.play(url)
|
|
|
|
|
|
class MpvRenderer(QQuickFramebufferObject.Renderer):
|
|
"""MpvRenderer:
|
|
This class implements the QQuickFramebufferObject's Renderer subsystem.
|
|
It augments the base renderer with an instance of mpv's render API."""
|
|
|
|
def __init__(self, parent=None):
|
|
print("Creating MpvRenderer")
|
|
super(MpvRenderer, self).__init__()
|
|
self.obj = parent
|
|
self.ctx = MpvRenderContext(
|
|
self.obj.mpv,
|
|
"opengl",
|
|
opengl_init_params={"get_proc_address": self.obj._proc_addr_wrapper},
|
|
)
|
|
self.ctx.update_cb = self.obj.on_update
|
|
|
|
def createFramebufferObject(self, size: QSize) -> QOpenGLFramebufferObject:
|
|
"""Overrides the base createFramebufferObject function, augmenting it to
|
|
create an MpvRenderContext using opengl"""
|
|
|
|
return QQuickFramebufferObject.Renderer.createFramebufferObject(self, size)
|
|
|
|
def render(self):
|
|
"""Overrides the base render function, calling mpv's render functions instead"""
|
|
if self.ctx:
|
|
factor = self.obj.scale()
|
|
rect = self.obj.size()
|
|
|
|
# width and height are floats
|
|
width = int(rect.width() * factor)
|
|
height = int(rect.height() * factor)
|
|
fbo = int(self.framebufferObject().handle())
|
|
|
|
self.ctx.render(
|
|
flip_y=False, opengl_fbo={"w": width, "h": height, "fbo": fbo}
|
|
)
|