docs: convert NULL, TRUE, and FALSE to %NULL, %TRUE, and %FALSE
[platform/upstream/gstreamer.git] / gst / gstbin.c
index b182618..77bbc3e 100644 (file)
  * </variablelist>
  *
  * 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.
  *
  * </para>
  * </refsect2>
- *
- * Last reviewed on 2012-03-28 (0.11.3)
  */
 
 #include "gst_private.h"
@@ -189,8 +187,6 @@ struct _GstBinPrivate
 
   guint32 structure_cookie;
 
-  GstContext *context;
-
 #if 0
   /* cached index */
   GstIndex *index;
@@ -200,6 +196,9 @@ struct _GstBinPrivate
   gboolean message_forward;
 
   gboolean posted_eos;
+  gboolean posted_playing;
+
+  GList *contexts;
 };
 
 typedef struct
@@ -218,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,
@@ -372,7 +370,7 @@ 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.
    */
@@ -408,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
@@ -452,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);
@@ -532,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);
 }
 
@@ -886,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
   }
@@ -1013,12 +1013,16 @@ is_eos (GstBin * bin, guint32 * seqnum)
  *
  * call with bin LOCK */
 static gboolean
-is_stream_start (GstBin * bin, guint32 * seqnum)
+is_stream_start (GstBin * bin, guint32 * seqnum, gboolean * have_group_id,
+    guint * group_id)
 {
   gboolean result;
-  gint n_stream_start = 0;
   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;
@@ -1031,7 +1035,18 @@ is_stream_start (GstBin * bin, guint32 * seqnum)
                   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));
-        n_stream_start++;
+        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));
@@ -1041,6 +1056,14 @@ is_stream_start (GstBin * bin, guint32 * seqnum)
     }
   }
 
+  /* 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;
 }
 
@@ -1073,6 +1096,7 @@ 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));
 
@@ -1144,8 +1168,8 @@ gst_bin_add_func (GstBin * bin, GstElement * element)
    * a new clock will be selected */
   gst_element_set_clock (element, GST_ELEMENT_CLOCK (bin));
 
-  if (bin->priv->context)
-    gst_element_set_context (element, bin->priv->context);
+  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 */
@@ -1255,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
@@ -1568,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
@@ -1609,7 +1633,7 @@ no_function:
  *
  * 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)
@@ -1649,7 +1673,7 @@ iterate_child_recurse (GstIterator * it, const GValue * item)
  *
  * 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)
@@ -1706,7 +1730,7 @@ sink_iterator_filter (const GValue * vchild, GValue * vbin)
  *
  * 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)
@@ -1766,7 +1790,7 @@ src_iterator_filter (const GValue * vchild, GValue * vbin)
  *
  * 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)
@@ -2142,7 +2166,7 @@ gst_bin_sort_iterator_new (GstBin * bin)
  *
  * 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)
@@ -2485,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 (newstate == GST_STATE_PLAYING && pending == GST_STATE_VOID_PENDING)
-    bin_do_eos (GST_BIN_CAST (element));
+  if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_STATE_CHANGED &&
+      GST_MESSAGE_SRC (msg) == GST_OBJECT_CAST (element)) {
+    GstState newstate, pending;
 
-  if (pklass->state_changed)
-    pklass->state_changed (element, oldstate, 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;
+    }
+  }
+
+  gst_message_unref (msg);
+
+  return ret;
 }
 
 static GstStateChangeReturn
@@ -2553,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;
@@ -3167,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
@@ -3197,12 +3256,14 @@ 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);
+  stream_start = is_stream_start (bin, &seqnum, &have_group_id, &group_id);
   GST_OBJECT_UNLOCK (bin);
 
   if (stream_start) {
@@ -3214,6 +3275,9 @@ bin_do_stream_start (GstBin * 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);
@@ -3221,8 +3285,7 @@ bin_do_stream_start (GstBin * bin)
   }
 }
 
-/* must be called with the object lock. This function releases the lock to post
- * the message. */
+/* must be called without the object lock as it posts messages */
 static void
 bin_do_message_forward (GstBin * bin, GstMessage * message)
 {
@@ -3231,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
@@ -3241,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:
@@ -3331,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);
@@ -3368,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) &&
@@ -3401,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. */
@@ -3442,7 +3532,7 @@ gst_bin_handle_message_func (GstBin * bin, GstMessage * message)
     {
       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);
@@ -3450,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,
@@ -3499,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;
@@ -3532,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;
@@ -3598,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;
   }
@@ -3962,13 +4092,13 @@ gst_bin_set_context (GstElement * element, GstContext * context)
 
   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);
+          context) == GST_ITERATOR_RESYNC)
+    gst_iterator_resync (children);
   gst_iterator_free (children);
-  GST_OBJECT_LOCK (bin);
-  gst_context_replace (&bin->priv->context, context);
-  GST_OBJECT_UNLOCK (bin);
 }
 
 static gint
@@ -3992,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)
@@ -4034,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)
@@ -4140,7 +4270,7 @@ gst_bin_get_by_interface (GstBin * bin, GType iface)
  * 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)