outputselector: check for pending srcpad in _get_property()
[platform/upstream/gstreamer.git] / plugins / elements / gstoutputselector.c
1 /* GStreamer
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., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, 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 const GstElementDetails gst_output_selector_details =
39 GST_ELEMENT_DETAILS ("Output selector",
40     "Generic",
41     "1-to-N output stream selectoring",
42     "Stefan Kost <stefan.kost@nokia.com>");
43
44 static GstStaticPadTemplate gst_output_selector_sink_factory =
45 GST_STATIC_PAD_TEMPLATE ("sink",
46     GST_PAD_SINK,
47     GST_PAD_ALWAYS,
48     GST_STATIC_CAPS_ANY);
49
50 static GstStaticPadTemplate gst_output_selector_src_factory =
51 GST_STATIC_PAD_TEMPLATE ("src%d",
52     GST_PAD_SRC,
53     GST_PAD_REQUEST,
54     GST_STATIC_CAPS_ANY);
55
56 enum
57 {
58   PROP_0,
59   PROP_ACTIVE_PAD,
60   PROP_RESEND_LATEST,
61   PROP_LAST
62 };
63
64 GST_BOILERPLATE (GstOutputSelector, gst_output_selector, GstElement,
65     GST_TYPE_ELEMENT);
66
67 static void gst_output_selector_dispose (GObject * object);
68 static void gst_output_selector_set_property (GObject * object,
69     guint prop_id, const GValue * value, GParamSpec * pspec);
70 static void gst_output_selector_get_property (GObject * object,
71     guint prop_id, GValue * value, GParamSpec * pspec);
72 static GstPad *gst_output_selector_request_new_pad (GstElement * element,
73     GstPadTemplate * templ, const gchar * unused);
74 static void gst_output_selector_release_pad (GstElement * element,
75     GstPad * pad);
76 static GstFlowReturn gst_output_selector_chain (GstPad * pad, GstBuffer * buf);
77 static GstFlowReturn gst_output_selector_buffer_alloc (GstPad * pad,
78     guint64 offset, guint size, GstCaps * caps, GstBuffer ** buf);
79 static GstStateChangeReturn gst_output_selector_change_state (GstElement *
80     element, GstStateChange transition);
81 static gboolean gst_output_selector_handle_sink_event (GstPad * pad,
82     GstEvent * event);
83
84 static void
85 gst_output_selector_base_init (gpointer g_class)
86 {
87   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
88
89   gst_element_class_set_details (element_class, &gst_output_selector_details);
90   gst_element_class_add_pad_template (element_class,
91       gst_static_pad_template_get (&gst_output_selector_sink_factory));
92   gst_element_class_add_pad_template (element_class,
93       gst_static_pad_template_get (&gst_output_selector_src_factory));
94 }
95
96 static void
97 gst_output_selector_class_init (GstOutputSelectorClass * klass)
98 {
99   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
100   GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
101
102   parent_class = g_type_class_peek_parent (klass);
103
104   gobject_class->dispose = gst_output_selector_dispose;
105
106   gobject_class->set_property =
107       GST_DEBUG_FUNCPTR (gst_output_selector_set_property);
108   gobject_class->get_property =
109       GST_DEBUG_FUNCPTR (gst_output_selector_get_property);
110
111   g_object_class_install_property (gobject_class, PROP_ACTIVE_PAD,
112       g_param_spec_object ("active-pad", "Active pad",
113           "Currently active src pad", GST_TYPE_PAD, G_PARAM_READWRITE));
114   g_object_class_install_property (gobject_class, PROP_RESEND_LATEST,
115       g_param_spec_boolean ("resend-latest", "Resend latest buffer",
116           "Resend latest buffer after a switch to a new pad", FALSE,
117           G_PARAM_READWRITE));
118
119   gstelement_class->request_new_pad =
120       GST_DEBUG_FUNCPTR (gst_output_selector_request_new_pad);
121   gstelement_class->release_pad =
122       GST_DEBUG_FUNCPTR (gst_output_selector_release_pad);
123
124   gstelement_class->change_state = gst_output_selector_change_state;
125
126   GST_DEBUG_CATEGORY_INIT (output_selector_debug,
127       "output-selector", 0, "An output stream selector element");
128 }
129
130 static void
131 gst_output_selector_init (GstOutputSelector * sel,
132     GstOutputSelectorClass * g_class)
133 {
134   sel->sinkpad =
135       gst_pad_new_from_static_template (&gst_output_selector_sink_factory,
136       "sink");
137   gst_pad_set_chain_function (sel->sinkpad,
138       GST_DEBUG_FUNCPTR (gst_output_selector_chain));
139   gst_pad_set_event_function (sel->sinkpad,
140       GST_DEBUG_FUNCPTR (gst_output_selector_handle_sink_event));
141   gst_pad_set_bufferalloc_function (sel->sinkpad,
142       GST_DEBUG_FUNCPTR (gst_output_selector_buffer_alloc));
143   /*
144      gst_pad_set_setcaps_function (sel->sinkpad,
145      GST_DEBUG_FUNCPTR (gst_pad_proxy_setcaps));
146      gst_pad_set_getcaps_function (sel->sinkpad,
147      GST_DEBUG_FUNCPTR (gst_pad_proxy_getcaps));
148    */
149
150   gst_element_add_pad (GST_ELEMENT (sel), sel->sinkpad);
151
152   /* srcpad management */
153   sel->active_srcpad = NULL;
154   sel->nb_srcpads = 0;
155   gst_segment_init (&sel->segment, GST_FORMAT_UNDEFINED);
156   sel->pending_srcpad = NULL;
157
158   sel->resend_latest = FALSE;
159   sel->latest_buffer = NULL;
160 }
161
162 static void
163 gst_output_selector_reset (GstOutputSelector * osel)
164 {
165   if (osel->pending_srcpad != NULL) {
166     gst_object_unref (osel->pending_srcpad);
167     osel->pending_srcpad = NULL;
168   }
169   if (osel->latest_buffer != NULL) {
170     gst_buffer_unref (osel->latest_buffer);
171     osel->latest_buffer = NULL;
172   }
173   gst_segment_init (&osel->segment, GST_FORMAT_UNDEFINED);
174 }
175
176 static void
177 gst_output_selector_dispose (GObject * object)
178 {
179   GstOutputSelector *osel = GST_OUTPUT_SELECTOR (object);
180
181   gst_output_selector_reset (osel);
182
183   G_OBJECT_CLASS (parent_class)->dispose (object);
184 }
185
186 static void
187 gst_output_selector_set_property (GObject * object, guint prop_id,
188     const GValue * value, GParamSpec * pspec)
189 {
190   GstOutputSelector *sel = GST_OUTPUT_SELECTOR (object);
191
192   switch (prop_id) {
193     case PROP_ACTIVE_PAD:
194     {
195       GstPad *next_pad;
196
197       next_pad = g_value_get_object (value);
198
199       GST_INFO_OBJECT (sel, "Activating pad %s:%s",
200           GST_DEBUG_PAD_NAME (next_pad));
201
202       GST_OBJECT_LOCK (object);
203       if (next_pad != sel->active_srcpad) {
204         /* switch to new srcpad in next chain run */
205         if (sel->pending_srcpad != NULL) {
206           GST_INFO ("replacing pending switch");
207           gst_object_unref (sel->pending_srcpad);
208         }
209         if (next_pad)
210           gst_object_ref (next_pad);
211         sel->pending_srcpad = next_pad;
212       } else {
213         GST_INFO ("pad already active");
214         if (sel->pending_srcpad != NULL) {
215           gst_object_unref (sel->pending_srcpad);
216           sel->pending_srcpad = NULL;
217         }
218       }
219       GST_OBJECT_UNLOCK (object);
220       break;
221     }
222     case PROP_RESEND_LATEST:{
223       sel->resend_latest = g_value_get_boolean (value);
224       break;
225     }
226     default:
227       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
228       break;
229   }
230 }
231
232 static void
233 gst_output_selector_get_property (GObject * object, guint prop_id,
234     GValue * value, GParamSpec * pspec)
235 {
236   GstOutputSelector *sel = GST_OUTPUT_SELECTOR (object);
237
238   switch (prop_id) {
239     case PROP_ACTIVE_PAD:
240       GST_OBJECT_LOCK (object);
241       g_value_set_object (value,
242           sel->pending_srcpad ? sel->pending_srcpad : sel->active_srcpad);
243       GST_OBJECT_UNLOCK (object);
244       break;
245     case PROP_RESEND_LATEST:{
246       GST_OBJECT_LOCK (object);
247       g_value_set_boolean (value, sel->resend_latest);
248       GST_OBJECT_UNLOCK (object);
249       break;
250     }
251     default:
252       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
253       break;
254   }
255 }
256
257 static GstFlowReturn
258 gst_output_selector_buffer_alloc (GstPad * pad, guint64 offset, guint size,
259     GstCaps * caps, GstBuffer ** buf)
260 {
261   GstOutputSelector *sel;
262   GstFlowReturn res;
263   GstPad *allocpad;
264
265   sel = GST_OUTPUT_SELECTOR (GST_PAD_PARENT (pad));
266   res = GST_FLOW_NOT_LINKED;
267
268   GST_OBJECT_LOCK (sel);
269   allocpad = sel->pending_srcpad ? sel->pending_srcpad : sel->active_srcpad;
270   if (allocpad) {
271     /* if we had a previous pad we used for allocating a buffer, continue using
272      * it. */
273     GST_DEBUG_OBJECT (sel, "using pad %s:%s for alloc",
274         GST_DEBUG_PAD_NAME (allocpad));
275     gst_object_ref (allocpad);
276     GST_OBJECT_UNLOCK (sel);
277
278     res = gst_pad_alloc_buffer (allocpad, offset, size, caps, buf);
279     gst_object_unref (allocpad);
280
281     GST_OBJECT_LOCK (sel);
282   } else {
283     /* fallback case, allocate a buffer of our own, add pad caps. */
284     GST_DEBUG_OBJECT (pad, "fallback buffer alloc");
285     *buf = NULL;
286     res = GST_FLOW_OK;
287   }
288   GST_OBJECT_UNLOCK (sel);
289
290   GST_DEBUG_OBJECT (sel, "buffer alloc finished: %s", gst_flow_get_name (res));
291
292   return res;
293 }
294
295 static GstPad *
296 gst_output_selector_request_new_pad (GstElement * element,
297     GstPadTemplate * templ, const gchar * name)
298 {
299   gchar *padname;
300   GstPad *srcpad;
301   GstOutputSelector *osel;
302
303   osel = GST_OUTPUT_SELECTOR (element);
304
305   GST_DEBUG_OBJECT (osel, "requesting pad");
306
307   GST_OBJECT_LOCK (osel);
308   padname = g_strdup_printf ("src%d", osel->nb_srcpads++);
309   srcpad = gst_pad_new_from_template (templ, padname);
310   GST_OBJECT_UNLOCK (osel);
311
312   gst_pad_set_active (srcpad, TRUE);
313   gst_element_add_pad (GST_ELEMENT (osel), srcpad);
314
315   /* Set the first requested src pad as active by default */
316   if (osel->active_srcpad == NULL) {
317     osel->active_srcpad = srcpad;
318   }
319   g_free (padname);
320
321   return srcpad;
322 }
323
324 static void
325 gst_output_selector_release_pad (GstElement * element, GstPad * pad)
326 {
327   GstOutputSelector *osel;
328
329   osel = GST_OUTPUT_SELECTOR (element);
330
331   GST_DEBUG_OBJECT (osel, "releasing pad");
332
333   gst_pad_set_active (pad, FALSE);
334
335   gst_element_remove_pad (GST_ELEMENT_CAST (osel), pad);
336 }
337
338 static gboolean
339 gst_output_selector_switch (GstOutputSelector * osel)
340 {
341   gboolean res = FALSE;
342   GstEvent *ev = NULL;
343   GstSegment *seg = NULL;
344   gint64 start = 0, position = 0;
345
346   /* Switch */
347   GST_OBJECT_LOCK (GST_OBJECT (osel));
348   GST_INFO ("switching to pad %" GST_PTR_FORMAT, osel->pending_srcpad);
349   if (gst_pad_is_linked (osel->pending_srcpad)) {
350     osel->active_srcpad = osel->pending_srcpad;
351     res = TRUE;
352   }
353   gst_object_unref (osel->pending_srcpad);
354   osel->pending_srcpad = NULL;
355   GST_OBJECT_UNLOCK (GST_OBJECT (osel));
356
357   /* Send NEWSEGMENT event and latest buffer if switching succeeded */
358   if (res) {
359     /* Send NEWSEGMENT to the pad we are going to switch to */
360     seg = &osel->segment;
361     /* If resending then mark newsegment start and position accordingly */
362     if (osel->resend_latest && osel->latest_buffer &&
363         GST_BUFFER_TIMESTAMP_IS_VALID (osel->latest_buffer)) {
364       start = position = GST_BUFFER_TIMESTAMP (osel->latest_buffer);
365     } else {
366       start = position = seg->last_stop;
367     }
368     ev = gst_event_new_new_segment (TRUE, seg->rate,
369         seg->format, start, seg->stop, position);
370     if (!gst_pad_push_event (osel->active_srcpad, ev)) {
371       GST_WARNING_OBJECT (osel,
372           "newsegment handling failed in %" GST_PTR_FORMAT,
373           osel->active_srcpad);
374     }
375
376     /* Resend latest buffer to newly switched pad */
377     if (osel->resend_latest && osel->latest_buffer) {
378       GST_INFO ("resending latest buffer");
379       gst_pad_push (osel->active_srcpad, osel->latest_buffer);
380       osel->latest_buffer = NULL;
381     }
382   } else {
383     GST_WARNING_OBJECT (osel, "switch failed, pad not linked");
384   }
385
386   return res;
387 }
388
389 static GstFlowReturn
390 gst_output_selector_chain (GstPad * pad, GstBuffer * buf)
391 {
392   GstFlowReturn res;
393   GstOutputSelector *osel;
394   GstClockTime last_stop, duration;
395
396   osel = GST_OUTPUT_SELECTOR (gst_pad_get_parent (pad));
397
398   if (osel->pending_srcpad) {
399     /* Do the switch */
400     gst_output_selector_switch (osel);
401   }
402
403   if (osel->latest_buffer) {
404     gst_buffer_unref (osel->latest_buffer);
405     osel->latest_buffer = NULL;
406   }
407
408   if (osel->resend_latest) {
409     /* Keep reference to latest buffer to resend it after switch */
410     osel->latest_buffer = gst_buffer_ref (buf);
411   }
412
413   /* Keep track of last stop and use it in NEWSEGMENT start after 
414      switching to a new src pad */
415   last_stop = GST_BUFFER_TIMESTAMP (buf);
416   if (GST_CLOCK_TIME_IS_VALID (last_stop)) {
417     duration = GST_BUFFER_DURATION (buf);
418     if (GST_CLOCK_TIME_IS_VALID (duration)) {
419       last_stop += duration;
420     }
421     GST_LOG_OBJECT (osel, "setting last stop %" GST_TIME_FORMAT,
422         GST_TIME_ARGS (last_stop));
423     gst_segment_set_last_stop (&osel->segment, osel->segment.format, last_stop);
424   }
425
426   GST_LOG_OBJECT (osel, "pushing buffer to %" GST_PTR_FORMAT,
427       osel->active_srcpad);
428   res = gst_pad_push (osel->active_srcpad, buf);
429   gst_object_unref (osel);
430
431   return res;
432 }
433
434 static GstStateChangeReturn
435 gst_output_selector_change_state (GstElement * element,
436     GstStateChange transition)
437 {
438   GstOutputSelector *sel;
439   GstStateChangeReturn result;
440
441   sel = GST_OUTPUT_SELECTOR (element);
442
443   switch (transition) {
444     case GST_STATE_CHANGE_READY_TO_PAUSED:
445       break;
446     default:
447       break;
448   }
449
450   result = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
451
452   switch (transition) {
453     case GST_STATE_CHANGE_PAUSED_TO_READY:
454       gst_output_selector_reset (sel);
455       break;
456     default:
457       break;
458   }
459
460   return result;
461 }
462
463 static gboolean
464 gst_output_selector_handle_sink_event (GstPad * pad, GstEvent * event)
465 {
466   gboolean res = TRUE;
467   GstOutputSelector *sel;
468   GstPad *output_pad = NULL;
469
470   sel = GST_OUTPUT_SELECTOR (gst_pad_get_parent (pad));
471
472   switch (GST_EVENT_TYPE (event)) {
473     case GST_EVENT_NEWSEGMENT:
474     {
475       gboolean update;
476       GstFormat format;
477       gdouble rate, arate;
478       gint64 start, stop, time;
479
480       gst_event_parse_new_segment_full (event, &update, &rate, &arate, &format,
481           &start, &stop, &time);
482
483       GST_DEBUG_OBJECT (sel,
484           "configured NEWSEGMENT update %d, rate %lf, applied rate %lf, "
485           "format %d, " "%" G_GINT64_FORMAT " -- %" G_GINT64_FORMAT ", time %"
486           G_GINT64_FORMAT, update, rate, arate, format, start, stop, time);
487
488       gst_segment_set_newsegment_full (&sel->segment, update,
489           rate, arate, format, start, stop, time);
490
491       /* Send newsegment to all src pads */
492       gst_pad_event_default (pad, event);
493       break;
494     }
495     case GST_EVENT_EOS:
496       /* Send eos to all src pads */
497       gst_pad_event_default (pad, event);
498       break;
499     default:
500       /* Send other events to pending or active src pad */
501       output_pad =
502           sel->pending_srcpad ? sel->pending_srcpad : sel->active_srcpad;
503       res = gst_pad_push_event (output_pad, event);
504       break;
505   }
506
507   gst_object_unref (sel);
508
509   return res;
510 }