2 * Copyright (C) <2007> Wim Taymans <wim@fluendo.com>
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.
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.
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.
21 * SECTION:element-rtpclient
22 * @short_description: handle media from one RTP client
23 * @see_also: rtpjitterbuffer, rtpbin, rtpsession
27 * This element handles RTP data from one client. It accepts multiple RTP streams that
28 * should be synchronized together.
30 * <title>Example pipelines</title>
33 * gst-launch -v filesrc location=sine.ogg ! oggdemux ! vorbisdec ! audioconvert ! alsasink
38 * Last reviewed on 2007-04-02 (0.10.6)
46 #include "gstrtpclient.h"
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>");
56 static GstStaticPadTemplate rtpclient_rtp_sink_template =
57 GST_STATIC_PAD_TEMPLATE ("rtp_sink_%d",
60 GST_STATIC_CAPS ("application/x-rtp")
63 static GstStaticPadTemplate rtpclient_sync_sink_template =
64 GST_STATIC_PAD_TEMPLATE ("sync_sink_%d",
67 GST_STATIC_CAPS ("application/x-rtcp")
71 static GstStaticPadTemplate rtpclient_rtp_src_template =
72 GST_STATIC_PAD_TEMPLATE ("rtp_src_%d_%d",
75 GST_STATIC_CAPS ("application/x-rtp")
78 #define GST_RTP_CLIENT_GET_PRIVATE(obj) \
79 (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GST_TYPE_RTP_CLIENT, GstRTPClientPrivate))
81 struct _GstRTPClientPrivate
85 /* all the info needed to handle the stream with SSRC */
90 /* the SSRC of this stream */
97 /* the jitterbuffer */
98 GstElement *jitterbuffer;
99 /* the payload demuxer */
101 /* the new-pad signal */
103 } GstRTPClientStream;
105 /* the PT demuxer found a new payload type */
107 new_pad (GstElement * element, GstPad * pad, GstRTPClientStream * stream)
111 /* create a new stream for SSRC.
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.
117 * +-----------------+ +---------------+
118 * | rtpjitterbuffer | | rtpptdemux |
119 * +- sink src - sink |
120 * / +-----------------+ +---------------+
123 static GstRTPClientStream *
124 create_stream (GstRTPClient * rtpclient, guint32 ssrc)
126 GstRTPClientStream *stream;
128 GstPad *srcpad, *sinkpad;
129 GstPadLinkReturn res;
131 stream = g_new0 (GstRTPClientStream, 1);
133 stream->client = rtpclient;
135 stream->jitterbuffer = gst_element_factory_make ("rtpjitterbuffer", NULL);
136 if (!stream->jitterbuffer)
137 goto no_jitterbuffer;
139 stream->ptdemux = gst_element_factory_make ("rtpptdemux", NULL);
140 if (!stream->ptdemux)
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);
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);
154 if (res != GST_PAD_LINK_OK)
157 /* add stream to list */
158 rtpclient->streams = g_list_prepend (rtpclient->streams, stream);
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);
166 gst_element_add_pad (GST_ELEMENT_CAST (rtpclient), stream->rtp_sink);
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);
179 g_warning ("could not create rtpjitterbuffer element");
184 gst_object_unref (stream->jitterbuffer);
186 g_warning ("could not create rtpptdemux element");
191 gst_bin_remove (GST_BIN_CAST (rtpclient), stream->jitterbuffer);
192 gst_bin_remove (GST_BIN_CAST (rtpclient), stream->ptdemux);
194 g_warning ("could not link jitterbuffer and rtpptdemux element");
201 free_stream (GstRTPClientStream * stream)
203 gst_object_unref (stream->jitterbuffer);
208 /* find the stream for the given SSRC, return NULL if the stream did not exist
210 static GstRTPClientStream *
211 find_stream_by_ssrc (GstRTPClient * client, guint32 ssrc)
213 GstRTPClientStream *stream;
216 for (walk = client->streams; walk; walk = g_list_next (walk)) {
217 stream = (GstRTPClientStream *) walk->data;
218 if (stream->ssrc == ssrc)
224 /* signals and args */
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);
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);
250 /*static guint gst_rtp_client_signals[LAST_SIGNAL] = { 0 }; */
252 GST_BOILERPLATE (GstRTPClient, gst_rtp_client, GstBin, GST_TYPE_BIN);
255 gst_rtp_client_base_init (gpointer klass)
257 GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
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));
266 gst_element_class_add_pad_template (element_class,
267 gst_static_pad_template_get (&rtpclient_rtp_src_template));
269 gst_element_class_set_details (element_class, &rtpclient_details);
273 gst_rtp_client_class_init (GstRTPClientClass * klass)
275 GObjectClass *gobject_class;
276 GstElementClass *gstelement_class;
278 gobject_class = (GObjectClass *) klass;
279 gstelement_class = (GstElementClass *) klass;
281 g_type_class_add_private (klass, sizeof (GstRTPClientPrivate));
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;
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);
296 gst_rtp_client_init (GstRTPClient * rtpclient, GstRTPClientClass * klass)
298 rtpclient->priv = GST_RTP_CLIENT_GET_PRIVATE (rtpclient);
302 gst_rtp_client_finalize (GObject * object)
304 GstRTPClient *rtpclient;
306 rtpclient = GST_RTP_CLIENT (object);
308 G_OBJECT_CLASS (parent_class)->finalize (object);
312 gst_rtp_client_set_property (GObject * object, guint prop_id,
313 const GValue * value, GParamSpec * pspec)
315 GstRTPClient *rtpclient;
317 rtpclient = GST_RTP_CLIENT (object);
321 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
327 gst_rtp_client_get_property (GObject * object, guint prop_id,
328 GValue * value, GParamSpec * pspec)
330 GstRTPClient *rtpclient;
332 rtpclient = GST_RTP_CLIENT (object);
336 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
341 static GstStateChangeReturn
342 gst_rtp_client_change_state (GstElement * element, GstStateChange transition)
344 GstStateChangeReturn res;
345 GstRTPClient *rtpclient;
347 rtpclient = GST_RTP_CLIENT (element);
349 switch (transition) {
350 case GST_STATE_CHANGE_NULL_TO_READY:
352 case GST_STATE_CHANGE_READY_TO_PAUSED:
354 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
360 res = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
362 switch (transition) {
363 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
365 case GST_STATE_CHANGE_PAUSED_TO_READY:
367 case GST_STATE_CHANGE_READY_TO_NULL:
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.
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.
383 gst_rtp_client_request_new_pad (GstElement * element,
384 GstPadTemplate * templ, const gchar * name)
386 GstRTPClient *rtpclient;
387 GstElementClass *klass;
388 GstPadTemplate *rtp_sink_templ, *sync_sink_templ;
390 GstRTPClientStream *stream;
393 g_return_val_if_fail (templ != NULL, NULL);
394 g_return_val_if_fail (GST_IS_RTP_CLIENT (element), NULL);
396 if (templ->direction != GST_PAD_SINK)
397 goto wrong_direction;
399 rtpclient = GST_RTP_CLIENT (element);
400 klass = GST_ELEMENT_GET_CLASS (element);
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");
406 if (templ != rtp_sink_templ && templ != sync_sink_templ)
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
413 if (name == NULL || strlen (name) < 9)
416 ssrc = atoi (&name[9]);
418 /* see if a stream with that name exists, if so we have an error. */
419 stream = find_stream_by_ssrc (rtpclient, ssrc);
423 /* ok, create new stream */
424 stream = create_stream (rtpclient, ssrc);
426 goto stream_not_found;
428 result = stream->rtp_sink;
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)
435 ssrc = atoi (&name[10]);
438 stream = find_stream_by_ssrc (rtpclient, ssrc);
440 goto stream_not_found;
443 gst_pad_new_from_static_template (&rtpclient_sync_sink_template, name);
444 gst_element_add_pad (GST_ELEMENT_CAST (rtpclient), stream->sync_sink);
446 result = stream->sync_sink;
454 g_warning ("rtpclient: request pad that is not a SINK pad");
459 g_warning ("rtpclient: this is not our template");
464 g_warning ("rtpclient: no padname was specified");
469 g_warning ("rtpclient: stream with SSRC %d already registered", ssrc);
474 g_warning ("rtpclient: stream with SSRC %d not yet registered", ssrc);
480 gst_rtp_client_release_pad (GstElement * element, GstPad * pad)