mpv.py: Add overlay support

This commit is contained in:
jaseg 2020-07-16 15:01:35 +02:00
parent 44ca94749a
commit 7362f663c1

104
mpv.py
View file

@ -743,6 +743,80 @@ class GeneratorStream:
self._read_iter = iter([]) # make next read() call return EOF self._read_iter = iter([]) # make next read() call return EOF
# TODO? # TODO?
class ImageOverlay:
def __init__(self, m, overlay_id, img=None, pos=(0, 0)):
self.m = m
self.overlay_id = overlay_id
self.pos = pos
self._size = None
if img is not None:
self.update(img)
def update(self, img=None, pos=None):
from PIL import Image
if img is not None:
self.img = img
img = self.img
w, h = img.size
stride = w*4
if pos is not None:
self.pos = pos
x, y = self.pos
# Pre-multiply alpha channel
bg = Image.new('RGBA', (w, h), (0, 0, 0, 0))
out = Image.alpha_composite(bg, img)
# Copy image to ctypes buffer
if img.size != self._size:
self._buf = create_string_buffer(w*h*4)
self._size = img.size
self._buf[:] = out.tobytes('raw', 'BGRA')
source = '&' + str(addressof(self._buf))
self.m.overlay_add(self.overlay_id, x, y, source, 0, 'bgra', w, h, stride)
def remove(self):
self.m.remove_overlay(self.overlay_id)
class FileOverlay:
def __init__(self, m, overlay_id, filename=None, size=None, stride=None, pos=(0,0)):
self.m = m
self.overlay_id = overlay_id
self.pos = pos
self.size = size
self.stride = stride
if filename is not None:
self.update(filename)
def update(self, filename=None, size=None, stride=None, pos=None):
if filename is not None:
self.filename = filename
if pos is not None:
self.pos = pos
if size is not None:
self.size = size
if stride is not None:
self.stride = stride
x, y = self.pos
w, h = self.size
stride = self.stride or 4*w
self.m.overlay_add(self, self.overlay_id, x, y, self.filename, 0, 'bgra', w, h, stride)
def remove(self):
self.m.remove_overlay(self.overlay_id)
class MPV(object): class MPV(object):
"""See man mpv(1) for the details of the implemented commands. All mpv properties can be accessed as """See man mpv(1) for the details of the implemented commands. All mpv properties can be accessed as
``my_mpv.some_property`` and all mpv options can be accessed as ``my_mpv['some-option']``. ``my_mpv.some_property`` and all mpv options can be accessed as ``my_mpv['some-option']``.
@ -796,6 +870,8 @@ class MPV(object):
self.register_stream_protocol('python', self._python_stream_open) self.register_stream_protocol('python', self._python_stream_open)
self._python_streams = {} self._python_streams = {}
self._python_stream_catchall = None self._python_stream_catchall = None
self.overlay_ids = set()
self.overlays = {}
if loglevel is not None or log_handler is not None: if loglevel is not None or log_handler is not None:
self.set_loglevel(loglevel or 'terminal-default') self.set_loglevel(loglevel or 'terminal-default')
if start_event_thread: if start_event_thread:
@ -918,6 +994,34 @@ class MPV(object):
b,g,r,a = img.split() b,g,r,a = img.split()
return Image.merge('RGB', (r,g,b)) return Image.merge('RGB', (r,g,b))
def allocate_overlay_id(self):
free_ids = set(range(64)) - self.overlay_ids
if not free_ids:
raise IndexError('All overlay IDs are in use')
next_id, *_ = sorted(free_ids)
self.overlay_ids.add(next_id)
return next_id
def free_overlay_id(self, overlay_id):
self.overlay_ids.remove(overlay_id)
def create_file_overlay(self, filename=None, size=None, stride=None, pos=(0,0)):
overlay_id = self.allocate_overlay_id()
overlay = FileOverlay(self, overlay_id, filename, size, stride, pos)
self.overlays[overlay_id] = overlay
return overlay
def create_image_overlay(self, img=None, pos=(0,0)):
overlay_id = self.allocate_overlay_id()
overlay = ImageOverlay(self, overlay_id, img, pos)
self.overlays[overlay_id] = overlay
return overlay
def remove_overlay(self, overlay_id):
self.overlay_remove(overlay_id)
self.free_overlay_id(overlay_id)
del self.overlays[overlay_id]
def playlist_next(self, mode='weak'): def playlist_next(self, mode='weak'):
"""Mapped mpv playlist_next command, see man mpv(1).""" """Mapped mpv playlist_next command, see man mpv(1)."""
self.command('playlist_next', mode) self.command('playlist_next', mode)