|
import ctypes |
|
import sys |
|
|
|
import gi |
|
|
|
gi.require_version("Gtk", "4.0") |
|
from gi.repository import GLib, Gtk |
|
from mpv import MPV, MpvGlGetProcAddressFn, MpvRenderContext |
|
from OpenGL import GL |
|
|
|
|
|
class MyApplication(Gtk.Application): |
|
def __init__(self): |
|
super().__init__(application_id="org.example.App") |
|
self.renderer = MyRenderer() |
|
self.renderer.connect("realize", self.on_renderer_ready) |
|
|
|
def on_renderer_ready(self, *_): |
|
self.renderer.play("test.webm") |
|
|
|
def do_activate(self): |
|
win = self.props.active_window |
|
if not win: |
|
win = Gtk.ApplicationWindow(application=self) |
|
win.set_default_size(1280, 720) |
|
win.set_child(self.renderer) |
|
win.present() |
|
|
|
|
|
class MyRenderer(Gtk.GLArea): |
|
def __init__(self, **properties): |
|
super().__init__(**properties) |
|
self.set_auto_render(False) |
|
self.connect("realize", self.on_realize) |
|
|
|
self._mpv = MPV(vo="libmpv", keep_open="yes") |
|
self._ctx = None |
|
self._ctx_opengl_params = {"get_proc_address": MpvGlGetProcAddressFn(get_proc_address_wrapper())} |
|
|
|
def on_realize(self, *_): |
|
self.make_current() |
|
self._ctx = MpvRenderContext(self._mpv, "opengl", opengl_init_params=self._ctx_opengl_params) |
|
self._ctx.update_cb = self.on_mpv_callback |
|
|
|
def on_mpv_callback(self): |
|
GLib.idle_add(self.call_frame_ready, None, GLib.PRIORITY_HIGH) |
|
|
|
def call_frame_ready(self, *_): |
|
if self._ctx.update(): |
|
self.queue_render() |
|
|
|
def do_render(self, *_): |
|
if not self._ctx: |
|
return False |
|
|
|
factor = self.get_scale_factor() |
|
width = self.get_width() * factor |
|
height = self.get_height() * factor |
|
fbo = GL.glGetIntegerv(GL.GL_DRAW_FRAMEBUFFER_BINDING) |
|
self._ctx.render(flip_y=True, opengl_fbo={"w": width, "h": height, "fbo": fbo}) |
|
return True |
|
|
|
def play(self, file): |
|
self._mpv.play(file) |
|
|
|
|
|
def get_proc_address_wrapper(): |
|
def glx_impl(name: bytes): |
|
from OpenGL import GLX |
|
|
|
return GLX.glXGetProcAddress(name.decode("utf-8")) |
|
|
|
def egl_impl(name: bytes): |
|
from OpenGL import EGL |
|
|
|
return EGL.eglGetProcAddress(name.decode("utf-8")) |
|
|
|
platform_func = None |
|
|
|
try: |
|
from OpenGL import GLX |
|
|
|
platform_func = glx_impl |
|
except (AttributeError, ImportError): |
|
pass |
|
|
|
if platform_func is None: |
|
try: |
|
from OpenGL import EGL |
|
|
|
platform_func = egl_impl |
|
except (AttributeError, ImportError): |
|
pass |
|
|
|
if platform_func is None: |
|
raise RuntimeError("Cannot initialize OpenGL") |
|
|
|
def wrapper(_, name: bytes): |
|
address = platform_func(name) |
|
return ctypes.cast(address, ctypes.c_void_p).value |
|
|
|
return wrapper |
|
|
|
|
|
if __name__ == "__main__": |
|
sys.exit(MyApplication().run(sys.argv)) |