2 * GStreamer Funnel element
4 * Copyright 2007 Collabora Ltd.
5 * @author: Olivier Crete <olivier.crete@collabora.co.uk>
6 * Copyright 2007 Nokia Corp.
8 * gstfunnel.c: Simple Funnel element
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
26 * SECTION:element-funnel
29 * Takes packets from various input sinks into one output source.
31 * funnel always outputs a single, open ended segment from
32 * 0 with in %GST_FORMAT_TIME and outputs the buffers of the
33 * different sinkpads with timestamps that are set to the
34 * running time for that stream. funnel does not synchronize
35 * the different input streams but simply forwards all buffers
36 * immediately when they arrive.
44 #include "gstfunnel.h"
46 GST_DEBUG_CATEGORY_STATIC (gst_funnel_debug);
47 #define GST_CAT_DEFAULT gst_funnel_debug
49 GType gst_funnel_pad_get_type (void);
50 #define GST_TYPE_FUNNEL_PAD \
51 (gst_funnel_pad_get_type())
52 #define GST_FUNNEL_PAD(obj) \
53 (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_FUNNEL_PAD, GstFunnelPad))
54 #define GST_FUNNEL_PAD_CLASS(klass) \
55 (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_FUNNEL_PAD, GstFunnelPadClass))
56 #define GST_IS_FUNNEL_PAD(obj) \
57 (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_FUNNEL_PAD))
58 #define GST_IS_FUNNEL_PAD_CLASS(klass) \
59 (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_FUNNEL_PAD))
60 #define GST_FUNNEL_PAD_CAST(obj) \
61 ((GstFunnelPad *)(obj))
63 typedef struct _GstFunnelPad GstFunnelPad;
64 typedef struct _GstFunnelPadClass GstFunnelPadClass;
73 struct _GstFunnelPadClass
78 G_DEFINE_TYPE (GstFunnelPad, gst_funnel_pad, GST_TYPE_PAD);
80 #define DEFAULT_FORWARD_STICKY_EVENTS TRUE
85 PROP_FORWARD_STICKY_EVENTS
89 gst_funnel_pad_class_init (GstFunnelPadClass * klass)
94 gst_funnel_pad_init (GstFunnelPad * pad)
99 static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink_%u",
102 GST_STATIC_CAPS_ANY);
104 static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
107 GST_STATIC_CAPS_ANY);
110 GST_DEBUG_CATEGORY_INIT (gst_funnel_debug, "funnel", 0, "funnel element");
111 #define gst_funnel_parent_class parent_class
112 G_DEFINE_TYPE_WITH_CODE (GstFunnel, gst_funnel, GST_TYPE_ELEMENT, _do_init);
114 static GstStateChangeReturn gst_funnel_change_state (GstElement * element,
115 GstStateChange transition);
116 static GstPad *gst_funnel_request_new_pad (GstElement * element,
117 GstPadTemplate * templ, const gchar * name, const GstCaps * caps);
118 static void gst_funnel_release_pad (GstElement * element, GstPad * pad);
120 static GstFlowReturn gst_funnel_sink_chain (GstPad * pad, GstObject * parent,
122 static GstFlowReturn gst_funnel_sink_chain_list (GstPad * pad,
123 GstObject * parent, GstBufferList * list);
124 static gboolean gst_funnel_sink_event (GstPad * pad, GstObject * parent,
128 gst_funnel_set_property (GObject * object, guint prop_id,
129 const GValue * value, GParamSpec * pspec)
131 GstFunnel *funnel = GST_FUNNEL_CAST (object);
134 case PROP_FORWARD_STICKY_EVENTS:
135 funnel->forward_sticky_events = g_value_get_boolean (value);
138 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
144 gst_funnel_get_property (GObject * object, guint prop_id, GValue * value,
147 GstFunnel *funnel = GST_FUNNEL_CAST (object);
150 case PROP_FORWARD_STICKY_EVENTS:
151 g_value_set_boolean (value, funnel->forward_sticky_events);
154 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
160 gst_funnel_dispose (GObject * object)
162 GstFunnel *funnel = GST_FUNNEL_CAST (object);
165 gst_object_replace ((GstObject **) & funnel->last_sinkpad, NULL);
168 for (item = GST_ELEMENT_PADS (object); item; item = g_list_next (item)) {
169 GstPad *pad = GST_PAD (item->data);
171 if (GST_PAD_IS_SINK (pad)) {
172 gst_element_release_request_pad (GST_ELEMENT (object), pad);
177 G_OBJECT_CLASS (parent_class)->dispose (object);
181 gst_funnel_class_init (GstFunnelClass * klass)
183 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
184 GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
186 gobject_class->set_property = gst_funnel_set_property;
187 gobject_class->get_property = gst_funnel_get_property;
188 gobject_class->dispose = GST_DEBUG_FUNCPTR (gst_funnel_dispose);
190 g_object_class_install_property (gobject_class, PROP_FORWARD_STICKY_EVENTS,
191 g_param_spec_boolean ("forward-sticky-events", "Forward sticky events",
192 "Forward sticky events on stream changes",
193 DEFAULT_FORWARD_STICKY_EVENTS,
194 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
195 GST_PARAM_MUTABLE_READY));
197 gst_element_class_set_static_metadata (gstelement_class,
198 "Funnel pipe fitting", "Generic", "N-to-1 pipe fitting",
199 "Olivier Crete <olivier.crete@collabora.co.uk>");
201 gst_element_class_add_static_pad_template (gstelement_class, &sink_template);
202 gst_element_class_add_static_pad_template (gstelement_class, &src_template);
204 gstelement_class->request_new_pad =
205 GST_DEBUG_FUNCPTR (gst_funnel_request_new_pad);
206 gstelement_class->release_pad = GST_DEBUG_FUNCPTR (gst_funnel_release_pad);
207 gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_funnel_change_state);
211 gst_funnel_init (GstFunnel * funnel)
213 funnel->srcpad = gst_pad_new_from_static_template (&src_template, "src");
214 gst_pad_use_fixed_caps (funnel->srcpad);
216 gst_element_add_pad (GST_ELEMENT (funnel), funnel->srcpad);
218 funnel->forward_sticky_events = DEFAULT_FORWARD_STICKY_EVENTS;
222 gst_funnel_request_new_pad (GstElement * element, GstPadTemplate * templ,
223 const gchar * name, const GstCaps * caps)
227 GST_DEBUG_OBJECT (element, "requesting pad");
229 sinkpad = GST_PAD_CAST (g_object_new (GST_TYPE_FUNNEL_PAD,
230 "name", name, "direction", templ->direction, "template", templ,
233 gst_pad_set_chain_function (sinkpad,
234 GST_DEBUG_FUNCPTR (gst_funnel_sink_chain));
235 gst_pad_set_chain_list_function (sinkpad,
236 GST_DEBUG_FUNCPTR (gst_funnel_sink_chain_list));
237 gst_pad_set_event_function (sinkpad,
238 GST_DEBUG_FUNCPTR (gst_funnel_sink_event));
240 GST_OBJECT_FLAG_SET (sinkpad, GST_PAD_FLAG_PROXY_CAPS);
241 GST_OBJECT_FLAG_SET (sinkpad, GST_PAD_FLAG_PROXY_ALLOCATION);
243 gst_pad_set_active (sinkpad, TRUE);
245 gst_element_add_pad (element, sinkpad);
247 GST_DEBUG_OBJECT (element, "requested pad %s:%s",
248 GST_DEBUG_PAD_NAME (sinkpad));
254 gst_funnel_all_sinkpads_eos_unlocked (GstFunnel * funnel, GstPad * pad)
256 GstElement *element = GST_ELEMENT_CAST (funnel);
258 gboolean all_eos = FALSE;
261 if (element->numsinkpads == 0)
264 for (item = element->sinkpads; item != NULL; item = g_list_next (item)) {
265 GstFunnelPad *sinkpad = GST_FUNNEL_PAD_CAST (item->data);
267 if (!sinkpad->got_eos) {
279 gst_funnel_release_pad (GstElement * element, GstPad * pad)
281 GstFunnel *funnel = GST_FUNNEL_CAST (element);
282 GstFunnelPad *fpad = GST_FUNNEL_PAD_CAST (pad);
284 gboolean send_eos = FALSE;
286 GST_DEBUG_OBJECT (funnel, "releasing pad %s:%s", GST_DEBUG_PAD_NAME (pad));
288 gst_pad_set_active (pad, FALSE);
290 got_eos = fpad->got_eos;
292 gst_element_remove_pad (GST_ELEMENT_CAST (funnel), pad);
294 GST_OBJECT_LOCK (funnel);
295 if (!got_eos && gst_funnel_all_sinkpads_eos_unlocked (funnel, NULL)) {
296 GST_DEBUG_OBJECT (funnel, "Pad removed. All others are EOS. Sending EOS");
299 GST_OBJECT_UNLOCK (funnel);
302 if (!gst_pad_push_event (funnel->srcpad, gst_event_new_eos ()))
303 GST_WARNING_OBJECT (funnel, "Failure pushing EOS");
307 forward_events (GstPad * pad, GstEvent ** event, gpointer user_data)
309 GstPad *srcpad = user_data;
311 if (GST_EVENT_TYPE (*event) != GST_EVENT_EOS)
312 gst_pad_push_event (srcpad, gst_event_ref (*event));
318 gst_funnel_sink_chain_object (GstPad * pad, GstFunnel * funnel,
319 gboolean is_list, GstMiniObject * obj)
323 GST_DEBUG_OBJECT (pad, "received %" GST_PTR_FORMAT, obj);
325 GST_PAD_STREAM_LOCK (funnel->srcpad);
327 if ((funnel->last_sinkpad == NULL) || (funnel->forward_sticky_events
328 && (funnel->last_sinkpad != pad))) {
329 gst_object_replace ((GstObject **) & funnel->last_sinkpad,
332 GST_DEBUG_OBJECT (pad, "Forwarding sticky events");
333 gst_pad_sticky_events_foreach (pad, forward_events, funnel->srcpad);
337 res = gst_pad_push_list (funnel->srcpad, GST_BUFFER_LIST_CAST (obj));
339 res = gst_pad_push (funnel->srcpad, GST_BUFFER_CAST (obj));
341 GST_PAD_STREAM_UNLOCK (funnel->srcpad);
343 GST_LOG_OBJECT (pad, "handled buffer%s %s", (is_list ? "list" : ""),
344 gst_flow_get_name (res));
350 gst_funnel_sink_chain_list (GstPad * pad, GstObject * parent,
351 GstBufferList * list)
353 GstFunnel *funnel = GST_FUNNEL_CAST (parent);
355 return gst_funnel_sink_chain_object (pad, funnel, TRUE,
356 GST_MINI_OBJECT_CAST (list));
360 gst_funnel_sink_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer)
362 GstFunnel *funnel = GST_FUNNEL_CAST (parent);
364 return gst_funnel_sink_chain_object (pad, funnel, FALSE,
365 GST_MINI_OBJECT_CAST (buffer));
369 gst_funnel_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
371 GstFunnel *funnel = GST_FUNNEL_CAST (parent);
372 GstFunnelPad *fpad = GST_FUNNEL_PAD_CAST (pad);
373 gboolean forward = TRUE;
375 gboolean unlock = FALSE;
377 GST_DEBUG_OBJECT (pad, "received event %" GST_PTR_FORMAT, event);
379 if (GST_EVENT_IS_STICKY (event)) {
381 GST_PAD_STREAM_LOCK (funnel->srcpad);
383 if (GST_EVENT_TYPE (event) == GST_EVENT_EOS) {
384 GST_OBJECT_LOCK (funnel);
385 fpad->got_eos = TRUE;
386 if (!gst_funnel_all_sinkpads_eos_unlocked (funnel, pad)) {
391 GST_OBJECT_UNLOCK (funnel);
392 } else if (pad != funnel->last_sinkpad) {
395 } else if (GST_EVENT_TYPE (event) == GST_EVENT_FLUSH_STOP) {
397 GST_PAD_STREAM_LOCK (funnel->srcpad);
398 GST_OBJECT_LOCK (funnel);
399 fpad->got_eos = FALSE;
400 GST_OBJECT_UNLOCK (funnel);
403 if (forward && GST_EVENT_IS_SERIALIZED (event)) {
404 /* If no data is coming and we receive serialized event, need to forward all sticky events.
405 * Otherwise downstream has an inconsistent set of sticky events when
406 * handling the new event. */
409 GST_PAD_STREAM_LOCK (funnel->srcpad);
412 if ((funnel->last_sinkpad == NULL) || (funnel->forward_sticky_events
413 && (funnel->last_sinkpad != pad))) {
414 gst_object_replace ((GstObject **) & funnel->last_sinkpad,
416 gst_pad_sticky_events_foreach (pad, forward_events, funnel->srcpad);
421 res = gst_pad_push_event (funnel->srcpad, event);
423 gst_event_unref (event);
426 GST_PAD_STREAM_UNLOCK (funnel->srcpad);
432 reset_pad (const GValue * data, gpointer user_data)
434 GstPad *pad = g_value_get_object (data);
435 GstFunnelPad *fpad = GST_FUNNEL_PAD_CAST (pad);
436 GstFunnel *funnel = user_data;
438 GST_OBJECT_LOCK (funnel);
439 fpad->got_eos = FALSE;
440 GST_OBJECT_UNLOCK (funnel);
443 static GstStateChangeReturn
444 gst_funnel_change_state (GstElement * element, GstStateChange transition)
446 GstStateChangeReturn ret;
448 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
449 if (ret == GST_STATE_CHANGE_FAILURE)
452 switch (transition) {
453 case GST_STATE_CHANGE_PAUSED_TO_READY:
455 GstIterator *iter = gst_element_iterate_sink_pads (element);
456 GstIteratorResult res;
459 res = gst_iterator_foreach (iter, reset_pad, element);
460 if (res == GST_ITERATOR_RESYNC)
461 gst_iterator_resync (iter);
462 } while (res == GST_ITERATOR_RESYNC);
463 gst_iterator_free (iter);
465 if (res == GST_ITERATOR_ERROR)
466 return GST_STATE_CHANGE_FAILURE;