rtpbin: Regularly emit the sync signal

Even if no new synchronization information is available.

This is necessary because the timestamp offset logic in rtpbin depends
on the base RTP time that is determined by the jitterbuffer, but this
changes all the time (especially in mode=slave) and the timestamp
offsets have to be updated accordingly. Doing so is especially important
if they're only determined by the RTP-Info, which never changes from the
very beginning.

The interval can be configured via the new min-sync-interval property.
Synchronization happens at least that often, but at most as often as the
old sync-interval property allows.
Both intervals are now based on the monotonic system clock.

Additionally, clean up synchronization code a bit, only emit either
inband NTP or RTCP SR synchronization at the same time, based on which
one has the more recent time information, and only emit RTP-Info
synchronization if it wasn't provided previously at the same time as the
NTP-based synchronization information.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6543>
This commit is contained in:
Sebastian Dröge 2024-04-18 09:16:05 +03:00 committed by GStreamer Marge Bot
parent df8c29e340
commit 9156b373e6
2 changed files with 281 additions and 159 deletions

View file

@ -19047,6 +19047,20 @@
"type": "guint64",
"writable": true
},
"min-sync-interval": {
"blurb": "Minimum RTCP SR / NTP-64 interval synchronization (ms)",
"conditionally-available": false,
"construct": false,
"construct-only": false,
"controllable": false,
"default": "0",
"max": "-1",
"min": "0",
"mutable": "null",
"readable": true,
"type": "guint",
"writable": true
},
"mode": {
"blurb": "Control the buffering algorithm in use",
"conditionally-available": false,
@ -29145,4 +29159,4 @@
"tracers": {},
"url": "Unknown package origin"
}
}
}

View file

