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