summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSebastian Dröge <sebastian@centricular.com>2016-03-14 17:06:53 +0200
committerSebastian Dröge <sebastian@centricular.com>2016-03-14 17:09:32 +0200
commit9c2d76fb9fd1f95bd87a7a0dbd3c49ea3510a03e (patch)
treea00997272a8eeae585565ec9acdc441ad5bceb67
parent65390b5129ce44b0785ec765dbf659249ab59cca (diff)
downloadgstreamer-plugins-base-9c2d76fb9fd1f95bd87a7a0dbd3c49ea3510a03e.tar.gz
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
-rw-r--r--gst/playback/gstdecodebin2.c75
1 files changed, 75 insertions, 0 deletions
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;