Merge branch 'upstream/1.16' into tizen_gst_1.16.2
[platform/upstream/gst-plugins-base.git] / gst / playback / gstdecodebin2.c
index eedcd6d..3b4bd41 100644 (file)
@@ -121,6 +121,7 @@ GST_DEBUG_CATEGORY_STATIC (gst_decode_bin_debug);
 
 typedef struct _GstPendingPad GstPendingPad;
 typedef struct _GstDecodeElement GstDecodeElement;
+typedef struct _GstDemuxerPad GstDemuxerPad;
 typedef struct _GstDecodeChain GstDecodeChain;
 typedef struct _GstDecodeGroup GstDecodeGroup;
 typedef struct _GstDecodePad GstDecodePad;
@@ -191,6 +192,13 @@ struct _GstDecodeBin
   GList *buffering_status;      /* element currently buffering messages */
   GMutex buffering_lock;
   GMutex buffering_post_lock;
+
+  GMutex cleanup_lock;          /* Mutex used to protect the cleanup thread */
+  GThread *cleanup_thread;      /* thread used to free chains asynchronously.
+                                 * We store it to make sure we end up joining it
+                                 * before stopping the element.
+                                 * Protected by the object lock */
+  GList *cleanup_groups;        /* List of groups to free  */
 };
 
 struct _GstDecodeBinClass
@@ -417,6 +425,14 @@ struct _GstDecodeElement
   gulong no_more_pads_id;
 };
 
+struct _GstDemuxerPad
+{
+  GWeakRef weakPad;
+  gulong event_probe_id;
+  gulong query_probe_id;
+};
+
+
 /* GstDecodeGroup
  *
  * Streams belonging to the same group/chain of a media file
@@ -438,6 +454,7 @@ struct _GstDecodeGroup
   gboolean drained;             /* TRUE if the all children are drained */
 
   GList *children;              /* List of GstDecodeChains in this group */
+  GList *demuxer_pad_probe_ids;
 
   GList *reqpads;               /* List of RequestPads for multiqueue, there is
                                  * exactly one RequestPad per child chain */
@@ -728,11 +745,11 @@ gst_decode_bin_class_init (GstDecodeBinClass * klass)
    * emitted before looking for any elements that can handle that stream.
    *
    * >   Invocation of signal handlers stops after the first signal handler
-   * >   returns #FALSE. Signal handlers are invoked in the order they were
+   * >   returns %FALSE. Signal handlers are invoked in the order they were
    * >   connected in.
    *
