* SECTION:element-gstrtpbin
* @see_also: gstrtpjitterbuffer, gstrtpsession, gstrtpptdemux, gstrtpssrcdemux
*
- * <refsect2>
- * <para>
- * RTP bin combines the functions of gstrtpsession, gstrtpssrcdemux, gstrtpjitterbuffer
- * and gstrtpptdemux in one element. It allows for multiple RTP sessions that will
- * be synchronized together using RTCP SR packets.
- * </para>
- * <para>
- * gstrtpbin is configured with a number of request pads that define the
- * functionality that is activated, similar to the gstrtpsession element.
- * </para>
- * <para>
- * To use gstrtpbin as an RTP receiver, request a recv_rtp_sink_%%d pad. The session
+ * RTP bin combines the functions of #GstRtpSession, #GstRtpsSrcDemux,
+ * #GstRtpJitterBuffer and #GstRtpPtDemux in one element. It allows for multiple
+ * RTP sessions that will be synchronized together using RTCP SR packets.
+ *
+ * #GstRtpBin is configured with a number of request pads that define the
+ * functionality that is activated, similar to the #GstRtpSession element.
+ *
+ * To use #GstRtpBin as an RTP receiver, request a recv_rtp_sink_%%d pad. The session
* number must be specified in the pad name.
* Data received on the recv_rtp_sink_%%d pad will be processed in the gstrtpsession
- * manager and after being validated forwarded on gstrtpssrcdemuxer element. Each
- * RTP stream is demuxed based on the SSRC and send to a gstrtpjitterbuffer. After
+ * manager and after being validated forwarded on #GstRtpsSrcDemux element. Each
+ * RTP stream is demuxed based on the SSRC and send to a #GstRtpJitterBuffer. After
* the packets are released from the jitterbuffer, they will be forwarded to a
- * gstrtpptdemuxer element. The gstrtpptdemuxer element will demux the packets based
+ * #GstRtpsSrcDemux element. The #GstRtpsSrcDemux element will demux the packets based
* on the payload type and will create a unique pad recv_rtp_src_%%d_%%d_%%d on
* gstrtpbin with the session number, SSRC and payload type respectively as the pad
* name.
- * </para>
- * <para>
- * To also use gstrtpbin as an RTCP receiver, request a recv_rtcp_sink_%%d pad. The
+ *
+ * To also use #GstRtpBin as an RTCP receiver, request a recv_rtcp_sink_%%d pad. The
* session number must be specified in the pad name.
- * </para>
- * <para>
+ *
* If you want the session manager to generate and send RTCP packets, request
* the send_rtcp_src_%%d pad with the session number in the pad name. Packet pushed
* on this pad contain SR/RR RTCP reports that should be sent to all participants
* in the session.
- * </para>
- * <para>
- * To use gstrtpbin as a sender, request a send_rtp_sink_%%d pad, which will
+ *
+ * To use #GstRtpBin as a sender, request a send_rtp_sink_%%d pad, which will
* automatically create a send_rtp_src_%%d pad. If the session number is not provided,
* the pad from the lowest available session will be returned. The session manager will modify the
* SSRC in the RTP packets to its own SSRC and wil forward the packets on the
* send_rtp_src_%%d pad after updating its internal state.
- * </para>
- * <para>
+ *
* 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
+ * 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
* signal.
- * </para>
+ *
+ * <refsect2>
* <title>Example pipelines</title>
- * <para>
- * <programlisting>
+ * |[
* gst-launch udpsrc port=5000 caps="application/x-rtp, ..." ! .recv_rtp_sink_0 \
* gstrtpbin ! rtptheoradepay ! theoradec ! xvimagesink
- * </programlisting>
- * Receive RTP data from port 5000 and send to the session 0 in gstrtpbin.
- * </para>
- * <para>
- * <programlisting>
+ * ]| Receive RTP data from port 5000 and send to the session 0 in gstrtpbin.
+ * |[
* gst-launch gstrtpbin name=rtpbin \
* v4l2src ! ffmpegcolorspace ! ffenc_h263 ! rtph263ppay ! rtpbin.send_rtp_sink_0 \
* rtpbin.send_rtp_src_0 ! udpsink port=5000 \
* rtpbin.send_rtp_src_1 ! udpsink port=5002 \
* rtpbin.send_rtcp_src_1 ! udpsink port=5003 sync=false async=false \
* udpsrc port=5007 ! rtpbin.recv_rtcp_sink_1
- * </programlisting>
- * Encode and payload H263 video captured from a v4l2src. Encode and payload AMR
+ * ]| Encode and payload H263 video captured from a v4l2src. Encode and payload AMR
* audio generated from audiotestsrc. The video is sent to session 0 in rtpbin
* and the audio is sent to session 1. Video packets are sent on UDP port 5000
* and audio packets on port 5002. The video RTCP packets for session 0 are sent
* is received on port 5007. Since RTCP packets from the sender should be sent
* as soon as possible and do not participate in preroll, sync=false and
* async=false is configured on udpsink
- * </para>
- * <para>
- * <programlisting>
- * gst-launch -v gstrtpbin name=rtpbin \
+ * |[
+ * gst-launch -v gstrtpbin name=rtpbin \
* udpsrc caps="application/x-rtp,media=(string)video,clock-rate=(int)90000,encoding-name=(string)H263-1998" \
* port=5000 ! rtpbin.recv_rtp_sink_0 \
* rtpbin. ! rtph263pdepay ! ffdec_h263 ! xvimagesink \
* rtpbin. ! rtpamrdepay ! amrnbdec ! alsasink \
* udpsrc port=5003 ! rtpbin.recv_rtcp_sink_1 \
* rtpbin.send_rtcp_src_1 ! udpsink port=5007 sync=false async=false
- * </programlisting>
- * Receive H263 on port 5000, send it through rtpbin in session 0, depayload,
+ * ]| Receive H263 on port 5000, send it through rtpbin in session 0, depayload,
* decode and display the video.
* Receive AMR on port 5002, send it through rtpbin in session 1, depayload,
* decode and play the audio.
* synchronisation.
* Send RTCP reports for session 0 on port 5005 and RTCP reports for session 1
* on port 5007.
- * </para>
* </refsect2>
*
* Last reviewed on 2007-08-30 (0.10.6)
#include "gstrtpbin-marshal.h"
#include "gstrtpbin.h"
+#include "gstrtpsession.h"
+#include "gstrtpjitterbuffer.h"
GST_DEBUG_CATEGORY_STATIC (gst_rtp_bin_debug);
#define GST_CAT_DEFAULT gst_rtp_bin_debug
SIGNAL_ON_BYE_SSRC,
SIGNAL_ON_BYE_TIMEOUT,
SIGNAL_ON_TIMEOUT,
+ SIGNAL_ON_SENDER_TIMEOUT,
LAST_SIGNAL
};
gint64 unix_delta;
/* for lip-sync */
+ guint64 last_clock_base;
guint64 clock_base;
guint64 clock_base_time;
gint clock_rate;
gint64 ts_offset;
- gint64 prev_ts_offset;
gint last_pt;
};
sess->id, ssrc);
}
+static void
+on_sender_timeout (GstElement * session, guint32 ssrc, GstRtpBinSession * sess)
+{
+ g_signal_emit (sess->bin, gst_rtp_bin_signals[SIGNAL_ON_SENDER_TIMEOUT], 0,
+ sess->id, ssrc);
+}
+
/* create a session with the given id. Must be called with RTP_BIN_LOCK */
static GstRtpBinSession *
create_session (GstRtpBin * rtpbin, gint id)
g_signal_connect (sess->session, "on-bye-timeout",
(GCallback) on_bye_timeout, sess);
g_signal_connect (sess->session, "on-timeout", (GCallback) on_timeout, sess);
+ g_signal_connect (sess->session, "on-sender-timeout",
+ (GCallback) on_sender_timeout, sess);
/* FIXME, change state only to what's needed */
gst_bin_add (GST_BIN_CAST (rtpbin), session);
g_value_init (&ret, GST_TYPE_CAPS);
g_value_set_boxed (&ret, NULL);
+ GST_RTP_SESSION_UNLOCK (session);
+
g_signal_emitv (args, gst_rtp_bin_signals[SIGNAL_REQUEST_PT_MAP], 0, &ret);
+ GST_RTP_SESSION_LOCK (session);
+
g_value_unset (&args[0]);
g_value_unset (&args[1]);
g_value_unset (&args[2]);
+
+ /* look in the cache again because we let the lock go */
+ caps = g_hash_table_lookup (session->ptmap, GINT_TO_POINTER (pt));
+ if (caps) {
+ gst_caps_ref (caps);
+ g_value_unset (&ret);
+ goto done;
+ }
+
caps = (GstCaps *) g_value_dup_boxed (&ret);
g_value_unset (&ret);
if (!caps)
/* calculate offsets for each stream */
for (walk = client->streams; walk; walk = g_slist_next (walk)) {
GstRtpBinStream *ostream = (GstRtpBinStream *) walk->data;
-
- if (ostream->unix_delta == 0)
- continue;
+ gint64 prev_ts_offset;
ostream->ts_offset = ostream->unix_delta - min;
+ g_object_get (ostream->buffer, "ts-offset", &prev_ts_offset, NULL);
+
/* delta changed, see how much */
- if (ostream->prev_ts_offset != ostream->ts_offset) {
+ if (prev_ts_offset != ostream->ts_offset) {
gint64 diff;
- if (ostream->prev_ts_offset > ostream->ts_offset)
- diff = ostream->prev_ts_offset - ostream->ts_offset;
+ if (prev_ts_offset > ostream->ts_offset)
+ diff = prev_ts_offset - ostream->ts_offset;
else
- diff = ostream->ts_offset - ostream->prev_ts_offset;
+ diff = ostream->ts_offset - prev_ts_offset;
+
+ GST_DEBUG_OBJECT (bin,
+ "ts-offset %" G_GUINT64_FORMAT ", prev %" G_GUINT64_FORMAT
+ ", diff: %" G_GINT64_FORMAT, ostream->ts_offset, prev_ts_offset,
+ diff);
- /* only change diff when it changed more than 1 millisecond. This
+ /* only change diff when it changed more than 4 milliseconds. This
* compensates for rounding errors in NTP to RTP timestamp
* conversions */
- if (diff > GST_MSECOND)
+ if (diff > 4 * GST_MSECOND && diff < (3 * GST_SECOND)) {
g_object_set (ostream->buffer, "ts-offset", ostream->ts_offset, NULL);
-
- ostream->prev_ts_offset = ostream->ts_offset;
+ }
}
GST_DEBUG_OBJECT (bin, "stream SSRC %08x, delta %" G_GINT64_FORMAT,
ostream->ssrc, ostream->ts_offset);
guint32 rtptime;
gboolean have_sr, have_sdes;
gboolean more;
+ guint64 clock_base;
+ guint64 clock_base_time;
stream = gst_pad_get_element_private (pad);
bin = stream->bin;
if (!gst_rtcp_buffer_validate (buffer))
goto invalid_rtcp;
+ /* get the last relation between the rtp timestamps and the gstreamer
+ * timestamps. We get this info directly from the jitterbuffer which
+ * constructs gstreamer timestamps from rtp timestamps */
+ gst_rtp_jitter_buffer_get_sync (GST_RTP_JITTER_BUFFER (stream->buffer),
+ &clock_base, &clock_base_time);
+
+ /* clock base changes when there is a huge gap in the timestamps or seqnum.
+ * When this happens we don't want to calculate the extended timestamp based
+ * on the previous one but reset the calculation. */
+ if (stream->last_clock_base != clock_base) {
+ stream->last_extrtptime = -1;
+ stream->last_clock_base = clock_base;
+ }
+
have_sr = FALSE;
have_sdes = FALSE;
GST_RTCP_BUFFER_FOR_PACKETS (more, buffer, &packet) {
gst_rtcp_packet_sdes_get_entry (&packet, &type, &len, &data);
if (type == GST_RTCP_SDES_CNAME) {
- stream->clock_base = GST_BUFFER_OFFSET (buffer);
- stream->clock_base_time = GST_BUFFER_OFFSET_END (buffer);
+ stream->clock_base = clock_base;
+ stream->clock_base_time = clock_base_time;
/* associate the stream to CNAME */
gst_rtp_bin_associate (bin, stream, len, data);
}
* @rtpbin: the object which received the signal
*
* Clear all previously cached pt-mapping obtained with
- * GstRtpBin::request-pt-map.
+ * #GstRtpBin::request-pt-map.
*/
gst_rtp_bin_signals[SIGNAL_CLEAR_PT_MAP] =
g_signal_new ("clear-pt-map", G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpBinClass, on_timeout),
NULL, NULL, gst_rtp_bin_marshal_VOID__UINT_UINT, G_TYPE_NONE, 2,
G_TYPE_UINT, G_TYPE_UINT);
+ /**
+ * GstRtpBin::on-sender-timeout:
+ * @rtpbin: the object which received the signal
+ * @session: the session
+ * @ssrc: the SSRC
+ *
+ * Notify of a sender SSRC that has timed out and became a receiver
+ */
+ gst_rtp_bin_signals[SIGNAL_ON_SENDER_TIMEOUT] =
+ g_signal_new ("on-sender-timeout", G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpBinClass, on_sender_timeout),
+ NULL, NULL, gst_rtp_bin_marshal_VOID__UINT_UINT, G_TYPE_NONE, 2,
+ G_TYPE_UINT, G_TYPE_UINT);
g_object_class_install_property (gobject_class, PROP_SDES_CNAME,
g_param_spec_string ("sdes-cname", "SDES CNAME",
rtpbin = session->bin;
- GST_DEBUG_OBJECT (rtpbin, "new SSRC pad %08x", ssrc);
+ GST_DEBUG_OBJECT (rtpbin, "new SSRC pad %08x, %s:%s", ssrc,
+ GST_DEBUG_PAD_NAME (pad));
GST_RTP_BIN_SHUTDOWN_LOCK (rtpbin, shutdown);
gst_caps_to_string (caps), GST_DEBUG_PAD_NAME (pad));
}
+ stream->last_clock_base = -1;
if (gst_structure_get_uint (s, "clock-base", &val))
stream->clock_base = val;
else
GstRtpBin *rtpbin;
GstElementClass *klass;
GstPad *result;
+
gchar *pad_name = NULL;
g_return_val_if_fail (templ != NULL, NULL);