mirror of https://github.com/kivy/kivy.git
gstplayer: implement duration, position, and seek + correctly register an atexit callback to clean all the remaining worker.
This commit is contained in:
parent
b5e11fd025
commit
a81d916c92
|
@ -1,4 +1,6 @@
|
|||
from libcpp cimport bool
|
||||
from weakref import ref
|
||||
import atexit
|
||||
|
||||
cdef extern from 'gst/gst.h':
|
||||
ctypedef void *GstPipeline
|
||||
|
@ -12,6 +14,8 @@ cdef extern from 'gst/gst.h':
|
|||
ctypedef unsigned long gulong
|
||||
ctypedef void *gpointer
|
||||
ctypedef char const_gchar 'const gchar'
|
||||
ctypedef long int gint64
|
||||
ctypedef unsigned long long GstClockTime
|
||||
|
||||
ctypedef enum GstState:
|
||||
GST_STATE_VOID_PENDING
|
||||
|
@ -20,6 +24,13 @@ cdef extern from 'gst/gst.h':
|
|||
GST_STATE_PAUSED
|
||||
GST_STATE_PLAYING
|
||||
|
||||
ctypedef enum GstFormat:
|
||||
GST_FORMAT_TIME
|
||||
|
||||
ctypedef enum GstSeekFlags:
|
||||
GST_SEEK_FLAG_KEY_UNIT
|
||||
GST_SEEK_FLAG_FLUSH
|
||||
|
||||
ctypedef enum GstStateChangeReturn:
|
||||
pass
|
||||
|
||||
|
@ -38,11 +49,21 @@ cdef extern from 'gst/gst.h':
|
|||
GstElement *gst_pipeline_new(const_gchar *name)
|
||||
void gst_bus_enable_sync_message_emission(GstBus *bus)
|
||||
GstBus *gst_pipeline_get_bus(GstPipeline *pipeline)
|
||||
GstStateChangeReturn gst_element_get_state(
|
||||
GstElement *element, GstState *state, GstState *pending,
|
||||
GstClockTime timeout) nogil
|
||||
GstStateChangeReturn gst_element_set_state(
|
||||
GstElement *element, GstState state) nogil
|
||||
void g_signal_emit_by_name(gpointer instance, const_gchar *detailed_signal,
|
||||
void *retvalue)
|
||||
void g_error_free(GError *error)
|
||||
bool gst_element_query_position(
|
||||
GstElement *element, GstFormat format, gint64 *cur)
|
||||
bool gst_element_query_duration(
|
||||
GstElement *element, GstFormat format, gint64 *cur)
|
||||
bool gst_element_seek_simple(
|
||||
GstElement *element, GstFormat format,
|
||||
GstSeekFlags seek_flags, gint64 seek_pos)
|
||||
|
||||
cdef extern from '_gstplayer.h':
|
||||
void g_object_set_void(GstElement *element, char *name, void *value)
|
||||
|
@ -54,6 +75,25 @@ cdef extern from '_gstplayer.h':
|
|||
void c_appsink_disconnect(GstElement *appsink, gulong handler_id)
|
||||
|
||||
|
||||
#
|
||||
# prevent gstreamer crash when some player are still working.
|
||||
#
|
||||
|
||||
cdef list _instances = []
|
||||
|
||||
def _on_player_deleted(wk):
|
||||
if wk in _instances:
|
||||
_instances.remove(wk)
|
||||
|
||||
@atexit.register
|
||||
def gst_exit_clean():
|
||||
for wk in _instances:
|
||||
player = wk()
|
||||
if player:
|
||||
player.stop()
|
||||
player.unload()
|
||||
|
||||
|
||||
class GstPlayerException(Exception):
|
||||
pass
|
||||
|
||||
|
@ -88,6 +128,7 @@ cdef class GstPlayer:
|
|||
cdef GstBus *bus
|
||||
cdef object uri, sample_cb
|
||||
cdef gulong handler_id
|
||||
cdef object __weakref__
|
||||
|
||||
def __cinit__(self, uri, sample_cb):
|
||||
self.pipeline = self.playbin = self.appsink = NULL
|
||||
|
@ -98,6 +139,7 @@ cdef class GstPlayer:
|
|||
super(GstPlayer, self).__init__()
|
||||
self.uri = uri
|
||||
self.sample_cb = sample_cb
|
||||
_instances.append(ref(self, _on_player_deleted))
|
||||
|
||||
# ensure gstreamer is init
|
||||
_gst_init()
|
||||
|
@ -135,7 +177,7 @@ cdef class GstPlayer:
|
|||
if self.appsink == NULL:
|
||||
raise GstPlayerException('Unable to create an appsink')
|
||||
|
||||
g_object_set_caps(self.appsink, 'video/x-raw,format=RGBA')
|
||||
g_object_set_caps(self.appsink, 'video/x-raw,format=RGB')
|
||||
g_object_set_int(self.appsink, 'max-buffers', 5)
|
||||
g_object_set_int(self.appsink, 'drop', 1)
|
||||
g_object_set_int(self.appsink, 'sync', 1)
|
||||
|
@ -168,12 +210,19 @@ cdef class GstPlayer:
|
|||
gst_element_set_state(self.pipeline, GST_STATE_PAUSED)
|
||||
|
||||
def unload(self):
|
||||
cdef GstState current_state, pending_state
|
||||
|
||||
if self.appsink != NULL and self.handler_id != 0:
|
||||
c_appsink_disconnect(self.appsink, self.handler_id)
|
||||
|
||||
if self.pipeline != NULL:
|
||||
# the state changes are async. if we want to guarantee that the
|
||||
# state is set to NULL, we need to query it. We also put a 5s
|
||||
# timeout for safety, but normally, nobody should hit it.
|
||||
with nogil:
|
||||
gst_element_set_state(self.pipeline, GST_STATE_NULL)
|
||||
gst_element_get_state(self.pipeline, ¤t_state,
|
||||
&pending_state, <GstClockTime>5e9)
|
||||
gst_object_unref(self.pipeline)
|
||||
|
||||
if self.bus != NULL:
|
||||
|
@ -190,7 +239,27 @@ cdef class GstPlayer:
|
|||
g_object_set_double(self.playbin, 'volume', volume)
|
||||
|
||||
def get_duration(self):
|
||||
return -1
|
||||
cdef gint64 duration = 0
|
||||
if self.playbin == NULL:
|
||||
return -1
|
||||
if not gst_element_query_duration(
|
||||
self.playbin, GST_FORMAT_TIME, &duration):
|
||||
return -1
|
||||
return duration / 1e9
|
||||
|
||||
def get_position(self):
|
||||
return -1
|
||||
cdef gint64 position = 0
|
||||
if self.playbin == NULL:
|
||||
return -1
|
||||
if not gst_element_query_position(
|
||||
self.playbin, GST_FORMAT_TIME, &position):
|
||||
return -1
|
||||
return position / 1e9
|
||||
|
||||
def seek(self, percent):
|
||||
if self.playbin == NULL:
|
||||
return
|
||||
seek_t = percent * self.get_duration() * 1e9
|
||||
seek_flags = GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_KEY_UNIT
|
||||
gst_element_seek_simple(self.playbin, GST_FORMAT_TIME,
|
||||
<GstSeekFlags>seek_flags, seek_t)
|
||||
|
|
Loading…
Reference in New Issue