-   * Returns: #TRUE if you wish decodebin to look for elements that can
-   * handle the given @caps. If #FALSE, those caps will be considered as
+   * Returns: %TRUE if you wish decodebin to look for elements that can
+   * handle the given @caps. If %FALSE, those caps will be considered as
    * final and the pad will be exposed as such (see 'pad-added' signal of
    * #GstElement).
    */
@@ -748,7 +765,7 @@ gst_decode_bin_class_init (GstDecodeBinClass * klass)
    * @pad: The #GstPad.
    * @caps: The #GstCaps found.
    *
-   * This function is emited when an array of possible factories for @caps on
+   * This signal is emitted when an array of possible factories for @caps on
    * @pad is needed. Decodebin will by default return an array with all
    * compatible factories, sorted by rank.
    *
@@ -780,15 +797,15 @@ gst_decode_bin_class_init (GstDecodeBinClass * klass)
    * @factories: A #GValueArray of possible #GstElementFactory to use.
    *
    * Once decodebin has found the possible #GstElementFactory objects to try
-   * for @caps on @pad, this signal is emited. The purpose of the signal is for
+   * for @caps on @pad, this signal is emitted. The purpose of the signal is for
    * the application to perform additional sorting or filtering on the element
    * factory array.
    *
-   * The callee should copy and modify @factories or return #NULL if the
+   * The callee should copy and modify @factories or return %NULL if the
    * order should not change.
    *
    * >   Invocation of signal handlers stops after one signal handler has
-   * >   returned something else than #NULL. Signal handlers are invoked in
+   * >   returned something else than %NULL. Signal handlers are invoked in
    * >   the order they were connected in.
    * >   Don't connect signal handlers with the #G_CONNECT_AFTER flag to this
    * >   signal, they will never be invoked!
@@ -855,7 +872,7 @@ gst_decode_bin_class_init (GstDecodeBinClass * klass)
    * be used to tell the element about the downstream supported caps
    * for example.
    *
-   * Returns: #TRUE if the query was handled, #FALSE otherwise.
+   * Returns: %TRUE if the query was handled, %FALSE otherwise.
    */
   gst_decode_bin_signals[SIGNAL_AUTOPLUG_QUERY] =
       g_signal_new ("autoplug-query", G_TYPE_FROM_CLASS (klass),
@@ -1100,6 +1117,9 @@ gst_decode_bin_init (GstDecodeBin * decode_bin)
   g_mutex_init (&decode_bin->buffering_lock);
   g_mutex_init (&decode_bin->buffering_post_lock);
 
+  g_mutex_init (&decode_bin->cleanup_lock);
+  decode_bin->cleanup_thread = NULL;
+
   decode_bin->encoding = g_strdup (DEFAULT_SUBTITLE_ENCODING);
   decode_bin->caps = gst_static_caps_get (&default_raw_caps);
   decode_bin->use_buffering = DEFAULT_USE_BUFFERING;
@@ -1157,6 +1177,7 @@ gst_decode_bin_finalize (GObject * object)
   g_mutex_clear (&decode_bin->buffering_lock);
   g_mutex_clear (&decode_bin->buffering_post_lock);
   g_mutex_clear (&decode_bin->factories_lock);
+  g_mutex_clear (&decode_bin->cleanup_lock);
 
   G_OBJECT_CLASS (parent_class)->finalize (object);
 }
@@ -3583,6 +3604,24 @@ gst_decode_group_free_internal (GstDecodeGroup * group, gboolean hide)
 
   GST_DEBUG_OBJECT (group->dbin, "%s group %p", (hide ? "Hiding" : "Freeing"),
       group);
+
+  if (!hide) {
+    for (l = group->demuxer_pad_probe_ids; l != NULL; l = l->next) {
+      GstDemuxerPad *demuxer_pad = l->data;
+      GstPad *sinkpad = g_weak_ref_get (&demuxer_pad->weakPad);
+
+      if (sinkpad != NULL) {
+        gst_pad_remove_probe (sinkpad, demuxer_pad->event_probe_id);
+        gst_pad_remove_probe (sinkpad, demuxer_pad->query_probe_id);
+        g_weak_ref_clear (&demuxer_pad->weakPad);
+        gst_object_unref (sinkpad);
+      }
+      g_free (demuxer_pad);
+    }
+    g_list_free (group->demuxer_pad_probe_ids);
+    group->demuxer_pad_probe_ids = NULL;
+  }
+
   for (l = group->children; l; l = l->next) {
     GstDecodeChain *chain = (GstDecodeChain *) l->data;
 
@@ -3693,12 +3732,30 @@ gst_decode_chain_start_free_hidden_groups_thread (GstDecodeChain * chain)
   GThread *thread;
   GError *error = NULL;
   GList *old_groups;
+  GstDecodeBin *dbin = chain->dbin;
 
   old_groups = chain->old_groups;
   if (!old_groups)
     return;
 
+  /* If we already have a thread running, wait for it to finish */
+  g_mutex_lock (&dbin->cleanup_lock);
+  if (dbin->cleanup_thread) {
+    g_thread_join (dbin->cleanup_thread);
+    dbin->cleanup_thread = NULL;
+  }
+
   chain->old_groups = NULL;
+
+  if (dbin->shutdown) {
+    /* If we're shutting down, add the groups to be cleaned up in the
+     * state change handler (which *is* another thread). Also avoids
+     * playing racy games with the state change handler */
+    dbin->cleanup_groups = g_list_concat (dbin->cleanup_groups, old_groups);
+    g_mutex_unlock (&dbin->cleanup_lock);
+    return;
+  }
+
   thread = g_thread_try_new ("free-hidden-groups",
       (GThreadFunc) gst_decode_chain_free_hidden_groups, old_groups, &error);
   if (!thread || error) {
@@ -3706,11 +3763,14 @@ gst_decode_chain_start_free_hidden_groups_thread (GstDecodeChain * chain)
         error ? error->message : "unknown reason");
     g_clear_error (&error);
     chain->old_groups = old_groups;
+    g_mutex_unlock (&dbin->cleanup_lock);
     return;
   }
+
+  dbin->cleanup_thread = thread;
+  g_mutex_unlock (&dbin->cleanup_lock);
+
   GST_DEBUG_OBJECT (chain->dbin, "Started free-hidden-groups thread");
-  /* We do not need to wait for it or get any results from it */
-  g_thread_unref (thread);
 }
 
 static void
