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