2 * Copyright (C) 2003 Julien Moutte <julien@moutte.net>
3 * Copyright (C) 2005 Ronald S. Bultje <rbultje@ronald.bitfreak.net>
4 * Copyright (C) 2005 Jan Schmidt <thaytan@mad.scientist.com>
5 * Copyright (C) 2007 Wim Taymans <wim.taymans@gmail.com>
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.
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.
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.
29 #include "gststreamselector.h"
31 GST_DEBUG_CATEGORY_STATIC (stream_selector_debug);
32 #define GST_CAT_DEFAULT stream_selector_debug
34 static GstStaticPadTemplate gst_stream_selector_sink_factory =
35 GST_STATIC_PAD_TEMPLATE ("sink_%u",
40 static GstStaticPadTemplate gst_stream_selector_src_factory =
41 GST_STATIC_PAD_TEMPLATE ("src",
54 static gboolean gst_stream_selector_is_active_sinkpad (GstStreamSelector * sel,
56 static GstPad *gst_stream_selector_activate_sinkpad (GstStreamSelector * sel,
58 static GstPad *gst_stream_selector_get_linked_pad (GstPad * pad,
61 #define GST_TYPE_SELECTOR_PAD \
62 (gst_selector_pad_get_type())
63 #define GST_SELECTOR_PAD(obj) \
64 (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_SELECTOR_PAD, GstSelectorPad))
65 #define GST_SELECTOR_PAD_CLASS(klass) \
66 (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_SELECTOR_PAD, GstSelectorPadClass))
67 #define GST_IS_SELECTOR_PAD(obj) \
68 (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_SELECTOR_PAD))
69 #define GST_IS_SELECTOR_PAD_CLASS(klass) \
70 (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_SELECTOR_PAD))
71 #define GST_SELECTOR_PAD_CAST(obj) \
72 ((GstSelectorPad *)(obj))
74 typedef struct _GstSelectorPad GstSelectorPad;
75 typedef struct _GstSelectorPadClass GstSelectorPadClass;
77 struct _GstSelectorPad
83 gboolean segment_pending;
88 struct _GstSelectorPadClass
93 static void gst_selector_pad_finalize (GObject * object);
95 static void gst_selector_pad_get_property (GObject * object,
96 guint prop_id, GValue * value, GParamSpec * pspec);
98 static void gst_selector_pad_reset (GstSelectorPad * pad);
99 static gboolean gst_selector_pad_event (GstPad * pad, GstEvent * event);
100 static GstCaps *gst_selector_pad_getcaps (GstPad * pad, GstCaps * filter);
101 static GstFlowReturn gst_selector_pad_chain (GstPad * pad, GstBuffer * buf);
111 GType gst_selector_pad_get_type (void);
112 G_DEFINE_TYPE (GstSelectorPad, gst_selector_pad, GST_TYPE_PAD);
115 gst_selector_pad_class_init (GstSelectorPadClass * klass)
117 GObjectClass *gobject_class;
119 gobject_class = (GObjectClass *) klass;
121 gobject_class->finalize = gst_selector_pad_finalize;
122 gobject_class->get_property = gst_selector_pad_get_property;
124 g_object_class_install_property (gobject_class, PROP_PAD_TAGS,
125 g_param_spec_boxed ("tags", "Tags",
126 "The currently active tags on the pad", GST_TYPE_TAG_LIST,
127 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
129 g_object_class_install_property (gobject_class, PROP_PAD_ACTIVE,
130 g_param_spec_boolean ("active", "Active",
131 "If the pad is currently active", FALSE,
132 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
136 gst_selector_pad_init (GstSelectorPad * pad)
138 gst_selector_pad_reset (pad);
142 gst_selector_pad_finalize (GObject * object)
146 pad = GST_SELECTOR_PAD_CAST (object);
149 gst_tag_list_free (pad->tags);
151 G_OBJECT_CLASS (gst_selector_pad_parent_class)->finalize (object);
155 gst_selector_pad_get_property (GObject * object,
156 guint prop_id, GValue * value, GParamSpec * pspec)
160 pad = GST_SELECTOR_PAD (object);
164 GST_OBJECT_LOCK (object);
165 g_value_set_boxed (value, pad->tags);
166 GST_OBJECT_UNLOCK (object);
168 case PROP_PAD_ACTIVE:
170 GstStreamSelector *sel;
172 sel = GST_STREAM_SELECTOR (gst_pad_get_parent (pad));
173 g_value_set_boolean (value, gst_stream_selector_is_active_sinkpad (sel,
174 GST_PAD_CAST (pad)));
175 gst_object_unref (sel);
179 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
185 gst_selector_pad_reset (GstSelectorPad * pad)
189 gst_segment_init (&pad->segment, GST_FORMAT_UNDEFINED);
193 gst_selector_pad_event (GstPad * pad, GstEvent * event)
196 gboolean forward = TRUE;
197 GstStreamSelector *sel;
198 GstSelectorPad *selpad;
199 GstPad *active_sinkpad;
201 sel = GST_STREAM_SELECTOR (gst_pad_get_parent (pad));
202 selpad = GST_SELECTOR_PAD_CAST (pad);
204 /* only forward if we are dealing with the active sinkpad */
205 active_sinkpad = gst_stream_selector_activate_sinkpad (sel, pad);
206 forward = (active_sinkpad == pad);
208 switch (GST_EVENT_TYPE (event)) {
209 case GST_EVENT_FLUSH_STOP:
210 gst_selector_pad_reset (selpad);
212 case GST_EVENT_SEGMENT:
214 gst_event_copy_segment (event, &selpad->segment);
216 GST_DEBUG_OBJECT (selpad, "configured SEGMENT %" GST_SEGMENT_FORMAT,
218 /* if we are not going to forward the segment, mark the segment as
221 selpad->segment_pending = TRUE;
228 GST_OBJECT_LOCK (selpad);
230 gst_tag_list_free (selpad->tags);
231 gst_event_parse_tag (event, &tags);
233 tags = gst_tag_list_copy (tags);
235 GST_DEBUG_OBJECT (sel, "received tags %" GST_PTR_FORMAT, selpad->tags);
236 GST_OBJECT_UNLOCK (selpad);
246 res = gst_pad_push_event (sel->srcpad, event);
248 gst_event_unref (event);
250 gst_object_unref (sel);
256 gst_selector_pad_getcaps (GstPad * pad, GstCaps * filter)
258 GstStreamSelector *sel;
261 sel = GST_STREAM_SELECTOR (gst_pad_get_parent (pad));
263 GST_DEBUG_OBJECT (sel, "Getting caps of srcpad peer");
264 caps = gst_pad_peer_get_caps (sel->srcpad, filter);
266 caps = (filter ? gst_caps_ref (filter) : gst_caps_new_any ());
268 gst_object_unref (sel);
274 gst_selector_pad_chain (GstPad * pad, GstBuffer * buf)
276 GstStreamSelector *sel;
278 GstPad *active_sinkpad;
279 GstSelectorPad *selpad;
280 GstClockTime timestamp;
283 sel = GST_STREAM_SELECTOR (gst_pad_get_parent (pad));
284 selpad = GST_SELECTOR_PAD_CAST (pad);
285 seg = &selpad->segment;
287 active_sinkpad = gst_stream_selector_activate_sinkpad (sel, pad);
289 timestamp = GST_BUFFER_TIMESTAMP (buf);
290 if (GST_CLOCK_TIME_IS_VALID (timestamp)) {
291 GST_DEBUG_OBJECT (sel, "received timestamp %" GST_TIME_FORMAT,
292 GST_TIME_ARGS (timestamp));
293 seg->position = timestamp;
296 /* Ignore buffers from pads except the selected one */
297 if (pad != active_sinkpad)
300 /* if we have a pending segment, push it out now */
301 if (selpad->segment_pending) {
302 gst_pad_push_event (sel->srcpad, gst_event_new_segment (seg));
303 selpad->segment_pending = FALSE;
307 GST_DEBUG_OBJECT (sel, "Forwarding buffer %p from pad %s:%s", buf,
308 GST_DEBUG_PAD_NAME (pad));
309 res = gst_pad_push (sel->srcpad, buf);
311 gst_object_unref (sel);
313 /* dropped buffers */
316 GST_DEBUG_OBJECT (sel, "Ignoring buffer %p from pad %s:%s",
317 buf, GST_DEBUG_PAD_NAME (pad));
318 gst_buffer_unref (buf);
319 res = GST_FLOW_NOT_LINKED;
324 static void gst_stream_selector_dispose (GObject * object);
326 static void gst_stream_selector_set_property (GObject * object,
327 guint prop_id, const GValue * value, GParamSpec * pspec);
328 static void gst_stream_selector_get_property (GObject * object,
329 guint prop_id, GValue * value, GParamSpec * pspec);
331 static GstPad *gst_stream_selector_request_new_pad (GstElement * element,
332 GstPadTemplate * templ, const gchar * unused, const GstCaps * caps);
333 static void gst_stream_selector_release_pad (GstElement * element,
335 static GstIterator *gst_stream_selector_pad_iterate_linked_pads (GstPad * pad);
336 static GstCaps *gst_stream_selector_getcaps (GstPad * pad, GstCaps * filter);
338 #define gst_stream_selector_parent_class parent_class
339 G_DEFINE_TYPE (GstStreamSelector, gst_stream_selector, GST_TYPE_ELEMENT);
342 gst_stream_selector_class_init (GstStreamSelectorClass * klass)
344 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
345 GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
347 parent_class = g_type_class_peek_parent (klass);
349 gobject_class->dispose = gst_stream_selector_dispose;
351 gobject_class->set_property = gst_stream_selector_set_property;
352 gobject_class->get_property = gst_stream_selector_get_property;
354 g_object_class_install_property (gobject_class, PROP_N_PADS,
355 g_param_spec_uint ("n-pads", "Number of Pads",
356 "The number of sink pads", 0, G_MAXUINT, 0,
357 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
358 g_object_class_install_property (gobject_class, PROP_ACTIVE_PAD,
359 g_param_spec_object ("active-pad", "Active Pad",
360 "The currently active sink pad", GST_TYPE_PAD,
361 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
363 gstelement_class->request_new_pad = gst_stream_selector_request_new_pad;
364 gstelement_class->release_pad = gst_stream_selector_release_pad;
366 gst_element_class_set_details_simple (gstelement_class,
367 "StreamSelector", "Generic",
368 "N-to-1 input stream_selectoring",
369 "Julien Moutte <julien@moutte.net>, "
370 "Jan Schmidt <thaytan@mad.scientist.com>, "
371 "Wim Taymans <wim.taymans@gmail.com>");
373 gst_element_class_add_pad_template (gstelement_class,
374 gst_static_pad_template_get (&gst_stream_selector_sink_factory));
375 gst_element_class_add_pad_template (gstelement_class,
376 gst_static_pad_template_get (&gst_stream_selector_src_factory));
378 GST_DEBUG_CATEGORY_INIT (stream_selector_debug,
379 "streamselector", 0, "A stream-selector element");
383 gst_stream_selector_init (GstStreamSelector * sel)
385 sel->srcpad = gst_pad_new ("src", GST_PAD_SRC);
386 gst_pad_set_iterate_internal_links_function (sel->srcpad,
387 GST_DEBUG_FUNCPTR (gst_stream_selector_pad_iterate_linked_pads));
388 gst_pad_set_getcaps_function (sel->srcpad,
389 GST_DEBUG_FUNCPTR (gst_stream_selector_getcaps));
390 gst_element_add_pad (GST_ELEMENT (sel), sel->srcpad);
391 /* sinkpad management */
393 sel->active_sinkpad = NULL;
394 gst_segment_init (&sel->segment, GST_FORMAT_UNDEFINED);
398 gst_stream_selector_dispose (GObject * object)
400 GstStreamSelector *sel = GST_STREAM_SELECTOR (object);
402 if (sel->active_sinkpad) {
403 gst_object_unref (sel->active_sinkpad);
404 sel->active_sinkpad = NULL;
407 G_OBJECT_CLASS (parent_class)->dispose (object);
411 gst_stream_selector_set_property (GObject * object, guint prop_id,
412 const GValue * value, GParamSpec * pspec)
414 GstStreamSelector *sel = GST_STREAM_SELECTOR (object);
417 case PROP_ACTIVE_PAD:
420 GstPad **active_pad_p;
422 pad = g_value_get_object (value);
424 GST_OBJECT_LOCK (object);
425 if (pad != sel->active_sinkpad) {
426 GstSelectorPad *selpad;
428 selpad = GST_SELECTOR_PAD_CAST (pad);
429 /* we can only activate pads that have data received */
430 if (selpad && !selpad->active) {
431 GST_DEBUG_OBJECT (sel, "No data received on pad %" GST_PTR_FORMAT,
434 active_pad_p = &sel->active_sinkpad;
435 gst_object_replace ((GstObject **) active_pad_p,
436 GST_OBJECT_CAST (pad));
437 GST_DEBUG_OBJECT (sel, "New active pad is %" GST_PTR_FORMAT,
438 sel->active_sinkpad);
441 GST_OBJECT_UNLOCK (object);
445 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
451 gst_stream_selector_get_property (GObject * object, guint prop_id,
452 GValue * value, GParamSpec * pspec)
454 GstStreamSelector *sel = GST_STREAM_SELECTOR (object);
458 GST_OBJECT_LOCK (object);
459 g_value_set_uint (value, sel->n_pads);
460 GST_OBJECT_UNLOCK (object);
462 case PROP_ACTIVE_PAD:{
463 GST_OBJECT_LOCK (object);
464 g_value_set_object (value, sel->active_sinkpad);
465 GST_OBJECT_UNLOCK (object);
469 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
475 gst_stream_selector_get_linked_pad (GstPad * pad, gboolean strict)
477 GstStreamSelector *sel;
478 GstPad *otherpad = NULL;
480 sel = GST_STREAM_SELECTOR (gst_pad_get_parent (pad));
482 GST_OBJECT_LOCK (sel);
483 if (pad == sel->srcpad)
484 otherpad = sel->active_sinkpad;
485 else if (pad == sel->active_sinkpad || !strict)
486 otherpad = sel->srcpad;
488 gst_object_ref (otherpad);
489 GST_OBJECT_UNLOCK (sel);
490 gst_object_unref (sel);
495 gst_stream_selector_getcaps (GstPad * pad, GstCaps * filter)
501 otherpad = gst_stream_selector_get_linked_pad (pad, FALSE);
502 parent = gst_object_get_parent (GST_OBJECT (pad));
504 GST_DEBUG_OBJECT (parent,
505 "Pad %s:%s not linked, returning ANY", GST_DEBUG_PAD_NAME (pad));
506 caps = (filter ? gst_caps_ref (filter) : gst_caps_new_any ());
508 GST_DEBUG_OBJECT (parent,
509 "Pad %s:%s is linked (to %s:%s), returning peer caps",
510 GST_DEBUG_PAD_NAME (pad), GST_DEBUG_PAD_NAME (otherpad));
511 /* if the peer has caps, use those. If the pad is not linked, this function
512 * returns NULL and we return ANY */
513 if (!(caps = gst_pad_peer_get_caps (otherpad, filter)))
514 caps = (filter ? gst_caps_ref (filter) : gst_caps_new_any ());
515 gst_object_unref (otherpad);
518 gst_object_unref (parent);
522 /* check if the pad is the active sinkpad */
524 gst_stream_selector_is_active_sinkpad (GstStreamSelector * sel, GstPad * pad)
528 GST_OBJECT_LOCK (sel);
529 res = (pad == sel->active_sinkpad);
530 GST_OBJECT_UNLOCK (sel);
535 /* Get or create the active sinkpad */
537 gst_stream_selector_activate_sinkpad (GstStreamSelector * sel, GstPad * pad)
539 GstPad *active_sinkpad;
540 GstSelectorPad *selpad;
542 selpad = GST_SELECTOR_PAD_CAST (pad);
544 GST_OBJECT_LOCK (sel);
545 selpad->active = TRUE;
546 active_sinkpad = sel->active_sinkpad;
547 if (active_sinkpad == NULL) {
548 /* first pad we get an alloc on becomes the activated pad by default */
549 active_sinkpad = sel->active_sinkpad = gst_object_ref (pad);
550 GST_DEBUG_OBJECT (sel, "Activating pad %s:%s", GST_DEBUG_PAD_NAME (pad));
552 GST_OBJECT_UNLOCK (sel);
554 return active_sinkpad;
558 gst_stream_selector_pad_iterate_linked_pads (GstPad * pad)
560 GstStreamSelector *sel = GST_STREAM_SELECTOR (gst_pad_get_parent (pad));
561 GValue value = { 0, };
563 GstIterator *ret = NULL;
565 otherpad = gst_stream_selector_get_linked_pad (pad, TRUE);
567 g_value_init (&value, GST_TYPE_PAD);
568 g_value_set_object (&value, otherpad);
569 ret = gst_iterator_new_single (GST_TYPE_PAD, &value);
570 g_value_unset (&value);
571 gst_object_unref (otherpad);
573 gst_object_unref (sel);
579 gst_stream_selector_request_new_pad (GstElement * element,
580 GstPadTemplate * templ, const gchar * unused, const GstCaps * caps)
582 GstStreamSelector *sel;
584 GstPad *sinkpad = NULL;
586 sel = GST_STREAM_SELECTOR (element);
587 g_return_val_if_fail (templ->direction == GST_PAD_SINK, NULL);
588 GST_LOG_OBJECT (sel, "Creating new pad %d", sel->padcount);
589 GST_OBJECT_LOCK (sel);
590 name = g_strdup_printf ("sink_%u", sel->padcount++);
591 sinkpad = g_object_new (GST_TYPE_SELECTOR_PAD,
592 "name", name, "direction", templ->direction, "template", templ, NULL);
595 GST_OBJECT_UNLOCK (sel);
597 gst_pad_set_event_function (sinkpad,
598 GST_DEBUG_FUNCPTR (gst_selector_pad_event));
599 gst_pad_set_getcaps_function (sinkpad,
600 GST_DEBUG_FUNCPTR (gst_selector_pad_getcaps));
601 gst_pad_set_chain_function (sinkpad,
602 GST_DEBUG_FUNCPTR (gst_selector_pad_chain));
603 gst_pad_set_iterate_internal_links_function (sinkpad,
604 GST_DEBUG_FUNCPTR (gst_stream_selector_pad_iterate_linked_pads));
606 gst_pad_set_active (sinkpad, TRUE);
607 gst_element_add_pad (GST_ELEMENT (sel), sinkpad);
612 gst_stream_selector_release_pad (GstElement * element, GstPad * pad)
614 GstStreamSelector *sel;
616 sel = GST_STREAM_SELECTOR (element);
617 GST_LOG_OBJECT (sel, "Releasing pad %s:%s", GST_DEBUG_PAD_NAME (pad));
619 GST_OBJECT_LOCK (sel);
620 /* if the pad was the active pad, makes us select a new one */
621 if (sel->active_sinkpad == pad) {
622 GST_DEBUG_OBJECT (sel, "Deactivating pad %s:%s", GST_DEBUG_PAD_NAME (pad));
623 sel->active_sinkpad = NULL;
626 GST_OBJECT_UNLOCK (sel);
628 gst_pad_set_active (pad, FALSE);
629 gst_element_remove_pad (GST_ELEMENT (sel), pad);