Clock fixes. Added async callbacks and clock unscheduling.

Original commit message from CVS:
Clock fixes. Added async callbacks and clock unscheduling.
Threading fixes. Fixed race condition in GstTask and possible
deadlock in _pad_get_caps(). Made various subsystems (query,
format,..) threadsafe.
Lots of cleanups and documentation.
This commit is contained in:
Wim Taymans 2005-01-06 18:17:12 +00:00
parent 2b58a38a2b
commit 74ca793b0a
49 changed files with 1732 additions and 603 deletions

105
ChangeLog
View file

@ -1,3 +1,108 @@
2005-01-06 Wim Taymans <wim@fluendo.com>
* gst/elements/gstfakesrc.c: (gst_fakesrc_loop),
(gst_fakesrc_activate):
* gst/elements/gstfilesrc.c: (gst_filesrc_getrange),
(gst_filesrc_loop), (gst_filesrc_activate):
* gst/gstbin.c: (gst_bin_set_index), (gst_bin_set_clock),
(gst_bin_set_bus), (gst_bin_set_scheduler), (gst_bin_add_func),
(gst_bin_remove_func), (gst_bin_iterate_elements),
(bin_element_is_sink), (gst_bin_get_state), (gst_bin_change_state),
(gst_bin_get_by_name), (gst_bin_get_by_name_recurse_up):
* gst/gstbuffer.c: (gst_buffer_get_type), (gst_buffer_set_caps):
* gst/gstbuffer.h:
* gst/gstbus.c: (gst_bus_post), (gst_bus_create_watch),
(bus_destroy):
* gst/gstbus.h:
* gst/gstclock.c: (gst_clock_entry_new), (gst_clock_id_ref),
(_gst_clock_id_free), (gst_clock_id_unref),
(gst_clock_id_compare_func), (gst_clock_id_wait),
(gst_clock_id_wait_async), (gst_clock_class_init),
(gst_clock_init), (gst_clock_dispose), (gst_clock_get_time),
(gst_clock_set_time_adjust), (gst_clock_set_property),
(gst_clock_get_property):
* gst/gstclock.h:
* gst/gstelement.c: (gst_element_add_pad),
(gst_element_remove_pad), (gst_element_post_message),
(gst_element_set_locked_state),
(gst_element_sync_state_with_parent), (gst_element_get_state_func),
(gst_element_abort_state), (gst_element_commit_state),
(gst_element_pads_activate):
* gst/gstelement.h:
* gst/gstformat.c: (_gst_format_initialize), (gst_format_register),
(gst_format_get_by_nick), (gst_format_get_details),
(gst_format_iterate_definitions):
* gst/gstformat.h:
* gst/gstiterator.c: (gst_iterator_new), (gst_list_iterator_next),
(gst_list_iterator_free), (gst_iterator_new_list),
(gst_iterator_next), (gst_iterator_resync), (filter_next):
* gst/gstiterator.h:
* gst/gstmemchunk.c: (gst_mem_chunk_alloc), (gst_mem_chunk_alloc0),
(gst_mem_chunk_free):
* gst/gstmessage.c: (_gst_message_free):
* gst/gstmessage.h:
* gst/gstobject.c: (gst_object_class_init), (gst_object_init),
(gst_object_ref), (gst_object_unref), (gst_object_sink),
(gst_object_dispose), (gst_object_dispatch_properties_changed),
(gst_object_set_name_default), (gst_object_set_name),
(gst_object_set_parent), (gst_object_unparent),
(gst_object_check_uniqueness), (gst_object_get_path_string):
* gst/gstpad.c: (gst_pad_set_active), (gst_pad_set_blocked_async),
(gst_pad_set_acceptcaps_function),
(gst_pad_set_fixatecaps_function), (gst_pad_unlink),
(gst_pad_link_prepare_filtered), (gst_pad_link_filtered),
(gst_pad_set_pad_template), (gst_pad_relink_filtered),
(gst_real_pad_get_caps_unlocked), (gst_pad_peer_get_caps),
(gst_pad_fixate_caps), (gst_pad_accept_caps),
(gst_pad_peer_accept_caps), (gst_pad_set_caps),
(gst_pad_configure_sink), (gst_pad_configure_src),
(gst_pad_realize), (gst_pad_alloc_buffer), (gst_pad_push),
(gst_pad_pull_range), (gst_pad_push_event), (gst_pad_send_event):
* gst/gstpad.h:
* gst/gstpipeline.c: (is_eos), (pipeline_bus_handler),
(gst_pipeline_change_state), (gst_pipeline_get_clock_func),
(gst_pipeline_use_clock), (gst_pipeline_auto_clock):
* gst/gstpipeline.h:
* gst/gstpluginfeature.c: (gst_plugin_feature_get_name),
(gst_plugin_feature_set_rank), (gst_plugin_feature_get_rank):
* gst/gstpluginfeature.h:
* gst/gstprobe.c: (gst_probe_dispatcher_dispatch):
* gst/gstquery.c: (_gst_query_type_initialize),
(gst_query_type_register), (gst_query_type_get_by_nick),
(gst_query_type_get_details), (gst_query_type_iterate_definitions):
* gst/gstquery.h:
* gst/gstqueue.c: (gst_queue_init), (gst_queue_bufferalloc),
(gst_queue_loop):
* gst/gstsystemclock.c: (gst_system_clock_class_init),
(gst_system_clock_init), (gst_system_clock_dispose),
(gst_system_clock_async_thread), (gst_system_clock_id_wait),
(gst_system_clock_id_wait_async), (gst_system_clock_id_unschedule):
* gst/gstsystemclock.h:
* gst/gsttask.c: (gst_task_init), (gst_task_dispose),
(gst_task_create), (gst_task_get_state), (gst_task_start),
(gst_task_stop), (gst_task_pause):
* gst/gsttask.h:
* gst/gsttrashstack.h:
* gst/schedulers/threadscheduler.c:
(gst_thread_scheduler_task_init),
(gst_thread_scheduler_task_start),
(gst_thread_scheduler_task_stop),
(gst_thread_scheduler_task_pause), (gst_thread_scheduler_func),
(gst_thread_scheduler_create_task):
* testsuite/clock/.cvsignore:
* testsuite/clock/Makefile.am:
* testsuite/clock/clock1.c: (main):
* testsuite/clock/clock2.c: (gst_clock_debug), (element_wait),
(main):
* testsuite/clock/clock3.c: (gst_clock_debug), (ok_callback),
(error_callback), (main):
Clock fixes. Added async callbacks and clock unscheduling.
Threading fixes. Fixed race condition in GstTask and possible
deadlock in _pad_get_caps(). Made various subsystems (query,
format,..) threadsafe.
Lots of cleanups and documentation.
2005-01-04 Wim Taymans <wim@fluendo.com>
* gst/elements/gstfilesrc.c: (gst_filesrc_getrange),

View file

@ -189,7 +189,7 @@ static void gst_fakesrc_set_clock (GstElement * element, GstClock * clock);
static GstElementStateReturn gst_fakesrc_change_state (GstElement * element);
static gboolean gst_fakesrc_loop (GstPad * pad);
static void gst_fakesrc_loop (GstPad * pad);
static guint gst_fakesrc_signals[LAST_SIGNAL] = { 0 };
@ -778,13 +778,12 @@ gst_fakesrc_create_buffer (GstFakeSrc * src)
return buf;
}
static gboolean
static void
gst_fakesrc_loop (GstPad * pad)
{
GstFakeSrc *src;
GstBuffer *buf;
GstClockTime time;
gboolean result = TRUE;
src = GST_FAKESRC (GST_OBJECT_PARENT (pad));
@ -799,14 +798,14 @@ gst_fakesrc_loop (GstPad * pad)
gst_pad_push_event (pad, gst_event_new (GST_EVENT_SEGMENT_DONE));
} else {
gst_pad_push_event (pad, gst_event_new (GST_EVENT_EOS));
result = FALSE;
gst_task_pause (src->task);
goto done;
}
}
if (src->rt_num_buffers == 0) {
gst_pad_push_event (pad, gst_event_new (GST_EVENT_EOS));
result = FALSE;
gst_task_pause (src->task);
goto done;
} else {
if (src->rt_num_buffers > 0)
@ -816,7 +815,7 @@ gst_fakesrc_loop (GstPad * pad)
if (src->eos) {
GST_INFO ("fakesrc is setting eos on pad");
gst_pad_push_event (pad, gst_event_new (GST_EVENT_EOS));
result = FALSE;
gst_task_pause (src->task);
goto done;
}
@ -860,8 +859,6 @@ gst_fakesrc_loop (GstPad * pad)
done:
GST_STREAM_UNLOCK (pad);
return result;
}
#if 0

View file

@ -881,7 +881,7 @@ gst_filesrc_close_file (GstFileSrc * src)
GST_FLAG_UNSET (src, GST_FILESRC_OPEN);
}
static gboolean
static void
gst_filesrc_loop (GstElement * element)
{
GstFileSrc *filesrc;
@ -892,14 +892,13 @@ gst_filesrc_loop (GstElement * element)
result = gst_filesrc_get (filesrc->srcpad, &buffer);
if (result != GST_FLOW_OK) {
return FALSE;
gst_task_stop (filesrc->task);
return;
}
result = gst_pad_push (filesrc->srcpad, buffer);
if (result != GST_FLOW_OK) {
return FALSE;
gst_task_stop (filesrc->task);
}
return TRUE;
}

View file

