summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorjaseg <code@jaseg.net>2020-01-02 16:21:47 +0100
committerjaseg <code@jaseg.net>2020-01-03 01:08:16 +0100
commit3c21d679da191f75e1fc929579a8454eeed039fd (patch)
tree6a405bccd81c0549a12a8926857f1d92bc7fd084
parent3205f1b6f8869f2714817d9d93ddadb48c839e83 (diff)
downloadpython-mpv-3c21d679da191f75e1fc929579a8454eeed039fd.tar.gz
python-mpv-3c21d679da191f75e1fc929579a8454eeed039fd.tar.bz2
python-mpv-3c21d679da191f75e1fc929579a8454eeed039fd.zip
Initial libmpv/render.h mapping
For an example using this with GTK see: https://gist.github.com/jaseg/657e8ecca3267c0d82ec85d40f423caa
-rw-r--r--mpv.py206
1 files changed, 195 insertions, 11 deletions
diff --git a/mpv.py b/mpv.py
index cd8c8ee..b0d6917 100644
--- a/mpv.py
+++ b/mpv.py
@@ -56,6 +56,9 @@ else:
class MpvHandle(c_void_p):
pass
+class MpvRenderCtxHandle(c_void_p):
+ pass
+
class MpvOpenGLCbContext(c_void_p):
pass
@@ -125,6 +128,106 @@ class ErrorCode(object):
if ex:
raise ex(ec, *args)
+MpvGlGetProcAddressFn = CFUNCTYPE(c_void_p, c_void_p, c_char_p)
+class MpvOpenGLInitParams(Structure):
+ _fields_ = [('get_proc_address', MpvGlGetProcAddressFn),
+ ('get_proc_address_ctx', c_void_p),
+ ('extra_exts', c_void_p)]
+
+ def __init__(self, get_proc_address):
+ self.get_proc_address = get_proc_address
+ self.get_proc_address_ctx = None
+ self.extra_exts = None
+
+class MpvOpenGLFBO(Structure):
+ _fields_ = [('fbo', c_int),
+ ('w', c_int),
+ ('h', c_int),
+ ('internal_format', c_int)]
+
+ def __init__(self, w, h, fbo=0, internal_format=0):
+ self.w, self.h = w, h
+ self.fbo = fbo
+ self.internal_format = internal_format
+
+class MpvRenderFrameInfo(Structure):
+ _fields_ = [('flags', c_int64),
+ ('target_time', c_int64)]
+
+ def as_dict(self):
+ return {'flags': self.flags,
+ 'target_time': self.target_time}
+
+class MpvOpenGLDRMParams(Structure):
+ _fields_ = [('fd', c_int),
+ ('crtc_id', c_int),
+ ('connector_id', c_int),
+ ('atomic_request_ptr', c_void_p),
+ ('render_fd', c_int)]
+
+class MpvOpenGLDRMDrawSurfaceSize(Structure):
+ _fields_ = [('width', c_int), ('height', c_int)]
+
+class MpvOpenGLDRMParamsV2(Structure):
+ _fields_ = [('fd', c_int),
+ ('crtc_id', c_int),
+ ('connector_id', c_int),
+ ('atomic_request_ptr', c_void_p),
+ ('render_fd', c_int)]
+
+ def __init__(self, crtc_id, connector_id, atomic_request_ptr, fd=-1, render_fd=-1):
+ self.crtc_id, self.connector_id = crtc_id, connector_id
+ self.atomic_request_ptr = atomic_request_ptr
+ self.fd, self.render_fd = fd, render_fd
+
+
+class MpvRenderParam(Structure):
+ _fields_ = [('type_id', c_int),
+ ('data', c_void_p)]
+
+ # maps human-readable type name to (type_id, argtype) tuple.
+ # The type IDs come from libmpv/render.h
+ TYPES = {"invalid" :(0, None),
+ "api_type" :(1, str),
+ "opengl_init_params" :(2, MpvOpenGLInitParams),
+ "opengl_fbo" :(3, MpvOpenGLFBO),
+ "flip_y" :(4, bool),
+ "depth" :(5, int),
+ "icc_profile" :(6, bytes),
+ "ambient_light" :(7, int),
+ "x11_display" :(8, c_void_p),
+ "wl_display" :(9, c_void_p),
+ "advanced_control" :(10, bool),
+ "next_frame_info" :(11, MpvRenderFrameInfo),
+ "block_for_target_time" :(12, bool),
+ "skip_rendering" :(13, bool),
+ "drm_display" :(14, MpvOpenGLDRMParams),
+ "drm_draw_surface_size" :(15, MpvOpenGLDRMDrawSurfaceSize),
+ "drm_display_v2" :(16, MpvOpenGLDRMParamsV2)}
+
+ def __init__(self, name, value=None):
+ if name not in self.TYPES:
+ raise ValueError('unknown render param type "{}"'.format(name))
+ self.type_id, cons = self.TYPES[name]
+ if cons is None:
+ self.value = None
+ self.data = c_void_p()
+ elif cons is str:
+ self.value = value
+ self.data = cast(c_char_p(value.encode('utf-8')), c_void_p)
+ elif cons is bytes:
+ self.value = MpvByteArray(value)
+ self.data = cast(pointer(self.value), c_void_p)
+ elif cons is bool:
+ self.value = c_int(int(bool(value)))
+ self.data = cast(pointer(self.value), c_void_p)
+ else:
+ self.value = cons(**value)
+ self.data = cast(pointer(self.value), c_void_p)
+
+def kwargs_to_render_param_array(kwargs):
+ t = MpvRenderParam * (len(kwargs)+1)
+ return t(*kwargs.items(), ('invalid', None))
class MpvFormat(c_int):
NONE = 0
@@ -211,6 +314,11 @@ class MpvByteArray(Structure):
_fields_ = [('data', c_void_p),
('size', c_size_t)]
+ def __init__(self, value):
+ self._value = value
+ self.data = cast(c_char_p(value), c_void_p)
+ self.size = len(value)
+
def bytes_value(self):
return cast(self.data, POINTER(c_char))[:self.size]
@@ -357,17 +465,30 @@ StreamOpenFn = CFUNCTYPE(c_int, c_void_p, c_char_p, POINTER(StreamCallbackInfo))
WakeupCallback = CFUNCTYPE(None, c_void_p)
+RenderUpdateFn = CFUNCTYPE(None, c_void_p)
+
OpenGlCbUpdateFn = CFUNCTYPE(None, c_void_p)
OpenGlCbGetProcAddrFn = CFUNCTYPE(c_void_p, c_void_p, c_char_p)
-def _handle_func(name, args, restype, errcheck, ctx=MpvHandle):
+def _handle_func(name, args, restype, errcheck, ctx=MpvHandle, deprecated=False):
func = getattr(backend, name)
func.argtypes = [ctx] + args if ctx else args
if restype is not None:
func.restype = restype
if errcheck is not None:
func.errcheck = errcheck
- globals()['_'+name] = func
+ if deprecated:
+ @wraps(func)
+ def wrapper(*args, **kwargs):
+ if not wrapper.warned: # Only warn on first invocation to prevent spamming
+ warn("Backend C api has been deprecated: " + name, DeprecationWarning, stacklevel=2)
+ wrapper.warned = True
+ return func(*args, **kwargs)
+ wrapper.warned = False
+
+ globals()['_'+name] = wrapper
+ else:
+ globals()['_'+name] = func
def bytes_free_errcheck(res, func, *args):
notnull_errcheck(res, func, *args)
@@ -383,8 +504,8 @@ def notnull_errcheck(res, func, *args):
ec_errcheck = ErrorCode.raise_for_ec
-def _handle_gl_func(name, args=[], restype=None):
- _handle_func(name, args, restype, errcheck=None, ctx=MpvOpenGLCbContext)
+def _handle_gl_func(name, args=[], restype=None, deprecated=False):
+ _handle_func(name, args, restype, errcheck=None, ctx=MpvOpenGLCbContext, deprecated=deprecated)
backend.mpv_client_api_version.restype = c_ulong
def _mpv_client_api_version():
@@ -439,14 +560,26 @@ _handle_func('mpv_get_wakeup_pipe', [],
_handle_func('mpv_stream_cb_add_ro', [c_char_p, c_void_p, StreamOpenFn], c_int, ec_errcheck)
-_handle_func('mpv_get_sub_api', [MpvSubApi], c_void_p, notnull_errcheck)
+_handle_func('mpv_render_context_create', [MpvRenderCtxHandle, MpvHandle, POINTER(MpvRenderParam)], c_int, ec_errcheck, ctx=None)
+_handle_func('mpv_render_context_set_parameter', [MpvRenderParam], c_int, ec_errcheck, ctx=MpvRenderCtxHandle)
+_handle_func('mpv_render_context_get_info', [MpvRenderParam], c_int, ec_errcheck, ctx=MpvRenderCtxHandle)
+_handle_func('mpv_render_context_set_update_callback', [RenderUpdateFn, c_void_p], None, errcheck=None, ctx=MpvRenderCtxHandle)
+_handle_func('mpv_render_context_update', [], c_int64, errcheck=None, ctx=MpvRenderCtxHandle)
+_handle_func('mpv_render_context_render', [POINTER(MpvRenderParam)], c_int, ec_errcheck, ctx=MpvRenderCtxHandle)
+_handle_func('mpv_render_context_report_swap', [], None, errcheck=None, ctx=MpvRenderCtxHandle)
+_handle_func('mpv_render_context_free', [], None, errcheck=None, ctx=MpvRenderCtxHandle)
+
+
+# Deprecated in v0.29.0 and may disappear eventually
+if hasattr(backend, 'mpv_get_sub_api'):
+ _handle_func('mpv_get_sub_api', [MpvSubApi], c_void_p, notnull_errcheck, deprecated=True)
-_handle_gl_func('mpv_opengl_cb_set_update_callback', [OpenGlCbUpdateFn, c_void_p])
-_handle_gl_func('mpv_opengl_cb_init_gl', [c_char_p, OpenGlCbGetProcAddrFn, c_void_p], c_int)
-_handle_gl_func('mpv_opengl_cb_draw', [c_int, c_int, c_int], c_int)
-_handle_gl_func('mpv_opengl_cb_render', [c_int, c_int], c_int)
-_handle_gl_func('mpv_opengl_cb_report_flip', [c_ulonglong], c_int)
-_handle_gl_func('mpv_opengl_cb_uninit_gl', [], c_int)
+ _handle_gl_func('mpv_opengl_cb_set_update_callback', [OpenGlCbUpdateFn, c_void_p], deprecated=True)
+ _handle_gl_func('mpv_opengl_cb_init_gl', [c_char_p, OpenGlCbGetProcAddrFn, c_void_p], c_int, deprecated=True)
+ _handle_gl_func('mpv_opengl_cb_draw', [c_int, c_int, c_int], c_int, deprecated=True)
+ _handle_gl_func('mpv_opengl_cb_render', [c_int, c_int], c_int, deprecated=True)
+ _handle_gl_func('mpv_opengl_cb_report_flip', [c_ulonglong], c_int, deprecated=True)
+ _handle_gl_func('mpv_opengl_cb_uninit_gl', [], c_int, deprecated=True)
def _mpv_coax_proptype(value, proptype=str):
@@ -1371,3 +1504,54 @@ class MPV(object):
return self._get_property('option-info/'+name)
except AttributeError:
return None
+
+class MpvRenderContext:
+ def __init__(self, mpv, api_type, **kwargs):
+ self._mpv = mpv
+ kwargs['api_type'] = api_type
+
+ buf = cast(create_string_buffer(sizeof(MpvRenderCtxHandle)), POINTER(MpvRenderCtxHandle))
+ _mpv_render_context_create(buf, mpv.handle, kwargs_to_render_param_array(kwargs))
+ self._handle = buf.contents
+
+ def free(self):
+ _mpv_render_context_free(self._handle)
+
+ def __setattr__(self, name, value):
+ if name.startswith('_'):
+ super().__setattr__(name, value)
+
+ elif name == 'update_cb':
+ func = value if value else (lambda: None)
+ self._update_cb = value
+ self._update_fn_wrapper = RenderUpdateFn(lambda _userdata: func())
+ _mpv_render_context_set_update_callback(self._handle, self._update_fn_wrapper, None)
+
+ else:
+ param = MpvRenderParam(name, value)
+ _mpv_render_context_set_parameter(self._handle, param)
+
+ def __getattr__(self, name):
+ if name == 'update_cb':
+ return self._update_cb
+
+ elif name == 'handle':
+ return self._handle
+
+ param = MpvRenderParam(name)
+ data_type = type(param.data.contents)
+ buf = cast(create_string_buffer(sizeof(data_type)), POINTER(data_type))
+ param.data = buf
+ _mpv_render_context_get_info(self._handle, param)
+ return buf.contents.as_dict()
+
+ def update(self):
+ """ Calls mpv_render_context_update and returns the MPV_RENDER_UPDATE_FRAME flag (see render.h) """
+ return bool(_mpv_render_context_update(self._handle) & 1)
+
+ def render(self, **kwargs):
+ _mpv_render_context_render(self._handle, kwargs_to_render_param_array(kwargs))
+
+ def report_swap(self):
+ _mpv_render_context_report_swap(self._handle)
+