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-rtpbin
22 * @short_description: handle media from one RTP bin
23 * @see_also: rtpjitterbuffer, rtpclient, rtpsession
28 * <title>Example pipelines</title>
31 * gst-launch -v filesrc location=sine.ogg ! oggdemux ! vorbisdec ! audioconvert ! alsasink
36 * Last reviewed on 2007-04-02 (0.10.6)
44 #include "gstrtpbin.h"
46 GST_DEBUG_CATEGORY_STATIC (gst_rtp_bin_debug);
47 #define GST_CAT_DEFAULT gst_rtp_bin_debug
50 /* elementfactory information */
51 static const GstElementDetails rtpbin_details = GST_ELEMENT_DETAILS ("RTP Bin",
52 "Filter/Editor/Video",
53 "Implement an RTP bin",
54 "Wim Taymans <wim@fluendo.com>");
57 static GstStaticPadTemplate rtpbin_recv_rtp_sink_template =
58 GST_STATIC_PAD_TEMPLATE ("recv_rtp_sink_%d",
61 GST_STATIC_CAPS ("application/x-rtp")
64 static GstStaticPadTemplate rtpbin_recv_rtcp_sink_template =
65 GST_STATIC_PAD_TEMPLATE ("recv_rtcp_sink_%d",
68 GST_STATIC_CAPS ("application/x-rtcp")
71 static GstStaticPadTemplate rtpbin_send_rtp_sink_template =
72 GST_STATIC_PAD_TEMPLATE ("send_rtp_sink_%d",
75 GST_STATIC_CAPS ("application/x-rtp")
79 static GstStaticPadTemplate rtpbin_recv_rtp_src_template =
80 GST_STATIC_PAD_TEMPLATE ("recv_rtp_src_%d_%d_%d",
83 GST_STATIC_CAPS ("application/x-rtp")
86 static GstStaticPadTemplate rtpbin_rtcp_src_template =
87 GST_STATIC_PAD_TEMPLATE ("rtcp_src_%d",
90 GST_STATIC_CAPS ("application/x-rtcp")
93 static GstStaticPadTemplate rtpbin_send_rtp_src_template =
94 GST_STATIC_PAD_TEMPLATE ("send_rtp_src_%d",
97 GST_STATIC_CAPS ("application/x-rtp")
100 #define GST_RTP_BIN_GET_PRIVATE(obj) \
101 (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GST_TYPE_RTP_BIN, GstRTPBinPrivate))
103 struct _GstRTPBinPrivate
108 /* signals and args */
121 typedef struct _GstRTPBinSession GstRTPBinSession;
122 typedef struct _GstRTPBinStream GstRTPBinStream;
123 typedef struct _GstRTPBinClient GstRTPBinClient;
125 /* Manages the RTP stream for one SSRC.
127 * We pipe the stream (comming from the SSRC demuxer) into a jitterbuffer.
128 * If we see an SDES RTCP packet that links multiple SSRCs together based on a
129 * common CNAME, we create a GstRTPBinClient structure to group the SSRCs
130 * together (see below).
132 struct _GstRTPBinStream
134 /* the SSRC of this stream */
138 /* the session this SSRC belongs to */
139 GstRTPBinSession *session;
140 /* the jitterbuffer of the SSRC */
142 /* the PT demuxer of the SSRC */
144 gulong demux_newpad_sig;
147 /* Manages the receiving end of the packets.
149 * There is one such structure for each RTP session (audio/video/...).
150 * We get the RTP/RTCP packets and stuff them into the session manager. From
151 * there they are pushed into an SSRC demuxer that splits the stream based on
152 * SSRC. Each of the SSRC streams go into their own jitterbuffer (managed with
153 * the GstRTPBinStream above).
155 struct _GstRTPBinSession
161 /* the session element */
163 /* the SSRC demuxer */
165 gulong demux_newpad_sig;
167 /* list of GstRTPBinStream */
170 /* the pads of the session */
171 GstPad *recv_rtp_sink;
172 GstPad *recv_rtp_src;
173 GstPad *recv_rtcp_sink;
174 GstPad *recv_rtcp_src;
175 GstPad *send_rtp_sink;
176 GstPad *send_rtp_src;
180 /* find a session with the given id */
181 static GstRTPBinSession *
182 find_session_by_id (GstRTPBin * rtpbin, gint id)
186 for (walk = rtpbin->sessions; walk; walk = g_slist_next (walk)) {
187 GstRTPBinSession *sess = (GstRTPBinSession *) walk->data;
195 /* create a session with the given id */
196 static GstRTPBinSession *
197 create_session (GstRTPBin * rtpbin, gint id)
199 GstRTPBinSession *sess;
200 GstElement *elem, *demux;
202 if (!(elem = gst_element_factory_make ("rtpsession", NULL)))
205 if (!(demux = gst_element_factory_make ("rtpssrcdemux", NULL)))
208 sess = g_new0 (GstRTPBinSession, 1);
211 sess->session = elem;
213 rtpbin->sessions = g_slist_prepend (rtpbin->sessions, sess);
215 gst_bin_add (GST_BIN_CAST (rtpbin), elem);
216 gst_element_set_state (elem, GST_STATE_PLAYING);
217 gst_bin_add (GST_BIN_CAST (rtpbin), demux);
218 gst_element_set_state (demux, GST_STATE_PLAYING);
225 g_warning ("rtpbin: could not create rtpsession element");
230 gst_object_unref (elem);
231 g_warning ("rtpbin: could not create rtpssrcdemux element");
237 static GstRTPBinStream *
238 find_stream_by_ssrc (GstRTPBinSession * session, guint32 ssrc)
242 for (walk = session->streams; walk; walk = g_slist_next (walk)) {
243 GstRTPBinStream *stream = (GstRTPBinStream *) walk->data;
245 if (stream->ssrc == ssrc)
252 static GstRTPBinStream *
253 create_stream (GstRTPBinSession * session, guint32 ssrc)
255 GstElement *buffer, *demux;
256 GstRTPBinStream *stream;
258 if (!(buffer = gst_element_factory_make ("rtpjitterbuffer", NULL)))
259 goto no_jitterbuffer;
261 if (!(demux = gst_element_factory_make ("rtpptdemux", NULL)))
264 stream = g_new0 (GstRTPBinStream, 1);
266 stream->bin = session->bin;
267 stream->session = session;
268 stream->buffer = buffer;
269 stream->demux = demux;
270 session->streams = g_slist_prepend (session->streams, stream);
272 gst_bin_add (GST_BIN_CAST (session->bin), buffer);
273 gst_element_set_state (buffer, GST_STATE_PLAYING);
274 gst_bin_add (GST_BIN_CAST (session->bin), demux);
275 gst_element_set_state (demux, GST_STATE_PLAYING);
278 gst_element_link (buffer, demux);
285 g_warning ("rtpbin: could not create rtpjitterbuffer element");
290 gst_object_unref (buffer);
291 g_warning ("rtpbin: could not create rtpptdemux element");
296 /* Manages the RTP streams that come from one client and should therefore be
299 struct _GstRTPBinClient
301 /* the common CNAME for the streams */
307 /* GObject vmethods */
308 static void gst_rtp_bin_finalize (GObject * object);
309 static void gst_rtp_bin_set_property (GObject * object, guint prop_id,
310 const GValue * value, GParamSpec * pspec);
311 static void gst_rtp_bin_get_property (GObject * object, guint prop_id,
312 GValue * value, GParamSpec * pspec);
314 /* GstElement vmethods */
315 static GstClock *gst_rtp_bin_provide_clock (GstElement * element);
316 static GstStateChangeReturn gst_rtp_bin_change_state (GstElement * element,
317 GstStateChange transition);
318 static GstPad *gst_rtp_bin_request_new_pad (GstElement * element,
319 GstPadTemplate * templ, const gchar * name);
320 static void gst_rtp_bin_release_pad (GstElement * element, GstPad * pad);
322 /*static guint gst_rtp_bin_signals[LAST_SIGNAL] = { 0 }; */
324 GST_BOILERPLATE (GstRTPBin, gst_rtp_bin, GstBin, GST_TYPE_BIN);
327 gst_rtp_bin_base_init (gpointer klass)
329 GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
332 gst_element_class_add_pad_template (element_class,
333 gst_static_pad_template_get (&rtpbin_recv_rtp_sink_template));
334 gst_element_class_add_pad_template (element_class,
335 gst_static_pad_template_get (&rtpbin_recv_rtcp_sink_template));
336 gst_element_class_add_pad_template (element_class,
337 gst_static_pad_template_get (&rtpbin_send_rtp_sink_template));
340 gst_element_class_add_pad_template (element_class,
341 gst_static_pad_template_get (&rtpbin_recv_rtp_src_template));
342 gst_element_class_add_pad_template (element_class,
343 gst_static_pad_template_get (&rtpbin_rtcp_src_template));
344 gst_element_class_add_pad_template (element_class,
345 gst_static_pad_template_get (&rtpbin_send_rtp_src_template));
347 gst_element_class_set_details (element_class, &rtpbin_details);
351 gst_rtp_bin_class_init (GstRTPBinClass * klass)
353 GObjectClass *gobject_class;
354 GstElementClass *gstelement_class;
356 gobject_class = (GObjectClass *) klass;
357 gstelement_class = (GstElementClass *) klass;
359 g_type_class_add_private (klass, sizeof (GstRTPBinPrivate));
361 gobject_class->finalize = gst_rtp_bin_finalize;
362 gobject_class->set_property = gst_rtp_bin_set_property;
363 gobject_class->get_property = gst_rtp_bin_get_property;
365 gstelement_class->provide_clock =
366 GST_DEBUG_FUNCPTR (gst_rtp_bin_provide_clock);
367 gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_rtp_bin_change_state);
368 gstelement_class->request_new_pad =
369 GST_DEBUG_FUNCPTR (gst_rtp_bin_request_new_pad);
370 gstelement_class->release_pad = GST_DEBUG_FUNCPTR (gst_rtp_bin_release_pad);
372 GST_DEBUG_CATEGORY_INIT (gst_rtp_bin_debug, "rtpbin", 0, "RTP bin");
376 gst_rtp_bin_init (GstRTPBin * rtpbin, GstRTPBinClass * klass)
378 rtpbin->priv = GST_RTP_BIN_GET_PRIVATE (rtpbin);
379 rtpbin->provided_clock = gst_system_clock_obtain ();
383 gst_rtp_bin_finalize (GObject * object)
387 rtpbin = GST_RTP_BIN (object);
389 G_OBJECT_CLASS (parent_class)->finalize (object);
393 gst_rtp_bin_set_property (GObject * object, guint prop_id,
394 const GValue * value, GParamSpec * pspec)
398 rtpbin = GST_RTP_BIN (object);
402 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
408 gst_rtp_bin_get_property (GObject * object, guint prop_id,
409 GValue * value, GParamSpec * pspec)
413 rtpbin = GST_RTP_BIN (object);
417 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
423 gst_rtp_bin_provide_clock (GstElement * element)
427 rtpbin = GST_RTP_BIN (element);
429 return GST_CLOCK_CAST (gst_object_ref (rtpbin->provided_clock));
432 static GstStateChangeReturn
433 gst_rtp_bin_change_state (GstElement * element, GstStateChange transition)
435 GstStateChangeReturn res;
438 rtpbin = GST_RTP_BIN (element);
440 switch (transition) {
441 case GST_STATE_CHANGE_NULL_TO_READY:
443 case GST_STATE_CHANGE_READY_TO_PAUSED:
445 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
451 res = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
453 switch (transition) {
454 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
456 case GST_STATE_CHANGE_PAUSED_TO_READY:
458 case GST_STATE_CHANGE_READY_TO_NULL:
466 /* a new pad (SSRC) was created in @session */
468 new_payload_found (GstElement * element, guint pt, GstPad * pad,
469 GstRTPBinStream * stream)
472 GstElementClass *klass;
473 GstPadTemplate *templ;
477 rtpbin = stream->bin;
479 GST_DEBUG ("new payload pad %d", pt);
481 /* ghost the pad to the parent */
482 klass = GST_ELEMENT_GET_CLASS (rtpbin);
483 templ = gst_element_class_get_pad_template (klass, "recv_rtp_src_%d_%d_%d");
484 padname = g_strdup_printf ("recv_rtp_src_%d_%u_%d",
485 stream->session->id, stream->ssrc, pt);
486 gpad = gst_ghost_pad_new_from_template (padname, pad, templ);
489 gst_pad_set_active (gpad, TRUE);
490 gst_element_add_pad (GST_ELEMENT_CAST (rtpbin), gpad);
493 /* a new pad (SSRC) was created in @session */
495 new_ssrc_pad_found (GstElement * element, guint ssrc, GstPad * pad,
496 GstRTPBinSession * session)
498 GstRTPBinStream *stream;
501 GST_DEBUG_OBJECT (session->bin, "new SSRC pad %08x", ssrc);
503 /* create new stream */
504 stream = create_stream (session, ssrc);
508 /* get pad and link */
509 GST_DEBUG_OBJECT (session->bin, "linking jitterbuffer");
510 sinkpad = gst_element_get_static_pad (stream->buffer, "sink");
511 gst_pad_link (pad, sinkpad);
512 gst_object_unref (sinkpad);
514 /* connect to the new-pad signal of the payload demuxer */
515 stream->demux_newpad_sig = g_signal_connect (stream->demux,
516 "new-payload-type", (GCallback) new_payload_found, stream);
523 GST_DEBUG ("could not create stream");
528 /* Create a pad for receiving RTP for the session in @name
531 create_recv_rtp (GstRTPBin * rtpbin, GstPadTemplate * templ, const gchar * name)
533 GstPad *result, *sinkdpad;
535 GstRTPBinSession *session;
536 GstPadLinkReturn lres;
538 /* first get the session number */
539 if (name == NULL || sscanf (name, "recv_rtp_sink_%d", &sessid) != 1)
542 GST_DEBUG_OBJECT (rtpbin, "finding session %d", sessid);
544 /* get or create session */
545 session = find_session_by_id (rtpbin, sessid);
547 GST_DEBUG_OBJECT (rtpbin, "creating session %d", sessid);
548 /* create session now */
549 session = create_session (rtpbin, sessid);
553 /* check if pad was requested */
554 if (session->recv_rtp_sink != NULL)
557 GST_DEBUG_OBJECT (rtpbin, "getting RTP sink pad");
558 /* get recv_rtp pad and store */
559 session->recv_rtp_sink =
560 gst_element_get_request_pad (session->session, "recv_rtp_sink");
561 if (session->recv_rtp_sink == NULL)
564 GST_DEBUG_OBJECT (rtpbin, "getting RTP src pad");
565 /* get srcpad, link to SSRCDemux */
566 session->recv_rtp_src =
567 gst_element_get_static_pad (session->session, "recv_rtp_src");
568 if (session->recv_rtp_src == NULL)
571 GST_DEBUG_OBJECT (rtpbin, "getting demuxer sink pad");
572 sinkdpad = gst_element_get_static_pad (session->demux, "sink");
573 lres = gst_pad_link (session->recv_rtp_src, sinkdpad);
574 gst_object_unref (sinkdpad);
575 if (lres != GST_PAD_LINK_OK)
578 /* connect to the new-ssrc-pad signal of the demuxer */
579 session->demux_newpad_sig = g_signal_connect (session->demux,
580 "new-ssrc-pad", (GCallback) new_ssrc_pad_found, session);
582 GST_DEBUG_OBJECT (rtpbin, "ghosting session sink pad");
584 gst_ghost_pad_new_from_template (name, session->recv_rtp_sink, templ);
585 gst_pad_set_active (result, TRUE);
586 gst_element_add_pad (GST_ELEMENT_CAST (rtpbin), result);
593 g_warning ("rtpbin: invalid name given");
598 /* create_session already warned */
603 g_warning ("rtpbin: recv_rtp pad already requested for session %d", sessid);
608 g_warning ("rtpbin: failed to get session pad");
613 g_warning ("rtpbin: failed to link pads");
618 /* Create a pad for receiving RTCP for the session in @name
621 create_recv_rtcp (GstRTPBin * rtpbin, GstPadTemplate * templ,
626 GstRTPBinSession *session;
630 GstPadLinkReturn lres;
633 /* first get the session number */
634 if (name == NULL || sscanf (name, "recv_rtcp_sink_%d", &sessid) != 1)
637 GST_DEBUG_OBJECT (rtpbin, "finding session %d", sessid);
639 /* get the session, it must exist or we error */
640 session = find_session_by_id (rtpbin, sessid);
644 /* check if pad was requested */
645 if (session->recv_rtcp_sink != NULL)
648 GST_DEBUG_OBJECT (rtpbin, "getting RTCP sink pad");
650 /* get recv_rtp pad and store */
651 session->recv_rtcp_sink =
652 gst_element_get_request_pad (session->session, "recv_rtcp_sink");
653 if (session->recv_rtcp_sink == NULL)
657 /* get srcpad, link to SSRCDemux */
658 GST_DEBUG_OBJECT (rtpbin, "getting sync src pad");
659 session->recv_rtcp_src =
660 gst_element_get_static_pad (session->session, "sync_src");
661 if (session->recv_rtcp_src == NULL)
664 GST_DEBUG_OBJECT (rtpbin, "linking sync to demux");
665 sinkdpad = gst_element_get_static_pad (session->demux, "sink");
666 lres = gst_pad_link (session->recv_rtcp_src, sinkdpad);
667 gst_object_unref (sinkdpad);
668 if (lres != GST_PAD_LINK_OK)
673 gst_ghost_pad_new_from_template (name, session->recv_rtcp_sink, templ);
674 gst_pad_set_active (result, TRUE);
675 gst_element_add_pad (GST_ELEMENT_CAST (rtpbin), result);
682 g_warning ("rtpbin: invalid name given");
687 g_warning ("rtpbin: no session with id %d", sessid);
692 g_warning ("rtpbin: recv_rtcp pad already requested for session %d",
698 g_warning ("rtpbin: failed to get session pad");
704 g_warning ("rtpbin: failed to link pads");
710 /* Create a pad for sending RTP for the session in @name
713 create_send_rtp (GstRTPBin * rtpbin, GstPadTemplate * templ, const gchar * name)
715 GstPad *result, *srcpad, *srcghost;
718 GstRTPBinSession *session;
719 GstElementClass *klass;
721 /* first get the session number */
722 if (name == NULL || sscanf (name, "send_rtp_sink_%d", &sessid) != 1)
725 /* get or create session */
726 session = find_session_by_id (rtpbin, sessid);
728 /* create session now */
729 session = create_session (rtpbin, sessid);
734 /* check if pad was requested */
735 if (session->send_rtp_sink != NULL)
738 /* get recv_rtp pad and store */
739 session->send_rtp_sink =
740 gst_element_get_request_pad (session->session, "send_rtp_sink");
741 if (session->send_rtp_sink == NULL)
745 gst_ghost_pad_new_from_template (name, session->send_rtp_sink, templ);
746 gst_pad_set_active (result, TRUE);
747 gst_element_add_pad (GST_ELEMENT_CAST (rtpbin), result);
750 srcpad = gst_element_get_pad (session->session, "send_rtp_src");
754 /* ghost the new source pad */
755 klass = GST_ELEMENT_GET_CLASS (rtpbin);
756 gname = g_strdup_printf ("send_rtp_src_%d", sessid);
757 templ = gst_element_class_get_pad_template (klass, "send_rtp_src_%d");
759 gst_ghost_pad_new_from_template (gname, session->send_rtp_sink, templ);
760 gst_pad_set_active (srcghost, TRUE);
761 gst_element_add_pad (GST_ELEMENT_CAST (rtpbin), srcghost);
769 g_warning ("rtpbin: invalid name given");
774 /* create_session already warned */
779 g_warning ("rtpbin: send_rtp pad already requested for session %d", sessid);
784 g_warning ("rtpbin: failed to get session pad for session %d", sessid);
789 g_warning ("rtpbin: failed to get rtp source pad for session %d", sessid);
794 /* Create a pad for sending RTCP for the session in @name
797 create_rtcp (GstRTPBin * rtpbin, GstPadTemplate * templ, const gchar * name)
801 GstRTPBinSession *session;
803 /* first get the session number */
804 if (name == NULL || sscanf (name, "rtcp_src_%d", &sessid) != 1)
807 /* get or create session */
808 session = find_session_by_id (rtpbin, sessid);
812 /* check if pad was requested */
813 if (session->rtcp_src != NULL)
816 /* get rtcp_src pad and store */
818 gst_element_get_request_pad (session->session, "rtcp_src");
819 if (session->rtcp_src == NULL)
822 result = gst_ghost_pad_new_from_template (name, session->rtcp_src, templ);
823 gst_pad_set_active (result, TRUE);
824 gst_element_add_pad (GST_ELEMENT_CAST (rtpbin), result);
831 g_warning ("rtpbin: invalid name given");
836 g_warning ("rtpbin: session with id %d does not exist", sessid);
841 g_warning ("rtpbin: rtcp_src pad already requested for session %d", sessid);
846 g_warning ("rtpbin: failed to get rtcp pad for session %d", sessid);
854 gst_rtp_bin_request_new_pad (GstElement * element,
855 GstPadTemplate * templ, const gchar * name)
858 GstElementClass *klass;
861 g_return_val_if_fail (templ != NULL, NULL);
862 g_return_val_if_fail (GST_IS_RTP_BIN (element), NULL);
864 rtpbin = GST_RTP_BIN (element);
865 klass = GST_ELEMENT_GET_CLASS (element);
867 /* figure out the template */
868 if (templ == gst_element_class_get_pad_template (klass, "recv_rtp_sink_%d")) {
869 result = create_recv_rtp (rtpbin, templ, name);
870 } else if (templ == gst_element_class_get_pad_template (klass,
871 "recv_rtcp_sink_%d")) {
872 result = create_recv_rtcp (rtpbin, templ, name);
873 } else if (templ == gst_element_class_get_pad_template (klass,
874 "send_rtp_sink_%d")) {
875 result = create_send_rtp (rtpbin, templ, name);
876 } else if (templ == gst_element_class_get_pad_template (klass, "rtcp_src_%d")) {
877 result = create_rtcp (rtpbin, templ, name);
886 g_warning ("rtpbin: this is not our template");
892 gst_rtp_bin_release_pad (GstElement * element, GstPad * pad)