New upstream version 1.9.90
diff --git a/tests/check/elements/rtpjitterbuffer.c b/tests/check/elements/rtpjitterbuffer.c
index 10810fa..1a9d0e6 100644
--- a/tests/check/elements/rtpjitterbuffer.c
+++ b/tests/check/elements/rtpjitterbuffer.c
@@ -5,7 +5,7 @@
* Copyright (C) 2012 Cisco Systems, Inc
* Authors: Kelley Rogers <kelro@cisco.com>
* Havard Graff <hgraff@cisco.com>
- * Copyright (C) 2013-2015 Pexip AS
+ * Copyright (C) 2013-2016 Pexip AS
* Stian Selnes <stian@pexip>
* Havard Graff <havard@pexip>
*
@@ -447,6 +447,15 @@
TRUE, seq_num, seq_num * PCMU_RTP_TS_DURATION);
}
+static GstBuffer *
+generate_test_buffer_rtx (GstClockTime dts, guint seq_num)
+{
+ GstBuffer *buffer = generate_test_buffer_full (dts, TRUE, seq_num,
+ seq_num * PCMU_RTP_TS_DURATION);
+ GST_BUFFER_FLAG_SET (buffer, GST_RTP_BUFFER_FLAG_RETRANSMISSION);
+ return buffer;
+}
+
static gint
get_rtp_seq_num (GstBuffer * buf)
{
@@ -520,6 +529,29 @@
gst_event_unref (event);
}
+static gboolean
+verify_jb_stats (GstElement * jb, GstStructure * expected)
+{
+ gboolean ret;
+ GstStructure *actual;
+ g_object_get (jb, "stats", &actual, NULL);
+
+ ret = gst_structure_is_subset (actual, expected);
+
+ if (!ret) {
+ gchar *e_str = gst_structure_to_string (expected);
+ gchar *a_str = gst_structure_to_string (actual);
+ fail_unless (ret, "%s is not a subset of %s", e_str, a_str);
+ g_free (e_str);
+ g_free (a_str);
+ }
+
+ gst_structure_free (expected);
+ gst_structure_free (actual);
+
+ return ret;
+}
+
GST_START_TEST (test_only_one_lost_event_on_large_gaps)
{
GstHarness *h = gst_harness_new ("rtpjitterbuffer");
@@ -604,6 +636,10 @@
fail_unless_equals_uint64 (10 * GST_SECOND, GST_BUFFER_PTS (out_buf));
gst_buffer_unref (out_buf);
+ fail_unless (verify_jb_stats (h->element,
+ gst_structure_new ("application/x-rtp-jitterbuffer-stats",
+ "num-lost", G_TYPE_UINT64, (guint64) 499, NULL)));
+
gst_object_unref (testclock);
gst_harness_teardown (h);
}
@@ -688,6 +724,11 @@
fail_unless_equals_int (5, get_rtp_seq_num (out_buf));
gst_buffer_unref (out_buf);
+ fail_unless (verify_jb_stats (h->element,
+ gst_structure_new ("application/x-rtp-jitterbuffer-stats",
+ "num-pushed", G_TYPE_UINT64, (guint64) 5,
+ "num-lost", G_TYPE_UINT64, (guint64) 1, NULL)));
+
gst_object_unref (testclock);
gst_harness_teardown (h);
}
@@ -749,12 +790,76 @@
fail_unless_equals_int (5, get_rtp_seq_num (out_buf));
gst_buffer_unref (out_buf);
+ fail_unless (verify_jb_stats (h->element,
+ gst_structure_new ("application/x-rtp-jitterbuffer-stats",
+ "num-pushed", G_TYPE_UINT64, (guint64) 4,
+ "num-lost", G_TYPE_UINT64, (guint64) 2, NULL)));
+
gst_object_unref (testclock);
gst_harness_teardown (h);
}
GST_END_TEST;
+
+GST_START_TEST (test_num_late_when_considered_lost_arrives)
+{
+ GstHarness *h = gst_harness_new ("rtpjitterbuffer");
+ gboolean do_lost = __i__ != 0;
+
+ gst_harness_set_src_caps (h, generate_caps ());
+ g_object_set (h->element, "do-lost", do_lost, "latency", 100, NULL);
+
+ /* push the first buffer through */
+ fail_unless_equals_int (GST_FLOW_OK,
+ gst_harness_push (h, generate_test_buffer (0)));
+ /* sync on the first packet */
+ gst_harness_crank_single_clock_wait (h);
+
+ /* gap of 1 */
+ fail_unless_equals_int (GST_FLOW_OK,
+ gst_harness_push (h, generate_test_buffer (2)));
+
+ /* crank to output lost-event */
+ gst_harness_crank_single_clock_wait (h);
+
+ if (do_lost) {
+ /* drop GstEventStreamStart & GstEventCaps & GstEventSegment */
+ for (gint i = 0; i < 3; i++)
+ gst_event_unref (gst_harness_pull_event (h));
+
+ /* we should now receive packet-lost-events for buffer 1 */
+ verify_lost_event (gst_harness_pull_event (h),
+ 1, 1 * PCMU_BUF_DURATION, PCMU_BUF_DURATION);
+ }
+
+ /* pull out buffers to ensure determinism */
+ gst_buffer_unref (gst_harness_pull (h));
+ gst_buffer_unref (gst_harness_pull (h));
+
+ /* we have one lost packet in the stats */
+ fail_unless (verify_jb_stats (h->element,
+ gst_structure_new ("application/x-rtp-jitterbuffer-stats",
+ "num-pushed", G_TYPE_UINT64, (guint64) 2,
+ "num-lost", G_TYPE_UINT64, (guint64) 1,
+ "num-late", G_TYPE_UINT64, (guint64) 0, NULL)));
+
+ /* buffer 1 now arrives (too late) */
+ fail_unless_equals_int (GST_FLOW_OK,
+ gst_harness_push (h, generate_test_buffer (1)));
+
+ /* and this increments num-late */
+ fail_unless (verify_jb_stats (h->element,
+ gst_structure_new ("application/x-rtp-jitterbuffer-stats",
+ "num-pushed", G_TYPE_UINT64, (guint64) 2,
+ "num-lost", G_TYPE_UINT64, (guint64) 1,
+ "num-late", G_TYPE_UINT64, (guint64) 1, NULL)));
+
+ gst_harness_teardown (h);
+}
+
+GST_END_TEST;
+
GST_START_TEST (test_all_packets_are_timestamped_zero)
{
GstHarness *h = gst_harness_new ("rtpjitterbuffer");
@@ -809,12 +914,138 @@
fail_unless_equals_int (5, get_rtp_seq_num (out_buf));
gst_buffer_unref (out_buf);
+ fail_unless (verify_jb_stats (h->element,
+ gst_structure_new ("application/x-rtp-jitterbuffer-stats",
+ "num-pushed", G_TYPE_UINT64, (guint64) 4,
+ "num-lost", G_TYPE_UINT64, (guint64) 2, NULL)));
+
gst_object_unref (testclock);
gst_harness_teardown (h);
}
GST_END_TEST;
+GST_START_TEST (test_reorder_of_non_equidistant_packets)
+{
+ GstHarness *h = gst_harness_new ("rtpjitterbuffer");
+ GstTestClock *testclock;
+ gint latency_ms = 5;
+ GstClockID pending_id;
+ GstClockTime time;
+ gint seq, frame;
+ gint num_init_frames = 1;
+ const GstClockTime frame_dur = PCMU_BUF_DURATION;
+ const guint32 frame_rtp_ts_dur = PCMU_RTP_TS_DURATION;
+
+ gst_harness_set_src_caps (h, generate_caps ());
+ testclock = gst_harness_get_testclock (h);
+ g_object_set (h->element, "do-lost", TRUE, "latency", latency_ms, NULL);
+
+ for (frame = 0, seq = 0; frame < num_init_frames; frame++, seq += 2) {
+ /* Push a couple of packets with identical timestamp, typical for a video
+ * stream where one frame generates multiple packets. */
+ gst_harness_set_time (h, frame * frame_dur);
+ gst_harness_push (h, generate_test_buffer_full (frame * frame_dur, FALSE,
+ seq, frame * frame_rtp_ts_dur));
+ gst_harness_push (h, generate_test_buffer_full (frame * frame_dur, TRUE,
+ seq + 1, frame * frame_rtp_ts_dur));
+
+ if (frame == 0)
+ /* deadline for buffer 0 expires */
+ gst_harness_crank_single_clock_wait (h);
+
+ gst_buffer_unref (gst_harness_pull (h));
+ gst_buffer_unref (gst_harness_pull (h));
+ }
+
+ /* Finally push the last frame reordered */
+ gst_harness_set_time (h, frame * frame_dur);
+ gst_harness_push (h, generate_test_buffer_full (frame * frame_dur, TRUE,
+ seq + 1, frame * frame_rtp_ts_dur));
+
+ /* Check the scheduled lost timer. The expected arrival of this packet
+ * should be assumed to be the same as the last packet received since we
+ * don't know wether the missing packet belonged to this or previous
+ * frame. */
+ gst_test_clock_wait_for_next_pending_id (testclock, &pending_id);
+ time = gst_clock_id_get_time (pending_id);
+ fail_unless_equals_int64 (time, frame * frame_dur + latency_ms * GST_MSECOND);
+ gst_clock_id_unref (pending_id);
+
+ /* And then missing packet arrives just in time */
+ gst_harness_set_time (h, time - 1);
+ gst_harness_push (h, generate_test_buffer_full (time - 1, FALSE, seq,
+ frame * frame_rtp_ts_dur));
+
+ gst_buffer_unref (gst_harness_pull (h));
+ gst_buffer_unref (gst_harness_pull (h));
+
+ gst_object_unref (testclock);
+ gst_harness_teardown (h);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_loss_equidistant_spacing_with_parameter_packets)
+{
+ GstHarness *h = gst_harness_new ("rtpjitterbuffer");
+ GstTestClock *testclock;
+ GstEvent *event;
+ gint latency_ms = 5;
+ gint seq, frame;
+ gint num_init_frames = 10;
+
+ gst_harness_set_src_caps (h, generate_caps ());
+ testclock = gst_harness_get_testclock (h);
+ g_object_set (h->element, "do-lost", TRUE, "latency", latency_ms, NULL);
+
+ /* drop stream-start, caps, segment */
+ for (int i = 0; i < 3; i++)
+ gst_event_unref (gst_harness_pull_event (h));
+
+ for (frame = 0, seq = 0; frame < num_init_frames; frame++, seq++) {
+ gst_harness_set_time (h, frame * PCMU_BUF_DURATION);
+ gst_harness_push (h, generate_test_buffer_full (frame * PCMU_BUF_DURATION,
+ TRUE, seq, frame * PCMU_RTP_TS_DURATION));
+
+ if (frame == 0)
+ /* deadline for buffer 0 expires */
+ gst_harness_crank_single_clock_wait (h);
+
+ gst_buffer_unref (gst_harness_pull (h));
+ }
+
+ /* Push three packets with same rtptime, simulating parameter packets +
+ * frame. This should not disable equidistant mode as it is common for
+ * certain audio codecs. */
+ for (gint i = 0; i < 3; i++) {
+ gst_harness_set_time (h, frame * PCMU_BUF_DURATION);
+ gst_harness_push (h, generate_test_buffer_full (frame * PCMU_BUF_DURATION,
+ i == 2, seq++, frame * PCMU_RTP_TS_DURATION));
+ gst_buffer_unref (gst_harness_pull (h));
+ }
+ frame++;
+
+ /* Finally push the last packet introducing a gap */
+ gst_harness_set_time (h, frame * PCMU_BUF_DURATION);
+ gst_harness_push (h, generate_test_buffer_full (frame * PCMU_BUF_DURATION,
+ TRUE, seq + 1, frame * PCMU_RTP_TS_DURATION));
+
+ /* Check that the lost event has been generated assuming equidistant
+ * spacing. */
+ event = gst_harness_pull_event (h);
+ verify_lost_event (event, seq,
+ frame * PCMU_BUF_DURATION - PCMU_BUF_DURATION / 2, PCMU_BUF_DURATION / 2);
+
+ gst_buffer_unref (gst_harness_pull (h));
+
+ gst_object_unref (testclock);
+ gst_harness_teardown (h);
+}
+
+GST_END_TEST;
+
+
static void
gst_test_clock_set_time_and_process (GstTestClock * testclock,
GstClockTime time)
@@ -899,128 +1130,201 @@
{
GstHarness *h = gst_harness_new ("rtpjitterbuffer");
GstTestClock *testclock;
- GstBuffer *out_buf;
- GstEvent *out_event;
- gint jb_latency_ms = 200;
- const GstClockTime rtx_retry_timeout = 40 * GST_MSECOND;
- GstStructure *rtx_stats;
- const GValue *rtx_stat;
- gint i;
+ gint latency_ms = 200;
+ gint rtx_delay_ms;
+ GstClockTime last_rtx_request, now;
- gst_harness_set_src_caps (h, generate_caps ());
testclock = gst_harness_get_testclock (h);
+ gst_harness_set_src_caps (h, generate_caps ());
+ g_object_set (h->element, "do-retransmission", TRUE, "latency", latency_ms,
+ NULL);
- g_object_set (h->element, "do-lost", TRUE, NULL);
- g_object_set (h->element, "do-retransmission", TRUE, NULL);
- g_object_set (h->element, "latency", jb_latency_ms, NULL);
- g_object_set (h->element, "rtx-retry-period", 120, NULL);
+ for (gint i = 0; i <= latency_ms / PCMU_BUF_MS; i++) {
+ gst_test_clock_set_time (testclock, i * PCMU_BUF_DURATION);
+ fail_unless_equals_int (GST_FLOW_OK,
+ gst_harness_push (h, generate_test_buffer (i)));
+ gst_harness_wait_for_clock_id_waits (h, 1, 60);
+ }
- /* push the first buffer in */
- fail_unless_equals_int (GST_FLOW_OK,
- gst_harness_push (h, generate_test_buffer (0)));
+ gst_harness_crank_single_clock_wait (h);
+ fail_unless_equals_int64 (latency_ms * GST_MSECOND,
+ gst_clock_get_time (GST_CLOCK (testclock)));
- gst_harness_set_time (h, 20 * GST_MSECOND);
-
- /* 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 2*jitter==0 and 0.5*packet_spacing==10ms */
- fail_unless_equals_int (GST_FLOW_OK,
- gst_harness_push (h, generate_test_buffer (1)));
-
- /* push buffer 4, 2 and 3 are missing now, we should get
- * retransmission events for 3 at 100ms*/
- fail_unless_equals_int (GST_FLOW_OK,
- gst_harness_push (h, generate_test_buffer (4)));
-
- /* wait for first retransmission request */
- gst_test_clock_set_time_and_process (testclock, 50 * GST_MSECOND);
+ for (gint i = 0; i <= latency_ms / PCMU_BUF_MS; i++)
+ gst_buffer_unref (gst_harness_pull (h));
/* drop reconfigure event */
gst_event_unref (gst_harness_pull_upstream_event (h));
- /* drop GstEventStreamStart & GstEventCaps & GstEventSegment */
- for (int i = 0; i < 3; i++)
- gst_event_unref (gst_harness_pull_event (h));
- /* First event for 2 */
- out_event = gst_harness_pull_upstream_event (h);
- verify_rtx_event (out_event, 2, rtx_retry_timeout, 10, PCMU_BUF_DURATION);
+ /*
+ The expected sequence of buffers is this:
+ ____ ____ ____ ____
+ ... | 10 | | 11 | | 12 | | 13 |
+ –––– –––– –––– ––––
+ 200ms 220ms 240ms 260ms
- /* wait for second retransmission request */
- gst_test_clock_set_time_and_process (testclock, 60 * GST_MSECOND);
+ But instead we get this:
+ ____ _ _ _ _ ____
+ ... | 10 | | | | | | 13 |
+ –––– - - - - ––––
+ 200ms 260ms
- /* Second event for 3 */
- out_event = gst_harness_pull_upstream_event (h);
- verify_rtx_event (out_event, 3, 60 * GST_MSECOND, 0, PCMU_BUF_DURATION);
+ Now it is important to note that the next thing that happens is that
+ the RTX timeout for packet 11 will happen at time 230ms, so we crank
+ the timer thread to advance the time to this: */
+ gst_harness_crank_single_clock_wait (h);
+ rtx_delay_ms = PCMU_BUF_MS / 2;
+ verify_rtx_event (gst_harness_pull_upstream_event (h),
+ 11, 11 * PCMU_BUF_DURATION, rtx_delay_ms, PCMU_BUF_DURATION);
+ last_rtx_request = gst_clock_get_time (GST_CLOCK (testclock));
+ fail_unless_equals_int64 (last_rtx_request,
+ 11 * PCMU_BUF_DURATION + rtx_delay_ms * GST_MSECOND);
+ gst_harness_wait_for_clock_id_waits (h, 1, 60);
- /* now we wait for the next timeout for 2 */
- gst_test_clock_set_time_and_process (testclock, 90 * GST_MSECOND);
-
- /* First event for 2 */
- out_event = gst_harness_pull_upstream_event (h);
- verify_rtx_event (out_event, 2, rtx_retry_timeout, 50, PCMU_BUF_DURATION);
-
- /* now we wait for the next timeout for 3 */
- gst_test_clock_set_time_and_process (testclock, 100 * GST_MSECOND);
-
- /* Second event for 3 */
- out_event = gst_harness_pull_upstream_event (h);
- verify_rtx_event (out_event, 3, 60 * GST_MSECOND, 40, PCMU_BUF_DURATION);
-
- /* make buffer 3 */
+ /* The next scheduled RTX for packet 11 is now at 230 + 40 = 270ms,
+ so the next thing that happens is that buffer 13 arrives in perfect time: */
+ now = 13 * PCMU_BUF_DURATION;
+ gst_harness_set_time (h, now);
fail_unless_equals_int (GST_FLOW_OK,
- gst_harness_push (h, generate_test_buffer (3)));
+ gst_harness_push (h,
+ generate_test_buffer_full (now, TRUE, 13,
+ 13 * PCMU_RTP_TS_DURATION)));
- /* make more buffers */
- for (i = 5; i < 15; i++) {
+ /*
+
+ This will estimate the dts on the two missing packets to:
+ ____ ____
+ ... | 11 | | 12 | ...
+ –––– ––––
+ 220ms 240ms
+
+ And given their regular interspacing of 20ms, it will schedule two RTX
+ timers for them like so:
+
+ ____ ____
+ ... | 11 | | 12 | ...
+ –––– ––––
+ 230ms 250ms
+
+ There are however two problems, packet 11 we have already sent one RTX for
+ and its timeout is currently at 270ms, so we should not tamper with that,
+ and as for packet 12, 250ms has already expired, so we now expect to see
+ an rtx-event being sent for packet 12 immediately: */
+ verify_rtx_event (gst_harness_pull_upstream_event (h),
+ 12, 12 * PCMU_BUF_DURATION, rtx_delay_ms, PCMU_BUF_DURATION);
+
+ /* and another crank will see the second RTX event being sent for packet 11 */
+ gst_harness_crank_single_clock_wait (h);
+ rtx_delay_ms += 40;
+ verify_rtx_event (gst_harness_pull_upstream_event (h),
+ 11, 11 * PCMU_BUF_DURATION, rtx_delay_ms, PCMU_BUF_DURATION);
+ last_rtx_request = gst_clock_get_time (GST_CLOCK (testclock));
+ fail_unless_equals_int64 (last_rtx_request,
+ 11 * PCMU_BUF_DURATION + rtx_delay_ms * GST_MSECOND);
+
+ gst_object_unref (testclock);
+ gst_harness_teardown (h);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (text_rtx_two_missing_early)
+{
+ GstHarness *h = gst_harness_new ("rtpjitterbuffer");
+ GstTestClock *testclock;
+ gint latency_ms = 30;
+ GstClockTime last_rtx_request, now;
+
+ testclock = gst_harness_get_testclock (h);
+ gst_harness_set_src_caps (h, generate_caps ());
+ g_object_set (h->element, "do-retransmission", TRUE, "latency", latency_ms,
+ NULL);
+
+ for (gint i = 0; i <= latency_ms / PCMU_BUF_MS; i++) {
+ gst_test_clock_set_time (testclock, i * PCMU_BUF_DURATION);
fail_unless_equals_int (GST_FLOW_OK,
gst_harness_push (h, generate_test_buffer (i)));
+ gst_harness_wait_for_clock_id_waits (h, 1, 60);
}
- gst_test_clock_set_time_and_process (testclock, 130 * GST_MSECOND);
+ gst_harness_crank_single_clock_wait (h);
+ fail_unless_equals_int64 (latency_ms * GST_MSECOND,
+ gst_clock_get_time (GST_CLOCK (testclock)));
- /* now we only get requests for 2 */
- out_event = gst_harness_pull_upstream_event (h);
- verify_rtx_event (out_event, 2, rtx_retry_timeout, 90, PCMU_BUF_DURATION);
+ for (gint i = 0; i <= latency_ms / PCMU_BUF_MS; i++)
+ gst_buffer_unref (gst_harness_pull (h));
- /* this is when buffer 0 deadline expires */
- gst_test_clock_set_time_and_process (testclock, 200 * GST_MSECOND);
+ /* drop reconfigure event */
+ gst_event_unref (gst_harness_pull_upstream_event (h));
- for (i = 0; i < 2; i++) {
- GST_DEBUG ("popping %d", i);
- out_buf = gst_harness_pull (h);
- fail_unless_equals_int (i, get_rtp_seq_num (out_buf));
- gst_buffer_unref (out_buf);
- }
+ /*
+ The expected sequence of buffers is this:
+ ___ ___ ___ ___ ___
+ | 0 | | 1 | | 2 | | 3 | | 4 |
+ ––– ––– ––– ––– –––
+ 0ms 20ms 40ms 60ms 80ms
- /* this is when 2 is lost */
- gst_test_clock_set_time_and_process (testclock, 240 * GST_MSECOND);
+ But instead we get this:
+ ___ ___ _ _ _ _ ___
+ | 0 | | 1 | | | | | | 4 |
+ ––– ––– – – – – –––
+ 0ms 20ms 41ms
- /* we should now receive a packet-lost-event for buffer 2 */
- out_event = gst_harness_pull_event (h);
- verify_lost_event (out_event, 2, 40 * GST_MSECOND, PCMU_BUF_DURATION);
+ */
- /* verify that buffers made it through! */
- for (i = 3; i < 15; i++) {
- GST_DEBUG ("popping %d", i);
- out_buf = gst_harness_pull (h);
- fail_unless_equals_int (i, get_rtp_seq_num (out_buf));
- gst_buffer_unref (out_buf);
- }
- /* should still have only seen 1 packet lost events,
- so no events in the queue */
- fail_unless_equals_int (0, gst_harness_events_in_queue (h));
+ now = 41 * GST_MSECOND;
+ gst_harness_set_time (h, now);
+ fail_unless_equals_int (GST_FLOW_OK,
+ gst_harness_push (h,
+ generate_test_buffer_full (now, TRUE, 4, 4 * PCMU_RTP_TS_DURATION)));
- g_object_get (h->element, "stats", &rtx_stats, NULL);
+ /*
- rtx_stat = gst_structure_get_value (rtx_stats, "rtx-count");
- fail_unless_equals_uint64 (5, g_value_get_uint64 (rtx_stat));
+ With the now calculated packet-spacing of (41-20) / 3 = 7,
+ giving us these expected times:
+ ___ ___ ___ ___ ___
+ | 0 | | 1 | | 2 | | 3 | | 4 |
+ ––– ––– ––– ––– –––
+ 0ms 20ms 27ms 34ms 41ms
- rtx_stat = gst_structure_get_value (rtx_stats, "rtx-success-count");
- fail_unless_equals_uint64 (1, g_value_get_uint64 (rtx_stat));
+ For RTX, the inital RTX-timeouts for the missing buffers are
+ the expected arrival time + half the packet-spacing time, like this:
+ ___ ___
+ | 2 | | 3 |
+ ––– –––
+ 50ms 70ms
- rtx_stat = gst_structure_get_value (rtx_stats, "rtx-rtt");
- fail_unless_equals_uint64 (0, g_value_get_uint64 (rtx_stat));
- gst_structure_free (rtx_stats);
+ But since we have re-calculated the estimated arrival-time
+ of these buffers, we have to adjust the RTX timeout as well,
+ and we use the original delay (packet-spacing / 2) = 10ms,
+ and add it on:
+ ___ ___
+ | 2 | | 3 |
+ ––– –––
+ 37ms 44ms
+
+ Also note that the first RTX request is now scheduled for a
+ time that is prior to NOW (37ms < 41ms), so it will be sent straight
+ away without us needing to "crank" the timer-thread
+
+ */
+
+ /* The RTX request for packet 2 has timestamp 27ms and delay 10ms */
+ verify_rtx_event (gst_harness_pull_upstream_event (h),
+ 2, 27 * GST_MSECOND, 10, PCMU_BUF_DURATION);
+ /* and is sent immediately after packet 4 arrives (41ms) */
+ last_rtx_request = gst_clock_get_time (GST_CLOCK (testclock));
+ fail_unless_equals_int64 (last_rtx_request, now);
+
+ /* crank the timer thread */
+ gst_harness_crank_single_clock_wait (h);
+
+ /* The RTX request for packet 3 has timestamp 34ms and delay 10ms */
+ verify_rtx_event (gst_harness_pull_upstream_event (h),
+ 3, 34 * GST_MSECOND, 10, PCMU_BUF_DURATION);
+ /* and is sent at 44ms */
+ last_rtx_request = gst_clock_get_time (GST_CLOCK (testclock));
+ fail_unless_equals_int64 (last_rtx_request, 44 * GST_MSECOND);
gst_object_unref (testclock);
gst_harness_teardown (h);
@@ -1073,12 +1377,12 @@
/* we should now receive retransmission requests for 2 -> 5 */
out_event = gst_harness_pull_upstream_event (h);
- verify_rtx_event (out_event, 2, 20 * GST_MSECOND, 30, PCMU_BUF_DURATION);
+ verify_rtx_event (out_event, 2, 20 * GST_MSECOND, 17, PCMU_BUF_DURATION);
for (i = 3; i < 5; i++) {
GST_DEBUG ("popping %d", i);
out_event = gst_harness_pull_upstream_event (h);
- verify_rtx_event (out_event, i, 20 * GST_MSECOND, 0, PCMU_BUF_DURATION);
+ verify_rtx_event (out_event, i, 20 * GST_MSECOND, 17, PCMU_BUF_DURATION);
}
fail_unless_equals_int (0, gst_harness_upstream_events_in_queue (h));
@@ -1089,7 +1393,7 @@
/* we should now receive retransmission requests for 5 */
out_event = gst_harness_pull_upstream_event (h);
- verify_rtx_event (out_event, 5, 20 * GST_MSECOND, 0, PCMU_BUF_DURATION);
+ verify_rtx_event (out_event, 5, 20 * GST_MSECOND, 17, PCMU_BUF_DURATION);
/* wait for timeout for rtx 6 -> 7 */
gst_test_clock_set_time_and_process (testclock, 60 * GST_MSECOND);
@@ -1097,7 +1401,7 @@
for (i = 6; i < 8; i++) {
GST_DEBUG ("popping %d", i);
out_event = gst_harness_pull_upstream_event (h);
- verify_rtx_event (out_event, i, 20 * GST_MSECOND, 0, PCMU_BUF_DURATION);
+ verify_rtx_event (out_event, i, 20 * GST_MSECOND, 17, PCMU_BUF_DURATION);
}
/* churn through 7 sync_times until the new buffer gets pushed out */
@@ -1113,9 +1417,6 @@
gst_buffer_unref (out_buf);
}
- /* churn through 1 sync_time until the next buffer gets pushed out */
- gst_harness_crank_single_clock_wait (h);
-
for (i = 2; i < 8; i++) {
GST_DEBUG ("popping lost event %d", i);
out_event = gst_harness_pull_event (h);
@@ -1132,15 +1433,635 @@
gst_buffer_unref (out_buf);
}
- GST_DEBUG ("waiting for 240ms");
- gst_test_clock_set_time_and_process (testclock, 240 * GST_MSECOND);
-
GST_DEBUG ("popping lost event 10");
out_event = gst_harness_pull_event (h);
verify_lost_event (out_event, 10, 40 * GST_MSECOND, PCMU_BUF_DURATION);
fail_unless_equals_int (0, gst_harness_events_in_queue (h));
- fail_unless_equals_int (20, gst_harness_upstream_events_in_queue (h));
+ fail_unless_equals_int (15, gst_harness_upstream_events_in_queue (h));
+
+ fail_unless (verify_jb_stats (h->element,
+ gst_structure_new ("application/x-rtp-jitterbuffer-stats",
+ "num-lost", G_TYPE_UINT64, (guint64) 7,
+ "rtx-count", G_TYPE_UINT64, (guint64) 21,
+ "rtx-success-count", G_TYPE_UINT64, (guint64) 0,
+ "rtx-rtt", G_TYPE_UINT64, (guint64) 0, NULL)));
+
+ gst_object_unref (testclock);
+ gst_harness_teardown (h);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_rtx_buffer_arrives_just_in_time)
+{
+ GstHarness *h = gst_harness_new ("rtpjitterbuffer");
+ GstTestClock *testclock;
+ gint latency_ms = 5 * PCMU_BUF_MS;
+ gint num_init_buffers = latency_ms / PCMU_BUF_MS + 1;
+ GstBuffer *buffer;
+ GstClockTime now, last_rtx_request;
+
+ testclock = gst_harness_get_testclock (h);
+ gst_harness_set_src_caps (h, generate_caps ());
+ g_object_set (h->element, "do-retransmission", TRUE, "latency", latency_ms,
+ "rtx-max-retries", 1, NULL);
+
+ /* Push/pull buffers and advance time past buffer 0's timeout (in order to
+ * simplify the test) */
+ for (gint i = 0; i < num_init_buffers; i++) {
+ gst_test_clock_set_time (testclock, i * PCMU_BUF_DURATION);
+ fail_unless_equals_int (GST_FLOW_OK,
+ gst_harness_push (h, generate_test_buffer (i)));
+ gst_harness_wait_for_clock_id_waits (h, 1, 60);
+ }
+
+ gst_harness_crank_single_clock_wait (h);
+ fail_unless_equals_int64 (latency_ms * GST_MSECOND,
+ gst_clock_get_time (GST_CLOCK (testclock)));
+
+ for (gint i = 0; i < num_init_buffers; i++)
+ gst_buffer_unref (gst_harness_pull (h));
+
+ /* drop reconfigure event */
+ gst_event_unref (gst_harness_pull_upstream_event (h));
+
+ /* Crank clock to send retransmission events requesting seqnum 6 which has
+ * not arrived yet. */
+ gst_harness_crank_single_clock_wait (h);
+ verify_rtx_event (gst_harness_pull_upstream_event (h),
+ 6, 6 * PCMU_BUF_DURATION, 10, PCMU_BUF_DURATION);
+
+ last_rtx_request = gst_clock_get_time (GST_CLOCK (testclock));
+ fail_unless_equals_int64 (last_rtx_request, 130 * GST_MSECOND);
+
+ /* seqnum 6 arrives just before it times out and is considered lost */
+ now = 200 * GST_MSECOND;
+ gst_test_clock_set_time (testclock, now);
+ fail_unless_equals_int (GST_FLOW_OK, gst_harness_push (h,
+ generate_test_buffer_rtx (now, 6)));
+ buffer = gst_harness_pull (h);
+ fail_unless_equals_int (6, get_rtp_seq_num (buffer));
+ gst_buffer_unref (buffer);
+
+ fail_unless (verify_jb_stats (h->element,
+ gst_structure_new ("application/x-rtp-jitterbuffer-stats",
+ "num-pushed", G_TYPE_UINT64, (guint64) num_init_buffers + 1,
+ "num-lost", G_TYPE_UINT64, (guint64) 0,
+ "rtx-count", G_TYPE_UINT64, (guint64) 1,
+ "rtx-success-count", G_TYPE_UINT64, (guint64) 1,
+ "rtx-per-packet", G_TYPE_DOUBLE, 1.0,
+ "rtx-rtt", G_TYPE_UINT64, (guint64) (now - last_rtx_request),
+ NULL)));
+
+ gst_object_unref (testclock);
+ gst_harness_teardown (h);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_rtx_buffer_arrives_too_late)
+{
+ GstHarness *h = gst_harness_new ("rtpjitterbuffer");
+ GstTestClock *testclock;
+ gint latency_ms = 5 * PCMU_BUF_MS;
+ gint num_init_buffers = latency_ms / PCMU_BUF_MS + 1;
+ GstClockTime now, last_rtx_request;
+
+ testclock = gst_harness_get_testclock (h);
+ gst_harness_set_src_caps (h, generate_caps ());
+ g_object_set (h->element, "do-retransmission", TRUE, "latency", latency_ms,
+ "do-lost", TRUE, "rtx-max-retries", 1, NULL);
+
+ /* Push/pull buffers and advance time past buffer 0's timeout (in order to
+ * simplify the test) */
+ for (gint i = 0; i < num_init_buffers; i++) {
+ gst_test_clock_set_time (testclock, i * PCMU_BUF_DURATION);
+ fail_unless_equals_int (GST_FLOW_OK,
+ gst_harness_push (h, generate_test_buffer (i)));
+ gst_harness_wait_for_clock_id_waits (h, 1, 60);
+ }
+
+ gst_harness_crank_single_clock_wait (h);
+ fail_unless_equals_int64 (latency_ms * GST_MSECOND,
+ gst_clock_get_time (GST_CLOCK (testclock)));
+
+ for (gint i = 0; i < num_init_buffers; i++)
+ gst_buffer_unref (gst_harness_pull (h));
+
+ /* drop reconfigure event */
+ gst_event_unref (gst_harness_pull_upstream_event (h));
+ /* drop GstEventStreamStart & GstEventCaps & GstEventSegment */
+ for (gint i = 0; i < 3; i++)
+ gst_event_unref (gst_harness_pull_event (h));
+
+ /* Crank clock to send retransmission events requesting seqnum 6 which has
+ * not arrived yet. */
+ gst_harness_crank_single_clock_wait (h);
+ verify_rtx_event (gst_harness_pull_upstream_event (h),
+ 6, 6 * PCMU_BUF_DURATION, 10, PCMU_BUF_DURATION);
+
+ last_rtx_request = gst_clock_get_time (GST_CLOCK (testclock));
+ fail_unless_equals_int64 (last_rtx_request, 130 * GST_MSECOND);
+
+ /* seqnum 6 is considered lost */
+ gst_harness_crank_single_clock_wait (h);
+ verify_lost_event (gst_harness_pull_event (h), 6,
+ 6 * PCMU_BUF_DURATION, PCMU_BUF_DURATION);
+
+ /* seqnum 6 arrives too late */
+ now = gst_clock_get_time (GST_CLOCK (testclock));
+ fail_unless_equals_int (GST_FLOW_OK, gst_harness_push (h,
+ generate_test_buffer_rtx (now, 6)));
+
+ fail_unless (verify_jb_stats (h->element,
+ gst_structure_new ("application/x-rtp-jitterbuffer-stats",
+ "num-pushed", G_TYPE_UINT64, (guint64) num_init_buffers,
+ "num-lost", G_TYPE_UINT64, (guint64) 1,
+ "num-late", G_TYPE_UINT64, (guint64) 1,
+ "num-duplicates", G_TYPE_UINT64, (guint64) 0,
+ "rtx-count", G_TYPE_UINT64, (guint64) 1,
+ "rtx-success-count", G_TYPE_UINT64, (guint64) 0,
+ "rtx-per-packet", G_TYPE_DOUBLE, 1.0,
+ "rtx-rtt", G_TYPE_UINT64, (guint64) (now - last_rtx_request),
+ NULL)));
+
+ gst_object_unref (testclock);
+ gst_harness_teardown (h);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_rtx_original_buffer_does_not_update_rtx_stats)
+{
+ GstHarness *h = gst_harness_new ("rtpjitterbuffer");
+ GstTestClock *testclock;
+ gint latency_ms = 5 * PCMU_BUF_MS;
+ gint num_init_buffers = latency_ms / PCMU_BUF_MS + 1;
+ GstBuffer *buffer;
+ GstClockTime now, last_rtx_request;
+
+ testclock = gst_harness_get_testclock (h);
+ gst_harness_set_src_caps (h, generate_caps ());
+ g_object_set (h->element, "do-retransmission", TRUE, "latency", latency_ms,
+ "rtx-max-retries", 1, NULL);
+
+ /* Push/pull buffers and advance time past buffer 0's timeout (in order to
+ * simplify the test) */
+ for (gint i = 0; i < num_init_buffers; i++) {
+ gst_test_clock_set_time (testclock, i * PCMU_BUF_DURATION);
+ fail_unless_equals_int (GST_FLOW_OK,
+ gst_harness_push (h, generate_test_buffer (i)));
+ gst_harness_wait_for_clock_id_waits (h, 1, 60);
+ }
+
+ gst_harness_crank_single_clock_wait (h);
+ fail_unless_equals_int64 (latency_ms * GST_MSECOND,
+ gst_clock_get_time (GST_CLOCK (testclock)));
+
+ for (gint i = 0; i < num_init_buffers; i++)
+ gst_buffer_unref (gst_harness_pull (h));
+
+ /* drop reconfigure event */
+ gst_event_unref (gst_harness_pull_upstream_event (h));
+
+ /* Crank clock to send retransmission events requesting seqnum 6 which has
+ * not arrived yet. */
+ gst_harness_crank_single_clock_wait (h);
+ verify_rtx_event (gst_harness_pull_upstream_event (h),
+ 6, 6 * PCMU_BUF_DURATION, 10, PCMU_BUF_DURATION);
+
+ last_rtx_request = gst_clock_get_time (GST_CLOCK (testclock));
+ fail_unless_equals_int64 (last_rtx_request, 130 * GST_MSECOND);
+
+ /* ORIGINAL seqnum 6 arrives just before it times out and is considered
+ * lost. */
+ now = 200 * GST_MSECOND;
+ gst_test_clock_set_time (testclock, now);
+ fail_unless_equals_int (GST_FLOW_OK, gst_harness_push (h,
+ generate_test_buffer_full (now, TRUE, 6, 6 * PCMU_RTP_TS_DURATION)));
+ buffer = gst_harness_pull (h);
+ fail_unless_equals_int (6, get_rtp_seq_num (buffer));
+ gst_buffer_unref (buffer);
+
+ /* The original buffer does not count in the RTX stats. */
+ fail_unless (verify_jb_stats (h->element,
+ gst_structure_new ("application/x-rtp-jitterbuffer-stats",
+ "num-pushed", G_TYPE_UINT64, (guint64) num_init_buffers + 1,
+ "num-lost", G_TYPE_UINT64, (guint64) 0,
+ "num-late", G_TYPE_UINT64, (guint64) 0,
+ "num-duplicates", G_TYPE_UINT64, (guint64) 0,
+ "rtx-count", G_TYPE_UINT64, (guint64) 1,
+ "rtx-success-count", G_TYPE_UINT64, (guint64) 0,
+ "rtx-per-packet", G_TYPE_DOUBLE, 0.0,
+ "rtx-rtt", G_TYPE_UINT64, (guint64) 0, NULL)));
+
+ /* Now the retransmitted packet arrives and stats should be updated. Note
+ * that the buffer arrives in time and should not be considered late, but
+ * a duplicate. */
+ fail_unless_equals_int (GST_FLOW_OK, gst_harness_push (h,
+ generate_test_buffer_rtx (now, 6)));
+ fail_unless (verify_jb_stats (h->element,
+ gst_structure_new ("application/x-rtp-jitterbuffer-stats",
+ "num-pushed", G_TYPE_UINT64, (guint64) num_init_buffers + 1,
+ "num-lost", G_TYPE_UINT64, (guint64) 0,
+ "num-late", G_TYPE_UINT64, (guint64) 0,
+ "num-duplicates", G_TYPE_UINT64, (guint64) 1,
+ "rtx-count", G_TYPE_UINT64, (guint64) 1,
+ "rtx-success-count", G_TYPE_UINT64, (guint64) 0,
+ "rtx-per-packet", G_TYPE_DOUBLE, 1.0,
+ "rtx-rtt", G_TYPE_UINT64, (guint64) (now - last_rtx_request),
+ NULL)));
+
+ gst_object_unref (testclock);
+ gst_harness_teardown (h);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_rtx_duplicate_packet_updates_rtx_stats)
+{
+ GstHarness *h = gst_harness_new ("rtpjitterbuffer");
+ GstTestClock *testclock;
+ gint latency_ms = 5 * PCMU_BUF_MS;
+ gint num_init_buffers = latency_ms / PCMU_BUF_MS + 1;
+ GstClockTime now, rtx_request_6, rtx_request_7;
+ gint rtx_delay_ms;
+
+ testclock = gst_harness_get_testclock (h);
+ gst_harness_set_src_caps (h, generate_caps ());
+ g_object_set (h->element, "do-retransmission", TRUE, "latency", latency_ms,
+ NULL);
+
+ /* Push/pull buffers and advance time past buffer 0's timeout (in order to
+ * simplify the test) */
+ for (gint i = 0; i < num_init_buffers; i++) {
+ gst_test_clock_set_time (testclock, i * PCMU_BUF_DURATION);
+ fail_unless_equals_int (GST_FLOW_OK,
+ gst_harness_push (h, generate_test_buffer (i)));
+ gst_harness_wait_for_clock_id_waits (h, 1, 60);
+ }
+
+ gst_harness_crank_single_clock_wait (h);
+ fail_unless_equals_int64 (latency_ms * GST_MSECOND,
+ gst_clock_get_time (GST_CLOCK (testclock)));
+
+ for (gint i = 0; i < num_init_buffers; i++)
+ gst_buffer_unref (gst_harness_pull (h));
+
+ /* Drop reconfigure event */
+ gst_event_unref (gst_harness_pull_upstream_event (h));
+
+ /* Push packet 8 so that 6 and 7 is missing */
+ fail_unless_equals_int (GST_FLOW_OK,
+ gst_harness_push (h, generate_test_buffer (8)));
+
+ /* Wait for NACKs on 6 and 7 */
+ gst_harness_crank_single_clock_wait (h);
+ rtx_delay_ms = 10;
+ verify_rtx_event (gst_harness_pull_upstream_event (h),
+ 6, 6 * PCMU_BUF_DURATION, rtx_delay_ms, PCMU_BUF_DURATION);
+ rtx_request_6 = gst_clock_get_time (GST_CLOCK (testclock));
+ fail_unless_equals_int64 (rtx_request_6,
+ 6 * PCMU_BUF_DURATION + rtx_delay_ms * GST_MSECOND);
+
+ gst_harness_crank_single_clock_wait (h);
+ verify_rtx_event (gst_harness_pull_upstream_event (h),
+ 7, 7 * PCMU_BUF_DURATION, rtx_delay_ms, PCMU_BUF_DURATION);
+ rtx_request_7 = gst_clock_get_time (GST_CLOCK (testclock));
+ fail_unless_equals_int64 (rtx_request_7,
+ 7 * PCMU_BUF_DURATION + rtx_delay_ms * GST_MSECOND);
+
+ /* Original packet 7 arrives */
+ now = 150 * GST_MSECOND;
+ gst_test_clock_set_time (testclock, now);
+ fail_unless_equals_int (GST_FLOW_OK, gst_harness_push (h,
+ generate_test_buffer_full (now, TRUE, 7, 7 * PCMU_RTP_TS_DURATION)));
+
+ /* We're still waiting for packet 6, so 7 should not be pushed */
+ gst_harness_wait_for_clock_id_waits (h, 1, 60);
+ fail_unless_equals_int (gst_harness_buffers_in_queue (h), 0);
+
+ /* The original buffer does not count in the RTX stats. */
+ fail_unless (verify_jb_stats (h->element,
+ gst_structure_new ("application/x-rtp-jitterbuffer-stats",
+ "num-lost", G_TYPE_UINT64, (guint64) 0,
+ "num-late", G_TYPE_UINT64, (guint64) 0,
+ "num-duplicates", G_TYPE_UINT64, (guint64) 0,
+ "rtx-count", G_TYPE_UINT64, (guint64) 2,
+ "rtx-success-count", G_TYPE_UINT64, (guint64) 0,
+ "rtx-per-packet", G_TYPE_DOUBLE, 0.0,
+ "rtx-rtt", G_TYPE_UINT64, (guint64) 0, NULL)));
+
+ /* Push RTX packet 7. Should be dropped as duplicate but update RTX stats. */
+ now = 160 * GST_MSECOND;
+ gst_test_clock_set_time (testclock, now);
+ fail_unless_equals_int (GST_FLOW_OK, gst_harness_push (h,
+ generate_test_buffer_rtx (now, 7)));
+ gst_harness_wait_for_clock_id_waits (h, 1, 60);
+ fail_unless_equals_int (gst_harness_buffers_in_queue (h), 0);
+
+ /* Check RTX stats with updated num-duplicates and rtx-rtt fields */
+ fail_unless (verify_jb_stats (h->element,
+ gst_structure_new ("application/x-rtp-jitterbuffer-stats",
+ "num-pushed", G_TYPE_UINT64, (guint64) num_init_buffers,
+ "num-lost", G_TYPE_UINT64, (guint64) 0,
+ "num-late", G_TYPE_UINT64, (guint64) 0,
+ "num-duplicates", G_TYPE_UINT64, (guint64) 1,
+ "rtx-count", G_TYPE_UINT64, (guint64) 2,
+ "rtx-success-count", G_TYPE_UINT64, (guint64) 0,
+ "rtx-per-packet", G_TYPE_DOUBLE, 1.0,
+ "rtx-rtt", G_TYPE_UINT64, (guint64) (now - rtx_request_7),
+ NULL)));
+
+ /* RTX packet 6 arrives, both 6, 7 and 8 is ready to be pulled */
+ fail_unless_equals_int (GST_FLOW_OK, gst_harness_push (h,
+ generate_test_buffer_rtx (now, 6)));
+
+ for (gint i = 6; i <= 8; i++) {
+ GstBuffer *buf = gst_harness_pull (h);
+ fail_unless_equals_int (i, get_rtp_seq_num (buf));
+ gst_buffer_unref (buf);
+ }
+
+ /* RTX stats is updated with success count increased. */
+ fail_unless (verify_jb_stats (h->element,
+ gst_structure_new ("application/x-rtp-jitterbuffer-stats",
+ "num-pushed", G_TYPE_UINT64, (guint64) num_init_buffers + 3,
+ "num-lost", G_TYPE_UINT64, (guint64) 0,
+ "num-late", G_TYPE_UINT64, (guint64) 0,
+ "num-duplicates", G_TYPE_UINT64, (guint64) 1,
+ "rtx-count", G_TYPE_UINT64, (guint64) 2,
+ "rtx-success-count", G_TYPE_UINT64, (guint64) 1,
+ "rtx-per-packet", G_TYPE_DOUBLE, 1.0,
+ "rtx-rtt", G_TYPE_UINT64, (guint64)
+ /* Use the rtx-rtt formula. Can be subject to change though. */
+ ((now - rtx_request_6) + 47 * (now - rtx_request_7)) / 48,
+ NULL)));
+
+ gst_object_unref (testclock);
+ gst_harness_teardown (h);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_rtx_buffer_arrives_after_lost_updates_rtx_stats)
+{
+ GstHarness *h = gst_harness_new ("rtpjitterbuffer");
+ GstTestClock *testclock;
+ gint latency_ms = 5 * PCMU_BUF_MS;
+ gint num_init_buffers = latency_ms / PCMU_BUF_MS + 1;
+ GstClockTime now, last_rtx_request;
+
+ testclock = gst_harness_get_testclock (h);
+ gst_harness_set_src_caps (h, generate_caps ());
+ g_object_set (h->element, "do-retransmission", TRUE, "latency", latency_ms,
+ "do-lost", TRUE, "rtx-max-retries", 1, NULL);
+
+ /* Push/pull buffers and advance time past buffer 0's timeout (in order to
+ * simplify the test) */
+ for (gint i = 0; i < num_init_buffers; i++) {
+ gst_test_clock_set_time (testclock, i * PCMU_BUF_DURATION);
+ fail_unless_equals_int (GST_FLOW_OK,
+ gst_harness_push (h, generate_test_buffer (i)));
+ gst_harness_wait_for_clock_id_waits (h, 1, 60);
+ }
+
+ gst_harness_crank_single_clock_wait (h);
+ fail_unless_equals_int64 (latency_ms * GST_MSECOND,
+ gst_clock_get_time (GST_CLOCK (testclock)));
+
+ for (gint i = 0; i < num_init_buffers; i++)
+ gst_buffer_unref (gst_harness_pull (h));
+
+ /* drop reconfigure event */
+ gst_event_unref (gst_harness_pull_upstream_event (h));
+ /* drop GstEventStreamStart & GstEventCaps & GstEventSegment */
+ for (gint i = 0; i < 3; i++)
+ gst_event_unref (gst_harness_pull_event (h));
+
+ /* Crank clock to send retransmission events requesting seqnum 6 which has
+ * not arrived yet. */
+ gst_harness_crank_single_clock_wait (h);
+ verify_rtx_event (gst_harness_pull_upstream_event (h),
+ 6, 6 * PCMU_BUF_DURATION, 10, PCMU_BUF_DURATION);
+
+ last_rtx_request = gst_clock_get_time (GST_CLOCK (testclock));
+ fail_unless_equals_int64 (last_rtx_request, 130 * GST_MSECOND);
+
+ /* seqnum 6 is considered lost */
+ gst_harness_crank_single_clock_wait (h);
+ verify_lost_event (gst_harness_pull_event (h), 6,
+ 6 * PCMU_BUF_DURATION, PCMU_BUF_DURATION);
+
+ /* seqnum 6 arrives too late */
+ now = gst_clock_get_time (GST_CLOCK (testclock));
+ fail_unless_equals_int (GST_FLOW_OK, gst_harness_push (h,
+ generate_test_buffer_rtx (now, 6)));
+
+ fail_unless (verify_jb_stats (h->element,
+ gst_structure_new ("application/x-rtp-jitterbuffer-stats",
+ "num-pushed", G_TYPE_UINT64, (guint64) num_init_buffers,
+ "num-lost", G_TYPE_UINT64, (guint64) 1,
+ "num-late", G_TYPE_UINT64, (guint64) 1,
+ "num-duplicates", G_TYPE_UINT64, (guint64) 0,
+ "rtx-count", G_TYPE_UINT64, (guint64) 1,
+ "rtx-success-count", G_TYPE_UINT64, (guint64) 0,
+ "rtx-per-packet", G_TYPE_DOUBLE, 1.0,
+ "rtx-rtt", G_TYPE_UINT64, (guint64) (now - last_rtx_request),
+ NULL)));
+
+ gst_object_unref (testclock);
+ gst_harness_teardown (h);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_rtx_rtt_larger_than_retry_timeout)
+{
+ /* When RTT is larger than retry period we will send two or more requests
+ * before receiving any retransmission packets */
+ GstHarness *h = gst_harness_new ("rtpjitterbuffer");
+ GstTestClock *testclock;
+ gint latency_ms = 100;
+ gint num_init_buffers = latency_ms / PCMU_BUF_MS + 1;
+ gint rtx_retry_timeout_ms = 20;
+ gint rtx_delay_ms = 10;
+ gint rtt = rtx_retry_timeout_ms * GST_MSECOND + 1;
+ GstClockTime now, first_request, second_request;
+
+ testclock = gst_harness_get_testclock (h);
+ gst_harness_set_src_caps (h, generate_caps ());
+ g_object_set (h->element, "do-retransmission", TRUE, "latency", latency_ms,
+ "rtx-retry-timeout", rtx_retry_timeout_ms, NULL);
+
+ /* Push/pull buffers and advance time past buffer 0's timeout (in order to
+ * simplify the test) */
+ for (gint i = 0; i < num_init_buffers; i++) {
+ gst_test_clock_set_time (testclock, i * PCMU_BUF_DURATION);
+ fail_unless_equals_int (GST_FLOW_OK,
+ gst_harness_push (h, generate_test_buffer (i)));
+ gst_harness_wait_for_clock_id_waits (h, 1, 60);
+ }
+
+ gst_harness_crank_single_clock_wait (h);
+ fail_unless_equals_int64 (latency_ms * GST_MSECOND,
+ gst_clock_get_time (GST_CLOCK (testclock)));
+
+ for (gint i = 0; i < num_init_buffers; i++)
+ gst_buffer_unref (gst_harness_pull (h));
+
+ /* Drop reconfigure event */
+ gst_event_unref (gst_harness_pull_upstream_event (h));
+
+ /* Wait for first NACK on 6 */
+ gst_harness_crank_single_clock_wait (h);
+ verify_rtx_event (gst_harness_pull_upstream_event (h),
+ 6, 6 * PCMU_BUF_DURATION, rtx_delay_ms, PCMU_BUF_DURATION);
+ first_request = gst_clock_get_time (GST_CLOCK (testclock));
+ fail_unless_equals_int64 (first_request,
+ 6 * PCMU_BUF_DURATION + rtx_delay_ms * GST_MSECOND);
+
+ /* Packet 7 arrives in time (so that we avoid its EXPECTED timers to
+ * interfer with our test) */
+ gst_test_clock_set_time (testclock, 7 * PCMU_BUF_DURATION);
+ fail_unless_equals_int (GST_FLOW_OK,
+ gst_harness_push (h, generate_test_buffer (7)));
+
+ /* Simulating RTT > rtx-retry-timeout, we send a new NACK before receiving
+ * the RTX packet. Wait for second NACK on 6 */
+ gst_harness_crank_single_clock_wait (h);
+ rtx_delay_ms += rtx_retry_timeout_ms;
+ verify_rtx_event (gst_harness_pull_upstream_event (h),
+ 6, 6 * PCMU_BUF_DURATION, rtx_delay_ms, PCMU_BUF_DURATION);
+ second_request = gst_clock_get_time (GST_CLOCK (testclock));
+ fail_unless_equals_int64 (second_request,
+ 6 * PCMU_BUF_DURATION + rtx_delay_ms * GST_MSECOND);
+
+ /* The first retransmitted packet arrives */
+ now = first_request + rtt;
+ gst_test_clock_set_time (testclock, now);
+ fail_unless_equals_int (GST_FLOW_OK, gst_harness_push (h,
+ generate_test_buffer_rtx (now, 6)));
+
+ /* Pull packet 6 and 7 */
+ gst_buffer_unref (gst_harness_pull (h));
+ gst_buffer_unref (gst_harness_pull (h));
+
+ /* Stats should be updated. Note that RTT is not updated since we cannot be
+ * sure whether the RTX packet is in response to the first or second NACK. */
+ fail_unless (verify_jb_stats (h->element,
+ gst_structure_new ("application/x-rtp-jitterbuffer-stats",
+ "num-pushed", G_TYPE_UINT64, (guint64) num_init_buffers + 2,
+ "num-lost", G_TYPE_UINT64, (guint64) 0,
+ "num-late", G_TYPE_UINT64, (guint64) 0,
+ "num-duplicates", G_TYPE_UINT64, (guint64) 0,
+ "rtx-count", G_TYPE_UINT64, (guint64) 2,
+ "rtx-success-count", G_TYPE_UINT64, (guint64) 1,
+ "rtx-per-packet", G_TYPE_DOUBLE, 2.0,
+ "rtx-rtt", G_TYPE_UINT64, (guint64) 0, NULL)));
+
+ /* Packet 8 arrives in time */
+ gst_test_clock_set_time (testclock, 8 * PCMU_BUF_DURATION);
+ fail_unless_equals_int (GST_FLOW_OK,
+ gst_harness_push (h, generate_test_buffer (8)));
+ gst_buffer_unref (gst_harness_pull (h));
+
+ /* Now the second retransmitted packet arrives */
+ now = second_request + rtt;
+ gst_test_clock_set_time (testclock, now);
+ fail_unless_equals_int (GST_FLOW_OK, gst_harness_push (h,
+ generate_test_buffer_rtx (now, 6)));
+
+ /* The stats is updated with the correct RTT. */
+ fail_unless (verify_jb_stats (h->element,
+ gst_structure_new ("application/x-rtp-jitterbuffer-stats",
+ "num-pushed", G_TYPE_UINT64, (guint64) num_init_buffers + 3,
+ "num-lost", G_TYPE_UINT64, (guint64) 0,
+ "num-late", G_TYPE_UINT64, (guint64) 0,
+ "num-duplicates", G_TYPE_UINT64, (guint64) 1,
+ "rtx-count", G_TYPE_UINT64, (guint64) 2,
+ "rtx-success-count", G_TYPE_UINT64, (guint64) 1,
+ "rtx-per-packet", G_TYPE_DOUBLE, 2.0,
+ "rtx-rtt", G_TYPE_UINT64, (guint64) rtt, NULL)));
+
+ gst_object_unref (testclock);
+ gst_harness_teardown (h);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_rtx_no_request_if_time_past_retry_period)
+{
+ GstHarness *h = gst_harness_new ("rtpjitterbuffer");
+ const gint latency_ms = 200;
+ const gint retry_period_ms = 120;
+ GstTestClock *testclock;
+ GstClockID pending_id, processed_id;
+ GstClockTime time;
+ GstEvent *event;
+ gint i;
+
+ gst_harness_set_src_caps (h, generate_caps ());
+ testclock = gst_harness_get_testclock (h);
+
+ g_object_set (h->element, "do-lost", TRUE, NULL);
+ g_object_set (h->element, "do-retransmission", TRUE, NULL);
+ g_object_set (h->element, "latency", latency_ms, NULL);
+ g_object_set (h->element, "rtx-retry-period", retry_period_ms, NULL);
+
+ /* push the first couple of buffers */
+ fail_unless_equals_int (GST_FLOW_OK,
+ gst_harness_push (h, generate_test_buffer (0)));
+
+ gst_harness_set_time (h, 1 * PCMU_BUF_DURATION);
+ fail_unless_equals_int (GST_FLOW_OK,
+ gst_harness_push (h, generate_test_buffer (1)));
+
+ /* drop reconfigure event */
+ gst_event_unref (gst_harness_pull_upstream_event (h));
+ /* drop GstEventStreamStart & GstEventCaps & GstEventSegment */
+ for (i = 0; i < 3; i++)
+ gst_event_unref (gst_harness_pull_event (h));
+
+ /* Wait for the first EXPECTED timer to be scheduled */
+ gst_test_clock_wait_for_next_pending_id (testclock, &pending_id);
+ time = gst_clock_id_get_time (pending_id);
+ fail_unless_equals_int64 (time, 2 * PCMU_BUF_DURATION + 10 * GST_MSECOND);
+
+ /* Let the first EXPECTED timer time out and be sent. However, set the 'now'
+ * time to be past the retry-period simulating that the jitterbuffer has too
+ * much to do and is not able to process all timers in real-time. In this
+ * case the jitterbuffer should not schedule a new EXPECTED timer as that
+ * would just make matters worse (more unnecessary processing of a request
+ * that is already too late to be valuable). In practice this typically
+ * happens for high loss networks with low RTT. */
+ gst_test_clock_set_time (testclock,
+ 2 * PCMU_BUF_DURATION + retry_period_ms * GST_MSECOND + 1);
+ processed_id = gst_test_clock_process_next_clock_id (testclock);
+ fail_unless (pending_id == processed_id);
+ gst_clock_id_unref (pending_id);
+ gst_clock_id_unref (processed_id);
+
+ /* Verify the event. It could be argued that this request is already too
+ * late and unnecessary. However, in order to keep things simple (for now)
+ * we just keep the already scehduled EXPECTED timer, but refrain from
+ * scheduled another EXPECTED timer */
+ event = gst_harness_pull_upstream_event (h);
+ verify_rtx_event (event, 2, 2 * PCMU_BUF_DURATION, 10, PCMU_BUF_DURATION);
+
+ /* "crank" to reach the DEADLINE for packet 0 */
+ gst_harness_crank_single_clock_wait (h);
+ gst_buffer_unref (gst_harness_pull (h));
+ gst_buffer_unref (gst_harness_pull (h));
+
+ fail_unless_equals_int (0, gst_harness_upstream_events_in_queue (h));
+ fail_unless_equals_int (0, gst_harness_events_in_queue (h));
+
+ /* "crank" to time out the LOST event */
+ gst_harness_crank_single_clock_wait (h);
+ event = gst_harness_pull_event (h);
+ verify_lost_event (event, 2, 2 * PCMU_BUF_DURATION, PCMU_BUF_DURATION);
gst_object_unref (testclock);
gst_harness_teardown (h);
@@ -1154,8 +2075,6 @@
GstTestClock *testclock;
const gint jb_latency_ms = 200;
- guint32 timestamp_ms = 0;
- guint32 rtp_ts = 0;
gint i;
GstEvent *out_event;
GstBuffer *out_buf;
@@ -1172,10 +2091,7 @@
fail_unless_equals_int (GST_FLOW_OK,
gst_harness_push (h, generate_test_buffer (0)));
- timestamp_ms += 20;
- rtp_ts += PCMU_RTP_TS_DURATION;
- gst_harness_set_time (h, timestamp_ms * GST_MSECOND);
-
+ gst_harness_set_time (h, 1 * PCMU_BUF_DURATION);
fail_unless_equals_int (GST_FLOW_OK,
gst_harness_push (h, generate_test_buffer (1)));
@@ -1210,12 +2126,23 @@
fail_unless_equals_int (GST_FLOW_OK,
gst_harness_push (h, generate_test_buffer (16)));
- /* FIXME: something is up with the timestamp here!!?! */
+ /* Manually check the first rtx event */
out_event = gst_harness_pull_upstream_event (h);
- verify_rtx_event (out_event, 6, 119999994, 0, PCMU_BUF_DURATION);
- /* lost more rtx with weird timestamps... */
- for (i = 0; i < 13; i++) {
- gst_event_unref (gst_harness_pull_upstream_event (h));
+ verify_rtx_event (out_event, 6, 6 * PCMU_BUF_DURATION, 10, PCMU_BUF_DURATION);
+ /* Go throught the rest of rtx events. A bit more relaxed since order is
+ * partly an implentation detail. */
+ for (i = 0; i < 12; i++) {
+ const GstStructure *s;
+ guint seqnum, retry;
+
+ fail_unless (out_event = gst_harness_pull_upstream_event (h));
+ fail_unless (s = gst_event_get_structure (out_event));
+ fail_unless (gst_structure_get_uint (s, "seqnum", &seqnum));
+ fail_unless (gst_structure_get_uint (s, "retry", &retry));
+ fail_unless (seqnum >= 6 && seqnum <= 12);
+
+ verify_rtx_event (out_event, seqnum, seqnum * PCMU_BUF_DURATION,
+ 10 + retry * 40, PCMU_BUF_DURATION);
}
fail_unless_equals_int (0, gst_harness_upstream_events_in_queue (h));
@@ -1227,23 +2154,20 @@
gst_harness_push (h, generate_test_buffer (i)));
}
- /* FIXME: wtf is going on with timestamps and durations here??!? */
gst_harness_crank_single_clock_wait (h);
out_event = gst_harness_pull_event (h);
- verify_lost_event (out_event, 3, 41428571, 78571423);
-
- /* FIXME: and these rtx... */
- gst_harness_crank_single_clock_wait (h);
- out_event = gst_harness_pull_upstream_event (h);
- verify_rtx_event (out_event, 7, 141428565, 120, PCMU_BUF_DURATION);
+ verify_lost_event (out_event, 3, 3 * PCMU_BUF_DURATION,
+ 3 * PCMU_BUF_DURATION);
gst_harness_crank_single_clock_wait (h);
out_event = gst_harness_pull_event (h);
- verify_lost_event (out_event, 6, 119999994, 21428571);
+ verify_lost_event (out_event, 6, 6 * PCMU_BUF_DURATION,
+ 1 * PCMU_BUF_DURATION);
gst_harness_crank_single_clock_wait (h);
out_event = gst_harness_pull_event (h);
- verify_lost_event (out_event, 7, 141428565, 21428571);
+ verify_lost_event (out_event, 7, 7 * PCMU_BUF_DURATION,
+ 1 * PCMU_BUF_DURATION);
/* 8 */
for (i = 8; i <= 16; i++) {
@@ -1274,6 +2198,14 @@
fail_unless_equals_int (0, gst_harness_events_in_queue (h));
fail_unless_equals_int (0, gst_harness_buffers_in_queue (h));
+ fail_unless (verify_jb_stats (h->element,
+ gst_structure_new ("application/x-rtp-jitterbuffer-stats",
+ "num-pushed", G_TYPE_UINT64, (guint64) 11,
+ "num-lost", G_TYPE_UINT64, (guint64) 7,
+ "rtx-count", G_TYPE_UINT64, (guint64) 19,
+ "rtx-success-count", G_TYPE_UINT64, (guint64) 0,
+ "rtx-rtt", G_TYPE_UINT64, (guint64) 0, NULL)));
+
gst_object_unref (testclock);
gst_harness_teardown (h);
}
@@ -1360,6 +2292,10 @@
verify_lost_event (out_event, i, i * dur, dur);
}
+ fail_unless (verify_jb_stats (h->element,
+ gst_structure_new ("application/x-rtp-jitterbuffer-stats",
+ "num-lost", G_TYPE_UINT64, (guint64) 4, NULL)));
+
gst_object_unref (testclock);
gst_harness_teardown (h);
}
@@ -1488,12 +2424,53 @@
fail_unless_equals_int ((4 + seq_offset) & 0xffff, get_rtp_seq_num (buffer));
gst_buffer_unref (buffer);
+ /* we have lost 3, and one of them arrived eventually, but too late */
+ fail_unless (verify_jb_stats (h->element,
+ gst_structure_new ("application/x-rtp-jitterbuffer-stats",
+ "num-pushed", G_TYPE_UINT64, (guint64) 2,
+ "num-lost", G_TYPE_UINT64, (guint64) 3,
+ "num-late", G_TYPE_UINT64, (guint64) 1, NULL)));
+
gst_object_unref (testclock);
gst_harness_teardown (h);
}
GST_END_TEST;
+GST_START_TEST (test_performance)
+{
+ GstHarness *h =
+ gst_harness_new_parse
+ ("rtpjitterbuffer do-lost=1 do-retransmission=1 latency=1000");
+ GTimer *timer = g_timer_new ();
+ const gdouble test_duration = 2.0;
+ guint buffers_pushed = 0;
+ guint buffers_received;
+
+ gst_harness_set_src_caps (h, generate_caps ());
+ gst_harness_use_systemclock (h);
+
+ while (g_timer_elapsed (timer, NULL) < test_duration) {
+ /* Simulate 1ms packets */
+ guint n = buffers_pushed * 2; // every packet also produces a gap
+ guint16 seqnum = n & 0xffff;
+ guint32 rtp_ts = n * 8;
+ GstClockTime dts = n * GST_MSECOND;
+ gst_harness_push (h, generate_test_buffer_full (dts, TRUE, seqnum, rtp_ts));
+ buffers_pushed++;
+ g_usleep (G_USEC_PER_SEC / 10000);
+ }
+ g_timer_destroy (timer);
+
+ buffers_received = gst_harness_buffers_received (h);
+ GST_INFO ("Pushed %d, received %d (%.1f%%)", buffers_pushed, buffers_received,
+ 100.0 * buffers_received / buffers_pushed);
+
+ gst_harness_teardown (h);
+}
+
+GST_END_TEST;
+
static Suite *
rtpjitterbuffer_suite (void)
{
@@ -1506,13 +2483,30 @@
tcase_add_test (tc_chain, test_push_unordered);
tcase_add_test (tc_chain, test_basetime);
tcase_add_test (tc_chain, test_clear_pt_map);
+
tcase_add_test (tc_chain, test_only_one_lost_event_on_large_gaps);
tcase_add_test (tc_chain, test_two_lost_one_arrives_in_time);
tcase_add_test (tc_chain, test_late_packets_still_makes_lost_events);
tcase_add_test (tc_chain, test_all_packets_are_timestamped_zero);
+ tcase_add_loop_test (tc_chain, test_num_late_when_considered_lost_arrives, 0,
+ 2);
+ 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_test (tc_chain, test_rtx_expected_next);
tcase_add_test (tc_chain, test_rtx_two_missing);
+ tcase_add_test (tc_chain, text_rtx_two_missing_early);
tcase_add_test (tc_chain, test_rtx_packet_delay);
+ tcase_add_test (tc_chain, test_rtx_buffer_arrives_just_in_time);
+ tcase_add_test (tc_chain, test_rtx_buffer_arrives_too_late);
+ tcase_add_test (tc_chain, test_rtx_original_buffer_does_not_update_rtx_stats);
+ tcase_add_test (tc_chain, test_rtx_duplicate_packet_updates_rtx_stats);
+ tcase_add_test (tc_chain,
+ test_rtx_buffer_arrives_after_lost_updates_rtx_stats);
+ tcase_add_test (tc_chain, test_rtx_rtt_larger_than_retry_timeout);
+ tcase_add_test (tc_chain, test_rtx_no_request_if_time_past_retry_period);
+
tcase_add_test (tc_chain, test_gap_exceeds_latency);
tcase_add_test (tc_chain, test_deadline_ts_offset);
tcase_add_test (tc_chain, test_dts_gap_larger_than_latency);
@@ -1522,6 +2516,8 @@
test_considered_lost_packet_in_large_gap_arrives, 0,
G_N_ELEMENTS (test_considered_lost_packet_in_large_gap_arrives_input));
+ tcase_add_test (tc_chain, test_performance);
+
return s;
}