1 /*
2  * GStreamer
3  *
4  *  Copyright 2013 Collabora Ltd
5  *   @author: Olivier Crete <olivier.crete@collabora.com>
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public
18  * License along with this library; if not, write to the
19  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20  * Boston, MA 02111-1307, USA.
21  *
22  */
23 
24 /**
25  * SECTION:gstinsertbin
26  * @short_description: A #GstBin to insertally link filter-like elements.
27  *
28  * This element is a #GstBin that has a single source and sink pad. It allows
29  * the user (the application) to easily add and remove filter-like element
30  * (that has a single source and sink pad), to the pipeline while it is running.
31  * It features a fully asynchronous API inspired by GLib's GAsyncResult based
32  * APIs.
33  *
34  * Each operation (addition or removal) can take a callback, this callback
35  * is guaranteed to be called. Unlike GIO, there is no guarantee about where
36  * this callback will be called from, it could be called before the action
37  * returns or it could be called later from another thread. The signature of
38  * this callback GstInsertBinCallback().
39  *
40  * Since: 1.2
41  */
42 
43 #ifdef HAVE_CONFIG_H
44 #include "config.h"
45 #endif
46 
47 #include "gstinsertbin.h"
48 
49 
50 GST_DEBUG_CATEGORY_STATIC (insert_bin_debug);
51 #define GST_CAT_DEFAULT (insert_bin_debug)
52 
53 
54 static GstStaticPadTemplate gst_insert_bin_sink_template =
55 GST_STATIC_PAD_TEMPLATE ("sink",
56     GST_PAD_SINK,
57     GST_PAD_ALWAYS,
58     GST_STATIC_CAPS_ANY);
59 
60 static GstStaticPadTemplate gst_insert_bin_src_template =
61 GST_STATIC_PAD_TEMPLATE ("src",
62     GST_PAD_SRC,
63     GST_PAD_ALWAYS,
64     GST_STATIC_CAPS_ANY);
65 
66 enum
67 {
68   SIG_PREPEND,
69   SIG_APPEND,
70   SIG_INSERT_BEFORE,
71   SIG_INSERT_AFTER,
72   SIG_REMOVE,
73   LAST_SIGNAL
74 };
75 
76 static guint signals[LAST_SIGNAL];
77 
78 enum
79 {
80   PROP_0,
81 };
82 
83 struct _GstInsertBinPrivate
84 {
85   GstPad *srcpad;
86   GstPad *sinkpad;
87 
88   GQueue change_queue;
89 };
90 
91 typedef enum
92 {
93   GST_INSERT_BIN_ACTION_ADD,
94   GST_INSERT_BIN_ACTION_REMOVE
95 } GstInsertBinAction;
96 
97 
98 typedef enum
99 {
100   DIRECTION_NONE,
101   DIRECTION_AFTER,
102   DIRECTION_BEFORE
103 } GstInsertBinDirection;
104 
105 struct ChangeData
106 {
107   GstElement *element;
108   GstInsertBinAction action;
109   GstElement *sibling;
110   GstInsertBinDirection direction;
111 
112   GstInsertBinCallback callback;
113   gpointer user_data;
114 };
115 
116 static void gst_insert_bin_dispose (GObject * object);
117 
118 
119 static void gst_insert_bin_do_change (GstInsertBin * self, GstPad * pad);
120 static GstPadProbeReturn pad_blocked_cb (GstPad * pad, GstPadProbeInfo * info,
121     gpointer user_data);
122 
123 G_DEFINE_TYPE_WITH_PRIVATE (GstInsertBin, gst_insert_bin, GST_TYPE_BIN);
124 
125 static void
gst_insert_bin_class_init(GstInsertBinClass * klass)126 gst_insert_bin_class_init (GstInsertBinClass * klass)
127 {
128   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
129   GstElementClass *gstelement_class = (GstElementClass *) klass;
130 
131   GST_DEBUG_CATEGORY_INIT (insert_bin_debug, "insertbin", 0, "Insert Bin");
132 
133   gst_element_class_add_static_pad_template (gstelement_class,
134       &gst_insert_bin_src_template);
135   gst_element_class_add_static_pad_template (gstelement_class,
136       &gst_insert_bin_sink_template);
137   gst_element_class_set_static_metadata (gstelement_class, "Insert Bin",
138       "Generic/Bin/Filter", "Auto-links filter style elements insertally",
139       "Olivier Crete <olivier.crete@collabora.com>");
140 
141   gobject_class->dispose = gst_insert_bin_dispose;
142 
143   /**
144    * GstInsertBin::prepend:
145    * @element: the #GstElement to add
146    * @callback: the callback to call when the element has been added or not, or
147    *  %NULL
148    * @user_data: The data to pass to the callback
149    * @user_data2: The user data of the signal (ignored)
150    *
151    * This action signal adds the filter like element before any other element
152    * in the bin.
153    *
154    * Same as gst_insert_bin_prepend()
155    */
156   signals[SIG_PREPEND] = g_signal_new_class_handler ("prepend",
157       G_TYPE_FROM_CLASS (klass),
158       G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
159       G_CALLBACK (gst_insert_bin_prepend),
160       NULL, NULL, NULL,
161       G_TYPE_NONE, 3, GST_TYPE_ELEMENT, G_TYPE_POINTER, G_TYPE_POINTER);
162 
163   /**
164    * GstInsertBin::append:
165    * @element: the #GstElement to add
166    * @callback: the callback to call when the element has been added or not, or
167    *  %NULL
168    * @user_data: The data to pass to the callback
169    * @user_data2: The user data of the signal (ignored)
170    *
171    * This action signal adds the filter like element after any other element
172    * in the bin.
173    *
174    * Same as gst_insert_bin_append()
175    */
176   signals[SIG_APPEND] = g_signal_new_class_handler ("append",
177       G_TYPE_FROM_CLASS (klass),
178       G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
179       G_CALLBACK (gst_insert_bin_append),
180       NULL, NULL, NULL,
181       G_TYPE_NONE, 3, GST_TYPE_ELEMENT, G_TYPE_POINTER, G_TYPE_POINTER);
182 
183   /**
184    * GstInsertBin::insert-before:
185    * @element: the #GstElement to add
186    * @sibling: the #GstElement to add @element before
187    * @callback: the callback to call when the element has been added or not, or
188    *  %NULL
189    * @user_data: The data to pass to the callback
190    * @user_data2: The user data of the signal (ignored)
191    *
192    * This action signal adds the filter like element before the @sibling
193    * element in the bin.
194    *
195    * Same as gst_insert_bin_insert_before()
196    */
197   signals[SIG_INSERT_BEFORE] = g_signal_new_class_handler ("insert-before",
198       G_TYPE_FROM_CLASS (klass),
199       G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
200       G_CALLBACK (gst_insert_bin_insert_before),
201       NULL, NULL, NULL,
202       G_TYPE_NONE, 4, GST_TYPE_ELEMENT, GST_TYPE_ELEMENT,
203       G_TYPE_POINTER, G_TYPE_POINTER);
204 
205   /**
206    * GstInsertBin::insert-after:
207    * @element: the #GstElement to add
208    * @sibling: the #GstElement to add @element after
209    * @callback: the callback to call when the element has been added or not, or
210    *  %NULL
211    * @user_data: The data to pass to the callback
212    * @user_data2: The user data of the signal (ignored)
213    *
214    * This action signal adds the filter like element after the @sibling
215    * element in the bin.
216    * element in the bin.
217    *
218    * Same as gst_insert_bin_insert_after()
219    */
220   signals[SIG_INSERT_AFTER] = g_signal_new_class_handler ("insert-after",
221       G_TYPE_FROM_CLASS (klass),
222       G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
223       G_CALLBACK (gst_insert_bin_insert_after),
224       NULL, NULL, NULL,
225       G_TYPE_NONE, 4, GST_TYPE_ELEMENT, GST_TYPE_ELEMENT,
226       G_TYPE_POINTER, G_TYPE_POINTER);
227 
228 
229   /**
230    * GstInsertBin::remove:
231    * @element: the #GstElement to remove
232    * @callback: the callback to call when the element has been removed or not,
233    * or %NULL
234    * @user_data: The data to pass to the callback
235    * @user_data2: The user data of the signal (ignored)
236    *
237    * This action signal removed the filter like element from the bin.
238    *
239    * Same as gst_insert_bin_remove()
240    */
241   signals[SIG_REMOVE] = g_signal_new_class_handler ("remove",
242       G_TYPE_FROM_CLASS (klass),
243       G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
244       G_CALLBACK (gst_insert_bin_remove),
245       NULL, NULL, NULL,
246       G_TYPE_NONE, 3, GST_TYPE_ELEMENT, G_TYPE_POINTER, G_TYPE_POINTER);
247 }
248 
249 static void
change_data_free(struct ChangeData * data)250 change_data_free (struct ChangeData *data)
251 {
252   gst_object_unref (data->element);
253   if (data->sibling)
254     gst_object_unref (data->sibling);
255   g_slice_free (struct ChangeData, data);
256 }
257 
258 
259 static void
gst_insert_bin_change_data_complete(GstInsertBin * self,struct ChangeData * data,gboolean success)260 gst_insert_bin_change_data_complete (GstInsertBin * self,
261     struct ChangeData *data, gboolean success)
262 {
263   if (data->callback)
264     data->callback (self, data->element, success, data->user_data);
265 
266   change_data_free (data);
267 }
268 
269 static void
gst_insert_bin_init(GstInsertBin * self)270 gst_insert_bin_init (GstInsertBin * self)
271 {
272   GstProxyPad *internal;
273 
274   self->priv = gst_insert_bin_get_instance_private (self);
275 
276   g_queue_init (&self->priv->change_queue);
277 
278   self->priv->sinkpad = gst_ghost_pad_new_no_target_from_template ("sink",
279       gst_element_class_get_pad_template (GST_ELEMENT_GET_CLASS (self),
280           "sink"));
281   gst_element_add_pad (GST_ELEMENT (self), self->priv->sinkpad);
282 
283   internal = gst_proxy_pad_get_internal (GST_PROXY_PAD (self->priv->sinkpad));
284   self->priv->srcpad = gst_ghost_pad_new_from_template ("src",
285       GST_PAD (internal),
286       gst_element_class_get_pad_template (GST_ELEMENT_GET_CLASS (self), "src"));
287   gst_object_unref (internal);
288   gst_element_add_pad (GST_ELEMENT (self), self->priv->srcpad);
289 }
290 
291 static void
gst_insert_bin_dispose(GObject * object)292 gst_insert_bin_dispose (GObject * object)
293 {
294   GstInsertBin *self = GST_INSERT_BIN (object);
295   struct ChangeData *data;
296 
297   while ((data = g_queue_pop_head (&self->priv->change_queue)))
298     gst_insert_bin_change_data_complete (self, data, FALSE);
299 
300   gst_ghost_pad_set_target (GST_GHOST_PAD (self->priv->srcpad), NULL);
301   gst_ghost_pad_set_target (GST_GHOST_PAD (self->priv->sinkpad), NULL);
302 
303   G_OBJECT_CLASS (gst_insert_bin_parent_class)->dispose (object);
304 }
305 
306 static gboolean
validate_element(GstInsertBin * self,GstElement * element)307 validate_element (GstInsertBin * self, GstElement * element)
308 {
309   gboolean valid = TRUE;
310 
311   GST_OBJECT_LOCK (element);
312   if (element->numsrcpads != 1 || element->numsinkpads != 1) {
313     GST_WARNING_OBJECT (self,
314         "Element does not have a single src and sink pad");
315     valid = FALSE;
316   }
317 
318   if (GST_OBJECT_PARENT (element) != NULL) {
319     GST_WARNING_OBJECT (self, "Element already has a parent");
320     valid = FALSE;
321   }
322   GST_OBJECT_UNLOCK (element);
323 
324   return valid;
325 }
326 
327 static GstPad *
get_single_pad(GstElement * element,GstPadDirection direction)328 get_single_pad (GstElement * element, GstPadDirection direction)
329 {
330   GstPad *pad = NULL;
331 
332   GST_OBJECT_LOCK (element);
333   if (direction == GST_PAD_SRC) {
334     if (element->numsrcpads == 1)
335       pad = element->srcpads->data;
336   } else {
337     if (element->numsinkpads == 1)
338       pad = element->sinkpads->data;
339   }
340 
341   if (pad)
342     gst_object_ref (pad);
343   GST_OBJECT_UNLOCK (element);
344 
345   return pad;
346 }
347 
348 static gboolean
is_right_direction_for_block(GstPad * pad)349 is_right_direction_for_block (GstPad * pad)
350 {
351   gboolean right_direction;
352 
353   GST_OBJECT_LOCK (pad);
354   switch (pad->mode) {
355     case GST_PAD_MODE_NONE:
356       right_direction = TRUE;
357       break;
358     case GST_PAD_MODE_PUSH:
359       right_direction = (pad->direction == GST_PAD_SRC);
360       break;
361     case GST_PAD_MODE_PULL:
362       right_direction = (pad->direction == GST_PAD_SINK);
363       break;
364     default:
365       right_direction = FALSE;
366       g_assert_not_reached ();
367   }
368   GST_OBJECT_UNLOCK (pad);
369 
370   return right_direction;
371 }
372 
373 static void
gst_insert_bin_block_pad_unlock(GstInsertBin * self)374 gst_insert_bin_block_pad_unlock (GstInsertBin * self)
375 {
376   struct ChangeData *data;
377   GstPad *pad;
378   GstPadProbeType probetype;
379 
380 again:
381 
382   data = g_queue_peek_head (&self->priv->change_queue);
383   if (!data) {
384     GST_OBJECT_UNLOCK (self);
385     return;
386   }
387 
388   if (data->action == GST_INSERT_BIN_ACTION_ADD &&
389       !validate_element (self, data->element))
390     goto error;
391 
392   if (data->action == GST_INSERT_BIN_ACTION_ADD) {
393     if (data->sibling) {
394       if (data->direction == DIRECTION_BEFORE)
395         pad = get_single_pad (data->sibling, GST_PAD_SINK);
396       else
397         pad = get_single_pad (data->sibling, GST_PAD_SRC);
398     } else {
399       if (data->direction == DIRECTION_BEFORE)
400         pad = (GstPad *)
401             gst_proxy_pad_get_internal (GST_PROXY_PAD (self->priv->srcpad));
402       else
403         pad = (GstPad *)
404             gst_proxy_pad_get_internal (GST_PROXY_PAD (self->priv->sinkpad));
405     }
406 
407     if (!pad) {
408       GST_WARNING_OBJECT (self, "Can not obtain a sibling pad to block"
409           " before adding");
410       goto error;
411     }
412 
413     if (!is_right_direction_for_block (pad)) {
414       GstPad *peer = gst_pad_get_peer (pad);
415 
416       if (peer) {
417         gst_object_unref (pad);
418         pad = peer;
419       }
420     }
421   } else {
422     GstPad *element_pad;
423 
424     element_pad = get_single_pad (data->element, GST_PAD_SINK);
425     if (!element_pad) {
426       GST_WARNING_OBJECT (self, "Can not obtain the element's sink pad");
427       goto error;
428     }
429 
430     if (!is_right_direction_for_block (element_pad)) {
431       pad = gst_pad_get_peer (element_pad);
432     } else {
433       gst_object_unref (element_pad);
434 
435       element_pad = get_single_pad (data->element, GST_PAD_SRC);
436       if (!element_pad) {
437         GST_WARNING_OBJECT (self, "Can not obtain the element's src pad");
438         goto error;
439       }
440 
441       pad = gst_pad_get_peer (element_pad);
442     }
443     gst_object_unref (element_pad);
444 
445     if (!pad) {
446       GST_WARNING_OBJECT (self, "Can not obtain a sibling pad for removing");
447       goto error;
448     }
449   }
450 
451   if (GST_PAD_IS_SRC (pad))
452     probetype = GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM;
453   else
454     probetype = GST_PAD_PROBE_TYPE_BLOCK_UPSTREAM;
455 
456   GST_OBJECT_UNLOCK (self);
457   gst_pad_add_probe (pad, GST_PAD_PROBE_TYPE_IDLE | probetype, pad_blocked_cb,
458       self, NULL);
459   gst_object_unref (pad);
460   return;
461 
462 error:
463   g_queue_pop_head (&self->priv->change_queue);
464   gst_insert_bin_change_data_complete (self, data, FALSE);
465   goto again;
466 }
467 
468 static GstPadProbeReturn
wait_and_drop_eos_cb(GstPad * pad,GstPadProbeInfo * info,gpointer user_data)469 wait_and_drop_eos_cb (GstPad * pad, GstPadProbeInfo * info, gpointer user_data)
470 {
471   GstPad *peer;
472 
473   if (GST_EVENT_TYPE (GST_PAD_PROBE_INFO_EVENT (info)) != GST_EVENT_EOS)
474     return GST_PAD_PROBE_PASS;
475 
476   peer = gst_pad_get_peer (pad);
477   if (peer) {
478     gst_pad_unlink (pad, peer);
479     gst_object_unref (peer);
480   }
481 
482   return GST_PAD_PROBE_DROP;
483 }
484 
485 static void
gst_insert_bin_do_change(GstInsertBin * self,GstPad * pad)486 gst_insert_bin_do_change (GstInsertBin * self, GstPad * pad)
487 {
488   struct ChangeData *data;
489 
490   GST_OBJECT_LOCK (self);
491 
492   if (!is_right_direction_for_block (pad)) {
493     GST_WARNING_OBJECT (self, "Block pad does not have the expected direction");
494     goto next;
495   }
496 
497   while ((data = g_queue_pop_head (&self->priv->change_queue)) != NULL) {
498     GstPad *peer = NULL;
499     GstPad *other_peer = NULL;
500 
501     GST_OBJECT_UNLOCK (self);
502 
503 
504     if (data->action == GST_INSERT_BIN_ACTION_ADD &&
505         !validate_element (self, data->element))
506       goto error;
507 
508     peer = gst_pad_get_peer (pad);
509 
510     if (peer == NULL) {
511       GST_WARNING_OBJECT (self, "Blocked pad has no peer");
512       goto error;
513     }
514 
515     if (data->action == GST_INSERT_BIN_ACTION_ADD) {
516       GstPad *srcpad = NULL, *sinkpad = NULL;
517       GstPad *peersrcpad, *peersinkpad;
518 
519       /* First let's make sure we have the right pad */
520       if (data->sibling) {
521         GstElement *parent = NULL;
522         GstPad *siblingpad;
523 
524         if ((gst_pad_get_direction (pad) == GST_PAD_SRC &&
525                 data->direction == DIRECTION_BEFORE) ||
526             (gst_pad_get_direction (pad) == GST_PAD_SINK &&
527                 data->direction == DIRECTION_AFTER))
528           siblingpad = peer;
529         else
530           siblingpad = pad;
531 
532         parent = gst_pad_get_parent_element (siblingpad);
533         if (parent != NULL)
534           gst_object_unref (parent);
535 
536         if (parent != data->sibling)
537           goto retry;
538       } else {
539         GstObject *parent;
540         GstPad *ghost;
541         GstPad *proxypad;
542 
543         if (data->direction == DIRECTION_BEFORE) {
544           ghost = self->priv->srcpad;
545           if (gst_pad_get_direction (pad) == GST_PAD_SINK)
546             proxypad = pad;
547           else
548             proxypad = peer;
549         } else {
550           ghost = self->priv->sinkpad;
551           if (gst_pad_get_direction (pad) == GST_PAD_SINK)
552             proxypad = peer;
553           else
554             proxypad = pad;
555         }
556 
557         if (!GST_IS_PROXY_PAD (proxypad))
558           goto retry;
559         parent = gst_pad_get_parent (proxypad);
560         if (!parent)
561           goto retry;
562         gst_object_unref (parent);
563         if (GST_PAD_CAST (parent) != ghost)
564           goto retry;
565       }
566 
567       if (gst_pad_get_direction (pad) == GST_PAD_SRC) {
568         peersrcpad = pad;
569         peersinkpad = peer;
570       } else {
571         peersrcpad = peer;
572         peersinkpad = pad;
573       }
574 
575       if (GST_IS_PROXY_PAD (peersrcpad)) {
576         GstObject *parent = gst_pad_get_parent (peersrcpad);
577 
578         if (GST_PAD_CAST (parent) == self->priv->sinkpad)
579           peersrcpad = NULL;
580 
581         if (parent)
582           gst_object_unref (parent);
583       }
584 
585       if (GST_IS_PROXY_PAD (peersinkpad)) {
586         GstObject *parent = gst_pad_get_parent (peersinkpad);
587 
588         if (GST_PAD_CAST (parent) == self->priv->srcpad)
589           peersinkpad = NULL;
590 
591         if (parent)
592           gst_object_unref (parent);
593       }
594 
595       if (peersinkpad && peersrcpad) {
596         gst_pad_unlink (peersrcpad, peersinkpad);
597       } else {
598         if (!peersinkpad)
599           gst_ghost_pad_set_target (GST_GHOST_PAD (self->priv->srcpad), NULL);
600         if (!peersrcpad)
601           gst_ghost_pad_set_target (GST_GHOST_PAD (self->priv->sinkpad), NULL);
602       }
603 
604       srcpad = get_single_pad (data->element, GST_PAD_SRC);
605       sinkpad = get_single_pad (data->element, GST_PAD_SINK);
606 
607       if (srcpad == NULL || sinkpad == NULL) {
608         GST_WARNING_OBJECT (self, "Can not get element src or sink pad");
609         goto error;
610       }
611 
612       if (!gst_bin_add (GST_BIN (self), data->element)) {
613         GST_WARNING_OBJECT (self, "Can not add element to bin");
614         goto error;
615       }
616 
617       if (peersrcpad) {
618         if (GST_PAD_LINK_FAILED (gst_pad_link (peersrcpad, sinkpad))) {
619           GST_WARNING_OBJECT (self, "Can not link sibling's %s:%s pad"
620               " to element's %s:%s pad", GST_DEBUG_PAD_NAME (peersrcpad),
621               GST_DEBUG_PAD_NAME (sinkpad));
622           goto error;
623         }
624       } else {
625         if (!gst_ghost_pad_set_target (GST_GHOST_PAD (self->priv->sinkpad),
626                 sinkpad)) {
627           GST_WARNING_OBJECT (self, "Can not set %s:%s as target for %s:%s",
628               GST_DEBUG_PAD_NAME (sinkpad),
629               GST_DEBUG_PAD_NAME (self->priv->sinkpad));
630           goto error;
631         }
632       }
633 
634       if (peersinkpad) {
635         if (GST_PAD_LINK_FAILED (gst_pad_link (srcpad, peersinkpad))) {
636           GST_WARNING_OBJECT (self, "Can not link element's %s:%s pad"
637               " to sibling's %s:%s pad", GST_DEBUG_PAD_NAME (srcpad),
638               GST_DEBUG_PAD_NAME (peersinkpad));
639           goto error;
640         }
641       } else {
642         if (!gst_ghost_pad_set_target (GST_GHOST_PAD (self->priv->srcpad),
643                 srcpad)) {
644           GST_WARNING_OBJECT (self, "Can not set %s:%s as target for %s:%s",
645               GST_DEBUG_PAD_NAME (srcpad),
646               GST_DEBUG_PAD_NAME (self->priv->srcpad));
647           goto error;
648         }
649       }
650 
651       gst_object_unref (srcpad);
652       gst_object_unref (sinkpad);
653 
654       if (!gst_element_sync_state_with_parent (data->element)) {
655         GST_WARNING_OBJECT (self, "Can not sync element's state with parent");
656         goto error;
657       }
658     } else {
659       GstElement *parent = NULL;
660       GstPad *other_pad;
661       GstCaps *caps = NULL, *peercaps = NULL;
662       gboolean can_intersect;
663       gboolean success;
664 
665       parent = gst_pad_get_parent_element (peer);
666       if (parent != NULL)
667         gst_object_unref (parent);
668 
669       if (parent != data->element)
670         goto retry;
671 
672       if (gst_pad_get_direction (peer) == GST_PAD_SRC)
673         other_pad = get_single_pad (data->element, GST_PAD_SINK);
674       else
675         other_pad = get_single_pad (data->element, GST_PAD_SRC);
676 
677       if (!other_pad) {
678         GST_WARNING_OBJECT (self, "Can not get element's other pad");
679         goto error;
680       }
681 
682       other_peer = gst_pad_get_peer (other_pad);
683       gst_object_unref (other_pad);
684 
685       if (!other_peer) {
686         GST_WARNING_OBJECT (self, "Can not get element's other peer");
687         goto error;
688       }
689 
690       /* Get the negotiated caps for the source pad peer,
691        * because renegotiation while the pipeline is playing doesn't work
692        * that fast.
693        */
694       if (gst_pad_get_direction (pad) == GST_PAD_SRC)
695         caps = gst_pad_get_current_caps (pad);
696       else
697         peercaps = gst_pad_get_current_caps (other_peer);
698       if (!caps)
699         caps = gst_pad_query_caps (pad, NULL);
700       if (!peercaps)
701         peercaps = gst_pad_query_caps (other_peer, NULL);
702       can_intersect = gst_caps_can_intersect (caps, peercaps);
703       gst_caps_unref (caps);
704       gst_caps_unref (peercaps);
705 
706       if (!can_intersect) {
707         GST_WARNING_OBJECT (self, "Pads are incompatible without the element");
708         goto error;
709       }
710 
711       if (gst_pad_get_direction (other_peer) == GST_PAD_SRC &&
712           gst_pad_is_active (other_peer)) {
713         gulong probe_id;
714 
715         probe_id = gst_pad_add_probe (other_peer,
716             GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM,
717             wait_and_drop_eos_cb, NULL, NULL);
718         gst_pad_send_event (peer, gst_event_new_eos ());
719         gst_pad_remove_probe (other_peer, probe_id);
720       }
721 
722       gst_element_set_locked_state (data->element, TRUE);
723       gst_element_set_state (data->element, GST_STATE_NULL);
724       if (!gst_bin_remove (GST_BIN (self), data->element)) {
725         GST_WARNING_OBJECT (self, "Element removal rejected");
726         goto error;
727       }
728       gst_element_set_locked_state (data->element, FALSE);
729 
730       if (gst_pad_get_direction (pad) == GST_PAD_SRC)
731         success = GST_PAD_LINK_SUCCESSFUL (gst_pad_link_full (pad, other_peer,
732                 GST_PAD_LINK_CHECK_HIERARCHY |
733                 GST_PAD_LINK_CHECK_TEMPLATE_CAPS));
734       else
735         success = GST_PAD_LINK_SUCCESSFUL (gst_pad_link_full (other_peer, pad,
736                 GST_PAD_LINK_CHECK_HIERARCHY |
737                 GST_PAD_LINK_CHECK_TEMPLATE_CAPS));
738       gst_object_unref (other_peer);
739       other_peer = NULL;
740 
741       if (!success) {
742         GST_ERROR_OBJECT (self, "Could not re-link after the element's"
743             " removal");
744         goto error;
745       }
746     }
747 
748 
749     gst_insert_bin_change_data_complete (self, data, TRUE);
750     gst_object_unref (peer);
751 
752     GST_OBJECT_LOCK (self);
753     continue;
754   done:
755     if (other_peer != NULL)
756       gst_object_unref (other_peer);
757 
758     if (peer != NULL)
759       gst_object_unref (peer);
760     break;
761   retry:
762     GST_OBJECT_LOCK (self);
763     g_queue_push_head (&self->priv->change_queue, data);
764     goto done;
765   error:
766     /* Handle error */
767     gst_insert_bin_change_data_complete (self, data, FALSE);
768     GST_OBJECT_LOCK (self);
769     goto done;
770   }
771 
772 next:
773   gst_insert_bin_block_pad_unlock (self);
774 }
775 
776 
777 
778 static GstPadProbeReturn
pad_blocked_cb(GstPad * pad,GstPadProbeInfo * info,gpointer user_data)779 pad_blocked_cb (GstPad * pad, GstPadProbeInfo * info, gpointer user_data)
780 {
781   GstInsertBin *self = GST_INSERT_BIN (user_data);
782 
783   g_assert (GST_PAD_PROBE_INFO_TYPE (info) &
784       (GST_PAD_PROBE_TYPE_BLOCK | GST_PAD_PROBE_TYPE_IDLE));
785 
786   gst_insert_bin_do_change (self, pad);
787 
788   return GST_PAD_PROBE_REMOVE;
789 }
790 
791 static void
gst_insert_bin_add_operation(GstInsertBin * self,GstElement * element,GstInsertBinAction action,GstElement * sibling,GstInsertBinDirection direction,GstInsertBinCallback callback,gpointer user_data)792 gst_insert_bin_add_operation (GstInsertBin * self,
793     GstElement * element, GstInsertBinAction action, GstElement * sibling,
794     GstInsertBinDirection direction, GstInsertBinCallback callback,
795     gpointer user_data)
796 {
797   struct ChangeData *data = g_slice_new (struct ChangeData);
798   gboolean block_pad;
799 
800   data->element = element;
801   data->action = action;
802   data->sibling = sibling;
803   if (data->sibling)
804     gst_object_ref (data->sibling);
805   data->direction = direction;
806   data->callback = callback;
807   data->user_data = user_data;
808 
809   GST_OBJECT_LOCK (self);
810   block_pad = g_queue_is_empty (&self->priv->change_queue);
811   g_queue_push_tail (&self->priv->change_queue, data);
812 
813   if (block_pad)
814     gst_insert_bin_block_pad_unlock (self);
815   else
816     GST_OBJECT_UNLOCK (self);
817 }
818 
819 static void
gst_insert_bin_add(GstInsertBin * self,GstElement * element,GstElement * sibling,GstInsertBinDirection direction,GstInsertBinCallback callback,gpointer user_data)820 gst_insert_bin_add (GstInsertBin * self, GstElement * element,
821     GstElement * sibling, GstInsertBinDirection direction,
822     GstInsertBinCallback callback, gpointer user_data)
823 {
824   gst_object_ref_sink (element);
825 
826   if (!validate_element (self, element))
827     goto reject;
828 
829   if (sibling) {
830     gboolean is_parent;
831 
832     GST_OBJECT_LOCK (sibling);
833     is_parent = (GST_OBJECT_PARENT (sibling) == GST_OBJECT_CAST (self));
834     GST_OBJECT_UNLOCK (sibling);
835 
836     if (!is_parent)
837       goto reject;
838   }
839 
840 
841   gst_insert_bin_add_operation (self, element, GST_INSERT_BIN_ACTION_ADD,
842       sibling, direction, callback, user_data);
843   return;
844 
845 reject:
846   if (callback)
847     callback (self, element, FALSE, user_data);
848   gst_object_unref (element);
849 }
850 
851 
852 /**
853  * gst_insert_bin_prepend:
854  * @element: the #GstElement to add
855  * @callback: (scope async): the callback to call when the element has been
856  *  added or not, or %NULL
857  * @user_data: The data to pass to the callback
858  *
859  * This action signal adds the filter like element before any other element
860  * in the bin.
861  *
862  * Same as the #GstInsertBin::prepend signal.
863  *
864  * Since: 1.2
865  */
866 
867 void
gst_insert_bin_prepend(GstInsertBin * self,GstElement * element,GstInsertBinCallback callback,gpointer user_data)868 gst_insert_bin_prepend (GstInsertBin * self, GstElement * element,
869     GstInsertBinCallback callback, gpointer user_data)
870 {
871   g_return_if_fail (GST_IS_ELEMENT (element));
872 
873   gst_insert_bin_add (self, element, NULL, DIRECTION_AFTER,
874       callback, user_data);
875 }
876 
877 /**
878  * gst_insert_bin_append:
879  * @element: the #GstElement to add
880  * @callback: (scope async): the callback to call when the element has been
881  *  added or not, or %NULL
882  * @user_data: The data to pass to the callback
883  *
884  * This action signal adds the filter like element after any other element
885  * in the bin.
886  *
887  * Same as the #GstInsertBin::append signal.
888  *
889  * Since: 1.2
890  */
891 
892 void
gst_insert_bin_append(GstInsertBin * self,GstElement * element,GstInsertBinCallback callback,gpointer user_data)893 gst_insert_bin_append (GstInsertBin * self, GstElement * element,
894     GstInsertBinCallback callback, gpointer user_data)
895 {
896   g_return_if_fail (GST_IS_ELEMENT (element));
897 
898   gst_insert_bin_add (self, element, NULL, DIRECTION_BEFORE,
899       callback, user_data);
900 }
901 
902 /**
903  * gst_insert_bin_insert_before:
904  * @element: the #GstElement to add
905  * @sibling: the #GstElement to add @element before
906  * @callback: (scope async): the callback to call when the element has been
907  *  added or not, or %NULL
908  * @user_data: The data to pass to the callback
909  *
910  * This action signal adds the filter like element before the @sibling
911  * element in the bin.
912  *
913  * Same as the #GstInsertBin::insert-before signal.
914  *
915  * Since: 1.2
916  */
917 void
gst_insert_bin_insert_before(GstInsertBin * self,GstElement * element,GstElement * sibling,GstInsertBinCallback callback,gpointer user_data)918 gst_insert_bin_insert_before (GstInsertBin * self, GstElement * element,
919     GstElement * sibling, GstInsertBinCallback callback, gpointer user_data)
920 {
921   g_return_if_fail (GST_IS_ELEMENT (element));
922   g_return_if_fail (GST_IS_ELEMENT (sibling));
923 
924   gst_insert_bin_add (self, element, sibling, DIRECTION_BEFORE,
925       callback, user_data);
926 }
927 
928 /**
929  * gst_insert_bin_insert_after:
930  * @element: the #GstElement to add
931  * @sibling: the #GstElement to add @element after
932  * @callback: (scope async): the callback to call when the element has been
933  *  added or not, or %NULL
934  * @user_data: The data to pass to the callback
935  *
936  * This action signal adds the filter like element after the @sibling
937  * element in the bin.
938  *
939  * Same as the #GstInsertBin::insert-after signal.
940  *
941  * Since: 1.2
942  */
943 void
gst_insert_bin_insert_after(GstInsertBin * self,GstElement * element,GstElement * sibling,GstInsertBinCallback callback,gpointer user_data)944 gst_insert_bin_insert_after (GstInsertBin * self, GstElement * element,
945     GstElement * sibling, GstInsertBinCallback callback, gpointer user_data)
946 {
947   g_return_if_fail (GST_IS_ELEMENT (element));
948   g_return_if_fail (GST_IS_ELEMENT (sibling));
949 
950   gst_insert_bin_add (self, element, sibling, DIRECTION_AFTER,
951       callback, user_data);
952 }
953 
954 /**
955  * gst_insert_bin_remove:
956  * @element: the #GstElement to remove
957  * @callback: (scope async): the callback to call when the element has been
958  *  removed or not, or %NULL
959  * @user_data: The data to pass to the callback
960  *
961  * This action signal removed the filter like element from the bin.
962  *
963  * Same as the #GstInsertBin::remove signal.
964  *
965  * Since: 1.2
966  */
967 
968 void
gst_insert_bin_remove(GstInsertBin * self,GstElement * element,GstInsertBinCallback callback,gpointer user_data)969 gst_insert_bin_remove (GstInsertBin * self, GstElement * element,
970     GstInsertBinCallback callback, gpointer user_data)
971 {
972   GstObject *parent;
973 
974   g_return_if_fail (GST_IS_ELEMENT (element));
975 
976   parent = gst_element_get_parent (element);
977 
978   if (parent) {
979     gboolean is_parent;
980 
981     is_parent = (parent == GST_OBJECT_CAST (self));
982     gst_object_unref (parent);
983 
984     if (!is_parent) {
985       if (callback)
986         callback (self, element, FALSE, user_data);
987       return;
988     }
989   } else {
990     GList *item;
991     struct ChangeData *data = NULL;
992 
993     GST_OBJECT_LOCK (self);
994     for (item = self->priv->change_queue.head; item; item = item->next) {
995       data = item->data;
996 
997       if (data->element == element) {
998         if (data->action == GST_INSERT_BIN_ACTION_ADD)
999           g_queue_delete_link (&self->priv->change_queue, item);
1000         break;
1001       }
1002       data = NULL;
1003     }
1004     GST_OBJECT_UNLOCK (self);
1005 
1006     if (data) {
1007       gst_object_ref (element);
1008       gst_insert_bin_change_data_complete (self, data, TRUE);
1009       if (callback)
1010         callback (self, element, TRUE, user_data);
1011       gst_object_unref (element);
1012     } else {
1013       if (callback)
1014         callback (self, element, FALSE, user_data);
1015     }
1016     return;
1017   }
1018 
1019   gst_object_ref (element);
1020 
1021   gst_insert_bin_add_operation (self, element, GST_INSERT_BIN_ACTION_REMOVE,
1022       NULL, FALSE, callback, user_data);
1023 }
1024 
1025 /**
1026  * gst_insert_bin_new:
1027  * @name: (allow-none): The name of the new #GstInsertBin element (or %NULL)
1028  *
1029  * Creates a new #GstInsertBin
1030  *
1031  * Returns: The new #GstInsertBin
1032  *
1033  * Since: 1.2
1034  */
1035 
1036 GstElement *
gst_insert_bin_new(const gchar * name)1037 gst_insert_bin_new (const gchar * name)
1038 {
1039   if (name)
1040     return g_object_new (GST_TYPE_INSERT_BIN, "name", name, NULL);
1041   else
1042     return g_object_new (GST_TYPE_INSERT_BIN, NULL);
1043 }
1044