@@ -3796,8 +3856,7 @@ gst_decode_group_new (GstDecodeBin * dbin, GstDecodeChain * parent)
 
 #ifdef TIZEN_FEATURE_TRUSTZONE
   /*tzmultiqueue patch : when this flag is set to TRUE, we will use tzmultiqueue instead of multiqueue element in pipeline*/
-  if(dbin->use_trustzone)
-  {
+  if (dbin->use_trustzone) {
     GST_DEBUG_OBJECT (dbin, "decodebin2 use tzmultiqueue");
     mq = group->multiqueue = gst_element_factory_make ("tzmultiqueue", NULL);
   }
@@ -3822,6 +3881,7 @@ gst_decode_group_new (GstDecodeBin * dbin, GstDecodeChain * parent)
 
   group->overrunsig = g_signal_connect (mq, "overrun",
       G_CALLBACK (multi_queue_overrun_cb), group);
+  group->demuxer_pad_probe_ids = NULL;
 
   gst_element_set_state (mq, GST_STATE_PAUSED);
   gst_bin_add (GST_BIN (dbin), gst_object_ref (mq));
@@ -3851,6 +3911,7 @@ static GstPad *
 gst_decode_group_control_demuxer_pad (GstDecodeGroup * group, GstPad * pad)
 {
   GstDecodeBin *dbin;
+  GstDemuxerPad *demuxer_pad;
   GstPad *srcpad, *sinkpad;
   GstIterator *it = NULL;
   GValue item = { 0, };
@@ -3884,12 +3945,21 @@ gst_decode_group_control_demuxer_pad (GstDecodeGroup * group, GstPad * pad)
         sinkpad);
     goto error;
   }
-  gst_pad_add_probe (sinkpad, GST_PAD_PROBE_TYPE_EVENT_UPSTREAM,
-      sink_pad_event_probe, group, NULL);
-  gst_pad_add_probe (sinkpad, GST_PAD_PROBE_TYPE_QUERY_UPSTREAM,
-      sink_pad_query_probe, group, NULL);
 
   CHAIN_MUTEX_LOCK (group->parent);
+
+  /* Note: GWeakRefs can't be moved in memory once they're in use, so do a
+   * dedicated alloc for the GstDemuxerPad struct that contains it */
+  demuxer_pad = g_new0 (GstDemuxerPad, 1);
+  demuxer_pad->event_probe_id = gst_pad_add_probe (sinkpad,
+      GST_PAD_PROBE_TYPE_EVENT_UPSTREAM, sink_pad_event_probe, group, NULL);
+  demuxer_pad->query_probe_id = gst_pad_add_probe (sinkpad,
+      GST_PAD_PROBE_TYPE_QUERY_UPSTREAM, sink_pad_query_probe, group, NULL);
+
+  g_weak_ref_set (&demuxer_pad->weakPad, sinkpad);
+  group->demuxer_pad_probe_ids =
+      g_list_prepend (group->demuxer_pad_probe_ids, demuxer_pad);
+
   group->reqpads = g_list_prepend (group->reqpads, gst_object_ref (sinkpad));
   CHAIN_MUTEX_UNLOCK (group->parent);
 
@@ -4626,6 +4696,14 @@ retry:
     g_list_foreach (endpads, (GFunc) gst_object_unref, NULL);
     g_list_free (endpads);
     g_string_free (missing_plugin_details, TRUE);
+    /* Failures could be due to the fact that we are currently shutting down (recheck) */
+    DYN_LOCK (dbin);
+    if (G_UNLIKELY (dbin->shutdown)) {
+      GST_WARNING_OBJECT (dbin, "Currently, shutting down, aborting exposing");
+      DYN_UNLOCK (dbin);
+      return FALSE;
+    }
+    DYN_UNLOCK (dbin);
     GST_ERROR_OBJECT (dbin, "Broken chain/group tree");
     g_return_val_if_reached (FALSE);
     return FALSE;
@@ -4706,6 +4784,14 @@ retry:
   /* re-order pads : video, then audio, then others */
   endpads = g_list_sort (endpads, (GCompareFunc) sort_end_pads);
 
