Added: trunk/Tools/gtk/patches/gst-plugins-good-0001-rtpbin-pipeline-gets-an-EOS-when-any-rtpsources-byes.patch (0 => 208940)
--- trunk/Tools/gtk/patches/gst-plugins-good-0001-rtpbin-pipeline-gets-an-EOS-when-any-rtpsources-byes.patch (rev 0)
+++ trunk/Tools/gtk/patches/gst-plugins-good-0001-rtpbin-pipeline-gets-an-EOS-when-any-rtpsources-byes.patch 2016-11-21 08:09:03 UTC (rev 208940)
@@ -0,0 +1,156 @@
+From eeea2a7fe88a17b15318d5b6ae6e190b2f777030 Mon Sep 17 00:00:00 2001
+From: "Alejandro G. Castro" <[email protected]>
+Date: Wed, 26 Oct 2016 13:21:29 +0200
+Subject: [PATCH] rtpbin: pipeline gets an EOS when any rtpsources byes
+
+Instead of sending EOS when a source byes we have to wait for
+all the sources to be gone, which means they already sent BYE and
+were removed from the session. We now handle the EOS in the rtcp
+loop checking the amount of sources in the session.
+
+https://bugzilla.gnome.org/show_bug.cgi?id=773218
+---
+ gst/rtpmanager/gstrtpsession.c | 29 +++++++++++++++++++----------
+ gst/rtpmanager/rtpsession.c | 6 +-----
+ gst/rtpmanager/rtpsession.h | 3 +--
+ 3 files changed, 21 insertions(+), 17 deletions(-)
+
+diff --git a/gst/rtpmanager/gstrtpsession.c b/gst/rtpmanager/gstrtpsession.c
+index 3688e85..fd1f2b1 100644
+--- a/gst/rtpmanager/gstrtpsession.c
++++ b/gst/rtpmanager/gstrtpsession.c
+@@ -293,7 +293,7 @@ static GstFlowReturn gst_rtp_session_process_rtp (RTPSession * sess,
+ static GstFlowReturn gst_rtp_session_send_rtp (RTPSession * sess,
+ RTPSource * src, gpointer data, gpointer user_data);
+ static GstFlowReturn gst_rtp_session_send_rtcp (RTPSession * sess,
+- RTPSource * src, GstBuffer * buffer, gboolean eos, gpointer user_data);
++ RTPSource * src, GstBuffer * buffer, gpointer user_data);
+ static GstFlowReturn gst_rtp_session_sync_rtcp (RTPSession * sess,
+ GstBuffer * buffer, gpointer user_data);
+ static gint gst_rtp_session_clock_rate (RTPSession * sess, guint8 payload,
+@@ -1156,6 +1156,22 @@ rtcp_thread (GstRtpSession * rtpsession)
+ GST_RTP_SESSION_UNLOCK (rtpsession);
+ rtp_session_on_timeout (session, current_time, ntpnstime, running_time);
+ GST_RTP_SESSION_LOCK (rtpsession);
++
++ if (!rtp_session_get_num_sources (session)) {
++ /* when no sources left in the session, all of the them have went
++ * BYE at some point and removed, we can send EOS to the
++ * pipeline. */
++ GstPad *rtcp_src = rtpsession->send_rtcp_src;
++
++ if (rtcp_src) {
++ gst_object_ref (rtcp_src);
++ GST_LOG_OBJECT (rtpsession, "sending EOS");
++ GST_RTP_SESSION_UNLOCK (rtpsession);
++ gst_pad_push_event (rtpsession->send_rtcp_src, gst_event_new_eos ());
++ GST_RTP_SESSION_LOCK (rtpsession);
++ gst_object_unref (rtcp_src);
++ }
++ }
+ }
+ /* mark the thread as stopped now */
+ rtpsession->priv->thread_stopped = TRUE;
+@@ -1413,11 +1429,10 @@ do_rtcp_events (GstRtpSession * rtpsession, GstPad * srcpad)
+ }
+
+ /* called when the session manager has an RTCP packet ready for further
+- * sending. The eos flag is set when an EOS event should be sent downstream as
+- * well. */
++ * sending. */
+ static GstFlowReturn
+ gst_rtp_session_send_rtcp (RTPSession * sess, RTPSource * src,
+- GstBuffer * buffer, gboolean eos, gpointer user_data)
++ GstBuffer * buffer, gpointer user_data)
+ {
+ GstFlowReturn result;
+ GstRtpSession *rtpsession;
+@@ -1440,11 +1455,6 @@ gst_rtp_session_send_rtcp (RTPSession * sess, RTPSource * src,
+ GST_LOG_OBJECT (rtpsession, "sending RTCP");
+ result = gst_pad_push (rtcp_src, buffer);
+
+- /* we have to send EOS after this packet */
+- if (eos) {
+- GST_LOG_OBJECT (rtpsession, "sending EOS");
+- gst_pad_push_event (rtcp_src, gst_event_new_eos ());
+- }
+ gst_object_unref (rtcp_src);
+ } else {
+ GST_RTP_SESSION_UNLOCK (rtpsession);
+@@ -2056,7 +2066,6 @@ gst_rtp_session_event_send_rtcp_src (GstPad * pad, GstObject * parent,
+ return ret;
+ }
+
+-
+ static gboolean
+ gst_rtp_session_event_send_rtp_sink (GstPad * pad, GstObject * parent,
+ GstEvent * event)
+diff --git a/gst/rtpmanager/rtpsession.c b/gst/rtpmanager/rtpsession.c
+index 8b33b6b..aa8b40b 100644
+--- a/gst/rtpmanager/rtpsession.c
++++ b/gst/rtpmanager/rtpsession.c
+@@ -3263,7 +3263,6 @@ early_exit:
+ typedef struct
+ {
+ RTPSource *source;
+- gboolean is_bye;
+ GstBuffer *buffer;
+ } ReportOutput;
+
+@@ -3874,7 +3873,6 @@ static void
+ generate_rtcp (const gchar * key, RTPSource * source, ReportData * data)
+ {
+ RTPSession *sess = data->sess;
+- gboolean is_bye = FALSE;
+ ReportOutput *output;
+
+ /* only generate RTCP for active internal sources */
+@@ -3893,7 +3891,6 @@ generate_rtcp (const gchar * key, RTPSource * source, ReportData * data)
+ if (source->marked_bye) {
+ /* send BYE */
+ make_source_bye (sess, source, data);
+- is_bye = TRUE;
+ } else if (!data->is_early) {
+ /* loop over all known sources and add report blocks. If we are early, we
+ * just make a minimal RTCP packet and skip this step */
+@@ -3918,7 +3915,6 @@ generate_rtcp (const gchar * key, RTPSource * source, ReportData * data)
+
+ output = g_slice_new (ReportOutput);
+ output->source = g_object_ref (source);
+- output->is_bye = is_bye;
+ output->buffer = data->rtcp;
+ /* queue the RTCP packet to push later */
+ g_queue_push_tail (&data->output, output);
+@@ -4098,7 +4094,7 @@ done:
+ GST_DEBUG ("%p, sending RTCP packet, avg size %u, %u", &sess->stats,
+ sess->stats.avg_rtcp_packet_size, packet_size);
+ result =
+- sess->callbacks.send_rtcp (sess, source, buffer, output->is_bye,
++ sess->callbacks.send_rtcp (sess, source, buffer,
+ sess->send_rtcp_user_data);
+
+ RTP_SESSION_LOCK (sess);
+diff --git a/gst/rtpmanager/rtpsession.h b/gst/rtpmanager/rtpsession.h
+index 9fa9327..c25981a 100644
+--- a/gst/rtpmanager/rtpsession.h
++++ b/gst/rtpmanager/rtpsession.h
+@@ -71,7 +71,6 @@ typedef GstFlowReturn (*RTPSessionSendRTP) (RTPSession *sess, RTPSource *src, gp
+ * @sess: an #RTPSession
+ * @src: the #RTPSource
+ * @buffer: the RTCP buffer ready for sending
+- * @eos: if an EOS event should be pushed
+ * @user_data: user data specified when registering
+ *
+ * This callback will be called when @sess has @buffer ready for sending to
+@@ -80,7 +79,7 @@ typedef GstFlowReturn (*RTPSessionSendRTP) (RTPSession *sess, RTPSource *src, gp
+ * Returns: a #GstFlowReturn.
+ */
+ typedef GstFlowReturn (*RTPSessionSendRTCP) (RTPSession *sess, RTPSource *src, GstBuffer *buffer,
+- gboolean eos, gpointer user_data);
++ gpointer user_data);
+
+ /**
+ * RTPSessionSyncRTCP:
+--
+2.10.2
+
Added: trunk/Tools/gtk/patches/gst-plugins-good-0003-rtpbin-receive-bundle-support.patch (0 => 208940)
--- trunk/Tools/gtk/patches/gst-plugins-good-0003-rtpbin-receive-bundle-support.patch (rev 0)
+++ trunk/Tools/gtk/patches/gst-plugins-good-0003-rtpbin-receive-bundle-support.patch 2016-11-21 08:09:03 UTC (rev 208940)
@@ -0,0 +1,1018 @@
+From dcd3ce9751cdef0b5ab1fa118355f92bdfe82cb3 Mon Sep 17 00:00:00 2001
+From: Philippe Normand <[email protected]>
+Date: Wed, 16 Nov 2016 08:56:34 +0100
+Subject: [PATCH] rtpbin: receive bundle support
+
+A new signal named on-bundled-ssrc is provided and can be
+used by the application to redirect a stream to a different
+GstRtpSession or to keep the RTX stream grouped within the
+GstRtpSession of the same media type.
+
+https://bugzilla.gnome.org/show_bug.cgi?id=772740
+---
+ docs/plugins/gst-plugins-good-plugins.signals | 8 +
+ gst/rtpmanager/gstrtpbin.c | 562 ++++++++++++++++++--------
+ gst/rtpmanager/gstrtpbin.h | 2 +
+ tests/check/Makefile.am | 4 +
+ tests/check/elements/.gitignore | 1 +
+ tests/check/elements/rtpbundle.c | 390 ++++++++++++++++++
+ tests/check/meson.build | 1 +
+ tests/examples/rtp/.gitignore | 2 +
+ tests/examples/rtp/Makefile.am | 10 +-
+ tests/examples/rtp/client-rtpbundle.c | 266 ++++++++++++
+ tests/examples/rtp/server-rtpbundle.c | 179 ++++++++
+ 11 files changed, 1265 insertions(+), 160 deletions(-)
+ create mode 100644 tests/check/elements/rtpbundle.c
+ create mode 100644 tests/examples/rtp/client-rtpbundle.c
+ create mode 100644 tests/examples/rtp/server-rtpbundle.c
+
+diff --git a/docs/plugins/gst-plugins-good-plugins.signals b/docs/plugins/gst-plugins-good-plugins.signals
+index 3db17e9..44bbdda 100644
+--- a/docs/plugins/gst-plugins-good-plugins.signals
++++ b/docs/plugins/gst-plugins-good-plugins.signals
+@@ -375,6 +375,14 @@ guint arg1
+ </SIGNAL>
+
+ <SIGNAL>
++<NAME>GstRtpBin::on-bundled-ssrc</NAME>
++<RETURNS>guint</RETURNS>
++<FLAGS>l</FLAGS>
++GstRtpBin *gstrtpbin
++guint arg1
++</SIGNAL>
++
++<SIGNAL>
+ <NAME>GstRtpJitterBuffer::clear-pt-map</NAME>
+ <RETURNS>void</RETURNS>
+ <FLAGS>la</FLAGS>
+diff --git a/gst/rtpmanager/gstrtpbin.c b/gst/rtpmanager/gstrtpbin.c
+index 648adb9..f58de01 100644
+--- a/gst/rtpmanager/gstrtpbin.c
++++ b/gst/rtpmanager/gstrtpbin.c
+@@ -53,6 +53,13 @@
+ * SSRC in the RTP packets to its own SSRC and wil forward the packets on the
+ * send_rtp_src_\%u pad after updating its internal state.
+ *
++ * #GstRtpBin can also demultiplex incoming bundled streams. The first
++ * #GstRtpSession will have a #GstRtpSsrcDemux element splitting the streams
++ * based on their SSRC and potentially dispatched to a different #GstRtpSession.
++ * Because retransmission SSRCs need to be merged with the corresponding media
++ * stream the #GstRtpBin::on-bundled-ssrc signal is emitted so that the
++ * application can find out to which session the SSRC belongs.
++ *
+ * The session manager needs the clock-rate of the payload types it is handling
+ * and will signal the #GstRtpSession::request-pt-map signal when it needs such a
+ * mapping. One can clear the cached values with the #GstRtpSession::clear-pt-map
+@@ -276,6 +283,8 @@ enum
+ SIGNAL_ON_NEW_SENDER_SSRC,
+ SIGNAL_ON_SENDER_SSRC_ACTIVE,
+
++ SIGNAL_ON_BUNDLED_SSRC,
++
+ LAST_SIGNAL
+ };
+
+@@ -362,6 +371,14 @@ static void remove_send_rtp (GstRtpBin * rtpbin, GstRtpBinSession * session);
+ static void remove_rtcp (GstRtpBin * rtpbin, GstRtpBinSession * session);
+ static void free_client (GstRtpBinClient * client, GstRtpBin * bin);
+ static void free_stream (GstRtpBinStream * stream, GstRtpBin * bin);
++static GstRtpBinSession *create_session (GstRtpBin * rtpbin, gint id);
++static GstPad *complete_session_sink (GstRtpBin * rtpbin,
++ GstRtpBinSession * session, gboolean bundle_demuxer_needed);
++static void
++complete_session_receiver (GstRtpBin * rtpbin, GstRtpBinSession * session,
++ guint sessid);
++static GstPad *complete_session_rtcp (GstRtpBin * rtpbin,
++ GstRtpBinSession * session, guint sessid, gboolean bundle_demuxer_needed);
+
+ /* Manages the RTP stream for one SSRC.
+ *
+@@ -428,6 +445,12 @@ struct _GstRtpBinSession
+ gulong demux_newpad_sig;
+ gulong demux_padremoved_sig;
+
++ /* Bundling support */
++ GstElement *rtp_funnel;
++ GstElement *rtcp_funnel;
++ GstElement *bundle_demux;
++ gulong bundle_demux_newpad_sig;
++
+ GMutex lock;
+
+ /* list of GstRtpBinStream */
+@@ -629,6 +652,96 @@ ssrc_demux_pad_removed (GstElement * element, guint ssrc, GstPad * pad,
+ GST_RTP_BIN_UNLOCK (rtpbin);
+ }
+
++static void
++new_bundled_ssrc_pad_found (GstElement * element, guint ssrc, GstPad * pad,
++ GstRtpBinSession * session)
++{
++ GValue result = G_VALUE_INIT;
++ GValue params[2] = { G_VALUE_INIT, G_VALUE_INIT };
++ guint session_id = 0;
++ GstRtpBinSession *target_session = NULL;
++ GstRtpBin *rtpbin = session->bin;
++ gchar *name;
++ GstPad *src_pad;
++ GstPad *recv_rtp_sink = NULL;
++ GstPad *recv_rtcp_sink = NULL;
++ GstPadLinkReturn ret;
++
++ GST_RTP_BIN_DYN_LOCK (rtpbin);
++ GST_DEBUG_OBJECT (rtpbin, "new bundled SSRC pad %08x, %s:%s", ssrc,
++ GST_DEBUG_PAD_NAME (pad));
++
++ g_value_init (&result, G_TYPE_UINT);
++ g_value_init (¶ms[0], GST_TYPE_ELEMENT);
++ g_value_set_object (¶ms[0], rtpbin);
++ g_value_init (¶ms[1], G_TYPE_UINT);
++ g_value_set_uint (¶ms[1], ssrc);
++
++ g_signal_emitv (params,
++ gst_rtp_bin_signals[SIGNAL_ON_BUNDLED_SSRC], 0, &result);
++ g_value_unset (¶ms[0]);
++
++ session_id = g_value_get_uint (&result);
++ if (session_id == 0) {
++ target_session = session;
++ } else {
++ target_session = find_session_by_id (rtpbin, (gint) session_id);
++ if (!target_session) {
++ target_session = create_session (rtpbin, session_id);
++ }
++ if (!target_session->recv_rtp_sink) {
++ recv_rtp_sink = complete_session_sink (rtpbin, target_session, FALSE);
++ }
++
++ if (!target_session->recv_rtp_src)
++ complete_session_receiver (rtpbin, target_session, session_id);
++
++ if (!target_session->recv_rtcp_sink) {
++ recv_rtcp_sink =
++ complete_session_rtcp (rtpbin, target_session, session_id, FALSE);
++ }
++ }
++
++ GST_DEBUG_OBJECT (rtpbin, "Assigning bundled ssrc %u to session %u", ssrc,
++ session_id);
++
++ if (!recv_rtp_sink) {
++ recv_rtp_sink =
++ gst_element_get_request_pad (target_session->rtp_funnel, "sink_%u");
++ }
++
++ if (!recv_rtcp_sink) {
++ recv_rtcp_sink =
++ gst_element_get_request_pad (target_session->rtcp_funnel, "sink_%u");
++ }
++
++ name = g_strdup_printf ("src_%u", ssrc);
++ src_pad = gst_element_get_static_pad (element, name);
++ ret = gst_pad_link (src_pad, recv_rtp_sink);
++ g_free (name);
++ gst_object_unref (src_pad);
++ gst_object_unref (recv_rtp_sink);
++ if (ret != GST_PAD_LINK_OK) {
++ g_warning
++ ("rtpbin: failed to link bundle demuxer to receive rtp funnel for session %u",
++ session_id);
++ }
++
++ name = g_strdup_printf ("rtcp_src_%u", ssrc);
++ src_pad = gst_element_get_static_pad (element, name);
++ gst_pad_link (src_pad, recv_rtcp_sink);
++ g_free (name);
++ gst_object_unref (src_pad);
++ gst_object_unref (recv_rtcp_sink);
++ if (ret != GST_PAD_LINK_OK) {
++ g_warning
++ ("rtpbin: failed to link bundle demuxer to receive rtcp sink pad for session %u",
++ session_id);
++ }
++
++ GST_RTP_BIN_DYN_UNLOCK (rtpbin);
++}
++
+ /* create a session with the given id. Must be called with RTP_BIN_LOCK */
+ static GstRtpBinSession *
+ create_session (GstRtpBin * rtpbin, gint id)
+@@ -649,6 +762,10 @@ create_session (GstRtpBin * rtpbin, gint id)
+ sess->bin = rtpbin;
+ sess->session = session;
+ sess->demux = demux;
++
++ sess->rtp_funnel = gst_element_factory_make ("funnel", NULL);
++ sess->rtcp_funnel = gst_element_factory_make ("funnel", NULL);
++
+ sess->ptmap = g_hash_table_new_full (NULL, NULL, NULL,
+ (GDestroyNotify) gst_caps_unref);
+ rtpbin->sessions = g_slist_prepend (rtpbin->sessions, sess);
+@@ -696,6 +813,8 @@ create_session (GstRtpBin * rtpbin, gint id)
+
+ gst_bin_add (GST_BIN_CAST (rtpbin), session);
+ gst_bin_add (GST_BIN_CAST (rtpbin), demux);
++ gst_bin_add (GST_BIN_CAST (rtpbin), sess->rtp_funnel);
++ gst_bin_add (GST_BIN_CAST (rtpbin), sess->rtcp_funnel);
+
+ GST_OBJECT_LOCK (rtpbin);
+ target = GST_STATE_TARGET (rtpbin);
+@@ -704,6 +823,8 @@ create_session (GstRtpBin * rtpbin, gint id)
+ /* change state only to what's needed */
+ gst_element_set_state (demux, target);
+ gst_element_set_state (session, target);
++ gst_element_set_state (sess->rtp_funnel, target);
++ gst_element_set_state (sess->rtcp_funnel, target);
+
+ return sess;
+
+@@ -807,7 +928,7 @@ get_pt_map (GstRtpBinSession * session, guint pt)
+ GValue ret = { 0 };
+ GValue args[3] = { {0}, {0}, {0} };
+
+- GST_DEBUG ("searching pt %d in cache", pt);
++ GST_DEBUG ("searching pt %u in cache", pt);
+
+ GST_RTP_SESSION_LOCK (session);
+
+@@ -820,7 +941,7 @@ get_pt_map (GstRtpBinSession * session, guint pt)
+
+ bin = session->bin;
+
+- GST_DEBUG ("emiting signal for pt %d in session %d", pt, session->id);
++ GST_DEBUG ("emiting signal for pt %u in session %u", pt, session->id);
+
+ /* not in cache, send signal to request caps */
+ g_value_init (&args[0], GST_TYPE_ELEMENT);
+@@ -856,7 +977,7 @@ get_pt_map (GstRtpBinSession * session, guint pt)
+ if (!caps)
+ goto no_caps;
+
+- GST_DEBUG ("caching pt %d as %" GST_PTR_FORMAT, pt, caps);
++ GST_DEBUG ("caching pt %u as %" GST_PTR_FORMAT, pt, caps);
+
+ /* store in cache, take additional ref */
+ g_hash_table_insert (session->ptmap, GINT_TO_POINTER (pt),
+@@ -947,7 +1068,7 @@ gst_rtp_bin_get_session (GstRtpBin * bin, guint session_id)
+ GstElement *ret = NULL;
+
+ GST_RTP_BIN_LOCK (bin);
+- GST_DEBUG_OBJECT (bin, "retrieving GstRtpSession, index: %d", session_id);
++ GST_DEBUG_OBJECT (bin, "retrieving GstRtpSession, index: %u", session_id);
+ session = find_session_by_id (bin, (gint) session_id);
+ if (session) {
+ ret = gst_object_ref (session->session);
+@@ -964,7 +1085,7 @@ gst_rtp_bin_get_internal_session (GstRtpBin * bin, guint session_id)
+ GstRtpBinSession *session;
+
+ GST_RTP_BIN_LOCK (bin);
+- GST_DEBUG_OBJECT (bin, "retrieving internal RTPSession object, index: %d",
++ GST_DEBUG_OBJECT (bin, "retrieving internal RTPSession object, index: %u",
+ session_id);
+ session = find_session_by_id (bin, (gint) session_id);
+ if (session) {
+@@ -2194,6 +2315,29 @@ gst_rtp_bin_class_init (GstRtpBinClass * klass)
+ on_sender_ssrc_active), NULL, NULL, g_cclosure_marshal_generic,
+ G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_UINT);
+
++
++ /**
++ * GstRtpBin::on-bundled-ssrc:
++ * @rtpbin: the object which received the signal
++ * @ssrc: the bundled SSRC
++ *
++ * Notify of a new incoming bundled SSRC. If no handler is connected to the
++ * signal then the #GstRtpSession created for the recv_rtp_sink_\%u
++ * request pad will be managing this new SSRC. However if there is a handler
++ * connected then the application can decided to dispatch this new stream to
++ * another session by providing its ID as return value of the handler. This
++ * can be particularly useful to keep retransmission SSRCs grouped with the
++ * session for which they handle retransmission.
++ *
++ * Since: 1.12
++ */
++ gst_rtp_bin_signals[SIGNAL_ON_BUNDLED_SSRC] =
++ g_signal_new ("on-bundled-ssrc", G_TYPE_FROM_CLASS (klass),
++ G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpBinClass,
++ on_bundled_ssrc), NULL, NULL,
++ g_cclosure_marshal_generic, G_TYPE_UINT, 1, G_TYPE_UINT);
++
++
+ g_object_class_install_property (gobject_class, PROP_SDES,
+ g_param_spec_boxed ("sdes", "SDES",
+ "The SDES items of this session",
+@@ -3021,7 +3165,7 @@ new_payload_found (GstElement * element, guint pt, GstPad * pad,
+
+ rtpbin = stream->bin;
+
+- GST_DEBUG ("new payload pad %d", pt);
++ GST_DEBUG_OBJECT (rtpbin, "new payload pad %u", pt);
+
+ GST_RTP_BIN_SHUTDOWN_LOCK (rtpbin, shutdown);
+
+@@ -3078,7 +3222,7 @@ pt_map_requested (GstElement * element, guint pt, GstRtpBinSession * session)
+
+ rtpbin = session->bin;
+
+- GST_DEBUG_OBJECT (rtpbin, "payload map requested for pt %d in session %d", pt,
++ GST_DEBUG_OBJECT (rtpbin, "payload map requested for pt %u in session %u", pt,
+ session->id);
+
+ caps = get_pt_map (session, pt);
+@@ -3099,7 +3243,7 @@ static void
+ payload_type_change (GstElement * element, guint pt, GstRtpBinSession * session)
+ {
+ GST_DEBUG_OBJECT (session->bin,
+- "emiting signal for pt type changed to %d in session %d", pt,
++ "emiting signal for pt type changed to %u in session %u", pt,
+ session->id);
+
+ g_signal_emit (session->bin, gst_rtp_bin_signals[SIGNAL_PAYLOAD_TYPE_CHANGE],
+@@ -3246,15 +3390,42 @@ no_stream:
+ }
+ }
+
+-static gboolean
+-complete_session_sink (GstRtpBin * rtpbin, GstRtpBinSession * session)
++static void
++session_maybe_create_bundle_demuxer (GstRtpBinSession * session)
++{
++ GstRtpBin *rtpbin;
++
++ if (session->bundle_demux)
++ return;
++
++ rtpbin = session->bin;
++ if (g_signal_has_handler_pending (rtpbin,
++ gst_rtp_bin_signals[SIGNAL_ON_BUNDLED_SSRC], 0, TRUE)) {
++ GST_DEBUG_OBJECT (rtpbin, "Adding a bundle SSRC demuxer to session %u",
++ session->id);
++ session->bundle_demux = gst_element_factory_make ("rtpssrcdemux", NULL);
++ session->bundle_demux_newpad_sig = g_signal_connect (session->bundle_demux,
++ "new-ssrc-pad", (GCallback) new_bundled_ssrc_pad_found, session);
++
++ gst_bin_add (GST_BIN_CAST (rtpbin), session->bundle_demux);
++ gst_element_sync_state_with_parent (session->bundle_demux);
++ } else {
++ GST_DEBUG_OBJECT (rtpbin,
++ "No handler for the on-bundled-ssrc signal so no need for a bundle SSRC demuxer in session %u",
++ session->id);
++ }
++}
++
++static GstPad *
++complete_session_sink (GstRtpBin * rtpbin, GstRtpBinSession * session,
++ gboolean bundle_demuxer_needed)
+ {
+- gchar *gname;
+ guint sessid = session->id;
+ GstPad *recv_rtp_sink;
++ GstPad *funnel_src;
+ GstElement *decoder;
+- GstElementClass *klass;
+- GstPadTemplate *templ;
++
++ g_assert (!session->recv_rtp_sink);
+
+ /* get recv_rtp pad and store */
+ session->recv_rtp_sink =
+@@ -3265,6 +3436,9 @@ complete_session_sink (GstRtpBin * rtpbin, GstRtpBinSession * session)
+ g_signal_connect (session->recv_rtp_sink, "notify::caps",
+ (GCallback) caps_changed, session);
+
++ if (bundle_demuxer_needed)
++ session_maybe_create_bundle_demuxer (session);
++
+ GST_DEBUG_OBJECT (rtpbin, "requesting RTP decoder");
+ decoder = session_request_element (session, SIGNAL_REQUEST_RTP_DECODER);
+ if (decoder) {
+@@ -3282,7 +3456,14 @@ complete_session_sink (GstRtpBin * rtpbin, GstRtpBinSession * session)
+ if (decsrc == NULL)
+ goto dec_src_failed;
+
+- ret = gst_pad_link (decsrc, session->recv_rtp_sink);
++ if (session->bundle_demux) {
++ GstPad *demux_sink;
++ demux_sink = gst_element_get_static_pad (session->bundle_demux, "sink");
++ ret = gst_pad_link (decsrc, demux_sink);
++ gst_object_unref (demux_sink);
++ } else {
++ ret = gst_pad_link (decsrc, session->recv_rtp_sink);
++ }
+ gst_object_unref (decsrc);
+
+ if (ret != GST_PAD_LINK_OK)
+@@ -3290,81 +3471,54 @@ complete_session_sink (GstRtpBin * rtpbin, GstRtpBinSession * session)
+
+ } else {
+ GST_DEBUG_OBJECT (rtpbin, "no RTP decoder given");
+- recv_rtp_sink = gst_object_ref (session->recv_rtp_sink);
++ if (session->bundle_demux) {
++ recv_rtp_sink =
++ gst_element_get_static_pad (session->bundle_demux, "sink");
++ } else {
++ recv_rtp_sink =
++ gst_element_get_request_pad (session->rtp_funnel, "sink_%u");
++ }
+ }
+
+- GST_DEBUG_OBJECT (rtpbin, "ghosting session sink pad");
+- klass = GST_ELEMENT_GET_CLASS (rtpbin);
+- gname = g_strdup_printf ("recv_rtp_sink_%u", sessid);
+- templ = gst_element_class_get_pad_template (klass, "recv_rtp_sink_%u");
+- session->recv_rtp_sink_ghost =
+- gst_ghost_pad_new_from_template (gname, recv_rtp_sink, templ);
+- gst_object_unref (recv_rtp_sink);
+- gst_pad_set_active (session->recv_rtp_sink_ghost, TRUE);
+- gst_element_add_pad (GST_ELEMENT_CAST (rtpbin), session->recv_rtp_sink_ghost);
+- g_free (gname);
++ funnel_src = gst_element_get_static_pad (session->rtp_funnel, "src");
++ gst_pad_link (funnel_src, session->recv_rtp_sink);
++ gst_object_unref (funnel_src);
+
+- return TRUE;
++ return recv_rtp_sink;
+
+ /* ERRORS */
+ pad_failed:
+ {
+ g_warning ("rtpbin: failed to get session recv_rtp_sink pad");
+- return FALSE;
++ return NULL;
+ }
+ dec_sink_failed:
+ {
+- g_warning ("rtpbin: failed to get decoder sink pad for session %d", sessid);
+- return FALSE;
++ g_warning ("rtpbin: failed to get decoder sink pad for session %u", sessid);
++ return NULL;
+ }
+ dec_src_failed:
+ {
+- g_warning ("rtpbin: failed to get decoder src pad for session %d", sessid);
++ g_warning ("rtpbin: failed to get decoder src pad for session %u", sessid);
+ gst_object_unref (recv_rtp_sink);
+- return FALSE;
++ return NULL;
+ }
+ dec_link_failed:
+ {
+- g_warning ("rtpbin: failed to link rtp decoder for session %d", sessid);
++ g_warning ("rtpbin: failed to link rtp decoder for session %u", sessid);
+ gst_object_unref (recv_rtp_sink);
+- return FALSE;
++ return NULL;
+ }
+ }
+
+-/* Create a pad for receiving RTP for the session in @name. Must be called with
+- * RTP_BIN_LOCK.
+- */
+-static GstPad *
+-create_recv_rtp (GstRtpBin * rtpbin, GstPadTemplate * templ, const gchar * name)
++static void
++complete_session_receiver (GstRtpBin * rtpbin, GstRtpBinSession * session,
++ guint sessid)
+ {
+- guint sessid;
+ GstElement *aux;
+ GstPad *recv_rtp_src;
+- GstRtpBinSession *session;
+-
+- /* first get the session number */
+- if (name == NULL || sscanf (name, "recv_rtp_sink_%u", &sessid) != 1)
+- goto no_name;
+-
+- GST_DEBUG_OBJECT (rtpbin, "finding session %d", sessid);
+
+- /* get or create session */
+- session = find_session_by_id (rtpbin, sessid);
+- if (!session) {
+- GST_DEBUG_OBJECT (rtpbin, "creating session %d", sessid);
+- /* create session now */
+- session = create_session (rtpbin, sessid);
+- if (session == NULL)
+- goto create_error;
+- }
+-
+- /* check if pad was requested */
+- if (session->recv_rtp_sink_ghost != NULL)
+- return session->recv_rtp_sink_ghost;
+-
+- /* setup the session sink pad */
+- if (!complete_session_sink (rtpbin, session))
+- goto session_sink_failed;
++ g_assert (!session->recv_rtp_src);
+
+ session->recv_rtp_src =
+ gst_element_get_static_pad (session->session, "recv_rtp_src");
+@@ -3381,7 +3535,7 @@ create_recv_rtp (GstRtpBin * rtpbin, GstPadTemplate * templ, const gchar * name)
+
+ GST_DEBUG_OBJECT (rtpbin, "linking AUX receiver");
+
+- pname = g_strdup_printf ("sink_%d", sessid);
++ pname = g_strdup_printf ("sink_%u", sessid);
+ auxsink = gst_element_get_static_pad (aux, pname);
+ g_free (pname);
+ if (auxsink == NULL)
+@@ -3394,7 +3548,7 @@ create_recv_rtp (GstRtpBin * rtpbin, GstPadTemplate * templ, const gchar * name)
+
+ /* this can be NULL when this AUX element is not to be linked to
+ * an SSRC demuxer */
+- pname = g_strdup_printf ("src_%d", sessid);
++ pname = g_strdup_printf ("src_%u", sessid);
+ recv_rtp_src = gst_element_get_static_pad (aux, pname);
+ g_free (pname);
+ } else {
+@@ -3408,8 +3562,8 @@ create_recv_rtp (GstRtpBin * rtpbin, GstPadTemplate * templ, const gchar * name)
+ sinkdpad = gst_element_get_static_pad (session->demux, "sink");
+ GST_DEBUG_OBJECT (rtpbin, "linking demuxer RTP sink pad");
+ gst_pad_link_full (recv_rtp_src, sinkdpad, GST_PAD_LINK_CHECK_NOTHING);
+- gst_object_unref (recv_rtp_src);
+ gst_object_unref (sinkdpad);
++ gst_object_unref (recv_rtp_src);
+
+ /* connect to the new-ssrc-pad signal of the SSRC demuxer */
+ session->demux_newpad_sig = g_signal_connect (session->demux,
+@@ -3417,6 +3571,71 @@ create_recv_rtp (GstRtpBin * rtpbin, GstPadTemplate * templ, const gchar * name)
+ session->demux_padremoved_sig = g_signal_connect (session->demux,
+ "removed-ssrc-pad", (GCallback) ssrc_demux_pad_removed, session);
+ }
++
++ return;
++
++pad_failed:
++ {
++ g_warning ("rtpbin: failed to get session recv_rtp_src pad");
++ return;
++ }
++aux_sink_failed:
++ {
++ g_warning ("rtpbin: failed to get AUX sink pad for session %u", sessid);
++ return;
++ }
++aux_link_failed:
++ {
++ g_warning ("rtpbin: failed to link AUX pad to session %u", sessid);
++ return;
++ }
++}
++
++/* Create a pad for receiving RTP for the session in @name. Must be called with
++ * RTP_BIN_LOCK.
++ */
++static GstPad *
++create_recv_rtp (GstRtpBin * rtpbin, GstPadTemplate * templ, const gchar * name)
++{
++ guint sessid;
++ GstRtpBinSession *session;
++ GstPad *recv_rtp_sink;
++
++ /* first get the session number */
++ if (name == NULL || sscanf (name, "recv_rtp_sink_%u", &sessid) != 1)
++ goto no_name;
++
++ GST_DEBUG_OBJECT (rtpbin, "finding session %u", sessid);
++
++ /* get or create session */
++ session = find_session_by_id (rtpbin, sessid);
++ if (!session) {
++ GST_DEBUG_OBJECT (rtpbin, "creating session %u", sessid);
++ /* create session now */
++ session = create_session (rtpbin, sessid);
++ if (session == NULL)
++ goto create_error;
++ }
++
++ /* check if pad was requested */
++ if (session->recv_rtp_sink_ghost != NULL)
++ return session->recv_rtp_sink_ghost;
++
++ /* setup the session sink pad */
++ recv_rtp_sink = complete_session_sink (rtpbin, session, TRUE);
++ if (!recv_rtp_sink)
++ goto session_sink_failed;
++
++
++ GST_DEBUG_OBJECT (rtpbin, "ghosting session sink pad");
++ session->recv_rtp_sink_ghost =
++ gst_ghost_pad_new_from_template (name, recv_rtp_sink, templ);
++ gst_object_unref (recv_rtp_sink);
++ gst_pad_set_active (session->recv_rtp_sink_ghost, TRUE);
++ gst_element_add_pad (GST_ELEMENT_CAST (rtpbin), session->recv_rtp_sink_ghost);
++
++ complete_session_receiver (rtpbin, session, sessid);
++
+ return session->recv_rtp_sink_ghost;
+
+ /* ERRORS */
+@@ -3435,21 +3654,6 @@ session_sink_failed:
+ /* warning already done */
+ return NULL;
+ }
+-pad_failed:
+- {
+- g_warning ("rtpbin: failed to get session recv_rtp_src pad");
+- return NULL;
+- }
+-aux_sink_failed:
+- {
+- g_warning ("rtpbin: failed to get AUX sink pad for session %d", sessid);
+- return NULL;
+- }
+-aux_link_failed:
+- {
+- g_warning ("rtpbin: failed to link AUX pad to session %d", sessid);
+- return NULL;
+- }
+ }
+
+ static void
+@@ -3463,6 +3667,11 @@ remove_recv_rtp (GstRtpBin * rtpbin, GstRtpBinSession * session)
+ g_signal_handler_disconnect (session->demux, session->demux_padremoved_sig);
+ session->demux_padremoved_sig = 0;
+ }
++ if (session->bundle_demux_newpad_sig) {
++ g_signal_handler_disconnect (session->bundle_demux,
++ session->bundle_demux_newpad_sig);
++ session->bundle_demux_newpad_sig = 0;
++ }
+ if (session->recv_rtp_src) {
+ gst_object_unref (session->recv_rtp_src);
+ session->recv_rtp_src = NULL;
+@@ -3480,37 +3689,14 @@ remove_recv_rtp (GstRtpBin * rtpbin, GstRtpBinSession * session)
+ }
+ }
+
+-/* Create a pad for receiving RTCP for the session in @name. Must be called with
+- * RTP_BIN_LOCK.
+- */
+ static GstPad *
+-create_recv_rtcp (GstRtpBin * rtpbin, GstPadTemplate * templ,
+- const gchar * name)
++complete_session_rtcp (GstRtpBin * rtpbin, GstRtpBinSession * session,
++ guint sessid, gboolean bundle_demuxer_needed)
+ {
+- guint sessid;
+ GstElement *decoder;
+- GstRtpBinSession *session;
+- GstPad *sinkdpad, *decsink;
+-
+- /* first get the session number */
+- if (name == NULL || sscanf (name, "recv_rtcp_sink_%u", &sessid) != 1)
+- goto no_name;
+-
+- GST_DEBUG_OBJECT (rtpbin, "finding session %d", sessid);
+-
+- /* get or create the session */
+- session = find_session_by_id (rtpbin, sessid);
+- if (!session) {
+- GST_DEBUG_OBJECT (rtpbin, "creating session %d", sessid);
+- /* create session now */
+- session = create_session (rtpbin, sessid);
+- if (session == NULL)
+- goto create_error;
+- }
+-
+- /* check if pad was requested */
+- if (session->recv_rtcp_sink_ghost != NULL)
+- return session->recv_rtcp_sink_ghost;
++ GstPad *sinkdpad;
++ GstPad *decsink = NULL;
++ GstPad *funnel_src;
+
+ /* get recv_rtp pad and store */
+ GST_DEBUG_OBJECT (rtpbin, "getting RTCP sink pad");
+@@ -3519,6 +3705,9 @@ create_recv_rtcp (GstRtpBin * rtpbin, GstPadTemplate * templ,
+ if (session->recv_rtcp_sink == NULL)
+ goto pad_failed;
+
++ if (bundle_demuxer_needed)
++ session_maybe_create_bundle_demuxer (session);
++
+ GST_DEBUG_OBJECT (rtpbin, "getting RTCP decoder");
+ decoder = session_request_element (session, SIGNAL_REQUEST_RTCP_DECODER);
+ if (decoder) {
+@@ -3535,14 +3724,26 @@ create_recv_rtcp (GstRtpBin * rtpbin, GstPadTemplate * templ,
+ if (decsrc == NULL)
+ goto dec_src_failed;
+
+- ret = gst_pad_link (decsrc, session->recv_rtcp_sink);
++ if (session->bundle_demux) {
++ GstPad *demux_sink;
++ demux_sink =
++ gst_element_get_static_pad (session->bundle_demux, "rtcp_sink");
++ ret = gst_pad_link (decsrc, demux_sink);
++ gst_object_unref (demux_sink);
++ } else {
++ ret = gst_pad_link (decsrc, session->recv_rtcp_sink);
++ }
+ gst_object_unref (decsrc);
+
+ if (ret != GST_PAD_LINK_OK)
+ goto dec_link_failed;
+ } else {
+ GST_DEBUG_OBJECT (rtpbin, "no RTCP decoder given");
+- decsink = gst_object_ref (session->recv_rtcp_sink);
++ if (session->bundle_demux) {
++ decsink = gst_element_get_static_pad (session->bundle_demux, "rtcp_sink");
++ } else {
++ decsink = gst_element_get_request_pad (session->rtcp_funnel, "sink_%u");
++ }
+ }
+
+ /* get srcpad, link to SSRCDemux */
+@@ -3556,26 +3757,12 @@ create_recv_rtcp (GstRtpBin * rtpbin, GstPadTemplate * templ,
+ gst_pad_link_full (session->sync_src, sinkdpad, GST_PAD_LINK_CHECK_NOTHING);
+ gst_object_unref (sinkdpad);
+
+- session->recv_rtcp_sink_ghost =
+- gst_ghost_pad_new_from_template (name, decsink, templ);
+- gst_object_unref (decsink);
+- gst_pad_set_active (session->recv_rtcp_sink_ghost, TRUE);
+- gst_element_add_pad (GST_ELEMENT_CAST (rtpbin),
+- session->recv_rtcp_sink_ghost);
++ funnel_src = gst_element_get_static_pad (session->rtcp_funnel, "src");
++ gst_pad_link (funnel_src, session->recv_rtcp_sink);
++ gst_object_unref (funnel_src);
+
+- return session->recv_rtcp_sink_ghost;
++ return decsink;
+
+- /* ERRORS */
+-no_name:
+- {
+- g_warning ("rtpbin: invalid name given");
+- return NULL;
+- }
+-create_error:
+- {
+- /* create_session already warned */
+- return NULL;
+- }
+ pad_failed:
+ {
+ g_warning ("rtpbin: failed to get session rtcp_sink pad");
+@@ -3583,25 +3770,82 @@ pad_failed:
+ }
+ dec_sink_failed:
+ {
+- g_warning ("rtpbin: failed to get decoder sink pad for session %d", sessid);
++ g_warning ("rtpbin: failed to get decoder sink pad for session %u", sessid);
+ return NULL;
+ }
+ dec_src_failed:
+ {
+- g_warning ("rtpbin: failed to get decoder src pad for session %d", sessid);
+- gst_object_unref (decsink);
+- return NULL;
++ g_warning ("rtpbin: failed to get decoder src pad for session %u", sessid);
++ goto cleanup;
+ }
+ dec_link_failed:
+ {
+- g_warning ("rtpbin: failed to link rtcp decoder for session %d", sessid);
+- gst_object_unref (decsink);
+- return NULL;
++ g_warning ("rtpbin: failed to link rtcp decoder for session %u", sessid);
++ goto cleanup;
+ }
+ src_pad_failed:
+ {
+ g_warning ("rtpbin: failed to get session sync_src pad");
+- gst_object_unref (decsink);
++ }
++
++cleanup:
++ gst_object_unref (decsink);
++ return NULL;
++}
++
++/* Create a pad for receiving RTCP for the session in @name. Must be called with
++ * RTP_BIN_LOCK.
++ */
++static GstPad *
++create_recv_rtcp (GstRtpBin * rtpbin, GstPadTemplate * templ,
++ const gchar * name)
++{
++ guint sessid;
++ GstRtpBinSession *session;
++ GstPad *decsink = NULL;
++
++ /* first get the session number */
++ if (name == NULL || sscanf (name, "recv_rtcp_sink_%u", &sessid) != 1)
++ goto no_name;
++
++ GST_DEBUG_OBJECT (rtpbin, "finding session %u", sessid);
++
++ /* get or create the session */
++ session = find_session_by_id (rtpbin, sessid);
++ if (!session) {
++ GST_DEBUG_OBJECT (rtpbin, "creating session %u", sessid);
++ /* create session now */
++ session = create_session (rtpbin, sessid);
++ if (session == NULL)
++ goto create_error;
++ }
++
++ /* check if pad was requested */
++ if (session->recv_rtcp_sink_ghost != NULL)
++ return session->recv_rtcp_sink_ghost;
++
++ decsink = complete_session_rtcp (rtpbin, session, sessid, TRUE);
++ if (!decsink)
++ goto create_error;
++
++ session->recv_rtcp_sink_ghost =
++ gst_ghost_pad_new_from_template (name, decsink, templ);
++ gst_object_unref (decsink);
++ gst_pad_set_active (session->recv_rtcp_sink_ghost, TRUE);
++ gst_element_add_pad (GST_ELEMENT_CAST (rtpbin),
++ session->recv_rtcp_sink_ghost);
++
++ return session->recv_rtcp_sink_ghost;
++
++ /* ERRORS */
++no_name:
++ {
++ g_warning ("rtpbin: invalid name given");
++ return NULL;
++ }
++create_error:
++ {
++ /* create_session already warned */
+ return NULL;
+ }
+ }
+@@ -3651,7 +3895,7 @@ complete_session_src (GstRtpBin * rtpbin, GstRtpBinSession * session)
+ GstPadLinkReturn ret;
+
+ GST_DEBUG_OBJECT (rtpbin, "linking RTP encoder");
+- ename = g_strdup_printf ("rtp_src_%d", sessid);
++ ename = g_strdup_printf ("rtp_src_%u", sessid);
+ encsrc = gst_element_get_static_pad (encoder, ename);
+ g_free (ename);
+
+@@ -3660,7 +3904,7 @@ complete_session_src (GstRtpBin * rtpbin, GstRtpBinSession * session)
+
+ send_rtp_src = encsrc;
+
+- ename = g_strdup_printf ("rtp_sink_%d", sessid);
++ ename = g_strdup_printf ("rtp_sink_%u", sessid);
+ encsink = gst_element_get_static_pad (encoder, ename);
+ g_free (ename);
+ if (encsink == NULL)
+@@ -3694,23 +3938,23 @@ complete_session_src (GstRtpBin * rtpbin, GstRtpBinSession * session)
+ /* ERRORS */
+ no_srcpad:
+ {
+- g_warning ("rtpbin: failed to get rtp source pad for session %d", sessid);
++ g_warning ("rtpbin: failed to get rtp source pad for session %u", sessid);
+ return FALSE;
+ }
+ enc_src_failed:
+ {
+- g_warning ("rtpbin: failed to get encoder src pad for session %d", sessid);
++ g_warning ("rtpbin: failed to get encoder src pad for session %u", sessid);
+ return FALSE;
+ }
+ enc_sink_failed:
+ {
+- g_warning ("rtpbin: failed to get encoder sink pad for session %d", sessid);
++ g_warning ("rtpbin: failed to get encoder sink pad for session %u", sessid);
+ gst_object_unref (send_rtp_src);
+ return FALSE;
+ }
+ enc_link_failed:
+ {
+- g_warning ("rtpbin: failed to link rtp encoder for session %d", sessid);
++ g_warning ("rtpbin: failed to link rtp encoder for session %u", sessid);
+ gst_object_unref (send_rtp_src);
+ return FALSE;
+ }
+@@ -3772,22 +4016,22 @@ create_error:
+ }
+ existing_session:
+ {
+- g_warning ("rtpbin: session %d is already a sender", sessid);
++ g_warning ("rtpbin: session %u is already a sender", sessid);
+ return FALSE;
+ }
+ pad_failed:
+ {
+- g_warning ("rtpbin: failed to get session pad for session %d", sessid);
++ g_warning ("rtpbin: failed to get session pad for session %u", sessid);
+ return FALSE;
+ }
+ aux_link_failed:
+ {
+- g_warning ("rtpbin: failed to link AUX for session %d", sessid);
++ g_warning ("rtpbin: failed to link AUX for session %u", sessid);
+ return FALSE;
+ }
+ session_src_failed:
+ {
+- g_warning ("rtpbin: failed to complete AUX for session %d", sessid);
++ g_warning ("rtpbin: failed to complete AUX for session %u", sessid);
+ return FALSE;
+ }
+ }
+@@ -3847,7 +4091,7 @@ create_send_rtp (GstRtpBin * rtpbin, GstPadTemplate * templ, const gchar * name)
+ if (!setup_aux_sender (rtpbin, session, aux))
+ goto aux_session_failed;
+
+- pname = g_strdup_printf ("sink_%d", sessid);
++ pname = g_strdup_printf ("sink_%u", sessid);
+ send_rtp_sink = gst_element_get_static_pad (aux, pname);
+ g_free (pname);
+
+@@ -3887,27 +4131,27 @@ create_error:
+ }
+ existing_session:
+ {
+- g_warning ("rtpbin: session %d is already in use", sessid);
++ g_warning ("rtpbin: session %u is already in use", sessid);
+ return NULL;
+ }
+ aux_session_failed:
+ {
+- g_warning ("rtpbin: failed to get AUX sink pad for session %d", sessid);
++ g_warning ("rtpbin: failed to get AUX sink pad for session %u", sessid);
+ return NULL;
+ }
+ aux_sink_failed:
+ {
+- g_warning ("rtpbin: failed to get AUX sink pad for session %d", sessid);
++ g_warning ("rtpbin: failed to get AUX sink pad for session %u", sessid);
+ return NULL;
+ }
+ pad_failed:
+ {
+- g_warning ("rtpbin: failed to get session pad for session %d", sessid);
++ g_warning ("rtpbin: failed to get session pad for session %u", sessid);
+ return NULL;
+ }
+ session_src_failed:
+ {
+- g_warning ("rtpbin: failed to setup source pads for session %d", sessid);
++ g_warning ("rtpbin: failed to setup source pads for session %u", sessid);
+ return NULL;
+ }
+ }
+@@ -3978,13 +4222,13 @@ create_rtcp (GstRtpBin * rtpbin, GstPadTemplate * templ, const gchar * name)
+
+ GST_DEBUG_OBJECT (rtpbin, "linking RTCP encoder");
+
+- ename = g_strdup_printf ("rtcp_src_%d", sessid);
++ ename = g_strdup_printf ("rtcp_src_%u", sessid);
+ encsrc = gst_element_get_static_pad (encoder, ename);
+ g_free (ename);
+ if (encsrc == NULL)
+ goto enc_src_failed;
+
+- ename = g_strdup_printf ("rtcp_sink_%d", sessid);
++ ename = g_strdup_printf ("rtcp_sink_%u", sessid);
+ encsink = gst_element_get_static_pad (encoder, ename);
+ g_free (ename);
+ if (encsink == NULL)
+@@ -4021,23 +4265,23 @@ no_session:
+ }
+ pad_failed:
+ {
+- g_warning ("rtpbin: failed to get rtcp pad for session %d", sessid);
++ g_warning ("rtpbin: failed to get rtcp pad for session %u", sessid);
+ return NULL;
+ }
+ enc_src_failed:
+ {
+- g_warning ("rtpbin: failed to get encoder src pad for session %d", sessid);
++ g_warning ("rtpbin: failed to get encoder src pad for session %u", sessid);
+ return NULL;
+ }
+ enc_sink_failed:
+ {
+- g_warning ("rtpbin: failed to get encoder sink pad for session %d", sessid);
++ g_warning ("rtpbin: failed to get encoder sink pad for session %u", sessid);
+ gst_object_unref (encsrc);
+ return NULL;
+ }
+ enc_link_failed:
+ {
+- g_warning ("rtpbin: failed to link rtcp encoder for session %d", sessid);
++ g_warning ("rtpbin: failed to link rtcp encoder for session %u", sessid);
+ gst_object_unref (encsrc);
+ return NULL;
+ }
+diff --git a/gst/rtpmanager/gstrtpbin.h b/gst/rtpmanager/gstrtpbin.h
+index fb13a47..384b76d 100644
+--- a/gst/rtpmanager/gstrtpbin.h
++++ b/gst/rtpmanager/gstrtpbin.h
+@@ -127,6 +127,8 @@ struct _GstRtpBinClass {
+
+ void (*on_new_sender_ssrc) (GstRtpBin *rtpbin, guint session, guint32 ssrc);
+ void (*on_sender_ssrc_active) (GstRtpBin *rtpbin, guint session, guint32 ssrc);
++
++ guint (*on_bundled_ssrc) (GstRtpBin *rtpbin, guint ssrc);
+ };
+
+ GType gst_rtp_bin_get_type (void);