various: typo fixes
[platform/upstream/gstreamer.git] / gst / encoding / gststreamsplitter.c
1 /* GStreamer Stream Splitter
2  * Copyright (C) 2010 Edward Hervey <edward.hervey@collabora.co.uk>
3  *           (C) 2009 Nokia Corporation
4  *
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.
9  *
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.
14  *
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.
19  */
20
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24
25 #include "gststreamsplitter.h"
26
27 static GstStaticPadTemplate src_template =
28 GST_STATIC_PAD_TEMPLATE ("src_%d", GST_PAD_SRC, GST_PAD_REQUEST,
29     GST_STATIC_CAPS_ANY);
30
31 static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
32     GST_PAD_SINK,
33     GST_PAD_ALWAYS,
34     GST_STATIC_CAPS_ANY);
35
36 GST_DEBUG_CATEGORY_STATIC (gst_stream_splitter_debug);
37 #define GST_CAT_DEFAULT gst_stream_splitter_debug
38
39 G_DEFINE_TYPE (GstStreamSplitter, gst_stream_splitter, GST_TYPE_ELEMENT);
40
41 #define STREAMS_LOCK(obj) (g_mutex_lock(obj->lock))
42 #define STREAMS_UNLOCK(obj) (g_mutex_unlock(obj->lock))
43
44 static void gst_stream_splitter_dispose (GObject * object);
45
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,
49     GstPad * pad);
50
51 static void
52 gst_stream_splitter_class_init (GstStreamSplitterClass * klass)
53 {
54   GObjectClass *gobject_klass;
55   GstElementClass *gstelement_klass;
56
57   gobject_klass = (GObjectClass *) klass;
58   gstelement_klass = (GstElementClass *) klass;
59
60   gobject_klass->dispose = gst_stream_splitter_dispose;
61
62   GST_DEBUG_CATEGORY_INIT (gst_stream_splitter_debug, "streamsplitter", 0,
63       "Stream Splitter");
64
65   gst_element_class_add_static_pad_template (gstelement_klass,
66       &src_template);
67   gst_element_class_add_static_pad_template (gstelement_klass,
68       &sink_template);
69
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);
74
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>");
79 }
80
81 static void
82 gst_stream_splitter_dispose (GObject * object)
83 {
84   GstStreamSplitter *stream_splitter = (GstStreamSplitter *) object;
85
86   if (stream_splitter->lock) {
87     g_mutex_free (stream_splitter->lock);
88     stream_splitter->lock = NULL;
89   }
90
91   g_list_foreach (stream_splitter->pending_events, (GFunc) gst_event_unref,
92       NULL);
93   g_list_free (stream_splitter->pending_events);
94   stream_splitter->pending_events = NULL;
95
96   G_OBJECT_CLASS (gst_stream_splitter_parent_class)->dispose (object);
97 }
98
99 static GstFlowReturn
100 gst_stream_splitter_chain (GstPad * pad, GstBuffer * buf)
101 {
102   GstStreamSplitter *stream_splitter =
103       (GstStreamSplitter *) GST_PAD_PARENT (pad);
104   GstFlowReturn res;
105   GstPad *srcpad = NULL;
106
107   STREAMS_LOCK (stream_splitter);
108   if (stream_splitter->current)
109     srcpad = gst_object_ref (stream_splitter->current);
110   STREAMS_UNLOCK (stream_splitter);
111
112   if (G_UNLIKELY (srcpad == NULL))
113     goto nopad;
114
115   if (G_UNLIKELY (stream_splitter->pending_events)) {
116     GList *tmp;
117     GST_DEBUG_OBJECT (srcpad, "Pushing out pending events");
118
119     for (tmp = stream_splitter->pending_events; tmp; tmp = tmp->next) {
120       GstEvent *event = (GstEvent *) tmp->data;
121       gst_pad_push_event (srcpad, event);
122     }
123     g_list_free (stream_splitter->pending_events);
124     stream_splitter->pending_events = NULL;
125   }
126
127   /* Forward to currently activated stream */
128   res = gst_pad_push (srcpad, buf);
129   gst_object_unref (srcpad);
130
131   return res;
132
133 nopad:
134   GST_WARNING_OBJECT (stream_splitter, "No output pad was configured");
135   return GST_FLOW_ERROR;
136 }
137
138 static gboolean
139 gst_stream_splitter_sink_event (GstPad * pad, GstEvent * event)
140 {
141   GstStreamSplitter *stream_splitter =
142       (GstStreamSplitter *) GST_PAD_PARENT (pad);
143   gboolean res = TRUE;
144   gboolean toall = FALSE;
145   gboolean store = FALSE;
146   gboolean eos = FALSE;
147   gboolean flushpending = FALSE;
148
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
153    */
154
155   GST_DEBUG_OBJECT (stream_splitter, "Got event %s",
156       GST_EVENT_TYPE_NAME (event));
157
158   switch (GST_EVENT_TYPE (event)) {
159     case GST_EVENT_FLUSH_STOP:
160       flushpending = TRUE;
161       toall = TRUE;
162       break;
163     case GST_EVENT_FLUSH_START:
164       toall = TRUE;
165       break;
166     case GST_EVENT_EOS:
167       /* Replace with our custom eos event */
168       gst_event_unref (event);
169       event =
170           gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM,
171           gst_structure_empty_new ("stream-switching-eos"));
172       toall = TRUE;
173       eos = TRUE;
174       break;
175     default:
176       if (GST_EVENT_TYPE (event) & GST_EVENT_TYPE_SERIALIZED)
177         store = TRUE;
178   }
179
180   if (flushpending) {
181     g_list_foreach (stream_splitter->pending_events, (GFunc) gst_event_unref,
182         NULL);
183     g_list_free (stream_splitter->pending_events);
184     stream_splitter->pending_events = NULL;
185   }
186
187   if (store) {
188     stream_splitter->pending_events =
189         g_list_append (stream_splitter->pending_events, event);
190   } else if (toall || eos) {
191     GList *tmp;
192     guint32 cookie;
193
194     /* Send to all pads */
195     STREAMS_LOCK (stream_splitter);
196   resync:
197     if (G_UNLIKELY (stream_splitter->srcpads == NULL)) {
198       STREAMS_UNLOCK (stream_splitter);
199       /* No source pads */
200       gst_event_unref (event);
201       res = FALSE;
202       goto beach;
203     }
204     tmp = stream_splitter->srcpads;
205     cookie = stream_splitter->cookie;
206     while (tmp) {
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) */
213       if (eos)
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))
219         goto resync;
220       tmp = tmp->next;
221     }
222     STREAMS_UNLOCK (stream_splitter);
223     gst_event_unref (event);
224   } else {
225     GstPad *pad;
226
227     /* Only send to current pad */
228
229     STREAMS_LOCK (stream_splitter);
230     pad = stream_splitter->current;
231     STREAMS_UNLOCK (stream_splitter);
232     if (pad)
233       res = gst_pad_push_event (pad, event);
234     else {
235       gst_event_unref (event);
236       res = FALSE;
237     }
238   }
239
240 beach:
241   return res;
242 }
243
244 static GstCaps *
245 gst_stream_splitter_sink_getcaps (GstPad * pad)
246 {
247   GstStreamSplitter *stream_splitter =
248       (GstStreamSplitter *) GST_PAD_PARENT (pad);
249   guint32 cookie;
250   GList *tmp;
251   GstCaps *res = NULL;
252
253   /* Return the combination of all downstream caps */
254
255   STREAMS_LOCK (stream_splitter);
256
257 resync:
258   if (G_UNLIKELY (stream_splitter->srcpads == NULL)) {
259     res = gst_caps_new_any ();
260     goto beach;
261   }
262
263   res = NULL;
264   cookie = stream_splitter->cookie;
265   tmp = stream_splitter->srcpads;
266
267   while (tmp) {
268     GstPad *srcpad = (GstPad *) tmp->data;
269
270     STREAMS_UNLOCK (stream_splitter);
271     if (res) {
272       GstCaps *peercaps = gst_pad_peer_get_caps_reffed (srcpad);
273       if (peercaps)
274         gst_caps_merge (res, gst_caps_make_writable (peercaps));
275     } else {
276       res = gst_pad_peer_get_caps (srcpad);
277     }
278     STREAMS_LOCK (stream_splitter);
279
280     if (G_UNLIKELY (cookie != stream_splitter->cookie)) {
281       if (res)
282         gst_caps_unref (res);
283       goto resync;
284     }
285     tmp = tmp->next;
286   }
287
288 beach:
289   STREAMS_UNLOCK (stream_splitter);
290   return res;
291 }
292
293 static gboolean
294 gst_stream_splitter_sink_setcaps (GstPad * pad, GstCaps * caps)
295 {
296   GstStreamSplitter *stream_splitter =
297       (GstStreamSplitter *) GST_PAD_PARENT (pad);
298   guint32 cookie;
299   GList *tmp;
300   gboolean res;
301
302   GST_DEBUG_OBJECT (stream_splitter, "caps %" GST_PTR_FORMAT, caps);
303
304   /* Try on all pads, choose the one that succeeds as the current stream */
305   STREAMS_LOCK (stream_splitter);
306
307 resync:
308   if (G_UNLIKELY (stream_splitter->srcpads == NULL)) {
309     res = FALSE;
310     goto beach;
311   }
312
313   res = FALSE;
314   tmp = stream_splitter->srcpads;
315   cookie = stream_splitter->cookie;
316
317   while (tmp) {
318     GstPad *srcpad = (GstPad *) tmp->data;
319     GstCaps *peercaps;
320
321     STREAMS_UNLOCK (stream_splitter);
322     peercaps = gst_pad_peer_get_caps_reffed (srcpad);
323     if (peercaps) {
324       res = gst_caps_can_intersect (caps, peercaps);
325       gst_caps_unref (peercaps);
326     }
327     STREAMS_LOCK (stream_splitter);
328
329     if (G_UNLIKELY (cookie != stream_splitter->cookie))
330       goto resync;
331
332     if (res) {
333       /* FIXME : we need to switch properly */
334       GST_DEBUG_OBJECT (srcpad, "Setting caps on this pad was successful");
335       stream_splitter->current = srcpad;
336       goto beach;
337     }
338     tmp = tmp->next;
339   }
340
341 beach:
342   STREAMS_UNLOCK (stream_splitter);
343   return res;
344 }
345
346 static gboolean
347 gst_stream_splitter_src_event (GstPad * pad, GstEvent * event)
348 {
349   GstStreamSplitter *stream_splitter =
350       (GstStreamSplitter *) GST_PAD_PARENT (pad);
351
352   GST_DEBUG_OBJECT (pad, "%s", GST_EVENT_TYPE_NAME (event));
353
354   /* Forward upstream as is */
355   return gst_pad_push_event (stream_splitter->sinkpad, event);
356 }
357
358 static gboolean
359 gst_stream_splitter_src_query (GstPad * pad, GstQuery * query)
360 {
361   GstStreamSplitter *stream_splitter =
362       (GstStreamSplitter *) GST_PAD_PARENT (pad);
363
364   GST_DEBUG_OBJECT (pad, "%s", GST_QUERY_TYPE_NAME (query));
365
366   /* Forward upstream as is */
367   return gst_pad_peer_query (stream_splitter->sinkpad, query);
368 }
369
370 static void
371 gst_stream_splitter_init (GstStreamSplitter * stream_splitter)
372 {
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);
387
388   stream_splitter->lock = g_mutex_new ();
389 }
390
391 static GstPad *
392 gst_stream_splitter_request_new_pad (GstElement * element,
393     GstPadTemplate * templ, const gchar * name)
394 {
395   GstStreamSplitter *stream_splitter = (GstStreamSplitter *) element;
396   GstPad *srcpad;
397
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);
401
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);
408
409   return srcpad;
410 }
411
412 static void
413 gst_stream_splitter_release_pad (GstElement * element, GstPad * pad)
414 {
415   GstStreamSplitter *stream_splitter = (GstStreamSplitter *) element;
416   GList *tmp;
417
418   STREAMS_LOCK (stream_splitter);
419   tmp = g_list_find (stream_splitter->srcpads, pad);
420   if (tmp) {
421     GstPad *pad = (GstPad *) tmp->data;
422
423     stream_splitter->srcpads =
424         g_list_delete_link (stream_splitter->srcpads, tmp);
425     stream_splitter->cookie++;
426
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;
431     }
432
433     gst_element_remove_pad (element, pad);
434   }
435   STREAMS_UNLOCK (stream_splitter);
436
437   return;
438 }