+  /* Don't add pads if we are shutting down */
+  DYN_LOCK (dbin);
+  if (G_UNLIKELY (dbin->shutdown)) {
+    GST_WARNING_OBJECT (dbin, "Currently, shutting down, aborting exposing");
+    DYN_UNLOCK (dbin);
+    return FALSE;
+  }
+
   /* Expose pads */
   for (tmp = endpads; tmp; tmp = tmp->next) {
     GstDecodePad *dpad = (GstDecodePad *) tmp->data;
@@ -4736,6 +4822,7 @@ retry:
     /* 3. emit signal */
     GST_INFO_OBJECT (dpad, "added new decoded pad");
   }
+  DYN_UNLOCK (dbin);
 
   /* 4. Signal no-more-pads. This allows the application to hook stuff to the
    * exposed pads */
@@ -5220,14 +5307,14 @@ find_sink_pad (GstElement * element)
 static void
 unblock_pads (GstDecodeBin * dbin)
 {
-  GList *tmp;
-
   GST_LOG_OBJECT (dbin, "unblocking pads");
 
-  for (tmp = dbin->blocked_pads; tmp; tmp = tmp->next) {
+  while (dbin->blocked_pads) {
+    GList *tmp = dbin->blocked_pads;
     GstDecodePad *dpad = (GstDecodePad *) tmp->data;
     GstPad *opad;
 
+    dbin->blocked_pads = g_list_delete_link (dbin->blocked_pads, tmp);
     opad = gst_ghost_pad_get_target (GST_GHOST_PAD_CAST (dpad));
     if (opad) {
 
@@ -5240,15 +5327,17 @@ unblock_pads (GstDecodeBin * dbin)
     }
 
     dpad->blocked = FALSE;
+
+    /* We release the dyn lock since we want to allow the streaming threads
+     * to properly stop and not be blocked in our various probes */
+    DYN_UNLOCK (dbin);
     /* make flushing, prevent NOT_LINKED */
     gst_pad_set_active (GST_PAD_CAST (dpad), FALSE);
-    gst_object_unref (dpad);
+    DYN_LOCK (dbin);
+
     GST_DEBUG_OBJECT (dpad, "unblocked");
+    gst_object_unref (dpad);
   }
-
-  /* clear, no more blocked pads */
-  g_list_free (dbin->blocked_pads);
-  dbin->blocked_pads = NULL;
 }
 
 static void
@@ -5370,6 +5459,17 @@ gst_decode_bin_change_state (GstElement * element, GstStateChange transition)
       dbin->shutdown = TRUE;
       unblock_pads (dbin);
       DYN_UNLOCK (dbin);
+
+      /* Make sure we don't have cleanup races where
+       * we might be trying to deactivate pads (in the cleanup thread)
+       * at the same time as the default element deactivation
+       * (in PAUSED=>READY)  */
+      g_mutex_lock (&dbin->cleanup_lock);
+      if (dbin->cleanup_thread) {
+        g_thread_join (dbin->cleanup_thread);
+        dbin->cleanup_thread = NULL;
+      }
+      g_mutex_unlock (&dbin->cleanup_lock);
     default:
       break;
   }
@@ -5401,8 +5501,23 @@ gst_decode_bin_change_state (GstElement * element, GstStateChange transition)
       g_list_free_full (dbin->buffering_status,
           (GDestroyNotify) gst_message_unref);
       dbin->buffering_status = NULL;
+      /* Let's do a final check of leftover groups to free */
+      g_mutex_lock (&dbin->cleanup_lock);
+      if (dbin->cleanup_groups) {
+        gst_decode_chain_free_hidden_groups (dbin->cleanup_groups);
+        dbin->cleanup_groups = NULL;
+      }
+      g_mutex_unlock (&dbin->cleanup_lock);
       break;
     case GST_STATE_CHANGE_READY_TO_NULL:
+      /* Let's do a final check of leftover groups to free */
+      g_mutex_lock (&dbin->cleanup_lock);
+      if (dbin->cleanup_groups) {
+        gst_decode_chain_free_hidden_groups (dbin->cleanup_groups);
+        dbin->cleanup_groups = NULL;
+      }
+      g_mutex_unlock (&dbin->cleanup_lock);
+      break;
     default:
       break;
   }