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:
Havard Graff 2020-06-02 19:38:33 +02:00 committed by GStreamer Marge Bot
parent 309269a93b
commit 1368b4214b
2 changed files with 271 additions and 116 deletions

View file

@ -930,7 +930,7 @@ gst_rtp_jitter_buffer_class_init (GstRtpJitterBufferClass * klass)
* GstRtpJitterBuffer::on-npt-stop:
* @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.
*/
gst_rtp_jitter_buffer_signals[SIGNAL_ON_NPT_STOP] =
@ -2254,7 +2254,7 @@ get_rtx_delay (GstRtpJitterBufferPrivate * priv)
* had for this packet.
*/
static void
update_timers (GstRtpJitterBuffer * jitterbuffer, guint16 seqnum,
update_rtx_timers (GstRtpJitterBuffer * jitterbuffer, guint16 seqnum,
GstClockTime dts, GstClockTime pts, gboolean do_next_seqnum,
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
&& priv->do_retransmission && priv->rtx_next_seqnum;
&& priv->rtx_next_seqnum;
if (timer && timer->type != RTP_TIMER_DEADLINE) {
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) {
GstClockTime expected, delay;
GstClockTime next_expected_pts, delay;
/* 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);
/* and update/install timer for next seqnum */
GST_DEBUG_OBJECT (jitterbuffer, "Add RTX timer #%d, expected %"
GST_TIME_FORMAT ", delay %" GST_TIME_FORMAT ", packet-spacing %"
GST_DEBUG_OBJECT (jitterbuffer, "Add RTX timer #%d, next_expected_pts %"
GST_TIME_FORMAT ", delay %" GST_TIME_FORMAT ", est packet duration %"
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));
if (timer && !is_stats_timer) {
timer->type = RTP_TIMER_EXPECTED;
rtp_timer_queue_update_timer (priv->timers, timer, priv->next_in_seqnum,
expected, delay, 0, TRUE);
next_expected_pts, delay, 0, TRUE);
} else {
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) {
/* 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
calculate_expected (GstRtpJitterBuffer * jitterbuffer, guint32 expected,
guint16 seqnum, GstClockTime pts, gint gap)
gst_rtp_jitter_buffer_handle_missing_packets (GstRtpJitterBuffer * jitterbuffer,
guint32 missing_seqnum, guint16 current_seqnum, GstClockTime pts, gint gap,
GstClockTime now)
{
GstRtpJitterBufferPrivate *priv = jitterbuffer->priv;
GstClockTime duration, expected_pts;
GstClockTime est_pkt_duration, est_pts;
gboolean equidistant = priv->equidistant > 0;
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,
"pts %" GST_TIME_FORMAT ", last %" GST_TIME_FORMAT,
GST_TIME_ARGS (pts), GST_TIME_ARGS (last_in_pts));
if (pts == GST_CLOCK_TIME_NONE) {
GST_WARNING_OBJECT (jitterbuffer, "Have no PTS");
return;
}
"Missing packets: (#%u->#%u), gap %d, pts %" GST_TIME_FORMAT
", last-pts %" GST_TIME_FORMAT,
missing_seqnum, current_seqnum - 1, gap, GST_TIME_ARGS (pts),
GST_TIME_ARGS (last_in_pts));
if (equidistant) {
GstClockTime total_duration;
GstClockTimeDiff total_duration;
gboolean too_late;
/* the total duration spanned by the missing packets */
if (pts >= last_in_pts)
total_duration = pts - last_in_pts;
else
total_duration = 0;
total_duration = MAX (0, GST_CLOCK_DIFF (last_in_pts, pts));
/* interpolate between the current time and the last time based on
* number of packets we are missing, this is the estimated duration
* 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,
GST_TIME_ARGS (duration));
/* if we have valid packet-spacing, use that */
if (total_duration > 0 && priv->packet_spacing) {
est_pkt_duration = priv->packet_spacing;
}
if (total_duration > priv->latency_ns) {
GstClockTime gap_time;
est_pts = last_in_pts + est_pkt_duration;
GST_DEBUG_OBJECT (jitterbuffer, "estimated missing packet pts %"
GST_TIME_FORMAT " and duration %" GST_TIME_FORMAT,
GST_TIME_ARGS (est_pts), GST_TIME_ARGS (est_pkt_duration));
/* a packet is considered too late if our estimated pts plus all
applicable offsets are in the past */
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;
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;
}
/* gap time represents the total duration of all missing packets */
gap_time = MAX (0, GST_CLOCK_DIFF (est_pts, pts));
/* too many lost packets, some of the missing packets are already
* too late and we can generate lost packet events for them. */
GST_INFO_OBJECT (jitterbuffer,
"lost packets (%d, #%d->#%d) duration too large %" GST_TIME_FORMAT
" > %" GST_TIME_FORMAT ", consider %u lost (%" GST_TIME_FORMAT ")",
gap, expected, seqnum - 1, GST_TIME_ARGS (total_duration),
GST_TIME_ARGS (priv->latency_ns), lost_packets,
GST_TIME_ARGS (gap_time));
/* 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
for immediate processing */
if (lost_packets > 0) {
RtpTimer *timer;
GstClockTime timestamp =
apply_offset (jitterbuffer, last_in_pts + duration);
insert_lost_event (jitterbuffer, expected, lost_packets, timestamp,
gap_time, 0);
GstClockTime timestamp = apply_offset (jitterbuffer, est_pts);
timer = rtp_timer_queue_find (priv->timers, expected);
if (timer && timer->type == RTP_TIMER_EXPECTED) {
GST_INFO_OBJECT (jitterbuffer, "lost event for %d packet(s) (#%d->#%d) "
"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)
rtp_timer_queue_unschedule (priv->timers, timer);
GST_DEBUG_OBJECT (jitterbuffer, "removing timer for seqnum #%u",
expected);
missing_seqnum);
rtp_timer_free (timer);
}
expected += lost_packets;
last_in_pts += gap_time;
missing_seqnum += lost_packets;
est_pts += lost_duration;
}
}
expected_pts = last_in_pts + duration;
} else {
/* 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
* the last received pts. */
duration = 0;
expected_pts = pts;
est_pkt_duration = 0;
est_pts = pts;
}
if (priv->do_retransmission) {
RtpTimer *timer = rtp_timer_queue_find (priv->timers, expected);
GstClockTime rtx_delay = get_rtx_delay (priv);
/* 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));
/* if we had a timer for the first missing packet, update it. */
if (timer && timer->type == RTP_TIMER_EXPECTED) {
GstClockTime timeout = timer->timeout;
GstClockTime delay = MAX (rtx_delay, pts - expected_pts);
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));
timer->duration = duration;
if (timeout > (expected_pts + delay) && timer->num_rtx_retry == 0) {
rtp_timer_queue_update_timer (priv->timers, timer, timer->seqnum,
expected_pts, delay, 0, TRUE);
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) {
RtpTimer *timer = rtp_timer_queue_find (priv->timers, missing_seqnum);
/* if we had a timer for the missing packet, update it. */
if (timer && timer->type == RTP_TIMER_EXPECTED) {
timer->duration = duration;
if (timer->timeout > (est_pts + rtx_delay) && timer->num_rtx_retry == 0) {
rtp_timer_queue_update_timer (priv->timers, timer, timer->seqnum,
est_pts, rtx_delay, 0, TRUE);
GST_DEBUG_OBJECT (jitterbuffer, "Update RTX timer(s) #%u, "
"pts %" GST_TIME_FORMAT ", delay %" GST_TIME_FORMAT
", duration %" GST_TIME_FORMAT,
missing_seqnum, GST_TIME_ARGS (est_pts),
GST_TIME_ARGS (rtx_delay), GST_TIME_ARGS (duration));
}
} else {
GST_DEBUG_OBJECT (jitterbuffer, "Add RTX timer(s) #%u, "
"pts %" GST_TIME_FORMAT ", delay %" GST_TIME_FORMAT
", duration %" GST_TIME_FORMAT,
missing_seqnum, GST_TIME_ARGS (est_pts),
GST_TIME_ARGS (rtx_delay), GST_TIME_ARGS (duration));
rtp_timer_queue_set_expected (priv->timers, missing_seqnum, est_pts,
rtx_delay, duration);
}
expected++;
expected_pts += 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);
}
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 {
while (gst_rtp_buffer_compare_seqnum (expected, seqnum) > 0) {
rtp_timer_queue_set_lost (priv->timers, expected, expected_pts,
duration, timeout_offset (jitterbuffer));
expected_pts += duration;
expected++;
}
missing_seqnum++;
est_pts += duration;
}
}
@ -2856,6 +2906,7 @@ gst_rtp_jitter_buffer_chain (GstPad * pad, GstObject * parent,
guint16 seqnum;
guint32 expected, rtptime;
GstFlowReturn ret = GST_FLOW_OK;
GstClockTime now;
GstClockTime dts, pts;
guint64 latency_ts;
gboolean head;
@ -2884,6 +2935,7 @@ gst_rtp_jitter_buffer_chain (GstPad * pad, GstObject * parent,
gst_rtp_buffer_unmap (&rtp);
is_rtx = GST_BUFFER_IS_RETRANSMISSION (buffer);
now = get_current_running_time (jitterbuffer);
/* make sure we have PTS and DTS set */
pts = GST_BUFFER_PTS (buffer);
@ -2896,7 +2948,7 @@ gst_rtp_jitter_buffer_chain (GstPad * pad, GstObject * parent,
if (dts == -1) {
/* 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. */
dts = get_current_running_time (jitterbuffer);
dts = now;
pts = dts;
/* 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) {
GST_DEBUG_OBJECT (jitterbuffer, "%d missing packets", gap);
/* 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;
} else {
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))
head = TRUE;
/* update timers */
update_timers (jitterbuffer, seqnum, dts, pts, do_next_seqnum, is_rtx, timer);
/* update rtx timers */
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 */
if (priv->last_sr)
@ -3814,7 +3869,7 @@ do_expected_timeout (GstRtpJitterBuffer * jitterbuffer, RtpTimer * timer,
GstClockTimeDiff offset = 0;
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));
rtx_retry_timeout = get_rtx_retry_timeout (priv);
@ -3867,19 +3922,20 @@ do_expected_timeout (GstRtpJitterBuffer * jitterbuffer, RtpTimer * timer,
*/
timeout = timer->rtx_last + rtx_retry_timeout;
GST_DEBUG_OBJECT (jitterbuffer,
"timer #%i new timeout %" GST_TIME_FORMAT ", rtx retry timeout%"
"timer #%i new timeout %" GST_TIME_FORMAT ", rtx retry timeout %"
GST_TIME_FORMAT ", num_retry %u", timer->seqnum, GST_TIME_ARGS (timeout),
GST_TIME_ARGS (rtx_retry_timeout), timer->num_rtx_retry);
if ((priv->rtx_max_retries != -1
&& timer->num_rtx_retry >= priv->rtx_max_retries)
|| (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
* to a lost timer, leave the num_rtx_retry as it is for stats */
timer->type = RTP_TIMER_LOST;
timeout = timer->rtx_base;
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,
timeout, 0, offset, FALSE);

View file

@ -532,7 +532,9 @@ static void
push_test_buffer_now (GstHarness * h, guint seqnum, guint32 rtptime,
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);
if (rtx)
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,
seqnum, seqnum * TEST_RTP_TS_DURATION)));
/* we should now receive packet-lost-events for the gap
* FIXME: The timeout and duration here are a bit crap...
*/
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);
/* We get one "huge" lost-event accounting for all the missing time */
verify_lost_event (h, next_seqnum, 120 * GST_MSECOND, 9860 * 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);
fail_unless (GST_BUFFER_FLAG_IS_SET (out_buf, GST_BUFFER_FLAG_DISCONT));
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,
seq + 1, frame * TEST_RTP_TS_DURATION));
/* Check that the lost event has been generated assuming equidistant
* spacing. */
verify_lost_event (h, seq,
frame * TEST_BUF_DURATION - TEST_BUF_DURATION / 2, TEST_BUF_DURATION / 2);
/* given that the last known PTS (pkt#12) was 200ms and this last PTS (pkt#14) was 220ms,
and our current packet-spacing is 20ms, the lost-event gets a problem here:
If we use our packet-spacing, the last pushed packet (#12) should have
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));
@ -1261,6 +1279,83 @@ GST_START_TEST (test_loss_equidistant_spacing_with_parameter_packets)
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
gst_test_clock_set_time_and_process (GstTestClock * testclock,
GstClockTime time)
@ -1706,8 +1801,7 @@ GST_START_TEST (test_rtx_duplicate_packet_updates_rtx_stats)
gint latency_ms = 100;
gint next_seqnum;
GstClockTime now, rtx_request_6, rtx_request_7;
gint rtx_delay_ms_0 = TEST_BUF_MS / 2;
gint rtx_delay_ms_1 = TEST_BUF_MS;
gint rtx_delay_ms = TEST_BUF_MS / 2;
gint i;
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 */
gst_harness_crank_single_clock_wait (h);
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));
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);
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));
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 */
now = 161 * GST_MSECOND;
@ -3093,6 +3187,8 @@ check_for_stall (GstHarness * h, BufferArrayCtx * bufs, guint num_bufs)
GArray *array;
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 ());
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_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_not_bursting_requests);