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-marshal.h"
45 #include "gstrtpbin.h"
47 GST_DEBUG_CATEGORY_STATIC (gst_rtp_bin_debug);
48 #define GST_CAT_DEFAULT gst_rtp_bin_debug
51 /* elementfactory information */
52 static const GstElementDetails rtpbin_details = GST_ELEMENT_DETAILS ("RTP Bin",
53 "Filter/Editor/Video",
54 "Implement an RTP bin",
55 "Wim Taymans <wim@fluendo.com>");
58 static GstStaticPadTemplate rtpbin_recv_rtp_sink_template =
59 GST_STATIC_PAD_TEMPLATE ("recv_rtp_sink_%d",
62 GST_STATIC_CAPS ("application/x-rtp")
65 static GstStaticPadTemplate rtpbin_recv_rtcp_sink_template =
66 GST_STATIC_PAD_TEMPLATE ("recv_rtcp_sink_%d",
69 GST_STATIC_CAPS ("application/x-rtcp")
72 static GstStaticPadTemplate rtpbin_send_rtp_sink_template =
73 GST_STATIC_PAD_TEMPLATE ("send_rtp_sink_%d",
76 GST_STATIC_CAPS ("application/x-rtp")
80 static GstStaticPadTemplate rtpbin_recv_rtp_src_template =
81 GST_STATIC_PAD_TEMPLATE ("recv_rtp_src_%d_%d_%d",
84 GST_STATIC_CAPS ("application/x-rtp")
87 static GstStaticPadTemplate rtpbin_rtcp_src_template =
88 GST_STATIC_PAD_TEMPLATE ("rtcp_src_%d",
91 GST_STATIC_CAPS ("application/x-rtcp")
94 static GstStaticPadTemplate rtpbin_send_rtp_src_template =
95 GST_STATIC_PAD_TEMPLATE ("send_rtp_src_%d",
98 GST_STATIC_CAPS ("application/x-rtp")
101 #define GST_RTP_BIN_GET_PRIVATE(obj) \
102 (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GST_TYPE_RTP_BIN, GstRTPBinPrivate))
104 struct _GstRTPBinPrivate
109 /* signals and args */
112 SIGNAL_REQUEST_PT_MAP,
122 typedef struct _GstRTPBinSession GstRTPBinSession;
123 typedef struct _GstRTPBinStream GstRTPBinStream;
124 typedef struct _GstRTPBinClient GstRTPBinClient;
126 /* Manages the RTP stream for one SSRC.
128 * We pipe the stream (comming from the SSRC demuxer) into a jitterbuffer.
129 * If we see an SDES RTCP packet that links multiple SSRCs together based on a
130 * common CNAME, we create a GstRTPBinClient structure to group the SSRCs
131 * together (see below).
133 struct _GstRTPBinStream
135 /* the SSRC of this stream */
139 /* the session this SSRC belongs to */
140 GstRTPBinSession *session;
141 /* the jitterbuffer of the SSRC */
143 /* the PT demuxer of the SSRC */
145 gulong demux_newpad_sig;
148 /* Manages the receiving end of the packets.
150 * There is one such structure for each RTP session (audio/video/...).
151 * We get the RTP/RTCP packets and stuff them into the session manager. From
152 * there they are pushed into an SSRC demuxer that splits the stream based on
153 * SSRC. Each of the SSRC streams go into their own jitterbuffer (managed with
154 * the GstRTPBinStream above).
156 struct _GstRTPBinSession
162 /* the session element */
164 /* the SSRC demuxer */
166 gulong demux_newpad_sig;
168 /* list of GstRTPBinStream */
171 /* the pads of the session */
172 GstPad *recv_rtp_sink;
173 GstPad *recv_rtp_src;
174 GstPad *recv_rtcp_sink;
175 GstPad *recv_rtcp_src;
176 GstPad *send_rtp_sink;
177 GstPad *send_rtp_src;
181 /* find a session with the given id */
182 static GstRTPBinSession *
183 find_session_by_id (GstRTPBin * rtpbin, gint id)
187 for (walk = rtpbin->sessions; walk; walk = g_slist_next (walk)) {
188 GstRTPBinSession *sess = (GstRTPBinSession *) walk->data;
196 /* create a session with the given id */
197 static GstRTPBinSession *
198 create_session (GstRTPBin * rtpbin, gint id)
200 GstRTPBinSession *sess;
201 GstElement *elem, *demux;
203 if (!(elem = gst_element_factory_make ("rtpsession", NULL)))
206 if (!(demux = gst_element_factory_make ("rtpssrcdemux", NULL)))
209 sess = g_new0 (GstRTPBinSession, 1);
212 sess->session = elem;
214 rtpbin->sessions = g_slist_prepend (rtpbin->sessions, sess);
216 gst_bin_add (GST_BIN_CAST (rtpbin), elem);
217 gst_element_set_state (elem, GST_STATE_PLAYING);
218 gst_bin_add (GST_BIN_CAST (rtpbin), demux);
219 gst_element_set_state (demux, GST_STATE_PLAYING);
226 g_warning ("rtpbin: could not create rtpsession element");
231 gst_object_unref (elem);
232 g_warning ("rtpbin: could not create rtpssrcdemux element");
238 static GstRTPBinStream *
239 find_stream_by_ssrc (GstRTPBinSession * session, guint32 ssrc)
243 for (walk = session->streams; walk; walk = g_slist_next (walk)) {
244 GstRTPBinStream *stream = (GstRTPBinStream *) walk->data;
246 if (stream->ssrc == ssrc)
253 static GstRTPBinStream *
254 create_stream (GstRTPBinSession * session, guint32 ssrc)
256 GstElement *buffer, *demux;
257 GstRTPBinStream *stream;
259 if (!(buffer = gst_element_factory_make ("rtpjitterbuffer", NULL)))
260 goto no_jitterbuffer;
262 if (!(demux = gst_element_factory_make ("rtpptdemux", NULL)))
265 stream = g_new0 (GstRTPBinStream, 1);
267 stream->bin = session->bin;
268 stream->session = session;
269 stream->buffer = buffer;
270 stream->demux = demux;
271 session->streams = g_slist_prepend (session->streams, stream);
273 gst_bin_add (GST_BIN_CAST (session->bin), buffer);
274 gst_element_set_state (buffer, GST_STATE_PLAYING);
275 gst_bin_add (GST_BIN_CAST (session->bin), demux);
276 gst_element_set_state (demux, GST_STATE_PLAYING);
279 gst_element_link (buffer, demux);
286 g_warning ("rtpbin: could not create rtpjitterbuffer element");
291 gst_object_unref (buffer);
292 g_warning ("rtpbin: could not create rtpptdemux element");
297 /* Manages the RTP streams that come from one client and should therefore be
300 struct _GstRTPBinClient
302 /* the common CNAME for the streams */
308 /* GObject vmethods */
309 static void gst_rtp_bin_finalize (GObject * object);
310 static void gst_rtp_bin_set_property (GObject * object, guint prop_id,
311 const GValue * value, GParamSpec * pspec);
312 static void gst_rtp_bin_get_property (GObject * object, guint prop_id,
313 GValue * value, GParamSpec * pspec);
315 /* GstElement vmethods */
316 static GstClock *gst_rtp_bin_provide_clock (GstElement * element);
317 static GstStateChangeReturn gst_rtp_bin_change_state (GstElement * element,
318 GstStateChange transition);
319 static GstPad *gst_rtp_bin_request_new_pad (GstElement * element,
320 GstPadTemplate * templ, const gchar * name);
321 static void gst_rtp_bin_release_pad (GstElement * element, GstPad * pad);
323 static guint gst_rtp_bin_signals[LAST_SIGNAL] = { 0 };
325 GST_BOILERPLATE (GstRTPBin, gst_rtp_bin, GstBin, GST_TYPE_BIN);
328 gst_rtp_bin_base_init (gpointer klass)
330 GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
333 gst_element_class_add_pad_template (element_class,
334 gst_static_pad_template_get (&rtpbin_recv_rtp_sink_template));
335 gst_element_class_add_pad_template (element_class,
336 gst_static_pad_template_get (&rtpbin_recv_rtcp_sink_template));
337 gst_element_class_add_pad_template (element_class,
338 gst_static_pad_template_get (&rtpbin_send_rtp_sink_template));
341 gst_element_class_add_pad_template (element_class,
342 gst_static_pad_template_get (&rtpbin_recv_rtp_src_template));
343 gst_element_class_add_pad_template (element_class,
344 gst_static_pad_template_get (&rtpbin_rtcp_src_template));
345 gst_element_class_add_pad_template (element_class,
346 gst_static_pad_template_get (&rtpbin_send_rtp_src_template));
348 gst_element_class_set_details (element_class, &rtpbin_details);
352 gst_rtp_bin_class_init (GstRTPBinClass * klass)
354 GObjectClass *gobject_class;
355 GstElementClass *gstelement_class;
357 gobject_class = (GObjectClass *) klass;
358 gstelement_class = (GstElementClass *) klass;
360 g_type_class_add_private (klass, sizeof (GstRTPBinPrivate));
362 gobject_class->finalize = gst_rtp_bin_finalize;
363 gobject_class->set_property = gst_rtp_bin_set_property;
364 gobject_class->get_property = gst_rtp_bin_get_property;
366 gst_rtp_bin_signals[SIGNAL_REQUEST_PT_MAP] =
367 g_signal_new ("request-pt-map", G_TYPE_FROM_CLASS (klass),
368 G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRTPBinClass, request_pt_map),
369 NULL, NULL, gst_rtp_bin_marshal_BOXED__UINT, GST_TYPE_CAPS, 1,
372 gstelement_class->provide_clock =
373 GST_DEBUG_FUNCPTR (gst_rtp_bin_provide_clock);
374 gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_rtp_bin_change_state);
375 gstelement_class->request_new_pad =
376 GST_DEBUG_FUNCPTR (gst_rtp_bin_request_new_pad);
377 gstelement_class->release_pad = GST_DEBUG_FUNCPTR (gst_rtp_bin_release_pad);
379 GST_DEBUG_CATEGORY_INIT (gst_rtp_bin_debug, "rtpbin", 0, "RTP bin");
383 gst_rtp_bin_init (GstRTPBin * rtpbin, GstRTPBinClass * klass)
385 rtpbin->priv = GST_RTP_BIN_GET_PRIVATE (rtpbin);
386 rtpbin->provided_clock = gst_system_clock_obtain ();
390 gst_rtp_bin_finalize (GObject * object)
394 rtpbin = GST_RTP_BIN (object);
396 G_OBJECT_CLASS (parent_class)->finalize (object);
400 gst_rtp_bin_set_property (GObject * object, guint prop_id,
401 const GValue * value, GParamSpec * pspec)
405 rtpbin = GST_RTP_BIN (object);
409 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
415 gst_rtp_bin_get_property (GObject * object, guint prop_id,
416 GValue * value, GParamSpec * pspec)
420 rtpbin = GST_RTP_BIN (object);
424 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
430 gst_rtp_bin_provide_clock (GstElement * element)
434 rtpbin = GST_RTP_BIN (element);
436 return GST_CLOCK_CAST (gst_object_ref (rtpbin->provided_clock));
439 static GstStateChangeReturn
440 gst_rtp_bin_change_state (GstElement * element, GstStateChange transition)
442 GstStateChangeReturn res;
445 rtpbin = GST_RTP_BIN (element);
447 switch (transition) {
448 case GST_STATE_CHANGE_NULL_TO_READY:
450 case GST_STATE_CHANGE_READY_TO_PAUSED:
452 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
458 res = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
460 switch (transition) {
461 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
463 case GST_STATE_CHANGE_PAUSED_TO_READY:
465 case GST_STATE_CHANGE_READY_TO_NULL:
473 /* a new pad (SSRC) was created in @session */
475 new_payload_found (GstElement * element, guint pt, GstPad * pad,
476 GstRTPBinStream * stream)
479 GstElementClass *klass;
480 GstPadTemplate *templ;
484 rtpbin = stream->bin;
486 GST_DEBUG ("new payload pad %d", pt);
488 /* ghost the pad to the parent */
489 klass = GST_ELEMENT_GET_CLASS (rtpbin);
490 templ = gst_element_class_get_pad_template (klass, "recv_rtp_src_%d_%d_%d");
491 padname = g_strdup_printf ("recv_rtp_src_%d_%u_%d",
492 stream->session->id, stream->ssrc, pt);
493 gpad = gst_ghost_pad_new_from_template (padname, pad, templ);
496 gst_pad_set_active (gpad, TRUE);
497 gst_element_add_pad (GST_ELEMENT_CAST (rtpbin), gpad);
500 /* a new pad (SSRC) was created in @session */
502 new_ssrc_pad_found (GstElement * element, guint ssrc, GstPad * pad,
503 GstRTPBinSession * session)
505 GstRTPBinStream *stream;
508 GST_DEBUG_OBJECT (session->bin, "new SSRC pad %08x", ssrc);
510 /* create new stream */
511 stream = create_stream (session, ssrc);
515 /* get pad and link */
516 GST_DEBUG_OBJECT (session->bin, "linking jitterbuffer");
517 sinkpad = gst_element_get_static_pad (stream->buffer, "sink");
518 gst_pad_link (pad, sinkpad);
519 gst_object_unref (sinkpad);
521 /* connect to the new-pad signal of the payload demuxer */
522 stream->demux_newpad_sig = g_signal_connect (stream->demux,
523 "new-payload-type", (GCallback) new_payload_found, stream);
530 GST_DEBUG ("could not create stream");
535 /* Create a pad for receiving RTP for the session in @name
538 create_recv_rtp (GstRTPBin * rtpbin, GstPadTemplate * templ, const gchar * name)
540 GstPad *result, *sinkdpad;
542 GstRTPBinSession *session;
543 GstPadLinkReturn lres;
545 /* first get the session number */
546 if (name == NULL || sscanf (name, "recv_rtp_sink_%d", &sessid) != 1)
549 GST_DEBUG_OBJECT (rtpbin, "finding session %d", sessid);
551 /* get or create session */
552 session = find_session_by_id (rtpbin, sessid);
554 GST_DEBUG_OBJECT (rtpbin, "creating session %d", sessid);
555 /* create session now */
556 session = create_session (rtpbin, sessid);
560 /* check if pad was requested */
561 if (session->recv_rtp_sink != NULL)
564 GST_DEBUG_OBJECT (rtpbin, "getting RTP sink pad");
565 /* get recv_rtp pad and store */
566 session->recv_rtp_sink =
567 gst_element_get_request_pad (session->session, "recv_rtp_sink");
568 if (session->recv_rtp_sink == NULL)
571 GST_DEBUG_OBJECT (rtpbin, "getting RTP src pad");
572 /* get srcpad, link to SSRCDemux */
573 session->recv_rtp_src =
574 gst_element_get_static_pad (session->session, "recv_rtp_src");
575 if (session->recv_rtp_src == NULL)
578 GST_DEBUG_OBJECT (rtpbin, "getting demuxer sink pad");
579 sinkdpad = gst_element_get_static_pad (session->demux, "sink");
580 lres = gst_pad_link (session->recv_rtp_src, sinkdpad);
581 gst_object_unref (sinkdpad);
582 if (lres != GST_PAD_LINK_OK)
585 /* connect to the new-ssrc-pad signal of the demuxer */
586 session->demux_newpad_sig = g_signal_connect (session->demux,
587 "new-ssrc-pad", (GCallback) new_ssrc_pad_found, session);
589 GST_DEBUG_OBJECT (rtpbin, "ghosting session sink pad");
591 gst_ghost_pad_new_from_template (name, session->recv_rtp_sink, templ);
592 gst_pad_set_active (result, TRUE);
593 gst_element_add_pad (GST_ELEMENT_CAST (rtpbin), result);
600 g_warning ("rtpbin: invalid name given");
605 /* create_session already warned */
610 g_warning ("rtpbin: recv_rtp pad already requested for session %d", sessid);
615 g_warning ("rtpbin: failed to get session pad");
620 g_warning ("rtpbin: failed to link pads");
625 /* Create a pad for receiving RTCP for the session in @name
628 create_recv_rtcp (GstRTPBin * rtpbin, GstPadTemplate * templ,
633 GstRTPBinSession *session;
637 GstPadLinkReturn lres;
640 /* first get the session number */
641 if (name == NULL || sscanf (name, "recv_rtcp_sink_%d", &sessid) != 1)
644 GST_DEBUG_OBJECT (rtpbin, "finding session %d", sessid);
646 /* get the session, it must exist or we error */
647 session = find_session_by_id (rtpbin, sessid);
651 /* check if pad was requested */
652 if (session->recv_rtcp_sink != NULL)
655 GST_DEBUG_OBJECT (rtpbin, "getting RTCP sink pad");
657 /* get recv_rtp pad and store */
658 session->recv_rtcp_sink =
659 gst_element_get_request_pad (session->session, "recv_rtcp_sink");
660 if (session->recv_rtcp_sink == NULL)
664 /* get srcpad, link to SSRCDemux */
665 GST_DEBUG_OBJECT (rtpbin, "getting sync src pad");
666 session->recv_rtcp_src =
667 gst_element_get_static_pad (session->session, "sync_src");
668 if (session->recv_rtcp_src == NULL)
671 GST_DEBUG_OBJECT (rtpbin, "linking sync to demux");
672 sinkdpad = gst_element_get_static_pad (session->demux, "sink");
673 lres = gst_pad_link (session->recv_rtcp_src, sinkdpad);
674 gst_object_unref (sinkdpad);
675 if (lres != GST_PAD_LINK_OK)
680 gst_ghost_pad_new_from_template (name, session->recv_rtcp_sink, templ);
681 gst_pad_set_active (result, TRUE);
682 gst_element_add_pad (GST_ELEMENT_CAST (rtpbin), result);
689 g_warning ("rtpbin: invalid name given");
694 g_warning ("rtpbin: no session with id %d", sessid);
699 g_warning ("rtpbin: recv_rtcp pad already requested for session %d",
705 g_warning ("rtpbin: failed to get session pad");
711 g_warning ("rtpbin: failed to link pads");
717 /* Create a pad for sending RTP for the session in @name
720 create_send_rtp (GstRTPBin * rtpbin, GstPadTemplate * templ, const gchar * name)
722 GstPad *result, *srcpad, *srcghost;
725 GstRTPBinSession *session;
726 GstElementClass *klass;
728 /* first get the session number */
729 if (name == NULL || sscanf (name, "send_rtp_sink_%d", &sessid) != 1)
732 /* get or create session */
733 session = find_session_by_id (rtpbin, sessid);
735 /* create session now */
736 session = create_session (rtpbin, sessid);
741 /* check if pad was requested */
742 if (session->send_rtp_sink != NULL)
745 /* get recv_rtp pad and store */
746 session->send_rtp_sink =
747 gst_element_get_request_pad (session->session, "send_rtp_sink");
748 if (session->send_rtp_sink == NULL)
752 gst_ghost_pad_new_from_template (name, session->send_rtp_sink, templ);
753 gst_pad_set_active (result, TRUE);
754 gst_element_add_pad (GST_ELEMENT_CAST (rtpbin), result);
757 srcpad = gst_element_get_pad (session->session, "send_rtp_src");
761 /* ghost the new source pad */
762 klass = GST_ELEMENT_GET_CLASS (rtpbin);
763 gname = g_strdup_printf ("send_rtp_src_%d", sessid);
764 templ = gst_element_class_get_pad_template (klass, "send_rtp_src_%d");
766 gst_ghost_pad_new_from_template (gname, session->send_rtp_sink, templ);
767 gst_pad_set_active (srcghost, TRUE);
768 gst_element_add_pad (GST_ELEMENT_CAST (rtpbin), srcghost);
776 g_warning ("rtpbin: invalid name given");
781 /* create_session already warned */
786 g_warning ("rtpbin: send_rtp pad already requested for session %d", sessid);
791 g_warning ("rtpbin: failed to get session pad for session %d", sessid);
796 g_warning ("rtpbin: failed to get rtp source pad for session %d", sessid);
801 /* Create a pad for sending RTCP for the session in @name
804 create_rtcp (GstRTPBin * rtpbin, GstPadTemplate * templ, const gchar * name)
808 GstRTPBinSession *session;
810 /* first get the session number */
811 if (name == NULL || sscanf (name, "rtcp_src_%d", &sessid) != 1)
814 /* get or create session */
815 session = find_session_by_id (rtpbin, sessid);
819 /* check if pad was requested */
820 if (session->rtcp_src != NULL)
823 /* get rtcp_src pad and store */
825 gst_element_get_request_pad (session->session, "rtcp_src");
826 if (session->rtcp_src == NULL)
829 result = gst_ghost_pad_new_from_template (name, session->rtcp_src, templ);
830 gst_pad_set_active (result, TRUE);
831 gst_element_add_pad (GST_ELEMENT_CAST (rtpbin), result);
838 g_warning ("rtpbin: invalid name given");
843 g_warning ("rtpbin: session with id %d does not exist", sessid);
848 g_warning ("rtpbin: rtcp_src pad already requested for session %d", sessid);
853 g_warning ("rtpbin: failed to get rtcp pad for session %d", sessid);
861 gst_rtp_bin_request_new_pad (GstElement * element,
862 GstPadTemplate * templ, const gchar * name)
865 GstElementClass *klass;
868 g_return_val_if_fail (templ != NULL, NULL);
869 g_return_val_if_fail (GST_IS_RTP_BIN (element), NULL);
871 rtpbin = GST_RTP_BIN (element);
872 klass = GST_ELEMENT_GET_CLASS (element);
874 /* figure out the template */
875 if (templ == gst_element_class_get_pad_template (klass, "recv_rtp_sink_%d")) {
876 result = create_recv_rtp (rtpbin, templ, name);
877 } else if (templ == gst_element_class_get_pad_template (klass,
878 "recv_rtcp_sink_%d")) {
879 result = create_recv_rtcp (rtpbin, templ, name);
880 } else if (templ == gst_element_class_get_pad_template (klass,
881 "send_rtp_sink_%d")) {
882 result = create_send_rtp (rtpbin, templ, name);
883 } else if (templ == gst_element_class_get_pad_template (klass, "rtcp_src_%d")) {
884 result = create_rtcp (rtpbin, templ, name);
893 g_warning ("rtpbin: this is not our template");
899 gst_rtp_bin_release_pad (GstElement * element, GstPad * pad)