@ -198,6 +198,10 @@ gst_bin_new (const gchar * name)
return gst_element_factory_make ("bin", name);
}
/* set the index on all elements in this bin
*
* MT safe
*/
#ifndef GST_DISABLE_INDEX
static void
gst_bin_set_index (GstElement * element, GstIndex * index)
@ -217,6 +221,10 @@ gst_bin_set_index (GstElement * element, GstIndex * index)
}
#endif
/* set the clock on all elements in this bin
*
* MT safe
*/
static void
gst_bin_set_clock (GstElement * element, GstClock * clock)
{
@ -234,6 +242,10 @@ gst_bin_set_clock (GstElement * element, GstClock * clock)
GST_UNLOCK (bin);
}
/* get the clock for this bin by asking all of the children in this bin
*
* MT safe
*/
static GstClock *
gst_bin_get_clock (GstElement * element)
{
@ -256,6 +268,10 @@ gst_bin_get_clock (GstElement * element)
return result;
}
/* set the bus on all of the children in this bin
*
* MT safe
*/
static void
gst_bin_set_bus (GstElement * element, GstBus * bus)
{
@ -275,6 +291,10 @@ gst_bin_set_bus (GstElement * element, GstBus * bus)
GST_UNLOCK (bin);
}
/* set the scheduler on all of the children in this bin
*
* MT safe
*/
static void
gst_bin_set_scheduler (GstElement * element, GstScheduler * sched)
{
@ -294,6 +314,10 @@ gst_bin_set_scheduler (GstElement * element, GstScheduler * sched)
GST_UNLOCK (bin);
}
/* add an element to this bin
*
* MT safe
*/
static gboolean
gst_bin_add_func (GstBin * bin, GstElement * element)
{
@ -303,6 +327,8 @@ gst_bin_add_func (GstBin * bin, GstElement * element)
if (G_UNLIKELY (GST_ELEMENT_CAST (element) == GST_ELEMENT_CAST (bin)))
goto adding_itself;
/* get the element name to make sure it is unique in this bin, FIXME, another
* thread can change the name after the unlock. */
GST_LOCK (element);
elem_name = g_strdup (GST_ELEMENT_NAME (element));
GST_UNLOCK (element);
@ -399,11 +425,16 @@ no_function:
return FALSE;
}
/* remove an element from the bin
*
* MT safe
*/
static gboolean
gst_bin_remove_func (GstBin * bin, GstElement * element)
{
gchar *elem_name;
/* grab element name so we can print it */
GST_LOCK (element);
elem_name = g_strdup (GST_ELEMENT_NAME (element));
GST_UNLOCK (element);
@ -424,6 +455,8 @@ gst_bin_remove_func (GstBin * bin, GstElement * element)
g_free (elem_name);
gst_element_set_manager (element, NULL);
gst_element_set_bus (element, NULL);
gst_element_set_scheduler (element, NULL);
/* we ref here because after the _unparent() the element can be disposed
* and we still need it to fire a signal. */
@ -507,6 +540,7 @@ gst_bin_iterate_elements (GstBin * bin)
g_return_val_if_fail (GST_IS_BIN (bin), NULL);
GST_LOCK (bin);
/* add ref because the iterator refs the bin */
gst_object_ref (GST_OBJECT (bin));
result = gst_iterator_new_list (GST_GET_LOCK (bin),
&bin->children_cookie,
@ -521,18 +555,25 @@ gst_bin_iterate_elements (GstBin * bin)
}
/* returns 0 if the element is a sink, this is made so that
* we can use this function as a filter */
* we can use this function as a filter
*
* MT safe
*/
static gint
bin_element_is_sink (GstElement * child, GstBin * bin)
{
gint ret = 1;
/* we lock the child here for the remainder of the function to
* get its pads and name safely. */
GST_LOCK (child);
/* check if this is a sink element, these are the elements
* without (linked) source pads. */
if (child->numsrcpads == 0) {
/* shortcut */
GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, bin,
"adding child %s as sink", gst_element_get_name (child));
"adding child %s as sink", GST_OBJECT_NAME (child));
ret = 0;
} else {
/* loop over all pads, try to figure out if this element
@ -540,26 +581,30 @@ bin_element_is_sink (GstElement * child, GstBin * bin)
GList *pads;
gboolean connected_src = FALSE;
/* FIXME not MT safe */
for (pads = child->srcpads; pads; pads = g_list_next (pads)) {
pads = child->srcpads;
while (pads) {
GstPad *pad = GST_PAD (pads->data);
if (GST_PAD_IS_LINKED (pad)) {
connected_src = TRUE;
break;
}
pads = g_list_next (pads);
}
if (connected_src) {
GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, bin,
"not adding child %s as sink: linked source pads",
gst_element_get_name (child));
GST_OBJECT_NAME (child));
} else {
GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, bin,
"adding child %s as sink since it has unlinked source pads",
gst_element_get_name (child));
GST_OBJECT_NAME (child));
ret = 0;
}
}
GST_UNLOCK (child);
/* we did not find the element, need to release the ref
* added by the iterator */
if (ret == 1)
@ -595,52 +640,64 @@ gst_bin_iterate_sinks (GstBin * bin)
return result;
}
static gint
bin_find_pending_child (GstElement * child, GTimeVal * timeout)
{
gboolean ret;
ret = gst_element_get_state (GST_ELEMENT (child), NULL, NULL, timeout);
/* ret is false if some child is still performing the state change */
gst_object_unref (GST_OBJECT (child));
return (ret == FALSE ? 0 : 1);
}
/* this functions loops over all children, as soon as one is
* still performing the state change, FALSE is returned. */
* still performing the state change, FALSE is returned.
*
* MT safe
*/
static gboolean
gst_bin_get_state (GstElement * element, GstElementState * state,
GstElementState * pending, GTimeVal * timeout)
{
gboolean ret = TRUE;
GstBin *bin = GST_BIN (element);
GstIterator *children;
gboolean have_async = FALSE;
gpointer child;
gboolean ret;
GList *children;
guint32 children_cookie;
/* we cannot take the state lock yet as we might block when querying
* the children, holding the lock too long for no reason */
/* FIXME, we can loop the list ourselves instead of creating the
* iterator */
children = gst_bin_iterate_sinks (bin);
child = gst_iterator_find_custom (children, timeout,
(GCompareFunc) bin_find_pending_child);
gst_iterator_free (children);
/* we unreffed the child in the comparefunc */
if (child) {
have_async = TRUE;
ret = FALSE;
}
* the children, holding the lock too long for no reason. */
/* next we poll all children for their state to see if one of them
* is still busy with its state change. */
GST_LOCK (bin);
restart:
ret = TRUE;
children = bin->children;
children_cookie = bin->children_cookie;
while (children) {
GstElement *child = GST_ELEMENT_CAST (children->data);
gst_object_ref (GST_OBJECT_CAST (child));
GST_UNLOCK (bin);
/* ret is false if some child is still performing the state change */
ret = gst_element_get_state (child, NULL, NULL, timeout);
gst_object_unref (GST_OBJECT_CAST (child));
if (!ret) {
/* some child is still busy, return FALSE */
goto done;
}
/* now grab the lock to iterate to the next child */
GST_LOCK (bin);
if (G_UNLIKELY (children_cookie != bin->children_cookie))
/* child added/removed during state change, restart */
goto restart;
children = g_list_next (children);
}
GST_UNLOCK (bin);
done:
/* now we can take the state lock */
GST_STATE_LOCK (bin);
if (!have_async) {
if (ret) {
/* no async children, we can commit the state */
gst_element_commit_state (element);
}
/* and report the state */
/* and report the state if needed */
if (state)
*state = GST_STATE (element);
if (pending)
@ -651,7 +708,10 @@ gst_bin_get_state (GstElement * element, GstElementState * state,
return ret;
}
/* this function is called with the STATE_LOCK held */
/* this function is called with the STATE_LOCK held.
*
* MT safe.
*/
static GstElementStateReturn
gst_bin_change_state (GstElement * element)
{
@ -659,9 +719,8 @@ gst_bin_change_state (GstElement * element)
GstElementStateReturn ret;
GstElementState old_state, pending;
gboolean have_async = FALSE;
GstIterator *sinks;
gboolean done = FALSE;
GList *children;
guint32 children_cookie;
GQueue *elem_queue; /* list of elements waiting for a state change */
bin = GST_BIN (element);
@ -677,44 +736,51 @@ gst_bin_change_state (GstElement * element)
if (pending == GST_STATE_VOID_PENDING)
return GST_STATE_SUCCESS;
/* all elements added to this queue should have their refcount
* incremented */
elem_queue = g_queue_new ();
/* first step, find all sink elements, these are the elements
* without (linked) source pads. */
/* FIXME, we can iterate the list ourselves */
sinks = gst_bin_iterate_sinks (bin);
while (!done) {
gpointer child;
GST_LOCK (bin);
restart:
children = bin->children;
children_cookie = bin->children_cookie;
while (children) {
GstElement *child = GST_ELEMENT_CAST (children->data);
switch (gst_iterator_next (sinks, &child)) {
case GST_ITERATOR_OK:
/* this also keeps the refcount on the element */
g_queue_push_tail (elem_queue, child);
break;
case GST_ITERATOR_RESYNC:
/* undo what we had */
g_queue_foreach (elem_queue, (GFunc) gst_object_unref, NULL);
while (g_queue_pop_head (elem_queue));
gst_iterator_resync (sinks);
break;
case GST_ITERATOR_DONE:
done = TRUE;
break;
default:
g_assert_not_reached ();
break;
gst_object_ref (GST_OBJECT_CAST (child));
GST_UNLOCK (bin);
if (bin_element_is_sink (child, bin) == 0) {
/* this also keeps the refcount on the element, note that
* the _is_sink function unrefs the element when it is not
* a sink. */
g_queue_push_tail (elem_queue, child);
}
GST_LOCK (bin);
if (G_UNLIKELY (children_cookie != bin->children_cookie)) {
/* undo what we had */
g_queue_foreach (elem_queue, (GFunc) gst_object_unref, NULL);
while (g_queue_pop_head (elem_queue));
goto restart;
}
children = g_list_next (children);
}
gst_iterator_free (sinks);
GST_UNLOCK (bin);
/* second step, change state of elements in the queue */
while (!g_queue_is_empty (elem_queue)) {
GstElement *qelement = g_queue_pop_head (elem_queue);
GList *pads;
gboolean locked;
/* queue all elements connected to the sinkpads of this element */
/* FIXME, not MT safe !! */
for (pads = qelement->sinkpads; pads; pads = g_list_next (pads)) {
GST_LOCK (qelement);
pads = qelement->sinkpads;
while (pads) {
GstPad *pad = GST_PAD (pads->data);
GstPad *peer;
@ -730,9 +796,10 @@ gst_bin_change_state (GstElement * element)
if (peer_elem) {
GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element,
"adding element %s to queue", gst_element_get_name (peer_elem));
"adding element %s to queue", GST_ELEMENT_NAME (peer_elem));
/* is reffed before pushing on the queue */
/* was reffed before pushing on the queue by the
* gst_object_get_parent() call we used to get the element. */
g_queue_push_tail (elem_queue, peer_elem);
}
gst_object_unref (GST_OBJECT (peer));
@ -740,12 +807,15 @@ gst_bin_change_state (GstElement * element)
GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element,
"pad %s:%s does not have a peer", GST_DEBUG_PAD_NAME (pad));
}
pads = g_list_next (pads);
}
/* peel off the locked flag and release the element lock */
locked = GST_FLAG_IS_SET (qelement, GST_ELEMENT_LOCKED_STATE);
GST_UNLOCK (qelement);
if (GST_FLAG_IS_SET (qelement, GST_ELEMENT_LOCKED_STATE))
if (G_UNLIKELY (locked))
goto next_element;
qelement->base_time = element->base_time;
ret = gst_element_set_state (qelement, pending);
switch (ret) {
@ -853,6 +923,7 @@ gst_bin_get_by_name (GstBin * bin, const gchar * name)
GST_CAT_INFO (GST_CAT_PARENTAGE, "[%s]: looking up child element %s",
GST_ELEMENT_NAME (bin), name);
/* we recursively lock all elements, this might be a bit too much.. */
GST_LOCK (bin);
for (children = bin->children; children; children = g_list_next (children)) {
GstElement *child = GST_ELEMENT_CAST (children->data);
@ -861,6 +932,7 @@ gst_bin_get_by_name (GstBin * bin, const gchar * name)
GST_LOCK (child);
eq = strcmp (GST_ELEMENT_NAME (child), name) == 0;
GST_UNLOCK (child);
if (eq) {
result = child;
break;

View file

@ -28,7 +28,7 @@
#include "gstmemchunk.h"
#include "gstinfo.h"
GType _gst_buffer_type;
GType _gst_buffer_type = 0;
#ifndef GST_DISABLE_TRACE
/* #define GST_WITH_ALLOC_TRACE */
@ -60,7 +60,7 @@ _gst_buffer_initialize (void)
GType
gst_buffer_get_type (void)
{
if (_gst_buffer_type == 0) {
if (G_UNLIKELY (_gst_buffer_type == 0)) {
_gst_buffer_type = g_boxed_type_register_static ("GstBuffer",
(GBoxedCopyFunc) gst_data_copy, (GBoxedFreeFunc) gst_data_unref);
}
@ -88,6 +88,8 @@ _gst_buffer_sub_free (GstBuffer * buffer)
*
* Frees the memory associated with the buffer including the buffer data,
* unless the GST_BUFFER_DONTFREE flags was set or the buffer data is NULL.
*
* MT safe.
*/
void
gst_buffer_default_free (GstBuffer * buffer)
@ -121,6 +123,8 @@ gst_buffer_default_free (GstBuffer * buffer)
* is increased.
*
* Returns: the new #GstBuffer.
*
* MT safe.
*/
GstBuffer *
gst_buffer_default_copy (GstBuffer * buffer)
@ -194,6 +198,8 @@ gst_buffer_free_chunk (GstBuffer * buffer)
* Creates a newly allocated buffer without any data.
*
* Returns: the new #GstBuffer.
*
* MT safe.
*/
GstBuffer *
gst_buffer_new (void)
@ -231,6 +237,8 @@ gst_buffer_new (void)
* Creates a newly allocated buffer with data of the given size.
*
* Returns: the new #GstBuffer.
*
* MT safe.
*/
GstBuffer *
gst_buffer_new_and_alloc (guint size)
@ -255,9 +263,14 @@ gst_buffer_new_and_alloc (guint size)
* is not media type attached to this buffer or when the media
* type is the same as the previous received buffer.
*
* This function does not increment the refcount of the caps. The
* caps pointer will therefore remain valid until the buffer is
* unreffed.
*
* Returns: the #GstCaps, or NULL if there was an error or there
* were no caps on this buffer.
*/
/* FIXME can we make this threadsafe without a lock on the buffer? */
GstCaps *
gst_buffer_get_caps (GstBuffer * buffer)
{
@ -275,21 +288,26 @@ gst_buffer_get_caps (GstBuffer * buffer)
* be increased and any previous caps on the buffer will be
* unreffed.
*/
/* FIXME can we make this threadsafe without a lock on the buffer? */
void
gst_buffer_set_caps (GstBuffer * buffer, GstCaps * caps)
{
GstCaps *oldcaps;
g_return_if_fail (buffer != NULL);
/* unref old caps if any */
if (GST_BUFFER_CAPS (buffer)) {
gst_caps_unref (GST_BUFFER_CAPS (buffer));
}
/* get old caps */
oldcaps = GST_BUFFER_CAPS (buffer);
/* ref new caps if any */
if (caps)
caps = gst_caps_ref (caps);
/* set caps */
GST_BUFFER_CAPS (buffer) = caps;
/* unref old caps if any */
if (oldcaps) {
gst_caps_unref (oldcaps);
}
}
/**
@ -305,6 +323,8 @@ gst_buffer_set_caps (GstBuffer * buffer, GstCaps * caps)
* The duration field of the new buffer are set to GST_CLOCK_TIME_NONE.
*
* Returns: the new #GstBuffer, or NULL if there was an error.
*
* MT safe.
*/
GstBuffer *
gst_buffer_create_sub (GstBuffer * parent, guint offset, guint size)
@ -381,6 +401,8 @@ gst_buffer_create_sub (GstBuffer * parent, guint offset, guint size)
* is created without copying the data.
*
* Returns: the new #GstBuffer that's the concatenation of the source buffers.
*
* MT safe.
*/
GstBuffer *
gst_buffer_merge (GstBuffer * buf1, GstBuffer * buf2)
@ -403,6 +425,8 @@ gst_buffer_merge (GstBuffer * buf1, GstBuffer * buf2)
*
* Returns: TRUE if the buffers are contiguous,
* FALSE if a copy would be required.
*
* MT safe.
*/
gboolean
gst_buffer_is_span_fast (GstBuffer * buf1, GstBuffer * buf2)
@ -437,6 +461,8 @@ gst_buffer_is_span_fast (GstBuffer * buf1, GstBuffer * buf2)
* gst_buffer_is_span_fast() to determine if a memcpy will be needed.
*
* Returns: the new #GstBuffer that spans the two source buffers.
*
* MT safe.
*/
GstBuffer *
gst_buffer_span (GstBuffer * buf1, guint32 offset, GstBuffer * buf2,

View file

@ -81,11 +81,12 @@ extern GType _gst_buffer_type;
* @GST_BUFFER_ORIGINAL: buffer is not a copy of another buffer.
* @GST_BUFFER_DONTFREE: do not try to free the data when this buffer is
* unreferenced.
* @GST_BUFFER_KEY_UNIT: the buffer holds a key unit, a unit that can be
* decoded independently of other buffers.
* This flag has been deprecated, see #GST_BUFFER_DELTA_UNIT.
* @GST_BUFFER_DONTKEEP:
* @GST_BUFFER_PREROLL: The buffer is part of the preroll phase and should not
* be displayed.
* @GST_BUFFER_DISCONT: The buffer is the first after a discontinuity in the
* stream.
* @GST_BUFFER_IN_CAPS: the buffer has been added as a field in a #GstCaps.
* @GST_BUFFER_GAP: the buffer has been created to fill a gap in the stream.
* @GST_BUFFER_DELTA_UNIT: this unit cannot be decoded independently.
* Since 0.8.5
* @GST_BUFFER_FLAG_LAST: additional flags can be added starting from this flag.

View file

@ -142,6 +142,15 @@ gst_bus_get_property (GObject * object, guint prop_id,
}
}
/**
* gst_bus_post:
* @bus: a #GstBus to post on
* @message: The #GstMessage to post
*
* Post a message on the given bus.
*
* Returns: the new #GstBuffer.
*/
gboolean
gst_bus_post (GstBus * bus, GstMessage * message)
{
@ -151,32 +160,42 @@ gst_bus_post (GstBus * bus, GstMessage * message)
g_return_val_if_fail (GST_IS_BUS (bus), FALSE);
g_return_val_if_fail (GST_IS_MESSAGE (message), FALSE);
/* first call the sync handler if it is installed */
if (bus->sync_handler) {
reply = bus->sync_handler (bus, message, bus->sync_handler_data);
}
/* now see what we should do with the message */
switch (reply) {
case GST_BUS_DROP:
/* drop the message */
break;
case GST_BUS_PASS:
/* pass the message to the async queue */
g_async_queue_push (bus->queue, message);
c = 'p';
write (bus->control_socket[1], &c, 1);
break;
case GST_BUS_ASYNC:
{
/* async delivery, we need a mutex and a cond to block
* on */
GMutex *lock = g_mutex_new ();
GCond *cond = g_cond_new ();
message->cond = cond;
message->lock = lock;
GST_MESSAGE_COND (message) = cond;
GST_MESSAGE_GET_LOCK (message) = lock;
GST_DEBUG ("waiting for async delivery of message %p", message);
/* now we lock the message mutex, send the message to the async
* queue. When the message is handled by the app and destroyed,
* the cond will be signalled and we can continue */
g_mutex_lock (lock);
g_async_queue_push (bus->queue, message);
c = 'p';
write (bus->control_socket[1], &c, 1);
/* now block till the message is freed */
g_cond_wait (cond, lock);
g_mutex_unlock (lock);
@ -191,6 +210,15 @@ gst_bus_post (GstBus * bus, GstMessage * message)
return TRUE;
}
/**
* gst_bus_have_pending:
* @bus: a #GstBus to check
*
* Check if there are pending messages on the bus that should be
* handled.
*
* Returns: TRUE if there are messages on the bus to be handled.
*/
gboolean
gst_bus_have_pending (GstBus * bus)
{
@ -203,6 +231,15 @@ gst_bus_have_pending (GstBus * bus)
return (length > 0);
}
/**
* gst_bus_pop:
* @bus: a #GstBus to pop
*
* Get a message from the bus.
*
* Returns: The #GstMessage that is on the bus or NULL when there are no
* messages available.
*/
GstMessage *
gst_bus_pop (GstBus * bus)
{
@ -217,6 +254,16 @@ gst_bus_pop (GstBus * bus)
return message;
}
/**
* gst_bus_set_sync_handler:
* @bus: a #GstBus to install the handler on
* @func: The handler function to install
* @data: User data that will be sent to the handler function.
*
* Install a synchronous handler on the bus. The function will be called
* every time a new message is posted on the bus. Note that the function
* will be called in the same thread context as the posting object.
*/
void
gst_bus_set_sync_handler (GstBus * bus, GstBusSyncHandler func, gpointer data)
{
@ -226,12 +273,20 @@ gst_bus_set_sync_handler (GstBus * bus, GstBusSyncHandler func, gpointer data)
bus->sync_handler_data = data;
}
/**
* gst_bus_create_watch:
* @bus: a #GstBus to create the watch for
*
* Create watch for this bus.
*
* Returns: A #GSource that can be added to a mainloop.
*/
GSource *
gst_bus_create_watch (GstBus * bus)
{
GSource *source;
g_return_val_if_fail (GST_IS_BUS (bus), FALSE);
g_return_val_if_fail (GST_IS_BUS (bus), NULL);
source = g_io_create_watch (bus->io_channel, G_IO_IN);
@ -266,13 +321,20 @@ bus_callback (GIOChannel * channel, GIOCondition cond, GstBusWatch * watch)
static void
bus_destroy (GstBusWatch * watch)
{
g_print ("destroy\n");
if (watch->notify) {
watch->notify (watch->user_data);
}
g_free (watch);
}
/**
* gst_bus_add_watch_full:
* @bus: a #GstBus to create the watch for
*
* Adds the bus to the mainloop with the given priority.
*
* Returns: The event source id.
*/
guint
gst_bus_add_watch_full (GstBus * bus, gint priority,
GstBusHandler handler, gpointer user_data, GDestroyNotify notify)
@ -303,6 +365,14 @@ gst_bus_add_watch_full (GstBus * bus, gint priority,
return id;
}
/**
* gst_bus_add_watch:
* @bus: a #GstBus to create the watch for
*
* Adds the bus to the mainloop with the default priority.
*
* Returns: The event source id.
*/
guint
gst_bus_add_watch (GstBus * bus, GstBusHandler handler, gpointer user_data)
{

View file

@ -26,6 +26,7 @@
#include <gst/gstmessage.h>
G_BEGIN_DECLS
/* --- standard type macros --- */
#define GST_TYPE_BUS (gst_bus_get_type ())
#define GST_BUS(bus) (G_TYPE_CHECK_INSTANCE_CAST ((bus), GST_TYPE_BUS, GstBus))
@ -34,30 +35,29 @@ G_BEGIN_DECLS
#define GST_IS_BUS_CLASS(bclass) (G_TYPE_CHECK_CLASS_TYPE ((bclass), GST_TYPE_BUS))
#define GST_BUS_GET_CLASS(bus) (G_TYPE_INSTANCE_GET_CLASS ((bus), GST_TYPE_BUS, GstBusClass))
#define GST_BUS_CAST(bus) ((GstBus*)(bus))
typedef enum
typedef enum
{
GST_BUS_DROP = 0, /* drop message */
GST_BUS_PASS = 1, /* pass message to async queue */
GST_BUS_ASYNC = 2, /* pass message to async queue, continue if message is handled */
} GstBusSyncReply;
typedef GstBusSyncReply (*GstBusSyncHandler) (GstBus * bus,
GstMessage * message, gpointer data);
typedef gboolean (*GstBusHandler) (GstBus * bus, GstMessage * message,
gpointer data);
typedef GstBusSyncReply (*GstBusSyncHandler) (GstBus * bus, GstMessage * message, gpointer data);
typedef gboolean (*GstBusHandler) (GstBus * bus, GstMessage * message, gpointer data);
struct _GstBus
{
GstObject object;
GstObject object;
/*< private > */
GAsyncQueue *queue;
GAsyncQueue *queue;
GstBusSyncHandler sync_handler;
gpointer sync_handler_data;
gpointer sync_handler_data;
gint control_socket[2];
GIOChannel *io_channel;
gint control_socket[2];
GIOChannel *io_channel;
/*< private > */
gpointer _gst_reserved[GST_PADDING];
@ -71,23 +71,26 @@ struct _GstBusClass
gpointer _gst_reserved[GST_PADDING];
};
GType gst_bus_get_type (void);
GType gst_bus_get_type (void);
gboolean gst_bus_post (GstBus * bus, GstMessage * message);
gboolean gst_bus_post (GstBus * bus, GstMessage * message);
gboolean gst_bus_have_pending (GstBus * bus);
const GstMessage *gst_bus_peek (GstBus * bus);
GstMessage *gst_bus_pop (GstBus * bus);
gboolean gst_bus_have_pending (GstBus * bus);
const GstMessage * gst_bus_peek (GstBus * bus);
GstMessage * gst_bus_pop (GstBus * bus);
void gst_bus_set_sync_handler (GstBus * bus, GstBusSyncHandler func,
gpointer data);
void gst_bus_set_sync_handler (GstBus * bus, GstBusSyncHandler func,
gpointer data);
GSource *gst_bus_create_watch (GstBus * bus);
guint gst_bus_add_watch_full (GstBus * bus,
gint priority,
GstBusHandler handler, gpointer user_data, GDestroyNotify notify);
guint gst_bus_add_watch (GstBus * bus,
GstBusHandler handler, gpointer user_data);
GSource * gst_bus_create_watch (GstBus * bus);
guint gst_bus_add_watch_full (GstBus * bus,
gint priority,
GstBusHandler handler,
gpointer user_data,
GDestroyNotify notify);
guint gst_bus_add_watch (GstBus * bus,
GstBusHandler handler,
gpointer user_data);
G_END_DECLS
#endif /* __GST_BUS_H__ */

View file

@ -1,6 +1,7 @@
/* GStreamer
* Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
* 2000 Wim Taymans <wtay@chello.be>
* 2004 Wim Taymans <wim@fluendo.com>
*
* gstclock.c: Clock subsystem for maintaining time sync
*
@ -47,9 +48,6 @@ enum
static GstMemChunk *_gst_clock_entries_chunk;
void gst_clock_id_unlock (GstClockID id);
static void gst_clock_class_init (GstClockClass * klass);
static void gst_clock_init (GstClock * clock);
static void gst_clock_dispose (GObject * object);
@ -75,25 +73,86 @@ gst_clock_entry_new (GstClock * clock, GstClockTime time,
#ifndef GST_DISABLE_TRACE
gst_alloc_trace_new (_gst_clock_entry_trace, entry);
#endif
GST_CAT_DEBUG (GST_CAT_CLOCK, "created entry %p", entry);
gst_atomic_int_init (&entry->refcount, 1);
entry->clock = clock;
entry->time = time;
entry->interval = interval;
entry->type = type;
entry->status = GST_CLOCK_ENTRY_OK;
entry->status = GST_CLOCK_BUSY;
return (GstClockID) entry;
}
/**
* gst_clock_id_ref:
* @id: The clockid to ref
*
* Increase the refcount of the given clockid.
*
* Returns: The same #GstClockID with increased refcount.
*
* MT safe.
*/
GstClockID
gst_clock_id_ref (GstClockID id)
{
g_return_val_if_fail (id != NULL, NULL);
gst_atomic_int_inc (&((GstClockEntry *) id)->refcount);
return id;
}
static void
_gst_clock_id_free (GstClockID id)
{
g_return_if_fail (id != NULL);
GST_CAT_DEBUG (GST_CAT_CLOCK, "freed entry %p", id);
#ifndef GST_DISABLE_TRACE
gst_alloc_trace_free (_gst_clock_entry_trace, id);
#endif
gst_mem_chunk_free (_gst_clock_entries_chunk, id);
}
/**
* gst_clock_id_unref:
* @id: The clockid to unref
*
* Unref the given clockid. When the refcount reaches 0 the
* #GstClockID will be freed.
*
* MT safe.
*/
void
gst_clock_id_unref (GstClockID id)
{
gint zero;
g_return_if_fail (id != NULL);
zero = gst_atomic_int_dec_and_test (&((GstClockEntry *) id)->refcount);
/* if we ended up with the refcount at zero, free the id */
if (zero) {
_gst_clock_id_free (id);
}
}
/**
* gst_clock_new_single_shot_id
* @clock: The clockid to get a single shot notification from
* @time: the requested time
*
* Get an ID from the given clock to trigger a single shot
* notification at the requested time.
* notification at the requested time. The single shot id should be
* unreffed after usage.
*
* Returns: An id that can be used to request the time notification.
*
* MT safe.
*/
GstClockID
gst_clock_new_single_shot_id (GstClock * clock, GstClockTime time)
@ -112,9 +171,12 @@ gst_clock_new_single_shot_id (GstClock * clock, GstClockTime time)
*
* Get an ID from the given clock to trigger a periodic notification.
* The periodeic notifications will be start at time start_time and
* will then be fired with the given interval.
* will then be fired with the given interval. The id should be unreffed
* after usage.
*
* Returns: An id that can be used to request the time notification.
*
* MT safe.
*/
GstClockID
gst_clock_new_periodic_id (GstClock * clock, GstClockTime start_time,
@ -128,13 +190,38 @@ gst_clock_new_periodic_id (GstClock * clock, GstClockTime start_time,
start_time, interval, GST_CLOCK_ENTRY_PERIODIC);
}
/**
* gst_clock_id_compare_func
* @id1: A clockid
* @id2: A clockid to compare with
*
* Compares the two GstClockID instances. This function can be used
* as a GCompareFunc when sorting ids.
*
* Returns: negative value if a < b; zero if a = b; positive value if a > b
*
* MT safe.
*/
gint
gst_clock_id_compare_func (gconstpointer id1, gconstpointer id2)
{
GstClockEntry *entry1, *entry2;
entry1 = (GstClockEntry *) id1;
entry2 = (GstClockEntry *) id2;
return GST_CLOCK_ENTRY_TIME (entry1) - GST_CLOCK_ENTRY_TIME (entry2);
}
/**
* gst_clock_id_get_time
* @id: The clockid to query
*
* Get the time of the clock ID
*
* Returns: the time of the given clock id
* Returns: the time of the given clock id.
*
* MT safe.
*/
GstClockTime
gst_clock_id_get_time (GstClockID id)
@ -151,16 +238,18 @@ gst_clock_id_get_time (GstClockID id)
* @jitter: A pointer that will contain the jitter
*
* Perform a blocking wait on the given ID. The jitter arg can be
* NULL
* NULL.
*
* Returns: the result of the blocking wait.
*
* MT safe.
*/
GstClockReturn
gst_clock_id_wait (GstClockID id, GstClockTimeDiff * jitter)
{
GstClockEntry *entry;
GstClock *clock;
GstClockReturn res = GST_CLOCK_UNSUPPORTED;
GstClockReturn res;
GstClockTime requested;
GstClockClass *cclass;
@ -169,43 +258,38 @@ gst_clock_id_wait (GstClockID id, GstClockTimeDiff * jitter)
entry = (GstClockEntry *) id;
requested = GST_CLOCK_ENTRY_TIME (entry);
if (!GST_CLOCK_TIME_IS_VALID (requested)) {
GST_CAT_DEBUG (GST_CAT_CLOCK, "invalid time requested, returning _TIMEOUT");
return GST_CLOCK_TIMEOUT;
}
if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (requested)))
goto invalid_time;
clock = GST_CLOCK_ENTRY_CLOCK (entry);
cclass = GST_CLOCK_GET_CLASS (clock);
if (cclass->wait) {
GstClockTime now;
if (G_LIKELY (cclass->wait)) {
GST_LOCK (clock);
clock->entries = g_list_prepend (clock->entries, entry);
GST_UNLOCK (clock);
GST_CAT_DEBUG (GST_CAT_CLOCK, "waiting on clock");
do {
res = cclass->wait (clock, entry);
}
while (res == GST_CLOCK_ENTRY_RESTART);
GST_CAT_DEBUG (GST_CAT_CLOCK, "done waiting");
GST_LOCK (clock);
clock->entries = g_list_remove (clock->entries, entry);
GST_UNLOCK (clock);
GST_CAT_DEBUG (GST_CAT_CLOCK, "waiting on clock entry %p", id);
res = cclass->wait (clock, entry);
GST_CAT_DEBUG (GST_CAT_CLOCK, "done waiting entry %p", id);
if (jitter) {
now = gst_clock_get_time (clock);
GstClockTime now = gst_clock_get_time (clock);
*jitter = now - requested;
}
if (clock->stats) {
gst_clock_update_stats (clock);
}
} else {
res = GST_CLOCK_UNSUPPORTED;
}
return res;
/* ERRORS */
invalid_time:
{
GST_CAT_DEBUG (GST_CAT_CLOCK, "invalid time requested, returning _BADTIME");
return GST_CLOCK_BADTIME;
}
}
/**
@ -215,9 +299,14 @@ gst_clock_id_wait (GstClockID id, GstClockTimeDiff * jitter)
* @user_data: User data passed in the calback
*
* Register a callback on the given clockid with the given
* function and user_data.
* function and user_data. When passing an id with an invalid
* time to this function, the callback will be called immediatly
* with a time set to GST_CLOCK_TIME_NONE. The callback will
* be called when the time of the id has been reached.
*
* Returns: the result of the non blocking wait.
*
* MT safe.
*/
GstClockReturn
gst_clock_id_wait_async (GstClockID id,
@ -225,19 +314,19 @@ gst_clock_id_wait_async (GstClockID id,
{
GstClockEntry *entry;
GstClock *clock;
GstClockReturn res = GST_CLOCK_UNSUPPORTED;
GstClockReturn res;
GstClockClass *cclass;
GstClockTime requested;
g_return_val_if_fail (id != NULL, GST_CLOCK_ERROR);
g_return_val_if_fail (func != NULL, GST_CLOCK_ERROR);
entry = (GstClockEntry *) id;
clock = entry->clock;
requested = GST_CLOCK_ENTRY_TIME (entry);
clock = GST_CLOCK_ENTRY_CLOCK (entry);
if (!GST_CLOCK_TIME_IS_VALID (GST_CLOCK_ENTRY_TIME (entry))) {
(func) (clock, GST_CLOCK_TIME_NONE, id, user_data);
return GST_CLOCK_TIMEOUT;
}
if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (requested)))
goto invalid_time;
cclass = GST_CLOCK_GET_CLASS (clock);
@ -246,26 +335,28 @@ gst_clock_id_wait_async (GstClockID id,
entry->user_data = user_data;
res = cclass->wait_async (clock, entry);
} else {
res = GST_CLOCK_UNSUPPORTED;
}
return res;
}
#if 0
static void
gst_clock_reschedule_func (GstClockEntry * entry)
{
entry->status = GST_CLOCK_ENTRY_OK;
gst_clock_id_unlock ((GstClockID) entry);
/* ERRORS */
invalid_time:
{
(func) (clock, GST_CLOCK_TIME_NONE, id, user_data);
GST_CAT_DEBUG (GST_CAT_CLOCK, "invalid time requested, returning _BADTIME");
return GST_CLOCK_BADTIME;
}
}
#endif
/**
* gst_clock_id_unschedule:
* @id: The id to unschedule
*
* Cancel an outstanding async notification request with the given ID.
* Cancel an outstanding request with the given ID. This can either
* be an outstanding async notification or a pending sync notification.
*
* MT safe.
*/
void
gst_clock_id_unschedule (GstClockID id)
@ -285,47 +376,6 @@ gst_clock_id_unschedule (GstClockID id)
cclass->unschedule (clock, entry);
}
/**
* gst_clock_id_free:
* @id: The clockid to free
*
* Free the resources held by the given id
*/
void
gst_clock_id_free (GstClockID id)
{
g_return_if_fail (id != NULL);
#ifndef GST_DISABLE_TRACE
gst_alloc_trace_free (_gst_clock_entry_trace, id);
#endif
gst_mem_chunk_free (_gst_clock_entries_chunk, id);
}
/**
* gst_clock_id_unlock:
* @id: The clockid to unlock
*
* Unlock the givan ClockID.
*/
void
gst_clock_id_unlock (GstClockID id)
{
GstClockEntry *entry;
GstClock *clock;
GstClockClass *cclass;
g_return_if_fail (id != NULL);
entry = (GstClockEntry *) id;
clock = entry->clock;
cclass = GST_CLOCK_GET_CLASS (clock);
if (cclass->unlock)
cclass->unlock (clock, entry);
}
/**
* GstClock abstract base class implementation
@ -384,15 +434,6 @@ gst_clock_class_init (GstClockClass * klass)
g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_STATS,
g_param_spec_boolean ("stats", "Stats", "Enable clock stats",
FALSE, G_PARAM_READWRITE));
g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_MAX_DIFF,
g_param_spec_int64 ("max-diff", "Max diff",
"The maximum amount of time to wait in nanoseconds", 0, G_MAXINT64,
DEFAULT_MAX_DIFF, G_PARAM_READWRITE));
g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_EVENT_DIFF,
g_param_spec_uint64 ("event-diff", "event diff",
"The amount of time that may elapse until 2 events are treated as happening at different times",
0, G_MAXUINT64, DEFAULT_EVENT_DIFF,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
}
static void
@ -401,6 +442,7 @@ gst_clock_init (GstClock * clock)
clock->adjust = 0;
clock->last_time = 0;
clock->entries = NULL;
clock->entries_changed = g_cond_new ();
clock->flags = 0;
clock->stats = FALSE;
}
@ -408,7 +450,9 @@ gst_clock_init (GstClock * clock)
static void
gst_clock_dispose (GObject * object)
{
//GstClock *clock = GST_CLOCK (object);
GstClock *clock = GST_CLOCK (object);
g_cond_free (clock->entries_changed);
G_OBJECT_CLASS (parent_class)->dispose (object);
}
@ -469,27 +513,36 @@ gst_clock_get_resolution (GstClock * clock)
* Gets the current time of the given clock. The time is always
* monotonically increasing.
*
* Returns: the time of the clock.
* Returns: the time of the clock. Or GST_CLOCK_TIME_NONE when
* giving wrong input.
*
* MT safe.
*/
GstClockTime
gst_clock_get_time (GstClock * clock)
{
GstClockTime ret = G_GINT64_CONSTANT (0);
GstClockTime ret;
GstClockClass *cclass;
g_return_val_if_fail (GST_IS_CLOCK (clock), G_GINT64_CONSTANT (0));
g_return_val_if_fail (GST_IS_CLOCK (clock), GST_CLOCK_TIME_NONE);
cclass = GST_CLOCK_GET_CLASS (clock);
if (cclass->get_internal_time) {
ret = cclass->get_internal_time (clock) + clock->adjust;
ret = cclass->get_internal_time (clock);
} else {
ret = G_GINT64_CONSTANT (0);
}
/* make sure the time is increasing, else return last_time */
GST_LOCK (clock);
ret += clock->adjust;
if ((gint64) ret < (gint64) clock->last_time) {
ret = clock->last_time;
} else {
clock->last_time = ret;
}
GST_UNLOCK (clock);
return ret;
}
@ -504,36 +557,17 @@ gst_clock_get_time (GstClock * clock)
* moves it backwards. Note that _get_time() always returns
* increasing values so when you move the clock backwards, _get_time()
* will report the previous value until the clock catches up.
*
* MT safe.
*/
void
gst_clock_set_time_adjust (GstClock * clock, GstClockTime adjust)
{
g_return_if_fail (GST_IS_CLOCK (clock));
clock->adjust = adjust;
}
/**
* gst_clock_get_next_id
* @clock: The clock to query
*
* Get the clockid of the next event.
*
* Returns: a clockid or NULL is no event is pending.
*/
GstClockID
gst_clock_get_next_id (GstClock * clock)
{
GstClockEntry *entry = NULL;
g_return_val_if_fail (GST_IS_CLOCK (clock), NULL);
GST_LOCK (clock);
if (clock->entries)
entry = GST_CLOCK_ENTRY (clock->entries->data);
clock->adjust = adjust;
GST_UNLOCK (clock);
return (GstClockID *) entry;
}
static void
@ -554,10 +588,6 @@ gst_clock_set_property (GObject * object, guint prop_id,
clock->stats = g_value_get_boolean (value);
g_object_notify (object, "stats");
break;
case ARG_MAX_DIFF:
break;
case ARG_EVENT_DIFF:
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@ -576,10 +606,6 @@ gst_clock_get_property (GObject * object, guint prop_id,
case ARG_STATS:
g_value_set_boolean (value, clock->stats);
break;
case ARG_MAX_DIFF:
break;
case ARG_EVENT_DIFF:
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;

View file

@ -81,15 +81,18 @@ typedef struct _GstClockClass GstClockClass;
typedef gboolean (*GstClockCallback) (GstClock *clock, GstClockTime time,
GstClockID id, gpointer user_data);
typedef enum {
/*< protected >*/
GST_CLOCK_ENTRY_OK,
GST_CLOCK_ENTRY_EARLY,
GST_CLOCK_ENTRY_RESTART
} GstClockEntryStatus;
typedef enum
{
GST_CLOCK_OK = 0,
GST_CLOCK_EARLY = 1,
GST_CLOCK_UNSCHEDULED = 2,
GST_CLOCK_BUSY = 3,
GST_CLOCK_BADTIME = 4,
GST_CLOCK_ERROR = 5,
GST_CLOCK_UNSUPPORTED = 6,
} GstClockReturn;
typedef enum {
/*< protected >*/
GST_CLOCK_ENTRY_SINGLE,
GST_CLOCK_ENTRY_PERIODIC
} GstClockEntryType;
@ -102,26 +105,17 @@ typedef enum {
#define GST_CLOCK_ENTRY_STATUS(entry) ((entry)->status)
struct _GstClockEntry {
GstAtomicInt refcount;
/*< protected >*/
GstClock *clock;
GstClockEntryType type;
GstClockTime time;
GstClockTime interval;
GstClockEntryStatus status;
GstClockReturn status;
GstClockCallback func;
gpointer user_data;
};
typedef enum
{
GST_CLOCK_UNSCHEDULED = 0,
GST_CLOCK_TIMEOUT = 1,
GST_CLOCK_EARLY = 2,
GST_CLOCK_ERROR = 3,
GST_CLOCK_UNSUPPORTED = 4,
GST_CLOCK_OK = 5
} GstClockReturn;
typedef enum
{
GST_CLOCK_FLAG_CAN_DO_SINGLE_SYNC = (1 << 1),
@ -129,11 +123,15 @@ typedef enum
GST_CLOCK_FLAG_CAN_DO_PERIODIC_SYNC = (1 << 3),
GST_CLOCK_FLAG_CAN_DO_PERIODIC_ASYNC = (1 << 4),
GST_CLOCK_FLAG_CAN_SET_RESOLUTION = (1 << 5),
GST_CLOCK_FLAG_CAN_SET_SPEED = (1 << 6)
} GstClockFlags;
#define GST_CLOCK_FLAGS(clock) (GST_CLOCK(clock)->flags)
#define GST_CLOCK_COND(clock) (GST_CLOCK_CAST(clock)->entries_changed)
#define GST_CLOCK_WAIT(clock) g_cond_wait(GST_CLOCK_COND(clock),GST_GET_LOCK(clock))
#define GST_CLOCK_TIMED_WAIT(clock,tv) g_cond_timed_wait(GST_CLOCK_COND(clock),GST_GET_LOCK(clock),tv)
#define GST_CLOCK_SIGNAL(clock) g_cond_broadcast(GST_CLOCK_COND(clock))
struct _GstClock {
GstObject object;
@ -144,6 +142,7 @@ struct _GstClock {
GstClockTime adjust;
GstClockTime last_time;
GList *entries;
GCond *entries_changed;
/*< private >*/
guint64 resolution;
@ -164,10 +163,9 @@ struct _GstClockClass {
GstClockTime (*get_internal_time) (GstClock *clock);
/* waiting on an ID */
GstClockEntryStatus (*wait) (GstClock *clock, GstClockEntry *entry);
GstClockEntryStatus (*wait_async) (GstClock *clock, GstClockEntry *entry);
GstClockReturn (*wait) (GstClock *clock, GstClockEntry *entry);
GstClockReturn (*wait_async) (GstClock *clock, GstClockEntry *entry);
void (*unschedule) (GstClock *clock, GstClockEntry *entry);
void (*unlock) (GstClock *clock, GstClockEntry *entry);
/*< private >*/
gpointer _gst_reserved[GST_PADDING];
@ -188,9 +186,14 @@ GstClockID gst_clock_new_single_shot_id (GstClock *clock,
GstClockID gst_clock_new_periodic_id (GstClock *clock,
GstClockTime start_time,
GstClockTime interval);
GstClockID gst_clock_get_next_id (GstClock *clock);
/* reference counting */
GstClockID gst_clock_id_ref (GstClockID id);
void gst_clock_id_unref (GstClockID id);
/* operations on IDs */
gint gst_clock_id_compare_func (gconstpointer id1, gconstpointer id2);
GstClockTime gst_clock_id_get_time (GstClockID id);
GstClockReturn gst_clock_id_wait (GstClockID id,
GstClockTimeDiff *jitter);
@ -198,8 +201,6 @@ GstClockReturn gst_clock_id_wait_async (GstClockID id,
GstClockCallback func,
gpointer user_data);
void gst_clock_id_unschedule (GstClockID id);
void gst_clock_id_unlock (GstClockID id);
void gst_clock_id_free (GstClockID id);
G_END_DECLS

View file

@ -435,34 +435,29 @@ gst_element_get_index (GstElement * element)
gboolean
gst_element_add_pad (GstElement * element, GstPad * pad)
{
GstElement *old_parent;
gchar *pad_name;
g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE);
g_return_val_if_fail (GST_IS_PAD (pad), FALSE);
/* first check to make sure the pad hasn't already been added to another
* element */
old_parent = gst_pad_get_parent (pad);
if (G_UNLIKELY (old_parent != NULL))
goto had_parent;
GST_LOCK (element);
/* locking pad to look at the name */
GST_LOCK (pad);
/* then check to see if there's already a pad by that name here */
pad_name = g_strdup (GST_PAD_NAME (pad));
GST_UNLOCK (pad);
if (G_UNLIKELY (!gst_object_check_uniqueness (element->pads,
GST_PAD_NAME (pad))))
/* then check to see if there's already a pad by that name here */
GST_LOCK (element);
if (G_UNLIKELY (!gst_object_check_uniqueness (element->pads, pad_name)))
goto name_exists;
/* try to set the pad's parent */
if (G_UNLIKELY (!gst_object_set_parent (GST_OBJECT_CAST (pad),
GST_OBJECT_CAST (element))))
goto had_parent;
GST_CAT_INFO_OBJECT (GST_CAT_ELEMENT_PADS, element, "adding pad '%s'",
GST_STR_NULL (GST_PAD_NAME (pad)));
GST_UNLOCK (pad);
/* set the pad's parent */
gst_object_set_parent (GST_OBJECT_CAST (pad), GST_OBJECT_CAST (element));
/* add it to the list */
switch (gst_pad_get_direction (pad)) {
case GST_PAD_SRC:
@ -474,7 +469,7 @@ gst_element_add_pad (GstElement * element, GstPad * pad)
element->numsinkpads++;
break;
default:
g_assert_not_reached ();
/* can happen for ghost pads */
break;
}
element->pads = g_list_prepend (element->pads, pad);
@ -494,29 +489,23 @@ gst_element_add_pad (GstElement * element, GstPad * pad)
return TRUE;
had_parent:
name_exists:
{
gchar *parent_name = gst_element_get_name (old_parent);
gst_object_unref (GST_OBJECT (old_parent));
GST_LOCK (pad);
GST_LOCK (element);
g_critical
("Padname %s:%s already has parent when trying to add to element %s",
parent_name, GST_PAD_NAME (pad), GST_ELEMENT_NAME (element));
g_critical ("Padname %s is not unique in element %s, not adding",
pad_name, GST_ELEMENT_NAME (element));
GST_UNLOCK (element);
GST_UNLOCK (pad);
g_free (parent_name);
g_free (pad_name);
return FALSE;
}
had_parent:
{
g_critical
("Pad %s already has parent when trying to add to element %s",
pad_name, GST_ELEMENT_NAME (element));
GST_UNLOCK (element);
g_free (pad_name);
return FALSE;
}
name_exists:
g_critical ("Padname %s is not unique in element %s, not adding\n",
GST_PAD_NAME (pad), GST_ELEMENT_NAME (element));
GST_UNLOCK (pad);
GST_UNLOCK (element);
return FALSE;
}
/**
@ -578,6 +567,8 @@ gst_element_remove_pad (GstElement * element, GstPad * pad)
if (G_UNLIKELY (current_parent != element))
goto not_our_pad;
gst_object_unref (GST_OBJECT_CAST (current_parent));
/* FIXME, is this redundant with pad disposal? */
if (GST_IS_REAL_PAD (pad)) {
GstPad *peer = gst_pad_get_peer (pad);
@ -591,6 +582,7 @@ gst_element_remove_pad (GstElement * element, GstPad * pad)
gst_pad_unlink (pad, GST_PAD_CAST (peer));
else
gst_pad_unlink (GST_PAD_CAST (peer), pad);
gst_object_unref (GST_OBJECT (peer));
}
} else if (GST_IS_GHOST_PAD (pad)) {
@ -836,6 +828,8 @@ gst_element_get_pad (GstElement * element, const gchar * name)
* Retrieves an iterattor of @element's pads.
*
* Returns: the #GstIterator of pads.
*
* MT safe.
*/
GstIterator *
gst_element_iterate_pads (GstElement * element)
@ -1171,6 +1165,8 @@ gst_element_get_query_types (GstElement * element)
* forwards the query to a random usable sinkpad of this element.
*
* Returns: TRUE if the query could be performed.
*
* MT safe.
*/
gboolean
gst_element_query (GstElement * element, GstQueryType type,
@ -1220,6 +1216,8 @@ gst_element_query (GstElement * element, GstQueryType type,
* the query will be forwarded to a random sink pad.
*
* Returns: An array of #GstFormat elements.
*
* MT safe.
*/
const GstFormat *
gst_element_get_formats (GstElement * element)
@ -1264,6 +1262,8 @@ gst_element_get_formats (GstElement * element)
* the query will be forwarded to a random sink pad.
*
* Returns: TRUE if the conversion could be performed.
*
* MT safe.
*/
gboolean
gst_element_convert (GstElement * element,
@ -1330,7 +1330,7 @@ gst_element_post_message (GstElement * element, GstMessage * message)
GST_LOCK (element);
bus = element->bus;
if (bus == NULL) {
if (G_UNLIKELY (bus == NULL)) {
GST_DEBUG ("... but I won't because I have no bus");
GST_UNLOCK (element);
gst_data_unref (GST_DATA (message));
@ -1499,7 +1499,7 @@ gst_element_set_locked_state (GstElement * element, gboolean locked_state)
g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE);
GST_LOCK (element);
old = !!GST_FLAG_IS_SET (element, GST_ELEMENT_LOCKED_STATE);
old = GST_FLAG_IS_SET (element, GST_ELEMENT_LOCKED_STATE);
if (G_UNLIKELY (old == locked_state))
goto was_ok;
@ -1546,6 +1546,7 @@ gst_element_sync_state_with_parent (GstElement * element)
gst_element_state_get_name (GST_STATE (element)),
GST_ELEMENT_NAME (parent),
gst_element_state_get_name (GST_STATE (parent)));
if (gst_element_set_state (element, GST_STATE (parent)) == GST_STATE_FAILURE) {
return FALSE;
}
@ -1565,6 +1566,7 @@ gst_element_get_state_func (GstElement * element,
GST_STATE_LOCK (element);
old_pending = GST_STATE_PENDING (element);
if (old_pending != GST_STATE_VOID_PENDING) {
/* we have a pending state change, wait for it to complete */
if (!GST_STATE_TIMED_WAIT (element, timeout)) {
/* timeout triggered */
if (state)
@ -1578,7 +1580,7 @@ gst_element_get_state_func (GstElement * element,
}
}
/* if nothing is pending anymore we can return TRUE and
* set the values of the current and panding state */
* set the values of the current and pending state */
if (GST_STATE_PENDING (element) == GST_STATE_VOID_PENDING) {
if (state)
*state = GST_STATE (element);
@ -1654,7 +1656,7 @@ gst_element_abort_state (GstElement * element)
GST_STATE_PENDING (element) = GST_STATE_VOID_PENDING;
g_cond_broadcast (GST_STATE_GET_COND (element));
GST_STATE_BROADCAST (element);
}
}
@ -1690,7 +1692,7 @@ gst_element_commit_state (GstElement * element)
g_signal_emit (G_OBJECT (element), gst_element_signals[STATE_CHANGE],
0, old_state, pending);
g_cond_broadcast (GST_STATE_GET_COND (element));
GST_STATE_BROADCAST (element);
}
}
@ -1798,7 +1800,13 @@ exit:
return return_val;
}
/* is called with STATE_LOCK */
/* is called with STATE_LOCK
*
* This function activates the pads of a given element.
*
* TODO: activates pads from src to sinks?
*
* */
static gboolean
gst_element_pads_activate (GstElement * element, gboolean active)
{
@ -1817,11 +1825,14 @@ restart:
gst_object_ref (GST_OBJECT (pad));
GST_UNLOCK (element);
/* we only care about real pads */
if (GST_IS_REAL_PAD (pad)) {
GstRealPad *peer;
gboolean pad_loop;
gboolean delay = FALSE;
/* see if the pad has a loop function and grab
* the peer */
GST_LOCK (pad);
pad_loop = GST_RPAD_LOOPFUNC (pad) != NULL;
peer = GST_RPAD_PEER (pad);
@ -1832,13 +1843,18 @@ restart:
if (peer) {
gboolean peer_loop;
/* see if the peer has a loop function */
peer_loop = GST_RPAD_LOOPFUNC (peer) != NULL;
/* sinkpads with a loop function are delayed since they
* need the srcpad to be active first */
if (GST_PAD_IS_SINK (pad) && pad_loop) {
GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element,
"delaying pad %s", GST_OBJECT_NAME (pad));
delay = TRUE;
} else if (GST_PAD_IS_SRC (pad) && peer_loop) {
/* If the pad is a source and the peer has a loop function,
* we can activate the srcpad and then the loopbased sinkpad */
GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element,
"%sactivating pad %s", (active ? "" : "(de)"),
GST_OBJECT_NAME (pad));
@ -1846,15 +1862,19 @@ restart:
(active ? GST_ACTIVATE_PULL : GST_ACTIVATE_NONE));
GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element,
"activating delayed pad %s", GST_OBJECT_NAME (peer));
"%sactivating delayed pad %s", (active ? "" : "(de)"),
GST_OBJECT_NAME (peer));
result &= gst_pad_set_active (GST_PAD (peer),
(active ? GST_ACTIVATE_PULL : GST_ACTIVATE_NONE));
/* set flag here since we don't want the code below to activate
* the pad again */
delay = TRUE;
}
gst_object_unref (GST_OBJECT (peer));
}
/* all other conditions are just push based pads */
if (!delay) {
GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element,
"%sactivating pad %s", (active ? "" : "(de)"),
@ -2307,11 +2327,12 @@ gst_element_get_scheduler (GstElement * element)
/**
* gst_element_create_task:
* @element: a #GstElement to get the manager of.
* @element: a #GstElement to create the task for.
* @func: the taskfunction to run
* @data: user data passed to the taskfunction.
*
* Creates a new GstTask.
* Creates a new GstTask. This function uses the current manager of
* the element to instantiate a new task.
*
* Returns: the newly created #GstTask.
*

View file

@ -161,6 +161,8 @@ G_STMT_START { \
#define GST_STATE_WAIT(elem) g_cond_wait (GST_STATE_GET_COND (elem), GST_STATE_GET_LOCK (elem))
#define GST_STATE_TIMED_WAIT(elem, timeval) g_cond_timed_wait (GST_STATE_GET_COND (elem), GST_STATE_GET_LOCK (elem),\
timeval)
#define GST_STATE_SIGNAL(elem) g_cond_signal (GST_STATE_GET_COND (elem));
#define GST_STATE_BROADCAST(elem) g_cond_broadcast (GST_STATE_GET_COND (elem));
typedef struct _GstElementFactory GstElementFactory;
typedef struct _GstElementFactoryClass GstElementFactoryClass;

View file

@ -1,6 +1,7 @@
/* GStreamer
* Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
* 2000 Wim Taymans <wim.taymans@chello.be>
* 2005 Wim Taymans <wim@fluendo.com>
*
* gstformat.c: GstFormat registration
*
@ -25,10 +26,11 @@
#include "gst_private.h"
#include "gstformat.h"
static GStaticMutex mutex = G_STATIC_MUTEX_INIT;
static GList *_gst_formats = NULL;
static GHashTable *_nick_to_format = NULL;
static GHashTable *_format_to_nick = NULL;
static gint _n_values = 1; /* we start from 1 because 0 reserved for UNDEFINED */
static guint32 _n_values = 1; /* we start from 1 because 0 reserved for UNDEFINED */
static GstFormatDefinition standard_definitions[] = {
{GST_FORMAT_DEFAULT, "default", "Default format for the media type"},
@ -44,6 +46,7 @@ _gst_format_initialize (void)
{
GstFormatDefinition *standards = standard_definitions;
g_static_mutex_lock (&mutex);
if (_nick_to_format == NULL) {
_nick_to_format = g_hash_table_new (g_str_hash, g_str_equal);
_format_to_nick = g_hash_table_new (NULL, NULL);
@ -58,6 +61,7 @@ _gst_format_initialize (void)
standards++;
_n_values++;
}
g_static_mutex_unlock (&mutex);
}
/**
@ -66,10 +70,12 @@ _gst_format_initialize (void)
* @description: The description of the new format
*
* Create a new GstFormat based on the nick or return an
* allrady registered format with that nick
* already registered format with that nick.
*
* Returns: A new GstFormat or an already registered format
* with the same nick.
*
* MT safe.
*/
GstFormat
gst_format_register (const gchar * nick, const gchar * description)
@ -89,11 +95,13 @@ gst_format_register (const gchar * nick, const gchar * description)
format->nick = g_strdup (nick);
format->description = g_strdup (description);
g_static_mutex_lock (&mutex);
g_hash_table_insert (_nick_to_format, format->nick, format);
g_hash_table_insert (_format_to_nick, GINT_TO_POINTER (format->value),
format);
_gst_formats = g_list_append (_gst_formats, format);
_n_values++;
g_static_mutex_unlock (&mutex);
return format->value;
}
@ -114,7 +122,9 @@ gst_format_get_by_nick (const gchar * nick)
g_return_val_if_fail (nick != NULL, 0);
g_static_mutex_lock (&mutex);
format = g_hash_table_lookup (_nick_to_format, nick);
g_static_mutex_unlock (&mutex);
if (format != NULL)
return format->value;
@ -154,22 +164,38 @@ gst_formats_contains (const GstFormat * formats, GstFormat format)
* Get details about the given format.
*
* Returns: The #GstFormatDefinition for @format or NULL on failure.
*
* MT safe.
*/
const GstFormatDefinition *
gst_format_get_details (GstFormat format)
{
return g_hash_table_lookup (_format_to_nick, GINT_TO_POINTER (format));
const GstFormatDefinition *result;
g_static_mutex_lock (&mutex);
result = g_hash_table_lookup (_format_to_nick, GINT_TO_POINTER (format));
g_static_mutex_unlock (&mutex);
return result;
}
/**
* gst_format_get_definitions:
* gst_format_iterate_definitions:
*
* Get a list of all the registered formats.
* Iterate all the registered formats. The format definition is read
* only.
*
* Returns: A GList of #GstFormatDefinition.
* Returns: A GstIterator of #GstFormatDefinition.
*/
const GList *
gst_format_get_definitions (void)
GstIterator *
gst_format_iterate_definitions (void)
{
return _gst_formats;
GstIterator *result;
g_static_mutex_lock (&mutex);
result = gst_iterator_new_list (g_static_mutex_get_mutex (&mutex),
&_n_values, &_gst_formats, NULL, NULL, NULL, NULL);
g_static_mutex_unlock (&mutex);
return result;
}

View file

@ -27,6 +27,8 @@
#include <glib.h>
#include <gst/gstiterator.h>
G_BEGIN_DECLS
typedef enum {
@ -88,8 +90,7 @@ gboolean gst_formats_contains (const GstFormat *formats, GstFormat format);
/* query for format details */
G_CONST_RETURN GstFormatDefinition*
gst_format_get_details (GstFormat format);
G_CONST_RETURN GList*
gst_format_get_definitions (void);
GstIterator* gst_format_iterate_definitions (void);
G_END_DECLS

View file

@ -1,7 +1,7 @@
/* GStreamer
* Copyright (C) 2004 Wim Taymans <wim@fluendo.com>
*
* gstiterator.h: Base class for iterating lists.
* gstiterator.h: Base class for iterating datastructures.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
@ -37,6 +37,22 @@ gst_iterator_init (GstIterator * it,
it->free = free;
}
/**
* gst_iterator_new:
* @size: the size of the iterator structure
* @lock: pointer to a #GMutex.
* @master_cookie: pointer to a guint32 to protect the iterated object.
* @next: function to get next item
* @resync: function to resync the iterator
* @free: function to free the iterator
*
* Create a new iterator. This function is mainly used for objects
* implementing the next/resync/free function to iterate a data structure.
*
* Returns: the new #GstIterator.
*
* MT safe.
*/
GstIterator *
gst_iterator_new (guint size,
GMutex * lock,
@ -102,6 +118,22 @@ gst_list_iterator_free (GstListIterator * it)
g_free (it);
}
/**
* gst_iterator_new_list:
* @lock: pointer to a #GMutex protecting the list.
* @master_cookie: pointer to a guint32 to protect the list.
* @list: pointer to the list
* @owner: object owning the list
* @ref: function to ref each item
* @unref: function to unref each item
* @free: function to free the owner of the list
*
* Create a new iterator designed for iterating @list.
*
* Returns: the new #GstIterator for @list.
*
* MT safe.
*/
GstIterator *
gst_iterator_new_list (GMutex * lock,
guint32 * master_cookie,
@ -130,6 +162,17 @@ gst_iterator_new_list (GMutex * lock,
return GST_ITERATOR (result);
}
/**
* gst_iterator_next:
* @it: The #GstIterator to iterate
* @elem: pointer to hold next element
*
* Get the next item from the iterator.
*
* Returns: The result of the iteration.
*
* MT safe.
*/
GstIteratorResult
gst_iterator_next (GstIterator * it, gpointer * elem)
{
@ -138,9 +181,9 @@ gst_iterator_next (GstIterator * it, gpointer * elem)
g_return_val_if_fail (it != NULL, GST_ITERATOR_ERROR);
g_return_val_if_fail (elem != NULL, GST_ITERATOR_ERROR);
if (it->lock)
if (G_LIKELY (it->lock))
g_mutex_lock (it->lock);
if (*it->master_cookie != it->cookie) {
if (G_UNLIKELY (*it->master_cookie != it->cookie)) {
result = GST_ITERATOR_RESYNC;
goto done;
}
@ -148,25 +191,42 @@ gst_iterator_next (GstIterator * it, gpointer * elem)
result = it->next (it, elem);
done:
if (it->lock)
if (G_LIKELY (it->lock))
g_mutex_unlock (it->lock);
return result;
}
/**
* gst_iterator_resync:
* @it: The #GstIterator to resync
*
* Resync the iterator. this function is mostly called
* after #gst_iterator_next() returned #GST_ITERATOR_RESYNC.
*
* MT safe.
*/
void
gst_iterator_resync (GstIterator * it)
{
g_return_if_fail (it != NULL);
if (it->lock)
if (G_LIKELY (it->lock))
g_mutex_lock (it->lock);
it->resync (it);
it->cookie = *it->master_cookie;
if (it->lock)
if (G_LIKELY (it->lock))
g_mutex_unlock (it->lock);
}
/**
* gst_iterator_free:
* @it: The #GstIterator to free
*
* Free the iterator.
*
* MT safe.
*/
void
gst_iterator_free (GstIterator * it)
{
@ -189,6 +249,9 @@ typedef struct _GstIteratorFilter
} GstIteratorFilter;
/* this function can iterate in 3 modes:
* filter, foreach and find_custom.
*/
static GstIteratorResult
filter_next (GstIteratorFilter * it, gpointer * elem)
{
@ -197,16 +260,16 @@ filter_next (GstIteratorFilter * it, gpointer * elem)
*elem = NULL;
if (it->found)
if (G_UNLIKELY (it->found))
return GST_ITERATOR_DONE;
while (!done) {
while (G_LIKELY (!done)) {
gpointer item;
result = gst_iterator_next (it->slave, &item);
switch (result) {
case GST_ITERATOR_OK:
if (GST_ITERATOR (it)->lock)
if (G_LIKELY (GST_ITERATOR (it)->lock))
g_mutex_unlock (GST_ITERATOR (it)->lock);
if (it->compare) {
if (it->func (item, it->user_data) == 0) {
@ -218,7 +281,7 @@ filter_next (GstIteratorFilter * it, gpointer * elem)
} else {
it->func (item, it->user_data);
}
if (GST_ITERATOR (it)->lock)
if (G_LIKELY (GST_ITERATOR (it)->lock))
g_mutex_lock (GST_ITERATOR (it)->lock);
break;
case GST_ITERATOR_RESYNC:
@ -254,6 +317,23 @@ filter_free (GstIteratorFilter * it)
g_free (it);
}
/**
* gst_iterator_filter:
* @it: The #GstIterator to filter
* @user_data: user data passed to the compare function
* @func: the compare function to select elements
*
* Create a new iterator from an existing iterator. The new iterator
* will only return those elements that match the given compare function.
* The GCompareFunc should return 0 for elements that should be included
* in the iterator.
*
* When this iterator is freed, @it will also be freed.
*
* Returns: a new #GstIterator.
*
* MT safe.
*/
GstIterator *
gst_iterator_filter (GstIterator * it, gpointer user_data, GCompareFunc func)
{
@ -278,6 +358,17 @@ gst_iterator_filter (GstIterator * it, gpointer user_data, GCompareFunc func)
return GST_ITERATOR (result);
}
/**
* gst_iterator_foreach:
* @it: The #GstIterator to iterate
* @function: the function to call for each element.
* @user_data: user data passed to the function
*
* Iterate over all element of @it and call the given function for
* each element.
*
* MT safe.
*/
void
gst_iterator_foreach (GstIterator * it, GFunc function, gpointer user_data)
{
@ -303,6 +394,20 @@ gst_iterator_foreach (GstIterator * it, GFunc function, gpointer user_data)
gst_iterator_free (GST_ITERATOR (&filter));
}
/**
* gst_iterator_find_custom:
* @it: The #GstIterator to iterate
* @user_data: user data passed to the compare function
* @func: the compare function to use
*
* Find the first element in @it that matches the compare function.
* The compare function should return 0 when the element is found.
*
* Returns: The element in the iterator that matches the compare
* function or NULL when no element matched.
*
* MT safe.
*/
gpointer
gst_iterator_find_custom (GstIterator * it, gpointer user_data,
GCompareFunc func)

View file

@ -27,10 +27,10 @@
G_BEGIN_DECLS
typedef enum {
GST_ITERATOR_DONE = 0,
GST_ITERATOR_OK = 1,
GST_ITERATOR_RESYNC = 2,
GST_ITERATOR_ERROR = 3,
GST_ITERATOR_DONE = 0, /* no more items in the iterator */
GST_ITERATOR_OK = 1, /* item retrieved */
GST_ITERATOR_RESYNC = 2, /* datastructures changed while iterating */
GST_ITERATOR_ERROR = 3, /* some error happened */
} GstIteratorResult;
typedef struct _GstIterator GstIterator;
@ -59,6 +59,7 @@ struct _GstIterator {
iterator was created */
};
/* creating iterators */
GstIterator* gst_iterator_new (guint size,
GMutex *lock,
guint32 *master_cookie,
@ -74,10 +75,12 @@ GstIterator* gst_iterator_new_list (GMutex *lock,
GstIteratorUnrefFunction unref,
GstIteratorDisposeFunction free);
/* using iterators */
GstIteratorResult gst_iterator_next (GstIterator *it, gpointer *result);
void gst_iterator_resync (GstIterator *it);
void gst_iterator_free (GstIterator *it);
/* special functions that operate on iterators */
void gst_iterator_foreach (GstIterator *it, GFunc function,
gpointer user_data);
gpointer gst_iterator_find_custom (GstIterator *it, gpointer user_data,

View file

@ -1,5 +1,9 @@
/* GStreamer
* Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
* <2005> Wim Taymans <wim@fluendo.com>
*
* gstmemchunk.c: implementation of lockfree allocation of fixed
* size memory chunks.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
@ -28,7 +32,6 @@
#include <valgrind/valgrind.h>
#endif
#define GST_MEM_CHUNK_AREA(chunk) (((GstMemChunkElement*)(chunk))->area)
#define GST_MEM_CHUNK_DATA(chunk) ((gpointer)(((GstMemChunkElement*)(chunk)) + 1))
#define GST_MEM_CHUNK_LINK(mem) ((GstMemChunkElement*)((guint8*)(mem) - sizeof (GstMemChunkElement)))
@ -105,6 +108,8 @@ populate (GstMemChunk * mem_chunk)
* when it is too small (with a small overhead when that happens)
*
* Returns: a new #GstMemChunk
*
* MT safe.
*/
GstMemChunk *
gst_mem_chunk_new (gchar * name, gint atom_size, gulong area_size, gint type)
@ -152,7 +157,9 @@ free_area (gpointer key, gpointer value, gpointer user_data)
* gst_mem_chunk_destroy:
* @mem_chunk: the GstMemChunk to destroy
*
* Free the memory allocated by the memchunk
* Free the memory allocated by the memchunk. This function
* is not Threadsafe as it does not wait for all outstanding
* allocations to be freed.
*/
void
gst_mem_chunk_destroy (GstMemChunk * mem_chunk)
@ -186,6 +193,8 @@ gst_mem_chunk_destroy (GstMemChunk * mem_chunk)
* was created.
*
* Returns: a pointer to the allocated memory region.
*
* MT safe.
*/
gpointer
gst_mem_chunk_alloc (GstMemChunk * mem_chunk)
@ -197,15 +206,20 @@ gst_mem_chunk_alloc (GstMemChunk * mem_chunk)
again:
chunk = gst_trash_stack_pop (&mem_chunk->stack);
/* chunk is empty, try to refill */
if (!chunk) {
if (populate (mem_chunk))
if (G_UNLIKELY (!chunk)) {
if (G_LIKELY (populate (mem_chunk))) {
goto again;
else
} else {
/* this happens when we are in cleanup mode and we
* allocate all remaining chunks for cleanup */
return NULL;
}
}
#ifdef HAVE_VALGRIND
VALGRIND_MALLOCLIKE_BLOCK (GST_MEM_CHUNK_DATA (chunk), mem_chunk->atom_size,
0, 0);
if (G_UNLIKELY (__gst_in_valgrind ())) {
VALGRIND_MALLOCLIKE_BLOCK (GST_MEM_CHUNK_DATA (chunk), mem_chunk->atom_size,
0, 0);
}
#endif
return GST_MEM_CHUNK_DATA (chunk);
}
@ -219,13 +233,15 @@ again:
* was created. The memory will be set to all zeroes.
*
* Returns: a pointer to the allocated memory region.
*
* MT safe.
*/
gpointer
gst_mem_chunk_alloc0 (GstMemChunk * mem_chunk)
{
gpointer mem = gst_mem_chunk_alloc (mem_chunk);
if (mem)
if (G_LIKELY (mem))
memset (mem, 0, mem_chunk->atom_size);
return mem;
@ -237,6 +253,8 @@ gst_mem_chunk_alloc0 (GstMemChunk * mem_chunk)
* @mem: the memory region to hand back to the chunk
*
* Free the memeory region allocated from the chunk.
*
* MT safe.
*/
void
gst_mem_chunk_free (GstMemChunk * mem_chunk, gpointer mem)
@ -249,7 +267,9 @@ gst_mem_chunk_free (GstMemChunk * mem_chunk, gpointer mem)
chunk = GST_MEM_CHUNK_LINK (mem);
#ifdef HAVE_VALGRIND
VALGRIND_FREELIKE_BLOCK (mem, 0);
if (G_UNLIKELY (__gst_in_valgrind ())) {
VALGRIND_FREELIKE_BLOCK (mem, 0);
}
#endif
gst_trash_stack_push (&mem_chunk->stack, chunk);
}

View file

@ -96,9 +96,9 @@ _gst_message_free (GstMessage * message)
gst_object_unref (GST_MESSAGE_SRC (message));
}
if (message->lock) {
g_mutex_lock (message->lock);
g_cond_signal (message->cond);
g_mutex_unlock (message->lock);
GST_MESSAGE_LOCK (message);
GST_MESSAGE_SIGNAL (message);
GST_MESSAGE_UNLOCK (message);
}
switch (GST_MESSAGE_TYPE (message)) {
case GST_MESSAGE_ERROR:
@ -137,6 +137,8 @@ gst_message_get_type (void)
* Allocate a new message of the given type.
*
* Returns: A new message.
*
* MT safe.
*/
GstMessage *
gst_message_new (GstMessageType type, GstObject * src)
@ -171,6 +173,8 @@ gst_message_new (GstMessageType type, GstObject * src)
* Create a new eos message.
*
* Returns: The new eos message.
*
* MT safe.
*/
GstMessage *
gst_message_new_eos (GstObject * src)
@ -188,6 +192,8 @@ gst_message_new_eos (GstObject * src)
* Create a new error message.
*
* Returns: The new error message.
*
* MT safe.
*/
GstMessage *
gst_message_new_error (GstObject * src, GError * error, gchar * debug)
@ -207,6 +213,8 @@ gst_message_new_error (GstObject * src, GError * error, gchar * debug)
* Create a new warning message.
*
* Returns: The new warning message.
*
* MT safe.
*/
GstMessage *
gst_message_new_warning (GstObject * src, GError * error, gchar * debug)
@ -226,6 +234,8 @@ gst_message_new_warning (GstObject * src, GError * error, gchar * debug)
* Create a new tag message.
*
* Returns: The new tag message.
*
* MT safe.
*/
GstMessage *
gst_message_new_tag (GstObject * src, GstTagList * tag_list)

View file

@ -49,6 +49,13 @@ typedef enum
#define GST_MESSAGE(message) ((GstMessage*)(message))
#define GST_IS_MESSAGE(message) (GST_DATA_TYPE(message) == GST_TYPE_MESSAGE)
#define GST_MESSAGE_GET_LOCK(message) (GST_MESSAGE(message)->lock)
#define GST_MESSAGE_LOCK(message) g_mutex_lock(GST_MESSAGE_GET_LOCK(message))
#define GST_MESSAGE_UNLOCK(message) g_mutex_unlock(GST_MESSAGE_GET_LOCK(message))
#define GST_MESSAGE_COND(message) (GST_MESSAGE(message)->cond)
#define GST_MESSAGE_WAIT(message) g_cond_wait(GST_MESSAGE_COND(message),GST_MESSAGE_GET_LOCK(message))
#define GST_MESSAGE_SIGNAL(message) g_cond_signal(GST_MESSAGE_COND(message))
#define GST_MESSAGE_TYPE(message) (GST_MESSAGE(message)->type)
#define GST_MESSAGE_TIMESTAMP(message) (GST_MESSAGE(message)->timestamp)
#define GST_MESSAGE_SRC(message) (GST_MESSAGE(message)->src)
@ -94,10 +101,10 @@ struct _GstMessage
gpointer _gst_reserved[GST_PADDING];
};
void _gst_message_initialize (void);
void _gst_message_initialize (void);
GType gst_message_get_type (void);
GstMessage *gst_message_new (GstMessageType type, GstObject * src);
GType gst_message_get_type (void);
GstMessage * gst_message_new (GstMessageType type, GstObject * src);
/* refcounting */
#define gst_message_ref(ev) GST_MESSAGE (gst_data_ref (GST_DATA (ev)))
@ -106,12 +113,10 @@ GstMessage *gst_message_new (GstMessageType type, GstObject * src);
/* copy message */
#define gst_message_copy(ev) GST_MESSAGE (gst_data_copy (GST_DATA (ev)))
GstMessage *gst_message_new_eos (GstObject * src);
GstMessage *gst_message_new_error (GstObject * src, GError * error,
gchar * debug);
GstMessage *gst_message_new_warning (GstObject * src, GError * error,
gchar * debug);
GstMessage *gst_message_new_tag (GstObject * src, GstTagList * tag_list);
GstMessage * gst_message_new_eos (GstObject * src);
GstMessage * gst_message_new_error (GstObject * src, GError * error, gchar * debug);
GstMessage * gst_message_new_warning (GstObject * src, GError * error, gchar * debug);
GstMessage * gst_message_new_tag (GstObject * src, GstTagList * tag_list);
G_END_DECLS
#endif /* __GST_MESSAGE_H__ */

View file

@ -1,6 +1,7 @@
/* GStreamer
* Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
* 2000 Wim Taymans <wtay@chello.be>
* 2005 Wim Taymans <wim@fluendo.com>
*
* gstobject.c: Fundamental class used for all of GStreamer
*
@ -33,6 +34,15 @@
#define DEBUG_REFCOUNT
#define REFCOUNT_HACK
/* Refcount hack: since glib is not threadsafe, the glib refcounter can be
* screwed up and the object can be freed unexpectedly. We use an evil hack
* to work around this problem. We set the glib refcount to a high value so
* that glib will never unref the object under realistic circumstances. Then
* we use our own atomic refcounting to do proper MT safe refcounting.
*
* A proper fix is of course to make the glib refcounting threadsafe which is
* planned.
*/
#ifdef REFCOUNT_HACK
#define PATCH_REFCOUNT(obj) ((GObject*)(obj))->ref_count = 100000;
#define PATCH_REFCOUNT1(obj) ((GObject*)(obj))->ref_count = 1;
@ -196,9 +206,7 @@ gst_object_init (GstObject * object)
object->parent = NULL;
object->name = NULL;
gst_atomic_int_init (&(object)->refcount, 1);
#ifdef REFCOUNT_HACK
PATCH_REFCOUNT (object);
#endif
object->flags = 0;
GST_FLAG_SET (object, GST_OBJECT_FLOATING);
@ -568,12 +576,13 @@ gst_object_default_deep_notify (GObject * object, GstObject * orig,
}
}
static void
static gboolean
gst_object_set_name_default (GstObject * object)
{
gint count;
gchar *name, *tmp;
const gchar *type_name;
gboolean result;
type_name = G_OBJECT_TYPE_NAME (object);
@ -599,8 +608,10 @@ gst_object_set_name_default (GstObject * object)
name = g_ascii_strdown (tmp, strlen (tmp));
g_free (tmp);
gst_object_set_name (object, name);
result = gst_object_set_name (object, name);
g_free (name);
return result;
}
/**
@ -613,32 +624,41 @@ gst_object_set_name_default (GstObject * object)
* This function makes a copy of the provided name, so the caller
* retains ownership of the name it sent.
*
* Returns: TRUE if the name could be set.
* Returns: TRUE if the name could be set. Objects that have
* a parent cannot be renamed.
*
* MT safe. This function grabs and releases the object's LOCK.
*/
gboolean
gst_object_set_name (GstObject * object, const gchar * name)
{
gboolean result;
g_return_val_if_fail (GST_IS_OBJECT (object), FALSE);
GST_LOCK (object);
/* parented objects cannot be renamed */
if (object->parent != NULL) {
GST_UNLOCK (object);
return FALSE;
}
if (G_UNLIKELY (object->parent != NULL))
goto had_parent;
if (name != NULL) {
g_free (object->name);
object->name = g_strdup (name);
GST_UNLOCK (object);
result = TRUE;
} else {
GST_UNLOCK (object);
gst_object_set_name_default (object);
result = gst_object_set_name_default (object);
}
return result;
/* error */
had_parent:
{
GST_UNLOCK (object);
return FALSE;
}
return TRUE;
}
/**
@ -677,7 +697,6 @@ gst_object_get_name (GstObject * object)
* This function makes a copy of the provided name prefix, so the caller
* retains ownership of the name prefix it sent.
*
*
* MT safe. This function grabs and releases the object's LOCK.
*/
void
@ -858,15 +877,18 @@ gst_object_check_uniqueness (GList * list, const gchar * name)
for (; list; list = g_list_next (list)) {
GstObject *child;
gboolean eq;
child = GST_OBJECT (list->data);
GST_LOCK (child);
if (strcmp (GST_OBJECT_NAME (child), name) == 0) {
GST_UNLOCK (child);
eq = strcmp (GST_OBJECT_NAME (child), name) == 0;
GST_UNLOCK (child);
if (G_UNLIKELY (eq)) {
result = FALSE;
break;
}
GST_UNLOCK (child);
}
return result;
}

View file

@ -47,6 +47,7 @@ GST_DEBUG_CATEGORY_STATIC (debug_dataflow);
}G_STMT_END
#define GST_CAT_DEFAULT GST_CAT_PADS
/* realize and pad and grab the lock of the realized pad. */
#define GST_PAD_REALIZE_AND_LOCK(pad, realpad, lost_ghostpad) \
GST_LOCK (pad); \
realpad = GST_PAD_REALIZE (pad); \
@ -336,6 +337,8 @@ gst_pad_custom_new (GType type, const gchar * name, GstPadDirection direction)
* This function makes a copy of the name so you can safely free the name.
*
* Returns: a new #GstPad, or NULL in case of an error.
*
* MT safe.
*/
GstPad *
gst_pad_new (const gchar * name, GstPadDirection direction)
@ -444,6 +447,7 @@ gst_pad_set_active (GstPad * pad, GstActivateMode mode)
old = GST_PAD_IS_ACTIVE (realpad);
/* if nothing changed, we can just exit */
if (G_UNLIKELY (old == active))
goto exit;
@ -544,7 +548,7 @@ lost_ghostpad:
* reasons stated above.
*
* Returns: TRUE if the pad could be blocked. This function can fail
* wrong parameters were passed or the pad was already in the
* if wrong parameters were passed or the pad was already in the
* requested state.
*
* MT safe.
@ -707,7 +711,7 @@ gst_pad_set_loop_function (GstPad * pad, GstPadLoopFunction loop)
* @chain: the #GstPadChainFunction to set.
*
* Sets the given chain function for the pad. The chain function is called to
* process a #GstData input buffer.
* process a #GstBuffer input buffer.
*/
void
gst_pad_set_chain_function (GstPad * pad, GstPadChainFunction chain)
@ -1511,7 +1515,9 @@ gst_pad_set_pad_template (GstPad * pad, GstPadTemplate * templ)
{
/* this function would need checks if it weren't static */
GST_LOCK (pad);
gst_object_replace ((GstObject **) & pad->padtemplate, (GstObject *) templ);
GST_UNLOCK (pad);
if (templ) {
gst_object_sink (GST_OBJECT (templ));
@ -1711,6 +1717,7 @@ not_linked_together:
}
}
/* should be called with the pad LOCK held */
static GstCaps *
gst_real_pad_get_caps_unlocked (GstRealPad * realpad)
{
@ -1784,9 +1791,12 @@ done:
filter = GST_RPAD_APPFILTER (realpad);
if (filter) {
GstCaps *temp = result;
GST_CAT_DEBUG (GST_CAT_CAPS,
"app filter %p %" GST_PTR_FORMAT, filter, filter);
result = gst_caps_intersect (result, filter);
result = gst_caps_intersect (temp, filter);
gst_caps_unref (temp);
GST_CAT_DEBUG (GST_CAT_CAPS,
"caps after intersection with app filter %p %" GST_PTR_FORMAT, result,
result);
@ -1802,6 +1812,8 @@ done:
*
* Returns: the #GstCaps of this pad. This function returns a new caps, so use
* gst_caps_unref to get rid of it.
*
* MT safe.
*/
GstCaps *
gst_pad_get_caps (GstPad * pad)
@ -1870,9 +1882,13 @@ gst_pad_peer_get_caps (GstPad * pad)
if (G_UNLIKELY (GST_RPAD_IS_IN_GETCAPS (peerpad)))
goto was_dispatching;
result = gst_pad_get_caps (GST_PAD_CAST (peerpad));
gst_object_ref (GST_OBJECT (peerpad));
GST_UNLOCK (realpad);
result = gst_pad_get_caps (GST_PAD_CAST (peerpad));
gst_object_unref (GST_OBJECT (peerpad));
return result;
lost_ghostpad:
@ -2727,8 +2743,7 @@ handle_pad_block (GstRealPad * pad)
* @pad: a source #GstPad.
* @buffer: the #GstBuffer to push.
*
* Pushes a buffer to the peer of @pad. @pad must be linked. May
* only be called by @pad's parent.
* Pushes a buffer to the peer of @pad. @pad must be linked.
*
* Returns: a #GstFlowReturn from the peer pad.
*
@ -2837,8 +2852,7 @@ no_function:
* @offset: The start offset of the buffer
* @length: The length of the buffer
*
* Pulls a buffer from the peer pad. May only be called by @pad's
* parent.
* Pulls a buffer from the peer pad. @pad must be linked.
*
* Returns: a #GstFlowReturn from the peer pad.
*
@ -2884,16 +2898,19 @@ gst_pad_pull_range (GstPad * pad, guint64 offset, guint size,
/* ERROR recovery here */
not_connected:
GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad,
"pulling range, but it was not linked");
GST_UNLOCK_RETURN (pad, GST_FLOW_NOT_CONNECTED);
{
GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad,
"pulling range, but it was not linked");
GST_UNLOCK_RETURN (pad, GST_FLOW_NOT_CONNECTED);
}
no_function:
GST_ELEMENT_ERROR (GST_PAD_PARENT (pad), CORE, PAD, (NULL),
("pullrange on pad %s:%s but the peer pad %s:%s has no getrangefunction",
GST_DEBUG_PAD_NAME (pad), GST_DEBUG_PAD_NAME (peer)));
gst_object_unref (GST_OBJECT (peer));
return GST_FLOW_ERROR;
{
GST_ELEMENT_ERROR (GST_PAD_PARENT (pad), CORE, PAD, (NULL),
("pullrange on pad %s:%s but the peer pad %s:%s has no getrangefunction",
GST_DEBUG_PAD_NAME (pad), GST_DEBUG_PAD_NAME (peer)));
gst_object_unref (GST_OBJECT (peer));
return GST_FLOW_ERROR;
}
}
/************************************************************************
@ -3477,13 +3494,10 @@ gst_pad_push_event (GstPad * pad, GstEvent * event)
/* ERROR handling */
not_linked:
result = FALSE;
goto error;
error:
GST_UNLOCK (pad);
return result;
{
GST_UNLOCK (pad);
return FALSE;
}
}
/**
@ -3538,12 +3552,12 @@ gst_pad_send_event (GstPad * pad, GstEvent * event)
/* ERROR handling */
no_function:
g_warning ("pad %s:%s has no event handler, file a bug.",
GST_DEBUG_PAD_NAME (rpad));
result = FALSE;
gst_event_unref (event);
return result;
{
g_warning ("pad %s:%s has no event handler, file a bug.",
GST_DEBUG_PAD_NAME (rpad));
gst_event_unref (event);
return FALSE;
}
}
typedef struct

View file

@ -132,7 +132,7 @@ typedef enum {
typedef gboolean (*GstPadActivateFunction) (GstPad *pad, GstActivateMode mode);
/* data passing */
typedef gboolean (*GstPadLoopFunction) (GstPad *pad);
typedef void (*GstPadLoopFunction) (GstPad *pad);
typedef GstFlowReturn (*GstPadChainFunction) (GstPad *pad, GstBuffer *buffer);
typedef GstFlowReturn (*GstPadGetRangeFunction) (GstPad *pad, guint64 offset,
guint length, GstBuffer **buffer);

View file

@ -209,6 +209,7 @@ is_eos (GstPipeline * pipeline)
return result;
}
/* FIXME, make me threadsafe */
static GstBusSyncReply
pipeline_bus_handler (GstBus * bus, GstMessage * message,
GstPipeline * pipeline)
@ -224,8 +225,10 @@ pipeline_bus_handler (GstBus * bus, GstMessage * message,
switch (GST_MESSAGE_TYPE (message)) {
case GST_MESSAGE_EOS:
if (GST_MESSAGE_SRC (message) != GST_OBJECT (pipeline)) {
GST_LOCK (bus);
pipeline->eosed =
g_list_prepend (pipeline->eosed, GST_MESSAGE_SRC (message));
GST_UNLOCK (bus);
if (is_eos (pipeline)) {
posteos = TRUE;
}
@ -257,6 +260,8 @@ pipeline_bus_handler (GstBus * bus, GstMessage * message,
* Create a new pipeline with the given name.
*
* Returns: newly created GstPipeline
*
* MT safe.
*/
GstElement *
gst_pipeline_new (const gchar * name)
@ -264,6 +269,7 @@ gst_pipeline_new (const gchar * name)
return gst_element_factory_make ("pipeline", name);
}
/* MT safe */
static GstElementStateReturn
gst_pipeline_change_state (GstElement * element)
{
@ -276,14 +282,18 @@ gst_pipeline_change_state (GstElement * element)
gst_scheduler_setup (GST_ELEMENT_SCHEDULER (pipeline));
break;
case GST_STATE_READY_TO_PAUSED:
gst_element_set_clock (element, gst_element_get_clock (element));
{
GstClock *clock;
clock = gst_element_get_clock (element);
gst_element_set_clock (element, clock);
pipeline->eosed = NULL;
break;
}
case GST_STATE_PAUSED_TO_PLAYING:
if (element->clock) {
/* we set time slightly ahead because of context switches */
pipeline->start_time =
gst_clock_get_time (element->clock) + 10 * GST_MSECOND;
pipeline->start_time = gst_clock_get_time (element->clock); // + 10*GST_MSECOND;
element->base_time = pipeline->start_time - pipeline->stream_time;
}
GST_DEBUG ("stream_time=%" G_GUINT64_FORMAT ", start_time=%"
@ -335,6 +345,8 @@ gst_pipeline_change_state (GstElement * element)
* Gets the #GstScheduler of this pipeline.
*
* Returns: a GstScheduler.
*
* MT safe.
*/
GstScheduler *
gst_pipeline_get_scheduler (GstPipeline * pipeline)
@ -349,6 +361,8 @@ gst_pipeline_get_scheduler (GstPipeline * pipeline)
* Gets the #GstBus of this pipeline.
*
* Returns: a GstBus
*
* MT safe.
*/
GstBus *
gst_pipeline_get_bus (GstPipeline * pipeline)
@ -363,12 +377,16 @@ gst_pipeline_get_clock_func (GstElement * element)
GstPipeline *pipeline = GST_PIPELINE (element);
/* if we have a fixed clock, use that one */
GST_LOCK (pipeline);
if (GST_FLAG_IS_SET (pipeline, GST_PIPELINE_FLAG_FIXED_CLOCK)) {
clock = pipeline->fixed_clock;
gst_object_ref (GST_OBJECT (clock));
GST_UNLOCK (pipeline);
GST_CAT_DEBUG (GST_CAT_CLOCK, "pipeline using fixed clock %p (%s)",
clock, clock ? GST_STR_NULL (GST_OBJECT_NAME (clock)) : "-");
} else {
GST_UNLOCK (pipeline);
clock =
GST_ELEMENT_CLASS (parent_class)->get_clock (GST_ELEMENT (pipeline));
/* no clock, use a system clock */
@ -413,16 +431,20 @@ gst_pipeline_get_clock (GstPipeline * pipeline)
* Force the pipeline to use the given clock. The pipeline will
* always use the given clock even if new clock providers are added
* to this pipeline.
*
* MT safe.
*/
void
gst_pipeline_use_clock (GstPipeline * pipeline, GstClock * clock)
{
g_return_if_fail (GST_IS_PIPELINE (pipeline));
GST_LOCK (pipeline);
GST_FLAG_SET (pipeline, GST_PIPELINE_FLAG_FIXED_CLOCK);
gst_object_replace ((GstObject **) & pipeline->fixed_clock,
(GstObject *) clock);
GST_LOCK (pipeline);
GST_CAT_DEBUG (GST_CAT_CLOCK, "pipeline using fixed clock %p (%s)", clock,
(clock ? GST_OBJECT_NAME (clock) : "nil"));
@ -435,6 +457,8 @@ gst_pipeline_use_clock (GstPipeline * pipeline, GstClock * clock)
*
* Set the clock for the pipeline. The clock will be distributed
* to all the elements managed by the pipeline.
*
* MT safe.
*/
void
gst_pipeline_set_clock (GstPipeline * pipeline, GstClock * clock)
@ -450,6 +474,8 @@ gst_pipeline_set_clock (GstPipeline * pipeline, GstClock * clock)
* @pipeline: the pipeline
*
* Let the pipeline select a clock automatically.
*
* MT safe.
*/
void
gst_pipeline_auto_clock (GstPipeline * pipeline)
@ -459,7 +485,9 @@ gst_pipeline_auto_clock (GstPipeline * pipeline)
GST_FLAG_UNSET (pipeline, GST_PIPELINE_FLAG_FIXED_CLOCK);
GST_LOCK (pipeline);
gst_object_replace ((GstObject **) & pipeline->fixed_clock, NULL);
GST_UNLOCK (pipeline);
GST_CAT_DEBUG (GST_CAT_CLOCK, "pipeline using automatic clock");
}

View file

@ -48,18 +48,21 @@ typedef enum {
struct _GstPipeline {
GstBin bin;
/*< public >*/ /* with LOCK */
GstClock *fixed_clock; /* fixed clock if any */
GstClockTime start_time;
GstClockTime stream_time;
GList *eosed; /* list of elements that posted EOS */
/*< private >*/
gpointer _gst_reserved[GST_PADDING];
};
struct _GstPipelineClass {
GstBinClass parent_class;
/*< private >*/
gpointer _gst_reserved[GST_PADDING];
};

View file

@ -143,23 +143,6 @@ gst_plugin_feature_type_name_filter (GstPluginFeature * feature,
|| !strcmp (data->name, GST_PLUGIN_FEATURE_NAME (feature))));
}
/**
* gst_plugin_feature_set_rank:
* @feature: feature to rank
* @rank: rank value - higher number means more priority rank
*
* Specifies a rank for a plugin feature, so that autoplugging uses
* the most appropriate feature.
*/
void
gst_plugin_feature_set_rank (GstPluginFeature * feature, guint rank)
{
g_return_if_fail (feature != NULL);
g_return_if_fail (GST_IS_PLUGIN_FEATURE (feature));
feature->rank = rank;
}
/**
* gst_plugin_feature_set_name:
* @feature: a feature
@ -167,7 +150,8 @@ gst_plugin_feature_set_rank (GstPluginFeature * feature, guint rank)
*
* Sets the name of a plugin feature. The name uniquely identifies a feature
* within all features of the same type. Renaming a plugin feature is not
* allowed.
* allowed. A copy is made of the name so you should free the supplied @name
* after calling this function.
*/
void
gst_plugin_feature_set_name (GstPluginFeature * feature, const gchar * name)
@ -182,22 +166,6 @@ gst_plugin_feature_set_name (GstPluginFeature * feature, const gchar * name)
}
}
/**
* gst_plugin_feature_get rank:
* @feature: a feature
*
* Gets the rank of a plugin feature.
*
* Returns: The rank of the feature
*/
guint
gst_plugin_feature_get_rank (GstPluginFeature * feature)
{
g_return_val_if_fail (GST_IS_PLUGIN_FEATURE (feature), GST_RANK_NONE);
return feature->rank;
}
/**
* gst_plugin_feature_get_name:
* @feature: a feature
@ -213,3 +181,36 @@ gst_plugin_feature_get_name (GstPluginFeature * feature)
return feature->name;
}
/**
* gst_plugin_feature_set_rank:
* @feature: feature to rank
* @rank: rank value - higher number means more priority rank
*
* Specifies a rank for a plugin feature, so that autoplugging uses
* the most appropriate feature.
*/
void
gst_plugin_feature_set_rank (GstPluginFeature * feature, guint rank)
{
g_return_if_fail (feature != NULL);
g_return_if_fail (GST_IS_PLUGIN_FEATURE (feature));
feature->rank = rank;
}
/**
* gst_plugin_feature_get rank:
* @feature: a feature
*
* Gets the rank of a plugin feature.
*
* Returns: The rank of the feature
*/
guint
gst_plugin_feature_get_rank (GstPluginFeature * feature)
{
g_return_val_if_fail (GST_IS_PLUGIN_FEATURE (feature), GST_RANK_NONE);
return feature->rank;
}

View file

@ -58,6 +58,7 @@ struct _GstPluginFeatureClass {
void (*unload_thyself) (GstPluginFeature *feature);
/*< private >*/
gpointer _gst_reserved[GST_PADDING];
};

View file

@ -30,7 +30,9 @@
* @callback: the function to call when the probe is triggered
* @user_data: data passed to the callback function
*
* Create a new probe with the specified parameters
* Create a new probe with the specified parameters. The single shot
* probe will be fired only once. It is the responsability of the
* application to free the single probe after it has been fired.
*
* Returns: a new #GstProbe.
*/
@ -221,7 +223,8 @@ gst_probe_dispatcher_dispatch (GstProbeDispatcher * disp, GstData ** data)
if (probe->single_shot) {
disp->probes = g_slist_remove (disp->probes, probe);
gst_probe_destroy (probe);
/* do not free the probe here as it cannot be made threadsafe */
//gst_probe_destroy (probe);
}
}

View file

@ -1,6 +1,7 @@
/* GStreamer
* Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
* 2000 Wim Taymans <wim.taymans@chello.be>
* 2005 Wim Taymans <wim@fluendo.com>
*
* gstquery.c: GstQueryType registration
*
@ -25,10 +26,11 @@
#include "gst_private.h"
#include "gstquery.h"
static GStaticMutex mutex = G_STATIC_MUTEX_INIT;
static GList *_gst_queries = NULL;
static GHashTable *_nick_to_query = NULL;
static GHashTable *_query_type_to_nick = NULL;
static gint _n_values = 1; /* we start from 1 because 0 reserved for NONE */
static guint32 _n_values = 1; /* we start from 1 because 0 reserved for NONE */
static GstQueryTypeDefinition standard_definitions[] = {
{GST_QUERY_TOTAL, "total", "Total length"},
@ -46,6 +48,7 @@ _gst_query_type_initialize (void)
{
GstQueryTypeDefinition *standards = standard_definitions;
g_static_mutex_lock (&mutex);
if (_nick_to_query == NULL) {
_nick_to_query = g_hash_table_new (g_str_hash, g_str_equal);
_query_type_to_nick = g_hash_table_new (NULL, NULL);
@ -60,6 +63,7 @@ _gst_query_type_initialize (void)
standards++;
_n_values++;
}
g_static_mutex_unlock (&mutex);
}
/**
@ -91,11 +95,13 @@ gst_query_type_register (const gchar * nick, const gchar * description)
query->nick = g_strdup (nick);
query->description = g_strdup (description);
g_static_mutex_lock (&mutex);
g_hash_table_insert (_nick_to_query, query->nick, query);
g_hash_table_insert (_query_type_to_nick, GINT_TO_POINTER (query->value),
query);
_gst_queries = g_list_append (_gst_queries, query);
_n_values++;
g_static_mutex_unlock (&mutex);
return query->value;
}
@ -116,7 +122,9 @@ gst_query_type_get_by_nick (const gchar * nick)
g_return_val_if_fail (nick != NULL, 0);
g_static_mutex_lock (&mutex);
query = g_hash_table_lookup (_nick_to_query, nick);
g_static_mutex_unlock (&mutex);
if (query != NULL)
return query->value;
@ -160,18 +168,32 @@ gst_query_types_contains (const GstQueryType * types, GstQueryType type)
const GstQueryTypeDefinition *
gst_query_type_get_details (GstQueryType type)
{
return g_hash_table_lookup (_query_type_to_nick, GINT_TO_POINTER (type));
const GstQueryTypeDefinition *result;
g_static_mutex_lock (&mutex);
result = g_hash_table_lookup (_query_type_to_nick, GINT_TO_POINTER (type));
g_static_mutex_unlock (&mutex);
return result;
}
/**
* gst_query_type_get_definitions:
* gst_query_type_iterate_definitions:
*
* Get a list of all the registered query types.
* Get an Iterator of all the registered query types. The querytype
* definition is read only.
*
* Returns: A GList of #GstQueryTypeDefinition.
* Returns: A #GstIterator of #GstQueryTypeDefinition.
*/
const GList *
gst_query_type_get_definitions (void)
GstIterator *
gst_query_type_iterate_definitions (void)
{
return _gst_queries;
GstIterator *result;
g_static_mutex_lock (&mutex);
result = gst_iterator_new_list (g_static_mutex_get_mutex (&mutex),
&_n_values, &_gst_queries, NULL, NULL, NULL, NULL);
g_static_mutex_unlock (&mutex);
return result;
}

View file

@ -1,6 +1,7 @@
/* GStreamer
* Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
* 2000 Wim Taymans <wim.taymans@chello.be>
* 2005 Wim Taymans <wim@fluendo.com>
*
* gstquery.h: GstQuery API declaration
*
@ -26,6 +27,8 @@
#include <glib.h>
#include <gst/gstiterator.h>
G_BEGIN_DECLS
typedef enum {
@ -87,8 +90,8 @@ gboolean gst_query_types_contains (const GstQueryType *types, GstQ
/* query for query details */
G_CONST_RETURN GstQueryTypeDefinition*
gst_query_type_get_details (GstQueryType type);
G_CONST_RETURN GList* gst_query_type_get_definitions (void);
gst_query_type_get_details (GstQueryType type);
GstIterator* gst_query_type_iterate_definitions (void);
G_END_DECLS

View file

@ -127,7 +127,7 @@ static void gst_queue_get_property (GObject * object,
static GstFlowReturn gst_queue_chain (GstPad * pad, GstBuffer * buffer);
static GstBuffer *gst_queue_bufferalloc (GstPad * pad, guint64 offset,
guint size, GstCaps * caps);
static gboolean gst_queue_loop (GstPad * pad);
static void gst_queue_loop (GstPad * pad);
static gboolean gst_queue_handle_sink_event (GstPad * pad, GstEvent * event);
@ -644,7 +644,7 @@ out_unref:
return GST_FLOW_OK;
}
static gboolean
static void
gst_queue_loop (GstPad * pad)
{
GstQueue *queue;
@ -697,6 +697,7 @@ restart:
GST_QUEUE_MUTEX_LOCK;
} else {
if (GST_EVENT_TYPE (data) == GST_EVENT_EOS) {
gst_task_pause (queue->task);
result = FALSE;
}
GST_QUEUE_MUTEX_UNLOCK;
@ -711,8 +712,6 @@ restart:
GST_CAT_LOG_OBJECT (queue_dataflow, queue, "signalling item_del");
g_cond_signal (queue->item_del);
GST_QUEUE_MUTEX_UNLOCK;
return result;
}

View file

@ -1,6 +1,6 @@
/* GStreamer
* Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
* 2000 Wim Taymans <wim.taymans@chello.be>
* 2004 Wim Taymans <wim@fluendo.com>
*
* gstsystemclock.c: Default clock, uses the system clock
*
@ -20,13 +20,12 @@
* Boston, MA 02111-1307, USA.
*/
#include <time.h>
#include "gst_private.h"
#include "gstinfo.h"
#include "gstsystemclock.h"
/* the one instance of the systemclock */
static GstClock *_the_system_clock = NULL;
static void gst_system_clock_class_init (GstSystemClockClass * klass);
@ -35,9 +34,13 @@ static void gst_system_clock_dispose (GObject * object);
static GstClockTime gst_system_clock_get_internal_time (GstClock * clock);
static guint64 gst_system_clock_get_resolution (GstClock * clock);
static GstClockEntryStatus gst_system_clock_wait (GstClock * clock,
static GstClockReturn gst_system_clock_id_wait (GstClock * clock,
GstClockEntry * entry);
static void gst_system_clock_unlock (GstClock * clock, GstClockEntry * entry);
static GstClockReturn gst_system_clock_id_wait_async (GstClock * clock,
GstClockEntry * entry);
static void gst_system_clock_id_unschedule (GstClock * clock,
GstClockEntry * entry);
static void gst_system_clock_async_thread (GstClock * clock);
static GStaticMutex _gst_sysclock_mutex = G_STATIC_MUTEX_INIT;
@ -87,22 +90,43 @@ gst_system_clock_class_init (GstSystemClockClass * klass)
gstclock_class->get_internal_time = gst_system_clock_get_internal_time;
gstclock_class->get_resolution = gst_system_clock_get_resolution;
gstclock_class->wait = gst_system_clock_wait;
gstclock_class->unlock = gst_system_clock_unlock;
gstclock_class->wait = gst_system_clock_id_wait;
gstclock_class->wait_async = gst_system_clock_id_wait_async;
gstclock_class->unschedule = gst_system_clock_id_unschedule;
}
static void
gst_system_clock_init (GstSystemClock * clock)
{
clock->mutex = g_mutex_new ();
clock->cond = g_cond_new ();
GError *error = NULL;
GST_CLOCK_FLAGS (clock) =
GST_CLOCK_FLAG_CAN_DO_SINGLE_SYNC |
GST_CLOCK_FLAG_CAN_DO_SINGLE_ASYNC |
GST_CLOCK_FLAG_CAN_DO_PERIODIC_SYNC |
GST_CLOCK_FLAG_CAN_DO_PERIODIC_ASYNC;
GST_LOCK (clock);
clock->thread = g_thread_create ((GThreadFunc) gst_system_clock_async_thread,
clock, TRUE, &error);
if (error)
goto no_thread;
/* wait for it to spin up */
GST_CLOCK_WAIT (clock);
GST_UNLOCK (clock);
return;
no_thread:
{
g_warning ("could not create async clock thread: %s", error->message);
}
}
static void
gst_system_clock_dispose (GObject * object)
{
GstClock *clock = (GstClock *) object;
GstSystemClock *sysclock = (GstSystemClock *) object;
/* there are subclasses of GstSystemClock running around... */
if (_the_system_clock == clock) {
@ -111,19 +135,19 @@ gst_system_clock_dispose (GObject * object)
/* no parent dispose here, this is bad enough already */
} else {
G_OBJECT_CLASS (parent_class)->dispose (object);
/* FIXME: Notifying before freeing? */
g_cond_free (sysclock->cond);
g_mutex_free (sysclock->mutex);
}
}
/**
* gst_system_clock_obtain
*
* Get a handle to the default system clock.
* Get a handle to the default system clock. The refcount of the
* clock will be increased so you need to unref the clock after
* usage.
*
* Returns: the default clock.
*
* MT safe.
*/
GstClock *
gst_system_clock_obtain (void)
@ -164,6 +188,95 @@ have_clock:
return clock;
}
/* this thread reads the sorted clock entries from the queue.
*
* It waits on each of them and fires the callback when the timeout occurs.
*
* When an entry in the queue was canceled, it is simply skipped.
*
* When waiting for an entry, it can become canceled, in that case we don't
* call the callback but move to the next item in the queue.
*
* MT safe.
*/
static void
gst_system_clock_async_thread (GstClock * clock)
{
GstSystemClock *sysclock = GST_SYSTEM_CLOCK (clock);
GST_CAT_DEBUG (GST_CAT_CLOCK, "enter system clock thread");
GST_LOCK (clock);
/* signal spinup */
GST_CLOCK_SIGNAL (clock);
/* now enter our infinite loop */
while (!sysclock->stopping) {
GstClockEntry *entry;
GstClockReturn res;
/* check if something to be done */
while (clock->entries == NULL) {
GST_CAT_DEBUG (GST_CAT_CLOCK, "nothing to wait for");
/* wait for work to do */
GST_CLOCK_WAIT (clock);
GST_CAT_DEBUG (GST_CAT_CLOCK, "got signal");
/* clock was stopping, exit */
if (sysclock->stopping)
goto exit;
}
/* pick the next entry */
entry = clock->entries->data;
/* if it was unscheduled, just move on to the next entry */
if (entry->status == GST_CLOCK_UNSCHEDULED) {
GST_CAT_DEBUG (GST_CAT_CLOCK, "entry %p was unscheduled", entry);
goto next_entry;
}
/* now wait for the entry, it's a bit stupid to release the lock
* here but we have to since the next function grabs the lock... */
GST_UNLOCK (clock);
res = gst_system_clock_id_wait (clock, (GstClockID) entry);
GST_LOCK (clock);
switch (res) {
case GST_CLOCK_UNSCHEDULED:
/* entry was unscheduled, move to the next */
GST_CAT_DEBUG (GST_CAT_CLOCK, "async entry %p unscheduled", entry);
goto next_entry;
case GST_CLOCK_OK:
case GST_CLOCK_EARLY:
/* entry timed out normally, fire the callback and move to the next
* entry */
GST_CAT_DEBUG (GST_CAT_CLOCK, "async entry %p unlocked", entry);
if (entry->func) {
entry->func (clock, entry->time, (GstClockID) entry,
entry->user_data);
}
goto next_entry;
case GST_CLOCK_BUSY:
/* somebody unlocked the entry but is was not canceled, This means that
* either a new entry was added in front of the queue or some other entry
* was canceled. Whatever it is, pick the head entry of the list and
* continue waiting. */
GST_CAT_DEBUG (GST_CAT_CLOCK, "async entry %p needs restart", entry);
continue;
default:
GST_CAT_DEBUG (GST_CAT_CLOCK,
"strange result %d waiting for %p, skipping", res, entry);
goto next_entry;
}
next_entry:
/* we remove the current entry and unref it */
clock->entries = g_list_remove (clock->entries, entry);
gst_clock_id_unref ((GstClockID) entry);
}
exit:
/* signal exit */
GST_CLOCK_SIGNAL (clock);
GST_UNLOCK (clock);
GST_CAT_DEBUG (GST_CAT_CLOCK, "exit system clock thread");
}
/* MT safe */
static GstClockTime
gst_system_clock_get_internal_time (GstClock * clock)
{
@ -180,23 +293,36 @@ gst_system_clock_get_resolution (GstClock * clock)
return 1 * GST_USECOND;
}
static GstClockEntryStatus
gst_system_clock_wait (GstClock * clock, GstClockEntry * entry)
/* synchronously wait on the given GstClockEntry.
*
* We do this by blocking on the global clock GCond variable with
* the requested time as a timeout. This allows us to unblock the
* entry by signaling the GCond variable.
*
* Note that signaling the global GCond unlocks all waiting entries. So
* we need to check if an unlocked entry has changed when it unlocks.
*
* Entries that arrive too late are simply not waited on and a
* GST_CLOCK_EARLY result is returned.
*
* MT safe.
*/
static GstClockReturn
gst_system_clock_id_wait (GstClock * clock, GstClockEntry * entry)
{
GstClockEntryStatus res;
GstClockTime current, target;
gint64 diff;
GstSystemClock *sysclock = GST_SYSTEM_CLOCK (clock);
current = gst_clock_get_time (clock);
diff = GST_CLOCK_ENTRY_TIME (entry) - current;
target = GST_CLOCK_ENTRY_TIME (entry);
GST_CAT_DEBUG (GST_CAT_CLOCK, "real_target %" GST_TIME_FORMAT
GST_CAT_DEBUG (GST_CAT_CLOCK, "entry %p real_target %" GST_TIME_FORMAT
" target %" GST_TIME_FORMAT
" now %" GST_TIME_FORMAT
" diff %" G_GINT64_FORMAT,
entry,
GST_TIME_ARGS (target),
GST_TIME_ARGS (GST_CLOCK_ENTRY_TIME (entry)),
GST_TIME_ARGS (current), diff);
@ -205,23 +331,77 @@ gst_system_clock_wait (GstClock * clock, GstClockEntry * entry)
GTimeVal tv;
GST_TIME_TO_TIMEVAL (target, tv);
g_mutex_lock (sysclock->mutex);
g_cond_timed_wait (sysclock->cond, sysclock->mutex, &tv);
g_mutex_unlock (sysclock->mutex);
res = entry->status;
GST_LOCK (clock);
while (TRUE) {
/* now wait on the entry, it either times out or the cond is signaled. */
if (!GST_CLOCK_TIMED_WAIT (clock, &tv)) {
/* timeout, this is fine, we can report success now */
GST_CAT_DEBUG (GST_CAT_CLOCK, "entry %p unlocked after timeout", entry);
entry->status = GST_CLOCK_OK;
break;
} else {
/* the waiting is interrupted because the GCond was signaled. This can
* be because this or some other entry was unscheduled. */
GST_CAT_DEBUG (GST_CAT_CLOCK, "entry %p unlocked with signal", entry);
/* if the entry is unscheduled, we can stop waiting for it */
if (entry->status == GST_CLOCK_UNSCHEDULED)
break;
}
}
GST_UNLOCK (clock);
} else {
res = GST_CLOCK_ENTRY_EARLY;
entry->status = GST_CLOCK_EARLY;
}
return res;
return entry->status;
}
static void
gst_system_clock_unlock (GstClock * clock, GstClockEntry * entry)
/* Add an entry to the list of pending async waits. The entry is inserted
* in sorted order. If we inserted the entry at the head of the list, we
* need to signal the thread as it might either be waiting on it or waiting
* for a new entry.
*
* MT safe.
*/
static GstClockReturn
gst_system_clock_id_wait_async (GstClock * clock, GstClockEntry * entry)
{
GstSystemClock *sysclock = GST_SYSTEM_CLOCK (clock);
GST_CAT_DEBUG (GST_CAT_CLOCK, "adding entry %p", entry);
g_mutex_lock (sysclock->mutex);
g_cond_broadcast (sysclock->cond);
g_mutex_unlock (sysclock->mutex);
GST_LOCK (clock);
/* need to take a ref */
gst_clock_id_ref ((GstClockID) entry);
/* insert the entry in sorted order */
clock->entries = g_list_insert_sorted (clock->entries, entry,
gst_clock_id_compare_func);
/* only need to send the signal if the entry was added to the
* front, else the thread is just waiting for another entry and
* will discard this entry automatically. */
if (clock->entries->data == entry) {
GST_CAT_DEBUG (GST_CAT_CLOCK, "send signal");
GST_CLOCK_SIGNAL (clock);
}
GST_UNLOCK (clock);
return GST_CLOCK_OK;
}
/* unschedule an entry. This will set the state of the entry to GST_CLOCK_UNSCHEDULED
* and will signal any thread waiting for entries to recheck their entry.
* We cannot really decide if the signal is needed or not because the entry
* could be waited on in async or sync mode.
*
* MT safe.
*/
static void
gst_system_clock_id_unschedule (GstClock * clock, GstClockEntry * entry)
{
GST_CAT_DEBUG (GST_CAT_CLOCK, "unscheduling entry %p", entry);
GST_LOCK (clock);
entry->status = GST_CLOCK_UNSCHEDULED;
GST_CAT_DEBUG (GST_CAT_CLOCK, "send signal");
GST_CLOCK_SIGNAL (clock);
GST_UNLOCK (clock);
}

View file

@ -42,8 +42,9 @@ typedef struct _GstSystemClockClass GstSystemClockClass;
struct _GstSystemClock {
GstClock clock;
GMutex * mutex;
GCond * cond;
/*< private >*/
GThread *thread; /* thread for async notify */
gboolean stopping;
gpointer _gst_reserved[GST_PADDING];
};
@ -51,6 +52,7 @@ struct _GstSystemClock {
struct _GstSystemClockClass {
GstClockClass parent_class;
/*< private >*/
gpointer _gst_reserved[GST_PADDING];
};

View file

@ -1,6 +1,6 @@
/* GStreamer
* Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
* 2000 Wim Taymans <wim.taymans@chello.be>
* 2005 Wim Taymans <wim@fluendo.com>
*
* gsttask.c: Streaming tasks
*
@ -26,7 +26,7 @@
#include "gsttask.h"
static void gst_task_class_init (GstTaskClass * klass);
static void gst_task_init (GstTask * sched);
static void gst_task_init (GstTask * task);
static void gst_task_dispose (GObject * object);
static GstObjectClass *parent_class = NULL;
@ -70,8 +70,10 @@ gst_task_class_init (GstTaskClass * klass)
}
static void
gst_task_init (GstTask * sched)
gst_task_init (GstTask * task)
{
task->cond = g_cond_new ();
task->state = GST_TASK_STOPPED;
}
static void
@ -79,19 +81,73 @@ gst_task_dispose (GObject * object)
{
GstTask *task = GST_TASK (object);
/* thse lists should all be NULL */
GST_DEBUG ("task %p dispose", task);
g_cond_free (task->cond);
G_OBJECT_CLASS (parent_class)->dispose (object);
}
/**
* gst_task_create:
* @func: The #GstTaskFunction to use
* @data: User data to pass to @func
*
* Create a new Task that will repeadedly call the provided @func
* with @data as a parameter. Typically the task will run in
* a new thread.
*
* Returns: A new #GstTask.
*
* MT safe.
*/
GstTask *
gst_task_create (GstTaskFunction func, gpointer data)
{
return NULL;
}
/**
* gst_task_get_state:
* @task: The #GstTask to query
*
* Get the current state of the task.
*
* Returns: The #GstTaskState of the task
*
* MT safe.
*/
GstTaskState
gst_task_get_state (GstTask * task)
{
GstTaskState result;
g_return_val_if_fail (GST_IS_TASK (task), GST_TASK_STOPPED);
GST_LOCK (task);
result = task->state;
GST_UNLOCK (task);
return result;
}
/**
* gst_task_start:
* @task: The #GstTask to start
*
* Starts @task.
*
* Returns: TRUE if the task could be started.
*
* MT safe.
*/
gboolean
gst_task_start (GstTask * task)
{
GstTaskClass *tclass;
gboolean result = FALSE;
g_return_val_if_fail (GST_IS_TASK (task), result);
g_return_val_if_fail (GST_IS_TASK (task), FALSE);
tclass = GST_TASK_GET_CLASS (task);
@ -101,13 +157,23 @@ gst_task_start (GstTask * task)
return result;
}
/**
* gst_task_stop:
* @task: The #GstTask to stop
*
* Stops @task.
*
* Returns: TRUE if the task could be stopped.
*
* MT safe.
*/
gboolean
gst_task_stop (GstTask * task)
{
GstTaskClass *tclass;
gboolean result = FALSE;
g_return_val_if_fail (GST_IS_TASK (task), result);
g_return_val_if_fail (GST_IS_TASK (task), FALSE);
tclass = GST_TASK_GET_CLASS (task);
@ -117,13 +183,23 @@ gst_task_stop (GstTask * task)
return result;
}
/**
* gst_task_pause:
* @task: The #GstTask to pause
*
* Pauses @task.
*
* Returns: TRUE if the task could be paused.
*
* MT safe.
*/
gboolean
gst_task_pause (GstTask * task)
{
GstTaskClass *tclass;
gboolean result = FALSE;
g_return_val_if_fail (GST_IS_TASK (task), result);
g_return_val_if_fail (GST_IS_TASK (task), FALSE);
tclass = GST_TASK_GET_CLASS (task);

View file

@ -1,6 +1,8 @@
/* GStreamer
* Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
* <2004> Wim Taymans <wim@fluendo.com>
* <2005> Wim Taymans <wim@fluendo.com>
*
* gsttask.h: Streaming tasks
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
@ -25,7 +27,7 @@
G_BEGIN_DECLS
typedef gboolean (*GstTaskFunction) (void *data);
typedef void (*GstTaskFunction) (void *data);
/* --- standard type macros --- */
#define GST_TYPE_TASK (gst_task_get_type ())
@ -34,28 +36,56 @@ typedef gboolean (*GstTaskFunction) (void *data);
#define GST_TASK_CLASS(tclass) (G_TYPE_CHECK_CLASS_CAST ((tclass), GST_TYPE_TASK, GstTaskClass))
#define GST_IS_TASK_CLASS(tclass) (G_TYPE_CHECK_CLASS_TYPE ((tclass), GST_TYPE_TASK))
#define GST_TASK_GET_CLASS(task) (G_TYPE_INSTANCE_GET_CLASS ((task), GST_TYPE_TASK, GstTaskClass))
#define GST_TASK_CAST(task) ((GstTask*)(task))
typedef struct _GstTask GstTask;
typedef struct _GstTaskClass GstTaskClass;
typedef enum {
GST_TASK_STARTED,
GST_TASK_STOPPED,
GST_TASK_PAUSED,
} GstTaskState;
#define GST_TASK_STATE(task) (GST_TASK_CAST(task)->state)
#define GST_TASK_GET_COND(task) (GST_TASK_CAST(task)->cond)
#define GST_TASK_WAIT(task) g_cond_wait(GST_TASK_GET_COND (task), GST_GET_LOCK (task))
#define GST_TASK_SIGNAL(task) g_cond_signal(GST_TASK_GET_COND (task))
#define GST_TASK_BROADCAST(task) g_cond_breadcast(GST_TASK_GET_COND (task))
struct _GstTask {
GstObject object;
/*< public >*/ /* with LOCK */
GstTaskState state;
GCond *cond;
GstTaskFunction func;
gpointer data;
/*< private >*/
gpointer _gst_reserved[GST_PADDING];
};
struct _GstTaskClass {
GstObjectClass parent_class;
/*< protected >*/
gboolean (*start) (GstTask *task);
gboolean (*stop) (GstTask *task);
gboolean (*pause) (GstTask *task);
/*< private >*/
gpointer _gst_reserved[GST_PADDING];
};
GType gst_task_get_type (void);
GstTask* gst_task_create (GstTaskFunction func, gpointer data);
GstTaskState gst_task_get_state (GstTask *task);
gboolean gst_task_start (GstTask *task);
gboolean gst_task_stop (GstTask *task);
gboolean gst_task_pause (GstTask *task);

View file

@ -124,6 +124,7 @@ gst_trash_stack_pop (GstTrashStack *stack)
}
#else
#warning "using fallback trashstack implementation, performance may suffer"
/*
* generic implementation

View file

@ -76,30 +76,14 @@ struct _GstThreadSchedulerClass
typedef struct _GstThreadSchedulerTask GstThreadSchedulerTask;
typedef struct _GstThreadSchedulerTaskClass GstThreadSchedulerTaskClass;
typedef enum
{
STATE_STOPPED,
STATE_STARTED,
STATE_PAUSED
} TaskState;
struct _GstThreadSchedulerTask
{
GstTask task;
TaskState state;
GMutex *lock;
GCond *cond;
GstTaskFunction func;
gpointer data;
};
struct _GstThreadSchedulerTaskClass
{
GstTaskClass parent_class;
};
static void gst_thread_scheduler_task_class_init (gpointer g_class,
@ -148,9 +132,7 @@ gst_thread_scheduler_task_class_init (gpointer klass, gpointer class_data)
static void
gst_thread_scheduler_task_init (GstThreadSchedulerTask * task)
{
task->state = STATE_STOPPED;
task->lock = g_mutex_new ();
task->cond = g_cond_new ();
GST_TASK (task)->state = GST_TASK_STOPPED;
}
static gboolean
@ -158,17 +140,23 @@ gst_thread_scheduler_task_start (GstTask * task)
{
GstThreadSchedulerTask *ttask = GST_THREAD_SCHEDULER_TASK (task);
GstThreadScheduler *tsched =
GST_THREAD_SCHEDULER (gst_object_get_parent (GST_OBJECT (task)));
GST_THREAD_SCHEDULER (GST_OBJECT_PARENT (GST_OBJECT (task)));
GstTaskState old;
g_mutex_lock (ttask->lock);
if (ttask->state == STATE_STOPPED) {
ttask->state = STATE_STARTED;
g_thread_pool_push (tsched->pool, task, NULL);
} else {
ttask->state = STATE_STARTED;
g_cond_signal (ttask->cond);
GST_LOCK (ttask);
old = GST_TASK_CAST (ttask)->state;
GST_TASK_CAST (ttask)->state = GST_TASK_STARTED;
switch (old) {
case GST_TASK_STOPPED:
g_thread_pool_push (tsched->pool, task, NULL);
break;
case GST_TASK_PAUSED:
GST_TASK_SIGNAL (ttask);
break;
case GST_TASK_STARTED:
break;
}
g_mutex_unlock (ttask->lock);
GST_UNLOCK (ttask);
return TRUE;
}
@ -177,13 +165,22 @@ static gboolean
gst_thread_scheduler_task_stop (GstTask * task)
{
GstThreadSchedulerTask *ttask = GST_THREAD_SCHEDULER_TASK (task);
GstTaskState old;
g_mutex_lock (ttask->lock);
if (ttask->state != STATE_STOPPED) {
ttask->state = STATE_STOPPED;
g_cond_signal (ttask->cond);
GST_LOCK (ttask);
old = GST_TASK_CAST (ttask)->state;
GST_TASK_CAST (ttask)->state = GST_TASK_STOPPED;
switch (old) {
case GST_TASK_STOPPED:
break;
case GST_TASK_PAUSED:
GST_TASK_SIGNAL (ttask);
break;
case GST_TASK_STARTED:
break;
}
g_mutex_unlock (ttask->lock);
GST_UNLOCK (ttask);
return TRUE;
}
@ -191,12 +188,24 @@ static gboolean
gst_thread_scheduler_task_pause (GstTask * task)
{
GstThreadSchedulerTask *ttask = GST_THREAD_SCHEDULER_TASK (task);
GstThreadScheduler *tsched =
GST_THREAD_SCHEDULER (GST_OBJECT_PARENT (GST_OBJECT (task)));
GstTaskState old;
g_mutex_lock (ttask->lock);
if (ttask->state != STATE_PAUSED) {
ttask->state = STATE_PAUSED;
GST_LOCK (ttask);
old = GST_TASK_CAST (ttask)->state;
GST_TASK_CAST (ttask)->state = GST_TASK_PAUSED;
switch (old) {
case GST_TASK_STOPPED:
g_thread_pool_push (tsched->pool, task, NULL);
break;
case GST_TASK_PAUSED:
break;
case GST_TASK_STARTED:
break;
}
g_mutex_unlock (ttask->lock);
GST_UNLOCK (ttask);
return TRUE;
}
@ -253,32 +262,35 @@ gst_thread_scheduler_class_init (gpointer klass, gpointer class_data)
}
static void
gst_thread_scheduler_func (GstThreadSchedulerTask * task,
gst_thread_scheduler_func (GstThreadSchedulerTask * ttask,
GstThreadScheduler * sched)
{
gboolean res;
GstTask *task = GST_TASK (ttask);
gst_object_ref (GST_OBJECT (task));
GST_DEBUG_OBJECT (sched, "Entering task %p, thread %p", task,
g_thread_self ());
g_mutex_lock (task->lock);
while (G_LIKELY (task->state != STATE_STOPPED)) {
if (task->state == STATE_PAUSED) {
g_cond_wait (task->cond, task->lock);
if (task->state == STATE_STOPPED)
break;
}
g_mutex_unlock (task->lock);
res = task->func (task->data);
g_mutex_lock (task->lock);
if (G_UNLIKELY (!res)) {
task->state = STATE_STOPPED;
GST_LOCK (task);
while (G_LIKELY (task->state != GST_TASK_STOPPED)) {
while (G_UNLIKELY (task->state == GST_TASK_PAUSED)) {
GST_TASK_SIGNAL (task);
GST_TASK_WAIT (task);
if (task->state == GST_TASK_STOPPED)
goto done;
}
GST_UNLOCK (task);
task->func (task->data);
GST_LOCK (task);
}
g_mutex_unlock (task->lock);
done:
GST_UNLOCK (task);
GST_DEBUG_OBJECT (sched, "Exit task %p, thread %p", task, g_thread_self ());
gst_object_unref (GST_OBJECT (task));
}
@ -299,12 +311,12 @@ gst_thread_scheduler_create_task (GstScheduler * sched, GstTaskFunction func,
GST_THREAD_SCHEDULER_TASK (g_object_new (GST_TYPE_THREAD_SCHEDULER_TASK,
NULL));
gst_object_set_parent (GST_OBJECT (task), GST_OBJECT (sched));
task->func = func;
task->data = data;
GST_TASK_CAST (task)->func = func;
GST_TASK_CAST (task)->data = data;
GST_DEBUG_OBJECT (sched, "Created task %p", task);
return GST_TASK (task);
return GST_TASK_CAST (task);
}
static void

View file

@ -189,7 +189,7 @@ static void gst_fakesrc_set_clock (GstElement * element, GstClock * clock);
static GstElementStateReturn gst_fakesrc_change_state (GstElement * element);
static gboolean gst_fakesrc_loop (GstPad * pad);
static void gst_fakesrc_loop (GstPad * pad);
static guint gst_fakesrc_signals[LAST_SIGNAL] = { 0 };
@ -778,13 +778,12 @@ gst_fakesrc_create_buffer (GstFakeSrc * src)
return buf;
}
static gboolean
static void
gst_fakesrc_loop (GstPad * pad)
{
GstFakeSrc *src;
GstBuffer *buf;
GstClockTime time;
gboolean result = TRUE;
src = GST_FAKESRC (GST_OBJECT_PARENT (pad));
@ -799,14 +798,14 @@ gst_fakesrc_loop (GstPad * pad)
gst_pad_push_event (pad, gst_event_new (GST_EVENT_SEGMENT_DONE));
} else {
gst_pad_push_event (pad, gst_event_new (GST_EVENT_EOS));
result = FALSE;
gst_task_pause (src->task);
goto done;
}
}
if (src->rt_num_buffers == 0) {
gst_pad_push_event (pad, gst_event_new (GST_EVENT_EOS));
result = FALSE;
gst_task_pause (src->task);
goto done;
} else {
if (src->rt_num_buffers > 0)
@ -816,7 +815,7 @@ gst_fakesrc_loop (GstPad * pad)
if (src->eos) {
GST_INFO ("fakesrc is setting eos on pad");
gst_pad_push_event (pad, gst_event_new (GST_EVENT_EOS));
result = FALSE;
gst_task_pause (src->task);
goto done;
}
@ -860,8 +859,6 @@ gst_fakesrc_loop (GstPad * pad)
done:
GST_STREAM_UNLOCK (pad);
return result;
}
#if 0

View file

@ -881,7 +881,7 @@ gst_filesrc_close_file (GstFileSrc * src)
GST_FLAG_UNSET (src, GST_FILESRC_OPEN);
}
static gboolean
static void
gst_filesrc_loop (GstElement * element)
{
GstFileSrc *filesrc;
@ -892,14 +892,13 @@ gst_filesrc_loop (GstElement * element)
result = gst_filesrc_get (filesrc->srcpad, &buffer);
if (result != GST_FLOW_OK) {
return FALSE;
gst_task_stop (filesrc->task);
return;
}
result = gst_pad_push (filesrc->srcpad, buffer);
if (result != GST_FLOW_OK) {
return FALSE;
gst_task_stop (filesrc->task);
}
return TRUE;
}

View file

@ -127,7 +127,7 @@ static void gst_queue_get_property (GObject * object,
static GstFlowReturn gst_queue_chain (GstPad * pad, GstBuffer * buffer);
static GstBuffer *gst_queue_bufferalloc (GstPad * pad, guint64 offset,
guint size, GstCaps * caps);
static gboolean gst_queue_loop (GstPad * pad);
static void gst_queue_loop (GstPad * pad);
static gboolean gst_queue_handle_sink_event (GstPad * pad, GstEvent * event);
@ -644,7 +644,7 @@ out_unref:
return GST_FLOW_OK;
}
static gboolean
static void
gst_queue_loop (GstPad * pad)
{
GstQueue *queue;
@ -697,6 +697,7 @@ restart:
GST_QUEUE_MUTEX_LOCK;
} else {
if (GST_EVENT_TYPE (data) == GST_EVENT_EOS) {
gst_task_pause (queue->task);
result = FALSE;
}
GST_QUEUE_MUTEX_UNLOCK;
@ -711,8 +712,6 @@ restart:
GST_CAT_LOG_OBJECT (queue_dataflow, queue, "signalling item_del");
g_cond_signal (queue->item_del);
GST_QUEUE_MUTEX_UNLOCK;
return result;
}

View file

@ -1,3 +1,4 @@
clock1
clock2
clock3
signedness

View file

@ -1,5 +1,5 @@
include ../Rules
tests_pass = signedness clock1 clock2
tests_pass = signedness clock1 clock2 clock3
tests_fail =
tests_ignore =

View file

@ -43,13 +43,12 @@ main (int argc, char *argv[])
gst_bin_add_many (GST_BIN (pipeline), src, id, sink, NULL);
gst_element_link_many (src, id, sink, NULL);
clock = gst_bin_get_clock (GST_BIN (pipeline));
clock = gst_pipeline_get_clock (GST_PIPELINE (pipeline));
g_assert (clock != NULL);
gst_clock_debug (clock);
gst_clock_debug (clock);
gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_PLAYING);
gst_bin_iterate (GST_BIN (pipeline));
gst_clock_debug (clock);
gst_clock_debug (clock);
gst_clock_debug (clock);

View file

@ -7,18 +7,33 @@
*/
#include <gst/gst.h>
static GstClock *clock = NULL;
void
gst_clock_debug (GstClock * clock, GstElement * fakesink)
{
g_print ("Clock info: time %" G_GUINT64_FORMAT " - Element info: time %"
G_GUINT64_FORMAT "\n", gst_clock_get_time (clock),
gst_element_get_time (fakesink));
GstClockTime time;
time = gst_clock_get_time (clock);
g_print ("Clock info: time %" G_GUINT64_FORMAT " Element %" GST_TIME_FORMAT
"\n", time, GST_TIME_ARGS (time - fakesink->base_time));
}
static void
element_wait (GstElement * element, GstClockTime time)
{
GstClockID id;
id = gst_clock_new_single_shot_id (clock, time + element->base_time);
gst_clock_id_wait (id, NULL);
}
int
main (int argc, char *argv[])
{
GstClock *clock = NULL;
GstElement *pipeline, *fakesrc, *fakesink;
gst_init (&argc, &argv);
@ -41,10 +56,10 @@ main (int argc, char *argv[])
g_usleep (G_USEC_PER_SEC);
gst_clock_debug (clock, fakesink);
gst_element_wait (fakesink, 2 * GST_SECOND);
element_wait (fakesink, 2 * GST_SECOND);
gst_clock_debug (clock, fakesink);
gst_element_wait (fakesink, 5 * GST_SECOND);
element_wait (fakesink, 5 * GST_SECOND);
gst_clock_debug (clock, fakesink);
g_usleep (G_USEC_PER_SEC);

View file

@ -0,0 +1,107 @@
/*
* testsuite program to test clock behaviour
*
* creates a fakesrc ! identity ! fakesink pipeline
* registers a callback on fakesrc and one on fakesink
* also register a normal GLib timeout which should not be reached
*/
#include <gst/gst.h>
static GstClock *clock = NULL;
void
gst_clock_debug (GstClock * clock)
{
GstClockTime time;
time = gst_clock_get_time (clock);
g_print ("Clock info: time %" G_GUINT64_FORMAT "\n", time);
}
static gboolean
ok_callback (GstClock * clock, GstClockTime time,
GstClockID id, gpointer user_data)
{
g_print ("unlocked async id %p\n", id);
return FALSE;
}
static gboolean
error_callback (GstClock * clock, GstClockTime time,
GstClockID id, gpointer user_data)
{
g_print ("unlocked unscheduled async id %p, this is wrong\n", id);
g_assert_not_reached ();
return FALSE;
}
int
main (int argc, char *argv[])
{
GstClockID id, id2;
GstClockTime base;
GstClockReturn result;
gst_init (&argc, &argv);
clock = gst_system_clock_obtain ();
g_assert (clock != NULL);
gst_clock_debug (clock);
base = gst_clock_get_time (clock);
id = gst_clock_new_single_shot_id (clock, base + GST_SECOND);
g_assert (id);
g_print ("waiting one second\n");
result = gst_clock_id_wait (id, NULL);
gst_clock_debug (clock);
g_assert (result == GST_CLOCK_OK);
g_print ("waiting in the past\n");
result = gst_clock_id_wait (id, NULL);
gst_clock_debug (clock);
g_assert (result == GST_CLOCK_EARLY);
gst_clock_id_unref (id);
id = gst_clock_new_single_shot_id (clock, base + 2 * GST_SECOND);
g_print ("waiting one second async id %p\n", id);
result = gst_clock_id_wait_async (id, ok_callback, NULL);
gst_clock_id_unref (id);
g_assert (result == GST_CLOCK_OK);
g_usleep (2 * G_USEC_PER_SEC);
id = gst_clock_new_single_shot_id (clock, base + 5 * GST_SECOND);
g_print ("waiting one second async, with cancel on id %p\n", id);
result = gst_clock_id_wait_async (id, error_callback, NULL);
g_assert (result == GST_CLOCK_OK);
g_usleep (G_USEC_PER_SEC / 2);
g_print ("cancel id %p after 0.5 seconds\n", id);
gst_clock_id_unschedule (id);
gst_clock_id_unref (id);
g_print ("canceled id %p\n", id);
g_print ("waiting multiple one second async, with cancel\n");
id = gst_clock_new_single_shot_id (clock, base + 5 * GST_SECOND);
id2 = gst_clock_new_single_shot_id (clock, base + 6 * GST_SECOND);
g_print ("waiting id %p\n", id);
result = gst_clock_id_wait_async (id, ok_callback, NULL);
g_assert (result == GST_CLOCK_OK);
gst_clock_id_unref (id);
g_print ("waiting id %p\n", id2);
result = gst_clock_id_wait_async (id2, error_callback, NULL);
g_assert (result == GST_CLOCK_OK);
g_usleep (G_USEC_PER_SEC / 2);
g_print ("cancel id %p after 0.5 seconds\n", id2);
gst_clock_id_unschedule (id2);
gst_clock_id_unref (id2);
g_print ("canceled id %p\n", id2);
g_usleep (2 * G_USEC_PER_SEC);
/* success */
return 0;
}

View file

@ -1,3 +1,4 @@
clock1
clock2
clock3
signedness

View file

@ -1,5 +1,5 @@
include ../Rules
tests_pass = signedness clock1 clock2
tests_pass = signedness clock1 clock2 clock3
tests_fail =
tests_ignore =

View file

@ -43,13 +43,12 @@ main (int argc, char *argv[])
gst_bin_add_many (GST_BIN (pipeline), src, id, sink, NULL);
gst_element_link_many (src, id, sink, NULL);
clock = gst_bin_get_clock (GST_BIN (pipeline));
clock = gst_pipeline_get_clock (GST_PIPELINE (pipeline));
g_assert (clock != NULL);
gst_clock_debug (clock);
gst_clock_debug (clock);
gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_PLAYING);
gst_bin_iterate (GST_BIN (pipeline));
gst_clock_debug (clock);
gst_clock_debug (clock);
gst_clock_debug (clock);

View file

@ -7,18 +7,33 @@
*/
#include <gst/gst.h>
static GstClock *clock = NULL;
void
gst_clock_debug (GstClock * clock, GstElement * fakesink)
{
g_print ("Clock info: time %" G_GUINT64_FORMAT " - Element info: time %"
G_GUINT64_FORMAT "\n", gst_clock_get_time (clock),
gst_element_get_time (fakesink));
GstClockTime time;
time = gst_clock_get_time (clock);
g_print ("Clock info: time %" G_GUINT64_FORMAT " Element %" GST_TIME_FORMAT
"\n", time, GST_TIME_ARGS (time - fakesink->base_time));
}
static void
element_wait (GstElement * element, GstClockTime time)
{
GstClockID id;
id = gst_clock_new_single_shot_id (clock, time + element->base_time);
gst_clock_id_wait (id, NULL);
}
int
main (int argc, char *argv[])
{
GstClock *clock = NULL;
GstElement *pipeline, *fakesrc, *fakesink;
gst_init (&argc, &argv);
@ -41,10 +56,10 @@ main (int argc, char *argv[])
g_usleep (G_USEC_PER_SEC);
gst_clock_debug (clock, fakesink);
gst_element_wait (fakesink, 2 * GST_SECOND);
element_wait (fakesink, 2 * GST_SECOND);
gst_clock_debug (clock, fakesink);
gst_element_wait (fakesink, 5 * GST_SECOND);
element_wait (fakesink, 5 * GST_SECOND);
gst_clock_debug (clock, fakesink);
g_usleep (G_USEC_PER_SEC);

107
testsuite/clock/clock3.c Normal file
View file

@ -0,0 +1,107 @@
/*
* testsuite program to test clock behaviour
*
* creates a fakesrc ! identity ! fakesink pipeline
* registers a callback on fakesrc and one on fakesink
* also register a normal GLib timeout which should not be reached
*/
#include <gst/gst.h>
static GstClock *clock = NULL;
void
gst_clock_debug (GstClock * clock)
{
GstClockTime time;
time = gst_clock_get_time (clock);
g_print ("Clock info: time %" G_GUINT64_FORMAT "\n", time);
}
static gboolean
ok_callback (GstClock * clock, GstClockTime time,
GstClockID id, gpointer user_data)
{
g_print ("unlocked async id %p\n", id);
return FALSE;
}
static gboolean
error_callback (GstClock * clock, GstClockTime time,
GstClockID id, gpointer user_data)
{
g_print ("unlocked unscheduled async id %p, this is wrong\n", id);
g_assert_not_reached ();
return FALSE;
}
int
main (int argc, char *argv[])
{
GstClockID id, id2;
GstClockTime base;
GstClockReturn result;
gst_init (&argc, &argv);
clock = gst_system_clock_obtain ();
g_assert (clock != NULL);
gst_clock_debug (clock);
base = gst_clock_get_time (clock);
id = gst_clock_new_single_shot_id (clock, base + GST_SECOND);
g_assert (id);
g_print ("waiting one second\n");
result = gst_clock_id_wait (id, NULL);
gst_clock_debug (clock);
g_assert (result == GST_CLOCK_OK);
g_print ("waiting in the past\n");
result = gst_clock_id_wait (id, NULL);
gst_clock_debug (clock);
g_assert (result == GST_CLOCK_EARLY);
gst_clock_id_unref (id);
id = gst_clock_new_single_shot_id (clock, base + 2 * GST_SECOND);
g_print ("waiting one second async id %p\n", id);
result = gst_clock_id_wait_async (id, ok_callback, NULL);
gst_clock_id_unref (id);
g_assert (result == GST_CLOCK_OK);
g_usleep (2 * G_USEC_PER_SEC);
id = gst_clock_new_single_shot_id (clock, base + 5 * GST_SECOND);
g_print ("waiting one second async, with cancel on id %p\n", id);
result = gst_clock_id_wait_async (id, error_callback, NULL);
g_assert (result == GST_CLOCK_OK);
g_usleep (G_USEC_PER_SEC / 2);
g_print ("cancel id %p after 0.5 seconds\n", id);
gst_clock_id_unschedule (id);
gst_clock_id_unref (id);
g_print ("canceled id %p\n", id);
g_print ("waiting multiple one second async, with cancel\n");
id = gst_clock_new_single_shot_id (clock, base + 5 * GST_SECOND);
id2 = gst_clock_new_single_shot_id (clock, base + 6 * GST_SECOND);
g_print ("waiting id %p\n", id);
result = gst_clock_id_wait_async (id, ok_callback, NULL);
g_assert (result == GST_CLOCK_OK);
gst_clock_id_unref (id);
g_print ("waiting id %p\n", id2);
result = gst_clock_id_wait_async (id2, error_callback, NULL);
g_assert (result == GST_CLOCK_OK);
g_usleep (G_USEC_PER_SEC / 2);
g_print ("cancel id %p after 0.5 seconds\n", id2);
gst_clock_id_unschedule (id2);
gst_clock_id_unref (id2);
g_print ("canceled id %p\n", id2);
g_usleep (2 * G_USEC_PER_SEC);
/* success */
return 0;
}