1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145
|
#! /usr/bin/env python3
#
# GlSurface example code for VLC Python bindings
# Copyright (C) 2020 Daniƫl van Adrichem <daniel5gh@spiet.nl>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
#
"""VLC GlSurface example
"""
import time
import ctypes
from threading import Lock
import numpy as np
from OpenGL.GL import (GL_TEXTURE_2D, glTexSubImage2D, glTexImage2D,
GL_BGR, GL_RGB,
GL_UNSIGNED_BYTE)
import vlc
class Surface(object):
"""A lockable image buffer
"""
def __init__(self, w, h):
self._width = w
self._height = h
# size in bytes when RV32 *4 or RV24 * 3
self._row_size = self._width * 3
self._buf_size = self._height * self._row_size
# allocate buffer
self._buf1 = np.zeros(self._buf_size, dtype=np.ubyte)
# get pointer to buffer
self._buf_p = self._buf1.ctypes.data_as(ctypes.c_void_p)
self._lock = Lock()
def update_gl(self):
# with self._lock:
glTexSubImage2D(GL_TEXTURE_2D,
0, 0, 0,
self._width,
self._height,
GL_BGR,
GL_UNSIGNED_BYTE,
self._buf1)
def create_texture_gl(self):
glTexImage2D(GL_TEXTURE_2D,
0,
GL_RGB,
self._width, # width
self._height, # height
0,
GL_BGR,
GL_UNSIGNED_BYTE,
None)
@property
def width(self):
return self._width
@property
def height(self):
return self._height
@property
def row_size(self):
return self._row_size
@property
def buf(self):
return self._buf1
@property
def buf_pointer(self):
return self._buf_p
def lock(self):
self._lock.acquire()
def unlock(self):
self._lock.release()
def __enter__(self, *args):
return self._lock.__enter__(*args)
def __exit__(self, *args):
return self._lock.__exit__(*args)
def get_libvlc_lock_callback(self):
@vlc.VideoLockCb
def _cb(opaque, planes):
self._lock.acquire()
planes[0] = self._buf_p
return _cb
def get_libvlc_unlock_callback(self):
@vlc.VideoUnlockCb
def _cb(opaque, picta, planes):
self._lock.release()
return _cb
if __name__ == '__main__':
import sys
player = vlc.MediaPlayer(sys.argv[1])
# play and stop so video_get_size gets a correct value
# setting all callbacks to None prevents a window being created on play
player.video_set_callbacks(None, None, None, None)
# play and stop so video_get_size gets a correct value
player.play()
time.sleep(1)
player.stop()
w, h = player.video_get_size()
surface = Surface(w, h)
# need to keep a reference to the CFUNCTYPEs or else it will get GCed
_lock_cb = surface.get_libvlc_lock_callback()
_unlock_cb = surface.get_libvlc_unlock_callback()
player.video_set_callbacks(_lock_cb, _unlock_cb, None, None)
player.video_set_format(
"RV24",
surface.width,
surface.height,
surface.row_size)
# this starts populating the surface's buf with pixels, from another thread
player.play()
# in main thread, where gl context is current:
# FIXME: add some code to embed the surface + a mainloop
# v.surface.update_gl()
|