--- a/gstreamer_core/gst/gstbin.c Wed Mar 31 22:03:18 2010 +0300
+++ b/gstreamer_core/gst/gstbin.c Tue Aug 31 15:30:33 2010 +0300
@@ -51,10 +51,9 @@
*
* gst_object_unref() is used to drop your reference to the bin.
*
- * The <link linkend="GstBin-element-added">element-added</link> signal is
- * fired whenever a new element is added to the bin. Likewise the <link
- * linkend="GstBin-element-removed">element-removed</link> signal is fired
- * whenever an element is removed from the bin.
+ * The #GstBin::element-added signal is fired whenever a new element is added to
+ * the bin. Likewise the #GstBin::element-removed signal is fired whenever an
+ * element is removed from the bin.
*
* <refsect2><title>Notes</title>
* <para>
@@ -174,7 +173,6 @@
#ifdef __SYMBIAN32__
#include <glib_global.h>
#endif
-
/* enable for DURATION caching.
* FIXME currently too many elements don't update
* their duration when it changes so we return inaccurate values. */
@@ -193,9 +191,20 @@
* a toplevel bin */
#define BIN_IS_TOPLEVEL(bin) ((GST_OBJECT_PARENT (bin) == NULL) || bin->priv->asynchandling)
+#define GST_BIN_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GST_TYPE_BIN, GstBinPrivate))
+
struct _GstBinPrivate
{
gboolean asynchandling;
+ /* if we get an ASYNC_DONE message from ourselves, this means that the
+ * subclass will simulate ASYNC behaviour without having ASYNC children. When
+ * such an ASYNC_DONE message is posted while we are doing a state change, we
+ * have to process the message after finishing the state change even when no
+ * child returned GST_STATE_CHANGE_ASYNC. */
+ gboolean pending_async_done;
+
+ guint32 structure_cookie;
};
typedef struct
@@ -216,16 +225,15 @@
GstStateChange transition);
static GstStateChangeReturn gst_bin_get_state_func (GstElement * element,
GstState * state, GstState * pending, GstClockTime timeout);
-static void bin_handle_async_done (GstBin * bin, GstStateChangeReturn ret);
+static void bin_handle_async_done (GstBin * bin, GstStateChangeReturn ret,
+ gboolean flag_pending);
static void bin_handle_async_start (GstBin * bin, gboolean new_base_time);
static void bin_push_state_continue (BinContinueData * data);
static gboolean gst_bin_add_func (GstBin * bin, GstElement * element);
static gboolean gst_bin_remove_func (GstBin * bin, GstElement * element);
-#ifndef GST_DISABLE_INDEX
static void gst_bin_set_index_func (GstElement * element, GstIndex * index);
-#endif
static GstClock *gst_bin_provide_clock_func (GstElement * element);
static gboolean gst_bin_set_clock_func (GstElement * element, GstClock * clock);
@@ -235,6 +243,8 @@
GstMessage * message, GstBin * bin);
static gboolean gst_bin_query (GstElement * element, GstQuery * query);
+static gboolean gst_bin_do_latency_func (GstBin * bin);
+
#ifndef GST_DISABLE_LOADSAVE
static xmlNodePtr gst_bin_save_thyself (GstObject * object, xmlNodePtr parent);
static void gst_bin_restore_thyself (GstObject * object, xmlNodePtr self);
@@ -253,6 +263,7 @@
{
ELEMENT_ADDED,
ELEMENT_REMOVED,
+ DO_LATENCY,
LAST_SIGNAL
};
@@ -265,69 +276,35 @@
/* FILL ME */
};
-static void gst_bin_base_init (gpointer g_class);
-static void gst_bin_class_init (GstBinClass * klass);
-static void gst_bin_init (GstBin * bin);
static void gst_bin_child_proxy_init (gpointer g_iface, gpointer iface_data);
-static GstElementClass *parent_class = NULL;
static guint gst_bin_signals[LAST_SIGNAL] = { 0 };
-/**
- * gst_bin_get_type:
- *
- * Returns: the type of #GstBin
- */
-#ifdef __SYMBIAN32__
-EXPORT_C
-#endif
-
-GType
-gst_bin_get_type (void)
-{
- static GType gst_bin_type = 0;
- const gchar *compat;
-
- if (G_UNLIKELY (gst_bin_type == 0)) {
- static const GTypeInfo bin_info = {
- sizeof (GstBinClass),
- gst_bin_base_init,
- NULL,
- (GClassInitFunc) gst_bin_class_init,
- NULL,
- NULL,
- sizeof (GstBin),
- 0,
- (GInstanceInitFunc) gst_bin_init,
- NULL
- };
- static const GInterfaceInfo child_proxy_info = {
- gst_bin_child_proxy_init,
- NULL,
- NULL
- };
-
- gst_bin_type =
- g_type_register_static (GST_TYPE_ELEMENT, "GstBin", &bin_info, 0);
-
- g_type_add_interface_static (gst_bin_type, GST_TYPE_CHILD_PROXY,
- &child_proxy_info);
-
- GST_DEBUG_CATEGORY_INIT (bin_debug, "bin", GST_DEBUG_BOLD,
- "debugging info for the 'bin' container element");
-
- /* compatibility stuff */
- compat = g_getenv ("GST_COMPAT");
- if (compat != NULL) {
- if (strstr (compat, "no-live-preroll"))
- enable_latency = FALSE;
- else if (strstr (compat, "live-preroll"))
- enable_latency = TRUE;
- }
- }
- return gst_bin_type;
+#define _do_init(type) \
+{ \
+ const gchar *compat; \
+ static const GInterfaceInfo iface_info = { \
+ gst_bin_child_proxy_init, \
+ NULL, \
+ NULL}; \
+ \
+ g_type_add_interface_static (type, GST_TYPE_CHILD_PROXY, &iface_info); \
+ \
+ GST_DEBUG_CATEGORY_INIT (bin_debug, "bin", GST_DEBUG_BOLD, \
+ "debugging info for the 'bin' container element"); \
+ \
+ /* compatibility stuff */ \
+ compat = g_getenv ("GST_COMPAT"); \
+ if (compat != NULL) { \
+ if (strstr (compat, "no-live-preroll")) \
+ enable_latency = FALSE; \
+ else if (strstr (compat, "live-preroll")) \
+ enable_latency = TRUE; \
+ } \
}
+GST_BOILERPLATE_FULL (GstBin, gst_bin, GstElement, GST_TYPE_ELEMENT, _do_init);
+
static void
gst_bin_base_init (gpointer g_class)
{
@@ -385,6 +362,22 @@
iface->get_child_by_index = gst_bin_child_proxy_get_child_by_index;
}
+static gboolean
+_gst_boolean_accumulator (GSignalInvocationHint * ihint,
+ GValue * return_accu, const GValue * handler_return, gpointer dummy)
+{
+ gboolean myboolean;
+
+ myboolean = g_value_get_boolean (handler_return);
+ if (!(ihint->run_type & G_SIGNAL_RUN_CLEANUP))
+ g_value_set_boolean (return_accu, myboolean);
+
+ GST_DEBUG ("invocation %d, %d", ihint->run_type, myboolean);
+
+ /* stop emission */
+ return FALSE;
+}
+
static void
gst_bin_class_init (GstBinClass * klass)
{
@@ -397,7 +390,7 @@
gstobject_class = (GstObjectClass *) klass;
gstelement_class = (GstElementClass *) klass;
- parent_class = g_type_class_peek_parent (klass);
+ g_type_class_add_private (klass, sizeof (GstBinPrivate));
gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_bin_set_property);
gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_bin_get_property);
@@ -414,7 +407,7 @@
g_object_class_install_property (gobject_class, PROP_ASYNC_HANDLING,
g_param_spec_boolean ("async-handling", "Async Handling",
"The bin will handle Asynchronous state changes",
- DEFAULT_ASYNC_HANDLING, G_PARAM_READWRITE));
+ DEFAULT_ASYNC_HANDLING, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
/**
* GstBin::element-added:
@@ -438,6 +431,29 @@
g_signal_new ("element-removed", G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GstBinClass, element_removed), NULL,
NULL, gst_marshal_VOID__OBJECT, G_TYPE_NONE, 1, GST_TYPE_ELEMENT);
+ /**
+ * GstBin::do-latency:
+ * @bin: the #GstBin
+ *
+ * Will be emitted when the bin needs to perform latency calculations. This
+ * signal is only emited for toplevel bins or when async-handling is
+ * enabled.
+ *
+ * Only one signal handler is invoked. If no signals are connected, the
+ * default handler is invoked, which will query and distribute the lowest
+ * possible latency to all sinks.
+ *
+ * Connect to this signal if the default latency calculations are not
+ * sufficient, like when you need different latencies for different sinks in
+ * the same pipeline.
+ *
+ * Since: 0.10.22
+ */
+ gst_bin_signals[DO_LATENCY] =
+ g_signal_new ("do-latency", G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstBinClass, do_latency),
+ _gst_boolean_accumulator, NULL, gst_marshal_BOOLEAN__VOID,
+ G_TYPE_BOOLEAN, 0, G_TYPE_NONE);
gobject_class->dispose = GST_DEBUG_FUNCPTR (gst_bin_dispose);
@@ -450,9 +466,7 @@
gstelement_class->change_state =
GST_DEBUG_FUNCPTR (gst_bin_change_state_func);
gstelement_class->get_state = GST_DEBUG_FUNCPTR (gst_bin_get_state_func);
-#ifndef GST_DISABLE_INDEX
gstelement_class->set_index = GST_DEBUG_FUNCPTR (gst_bin_set_index_func);
-#endif
gstelement_class->provide_clock =
GST_DEBUG_FUNCPTR (gst_bin_provide_clock_func);
gstelement_class->set_clock = GST_DEBUG_FUNCPTR (gst_bin_set_clock_func);
@@ -464,6 +478,8 @@
klass->remove_element = GST_DEBUG_FUNCPTR (gst_bin_remove_func);
klass->handle_message = GST_DEBUG_FUNCPTR (gst_bin_handle_message_func);
+ klass->do_latency = GST_DEBUG_FUNCPTR (gst_bin_do_latency_func);
+
GST_DEBUG ("creating bin thread pool");
err = NULL;
klass->pool =
@@ -474,7 +490,7 @@
}
static void
-gst_bin_init (GstBin * bin)
+gst_bin_init (GstBin * bin, GstBinClass * klass)
{
GstBus *bus;
@@ -492,8 +508,9 @@
bus);
gst_bus_set_sync_handler (bus, (GstBusSyncHandler) bin_bus_handler, bin);
- bin->priv = g_new0 (GstBinPrivate, 1);
+ bin->priv = GST_BIN_GET_PRIVATE (bin);
bin->priv->asynchandling = DEFAULT_ASYNC_HANDLING;
+ bin->priv->structure_cookie = 0;
}
static void
@@ -506,11 +523,12 @@
GST_CAT_DEBUG_OBJECT (GST_CAT_REFCOUNTING, object, "dispose");
- bin_remove_messages (bin, NULL, GST_MESSAGE_ANY);
-
+ GST_OBJECT_LOCK (object);
gst_object_replace ((GstObject **) child_bus_p, NULL);
gst_object_replace ((GstObject **) provided_clock_p, NULL);
gst_object_replace ((GstObject **) clock_provider_p, NULL);
+ bin_remove_messages (bin, NULL, GST_MESSAGE_ANY);
+ GST_OBJECT_UNLOCK (object);
while (bin->children) {
gst_bin_remove (bin, GST_ELEMENT_CAST (bin->children->data));
@@ -520,11 +538,6 @@
GST_STR_NULL (GST_OBJECT_NAME (object)));
}
- if (bin->priv) {
- g_free (bin->priv);
- bin->priv = NULL;
- }
-
G_OBJECT_CLASS (parent_class)->dispose (object);
}
@@ -590,24 +603,45 @@
*
* MT safe
*/
-#ifndef GST_DISABLE_INDEX
static void
gst_bin_set_index_func (GstElement * element, GstIndex * index)
{
GstBin *bin;
- GList *children;
+ gboolean done;
+ GstIterator *it;
bin = GST_BIN (element);
- GST_OBJECT_LOCK (bin);
- for (children = bin->children; children; children = g_list_next (children)) {
- GstElement *child = GST_ELEMENT (children->data);
-
- gst_element_set_index (child, index);
+ it = gst_bin_iterate_elements (bin);
+
+ done = FALSE;
+ while (!done) {
+ gpointer data;
+
+ switch (gst_iterator_next (it, &data)) {
+ case GST_ITERATOR_OK:
+ {
+ GstElement *child = GST_ELEMENT_CAST (data);
+
+ GST_DEBUG_OBJECT (bin, "setting index on %s", GST_ELEMENT_NAME (child));
+ gst_element_set_index (child, index);
+
+ gst_object_unref (child);
+ break;
+ }
+ case GST_ITERATOR_RESYNC:
+ GST_DEBUG_OBJECT (bin, "iterator doing resync");
+ gst_iterator_resync (it);
+ break;
+ default:
+ case GST_ITERATOR_DONE:
+ GST_DEBUG_OBJECT (bin, "iterator done");
+ done = TRUE;
+ break;
+ }
}
- GST_OBJECT_UNLOCK (bin);
+ gst_iterator_free (it);
}
-#endif
/* set the clock on all elements in this bin
*
@@ -616,21 +650,42 @@
static gboolean
gst_bin_set_clock_func (GstElement * element, GstClock * clock)
{
- GList *children;
GstBin *bin;
+ gboolean done;
+ GstIterator *it;
gboolean res = TRUE;
bin = GST_BIN (element);
- GST_OBJECT_LOCK (bin);
- if (element->clock != clock) {
- for (children = bin->children; children; children = g_list_next (children)) {
- GstElement *child = GST_ELEMENT (children->data);
-
- res &= gst_element_set_clock (child, clock);
+ it = gst_bin_iterate_elements (bin);
+
+ done = FALSE;
+ while (!done) {
+ gpointer data;
+
+ switch (gst_iterator_next (it, &data)) {
+ case GST_ITERATOR_OK:
+ {
+ GstElement *child = GST_ELEMENT_CAST (data);
+
+ res &= gst_element_set_clock (child, clock);
+
+ gst_object_unref (child);
+ break;
+ }
+ case GST_ITERATOR_RESYNC:
+ GST_DEBUG_OBJECT (bin, "iterator doing resync");
+ gst_iterator_resync (it);
+ res = TRUE;
+ break;
+ default:
+ case GST_ITERATOR_DONE:
+ GST_DEBUG_OBJECT (bin, "iterator done");
+ done = TRUE;
+ break;
}
}
- GST_OBJECT_UNLOCK (bin);
+ gst_iterator_free (it);
return res;
}
@@ -731,6 +786,7 @@
eq &= GST_MESSAGE_SRC (message) == target->src;
if (target->types)
eq &= (GST_MESSAGE_TYPE (message) & target->types) != 0;
+ GST_LOG ("looking at message %p: %d", message, eq);
return (eq ? 0 : 1);
}
@@ -767,7 +823,7 @@
return result;
}
-/* with LOCK, returns TRUE if message had a valid SRC, takes ref on
+/* with LOCK, returns TRUE if message had a valid SRC, takes ownership of
* the message.
*
* A message that is cached and has the same SRC and type is replaced
@@ -786,12 +842,16 @@
if ((src = GST_MESSAGE_SRC (message))) {
/* first find the previous message posted by this element */
if ((previous = find_message (bin, src, types))) {
+ GstMessage *previous_msg;
+
/* if we found a previous message, replace it */
- gst_message_unref (previous->data);
+ previous_msg = previous->data;
previous->data = message;
- GST_DEBUG_OBJECT (bin, "replace old message %s from %s",
- name, GST_ELEMENT_NAME (src));
+ GST_DEBUG_OBJECT (bin, "replace old message %s from %s with %s message",
+ GST_MESSAGE_TYPE_NAME (previous_msg), GST_ELEMENT_NAME (src), name);
+
+ gst_message_unref (previous_msg);
} else {
/* keep new message */
bin->messages = g_list_prepend (bin->messages, message);
@@ -935,12 +995,14 @@
bin->children = g_list_prepend (bin->children, element);
bin->numchildren++;
bin->children_cookie++;
+ bin->priv->structure_cookie++;
/* distribute the bus */
gst_element_set_bus (element, bin->child_bus);
- /* propagate the current base_time and clock */
+ /* propagate the current base_time, start_time and clock */
gst_element_set_base_time (element, GST_ELEMENT (bin)->base_time);
+ gst_element_set_start_time (element, GST_ELEMENT_START_TIME (bin));
/* it's possible that the element did not accept the clock but
* that is not important right now. When the pipeline goes to PLAYING,
* a new clock will be selected */
@@ -968,7 +1030,7 @@
}
case GST_STATE_CHANGE_NO_PREROLL:
/* ignore all async elements we might have and commit our state */
- bin_handle_async_done (bin, ret);
+ bin_handle_async_done (bin, ret, FALSE);
break;
case GST_STATE_CHANGE_FAILURE:
break;
@@ -1087,6 +1149,8 @@
GstIterator *it;
gboolean is_sink, othersink, found;
GstMessage *clock_message = NULL;
+ GstClock **provided_clock_p;
+ GstElement **clock_provider_p;
GList *walk, *next;
gboolean other_async, this_async, have_no_preroll;
GstStateChangeReturn ret;
@@ -1148,6 +1212,7 @@
* so that others can detect a change in the children list. */
bin->numchildren--;
bin->children_cookie++;
+ bin->priv->structure_cookie++;
if (is_sink && !othersink) {
/* we're not a sink anymore */
@@ -1163,6 +1228,10 @@
bin->clock_dirty = TRUE;
clock_message =
gst_message_new_clock_lost (GST_OBJECT_CAST (bin), bin->provided_clock);
+ provided_clock_p = &bin->provided_clock;
+ clock_provider_p = &bin->clock_provider;
+ gst_object_replace ((GstObject **) provided_clock_p, NULL);
+ gst_object_replace ((GstObject **) clock_provider_p, NULL);
}
/* remove messages for the element, if there was a pending ASYNC_START
@@ -1173,22 +1242,46 @@
for (walk = bin->messages; walk; walk = next) {
GstMessage *message = (GstMessage *) walk->data;
GstElement *src = GST_ELEMENT_CAST (GST_MESSAGE_SRC (message));
+ gboolean remove;
next = g_list_next (walk);
-
- if (GST_MESSAGE_TYPE (message) == GST_MESSAGE_ASYNC_START) {
- if (src == element)
- this_async = TRUE;
- else
- other_async = TRUE;
-
- GST_DEBUG_OBJECT (GST_MESSAGE_SRC (message),
- "looking at message %p", message);
+ remove = FALSE;
+
+ switch (GST_MESSAGE_TYPE (message)) {
+ case GST_MESSAGE_ASYNC_START:
+ if (src == element)
+ this_async = TRUE;
+ else
+ other_async = TRUE;
+
+ GST_DEBUG_OBJECT (src, "looking at message %p", message);
+ break;
+ case GST_MESSAGE_STRUCTURE_CHANGE:
+ {
+ GstElement *owner;
+
+ GST_DEBUG_OBJECT (src, "looking at structure change message %p",
+ message);
+ /* it's unlikely that this message is still in the list of messages
+ * because this would mean that a link/unlink is busy in another thread
+ * while we remove the element. We still have to remove the message
+ * because we might not receive the done message anymore when the element
+ * is removed from the bin. */
+ gst_message_parse_structure_change (message, NULL, &owner, NULL);
+ if (owner == element)
+ remove = TRUE;
+ break;
+ }
+ default:
+ break;
}
- if (src == element) {
+ if (src == element)
+ remove = TRUE;
+
+ if (remove) {
/* delete all message types */
- GST_DEBUG_OBJECT (GST_MESSAGE_SRC (message),
- "deleting message %p of element \"%s\"", message, elem_name);
+ GST_DEBUG_OBJECT (src, "deleting message %p of element \"%s\"",
+ message, elem_name);
bin->messages = g_list_delete_link (bin->messages, walk);
gst_message_unref (message);
}
@@ -1215,7 +1308,7 @@
else
ret = GST_STATE_CHANGE_SUCCESS;
- bin_handle_async_done (bin, ret);
+ bin_handle_async_done (bin, ret, FALSE);
} else {
GST_DEBUG_OBJECT (bin,
"recalc state preroll: %d, other async: %d, this async %d",
@@ -1695,18 +1788,28 @@
gboolean linked = FALSE;
GST_OBJECT_LOCK (element);
- /* don't touch degree if element has no sourcepads */
+ /* don't touch degree if element has no sinkpads */
if (element->numsinkpads != 0) {
/* loop over all sinkpads, decrement degree for all connected
* elements in this bin */
GList *pads;
for (pads = element->sinkpads; pads; pads = g_list_next (pads)) {
- GstPad *peer;
-
- if ((peer = gst_pad_get_peer (GST_PAD_CAST (pads->data)))) {
+ GstPad *pad, *peer;
+
+ pad = GST_PAD_CAST (pads->data);
+
+ if ((peer = gst_pad_get_peer (pad))) {
GstElement *peer_element;
+ /* we're iterating over the sinkpads, this is the peer and thus the
+ * srcpad, check if it's busy in a link/unlink */
+ if (G_UNLIKELY (find_message (bit->bin, GST_OBJECT_CAST (peer),
+ GST_MESSAGE_STRUCTURE_CHANGE))) {
+ gst_object_unref (peer);
+ continue;
+ }
+
if ((peer_element = gst_pad_get_parent_element (peer))) {
GST_OBJECT_LOCK (peer_element);
/* check that we don't go outside of this bin */
@@ -1729,7 +1832,10 @@
GST_ELEMENT_NAME (peer_element), old_deg, new_deg,
GST_ELEMENT_NAME (element));
- /* update degree */
+ /* update degree, it is possible that an element was in 0 and
+ * reaches -1 here. This would mean that the element had no sinkpads
+ * but became linked while the state change was happening. We will
+ * resync on this with the structure change message. */
if (new_deg == 0) {
/* degree hit 0, add to queue */
add_to_queue (bit, peer_element);
@@ -1857,7 +1963,7 @@
gst_iterator_new (sizeof (GstBinSortIterator),
GST_TYPE_ELEMENT,
GST_OBJECT_GET_LOCK (bin),
- &bin->children_cookie,
+ &bin->priv->structure_cookie,
(GstIteratorNextFunction) gst_bin_sort_iterator_next,
(GstIteratorItemFunction) NULL,
(GstIteratorResyncFunction) gst_bin_sort_iterator_resync,
@@ -1909,14 +2015,20 @@
static GstStateChangeReturn
gst_bin_element_set_state (GstBin * bin, GstElement * element,
- GstClockTime base_time, GstState current, GstState next)
+ GstClockTime base_time, GstClockTime start_time, GstState current,
+ GstState next)
{
GstStateChangeReturn ret;
gboolean locked;
GList *found;
+ GST_STATE_LOCK (element);
+
+ GST_OBJECT_LOCK (element);
+ /* set base_time and start time on child */
+ GST_ELEMENT_START_TIME (element) = start_time;
+ element->base_time = base_time;
/* peel off the locked flag */
- GST_OBJECT_LOCK (element);
locked = GST_OBJECT_FLAG_IS_SET (element, GST_ELEMENT_LOCKED_STATE);
/* get previous state return */
ret = GST_STATE_RETURN (element);
@@ -1970,12 +2082,11 @@
GST_ELEMENT_NAME (element), gst_element_state_get_name (next),
GST_TIME_ARGS (base_time));
- /* set base_time on child */
- gst_element_set_base_time (element, base_time);
-
/* change state */
ret = gst_element_set_state (element, next);
+ GST_STATE_UNLOCK (element);
+
return ret;
locked:
@@ -1983,12 +2094,14 @@
GST_DEBUG_OBJECT (element,
"element is locked, return previous return %s",
gst_element_state_change_return_get_name (ret));
+ GST_STATE_UNLOCK (element);
return ret;
}
was_busy:
{
GST_DEBUG_OBJECT (element, "element was busy, delaying state change");
GST_OBJECT_UNLOCK (bin);
+ GST_STATE_UNLOCK (element);
return GST_STATE_CHANGE_ASYNC;
}
}
@@ -2075,15 +2188,50 @@
}
}
-/* do latency correction. We do a latency query on the bin, and then send a
- * LATENCY event on the elements fo configure them */
+/**
+ * gst_bin_recalculate_latency:
+ * @bin: a #GstBin
+ *
+ * Query @bin for the current latency using and reconfigures this latency to all the
+ * elements with a LATENCY event.
+ *
+ * This method is typically called on the pipeline when a #GST_MESSAGE_LATENCY
+ * is posted on the bus.
+ *
+ * This function simply emits the 'do-latency' signal so any custom latency
+ * calculations will be performed.
+ *
+ * Returns: %TRUE if the latency could be queried and reconfigured.
+ *
+ * Since: 0.10.22.
+ */
+#ifdef __SYMBIAN32__
+EXPORT_C
+#endif
+
+gboolean
+gst_bin_recalculate_latency (GstBin * bin)
+{
+ gboolean res;
+
+ g_signal_emit (G_OBJECT (bin), gst_bin_signals[DO_LATENCY], 0, &res);
+ GST_DEBUG_OBJECT (bin, "latency returned %d", res);
+
+ return res;
+}
+
static gboolean
-do_bin_latency (GstElement * element)
+gst_bin_do_latency_func (GstBin * bin)
{
GstQuery *query;
+ GstElement *element;
GstClockTime min_latency, max_latency;
gboolean res;
+ g_return_val_if_fail (GST_IS_BIN (bin), FALSE);
+
+ element = GST_ELEMENT_CAST (bin);
+
GST_DEBUG_OBJECT (element, "querying latency");
query = gst_query_new_latency ();
@@ -2136,7 +2284,7 @@
GstState current, next;
gboolean have_async;
gboolean have_no_preroll;
- GstClockTime base_time;
+ GstClockTime base_time, start_time;
GstIterator *it;
gboolean done;
@@ -2160,7 +2308,7 @@
GST_OBJECT_UNLOCK (bin);
if (toplevel)
- do_bin_latency (element);
+ gst_bin_recalculate_latency (bin);
break;
}
case GST_STATE_PAUSED:
@@ -2210,6 +2358,7 @@
restart:
/* take base_time */
base_time = gst_element_get_base_time (element);
+ start_time = gst_element_get_start_time (element);
have_no_preroll = FALSE;
@@ -2225,7 +2374,9 @@
child = GST_ELEMENT_CAST (data);
/* set state and base_time now */
- ret = gst_bin_element_set_state (bin, child, base_time, current, next);
+ ret =
+ gst_bin_element_set_state (bin, child, base_time, start_time,
+ current, next);
switch (ret) {
case GST_STATE_CHANGE_SUCCESS:
@@ -2280,8 +2431,13 @@
goto done;
if (have_no_preroll) {
+ GST_CAT_DEBUG (GST_CAT_STATES,
+ "we have NO_PREROLL elements %s -> NO_PREROLL",
+ gst_element_state_change_return_get_name (ret));
ret = GST_STATE_CHANGE_NO_PREROLL;
} else if (have_async) {
+ GST_CAT_DEBUG (GST_CAT_STATES, "we have ASYNC elements %s -> ASYNC",
+ gst_element_state_change_return_get_name (ret));
ret = GST_STATE_CHANGE_ASYNC;
}
@@ -2290,8 +2446,13 @@
GST_OBJECT_LOCK (bin);
bin->polling = FALSE;
- if (ret != GST_STATE_CHANGE_ASYNC) {
- /* no element returned ASYNC, we can just complete. */
+ /* it's possible that we did not get ASYNC from the children while the bin is
+ * simulating ASYNC behaviour by posting an ASYNC_DONE message on the bus with
+ * itself as the source. In that case we still want to check if the state
+ * change completed. */
+ if (ret != GST_STATE_CHANGE_ASYNC && !bin->priv->pending_async_done) {
+ /* no element returned ASYNC and there are no pending async_done messages,
+ * we can just complete. */
GST_DEBUG_OBJECT (bin, "no async elements");
goto state_end;
}
@@ -2313,10 +2474,11 @@
bin_remove_messages (bin, NULL, GST_MESSAGE_ASYNC_DONE);
GST_DEBUG_OBJECT (bin, "async elements commited");
- bin_handle_async_done (bin, GST_STATE_CHANGE_SUCCESS);
+ bin_handle_async_done (bin, GST_STATE_CHANGE_SUCCESS, FALSE);
}
state_end:
+ bin->priv->pending_async_done = FALSE;
GST_OBJECT_UNLOCK (bin);
GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element,
@@ -2405,7 +2567,6 @@
GstBin *bin;
GstState current, next, pending;
GstStateChange transition;
- GstStateChangeReturn ret;
bin = data->bin;
pending = data->pending;
@@ -2426,6 +2587,7 @@
transition = (GstStateChange) GST_STATE_TRANSITION (current, next);
GST_STATE_NEXT (bin) = next;
+ GST_STATE_PENDING (bin) = pending;
/* mark busy */
GST_STATE_RETURN (bin) = GST_STATE_CHANGE_ASYNC;
GST_OBJECT_UNLOCK (bin);
@@ -2435,7 +2597,7 @@
gst_element_state_get_name (current),
gst_element_state_get_name (next), gst_element_state_get_name (pending));
- ret = gst_element_change_state (GST_ELEMENT_CAST (bin), transition);
+ gst_element_change_state (GST_ELEMENT_CAST (bin), transition);
GST_STATE_UNLOCK (bin);
GST_DEBUG_OBJECT (bin, "state continue done");
@@ -2513,7 +2675,6 @@
if (GST_STATE_RETURN (bin) == GST_STATE_CHANGE_NO_PREROLL)
goto was_no_preroll;
-
old_state = GST_STATE (bin);
/* when we PLAYING we go back to PAUSED, when preroll happens, we go back to
@@ -2572,7 +2733,8 @@
* This function is called with the OBJECT lock.
*/
static void
-bin_handle_async_done (GstBin * bin, GstStateChangeReturn ret)
+bin_handle_async_done (GstBin * bin, GstStateChangeReturn ret,
+ gboolean flag_pending)
{
GstState current, pending, target;
GstStateChangeReturn old_ret;
@@ -2616,6 +2778,11 @@
/* update current state */
current = GST_STATE (bin) = old_next;
} else {
+ GST_CAT_INFO_OBJECT (GST_CAT_STATES, bin,
+ "setting state from %s to %s, pending %s",
+ gst_element_state_get_name (old_state),
+ gst_element_state_get_name (old_state),
+ gst_element_state_get_name (pending));
current = old_state;
}
@@ -2647,7 +2814,7 @@
cont->pending = pending;
/* mark busy */
GST_STATE_RETURN (bin) = GST_STATE_CHANGE_ASYNC;
- GST_STATE_NEXT (bin) = pending;
+ GST_STATE_NEXT (bin) = GST_STATE_GET_NEXT (old_state, pending);
}
if (old_next != GST_STATE_PLAYING) {
@@ -2687,6 +2854,10 @@
was_busy:
{
GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, bin, "state change busy");
+ /* if we were busy with a state change and we are requested to flag a
+ * pending async done, we do so here */
+ if (flag_pending)
+ bin->priv->pending_async_done = TRUE;
return;
}
nothing_pending:
@@ -2755,18 +2926,16 @@
{
GstObject *src;
GstMessageType type;
+ GstMessage *tmessage;
+ guint32 seqnum;
src = GST_MESSAGE_SRC (message);
type = GST_MESSAGE_TYPE (message);
-// Message src can be NULL
- if(src)
GST_DEBUG_OBJECT (bin, "[msg %p] handling child %s message of type %s",
- message, GST_ELEMENT_NAME (src), GST_MESSAGE_TYPE_NAME (message));
- else
- GST_DEBUG_OBJECT (bin, "[msg %p] handling child %s message of type %s",
- message, "Null ", GST_MESSAGE_TYPE_NAME (message));
-
+ message, src ? GST_ELEMENT_NAME (src) : "(NULL)",
+ GST_MESSAGE_TYPE_NAME (message));
+
switch (type) {
case GST_MESSAGE_EOS:
{
@@ -2780,9 +2949,13 @@
/* if we are completely EOS, we forward an EOS message */
if (eos) {
- GST_DEBUG_OBJECT (bin, "all sinks posted EOS");
- gst_element_post_message (GST_ELEMENT_CAST (bin),
- gst_message_new_eos (GST_OBJECT_CAST (bin)));
+ seqnum = gst_message_get_seqnum (message);
+ tmessage = gst_message_new_eos (GST_OBJECT_CAST (bin));
+ gst_message_set_seqnum (tmessage, seqnum);
+
+ GST_DEBUG_OBJECT (bin,
+ "all sinks posted EOS, posting seqnum #%" G_GUINT32_FORMAT, seqnum);
+ gst_element_post_message (GST_ELEMENT_CAST (bin), tmessage);
}
break;
}
@@ -2794,13 +2967,37 @@
gst_message_unref (message);
break;
}
- case GST_MESSAGE_SEGMENT_START:
+ case GST_MESSAGE_SEGMENT_START:{
+ gboolean post = FALSE;
+ GstFormat format;
+ gint64 position;
+
+ gst_message_parse_segment_start (message, &format, &position);
+ seqnum = gst_message_get_seqnum (message);
+
GST_OBJECT_LOCK (bin);
+ /* if this is the first segment-start, post to parent but not to the
+ * application */
+ if (!find_message (bin, NULL, GST_MESSAGE_SEGMENT_START) &&
+ (GST_OBJECT_PARENT (bin) != NULL)) {
+ post = TRUE;
+ }
/* replace any previous segment_start message from this source
* with the new segment start message */
bin_replace_message (bin, message, GST_MESSAGE_SEGMENT_START);
GST_OBJECT_UNLOCK (bin);
+ if (post) {
+ tmessage = gst_message_new_segment_start (GST_OBJECT_CAST (bin),
+ format, position);
+ gst_message_set_seqnum (tmessage, seqnum);
+
+ /* post segment start with initial format and position. */
+ GST_DEBUG_OBJECT (bin, "posting SEGMENT_START (%u) bus message: %p",
+ seqnum, message);
+ gst_element_post_message (GST_ELEMENT_CAST (bin), tmessage);
+ }
break;
+ }
case GST_MESSAGE_SEGMENT_DONE:
{
gboolean post = FALSE;
@@ -2808,6 +3005,7 @@
gint64 position;
gst_message_parse_segment_done (message, &format, &position);
+ seqnum = gst_message_get_seqnum (message);
GST_OBJECT_LOCK (bin);
bin_replace_message (bin, message, GST_MESSAGE_SEGMENT_START);
@@ -2823,10 +3021,14 @@
}
GST_OBJECT_UNLOCK (bin);
if (post) {
+ tmessage = gst_message_new_segment_done (GST_OBJECT_CAST (bin),
+ format, position);
+ gst_message_set_seqnum (tmessage, seqnum);
+
/* post segment done with latest format and position. */
- gst_element_post_message (GST_ELEMENT_CAST (bin),
- gst_message_new_segment_done (GST_OBJECT_CAST (bin),
- format, position));
+ GST_DEBUG_OBJECT (bin, "posting SEGMENT_DONE (%u) bus message: %p",
+ seqnum, message);
+ gst_element_post_message (GST_ELEMENT_CAST (bin), tmessage);
}
break;
}
@@ -2897,12 +3099,9 @@
{
gboolean new_base_time;
GstState target;
- if(src)
+
GST_DEBUG_OBJECT (bin, "ASYNC_START message %p, %s", message,
- GST_OBJECT_NAME (src));
- else
- GST_DEBUG_OBJECT (bin, "ASYNC_START message %p, %s", message,
- "Null Source");
+ src ? GST_OBJECT_NAME (src) : "(NULL)");
gst_message_parse_async_start (message, &new_base_time);
@@ -2931,13 +3130,9 @@
case GST_MESSAGE_ASYNC_DONE:
{
GstState target;
-
- if(src)
+
GST_DEBUG_OBJECT (bin, "ASYNC_DONE message %p, %s", message,
- GST_OBJECT_NAME (src));
- else
- GST_DEBUG_OBJECT (bin, "ASYNC_DONE message %p, %s", message,
- "Null Source");
+ src ? GST_OBJECT_NAME (src) : "(NULL)");
GST_OBJECT_LOCK (bin);
target = GST_STATE_TARGET (bin);
@@ -2954,7 +3149,13 @@
bin_remove_messages (bin, NULL, GST_MESSAGE_ASYNC_DONE);
GST_DEBUG_OBJECT (bin, "async elements commited");
- bin_handle_async_done (bin, GST_STATE_CHANGE_SUCCESS);
+ /* when we get an async done message when a state change was busy, we
+ * need to set the pending_done flag so that at the end of the state
+ * change we can see if we need to verify pending async elements, hence
+ * the TRUE argument here. */
+ bin_handle_async_done (bin, GST_STATE_CHANGE_SUCCESS, TRUE);
+ } else {
+ GST_DEBUG_OBJECT (bin, "there are more async elements pending");
}
GST_OBJECT_UNLOCK (bin);
break;
@@ -2968,6 +3169,34 @@
break;
}
}
+ case GST_MESSAGE_STRUCTURE_CHANGE:
+ {
+ gboolean busy;
+
+ gst_message_parse_structure_change (message, NULL, NULL, &busy);
+
+ GST_OBJECT_LOCK (bin);
+ if (busy) {
+ /* while the pad is busy, avoid following it when doing state changes.
+ * Don't update the cookie yet, we will do that after the structure
+ * change finished and we are ready to inspect the new updated
+ * structure. */
+ bin_replace_message (bin, message, GST_MESSAGE_STRUCTURE_CHANGE);
+ message = NULL;
+ } else {
+ /* a pad link/unlink ended, signal the state change iterator that we
+ * need to resync by updating the structure_cookie. */
+ bin_remove_messages (bin, GST_MESSAGE_SRC (message),
+ GST_MESSAGE_STRUCTURE_CHANGE);
+ bin->priv->structure_cookie++;
+ }
+ GST_OBJECT_UNLOCK (bin);
+
+ if (message)
+ gst_message_unref (message);
+
+ break;
+ }
default:
goto forward;
}
@@ -3023,6 +3252,7 @@
gst_object_unref (item);
return TRUE;
}
+
static void
bin_query_duration_done (GstBin * bin, QueryFold * fold)
{
@@ -3062,6 +3292,7 @@
gst_object_unref (item);
return TRUE;
}
+
static void
bin_query_position_done (GstBin * bin, QueryFold * fold)
{
@@ -3089,14 +3320,16 @@
/* for the combined latency we collect the MAX of all min latencies and
* the MIN of all max latencies */
- if (min > fold->min)
- fold->min = min;
- if (fold->max == -1)
- fold->max = max;
- else if (max < fold->max)
- fold->max = max;
- if (fold->live == FALSE)
- fold->live = live;
+ if (live) {
+ if (min > fold->min)
+ fold->min = min;
+ if (fold->max == -1)
+ fold->max = max;
+ else if (max < fold->max)
+ fold->max = max;
+ if (fold->live == FALSE)
+ fold->live = live;
+ }
} else {
g_value_set_boolean (ret, FALSE);
GST_DEBUG_OBJECT (item, "failed query");
@@ -3210,6 +3443,7 @@
fold_data.query = query;
+ /* set the result of the query to FALSE initially */
g_value_init (&ret, G_TYPE_BOOLEAN);
g_value_set_boolean (&ret, res);
@@ -3458,12 +3692,12 @@
GST_CAT_INFO (GST_CAT_XML, "[%s]: saving %d children",
GST_ELEMENT_NAME (bin), bin->numchildren);
- children = bin->children;
+ children = g_list_last (bin->children);
while (children) {
child = GST_ELEMENT (children->data);
elementnode = xmlNewChild (childlist, NULL, (xmlChar *) "element", NULL);
gst_object_save_thyself (GST_OBJECT (child), elementnode);
- children = g_list_next (children);
+ children = g_list_previous (children);
}
return childlist;
}