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"
26 #include "gst/glib-compat-private.h"
28 static GstStaticPadTemplate src_template =
29 GST_STATIC_PAD_TEMPLATE ("src_%d", GST_PAD_SRC, GST_PAD_REQUEST,
32 static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
37 GST_DEBUG_CATEGORY_STATIC (gst_stream_splitter_debug);
38 #define GST_CAT_DEFAULT gst_stream_splitter_debug
40 G_DEFINE_TYPE (GstStreamSplitter, gst_stream_splitter, GST_TYPE_ELEMENT);
42 #define STREAMS_LOCK(obj) (g_mutex_lock(obj->lock))
43 #define STREAMS_UNLOCK(obj) (g_mutex_unlock(obj->lock))
45 static void gst_stream_splitter_dispose (GObject * object);
47 static GstPad *gst_stream_splitter_request_new_pad (GstElement * element,
48 GstPadTemplate * templ, const gchar * name);
49 static void gst_stream_splitter_release_pad (GstElement * element,
53 gst_stream_splitter_class_init (GstStreamSplitterClass * klass)
55 GObjectClass *gobject_klass;
56 GstElementClass *gstelement_klass;
58 gobject_klass = (GObjectClass *) klass;
59 gstelement_klass = (GstElementClass *) klass;
61 gobject_klass->dispose = gst_stream_splitter_dispose;
63 GST_DEBUG_CATEGORY_INIT (gst_stream_splitter_debug, "streamsplitter", 0,
66 gst_element_class_add_static_pad_template (gstelement_klass, &src_template);
67 gst_element_class_add_static_pad_template (gstelement_klass, &sink_template);
69 gstelement_klass->request_new_pad =
70 GST_DEBUG_FUNCPTR (gst_stream_splitter_request_new_pad);
71 gstelement_klass->release_pad =
72 GST_DEBUG_FUNCPTR (gst_stream_splitter_release_pad);
74 gst_element_class_set_details_simple (gstelement_klass,
75 "streamsplitter", "Generic",
76 "Splits streams based on their media type",
77 "Edward Hervey <edward.hervey@collabora.co.uk>");
81 gst_stream_splitter_dispose (GObject * object)
83 GstStreamSplitter *stream_splitter = (GstStreamSplitter *) object;
85 if (stream_splitter->lock) {
86 g_mutex_free (stream_splitter->lock);
87 stream_splitter->lock = NULL;
90 g_list_foreach (stream_splitter->pending_events, (GFunc) gst_event_unref,
92 g_list_free (stream_splitter->pending_events);
93 stream_splitter->pending_events = NULL;
95 G_OBJECT_CLASS (gst_stream_splitter_parent_class)->dispose (object);
99 gst_stream_splitter_chain (GstPad * pad, GstBuffer * buf)
101 GstStreamSplitter *stream_splitter =
102 (GstStreamSplitter *) GST_PAD_PARENT (pad);
104 GstPad *srcpad = NULL;
106 STREAMS_LOCK (stream_splitter);
107 if (stream_splitter->current)
108 srcpad = gst_object_ref (stream_splitter->current);
109 STREAMS_UNLOCK (stream_splitter);
111 if (G_UNLIKELY (srcpad == NULL))
114 if (G_UNLIKELY (stream_splitter->pending_events)) {
116 GST_DEBUG_OBJECT (srcpad, "Pushing out pending events");
118 for (tmp = stream_splitter->pending_events; tmp; tmp = tmp->next) {
119 GstEvent *event = (GstEvent *) tmp->data;
120 gst_pad_push_event (srcpad, event);
122 g_list_free (stream_splitter->pending_events);
123 stream_splitter->pending_events = NULL;
126 /* Forward to currently activated stream */
127 res = gst_pad_push (srcpad, buf);
128 gst_object_unref (srcpad);
133 GST_WARNING_OBJECT (stream_splitter, "No output pad was configured");
134 return GST_FLOW_ERROR;
138 gst_stream_splitter_sink_event (GstPad * pad, GstEvent * event)
140 GstStreamSplitter *stream_splitter =
141 (GstStreamSplitter *) GST_PAD_PARENT (pad);
143 gboolean toall = FALSE;
144 gboolean store = FALSE;
145 gboolean eos = FALSE;
146 gboolean flushpending = FALSE;
148 /* FLUSH_START/STOP : forward to all
149 * EOS : transform to CUSTOM_REAL_EOS and forward to all
150 * INBAND events : store to send in chain function to selected chain
151 * OUT_OF_BAND events : send to all
154 GST_DEBUG_OBJECT (stream_splitter, "Got event %s",
155 GST_EVENT_TYPE_NAME (event));
157 switch (GST_EVENT_TYPE (event)) {
158 case GST_EVENT_FLUSH_STOP:
162 case GST_EVENT_FLUSH_START:
166 /* Replace with our custom eos event */
167 gst_event_unref (event);
169 gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM,
170 gst_structure_empty_new ("stream-switching-eos"));
175 if (GST_EVENT_TYPE (event) & GST_EVENT_TYPE_SERIALIZED)
180 g_list_foreach (stream_splitter->pending_events, (GFunc) gst_event_unref,
182 g_list_free (stream_splitter->pending_events);
183 stream_splitter->pending_events = NULL;
187 stream_splitter->pending_events =
188 g_list_append (stream_splitter->pending_events, event);
189 } else if (toall || eos) {
193 /* Send to all pads */
194 STREAMS_LOCK (stream_splitter);
196 if (G_UNLIKELY (stream_splitter->srcpads == NULL)) {
197 STREAMS_UNLOCK (stream_splitter);
199 gst_event_unref (event);
203 tmp = stream_splitter->srcpads;
204 cookie = stream_splitter->cookie;
206 GstPad *srcpad = (GstPad *) tmp->data;
207 STREAMS_UNLOCK (stream_splitter);
208 /* In case of EOS, we first push out the real one to flush out
209 * each streams (but which will be discarded in the streamcombiner)
210 * before our custom one (which will be converted back to and EOS
211 * in the streamcombiner) */
213 gst_pad_push_event (srcpad, gst_event_new_eos ());
214 gst_event_ref (event);
215 res = gst_pad_push_event (srcpad, event);
216 STREAMS_LOCK (stream_splitter);
217 if (G_UNLIKELY (cookie != stream_splitter->cookie))
221 STREAMS_UNLOCK (stream_splitter);
222 gst_event_unref (event);
226 /* Only send to current pad */
228 STREAMS_LOCK (stream_splitter);
229 pad = stream_splitter->current;
230 STREAMS_UNLOCK (stream_splitter);
232 res = gst_pad_push_event (pad, event);
234 gst_event_unref (event);
244 gst_stream_splitter_sink_getcaps (GstPad * pad)
246 GstStreamSplitter *stream_splitter =
247 (GstStreamSplitter *) GST_PAD_PARENT (pad);
252 /* Return the combination of all downstream caps */
254 STREAMS_LOCK (stream_splitter);
257 if (G_UNLIKELY (stream_splitter->srcpads == NULL)) {
258 res = gst_caps_new_any ();
263 cookie = stream_splitter->cookie;
264 tmp = stream_splitter->srcpads;
267 GstPad *srcpad = (GstPad *) tmp->data;
269 STREAMS_UNLOCK (stream_splitter);
271 GstCaps *peercaps = gst_pad_peer_get_caps_reffed (srcpad);
273 gst_caps_merge (res, gst_caps_make_writable (peercaps));
275 res = gst_pad_peer_get_caps (srcpad);
277 STREAMS_LOCK (stream_splitter);
279 if (G_UNLIKELY (cookie != stream_splitter->cookie)) {
281 gst_caps_unref (res);
288 STREAMS_UNLOCK (stream_splitter);
293 gst_stream_splitter_sink_setcaps (GstPad * pad, GstCaps * caps)
295 GstStreamSplitter *stream_splitter =
296 (GstStreamSplitter *) GST_PAD_PARENT (pad);
301 GST_DEBUG_OBJECT (stream_splitter, "caps %" GST_PTR_FORMAT, caps);
303 /* Try on all pads, choose the one that succeeds as the current stream */
304 STREAMS_LOCK (stream_splitter);
307 if (G_UNLIKELY (stream_splitter->srcpads == NULL)) {
313 tmp = stream_splitter->srcpads;
314 cookie = stream_splitter->cookie;
317 GstPad *srcpad = (GstPad *) tmp->data;
320 STREAMS_UNLOCK (stream_splitter);
321 peercaps = gst_pad_peer_get_caps_reffed (srcpad);
323 res = gst_caps_can_intersect (caps, peercaps);
324 gst_caps_unref (peercaps);
326 STREAMS_LOCK (stream_splitter);
328 if (G_UNLIKELY (cookie != stream_splitter->cookie))
332 /* FIXME : we need to switch properly */
333 GST_DEBUG_OBJECT (srcpad, "Setting caps on this pad was successful");
334 stream_splitter->current = srcpad;
341 STREAMS_UNLOCK (stream_splitter);
346 gst_stream_splitter_src_event (GstPad * pad, GstEvent * event)
348 GstStreamSplitter *stream_splitter =
349 (GstStreamSplitter *) GST_PAD_PARENT (pad);
351 GST_DEBUG_OBJECT (pad, "%s", GST_EVENT_TYPE_NAME (event));
353 /* Forward upstream as is */
354 return gst_pad_push_event (stream_splitter->sinkpad, event);
358 gst_stream_splitter_src_query (GstPad * pad, GstQuery * query)
360 GstStreamSplitter *stream_splitter =
361 (GstStreamSplitter *) GST_PAD_PARENT (pad);
363 GST_DEBUG_OBJECT (pad, "%s", GST_QUERY_TYPE_NAME (query));
365 /* Forward upstream as is */
366 return gst_pad_peer_query (stream_splitter->sinkpad, query);
370 gst_stream_splitter_init (GstStreamSplitter * stream_splitter)
372 stream_splitter->sinkpad =
373 gst_pad_new_from_static_template (&sink_template, "sink");
374 /* FIXME : No buffer alloc for the time being, it will resort to the fallback */
375 /* gst_pad_set_bufferalloc_function (stream_splitter->sinkpad, */
376 /* gst_stream_splitter_buffer_alloc); */
377 gst_pad_set_chain_function (stream_splitter->sinkpad,
378 gst_stream_splitter_chain);
379 gst_pad_set_event_function (stream_splitter->sinkpad,
380 gst_stream_splitter_sink_event);
381 gst_pad_set_getcaps_function (stream_splitter->sinkpad,
382 gst_stream_splitter_sink_getcaps);
383 gst_pad_set_setcaps_function (stream_splitter->sinkpad,
384 gst_stream_splitter_sink_setcaps);
385 gst_element_add_pad (GST_ELEMENT (stream_splitter), stream_splitter->sinkpad);
387 stream_splitter->lock = g_mutex_new ();
391 gst_stream_splitter_request_new_pad (GstElement * element,
392 GstPadTemplate * templ, const gchar * name)
394 GstStreamSplitter *stream_splitter = (GstStreamSplitter *) element;
397 srcpad = gst_pad_new_from_static_template (&src_template, name);
398 gst_pad_set_event_function (srcpad, gst_stream_splitter_src_event);
399 gst_pad_set_query_function (srcpad, gst_stream_splitter_src_query);
401 STREAMS_LOCK (stream_splitter);
402 stream_splitter->srcpads = g_list_append (stream_splitter->srcpads, srcpad);
403 gst_pad_set_active (srcpad, TRUE);
404 gst_element_add_pad (element, srcpad);
405 stream_splitter->cookie++;
406 STREAMS_UNLOCK (stream_splitter);
412 gst_stream_splitter_release_pad (GstElement * element, GstPad * pad)
414 GstStreamSplitter *stream_splitter = (GstStreamSplitter *) element;
417 STREAMS_LOCK (stream_splitter);
418 tmp = g_list_find (stream_splitter->srcpads, pad);
420 GstPad *pad = (GstPad *) tmp->data;
422 stream_splitter->srcpads =
423 g_list_delete_link (stream_splitter->srcpads, tmp);
424 stream_splitter->cookie++;
426 if (pad == stream_splitter->current) {
427 /* Deactivate current flow */
428 GST_DEBUG_OBJECT (element, "Removed pad was the current one");
429 stream_splitter->current = NULL;
432 gst_element_remove_pad (element, pad);
434 STREAMS_UNLOCK (stream_splitter);