@ -160,6 +160,7 @@ enum
#define DEFAULT_SYNC_INTERVAL 0
#define DEFAULT_RFC7273_USE_SYSTEM_CLOCK FALSE
#define DEFAULT_RFC7273_REFERENCE_TIMESTAMP_META_ONLY FALSE
#define DEFAULT_MIN_SYNC_INTERVAL 15000
#define DEFAULT_AUTO_RTX_DELAY (20 * GST_MSECOND)
#define DEFAULT_AUTO_RTX_TIMEOUT (40 * GST_MSECOND)
@ -197,6 +198,7 @@ enum
PROP_SYNC_INTERVAL,
PROP_RFC7273_USE_SYSTEM_CLOCK,
PROP_RFC7273_REFERENCE_TIMESTAMP_META_ONLY,
PROP_MIN_SYNC_INTERVAL,
};
#define JBUF_LOCK(priv) G_STMT_START { \
@ -343,6 +345,7 @@ struct _GstRtpJitterBufferPrivate
guint sync_interval;
gboolean rfc7273_use_system_clock;
gboolean rfc7273_reference_timestamp_meta_only;
guint min_sync_interval;
/* Reference for GstReferenceTimestampMeta */
GstCaps *reference_timestamp_caps;
@ -396,7 +399,6 @@ struct _GstRtpJitterBufferPrivate
gint32 clock_rate;
gint64 clock_base;
gint64 ts_offset_remainder;
gboolean rtsp_sync_done;
/* when we are shutting down */
GstFlowReturn srcresult;
@ -410,14 +412,31 @@ struct _GstRtpJitterBufferPrivate
/* the latency of the upstream peer, we have to take this into account when
* synchronizing the buffers. */
GstClockTime peer_latency;
guint64 last_sr_ext_rtptime;
/* Last RTCP SR */
GstBuffer *last_sr;
guint32 last_sr_ssrc;
GstClockTime last_sr_ntpnstime;
guint64 last_sr_ext_rtptime;
gboolean have_new_sr;
/* Last inband NTP time */
GstClockTime last_inband_ntpnstime;
guint64 last_inband_ext_rtptime;
gboolean have_new_inband;
/* Latest known NTP/RTP time mapping from either RTCP SR or inband */
GstClockTime last_known_ntpnstime;
guint64 last_known_ext_rtptime;
/* If RTP-Info sync is pending */
gboolean rtp_info_sync_pending;
/* Last time the sync signal was emitted based on the monotonic system clock */
gint64 last_sync_time;
/* If we ever synced NTP times */
gboolean have_synced_ntp;
/* some accounting */
guint64 num_pushed;
guint64 num_lost;
@ -434,7 +453,6 @@ struct _GstRtpJitterBufferPrivate
GstClockTime last_dts;
GstClockTime last_pts;
guint64 last_rtptime;
GstClockTime last_ntpnstime;
GstClockTime avg_jitter;
/* for dropped packet messages */
@ -580,10 +598,11 @@ gst_rtp_jitter_buffer_clear_pt_map (GstRtpJitterBuffer * jitterbuffer);
static GstClockTime
gst_rtp_jitter_buffer_set_active (GstRtpJitterBuffer * jitterbuffer,
gboolean active, guint64 base_time);
static void do_handle_sync (GstRtpJitterBuffer * jitterbuffer);
static void do_handle_sync (GstRtpJitterBuffer * jitterbuffer, gint64 now);
static void do_handle_sync_inband (GstRtpJitterBuffer * jitterbuffer,
guint64 ntpnstime);
static void do_handle_sync_rtsp (GstRtpJitterBuffer * jitterbuffer);
gint64 now);
static void do_handle_sync_rtp_info (GstRtpJitterBuffer * jitterbuffer,
gint64 now);
static void unschedule_current_timer (GstRtpJitterBuffer * jitterbuffer);
@ -1057,6 +1076,19 @@ gst_rtp_jitter_buffer_class_init (GstRtpJitterBufferClass * klass)
DEFAULT_RFC7273_REFERENCE_TIMESTAMP_META_ONLY,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
/**
* GstRtpJitterBuffer:min-sync-interval:
*
* Determines how often to sync streams using RTCP data or inband NTP-64
* header extensions, even if no new RTCP SR information is available.
*
* Since: 1.26
*/
g_object_class_install_property (gobject_class, PROP_SYNC_INTERVAL,
g_param_spec_uint ("min-sync-interval", "Minimum Sync Interval",
"Minimum RTCP SR / NTP-64 interval synchronization (ms)",
0, G_MAXUINT, DEFAULT_MIN_SYNC_INTERVAL,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
/**
* GstRtpJitterBuffer::request-pt-map:
@ -1192,14 +1224,12 @@ gst_rtp_jitter_buffer_init (GstRtpJitterBuffer * jitterbuffer)
priv->rfc7273_use_system_clock = DEFAULT_RFC7273_USE_SYSTEM_CLOCK;
priv->rfc7273_reference_timestamp_meta_only =
DEFAULT_RFC7273_REFERENCE_TIMESTAMP_META_ONLY;
priv->min_sync_interval = DEFAULT_MIN_SYNC_INTERVAL;
priv->ts_offset_remainder = 0;
priv->last_dts = -1;
priv->last_pts = -1;
priv->last_rtptime = -1;
priv->last_ntpnstime = -1;
priv->last_known_ext_rtptime = -1;
priv->last_known_ntpnstime = -1;
priv->avg_jitter = 0;
priv->last_drop_msg_timestamp = GST_CLOCK_TIME_NONE;
priv->num_too_late = 0;
@ -1221,7 +1251,14 @@ gst_rtp_jitter_buffer_init (GstRtpJitterBuffer * jitterbuffer)
rtp_jitter_buffer_set_delay (priv->jbuf, priv->latency_ns);
rtp_jitter_buffer_set_buffering (priv->jbuf, FALSE);
priv->active = TRUE;
priv->rtsp_sync_done = FALSE;
priv->last_sr_ext_rtptime = -1;
priv->last_sr_ntpnstime = -1;
priv->last_inband_ext_rtptime = -1;
priv->last_inband_ntpnstime = -1;
priv->last_known_ext_rtptime = -1;
priv->last_known_ntpnstime = -1;
priv->last_sync_time = -1;
priv->srcpad =
gst_pad_new_from_static_template (&gst_rtp_jitter_buffer_src_template,
@ -1464,7 +1501,7 @@ gst_rtp_jitter_buffer_clear_pt_map (GstRtpJitterBuffer * jitterbuffer)
/* do not clear current content, but refresh state for new arrival */
GST_DEBUG_OBJECT (jitterbuffer, "reset jitterbuffer");
rtp_jitter_buffer_reset_skew (priv->jbuf);
priv->rtsp_sync_done = FALSE;
priv->last_sync_time = -1;
JBUF_UNLOCK (priv);
}
@ -1655,8 +1692,7 @@ gst_jitter_buffer_sink_parse_caps (GstRtpJitterBuffer * jitterbuffer,
priv->npt_stop = tval;
else
priv->npt_stop = -1;
priv->rtsp_sync_done = FALSE;
priv->rtp_info_sync_pending = TRUE;
GST_DEBUG_OBJECT (jitterbuffer,
"npt start/stop: %" GST_TIME_FORMAT "-%" GST_TIME_FORMAT,
@ -1845,9 +1881,17 @@ gst_rtp_jitter_buffer_flush_stop (GstRtpJitterBuffer * jitterbuffer)
priv->avg_jitter = 0;
priv->last_dts = -1;
priv->last_rtptime = -1;
priv->last_ntpnstime = -1;
priv->last_sr_ext_rtptime = -1;
priv->last_sr_ntpnstime = -1;
priv->last_inband_ext_rtptime = -1;
priv->last_inband_ntpnstime = -1;
priv->last_known_ext_rtptime = -1;
priv->last_known_ntpnstime = -1;
priv->last_sync_time = -1;
priv->have_synced_ntp = FALSE;
priv->have_new_inband = FALSE;
priv->have_new_sr = FALSE;
priv->rtp_info_sync_pending = TRUE;
priv->last_in_pts = 0;
priv->equidistant = 0;
priv->segment_seqnum = GST_SEQNUM_INVALID;
@ -1861,7 +1905,7 @@ gst_rtp_jitter_buffer_flush_stop (GstRtpJitterBuffer * jitterbuffer)
rtp_jitter_buffer_flush (priv->jbuf, NULL, NULL);
rtp_jitter_buffer_disable_buffering (priv->jbuf, FALSE);
rtp_jitter_buffer_reset_skew (priv->jbuf);
priv->rtsp_sync_done = FALSE;
priv->last_sync_time = -1;
rtp_timer_queue_remove_all (priv->timers);
g_queue_foreach (&priv->gap_packets, (GFunc) gst_buffer_unref, NULL);
g_queue_clear (&priv->gap_packets);
@ -1930,6 +1974,10 @@ gst_rtp_jitter_buffer_change_state (GstElement * element,
g_list_free_full (priv->cname_ssrc_mappings,
(GDestroyNotify) cname_ssrc_mapping_free);
priv->cname_ssrc_mappings = NULL;
priv->have_new_sr = FALSE;
priv->have_new_inband = FALSE;
priv->rtp_info_sync_pending = FALSE;
priv->have_synced_ntp = FALSE;
/* block until we go to PLAYING */
priv->blocked = TRUE;
priv->timer_running = TRUE;
@ -3081,7 +3129,7 @@ gst_rtp_jitter_buffer_reset (GstRtpJitterBuffer * jitterbuffer,
rtp_timer_queue_remove_all (priv->timers);
priv->discont = TRUE;
priv->last_popped_seqnum = -1;
priv->rtsp_sync_done = FALSE;
priv->last_sync_time = -1;
if (priv->gap_packets.head) {
GstBuffer *gap_buffer = priv->gap_packets.head->data;
@ -3499,8 +3547,18 @@ gst_rtp_jitter_buffer_chain (GstPad * pad, GstObject * parent,
ext_rtptime = priv->jbuf->ext_rtptime;
ext_rtptime = gst_rtp_buffer_ext_timestamp (&ext_rtptime, rtptime);
priv->last_known_ext_rtptime = ext_rtptime;
priv->last_known_ntpnstime = inband_ntp_time;
if (priv->last_inband_ntpnstime == GST_CLOCK_TIME_NONE ||
priv->last_inband_ntpnstime < inband_ntp_time) {
priv->last_inband_ntpnstime = inband_ntp_time;
priv->last_inband_ext_rtptime = ext_rtptime;
priv->have_new_inband = TRUE;
}
if (priv->last_known_ntpnstime == GST_CLOCK_TIME_NONE
|| priv->last_known_ntpnstime < inband_ntp_time) {
priv->last_known_ext_rtptime = ext_rtptime;
priv->last_known_ntpnstime = inband_ntp_time;
}
}
if (is_rtx) {
@ -3651,15 +3709,13 @@ gst_rtp_jitter_buffer_chain (GstPad * pad, GstObject * parent,
update_rtx_timers (jitterbuffer, seqnum, dts, pts, do_next_seqnum, is_rtx,
g_steal_pointer (&timer));
if (priv->seqnum_base != -1)
do_handle_sync_rtsp (jitterbuffer);
/* do sync if necessary: prefer inband, otherwise RTCP SR, otherwise
* RTP-Info */
gint64 now_monotonic = g_get_monotonic_time ();
/* we had an unhandled SR, handle it now */
if (priv->last_sr)
do_handle_sync (jitterbuffer);
if (inband_ntp_time != GST_CLOCK_TIME_NONE)
do_handle_sync_inband (jitterbuffer, inband_ntp_time);
do_handle_sync_inband (jitterbuffer, now_monotonic);
do_handle_sync (jitterbuffer, now_monotonic);
do_handle_sync_rtp_info (jitterbuffer, now_monotonic);
if (G_UNLIKELY (head)) {
/* signal addition of new buffer when the _loop is waiting. */
@ -4647,33 +4703,29 @@ pause:
}
static void
do_handle_sync_rtsp (GstRtpJitterBuffer * jitterbuffer)
do_handle_sync_rtp_info (GstRtpJitterBuffer * jitterbuffer, gint64 now)
{
GstRtpJitterBufferPrivate *priv;
GstRtpJitterBufferPrivate *priv = jitterbuffer->priv;
guint64 base_rtptime, base_time;
guint32 clock_rate;
guint64 clock_base;
guint64 npt_start;
priv = jitterbuffer->priv;
if (priv->rtsp_sync_done)
return;
/* clock-base is the RTP timestamp at npt-start */
const guint64 clock_base = priv->clock_base;
const guint64 npt_start = priv->npt_start;
/* no RTSP information in the caps */
if (priv->clock_base == -1 || priv->npt_start == -1) {
priv->rtsp_sync_done = TRUE;
if (priv->clock_base == -1 || priv->npt_start == -1)
return;
/* don't sync again if not required yet */
if (!priv->rtp_info_sync_pending && priv->last_sync_time != -1 &&
now - priv->last_sync_time < MAX (priv->sync_interval,
priv->min_sync_interval) * 1000)
return;
}
/* get the last values from the jitterbuffer */
rtp_jitter_buffer_get_sync (priv->jbuf, &base_rtptime, &base_time,
&clock_rate, NULL);
/* clock-base is the RTP timestamp at npt-start */
npt_start = priv->npt_start;
clock_base = priv->clock_base;
GST_DEBUG_OBJECT (jitterbuffer,
"NPT start %" GST_TIME_FORMAT ", clock-base %" G_GUINT64_FORMAT ", base %"
GST_TIME_FORMAT ", base RTP time %" G_GUINT64_FORMAT ", clock-rate %"
@ -4683,10 +4735,12 @@ do_handle_sync_rtsp (GstRtpJitterBuffer * jitterbuffer)
if (base_rtptime == -1 || clock_rate == -1 || base_time == -1) {
GST_ERROR_OBJECT (jitterbuffer,
"No base RTP time, clock rate or base time");
priv->rtsp_sync_done = TRUE;
return;
}
priv->last_sync_time = now;
priv->rtp_info_sync_pending = FALSE;
GstStructure *s;
GList *l;
@ -4713,25 +4767,26 @@ do_handle_sync_rtsp (GstRtpJitterBuffer * jitterbuffer)
gst_rtp_jitter_buffer_signals[SIGNAL_HANDLE_SYNC], 0, s);
JBUF_LOCK (priv);
gst_structure_free (s);
priv->rtsp_sync_done = TRUE;
}
static void
do_handle_sync_inband (GstRtpJitterBuffer * jitterbuffer, guint64 ntpnstime)
do_handle_sync_inband (GstRtpJitterBuffer * jitterbuffer, gint64 now)
{
GstRtpJitterBufferPrivate *priv;
GstRtpJitterBufferPrivate *priv = jitterbuffer->priv;
GstStructure *s;
guint64 base_rtptime, base_time;
guint32 clock_rate;
guint64 last_rtptime;
const GstClockTime ntpnstime = priv->last_inband_ntpnstime;
const guint64 ext_rtptime = priv->last_inband_ext_rtptime;
const guint64 clock_base = priv->clock_base;
const guint64 npt_start = priv->npt_start;
gboolean have_new_inband;
const gchar *cname = NULL;
GList *l;
priv = jitterbuffer->priv;
/* get the last values from the jitterbuffer */
rtp_jitter_buffer_get_sync (priv->jbuf, &base_rtptime, &base_time,
&clock_rate, &last_rtptime);
/* Have no inband NTP time */
if (ntpnstime == -1)
return;
for (l = priv->cname_ssrc_mappings; l; l = l->next) {
const CNameSSRCMapping *map = l->data;
@ -4742,39 +4797,64 @@ do_handle_sync_inband (GstRtpJitterBuffer * jitterbuffer, guint64 ntpnstime)
}
}
GST_DEBUG_OBJECT (jitterbuffer,
"inband NTP-64 %" GST_TIME_FORMAT " rtptime %" G_GUINT64_FORMAT ", base %"
G_GUINT64_FORMAT ", clock-rate %" G_GUINT32_FORMAT ", clock-base %"
G_GUINT64_FORMAT ", CNAME %s", GST_TIME_ARGS (ntpnstime), last_rtptime,
base_rtptime, clock_rate, priv->clock_base, GST_STR_NULL (cname));
/* no CNAME known yet for this ssrc */
if (cname == NULL) {
GST_DEBUG_OBJECT (jitterbuffer, "no CNAME for this packet known yet");
return;
}
if (priv->last_ntpnstime != GST_CLOCK_TIME_NONE
&& ntpnstime - priv->last_ntpnstime < priv->sync_interval * GST_MSECOND) {
GST_DEBUG_OBJECT (jitterbuffer,
"discarding RTCP sender packet for sync; "
"previous sender info too recent " "(previous NTP %" G_GUINT64_FORMAT
")", priv->last_ntpnstime);
have_new_inband = priv->have_new_inband;
priv->have_new_inband = FALSE;
/* don't sync again if not required yet */
if (!have_new_inband && priv->last_sync_time != -1 &&
now - priv->last_sync_time < MAX (priv->sync_interval,
priv->min_sync_interval) * 1000)
return;
/* don't sync based on the inband NTP time if a newer NTP time is known from
* the latest RTCP SR */
if (ntpnstime < priv->last_known_ntpnstime)
return;
/* get the last values from the jitterbuffer */
rtp_jitter_buffer_get_sync (priv->jbuf, &base_rtptime, &base_time,
&clock_rate, NULL);
GST_DEBUG_OBJECT (jitterbuffer,
"inband NTP-64 %" GST_TIME_FORMAT " rtptime %" G_GUINT64_FORMAT ", base %"
G_GUINT64_FORMAT ", clock-rate %" G_GUINT32_FORMAT ", clock-base %"
G_GUINT64_FORMAT ", CNAME %s", GST_TIME_ARGS (ntpnstime), ext_rtptime,
base_rtptime, clock_rate, priv->clock_base, GST_STR_NULL (cname));
if (priv->have_synced_ntp && priv->last_sync_time != -1
&& now - priv->last_sync_time < priv->sync_interval * 1000) {
GST_TRACE_OBJECT (jitterbuffer,
"discarding inband NTP time for sync; "
"previous NTP info too recent " "(previous NTP %" G_GUINT64_FORMAT
", inband NTP %" G_GUINT64_FORMAT ", synced %" G_GUINT64_FORMAT
"ms ago)", priv->last_known_ntpnstime, ntpnstime,
(now - priv->last_sync_time) / 1000);
return;
}
priv->last_ntpnstime = ntpnstime;
priv->last_sync_time = now;
priv->have_synced_ntp = TRUE;
s = gst_structure_new ("application/x-rtp-sync",
"base-rtptime", G_TYPE_UINT64, base_rtptime,
"base-time", G_TYPE_UINT64, base_time,
"clock-rate", G_TYPE_UINT, clock_rate,
"clock-base", G_TYPE_UINT64, priv->clock_base & G_MAXUINT32,
"npt-start", G_TYPE_UINT64, priv->npt_start,
"clock-base", G_TYPE_UINT64, clock_base & G_MAXUINT32,
"npt-start", G_TYPE_UINT64, npt_start,
"cname", G_TYPE_STRING, cname,
"ssrc", G_TYPE_UINT, priv->last_ssrc,
"inband-ext-rtptime", G_TYPE_UINT64, last_rtptime,
"inband-ext-rtptime", G_TYPE_UINT64, ext_rtptime,
"inband-ntpnstime", G_TYPE_UINT64, ntpnstime, NULL);
/* Same information is provided here already and doesn't have to be provided
* separately again */
priv->rtp_info_sync_pending = FALSE;
GST_DEBUG_OBJECT (jitterbuffer, "signaling sync");
JBUF_UNLOCK (priv);
g_signal_emit (jitterbuffer,
@ -4787,105 +4867,128 @@ do_handle_sync_inband (GstRtpJitterBuffer * jitterbuffer, guint64 ntpnstime)
* some sanity checks and then emit the handle-sync signal with the parameters.
* This function must be called with the LOCK */
static void
do_handle_sync (GstRtpJitterBuffer * jitterbuffer)
do_handle_sync (GstRtpJitterBuffer * jitterbuffer, gint64 now)
{
GstRtpJitterBufferPrivate *priv;
GstRtpJitterBufferPrivate *priv = jitterbuffer->priv;
guint64 base_rtptime, base_time;
guint32 clock_rate;
guint64 last_rtptime;
guint64 clock_base;
GstClockTime npt_start;
guint64 ext_rtptime, diff;
gboolean valid = TRUE, keep = FALSE;
const guint64 clock_base = priv->clock_base;
const GstClockTime npt_start = priv->npt_start;
const GstClockTime ntpnstime = priv->last_sr_ntpnstime;
guint64 ext_rtptime = priv->last_sr_ext_rtptime;
guint64 diff;
gboolean have_new_sr;
priv = jitterbuffer->priv;
/* If we had no RTCP SR yet, just return here */
if (!priv->last_sr)
return;
/* get the last values from the jitterbuffer */
rtp_jitter_buffer_get_sync (priv->jbuf, &base_rtptime, &base_time,
&clock_rate, &last_rtptime);
clock_base = priv->clock_base;
npt_start = priv->npt_start;
ext_rtptime = priv->last_sr_ext_rtptime;
GST_DEBUG_OBJECT (jitterbuffer,
"ext SR %" G_GUINT64_FORMAT ", NTP %" G_GUINT64_FORMAT ", base %"
G_GUINT64_FORMAT ", clock-rate %" G_GUINT32_FORMAT ", clock-base %"
G_GUINT64_FORMAT ", last-rtptime %" G_GUINT64_FORMAT, ext_rtptime,
priv->last_sr_ntpnstime, base_rtptime, clock_rate, clock_base,
last_rtptime);
ntpnstime, base_rtptime, clock_rate, clock_base, last_rtptime);
if (base_rtptime == -1 || clock_rate == -1 || base_time == -1) {
/* we keep this SR packet for later. When we get a valid RTP packet the
* above values will be set and we can try to use the SR packet */
GST_DEBUG_OBJECT (jitterbuffer, "keeping for later, no RTP values");
keep = TRUE;
} else {
/* we can't accept anything that happened before we did the last resync */
if (base_rtptime > ext_rtptime) {
GST_DEBUG_OBJECT (jitterbuffer, "dropping, older than base time");
valid = FALSE;
} else {
/* the SR RTP timestamp must be something close to what we last observed
* in the jitterbuffer */
if (ext_rtptime > last_rtptime) {
/* check how far ahead it is to our RTP timestamps */
diff = ext_rtptime - last_rtptime;
/* if bigger than configured maximum difference then we drop it */
if (jitterbuffer->priv->max_rtcp_rtp_time_diff != -1 &&
diff >
gst_util_uint64_scale (jitterbuffer->priv->max_rtcp_rtp_time_diff,
clock_rate, 1000)) {
GST_DEBUG_OBJECT (jitterbuffer, "too far ahead");
/* should drop this, but some RTSP servers end up with bogus
* way too ahead RTCP packet when repeated PAUSE/PLAY,
* so still trigger rtpbin sync but invalidate RTCP data
* (sync might use other methods) */
ext_rtptime = -1;
}
GST_DEBUG_OBJECT (jitterbuffer, "ext last %" G_GUINT64_FORMAT ", diff %"
G_GUINT64_FORMAT, last_rtptime, diff);
}
return;
}
/* we can't accept anything that happened before we did the last resync */
if (base_rtptime > ext_rtptime) {
GST_DEBUG_OBJECT (jitterbuffer, "dropping, older than base time");
gst_buffer_replace (&priv->last_sr, NULL);
return;
}
have_new_sr = priv->have_new_sr;
priv->have_new_sr = FALSE;
/* don't sync again if not required yet */
if (!have_new_sr && priv->last_sync_time != -1 &&
now - priv->last_sync_time < MAX (priv->sync_interval,
priv->min_sync_interval) * 1000)
return;
/* don't sync based on the RTCP SR NTP time if a newer NTP time is known from
* inband RTP header extension */
if (ntpnstime < priv->last_known_ntpnstime)
return;
if (priv->have_synced_ntp && priv->last_sync_time != -1
&& now - priv->last_sync_time < priv->sync_interval * 1000) {
GST_TRACE_OBJECT (jitterbuffer, "discarding RTCP SR packet for sync; "
"previous NTP info too recent "
"(previous NTP %" G_GUINT64_FORMAT ", SR NTP %" G_GUINT64_FORMAT
", synced %" G_GUINT64_FORMAT "ms ago)", priv->last_known_ntpnstime,
ntpnstime, (now - priv->last_sync_time) / 1000);
return;
}
/* the SR RTP timestamp must be something close to what we last observed
* in the jitterbuffer */
if (ext_rtptime > last_rtptime) {
/* check how far ahead it is to our RTP timestamps */
diff = ext_rtptime - last_rtptime;
/* if bigger than configured maximum difference then we drop it */
if (jitterbuffer->priv->max_rtcp_rtp_time_diff != -1 &&
diff >
gst_util_uint64_scale (jitterbuffer->priv->max_rtcp_rtp_time_diff,
clock_rate, 1000)) {
GST_DEBUG_OBJECT (jitterbuffer, "too far ahead");
/* should drop this, but some RTSP servers end up with bogus
* way too ahead RTCP packet when repeated PAUSE/PLAY,
* so still trigger rtpbin sync but invalidate RTCP data
* (sync might use other methods) */
ext_rtptime = -1;
}
GST_DEBUG_OBJECT (jitterbuffer, "ext last %" G_GUINT64_FORMAT ", diff %"
G_GUINT64_FORMAT, last_rtptime, diff);
}
priv->last_sync_time = now;
priv->have_synced_ntp = TRUE;
GstStructure *s;
GList *l;
s = gst_structure_new ("application/x-rtp-sync",
"base-rtptime", G_TYPE_UINT64, base_rtptime,
"base-time", G_TYPE_UINT64, base_time,
"clock-rate", G_TYPE_UINT, clock_rate,
"clock-base", G_TYPE_UINT64, clock_base & G_MAXUINT32,
"npt-start", G_TYPE_UINT64, npt_start,
"ssrc", G_TYPE_UINT, priv->last_sr_ssrc,
"sr-ext-rtptime", G_TYPE_UINT64, ext_rtptime,
"sr-ntpnstime", G_TYPE_UINT64, priv->last_sr_ntpnstime,
"sr-buffer", GST_TYPE_BUFFER, priv->last_sr, NULL);
for (l = priv->cname_ssrc_mappings; l; l = l->next) {
const CNameSSRCMapping *map = l->data;
if (map->ssrc == priv->last_ssrc) {
gst_structure_set (s, "cname", G_TYPE_STRING, map->cname, NULL);
break;
}
}
if (keep) {
GST_DEBUG_OBJECT (jitterbuffer, "keeping RTCP packet for later");
} else if (valid) {
GstStructure *s;
GList *l;
/* Same information is provided here already and doesn't have to be provided
* separately again */
priv->rtp_info_sync_pending = FALSE;
s = gst_structure_new ("application/x-rtp-sync",
"base-rtptime", G_TYPE_UINT64, base_rtptime,
"base-time", G_TYPE_UINT64, base_time,
"clock-rate", G_TYPE_UINT, clock_rate,
"clock-base", G_TYPE_UINT64, priv->clock_base & G_MAXUINT32,
"npt-start", G_TYPE_UINT64, npt_start,
"ssrc", G_TYPE_UINT, priv->last_sr_ssrc,
"sr-ext-rtptime", G_TYPE_UINT64, ext_rtptime,
"sr-ntpnstime", G_TYPE_UINT64, priv->last_sr_ntpnstime,
"sr-buffer", GST_TYPE_BUFFER, priv->last_sr, NULL);
for (l = priv->cname_ssrc_mappings; l; l = l->next) {
const CNameSSRCMapping *map = l->data;
if (map->ssrc == priv->last_ssrc) {
gst_structure_set (s, "cname", G_TYPE_STRING, map->cname, NULL);
break;
}
}
GST_DEBUG_OBJECT (jitterbuffer, "signaling sync");
gst_buffer_replace (&priv->last_sr, NULL);
JBUF_UNLOCK (priv);
g_signal_emit (jitterbuffer,
gst_rtp_jitter_buffer_signals[SIGNAL_HANDLE_SYNC], 0, s);
JBUF_LOCK (priv);
gst_structure_free (s);
} else {
GST_DEBUG_OBJECT (jitterbuffer, "dropping RTCP packet");
gst_buffer_replace (&priv->last_sr, NULL);
}
GST_DEBUG_OBJECT (jitterbuffer, "signaling sync");
JBUF_UNLOCK (priv);
g_signal_emit (jitterbuffer,
gst_rtp_jitter_buffer_signals[SIGNAL_HANDLE_SYNC], 0, s);
JBUF_LOCK (priv);
gst_structure_free (s);
}
#define GST_RTCP_BUFFER_FOR_PACKETS(b,buffer,packet) \
@ -5010,8 +5113,11 @@ out:
priv->last_sr_ssrc = ssrc;
priv->last_sr_ntpnstime = ntpnstime;
priv->last_known_ext_rtptime = ext_rtptime;
priv->last_known_ntpnstime = ntpnstime;
if (priv->last_known_ntpnstime == GST_CLOCK_TIME_NONE
|| priv->last_known_ntpnstime < ntpnstime) {
priv->last_known_ext_rtptime = ext_rtptime;
priv->last_known_ntpnstime = ntpnstime;
}
if (G_UNLIKELY (priv->last_ssrc == -1)) {
GST_DEBUG_OBJECT (jitterbuffer, "SSRC changed from %u to %u",
@ -5019,17 +5125,10 @@ out:
priv->last_ssrc = ssrc;
}
if (priv->last_ntpnstime != GST_CLOCK_TIME_NONE
&& ntpnstime - priv->last_ntpnstime < priv->sync_interval * GST_MSECOND) {
gst_buffer_replace (&priv->last_sr, NULL);
GST_DEBUG_OBJECT (jitterbuffer, "discarding RTCP sender packet for sync; "
"previous sender info too recent "
"(previous NTP %" G_GUINT64_FORMAT ")", priv->last_ntpnstime);
} else {
gst_buffer_replace (&priv->last_sr, buffer);
do_handle_sync (jitterbuffer);
priv->last_ntpnstime = ntpnstime;
}
gst_buffer_replace (&priv->last_sr, buffer);
priv->have_new_sr = TRUE;
gint64 now = g_get_monotonic_time ();
do_handle_sync (jitterbuffer, now);
JBUF_UNLOCK (priv);
@ -5399,7 +5498,11 @@ gst_rtp_jitter_buffer_set_property (GObject * object,
priv->rfc7273_reference_timestamp_meta_only = g_value_get_boolean (value);
JBUF_UNLOCK (priv);
break;
case PROP_MIN_SYNC_INTERVAL:
JBUF_LOCK (priv);
priv->min_sync_interval = g_value_get_uint (value);
JBUF_UNLOCK (priv);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@ -5576,6 +5679,11 @@ gst_rtp_jitter_buffer_get_property (GObject * object,
g_value_set_boolean (value, priv->rfc7273_reference_timestamp_meta_only);
JBUF_UNLOCK (priv);
break;
case PROP_MIN_SYNC_INTERVAL:
JBUF_LOCK (priv);
g_value_set_uint (value, priv->min_sync_interval);
JBUF_UNLOCK (priv);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;