1 /* GStreamer Stream Splitter
2 * Copyright (C) 2010 Edward Hervey <edward.hervey@collabora.co.uk>
3 * (C) 2009 Nokia Corporation
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 * Boston, MA 02111-1307, USA.
25 #include "gststreamsplitter.h"
27 static GstStaticPadTemplate src_template =
28 GST_STATIC_PAD_TEMPLATE ("src_%d", GST_PAD_SRC, GST_PAD_REQUEST,
31 static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
36 GST_DEBUG_CATEGORY_STATIC (gst_stream_splitter_debug);
37 #define GST_CAT_DEFAULT gst_stream_splitter_debug
39 G_DEFINE_TYPE (GstStreamSplitter, gst_stream_splitter, GST_TYPE_ELEMENT);
41 #define STREAMS_LOCK(obj) (g_mutex_lock(obj->lock))
42 #define STREAMS_UNLOCK(obj) (g_mutex_unlock(obj->lock))
44 static void gst_stream_splitter_dispose (GObject * object);
46 static GstPad *gst_stream_splitter_request_new_pad (GstElement * element,
47 GstPadTemplate * templ, const gchar * name);
48 static void gst_stream_splitter_release_pad (GstElement * element,
52 gst_stream_splitter_class_init (GstStreamSplitterClass * klass)
54 GObjectClass *gobject_klass;
55 GstElementClass *gstelement_klass;
57 gobject_klass = (GObjectClass *) klass;
58 gstelement_klass = (GstElementClass *) klass;
60 gobject_klass->dispose = gst_stream_splitter_dispose;
62 GST_DEBUG_CATEGORY_INIT (gst_stream_splitter_debug, "streamsplitter", 0,
65 gst_element_class_add_static_pad_template (gstelement_klass,
67 gst_element_class_add_static_pad_template (gstelement_klass,
70 gstelement_klass->request_new_pad =
71 GST_DEBUG_FUNCPTR (gst_stream_splitter_request_new_pad);
72 gstelement_klass->release_pad =
73 GST_DEBUG_FUNCPTR (gst_stream_splitter_release_pad);
75 gst_element_class_set_details_simple (gstelement_klass,
76 "streamsplitter", "Generic",
77 "Splits streams based on their media type",
78 "Edward Hervey <edward.hervey@collabora.co.uk>");
82 gst_stream_splitter_dispose (GObject * object)
84 GstStreamSplitter *stream_splitter = (GstStreamSplitter *) object;
86 if (stream_splitter->lock) {
87 g_mutex_free (stream_splitter->lock);
88 stream_splitter->lock = NULL;
91 g_list_foreach (stream_splitter->pending_events, (GFunc) gst_event_unref,
93 g_list_free (stream_splitter->pending_events);
94 stream_splitter->pending_events = NULL;
96 G_OBJECT_CLASS (gst_stream_splitter_parent_class)->dispose (object);
100 gst_stream_splitter_chain (GstPad * pad, GstBuffer * buf)
102 GstStreamSplitter *stream_splitter =
103 (GstStreamSplitter *) GST_PAD_PARENT (pad);
105 GstPad *srcpad = NULL;
107 STREAMS_LOCK (stream_splitter);
108 if (stream_splitter->current)
109 srcpad = gst_object_ref (stream_splitter->current);
110 STREAMS_UNLOCK (stream_splitter);
112 if (G_UNLIKELY (srcpad == NULL))
115 if (G_UNLIKELY (stream_splitter->pending_events)) {
117 GST_DEBUG_OBJECT (srcpad, "Pushing out pending events");
119 for (tmp = stream_splitter->pending_events; tmp; tmp = tmp->next) {
120 GstEvent *event = (GstEvent *) tmp->data;
121 gst_pad_push_event (srcpad, event);
123 g_list_free (stream_splitter->pending_events);
124 stream_splitter->pending_events = NULL;
127 /* Forward to currently activated stream */
128 res = gst_pad_push (srcpad, buf);
129 gst_object_unref (srcpad);
134 GST_WARNING_OBJECT (stream_splitter, "No output pad was configured");
135 return GST_FLOW_ERROR;
139 gst_stream_splitter_sink_event (GstPad * pad, GstEvent * event)
141 GstStreamSplitter *stream_splitter =
142 (GstStreamSplitter *) GST_PAD_PARENT (pad);
144 gboolean toall = FALSE;
145 gboolean store = FALSE;
146 gboolean eos = FALSE;
147 gboolean flushpending = FALSE;
149 /* FLUSH_START/STOP : forward to all
150 * EOS : transform to CUSTOM_REAL_EOS and forward to all
151 * INBAND events : store to send in chain function to selected chain
152 * OUT_OF_BAND events : send to all
155 GST_DEBUG_OBJECT (stream_splitter, "Got event %s",
156 GST_EVENT_TYPE_NAME (event));
158 switch (GST_EVENT_TYPE (event)) {
159 case GST_EVENT_FLUSH_STOP:
163 case GST_EVENT_FLUSH_START:
167 /* Replace with our custom eos event */
168 gst_event_unref (event);
170 gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM,
171 gst_structure_empty_new ("stream-switching-eos"));
176 if (GST_EVENT_TYPE (event) & GST_EVENT_TYPE_SERIALIZED)
181 g_list_foreach (stream_splitter->pending_events, (GFunc) gst_event_unref,
183 g_list_free (stream_splitter->pending_events);
184 stream_splitter->pending_events = NULL;
188 stream_splitter->pending_events =
189 g_list_append (stream_splitter->pending_events, event);
190 } else if (toall || eos) {
194 /* Send to all pads */
195 STREAMS_LOCK (stream_splitter);
197 if (G_UNLIKELY (stream_splitter->srcpads == NULL)) {
198 STREAMS_UNLOCK (stream_splitter);
200 gst_event_unref (event);
204 tmp = stream_splitter->srcpads;
205 cookie = stream_splitter->cookie;
207 GstPad *srcpad = (GstPad *) tmp->data;
208 STREAMS_UNLOCK (stream_splitter);
209 /* In case of EOS, we first push out the real one to flush out
210 * each streams (but which will be discarded in the streamcombiner)
211 * before our custom one (which will be converted back to and EOS
212 * in the streamcombiner) */
214 gst_pad_push_event (srcpad, gst_event_new_eos ());
215 gst_event_ref (event);
216 res = gst_pad_push_event (srcpad, event);
217 STREAMS_LOCK (stream_splitter);
218 if (G_UNLIKELY (cookie != stream_splitter->cookie))
222 STREAMS_UNLOCK (stream_splitter);
223 gst_event_unref (event);
227 /* Only send to current pad */
229 STREAMS_LOCK (stream_splitter);
230 pad = stream_splitter->current;
231 STREAMS_UNLOCK (stream_splitter);
233 res = gst_pad_push_event (pad, event);
235 gst_event_unref (event);
245 gst_stream_splitter_sink_getcaps (GstPad * pad)
247 GstStreamSplitter *stream_splitter =
248 (GstStreamSplitter *) GST_PAD_PARENT (pad);
253 /* Return the combination of all downstream caps */
255 STREAMS_LOCK (stream_splitter);
258 if (G_UNLIKELY (stream_splitter->srcpads == NULL)) {
259 res = gst_caps_new_any ();
264 cookie = stream_splitter->cookie;
265 tmp = stream_splitter->srcpads;
268 GstPad *srcpad = (GstPad *) tmp->data;
270 STREAMS_UNLOCK (stream_splitter);
272 GstCaps *peercaps = gst_pad_peer_get_caps_reffed (srcpad);
274 gst_caps_merge (res, gst_caps_make_writable (peercaps));
276 res = gst_pad_peer_get_caps (srcpad);
278 STREAMS_LOCK (stream_splitter);
280 if (G_UNLIKELY (cookie != stream_splitter->cookie)) {
282 gst_caps_unref (res);
289 STREAMS_UNLOCK (stream_splitter);
294 gst_stream_splitter_sink_setcaps (GstPad * pad, GstCaps * caps)
296 GstStreamSplitter *stream_splitter =
297 (GstStreamSplitter *) GST_PAD_PARENT (pad);
302 GST_DEBUG_OBJECT (stream_splitter, "caps %" GST_PTR_FORMAT, caps);
304 /* Try on all pads, choose the one that succeeds as the current stream */
305 STREAMS_LOCK (stream_splitter);
308 if (G_UNLIKELY (stream_splitter->srcpads == NULL)) {
314 tmp = stream_splitter->srcpads;
315 cookie = stream_splitter->cookie;
318 GstPad *srcpad = (GstPad *) tmp->data;
321 STREAMS_UNLOCK (stream_splitter);
322 peercaps = gst_pad_peer_get_caps_reffed (srcpad);
324 res = gst_caps_can_intersect (caps, peercaps);
325 gst_caps_unref (peercaps);
327 STREAMS_LOCK (stream_splitter);
329 if (G_UNLIKELY (cookie != stream_splitter->cookie))
333 /* FIXME : we need to switch properly */
334 GST_DEBUG_OBJECT (srcpad, "Setting caps on this pad was successful");
335 stream_splitter->current = srcpad;
342 STREAMS_UNLOCK (stream_splitter);
347 gst_stream_splitter_src_event (GstPad * pad, GstEvent * event)
349 GstStreamSplitter *stream_splitter =
350 (GstStreamSplitter *) GST_PAD_PARENT (pad);
352 GST_DEBUG_OBJECT (pad, "%s", GST_EVENT_TYPE_NAME (event));
354 /* Forward upstream as is */
355 return gst_pad_push_event (stream_splitter->sinkpad, event);
359 gst_stream_splitter_src_query (GstPad * pad, GstQuery * query)
361 GstStreamSplitter *stream_splitter =
362 (GstStreamSplitter *) GST_PAD_PARENT (pad);
364 GST_DEBUG_OBJECT (pad, "%s", GST_QUERY_TYPE_NAME (query));
366 /* Forward upstream as is */
367 return gst_pad_peer_query (stream_splitter->sinkpad, query);
371 gst_stream_splitter_init (GstStreamSplitter * stream_splitter)
373 stream_splitter->sinkpad =
374 gst_pad_new_from_static_template (&sink_template, "sink");
375 /* FIXME : No buffer alloc for the time being, it will resort to the fallback */
376 /* gst_pad_set_bufferalloc_function (stream_splitter->sinkpad, */
377 /* gst_stream_splitter_buffer_alloc); */
378 gst_pad_set_chain_function (stream_splitter->sinkpad,
379 gst_stream_splitter_chain);
380 gst_pad_set_event_function (stream_splitter->sinkpad,
381 gst_stream_splitter_sink_event);
382 gst_pad_set_getcaps_function (stream_splitter->sinkpad,
383 gst_stream_splitter_sink_getcaps);
384 gst_pad_set_setcaps_function (stream_splitter->sinkpad,
385 gst_stream_splitter_sink_setcaps);
386 gst_element_add_pad (GST_ELEMENT (stream_splitter), stream_splitter->sinkpad);
388 stream_splitter->lock = g_mutex_new ();
392 gst_stream_splitter_request_new_pad (GstElement * element,
393 GstPadTemplate * templ, const gchar * name)
395 GstStreamSplitter *stream_splitter = (GstStreamSplitter *) element;
398 srcpad = gst_pad_new_from_static_template (&src_template, name);
399 gst_pad_set_event_function (srcpad, gst_stream_splitter_src_event);
400 gst_pad_set_query_function (srcpad, gst_stream_splitter_src_query);
402 STREAMS_LOCK (stream_splitter);
403 stream_splitter->srcpads = g_list_append (stream_splitter->srcpads, srcpad);
404 gst_pad_set_active (srcpad, TRUE);
405 gst_element_add_pad (element, srcpad);
406 stream_splitter->cookie++;
407 STREAMS_UNLOCK (stream_splitter);
413 gst_stream_splitter_release_pad (GstElement * element, GstPad * pad)
415 GstStreamSplitter *stream_splitter = (GstStreamSplitter *) element;
418 STREAMS_LOCK (stream_splitter);
419 tmp = g_list_find (stream_splitter->srcpads, pad);
421 GstPad *pad = (GstPad *) tmp->data;
423 stream_splitter->srcpads =
424 g_list_delete_link (stream_splitter->srcpads, tmp);
425 stream_splitter->cookie++;
427 if (pad == stream_splitter->current) {
428 /* Deactivate current flow */
429 GST_DEBUG_OBJECT (element, "Removed pad was the current one");
430 stream_splitter->current = NULL;
433 gst_element_remove_pad (element, pad);
435 STREAMS_UNLOCK (stream_splitter);