Initial libmpv/render.h mapping
For an example using this with GTK see: https://gist.github.com/jaseg/657e8ecca3267c0d82ec85d40f423caa
This commit is contained in:
parent
3205f1b6f8
commit
3c21d679da
1 changed files with 195 additions and 11 deletions
206
mpv.py
206
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)
|
||||
|
||||
_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)
|
||||
|
||||
# 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], 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)
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue