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., 51 Franklin St, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
25 #include "gststreamsplitter.h"
27 static GstStaticPadTemplate src_template =
28 GST_STATIC_PAD_TEMPLATE ("src_%u", 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);
45 static void gst_stream_splitter_finalize (GObject * object);
47 static gboolean gst_stream_splitter_sink_setcaps (GstPad * pad, GstCaps * caps);
49 static GstPad *gst_stream_splitter_request_new_pad (GstElement * element,
50 GstPadTemplate * templ, const gchar * name, const GstCaps * caps);
51 static void gst_stream_splitter_release_pad (GstElement * element,
55 gst_stream_splitter_class_init (GstStreamSplitterClass * klass)
57 GObjectClass *gobject_klass;
58 GstElementClass *gstelement_klass;
60 gobject_klass = (GObjectClass *) klass;
61 gstelement_klass = (GstElementClass *) klass;
63 gobject_klass->dispose = gst_stream_splitter_dispose;
64 gobject_klass->finalize = gst_stream_splitter_finalize;
66 GST_DEBUG_CATEGORY_INIT (gst_stream_splitter_debug, "streamsplitter", 0,
69 gst_element_class_add_pad_template (gstelement_klass,
70 gst_static_pad_template_get (&src_template));
71 gst_element_class_add_pad_template (gstelement_klass,
72 gst_static_pad_template_get (&sink_template));
74 gstelement_klass->request_new_pad =
75 GST_DEBUG_FUNCPTR (gst_stream_splitter_request_new_pad);
76 gstelement_klass->release_pad =
77 GST_DEBUG_FUNCPTR (gst_stream_splitter_release_pad);
79 gst_element_class_set_static_metadata (gstelement_klass,
80 "streamsplitter", "Generic",
81 "Splits streams based on their media type",
82 "Edward Hervey <edward.hervey@collabora.co.uk>");
86 gst_stream_splitter_dispose (GObject * object)
88 GstStreamSplitter *stream_splitter = (GstStreamSplitter *) object;
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_finalize (GObject * object)
101 GstStreamSplitter *stream_splitter = (GstStreamSplitter *) object;
103 g_mutex_clear (&stream_splitter->lock);
105 G_OBJECT_CLASS (gst_stream_splitter_parent_class)->finalize (object);
109 gst_stream_splitter_chain (GstPad * pad, GstObject * parent, GstBuffer * buf)
111 GstStreamSplitter *stream_splitter = (GstStreamSplitter *) parent;
113 GstPad *srcpad = NULL;
115 STREAMS_LOCK (stream_splitter);
116 if (stream_splitter->current)
117 srcpad = gst_object_ref (stream_splitter->current);
118 STREAMS_UNLOCK (stream_splitter);
120 if (G_UNLIKELY (srcpad == NULL))
123 if (G_UNLIKELY (stream_splitter->pending_events)) {
125 GST_DEBUG_OBJECT (srcpad, "Pushing out pending events");
127 for (tmp = stream_splitter->pending_events; tmp; tmp = tmp->next) {
128 GstEvent *event = (GstEvent *) tmp->data;
129 gst_pad_push_event (srcpad, event);
131 g_list_free (stream_splitter->pending_events);
132 stream_splitter->pending_events = NULL;
135 /* Forward to currently activated stream */
136 res = gst_pad_push (srcpad, buf);
137 gst_object_unref (srcpad);
142 GST_WARNING_OBJECT (stream_splitter, "No output pad was configured");
143 return GST_FLOW_ERROR;
147 gst_stream_splitter_sink_event (GstPad * pad, GstObject * parent,
150 GstStreamSplitter *stream_splitter = (GstStreamSplitter *) parent;
152 gboolean toall = FALSE;
153 gboolean store = FALSE;
154 gboolean eos = FALSE;
155 gboolean flushpending = FALSE;
157 /* FLUSH_START/STOP : forward to all
158 * EOS : transform to CUSTOM_REAL_EOS and forward to all
159 * INBAND events : store to send in chain function to selected chain
160 * OUT_OF_BAND events : send to all
163 GST_DEBUG_OBJECT (stream_splitter, "Got event %s",
164 GST_EVENT_TYPE_NAME (event));
166 switch (GST_EVENT_TYPE (event)) {
171 gst_event_parse_caps (event, &caps);
172 res = gst_stream_splitter_sink_setcaps (pad, caps);
177 case GST_EVENT_FLUSH_STOP:
181 case GST_EVENT_FLUSH_START:
185 /* Replace with our custom eos event */
186 gst_event_unref (event);
188 gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM,
189 gst_structure_new_empty ("stream-switching-eos"));
194 if (GST_EVENT_TYPE (event) & GST_EVENT_TYPE_SERIALIZED)
199 g_list_foreach (stream_splitter->pending_events, (GFunc) gst_event_unref,
201 g_list_free (stream_splitter->pending_events);
202 stream_splitter->pending_events = NULL;
206 stream_splitter->pending_events =
207 g_list_append (stream_splitter->pending_events, event);
208 } else if (toall || eos) {
212 /* Send to all pads */
213 STREAMS_LOCK (stream_splitter);
215 if (G_UNLIKELY (stream_splitter->srcpads == NULL)) {
216 STREAMS_UNLOCK (stream_splitter);
218 gst_event_unref (event);
222 tmp = stream_splitter->srcpads;
223 cookie = stream_splitter->cookie;
225 GstPad *srcpad = (GstPad *) tmp->data;
226 STREAMS_UNLOCK (stream_splitter);
227 /* In case of EOS, we first push out the real one to flush out
228 * each streams (but which will be discarded in the streamcombiner)
229 * before our custom one (which will be converted back to and EOS
230 * in the streamcombiner) */
232 gst_pad_push_event (srcpad, gst_event_new_eos ());
233 gst_event_ref (event);
234 res = gst_pad_push_event (srcpad, event);
235 STREAMS_LOCK (stream_splitter);
236 if (G_UNLIKELY (cookie != stream_splitter->cookie))
240 STREAMS_UNLOCK (stream_splitter);
241 gst_event_unref (event);
245 /* Only send to current pad */
247 STREAMS_LOCK (stream_splitter);
248 pad = stream_splitter->current;
249 STREAMS_UNLOCK (stream_splitter);
251 res = gst_pad_push_event (pad, event);
253 gst_event_unref (event);
263 gst_stream_splitter_sink_getcaps (GstPad * pad, GstCaps * filter)
265 GstStreamSplitter *stream_splitter =
266 (GstStreamSplitter *) GST_PAD_PARENT (pad);
271 /* Return the combination of all downstream caps */
273 STREAMS_LOCK (stream_splitter);
276 if (G_UNLIKELY (stream_splitter->srcpads == NULL)) {
277 res = (filter ? gst_caps_ref (filter) : gst_caps_new_any ());
282 cookie = stream_splitter->cookie;
283 tmp = stream_splitter->srcpads;
286 GstPad *srcpad = (GstPad *) tmp->data;
288 STREAMS_UNLOCK (stream_splitter);
290 GstCaps *peercaps = gst_pad_peer_query_caps (srcpad, filter);
292 res = gst_caps_merge (res, peercaps);
294 res = gst_pad_peer_query_caps (srcpad, filter);
296 STREAMS_LOCK (stream_splitter);
298 if (G_UNLIKELY (cookie != stream_splitter->cookie)) {
300 gst_caps_unref (res);
307 STREAMS_UNLOCK (stream_splitter);
312 gst_stream_splitter_sink_query (GstPad * pad, GstObject * parent,
317 switch (GST_QUERY_TYPE (query)) {
320 GstCaps *filter, *caps;
322 gst_query_parse_caps (query, &filter);
323 caps = gst_stream_splitter_sink_getcaps (pad, filter);
324 gst_query_set_caps_result (query, caps);
325 gst_caps_unref (caps);
330 res = gst_pad_query_default (pad, parent, query);
337 gst_stream_splitter_sink_setcaps (GstPad * pad, GstCaps * caps)
339 GstStreamSplitter *stream_splitter =
340 (GstStreamSplitter *) GST_PAD_PARENT (pad);
345 GST_DEBUG_OBJECT (stream_splitter, "caps %" GST_PTR_FORMAT, caps);
347 /* Try on all pads, choose the one that succeeds as the current stream */
348 STREAMS_LOCK (stream_splitter);
351 if (G_UNLIKELY (stream_splitter->srcpads == NULL)) {
357 tmp = stream_splitter->srcpads;
358 cookie = stream_splitter->cookie;
361 GstPad *srcpad = (GstPad *) tmp->data;
364 STREAMS_UNLOCK (stream_splitter);
365 peercaps = gst_pad_peer_query_caps (srcpad, NULL);
367 res = gst_caps_can_intersect (caps, peercaps);
368 gst_caps_unref (peercaps);
370 STREAMS_LOCK (stream_splitter);
372 if (G_UNLIKELY (cookie != stream_splitter->cookie))
376 /* FIXME : we need to switch properly */
377 GST_DEBUG_OBJECT (srcpad, "Setting caps on this pad was successful");
378 stream_splitter->current = srcpad;
385 STREAMS_UNLOCK (stream_splitter);
390 gst_stream_splitter_src_event (GstPad * pad, GstObject * parent,
393 GstStreamSplitter *stream_splitter = (GstStreamSplitter *) parent;
395 GST_DEBUG_OBJECT (pad, "%s", GST_EVENT_TYPE_NAME (event));
397 /* Forward upstream as is */
398 return gst_pad_push_event (stream_splitter->sinkpad, event);
402 gst_stream_splitter_src_query (GstPad * pad, GstObject * parent,
405 GstStreamSplitter *stream_splitter = (GstStreamSplitter *) parent;
407 GST_DEBUG_OBJECT (pad, "%s", GST_QUERY_TYPE_NAME (query));
409 /* Forward upstream as is */
410 return gst_pad_peer_query (stream_splitter->sinkpad, query);
414 gst_stream_splitter_init (GstStreamSplitter * stream_splitter)
416 stream_splitter->sinkpad =
417 gst_pad_new_from_static_template (&sink_template, "sink");
418 gst_pad_set_chain_function (stream_splitter->sinkpad,
419 gst_stream_splitter_chain);
420 gst_pad_set_event_function (stream_splitter->sinkpad,
421 gst_stream_splitter_sink_event);
422 gst_pad_set_query_function (stream_splitter->sinkpad,
423 gst_stream_splitter_sink_query);
424 gst_element_add_pad (GST_ELEMENT (stream_splitter), stream_splitter->sinkpad);
426 g_mutex_init (&stream_splitter->lock);
430 gst_stream_splitter_request_new_pad (GstElement * element,
431 GstPadTemplate * templ, const gchar * name, const GstCaps * caps)
433 GstStreamSplitter *stream_splitter = (GstStreamSplitter *) element;
436 srcpad = gst_pad_new_from_static_template (&src_template, name);
437 gst_pad_set_event_function (srcpad, gst_stream_splitter_src_event);
438 gst_pad_set_query_function (srcpad, gst_stream_splitter_src_query);
440 STREAMS_LOCK (stream_splitter);
441 stream_splitter->srcpads = g_list_append (stream_splitter->srcpads, srcpad);
442 gst_pad_set_active (srcpad, TRUE);
443 gst_element_add_pad (element, srcpad);
444 stream_splitter->cookie++;
445 STREAMS_UNLOCK (stream_splitter);
451 gst_stream_splitter_release_pad (GstElement * element, GstPad * pad)
453 GstStreamSplitter *stream_splitter = (GstStreamSplitter *) element;
456 STREAMS_LOCK (stream_splitter);
457 tmp = g_list_find (stream_splitter->srcpads, pad);
459 GstPad *pad = (GstPad *) tmp->data;
461 stream_splitter->srcpads =
462 g_list_delete_link (stream_splitter->srcpads, tmp);
463 stream_splitter->cookie++;
465 if (pad == stream_splitter->current) {
466 /* Deactivate current flow */
467 GST_DEBUG_OBJECT (element, "Removed pad was the current one");
468 stream_splitter->current = NULL;
471 gst_element_remove_pad (element, pad);
473 STREAMS_UNLOCK (stream_splitter);