mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2025-02-03 04:52:28 +00:00
rtpjitterbuffer: clean up and improve missing packets handling
* Try to make variable and function names more clear. * Add plenty of comments describing the logic step-by-step. * Improve the logging around this, making the logs easier to read and understand when debugging these issues. * Revise the logic of packets that are actually beyond saving in doing the following: 1. Do an optimistic estimation of which packets can still arrive. 2. Based on this, find which packets (and duration) are now hopelessly lost. 3. Issue an immediate lost-event for the hopelessly lost and then add lost/rtx timers for the ones we still hope to save, meaning that if they are to arrive, they will not be discarded. * Revise the use of rtx-delay: Earlier the rtx-delay would vary, depending on the pts of the latest packet and the estimated pts of the packet it being issued a RTX for, but now that we aim to estimate the PTS of the missing packet accurately, the RTX delay should remain the same for all packets. Meaning: If the packet have a PTS of X, the delay in asked for a RTX for this packet is always a constant X + delay, not a variable one. * Finally ensure that the chaotic "check-for-stall" tests uses timestamps that starts from 0 to make them easier to debug. Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-good/-/merge_requests/952>
This commit is contained in:
parent
309269a93b
commit
1368b4214b
2 changed files with 271 additions and 116 deletions
|
@ -930,7 +930,7 @@ gst_rtp_jitter_buffer_class_init (GstRtpJitterBufferClass * klass)
|
||||||
* GstRtpJitterBuffer::on-npt-stop:
|
* GstRtpJitterBuffer::on-npt-stop:
|
||||||
* @buffer: the object which received the signal
|
* @buffer: the object which received the signal
|
||||||
*
|
*
|
||||||
* Signal that the jitterbufer has pushed the RTP packet that corresponds to
|
* Signal that the jitterbuffer has pushed the RTP packet that corresponds to
|
||||||
* the npt-stop position.
|
* the npt-stop position.
|
||||||
*/
|
*/
|
||||||
gst_rtp_jitter_buffer_signals[SIGNAL_ON_NPT_STOP] =
|
gst_rtp_jitter_buffer_signals[SIGNAL_ON_NPT_STOP] =
|
||||||
|
@ -2254,7 +2254,7 @@ get_rtx_delay (GstRtpJitterBufferPrivate * priv)
|
||||||
* had for this packet.
|
* had for this packet.
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
update_timers (GstRtpJitterBuffer * jitterbuffer, guint16 seqnum,
|
update_rtx_timers (GstRtpJitterBuffer * jitterbuffer, guint16 seqnum,
|
||||||
GstClockTime dts, GstClockTime pts, gboolean do_next_seqnum,
|
GstClockTime dts, GstClockTime pts, gboolean do_next_seqnum,
|
||||||
gboolean is_rtx, RtpTimer * timer)
|
gboolean is_rtx, RtpTimer * timer)
|
||||||
{
|
{
|
||||||
|
@ -2299,7 +2299,7 @@ update_timers (GstRtpJitterBuffer * jitterbuffer, guint16 seqnum,
|
||||||
}
|
}
|
||||||
|
|
||||||
do_next_seqnum = do_next_seqnum && priv->packet_spacing > 0
|
do_next_seqnum = do_next_seqnum && priv->packet_spacing > 0
|
||||||
&& priv->do_retransmission && priv->rtx_next_seqnum;
|
&& priv->rtx_next_seqnum;
|
||||||
|
|
||||||
if (timer && timer->type != RTP_TIMER_DEADLINE) {
|
if (timer && timer->type != RTP_TIMER_DEADLINE) {
|
||||||
if (timer->num_rtx_retry > 0) {
|
if (timer->num_rtx_retry > 0) {
|
||||||
|
@ -2326,27 +2326,27 @@ update_timers (GstRtpJitterBuffer * jitterbuffer, guint16 seqnum,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (do_next_seqnum && pts != GST_CLOCK_TIME_NONE) {
|
if (do_next_seqnum && pts != GST_CLOCK_TIME_NONE) {
|
||||||
GstClockTime expected, delay;
|
GstClockTime next_expected_pts, delay;
|
||||||
|
|
||||||
/* calculate expected arrival time of the next seqnum */
|
/* calculate expected arrival time of the next seqnum */
|
||||||
expected = pts + priv->packet_spacing;
|
next_expected_pts = pts + priv->packet_spacing;
|
||||||
|
|
||||||
delay = get_rtx_delay (priv);
|
delay = get_rtx_delay (priv);
|
||||||
|
|
||||||
/* and update/install timer for next seqnum */
|
/* and update/install timer for next seqnum */
|
||||||
GST_DEBUG_OBJECT (jitterbuffer, "Add RTX timer #%d, expected %"
|
GST_DEBUG_OBJECT (jitterbuffer, "Add RTX timer #%d, next_expected_pts %"
|
||||||
GST_TIME_FORMAT ", delay %" GST_TIME_FORMAT ", packet-spacing %"
|
GST_TIME_FORMAT ", delay %" GST_TIME_FORMAT ", est packet duration %"
|
||||||
GST_TIME_FORMAT ", jitter %" GST_TIME_FORMAT, priv->next_in_seqnum,
|
GST_TIME_FORMAT ", jitter %" GST_TIME_FORMAT, priv->next_in_seqnum,
|
||||||
GST_TIME_ARGS (expected), GST_TIME_ARGS (delay),
|
GST_TIME_ARGS (next_expected_pts), GST_TIME_ARGS (delay),
|
||||||
GST_TIME_ARGS (priv->packet_spacing), GST_TIME_ARGS (priv->avg_jitter));
|
GST_TIME_ARGS (priv->packet_spacing), GST_TIME_ARGS (priv->avg_jitter));
|
||||||
|
|
||||||
if (timer && !is_stats_timer) {
|
if (timer && !is_stats_timer) {
|
||||||
timer->type = RTP_TIMER_EXPECTED;
|
timer->type = RTP_TIMER_EXPECTED;
|
||||||
rtp_timer_queue_update_timer (priv->timers, timer, priv->next_in_seqnum,
|
rtp_timer_queue_update_timer (priv->timers, timer, priv->next_in_seqnum,
|
||||||
expected, delay, 0, TRUE);
|
next_expected_pts, delay, 0, TRUE);
|
||||||
} else {
|
} else {
|
||||||
rtp_timer_queue_set_expected (priv->timers, priv->next_in_seqnum,
|
rtp_timer_queue_set_expected (priv->timers, priv->next_in_seqnum,
|
||||||
expected, delay, priv->packet_spacing);
|
next_expected_pts, delay, priv->packet_spacing);
|
||||||
}
|
}
|
||||||
} else if (timer && timer->type != RTP_TIMER_DEADLINE && !is_stats_timer) {
|
} else if (timer && timer->type != RTP_TIMER_DEADLINE && !is_stats_timer) {
|
||||||
/* if we had a timer, remove it, we don't know when to expect the next
|
/* if we had a timer, remove it, we don't know when to expect the next
|
||||||
|
@ -2443,130 +2443,180 @@ insert_lost_event (GstRtpJitterBuffer * jitterbuffer,
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
calculate_expected (GstRtpJitterBuffer * jitterbuffer, guint32 expected,
|
gst_rtp_jitter_buffer_handle_missing_packets (GstRtpJitterBuffer * jitterbuffer,
|
||||||
guint16 seqnum, GstClockTime pts, gint gap)
|
guint32 missing_seqnum, guint16 current_seqnum, GstClockTime pts, gint gap,
|
||||||
|
GstClockTime now)
|
||||||
{
|
{
|
||||||
GstRtpJitterBufferPrivate *priv = jitterbuffer->priv;
|
GstRtpJitterBufferPrivate *priv = jitterbuffer->priv;
|
||||||
GstClockTime duration, expected_pts;
|
GstClockTime est_pkt_duration, est_pts;
|
||||||
gboolean equidistant = priv->equidistant > 0;
|
gboolean equidistant = priv->equidistant > 0;
|
||||||
GstClockTime last_in_pts = priv->last_in_pts;
|
GstClockTime last_in_pts = priv->last_in_pts;
|
||||||
|
GstClockTimeDiff offset = timeout_offset (jitterbuffer);
|
||||||
|
GstClockTime rtx_delay = get_rtx_delay (priv);
|
||||||
|
guint16 remaining_gap;
|
||||||
|
GstClockTimeDiff remaining_duration;
|
||||||
|
GstClockTimeDiff remainder_duration;
|
||||||
|
guint i;
|
||||||
|
|
||||||
GST_DEBUG_OBJECT (jitterbuffer,
|
GST_DEBUG_OBJECT (jitterbuffer,
|
||||||
"pts %" GST_TIME_FORMAT ", last %" GST_TIME_FORMAT,
|
"Missing packets: (#%u->#%u), gap %d, pts %" GST_TIME_FORMAT
|
||||||
GST_TIME_ARGS (pts), GST_TIME_ARGS (last_in_pts));
|
", last-pts %" GST_TIME_FORMAT,
|
||||||
|
missing_seqnum, current_seqnum - 1, gap, GST_TIME_ARGS (pts),
|
||||||
if (pts == GST_CLOCK_TIME_NONE) {
|
GST_TIME_ARGS (last_in_pts));
|
||||||
GST_WARNING_OBJECT (jitterbuffer, "Have no PTS");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (equidistant) {
|
if (equidistant) {
|
||||||
GstClockTime total_duration;
|
GstClockTimeDiff total_duration;
|
||||||
|
gboolean too_late;
|
||||||
|
|
||||||
/* the total duration spanned by the missing packets */
|
/* the total duration spanned by the missing packets */
|
||||||
if (pts >= last_in_pts)
|
total_duration = MAX (0, GST_CLOCK_DIFF (last_in_pts, pts));
|
||||||
total_duration = pts - last_in_pts;
|
|
||||||
else
|
|
||||||
total_duration = 0;
|
|
||||||
|
|
||||||
/* interpolate between the current time and the last time based on
|
/* interpolate between the current time and the last time based on
|
||||||
* number of packets we are missing, this is the estimated duration
|
* number of packets we are missing, this is the estimated duration
|
||||||
* for the missing packet based on equidistant packet spacing. */
|
* for the missing packet based on equidistant packet spacing. */
|
||||||
duration = total_duration / (gap + 1);
|
est_pkt_duration = total_duration / (gap + 1);
|
||||||
|
|
||||||
GST_DEBUG_OBJECT (jitterbuffer, "duration %" GST_TIME_FORMAT,
|
/* if we have valid packet-spacing, use that */
|
||||||
GST_TIME_ARGS (duration));
|
if (total_duration > 0 && priv->packet_spacing) {
|
||||||
|
est_pkt_duration = priv->packet_spacing;
|
||||||
if (total_duration > priv->latency_ns) {
|
|
||||||
GstClockTime gap_time;
|
|
||||||
guint lost_packets;
|
|
||||||
|
|
||||||
if (duration > 0) {
|
|
||||||
GstClockTime gap_dur = gap * duration;
|
|
||||||
if (gap_dur > priv->latency_ns)
|
|
||||||
gap_time = gap_dur - priv->latency_ns;
|
|
||||||
else
|
|
||||||
gap_time = 0;
|
|
||||||
lost_packets = gap_time / duration;
|
|
||||||
} else {
|
|
||||||
gap_time = total_duration - priv->latency_ns;
|
|
||||||
lost_packets = gap;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* too many lost packets, some of the missing packets are already
|
est_pts = last_in_pts + est_pkt_duration;
|
||||||
* too late and we can generate lost packet events for them. */
|
GST_DEBUG_OBJECT (jitterbuffer, "estimated missing packet pts %"
|
||||||
GST_INFO_OBJECT (jitterbuffer,
|
GST_TIME_FORMAT " and duration %" GST_TIME_FORMAT,
|
||||||
"lost packets (%d, #%d->#%d) duration too large %" GST_TIME_FORMAT
|
GST_TIME_ARGS (est_pts), GST_TIME_ARGS (est_pkt_duration));
|
||||||
" > %" GST_TIME_FORMAT ", consider %u lost (%" GST_TIME_FORMAT ")",
|
|
||||||
gap, expected, seqnum - 1, GST_TIME_ARGS (total_duration),
|
/* a packet is considered too late if our estimated pts plus all
|
||||||
GST_TIME_ARGS (priv->latency_ns), lost_packets,
|
applicable offsets are in the past */
|
||||||
GST_TIME_ARGS (gap_time));
|
too_late = now > (est_pts + offset);
|
||||||
|
|
||||||
|
/* Here we optimistically try to save any packets that could potentially
|
||||||
|
be saved by making sure we create lost/rtx timers for them, and for
|
||||||
|
the rest that could not possibly be saved, we create a "multi-lost"
|
||||||
|
event immediately containing the missing duration and sequence numbers */
|
||||||
|
if (too_late) {
|
||||||
|
guint lost_packets;
|
||||||
|
GstClockTime lost_duration;
|
||||||
|
GstClockTimeDiff gap_time;
|
||||||
|
guint saveable_packets;
|
||||||
|
GstClockTime saveable_duration;
|
||||||
|
|
||||||
|
/* gap time represents the total duration of all missing packets */
|
||||||
|
gap_time = MAX (0, GST_CLOCK_DIFF (est_pts, pts));
|
||||||
|
|
||||||
|
/* based on the estimated packet duration, we
|
||||||
|
can figure out how many packets we could possibly save */
|
||||||
|
saveable_packets = offset / est_pkt_duration;
|
||||||
|
/* and say that the amount of lost packet is the sequence-number
|
||||||
|
gap minus these saveable packets, but at least 1 */
|
||||||
|
lost_packets = MAX (1, (gint) gap - (gint) saveable_packets);
|
||||||
|
|
||||||
|
/* now we know how many packets we can actually save */
|
||||||
|
saveable_packets = gap - lost_packets;
|
||||||
|
|
||||||
|
/* we convert that to time */
|
||||||
|
saveable_duration = saveable_packets * est_pkt_duration;
|
||||||
|
|
||||||
|
/* and we now have the duration we need to fill */
|
||||||
|
lost_duration = GST_CLOCK_DIFF (saveable_duration, gap_time);
|
||||||
|
|
||||||
/* this multi-lost-packet event will be inserted directly into the packet-queue
|
/* this multi-lost-packet event will be inserted directly into the packet-queue
|
||||||
for immediate processing */
|
for immediate processing */
|
||||||
if (lost_packets > 0) {
|
if (lost_packets > 0) {
|
||||||
RtpTimer *timer;
|
RtpTimer *timer;
|
||||||
GstClockTime timestamp =
|
GstClockTime timestamp = apply_offset (jitterbuffer, est_pts);
|
||||||
apply_offset (jitterbuffer, last_in_pts + duration);
|
|
||||||
insert_lost_event (jitterbuffer, expected, lost_packets, timestamp,
|
|
||||||
gap_time, 0);
|
|
||||||
|
|
||||||
timer = rtp_timer_queue_find (priv->timers, expected);
|
GST_INFO_OBJECT (jitterbuffer, "lost event for %d packet(s) (#%d->#%d) "
|
||||||
if (timer && timer->type == RTP_TIMER_EXPECTED) {
|
"for duration %" GST_TIME_FORMAT, lost_packets, missing_seqnum,
|
||||||
|
missing_seqnum + lost_packets - 1, GST_TIME_ARGS (lost_duration));
|
||||||
|
|
||||||
|
insert_lost_event (jitterbuffer, missing_seqnum, lost_packets,
|
||||||
|
timestamp, lost_duration, 0);
|
||||||
|
|
||||||
|
timer = rtp_timer_queue_find (priv->timers, missing_seqnum);
|
||||||
|
if (timer && timer->type != RTP_TIMER_DEADLINE) {
|
||||||
if (timer->queued)
|
if (timer->queued)
|
||||||
rtp_timer_queue_unschedule (priv->timers, timer);
|
rtp_timer_queue_unschedule (priv->timers, timer);
|
||||||
GST_DEBUG_OBJECT (jitterbuffer, "removing timer for seqnum #%u",
|
GST_DEBUG_OBJECT (jitterbuffer, "removing timer for seqnum #%u",
|
||||||
expected);
|
missing_seqnum);
|
||||||
rtp_timer_free (timer);
|
rtp_timer_free (timer);
|
||||||
}
|
}
|
||||||
|
|
||||||
expected += lost_packets;
|
missing_seqnum += lost_packets;
|
||||||
last_in_pts += gap_time;
|
est_pts += lost_duration;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
expected_pts = last_in_pts + duration;
|
|
||||||
} else {
|
} else {
|
||||||
/* If we cannot assume equidistant packet spacing, the only thing we now
|
/* If we cannot assume equidistant packet spacing, the only thing we now
|
||||||
* for sure is that the missing packets have expected pts not later than
|
* for sure is that the missing packets have expected pts not later than
|
||||||
* the last received pts. */
|
* the last received pts. */
|
||||||
duration = 0;
|
est_pkt_duration = 0;
|
||||||
expected_pts = pts;
|
est_pts = pts;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Figure out how many more packets we are missing. */
|
||||||
|
remaining_gap = current_seqnum - missing_seqnum;
|
||||||
|
/* and how much time these packets represent */
|
||||||
|
remaining_duration = MAX (0, GST_CLOCK_DIFF (est_pts, pts));
|
||||||
|
/* Given the calculated packet-duration (packet spacing when equidistant),
|
||||||
|
the remainder is what we are left with after subtracting the ideal time
|
||||||
|
for the gap */
|
||||||
|
remainder_duration =
|
||||||
|
MAX (0, GST_CLOCK_DIFF (est_pkt_duration * remaining_gap,
|
||||||
|
remaining_duration));
|
||||||
|
|
||||||
|
GST_DEBUG_OBJECT (jitterbuffer, "remaining gap of %u, with "
|
||||||
|
"duration %" GST_TIME_FORMAT " gives remainder duration %"
|
||||||
|
GST_STIME_FORMAT, remaining_gap, GST_TIME_ARGS (remaining_duration),
|
||||||
|
GST_STIME_ARGS (remainder_duration));
|
||||||
|
|
||||||
|
for (i = 0; i < remaining_gap; i++) {
|
||||||
|
GstClockTime duration = est_pkt_duration;
|
||||||
|
/* we add the remainder on the first packet */
|
||||||
|
if (i == 0)
|
||||||
|
duration += remainder_duration;
|
||||||
|
|
||||||
|
/* clip duration to what is actually left */
|
||||||
|
remaining_duration = MAX (0, GST_CLOCK_DIFF (est_pts, pts));
|
||||||
|
duration = MIN (duration, remaining_duration);
|
||||||
|
|
||||||
if (priv->do_retransmission) {
|
if (priv->do_retransmission) {
|
||||||
RtpTimer *timer = rtp_timer_queue_find (priv->timers, expected);
|
RtpTimer *timer = rtp_timer_queue_find (priv->timers, missing_seqnum);
|
||||||
GstClockTime rtx_delay = get_rtx_delay (priv);
|
|
||||||
|
|
||||||
/* if we had a timer for the first missing packet, update it. */
|
/* if we had a timer for the missing packet, update it. */
|
||||||
if (timer && timer->type == RTP_TIMER_EXPECTED) {
|
if (timer && timer->type == RTP_TIMER_EXPECTED) {
|
||||||
GstClockTime timeout = timer->timeout;
|
|
||||||
GstClockTime delay = MAX (rtx_delay, pts - expected_pts);
|
|
||||||
|
|
||||||
timer->duration = duration;
|
timer->duration = duration;
|
||||||
if (timeout > (expected_pts + delay) && timer->num_rtx_retry == 0) {
|
if (timer->timeout > (est_pts + rtx_delay) && timer->num_rtx_retry == 0) {
|
||||||
rtp_timer_queue_update_timer (priv->timers, timer, timer->seqnum,
|
rtp_timer_queue_update_timer (priv->timers, timer, timer->seqnum,
|
||||||
expected_pts, delay, 0, TRUE);
|
est_pts, rtx_delay, 0, TRUE);
|
||||||
}
|
GST_DEBUG_OBJECT (jitterbuffer, "Update RTX timer(s) #%u, "
|
||||||
expected++;
|
"pts %" GST_TIME_FORMAT ", delay %" GST_TIME_FORMAT
|
||||||
expected_pts += duration;
|
", duration %" GST_TIME_FORMAT,
|
||||||
}
|
missing_seqnum, GST_TIME_ARGS (est_pts),
|
||||||
|
GST_TIME_ARGS (rtx_delay), GST_TIME_ARGS (duration));
|
||||||
while (gst_rtp_buffer_compare_seqnum (expected, seqnum) > 0) {
|
|
||||||
/* minimum delay the expected-timer has "waited" is the elapsed time
|
|
||||||
* since expected arrival of the missing packet */
|
|
||||||
GstClockTime delay = MAX (rtx_delay, pts - expected_pts);
|
|
||||||
rtp_timer_queue_set_expected (priv->timers, expected, expected_pts,
|
|
||||||
delay, duration);
|
|
||||||
expected_pts += duration;
|
|
||||||
expected++;
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
while (gst_rtp_buffer_compare_seqnum (expected, seqnum) > 0) {
|
GST_DEBUG_OBJECT (jitterbuffer, "Add RTX timer(s) #%u, "
|
||||||
rtp_timer_queue_set_lost (priv->timers, expected, expected_pts,
|
"pts %" GST_TIME_FORMAT ", delay %" GST_TIME_FORMAT
|
||||||
duration, timeout_offset (jitterbuffer));
|
", duration %" GST_TIME_FORMAT,
|
||||||
expected_pts += duration;
|
missing_seqnum, GST_TIME_ARGS (est_pts),
|
||||||
expected++;
|
GST_TIME_ARGS (rtx_delay), GST_TIME_ARGS (duration));
|
||||||
|
rtp_timer_queue_set_expected (priv->timers, missing_seqnum, est_pts,
|
||||||
|
rtx_delay, duration);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
GST_INFO_OBJECT (jitterbuffer,
|
||||||
|
"Add Lost timer for #%u, pts %" GST_TIME_FORMAT
|
||||||
|
", duration %" GST_TIME_FORMAT ", offset %" GST_STIME_FORMAT,
|
||||||
|
missing_seqnum, GST_TIME_ARGS (est_pts),
|
||||||
|
GST_TIME_ARGS (duration), GST_STIME_ARGS (offset));
|
||||||
|
rtp_timer_queue_set_lost (priv->timers, missing_seqnum, est_pts,
|
||||||
|
duration, offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
missing_seqnum++;
|
||||||
|
est_pts += duration;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2856,6 +2906,7 @@ gst_rtp_jitter_buffer_chain (GstPad * pad, GstObject * parent,
|
||||||
guint16 seqnum;
|
guint16 seqnum;
|
||||||
guint32 expected, rtptime;
|
guint32 expected, rtptime;
|
||||||
GstFlowReturn ret = GST_FLOW_OK;
|
GstFlowReturn ret = GST_FLOW_OK;
|
||||||
|
GstClockTime now;
|
||||||
GstClockTime dts, pts;
|
GstClockTime dts, pts;
|
||||||
guint64 latency_ts;
|
guint64 latency_ts;
|
||||||
gboolean head;
|
gboolean head;
|
||||||
|
@ -2884,6 +2935,7 @@ gst_rtp_jitter_buffer_chain (GstPad * pad, GstObject * parent,
|
||||||
gst_rtp_buffer_unmap (&rtp);
|
gst_rtp_buffer_unmap (&rtp);
|
||||||
|
|
||||||
is_rtx = GST_BUFFER_IS_RETRANSMISSION (buffer);
|
is_rtx = GST_BUFFER_IS_RETRANSMISSION (buffer);
|
||||||
|
now = get_current_running_time (jitterbuffer);
|
||||||
|
|
||||||
/* make sure we have PTS and DTS set */
|
/* make sure we have PTS and DTS set */
|
||||||
pts = GST_BUFFER_PTS (buffer);
|
pts = GST_BUFFER_PTS (buffer);
|
||||||
|
@ -2896,7 +2948,7 @@ gst_rtp_jitter_buffer_chain (GstPad * pad, GstObject * parent,
|
||||||
if (dts == -1) {
|
if (dts == -1) {
|
||||||
/* If we have no DTS here, i.e. no capture time, get one from the
|
/* If we have no DTS here, i.e. no capture time, get one from the
|
||||||
* clock now to have something to calculate with in the future. */
|
* clock now to have something to calculate with in the future. */
|
||||||
dts = get_current_running_time (jitterbuffer);
|
dts = now;
|
||||||
pts = dts;
|
pts = dts;
|
||||||
|
|
||||||
/* Remember that we estimated the DTS if we are running already
|
/* Remember that we estimated the DTS if we are running already
|
||||||
|
@ -3095,7 +3147,8 @@ gst_rtp_jitter_buffer_chain (GstPad * pad, GstObject * parent,
|
||||||
if (gap > 0) {
|
if (gap > 0) {
|
||||||
GST_DEBUG_OBJECT (jitterbuffer, "%d missing packets", gap);
|
GST_DEBUG_OBJECT (jitterbuffer, "%d missing packets", gap);
|
||||||
/* fill in the gap with EXPECTED timers */
|
/* fill in the gap with EXPECTED timers */
|
||||||
calculate_expected (jitterbuffer, expected, seqnum, pts, gap);
|
gst_rtp_jitter_buffer_handle_missing_packets (jitterbuffer, expected,
|
||||||
|
seqnum, pts, gap, now);
|
||||||
do_next_seqnum = TRUE;
|
do_next_seqnum = TRUE;
|
||||||
} else {
|
} else {
|
||||||
GST_DEBUG_OBJECT (jitterbuffer, "old packet received");
|
GST_DEBUG_OBJECT (jitterbuffer, "old packet received");
|
||||||
|
@ -3211,8 +3264,10 @@ gst_rtp_jitter_buffer_chain (GstPad * pad, GstObject * parent,
|
||||||
if (gst_rtp_jitter_buffer_fast_start (jitterbuffer))
|
if (gst_rtp_jitter_buffer_fast_start (jitterbuffer))
|
||||||
head = TRUE;
|
head = TRUE;
|
||||||
|
|
||||||
/* update timers */
|
/* update rtx timers */
|
||||||
update_timers (jitterbuffer, seqnum, dts, pts, do_next_seqnum, is_rtx, timer);
|
if (priv->do_retransmission)
|
||||||
|
update_rtx_timers (jitterbuffer, seqnum, dts, pts, do_next_seqnum, is_rtx,
|
||||||
|
timer);
|
||||||
|
|
||||||
/* we had an unhandled SR, handle it now */
|
/* we had an unhandled SR, handle it now */
|
||||||
if (priv->last_sr)
|
if (priv->last_sr)
|
||||||
|
@ -3814,7 +3869,7 @@ do_expected_timeout (GstRtpJitterBuffer * jitterbuffer, RtpTimer * timer,
|
||||||
GstClockTimeDiff offset = 0;
|
GstClockTimeDiff offset = 0;
|
||||||
GstClockTime timeout;
|
GstClockTime timeout;
|
||||||
|
|
||||||
GST_DEBUG_OBJECT (jitterbuffer, "expected %d didn't arrive, now %"
|
GST_DEBUG_OBJECT (jitterbuffer, "expected #%d didn't arrive, now %"
|
||||||
GST_TIME_FORMAT, timer->seqnum, GST_TIME_ARGS (now));
|
GST_TIME_FORMAT, timer->seqnum, GST_TIME_ARGS (now));
|
||||||
|
|
||||||
rtx_retry_timeout = get_rtx_retry_timeout (priv);
|
rtx_retry_timeout = get_rtx_retry_timeout (priv);
|
||||||
|
@ -3873,13 +3928,14 @@ do_expected_timeout (GstRtpJitterBuffer * jitterbuffer, RtpTimer * timer,
|
||||||
if ((priv->rtx_max_retries != -1
|
if ((priv->rtx_max_retries != -1
|
||||||
&& timer->num_rtx_retry >= priv->rtx_max_retries)
|
&& timer->num_rtx_retry >= priv->rtx_max_retries)
|
||||||
|| (timeout > timer->rtx_base + rtx_retry_period)) {
|
|| (timeout > timer->rtx_base + rtx_retry_period)) {
|
||||||
GST_DEBUG_OBJECT (jitterbuffer, "reschedule #%i as LOST timer",
|
|
||||||
timer->seqnum);
|
|
||||||
/* too many retransmission request, we now convert the timer
|
/* too many retransmission request, we now convert the timer
|
||||||
* to a lost timer, leave the num_rtx_retry as it is for stats */
|
* to a lost timer, leave the num_rtx_retry as it is for stats */
|
||||||
timer->type = RTP_TIMER_LOST;
|
timer->type = RTP_TIMER_LOST;
|
||||||
timeout = timer->rtx_base;
|
timeout = timer->rtx_base;
|
||||||
offset = timeout_offset (jitterbuffer);
|
offset = timeout_offset (jitterbuffer);
|
||||||
|
GST_DEBUG_OBJECT (jitterbuffer, "reschedule #%i as LOST timer for %"
|
||||||
|
GST_TIME_FORMAT, timer->seqnum,
|
||||||
|
GST_TIME_ARGS (timer->rtx_base + offset));
|
||||||
}
|
}
|
||||||
rtp_timer_queue_update_timer (priv->timers, timer, timer->seqnum,
|
rtp_timer_queue_update_timer (priv->timers, timer, timer->seqnum,
|
||||||
timeout, 0, offset, FALSE);
|
timeout, 0, offset, FALSE);
|
||||||
|
|
|
@ -532,7 +532,9 @@ static void
|
||||||
push_test_buffer_now (GstHarness * h, guint seqnum, guint32 rtptime,
|
push_test_buffer_now (GstHarness * h, guint seqnum, guint32 rtptime,
|
||||||
gboolean rtx)
|
gboolean rtx)
|
||||||
{
|
{
|
||||||
GstClockTime now = gst_clock_get_time (GST_ELEMENT_CLOCK (h->element));
|
GstClockTime now =
|
||||||
|
gst_clock_get_time (GST_ELEMENT_CLOCK (h->element)) -
|
||||||
|
h->element->base_time;
|
||||||
GstBuffer *buf = generate_test_buffer_full (now, seqnum, rtptime);
|
GstBuffer *buf = generate_test_buffer_full (now, seqnum, rtptime);
|
||||||
if (rtx)
|
if (rtx)
|
||||||
GST_BUFFER_FLAG_SET (buf, GST_RTP_BUFFER_FLAG_RETRANSMISSION);
|
GST_BUFFER_FLAG_SET (buf, GST_RTP_BUFFER_FLAG_RETRANSMISSION);
|
||||||
|
@ -906,14 +908,18 @@ GST_START_TEST (test_late_packets_still_makes_lost_events)
|
||||||
generate_test_buffer_full (now,
|
generate_test_buffer_full (now,
|
||||||
seqnum, seqnum * TEST_RTP_TS_DURATION)));
|
seqnum, seqnum * TEST_RTP_TS_DURATION)));
|
||||||
|
|
||||||
/* we should now receive packet-lost-events for the gap
|
/* We get one "huge" lost-event accounting for all the missing time */
|
||||||
* FIXME: The timeout and duration here are a bit crap...
|
verify_lost_event (h, next_seqnum, 120 * GST_MSECOND, 9860 * GST_MSECOND);
|
||||||
*/
|
|
||||||
verify_lost_event (h, next_seqnum, 3400 * GST_MSECOND, 6500 * GST_MSECOND);
|
|
||||||
verify_lost_event (h, next_seqnum + 1,
|
|
||||||
9900 * GST_MSECOND, 3300 * GST_MSECOND);
|
|
||||||
|
|
||||||
/* verify that packet @seqnum made it through! */
|
/* and the next packet is optimistically expected to be the one
|
||||||
|
just prior to our current packet, so we time that out with a crank */
|
||||||
|
gst_harness_crank_single_clock_wait (h);
|
||||||
|
|
||||||
|
/* and we verify that indeed this lost event was thought to should
|
||||||
|
have arrived 20ms prior to the packet that actually arrived */
|
||||||
|
verify_lost_event (h, next_seqnum + 1, 9980 * GST_MSECOND, 20 * GST_MSECOND);
|
||||||
|
|
||||||
|
/* and finally verify that the super-late packet made it through! */
|
||||||
out_buf = gst_harness_pull (h);
|
out_buf = gst_harness_pull (h);
|
||||||
fail_unless (GST_BUFFER_FLAG_IS_SET (out_buf, GST_BUFFER_FLAG_DISCONT));
|
fail_unless (GST_BUFFER_FLAG_IS_SET (out_buf, GST_BUFFER_FLAG_DISCONT));
|
||||||
fail_unless_equals_int (seqnum, get_rtp_seq_num (out_buf));
|
fail_unless_equals_int (seqnum, get_rtp_seq_num (out_buf));
|
||||||
|
@ -1248,10 +1254,22 @@ GST_START_TEST (test_loss_equidistant_spacing_with_parameter_packets)
|
||||||
gst_harness_push (h, generate_test_buffer_full (frame * TEST_BUF_DURATION,
|
gst_harness_push (h, generate_test_buffer_full (frame * TEST_BUF_DURATION,
|
||||||
seq + 1, frame * TEST_RTP_TS_DURATION));
|
seq + 1, frame * TEST_RTP_TS_DURATION));
|
||||||
|
|
||||||
/* Check that the lost event has been generated assuming equidistant
|
/* given that the last known PTS (pkt#12) was 200ms and this last PTS (pkt#14) was 220ms,
|
||||||
* spacing. */
|
and our current packet-spacing is 20ms, the lost-event gets a problem here:
|
||||||
verify_lost_event (h, seq,
|
If we use our packet-spacing, the last pushed packet (#12) should have
|
||||||
frame * TEST_BUF_DURATION - TEST_BUF_DURATION / 2, TEST_BUF_DURATION / 2);
|
a duration of 20ms, meaning we would expect the missing packet (#13) to
|
||||||
|
have a PTS of 220ms. However, packet #14 comes in at 220ms, so what is
|
||||||
|
the best estimation for the missing packet here?
|
||||||
|
|
||||||
|
Given that we want to estimate the most optimistic PTS in order to give
|
||||||
|
the packet as many chances as possible to arrive, we end up with a PTS
|
||||||
|
of 220ms and a duration of 0, since that will be the most optimistic
|
||||||
|
placement given that it has to be before pkt #14.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* timeout the lost-event */
|
||||||
|
gst_harness_crank_single_clock_wait (h);
|
||||||
|
verify_lost_event (h, seq, frame * TEST_BUF_DURATION, 0);
|
||||||
|
|
||||||
gst_buffer_unref (gst_harness_pull (h));
|
gst_buffer_unref (gst_harness_pull (h));
|
||||||
|
|
||||||
|
@ -1261,6 +1279,83 @@ GST_START_TEST (test_loss_equidistant_spacing_with_parameter_packets)
|
||||||
GST_END_TEST;
|
GST_END_TEST;
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
guint gap;
|
||||||
|
GstClockTime duration[3];
|
||||||
|
} ThreeLostPackets;
|
||||||
|
|
||||||
|
ThreeLostPackets no_fractional_lost_event_durations_input[] = {
|
||||||
|
{5, {60 * GST_MSECOND, 20 * GST_MSECOND, 20 * GST_MSECOND}},
|
||||||
|
{4, {40 * GST_MSECOND, 20 * GST_MSECOND, 20 * GST_MSECOND}},
|
||||||
|
{3, {20 * GST_MSECOND, 20 * GST_MSECOND, 20 * GST_MSECOND}},
|
||||||
|
{2, {20 * GST_MSECOND, 20 * GST_MSECOND, 0 * GST_MSECOND}},
|
||||||
|
{1, {20 * GST_MSECOND, 0 * GST_MSECOND, 0 * GST_MSECOND}},
|
||||||
|
{0, {0 * GST_MSECOND, 0 * GST_MSECOND, 0 * GST_MSECOND}},
|
||||||
|
};
|
||||||
|
|
||||||
|
/* This test looks after that fact that when we have equidistant
|
||||||
|
packetspacing, we try and keep that spacing for the lost events,
|
||||||
|
so we operate in "whole" packets.
|
||||||
|
*/
|
||||||
|
GST_START_TEST (test_no_fractional_lost_event_durations)
|
||||||
|
{
|
||||||
|
ThreeLostPackets *ctx = &no_fractional_lost_event_durations_input[__i__];
|
||||||
|
|
||||||
|
GstHarness *h = gst_harness_new ("rtpjitterbuffer");
|
||||||
|
GstClockTime now;
|
||||||
|
guint latency_ms = 100;
|
||||||
|
guint16 seqnum, gap_seqnum;
|
||||||
|
GstClockTime pts;
|
||||||
|
GstClockTime duration;
|
||||||
|
|
||||||
|
g_object_set (h->element, "do-lost", TRUE, NULL);
|
||||||
|
seqnum = construct_deterministic_initial_state (h, latency_ms);
|
||||||
|
gap_seqnum = seqnum + ctx->gap;
|
||||||
|
|
||||||
|
now = gap_seqnum * TEST_BUF_DURATION;
|
||||||
|
gst_harness_set_time (h, now);
|
||||||
|
fail_unless_equals_int (GST_FLOW_OK, gst_harness_push (h,
|
||||||
|
generate_test_buffer_full (now,
|
||||||
|
seqnum + 3, gap_seqnum * TEST_RTP_TS_DURATION)));
|
||||||
|
|
||||||
|
pts = seqnum * TEST_BUF_DURATION;
|
||||||
|
now = gst_clock_get_time (GST_ELEMENT_CLOCK (h->element));
|
||||||
|
/* check if the lost-event has expired, if not
|
||||||
|
crank to move the time ahead */
|
||||||
|
if (pts + latency_ms * GST_MSECOND > now)
|
||||||
|
gst_harness_crank_single_clock_wait (h);
|
||||||
|
duration = ctx->duration[0];
|
||||||
|
verify_lost_event (h, seqnum, pts, duration);
|
||||||
|
|
||||||
|
seqnum++;
|
||||||
|
pts += duration;
|
||||||
|
duration = ctx->duration[1];
|
||||||
|
now = gst_clock_get_time (GST_ELEMENT_CLOCK (h->element));
|
||||||
|
if (pts + latency_ms * GST_MSECOND > now)
|
||||||
|
gst_harness_crank_single_clock_wait (h);
|
||||||
|
verify_lost_event (h, seqnum, pts, duration);
|
||||||
|
|
||||||
|
seqnum++;
|
||||||
|
pts += duration;
|
||||||
|
duration = ctx->duration[2];
|
||||||
|
now = gst_clock_get_time (GST_ELEMENT_CLOCK (h->element));
|
||||||
|
if (pts + latency_ms * GST_MSECOND > now)
|
||||||
|
gst_harness_crank_single_clock_wait (h);
|
||||||
|
verify_lost_event (h, seqnum, pts, duration);
|
||||||
|
|
||||||
|
/* followed by the buffer */
|
||||||
|
gst_buffer_unref (gst_harness_pull (h));
|
||||||
|
/* verify that we have pulled out all waiting buffers and events */
|
||||||
|
fail_unless_equals_int (0, gst_harness_buffers_in_queue (h));
|
||||||
|
fail_unless_equals_int (0, gst_harness_events_in_queue (h));
|
||||||
|
|
||||||
|
gst_harness_teardown (h);
|
||||||
|
}
|
||||||
|
|
||||||
|
GST_END_TEST;
|
||||||
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
gst_test_clock_set_time_and_process (GstTestClock * testclock,
|
gst_test_clock_set_time_and_process (GstTestClock * testclock,
|
||||||
GstClockTime time)
|
GstClockTime time)
|
||||||
|
@ -1706,8 +1801,7 @@ GST_START_TEST (test_rtx_duplicate_packet_updates_rtx_stats)
|
||||||
gint latency_ms = 100;
|
gint latency_ms = 100;
|
||||||
gint next_seqnum;
|
gint next_seqnum;
|
||||||
GstClockTime now, rtx_request_6, rtx_request_7;
|
GstClockTime now, rtx_request_6, rtx_request_7;
|
||||||
gint rtx_delay_ms_0 = TEST_BUF_MS / 2;
|
gint rtx_delay_ms = TEST_BUF_MS / 2;
|
||||||
gint rtx_delay_ms_1 = TEST_BUF_MS;
|
|
||||||
gint i;
|
gint i;
|
||||||
|
|
||||||
g_object_set (h->element, "do-retransmission", TRUE, NULL);
|
g_object_set (h->element, "do-retransmission", TRUE, NULL);
|
||||||
|
@ -1721,17 +1815,17 @@ GST_START_TEST (test_rtx_duplicate_packet_updates_rtx_stats)
|
||||||
/* Wait for NACKs on 6 and 7 */
|
/* Wait for NACKs on 6 and 7 */
|
||||||
gst_harness_crank_single_clock_wait (h);
|
gst_harness_crank_single_clock_wait (h);
|
||||||
verify_rtx_event (h, 6, 6 * TEST_BUF_DURATION,
|
verify_rtx_event (h, 6, 6 * TEST_BUF_DURATION,
|
||||||
rtx_delay_ms_0, TEST_BUF_DURATION);
|
rtx_delay_ms, TEST_BUF_DURATION);
|
||||||
rtx_request_6 = gst_clock_get_time (GST_ELEMENT_CLOCK (h->element));
|
rtx_request_6 = gst_clock_get_time (GST_ELEMENT_CLOCK (h->element));
|
||||||
fail_unless_equals_int64 (rtx_request_6,
|
fail_unless_equals_int64 (rtx_request_6,
|
||||||
6 * TEST_BUF_DURATION + rtx_delay_ms_0 * GST_MSECOND);
|
6 * TEST_BUF_DURATION + rtx_delay_ms * GST_MSECOND);
|
||||||
|
|
||||||
gst_harness_crank_single_clock_wait (h);
|
gst_harness_crank_single_clock_wait (h);
|
||||||
verify_rtx_event (h,
|
verify_rtx_event (h,
|
||||||
7, 7 * TEST_BUF_DURATION, rtx_delay_ms_1, TEST_BUF_DURATION);
|
7, 7 * TEST_BUF_DURATION, rtx_delay_ms, TEST_BUF_DURATION);
|
||||||
rtx_request_7 = gst_clock_get_time (GST_ELEMENT_CLOCK (h->element));
|
rtx_request_7 = gst_clock_get_time (GST_ELEMENT_CLOCK (h->element));
|
||||||
fail_unless_equals_int64 (rtx_request_7,
|
fail_unless_equals_int64 (rtx_request_7,
|
||||||
7 * TEST_BUF_DURATION + rtx_delay_ms_1 * GST_MSECOND);
|
7 * TEST_BUF_DURATION + rtx_delay_ms * GST_MSECOND);
|
||||||
|
|
||||||
/* Original packet 7 arrives */
|
/* Original packet 7 arrives */
|
||||||
now = 161 * GST_MSECOND;
|
now = 161 * GST_MSECOND;
|
||||||
|
@ -3093,6 +3187,8 @@ check_for_stall (GstHarness * h, BufferArrayCtx * bufs, guint num_bufs)
|
||||||
GArray *array;
|
GArray *array;
|
||||||
|
|
||||||
gst_harness_use_systemclock (h);
|
gst_harness_use_systemclock (h);
|
||||||
|
gst_element_set_base_time (h->element,
|
||||||
|
gst_clock_get_time (GST_ELEMENT_CLOCK (h->element)));
|
||||||
gst_harness_set_src_caps (h, generate_caps ());
|
gst_harness_set_src_caps (h, generate_caps ());
|
||||||
|
|
||||||
g_object_get (h->element, "latency", &latency_ms, NULL);
|
g_object_get (h->element, "latency", &latency_ms, NULL);
|
||||||
|
@ -3218,6 +3314,9 @@ rtpjitterbuffer_suite (void)
|
||||||
tcase_add_test (tc_chain, test_reorder_of_non_equidistant_packets);
|
tcase_add_test (tc_chain, test_reorder_of_non_equidistant_packets);
|
||||||
tcase_add_test (tc_chain,
|
tcase_add_test (tc_chain,
|
||||||
test_loss_equidistant_spacing_with_parameter_packets);
|
test_loss_equidistant_spacing_with_parameter_packets);
|
||||||
|
tcase_add_loop_test (tc_chain, test_no_fractional_lost_event_durations, 0,
|
||||||
|
G_N_ELEMENTS (no_fractional_lost_event_durations_input));
|
||||||
|
|
||||||
|
|
||||||
tcase_add_test (tc_chain, test_rtx_expected_next);
|
tcase_add_test (tc_chain, test_rtx_expected_next);
|
||||||
tcase_add_test (tc_chain, test_rtx_not_bursting_requests);
|
tcase_add_test (tc_chain, test_rtx_not_bursting_requests);
|
||||||
|
|
Loading…
Reference in a new issue