• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* switchbin
2  * Copyright (C) 2016  Carlos Rafael Giani
3  *
4  * gstswitchbin.c: Element for switching between paths based on input caps
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
19  * Boston, MA 02110-1301, USA.
20  */
21 
22 /**
23  * SECTION:element-switchbin
24  * @title: switchbin
25  *
26  * switchbin is a helper element which chooses between a set of
27  * processing chains (paths) based on input caps, and changes if new caps
28  * arrive. Paths are child objects, which are accessed by the
29  * #GstChildProxy interface.
30  *
31  * Whenever new input caps are encountered at the switchbin's sinkpad,
32  * the * first path with matching caps is picked. The paths are looked at
33  * in order: path \#0's caps are looked at first, checked against the new
34  * input caps with gst_caps_can_intersect(), and if its return value
35  * is TRUE, path \#0 is picked. Otherwise, path \#1's caps are looked at etc.
36  * If no path matches, an error is reported.
37  *
38  * <refsect2>
39  * <title>Example launch line</title>
40  *
41  * In this example, if the data is raw PCM audio with 44.1 kHz, a volume
42  * element is used for reducing the audio volume to 10%. Otherwise, it is
43  * just passed through. So, a 44.1 kHz MP3 will sound quiet, a 48 kHz MP3
44  * will be at full volume.
45  *
46  * |[
47  *   gst-launch-1.0 uridecodebin uri=<URI> ! switchbin num-paths=2 \
48  *     path0::element="audioconvert ! volume volume=0.1" path0::caps="audio/x-raw, rate=44100" \
49  *     path1::element="identity" path1::caps="ANY" ! \
50  *     autoaudiosink
51  * ]|
52  *
53  * This example's path \#1 is a fallback "catch-all" path. Its caps are "ANY" caps,
54  * so any input caps will match against this. A catch-all path with an identity element
55  * is useful for cases where certain kinds of processing should only be done for specific
56  * formats, like the example above (it applies volume only to 44.1 kHz PCM audio).
57  * </refsect2>
58  *
59  */
60 
61 #include <string.h>
62 #include <stdio.h>
63 #include "gstswitchbin.h"
64 
65 
66 GST_DEBUG_CATEGORY (switch_bin_debug);
67 #define GST_CAT_DEFAULT switch_bin_debug
68 
69 enum
70 {
71   PROP_0,
72   PROP_NUM_PATHS,
73   PROP_CURRENT_PATH,
74   PROP_LAST
75 };
76 
77 #define DEFAULT_NUM_PATHS 0
78 GParamSpec *switchbin_props[PROP_LAST];
79 
80 #define PATH_LOCK(obj) g_mutex_lock(&(GST_SWITCH_BIN_CAST (obj)->path_mutex))
81 #define PATH_UNLOCK(obj) g_mutex_unlock(&(GST_SWITCH_BIN_CAST (obj)->path_mutex))
82 #define PATH_UNLOCK_AND_CHECK(obj) gst_switch_bin_unlock_paths_and_notify(GST_SWITCH_BIN_CAST (obj));
83 
84 
85 static GstStaticPadTemplate static_sink_template =
86 GST_STATIC_PAD_TEMPLATE ("sink",
87     GST_PAD_SINK,
88     GST_PAD_ALWAYS,
89     GST_STATIC_CAPS_ANY);
90 
91 static GstStaticPadTemplate static_src_template =
92 GST_STATIC_PAD_TEMPLATE ("src",
93     GST_PAD_SRC,
94     GST_PAD_ALWAYS,
95     GST_STATIC_CAPS_ANY);
96 
97 
98 
99 
100 static void gst_switch_bin_child_proxy_iface_init (gpointer iface,
101     gpointer iface_data);
102 static GObject *gst_switch_bin_child_proxy_get_child_by_index (GstChildProxy *
103     child_proxy, guint index);
104 static guint gst_switch_bin_child_proxy_get_children_count (GstChildProxy *
105     child_proxy);
106 
107 
108 G_DEFINE_TYPE_WITH_CODE (GstSwitchBin,
109     gst_switch_bin,
110     GST_TYPE_BIN,
111     G_IMPLEMENT_INTERFACE (GST_TYPE_CHILD_PROXY,
112         gst_switch_bin_child_proxy_iface_init)
113     );
114 GST_ELEMENT_REGISTER_DEFINE (switchbin, "switchbin", GST_RANK_NONE,
115     gst_switch_bin_get_type ());
116 
117 static void gst_switch_bin_unlock_paths_and_notify (GstSwitchBin * switchbin);
118 
119 static void gst_switch_bin_dispose (GObject * object);
120 static void gst_switch_bin_finalize (GObject * object);
121 static void gst_switch_bin_set_property (GObject * object, guint prop_id,
122     GValue const *value, GParamSpec * pspec);
123 static void gst_switch_bin_get_property (GObject * object, guint prop_id,
124     GValue * value, GParamSpec * pspec);
125 
126 static gboolean gst_switch_bin_sink_event (GstPad * pad,
127     GstObject * parent, GstEvent * event);
128 static gboolean gst_switch_bin_sink_query (GstPad * pad,
129     GstObject * parent, GstQuery * query);
130 static gboolean gst_switch_bin_src_query (GstPad * pad, GstObject * parent,
131     GstQuery * query);
132 
133 static gboolean gst_switch_bin_set_num_paths (GstSwitchBin * switch_bin,
134     guint new_num_paths);
135 static gboolean gst_switch_bin_select_path_for_caps (GstSwitchBin *
136     switch_bin, GstCaps * caps);
137 static gboolean gst_switch_bin_switch_to_path (GstSwitchBin * switch_bin,
138     GstSwitchBinPath * switch_bin_path);
139 static GstSwitchBinPath *gst_switch_bin_find_matching_path (GstSwitchBin *
140     switch_bin, GstCaps const *caps);
141 
142 static void gst_switch_bin_set_sinkpad_block (GstSwitchBin * switch_bin,
143     gboolean do_block);
144 static GstPadProbeReturn gst_switch_bin_blocking_pad_probe (GstPad * pad,
145     GstPadProbeInfo * info, gpointer user_data);
146 
147 static GstCaps *gst_switch_bin_get_allowed_caps (GstSwitchBin * switch_bin,
148     GstPad * switch_bin_pad, gchar const *pad_name, GstCaps * filter);
149 static gboolean gst_switch_bin_are_caps_acceptable (GstSwitchBin *
150     switch_bin, GstCaps const *caps);
151 
152 static void
gst_switch_bin_unlock_paths_and_notify(GstSwitchBin * switchbin)153 gst_switch_bin_unlock_paths_and_notify (GstSwitchBin * switchbin)
154 {
155   gboolean do_notify = switchbin->path_changed;
156   switchbin->path_changed = FALSE;
157   PATH_UNLOCK (switchbin);
158 
159   if (do_notify)
160     g_object_notify_by_pspec (G_OBJECT (switchbin),
161         switchbin_props[PROP_CURRENT_PATH]);
162 }
163 
164 static void
gst_switch_bin_child_proxy_iface_init(gpointer iface,G_GNUC_UNUSED gpointer iface_data)165 gst_switch_bin_child_proxy_iface_init (gpointer iface,
166     G_GNUC_UNUSED gpointer iface_data)
167 {
168   GstChildProxyInterface *child_proxy_iface = iface;
169 
170   child_proxy_iface->get_child_by_index =
171       GST_DEBUG_FUNCPTR (gst_switch_bin_child_proxy_get_child_by_index);
172   child_proxy_iface->get_children_count =
173       GST_DEBUG_FUNCPTR (gst_switch_bin_child_proxy_get_children_count);
174 }
175 
176 
177 static GObject *
gst_switch_bin_child_proxy_get_child_by_index(GstChildProxy * child_proxy,guint index)178 gst_switch_bin_child_proxy_get_child_by_index (GstChildProxy * child_proxy,
179     guint index)
180 {
181   GObject *result;
182   GstSwitchBin *switch_bin = GST_SWITCH_BIN (child_proxy);
183 
184   PATH_LOCK (switch_bin);
185 
186   if (G_UNLIKELY (index >= switch_bin->num_paths))
187     result = NULL;
188   else
189     result = g_object_ref (G_OBJECT (switch_bin->paths[index]));
190 
191   PATH_UNLOCK (switch_bin);
192 
193   return result;
194 }
195 
196 
197 static guint
gst_switch_bin_child_proxy_get_children_count(GstChildProxy * child_proxy)198 gst_switch_bin_child_proxy_get_children_count (GstChildProxy * child_proxy)
199 {
200   guint result;
201   GstSwitchBin *switch_bin = GST_SWITCH_BIN (child_proxy);
202 
203   PATH_LOCK (switch_bin);
204   result = switch_bin->num_paths;
205   PATH_UNLOCK (switch_bin);
206 
207   return result;
208 }
209 
210 
211 
212 static void
gst_switch_bin_class_init(GstSwitchBinClass * klass)213 gst_switch_bin_class_init (GstSwitchBinClass * klass)
214 {
215   GObjectClass *object_class;
216   GstElementClass *element_class;
217 
218   GST_DEBUG_CATEGORY_INIT (switch_bin_debug, "switchbin", 0, "switch bin");
219 
220   object_class = G_OBJECT_CLASS (klass);
221   element_class = GST_ELEMENT_CLASS (klass);
222 
223   gst_element_class_add_pad_template (element_class,
224       gst_static_pad_template_get (&static_sink_template));
225   gst_element_class_add_pad_template (element_class,
226       gst_static_pad_template_get (&static_src_template));
227 
228   object_class->dispose = GST_DEBUG_FUNCPTR (gst_switch_bin_dispose);
229   object_class->finalize = GST_DEBUG_FUNCPTR (gst_switch_bin_finalize);
230   object_class->set_property = GST_DEBUG_FUNCPTR (gst_switch_bin_set_property);
231   object_class->get_property = GST_DEBUG_FUNCPTR (gst_switch_bin_get_property);
232 
233   /**
234    * GstSwitchBin:num-paths
235    *
236    * Configure how many paths the switchbin will be choosing between. Attempting
237    * to configure a path outside the range 0..(n-1) will fail. Reducing the
238    * number of paths will release any paths outside the new range, which might
239    * trigger activation of a new path by re-assessing the current caps.
240    */
241   switchbin_props[PROP_NUM_PATHS] = g_param_spec_uint ("num-paths",
242       "Number of paths", "Number of paths", 0, G_MAXUINT - 1,
243       DEFAULT_NUM_PATHS, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
244   g_object_class_install_property (object_class,
245       PROP_NUM_PATHS, switchbin_props[PROP_NUM_PATHS]);
246 
247   /**
248    * GstSwitchBin:current-path
249    *
250    * Returns the currently selected path number. If there is no current
251    * path (due to no caps, or unsupported caps), the value is #G_MAXUINT. Read-only.
252    */
253   switchbin_props[PROP_CURRENT_PATH] =
254       g_param_spec_uint ("current-path", "Current Path",
255       "Currently selected path", 0, G_MAXUINT,
256       0, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
257   g_object_class_install_property (object_class,
258       PROP_CURRENT_PATH, switchbin_props[PROP_CURRENT_PATH]);
259 
260   gst_element_class_set_static_metadata (element_class,
261       "switchbin",
262       "Generic/Bin",
263       "Switch between sub-pipelines (paths) based on input caps",
264       "Carlos Rafael Giani <dv@pseudoterminal.org>");
265 }
266 
267 
268 static void
gst_switch_bin_init(GstSwitchBin * switch_bin)269 gst_switch_bin_init (GstSwitchBin * switch_bin)
270 {
271   GstPad *pad;
272 
273   switch_bin->num_paths = DEFAULT_NUM_PATHS;
274   switch_bin->paths = NULL;
275   switch_bin->current_path = NULL;
276   switch_bin->last_stream_start = NULL;
277   switch_bin->blocking_probe_id = 0;
278   switch_bin->drop_probe_id = 0;
279   switch_bin->last_caps = NULL;
280 
281   switch_bin->sinkpad = gst_ghost_pad_new_no_target_from_template ("sink",
282       gst_element_class_get_pad_template (GST_ELEMENT_GET_CLASS (switch_bin),
283           "sink")
284       );
285   gst_element_add_pad (GST_ELEMENT (switch_bin), switch_bin->sinkpad);
286 
287   switch_bin->srcpad = gst_ghost_pad_new_no_target_from_template ("src",
288       gst_element_class_get_pad_template (GST_ELEMENT_GET_CLASS (switch_bin),
289           "src")
290       );
291   gst_element_add_pad (GST_ELEMENT (switch_bin), switch_bin->srcpad);
292 
293   gst_pad_set_event_function (switch_bin->sinkpad, gst_switch_bin_sink_event);
294   gst_pad_set_query_function (switch_bin->sinkpad, gst_switch_bin_sink_query);
295   gst_pad_set_query_function (switch_bin->srcpad, gst_switch_bin_src_query);
296 
297   switch_bin->input_identity =
298       gst_element_factory_make ("identity", "input-identity");
299 
300   gst_bin_add (GST_BIN (switch_bin), switch_bin->input_identity);
301   pad = gst_element_get_static_pad (switch_bin->input_identity, "sink");
302   gst_ghost_pad_set_target (GST_GHOST_PAD (switch_bin->sinkpad), pad);
303   gst_object_unref (GST_OBJECT (pad));
304 }
305 
306 static void
gst_switch_bin_dispose(GObject * object)307 gst_switch_bin_dispose (GObject * object)
308 {
309   GstSwitchBin *switch_bin = GST_SWITCH_BIN (object);
310   guint i;
311 
312   /* Chaining up will release all children of the bin,
313    * invalidating any pointer to elements in the paths, so make sure
314    * and clear those first */
315   PATH_LOCK (switch_bin);
316   for (i = 0; i < switch_bin->num_paths; ++i) {
317     if (switch_bin->paths[i])
318       switch_bin->paths[i]->element = NULL;
319   }
320   PATH_UNLOCK (switch_bin);
321   G_OBJECT_CLASS (gst_switch_bin_parent_class)->dispose (object);
322 }
323 
324 static void
gst_switch_bin_finalize(GObject * object)325 gst_switch_bin_finalize (GObject * object)
326 {
327   GstSwitchBin *switch_bin = GST_SWITCH_BIN (object);
328   guint i;
329 
330   if (switch_bin->last_caps != NULL)
331     gst_caps_unref (switch_bin->last_caps);
332   if (switch_bin->last_stream_start != NULL)
333     gst_event_unref (switch_bin->last_stream_start);
334 
335   for (i = 0; i < switch_bin->num_paths; ++i) {
336     gst_object_unparent (GST_OBJECT (switch_bin->paths[i]));
337   }
338   g_free (switch_bin->paths);
339 
340   G_OBJECT_CLASS (gst_switch_bin_parent_class)->finalize (object);
341 }
342 
343 
344 static void
gst_switch_bin_set_property(GObject * object,guint prop_id,GValue const * value,GParamSpec * pspec)345 gst_switch_bin_set_property (GObject * object, guint prop_id,
346     GValue const *value, GParamSpec * pspec)
347 {
348   GstSwitchBin *switch_bin = GST_SWITCH_BIN (object);
349   switch (prop_id) {
350     case PROP_NUM_PATHS:
351       PATH_LOCK (switch_bin);
352       gst_switch_bin_set_num_paths (switch_bin, g_value_get_uint (value));
353       PATH_UNLOCK_AND_CHECK (switch_bin);
354       break;
355 
356     default:
357       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
358       break;
359   }
360 }
361 
362 
363 static void
gst_switch_bin_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)364 gst_switch_bin_get_property (GObject * object, guint prop_id, GValue * value,
365     GParamSpec * pspec)
366 {
367   GstSwitchBin *switch_bin = GST_SWITCH_BIN (object);
368   switch (prop_id) {
369     case PROP_NUM_PATHS:
370       PATH_LOCK (switch_bin);
371       g_value_set_uint (value, switch_bin->num_paths);
372       PATH_UNLOCK_AND_CHECK (switch_bin);
373       break;
374     case PROP_CURRENT_PATH:
375       PATH_LOCK (switch_bin);
376       if (switch_bin->current_path) {
377         guint i;
378         for (i = 0; i < switch_bin->num_paths; ++i) {
379           if (switch_bin->paths[i] == switch_bin->current_path) {
380             g_value_set_uint (value, i);
381             break;
382           }
383         }
384       } else {
385         /* No valid path, return MAXUINT */
386         g_value_set_uint (value, G_MAXUINT);
387       }
388       PATH_UNLOCK (switch_bin);
389       break;
390     default:
391       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
392       break;
393   }
394 }
395 
396 
397 static gboolean
gst_switch_bin_sink_event(GstPad * pad,GstObject * parent,GstEvent * event)398 gst_switch_bin_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
399 {
400   GstSwitchBin *switch_bin = GST_SWITCH_BIN (parent);
401 
402   switch (GST_EVENT_TYPE (event)) {
403     case GST_EVENT_STREAM_START:
404     {
405       GST_DEBUG_OBJECT (switch_bin,
406           "stream-start event observed; copying it for later use");
407       gst_event_replace (&(switch_bin->last_stream_start), event);
408 
409       return gst_pad_event_default (pad, parent, event);
410     }
411 
412     case GST_EVENT_CAPS:
413     {
414       /* Intercept the caps event to switch to an appropriate path, then
415        * resume default caps event processing */
416 
417       GstCaps *caps;
418       gboolean ret;
419 
420       gst_event_parse_caps (event, &caps);
421       GST_DEBUG_OBJECT (switch_bin,
422           "sink pad got caps event with caps %" GST_PTR_FORMAT
423           " ; looking for matching path", (gpointer) caps);
424 
425       PATH_LOCK (switch_bin);
426       ret = gst_switch_bin_select_path_for_caps (switch_bin, caps);
427       PATH_UNLOCK_AND_CHECK (switch_bin);
428 
429       if (!ret) {
430         gst_event_unref (event);
431         return FALSE;
432       } else
433         return gst_pad_event_default (pad, parent, event);
434     }
435 
436     default:
437       GST_DEBUG_OBJECT (switch_bin, "sink event: %s",
438           gst_event_type_get_name (GST_EVENT_TYPE (event)));
439       return gst_pad_event_default (pad, parent, event);
440   }
441 }
442 
443 
444 static gboolean
gst_switch_bin_handle_query(GstPad * pad,GstObject * parent,GstQuery * query,char const * pad_name)445 gst_switch_bin_handle_query (GstPad * pad, GstObject * parent, GstQuery * query,
446     char const *pad_name)
447 {
448   GstSwitchBin *switch_bin = GST_SWITCH_BIN (parent);
449 
450   switch (GST_QUERY_TYPE (query)) {
451     case GST_QUERY_CAPS:
452     {
453       GstCaps *filter, *caps;
454 
455       gst_query_parse_caps (query, &filter);
456 
457       PATH_LOCK (switch_bin);
458 
459       if (switch_bin->num_paths == 0) {
460         /* No paths exist - cannot return any caps */
461         caps = NULL;
462       } else if ((switch_bin->current_path == NULL)
463           || (switch_bin->current_path->element == NULL)) {
464         /* Paths exist, but there is no current path (or the path is a dropping path,
465          * so no element exists) - just return all allowed caps */
466         caps =
467             gst_switch_bin_get_allowed_caps (switch_bin, pad, pad_name, filter);
468       } else {
469         /* Paths exist and there is a current path
470          * Forward the query to its element */
471 
472         GstQuery *caps_query = gst_query_new_caps (NULL);
473         GstPad *element_pad =
474             gst_element_get_static_pad (switch_bin->current_path->element,
475             pad_name);
476 
477         caps = NULL;
478         if (gst_pad_query (element_pad, caps_query)) {
479           GstCaps *query_caps;
480           gst_query_parse_caps_result (caps_query, &query_caps);
481           caps = gst_caps_copy (query_caps);
482         }
483 
484         gst_query_unref (caps_query);
485         gst_object_unref (GST_OBJECT (element_pad));
486       }
487 
488 
489       PATH_UNLOCK (switch_bin);
490 
491       if (caps != NULL) {
492         GST_DEBUG_OBJECT (switch_bin, "%s caps query:  caps: %" GST_PTR_FORMAT,
493             pad_name, (gpointer) caps);
494         gst_query_set_caps_result (query, caps);
495         gst_caps_unref (caps);
496         return TRUE;
497       } else
498         return FALSE;
499     }
500 
501     case GST_QUERY_ACCEPT_CAPS:
502     {
503       GstCaps *caps;
504       gboolean acceptable;
505 
506       gst_query_parse_accept_caps (query, &caps);
507       PATH_LOCK (switch_bin);
508       acceptable = gst_switch_bin_are_caps_acceptable (switch_bin, caps);
509       PATH_UNLOCK (switch_bin);
510       GST_DEBUG_OBJECT (switch_bin,
511           "%s accept_caps query:  acceptable: %d  caps: %" GST_PTR_FORMAT,
512           pad_name, (gint) acceptable, (gpointer) caps);
513 
514       gst_query_set_accept_caps_result (query, acceptable);
515       return TRUE;
516     }
517 
518     default:
519       return gst_pad_query_default (pad, parent, query);
520   }
521 }
522 
523 
524 static gboolean
gst_switch_bin_sink_query(GstPad * pad,GstObject * parent,GstQuery * query)525 gst_switch_bin_sink_query (GstPad * pad, GstObject * parent, GstQuery * query)
526 {
527   return gst_switch_bin_handle_query (pad, parent, query, "sink");
528 }
529 
530 
531 static gboolean
gst_switch_bin_src_query(GstPad * pad,GstObject * parent,GstQuery * query)532 gst_switch_bin_src_query (GstPad * pad, GstObject * parent, GstQuery * query)
533 {
534   return gst_switch_bin_handle_query (pad, parent, query, "src");
535 }
536 
537 
538 static gboolean
gst_switch_bin_set_num_paths(GstSwitchBin * switch_bin,guint new_num_paths)539 gst_switch_bin_set_num_paths (GstSwitchBin * switch_bin, guint new_num_paths)
540 {
541   guint i;
542   gboolean cur_path_removed = FALSE;
543 
544   /* must be called with path lock held */
545 
546   if (switch_bin->num_paths == new_num_paths) {
547     GST_DEBUG_OBJECT (switch_bin,
548         "no change in number of paths - ignoring call");
549     return TRUE;
550   } else if (switch_bin->num_paths < new_num_paths) {
551     /* New number of paths is larger -> N new paths need to be created & added,
552      * where N = new_num_paths - switch_bin->num_paths. */
553 
554     GST_DEBUG_OBJECT (switch_bin, "adding %u new paths",
555         new_num_paths - switch_bin->num_paths);
556 
557     switch_bin->paths =
558         g_realloc (switch_bin->paths, sizeof (GstObject *) * new_num_paths);
559 
560     for (i = switch_bin->num_paths; i < new_num_paths; ++i) {
561       GstSwitchBinPath *path;
562       gchar *path_name;
563 
564       /* default names would be something like "switchbinpath0" */
565       path_name = g_strdup_printf ("path%u", i);
566       switch_bin->paths[i] = path =
567           g_object_new (gst_switch_bin_path_get_type (), "name", path_name,
568           NULL);
569       path->bin = switch_bin;
570 
571       gst_object_set_parent (GST_OBJECT (path), GST_OBJECT (switch_bin));
572       gst_child_proxy_child_added (GST_CHILD_PROXY (switch_bin),
573           G_OBJECT (path), path_name);
574 
575       GST_DEBUG_OBJECT (switch_bin, "added path #%u \"%s\" (%p)", i, path_name,
576           (gpointer) path);
577       g_free (path_name);
578     }
579   } else {
580     /* New number of paths is smaller -> the last N paths need to be removed,
581      * where N = switch_bin->num_paths - new_num_paths. If one of the paths
582      * that are being removed is the current path, then a new current path
583      * is selected. */
584     gchar *path_name;
585 
586     GST_DEBUG_OBJECT (switch_bin, "removing the last %u paths",
587         switch_bin->num_paths - new_num_paths);
588 
589     for (i = new_num_paths; i < switch_bin->num_paths; ++i) {
590       GstSwitchBinPath *path = switch_bin->paths[i];
591       path_name = g_strdup (GST_OBJECT_NAME (path));
592 
593       if (path == switch_bin->current_path) {
594         cur_path_removed = TRUE;
595         gst_switch_bin_switch_to_path (switch_bin, NULL);
596 
597         GST_DEBUG_OBJECT (switch_bin,
598             "path #%u \"%s\" (%p) is the current path - selecting a new current path will be necessary",
599             i, path_name, (gpointer) (switch_bin->paths[i]));
600       }
601 
602       gst_child_proxy_child_removed (GST_CHILD_PROXY (switch_bin),
603           G_OBJECT (path), path_name);
604       gst_object_unparent (GST_OBJECT (switch_bin->paths[i]));
605 
606       GST_DEBUG_OBJECT (switch_bin, "removed path #%u \"%s\" (%p)", i,
607           path_name, (gpointer) (switch_bin->paths[i]));
608       g_free (path_name);
609     }
610 
611     switch_bin->paths =
612         g_realloc (switch_bin->paths, sizeof (GstObject *) * new_num_paths);
613   }
614 
615   switch_bin->num_paths = new_num_paths;
616 
617   if (new_num_paths > 0) {
618     if (cur_path_removed) {
619       /* Select a new current path if the previous one was removed above */
620 
621       if (switch_bin->last_caps != NULL) {
622         GST_DEBUG_OBJECT (switch_bin,
623             "current path was removed earlier - need to select a new one based on the last caps %"
624             GST_PTR_FORMAT, (gpointer) (switch_bin->last_caps));
625         return gst_switch_bin_select_path_for_caps (switch_bin,
626             switch_bin->last_caps);
627       } else {
628         /* This should not happen. Every time a current path is selected, the
629          * caps that were used for the selection are copied as the last_caps.
630          * So, if a current path exists, but last_caps is NULL, it indicates
631          * a bug. For example, if the current path was selected without calling
632          * gst_switch_bin_select_path_for_caps(). */
633         g_assert_not_reached ();
634         return FALSE;           /* shuts up compiler warning */
635       }
636     } else
637       return TRUE;
638   } else
639     return gst_switch_bin_switch_to_path (switch_bin, NULL);
640 }
641 
642 
643 static gboolean
gst_switch_bin_select_path_for_caps(GstSwitchBin * switch_bin,GstCaps * caps)644 gst_switch_bin_select_path_for_caps (GstSwitchBin * switch_bin, GstCaps * caps)
645 {
646   /* must be called with path lock held */
647 
648   gboolean ret;
649   GstSwitchBinPath *path;
650 
651   path = gst_switch_bin_find_matching_path (switch_bin, caps);
652   if (path == NULL) {
653     /* No matching path found, the caps are incompatible. Report this and exit. */
654 
655     GST_ELEMENT_ERROR (switch_bin, STREAM, WRONG_TYPE,
656         ("could not find compatible path"), ("sink caps: %" GST_PTR_FORMAT,
657             (gpointer) caps));
658     ret = FALSE;
659   } else {
660     /* Matching path found. Try to switch to it. */
661 
662     GST_DEBUG_OBJECT (switch_bin, "found matching path \"%s\" (%p) - switching",
663         GST_OBJECT_NAME (path), (gpointer) path);
664     ret = gst_switch_bin_switch_to_path (switch_bin, path);
665   }
666 
667   if (ret && (caps != switch_bin->last_caps))
668     gst_caps_replace (&(switch_bin->last_caps), caps);
669 
670   return ret;
671 }
672 
673 
674 static gboolean
gst_switch_bin_switch_to_path(GstSwitchBin * switch_bin,GstSwitchBinPath * switch_bin_path)675 gst_switch_bin_switch_to_path (GstSwitchBin * switch_bin,
676     GstSwitchBinPath * switch_bin_path)
677 {
678   /* must be called with path lock held */
679 
680   gboolean ret = TRUE;
681 
682   if (switch_bin_path != NULL)
683     GST_DEBUG_OBJECT (switch_bin, "switching to path \"%s\" (%p)",
684         GST_OBJECT_NAME (switch_bin_path), (gpointer) switch_bin_path);
685   else
686     GST_DEBUG_OBJECT (switch_bin,
687         "switching to NULL path (= disabling current path)");
688 
689   /* No current path set and no path is to be set -> nothing to do */
690   if ((switch_bin_path == NULL) && (switch_bin->current_path == NULL))
691     return TRUE;
692 
693   /* If this path is already the current one, do nothing */
694   if (switch_bin->current_path == switch_bin_path)
695     return TRUE;
696 
697   /* Block incoming data to be able to safely switch */
698   gst_switch_bin_set_sinkpad_block (switch_bin, TRUE);
699 
700   /* Unlink the current path's element (if there is a current path) */
701   if (switch_bin->current_path != NULL) {
702     GstSwitchBinPath *cur_path = switch_bin->current_path;
703 
704     if (cur_path->element != NULL) {
705       gst_element_set_state (cur_path->element, GST_STATE_NULL);
706       gst_element_unlink (switch_bin->input_identity, cur_path->element);
707     }
708 
709     gst_ghost_pad_set_target (GST_GHOST_PAD (switch_bin->srcpad), NULL);
710 
711     switch_bin->current_path = NULL;
712     switch_bin->path_changed = TRUE;
713   }
714 
715   /* Link the new path's element (if a new path is specified) */
716   if (switch_bin_path != NULL) {
717     if (switch_bin_path->element != NULL) {
718       GstPad *pad;
719 
720       /* There is a path element. Link it into the pipeline. Data passes through
721        * it now, since its associated path just became the current one. */
722 
723       /* TODO: currently, only elements with one "src" "sink" always-pad are supported;
724        * add support for request and sometimes pads */
725 
726       pad = gst_element_get_static_pad (switch_bin_path->element, "src");
727       if (pad == NULL) {
728         GST_ERROR_OBJECT (switch_bin,
729             "path element has no static srcpad - cannot link");
730         ret = FALSE;
731         goto finish;
732       }
733 
734       if (!gst_ghost_pad_set_target (GST_GHOST_PAD (switch_bin->srcpad), pad)) {
735         GST_ERROR_OBJECT (switch_bin,
736             "could not set the path element's srcpad as the ghost srcpad's target");
737         ret = FALSE;
738       }
739 
740       gst_object_unref (GST_OBJECT (pad));
741 
742       if (!ret)
743         goto finish;
744 
745       if (!gst_element_link (switch_bin->input_identity,
746               switch_bin_path->element)) {
747         GST_ERROR_OBJECT (switch_bin,
748             "linking the path element's sinkpad failed ; check if the path element's sink caps and the upstream elements connected to the switchbin's sinkpad match");
749         ret = FALSE;
750         goto finish;
751       }
752 
753       /* Unlock the element's state in case it was locked earlier
754        * so its state can be synced to the switchbin's */
755       gst_element_set_locked_state (switch_bin_path->element, FALSE);
756       if (!gst_element_sync_state_with_parent (switch_bin_path->element)) {
757         GST_ERROR_OBJECT (switch_bin,
758             "could not sync the path element's state with that of the switchbin");
759         ret = FALSE;
760         goto finish;
761       }
762     } else {
763       GstPad *srcpad;
764 
765       /* There is no path element. This will probably yield an error
766        * into the pipeline unless we're shutting down */
767       GST_DEBUG_OBJECT (switch_bin, "path has no element ; will drop data");
768 
769       srcpad = gst_element_get_static_pad (switch_bin->input_identity, "src");
770 
771       g_assert (srcpad != NULL);
772 
773       if (!gst_ghost_pad_set_target (GST_GHOST_PAD (switch_bin->srcpad),
774               srcpad)) {
775         GST_ERROR_OBJECT (switch_bin,
776             "could not set the path element's srcpad as the ghost srcpad's target");
777         ret = FALSE;
778       }
779 
780       GST_DEBUG_OBJECT (switch_bin,
781           "pushing stream-start downstream before disabling");
782       gst_element_send_event (switch_bin->input_identity,
783           gst_event_ref (switch_bin->last_stream_start));
784 
785       gst_object_unref (GST_OBJECT (srcpad));
786     }
787   }
788 
789   switch_bin->current_path = switch_bin_path;
790   switch_bin->path_changed = TRUE;
791 
792   /* If there is a new path to use, unblock the input */
793   if (switch_bin_path != NULL)
794     gst_switch_bin_set_sinkpad_block (switch_bin, FALSE);
795 
796 finish:
797   return ret;
798 }
799 
800 
801 static GstSwitchBinPath *
gst_switch_bin_find_matching_path(GstSwitchBin * switch_bin,GstCaps const * caps)802 gst_switch_bin_find_matching_path (GstSwitchBin * switch_bin,
803     GstCaps const *caps)
804 {
805   /* must be called with path lock held */
806 
807   guint i;
808 
809   for (i = 0; i < switch_bin->num_paths; ++i) {
810     GstSwitchBinPath *path = switch_bin->paths[i];
811     if (gst_caps_can_intersect (caps, path->caps))
812       return path;
813   }
814 
815   return NULL;
816 }
817 
818 
819 static GstCaps *
gst_switch_bin_get_allowed_caps(GstSwitchBin * switch_bin,GstPad * switch_bin_pad,gchar const * pad_name,GstCaps * filter)820 gst_switch_bin_get_allowed_caps (GstSwitchBin * switch_bin,
821     GstPad * switch_bin_pad, gchar const *pad_name, GstCaps * filter)
822 {
823   /* must be called with path lock held */
824 
825   guint i;
826   GstCaps *total_path_caps;
827   gboolean is_sink_pad =
828       (gst_pad_get_direction (switch_bin_pad) == GST_PAD_SINK);
829 
830   /* The allowed caps are a combination of the caps of all paths, the
831    * filter caps, and the allowed caps as indicated by the result
832    * of the CAPS query on the current path's element.
833    * Since the CAPS query result can be influenced by an element's
834    * current state and link to other elements, the non-current
835    * path elements are not queried.
836    *
837    * In theory, it would be enough to just append all path caps. However,
838    * to refine this a bit further, in case of the current path, the
839    * path caps are first intersected with the result of the CAPS query.
840    * This narrows down the acceptable caps for this current path,
841    * hopefully providing better quality caps. */
842 
843   if (switch_bin->num_paths == 0) {
844     /* No paths exist, so nothing can be returned */
845     GST_ELEMENT_ERROR (switch_bin, STREAM, FAILED, ("no paths defined"),
846         ("there must be at least one path in order for switchbin to do anything"));
847     return NULL;
848   }
849 
850   total_path_caps = gst_caps_new_empty ();
851 
852   for (i = 0; i < switch_bin->num_paths; ++i) {
853     GstSwitchBinPath *path = switch_bin->paths[i];
854 
855     if (path->element != NULL) {
856       GstPad *pad;
857       GstCaps *caps, *intersected_caps;
858       GstQuery *caps_query = NULL;
859 
860       pad = gst_element_get_static_pad (path->element, pad_name);
861       caps_query = gst_query_new_caps (NULL);
862 
863       /* Query the path element for allowed caps. If this is
864        * successful, intersect the returned caps with the path caps for the sink pad,
865        * and append the result of the intersection to the total_path_caps,
866        * or just append the result to the total_path_caps if collecting srcpad caps. */
867       if (gst_pad_query (pad, caps_query)) {
868         gst_query_parse_caps_result (caps_query, &caps);
869         if (is_sink_pad) {
870           intersected_caps = gst_caps_intersect (caps, path->caps);
871         } else {
872           intersected_caps = gst_caps_copy (caps);
873         }
874         gst_caps_append (total_path_caps, intersected_caps);
875       } else if (is_sink_pad) {
876         /* Just assume the sink pad has the path caps if the query failed */
877         gst_caps_append (total_path_caps, gst_caps_ref (path->caps));
878       }
879 
880       gst_object_unref (GST_OBJECT (pad));
881       gst_query_unref (caps_query);
882     } else {
883       /* This is a path with no element (= is a dropping path),
884        * If querying the sink caps, append the path
885        * input caps, otherwise the output caps can be ANY */
886       if (is_sink_pad)
887         gst_caps_append (total_path_caps, gst_caps_ref (path->caps));
888       else
889         gst_caps_append (total_path_caps, gst_caps_new_any ());
890     }
891   }
892 
893   /* Apply filter caps if present */
894   if (filter != NULL) {
895     GstCaps *tmp_caps = total_path_caps;
896     total_path_caps = gst_caps_intersect (tmp_caps, filter);
897     gst_caps_unref (tmp_caps);
898   }
899 
900   return total_path_caps;
901 }
902 
903 
904 static gboolean
gst_switch_bin_are_caps_acceptable(GstSwitchBin * switch_bin,GstCaps const * caps)905 gst_switch_bin_are_caps_acceptable (GstSwitchBin * switch_bin,
906     GstCaps const *caps)
907 {
908   /* must be called with path lock held */
909 
910   return (gst_switch_bin_find_matching_path (switch_bin, caps) != NULL);
911 }
912 
913 
914 static void
gst_switch_bin_set_sinkpad_block(GstSwitchBin * switch_bin,gboolean do_block)915 gst_switch_bin_set_sinkpad_block (GstSwitchBin * switch_bin, gboolean do_block)
916 {
917   GstPad *pad;
918 
919   if ((do_block && (switch_bin->blocking_probe_id != 0)) || (!do_block
920           && (switch_bin->blocking_probe_id == 0)))
921     return;
922 
923   pad = gst_element_get_static_pad (switch_bin->input_identity, "sink");
924 
925   if (do_block) {
926     switch_bin->blocking_probe_id =
927         gst_pad_add_probe (pad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
928         gst_switch_bin_blocking_pad_probe, NULL, NULL);
929   } else {
930     gst_pad_remove_probe (pad, switch_bin->blocking_probe_id);
931     switch_bin->blocking_probe_id = 0;
932   }
933 
934   GST_DEBUG_OBJECT (switch_bin, "sinkpad block enabled: %d", do_block);
935 
936   gst_object_unref (GST_OBJECT (pad));
937 }
938 
939 
940 static GstPadProbeReturn
gst_switch_bin_blocking_pad_probe(G_GNUC_UNUSED GstPad * pad,GstPadProbeInfo * info,G_GNUC_UNUSED gpointer user_data)941 gst_switch_bin_blocking_pad_probe (G_GNUC_UNUSED GstPad * pad,
942     GstPadProbeInfo * info, G_GNUC_UNUSED gpointer user_data)
943 {
944   if (GST_PAD_PROBE_INFO_TYPE (info) & GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM) {
945     GstEvent *event = GST_PAD_PROBE_INFO_EVENT (info);
946     switch (GST_EVENT_TYPE (event)) {
947       case GST_EVENT_CAPS:
948       case GST_EVENT_STREAM_START:
949         return GST_PAD_PROBE_PASS;
950       default:
951         break;
952     }
953   }
954 
955   return GST_PAD_PROBE_OK;
956 }
957 
958 /************ GstSwitchBinPath ************/
959 
960 
961 typedef struct _GstSwitchBinPathClass GstSwitchBinPathClass;
962 
963 
964 #define GST_TYPE_SWITCH_BIN_PATH             (gst_switch_bin_path_get_type())
965 #define GST_SWITCH_BIN_PATH(obj)             (G_TYPE_CHECK_INSTANCE_CAST((obj), GST_TYPE_SWITCH_BIN_PATH, GstSwitchBinPath))
966 #define GST_SWITCH_BIN_PATH_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST((klass), GST_TYPE_SWITCH_BIN_PATH, GstSwitchBinPathClass))
967 #define GST_SWITCH_BIN_PATH_CAST(obj)        ((GstSwitchBinPath *)(obj))
968 #define GST_IS_SWITCH_BIN_PATH(obj)          (G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_TYPE_SWITCH_BIN_PATH))
969 #define GST_IS_SWITCH_BIN_PATH_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_SWITCH_BIN_PATH))
970 
971 
972 struct _GstSwitchBinPathClass
973 {
974   GstObjectClass parent_class;
975 };
976 
977 
978 enum
979 {
980   PROP_PATH_0,
981   PROP_ELEMENT,
982   PROP_PATH_CAPS
983 };
984 
985 
986 G_DEFINE_TYPE (GstSwitchBinPath, gst_switch_bin_path, GST_TYPE_OBJECT);
987 
988 
989 static void gst_switch_bin_path_dispose (GObject * object);
990 static void gst_switch_bin_path_set_property (GObject * object,
991     guint prop_id, GValue const *value, GParamSpec * pspec);
992 static void gst_switch_bin_path_get_property (GObject * object,
993     guint prop_id, GValue * value, GParamSpec * pspec);
994 
995 static gboolean gst_switch_bin_path_use_new_element (GstSwitchBinPath *
996     switch_bin_path, GstElement * new_element);
997 
998 
999 
1000 static void
gst_switch_bin_path_class_init(GstSwitchBinPathClass * klass)1001 gst_switch_bin_path_class_init (GstSwitchBinPathClass * klass)
1002 {
1003   GObjectClass *object_class = G_OBJECT_CLASS (klass);
1004 
1005   object_class->dispose = GST_DEBUG_FUNCPTR (gst_switch_bin_path_dispose);
1006   object_class->set_property =
1007       GST_DEBUG_FUNCPTR (gst_switch_bin_path_set_property);
1008   object_class->get_property =
1009       GST_DEBUG_FUNCPTR (gst_switch_bin_path_get_property);
1010 
1011   g_object_class_install_property (object_class,
1012       PROP_ELEMENT,
1013       g_param_spec_object ("element",
1014           "Element",
1015           "The path's element (if set to NULL, this path will drop any incoming data)",
1016           GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)
1017       );
1018   g_object_class_install_property (object_class,
1019       PROP_PATH_CAPS,
1020       g_param_spec_boxed ("caps",
1021           "Caps",
1022           "Caps which, if they are a subset of the input caps, select this path as the active one",
1023           GST_TYPE_CAPS, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)
1024       );
1025 }
1026 
1027 
1028 static void
gst_switch_bin_path_init(GstSwitchBinPath * switch_bin_path)1029 gst_switch_bin_path_init (GstSwitchBinPath * switch_bin_path)
1030 {
1031   switch_bin_path->caps = gst_caps_new_any ();
1032   switch_bin_path->element = NULL;
1033   switch_bin_path->bin = NULL;
1034 }
1035 
1036 
1037 static void
gst_switch_bin_path_dispose(GObject * object)1038 gst_switch_bin_path_dispose (GObject * object)
1039 {
1040   GstSwitchBinPath *switch_bin_path = GST_SWITCH_BIN_PATH (object);
1041 
1042   if (switch_bin_path->caps != NULL) {
1043     gst_caps_unref (switch_bin_path->caps);
1044     switch_bin_path->caps = NULL;
1045   }
1046 
1047   if (switch_bin_path->element != NULL) {
1048     gst_switch_bin_path_use_new_element (switch_bin_path, NULL);
1049   }
1050 
1051   /* element is managed by the bin itself */
1052 
1053   G_OBJECT_CLASS (gst_switch_bin_path_parent_class)->dispose (object);
1054 }
1055 
1056 
1057 static void
gst_switch_bin_path_set_property(GObject * object,guint prop_id,GValue const * value,GParamSpec * pspec)1058 gst_switch_bin_path_set_property (GObject * object, guint prop_id,
1059     GValue const *value, GParamSpec * pspec)
1060 {
1061   GstSwitchBinPath *switch_bin_path = GST_SWITCH_BIN_PATH (object);
1062   switch (prop_id) {
1063     case PROP_ELEMENT:
1064     {
1065       /* Get the object without modifying the refcount */
1066       GstElement *new_element = GST_ELEMENT (g_value_get_object (value));
1067 
1068       GST_OBJECT_LOCK (switch_bin_path);
1069       PATH_LOCK (switch_bin_path->bin);
1070       gst_switch_bin_path_use_new_element (switch_bin_path, new_element);
1071       PATH_UNLOCK_AND_CHECK (switch_bin_path->bin);
1072       GST_OBJECT_UNLOCK (switch_bin_path);
1073 
1074       break;
1075     }
1076 
1077     case PROP_PATH_CAPS:
1078     {
1079       GstCaps *old_caps;
1080       GstCaps const *new_caps = gst_value_get_caps (value);
1081 
1082       GST_OBJECT_LOCK (switch_bin_path);
1083       old_caps = switch_bin_path->caps;
1084       if (new_caps == NULL) {
1085         /* NULL caps are interpreted as ANY */
1086         switch_bin_path->caps = gst_caps_new_any ();
1087       } else
1088         switch_bin_path->caps = gst_caps_copy (new_caps);
1089       GST_OBJECT_UNLOCK (switch_bin_path);
1090 
1091       if (old_caps != NULL)
1092         gst_caps_unref (old_caps);
1093 
1094       /* the new caps do not get applied right away
1095        * they only start to be used with the next stream */
1096 
1097       break;
1098     }
1099 
1100     default:
1101       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1102       break;
1103   }
1104 }
1105 
1106 
1107 static void
gst_switch_bin_path_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)1108 gst_switch_bin_path_get_property (GObject * object, guint prop_id,
1109     GValue * value, GParamSpec * pspec)
1110 {
1111   GstSwitchBinPath *switch_bin_path = GST_SWITCH_BIN_PATH (object);
1112   switch (prop_id) {
1113     case PROP_ELEMENT:
1114       /* If a path element exists, increase its refcount first. This is
1115        * necessary because the code that called g_object_get() to fetch this
1116        * element will also unref it when it is finished with it. */
1117       if (switch_bin_path->element != NULL)
1118         gst_object_ref (GST_OBJECT (switch_bin_path->element));
1119 
1120       /* Use g_value_take_object() instead of g_value_set_object() as the latter
1121        * increases the element's refcount for the duration of the GValue's lifetime */
1122       g_value_take_object (value, switch_bin_path->element);
1123 
1124       break;
1125 
1126     case PROP_PATH_CAPS:
1127       GST_OBJECT_LOCK (switch_bin_path);
1128       gst_value_set_caps (value, switch_bin_path->caps);
1129       GST_OBJECT_UNLOCK (switch_bin_path);
1130       break;
1131 
1132     default:
1133       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1134       break;
1135   }
1136 }
1137 
1138 
1139 static gboolean
gst_switch_bin_path_use_new_element(GstSwitchBinPath * switch_bin_path,GstElement * new_element)1140 gst_switch_bin_path_use_new_element (GstSwitchBinPath * switch_bin_path,
1141     GstElement * new_element)
1142 {
1143   /* Must be called with lock */
1144 
1145   GstSwitchBinPath *current_path = switch_bin_path->bin->current_path;
1146   gboolean is_current_path = (current_path == switch_bin_path);
1147 
1148   /* Before switching the element, make sure it is not linked,
1149    * which is the case if this is the current path. */
1150   if (is_current_path)
1151     gst_switch_bin_switch_to_path (switch_bin_path->bin, NULL);
1152 
1153   /* Remove any present path element prior to using the new one */
1154   if (switch_bin_path->element != NULL) {
1155     gst_element_set_state (switch_bin_path->element, GST_STATE_NULL);
1156     /* gst_bin_remove automatically unrefs the path element */
1157     gst_bin_remove (GST_BIN (switch_bin_path->bin), switch_bin_path->element);
1158     switch_bin_path->element = NULL;
1159   }
1160 
1161   /* If there *is* a new element, use it. new_element == NULL is a valid case;
1162    * a NULL element is used in dropping paths, which will just use the drop probe
1163    * to drop buffers if they become the current path. */
1164   if (new_element != NULL) {
1165     gst_bin_add (GST_BIN (switch_bin_path->bin), new_element);
1166     switch_bin_path->element = new_element;
1167 
1168     /* Lock the element's state in case. This prevents freezes, which can happen
1169      * when an element from a not-current path tries to follow a state change,
1170      * but is unable to do so as long as it isn't linked. By locking the state,
1171      * it won't follow state changes, so the freeze does not happen. */
1172     gst_element_set_locked_state (new_element, TRUE);
1173   }
1174 
1175   /* We are done. Switch back to the path if it is the current one,
1176    * since we switched away from it earlier. */
1177   if (is_current_path)
1178     return gst_switch_bin_switch_to_path (switch_bin_path->bin, current_path);
1179   else
1180     return TRUE;
1181 }
1182