rtpsession: Fix race when sending PLI, FIR and NACK packets

Calling rtp_session_send_rtcp before marking the source as requiring a
pli/fir/nack meant the rtcp_thread could be scheduled and start running
before the source was updated. This meant the request would not be sent
early but instead was transmitted with the next regular RTCP packet.

Add test for nack generation.
This commit is contained in:
John Bassett 2018-04-30 10:54:19 +02:00 committed by Nicolas Dufresne
parent 6b50d142f3
commit 74a74bfc99
2 changed files with 102 additions and 16 deletions

View file

@ -4432,11 +4432,6 @@ rtp_session_request_key_unit (RTPSession * sess, guint32 ssrc,
{
RTPSource *src;
if (!rtp_session_send_rtcp (sess, 5 * GST_SECOND)) {
GST_DEBUG ("FIR/PLI not sent");
return FALSE;
}
RTP_SESSION_LOCK (sess);
src = find_source (sess, ssrc);
if (src == NULL)
@ -4454,6 +4449,10 @@ rtp_session_request_key_unit (RTPSession * sess, guint32 ssrc,
}
RTP_SESSION_UNLOCK (sess);
if (!rtp_session_send_rtcp (sess, 5 * GST_SECOND)) {
GST_DEBUG ("FIR/PLI not sent early, sending with next regular RTCP");
}
return TRUE;
/* ERRORS */
@ -4481,11 +4480,6 @@ rtp_session_request_nack (RTPSession * sess, guint32 ssrc, guint16 seqnum,
{
RTPSource *source;
if (!rtp_session_send_rtcp (sess, max_delay)) {
GST_DEBUG ("NACK not sent");
return FALSE;
}
RTP_SESSION_LOCK (sess);
source = find_source (sess, ssrc);
if (source == NULL)
@ -4495,6 +4489,10 @@ rtp_session_request_nack (RTPSession * sess, guint32 ssrc, guint16 seqnum,
rtp_source_register_nack (source, seqnum);
RTP_SESSION_UNLOCK (sess);
if (!rtp_session_send_rtcp (sess, max_delay)) {
GST_DEBUG ("NACK not sent early, sending with next regular RTCP");
}
return TRUE;
/* ERRORS */

View file

@ -203,8 +203,11 @@ session_harness_produce_rtcp (SessionHarness * h, gint num_rtcp_packets)
{
/* due to randomness in rescheduling of RTCP timeout, we need to
keep cranking until we have the desired amount of packets */
while (gst_harness_buffers_in_queue (h->rtcp_h) < num_rtcp_packets)
while (gst_harness_buffers_in_queue (h->rtcp_h) < num_rtcp_packets) {
session_harness_crank_clock (h);
/* allow the rtcp-thread to settle before checking the queue */
gst_test_clock_wait_for_next_pending_id (h->testclock, NULL);
}
}
static void
@ -231,6 +234,24 @@ session_harness_force_key_unit (SessionHarness * h,
gst_event_new_custom (GST_EVENT_CUSTOM_UPSTREAM, s));
}
static void
session_harness_rtp_retransmission_request (SessionHarness * h,
guint ssrc, guint seqnum, guint delay, guint deadline, guint avg_rtt)
{
GstClockTime running_time = GST_CLOCK_TIME_NONE;
GstStructure *s = gst_structure_new ("GstRTPRetransmissionRequest",
"running-time", GST_TYPE_CLOCK_TIME, running_time,
"ssrc", G_TYPE_UINT, ssrc,
"seqnum", G_TYPE_UINT, seqnum,
"delay", G_TYPE_UINT, delay,
"deadline", G_TYPE_UINT, deadline,
"avg-rtt", G_TYPE_UINT, avg_rtt,
NULL);
gst_harness_push_upstream_event (h->recv_rtp_h,
gst_event_new_custom (GST_EVENT_CUSTOM_UPSTREAM, s));
}
GST_START_TEST (test_multiple_ssrc_rr)
{
SessionHarness *h = session_harness_new ();
@ -1052,16 +1073,16 @@ GST_START_TEST (test_request_pli)
fail_unless_equals_int (GST_FLOW_OK,
session_harness_recv_rtp (h, generate_test_buffer (0, 0x12345678)));
/* fix to make the test deterministic: We need to wait for the RTCP-thread
to have settled to ensure the key-unit will considered once released */
gst_test_clock_wait_for_next_pending_id (h->testclock, NULL);
/* Wait for first regular RTCP to be sent so that we are clear to send early RTCP */
session_harness_produce_rtcp (h, 1);
gst_buffer_unref (session_harness_pull_rtcp (h));
/* request PLI */
session_harness_force_key_unit (h, 0, 0x12345678, TEST_BUF_PT, NULL, NULL);
session_harness_produce_rtcp (h, 1);
/* PLI should be produced immediately as early RTCP is allowed. Pull buffer
without advancing the clock to ensure this is the case */
buf = session_harness_pull_rtcp (h);
fail_unless (gst_rtcp_buffer_validate (buf));
gst_rtcp_buffer_map (buf, GST_MAP_READ, &rtcp);
fail_unless_equals_int (3, gst_rtcp_buffer_get_packet_count (&rtcp));
@ -1349,6 +1370,72 @@ GST_START_TEST (test_change_sent_sdes)
GST_END_TEST;
GST_START_TEST (test_request_nack)
{
SessionHarness *h = session_harness_new ();
GstBuffer *buf;
GstRTCPBuffer rtcp = GST_RTCP_BUFFER_INIT;
GstRTCPPacket rtcp_packet;
guint8 *fci_data;
guint32 fci_length;
g_object_set (h->internal_session, "internal-ssrc", 0xDEADBEEF, NULL);
/* Receive a RTP buffer from the wire */
fail_unless_equals_int (GST_FLOW_OK,
session_harness_recv_rtp (h, generate_test_buffer (0, 0x12345678)));
/* Wait for first regular RTCP to be sent so that we are clear to send early RTCP */
session_harness_produce_rtcp (h, 1);
gst_buffer_unref (session_harness_pull_rtcp (h));
/* request NACK immediately */
session_harness_rtp_retransmission_request (h, 0x12345678, 1234, 0, 0, 0);
/* NACK should be produced immediately as early RTCP is allowed. Pull buffer
without advancing the clock to ensure this is the case */
buf = session_harness_pull_rtcp (h);
fail_unless (gst_rtcp_buffer_validate (buf));
gst_rtcp_buffer_map (buf, GST_MAP_READ, &rtcp);
fail_unless_equals_int (3, gst_rtcp_buffer_get_packet_count (&rtcp));
fail_unless (gst_rtcp_buffer_get_first_packet (&rtcp, &rtcp_packet));
/* first a Receiver Report */
fail_unless_equals_int (GST_RTCP_TYPE_RR,
gst_rtcp_packet_get_type (&rtcp_packet));
fail_unless (gst_rtcp_packet_move_to_next (&rtcp_packet));
/* then a SDES */
fail_unless_equals_int (GST_RTCP_TYPE_SDES,
gst_rtcp_packet_get_type (&rtcp_packet));
fail_unless (gst_rtcp_packet_move_to_next (&rtcp_packet));
/* and then our NACK */
fail_unless_equals_int (GST_RTCP_TYPE_RTPFB,
gst_rtcp_packet_get_type (&rtcp_packet));
fail_unless_equals_int (GST_RTCP_RTPFB_TYPE_NACK,
gst_rtcp_packet_fb_get_type (&rtcp_packet));
fail_unless_equals_int (0xDEADBEEF,
gst_rtcp_packet_fb_get_sender_ssrc (&rtcp_packet));
fail_unless_equals_int (0x12345678,
gst_rtcp_packet_fb_get_media_ssrc (&rtcp_packet));
fci_data = gst_rtcp_packet_fb_get_fci (&rtcp_packet);
fci_length =
gst_rtcp_packet_fb_get_fci_length (&rtcp_packet) * sizeof (guint32);
fail_unless_equals_int (4, fci_length);
fail_unless_equals_int (GST_READ_UINT32_BE (fci_data), 1234L << 16);
gst_rtcp_buffer_unmap (&rtcp);
gst_buffer_unref (buf);
session_harness_free (h);
}
GST_END_TEST;
static Suite *
rtpsession_suite (void)
{
@ -1366,6 +1453,7 @@ rtpsession_suite (void)
tcase_add_test (tc_chain, test_ssrc_collision_when_sending);
tcase_add_test (tc_chain, test_request_fir);
tcase_add_test (tc_chain, test_request_pli);
tcase_add_test (tc_chain, test_request_nack);
tcase_add_test (tc_chain, test_illegal_rtcp_fb_packet);
tcase_add_test (tc_chain, test_feedback_rtcp_race);
tcase_add_test (tc_chain, test_receive_regular_pli);