typedef struct _GstPendingPad GstPendingPad;
typedef struct _GstDecodeElement GstDecodeElement;
+typedef struct _GstDemuxerPad GstDemuxerPad;
typedef struct _GstDecodeChain GstDecodeChain;
typedef struct _GstDecodeGroup GstDecodeGroup;
typedef struct _GstDecodePad GstDecodePad;
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
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
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 */
* 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).
*/
* @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.
*
* @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!
* 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),
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;
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);
}
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;
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) {
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
#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);
}
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));
gst_decode_group_control_demuxer_pad (GstDecodeGroup * group, GstPad * pad)
{
GstDecodeBin *dbin;
+ GstDemuxerPad *demuxer_pad;
GstPad *srcpad, *sinkpad;
GstIterator *it = NULL;
GValue item = { 0, };
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);
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;
/* 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;
/* 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 */
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) {
}
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
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;
}
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;
}