mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2026-07-05 00:06:16 -04:00
This new data structure is useful for reordering incoming packets if they arrive out-of-order. Many audio codecs require frames and/or packets to be processed in sequence order due to inter-frame dependencies, so reordering is critical for such encoded data. It also detects lost packets and reports those in sequence with received packets (crucial for proper PLC), and detects and drops late and duplicate packets.
1259 lines
48 KiB
C
1259 lines
48 KiB
C
/* PipeWire */
|
|
/* SPDX-FileCopyrightText: Copyright © 2026 Carlos Rafael Giani */
|
|
/* SPDX-License-Identifier: MIT */
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <assert.h>
|
|
#include <arpa/inet.h>
|
|
|
|
#include "config.h"
|
|
|
|
#include <pipewire/pipewire.h>
|
|
#include <module-rtp/rtp.h>
|
|
#include <module-rtp/jitter-buffer.h>
|
|
|
|
#include "pwtest.h"
|
|
|
|
PW_LOG_TOPIC(mod_topic, "test.rtp-jitter-buffer");
|
|
#define PW_LOG_TOPIC_DEFAULT mod_topic
|
|
|
|
enum test_event_type {
|
|
TEST_EVENT_OUTPUT_PACKET,
|
|
TEST_EVENT_LOST_PACKETS,
|
|
};
|
|
|
|
struct test_event {
|
|
enum test_event_type type;
|
|
union {
|
|
struct {
|
|
uint16_t seqnum;
|
|
} output;
|
|
struct {
|
|
uint16_t first_seqnum;
|
|
size_t count;
|
|
bool open_ended;
|
|
} lost;
|
|
};
|
|
};
|
|
|
|
#define MAX_TEST_EVENTS 256
|
|
#define MAX_TEST_PACKET_SIZE 2048
|
|
#define TEST_PACKET_SIZE 128
|
|
#define TEST_HEADER_SIZE 16
|
|
#define TEST_TIMESTAMP 123456
|
|
#define TEST_PACKET_DURATION (10 * SPA_NSEC_PER_MSEC)
|
|
|
|
struct test_context {
|
|
struct pw_loop *loop;
|
|
struct pw_main_loop *main_loop;
|
|
struct rtp_jitter_buffer jitter_buffer;
|
|
|
|
struct test_event events[MAX_TEST_EVENTS];
|
|
size_t num_events;
|
|
|
|
uint8_t packet_bytes[MAX_TEST_PACKET_SIZE];
|
|
};
|
|
|
|
static void send_packet(struct test_context *test_context, uint16_t seqnum)
|
|
{
|
|
/* Create a simulated RTP packet. Only write the sequence number
|
|
* into its header. The rest (SSRC, CSRC, payload type etc.) are
|
|
* of no interest to the jitter buffer - it only cares about the
|
|
* sequence number. */
|
|
struct rtp_header *header = (struct rtp_header *)(test_context->packet_bytes);
|
|
header->sequence_number = htons(seqnum);
|
|
int ret = rtp_jitter_buffer_insert_packet(&(test_context->jitter_buffer),
|
|
test_context->packet_bytes, (TEST_PACKET_SIZE), (TEST_HEADER_SIZE), (TEST_TIMESTAMP), seqnum);
|
|
assert(ret == 0);
|
|
}
|
|
|
|
static int test_output_rtp_packet(void *context, const uint8_t *packet_data, size_t packet_size,
|
|
size_t header_size, uint32_t timestamp, uint16_t seqnum)
|
|
{
|
|
struct test_context *test_context = context;
|
|
struct rtp_header *header = (struct rtp_header *)packet_data;
|
|
|
|
assert(test_context->num_events < MAX_TEST_EVENTS);
|
|
|
|
/* Check that this function is not simply passed
|
|
* the value of params.max_packet_size, and that
|
|
* the other values (header size, timestamp)
|
|
* are correct as well. */
|
|
pwtest_int_eq(packet_size, (size_t)(TEST_PACKET_SIZE));
|
|
pwtest_int_eq(header_size, (size_t)(TEST_HEADER_SIZE));
|
|
pwtest_int_eq(timestamp, (size_t)(TEST_TIMESTAMP));
|
|
|
|
/* Compare the seqnum that is given by the caller
|
|
* with the seqnum in the RTP header to verify that
|
|
* the packet data is correctly associated with the
|
|
* information from the function arguments. */
|
|
pwtest_int_eq(seqnum, ntohs(header->sequence_number));
|
|
|
|
test_context->events[test_context->num_events].type = TEST_EVENT_OUTPUT_PACKET;
|
|
test_context->events[test_context->num_events].output.seqnum = seqnum;
|
|
pw_log_debug("Output RTP packet with seqnum %" PRIu16, test_context->events[test_context->num_events].output.seqnum);
|
|
test_context->num_events++;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int test_signal_lost_packets(void *context, uint16_t seq_of_first_lost_packet,
|
|
size_t num_lost_packets, bool open_ended)
|
|
{
|
|
struct test_context *test_context = context;
|
|
|
|
assert(test_context->num_events < MAX_TEST_EVENTS);
|
|
|
|
test_context->events[test_context->num_events].type = TEST_EVENT_LOST_PACKETS;
|
|
test_context->events[test_context->num_events].lost.first_seqnum = seq_of_first_lost_packet;
|
|
test_context->events[test_context->num_events].lost.count = num_lost_packets;
|
|
test_context->events[test_context->num_events].lost.open_ended = open_ended;
|
|
test_context->num_events++;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void setup_test_context(struct test_context *test_context, size_t num_slots)
|
|
{
|
|
struct rtp_jitter_buffer_params params;
|
|
|
|
assert(test_context != NULL);
|
|
|
|
spa_memzero(test_context, sizeof(struct test_context));
|
|
|
|
pw_init(0, NULL);
|
|
|
|
test_context->main_loop = pw_main_loop_new(NULL);
|
|
assert(test_context->main_loop != NULL);
|
|
test_context->loop = pw_main_loop_get_loop(test_context->main_loop);
|
|
|
|
memset(¶ms, 0, sizeof(params));
|
|
params.num_slots = num_slots;
|
|
/* Set the maximum packet size to a value higher than TEST_PACKET_SIZE
|
|
* to be able to check in test_output_rtp_packet() that that function
|
|
* does not simply get the max_packet_size value as the packet size,
|
|
* but the _actual_ packet size. (Also see test_output_rtp_packet().) */
|
|
params.max_packet_size = MAX_TEST_PACKET_SIZE;
|
|
params.packet_duration = TEST_PACKET_DURATION;
|
|
params.loop = test_context->loop;
|
|
params.context = test_context;
|
|
params.output_rtp_packet = test_output_rtp_packet;
|
|
params.signal_lost_packets = test_signal_lost_packets;
|
|
|
|
int ret = rtp_jitter_buffer_init(&(test_context->jitter_buffer), ¶ms);
|
|
assert(ret == 0);
|
|
}
|
|
|
|
static void teardown_test_context(struct test_context *test_context)
|
|
{
|
|
assert(test_context != NULL);
|
|
|
|
rtp_jitter_buffer_shutdown(&(test_context->jitter_buffer));
|
|
if (test_context->main_loop != NULL)
|
|
pw_main_loop_destroy(test_context->main_loop);
|
|
pw_deinit();
|
|
}
|
|
|
|
#define SHIFT_TEST_EVENTS() \
|
|
do { \
|
|
memmove( \
|
|
&(test_context.events[0]), \
|
|
&(test_context.events[1]), \
|
|
(test_context.num_events - 1) * sizeof(struct test_event)); \
|
|
test_context.num_events--; \
|
|
} while (0)
|
|
|
|
#define CHECK_LOST_PACKET_EVENT(FIRST_SEQNUM, COUNT, OPEN_ENDED) \
|
|
do { \
|
|
pwtest_int_ge(test_context.num_events, 1u); \
|
|
pwtest_int_eq((int)(test_context.events[0].type), TEST_EVENT_LOST_PACKETS); \
|
|
pwtest_int_eq(test_context.events[0].lost.first_seqnum, (FIRST_SEQNUM)); \
|
|
pwtest_int_eq(test_context.events[0].lost.count, (size_t)(COUNT)); \
|
|
pwtest_int_eq(test_context.events[0].lost.open_ended, (OPEN_ENDED)); \
|
|
SHIFT_TEST_EVENTS(); \
|
|
} while (0)
|
|
|
|
#define CHECK_OUTPUT_PACKET_EVENT(SEQNUM) \
|
|
do { \
|
|
pwtest_int_ge(test_context.num_events, 1u); \
|
|
pwtest_int_eq((int)(test_context.events[0].type), TEST_EVENT_OUTPUT_PACKET); \
|
|
pwtest_int_eq(test_context.events[0].output.seqnum, (SEQNUM)); \
|
|
SHIFT_TEST_EVENTS(); \
|
|
} while (0)
|
|
|
|
PWTEST(rtp_jitter_buffer_test_consecutive_packets)
|
|
{
|
|
/* Simple test with packets that are passed to the jitter buffer
|
|
* in order, with no gaps. Immediate output is expected, since
|
|
* the jitter buffer will be in regular mode. */
|
|
|
|
struct test_context test_context;
|
|
|
|
setup_test_context(&test_context, 10);
|
|
|
|
/* Send packets 100, 101, 102, 103, 104 in order.
|
|
* All 5 should be immediately output, and the
|
|
* hold-back mode should remain disabled. */
|
|
for (uint16_t i = 0; i < 5; i++) {
|
|
uint16_t seqnum = 100 + i;
|
|
send_packet(&test_context, seqnum);
|
|
pwtest_bool_false(test_context.jitter_buffer.hold_back_mode);
|
|
}
|
|
|
|
pwtest_int_eq(test_context.num_events, 5u);
|
|
for (uint16_t i = 0; i < 5; i++) {
|
|
uint16_t seqnum = 100 + i;
|
|
CHECK_OUTPUT_PACKET_EVENT(seqnum);
|
|
}
|
|
|
|
teardown_test_context(&test_context);
|
|
|
|
return PWTEST_PASS;
|
|
}
|
|
|
|
PWTEST(rtp_jitter_buffer_test_simple_reordering)
|
|
{
|
|
/* Check that simple out-of-order packet arrival is handled properly.
|
|
* There should be no gaps signaled, and the packets should be output
|
|
* in order. */
|
|
|
|
struct test_context test_context;
|
|
|
|
setup_test_context(&test_context, 10);
|
|
|
|
/* Send 100, 101 in order. */
|
|
send_packet(&test_context, 100);
|
|
pwtest_bool_false(test_context.jitter_buffer.hold_back_mode);
|
|
send_packet(&test_context, 101);
|
|
pwtest_bool_false(test_context.jitter_buffer.hold_back_mode);
|
|
pwtest_int_eq(test_context.num_events, 2u);
|
|
CHECK_OUTPUT_PACKET_EVENT(100);
|
|
CHECK_OUTPUT_PACKET_EVENT(101);
|
|
|
|
/* Send 103. A gap at 102 is produced -> jitter buffer enables hold-back mode.
|
|
* No output takes place just yet, since 103 is held back.
|
|
* The valid seqnum window starts at 102 and ends at packet 103. */
|
|
send_packet(&test_context, 103);
|
|
pwtest_bool_true(test_context.jitter_buffer.hold_back_mode);
|
|
pwtest_int_eq(test_context.jitter_buffer.valid_seqnum_window_start_seqnum, 102u);
|
|
pwtest_int_eq(test_context.jitter_buffer.valid_seqnum_window_length, 2u);
|
|
pwtest_int_eq(test_context.num_events, 0u);
|
|
|
|
/* Send 102 to simulate out-of-order arrival. This fills the gap
|
|
* at 102 (implying that it is not signaled), and should cause
|
|
* 102 and 103 to be output (in order) and the hold-back mode
|
|
* to be disabled again. */
|
|
send_packet(&test_context, 102);
|
|
pwtest_bool_false(test_context.jitter_buffer.hold_back_mode);
|
|
pwtest_int_eq(test_context.num_events, 2u);
|
|
CHECK_OUTPUT_PACKET_EVENT(102);
|
|
CHECK_OUTPUT_PACKET_EVENT(103);
|
|
|
|
teardown_test_context(&test_context);
|
|
|
|
return PWTEST_PASS;
|
|
}
|
|
|
|
PWTEST(rtp_jitter_buffer_test_partial_output)
|
|
{
|
|
/* Test that partial output is done correctly when some
|
|
* gaps are filled. (Partial means that only part of the
|
|
* held-back packets are output.) */
|
|
|
|
struct test_context test_context;
|
|
|
|
setup_test_context(&test_context, 10);
|
|
|
|
/* Establish regular mode with packet 400. */
|
|
send_packet(&test_context, 400);
|
|
pwtest_bool_false(test_context.jitter_buffer.hold_back_mode);
|
|
pwtest_int_eq(test_context.num_events, 1u);
|
|
CHECK_OUTPUT_PACKET_EVENT(400);
|
|
|
|
/* Send in packet 402 to produce a gap at 401 and cause the
|
|
* jitter buffer to enter hold-back mode. */
|
|
send_packet(&test_context, 402);
|
|
pwtest_bool_true(test_context.jitter_buffer.hold_back_mode);
|
|
pwtest_int_eq(test_context.jitter_buffer.valid_seqnum_window_start_seqnum, 401u);
|
|
pwtest_int_eq(test_context.jitter_buffer.valid_seqnum_window_length, 2u);
|
|
pwtest_int_eq(test_context.num_events, 0u);
|
|
|
|
/* Send in packets 404 and 405. This keeps the gap at 401, adds
|
|
* a gap at 403, and keeps the jitter buffer in hold-back mode. */
|
|
send_packet(&test_context, 404);
|
|
pwtest_bool_true(test_context.jitter_buffer.hold_back_mode);
|
|
send_packet(&test_context, 405);
|
|
pwtest_bool_true(test_context.jitter_buffer.hold_back_mode);
|
|
pwtest_int_eq(test_context.num_events, 0u);
|
|
|
|
/* Send in packet 401, which fills the gap at 401. This allows
|
|
* the jitter buffer to output packets 401 and 402. But since
|
|
* another gap exists at 403, hold-back mode remains enabled. */
|
|
send_packet(&test_context, 401);
|
|
pwtest_bool_true(test_context.jitter_buffer.hold_back_mode);
|
|
pwtest_int_eq(test_context.num_events, 2u);
|
|
CHECK_OUTPUT_PACKET_EVENT(401);
|
|
CHECK_OUTPUT_PACKET_EVENT(402);
|
|
|
|
/* Send in packet 403, which fills the gap at 403. This allows
|
|
* the jitter buffer to output packets 403, 404, 405. Those were
|
|
* the remaining held-back packets, so hold-back mode should be
|
|
* turned off now. */
|
|
send_packet(&test_context, 403);
|
|
pwtest_bool_false(test_context.jitter_buffer.hold_back_mode);
|
|
pwtest_int_eq(test_context.num_events, 3u);
|
|
CHECK_OUTPUT_PACKET_EVENT(403);
|
|
CHECK_OUTPUT_PACKET_EVENT(404);
|
|
CHECK_OUTPUT_PACKET_EVENT(405);
|
|
|
|
/* Verify that regular mode is working properly by sending
|
|
* in packet 406. */
|
|
send_packet(&test_context, 406);
|
|
pwtest_bool_false(test_context.jitter_buffer.hold_back_mode);
|
|
pwtest_int_eq(test_context.num_events, 1u);
|
|
CHECK_OUTPUT_PACKET_EVENT(406);
|
|
|
|
teardown_test_context(&test_context);
|
|
|
|
return PWTEST_PASS;
|
|
}
|
|
|
|
PWTEST(rtp_jitter_buffer_test_explicit_drain_in_regular_mode)
|
|
{
|
|
/* Test what happens when explicitly draining the jitter buffer
|
|
* while in regular mode. Draining should be a no-op in this mode. */
|
|
|
|
struct test_context test_context;
|
|
|
|
setup_test_context(&test_context, 10);
|
|
|
|
/* Establish regular mode with packets 200 and 201. */
|
|
send_packet(&test_context, 200);
|
|
send_packet(&test_context, 201);
|
|
pwtest_bool_false(test_context.jitter_buffer.hold_back_mode);
|
|
pwtest_int_eq(test_context.num_events, 2u);
|
|
CHECK_OUTPUT_PACKET_EVENT(200);
|
|
CHECK_OUTPUT_PACKET_EVENT(201);
|
|
|
|
/* Drain, and then check the outcome. Check that it was a no-op. */
|
|
int ret = rtp_jitter_buffer_drain(&(test_context.jitter_buffer));
|
|
assert(ret == 0);
|
|
pwtest_bool_false(test_context.jitter_buffer.hold_back_mode);
|
|
pwtest_int_eq(test_context.num_events, 0u);
|
|
pwtest_int_eq(test_context.jitter_buffer.last_seqnum, 201);
|
|
|
|
teardown_test_context(&test_context);
|
|
|
|
return PWTEST_PASS;
|
|
}
|
|
|
|
PWTEST(rtp_jitter_buffer_test_explicit_drain_in_hold_back_mode)
|
|
{
|
|
/* Test what happens when explicitly draining the jitter buffer
|
|
* while in hold-back mode. Missing packets should be signaled
|
|
* as lost packets by this. */
|
|
|
|
struct test_context test_context;
|
|
|
|
setup_test_context(&test_context, 10);
|
|
|
|
/* Establish regular mode with packet 200. */
|
|
send_packet(&test_context, 200);
|
|
pwtest_bool_false(test_context.jitter_buffer.hold_back_mode);
|
|
pwtest_int_eq(test_context.num_events, 1u);
|
|
CHECK_OUTPUT_PACKET_EVENT(200);
|
|
|
|
/* Send in packets 202 and 205 to produce gap at 201, 203, 204
|
|
* and cause the jitter buffer to enter hold-back mode. */
|
|
send_packet(&test_context, 202);
|
|
send_packet(&test_context, 205);
|
|
pwtest_bool_true(test_context.jitter_buffer.hold_back_mode);
|
|
pwtest_int_eq(test_context.num_events, 0u);
|
|
|
|
/* Drain explicitly. This should output the following (in this order):
|
|
*
|
|
* - 1 lost packet, starting at seqnum 201, not open-ended
|
|
* - 1 packet output with seqnum 202
|
|
* - 2 lost packets, starting at seqnum 203, not open-ended
|
|
* - 1 packet output with seqnum 205
|
|
*
|
|
* This should also set the jitter buffer back to regular mode.
|
|
* The last_seqnum should be -1, since after explicit drain,
|
|
* the jitter buffer has no idea what packets will come next.*/
|
|
int ret = rtp_jitter_buffer_drain(&(test_context.jitter_buffer));
|
|
assert(ret == 0);
|
|
pwtest_bool_false(test_context.jitter_buffer.hold_back_mode);
|
|
pwtest_int_eq(test_context.num_events, 4u);
|
|
pwtest_int_eq(test_context.jitter_buffer.last_seqnum, -1);
|
|
CHECK_LOST_PACKET_EVENT(201, 1u, false);
|
|
CHECK_OUTPUT_PACKET_EVENT(202);
|
|
CHECK_LOST_PACKET_EVENT(203, 2u, false);
|
|
CHECK_OUTPUT_PACKET_EVENT(205);
|
|
|
|
/* Verify that regular mode is working properly by sending
|
|
* in packet 700. Since after draining, the last_seqnum is
|
|
* -1, a discontinuity in the sequence numbers is okay. */
|
|
send_packet(&test_context, 700);
|
|
pwtest_bool_false(test_context.jitter_buffer.hold_back_mode);
|
|
pwtest_int_eq(test_context.num_events, 1u);
|
|
CHECK_OUTPUT_PACKET_EVENT(700);
|
|
|
|
teardown_test_context(&test_context);
|
|
|
|
return PWTEST_PASS;
|
|
}
|
|
|
|
PWTEST(rtp_jitter_buffer_test_explicit_drain_coalesced_loss)
|
|
{
|
|
/* Test that a contiguous set of lost packets is coalesced
|
|
* into one signal lost packet signal. */
|
|
|
|
struct test_context test_context;
|
|
|
|
setup_test_context(&test_context, 10);
|
|
|
|
/* Establish regular mode with packet 50. */
|
|
send_packet(&test_context, 50);
|
|
pwtest_bool_false(test_context.jitter_buffer.hold_back_mode);
|
|
pwtest_int_eq(test_context.num_events, 1u);
|
|
CHECK_OUTPUT_PACKET_EVENT(50);
|
|
|
|
/* Send in packet 54 to produce gap at 51, 52, 53 and
|
|
* cause the jitter buffer to enter hold-back mode. */
|
|
send_packet(&test_context, 54);
|
|
pwtest_bool_true(test_context.jitter_buffer.hold_back_mode);
|
|
pwtest_int_eq(test_context.num_events, 0u);
|
|
|
|
/* Drain the jitter buffer. The packets 51, 52, 53 are
|
|
* now considered lost, and should be reported as such. */
|
|
int ret = rtp_jitter_buffer_drain(&(test_context.jitter_buffer));
|
|
assert(ret == 0);
|
|
pwtest_bool_false(test_context.jitter_buffer.hold_back_mode);
|
|
pwtest_int_eq(test_context.num_events, 2u);
|
|
pwtest_int_eq(test_context.jitter_buffer.last_seqnum, -1);
|
|
CHECK_LOST_PACKET_EVENT(51, 3u, false);
|
|
CHECK_OUTPUT_PACKET_EVENT(54);
|
|
|
|
teardown_test_context(&test_context);
|
|
|
|
return PWTEST_PASS;
|
|
}
|
|
|
|
PWTEST(rtp_jitter_buffer_test_explicit_drain_with_seqnum_wraparound)
|
|
{
|
|
/* Test what happens when explicitly draining the jitter
|
|
* buffer while in hold-back mode and with sequence numbers
|
|
* wrapping around. Missing packets should be signaled as
|
|
* lost packets by this. */
|
|
|
|
struct test_context test_context;
|
|
|
|
setup_test_context(&test_context, 10);
|
|
|
|
/* Establish regular mode with packet 65533. */
|
|
send_packet(&test_context, 65533);
|
|
pwtest_bool_false(test_context.jitter_buffer.hold_back_mode);
|
|
pwtest_int_eq(test_context.num_events, 1u);
|
|
CHECK_OUTPUT_PACKET_EVENT(65533);
|
|
|
|
/* Send in packets 65535 and 2 to produce gap at 65534, 0, 1
|
|
* and cause the jitter buffer to enter hold-back mode. */
|
|
send_packet(&test_context, 65535);
|
|
send_packet(&test_context, 2);
|
|
pwtest_bool_true(test_context.jitter_buffer.hold_back_mode);
|
|
pwtest_int_eq(test_context.num_events, 0u);
|
|
|
|
/* Drain explicitly. This should output the following (in this order):
|
|
*
|
|
* - 1 lost packet, starting at seqnum 65534, not open-ended
|
|
* - 1 packet output with seqnum 65535
|
|
* - 2 lost packets, starting at seqnum 0, not open-ended
|
|
* - 1 packet output with seqnum 2
|
|
*
|
|
* This should also set the jitter buffer back to regular mode.
|
|
* The last_seqnum should be -1, since after explicit drain,
|
|
* the jitter buffer has no idea what packets will come next.*/
|
|
int ret = rtp_jitter_buffer_drain(&(test_context.jitter_buffer));
|
|
assert(ret == 0);
|
|
pwtest_bool_false(test_context.jitter_buffer.hold_back_mode);
|
|
pwtest_int_eq(test_context.num_events, 4u);
|
|
pwtest_int_eq(test_context.jitter_buffer.last_seqnum, -1);
|
|
CHECK_LOST_PACKET_EVENT(65534, 1u, false);
|
|
CHECK_OUTPUT_PACKET_EVENT(65535);
|
|
CHECK_LOST_PACKET_EVENT(0, 2u, false);
|
|
CHECK_OUTPUT_PACKET_EVENT(2);
|
|
|
|
/* Verify that regular mode is working properly by sending
|
|
* in packet 700. Since after draining, the last_seqnum is
|
|
* -1, a discontinuity in the sequence numbers is okay. */
|
|
send_packet(&test_context, 700);
|
|
pwtest_bool_false(test_context.jitter_buffer.hold_back_mode);
|
|
pwtest_int_eq(test_context.num_events, 1u);
|
|
CHECK_OUTPUT_PACKET_EVENT(700);
|
|
|
|
teardown_test_context(&test_context);
|
|
|
|
return PWTEST_PASS;
|
|
}
|
|
|
|
PWTEST(rtp_jitter_buffer_test_stale_packets_in_regular_mode)
|
|
{
|
|
/* Test what happens when stale and old packets are sent into
|
|
* the jitter buffer in regular mode. They should be dropped
|
|
* without influencing the behavior of the jitter buffer. */
|
|
|
|
struct test_context test_context;
|
|
|
|
setup_test_context(&test_context, 10);
|
|
|
|
/* Establish regular mode with packets 100 and 101. */
|
|
send_packet(&test_context, 100);
|
|
pwtest_bool_false(test_context.jitter_buffer.hold_back_mode);
|
|
send_packet(&test_context, 101);
|
|
pwtest_bool_false(test_context.jitter_buffer.hold_back_mode);
|
|
pwtest_int_eq(test_context.num_events, 2u);
|
|
CHECK_OUTPUT_PACKET_EVENT(100);
|
|
CHECK_OUTPUT_PACKET_EVENT(101);
|
|
|
|
/* Send in packet 101. Since a packet 101 was already seen,
|
|
* this is a stale packet, and needs to be dropped. */
|
|
send_packet(&test_context, 101);
|
|
pwtest_bool_false(test_context.jitter_buffer.hold_back_mode);
|
|
pwtest_int_eq(test_context.num_events, 0u);
|
|
|
|
/* Send in packet 99. Since packets 100 and 101 were already seen,
|
|
* this is an old packet, and needs to be dropped. */
|
|
send_packet(&test_context, 99);
|
|
pwtest_bool_false(test_context.jitter_buffer.hold_back_mode);
|
|
pwtest_int_eq(test_context.num_events, 0u);
|
|
|
|
/* Verify that regular mode is working properly by sending
|
|
* in packet 102. */
|
|
send_packet(&test_context, 102);
|
|
pwtest_bool_false(test_context.jitter_buffer.hold_back_mode);
|
|
pwtest_int_eq(test_context.num_events, 1u);
|
|
CHECK_OUTPUT_PACKET_EVENT(102);
|
|
|
|
teardown_test_context(&test_context);
|
|
|
|
return PWTEST_PASS;
|
|
}
|
|
|
|
PWTEST(rtp_jitter_buffer_test_stale_packets_in_hold_back_mode)
|
|
{
|
|
/* Test what happens when stale and old packets are sent into
|
|
* the jitter buffer in hold-back mode. They should be dropped
|
|
* without influencing the behavior of the jitter buffer. */
|
|
|
|
struct test_context test_context;
|
|
|
|
setup_test_context(&test_context, 10);
|
|
|
|
/* Establish hold-back mode with packets 300 and 302.
|
|
* Hold-back mode gets active because of the gap at 301. */
|
|
send_packet(&test_context, 300);
|
|
pwtest_bool_false(test_context.jitter_buffer.hold_back_mode);
|
|
send_packet(&test_context, 302);
|
|
pwtest_bool_true(test_context.jitter_buffer.hold_back_mode);
|
|
pwtest_int_eq(test_context.num_events, 1u);
|
|
CHECK_OUTPUT_PACKET_EVENT(300);
|
|
|
|
/* Send in packet 299. Since packets 300 and 302 were already seen,
|
|
* this is an old packet, and needs to be dropped. */
|
|
send_packet(&test_context, 299);
|
|
pwtest_bool_true(test_context.jitter_buffer.hold_back_mode);
|
|
pwtest_int_eq(test_context.num_events, 0u);
|
|
|
|
/* Send in packet 300. Since a packet 300 was already seen,
|
|
* this is a stale packet, and needs to be dropped. */
|
|
send_packet(&test_context, 300);
|
|
pwtest_bool_true(test_context.jitter_buffer.hold_back_mode);
|
|
pwtest_int_eq(test_context.num_events, 0u);
|
|
|
|
/* Send in packet 302. This is another stale packet. The
|
|
* difference to the packet 300 check above is that the
|
|
* packet 302 that was previously observed is held back,
|
|
* and was not output thus far. */
|
|
send_packet(&test_context, 302);
|
|
pwtest_bool_true(test_context.jitter_buffer.hold_back_mode);
|
|
pwtest_int_eq(test_context.num_events, 0u);
|
|
|
|
/* Send in packet 301 to test that switching back
|
|
* to regular mode still works properly. */
|
|
send_packet(&test_context, 301);
|
|
pwtest_bool_false(test_context.jitter_buffer.hold_back_mode);
|
|
pwtest_int_eq(test_context.num_events, 2u);
|
|
CHECK_OUTPUT_PACKET_EVENT(301);
|
|
CHECK_OUTPUT_PACKET_EVENT(302);
|
|
|
|
teardown_test_context(&test_context);
|
|
|
|
return PWTEST_PASS;
|
|
}
|
|
|
|
PWTEST(rtp_jitter_buffer_test_flush)
|
|
{
|
|
/* Test the flush functionality. This should discard any held-back
|
|
* packets, without emitting them, and the jitter buffer should
|
|
* be back in regular mode afterwards. */
|
|
|
|
struct test_context test_context;
|
|
|
|
setup_test_context(&test_context, 10);
|
|
|
|
/* Establish hold-back mode with packets 500 and 502.
|
|
* Hold-back mode gets active because of the gap at 501. */
|
|
send_packet(&test_context, 500);
|
|
pwtest_bool_false(test_context.jitter_buffer.hold_back_mode);
|
|
send_packet(&test_context, 502);
|
|
pwtest_bool_true(test_context.jitter_buffer.hold_back_mode);
|
|
pwtest_int_eq(test_context.num_events, 1u);
|
|
CHECK_OUTPUT_PACKET_EVENT(500);
|
|
|
|
rtp_jitter_buffer_flush(&(test_context.jitter_buffer));
|
|
pwtest_bool_false(test_context.jitter_buffer.hold_back_mode);
|
|
pwtest_int_eq(test_context.num_events, 0u);
|
|
pwtest_int_eq(test_context.jitter_buffer.last_seqnum, -1);
|
|
|
|
/* Verify that regular mode is working properly by sending
|
|
* in packet 700. Since after flushing, the last_seqnum is
|
|
* -1, a discontinuity in the sequence numbers is okay. */
|
|
send_packet(&test_context, 700);
|
|
pwtest_bool_false(test_context.jitter_buffer.hold_back_mode);
|
|
pwtest_int_eq(test_context.num_events, 1u);
|
|
CHECK_OUTPUT_PACKET_EVENT(700);
|
|
|
|
teardown_test_context(&test_context);
|
|
|
|
return PWTEST_PASS;
|
|
}
|
|
|
|
PWTEST(rtp_jitter_buffer_test_seqnum_wraparound_regular)
|
|
{
|
|
/* Check that in regular mode, output of in-sequence packets
|
|
* works properly even when a sequence number wrap-around occurs. */
|
|
|
|
struct test_context test_context;
|
|
|
|
setup_test_context(&test_context, 10);
|
|
|
|
send_packet(&test_context, 65534);
|
|
pwtest_bool_false(test_context.jitter_buffer.hold_back_mode);
|
|
send_packet(&test_context, 65535);
|
|
pwtest_bool_false(test_context.jitter_buffer.hold_back_mode);
|
|
send_packet(&test_context, 0);
|
|
pwtest_bool_false(test_context.jitter_buffer.hold_back_mode);
|
|
send_packet(&test_context, 1);
|
|
pwtest_bool_false(test_context.jitter_buffer.hold_back_mode);
|
|
pwtest_int_eq(test_context.num_events, 4u);
|
|
CHECK_OUTPUT_PACKET_EVENT(65534);
|
|
CHECK_OUTPUT_PACKET_EVENT(65535);
|
|
CHECK_OUTPUT_PACKET_EVENT(0);
|
|
CHECK_OUTPUT_PACKET_EVENT(1);
|
|
|
|
teardown_test_context(&test_context);
|
|
|
|
return PWTEST_PASS;
|
|
}
|
|
|
|
PWTEST(rtp_jitter_buffer_test_seqnum_wraparound_with_reordering)
|
|
{
|
|
/* Check that in hold-back mode, output of in-sequence packets
|
|
* works properly even when a sequence number wrap-around occurs. */
|
|
|
|
struct test_context test_context;
|
|
|
|
setup_test_context(&test_context, 10);
|
|
|
|
/* Send packets 65534 and 65535 in order. */
|
|
send_packet(&test_context, 65534);
|
|
pwtest_bool_false(test_context.jitter_buffer.hold_back_mode);
|
|
send_packet(&test_context, 65535);
|
|
pwtest_bool_false(test_context.jitter_buffer.hold_back_mode);
|
|
pwtest_int_eq(test_context.num_events, 2u);
|
|
CHECK_OUTPUT_PACKET_EVENT(65534);
|
|
CHECK_OUTPUT_PACKET_EVENT(65535);
|
|
|
|
/* Send in packet 1, causing a gap at 0. */
|
|
send_packet(&test_context, 1);
|
|
pwtest_bool_true(test_context.jitter_buffer.hold_back_mode);
|
|
pwtest_int_eq(test_context.num_events, 0u);
|
|
|
|
/* Fill the gap by sending in packet 0, then check that
|
|
* packets 0 and 1 were now output in order. */
|
|
send_packet(&test_context, 0);
|
|
pwtest_bool_false(test_context.jitter_buffer.hold_back_mode);
|
|
pwtest_int_eq(test_context.num_events, 2u);
|
|
CHECK_OUTPUT_PACKET_EVENT(0);
|
|
CHECK_OUTPUT_PACKET_EVENT(1);
|
|
|
|
teardown_test_context(&test_context);
|
|
|
|
return PWTEST_PASS;
|
|
}
|
|
|
|
PWTEST(rtp_jitter_buffer_test_overextension_single_gap_no_end_gap)
|
|
{
|
|
/* Check what happens when hold-back mode is active, the
|
|
* valid seqnum window's maximum length is reached, and then,
|
|
* a packet with a sequence number that is one past the window
|
|
* range is added. This new packet would overextend the window,
|
|
* so the window is shifted forwards. However, it is only
|
|
* overextended by 1, so only the oldest slot in the window
|
|
* needs to be drained. In this case, that oldest slot contains
|
|
* the gap at the very beginning of the window. Also, since
|
|
* aside from that gap, there are no other ones, and the new
|
|
* packet (the one that overextends the window) comes directly
|
|
* after the last packet in the valid seqnum window, the
|
|
* jitter buffer will have no gaps left to take care of, so
|
|
* all held back packets can be output.
|
|
*
|
|
* This simulates cases where one packet is lost among
|
|
* a string of packets that all arrive in order. */
|
|
|
|
struct test_context test_context;
|
|
|
|
setup_test_context(&test_context, 10);
|
|
|
|
/* Produce a sequence of packets with a gap in them. Start at 100,
|
|
* skip 101, then go all the way to 110.
|
|
*
|
|
* First, packet 100 will immediately be output. Then, packet 102
|
|
* will enable hold-back mode (due to the gap at 101). The valid
|
|
* seqnum window then starts at 101, and extends all the way to 110.
|
|
* 110-101+1 = 10, which equals the max num packets of the jitter
|
|
* buffer here. In other words, after this, the jitter buffer valid
|
|
* range is as large as it can maximally be. */
|
|
send_packet(&test_context, 100);
|
|
for (uint16_t i = 102; i <= 110; i++) {
|
|
send_packet(&test_context, i);
|
|
pwtest_bool_true(test_context.jitter_buffer.hold_back_mode);
|
|
}
|
|
pwtest_int_eq(test_context.num_events, 1u);
|
|
pwtest_int_eq(test_context.jitter_buffer.valid_seqnum_window_length, 10u);
|
|
CHECK_OUTPUT_PACKET_EVENT(100);
|
|
|
|
/* Now insert packet 111. This would overextend the window, so the
|
|
* jitter buffer has to shift the window and drain the oldest slots
|
|
* that are no longer part of the shifted window. Since packet 111
|
|
* would overextend the window by 1, it means that the one oldest
|
|
* slot is drained. That oldest slot actually is the gap at 101.
|
|
* Since that gap was drained (resulting in a packet loss signal
|
|
* at seqnum 101 of length 1), only packets remain in the valid
|
|
* seqnum window, no gaps anymore, so the jitter buffer immediately
|
|
* outputs all of them, in order. */
|
|
send_packet(&test_context, 111);
|
|
pwtest_bool_false(test_context.jitter_buffer.hold_back_mode);
|
|
pwtest_int_eq(test_context.num_events, 11u);
|
|
CHECK_LOST_PACKET_EVENT(101, 1u, false);
|
|
for (uint16_t i = 102; i <= 111; i++)
|
|
CHECK_OUTPUT_PACKET_EVENT(i);
|
|
|
|
teardown_test_context(&test_context);
|
|
|
|
return PWTEST_PASS;
|
|
}
|
|
|
|
PWTEST(rtp_jitter_buffer_test_overextension_multiple_gaps_no_end_gap)
|
|
{
|
|
/* Check what happens when hold-back mode is active, the
|
|
* valid seqnum window's maximum length is reached, and then,
|
|
* a packet with a sequence number that is one past the window
|
|
* range is added. This new packet would overextend the window,
|
|
* so the window is shifted forwards. However, it is only
|
|
* overextended by 1, so only the oldest slot in the window
|
|
* needs to be drained. In this case, that oldest slot contains
|
|
* the gap at the very beginning of the window. Since there
|
|
* are more gaps present, the hold-back mode is not left.
|
|
*
|
|
* This simulates cases where more than one packet is lost
|
|
* among a string of packets that all arrive in order. */
|
|
|
|
struct test_context test_context;
|
|
|
|
setup_test_context(&test_context, 10);
|
|
|
|
/* Produce a sequence of packets with a gap in them. Start at 100,
|
|
* skip 101 and 102, and go all the way to 110.
|
|
*
|
|
* In the hold-back mode that results from this, the valid range
|
|
* then starts at 101, and extends all the way to 110. 110-101+1 = 10,
|
|
* which equals the max num packets of the jitter buffer here. In
|
|
* other words, after this, the jitter buffer valid range is as large
|
|
* as it can maximally be. */
|
|
send_packet(&test_context, 100);
|
|
send_packet(&test_context, 103);
|
|
pwtest_bool_true(test_context.jitter_buffer.hold_back_mode);
|
|
for (uint16_t i = 105; i <= 110; i++) {
|
|
send_packet(&test_context, i);
|
|
pwtest_bool_true(test_context.jitter_buffer.hold_back_mode);
|
|
}
|
|
pwtest_int_eq(test_context.num_events, 1u);
|
|
pwtest_int_eq(test_context.jitter_buffer.valid_seqnum_window_length, 10u);
|
|
CHECK_OUTPUT_PACKET_EVENT(100);
|
|
|
|
/* Now insert packet 111. This would overextend the window, so the
|
|
* jitter buffer has to shift the window and drain the oldest slots
|
|
* that are no longer part of the shifted window. Since packet 111
|
|
* would overextend the window by 1, it means that the one oldest
|
|
* slot is drained. But, at 102, there is also gap, and 102 is now
|
|
* the new start of the valid seqnum window, so the jitter buffer
|
|
* cannot output any packets yet. */
|
|
send_packet(&test_context, 111);
|
|
pwtest_bool_true(test_context.jitter_buffer.hold_back_mode);
|
|
pwtest_int_eq(test_context.num_events, 1u);
|
|
pwtest_int_eq(test_context.jitter_buffer.valid_seqnum_window_start_seqnum, 102u);
|
|
pwtest_int_eq(test_context.jitter_buffer.valid_seqnum_window_length, 10u);
|
|
CHECK_LOST_PACKET_EVENT(101, 1u, false);
|
|
|
|
/* To see that the behavior remains as expected, fill the gap at 102.
|
|
* Since 102 is the very beginning of the valid seqnum window, and there
|
|
* is a packet at 103, the jitter buffer can now output 102 and 103.
|
|
* Also, the valid seqnum window shrinks accordingly by 2, its length
|
|
* becoming 8 and its start seqnum becoming 104. */
|
|
send_packet(&test_context, 102);
|
|
pwtest_bool_true(test_context.jitter_buffer.hold_back_mode);
|
|
pwtest_int_eq(test_context.num_events, 2u);
|
|
pwtest_int_eq(test_context.jitter_buffer.valid_seqnum_window_start_seqnum, 104u);
|
|
pwtest_int_eq(test_context.jitter_buffer.valid_seqnum_window_length, 8u);
|
|
CHECK_OUTPUT_PACKET_EVENT(102);
|
|
CHECK_OUTPUT_PACKET_EVENT(103);
|
|
|
|
/* Finally, send in packet 104. By now, 104 is the start of the valid
|
|
* packet window, and a gap is there. Since this is the last gap in
|
|
* the jitter buffer, once it is filled, all packets can be output. */
|
|
send_packet(&test_context, 104);
|
|
pwtest_bool_false(test_context.jitter_buffer.hold_back_mode);
|
|
pwtest_int_eq(test_context.num_events, 8u);
|
|
for (uint16_t i = 104; i <= 111; i++)
|
|
CHECK_OUTPUT_PACKET_EVENT(i);
|
|
|
|
teardown_test_context(&test_context);
|
|
|
|
return PWTEST_PASS;
|
|
}
|
|
|
|
PWTEST(rtp_jitter_buffer_test_overextension_after_partial_output)
|
|
{
|
|
/* Check what happens when first, in hold-back mode, a partial
|
|
* drain happens, and then, the valid seqnum window is overextended. */
|
|
|
|
struct test_context test_context;
|
|
|
|
setup_test_context(&test_context, 5);
|
|
|
|
/* Add a packet 100, which is output immediately, since the
|
|
* jitter buffer is in regular mode. */
|
|
send_packet(&test_context, 100);
|
|
pwtest_bool_false(test_context.jitter_buffer.hold_back_mode);
|
|
pwtest_int_eq(test_context.num_events, 1u);
|
|
CHECK_OUTPUT_PACKET_EVENT(100);
|
|
|
|
/* Now add packet 102. Since there is a gap at 101, hold-back
|
|
* mode is enabled. The valid seqnum window starts at 101,
|
|
* and is of length 2. */
|
|
send_packet(&test_context, 102);
|
|
pwtest_bool_true(test_context.jitter_buffer.hold_back_mode);
|
|
pwtest_int_eq(test_context.jitter_buffer.valid_seqnum_window_start_seqnum, 101u);
|
|
pwtest_int_eq(test_context.jitter_buffer.valid_seqnum_window_length, 2u);
|
|
pwtest_int_eq(test_context.num_events, 0u);
|
|
|
|
/* Packets 103 to 105 are inserted. This fills the window to
|
|
* capacity, since now, it has been extended, and goes from
|
|
* 101 to 105. That is, it starts at 101, and is of length 5
|
|
* which equals the jitter buffer capacity). */
|
|
send_packet(&test_context, 103);
|
|
send_packet(&test_context, 104);
|
|
send_packet(&test_context, 105);
|
|
pwtest_bool_true(test_context.jitter_buffer.hold_back_mode);
|
|
pwtest_int_eq(test_context.jitter_buffer.valid_seqnum_window_start_seqnum, 101u);
|
|
pwtest_int_eq(test_context.jitter_buffer.valid_seqnum_window_length, 5u);
|
|
pwtest_int_eq(test_context.num_events, 0u);
|
|
|
|
/* Now add packet 101. This fills the gap. All 5 packets
|
|
* can be output, and the jitter buffer returns to the regular mode. */
|
|
send_packet(&test_context, 101);
|
|
pwtest_bool_false(test_context.jitter_buffer.hold_back_mode);
|
|
pwtest_int_eq(test_context.num_events, 5u);
|
|
CHECK_OUTPUT_PACKET_EVENT(101);
|
|
CHECK_OUTPUT_PACKET_EVENT(102);
|
|
CHECK_OUTPUT_PACKET_EVENT(103);
|
|
CHECK_OUTPUT_PACKET_EVENT(104);
|
|
CHECK_OUTPUT_PACKET_EVENT(105);
|
|
|
|
/* Re-enter the hold-back mode by adding packet 107 and
|
|
* intentionally leaving out packet 106. The valid seqnum
|
|
* window now starts at 106, and is of length 2. */
|
|
send_packet(&test_context, 107);
|
|
pwtest_bool_true(test_context.jitter_buffer.hold_back_mode);
|
|
pwtest_int_eq(test_context.jitter_buffer.valid_seqnum_window_start_seqnum, 106u);
|
|
pwtest_int_eq(test_context.jitter_buffer.valid_seqnum_window_length, 2u);
|
|
pwtest_int_eq(test_context.num_events, 0u);
|
|
|
|
/* Packets 108 to 110 are inserted. This fills the window to
|
|
* capacity, since now, it has been extended, and goes from
|
|
* 106 to 110. That is, it starts at 106, and is of length 5
|
|
* which equals the jitter buffer capacity). */
|
|
send_packet(&test_context, 108);
|
|
send_packet(&test_context, 109);
|
|
send_packet(&test_context, 110);
|
|
pwtest_bool_true(test_context.jitter_buffer.hold_back_mode);
|
|
pwtest_int_eq(test_context.jitter_buffer.valid_seqnum_window_start_seqnum, 106u);
|
|
pwtest_int_eq(test_context.jitter_buffer.valid_seqnum_window_length, 5u);
|
|
pwtest_int_eq(test_context.num_events, 0u);
|
|
|
|
/* Packet 111 is added. This overextends the window, since it would
|
|
* now go from 106 to 111. That is a length of 111-106+1 = 6, which
|
|
* is beyond the capacity (5).
|
|
*
|
|
* The overextension is still low enough that most of the window
|
|
* contents can be reused. In fact, only the oldest slot (the one
|
|
* containing the gap at 106) needs to be drained by signaling it
|
|
* as a packet 106 loss.
|
|
*
|
|
* Once packet 106 is signaled as lost, and the corresponding slot
|
|
* is drained, the leftovers are all packets, no gaps, so all packets
|
|
* from 107 to 111 are output.
|
|
*
|
|
* By combining this with multiple partial drains above, it is verified
|
|
* that valid_seqnum_window_start_seqnum updates (which happen during
|
|
* partial drains) do not break the overextension handling. */
|
|
send_packet(&test_context, 111);
|
|
pwtest_bool_false(test_context.jitter_buffer.hold_back_mode);
|
|
pwtest_int_eq(test_context.num_events, 6u);
|
|
CHECK_LOST_PACKET_EVENT(106, 1u, false);
|
|
CHECK_OUTPUT_PACKET_EVENT(107);
|
|
CHECK_OUTPUT_PACKET_EVENT(108);
|
|
CHECK_OUTPUT_PACKET_EVENT(109);
|
|
CHECK_OUTPUT_PACKET_EVENT(110);
|
|
CHECK_OUTPUT_PACKET_EVENT(111);
|
|
|
|
/* Verify regular mode recovery. */
|
|
send_packet(&test_context, 112);
|
|
pwtest_bool_false(test_context.jitter_buffer.hold_back_mode);
|
|
pwtest_int_eq(test_context.num_events, 1u);
|
|
CHECK_OUTPUT_PACKET_EVENT(112);
|
|
|
|
teardown_test_context(&test_context);
|
|
|
|
return PWTEST_PASS;
|
|
}
|
|
|
|
PWTEST(rtp_jitter_buffer_test_immediate_overextension_after_regular_mode)
|
|
{
|
|
/* Check what happens when a gap causes the jitter buffer to switch
|
|
* to the hold-back mode, but that gap is so large that it immediately
|
|
* overextends the valid seqnum window. The jitter buffer should
|
|
* instantly recognize the immediate overextension aqnd signal an open
|
|
* ended packet loss event. It does not stay in the hold-back mode,
|
|
* since there is nothing to hold back in that case. */
|
|
|
|
struct test_context test_context;
|
|
|
|
setup_test_context(&test_context, 10);
|
|
|
|
/* Send 100, 101 in order. */
|
|
send_packet(&test_context, 100);
|
|
pwtest_bool_false(test_context.jitter_buffer.hold_back_mode);
|
|
send_packet(&test_context, 101);
|
|
pwtest_bool_false(test_context.jitter_buffer.hold_back_mode);
|
|
pwtest_int_eq(test_context.num_events, 2u);
|
|
CHECK_OUTPUT_PACKET_EVENT(100);
|
|
CHECK_OUTPUT_PACKET_EVENT(101);
|
|
|
|
/* Send 200. A massive gap of far more than 10 packets is produced
|
|
* -> jitter buffer signals an open ended gap, but stays in regular mode. */
|
|
send_packet(&test_context, 200);
|
|
pwtest_bool_false(test_context.jitter_buffer.hold_back_mode);
|
|
pwtest_int_eq(test_context.num_events, 2u);
|
|
CHECK_LOST_PACKET_EVENT(102, 10u, true);
|
|
CHECK_OUTPUT_PACKET_EVENT(200);
|
|
|
|
teardown_test_context(&test_context);
|
|
|
|
return PWTEST_PASS;
|
|
}
|
|
|
|
PWTEST(rtp_jitter_buffer_test_immediate_overextension_after_regular_mode_threshold_open_closed_gap)
|
|
{
|
|
/* This is similar to rtp_jitter_buffer_test_immediate_overextension_after_regular_mode,
|
|
* but checks for a corner case. That is: If the gap length equals
|
|
* the number of slots, then the gap should not be reported as open.
|
|
*
|
|
* Test this by producing such a gap. Then further verify by repeating
|
|
* the test, but by a gap that is 1 packet larger than the number of
|
|
* slots. The first round should report a closed gap of a size equal
|
|
* to the number of slot. The second round should report an open gap. */
|
|
|
|
/* First round. */
|
|
{
|
|
struct test_context test_context;
|
|
|
|
setup_test_context(&test_context, 10);
|
|
|
|
/* Send 10, 11 in order. */
|
|
send_packet(&test_context, 10);
|
|
pwtest_bool_false(test_context.jitter_buffer.hold_back_mode);
|
|
send_packet(&test_context, 11);
|
|
pwtest_bool_false(test_context.jitter_buffer.hold_back_mode);
|
|
pwtest_int_eq(test_context.num_events, 2u);
|
|
CHECK_OUTPUT_PACKET_EVENT(10);
|
|
CHECK_OUTPUT_PACKET_EVENT(11);
|
|
|
|
/* Send 22. A gap of exactly 10 packets (= the number of slots)
|
|
* is produced -> jitter buffer signals a closed gap of size
|
|
* equal to the number of slots. */
|
|
send_packet(&test_context, 22);
|
|
pwtest_bool_false(test_context.jitter_buffer.hold_back_mode);
|
|
pwtest_int_eq(test_context.num_events, 2u);
|
|
CHECK_LOST_PACKET_EVENT(12, 10u, false);
|
|
CHECK_OUTPUT_PACKET_EVENT(22);
|
|
|
|
teardown_test_context(&test_context);
|
|
}
|
|
|
|
/* Second round. */
|
|
{
|
|
struct test_context test_context;
|
|
|
|
setup_test_context(&test_context, 10);
|
|
|
|
/* Send 10, 11 in order. */
|
|
send_packet(&test_context, 10);
|
|
pwtest_bool_false(test_context.jitter_buffer.hold_back_mode);
|
|
send_packet(&test_context, 11);
|
|
pwtest_bool_false(test_context.jitter_buffer.hold_back_mode);
|
|
pwtest_int_eq(test_context.num_events, 2u);
|
|
CHECK_OUTPUT_PACKET_EVENT(10);
|
|
CHECK_OUTPUT_PACKET_EVENT(11);
|
|
|
|
/* Send 23. A gap of exactly 11 packets (= 1 past the number
|
|
* of slots) is produced -> jitter buffer signals an open
|
|
* ended gap of size equal to the number of slots. */
|
|
send_packet(&test_context, 23);
|
|
pwtest_bool_false(test_context.jitter_buffer.hold_back_mode);
|
|
pwtest_int_eq(test_context.num_events, 2u);
|
|
CHECK_LOST_PACKET_EVENT(12, 10u, true);
|
|
CHECK_OUTPUT_PACKET_EVENT(23);
|
|
|
|
teardown_test_context(&test_context);
|
|
}
|
|
|
|
return PWTEST_PASS;
|
|
}
|
|
|
|
PWTEST(rtp_jitter_buffer_test_full_window_invalidation_non_open_ended_gap)
|
|
{
|
|
/* Check what happens when hold-back mode is active, the
|
|
* valid seqnum window's maximum length is reached, and then,
|
|
* a packet with a sequence number that is far enough to
|
|
* overextend the window past its current length. This means
|
|
* that the shifting method (verified in earlier tests above)
|
|
* won't work - the window is shifted completely past its
|
|
* current range, so none of those slots remain valid,
|
|
* and must all be drained. Furthermore, it means that between
|
|
* the last seqnum of the old window and the first seqnum of
|
|
* the new window, there is a gap. The jitter buffer is expected
|
|
* to do the following:
|
|
*
|
|
* 1. Drain the entire current valid seqnum window
|
|
* 2. Reset the window to only contain the seqnum of the new packet
|
|
* 3. Signal the gap between the old and the new window
|
|
*
|
|
* Here, the window is shifted far enough that none of the
|
|
* original content can be retained, but not so far that
|
|
* the gap between the old and new windows becomes too large
|
|
* to fully cover via PLC. As a result, that gap is signaled
|
|
* as packet loss, but as a non-open-ended one. */
|
|
|
|
struct test_context test_context;
|
|
|
|
setup_test_context(&test_context, 10);
|
|
|
|
/* Establish hold-back mode with packets 10 and 12.
|
|
* Hold-back mode gets active because of the gap at 11. */
|
|
send_packet(&test_context, 10);
|
|
pwtest_bool_false(test_context.jitter_buffer.hold_back_mode);
|
|
send_packet(&test_context, 12);
|
|
pwtest_bool_true(test_context.jitter_buffer.hold_back_mode);
|
|
pwtest_int_eq(test_context.num_events, 1u);
|
|
CHECK_OUTPUT_PACKET_EVENT(10);
|
|
|
|
/* Send in packet 22. This would overextend the window. Shifting
|
|
* the current window moves it past packet 12, so the jitter
|
|
* buffer must be fully drained. Since afterwards, there is
|
|
* nothing left in the jitter buffer other than the new packet,
|
|
* the valid seqnum window length becomes 1, and starts at 22.
|
|
* This means that there are no gaps left, so the contents
|
|
* (in this case, just the packet 22) can be output immediately.
|
|
* Also, the gap between the old window and the new window goes
|
|
* from seqnum 13 (one past the end of the old window) to seqnum
|
|
* 21 (one before the new packet 22). 21-13+1 = 9, which is
|
|
* less than the jitter buffer capacity (which is 10), so that
|
|
* gap is announced as non-open-ended packet loss. */
|
|
send_packet(&test_context, 22);
|
|
pwtest_int_eq(test_context.num_events, 4u);
|
|
CHECK_LOST_PACKET_EVENT(11, 1u, false);
|
|
CHECK_OUTPUT_PACKET_EVENT(12);
|
|
CHECK_LOST_PACKET_EVENT(13, 9u, false);
|
|
CHECK_OUTPUT_PACKET_EVENT(22);
|
|
|
|
teardown_test_context(&test_context);
|
|
|
|
return PWTEST_PASS;
|
|
}
|
|
|
|
PWTEST(rtp_jitter_buffer_test_full_window_invalidation_open_ended_gap)
|
|
{
|
|
/* Check what happens when hold-back mode is active, the
|
|
* valid seqnum window's maximum length is reached, and then,
|
|
* a packet with a sequence number that is far enough to
|
|
* overextend the window past its current length. This means
|
|
* that the shifting method (verified in earlier tests above)
|
|
* won't work - the window is shifted completely past its
|
|
* current range, so none of those slots remain valid,
|
|
* and must all be drained. Furthermore, it means that between
|
|
* the last seqnum of the old window and the first seqnum of
|
|
* the new window, there is a gap. The jitter buffer is expected
|
|
* to do the following:
|
|
*
|
|
* 1. Drain the entire current valid seqnum window
|
|
* 2. Reset the window to only contain the seqnum of the new packet
|
|
* 3. Signal the gap between the old and the new window
|
|
*
|
|
* Here, the window is shifted far enough that none of the
|
|
* original content can be retained, and that that the gap
|
|
* between the old and new windows becomes too large
|
|
* to fully cover via PLC. As a result, that gap is signaled
|
|
* as packet loss, but as an open-ended one. */
|
|
|
|
struct test_context test_context;
|
|
|
|
setup_test_context(&test_context, 10);
|
|
|
|
/* Establish hold-back mode with packets 10 and 12.
|
|
* Hold-back mode gets active because of the gap at 11. */
|
|
send_packet(&test_context, 10);
|
|
pwtest_bool_false(test_context.jitter_buffer.hold_back_mode);
|
|
send_packet(&test_context, 12);
|
|
pwtest_bool_true(test_context.jitter_buffer.hold_back_mode);
|
|
pwtest_int_eq(test_context.num_events, 1u);
|
|
CHECK_OUTPUT_PACKET_EVENT(10);
|
|
|
|
/* Send in packet 400. This would overextend the window. Shifting
|
|
* the current window moves it past packet 12, so the jitter
|
|
* buffer must be fully drained. Since afterwards, there is
|
|
* nothing left in the jitter buffer other than the new packet,
|
|
* the valid seqnum window length becomes 1, and starts at 400.
|
|
* This means that there are no gaps left, so the contents
|
|
* (in this case, just the packet 400) can be output immediately.
|
|
* Also, the gap between the old window and the new window goes
|
|
* from seqnum 13 (one past the end of the old window) to seqnum
|
|
* 399 (one before the new packet 400). 399-13+1 = 387, which is
|
|
* far beyond the jitter buffer capacity (which is 10). That gap
|
|
* is then signaled as an open ended packet loss with maximum
|
|
* length 10, meaning that any PLC/fadeout measure must not
|
|
* exceed the length of 10 packets. (In non-open-ended signals,
|
|
* the length instead specifies the exact length of the gap.)
|
|
* This is done to avoid excessive PLC/fadeout calculations,
|
|
* like in this case, where it otherwise would force PLC for
|
|
* 387 packets. Callers are encouraged to apply fadeout as well
|
|
* to not have a hard cutoff after the maximum (10 packets here).*/
|
|
send_packet(&test_context, 400);
|
|
pwtest_int_eq(test_context.num_events, 4u);
|
|
CHECK_LOST_PACKET_EVENT(11, 1u, false);
|
|
CHECK_OUTPUT_PACKET_EVENT(12);
|
|
CHECK_LOST_PACKET_EVENT(13, 10u, true);
|
|
CHECK_OUTPUT_PACKET_EVENT(400);
|
|
|
|
teardown_test_context(&test_context);
|
|
|
|
return PWTEST_PASS;
|
|
}
|
|
|
|
PWTEST(rtp_jitter_buffer_test_timeout_drain)
|
|
{
|
|
/* Check what happens when hold-back mode is enabled and
|
|
* the gaps are not filled in time. It is expected that the
|
|
* jitter buffer's timeout expires and forcibly drains
|
|
* its contents. */
|
|
|
|
struct test_context test_context;
|
|
struct timespec ts;
|
|
|
|
setup_test_context(&test_context, 10);
|
|
|
|
/* Establish hold-back mode with packets 60 and 62.
|
|
* Hold-back mode gets active because of the gap at 61. */
|
|
send_packet(&test_context, 60);
|
|
pwtest_bool_false(test_context.jitter_buffer.hold_back_mode);
|
|
send_packet(&test_context, 62);
|
|
pwtest_bool_true(test_context.jitter_buffer.hold_back_mode);
|
|
pwtest_int_eq(test_context.num_events, 1u);
|
|
CHECK_OUTPUT_PACKET_EVENT(60);
|
|
|
|
/* The jitter buffer's timeout timer is configured to expire
|
|
* when the total duration of its capacity passes after the
|
|
* hold-back mode was enabled. In this test, capacity is 10
|
|
* packets, and each packet covers 10ms, then the total duration
|
|
* is 10*10ms = 100 ms, and that will also be the timeout of
|
|
* that timer, and the gap that was detected earlier will have
|
|
* armed that timer. Sleep for 50ms longer than its timeout
|
|
* duration to make sure it expires and thus provokes the
|
|
* draining of the jitter buffer. */
|
|
ts.tv_sec = 0;
|
|
ts.tv_nsec = 10 * TEST_PACKET_DURATION + 50 * SPA_NSEC_PER_MSEC;
|
|
nanosleep(&ts, NULL);
|
|
|
|
/* Iterate the loop to process the timer expiration. */
|
|
pw_loop_enter(test_context.loop);
|
|
pw_loop_iterate(test_context.loop, 0);
|
|
pw_loop_leave(test_context.loop);
|
|
|
|
/* After draining, the jitter buffer should be back to regular
|
|
* mode, just as if rtp_jitter_buffer_drain() had been called. */
|
|
pwtest_bool_false(test_context.jitter_buffer.hold_back_mode);
|
|
pwtest_int_eq(test_context.num_events, 2u);
|
|
pwtest_int_eq(test_context.jitter_buffer.last_seqnum, -1);
|
|
CHECK_LOST_PACKET_EVENT(61, 1u, false);
|
|
CHECK_OUTPUT_PACKET_EVENT(62);
|
|
|
|
/* Verify that regular mode is working properly by sending
|
|
* in packet 700. Since after draining, the last_seqnum is
|
|
* -1, a discontinuity in the sequence numbers is okay. */
|
|
send_packet(&test_context, 700);
|
|
pwtest_bool_false(test_context.jitter_buffer.hold_back_mode);
|
|
pwtest_int_eq(test_context.num_events, 1u);
|
|
CHECK_OUTPUT_PACKET_EVENT(700);
|
|
|
|
teardown_test_context(&test_context);
|
|
|
|
return PWTEST_PASS;
|
|
}
|
|
|
|
PWTEST_SUITE(pw_module_rtp_common_lib)
|
|
{
|
|
pwtest_add(rtp_jitter_buffer_test_consecutive_packets, PWTEST_NOARG);
|
|
pwtest_add(rtp_jitter_buffer_test_simple_reordering, PWTEST_NOARG);
|
|
pwtest_add(rtp_jitter_buffer_test_partial_output, PWTEST_NOARG);
|
|
pwtest_add(rtp_jitter_buffer_test_explicit_drain_in_regular_mode, PWTEST_NOARG);
|
|
pwtest_add(rtp_jitter_buffer_test_explicit_drain_in_hold_back_mode, PWTEST_NOARG);
|
|
pwtest_add(rtp_jitter_buffer_test_explicit_drain_coalesced_loss, PWTEST_NOARG);
|
|
pwtest_add(rtp_jitter_buffer_test_explicit_drain_with_seqnum_wraparound, PWTEST_NOARG);
|
|
pwtest_add(rtp_jitter_buffer_test_stale_packets_in_regular_mode, PWTEST_NOARG);
|
|
pwtest_add(rtp_jitter_buffer_test_stale_packets_in_hold_back_mode, PWTEST_NOARG);
|
|
pwtest_add(rtp_jitter_buffer_test_flush, PWTEST_NOARG);
|
|
pwtest_add(rtp_jitter_buffer_test_seqnum_wraparound_regular, PWTEST_NOARG);
|
|
pwtest_add(rtp_jitter_buffer_test_seqnum_wraparound_with_reordering, PWTEST_NOARG);
|
|
pwtest_add(rtp_jitter_buffer_test_overextension_single_gap_no_end_gap, PWTEST_NOARG);
|
|
pwtest_add(rtp_jitter_buffer_test_overextension_multiple_gaps_no_end_gap, PWTEST_NOARG);
|
|
pwtest_add(rtp_jitter_buffer_test_overextension_after_partial_output, PWTEST_NOARG);
|
|
pwtest_add(rtp_jitter_buffer_test_immediate_overextension_after_regular_mode, PWTEST_NOARG);
|
|
pwtest_add(rtp_jitter_buffer_test_full_window_invalidation_non_open_ended_gap, PWTEST_NOARG);
|
|
pwtest_add(rtp_jitter_buffer_test_immediate_overextension_after_regular_mode_threshold_open_closed_gap, PWTEST_NOARG);
|
|
pwtest_add(rtp_jitter_buffer_test_full_window_invalidation_open_ended_gap, PWTEST_NOARG);
|
|
pwtest_add(rtp_jitter_buffer_test_timeout_drain, PWTEST_NOARG);
|
|
|
|
return PWTEST_PASS;
|
|
}
|