From 9c2d76fb9fd1f95bd87a7a0dbd3c49ea3510a03e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Mon, 14 Mar 2016 17:06:53 +0200 Subject: decodebin: Shut down all elements explicitly to NULL state before freeing the decode chain Due to transient locked state during autoplugging, some elements might be ignored by the GstBin::change_state() and might still be running. Which could then cause pad-added and similar accessing decodebin state that does not exist anymore, and crash. https://bugzilla.gnome.org/show_bug.cgi?id=763625 --- gst/playback/gstdecodebin2.c | 75 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/gst/playback/gstdecodebin2.c b/gst/playback/gstdecodebin2.c index d0850158f..84ea7468c 100644 --- a/gst/playback/gstdecodebin2.c +++ b/gst/playback/gstdecodebin2.c @@ -5165,6 +5165,80 @@ unblock_pads (GstDecodeBin * dbin) dbin->blocked_pads = NULL; } +static void +gst_decode_chain_stop (GstDecodeBin * dbin, GstDecodeChain * chain, + GQueue * elements) +{ + GQueue *internal_elements, internal_elements_ = G_QUEUE_INIT; + GList *l; + + CHAIN_MUTEX_LOCK (chain); + if (elements) { + internal_elements = elements; + } else { + internal_elements = &internal_elements_; + } + + for (l = chain->next_groups; l; l = l->next) { + GstDecodeGroup *group = l->data; + GList *m; + + for (m = group->children; m; m = m->next) { + GstDecodeChain *chain2 = m->data; + gst_decode_chain_stop (dbin, chain2, internal_elements); + } + if (group->multiqueue) + g_queue_push_head (internal_elements, gst_object_ref (group->multiqueue)); + } + + if (chain->active_group) { + for (l = chain->active_group->children; l; l = l->next) { + GstDecodeChain *chain2 = l->data; + gst_decode_chain_stop (dbin, chain2, internal_elements); + } + if (chain->active_group->multiqueue) + g_queue_push_head (internal_elements, + gst_object_ref (chain->active_group->multiqueue)); + } + + for (l = chain->old_groups; l; l = l->next) { + GstDecodeGroup *group = l->data; + GList *m; + + for (m = group->children; m; m = m->next) { + GstDecodeChain *chain2 = m->data; + gst_decode_chain_stop (dbin, chain2, internal_elements); + } + if (group->multiqueue) + g_queue_push_head (internal_elements, gst_object_ref (group->multiqueue)); + } + + for (l = chain->elements; l; l = l->next) { + GstDecodeElement *delem = l->data; + + if (delem->capsfilter) + g_queue_push_head (internal_elements, gst_object_ref (delem->capsfilter)); + g_queue_push_head (internal_elements, gst_object_ref (delem->element)); + } + + CHAIN_MUTEX_UNLOCK (chain); + + if (!elements) { + GstElement *element; + + EXPOSE_UNLOCK (dbin); + /* Shut down from bottom to top */ + while ((element = g_queue_pop_tail (internal_elements))) { + /* The bin must never ever change the state of this element anymore */ + gst_element_set_locked_state (element, TRUE); + gst_element_set_state (element, GST_STATE_NULL); + gst_object_unref (element); + } + g_queue_clear (internal_elements); + EXPOSE_LOCK (dbin); + } +} + static GstStateChangeReturn gst_decode_bin_change_state (GstElement * element, GstStateChange transition) { @@ -5229,6 +5303,7 @@ gst_decode_bin_change_state (GstElement * element, GstStateChange transition) do_async_done (dbin); EXPOSE_LOCK (dbin); if (dbin->decode_chain) { + gst_decode_chain_stop (dbin, dbin->decode_chain, NULL); chain_to_free = dbin->decode_chain; gst_decode_chain_free_internal (dbin->decode_chain, TRUE); dbin->decode_chain = NULL; -- cgit v1.2.1