Tizen 2.1 base
[platform/core/multimedia/gst-rtsp-server-wfd.git] / src / rtsp-funnel.c
1 /* GStreamer
2  * Copyright (C) 2008 Wim Taymans <wim.taymans at gmail.com>
3
4 * Copyright (c) 2012, 2013 Samsung Electronics Co., Ltd.
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19  * Boston, MA 02111-1307, USA.
20  *
21
22 * * Modifications by Samsung Electronics Co., Ltd.
23
24 * 1. Applied Miracast WFD Server function
25 */
26
27 /**
28  * SECTION:element-rtspfunnel
29  * @short_description: N-to-1 simple funnel
30  *
31  * Takes packets from various input sinks into one output source
32  */
33
34 #ifdef HAVE_CONFIG_H
35 #  include "config.h"
36 #endif
37
38 #include "rtsp-funnel.h"
39
40 GST_DEBUG_CATEGORY_STATIC (rtsp_funnel_debug);
41 #define GST_CAT_DEFAULT rtsp_funnel_debug
42
43 static const GstElementDetails rtsp_funnel_details =
44 GST_ELEMENT_DETAILS ("Farsight Funnel pipe fitting",
45     "Generic",
46     "N-to-1 pipe fitting",
47     "Olivier Crete <olivier.crete@collabora.co.uk>");
48
49 static GstStaticPadTemplate funnel_sink_template =
50 GST_STATIC_PAD_TEMPLATE ("sink%d",
51     GST_PAD_SINK,
52     GST_PAD_REQUEST,
53     GST_STATIC_CAPS_ANY);
54
55 static GstStaticPadTemplate funnel_src_template =
56 GST_STATIC_PAD_TEMPLATE ("src",
57     GST_PAD_SRC,
58     GST_PAD_ALWAYS,
59     GST_STATIC_CAPS_ANY);
60
61
62 static void
63 _do_init (GType type)
64 {
65   GST_DEBUG_CATEGORY_INIT (rtsp_funnel_debug, "rtspfunnel", 0,
66       "rtsp funnel element");
67 }
68
69 GST_BOILERPLATE_FULL (RTSPFunnel, rtsp_funnel, GstElement, GST_TYPE_ELEMENT,
70     _do_init);
71
72
73
74 static GstStateChangeReturn rtsp_funnel_change_state (GstElement * element,
75     GstStateChange transition);
76
77 static GstPad *rtsp_funnel_request_new_pad (GstElement * element,
78     GstPadTemplate * templ, const gchar * name);
79 static void rtsp_funnel_release_pad (GstElement * element, GstPad * pad);
80
81 static GstFlowReturn rtsp_funnel_chain (GstPad * pad, GstBuffer * buffer);
82 static gboolean rtsp_funnel_event (GstPad * pad, GstEvent * event);
83 static gboolean rtsp_funnel_src_event (GstPad * pad, GstEvent * event);
84 static GstCaps *rtsp_funnel_getcaps (GstPad * pad);
85
86
87 typedef struct
88 {
89   GstSegment segment;
90 } RTSPFunnelPadPrivate;
91
92 static void
93 rtsp_funnel_base_init (gpointer g_class)
94 {
95   GstElementClass *gstelement_class = GST_ELEMENT_CLASS (g_class);
96
97   gst_element_class_set_details (gstelement_class, &rtsp_funnel_details);
98
99   gst_element_class_add_pad_template (gstelement_class,
100       gst_static_pad_template_get (&funnel_sink_template));
101   gst_element_class_add_pad_template (gstelement_class,
102       gst_static_pad_template_get (&funnel_src_template));
103 }
104
105
106 static void
107 rtsp_funnel_dispose (GObject * object)
108 {
109   GList *item;
110
111 restart:
112   for (item = GST_ELEMENT_PADS (object); item; item = g_list_next (item)) {
113     GstPad *pad = GST_PAD (item->data);
114
115     if (GST_PAD_IS_SINK (pad)) {
116       gst_element_release_request_pad (GST_ELEMENT (object), pad);
117       goto restart;
118     }
119   }
120
121   G_OBJECT_CLASS (parent_class)->dispose (object);
122 }
123
124 static void
125 rtsp_funnel_class_init (RTSPFunnelClass * klass)
126 {
127   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
128   GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
129
130   gobject_class->dispose = GST_DEBUG_FUNCPTR (rtsp_funnel_dispose);
131
132   gstelement_class->request_new_pad =
133       GST_DEBUG_FUNCPTR (rtsp_funnel_request_new_pad);
134   gstelement_class->release_pad = GST_DEBUG_FUNCPTR (rtsp_funnel_release_pad);
135   gstelement_class->change_state = GST_DEBUG_FUNCPTR (rtsp_funnel_change_state);
136 }
137
138 static void
139 rtsp_funnel_init (RTSPFunnel * funnel, RTSPFunnelClass * g_class)
140 {
141   funnel->srcpad = gst_pad_new_from_static_template (&funnel_src_template,
142       "src");
143   gst_pad_set_event_function (funnel->srcpad, rtsp_funnel_src_event);
144   gst_pad_use_fixed_caps (funnel->srcpad);
145   gst_element_add_pad (GST_ELEMENT (funnel), funnel->srcpad);
146 }
147
148
149 static GstPad *
150 rtsp_funnel_request_new_pad (GstElement * element, GstPadTemplate * templ,
151     const gchar * name)
152 {
153   GstPad *sinkpad;
154   RTSPFunnelPadPrivate *priv = g_slice_alloc0 (sizeof (RTSPFunnelPadPrivate));
155
156   GST_DEBUG_OBJECT (element, "requesting pad");
157
158   sinkpad = gst_pad_new_from_template (templ, name);
159
160   gst_pad_set_chain_function (sinkpad, GST_DEBUG_FUNCPTR (rtsp_funnel_chain));
161   gst_pad_set_event_function (sinkpad, GST_DEBUG_FUNCPTR (rtsp_funnel_event));
162   gst_pad_set_getcaps_function (sinkpad,
163       GST_DEBUG_FUNCPTR (rtsp_funnel_getcaps));
164
165   gst_segment_init (&priv->segment, GST_FORMAT_UNDEFINED);
166   gst_pad_set_element_private (sinkpad, priv);
167
168   gst_pad_set_active (sinkpad, TRUE);
169
170   gst_element_add_pad (element, sinkpad);
171
172   return sinkpad;
173 }
174
175 static void
176 rtsp_funnel_release_pad (GstElement * element, GstPad * pad)
177 {
178   RTSPFunnel *funnel = RTSP_FUNNEL (element);
179   RTSPFunnelPadPrivate *priv = gst_pad_get_element_private (pad);
180
181   GST_DEBUG_OBJECT (funnel, "releasing pad");
182
183   gst_pad_set_active (pad, FALSE);
184
185   if (priv)
186     g_slice_free1 (sizeof (RTSPFunnelPadPrivate), priv);
187
188   gst_element_remove_pad (GST_ELEMENT_CAST (funnel), pad);
189 }
190
191 static GstCaps *
192 rtsp_funnel_getcaps (GstPad * pad)
193 {
194   RTSPFunnel *funnel = RTSP_FUNNEL (gst_pad_get_parent (pad));
195   GstCaps *caps;
196
197   caps = gst_pad_peer_get_caps (funnel->srcpad);
198   if (caps == NULL)
199     caps = gst_caps_copy (gst_pad_get_pad_template_caps (pad));
200
201   gst_object_unref (funnel);
202
203   return caps;
204 }
205
206 static GstFlowReturn
207 rtsp_funnel_chain (GstPad * pad, GstBuffer * buffer)
208 {
209   GstFlowReturn res;
210   RTSPFunnel *funnel = RTSP_FUNNEL (gst_pad_get_parent (pad));
211   RTSPFunnelPadPrivate *priv = gst_pad_get_element_private (pad);
212   GstEvent *event = NULL;
213   GstClockTime newts;
214   GstCaps *padcaps;
215
216   GST_DEBUG_OBJECT (funnel, "received buffer %p", buffer);
217
218   GST_OBJECT_LOCK (funnel);
219   if (priv->segment.format == GST_FORMAT_UNDEFINED) {
220     GST_WARNING_OBJECT (funnel, "Got buffer without segment,"
221         " setting segment [0,inf[");
222     gst_segment_set_newsegment_full (&priv->segment, FALSE, 1.0, 1.0,
223         GST_FORMAT_TIME, 0, -1, 0);
224   }
225
226   if (GST_CLOCK_TIME_IS_VALID (GST_BUFFER_TIMESTAMP (buffer)))
227     gst_segment_set_last_stop (&priv->segment, priv->segment.format,
228         GST_BUFFER_TIMESTAMP (buffer));
229
230   newts = gst_segment_to_running_time (&priv->segment,
231       priv->segment.format, GST_BUFFER_TIMESTAMP (buffer));
232   if (newts != GST_BUFFER_TIMESTAMP (buffer)) {
233     buffer = gst_buffer_make_metadata_writable (buffer);
234     GST_BUFFER_TIMESTAMP (buffer) = newts;
235   }
236
237   if (!funnel->has_segment) {
238     event = gst_event_new_new_segment_full (FALSE, 1.0, 1.0, GST_FORMAT_TIME,
239         0, -1, 0);
240     funnel->has_segment = TRUE;
241   }
242   GST_OBJECT_UNLOCK (funnel);
243
244   if (event) {
245     if (!gst_pad_push_event (funnel->srcpad, event)) {
246       GST_WARNING_OBJECT (funnel, "Could not push out newsegment event");
247       res = GST_FLOW_ERROR;
248       goto out;
249     }
250   }
251
252
253   GST_OBJECT_LOCK (pad);
254   padcaps = GST_PAD_CAPS (funnel->srcpad);
255   GST_OBJECT_UNLOCK (pad);
256
257   if (GST_BUFFER_CAPS (buffer) && GST_BUFFER_CAPS (buffer) != padcaps) {
258     if (!gst_pad_set_caps (funnel->srcpad, GST_BUFFER_CAPS (buffer))) {
259       res = GST_FLOW_NOT_NEGOTIATED;
260       goto out;
261     }
262   }
263
264   res = gst_pad_push (funnel->srcpad, buffer);
265
266   GST_LOG_OBJECT (funnel, "handled buffer %s", gst_flow_get_name (res));
267
268 out:
269   gst_object_unref (funnel);
270
271   return res;
272 }
273
274 static gboolean
275 rtsp_funnel_event (GstPad * pad, GstEvent * event)
276 {
277   RTSPFunnel *funnel = RTSP_FUNNEL (gst_pad_get_parent (pad));
278   RTSPFunnelPadPrivate *priv = gst_pad_get_element_private (pad);
279   gboolean forward = TRUE;
280   gboolean res = TRUE;
281
282   switch (GST_EVENT_TYPE (event)) {
283     case GST_EVENT_NEWSEGMENT:
284     {
285       gboolean update;
286       gdouble rate, arate;
287       GstFormat format;
288       gint64 start;
289       gint64 stop;
290       gint64 time;
291
292       gst_event_parse_new_segment_full (event, &update, &rate, &arate,
293           &format, &start, &stop, &time);
294
295
296       GST_OBJECT_LOCK (funnel);
297       gst_segment_set_newsegment_full (&priv->segment, update, rate, arate,
298           format, start, stop, time);
299       GST_OBJECT_UNLOCK (funnel);
300
301       forward = FALSE;
302       gst_event_unref (event);
303     }
304       break;
305     case GST_EVENT_FLUSH_STOP:
306     {
307       GST_OBJECT_LOCK (funnel);
308       gst_segment_init (&priv->segment, GST_FORMAT_UNDEFINED);
309       GST_OBJECT_UNLOCK (funnel);
310     }
311       break;
312     default:
313       break;
314   }
315
316
317   if (forward)
318     res = gst_pad_push_event (funnel->srcpad, event);
319
320   gst_object_unref (funnel);
321
322   return res;
323 }
324
325 static gboolean
326 rtsp_funnel_src_event (GstPad * pad, GstEvent * event)
327 {
328   GstElement *funnel;
329   GstIterator *iter;
330   GstPad *sinkpad;
331   gboolean result = FALSE;
332   gboolean done = FALSE;
333
334   funnel = gst_pad_get_parent_element (pad);
335   g_return_val_if_fail (funnel != NULL, FALSE);
336
337   iter = gst_element_iterate_sink_pads (funnel);
338
339   while (!done) {
340     switch (gst_iterator_next (iter, (gpointer) & sinkpad)) {
341       case GST_ITERATOR_OK:
342         gst_event_ref (event);
343         result |= gst_pad_push_event (sinkpad, event);
344         gst_object_unref (sinkpad);
345         break;
346       case GST_ITERATOR_RESYNC:
347         gst_iterator_resync (iter);
348         result = FALSE;
349         break;
350       case GST_ITERATOR_ERROR:
351         GST_WARNING_OBJECT (funnel, "Error iterating sinkpads");
352       case GST_ITERATOR_DONE:
353         done = TRUE;
354         break;
355     }
356   }
357   gst_iterator_free (iter);
358   gst_object_unref (funnel);
359   gst_event_unref (event);
360
361   return result;
362 }
363
364 static void
365 reset_pad (gpointer data, gpointer user_data)
366 {
367   GstPad *pad = data;
368   RTSPFunnelPadPrivate *priv = gst_pad_get_element_private (pad);
369
370   GST_OBJECT_LOCK (pad);
371   gst_segment_init (&priv->segment, GST_FORMAT_UNDEFINED);
372   GST_OBJECT_UNLOCK (pad);
373   gst_object_unref (pad);
374 }
375
376 static GstStateChangeReturn
377 rtsp_funnel_change_state (GstElement * element, GstStateChange transition)
378 {
379   RTSPFunnel *funnel = RTSP_FUNNEL (element);
380   GstStateChangeReturn ret;
381
382   switch (transition) {
383     case GST_STATE_CHANGE_READY_TO_PAUSED:
384     {
385       GstIterator *iter = gst_element_iterate_sink_pads (element);
386       GstIteratorResult res;
387
388       do {
389         res = gst_iterator_foreach (iter, reset_pad, NULL);
390       } while (res == GST_ITERATOR_RESYNC);
391
392       gst_iterator_free (iter);
393
394       if (res == GST_ITERATOR_ERROR)
395         return GST_STATE_CHANGE_FAILURE;
396
397       GST_OBJECT_LOCK (funnel);
398       funnel->has_segment = FALSE;
399       GST_OBJECT_UNLOCK (funnel);
400     }
401       break;
402     default:
403       break;
404   }
405
406   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
407
408   return ret;
409 }