rtpjitterbuffer: fix gap-time calculation and remove "late"

The amount of time that is completely expired and not worth waiting for,
is the duration of the packets in the gap (gap * duration) - the
latency (size) of the jitterbuffer (priv->latency_ns). This is the duration
that we make a "multi-lost" packet for.

The "late" concept made some sense in 0.10 as it reflected that a buffer
coming in had not been waited for at all, but had a timestamp that was
outside the jitterbuffer to wait for. With the rewrite of the waiting
(timeout) mechanism in 1.0, this no longer makes any sense, and the
variable no longer reflects anything meaningful (num > 0 is useless,
the duration is what matters)

Fixed up the tests that had been slightly modified in 1.0 to allow faulty
behavior to sneak in, and port some of them to use GstHarness.

https://bugzilla.gnome.org/show_bug.cgi?id=738363
This commit is contained in:
Havard Graff 2015-07-08 21:08:36 +02:00 committed by Sebastian Dröge
parent 40524e5a49
commit ddd032f56b
2 changed files with 311 additions and 287 deletions

View file

@ -2128,29 +2128,35 @@ calculate_expected (GstRtpJitterBuffer * jitterbuffer, guint32 expected,
GstClockTime gap_time; GstClockTime gap_time;
guint lost_packets; guint lost_packets;
gap_time = total_duration - priv->latency_ns;
if (duration > 0) { 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; lost_packets = gap_time / duration;
gap_time = lost_packets * duration;
} else { } else {
gap_time = total_duration - priv->latency_ns;
lost_packets = gap; lost_packets = gap;
} }
/* too many lost packets, some of the missing packets are already /* too many lost packets, some of the missing packets are already
* too late and we can generate lost packet events for them. */ * too late and we can generate lost packet events for them. */
GST_DEBUG_OBJECT (jitterbuffer, "too many lost packets %" GST_TIME_FORMAT GST_DEBUG_OBJECT (jitterbuffer,
" > %" GST_TIME_FORMAT ", consider %u lost", "lost packets (%d, #%d->#%d) duration too large %" GST_TIME_FORMAT
GST_TIME_ARGS (total_duration), GST_TIME_ARGS (priv->latency_ns), " > %" GST_TIME_FORMAT ", consider %u lost (%" GST_TIME_FORMAT ")",
lost_packets); gap, expected, seqnum, GST_TIME_ARGS (total_duration),
GST_TIME_ARGS (priv->latency_ns), lost_packets,
GST_TIME_ARGS(gap_time));
/* this timer will fire immediately and the lost event will be pushed from /* this timer will fire immediately and the lost event will be pushed from
* the timer thread */ * the timer thread */
add_timer (jitterbuffer, TIMER_TYPE_LOST, expected, lost_packets, if (lost_packets > 0) {
priv->last_in_dts + duration, 0, gap_time); add_timer (jitterbuffer, TIMER_TYPE_LOST, expected, lost_packets,
priv->last_in_dts + duration, 0, gap_time);
expected += lost_packets; expected += lost_packets;
priv->last_in_dts += gap_time; priv->last_in_dts += gap_time;
}
} }
expected_dts = priv->last_in_dts + duration; expected_dts = priv->last_in_dts + duration;
@ -3183,7 +3189,7 @@ do_lost_timeout (GstRtpJitterBuffer * jitterbuffer, TimerData * timer,
GstRtpJitterBufferPrivate *priv = jitterbuffer->priv; GstRtpJitterBufferPrivate *priv = jitterbuffer->priv;
GstClockTime duration, timestamp; GstClockTime duration, timestamp;
guint seqnum, lost_packets, num_rtx_retry, next_in_seqnum; guint seqnum, lost_packets, num_rtx_retry, next_in_seqnum;
gboolean late, head; gboolean head;
GstEvent *event; GstEvent *event;
RTPJitterBufferItem *item; RTPJitterBufferItem *item;
@ -3193,7 +3199,6 @@ do_lost_timeout (GstRtpJitterBuffer * jitterbuffer, TimerData * timer,
if (duration == GST_CLOCK_TIME_NONE && priv->packet_spacing > 0) if (duration == GST_CLOCK_TIME_NONE && priv->packet_spacing > 0)
duration = priv->packet_spacing; duration = priv->packet_spacing;
lost_packets = MAX (timer->num, 1); lost_packets = MAX (timer->num, 1);
late = timer->num > 0;
num_rtx_retry = timer->num_rtx_retry; num_rtx_retry = timer->num_rtx_retry;
/* we had a gap and thus we lost some packets. Create an event for this. */ /* we had a gap and thus we lost some packets. Create an event for this. */
@ -3218,7 +3223,6 @@ do_lost_timeout (GstRtpJitterBuffer * jitterbuffer, TimerData * timer,
"seqnum", G_TYPE_UINT, (guint) seqnum, "seqnum", G_TYPE_UINT, (guint) seqnum,
"timestamp", G_TYPE_UINT64, timestamp, "timestamp", G_TYPE_UINT64, timestamp,
"duration", G_TYPE_UINT64, duration, "duration", G_TYPE_UINT64, duration,
"late", G_TYPE_BOOLEAN, late,
"retry", G_TYPE_UINT, num_rtx_retry, NULL)); "retry", G_TYPE_UINT, num_rtx_retry, NULL));
item = alloc_item (event, ITEM_TYPE_LOST, -1, -1, seqnum, lost_packets, -1); item = alloc_item (event, ITEM_TYPE_LOST, -1, -1, seqnum, lost_packets, -1);

View file

