mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-11 19:06:33 +00:00
63c7a9ae43
Due to us not properly acknowleding the time when the last RTX was sent when scheduling a new one, it can easily happen that due to the packet you are requesting have a PTS that is slightly old (but not too old when adding the latency of the jitterbuffer), both its calculated second and third (etc.) timeout could already have passed. This would lead to a burst of RTX requests, which acts completely against its purpose, potentially spending a lot more bandwidth than needed. This has been properly reproduced in the test: test_rtx_not_bursting_requests The good news is that slightly re-thinking the logic concerning re-requesting RTX, made it a lot simpler to understand, and allows us to remove two members of the RtpTimer which no longer serves any purpose due to the refactoring. If desirable the whole "delay" concept can actually be removed completely from the timers, and simply just added to the timeout by the caller of the API. But that can be a change for a another time. The only external change (other than the improved behavior around bursting RTX) is that the "delay" field now stricly represents the delay between the PTS of the RTX-requested packet and the time it is requested on, whereas before this calculation was more about the theoretical calculated delay. This is visible in three other RTX-tests where the delay had to be adjusted slightly. I am confident however that this change is correct. Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-good/-/merge_requests/789>
326 lines
10 KiB
C
326 lines
10 KiB
C
/* GStreamer
|
|
*
|
|
* Copyright (C) 2019 Net Insight AB
|
|
* Author: Nicolas Dufresne <nicolas.dufresne@collabora.com>
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Library General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Library General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Library General Public
|
|
* License along with this library; if not, write to the
|
|
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
|
* Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
#include <gst/check/gstcheck.h>
|
|
#include "gst/rtpmanager/rtptimerqueue.h"
|
|
|
|
GST_START_TEST (test_timer_queue_set_timer)
|
|
{
|
|
RtpTimerQueue *queue = rtp_timer_queue_new ();
|
|
RtpTimer *timer10, *timer0;
|
|
|
|
rtp_timer_queue_set_timer (queue, RTP_TIMER_EXPECTED, 10,
|
|
1 * GST_SECOND, 2 * GST_SECOND, 5 * GST_SECOND, 0);
|
|
timer10 = rtp_timer_queue_find (queue, 10);
|
|
fail_unless (timer10);
|
|
fail_unless_equals_int (10, timer10->seqnum);
|
|
fail_unless_equals_int (RTP_TIMER_EXPECTED, timer10->type);
|
|
/* timer10->timeout = timerout + delay */
|
|
fail_unless_equals_uint64 (3 * GST_SECOND, timer10->timeout);
|
|
fail_unless_equals_uint64 (5 * GST_SECOND, timer10->duration);
|
|
fail_unless_equals_uint64 (1 * GST_SECOND, timer10->rtx_base);
|
|
fail_unless_equals_uint64 (GST_CLOCK_TIME_NONE, timer10->rtx_last);
|
|
fail_unless_equals_int (0, timer10->num_rtx_retry);
|
|
fail_unless_equals_int (0, timer10->num_rtx_received);
|
|
|
|
rtp_timer_queue_set_timer (queue, RTP_TIMER_LOST, 0,
|
|
0 * GST_SECOND, 2 * GST_SECOND, 0, 0);
|
|
timer0 = rtp_timer_queue_find (queue, 0);
|
|
fail_unless (timer0);
|
|
fail_unless_equals_int (0, timer0->seqnum);
|
|
fail_unless_equals_int (RTP_TIMER_LOST, timer0->type);
|
|
fail_unless_equals_uint64 (2 * GST_SECOND, timer0->timeout);
|
|
fail_unless_equals_uint64 (0, timer0->duration);
|
|
fail_unless_equals_uint64 (0, timer0->rtx_base);
|
|
fail_unless_equals_uint64 (GST_CLOCK_TIME_NONE, timer0->rtx_last);
|
|
fail_unless_equals_int (0, timer0->num_rtx_retry);
|
|
fail_unless_equals_int (0, timer0->num_rtx_received);
|
|
|
|
/* also check order while at it */
|
|
fail_unless (timer10->list.next == NULL);
|
|
fail_unless (timer10->list.prev == (GList *) timer0);
|
|
fail_unless (timer0->list.next == (GList *) timer10);
|
|
fail_unless (timer0->list.prev == NULL);
|
|
|
|
g_object_unref (queue);
|
|
}
|
|
|
|
GST_END_TEST;
|
|
|
|
GST_START_TEST (test_timer_queue_insert_head)
|
|
{
|
|
RtpTimerQueue *queue = rtp_timer_queue_new ();
|
|
RtpTimer *timer, *next, *prev;
|
|
|
|
rtp_timer_queue_set_deadline (queue, 1, -1, 0);
|
|
rtp_timer_queue_set_deadline (queue, 3, -1, 0);
|
|
rtp_timer_queue_set_deadline (queue, 2, -1, 0);
|
|
rtp_timer_queue_set_deadline (queue, 0, -1, 0);
|
|
|
|
timer = rtp_timer_queue_find (queue, 0);
|
|
fail_if (timer == NULL);
|
|
fail_unless_equals_int (0, timer->seqnum);
|
|
next = (RtpTimer *) timer->list.next;
|
|
prev = (RtpTimer *) timer->list.prev;
|
|
fail_unless (prev == NULL);
|
|
fail_if (next == NULL);
|
|
fail_unless_equals_int (1, next->seqnum);
|
|
|
|
timer = rtp_timer_queue_find (queue, 3);
|
|
fail_if (timer == NULL);
|
|
fail_unless_equals_int (3, timer->seqnum);
|
|
next = (RtpTimer *) timer->list.next;
|
|
prev = (RtpTimer *) timer->list.prev;
|
|
fail_if (prev == NULL);
|
|
fail_unless_equals_int (2, prev->seqnum);
|
|
fail_unless (next == NULL);
|
|
|
|
timer = rtp_timer_queue_find (queue, 2);
|
|
fail_if (timer == NULL);
|
|
fail_unless_equals_int (2, timer->seqnum);
|
|
next = (RtpTimer *) timer->list.next;
|
|
prev = (RtpTimer *) timer->list.prev;
|
|
fail_if (prev == NULL);
|
|
fail_if (next == NULL);
|
|
fail_unless_equals_int (1, prev->seqnum);
|
|
fail_unless_equals_int (3, next->seqnum);
|
|
|
|
timer = rtp_timer_queue_find (queue, 1);
|
|
fail_if (timer == NULL);
|
|
fail_unless_equals_int (1, timer->seqnum);
|
|
next = (RtpTimer *) timer->list.next;
|
|
prev = (RtpTimer *) timer->list.prev;
|
|
fail_if (prev == NULL);
|
|
fail_if (next == NULL);
|
|
fail_unless_equals_int (0, prev->seqnum);
|
|
fail_unless_equals_int (2, next->seqnum);
|
|
|
|
g_object_unref (queue);
|
|
}
|
|
|
|
GST_END_TEST;
|
|
|
|
GST_START_TEST (test_timer_queue_reschedule)
|
|
{
|
|
RtpTimerQueue *queue = rtp_timer_queue_new ();
|
|
RtpTimer *timer, *next, *prev;
|
|
|
|
rtp_timer_queue_set_deadline (queue, 3, 1 * GST_SECOND, 0);
|
|
rtp_timer_queue_set_deadline (queue, 1, 2 * GST_SECOND, 0);
|
|
rtp_timer_queue_set_deadline (queue, 2, 3 * GST_SECOND, 0);
|
|
rtp_timer_queue_set_deadline (queue, 0, 4 * GST_SECOND, 0);
|
|
|
|
timer = rtp_timer_queue_find (queue, 1);
|
|
fail_if (timer == NULL);
|
|
|
|
/* move to head, making sure seqnum order is respected */
|
|
rtp_timer_queue_set_deadline (queue, 1, 1 * GST_SECOND, 0);
|
|
next = (RtpTimer *) timer->list.next;
|
|
prev = (RtpTimer *) timer->list.prev;
|
|
fail_unless (prev == NULL);
|
|
fail_if (next == NULL);
|
|
fail_unless_equals_int (3, next->seqnum);
|
|
|
|
/* move head back */
|
|
rtp_timer_queue_set_deadline (queue, 1, 2 * GST_SECOND, 0);
|
|
next = (RtpTimer *) timer->list.next;
|
|
prev = (RtpTimer *) timer->list.prev;
|
|
fail_if (prev == NULL);
|
|
fail_if (next == NULL);
|
|
fail_unless_equals_int (3, prev->seqnum);
|
|
fail_unless_equals_int (2, next->seqnum);
|
|
|
|
/* move to tail */
|
|
timer = rtp_timer_queue_find (queue, 2);
|
|
fail_if (timer == NULL);
|
|
rtp_timer_queue_set_deadline (queue, 2, 4 * GST_SECOND, 0);
|
|
next = (RtpTimer *) timer->list.next;
|
|
prev = (RtpTimer *) timer->list.prev;
|
|
fail_if (prev == NULL);
|
|
fail_unless (next == NULL);
|
|
fail_unless_equals_int (0, prev->seqnum);
|
|
|
|
/* move tail back */
|
|
rtp_timer_queue_set_deadline (queue, 2, 3 * GST_SECOND, 0);
|
|
next = (RtpTimer *) timer->list.next;
|
|
prev = (RtpTimer *) timer->list.prev;
|
|
fail_if (prev == NULL);
|
|
fail_if (next == NULL);
|
|
fail_unless_equals_int (1, prev->seqnum);
|
|
fail_unless_equals_int (0, next->seqnum);
|
|
|
|
/* not moving toward head */
|
|
rtp_timer_queue_set_deadline (queue, 2, 2 * GST_SECOND, 0);
|
|
next = (RtpTimer *) timer->list.next;
|
|
prev = (RtpTimer *) timer->list.prev;
|
|
fail_if (prev == NULL);
|
|
fail_if (next == NULL);
|
|
fail_unless_equals_int (1, prev->seqnum);
|
|
fail_unless_equals_int (0, next->seqnum);
|
|
|
|
/* not moving toward tail */
|
|
rtp_timer_queue_set_deadline (queue, 2, 3 * GST_SECOND, 0);
|
|
next = (RtpTimer *) timer->list.next;
|
|
prev = (RtpTimer *) timer->list.prev;
|
|
fail_if (prev == NULL);
|
|
fail_if (next == NULL);
|
|
fail_unless_equals_int (1, prev->seqnum);
|
|
fail_unless_equals_int (0, next->seqnum);
|
|
|
|
/* inner move toward head */
|
|
rtp_timer_queue_set_deadline (queue, 2, GST_SECOND + GST_SECOND / 2, 0);
|
|
next = (RtpTimer *) timer->list.next;
|
|
prev = (RtpTimer *) timer->list.prev;
|
|
fail_if (prev == NULL);
|
|
fail_if (next == NULL);
|
|
fail_unless_equals_int (3, prev->seqnum);
|
|
fail_unless_equals_int (1, next->seqnum);
|
|
|
|
/* inner move toward tail */
|
|
rtp_timer_queue_set_deadline (queue, 2, 3 * GST_SECOND, 0);
|
|
next = (RtpTimer *) timer->list.next;
|
|
prev = (RtpTimer *) timer->list.prev;
|
|
fail_if (prev == NULL);
|
|
fail_if (next == NULL);
|
|
fail_unless_equals_int (1, prev->seqnum);
|
|
fail_unless_equals_int (0, next->seqnum);
|
|
|
|
g_object_unref (queue);
|
|
}
|
|
|
|
GST_END_TEST;
|
|
|
|
GST_START_TEST (test_timer_queue_pop_until)
|
|
{
|
|
RtpTimerQueue *queue = rtp_timer_queue_new ();
|
|
RtpTimer *timer;
|
|
|
|
rtp_timer_queue_set_deadline (queue, 2, 2 * GST_SECOND, 0);
|
|
rtp_timer_queue_set_deadline (queue, 1, 1 * GST_SECOND, 0);
|
|
rtp_timer_queue_set_deadline (queue, 0, -1, 0);
|
|
|
|
timer = rtp_timer_queue_pop_until (queue, 1 * GST_SECOND);
|
|
fail_if (timer == NULL);
|
|
fail_unless_equals_int (0, timer->seqnum);
|
|
rtp_timer_free (timer);
|
|
|
|
timer = rtp_timer_queue_pop_until (queue, 1 * GST_SECOND);
|
|
fail_if (timer == NULL);
|
|
fail_unless_equals_int (1, timer->seqnum);
|
|
rtp_timer_free (timer);
|
|
|
|
timer = rtp_timer_queue_pop_until (queue, 1 * GST_SECOND);
|
|
fail_unless (timer == NULL);
|
|
|
|
g_object_unref (queue);
|
|
}
|
|
|
|
GST_END_TEST;
|
|
|
|
GST_START_TEST (test_timer_queue_update_timer_seqnum)
|
|
{
|
|
RtpTimerQueue *queue = rtp_timer_queue_new ();
|
|
RtpTimer *timer;
|
|
|
|
rtp_timer_queue_set_deadline (queue, 2, 2 * GST_SECOND, 0);
|
|
|
|
timer = rtp_timer_queue_find (queue, 2);
|
|
fail_if (timer == NULL);
|
|
|
|
rtp_timer_queue_update_timer (queue, timer, 3, 3 * GST_SECOND, 0, 0, FALSE);
|
|
|
|
timer = rtp_timer_queue_find (queue, 2);
|
|
fail_unless (timer == NULL);
|
|
timer = rtp_timer_queue_find (queue, 3);
|
|
fail_if (timer == NULL);
|
|
|
|
fail_unless_equals_int (1, rtp_timer_queue_length (queue));
|
|
|
|
g_object_unref (queue);
|
|
}
|
|
|
|
GST_END_TEST;
|
|
|
|
GST_START_TEST (test_timer_queue_dup_timer)
|
|
{
|
|
RtpTimerQueue *queue = rtp_timer_queue_new ();
|
|
RtpTimer *timer;
|
|
|
|
rtp_timer_queue_set_deadline (queue, 2, 2 * GST_SECOND, 0);
|
|
|
|
timer = rtp_timer_queue_find (queue, 2);
|
|
fail_if (timer == NULL);
|
|
|
|
timer = rtp_timer_dup (timer);
|
|
timer->seqnum = 3;
|
|
rtp_timer_queue_insert (queue, timer);
|
|
|
|
fail_unless_equals_int (2, rtp_timer_queue_length (queue));
|
|
|
|
g_object_unref (queue);
|
|
}
|
|
|
|
GST_END_TEST;
|
|
|
|
GST_START_TEST (test_timer_queue_timer_offset)
|
|
{
|
|
RtpTimerQueue *queue = rtp_timer_queue_new ();
|
|
RtpTimer *timer;
|
|
|
|
rtp_timer_queue_set_timer (queue, RTP_TIMER_EXPECTED, 2, 2 * GST_SECOND,
|
|
GST_MSECOND, 0, GST_USECOND);
|
|
|
|
timer = rtp_timer_queue_find (queue, 2);
|
|
fail_if (timer == NULL);
|
|
fail_unless_equals_uint64 (2 * GST_SECOND + GST_MSECOND + GST_USECOND,
|
|
timer->timeout);
|
|
fail_unless_equals_int64 (GST_USECOND, timer->offset);
|
|
|
|
rtp_timer_queue_update_timer (queue, timer, 2, 3 * GST_SECOND,
|
|
2 * GST_MSECOND, 2 * GST_USECOND, FALSE);
|
|
fail_unless_equals_uint64 (3 * GST_SECOND + 2 * GST_MSECOND +
|
|
2 * GST_USECOND, timer->timeout);
|
|
fail_unless_equals_int64 (2 * GST_USECOND, timer->offset);
|
|
|
|
g_object_unref (queue);
|
|
}
|
|
|
|
GST_END_TEST;
|
|
|
|
static Suite *
|
|
rtptimerqueue_suite (void)
|
|
{
|
|
Suite *s = suite_create ("rtptimerqueue");
|
|
TCase *tc_chain = tcase_create ("general");
|
|
|
|
suite_add_tcase (s, tc_chain);
|
|
tcase_add_test (tc_chain, test_timer_queue_set_timer);
|
|
tcase_add_test (tc_chain, test_timer_queue_insert_head);
|
|
tcase_add_test (tc_chain, test_timer_queue_reschedule);
|
|
tcase_add_test (tc_chain, test_timer_queue_pop_until);
|
|
tcase_add_test (tc_chain, test_timer_queue_update_timer_seqnum);
|
|
tcase_add_test (tc_chain, test_timer_queue_dup_timer);
|
|
tcase_add_test (tc_chain, test_timer_queue_timer_offset);
|
|
|
|
return s;
|
|
}
|
|
|
|
GST_CHECK_MAIN (rtptimerqueue);
|