2984d3f9f21e6e92f99f3687eb2838461a65908d
[platform/upstream/gst-plugins-good.git] / gst / rtpmanager / gstrtpclient.c
1 /* GStreamer
2  * Copyright (C) <2007> Wim Taymans <wim@fluendo.com>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 /**
21  * SECTION:element-rtpclient
22  * @short_description: handle media from one RTP client
23  * @see_also: rtpjitterbuffer, rtpbin, rtpsession
24  *
25  * <refsect2>
26  * <para>
27  * This element handles RTP data from one client. It accepts multiple RTP streams that
28  * should be synchronized together.
29  * </para>
30  * <title>Example pipelines</title>
31  * <para>
32  * <programlisting>
33  * gst-launch -v filesrc location=sine.ogg ! oggdemux ! vorbisdec ! audioconvert ! alsasink
34  * </programlisting>
35  * </para>
36  * </refsect2>
37  *
38  * Last reviewed on 2007-04-02 (0.10.6)
39  */
40
41 #ifdef HAVE_CONFIG_H
42 #include "config.h"
43 #endif
44 #include <string.h>
45
46 #include "gstrtpclient.h"
47
48 /* elementfactory information */
49 static const GstElementDetails rtpclient_details =
50 GST_ELEMENT_DETAILS ("RTP Client",
51     "Filter/Editor/Video",
52     "Implement an RTP client",
53     "Wim Taymans <wim@fluendo.com>");
54
55 /* sink pads */
56 static GstStaticPadTemplate rtpclient_rtp_sink_template =
57 GST_STATIC_PAD_TEMPLATE ("rtp_sink_%d",
58     GST_PAD_SINK,
59     GST_PAD_REQUEST,
60     GST_STATIC_CAPS ("application/x-rtp")
61     );
62
63 static GstStaticPadTemplate rtpclient_sync_sink_template =
64 GST_STATIC_PAD_TEMPLATE ("sync_sink_%d",
65     GST_PAD_SINK,
66     GST_PAD_REQUEST,
67     GST_STATIC_CAPS ("application/x-rtcp")
68     );
69
70 /* src pads */
71 static GstStaticPadTemplate rtpclient_rtp_src_template =
72 GST_STATIC_PAD_TEMPLATE ("rtp_src_%d_%d",
73     GST_PAD_SRC,
74     GST_PAD_SOMETIMES,
75     GST_STATIC_CAPS ("application/x-rtp")
76     );
77
78 #define GST_RTP_CLIENT_GET_PRIVATE(obj)  \
79    (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GST_TYPE_RTP_CLIENT, GstRTPClientPrivate))
80
81 struct _GstRTPClientPrivate
82 {
83 };
84
85 /* all the info needed to handle the stream with SSRC */
86 typedef struct
87 {
88   GstRTPClient *client;
89
90   /* the SSRC of this stream */
91   guint32 ssrc;
92
93   /* RTP and RTCP in */
94   GstPad *rtp_sink;
95   GstPad *sync_sink;
96
97   /* the jitterbuffer */
98   GstElement *jitterbuffer;
99   /* the payload demuxer */
100   GstElement *ptdemux;
101   /* the new-pad signal */
102   gulong new_pad_sig;
103 } GstRTPClientStream;
104
105 /* the PT demuxer found a new payload type */
106 static void
107 new_pad (GstElement * element, GstPad * pad, GstRTPClientStream * stream)
108 {
109 }
110
111 /* create a new stream for SSRC.
112  *
113  * We create a jitterbuffer and an payload demuxer for the SSRC. The sinkpad of
114  * the jitterbuffer is ghosted to the bin. We connect a pad-added signal to
115  * rtpptdemux so that we can ghost the payload pads outside.
116  *
117  *       +-----------------+     +---------------+
118  *       | rtpjitterbuffer |     |  rtpptdemux   |
119  *   +- sink              src - sink             |
120  *  /    +-----------------+     +---------------+
121  *
122  */
123 static GstRTPClientStream *
124 create_stream (GstRTPClient * rtpclient, guint32 ssrc)
125 {
126   GstRTPClientStream *stream;
127   gchar *name;
128   GstPad *srcpad, *sinkpad;
129   GstPadLinkReturn res;
130
131   stream = g_new0 (GstRTPClientStream, 1);
132   stream->ssrc = ssrc;
133   stream->client = rtpclient;
134
135   stream->jitterbuffer = gst_element_factory_make ("rtpjitterbuffer", NULL);
136   if (!stream->jitterbuffer)
137     goto no_jitterbuffer;
138
139   stream->ptdemux = gst_element_factory_make ("rtpptdemux", NULL);
140   if (!stream->ptdemux)
141     goto no_ptdemux;
142
143   /* add elements to bin */
144   gst_bin_add (GST_BIN_CAST (rtpclient), stream->jitterbuffer);
145   gst_bin_add (GST_BIN_CAST (rtpclient), stream->ptdemux);
146
147   /* link jitterbuffer and PT demuxer */
148   srcpad = gst_element_get_pad (stream->jitterbuffer, "src");
149   sinkpad = gst_element_get_pad (stream->ptdemux, "sink");
150   res = gst_pad_link (srcpad, sinkpad);
151   gst_object_unref (srcpad);
152   gst_object_unref (sinkpad);
153
154   if (res != GST_PAD_LINK_OK)
155     goto could_not_link;
156
157   /* add stream to list */
158   rtpclient->streams = g_list_prepend (rtpclient->streams, stream);
159
160   /* ghost sinkpad */
161   name = g_strdup_printf ("rtp_sink_%d", ssrc);
162   sinkpad = gst_element_get_pad (stream->jitterbuffer, "sink");
163   stream->rtp_sink = gst_ghost_pad_new (name, sinkpad);
164   gst_object_unref (sinkpad);
165   g_free (name);
166   gst_element_add_pad (GST_ELEMENT_CAST (rtpclient), stream->rtp_sink);
167
168   /* add signal to ptdemuxer */
169   stream->new_pad_sig =
170       g_signal_connect (G_OBJECT (stream->ptdemux), "pad-added",
171       G_CALLBACK (new_pad), stream);
172
173   return stream;
174
175   /* ERRORS */
176 no_jitterbuffer:
177   {
178     g_free (stream);
179     g_warning ("could not create rtpjitterbuffer element");
180     return NULL;
181   }
182 no_ptdemux:
183   {
184     gst_object_unref (stream->jitterbuffer);
185     g_free (stream);
186     g_warning ("could not create rtpptdemux element");
187     return NULL;
188   }
189 could_not_link:
190   {
191     gst_bin_remove (GST_BIN_CAST (rtpclient), stream->jitterbuffer);
192     gst_bin_remove (GST_BIN_CAST (rtpclient), stream->ptdemux);
193     g_free (stream);
194     g_warning ("could not link jitterbuffer and rtpptdemux element");
195     return NULL;
196   }
197 }
198
199 #if 0
200 static void
201 free_stream (GstRTPClientStream * stream)
202 {
203   gst_object_unref (stream->jitterbuffer);
204   g_free (stream);
205 }
206 #endif
207
208 /* find the stream for the given SSRC, return NULL if the stream did not exist
209  */
210 static GstRTPClientStream *
211 find_stream_by_ssrc (GstRTPClient * client, guint32 ssrc)
212 {
213   GstRTPClientStream *stream;
214   GList *walk;
215
216   for (walk = client->streams; walk; walk = g_list_next (walk)) {
217     stream = (GstRTPClientStream *) walk->data;
218     if (stream->ssrc == ssrc)
219       return stream;
220   }
221   return NULL;
222 }
223
224 /* signals and args */
225 enum
226 {
227   /* FILL ME */
228   LAST_SIGNAL
229 };
230
231 enum
232 {
233   PROP_0
234 };
235
236 /* GObject vmethods */
237 static void gst_rtp_client_finalize (GObject * object);
238 static void gst_rtp_client_set_property (GObject * object, guint prop_id,
239     const GValue * value, GParamSpec * pspec);
240 static void gst_rtp_client_get_property (GObject * object, guint prop_id,
241     GValue * value, GParamSpec * pspec);
242
243 /* GstElement vmethods */
244 static GstStateChangeReturn gst_rtp_client_change_state (GstElement * element,
245     GstStateChange transition);
246 static GstPad *gst_rtp_client_request_new_pad (GstElement * element,
247     GstPadTemplate * templ, const gchar * name);
248 static void gst_rtp_client_release_pad (GstElement * element, GstPad * pad);
249
250 /*static guint gst_rtp_client_signals[LAST_SIGNAL] = { 0 }; */
251
252 GST_BOILERPLATE (GstRTPClient, gst_rtp_client, GstBin, GST_TYPE_BIN);
253
254 static void
255 gst_rtp_client_base_init (gpointer klass)
256 {
257   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
258
259   /* sink pads */
260   gst_element_class_add_pad_template (element_class,
261       gst_static_pad_template_get (&rtpclient_rtp_sink_template));
262   gst_element_class_add_pad_template (element_class,
263       gst_static_pad_template_get (&rtpclient_sync_sink_template));
264
265   /* src pads */
266   gst_element_class_add_pad_template (element_class,
267       gst_static_pad_template_get (&rtpclient_rtp_src_template));
268
269   gst_element_class_set_details (element_class, &rtpclient_details);
270 }
271
272 static void
273 gst_rtp_client_class_init (GstRTPClientClass * klass)
274 {
275   GObjectClass *gobject_class;
276   GstElementClass *gstelement_class;
277
278   gobject_class = (GObjectClass *) klass;
279   gstelement_class = (GstElementClass *) klass;
280
281   g_type_class_add_private (klass, sizeof (GstRTPClientPrivate));
282
283   gobject_class->finalize = gst_rtp_client_finalize;
284   gobject_class->set_property = gst_rtp_client_set_property;
285   gobject_class->get_property = gst_rtp_client_get_property;
286
287   gstelement_class->change_state =
288       GST_DEBUG_FUNCPTR (gst_rtp_client_change_state);
289   gstelement_class->request_new_pad =
290       GST_DEBUG_FUNCPTR (gst_rtp_client_request_new_pad);
291   gstelement_class->release_pad =
292       GST_DEBUG_FUNCPTR (gst_rtp_client_release_pad);
293 }
294
295 static void
296 gst_rtp_client_init (GstRTPClient * rtpclient, GstRTPClientClass * klass)
297 {
298   rtpclient->priv = GST_RTP_CLIENT_GET_PRIVATE (rtpclient);
299 }
300
301 static void
302 gst_rtp_client_finalize (GObject * object)
303 {
304   GstRTPClient *rtpclient;
305
306   rtpclient = GST_RTP_CLIENT (object);
307
308   G_OBJECT_CLASS (parent_class)->finalize (object);
309 }
310
311 static void
312 gst_rtp_client_set_property (GObject * object, guint prop_id,
313     const GValue * value, GParamSpec * pspec)
314 {
315   GstRTPClient *rtpclient;
316
317   rtpclient = GST_RTP_CLIENT (object);
318
319   switch (prop_id) {
320     default:
321       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
322       break;
323   }
324 }
325
326 static void
327 gst_rtp_client_get_property (GObject * object, guint prop_id,
328     GValue * value, GParamSpec * pspec)
329 {
330   GstRTPClient *rtpclient;
331
332   rtpclient = GST_RTP_CLIENT (object);
333
334   switch (prop_id) {
335     default:
336       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
337       break;
338   }
339 }
340
341 static GstStateChangeReturn
342 gst_rtp_client_change_state (GstElement * element, GstStateChange transition)
343 {
344   GstStateChangeReturn res;
345   GstRTPClient *rtpclient;
346
347   rtpclient = GST_RTP_CLIENT (element);
348
349   switch (transition) {
350     case GST_STATE_CHANGE_NULL_TO_READY:
351       break;
352     case GST_STATE_CHANGE_READY_TO_PAUSED:
353       break;
354     case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
355       break;
356     default:
357       break;
358   }
359
360   res = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
361
362   switch (transition) {
363     case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
364       break;
365     case GST_STATE_CHANGE_PAUSED_TO_READY:
366       break;
367     case GST_STATE_CHANGE_READY_TO_NULL:
368       break;
369     default:
370       break;
371   }
372   return res;
373 }
374
375 /* We have 2 request pads (rtp_sink_%d and sync_sink_%d), the %d is assumed to
376  * be the SSRC of the stream.
377  *
378  * We require that the rtp pad is requested first for a particular SSRC, then
379  * (optionaly) the sync pad can be requested. If no sync pad is requested, no
380  * sync information can be exchanged for this stream.
381  */
382 static GstPad *
383 gst_rtp_client_request_new_pad (GstElement * element,
384     GstPadTemplate * templ, const gchar * name)
385 {
386   GstRTPClient *rtpclient;
387   GstElementClass *klass;
388   GstPadTemplate *rtp_sink_templ, *sync_sink_templ;
389   guint32 ssrc;
390   GstRTPClientStream *stream;
391   GstPad *result;
392
393   g_return_val_if_fail (templ != NULL, NULL);
394   g_return_val_if_fail (GST_IS_RTP_CLIENT (element), NULL);
395
396   if (templ->direction != GST_PAD_SINK)
397     goto wrong_direction;
398
399   rtpclient = GST_RTP_CLIENT (element);
400   klass = GST_ELEMENT_GET_CLASS (element);
401
402   /* figure out the template */
403   rtp_sink_templ = gst_element_class_get_pad_template (klass, "rtp_sink_%d");
404   sync_sink_templ = gst_element_class_get_pad_template (klass, "sync_sink_%d");
405
406   if (templ != rtp_sink_templ && templ != sync_sink_templ)
407     goto wrong_template;
408
409   if (templ == rtp_sink_templ) {
410     /* create new rtp sink pad. If a stream with the pad number already exists
411      * we have an error, else we create the sinkpad, add a jitterbuffer and
412      * ptdemuxer. */
413     if (name == NULL || strlen (name) < 9)
414       goto no_name;
415
416     ssrc = atoi (&name[9]);
417
418     /* see if a stream with that name exists, if so we have an error. */
419     stream = find_stream_by_ssrc (rtpclient, ssrc);
420     if (stream != NULL)
421       goto stream_exists;
422
423     /* ok, create new stream */
424     stream = create_stream (rtpclient, ssrc);
425     if (stream == NULL)
426       goto stream_not_found;
427
428     result = stream->rtp_sink;
429   } else {
430     /* create new rtp sink pad. We can only do this if the RTP pad was
431      * requested before, meaning the session with the padnumber must exist. */
432     if (name == NULL || strlen (name) < 10)
433       goto no_name;
434
435     ssrc = atoi (&name[10]);
436
437     /* find stream */
438     stream = find_stream_by_ssrc (rtpclient, ssrc);
439     if (stream == NULL)
440       goto stream_not_found;
441
442     stream->sync_sink =
443         gst_pad_new_from_static_template (&rtpclient_sync_sink_template, name);
444     gst_element_add_pad (GST_ELEMENT_CAST (rtpclient), stream->sync_sink);
445
446     result = stream->sync_sink;
447   }
448
449   return result;
450
451   /* ERRORS */
452 wrong_direction:
453   {
454     g_warning ("rtpclient: request pad that is not a SINK pad");
455     return NULL;
456   }
457 wrong_template:
458   {
459     g_warning ("rtpclient: this is not our template");
460     return NULL;
461   }
462 no_name:
463   {
464     g_warning ("rtpclient: no padname was specified");
465     return NULL;
466   }
467 stream_exists:
468   {
469     g_warning ("rtpclient: stream with SSRC %d already registered", ssrc);
470     return NULL;
471   }
472 stream_not_found:
473   {
474     g_warning ("rtpclient: stream with SSRC %d not yet registered", ssrc);
475     return NULL;
476   }
477 }
478
479 static void
480 gst_rtp_client_release_pad (GstElement * element, GstPad * pad)
481 {
482 }