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