2 * Copyright (C) 2008 Nokia Corporation. (contact <stefan.kost@nokia.com>)
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.
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.
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.
21 * SECTION:element-output-selector
22 * @short_description: 1-to-N stream selectoring
23 * @see_also: #GstTee, #GstInputSelector
25 * Direct input stream to one out of N output pads.
34 #include "gstoutputselector.h"
36 GST_DEBUG_CATEGORY_STATIC (output_selector_debug);
37 #define GST_CAT_DEFAULT output_selector_debug
39 static const GstElementDetails gst_output_selector_details =
40 GST_ELEMENT_DETAILS ("Output selector",
42 "1-to-N output stream selectoring",
43 "Stefan Kost <stefan.kost@nokia.com>");
45 static GstStaticPadTemplate gst_output_selector_sink_factory =
46 GST_STATIC_PAD_TEMPLATE ("sink",
51 static GstStaticPadTemplate gst_output_selector_src_factory =
52 GST_STATIC_PAD_TEMPLATE ("src%d",
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,
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,
81 static GstElementClass *parent_class = NULL;
84 gst_output_selector_get_type (void)
86 static GType output_selector_type = 0;
88 if (!output_selector_type) {
89 static const GTypeInfo output_selector_info = {
90 sizeof (GstOutputSelectorClass),
91 (GBaseInitFunc) gst_output_selector_base_init,
93 (GClassInitFunc) gst_output_selector_class_init,
96 sizeof (GstOutputSelector),
98 (GInstanceInitFunc) gst_output_selector_init,
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");
107 return output_selector_type;
111 gst_output_selector_base_init (GstOutputSelectorClass * klass)
113 GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
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));
123 gst_output_selector_class_init (GstOutputSelectorClass * klass)
125 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
126 GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
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,
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;
150 gst_output_selector_init (GstOutputSelector * sel)
153 gst_pad_new_from_static_template (&gst_output_selector_sink_factory,
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));
160 gst_element_add_pad (GST_ELEMENT (sel), sel->sinkpad);
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));
170 /* srcpad management */
171 sel->active_srcpad = NULL;
173 gst_segment_init (&sel->segment, GST_FORMAT_UNDEFINED);
174 sel->pending_srcpad = NULL;
176 sel->resend_latest = FALSE;
177 sel->latest_buffer = NULL;
181 gst_output_selector_dispose (GObject * object)
183 GstOutputSelector *osel = GST_OUTPUT_SELECTOR (object);
185 if (osel->pending_srcpad != NULL) {
186 gst_object_unref (osel->pending_srcpad);
187 osel->pending_srcpad = NULL;
189 if (osel->latest_buffer != NULL) {
190 gst_buffer_unref (osel->latest_buffer);
191 osel->latest_buffer = NULL;
194 G_OBJECT_CLASS (parent_class)->dispose (object);
198 gst_output_selector_set_property (GObject * object, guint prop_id,
199 const GValue * value, GParamSpec * pspec)
201 GstOutputSelector *sel = GST_OUTPUT_SELECTOR (object);
204 case PROP_ACTIVE_PAD:{
206 gst_element_get_static_pad (GST_ELEMENT (sel),
207 g_value_get_string (value));
209 GST_WARNING ("pad %s not found, activation failed",
210 g_value_get_string (value));
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);
219 sel->pending_srcpad = next_pad;
221 GST_INFO ("pad already active");
222 gst_object_unref (next_pad);
226 case PROP_RESEND_LATEST:{
227 sel->resend_latest = g_value_get_boolean (value);
231 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
237 gst_output_selector_get_property (GObject * object, guint prop_id,
238 GValue * value, GParamSpec * pspec)
240 GstOutputSelector *sel = GST_OUTPUT_SELECTOR (object);
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));
248 g_value_set_string (value, "");
250 GST_OBJECT_UNLOCK (object);
253 case PROP_RESEND_LATEST:{
254 GST_OBJECT_LOCK (object);
255 g_value_set_boolean (value, sel->resend_latest);
256 GST_OBJECT_UNLOCK (object);
260 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
266 gst_output_selector_request_new_pad (GstElement * element,
267 GstPadTemplate * templ, const gchar * name)
271 GstOutputSelector *osel;
273 osel = GST_OUTPUT_SELECTOR (element);
275 GST_DEBUG_OBJECT (osel, "requesting pad");
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);
282 gst_pad_set_active (srcpad, TRUE);
283 gst_element_add_pad (GST_ELEMENT (osel), srcpad);
285 /* Set the first requested src pad as active by default */
286 if (osel->active_srcpad == NULL) {
287 osel->active_srcpad = srcpad;
295 gst_output_selector_release_pad (GstElement * element, GstPad * pad)
297 GstOutputSelector *osel;
299 osel = GST_OUTPUT_SELECTOR (element);
301 GST_DEBUG_OBJECT (osel, "releasing pad");
303 gst_pad_set_active (pad, FALSE);
305 gst_element_remove_pad (GST_ELEMENT_CAST (osel), pad);
309 gst_output_selector_switch (GstOutputSelector * osel)
313 GstSegment *seg = NULL;
314 gint64 start = 0, position = 0;
316 GST_INFO ("switching to pad %" GST_PTR_FORMAT, osel->pending_srcpad);
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);
326 start = position = seg->last_stop;
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);
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;
343 osel->active_srcpad = osel->pending_srcpad;
345 GST_WARNING ("switch failed, pad not linked");
349 gst_object_unref (osel->pending_srcpad);
350 osel->pending_srcpad = NULL;
356 gst_output_selector_chain (GstPad * pad, GstBuffer * buf)
359 GstOutputSelector *osel;
360 GstClockTime last_stop, duration;
362 osel = GST_OUTPUT_SELECTOR (gst_pad_get_parent (pad));
364 if (osel->pending_srcpad) {
366 gst_output_selector_switch (osel);
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);
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;
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);
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);
395 static GstStateChangeReturn
396 gst_output_selector_change_state (GstElement * element,
397 GstStateChange transition)
399 return GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
403 gst_output_selector_handle_sink_event (GstPad * pad, GstEvent * event)
406 GstOutputSelector *sel;
408 sel = GST_OUTPUT_SELECTOR (gst_pad_get_parent (pad));
410 switch (GST_EVENT_TYPE (event)) {
411 case GST_EVENT_NEWSEGMENT:
416 gint64 start, stop, time;
418 gst_event_parse_new_segment_full (event, &update, &rate, &arate, &format,
419 &start, &stop, &time);
421 GST_DEBUG ("configured NEWSEGMENT update %d, rate %lf, applied rate %lf, "
423 "%" G_GINT64_FORMAT " -- %" G_GINT64_FORMAT ", time %"
424 G_GINT64_FORMAT, update, rate, arate, format, start, stop, time);
426 gst_segment_set_newsegment_full (&sel->segment, update,
427 rate, arate, format, start, stop, time);
429 /* Send newsegment to all src pads */
430 gst_pad_event_default (pad, event);
435 /* Send eos to all src pads */
436 gst_pad_event_default (pad, event);
439 /* Send other events to active src pad */
440 res = gst_pad_push_event (sel->active_srcpad, event);
444 gst_object_unref (sel);