X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=gst%2Fgstbin.c;h=77bbc3e85f0a366d41d68561b57aee05167fd7a6;hb=e10266e3f3cf9b05b69198b1ac6faa9a62840e30;hp=8f8bd73b493a1cc632fb745baa8ee211e00033c8;hpb=6c6bb0e21739208fc85fb068983f3b86dc4d177a;p=platform%2Fupstream%2Fgstreamer.git diff --git a/gst/gstbin.c b/gst/gstbin.c index 8f8bd73..77bbc3e 100644 --- a/gst/gstbin.c +++ b/gst/gstbin.c @@ -17,8 +17,8 @@ * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. * * MT safe. */ @@ -78,7 +78,7 @@ * a SEGMENT_START have posted a SEGMENT_DONE. * * - * GST_MESSAGE_DURATION + * GST_MESSAGE_DURATION_CHANGED * Is posted by an element that detected a change * in the stream duration. The default bin behaviour is to clear any * cached duration values so that the next duration query will perform @@ -148,13 +148,11 @@ * * * A #GstBin will by default forward any event sent to it to all sink elements. - * If all the sinks return TRUE, the bin will also return TRUE, else FALSE is - * returned. If no sinks are in the bin, the event handler will return TRUE. + * If all the sinks return %TRUE, the bin will also return %TRUE, else %FALSE is + * returned. If no sinks are in the bin, the event handler will return %TRUE. * * * - * - * Last reviewed on 2012-03-28 (0.11.3) */ #include "gst_private.h" @@ -198,6 +196,9 @@ struct _GstBinPrivate gboolean message_forward; gboolean posted_eos; + gboolean posted_playing; + + GList *contexts; }; typedef struct @@ -216,8 +217,7 @@ static void gst_bin_get_property (GObject * object, guint prop_id, static GstStateChangeReturn gst_bin_change_state_func (GstElement * element, GstStateChange transition); -static void gst_bin_state_changed (GstElement * element, GstState oldstate, - GstState newstate, GstState pending); +static gboolean gst_bin_post_message (GstElement * element, GstMessage * msg); 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, @@ -242,6 +242,7 @@ static gboolean gst_bin_send_event (GstElement * element, GstEvent * event); static GstBusSyncReply bin_bus_handler (GstBus * bus, GstMessage * message, GstBin * bin); static gboolean gst_bin_query (GstElement * element, GstQuery * query); +static void gst_bin_set_context (GstElement * element, GstContext * context); static gboolean gst_bin_do_latency_func (GstBin * bin); @@ -369,11 +370,9 @@ gst_bin_class_init (GstBinClass * klass) /** * GstBin:async-handling: * - * If set to #TRUE, the bin will handle asynchronous state changes. + * If set to %TRUE, the bin will handle asynchronous state changes. * This should be used only if the bin subclass is modifying the state * of its children on its own. - * - * Since: 0.10.13 */ g_object_class_install_property (gobject_class, PROP_ASYNC_HANDLING, g_param_spec_boolean ("async-handling", "Async Handling", @@ -407,7 +406,7 @@ gst_bin_class_init (GstBinClass * klass) * @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 + * signal is only emitted for toplevel bins or when async-handling is * enabled. * * Only one signal handler is invoked. If no signals are connected, the @@ -417,8 +416,6 @@ gst_bin_class_init (GstBinClass * klass) * 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), @@ -437,8 +434,6 @@ gst_bin_class_init (GstBinClass * klass) * source. The structure of the message is named 'GstBinForwarded' and contains * a field named 'message' of type GST_TYPE_MESSAGE that contains the original * forwarded message. - * - * Since: 0.10.31 */ g_object_class_install_property (gobject_class, PROP_MESSAGE_FORWARD, g_param_spec_boolean ("message-forward", "Message Forward", @@ -447,7 +442,7 @@ gst_bin_class_init (GstBinClass * klass) gobject_class->dispose = gst_bin_dispose; - gst_element_class_set_metadata (gstelement_class, "Generic bin", + gst_element_class_set_static_metadata (gstelement_class, "Generic bin", "Generic/Bin", "Simple container object", "Erik Walthinsen ," @@ -455,7 +450,7 @@ gst_bin_class_init (GstBinClass * klass) gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_bin_change_state_func); - gstelement_class->state_changed = GST_DEBUG_FUNCPTR (gst_bin_state_changed); + gstelement_class->post_message = GST_DEBUG_FUNCPTR (gst_bin_post_message); gstelement_class->get_state = GST_DEBUG_FUNCPTR (gst_bin_get_state_func); #if 0 gstelement_class->get_index = GST_DEBUG_FUNCPTR (gst_bin_get_index_func); @@ -467,6 +462,7 @@ gst_bin_class_init (GstBinClass * klass) gstelement_class->send_event = GST_DEBUG_FUNCPTR (gst_bin_send_event); gstelement_class->query = GST_DEBUG_FUNCPTR (gst_bin_query); + gstelement_class->set_context = GST_DEBUG_FUNCPTR (gst_bin_set_context); klass->add_element = GST_DEBUG_FUNCPTR (gst_bin_add_func); klass->remove_element = GST_DEBUG_FUNCPTR (gst_bin_remove_func); @@ -500,7 +496,8 @@ gst_bin_init (GstBin * bin) bin->child_bus = bus; GST_DEBUG_OBJECT (bin, "using bus %" GST_PTR_FORMAT " to listen to children", bus); - gst_bus_set_sync_handler (bus, (GstBusSyncHandler) bin_bus_handler, bin); + gst_bus_set_sync_handler (bus, (GstBusSyncHandler) bin_bus_handler, bin, + NULL); bin->priv = GST_BIN_GET_PRIVATE (bin); bin->priv->asynchandling = DEFAULT_ASYNC_HANDLING; @@ -533,6 +530,8 @@ gst_bin_dispose (GObject * object) GST_STR_NULL (GST_OBJECT_NAME (object))); } + g_list_free_full (bin->priv->contexts, (GDestroyNotify) gst_context_unref); + G_OBJECT_CLASS (parent_class)->dispose (object); } @@ -887,8 +886,8 @@ find_message (GstBin * bin, GstObject * src, GstMessageType types) guint i; for (i = 0; i < 32; i++) - if (types & (1 << i)) - GST_DEBUG_OBJECT (bin, " %s", gst_message_type_get_name (1 << i)); + if (types & (1U << i)) + GST_DEBUG_OBJECT (bin, " %s", gst_message_type_get_name (1U << i)); } #endif } @@ -1008,6 +1007,66 @@ is_eos (GstBin * bin, guint32 * seqnum) return result && n_eos > 0; } + +/* Check if the bin is STREAM_START. We do this by scanning all sinks and + * checking if they posted an STREAM_START message. + * + * call with bin LOCK */ +static gboolean +is_stream_start (GstBin * bin, guint32 * seqnum, gboolean * have_group_id, + guint * group_id) +{ + gboolean result; + GList *walk, *msgs; + guint tmp_group_id; + gboolean first = TRUE, same_group_id = TRUE; + + *have_group_id = TRUE; + *group_id = 0; + result = TRUE; + for (walk = bin->children; walk; walk = g_list_next (walk)) { + GstElement *element; + + element = GST_ELEMENT_CAST (walk->data); + if (bin_element_is_sink (element, bin) == 0) { + /* check if element posted STREAM_START */ + if ((msgs = + find_message (bin, GST_OBJECT_CAST (element), + GST_MESSAGE_STREAM_START))) { + GST_DEBUG ("sink '%s' posted STREAM_START", GST_ELEMENT_NAME (element)); + *seqnum = gst_message_get_seqnum (GST_MESSAGE_CAST (msgs->data)); + if (gst_message_parse_group_id (GST_MESSAGE_CAST (msgs->data), + &tmp_group_id)) { + if (first) { + first = FALSE; + *group_id = tmp_group_id; + } else { + if (tmp_group_id != *group_id) + same_group_id = FALSE; + } + } else { + *have_group_id = FALSE; + } + } else { + GST_DEBUG ("sink '%s' did not post STREAM_START yet", + GST_ELEMENT_NAME (element)); + result = FALSE; + break; + } + } + } + + /* If all have a group_id we only consider this stream started + * if all group ids were the same and all sinks posted a stream-start + * message */ + if (*have_group_id) + return same_group_id && result; + /* otherwise consider this stream started after all sinks + * have reported stream-start for backward compatibility. + * FIXME 2.0: This should go away! */ + return result; +} + static void unlink_pads (const GValue * item, gpointer user_data) { @@ -1037,11 +1096,12 @@ gst_bin_add_func (GstBin * bin, GstElement * element) gboolean is_sink, is_source, provides_clock, requires_clock; GstMessage *clock_message = NULL, *async_message = NULL; GstStateChangeReturn ret; + GList *l; GST_DEBUG_OBJECT (bin, "element :%s", GST_ELEMENT_NAME (element)); /* we obviously can't add ourself to ourself */ - if (G_UNLIKELY (GST_ELEMENT_CAST (element) == GST_ELEMENT_CAST (bin))) + if (G_UNLIKELY (element == GST_ELEMENT_CAST (bin))) goto adding_itself; /* get the element name to make sure it is unique in this bin. */ @@ -1094,7 +1154,8 @@ gst_bin_add_func (GstBin * bin, GstElement * element) bin->children = g_list_prepend (bin->children, element); bin->numchildren++; bin->children_cookie++; - bin->priv->structure_cookie++; + if (!GST_BIN_IS_NO_RESYNC (bin)) + bin->priv->structure_cookie++; /* distribute the bus */ gst_element_set_bus (element, bin->child_bus); @@ -1107,6 +1168,9 @@ gst_bin_add_func (GstBin * bin, GstElement * element) * a new clock will be selected */ gst_element_set_clock (element, GST_ELEMENT_CLOCK (bin)); + for (l = bin->priv->contexts; l; l = l->next) + gst_element_set_context (element, l->data); + #if 0 /* set the cached index on the children */ if (bin->priv->index) @@ -1148,10 +1212,10 @@ no_state_recalc: /* post the messages on the bus of the element so that the bin can handle * them */ if (clock_message) - gst_element_post_message (GST_ELEMENT_CAST (element), clock_message); + gst_element_post_message (element, clock_message); if (async_message) - gst_element_post_message (GST_ELEMENT_CAST (element), async_message); + gst_element_post_message (element, async_message); /* unlink all linked pads */ it = gst_element_iterate_pads (element); @@ -1215,7 +1279,7 @@ had_parent: * * MT safe. * - * Returns: TRUE if the element could be added, FALSE if + * Returns: %TRUE if the element could be added, %FALSE if * the bin does not want to accept the element. */ gboolean @@ -1340,7 +1404,8 @@ gst_bin_remove_func (GstBin * bin, GstElement * element) * so that others can detect a change in the children list. */ bin->numchildren--; bin->children_cookie++; - bin->priv->structure_cookie++; + if (!GST_BIN_IS_NO_RESYNC (bin)) + bin->priv->structure_cookie++; if (is_sink && !othersink) { /* we're not a sink anymore */ @@ -1527,7 +1592,7 @@ not_in_bin: * * MT safe. * - * Returns: TRUE if the element could be removed, FALSE if + * Returns: %TRUE if the element could be removed, %FALSE if * the bin does not want to remove the element. */ gboolean @@ -1566,12 +1631,9 @@ no_function: * * Gets an iterator for the elements in this bin. * - * Each element yielded by the iterator will have its refcount increased, so - * unref after use. - * * MT safe. Caller owns returned value. * - * Returns: (transfer full): a #GstIterator of #GstElement, or NULL + * Returns: (transfer full): a #GstIterator of #GstElement, or %NULL */ GstIterator * gst_bin_iterate_elements (GstBin * bin) @@ -1609,12 +1671,9 @@ iterate_child_recurse (GstIterator * it, const GValue * item) * Gets an iterator for the elements in this bin. * This iterator recurses into GstBin children. * - * Each element yielded by the iterator will have its refcount increased, so - * unref after use. - * * MT safe. Caller owns returned value. * - * Returns: (transfer full): a #GstIterator of #GstElement, or NULL + * Returns: (transfer full): a #GstIterator of #GstElement, or %NULL */ GstIterator * gst_bin_iterate_recurse (GstBin * bin) @@ -1669,12 +1728,9 @@ sink_iterator_filter (const GValue * vchild, GValue * vbin) * Gets an iterator for all elements in the bin that have the * #GST_ELEMENT_FLAG_SINK flag set. * - * Each element yielded by the iterator will have its refcount increased, so - * unref after use. - * * MT safe. Caller owns returned value. * - * Returns: (transfer full): a #GstIterator of #GstElement, or NULL + * Returns: (transfer full): a #GstIterator of #GstElement, or %NULL */ GstIterator * gst_bin_iterate_sinks (GstBin * bin) @@ -1732,12 +1788,9 @@ src_iterator_filter (const GValue * vchild, GValue * vbin) * Gets an iterator for all elements in the bin that have the * #GST_ELEMENT_FLAG_SOURCE flag set. * - * Each element yielded by the iterator will have its refcount increased, so - * unref after use. - * * MT safe. Caller owns returned value. * - * Returns: (transfer full): a #GstIterator of #GstElement, or NULL + * Returns: (transfer full): a #GstIterator of #GstElement, or %NULL */ GstIterator * gst_bin_iterate_sources (GstBin * bin) @@ -1998,8 +2051,7 @@ find_element (GstElement * element, GstBinSortIterator * bit) } } -/* get next element in iterator. the returned element has the - * refcount increased */ +/* get next element in iterator. */ static GstIteratorResult gst_bin_sort_iterator_next (GstBinSortIterator * bit, GValue * result) { @@ -2112,12 +2164,9 @@ gst_bin_sort_iterator_new (GstBin * bin) * This function is used internally to perform the state changes * of the bin elements and for clock selection. * - * Each element yielded by the iterator will have its refcount increased, so - * unref after use. - * * MT safe. Caller owns returned value. * - * Returns: (transfer full): a #GstIterator of #GstElement, or NULL + * Returns: (transfer full): a #GstIterator of #GstElement, or %NULL */ GstIterator * gst_bin_iterate_sorted (GstBin * bin) @@ -2361,7 +2410,7 @@ gst_bin_src_pads_activate (GstBin * bin, gboolean active) GstIterator *iter; gboolean fold_ok; - GST_DEBUG_OBJECT (bin, "src_pads_activate with active %d", active); + GST_DEBUG_OBJECT (bin, "%s pads", active ? "activate" : "deactivate"); iter = gst_element_iterate_src_pads ((GstElement *) bin); fold_ok = iterator_activate_fold_with_resync (iter, &active); @@ -2369,14 +2418,14 @@ gst_bin_src_pads_activate (GstBin * bin, gboolean active) if (G_UNLIKELY (!fold_ok)) goto failed; - GST_DEBUG_OBJECT (bin, "pads_activate successful"); + GST_DEBUG_OBJECT (bin, "pad %sactivation successful", active ? "" : "de"); return TRUE; /* ERRORS */ failed: { - GST_DEBUG_OBJECT (bin, "source pads_activate failed"); + GST_DEBUG_OBJECT (bin, "pad %sactivation failed", active ? "" : "de"); return FALSE; } } @@ -2395,8 +2444,6 @@ failed: * calculations will be performed. * * Returns: %TRUE if the latency could be queried and reconfigured. - * - * Since: 0.10.22. */ gboolean gst_bin_recalculate_latency (GstBin * bin) @@ -2462,17 +2509,30 @@ gst_bin_do_latency_func (GstBin * bin) return res; } -static void -gst_bin_state_changed (GstElement * element, GstState oldstate, - GstState newstate, GstState pending) +static gboolean +gst_bin_post_message (GstElement * element, GstMessage * msg) { GstElementClass *pklass = (GstElementClass *) parent_class; + gboolean ret; + + ret = pklass->post_message (element, gst_message_ref (msg)); + + if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_STATE_CHANGED && + GST_MESSAGE_SRC (msg) == GST_OBJECT_CAST (element)) { + GstState newstate, pending; + + gst_message_parse_state_changed (msg, NULL, &newstate, &pending); + if (newstate == GST_STATE_PLAYING && pending == GST_STATE_VOID_PENDING) { + GST_BIN_CAST (element)->priv->posted_playing = TRUE; + bin_do_eos (GST_BIN_CAST (element)); + } else { + GST_BIN_CAST (element)->priv->posted_playing = FALSE; + } + } - if (newstate == GST_STATE_PLAYING && pending == GST_STATE_VOID_PENDING) - bin_do_eos (GST_BIN_CAST (element)); + gst_message_unref (msg); - if (pklass->state_changed) - pklass->state_changed (element, oldstate, newstate, pending); + return ret; } static GstStateChangeReturn @@ -2517,6 +2577,8 @@ gst_bin_change_state_func (GstElement * element, GstStateChange transition) GST_DEBUG_OBJECT (element, "clearing EOS elements"); bin_remove_messages (bin, NULL, GST_MESSAGE_EOS); bin->priv->posted_eos = FALSE; + if (current == GST_STATE_READY) + bin_remove_messages (bin, NULL, GST_MESSAGE_STREAM_START); GST_OBJECT_UNLOCK (bin); if (current == GST_STATE_READY) if (!(gst_bin_src_pads_activate (bin, TRUE))) @@ -2528,14 +2590,36 @@ gst_bin_change_state_func (GstElement * element, GstStateChange transition) GST_DEBUG_OBJECT (element, "clearing all cached messages"); bin_remove_messages (bin, NULL, GST_MESSAGE_ANY); GST_OBJECT_UNLOCK (bin); - if (current == GST_STATE_PAUSED) - if (!(gst_bin_src_pads_activate (bin, FALSE))) - goto activate_failure; + /* We might not have reached PAUSED yet due to async errors, + * make sure to always deactivate the pads nonetheless */ + if (!(gst_bin_src_pads_activate (bin, FALSE))) + goto activate_failure; break; case GST_STATE_NULL: - if (current == GST_STATE_READY) + if (current == GST_STATE_READY) { + GList *l; + if (!(gst_bin_src_pads_activate (bin, FALSE))) goto activate_failure; + + /* Remove all non-persistent contexts */ + GST_OBJECT_LOCK (bin); + for (l = bin->priv->contexts; l;) { + GstContext *context = l->data; + + if (!gst_context_is_persistent (context)) { + GList *next; + + gst_context_unref (context); + next = l->next; + bin->priv->contexts = g_list_delete_link (bin->priv->contexts, l); + l = next; + } else { + l = l->next; + } + } + GST_OBJECT_UNLOCK (bin); + } break; default: break; @@ -2724,8 +2808,8 @@ activate_failure: /* * This function is a utility event handler for seek events. - * It will send the event to all sinks or sources depending on the - * event-direction. + * It will send the event to all sinks or sources and appropriate + * ghost pads depending on the event-direction. * * Applications are free to override this behaviour and * implement their own seek handler, but this will work for @@ -2754,12 +2838,15 @@ gst_bin_send_event (GstElement * element, GstEvent * event) switch (gst_iterator_next (iter, &data)) { case GST_ITERATOR_OK: { - GstElement *child = g_value_get_object (&data);; + GstElement *child = g_value_get_object (&data); gst_event_ref (event); res &= gst_element_send_event (child, event); + GST_LOG_OBJECT (child, "After handling %s event: %d", GST_EVENT_TYPE_NAME (event), res); + + g_value_reset (&data); break; } case GST_ITERATOR_RESYNC: @@ -2776,6 +2863,45 @@ gst_bin_send_event (GstElement * element, GstEvent * event) } g_value_unset (&data); gst_iterator_free (iter); + + if (GST_EVENT_IS_DOWNSTREAM (event)) { + iter = gst_element_iterate_sink_pads (GST_ELEMENT (bin)); + GST_DEBUG_OBJECT (bin, "Sending %s event to sink pads", + GST_EVENT_TYPE_NAME (event)); + } else { + iter = gst_element_iterate_src_pads (GST_ELEMENT (bin)); + GST_DEBUG_OBJECT (bin, "Sending %s event to src pads", + GST_EVENT_TYPE_NAME (event)); + } + + done = FALSE; + while (!done) { + switch (gst_iterator_next (iter, &data)) { + case GST_ITERATOR_OK: + { + GstPad *pad = g_value_get_object (&data); + + gst_event_ref (event); + res &= gst_pad_send_event (pad, event); + GST_LOG_OBJECT (pad, "After handling %s event: %d", + GST_EVENT_TYPE_NAME (event), res); + break; + } + case GST_ITERATOR_RESYNC: + gst_iterator_resync (iter); + res = TRUE; + break; + case GST_ITERATOR_DONE: + done = TRUE; + break; + case GST_ITERATOR_ERROR: + g_assert_not_reached (); + break; + } + } + + g_value_unset (&data); + gst_iterator_free (iter); gst_event_unref (event); return res; @@ -3100,7 +3226,7 @@ bin_do_eos (GstBin * bin) */ eos = GST_STATE (bin) == GST_STATE_PLAYING && GST_STATE_PENDING (bin) == GST_STATE_VOID_PENDING - && is_eos (bin, &seqnum); + && bin->priv->posted_playing && is_eos (bin, &seqnum); GST_OBJECT_UNLOCK (bin); if (eos @@ -3125,8 +3251,41 @@ bin_do_eos (GstBin * bin) } } -/* must be called with the object lock. This function releases the lock to post - * the message. */ +static void +bin_do_stream_start (GstBin * bin) +{ + guint32 seqnum = 0; + gboolean stream_start; + gboolean have_group_id = FALSE; + guint group_id = 0; + + GST_OBJECT_LOCK (bin); + /* If all sinks are STREAM_START we forward the STREAM_START message + * to the parent bin or application + */ + stream_start = is_stream_start (bin, &seqnum, &have_group_id, &group_id); + GST_OBJECT_UNLOCK (bin); + + if (stream_start) { + GstMessage *tmessage; + + GST_OBJECT_LOCK (bin); + bin_remove_messages (bin, NULL, GST_MESSAGE_STREAM_START); + GST_OBJECT_UNLOCK (bin); + + tmessage = gst_message_new_stream_start (GST_OBJECT_CAST (bin)); + gst_message_set_seqnum (tmessage, seqnum); + if (have_group_id) + gst_message_set_group_id (tmessage, group_id); + + GST_DEBUG_OBJECT (bin, + "all sinks posted STREAM_START, posting seqnum #%" G_GUINT32_FORMAT, + seqnum); + gst_element_post_message (GST_ELEMENT_CAST (bin), tmessage); + } +} + +/* must be called without the object lock as it posts messages */ static void bin_do_message_forward (GstBin * bin, GstMessage * message) { @@ -3135,7 +3294,6 @@ bin_do_message_forward (GstBin * bin, GstMessage * message) GST_DEBUG_OBJECT (bin, "pass %s message upward", GST_MESSAGE_TYPE_NAME (message)); - GST_OBJECT_UNLOCK (bin); /* we need to convert these messages to element messages so that our parent * bin can easily ignore them and so that the application can easily @@ -3145,9 +3303,35 @@ bin_do_message_forward (GstBin * bin, GstMessage * message) "message", GST_TYPE_MESSAGE, message, NULL)); gst_element_post_message (GST_ELEMENT_CAST (bin), forwarded); + } +} - GST_OBJECT_LOCK (bin); +static void +gst_bin_update_context (GstBin * bin, GstContext * context) +{ + GList *l; + const gchar *context_type; + + GST_OBJECT_LOCK (bin); + context_type = gst_context_get_context_type (context); + for (l = bin->priv->contexts; l; l = l->next) { + GstContext *tmp = l->data; + const gchar *tmp_type = gst_context_get_context_type (tmp); + + /* Always store newest context but never replace + * a persistent one by a non-persistent one */ + if (strcmp (context_type, tmp_type) == 0 && + (gst_context_is_persistent (context) || + !gst_context_is_persistent (tmp))) { + gst_context_replace ((GstContext **) & l->data, context); + break; + } } + /* Not found? Add */ + if (l == NULL) + bin->priv->contexts = + g_list_prepend (bin->priv->contexts, gst_context_ref (context)); + GST_OBJECT_UNLOCK (bin); } /* handle child messages: @@ -3168,13 +3352,13 @@ bin_do_message_forward (GstBin * bin, GstMessage * message) * with the segment_done message. If there are no more segment_start * messages, post segment_done message upwards. * - * GST_MESSAGE_DURATION: remove all previously cached duration messages. + * GST_MESSAGE_DURATION_CHANGED: clear any cached durations. * Whenever someone performs a duration query on the bin, we store the * result so we can answer it quicker the next time. Any element that * changes its duration marks our cached values invalid. * This message is also posted upwards. This is currently disabled - * because too many elements don't post DURATION messages when the - * duration changes. + * because too many elements don't post DURATION_CHANGED messages when + * the duration changes. * * GST_MESSAGE_CLOCK_LOST: This message is posted by an element when it * can no longer provide a clock. The default bin behaviour is to @@ -3235,8 +3419,8 @@ gst_bin_handle_message_func (GstBin * bin, GstMessage * message) { /* collect all eos messages from the children */ - GST_OBJECT_LOCK (bin); bin_do_message_forward (bin, message); + GST_OBJECT_LOCK (bin); /* ref message for future use */ bin_replace_message (bin, message, GST_MESSAGE_EOS); GST_OBJECT_UNLOCK (bin); @@ -3244,6 +3428,18 @@ gst_bin_handle_message_func (GstBin * bin, GstMessage * message) bin_do_eos (bin); break; } + case GST_MESSAGE_STREAM_START: + { + + /* collect all stream_start messages from the children */ + GST_OBJECT_LOCK (bin); + /* ref message for future use */ + bin_replace_message (bin, message, GST_MESSAGE_STREAM_START); + GST_OBJECT_UNLOCK (bin); + + bin_do_stream_start (bin); + break; + } case GST_MESSAGE_STATE_DIRTY: { GST_WARNING_OBJECT (bin, "received deprecated STATE_DIRTY message"); @@ -3260,8 +3456,9 @@ gst_bin_handle_message_func (GstBin * bin, GstMessage * message) gst_message_parse_segment_start (message, &format, &position); seqnum = gst_message_get_seqnum (message); - GST_OBJECT_LOCK (bin); bin_do_message_forward (bin, 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) && @@ -3293,8 +3490,9 @@ gst_bin_handle_message_func (GstBin * bin, GstMessage * message) gst_message_parse_segment_done (message, &format, &position); seqnum = gst_message_get_seqnum (message); - GST_OBJECT_LOCK (bin); bin_do_message_forward (bin, message); + + GST_OBJECT_LOCK (bin); bin_replace_message (bin, message, GST_MESSAGE_SEGMENT_START); /* if there are no more segment_start messages, everybody posted * a segment_done and we can post one on the bus. */ @@ -3319,20 +3517,22 @@ gst_bin_handle_message_func (GstBin * bin, GstMessage * message) } break; } - case GST_MESSAGE_DURATION: + case GST_MESSAGE_DURATION_CHANGED: { - /* remove all cached duration messages, next time somebody asks + /* FIXME: remove all cached durations, next time somebody asks * for duration, we will recalculate. */ +#if 0 GST_OBJECT_LOCK (bin); - bin_remove_messages (bin, NULL, GST_MESSAGE_DURATION); + bin_remove_messages (bin, NULL, GST_MESSAGE_DURATION_CHANGED); GST_OBJECT_UNLOCK (bin); +#endif goto forward; } case GST_MESSAGE_CLOCK_LOST: { GstClock **provided_clock_p; GstElement **clock_provider_p; - gboolean playing, provided, forward; + gboolean playing, toplevel, provided, forward; GstClock *clock; gst_message_parse_clock_lost (message, &clock); @@ -3340,10 +3540,14 @@ gst_bin_handle_message_func (GstBin * bin, GstMessage * message) GST_OBJECT_LOCK (bin); bin->clock_dirty = TRUE; /* if we lost the clock that we provided, post to parent but - * only if we are PLAYING. */ + * only if we are not a top-level bin or PLAYING. + * The reason for this is that applications should be able + * to PAUSE/PLAY if they receive this message without worrying + * about the state of the pipeline. */ provided = (clock == bin->provided_clock); playing = (GST_STATE (bin) == GST_STATE_PLAYING); - forward = playing & provided; + toplevel = GST_OBJECT_PARENT (bin) == NULL; + forward = provided && (playing || !toplevel); if (provided) { GST_DEBUG_OBJECT (bin, "Lost clock %" GST_PTR_FORMAT " provided by %" GST_PTR_FORMAT, @@ -3389,9 +3593,9 @@ gst_bin_handle_message_func (GstBin * bin, GstMessage * message) GST_DEBUG_OBJECT (bin, "ASYNC_START message %p, %s", message, src ? GST_OBJECT_NAME (src) : "(NULL)"); - GST_OBJECT_LOCK (bin); bin_do_message_forward (bin, message); + GST_OBJECT_LOCK (bin); /* we ignore the message if we are going to <= READY */ if ((target = GST_STATE_TARGET (bin)) <= GST_STATE_READY) goto ignore_start_message; @@ -3422,9 +3626,9 @@ gst_bin_handle_message_func (GstBin * bin, GstMessage * message) gst_message_parse_async_done (message, &running_time); - GST_OBJECT_LOCK (bin); bin_do_message_forward (bin, message); + GST_OBJECT_LOCK (bin); /* ignore messages if we are shutting down */ if ((target = GST_STATE_TARGET (bin)) <= GST_STATE_READY) goto ignore_done_message; @@ -3478,7 +3682,8 @@ gst_bin_handle_message_func (GstBin * bin, GstMessage * message) * need to resync by updating the structure_cookie. */ bin_remove_messages (bin, GST_MESSAGE_SRC (message), GST_MESSAGE_STRUCTURE_CHANGE); - bin->priv->structure_cookie++; + if (!GST_BIN_IS_NO_RESYNC (bin)) + bin->priv->structure_cookie++; } GST_OBJECT_UNLOCK (bin); @@ -3487,6 +3692,42 @@ gst_bin_handle_message_func (GstBin * bin, GstMessage * message) break; } + case GST_MESSAGE_NEED_CONTEXT:{ + const gchar *context_type; + GList *l; + + gst_message_parse_context_type (message, &context_type); + GST_OBJECT_LOCK (bin); + for (l = bin->priv->contexts; l; l = l->next) { + GstContext *tmp = l->data; + const gchar *tmp_type = gst_context_get_context_type (tmp); + + if (strcmp (context_type, tmp_type) == 0) { + gst_element_set_context (GST_ELEMENT (src), l->data); + break; + } + } + GST_OBJECT_UNLOCK (bin); + + /* Forward if we couldn't answer the message */ + if (l == NULL) { + goto forward; + } else { + gst_message_unref (message); + } + + break; + } + case GST_MESSAGE_HAVE_CONTEXT:{ + GstContext *context; + + gst_message_parse_have_context (message, &context); + gst_bin_update_context (bin, context); + gst_context_unref (context); + + goto forward; + break; + } default: goto forward; } @@ -3526,9 +3767,14 @@ bin_query_min_max_init (GstBin * bin, QueryFold * fold) static gboolean bin_query_duration_fold (const GValue * vitem, GValue * ret, QueryFold * fold) { - GstElement *item = g_value_get_object (vitem); + gboolean res = FALSE; + GstObject *item = g_value_get_object (vitem); + if (GST_IS_PAD (item)) + res = gst_pad_query (GST_PAD (item), fold->query); + else + res = gst_element_query (GST_ELEMENT (item), fold->query); - if (gst_element_query (item, fold->query)) { + if (res) { gint64 duration; g_value_set_boolean (ret, TRUE); @@ -3537,6 +3783,12 @@ bin_query_duration_fold (const GValue * vitem, GValue * ret, QueryFold * fold) GST_DEBUG_OBJECT (item, "got duration %" G_GINT64_FORMAT, duration); + if (duration == -1) { + /* duration query succeeded, but duration is unknown */ + fold->max = -1; + return FALSE; + } + if (duration > fold->max) fold->max = duration; } @@ -3555,19 +3807,27 @@ bin_query_duration_done (GstBin * bin, QueryFold * fold) GST_DEBUG_OBJECT (bin, "max duration %" G_GINT64_FORMAT, fold->max); + /* FIXME: re-implement duration caching */ +#if 0 /* and cache now */ GST_OBJECT_LOCK (bin); bin->messages = g_list_prepend (bin->messages, gst_message_new_duration (GST_OBJECT_CAST (bin), format, fold->max)); GST_OBJECT_UNLOCK (bin); +#endif } static gboolean bin_query_position_fold (const GValue * vitem, GValue * ret, QueryFold * fold) { - GstElement *item = g_value_get_object (vitem); + gboolean res = FALSE; + GstObject *item = g_value_get_object (vitem); + if (GST_IS_PAD (item)) + res = gst_pad_query (GST_PAD (item), fold->query); + else + res = gst_element_query (GST_ELEMENT (item), fold->query); - if (gst_element_query (item, fold->query)) { + if (res) { gint64 position; g_value_set_boolean (ret, TRUE); @@ -3598,9 +3858,13 @@ bin_query_position_done (GstBin * bin, QueryFold * fold) static gboolean bin_query_latency_fold (const GValue * vitem, GValue * ret, QueryFold * fold) { - GstElement *item = g_value_get_object (vitem); - - if (gst_element_query (item, fold->query)) { + gboolean res = FALSE; + GstObject *item = g_value_get_object (vitem); + if (GST_IS_PAD (item)) + res = gst_pad_query (GST_PAD (item), fold->query); + else + res = gst_element_query (GST_ELEMENT (item), fold->query); + if (res) { GstClockTime min, max; gboolean live; @@ -3646,10 +3910,13 @@ bin_query_latency_done (GstBin * bin, QueryFold * fold) static gboolean bin_query_generic_fold (const GValue * vitem, GValue * ret, QueryFold * fold) { - GstElement *item = g_value_get_object (vitem); - gboolean res; - - if ((res = gst_element_query (item, fold->query))) { + gboolean res = FALSE; + GstObject *item = g_value_get_object (vitem); + if (GST_IS_PAD (item)) + res = gst_pad_query (GST_PAD (item), fold->query); + else + res = gst_element_query (GST_ELEMENT (item), fold->query); + if (res) { g_value_set_boolean (ret, TRUE); GST_DEBUG_OBJECT (item, "answered query %p", fold->query); } @@ -3658,21 +3925,65 @@ bin_query_generic_fold (const GValue * vitem, GValue * ret, QueryFold * fold) return !res; } +/* Perform a query iteration for the given bin. The query is stored in + * QueryFold and iter should be either a GstPad iterator or a + * GstElement iterator. */ +static gboolean +bin_iterate_fold (GstBin * bin, GstIterator * iter, QueryInitFunction fold_init, + QueryDoneFunction fold_done, GstIteratorFoldFunction fold_func, + QueryFold fold_data, gboolean default_return) +{ + gboolean res = default_return; + GValue ret = { 0 }; + /* set the result of the query to FALSE initially */ + g_value_init (&ret, G_TYPE_BOOLEAN); + g_value_set_boolean (&ret, res); + + while (TRUE) { + GstIteratorResult ires; + + ires = gst_iterator_fold (iter, fold_func, &ret, &fold_data); + + switch (ires) { + case GST_ITERATOR_RESYNC: + gst_iterator_resync (iter); + if (fold_init) + fold_init (bin, &fold_data); + g_value_set_boolean (&ret, res); + break; + case GST_ITERATOR_OK: + case GST_ITERATOR_DONE: + res = g_value_get_boolean (&ret); + if (fold_done != NULL && res) + fold_done (bin, &fold_data); + goto done; + default: + res = FALSE; + goto done; + } + } +done: + return res; +} + static gboolean gst_bin_query (GstElement * element, GstQuery * query) { GstBin *bin = GST_BIN_CAST (element); GstIterator *iter; + gboolean default_return = FALSE; gboolean res = FALSE; + gboolean src_pads_query_result = FALSE; GstIteratorFoldFunction fold_func; QueryInitFunction fold_init = NULL; QueryDoneFunction fold_done = NULL; QueryFold fold_data; - GValue ret = { 0 }; switch (GST_QUERY_TYPE (query)) { case GST_QUERY_DURATION: { + /* FIXME: implement duration caching in GstBin again */ +#if 0 GList *cached; GstFormat qformat; @@ -3683,7 +3994,7 @@ gst_bin_query (GstElement * element, GstQuery * query) for (cached = bin->messages; cached; cached = g_list_next (cached)) { GstMessage *message = (GstMessage *) cached->data; - if (GST_MESSAGE_TYPE (message) == GST_MESSAGE_DURATION && + if (GST_MESSAGE_TYPE (message) == GST_MESSAGE_DURATION_CHANGED && GST_MESSAGE_SRC (message) == GST_OBJECT_CAST (bin)) { GstFormat format; gint64 duration; @@ -3703,6 +4014,9 @@ gst_bin_query (GstElement * element, GstQuery * query) } } GST_OBJECT_UNLOCK (bin); +#else + GST_FIXME ("implement duration caching in GstBin again"); +#endif /* no cached value found, iterate and collect durations */ fold_func = (GstIteratorFoldFunction) bin_query_duration_fold; fold_init = bin_query_min_max_init; @@ -3721,7 +4035,7 @@ gst_bin_query (GstElement * element, GstQuery * query) fold_func = (GstIteratorFoldFunction) bin_query_latency_fold; fold_init = bin_query_min_max_init; fold_done = bin_query_latency_done; - res = TRUE; + default_return = TRUE; break; } default: @@ -3731,10 +4045,6 @@ gst_bin_query (GstElement * element, GstQuery * query) 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); - iter = gst_bin_iterate_sinks (bin); GST_DEBUG_OBJECT (bin, "Sending query %p (type %s) to sink children", query, GST_QUERY_TYPE_NAME (query)); @@ -3742,38 +4052,55 @@ gst_bin_query (GstElement * element, GstQuery * query) if (fold_init) fold_init (bin, &fold_data); - while (TRUE) { - GstIteratorResult ires; + res = + bin_iterate_fold (bin, iter, fold_init, fold_done, fold_func, fold_data, + default_return); + gst_iterator_free (iter); - ires = gst_iterator_fold (iter, fold_func, &ret, &fold_data); + if (!res) { + /* Query the source pads of the element */ + iter = gst_element_iterate_src_pads (element); + src_pads_query_result = + bin_iterate_fold (bin, iter, fold_init, fold_done, fold_func, + fold_data, default_return); + gst_iterator_free (iter); - switch (ires) { - case GST_ITERATOR_RESYNC: - gst_iterator_resync (iter); - if (fold_init) - fold_init (bin, &fold_data); - g_value_set_boolean (&ret, res); - break; - case GST_ITERATOR_OK: - case GST_ITERATOR_DONE: - res = g_value_get_boolean (&ret); - if (fold_done != NULL && res) - fold_done (bin, &fold_data); - goto done; - default: - res = FALSE; - goto done; - } + if (src_pads_query_result) + res = TRUE; } -done: - gst_iterator_free (iter); -exit: GST_DEBUG_OBJECT (bin, "query %p result %d", query, res); return res; } +static void +set_context (const GValue * item, gpointer user_data) +{ + GstElement *element = g_value_get_object (item); + + gst_element_set_context (element, user_data); +} + +static void +gst_bin_set_context (GstElement * element, GstContext * context) +{ + GstBin *bin; + GstIterator *children; + + g_return_if_fail (GST_IS_BIN (element)); + + bin = GST_BIN (element); + + gst_bin_update_context (bin, context); + + children = gst_bin_iterate_elements (bin); + while (gst_iterator_foreach (children, set_context, + context) == GST_ITERATOR_RESYNC) + gst_iterator_resync (children); + gst_iterator_free (children); +} + static gint compare_name (const GValue * velement, const gchar * name) { @@ -3795,11 +4122,11 @@ compare_name (const GValue * velement, const gchar * name) * Gets the element with the given name from a bin. This * function recurses into child bins. * - * Returns NULL if no element with the given name is found in the bin. + * Returns %NULL if no element with the given name is found in the bin. * * MT safe. Caller owns returned reference. * - * Returns: (transfer full): the #GstElement with the given name, or NULL + * Returns: (transfer full): the #GstElement with the given name, or %NULL */ GstElement * gst_bin_get_by_name (GstBin * bin, const gchar * name) @@ -3837,12 +4164,12 @@ gst_bin_get_by_name (GstBin * bin, const gchar * name) * Gets the element with the given name from this bin. If the * element is not found, a recursion is performed on the parent bin. * - * Returns NULL if: + * Returns %NULL if: * - no element with the given name is found in the bin * * MT safe. Caller owns returned reference. * - * Returns: (transfer full): the #GstElement with the given name, or NULL + * Returns: (transfer full): the #GstElement with the given name, or %NULL */ GstElement * gst_bin_get_by_name_recurse_up (GstBin * bin, const gchar * name) @@ -3940,13 +4267,10 @@ gst_bin_get_by_interface (GstBin * bin, GType iface) * The function recurses inside child bins. The iterator will yield a series * of #GstElement that should be unreffed after use. * - * Each element yielded by the iterator will have its refcount increased, so - * unref after use. - * * MT safe. Caller owns returned value. * * Returns: (transfer full): a #GstIterator of #GstElement for all elements - * in the bin implementing the given interface, or NULL + * in the bin implementing the given interface, or %NULL */ GstIterator * gst_bin_iterate_all_by_interface (GstBin * bin, GType iface)