gst/multifile/gstmultifilesink.c: Add a fixme comment.
[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  * @short_description: 1-to-N stream selectoring
23  * @see_also: #GstTee, #GstInputSelector
24  *
25  * Direct input stream to one out of N output pads.
26  */
27
28 #ifdef HAVE_CONFIG_H
29 #include "config.h"
30 #endif
31
32 #include <string.h>
33
34 #include "gstoutputselector.h"
35
36 GST_DEBUG_CATEGORY_STATIC (output_selector_debug);
37 #define GST_CAT_DEFAULT output_selector_debug
38
39 static const GstElementDetails gst_output_selector_details =
40 GST_ELEMENT_DETAILS ("Output selector",
41     "Generic",
42     "1-to-N output stream selectoring",
43     "Stefan Kost <stefan.kost@nokia.com>");
44
45 static GstStaticPadTemplate gst_output_selector_sink_factory =
46 GST_STATIC_PAD_TEMPLATE ("sink",
47     GST_PAD_SINK,
48     GST_PAD_ALWAYS,
49     GST_STATIC_CAPS_ANY);
50
51 static GstStaticPadTemplate gst_output_selector_src_factory =
52 GST_STATIC_PAD_TEMPLATE ("src%d",
53     GST_PAD_SRC,
54     GST_PAD_REQUEST,
55     GST_STATIC_CAPS_ANY);
56
57 enum
58 {
59   PROP_ACTIVE_PAD = 1,
60   PROP_RESEND_LATEST
61 };
62
63 static void gst_output_selector_dispose (GObject * object);
64 static void gst_output_selector_init (GstOutputSelector * sel);
65 static void gst_output_selector_base_init (GstOutputSelectorClass * klass);
66 static void gst_output_selector_class_init (GstOutputSelectorClass * klass);
67 static void gst_output_selector_set_property (GObject * object,
68     guint prop_id, const GValue * value, GParamSpec * pspec);
69 static void gst_output_selector_get_property (GObject * object,
70     guint prop_id, GValue * value, GParamSpec * pspec);
71 static GstPad *gst_output_selector_request_new_pad (GstElement * element,
72     GstPadTemplate * templ, const gchar * unused);
73 static void gst_output_selector_release_pad (GstElement * element,
74     GstPad * pad);
75 static GstFlowReturn gst_output_selector_chain (GstPad * pad, GstBuffer * buf);
76 static GstStateChangeReturn gst_output_selector_change_state (GstElement *
77     element, GstStateChange transition);
78 static gboolean gst_output_selector_handle_sink_event (GstPad * pad,
79     GstEvent * event);
80
81 static GstElementClass *parent_class = NULL;
82
83 GType
84 gst_output_selector_get_type (void)
85 {
86   static GType output_selector_type = 0;
87
88   if (!output_selector_type) {
89     static const GTypeInfo output_selector_info = {
90       sizeof (GstOutputSelectorClass),
91       (GBaseInitFunc) gst_output_selector_base_init,
92       NULL,
93       (GClassInitFunc) gst_output_selector_class_init,
94       NULL,
95       NULL,
96       sizeof (GstOutputSelector),
97       0,
98       (GInstanceInitFunc) gst_output_selector_init,
99     };
100     output_selector_type =
101         g_type_register_static (GST_TYPE_ELEMENT,
102         "GstOutputSelector", &output_selector_info, 0);
103     GST_DEBUG_CATEGORY_INIT (output_selector_debug,
104         "output-selector", 0, "An output stream selector element");
105   }
106
107   return output_selector_type;
108 }
109
110 static void
111 gst_output_selector_base_init (GstOutputSelectorClass * klass)
112 {
113   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
114
115   gst_element_class_set_details (element_class, &gst_output_selector_details);
116   gst_element_class_add_pad_template (element_class,
117       gst_static_pad_template_get (&gst_output_selector_sink_factory));
118   gst_element_class_add_pad_template (element_class,
119       gst_static_pad_template_get (&gst_output_selector_src_factory));
120 }
121
122 static void
123 gst_output_selector_class_init (GstOutputSelectorClass * klass)
124 {
125   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
126   GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
127
128   parent_class = g_type_class_peek_parent (klass);
129   gobject_class->set_property =
130       GST_DEBUG_FUNCPTR (gst_output_selector_set_property);
131   gobject_class->get_property =
132       GST_DEBUG_FUNCPTR (gst_output_selector_get_property);
133   g_object_class_install_property (gobject_class, PROP_ACTIVE_PAD,
134       g_param_spec_string ("active-pad", "Active pad",
135           "Name of the currently active src pad", NULL, G_PARAM_READWRITE));
136   g_object_class_install_property (gobject_class, PROP_RESEND_LATEST,
137       g_param_spec_boolean ("resend-latest", "Resend latest buffer",
138           "Resend latest buffer after a switch to a new pad", FALSE,
139           G_PARAM_READWRITE));
140   gobject_class->dispose = gst_output_selector_dispose;
141   gstelement_class->request_new_pad =
142       GST_DEBUG_FUNCPTR (gst_output_selector_request_new_pad);
143   gstelement_class->release_pad =
144       GST_DEBUG_FUNCPTR (gst_output_selector_release_pad);
145   gstelement_class->change_state = gst_output_selector_change_state;
146
147 }
148
149 static void
150 gst_output_selector_init (GstOutputSelector * sel)
151 {
152   sel->sinkpad =
153       gst_pad_new_from_static_template (&gst_output_selector_sink_factory,
154       "sink");
155   gst_pad_set_chain_function (sel->sinkpad,
156       GST_DEBUG_FUNCPTR (gst_output_selector_chain));
157   gst_pad_set_event_function (sel->sinkpad,
158       GST_DEBUG_FUNCPTR (gst_output_selector_handle_sink_event));
159
160   gst_element_add_pad (GST_ELEMENT (sel), sel->sinkpad);
161
162   /*
163      gst_pad_set_bufferalloc_function (sel->sinkpad,
164      GST_DEBUG_FUNCPTR (gst_output_selector_bufferalloc));
165      gst_pad_set_setcaps_function (sel->sinkpad,
166      GST_DEBUG_FUNCPTR (gst_pad_proxy_setcaps));
167      gst_pad_set_getcaps_function (sel->sinkpad,
168      GST_DEBUG_FUNCPTR (gst_pad_proxy_getcaps));
169    */
170   /* srcpad management */
171   sel->active_srcpad = NULL;
172   sel->nb_srcpads = 0;
173   gst_segment_init (&sel->segment, GST_FORMAT_UNDEFINED);
174   sel->pending_srcpad = NULL;
175
176   sel->resend_latest = FALSE;
177   sel->latest_buffer = NULL;
178 }
179
180 static void
181 gst_output_selector_dispose (GObject * object)
182 {
183   GstOutputSelector *osel = GST_OUTPUT_SELECTOR (object);
184
185   if (osel->pending_srcpad != NULL) {
186     gst_object_unref (osel->pending_srcpad);
187     osel->pending_srcpad = NULL;
188   }
189   if (osel->latest_buffer != NULL) {
190     gst_buffer_unref (osel->latest_buffer);
191     osel->latest_buffer = NULL;
192   }
193
194   G_OBJECT_CLASS (parent_class)->dispose (object);
195 }
196
197 static void
198 gst_output_selector_set_property (GObject * object, guint prop_id,
199     const GValue * value, GParamSpec * pspec)
200 {
201   GstOutputSelector *sel = GST_OUTPUT_SELECTOR (object);
202
203   switch (prop_id) {
204     case PROP_ACTIVE_PAD:{
205       GstPad *next_pad =
206           gst_element_get_static_pad (GST_ELEMENT (sel),
207           g_value_get_string (value));
208       if (!next_pad) {
209         GST_WARNING ("pad %s not found, activation failed",
210             g_value_get_string (value));
211         break;
212       }
213       if (next_pad != sel->active_srcpad) {
214         /* switch to new srcpad in next chain run */
215         if (sel->pending_srcpad != NULL) {
216           GST_INFO ("replacing pending switch");
217           gst_object_unref (sel->pending_srcpad);
218         }
219         sel->pending_srcpad = next_pad;
220       } else {
221         GST_INFO ("pad already active");
222         gst_object_unref (next_pad);
223       }
224       break;
225     }
226     case PROP_RESEND_LATEST:{
227       sel->resend_latest = g_value_get_boolean (value);
228       break;
229     }
230     default:
231       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
232       break;
233   }
234 }
235
236 static void
237 gst_output_selector_get_property (GObject * object, guint prop_id,
238     GValue * value, GParamSpec * pspec)
239 {
240   GstOutputSelector *sel = GST_OUTPUT_SELECTOR (object);
241
242   switch (prop_id) {
243     case PROP_ACTIVE_PAD:{
244       GST_OBJECT_LOCK (object);
245       if (sel->active_srcpad != NULL) {
246         g_value_take_string (value, gst_pad_get_name (sel->active_srcpad));
247       } else {
248         g_value_set_string (value, "");
249       }
250       GST_OBJECT_UNLOCK (object);
251       break;
252     }
253     case PROP_RESEND_LATEST:{
254       GST_OBJECT_LOCK (object);
255       g_value_set_boolean (value, sel->resend_latest);
256       GST_OBJECT_UNLOCK (object);
257       break;
258     }
259     default:
260       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
261       break;
262   }
263 }
264
265 static GstPad *
266 gst_output_selector_request_new_pad (GstElement * element,
267     GstPadTemplate * templ, const gchar * name)
268 {
269   gchar *padname;
270   GstPad *srcpad;
271   GstOutputSelector *osel;
272
273   osel = GST_OUTPUT_SELECTOR (element);
274
275   GST_DEBUG_OBJECT (osel, "requesting pad");
276
277   GST_OBJECT_LOCK (osel);
278   padname = g_strdup_printf ("src%d", osel->nb_srcpads++);
279   srcpad = gst_pad_new_from_template (templ, padname);
280   GST_OBJECT_UNLOCK (osel);
281
282   gst_pad_set_active (srcpad, TRUE);
283   gst_element_add_pad (GST_ELEMENT (osel), srcpad);
284
285   /* Set the first requested src pad as active by default */
286   if (osel->active_srcpad == NULL) {
287     osel->active_srcpad = srcpad;
288   }
289   g_free (padname);
290
291   return srcpad;
292 }
293
294 static void
295 gst_output_selector_release_pad (GstElement * element, GstPad * pad)
296 {
297   GstOutputSelector *osel;
298
299   osel = GST_OUTPUT_SELECTOR (element);
300
301   GST_DEBUG_OBJECT (osel, "releasing pad");
302
303   gst_pad_set_active (pad, FALSE);
304
305   gst_element_remove_pad (GST_ELEMENT_CAST (osel), pad);
306 }
307
308 static gboolean
309 gst_output_selector_switch (GstOutputSelector * osel)
310 {
311   gboolean res = TRUE;
312   GstEvent *ev = NULL;
313   GstSegment *seg = NULL;
314   gint64 start = 0, position = 0;
315
316   GST_INFO ("switching to pad %" GST_PTR_FORMAT, osel->pending_srcpad);
317
318   if (gst_pad_is_linked (osel->pending_srcpad)) {
319     /* Send NEWSEGMENT to the pad we are going to switch to */
320     seg = &osel->segment;
321     /* If resending then mark newsegment start and position accordingly */
322     if (osel->resend_latest && osel->latest_buffer &&
323         GST_BUFFER_TIMESTAMP_IS_VALID (osel->latest_buffer)) {
324       start = position = GST_BUFFER_TIMESTAMP (osel->latest_buffer);
325     } else {
326       start = position = seg->last_stop;
327     }
328     ev = gst_event_new_new_segment (TRUE, seg->rate,
329         seg->format, start, seg->stop, position);
330     if (!gst_pad_push_event (osel->pending_srcpad, ev)) {
331       GST_WARNING ("newsegment handling failed in %" GST_PTR_FORMAT,
332           osel->pending_srcpad);
333     }
334
335     /* Resend latest buffer to newly switched pad */
336     if (osel->resend_latest && osel->latest_buffer) {
337       GST_INFO ("resending latest buffer");
338       gst_pad_push (osel->pending_srcpad, osel->latest_buffer);
339       osel->latest_buffer = NULL;
340     }
341
342     /* Switch */
343     osel->active_srcpad = osel->pending_srcpad;
344   } else {
345     GST_WARNING ("switch failed, pad not linked");
346     res = FALSE;
347   }
348
349   gst_object_unref (osel->pending_srcpad);
350   osel->pending_srcpad = NULL;
351
352   return res;
353 }
354
355 static GstFlowReturn
356 gst_output_selector_chain (GstPad * pad, GstBuffer * buf)
357 {
358   GstFlowReturn res;
359   GstOutputSelector *osel;
360   GstClockTime last_stop, duration;
361
362   osel = GST_OUTPUT_SELECTOR (gst_pad_get_parent (pad));
363
364   if (osel->pending_srcpad) {
365     /* Do the switch */
366     gst_output_selector_switch (osel);
367   }
368
369   /* Keep reference to latest buffer to resend it after switch */
370   if (osel->resend_latest) {
371     if (osel->latest_buffer)
372       gst_buffer_unref (osel->latest_buffer);
373     osel->latest_buffer = gst_buffer_ref (buf);
374   }
375
376   /* Keep track of last stop and use it in NEWSEGMENT start after 
377      switching to a new src pad */
378   last_stop = GST_BUFFER_TIMESTAMP (buf);
379   if (GST_CLOCK_TIME_IS_VALID (last_stop)) {
380     duration = GST_BUFFER_DURATION (buf);
381     if (GST_CLOCK_TIME_IS_VALID (duration)) {
382       last_stop += duration;
383     }
384     GST_LOG ("setting last stop %" GST_TIME_FORMAT, GST_TIME_ARGS (last_stop));
385     gst_segment_set_last_stop (&osel->segment, osel->segment.format, last_stop);
386   }
387
388   GST_LOG ("pushing buffer to %" GST_PTR_FORMAT, osel->active_srcpad);
389   res = gst_pad_push (osel->active_srcpad, buf);
390   gst_object_unref (osel);
391
392   return res;
393 }
394
395 static GstStateChangeReturn
396 gst_output_selector_change_state (GstElement * element,
397     GstStateChange transition)
398 {
399   return GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
400 }
401
402 static gboolean
403 gst_output_selector_handle_sink_event (GstPad * pad, GstEvent * event)
404 {
405   gboolean res = TRUE;
406   GstOutputSelector *sel;
407
408   sel = GST_OUTPUT_SELECTOR (gst_pad_get_parent (pad));
409
410   switch (GST_EVENT_TYPE (event)) {
411     case GST_EVENT_NEWSEGMENT:
412     {
413       gboolean update;
414       GstFormat format;
415       gdouble rate, arate;
416       gint64 start, stop, time;
417
418       gst_event_parse_new_segment_full (event, &update, &rate, &arate, &format,
419           &start, &stop, &time);
420
421       GST_DEBUG ("configured NEWSEGMENT update %d, rate %lf, applied rate %lf, "
422           "format %d, "
423           "%" G_GINT64_FORMAT " -- %" G_GINT64_FORMAT ", time %"
424           G_GINT64_FORMAT, update, rate, arate, format, start, stop, time);
425
426       gst_segment_set_newsegment_full (&sel->segment, update,
427           rate, arate, format, start, stop, time);
428
429       /* Send newsegment to all src pads */
430       gst_pad_event_default (pad, event);
431
432       break;
433     }
434     case GST_EVENT_EOS:
435       /* Send eos to all src pads */
436       gst_pad_event_default (pad, event);
437       break;
438     default:
439       /* Send other events to active src pad */
440       res = gst_pad_push_event (sel->active_srcpad, event);
441       break;
442   }
443
444   gst_object_unref (sel);
445
446   return res;
447 }