tests: add retransmission jitterbuffer test

Store both DTS and PTS on buffers.
Make a queue for srcpad events.
Activate pads after linking so that we don't get RECONFIGURE events.
Add test for retransmission.
This commit is contained in:
Wim Taymans 2013-09-19 14:14:52 +02:00
parent fd6c57cf10
commit ee817363c7

View file

@ -336,8 +336,10 @@ typedef struct
GstPad *test_sink_pad, *test_src_pad;
GstClock *clock;
GAsyncQueue *buf_queue;
GAsyncQueue *event_queue;
GAsyncQueue *sink_event_queue;
GAsyncQueue *src_event_queue;
gint lost_event_count;
gint rtx_event_count;
} TestData;
static GstCaps *
@ -361,7 +363,8 @@ generate_test_buffer (GstClockTime gst_ts,
GstRTPBuffer rtp = GST_RTP_BUFFER_INIT;
buf = gst_rtp_buffer_new_allocate (payload_size, 0, 0);
GST_BUFFER_TIMESTAMP (buf) = gst_ts;
GST_BUFFER_DTS (buf) = gst_ts;
GST_BUFFER_PTS (buf) = gst_ts;
gst_rtp_buffer_map (buf, GST_MAP_READWRITE, &rtp);
gst_rtp_buffer_set_payload_type (&rtp, pcmu_payload_type);
@ -398,7 +401,24 @@ test_sink_pad_event_cb (GstPad * pad, GstObject * parent, GstEvent * event)
if (strcmp (gst_structure_get_name (structure), "GstRTPPacketLost") == 0)
data->lost_event_count++;
g_async_queue_push (data->event_queue, event);
g_async_queue_push (data->sink_event_queue, event);
return TRUE;
}
static gboolean
test_src_pad_event_cb (GstPad * pad, GstObject * parent, GstEvent * event)
{
TestData *data = gst_pad_get_element_private (pad);
const GstStructure *structure = gst_event_get_structure (event);
GST_DEBUG ("got event %" GST_PTR_FORMAT, event);
if (structure
&& strcmp (gst_structure_get_name (structure),
"GstRTPRetransmissionRequest") == 0)
data->rtx_event_count++;
g_async_queue_push (data->src_event_queue, event);
return TRUE;
}
@ -422,33 +442,39 @@ setup_testharness (TestData * data)
g_assert_cmpint (gst_element_set_state (data->jitter_buffer,
GST_STATE_PLAYING), !=, GST_STATE_CHANGE_FAILURE);
/* set up the buf and event queues */
data->buf_queue =
g_async_queue_new_full ((GDestroyNotify) gst_mini_object_unref);
data->sink_event_queue =
g_async_queue_new_full ((GDestroyNotify) gst_mini_object_unref);
data->src_event_queue =
g_async_queue_new_full ((GDestroyNotify) gst_mini_object_unref);
data->lost_event_count = 0;
data->rtx_event_count = 0;
/* link in the test source-pad */
data->test_src_pad = gst_pad_new ("src", GST_PAD_SRC);
gst_pad_set_element_private (data->test_src_pad, data);
gst_pad_set_event_function (data->test_src_pad, test_src_pad_event_cb);
jb_sink_pad = gst_element_get_static_pad (data->jitter_buffer, "sink");
g_assert_cmpint (gst_pad_link (data->test_src_pad, jb_sink_pad), ==,
GST_PAD_LINK_OK);
g_assert (gst_pad_set_active (data->test_src_pad, TRUE));
gst_object_unref (jb_sink_pad);
/* link in the test sink-pad */
data->test_sink_pad = gst_pad_new ("sink", GST_PAD_SINK);
gst_pad_set_element_private (data->test_sink_pad, data);
gst_pad_set_caps (data->test_sink_pad, generate_caps ());
gst_pad_set_chain_function (data->test_sink_pad, test_sink_pad_chain_cb);
gst_pad_set_event_function (data->test_sink_pad, test_sink_pad_event_cb);
jb_src_pad = gst_element_get_static_pad (data->jitter_buffer, "src");
g_assert_cmpint (gst_pad_link (jb_src_pad, data->test_sink_pad), ==,
GST_PAD_LINK_OK);
g_assert (gst_pad_set_active (data->test_sink_pad, TRUE));
gst_object_unref (jb_src_pad);
/* set up the buf and event queues */
data->buf_queue =
g_async_queue_new_full ((GDestroyNotify) gst_mini_object_unref);
data->event_queue =
g_async_queue_new_full ((GDestroyNotify) gst_mini_object_unref);
data->lost_event_count = 0;
gst_pad_set_element_private (data->test_sink_pad, data);
g_assert (gst_pad_set_active (data->test_src_pad, TRUE));
g_assert (gst_pad_set_active (data->test_sink_pad, TRUE));
gst_segment_init (&seg, GST_FORMAT_TIME);
@ -457,7 +483,7 @@ setup_testharness (TestData * data)
gst_pad_set_caps (data->test_src_pad, generate_caps ());
gst_pad_push_event (data->test_src_pad, gst_event_new_segment (&seg));
while ((obj = g_async_queue_try_pop (data->event_queue)))
while ((obj = g_async_queue_try_pop (data->sink_event_queue)))
gst_mini_object_unref (obj);
}
@ -482,8 +508,10 @@ destroy_testharness (TestData * data)
g_async_queue_unref (data->buf_queue);
data->buf_queue = NULL;
g_async_queue_unref (data->event_queue);
data->event_queue = NULL;
g_async_queue_unref (data->sink_event_queue);
data->sink_event_queue = NULL;
g_async_queue_unref (data->src_event_queue);
data->src_event_queue = NULL;
data->lost_event_count = 0;
}
@ -499,6 +527,7 @@ verify_lost_event (GstEvent * event, guint32 expected_seqnum,
GstClockTime timestamp;
GstClockTime duration;
gboolean late;
g_assert (gst_structure_get_uint (s, "seqnum", &seqnum));
value = gst_structure_get_value (s, "timestamp");
@ -517,6 +546,37 @@ verify_lost_event (GstEvent * event, guint32 expected_seqnum,
g_assert (late == expected_late);
}
static void
verify_rtx_event (GstEvent * event, guint32 expected_seqnum,
GstClockTime expected_timestamp, guint expected_delay,
GstClockTime expected_spacing)
{
const GstStructure *s = gst_event_get_structure (event);
const GValue *value;
guint32 seqnum;
GstClockTime timestamp, spacing;
guint delay;
g_assert (gst_structure_get_uint (s, "seqnum", &seqnum));
value = gst_structure_get_value (s, "running-time");
g_assert (value && G_VALUE_HOLDS_UINT64 (value));
timestamp = g_value_get_uint64 (value);
value = gst_structure_get_value (s, "delay");
g_assert (value && G_VALUE_HOLDS_UINT (value));
delay = g_value_get_uint (value);
value = gst_structure_get_value (s, "packet-spacing");
g_assert (value && G_VALUE_HOLDS_UINT64 (value));
spacing = g_value_get_uint64 (value);
g_assert_cmpint (seqnum, ==, expected_seqnum);
g_assert_cmpint (timestamp, ==, expected_timestamp);
g_assert_cmpint (delay, ==, expected_delay);
g_assert_cmpint (spacing, ==, expected_spacing);
}
GST_START_TEST (test_only_one_lost_event_on_large_gaps)
{
TestData data;
@ -578,7 +638,7 @@ GST_START_TEST (test_only_one_lost_event_on_large_gaps)
g_assert (id == test_id);
/* we should now receive a packet-lost-event for buffers 1 through 489 */
out_event = g_async_queue_timeout_pop (data.event_queue, timeout);
out_event = g_async_queue_timeout_pop (data.sink_event_queue, timeout);
g_assert (out_event != NULL);
g_assert_cmpint (data.lost_event_count, ==, 1);
verify_lost_event (out_event, 1, 1 * GST_MSECOND * 20, GST_MSECOND * 20 * 490,
@ -673,7 +733,7 @@ GST_START_TEST (test_two_lost_one_arrives_in_time)
== id);
/* we should now receive a packet-lost-event for buffer 3 */
out_event = g_async_queue_timeout_pop (data.event_queue, timeout);
out_event = g_async_queue_timeout_pop (data.sink_event_queue, timeout);
g_assert (out_event != NULL);
g_assert_cmpint (data.lost_event_count, ==, 1);
verify_lost_event (out_event, 3, 3 * GST_MSECOND * 20, GST_MSECOND * 20,
@ -757,7 +817,7 @@ GST_START_TEST (test_late_packets_still_makes_lost_events)
g_assert_cmpint (gst_pad_push (data.test_src_pad, in_buf), ==, GST_FLOW_OK);
/* we should now receive a packet-lost-event for buffer 3 and 4 */
out_event = g_async_queue_timeout_pop (data.event_queue, timeout);
out_event = g_async_queue_timeout_pop (data.sink_event_queue, timeout);
g_assert (out_event != NULL);
g_assert_cmpint (data.lost_event_count, ==, 1);
verify_lost_event (out_event, 3, 3 * GST_MSECOND * 20, GST_MSECOND * 20 * 2,
@ -824,11 +884,11 @@ GST_START_TEST (test_all_packets_are_timestamped_zero)
g_assert_cmpint (gst_pad_push (data.test_src_pad, in_buf), ==, GST_FLOW_OK);
/* we should now receive a packet-lost-event for buffer 3 and 4 */
out_event = g_async_queue_timeout_pop (data.event_queue, timeout);
out_event = g_async_queue_timeout_pop (data.sink_event_queue, timeout);
g_assert (out_event != NULL);
verify_lost_event (out_event, 3, 0, 0, FALSE);
out_event = g_async_queue_timeout_pop (data.event_queue, timeout);
out_event = g_async_queue_timeout_pop (data.sink_event_queue, timeout);
g_assert (out_event != NULL);
verify_lost_event (out_event, 4, 0, 0, FALSE);
@ -850,6 +910,85 @@ GST_START_TEST (test_all_packets_are_timestamped_zero)
GST_END_TEST;
GST_START_TEST (test_rtx_expected_next)
{
TestData data;
GstClockID id, tid;
GstBuffer *in_buf, *out_buf;
GstEvent *out_event;
gint jb_latency_ms = 200;
GstClockTime timeout = 20 * G_USEC_PER_SEC;
setup_testharness (&data);
g_object_set (data.jitter_buffer, "do-retransmission", TRUE, NULL);
g_object_set (data.jitter_buffer, "latency", jb_latency_ms, NULL);
g_object_set (data.jitter_buffer, "rtx-retry-period", 120, NULL);
gst_test_clock_set_time (GST_TEST_CLOCK (data.clock), 0);
/* push the first buffer in */
in_buf = generate_test_buffer (0 * GST_MSECOND, TRUE, 0, 0);
g_assert_cmpint (gst_pad_push (data.test_src_pad, in_buf), ==, GST_FLOW_OK);
gst_test_clock_set_time (GST_TEST_CLOCK (data.clock), 20 * GST_MSECOND);
/* put second buffer, the jitterbuffer should now know that the packet spacing
* is 20ms and should ask for retransmission of seqnum 2 in 20ms */
in_buf = generate_test_buffer (20 * GST_MSECOND, TRUE, 1, 160);
g_assert_cmpint (gst_pad_push (data.test_src_pad, in_buf), ==, GST_FLOW_OK);
gst_test_clock_wait_for_next_pending_id (GST_TEST_CLOCK (data.clock), &id);
gst_test_clock_set_time (GST_TEST_CLOCK (data.clock), 60 * GST_MSECOND);
g_assert (gst_test_clock_process_next_clock_id (GST_TEST_CLOCK (data.clock))
== id);
out_event = g_async_queue_timeout_pop (data.src_event_queue, timeout);
g_assert (out_event != NULL);
verify_rtx_event (out_event, 2, 40 * GST_MSECOND, 20, 20 * GST_MSECOND);
/* now we wait for the next timeout */
gst_test_clock_wait_for_next_pending_id (GST_TEST_CLOCK (data.clock), &id);
gst_test_clock_set_time (GST_TEST_CLOCK (data.clock), 100 * GST_MSECOND);
tid = gst_test_clock_process_next_clock_id (GST_TEST_CLOCK (data.clock));
g_assert (id == tid);
out_event = g_async_queue_timeout_pop (data.src_event_queue, timeout);
g_assert (out_event != NULL);
verify_rtx_event (out_event, 2, 40 * GST_MSECOND, 60, 20 * GST_MSECOND);
gst_test_clock_wait_for_next_pending_id (GST_TEST_CLOCK (data.clock), &id);
gst_test_clock_set_time (GST_TEST_CLOCK (data.clock), 140 * GST_MSECOND);
tid = gst_test_clock_process_next_clock_id (GST_TEST_CLOCK (data.clock));
g_assert (id == tid);
out_event = g_async_queue_timeout_pop (data.src_event_queue, timeout);
g_assert (out_event != NULL);
verify_rtx_event (out_event, 2, 40 * GST_MSECOND, 100, 20 * GST_MSECOND);
gst_test_clock_wait_for_next_pending_id (GST_TEST_CLOCK (data.clock), &id);
gst_test_clock_set_time (GST_TEST_CLOCK (data.clock), 200 * GST_MSECOND);
tid = gst_test_clock_process_next_clock_id (GST_TEST_CLOCK (data.clock));
g_assert (id == tid);
out_buf = g_async_queue_timeout_pop (data.buf_queue, timeout);
g_assert (out_buf != NULL);
gst_test_clock_wait_for_next_pending_id (GST_TEST_CLOCK (data.clock), &id);
gst_test_clock_set_time (GST_TEST_CLOCK (data.clock), 260 * GST_MSECOND);
g_assert (gst_test_clock_process_next_clock_id (GST_TEST_CLOCK (data.clock))
== id);
/* we should now receive a packet-lost-event for buffer 2 */
out_event = g_async_queue_timeout_pop (data.sink_event_queue, timeout);
g_assert (out_event != NULL);
verify_lost_event (out_event, 2, 40 * GST_MSECOND, 20 * GST_MSECOND, FALSE);
destroy_testharness (&data);
}
GST_END_TEST;
static Suite *
rtpjitterbuffer_suite (void)
{
@ -865,6 +1004,7 @@ rtpjitterbuffer_suite (void)
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_test (tc_chain, test_rtx_expected_next);
return s;
}