gstplayer: implement duration, position, and seek + correctly register an atexit callback to clean all the remaining worker.

This commit is contained in:
Mathieu Virbel 2013-12-15 20:54:29 +01:00
parent b5e11fd025
commit a81d916c92
1 changed files with 72 additions and 3 deletions

View File

@ -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, &current_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)