@ -5,6 +5,9 @@
* Copyright (C) 2012 Cisco Systems, Inc * Copyright (C) 2012 Cisco Systems, Inc
* Authors: Kelley Rogers <kelro@cisco.com> * Authors: Kelley Rogers <kelro@cisco.com>
* Havard Graff <hgraff@cisco.com> * Havard Graff <hgraff@cisco.com>
* Copyright (C) 2013-2015 Pexip AS
* Stian Selnes <stian@pexip>
* Havard Graff <havard@pexip>
* *
* This library is free software; you can redistribute it and/or * This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public * modify it under the terms of the GNU Library General Public
@ -24,6 +27,7 @@
#include <gst/check/gstcheck.h> #include <gst/check/gstcheck.h>
#include <gst/check/gsttestclock.h> #include <gst/check/gsttestclock.h>
#include <gst/check/gstharness.h>
#include <gst/rtp/gstrtpbuffer.h> #include <gst/rtp/gstrtpbuffer.h>
@ -374,10 +378,14 @@ GST_START_TEST (test_clear_pt_map)
} }
GST_END_TEST; GST_END_TEST;
static const guint payload_size = 160;
static const guint clock_rate = 8000; static const guint PCMU_BUF_CLOCK_RATE = 8000;
static const guint pcmu_payload_type = 0; static const guint PCMU_BUF_PT = 0;
static const guint test_ssrc = 0x01BADBAD; static const guint PCMU_BUF_SSRC = 0x01BADBAD;
static const guint PCMU_BUF_MS = 20;
static const GstClockTime PCMU_BUF_DURATION = PCMU_BUF_MS * GST_MSECOND;
static const guint PCMU_BUF_SIZE = 64000 * PCMU_BUF_MS / 1000;
static const guint PCMU_RTP_TS_DURATION = PCMU_BUF_CLOCK_RATE * PCMU_BUF_MS / 1000;
typedef struct typedef struct
{ {
@ -396,14 +404,14 @@ generate_caps (void)
{ {
return gst_caps_new_simple ("application/x-rtp", return gst_caps_new_simple ("application/x-rtp",
"media", G_TYPE_STRING, "audio", "media", G_TYPE_STRING, "audio",
"clock-rate", G_TYPE_INT, clock_rate, "clock-rate", G_TYPE_INT, PCMU_BUF_CLOCK_RATE,
"encoding-name", G_TYPE_STRING, "PCMU", "encoding-name", G_TYPE_STRING, "PCMU",
"payload", G_TYPE_INT, pcmu_payload_type, "payload", G_TYPE_INT, PCMU_BUF_PT,
"ssrc", G_TYPE_UINT, test_ssrc, NULL); "ssrc", G_TYPE_UINT, PCMU_BUF_SSRC, NULL);
} }
static GstBuffer * static GstBuffer *
generate_test_buffer (GstClockTime gst_ts, generate_test_buffer_full (GstClockTime gst_ts,
gboolean marker_bit, guint seq_num, guint32 rtp_ts) gboolean marker_bit, guint seq_num, guint32 rtp_ts)
{ {
GstBuffer *buf; GstBuffer *buf;
@ -411,19 +419,19 @@ generate_test_buffer (GstClockTime gst_ts,
guint i; guint i;
GstRTPBuffer rtp = GST_RTP_BUFFER_INIT; GstRTPBuffer rtp = GST_RTP_BUFFER_INIT;
buf = gst_rtp_buffer_new_allocate (payload_size, 0, 0); buf = gst_rtp_buffer_new_allocate (PCMU_BUF_SIZE, 0, 0);
GST_BUFFER_DTS (buf) = gst_ts; GST_BUFFER_DTS (buf) = gst_ts;
GST_BUFFER_PTS (buf) = gst_ts; GST_BUFFER_PTS (buf) = gst_ts;
gst_rtp_buffer_map (buf, GST_MAP_READWRITE, &rtp); gst_rtp_buffer_map (buf, GST_MAP_READWRITE, &rtp);
gst_rtp_buffer_set_payload_type (&rtp, pcmu_payload_type); gst_rtp_buffer_set_payload_type (&rtp, PCMU_BUF_PT);
gst_rtp_buffer_set_marker (&rtp, marker_bit); gst_rtp_buffer_set_marker (&rtp, marker_bit);
gst_rtp_buffer_set_seq (&rtp, seq_num); gst_rtp_buffer_set_seq (&rtp, seq_num);
gst_rtp_buffer_set_timestamp (&rtp, rtp_ts); gst_rtp_buffer_set_timestamp (&rtp, rtp_ts);
gst_rtp_buffer_set_ssrc (&rtp, test_ssrc); gst_rtp_buffer_set_ssrc (&rtp, PCMU_BUF_SSRC);
payload = gst_rtp_buffer_get_payload (&rtp); payload = gst_rtp_buffer_get_payload (&rtp);
for (i = 0; i < payload_size; i++) for (i = 0; i < PCMU_BUF_SIZE; i++)
payload[i] = 0xff; payload[i] = 0xff;
gst_rtp_buffer_unmap (&rtp); gst_rtp_buffer_unmap (&rtp);
@ -431,6 +439,13 @@ generate_test_buffer (GstClockTime gst_ts,
return buf; return buf;
} }
static GstBuffer *
generate_test_buffer (guint seq_num)
{
return generate_test_buffer_full (seq_num * PCMU_BUF_DURATION,
TRUE, seq_num, seq_num * PCMU_RTP_TS_DURATION);
}
static GstFlowReturn static GstFlowReturn
test_sink_pad_chain_cb (GstPad * pad, GstObject * parent, GstBuffer * buffer) test_sink_pad_chain_cb (GstPad * pad, GstObject * parent, GstBuffer * buffer)
{ {
@ -578,15 +593,13 @@ destroy_testharness (TestData * data)
static void static void
verify_lost_event (GstEvent * event, guint32 expected_seqnum, verify_lost_event (GstEvent * event, guint32 expected_seqnum,
GstClockTime expected_timestamp, GstClockTime expected_duration, GstClockTime expected_timestamp, GstClockTime expected_duration)
gboolean expected_late)
{ {
const GstStructure *s = gst_event_get_structure (event); const GstStructure *s = gst_event_get_structure (event);
const GValue *value; const GValue *value;
guint32 seqnum; guint32 seqnum;
GstClockTime timestamp; GstClockTime timestamp;
GstClockTime duration; GstClockTime duration;
gboolean late;
g_assert (gst_structure_get_uint (s, "seqnum", &seqnum)); g_assert (gst_structure_get_uint (s, "seqnum", &seqnum));
@ -598,12 +611,9 @@ verify_lost_event (GstEvent * event, guint32 expected_seqnum,
g_assert (value && G_VALUE_HOLDS_UINT64 (value)); g_assert (value && G_VALUE_HOLDS_UINT64 (value));
duration = g_value_get_uint64 (value); duration = g_value_get_uint64 (value);
g_assert (gst_structure_get_boolean (s, "late", &late)); fail_unless_equals_int (seqnum, expected_seqnum);
fail_unless_equals_int (timestamp, expected_timestamp);
g_assert_cmpint (seqnum, ==, expected_seqnum); fail_unless_equals_int (duration, expected_duration);
g_assert_cmpint (timestamp, ==, expected_timestamp);
g_assert_cmpint (duration, ==, expected_duration);
g_assert (late == expected_late);
gst_event_unref (event); gst_event_unref (event);
} }
@ -643,86 +653,79 @@ verify_rtx_event (GstEvent * event, guint32 expected_seqnum,
GST_START_TEST (test_only_one_lost_event_on_large_gaps) GST_START_TEST (test_only_one_lost_event_on_large_gaps)
{ {
TestData data; GstHarness * h = gst_harness_new ("rtpjitterbuffer");
GstTestClock * testclock;
GstClockID id, test_id; GstClockID id, test_id;
GstBuffer *in_buf, *out_buf; GstBuffer *out_buf;
GstEvent *out_event; GstEvent *out_event;
gint jb_latency_ms = 200; gint jb_latency_ms = 200;
guint buffer_size_ms = (payload_size * 1000) / clock_rate; gint num_lost_events = jb_latency_ms / PCMU_BUF_MS;
GstRTPBuffer rtp = GST_RTP_BUFFER_INIT; GstRTPBuffer rtp = GST_RTP_BUFFER_INIT;
setup_testharness (&data); gst_harness_set_src_caps (h, generate_caps ());
gst_harness_use_testclock (h);
testclock = gst_harness_get_testclock (h);
g_object_set (h->element,
"do-lost", TRUE,
"latency", jb_latency_ms,
NULL);
g_object_set (data.jitter_buffer, "latency", jb_latency_ms, NULL);
/* push the first buffer in */ /* push the first buffer in */
in_buf = generate_test_buffer (0 * GST_MSECOND, TRUE, 0, 0); gst_harness_push (h, generate_test_buffer (0));
gst_test_clock_set_time (GST_TEST_CLOCK (data.clock), 0);
g_assert_cmpint (gst_pad_push (data.test_src_pad, in_buf), ==, GST_FLOW_OK);
/* wait for the first buffer to be synced to timestamp + latency */ /* wait for the first buffer to be synced to timestamp + latency */
gst_test_clock_wait_for_next_pending_id (GST_TEST_CLOCK (data.clock), &id); gst_test_clock_wait_for_next_pending_id (testclock, &id);
/* increase the time to timestamp + latency and release the wait */ /* increase the time to timestamp + latency and release the wait */
gst_test_clock_set_time (GST_TEST_CLOCK (data.clock), gst_test_clock_set_time (testclock, jb_latency_ms * GST_MSECOND);
jb_latency_ms * GST_MSECOND); test_id = gst_test_clock_process_next_clock_id (testclock);
test_id = gst_test_clock_process_next_clock_id (GST_TEST_CLOCK (data.clock)); fail_unless (id == test_id);
g_assert (test_id == id);
gst_clock_id_unref (test_id); gst_clock_id_unref (test_id);
gst_clock_id_unref (id); gst_clock_id_unref (id);
/* check for the buffer coming out that was pushed in */ /* check for the buffer coming out that was pushed in */
out_buf = g_async_queue_pop (data.buf_queue); out_buf = gst_harness_pull (h);
g_assert (out_buf != NULL); g_assert (out_buf != NULL);
g_assert_cmpint (GST_BUFFER_DTS (out_buf), ==, 0); g_assert_cmpint (GST_BUFFER_DTS (out_buf), ==, 0);
g_assert_cmpint (GST_BUFFER_PTS (out_buf), ==, 0); g_assert_cmpint (GST_BUFFER_PTS (out_buf), ==, 0);
gst_buffer_unref (out_buf); gst_buffer_unref (out_buf);
/* wait a bit */ /* move time ahead to just before 10 seconds */
g_usleep (G_USEC_PER_SEC / 10); gst_test_clock_set_time (testclock, 10 * GST_SECOND - 1);
/* check that no buffers have been pushed out and no pending waits */ /* check that we have no pending waits */
g_assert_cmpint (g_async_queue_length (data.buf_queue), ==, 0); fail_unless_equals_int (0, gst_test_clock_peek_id_count (testclock));
g_assert (gst_test_clock_peek_next_pending_id (GST_TEST_CLOCK (data.clock),
&id) == FALSE);
/* a buffer now arrives perfectly on time */ /* a buffer now arrives perfectly on time */
in_buf = generate_test_buffer (10 * GST_SECOND, FALSE, 500, 500 * 160); gst_harness_push (h, generate_test_buffer (500));
g_assert_cmpint (gst_pad_push (data.test_src_pad, in_buf), ==, GST_FLOW_OK);
/* release the wait */ /* release the wait, advancing the clock to 10 sec */
gst_test_clock_wait_for_next_pending_id (GST_TEST_CLOCK (data.clock), &id); gst_harness_crank_single_clock_wait (h);
gst_test_clock_advance_time (GST_TEST_CLOCK (data.clock), GST_MSECOND * 20);
test_id = gst_test_clock_process_next_clock_id (GST_TEST_CLOCK (data.clock));
/* move time ahead 10 seconds */ /* drop GstEventStreamStart & GstEventCaps & GstEventSegment */
gst_test_clock_set_time (GST_TEST_CLOCK (data.clock), 10 * GST_SECOND); for (int i = 0; i < 3; i++)
gst_event_unref (gst_harness_pull_event (h));
g_assert (id == test_id); /* we should now receive a packet-lost-event for buffers 1 through 489 ... */
gst_clock_id_unref (test_id); out_event = gst_harness_pull_event (h);
gst_clock_id_unref (id);
/* we should now receive a packet-lost-event for buffers 1 through 489 */
out_event = g_async_queue_pop (data.sink_event_queue);
g_assert (out_event != NULL); g_assert (out_event != NULL);
g_assert_cmpint (data.lost_event_count, ==, 1); verify_lost_event (out_event, 1, 1 * PCMU_BUF_DURATION, PCMU_BUF_DURATION * 489);
verify_lost_event (out_event, 1, 1 * GST_MSECOND * 20, GST_MSECOND * 20 * 490,
TRUE);
/* churn through sync_times until the new buffer gets pushed out */ /* ... as well as 490 (since at 10 sec 490 is too late) */
while (g_async_queue_length (data.buf_queue) < 1) { out_event = gst_harness_pull_event (h);
if (gst_test_clock_peek_next_pending_id (GST_TEST_CLOCK (data.clock), &id)) { g_assert (out_event != NULL);
GstClockTime t = gst_clock_id_get_time (id); verify_lost_event (out_event, 490, 490 * PCMU_BUF_DURATION, PCMU_BUF_DURATION);
if (t > gst_clock_get_time (data.clock)) {
gst_test_clock_set_time (GST_TEST_CLOCK (data.clock), t); /* we get as many lost events as the the number of buffers the jitterbuffer is able to wait for */
} for (int i = 1; i < num_lost_events; i++) {
test_id = gst_harness_crank_single_clock_wait (h);
gst_test_clock_process_next_clock_id (GST_TEST_CLOCK (data.clock)); out_event = gst_harness_pull_event (h);
gst_clock_id_unref (test_id); g_assert (out_event != NULL);
gst_clock_id_unref (id); verify_lost_event (out_event, 490 + i, (490 + i) * PCMU_BUF_DURATION, PCMU_BUF_DURATION);
}
} }
out_buf = g_async_queue_pop (data.buf_queue); /* and then the buffer is released */
out_buf = gst_harness_pull (h);
g_assert (out_buf != NULL); g_assert (out_buf != NULL);
g_assert (GST_BUFFER_FLAG_IS_SET (out_buf, GST_BUFFER_FLAG_DISCONT)); g_assert (GST_BUFFER_FLAG_IS_SET (out_buf, GST_BUFFER_FLAG_DISCONT));
gst_rtp_buffer_map (out_buf, GST_MAP_READ, &rtp); gst_rtp_buffer_map (out_buf, GST_MAP_READ, &rtp);
@ -732,95 +735,79 @@ GST_START_TEST (test_only_one_lost_event_on_large_gaps)
g_assert_cmpint (GST_BUFFER_PTS (out_buf), ==, (10 * GST_SECOND)); g_assert_cmpint (GST_BUFFER_PTS (out_buf), ==, (10 * GST_SECOND));
gst_buffer_unref (out_buf); gst_buffer_unref (out_buf);
/* we get as many lost events as the the number of buffers the jitterbuffer gst_object_unref (testclock);
* is able to wait for (+ the one we already got) */ gst_harness_teardown (h);
g_assert_cmpint (data.lost_event_count, ==, jb_latency_ms / buffer_size_ms);
destroy_testharness (&data);
} }
GST_END_TEST; GST_END_TEST;
GST_START_TEST (test_two_lost_one_arrives_in_time) GST_START_TEST (test_two_lost_one_arrives_in_time)
{ {
TestData data; GstHarness * h = gst_harness_new ("rtpjitterbuffer");
GstClockID id, test_id; GstTestClock * testclock;
GstBuffer *in_buf, *out_buf; GstClockID id;
GstBuffer *out_buf;
GstEvent *out_event; GstEvent *out_event;
gint jb_latency_ms = 100; gint jb_latency_ms = 100; /* FIXME: setting this to 10 produces a strange result (30ms lost event), find out why! */
GstClockTime buffer_time, now;
gint b;
GstRTPBuffer rtp = GST_RTP_BUFFER_INIT; GstRTPBuffer rtp = GST_RTP_BUFFER_INIT;
GstClockTime buffer_time;
gint b;
setup_testharness (&data); gst_harness_set_src_caps (h, generate_caps ());
gst_harness_use_testclock (h);
testclock = gst_harness_get_testclock (h);
g_object_set (h->element,
"do-lost", TRUE,
"latency", jb_latency_ms,
NULL);
g_object_set (data.jitter_buffer, "latency", jb_latency_ms, NULL); /* push the first buffer through */
gst_harness_push (h, generate_test_buffer (0));
/* push the first buffer in */ gst_harness_crank_single_clock_wait (h);
in_buf = generate_test_buffer (0 * GST_MSECOND, TRUE, 0, 0); gst_buffer_unref (gst_harness_pull (h));
gst_test_clock_set_time (GST_TEST_CLOCK (data.clock), 0);
g_assert_cmpint (gst_pad_push (data.test_src_pad, in_buf), ==, GST_FLOW_OK);
gst_test_clock_wait_for_next_pending_id (GST_TEST_CLOCK (data.clock), &id);
now = jb_latency_ms * GST_MSECOND;
gst_test_clock_set_time (GST_TEST_CLOCK (data.clock), now);
test_id = gst_test_clock_process_next_clock_id (GST_TEST_CLOCK (data.clock));
g_assert (test_id == id);
gst_clock_id_unref (test_id);
gst_clock_id_unref (id);
out_buf = g_async_queue_pop (data.buf_queue);
g_assert (out_buf != NULL);
/* push some buffers arriving in perfect time! */ /* push some buffers arriving in perfect time! */
for (b = 1; b < 3; b++) { for (b = 1; b < 3; b++) {
buffer_time = b * GST_MSECOND * 20; buffer_time = b * PCMU_BUF_DURATION;
in_buf = generate_test_buffer (buffer_time, TRUE, b, b * 160); gst_harness_push (h, generate_test_buffer (b));
gst_test_clock_set_time (GST_TEST_CLOCK (data.clock), now + buffer_time);
g_assert_cmpint (gst_pad_push (data.test_src_pad, in_buf), ==, GST_FLOW_OK);
gst_buffer_unref (out_buf);
/* check for the buffer coming out that was pushed in */ /* check for the buffer coming out that was pushed in */
out_buf = g_async_queue_pop (data.buf_queue); out_buf = gst_harness_pull (h);
g_assert (out_buf != NULL); g_assert (out_buf != NULL);
g_assert_cmpint (GST_BUFFER_DTS (out_buf), ==, buffer_time); g_assert_cmpint (GST_BUFFER_DTS (out_buf), ==, buffer_time);
g_assert_cmpint (GST_BUFFER_PTS (out_buf), ==, buffer_time); g_assert_cmpint (GST_BUFFER_PTS (out_buf), ==, buffer_time);
gst_buffer_unref (out_buf);
} }
gst_buffer_unref (out_buf);
/* hop over 2 packets and make another one (gap of 2) */ /* hop over 2 packets and make another one (gap of 2) */
b = 5; b = 5;
buffer_time = b * GST_MSECOND * 20; buffer_time = b * PCMU_BUF_DURATION;
in_buf = generate_test_buffer (buffer_time, TRUE, b, b * 160); gst_harness_push (h, generate_test_buffer (b));
g_assert_cmpint (gst_pad_push (data.test_src_pad, in_buf), ==, GST_FLOW_OK);
/* verify that the jitterbuffer now wait for the latest moment it can push */ /* verify that the jitterbuffer now wait for the latest moment it can push */
/* the first lost buffer (buffer 3) out on (buffer-timestamp (60) + latency (10) = 70) */ /* the first lost buffer (buffer 3) out on (buffer-timestamp (60) + latency (100) = 160) */
gst_test_clock_wait_for_next_pending_id (GST_TEST_CLOCK (data.clock), &id); gst_test_clock_wait_for_next_pending_id (testclock, &id);
g_assert_cmpint (gst_clock_id_get_time (id), ==, g_assert_cmpint (gst_clock_id_get_time (id), ==, (3 * PCMU_BUF_DURATION) + (jb_latency_ms * GST_MSECOND));
(3 * GST_MSECOND * 20) + (jb_latency_ms * GST_MSECOND));
/* let the time expire... */
gst_test_clock_set_time (GST_TEST_CLOCK (data.clock),
gst_clock_id_get_time (id));
test_id = gst_test_clock_process_next_clock_id (GST_TEST_CLOCK (data.clock));
g_assert (test_id == id);
gst_clock_id_unref (test_id);
gst_clock_id_unref (id); gst_clock_id_unref (id);
/* let the time expire... */
gst_harness_crank_single_clock_wait (h);
/* drop GstEventStreamStart & GstEventCaps & GstEventSegment */
for (int i = 0; i < 3; i++)
gst_event_unref (gst_harness_pull_event (h));
/* we should now receive a packet-lost-event for buffer 3 */ /* we should now receive a packet-lost-event for buffer 3 */
out_event = g_async_queue_pop (data.sink_event_queue); out_event = gst_harness_pull_event (h);
g_assert (out_event != NULL); g_assert (out_event != NULL);
g_assert_cmpint (data.lost_event_count, ==, 1); verify_lost_event (out_event, 3, 3 * PCMU_BUF_DURATION, PCMU_BUF_DURATION);
verify_lost_event (out_event, 3, 3 * GST_MSECOND * 20, GST_MSECOND * 20,
FALSE);
/* buffer 4 now arrives just in time (time is 70, buffer 4 expires at 90) */ /* buffer 4 now arrives just in time (time is 70, buffer 4 expires at 90) */
b = 4; b = 4;
buffer_time = b * GST_MSECOND * 20; buffer_time = b * PCMU_BUF_DURATION;
in_buf = generate_test_buffer (buffer_time, TRUE, b, b * 160); gst_harness_push (h, generate_test_buffer (b));
g_assert_cmpint (gst_pad_push (data.test_src_pad, in_buf), ==, GST_FLOW_OK);
/* verify that buffer 4 made it through! */ /* verify that buffer 4 made it through! */
out_buf = g_async_queue_pop (data.buf_queue); out_buf = gst_harness_pull (h);
g_assert (out_buf != NULL); g_assert (out_buf != NULL);
g_assert (GST_BUFFER_FLAG_IS_SET (out_buf, GST_BUFFER_FLAG_DISCONT)); g_assert (GST_BUFFER_FLAG_IS_SET (out_buf, GST_BUFFER_FLAG_DISCONT));
gst_rtp_buffer_map (out_buf, GST_MAP_READ, &rtp); gst_rtp_buffer_map (out_buf, GST_MAP_READ, &rtp);
@ -829,7 +816,7 @@ GST_START_TEST (test_two_lost_one_arrives_in_time)
gst_buffer_unref (out_buf); gst_buffer_unref (out_buf);
/* and see that buffer 5 now arrives in a normal fashion */ /* and see that buffer 5 now arrives in a normal fashion */
out_buf = g_async_queue_pop (data.buf_queue); out_buf = gst_harness_pull (h);
g_assert (out_buf != NULL); g_assert (out_buf != NULL);
g_assert (!GST_BUFFER_FLAG_IS_SET (out_buf, GST_BUFFER_FLAG_DISCONT)); g_assert (!GST_BUFFER_FLAG_IS_SET (out_buf, GST_BUFFER_FLAG_DISCONT));
gst_rtp_buffer_map (out_buf, GST_MAP_READ, &rtp); gst_rtp_buffer_map (out_buf, GST_MAP_READ, &rtp);
@ -837,72 +824,68 @@ GST_START_TEST (test_two_lost_one_arrives_in_time)
gst_rtp_buffer_unmap (&rtp); gst_rtp_buffer_unmap (&rtp);
gst_buffer_unref (out_buf); gst_buffer_unref (out_buf);
/* should still have only seen 1 packet lost event */ gst_object_unref (testclock);
g_assert_cmpint (data.lost_event_count, ==, 1); gst_harness_teardown (h);
destroy_testharness (&data);
} }
GST_END_TEST; GST_END_TEST;
GST_START_TEST (test_late_packets_still_makes_lost_events) GST_START_TEST (test_late_packets_still_makes_lost_events)
{ {
TestData data; GstHarness * h = gst_harness_new ("rtpjitterbuffer");
GstClockID id, test_id; GstTestClock * testclock;
GstBuffer *in_buf, *out_buf; GstBuffer *out_buf;
GstEvent *out_event; GstEvent *out_event;
gint jb_latency_ms = 10; gint jb_latency_ms = 100;
GstRTPBuffer rtp = GST_RTP_BUFFER_INIT;
GstClockTime buffer_time; GstClockTime buffer_time;
gint b; gint b;
GstRTPBuffer rtp = GST_RTP_BUFFER_INIT;
setup_testharness (&data); gst_harness_set_src_caps (h, generate_caps ());
gst_harness_use_testclock (h);
testclock = gst_harness_get_testclock (h);
g_object_set (h->element,
"do-lost", TRUE,
"latency", jb_latency_ms,
NULL);
g_object_set (data.jitter_buffer, "latency", jb_latency_ms, NULL); /* advance the clock with 10 seconds */
gst_test_clock_set_time (testclock, 10 * GST_SECOND);
/* push the first buffer in */ /* push the first buffer through */
in_buf = generate_test_buffer (0 * GST_MSECOND, TRUE, 0, 0); gst_buffer_unref (gst_harness_push_and_pull (h, generate_test_buffer (0)));
g_assert_cmpint (gst_pad_push (data.test_src_pad, in_buf), ==, GST_FLOW_OK);
gst_test_clock_wait_for_next_pending_id (GST_TEST_CLOCK (data.clock), &id); /* push some buffers arriving in perfect time! */
gst_test_clock_set_time (GST_TEST_CLOCK (data.clock), 10 * GST_SECOND);
test_id = gst_test_clock_process_next_clock_id (GST_TEST_CLOCK (data.clock));
g_assert (test_id == id);
gst_clock_id_unref (id);
gst_clock_id_unref (test_id);
out_buf = g_async_queue_pop (data.buf_queue);
g_assert (out_buf != NULL);
/* push some buffers in! */
for (b = 1; b < 3; b++) { for (b = 1; b < 3; b++) {
buffer_time = b * GST_MSECOND * 20; buffer_time = b * PCMU_BUF_DURATION;
in_buf = generate_test_buffer (buffer_time, TRUE, b, b * 160); gst_harness_push (h, generate_test_buffer (b));
g_assert_cmpint (gst_pad_push (data.test_src_pad, in_buf), ==, GST_FLOW_OK);
gst_buffer_unref (out_buf);
/* check for the buffer coming out that was pushed in */ /* check for the buffer coming out that was pushed in */
out_buf = g_async_queue_pop (data.buf_queue); out_buf = gst_harness_pull (h);
g_assert (out_buf != NULL); g_assert (out_buf != NULL);
g_assert_cmpint (GST_BUFFER_DTS (out_buf), ==, buffer_time); g_assert_cmpint (GST_BUFFER_DTS (out_buf), ==, buffer_time);
g_assert_cmpint (GST_BUFFER_PTS (out_buf), ==, buffer_time); g_assert_cmpint (GST_BUFFER_PTS (out_buf), ==, buffer_time);
gst_buffer_unref (out_buf);
} }
gst_buffer_unref (out_buf);
/* hop over 2 packets and make another one (gap of 2) */ /* hop over 2 packets and make another one (gap of 2) */
b = 5; b = 5;
buffer_time = b * GST_MSECOND * 20; buffer_time = b * PCMU_BUF_DURATION;
in_buf = generate_test_buffer (buffer_time, TRUE, b, b * 160); gst_harness_push (h, generate_test_buffer (b));
g_assert_cmpint (gst_pad_push (data.test_src_pad, in_buf), ==, GST_FLOW_OK);
/* we should now receive a packet-lost-event for buffer 3 and 4 */ /* drop GstEventStreamStart & GstEventCaps & GstEventSegment */
out_event = g_async_queue_pop (data.sink_event_queue); for (int i = 0; i < 3; i++)
gst_event_unref (gst_harness_pull_event (h));
/* we should now receive packet-lost-events for buffer 3 and 4*/
out_event = gst_harness_pull_event (h);
g_assert (out_event != NULL); g_assert (out_event != NULL);
g_assert_cmpint (data.lost_event_count, ==, 1); verify_lost_event (out_event, 3, 3 * PCMU_BUF_DURATION, PCMU_BUF_DURATION);
verify_lost_event (out_event, 3, 3 * GST_MSECOND * 20, GST_MSECOND * 20 * 2, out_event = gst_harness_pull_event (h);
TRUE); g_assert (out_event != NULL);
verify_lost_event (out_event, 4, 4 * PCMU_BUF_DURATION, PCMU_BUF_DURATION);
/* verify that buffer 5 made it through! */ /* verify that buffer 5 made it through! */
out_buf = g_async_queue_pop (data.buf_queue); out_buf = gst_harness_pull (h);
g_assert (out_buf != NULL); g_assert (out_buf != NULL);
g_assert (GST_BUFFER_FLAG_IS_SET (out_buf, GST_BUFFER_FLAG_DISCONT)); g_assert (GST_BUFFER_FLAG_IS_SET (out_buf, GST_BUFFER_FLAG_DISCONT));
gst_rtp_buffer_map (out_buf, GST_MAP_READ, &rtp); gst_rtp_buffer_map (out_buf, GST_MAP_READ, &rtp);
@ -910,73 +893,65 @@ GST_START_TEST (test_late_packets_still_makes_lost_events)
gst_rtp_buffer_unmap (&rtp); gst_rtp_buffer_unmap (&rtp);
gst_buffer_unref (out_buf); gst_buffer_unref (out_buf);
/* should still have only seen 1 packet lost event */ gst_object_unref (testclock);
g_assert_cmpint (data.lost_event_count, ==, 1); gst_harness_teardown (h);
destroy_testharness (&data);
} }
GST_END_TEST; GST_END_TEST;
GST_START_TEST (test_all_packets_are_timestamped_zero) GST_START_TEST (test_all_packets_are_timestamped_zero)
{ {
TestData data; GstHarness * h = gst_harness_new ("rtpjitterbuffer");
GstClockID id, test_id; GstTestClock * testclock;
GstBuffer *in_buf, *out_buf; GstBuffer *out_buf;
GstEvent *out_event; GstEvent *out_event;
gint jb_latency_ms = 10; gint jb_latency_ms = 100;
gint b;
GstRTPBuffer rtp = GST_RTP_BUFFER_INIT; GstRTPBuffer rtp = GST_RTP_BUFFER_INIT;
gint b;
setup_testharness (&data); gst_harness_set_src_caps (h, generate_caps ());
gst_harness_use_testclock (h);
testclock = gst_harness_get_testclock (h);
g_object_set (h->element,
"do-lost", TRUE,
"latency", jb_latency_ms,
NULL);
g_object_set (data.jitter_buffer, "latency", jb_latency_ms, NULL); /* advance the clock with 10 seconds */
gst_test_clock_set_time (testclock, 10 * GST_SECOND);
/* push the first buffer in */ /* push the first buffer through */
in_buf = generate_test_buffer (0 * GST_MSECOND, TRUE, 0, 0); gst_buffer_unref (gst_harness_push_and_pull (h, generate_test_buffer (0)));
g_assert_cmpint (gst_pad_push (data.test_src_pad, in_buf), ==, GST_FLOW_OK);
gst_test_clock_wait_for_next_pending_id (GST_TEST_CLOCK (data.clock), &id); /* push some buffers in, all timestamped 0 */
gst_test_clock_set_time (GST_TEST_CLOCK (data.clock), 10 * GST_SECOND);
test_id = gst_test_clock_process_next_clock_id (GST_TEST_CLOCK (data.clock));
g_assert (test_id == id);
gst_clock_id_unref (test_id);
gst_clock_id_unref (id);
out_buf = g_async_queue_pop (data.buf_queue);
g_assert (out_buf != NULL);
/* push some buffers in! */
for (b = 1; b < 3; b++) { for (b = 1; b < 3; b++) {
in_buf = generate_test_buffer (0, TRUE, b, 0); gst_harness_push (h, generate_test_buffer_full (0 * GST_MSECOND, TRUE, b, 0));
g_assert_cmpint (gst_pad_push (data.test_src_pad, in_buf), ==, GST_FLOW_OK);
gst_buffer_unref (out_buf);
/* check for the buffer coming out that was pushed in */ /* check for the buffer coming out that was pushed in */
out_buf = g_async_queue_pop (data.buf_queue); out_buf = gst_harness_pull (h);
g_assert (out_buf != NULL); g_assert (out_buf != NULL);
g_assert_cmpint (GST_BUFFER_DTS (out_buf), ==, 0); g_assert_cmpint (GST_BUFFER_DTS (out_buf), ==, 0);
g_assert_cmpint (GST_BUFFER_PTS (out_buf), ==, 0); g_assert_cmpint (GST_BUFFER_PTS (out_buf), ==, 0);
gst_buffer_unref (out_buf);
} }
gst_buffer_unref (out_buf);
/* hop over 2 packets and make another one (gap of 2) */ /* hop over 2 packets and make another one (gap of 2) */
b = 5; b = 5;
in_buf = generate_test_buffer (0, TRUE, b, 0); gst_harness_push (h, generate_test_buffer_full (0 * GST_MSECOND, TRUE, b, 0));
g_assert_cmpint (gst_pad_push (data.test_src_pad, in_buf), ==, GST_FLOW_OK);
/* we should now receive a packet-lost-event for buffer 3 and 4 */ /* drop GstEventStreamStart & GstEventCaps & GstEventSegment */
out_event = g_async_queue_pop (data.sink_event_queue); for (int i = 0; i < 3; i++)
gst_event_unref (gst_harness_pull_event (h));
/* we should now receive packet-lost-events for buffer 3 and 4 */
out_event = gst_harness_pull_event (h);
g_assert (out_event != NULL); g_assert (out_event != NULL);
verify_lost_event (out_event, 3, 0, 0, FALSE); verify_lost_event (out_event, 3, 0, 0);
out_event = gst_harness_pull_event (h);
out_event = g_async_queue_pop (data.sink_event_queue);
g_assert (out_event != NULL); g_assert (out_event != NULL);
verify_lost_event (out_event, 4, 0, 0, FALSE); verify_lost_event (out_event, 4, 0, 0);
g_assert_cmpint (data.lost_event_count, ==, 2);
/* verify that buffer 5 made it through! */ /* verify that buffer 5 made it through! */
out_buf = g_async_queue_pop (data.buf_queue); out_buf = gst_harness_pull (h);
g_assert (out_buf != NULL); g_assert (out_buf != NULL);
g_assert (GST_BUFFER_FLAG_IS_SET (out_buf, GST_BUFFER_FLAG_DISCONT)); g_assert (GST_BUFFER_FLAG_IS_SET (out_buf, GST_BUFFER_FLAG_DISCONT));
gst_rtp_buffer_map (out_buf, GST_MAP_READ, &rtp); gst_rtp_buffer_map (out_buf, GST_MAP_READ, &rtp);
@ -984,12 +959,9 @@ GST_START_TEST (test_all_packets_are_timestamped_zero)
gst_rtp_buffer_unmap (&rtp); gst_rtp_buffer_unmap (&rtp);
gst_buffer_unref (out_buf); gst_buffer_unref (out_buf);
/* should still have only seen 2 packet lost events */ gst_object_unref (testclock);
g_assert_cmpint (data.lost_event_count, ==, 2); gst_harness_teardown (h);
destroy_testharness (&data);
} }
GST_END_TEST; GST_END_TEST;
GST_START_TEST (test_rtx_expected_next) GST_START_TEST (test_rtx_expected_next)
@ -1008,7 +980,7 @@ GST_START_TEST (test_rtx_expected_next)
gst_test_clock_set_time (GST_TEST_CLOCK (data.clock), 0); gst_test_clock_set_time (GST_TEST_CLOCK (data.clock), 0);
/* push the first buffer in */ /* push the first buffer in */
in_buf = generate_test_buffer (0 * GST_MSECOND, TRUE, 0, 0); in_buf = generate_test_buffer (0);
g_assert_cmpint (gst_pad_push (data.test_src_pad, in_buf), ==, GST_FLOW_OK); g_assert_cmpint (gst_pad_push (data.test_src_pad, in_buf), ==, GST_FLOW_OK);
gst_test_clock_set_time (GST_TEST_CLOCK (data.clock), 20 * GST_MSECOND); gst_test_clock_set_time (GST_TEST_CLOCK (data.clock), 20 * GST_MSECOND);
@ -1019,7 +991,7 @@ GST_START_TEST (test_rtx_expected_next)
/* put second buffer, the jitterbuffer should now know that the packet spacing /* put second buffer, the jitterbuffer should now know that the packet spacing
* is 20ms and should ask for retransmission of seqnum 2 in 20ms+10ms because * is 20ms and should ask for retransmission of seqnum 2 in 20ms+10ms because
* 2*jitter==0 and 0.5*packet_spacing==10ms */ * 2*jitter==0 and 0.5*packet_spacing==10ms */
in_buf = generate_test_buffer (20 * GST_MSECOND, TRUE, 1, 160); in_buf = generate_test_buffer (1);
g_assert_cmpint (gst_pad_push (data.test_src_pad, in_buf), ==, GST_FLOW_OK); g_assert_cmpint (gst_pad_push (data.test_src_pad, in_buf), ==, GST_FLOW_OK);
gst_test_clock_wait_for_next_pending_id (GST_TEST_CLOCK (data.clock), &id); gst_test_clock_wait_for_next_pending_id (GST_TEST_CLOCK (data.clock), &id);
@ -1031,7 +1003,7 @@ GST_START_TEST (test_rtx_expected_next)
out_event = g_async_queue_pop (data.src_event_queue); out_event = g_async_queue_pop (data.src_event_queue);
g_assert (out_event != NULL); g_assert (out_event != NULL);
verify_rtx_event (out_event, 2, 40 * GST_MSECOND, 10, 20 * GST_MSECOND); verify_rtx_event (out_event, 2, 40 * GST_MSECOND, 10, PCMU_BUF_DURATION);
/* now we wait for the next timeout, all following timeouts 40ms in the /* now we wait for the next timeout, all following timeouts 40ms in the
* future because this is rtx-retry-timeout */ * future because this is rtx-retry-timeout */
@ -1044,7 +1016,7 @@ GST_START_TEST (test_rtx_expected_next)
out_event = g_async_queue_pop (data.src_event_queue); out_event = g_async_queue_pop (data.src_event_queue);
g_assert (out_event != NULL); g_assert (out_event != NULL);
verify_rtx_event (out_event, 2, 40 * GST_MSECOND, 50, 20 * GST_MSECOND); verify_rtx_event (out_event, 2, 40 * GST_MSECOND, 50, PCMU_BUF_DURATION);
gst_test_clock_wait_for_next_pending_id (GST_TEST_CLOCK (data.clock), &id); gst_test_clock_wait_for_next_pending_id (GST_TEST_CLOCK (data.clock), &id);
gst_test_clock_set_time (GST_TEST_CLOCK (data.clock), 130 * GST_MSECOND); gst_test_clock_set_time (GST_TEST_CLOCK (data.clock), 130 * GST_MSECOND);
@ -1055,7 +1027,7 @@ GST_START_TEST (test_rtx_expected_next)
out_event = g_async_queue_pop (data.src_event_queue); out_event = g_async_queue_pop (data.src_event_queue);
g_assert (out_event != NULL); g_assert (out_event != NULL);
verify_rtx_event (out_event, 2, 40 * GST_MSECOND, 90, 20 * GST_MSECOND); verify_rtx_event (out_event, 2, 40 * GST_MSECOND, 90, PCMU_BUF_DURATION);
gst_test_clock_wait_for_next_pending_id (GST_TEST_CLOCK (data.clock), &id); gst_test_clock_wait_for_next_pending_id (GST_TEST_CLOCK (data.clock), &id);
gst_test_clock_set_time (GST_TEST_CLOCK (data.clock), 200 * GST_MSECOND); gst_test_clock_set_time (GST_TEST_CLOCK (data.clock), 200 * GST_MSECOND);
@ -1079,7 +1051,7 @@ GST_START_TEST (test_rtx_expected_next)
/* we should now receive a packet-lost-event for buffer 2 */ /* we should now receive a packet-lost-event for buffer 2 */
out_event = g_async_queue_pop (data.sink_event_queue); out_event = g_async_queue_pop (data.sink_event_queue);
g_assert (out_event != NULL); g_assert (out_event != NULL);
verify_lost_event (out_event, 2, 40 * GST_MSECOND, 20 * GST_MSECOND, FALSE); verify_lost_event (out_event, 2, 40 * GST_MSECOND, PCMU_BUF_DURATION);
destroy_testharness (&data); destroy_testharness (&data);
} }
@ -1106,7 +1078,7 @@ GST_START_TEST (test_rtx_two_missing)
gst_test_clock_set_time (GST_TEST_CLOCK (data.clock), 0); gst_test_clock_set_time (GST_TEST_CLOCK (data.clock), 0);
/* push the first buffer in */ /* push the first buffer in */
in_buf = generate_test_buffer (0 * GST_MSECOND, TRUE, 0, 0); in_buf = generate_test_buffer (0);
g_assert_cmpint (gst_pad_push (data.test_src_pad, in_buf), ==, GST_FLOW_OK); g_assert_cmpint (gst_pad_push (data.test_src_pad, in_buf), ==, GST_FLOW_OK);
gst_test_clock_set_time (GST_TEST_CLOCK (data.clock), 20 * GST_MSECOND); gst_test_clock_set_time (GST_TEST_CLOCK (data.clock), 20 * GST_MSECOND);
@ -1114,12 +1086,12 @@ GST_START_TEST (test_rtx_two_missing)
/* put second buffer, the jitterbuffer should now know that the packet spacing /* put second buffer, the jitterbuffer should now know that the packet spacing
* is 20ms and should ask for retransmission of seqnum 2 in 20ms+10ms because * is 20ms and should ask for retransmission of seqnum 2 in 20ms+10ms because
* 2*jitter==0 and 0.5*packet_spacing==10ms */ * 2*jitter==0 and 0.5*packet_spacing==10ms */
in_buf = generate_test_buffer (20 * GST_MSECOND, TRUE, 1, 160); in_buf = generate_test_buffer (1);
g_assert_cmpint (gst_pad_push (data.test_src_pad, in_buf), ==, GST_FLOW_OK); g_assert_cmpint (gst_pad_push (data.test_src_pad, in_buf), ==, GST_FLOW_OK);
/* push buffer 4, 2 and 3 are missing now, we should get retransmission events /* push buffer 4, 2 and 3 are missing now, we should get retransmission events
* for 3 at 100ms*/ * for 3 at 100ms*/
in_buf = generate_test_buffer (80 * GST_MSECOND, TRUE, 4, 4 * 160); in_buf = generate_test_buffer (4);
g_assert_cmpint (gst_pad_push (data.test_src_pad, in_buf), ==, GST_FLOW_OK); g_assert_cmpint (gst_pad_push (data.test_src_pad, in_buf), ==, GST_FLOW_OK);
/* wait for first retransmission request */ /* wait for first retransmission request */
@ -1133,7 +1105,7 @@ GST_START_TEST (test_rtx_two_missing)
/* First event for 2 */ /* First event for 2 */
out_event = g_async_queue_pop (data.src_event_queue); out_event = g_async_queue_pop (data.src_event_queue);
g_assert (out_event != NULL); g_assert (out_event != NULL);
verify_rtx_event (out_event, 2, 40 * GST_MSECOND, 10, 20 * GST_MSECOND); verify_rtx_event (out_event, 2, 40 * GST_MSECOND, 10, PCMU_BUF_DURATION);
/* wait for second retransmission request */ /* wait for second retransmission request */
gst_test_clock_wait_for_next_pending_id (GST_TEST_CLOCK (data.clock), &id); gst_test_clock_wait_for_next_pending_id (GST_TEST_CLOCK (data.clock), &id);
@ -1146,7 +1118,7 @@ GST_START_TEST (test_rtx_two_missing)
/* Second event for 3 */ /* Second event for 3 */
out_event = g_async_queue_pop (data.src_event_queue); out_event = g_async_queue_pop (data.src_event_queue);
g_assert (out_event != NULL); g_assert (out_event != NULL);
verify_rtx_event (out_event, 3, 60 * GST_MSECOND, 0, 20 * GST_MSECOND); verify_rtx_event (out_event, 3, 60 * GST_MSECOND, 0, PCMU_BUF_DURATION);
/* now we wait for the next timeout for 2 */ /* now we wait for the next timeout for 2 */
gst_test_clock_wait_for_next_pending_id (GST_TEST_CLOCK (data.clock), &id); gst_test_clock_wait_for_next_pending_id (GST_TEST_CLOCK (data.clock), &id);
@ -1159,7 +1131,7 @@ GST_START_TEST (test_rtx_two_missing)
/* First event for 2 */ /* First event for 2 */
out_event = g_async_queue_pop (data.src_event_queue); out_event = g_async_queue_pop (data.src_event_queue);
g_assert (out_event != NULL); g_assert (out_event != NULL);
verify_rtx_event (out_event, 2, 40 * GST_MSECOND, 50, 20 * GST_MSECOND); verify_rtx_event (out_event, 2, 40 * GST_MSECOND, 50, PCMU_BUF_DURATION);
/* now we wait for the next timeout for 3 */ /* now we wait for the next timeout for 3 */
gst_test_clock_wait_for_next_pending_id (GST_TEST_CLOCK (data.clock), &id); gst_test_clock_wait_for_next_pending_id (GST_TEST_CLOCK (data.clock), &id);
@ -1172,15 +1144,15 @@ GST_START_TEST (test_rtx_two_missing)
/* Second event for 3 */ /* Second event for 3 */
out_event = g_async_queue_pop (data.src_event_queue); out_event = g_async_queue_pop (data.src_event_queue);
g_assert (out_event != NULL); g_assert (out_event != NULL);
verify_rtx_event (out_event, 3, 60 * GST_MSECOND, 40, 20 * GST_MSECOND); verify_rtx_event (out_event, 3, 60 * GST_MSECOND, 40, PCMU_BUF_DURATION);
/* make buffer 3 */ /* make buffer 3 */
in_buf = generate_test_buffer (60 * GST_MSECOND, TRUE, 3, 3 * 160); in_buf = generate_test_buffer (3);
g_assert_cmpint (gst_pad_push (data.test_src_pad, in_buf), ==, GST_FLOW_OK); g_assert_cmpint (gst_pad_push (data.test_src_pad, in_buf), ==, GST_FLOW_OK);
/* make more buffers */ /* make more buffers */
for (i = 5; i < 15; i++) { for (i = 5; i < 15; i++) {
in_buf = generate_test_buffer (i * 20 * GST_MSECOND, TRUE, i, i * 160); in_buf = generate_test_buffer (i);
g_assert_cmpint (gst_pad_push (data.test_src_pad, in_buf), ==, GST_FLOW_OK); g_assert_cmpint (gst_pad_push (data.test_src_pad, in_buf), ==, GST_FLOW_OK);
} }
@ -1194,7 +1166,7 @@ GST_START_TEST (test_rtx_two_missing)
/* now we only get requests for 2 */ /* now we only get requests for 2 */
out_event = g_async_queue_pop (data.src_event_queue); out_event = g_async_queue_pop (data.src_event_queue);
g_assert (out_event != NULL); g_assert (out_event != NULL);
verify_rtx_event (out_event, 2, 40 * GST_MSECOND, 90, 20 * GST_MSECOND); verify_rtx_event (out_event, 2, 40 * GST_MSECOND, 90, PCMU_BUF_DURATION);
/* this is when buffer 0 deadline expires */ /* this is when buffer 0 deadline expires */
gst_test_clock_wait_for_next_pending_id (GST_TEST_CLOCK (data.clock), &id); gst_test_clock_wait_for_next_pending_id (GST_TEST_CLOCK (data.clock), &id);
@ -1225,7 +1197,7 @@ GST_START_TEST (test_rtx_two_missing)
/* we should now receive a packet-lost-event for buffer 2 */ /* we should now receive a packet-lost-event for buffer 2 */
out_event = g_async_queue_pop (data.sink_event_queue); out_event = g_async_queue_pop (data.sink_event_queue);
g_assert (out_event != NULL); g_assert (out_event != NULL);
verify_lost_event (out_event, 2, 40 * GST_MSECOND, 20 * GST_MSECOND, FALSE); verify_lost_event (out_event, 2, 40 * GST_MSECOND, PCMU_BUF_DURATION);
/* verify that buffers made it through! */ /* verify that buffers made it through! */
for (i = 3; i < 15; i++) { for (i = 3; i < 15; i++) {
@ -1273,14 +1245,14 @@ GST_START_TEST (test_rtx_packet_delay)
g_object_set (data.jitter_buffer, "rtx-retry-period", 120, NULL); g_object_set (data.jitter_buffer, "rtx-retry-period", 120, NULL);
/* push the first buffer in */ /* push the first buffer in */
in_buf = generate_test_buffer (0 * GST_MSECOND, TRUE, 0, 0); in_buf = generate_test_buffer (0);
GST_BUFFER_FLAG_SET (in_buf, GST_BUFFER_FLAG_DISCONT); GST_BUFFER_FLAG_SET (in_buf, GST_BUFFER_FLAG_DISCONT);
g_assert_cmpint (gst_pad_push (data.test_src_pad, in_buf), ==, GST_FLOW_OK); g_assert_cmpint (gst_pad_push (data.test_src_pad, in_buf), ==, GST_FLOW_OK);
/* put second buffer, the jitterbuffer should now know that the packet spacing /* put second buffer, the jitterbuffer should now know that the packet spacing
* is 20ms and should ask for retransmission of seqnum 2 in 20ms+10ms because * is 20ms and should ask for retransmission of seqnum 2 in 20ms+10ms because
* 2*jitter==0 and 0.5*packet_spacing==10ms */ * 2*jitter==0 and 0.5*packet_spacing==10ms */
in_buf = generate_test_buffer (20 * GST_MSECOND, TRUE, 1, 160); in_buf = generate_test_buffer (1);
g_assert_cmpint (gst_pad_push (data.test_src_pad, in_buf), ==, GST_FLOW_OK); g_assert_cmpint (gst_pad_push (data.test_src_pad, in_buf), ==, GST_FLOW_OK);
/* push buffer 8, 2 -> 7 are missing now. note that the rtp time is the same /* push buffer 8, 2 -> 7 are missing now. note that the rtp time is the same
@ -1288,30 +1260,30 @@ GST_START_TEST (test_rtx_packet_delay)
* the estimate for 2 could be refined now to 20ms. also packet 2, 3 and 4 are * the estimate for 2 could be refined now to 20ms. also packet 2, 3 and 4 are
* exceeding the max allowed reorder distance and should request a * exceeding the max allowed reorder distance and should request a
* retransmission right away */ * retransmission right away */
in_buf = generate_test_buffer (20 * GST_MSECOND, TRUE, 8, 8 * 160); in_buf = generate_test_buffer_full (20 * GST_MSECOND, TRUE, 8, 8 * PCMU_RTP_TS_DURATION);
g_assert_cmpint (gst_pad_push (data.test_src_pad, in_buf), ==, GST_FLOW_OK); g_assert_cmpint (gst_pad_push (data.test_src_pad, in_buf), ==, GST_FLOW_OK);
/* we should now receive retransmission requests for 2 -> 5 */ /* we should now receive retransmission requests for 2 -> 5 */
out_event = g_async_queue_pop (data.src_event_queue); out_event = g_async_queue_pop (data.src_event_queue);
g_assert (out_event != NULL); g_assert (out_event != NULL);
verify_rtx_event (out_event, 2, 20 * GST_MSECOND, 30, 20 * GST_MSECOND); verify_rtx_event (out_event, 2, 20 * GST_MSECOND, 30, PCMU_BUF_DURATION);
for (i = 3; i < 5; i++) { for (i = 3; i < 5; i++) {
GST_DEBUG ("popping %d", i); GST_DEBUG ("popping %d", i);
out_event = g_async_queue_pop (data.src_event_queue); out_event = g_async_queue_pop (data.src_event_queue);
g_assert (out_event != NULL); g_assert (out_event != NULL);
verify_rtx_event (out_event, i, 20 * GST_MSECOND, 0, 20 * GST_MSECOND); verify_rtx_event (out_event, i, 20 * GST_MSECOND, 0, PCMU_BUF_DURATION);
} }
g_assert_cmpint (data.rtx_event_count, ==, 3); g_assert_cmpint (data.rtx_event_count, ==, 3);
/* push 9, this should immediately request retransmission of 5 */ /* push 9, this should immediately request retransmission of 5 */
in_buf = generate_test_buffer (20 * GST_MSECOND, TRUE, 9, 9 * 160); in_buf = generate_test_buffer_full (20 * GST_MSECOND, TRUE, 9, 9 * PCMU_RTP_TS_DURATION);
g_assert_cmpint (gst_pad_push (data.test_src_pad, in_buf), ==, GST_FLOW_OK); g_assert_cmpint (gst_pad_push (data.test_src_pad, in_buf), ==, GST_FLOW_OK);
/* we should now receive retransmission requests for 5 */ /* we should now receive retransmission requests for 5 */
out_event = g_async_queue_pop (data.src_event_queue); out_event = g_async_queue_pop (data.src_event_queue);
g_assert (out_event != NULL); g_assert (out_event != NULL);
verify_rtx_event (out_event, 5, 20 * GST_MSECOND, 0, 20 * GST_MSECOND); verify_rtx_event (out_event, 5, 20 * GST_MSECOND, 0, PCMU_BUF_DURATION);
/* wait for timeout for rtx 6 -> 7 */ /* wait for timeout for rtx 6 -> 7 */
gst_test_clock_wait_for_next_pending_id (GST_TEST_CLOCK (data.clock), &id); gst_test_clock_wait_for_next_pending_id (GST_TEST_CLOCK (data.clock), &id);
@ -1325,7 +1297,7 @@ GST_START_TEST (test_rtx_packet_delay)
GST_DEBUG ("popping %d", i); GST_DEBUG ("popping %d", i);
out_event = g_async_queue_pop (data.src_event_queue); out_event = g_async_queue_pop (data.src_event_queue);
g_assert (out_event != NULL); g_assert (out_event != NULL);
verify_rtx_event (out_event, i, 20 * GST_MSECOND, 0, 20 * GST_MSECOND); verify_rtx_event (out_event, i, 20 * GST_MSECOND, 0, PCMU_BUF_DURATION);
} }
/* churn through sync_times until the new buffer gets pushed out */ /* churn through sync_times until the new buffer gets pushed out */
@ -1378,7 +1350,7 @@ GST_START_TEST (test_rtx_packet_delay)
GST_DEBUG ("popping lost event %d", i); GST_DEBUG ("popping lost event %d", i);
out_event = g_async_queue_pop (data.sink_event_queue); out_event = g_async_queue_pop (data.sink_event_queue);
g_assert (out_event != NULL); g_assert (out_event != NULL);
verify_lost_event (out_event, i, 20 * GST_MSECOND, 0, FALSE); verify_lost_event (out_event, i, 20 * GST_MSECOND, 0);
} }
/* verify that buffer 8 made it through! */ /* verify that buffer 8 made it through! */
@ -1405,7 +1377,7 @@ GST_START_TEST (test_rtx_packet_delay)
GST_DEBUG ("popping lost event 10"); GST_DEBUG ("popping lost event 10");
out_event = g_async_queue_pop (data.sink_event_queue); out_event = g_async_queue_pop (data.sink_event_queue);
g_assert (out_event != NULL); g_assert (out_event != NULL);
verify_lost_event (out_event, 10, 40 * GST_MSECOND, 20 * GST_MSECOND, FALSE); verify_lost_event (out_event, 10, 40 * GST_MSECOND, PCMU_BUF_DURATION);
/* should have seen 6 packet lost events */ /* should have seen 6 packet lost events */
g_assert_cmpint (data.lost_event_count, ==, 7); g_assert_cmpint (data.lost_event_count, ==, 7);
@ -1437,16 +1409,16 @@ GST_START_TEST (test_gap_exceeds_latency)
g_object_set (data.jitter_buffer, "rtx-retry-period", 120, NULL); g_object_set (data.jitter_buffer, "rtx-retry-period", 120, NULL);
gst_test_clock_set_time (GST_TEST_CLOCK (data.clock), 0); gst_test_clock_set_time (GST_TEST_CLOCK (data.clock), 0);
in_buf = generate_test_buffer (timestamp_ms * GST_MSECOND, TRUE, 0, rtp_ts); in_buf = generate_test_buffer_full (timestamp_ms * GST_MSECOND, TRUE, 0, rtp_ts);
GST_BUFFER_FLAG_SET (in_buf, GST_BUFFER_FLAG_DISCONT); GST_BUFFER_FLAG_SET (in_buf, GST_BUFFER_FLAG_DISCONT);
g_assert_cmpint (gst_pad_push (data.test_src_pad, in_buf), ==, GST_FLOW_OK); g_assert_cmpint (gst_pad_push (data.test_src_pad, in_buf), ==, GST_FLOW_OK);
timestamp_ms += 20; timestamp_ms += 20;
rtp_ts += 160; rtp_ts += PCMU_RTP_TS_DURATION;
gst_test_clock_set_time (GST_TEST_CLOCK (data.clock), gst_test_clock_set_time (GST_TEST_CLOCK (data.clock),
timestamp_ms * GST_MSECOND); timestamp_ms * GST_MSECOND);
in_buf = generate_test_buffer (timestamp_ms * GST_MSECOND, TRUE, 1, rtp_ts); in_buf = generate_test_buffer_full (timestamp_ms * GST_MSECOND, TRUE, 1, rtp_ts);
g_assert_cmpint (gst_pad_push (data.test_src_pad, in_buf), ==, GST_FLOW_OK); g_assert_cmpint (gst_pad_push (data.test_src_pad, in_buf), ==, GST_FLOW_OK);
last_rtp = rtp_ts; last_rtp = rtp_ts;
last_ts = timestamp_ms; last_ts = timestamp_ms;
@ -1482,49 +1454,49 @@ GST_START_TEST (test_gap_exceeds_latency)
g_assert_cmpint (seqnum, ==, 2); g_assert_cmpint (seqnum, ==, 2);
gst_event_unref (out_event); gst_event_unref (out_event);
/* Now data comes in again, a "bulk" lost packet is created for 3 -> 6 */ /* Now data comes in again, a "bulk" lost packet is created for 3 -> 5 */
rtp_ts += (160 * 15); rtp_ts += (PCMU_RTP_TS_DURATION * 15);
in_buf = generate_test_buffer (timestamp_ms * GST_MSECOND, TRUE, 16, rtp_ts); in_buf = generate_test_buffer_full (timestamp_ms * GST_MSECOND, TRUE, 16, rtp_ts);
g_assert_cmpint (gst_pad_push (data.test_src_pad, in_buf), ==, GST_FLOW_OK); g_assert_cmpint (gst_pad_push (data.test_src_pad, in_buf), ==, GST_FLOW_OK);
last_ts += 60; last_ts += 60;
last_rtp += 480; last_rtp += 480;
in_buf = generate_test_buffer (last_ts * GST_MSECOND, TRUE, 8, last_rtp); in_buf = generate_test_buffer_full (last_ts * GST_MSECOND, TRUE, 8, last_rtp);
g_assert_cmpint (gst_pad_push (data.test_src_pad, in_buf), ==, GST_FLOW_OK); g_assert_cmpint (gst_pad_push (data.test_src_pad, in_buf), ==, GST_FLOW_OK);
last_ts += 20; last_ts += PCMU_BUF_MS;
last_rtp += 160; last_rtp += PCMU_RTP_TS_DURATION;
in_buf = generate_test_buffer (last_ts * GST_MSECOND, TRUE, 9, last_rtp); in_buf = generate_test_buffer_full (last_ts * GST_MSECOND, TRUE, 9, last_rtp);
g_assert_cmpint (gst_pad_push (data.test_src_pad, in_buf), ==, GST_FLOW_OK); g_assert_cmpint (gst_pad_push (data.test_src_pad, in_buf), ==, GST_FLOW_OK);
last_ts += 20; last_ts += PCMU_BUF_MS;
last_rtp += 160; last_rtp += PCMU_RTP_TS_DURATION;
in_buf = generate_test_buffer (last_ts * GST_MSECOND, TRUE, 10, last_rtp); in_buf = generate_test_buffer_full (last_ts * GST_MSECOND, TRUE, 10, last_rtp);
g_assert_cmpint (gst_pad_push (data.test_src_pad, in_buf), ==, GST_FLOW_OK); g_assert_cmpint (gst_pad_push (data.test_src_pad, in_buf), ==, GST_FLOW_OK);
last_ts += 20; last_ts += PCMU_BUF_MS;
last_rtp += 160; last_rtp += PCMU_RTP_TS_DURATION;
in_buf = generate_test_buffer (last_ts * GST_MSECOND, TRUE, 11, last_rtp); in_buf = generate_test_buffer_full (last_ts * GST_MSECOND, TRUE, 11, last_rtp);
g_assert_cmpint (gst_pad_push (data.test_src_pad, in_buf), ==, GST_FLOW_OK); g_assert_cmpint (gst_pad_push (data.test_src_pad, in_buf), ==, GST_FLOW_OK);
last_ts += 20; last_ts += PCMU_BUF_MS;
last_rtp += 160; last_rtp += PCMU_RTP_TS_DURATION;
in_buf = generate_test_buffer (last_ts * GST_MSECOND, TRUE, 12, last_rtp); in_buf = generate_test_buffer_full (last_ts * GST_MSECOND, TRUE, 12, last_rtp);
g_assert_cmpint (gst_pad_push (data.test_src_pad, in_buf), ==, GST_FLOW_OK); g_assert_cmpint (gst_pad_push (data.test_src_pad, in_buf), ==, GST_FLOW_OK);
last_ts += 20; last_ts += PCMU_BUF_MS;
last_rtp += 160; last_rtp += PCMU_RTP_TS_DURATION;
in_buf = generate_test_buffer (last_ts * GST_MSECOND, TRUE, 13, last_rtp); in_buf = generate_test_buffer_full (last_ts * GST_MSECOND, TRUE, 13, last_rtp);
g_assert_cmpint (gst_pad_push (data.test_src_pad, in_buf), ==, GST_FLOW_OK); g_assert_cmpint (gst_pad_push (data.test_src_pad, in_buf), ==, GST_FLOW_OK);
last_ts += 20; last_ts += PCMU_BUF_MS;
last_rtp += 160; last_rtp += PCMU_RTP_TS_DURATION;
in_buf = generate_test_buffer (last_ts * GST_MSECOND, TRUE, 14, last_rtp); in_buf = generate_test_buffer_full (last_ts * GST_MSECOND, TRUE, 14, last_rtp);
g_assert_cmpint (gst_pad_push (data.test_src_pad, in_buf), ==, GST_FLOW_OK); g_assert_cmpint (gst_pad_push (data.test_src_pad, in_buf), ==, GST_FLOW_OK);
last_ts += 20; last_ts += PCMU_BUF_MS;
last_rtp += 160; last_rtp += PCMU_RTP_TS_DURATION;
in_buf = generate_test_buffer (last_ts * GST_MSECOND, TRUE, 15, last_rtp); in_buf = generate_test_buffer_full (last_ts * GST_MSECOND, TRUE, 15, last_rtp);
g_assert_cmpint (gst_pad_push (data.test_src_pad, in_buf), ==, GST_FLOW_OK); g_assert_cmpint (gst_pad_push (data.test_src_pad, in_buf), ==, GST_FLOW_OK);
/* Wait for data to be pushed. */ /* Wait for data to be pushed. */
@ -1546,6 +1518,12 @@ GST_START_TEST (test_gap_exceeds_latency)
g_assert_cmpint (seqnum, ==, 3); g_assert_cmpint (seqnum, ==, 3);
gst_event_unref (out_event); gst_event_unref (out_event);
out_event = g_async_queue_pop (data.sink_event_queue);
s = gst_event_get_structure (out_event);
g_assert (gst_structure_get_uint (s, "seqnum", &seqnum));
g_assert_cmpint (seqnum, ==, 6);
gst_event_unref (out_event);
out_event = g_async_queue_pop (data.sink_event_queue); out_event = g_async_queue_pop (data.sink_event_queue);
s = gst_event_get_structure (out_event); s = gst_event_get_structure (out_event);
g_assert (gst_structure_get_uint (s, "seqnum", &seqnum)); g_assert (gst_structure_get_uint (s, "seqnum", &seqnum));
@ -1585,7 +1563,6 @@ GST_START_TEST (test_gap_exceeds_latency)
destroy_testharness (&data); destroy_testharness (&data);
} }
GST_END_TEST; GST_END_TEST;
GST_START_TEST (test_deadline_ts_offset) GST_START_TEST (test_deadline_ts_offset)
@ -1602,7 +1579,7 @@ GST_START_TEST (test_deadline_ts_offset)
gst_test_clock_set_time (GST_TEST_CLOCK (data.clock), 0); gst_test_clock_set_time (GST_TEST_CLOCK (data.clock), 0);
/* push the first buffer in */ /* push the first buffer in */
in_buf = generate_test_buffer (0 * GST_MSECOND, TRUE, 0, 0); in_buf = generate_test_buffer (0);
g_assert_cmpint (gst_pad_push (data.test_src_pad, in_buf), ==, GST_FLOW_OK); g_assert_cmpint (gst_pad_push (data.test_src_pad, in_buf), ==, GST_FLOW_OK);
/* wait_next_timeout() syncs on the deadline timer */ /* wait_next_timeout() syncs on the deadline timer */
@ -1638,6 +1615,48 @@ GST_START_TEST (test_deadline_ts_offset)
GST_END_TEST; GST_END_TEST;
GST_START_TEST (test_dts_gap_larger_than_latency)
{
GstHarness * h = gst_harness_new ("rtpjitterbuffer");
GstTestClock * testclock;
GstEvent *out_event;
gint jb_latency_ms = 100;
GstClockTime dts_after_gap = (jb_latency_ms + 1) * GST_MSECOND;
gst_harness_set_src_caps (h, generate_caps ());
gst_harness_use_testclock (h);
testclock = gst_harness_get_testclock (h);
g_object_set (h->element,
"do-lost", TRUE,
"latency", jb_latency_ms,
NULL);
/* push first buffer through */
gst_harness_push (h, generate_test_buffer (0));
gst_harness_crank_single_clock_wait (h);
gst_buffer_unref (gst_harness_pull (h));
/* Push packet with DTS larger than latency */
gst_harness_push (h, generate_test_buffer_full (dts_after_gap,
TRUE, 5, 5 * PCMU_RTP_TS_DURATION));
/* drop GstEventStreamStart & GstEventCaps & GstEventSegment */
for (int i = 0; i < 3; i++)
gst_event_unref (gst_harness_pull_event (h));
/* Time out and verify lost events */
for (gint i = 1; i < 5; i++) {
GstClockTime dur = dts_after_gap / 5;
gst_harness_crank_single_clock_wait (h);
out_event = gst_harness_pull_event (h);
fail_unless (out_event != NULL);
verify_lost_event (out_event, i, i * dur, dur);
}
gst_object_unref (testclock);
gst_harness_teardown (h);
}
GST_END_TEST;
static Suite * static Suite *
rtpjitterbuffer_suite (void) rtpjitterbuffer_suite (void)
@ -1660,6 +1679,7 @@ rtpjitterbuffer_suite (void)
tcase_add_test (tc_chain, test_rtx_packet_delay); tcase_add_test (tc_chain, test_rtx_packet_delay);
tcase_add_test (tc_chain, test_gap_exceeds_latency); tcase_add_test (tc_chain, test_gap_exceeds_latency);
tcase_add_test (tc_chain, test_deadline_ts_offset); tcase_add_test (tc_chain, test_deadline_ts_offset);
tcase_add_test (tc_chain, test_dts_gap_larger_than_latency);
return s; return s;
} }