diff -r 9b2c3c7a1a9c -r 567bb019e3e3 gstreamer_core/gst/gstbin.c --- 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 element-added signal is - * fired whenever a new element is added to the bin. Likewise the element-removed 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. * * Notes * @@ -174,7 +173,6 @@ #ifdef __SYMBIAN32__ #include #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; }