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.
31 * Normally the SSRCs that map to the same CNAME (as given in the RTCP SDES messages)
32 * should be synchronized.
34 * <title>Example pipelines</title>
41 * Last reviewed on 2007-04-02 (0.10.6)
49 #include "gstrtpclient.h"
51 /* elementfactory information */
52 static const GstElementDetails rtpclient_details =
53 GST_ELEMENT_DETAILS ("RTP Client",
54 "Filter/Editor/Video",
55 "Implement an RTP client",
56 "Wim Taymans <wim@fluendo.com>");
59 static GstStaticPadTemplate rtpclient_rtp_sink_template =
60 GST_STATIC_PAD_TEMPLATE ("rtp_sink_%d",
63 GST_STATIC_CAPS ("application/x-rtp")
66 static GstStaticPadTemplate rtpclient_sync_sink_template =
67 GST_STATIC_PAD_TEMPLATE ("sync_sink_%d",
70 GST_STATIC_CAPS ("application/x-rtcp")
74 static GstStaticPadTemplate rtpclient_rtp_src_template =
75 GST_STATIC_PAD_TEMPLATE ("rtp_src_%d_%d",
78 GST_STATIC_CAPS ("application/x-rtp")
81 #define GST_RTP_CLIENT_GET_PRIVATE(obj) \
82 (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GST_TYPE_RTP_CLIENT, GstRTPClientPrivate))
84 struct _GstRTPClientPrivate
88 /* all the info needed to handle the stream with SSRC */
93 /* the SSRC of this stream */
100 /* the jitterbuffer */
101 GstElement *jitterbuffer;
102 /* the payload demuxer */
104 /* the new-pad signal */
106 } GstRTPClientStream;
108 /* the PT demuxer found a new payload type */
110 new_pad (GstElement * element, GstPad * pad, GstRTPClientStream * stream)
114 /* create a new stream for SSRC.
116 * We create a jitterbuffer and an payload demuxer for the SSRC. The sinkpad of
117 * the jitterbuffer is ghosted to the bin. We connect a pad-added signal to
118 * rtpptdemux so that we can ghost the payload pads outside.
120 * +-----------------+ +---------------+
121 * | rtpjitterbuffer | | rtpptdemux |
122 * +- sink src - sink |
123 * / +-----------------+ +---------------+
126 static GstRTPClientStream *
127 create_stream (GstRTPClient * rtpclient, guint32 ssrc)
129 GstRTPClientStream *stream;
131 GstPad *srcpad, *sinkpad;
132 GstPadLinkReturn res;
134 stream = g_new0 (GstRTPClientStream, 1);
136 stream->client = rtpclient;
138 stream->jitterbuffer = gst_element_factory_make ("rtpjitterbuffer", NULL);
139 if (!stream->jitterbuffer)
140 goto no_jitterbuffer;
142 stream->ptdemux = gst_element_factory_make ("rtpptdemux", NULL);
143 if (!stream->ptdemux)
146 /* add elements to bin */
147 gst_bin_add (GST_BIN_CAST (rtpclient), stream->jitterbuffer);
148 gst_bin_add (GST_BIN_CAST (rtpclient), stream->ptdemux);
150 /* link jitterbuffer and PT demuxer */
151 srcpad = gst_element_get_pad (stream->jitterbuffer, "src");
152 sinkpad = gst_element_get_pad (stream->ptdemux, "sink");
153 res = gst_pad_link (srcpad, sinkpad);
154 gst_object_unref (srcpad);
155 gst_object_unref (sinkpad);
157 if (res != GST_PAD_LINK_OK)
160 /* add stream to list */
161 rtpclient->streams = g_list_prepend (rtpclient->streams, stream);
164 name = g_strdup_printf ("rtp_sink_%d", ssrc);
165 sinkpad = gst_element_get_pad (stream->jitterbuffer, "sink");
166 stream->rtp_sink = gst_ghost_pad_new (name, sinkpad);
167 gst_object_unref (sinkpad);
169 gst_element_add_pad (GST_ELEMENT_CAST (rtpclient), stream->rtp_sink);
171 /* add signal to ptdemuxer */
172 stream->new_pad_sig =
173 g_signal_connect (G_OBJECT (stream->ptdemux), "pad-added",
174 G_CALLBACK (new_pad), stream);
182 g_warning ("could not create rtpjitterbuffer element");
187 gst_object_unref (stream->jitterbuffer);
189 g_warning ("could not create rtpptdemux element");
194 gst_bin_remove (GST_BIN_CAST (rtpclient), stream->jitterbuffer);
195 gst_bin_remove (GST_BIN_CAST (rtpclient), stream->ptdemux);
197 g_warning ("could not link jitterbuffer and rtpptdemux element");
204 free_stream (GstRTPClientStream * stream)
206 gst_object_unref (stream->jitterbuffer);
211 /* find the stream for the given SSRC, return NULL if the stream did not exist
213 static GstRTPClientStream *
214 find_stream_by_ssrc (GstRTPClient * client, guint32 ssrc)
216 GstRTPClientStream *stream;
219 for (walk = client->streams; walk; walk = g_list_next (walk)) {
220 stream = (GstRTPClientStream *) walk->data;
221 if (stream->ssrc == ssrc)
227 /* signals and args */
239 /* GObject vmethods */
240 static void gst_rtp_client_finalize (GObject * object);
241 static void gst_rtp_client_set_property (GObject * object, guint prop_id,
242 const GValue * value, GParamSpec * pspec);
243 static void gst_rtp_client_get_property (GObject * object, guint prop_id,
244 GValue * value, GParamSpec * pspec);
246 /* GstElement vmethods */
247 static GstStateChangeReturn gst_rtp_client_change_state (GstElement * element,
248 GstStateChange transition);
249 static GstPad *gst_rtp_client_request_new_pad (GstElement * element,
250 GstPadTemplate * templ, const gchar * name);
251 static void gst_rtp_client_release_pad (GstElement * element, GstPad * pad);
253 /*static guint gst_rtp_client_signals[LAST_SIGNAL] = { 0 }; */
255 GST_BOILERPLATE (GstRTPClient, gst_rtp_client, GstBin, GST_TYPE_BIN);
258 gst_rtp_client_base_init (gpointer klass)
260 GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
263 gst_element_class_add_pad_template (element_class,
264 gst_static_pad_template_get (&rtpclient_rtp_sink_template));
265 gst_element_class_add_pad_template (element_class,
266 gst_static_pad_template_get (&rtpclient_sync_sink_template));
269 gst_element_class_add_pad_template (element_class,
270 gst_static_pad_template_get (&rtpclient_rtp_src_template));
272 gst_element_class_set_details (element_class, &rtpclient_details);
276 gst_rtp_client_class_init (GstRTPClientClass * klass)
278 GObjectClass *gobject_class;
279 GstElementClass *gstelement_class;
281 gobject_class = (GObjectClass *) klass;
282 gstelement_class = (GstElementClass *) klass;
284 g_type_class_add_private (klass, sizeof (GstRTPClientPrivate));
286 gobject_class->finalize = gst_rtp_client_finalize;
287 gobject_class->set_property = gst_rtp_client_set_property;
288 gobject_class->get_property = gst_rtp_client_get_property;
290 gstelement_class->change_state =
291 GST_DEBUG_FUNCPTR (gst_rtp_client_change_state);
292 gstelement_class->request_new_pad =
293 GST_DEBUG_FUNCPTR (gst_rtp_client_request_new_pad);
294 gstelement_class->release_pad =
295 GST_DEBUG_FUNCPTR (gst_rtp_client_release_pad);
299 gst_rtp_client_init (GstRTPClient * rtpclient, GstRTPClientClass * klass)
301 rtpclient->priv = GST_RTP_CLIENT_GET_PRIVATE (rtpclient);
305 gst_rtp_client_finalize (GObject * object)
307 GstRTPClient *rtpclient;
309 rtpclient = GST_RTP_CLIENT (object);
311 G_OBJECT_CLASS (parent_class)->finalize (object);
315 gst_rtp_client_set_property (GObject * object, guint prop_id,
316 const GValue * value, GParamSpec * pspec)
318 GstRTPClient *rtpclient;
320 rtpclient = GST_RTP_CLIENT (object);
324 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
330 gst_rtp_client_get_property (GObject * object, guint prop_id,
331 GValue * value, GParamSpec * pspec)
333 GstRTPClient *rtpclient;
335 rtpclient = GST_RTP_CLIENT (object);
339 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
344 static GstStateChangeReturn
345 gst_rtp_client_change_state (GstElement * element, GstStateChange transition)
347 GstStateChangeReturn res;
348 GstRTPClient *rtpclient;
350 rtpclient = GST_RTP_CLIENT (element);
352 switch (transition) {
353 case GST_STATE_CHANGE_NULL_TO_READY:
355 case GST_STATE_CHANGE_READY_TO_PAUSED:
357 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
363 res = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
365 switch (transition) {
366 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
368 case GST_STATE_CHANGE_PAUSED_TO_READY:
370 case GST_STATE_CHANGE_READY_TO_NULL:
378 /* We have 2 request pads (rtp_sink_%d and sync_sink_%d), the %d is assumed to
379 * be the SSRC of the stream.
381 * We require that the rtp pad is requested first for a particular SSRC, then
382 * (optionaly) the sync pad can be requested. If no sync pad is requested, no
383 * sync information can be exchanged for this stream.
386 gst_rtp_client_request_new_pad (GstElement * element,
387 GstPadTemplate * templ, const gchar * name)
389 GstRTPClient *rtpclient;
390 GstElementClass *klass;
391 GstPadTemplate *rtp_sink_templ, *sync_sink_templ;
393 GstRTPClientStream *stream;
396 g_return_val_if_fail (templ != NULL, NULL);
397 g_return_val_if_fail (GST_IS_RTP_CLIENT (element), NULL);
399 if (templ->direction != GST_PAD_SINK)
400 goto wrong_direction;
402 rtpclient = GST_RTP_CLIENT (element);
403 klass = GST_ELEMENT_GET_CLASS (element);
405 /* figure out the template */
406 rtp_sink_templ = gst_element_class_get_pad_template (klass, "rtp_sink_%d");
407 sync_sink_templ = gst_element_class_get_pad_template (klass, "sync_sink_%d");
409 if (templ != rtp_sink_templ && templ != sync_sink_templ)
412 if (templ == rtp_sink_templ) {
413 /* create new rtp sink pad. If a stream with the pad number already exists
414 * we have an error, else we create the sinkpad, add a jitterbuffer and
416 if (name == NULL || strlen (name) < 9)
419 ssrc = atoi (&name[9]);
421 /* see if a stream with that name exists, if so we have an error. */
422 stream = find_stream_by_ssrc (rtpclient, ssrc);
426 /* ok, create new stream */
427 stream = create_stream (rtpclient, ssrc);
429 goto stream_not_found;
431 result = stream->rtp_sink;
433 /* create new rtp sink pad. We can only do this if the RTP pad was
434 * requested before, meaning the session with the padnumber must exist. */
435 if (name == NULL || strlen (name) < 10)
438 ssrc = atoi (&name[10]);
441 stream = find_stream_by_ssrc (rtpclient, ssrc);
443 goto stream_not_found;
446 gst_pad_new_from_static_template (&rtpclient_sync_sink_template, name);
447 gst_element_add_pad (GST_ELEMENT_CAST (rtpclient), stream->sync_sink);
449 result = stream->sync_sink;
457 g_warning ("rtpclient: request pad that is not a SINK pad");
462 g_warning ("rtpclient: this is not our template");
467 g_warning ("rtpclient: no padname was specified");
472 g_warning ("rtpclient: stream with SSRC %d already registered", ssrc);
477 g_warning ("rtpclient: stream with SSRC %d not yet registered", ssrc);
483 gst_rtp_client_release_pad (GstElement * element, GstPad * pad)