output-selector: Send all events to active src pad and EOS to all src pads
[platform/upstream/gstreamer.git] / plugins / elements / gstoutputselector.c
1 /* GStreamer output selector
2  * Copyright (C) 2008 Nokia Corporation. (contact <stefan.kost@nokia.com>)
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18  */
19
20 /**
21  * SECTION:element-output-selector
22  * @see_also: #GstOutputSelector, #GstInputSelector
23  *
24  * Direct input stream to one out of N output pads.
25  */
26
27 #ifdef HAVE_CONFIG_H
28 #include "config.h"
29 #endif
30
31 #include <string.h>
32
33 #include "gstoutputselector.h"
34
35 GST_DEBUG_CATEGORY_STATIC (output_selector_debug);
36 #define GST_CAT_DEFAULT output_selector_debug
37
38 static GstStaticPadTemplate gst_output_selector_sink_factory =
39 GST_STATIC_PAD_TEMPLATE ("sink",
40     GST_PAD_SINK,
41     GST_PAD_ALWAYS,
42     GST_STATIC_CAPS_ANY);
43
44 static GstStaticPadTemplate gst_output_selector_src_factory =
45 GST_STATIC_PAD_TEMPLATE ("src_%u",
46     GST_PAD_SRC,
47     GST_PAD_REQUEST,
48     GST_STATIC_CAPS_ANY);
49
50 #define GST_TYPE_OUTPUT_SELECTOR_PAD_NEGOTIATION_MODE (gst_output_selector_pad_negotiation_mode_get_type())
51 static GType
52 gst_output_selector_pad_negotiation_mode_get_type (void)
53 {
54   static GType pad_negotiation_mode_type = 0;
55   static GEnumValue pad_negotiation_modes[] = {
56     {GST_OUTPUT_SELECTOR_PAD_NEGOTIATION_MODE_NONE, "None", "none"},
57     {GST_OUTPUT_SELECTOR_PAD_NEGOTIATION_MODE_ALL, "All", "all"},
58     {GST_OUTPUT_SELECTOR_PAD_NEGOTIATION_MODE_ACTIVE, "Active", "active"},
59     {0, NULL, NULL}
60   };
61
62   if (!pad_negotiation_mode_type) {
63     pad_negotiation_mode_type =
64         g_enum_register_static ("GstOutputSelectorPadNegotiationMode",
65         pad_negotiation_modes);
66   }
67   return pad_negotiation_mode_type;
68 }
69
70
71 enum
72 {
73   PROP_0,
74   PROP_ACTIVE_PAD,
75   PROP_RESEND_LATEST,
76   PROP_PAD_NEGOTIATION_MODE
77 };
78
79 #define DEFAULT_PAD_NEGOTIATION_MODE GST_OUTPUT_SELECTOR_PAD_NEGOTIATION_MODE_ALL
80
81 #define _do_init \
82 GST_DEBUG_CATEGORY_INIT (output_selector_debug, \
83         "output-selector", 0, "Output stream selector");
84 #define gst_output_selector_parent_class parent_class
85 G_DEFINE_TYPE_WITH_CODE (GstOutputSelector, gst_output_selector,
86     GST_TYPE_ELEMENT, _do_init);
87
88 static void gst_output_selector_dispose (GObject * object);
89 static void gst_output_selector_set_property (GObject * object,
90     guint prop_id, const GValue * value, GParamSpec * pspec);
91 static void gst_output_selector_get_property (GObject * object,
92     guint prop_id, GValue * value, GParamSpec * pspec);
93 static GstPad *gst_output_selector_request_new_pad (GstElement * element,
94     GstPadTemplate * templ, const gchar * unused, const GstCaps * caps);
95 static void gst_output_selector_release_pad (GstElement * element,
96     GstPad * pad);
97 static GstFlowReturn gst_output_selector_chain (GstPad * pad,
98     GstObject * parent, GstBuffer * buf);
99 static GstStateChangeReturn gst_output_selector_change_state (GstElement *
100     element, GstStateChange transition);
101 static gboolean gst_output_selector_event (GstPad * pad, GstObject * parent,
102     GstEvent * event);
103 static gboolean gst_output_selector_query (GstPad * pad, GstObject * parent,
104     GstQuery * query);
105 static void gst_output_selector_switch_pad_negotiation_mode (GstOutputSelector *
106     sel, gint mode);
107
108 static void
109 gst_output_selector_class_init (GstOutputSelectorClass * klass)
110 {
111   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
112   GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
113
114   gobject_class->dispose = gst_output_selector_dispose;
115
116   gobject_class->set_property = gst_output_selector_set_property;
117   gobject_class->get_property = gst_output_selector_get_property;
118
119   g_object_class_install_property (gobject_class, PROP_ACTIVE_PAD,
120       g_param_spec_object ("active-pad", "Active pad",
121           "Currently active src pad", GST_TYPE_PAD,
122           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
123   g_object_class_install_property (gobject_class, PROP_RESEND_LATEST,
124       g_param_spec_boolean ("resend-latest", "Resend latest buffer",
125           "Resend latest buffer after a switch to a new pad", FALSE,
126           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
127   g_object_class_install_property (gobject_class, PROP_PAD_NEGOTIATION_MODE,
128       g_param_spec_enum ("pad-negotiation-mode", "Pad negotiation mode",
129           "The mode to be used for pad negotiation",
130           GST_TYPE_OUTPUT_SELECTOR_PAD_NEGOTIATION_MODE,
131           DEFAULT_PAD_NEGOTIATION_MODE,
132           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
133
134   gst_element_class_set_static_metadata (gstelement_class, "Output selector",
135       "Generic", "1-to-N output stream selector",
136       "Stefan Kost <stefan.kost@nokia.com>");
137   gst_element_class_add_pad_template (gstelement_class,
138       gst_static_pad_template_get (&gst_output_selector_sink_factory));
139   gst_element_class_add_pad_template (gstelement_class,
140       gst_static_pad_template_get (&gst_output_selector_src_factory));
141
142   gstelement_class->request_new_pad =
143       GST_DEBUG_FUNCPTR (gst_output_selector_request_new_pad);
144   gstelement_class->release_pad =
145       GST_DEBUG_FUNCPTR (gst_output_selector_release_pad);
146
147   gstelement_class->change_state = gst_output_selector_change_state;
148 }
149
150 static void
151 gst_output_selector_init (GstOutputSelector * sel)
152 {
153   sel->sinkpad =
154       gst_pad_new_from_static_template (&gst_output_selector_sink_factory,
155       "sink");
156   gst_pad_set_chain_function (sel->sinkpad,
157       GST_DEBUG_FUNCPTR (gst_output_selector_chain));
158   gst_pad_set_event_function (sel->sinkpad,
159       GST_DEBUG_FUNCPTR (gst_output_selector_event));
160   gst_pad_set_query_function (sel->sinkpad,
161       GST_DEBUG_FUNCPTR (gst_output_selector_query));
162
163   gst_element_add_pad (GST_ELEMENT (sel), sel->sinkpad);
164
165   /* srcpad management */
166   sel->active_srcpad = NULL;
167   sel->nb_srcpads = 0;
168   gst_segment_init (&sel->segment, GST_FORMAT_UNDEFINED);
169   sel->pending_srcpad = NULL;
170
171   sel->resend_latest = FALSE;
172   sel->latest_buffer = NULL;
173   gst_output_selector_switch_pad_negotiation_mode (sel,
174       DEFAULT_PAD_NEGOTIATION_MODE);
175 }
176
177 static void
178 gst_output_selector_reset (GstOutputSelector * osel)
179 {
180   if (osel->pending_srcpad != NULL) {
181     gst_object_unref (osel->pending_srcpad);
182     osel->pending_srcpad = NULL;
183   }
184   if (osel->latest_buffer != NULL) {
185     gst_buffer_unref (osel->latest_buffer);
186     osel->latest_buffer = NULL;
187   }
188   gst_segment_init (&osel->segment, GST_FORMAT_UNDEFINED);
189 }
190
191 static void
192 gst_output_selector_dispose (GObject * object)
193 {
194   GstOutputSelector *osel = GST_OUTPUT_SELECTOR (object);
195
196   gst_output_selector_reset (osel);
197
198   G_OBJECT_CLASS (parent_class)->dispose (object);
199 }
200
201 static void
202 gst_output_selector_set_property (GObject * object, guint prop_id,
203     const GValue * value, GParamSpec * pspec)
204 {
205   GstOutputSelector *sel = GST_OUTPUT_SELECTOR (object);
206
207   switch (prop_id) {
208     case PROP_ACTIVE_PAD:
209     {
210       GstPad *next_pad;
211
212       next_pad = g_value_get_object (value);
213
214       GST_INFO_OBJECT (sel, "Activating pad %s:%s",
215           GST_DEBUG_PAD_NAME (next_pad));
216
217       GST_OBJECT_LOCK (object);
218       if (next_pad != sel->active_srcpad) {
219         /* switch to new srcpad in next chain run */
220         if (sel->pending_srcpad != NULL) {
221           GST_INFO ("replacing pending switch");
222           gst_object_unref (sel->pending_srcpad);
223         }
224         if (next_pad)
225           gst_object_ref (next_pad);
226         sel->pending_srcpad = next_pad;
227       } else {
228         GST_INFO ("pad already active");
229         if (sel->pending_srcpad != NULL) {
230           gst_object_unref (sel->pending_srcpad);
231           sel->pending_srcpad = NULL;
232         }
233       }
234       GST_OBJECT_UNLOCK (object);
235       break;
236     }
237     case PROP_RESEND_LATEST:{
238       sel->resend_latest = g_value_get_boolean (value);
239       break;
240     }
241     case PROP_PAD_NEGOTIATION_MODE:{
242       gst_output_selector_switch_pad_negotiation_mode (sel,
243           g_value_get_enum (value));
244       break;
245     }
246     default:
247       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
248       break;
249   }
250 }
251
252 static void
253 gst_output_selector_get_property (GObject * object, guint prop_id,
254     GValue * value, GParamSpec * pspec)
255 {
256   GstOutputSelector *sel = GST_OUTPUT_SELECTOR (object);
257
258   switch (prop_id) {
259     case PROP_ACTIVE_PAD:
260       GST_OBJECT_LOCK (object);
261       g_value_set_object (value,
262           sel->pending_srcpad ? sel->pending_srcpad : sel->active_srcpad);
263       GST_OBJECT_UNLOCK (object);
264       break;
265     case PROP_RESEND_LATEST:{
266       GST_OBJECT_LOCK (object);
267       g_value_set_boolean (value, sel->resend_latest);
268       GST_OBJECT_UNLOCK (object);
269       break;
270     }
271     case PROP_PAD_NEGOTIATION_MODE:
272       g_value_set_enum (value, sel->pad_negotiation_mode);
273       break;
274     default:
275       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
276       break;
277   }
278 }
279
280 static GstPad *
281 gst_output_selector_get_active (GstOutputSelector * sel)
282 {
283   GstPad *active = NULL;
284
285   GST_OBJECT_LOCK (sel);
286   if (sel->pending_srcpad)
287     active = gst_object_ref (sel->pending_srcpad);
288   else if (sel->active_srcpad)
289     active = gst_object_ref (sel->active_srcpad);
290   GST_OBJECT_UNLOCK (sel);
291
292   return active;
293 }
294
295 static void
296 gst_output_selector_switch_pad_negotiation_mode (GstOutputSelector * sel,
297     gint mode)
298 {
299   sel->pad_negotiation_mode = mode;
300 }
301
302 static gboolean
303 forward_sticky_events (GstPad * pad, GstEvent ** event, gpointer user_data)
304 {
305   GstPad *srcpad = GST_PAD_CAST (user_data);
306
307   gst_pad_push_event (srcpad, gst_event_ref (*event));
308
309   return TRUE;
310 }
311
312 static GstPad *
313 gst_output_selector_request_new_pad (GstElement * element,
314     GstPadTemplate * templ, const gchar * name, const GstCaps * caps)
315 {
316   gchar *padname;
317   GstPad *srcpad;
318   GstOutputSelector *osel;
319
320   osel = GST_OUTPUT_SELECTOR (element);
321
322   GST_DEBUG_OBJECT (osel, "requesting pad");
323
324   GST_OBJECT_LOCK (osel);
325   padname = g_strdup_printf ("src_%u", osel->nb_srcpads++);
326   srcpad = gst_pad_new_from_template (templ, padname);
327   GST_OBJECT_UNLOCK (osel);
328
329   gst_pad_set_active (srcpad, TRUE);
330
331   /* Forward sticky events to the new srcpad */
332   gst_pad_sticky_events_foreach (osel->sinkpad, forward_sticky_events, srcpad);
333
334   gst_element_add_pad (GST_ELEMENT (osel), srcpad);
335
336   /* Set the first requested src pad as active by default */
337   if (osel->active_srcpad == NULL) {
338     osel->active_srcpad = srcpad;
339   }
340   g_free (padname);
341
342   return srcpad;
343 }
344
345 static void
346 gst_output_selector_release_pad (GstElement * element, GstPad * pad)
347 {
348   GstOutputSelector *osel;
349
350   osel = GST_OUTPUT_SELECTOR (element);
351
352   GST_DEBUG_OBJECT (osel, "releasing pad");
353
354   gst_pad_set_active (pad, FALSE);
355
356   gst_element_remove_pad (GST_ELEMENT_CAST (osel), pad);
357 }
358
359 static gboolean
360 gst_output_selector_switch (GstOutputSelector * osel)
361 {
362   gboolean res = FALSE;
363   GstEvent *ev = NULL;
364   GstSegment *seg = NULL;
365
366   /* Switch */
367   GST_OBJECT_LOCK (GST_OBJECT (osel));
368   GST_INFO_OBJECT (osel, "switching to pad %" GST_PTR_FORMAT,
369       osel->pending_srcpad);
370   if (gst_pad_is_linked (osel->pending_srcpad)) {
371     osel->active_srcpad = osel->pending_srcpad;
372     res = TRUE;
373   }
374   gst_object_unref (osel->pending_srcpad);
375   osel->pending_srcpad = NULL;
376   GST_OBJECT_UNLOCK (GST_OBJECT (osel));
377
378   /* Send SEGMENT event and latest buffer if switching succeeded
379    * and we already have a valid segment configured */
380   if (res) {
381     gst_pad_sticky_events_foreach (osel->sinkpad, forward_sticky_events,
382         osel->active_srcpad);
383
384     /* update segment if required */
385     if (osel->segment.format != GST_FORMAT_UNDEFINED) {
386       /* Send SEGMENT to the pad we are going to switch to */
387       seg = &osel->segment;
388       /* If resending then mark segment start and position accordingly */
389       if (osel->resend_latest && osel->latest_buffer &&
390           GST_BUFFER_TIMESTAMP_IS_VALID (osel->latest_buffer)) {
391         seg->position = GST_BUFFER_TIMESTAMP (osel->latest_buffer);
392       }
393
394       ev = gst_event_new_segment (seg);
395
396       if (!gst_pad_push_event (osel->active_srcpad, ev)) {
397         GST_WARNING_OBJECT (osel,
398             "newsegment handling failed in %" GST_PTR_FORMAT,
399             osel->active_srcpad);
400       }
401     }
402
403     /* Resend latest buffer to newly switched pad */
404     if (osel->resend_latest && osel->latest_buffer) {
405       GST_INFO ("resending latest buffer");
406       gst_pad_push (osel->active_srcpad, gst_buffer_ref (osel->latest_buffer));
407     }
408   } else {
409     GST_WARNING_OBJECT (osel, "switch failed, pad not linked");
410   }
411
412   return res;
413 }
414
415 static GstFlowReturn
416 gst_output_selector_chain (GstPad * pad, GstObject * parent, GstBuffer * buf)
417 {
418   GstFlowReturn res;
419   GstOutputSelector *osel;
420   GstClockTime position, duration;
421
422   osel = GST_OUTPUT_SELECTOR (parent);
423
424   /*
425    * The _switch function might push a buffer if 'resend-latest' is true.
426    *
427    * Elements/Applications (e.g. camerabin) might use pad probes to
428    * switch output-selector's active pad. If we simply switch and don't
429    * recheck any pending pad switch the following codepath could end
430    * up pushing a buffer on a non-active pad. This is bad.
431    *
432    * So we always should check the pending_srcpad before going further down
433    * the chain and pushing the new buffer
434    */
435   while (osel->pending_srcpad) {
436     /* Do the switch */
437     gst_output_selector_switch (osel);
438   }
439
440   if (osel->latest_buffer) {
441     gst_buffer_unref (osel->latest_buffer);
442     osel->latest_buffer = NULL;
443   }
444
445   if (osel->resend_latest) {
446     /* Keep reference to latest buffer to resend it after switch */
447     osel->latest_buffer = gst_buffer_ref (buf);
448   }
449
450   /* Keep track of last stop and use it in SEGMENT start after
451      switching to a new src pad */
452   position = GST_BUFFER_TIMESTAMP (buf);
453   if (GST_CLOCK_TIME_IS_VALID (position)) {
454     duration = GST_BUFFER_DURATION (buf);
455     if (GST_CLOCK_TIME_IS_VALID (duration)) {
456       position += duration;
457     }
458     GST_LOG_OBJECT (osel, "setting last stop %" GST_TIME_FORMAT,
459         GST_TIME_ARGS (position));
460     osel->segment.position = position;
461   }
462
463   GST_LOG_OBJECT (osel, "pushing buffer to %" GST_PTR_FORMAT,
464       osel->active_srcpad);
465   res = gst_pad_push (osel->active_srcpad, buf);
466
467   return res;
468 }
469
470 static GstStateChangeReturn
471 gst_output_selector_change_state (GstElement * element,
472     GstStateChange transition)
473 {
474   GstOutputSelector *sel;
475   GstStateChangeReturn result;
476
477   sel = GST_OUTPUT_SELECTOR (element);
478
479   switch (transition) {
480     case GST_STATE_CHANGE_READY_TO_PAUSED:
481       break;
482     default:
483       break;
484   }
485
486   result = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
487
488   switch (transition) {
489     case GST_STATE_CHANGE_PAUSED_TO_READY:
490       gst_output_selector_reset (sel);
491       break;
492     default:
493       break;
494   }
495
496   return result;
497 }
498
499 static gboolean
500 gst_output_selector_forward_event (GstOutputSelector * sel, GstEvent * event)
501 {
502   gboolean res = TRUE;
503   GstPad *active;
504
505   switch (sel->pad_negotiation_mode) {
506     case GST_OUTPUT_SELECTOR_PAD_NEGOTIATION_MODE_ALL:
507       /* Send to all src pads */
508       res = gst_pad_event_default (sel->sinkpad, GST_OBJECT_CAST (sel), event);
509       break;
510     case GST_OUTPUT_SELECTOR_PAD_NEGOTIATION_MODE_NONE:
511       gst_event_unref (event);
512       break;
513     default:
514       active = gst_output_selector_get_active (sel);
515       if (active) {
516         res = gst_pad_push_event (active, event);
517         gst_object_unref (active);
518       } else {
519         gst_event_unref (event);
520       }
521       break;
522   }
523
524   return res;
525 }
526
527 static gboolean
528 gst_output_selector_event (GstPad * pad, GstObject * parent, GstEvent * event)
529 {
530   gboolean res = TRUE;
531   GstOutputSelector *sel;
532   GstPad *active = NULL;
533
534   sel = GST_OUTPUT_SELECTOR (parent);
535
536   switch (GST_EVENT_TYPE (event)) {
537     case GST_EVENT_EOS:
538     {
539       res = gst_output_selector_forward_event (sel, event);
540       break;
541     }
542     case GST_EVENT_SEGMENT:
543     {
544       gst_event_copy_segment (event, &sel->segment);
545       GST_DEBUG_OBJECT (sel, "configured SEGMENT %" GST_SEGMENT_FORMAT,
546           &sel->segment);
547       /* fall through */
548     }
549     default:
550     {
551       active = gst_output_selector_get_active (sel);
552       if (active) {
553         res = gst_pad_push_event (active, event);
554         gst_object_unref (active);
555       } else {
556         gst_event_unref (event);
557       }
558       break;
559     }
560   }
561
562   return res;
563 }
564
565 static gboolean
566 gst_output_selector_query (GstPad * pad, GstObject * parent, GstQuery * query)
567 {
568   gboolean res = TRUE;
569   GstOutputSelector *sel;
570   GstPad *active = NULL;
571
572   sel = GST_OUTPUT_SELECTOR (parent);
573
574   switch (GST_QUERY_TYPE (query)) {
575     case GST_QUERY_CAPS:
576     {
577       switch (sel->pad_negotiation_mode) {
578         case GST_OUTPUT_SELECTOR_PAD_NEGOTIATION_MODE_ALL:
579           /* Send caps to all src pads */
580           res = gst_pad_proxy_query_caps (pad, query);
581           break;
582         case GST_OUTPUT_SELECTOR_PAD_NEGOTIATION_MODE_NONE:
583           res = FALSE;
584           break;
585         default:
586           active = gst_output_selector_get_active (sel);
587           if (active) {
588             res = gst_pad_peer_query (active, query);
589             gst_object_unref (active);
590           } else {
591             res = FALSE;
592           }
593           break;
594       }
595       break;
596     }
597     default:
598       res = gst_pad_query_default (pad, parent, query);
599       break;
600   }
601   return res;
602 }