diff --git a/gst/rtpmanager/gstrtpjitterbuffer.c b/gst/rtpmanager/gstrtpjitterbuffer.c index b0fbef709e..9077219add 100644 --- a/gst/rtpmanager/gstrtpjitterbuffer.c +++ b/gst/rtpmanager/gstrtpjitterbuffer.c @@ -2096,6 +2096,30 @@ get_rtx_delay (GstRtpJitterBufferPrivate * priv) return delay; } +/* Check if packet with seqnum is already considered definitely lost by being + * part of a "lost timer" for multiple packets */ +static gboolean +already_lost (GstRtpJitterBuffer * jitterbuffer, guint16 seqnum) +{ + GstRtpJitterBufferPrivate *priv = jitterbuffer->priv; + gint i, len; + + len = priv->timers->len; + for (i = 0; i < len; i++) { + TimerData *test = &g_array_index (priv->timers, TimerData, i); + gint gap = gst_rtp_buffer_compare_seqnum (test->seqnum, seqnum); + + if (test->num > 1 && test->type == TIMER_TYPE_LOST && gap >= 0 && + gap < test->num) { + GST_DEBUG ("seqnum #%d already considered definitely lost (#%d->#%d)", + seqnum, test->seqnum, (test->seqnum + test->num - 1) & 0xffff); + return TRUE; + } + } + + return FALSE; +} + /* we just received a packet with seqnum and dts. * * First check for old seqnum that we are still expecting. If the gap with the @@ -2302,7 +2326,7 @@ calculate_expected (GstRtpJitterBuffer * jitterbuffer, guint32 expected, GST_DEBUG_OBJECT (jitterbuffer, "lost packets (%d, #%d->#%d) duration too large %" GST_TIME_FORMAT " > %" GST_TIME_FORMAT ", consider %u lost (%" GST_TIME_FORMAT ")", - gap, expected, seqnum, GST_TIME_ARGS (total_duration), + gap, expected, seqnum - 1, GST_TIME_ARGS (total_duration), GST_TIME_ARGS (priv->latency_ns), lost_packets, GST_TIME_ARGS (gap_time)); @@ -2814,6 +2838,9 @@ gst_rtp_jitter_buffer_chain (GstPad * pad, GstObject * parent, goto too_late; } + if (already_lost (jitterbuffer, seqnum)) + goto already_lost; + /* let's drop oldest packet if the queue is already full and drop-on-latency * is set. We can only do this when there actually is a latency. When no * latency is set, we just pump it in the queue and let the other end push it @@ -2931,6 +2958,14 @@ too_late: gst_buffer_unref (buffer); goto finished; } +already_lost: + { + GST_DEBUG_OBJECT (jitterbuffer, "Packet #%d too late as it was already " + "considered lost", seqnum); + priv->num_late++; + gst_buffer_unref (buffer); + goto finished; + } duplicate: { GST_DEBUG_OBJECT (jitterbuffer, "Duplicate packet #%d detected, dropping", diff --git a/tests/check/elements/rtpjitterbuffer.c b/tests/check/elements/rtpjitterbuffer.c index 28c04feb05..3b519e0881 100644 --- a/tests/check/elements/rtpjitterbuffer.c +++ b/tests/check/elements/rtpjitterbuffer.c @@ -459,7 +459,7 @@ get_rtp_seq_num (GstBuffer * buf) } static void -verify_lost_event (GstEvent * event, guint32 expected_seqnum, +verify_lost_event (GstEvent * event, guint16 expected_seqnum, GstClockTime expected_timestamp, GstClockTime expected_duration) { const GstStructure *s = gst_event_get_structure (event); @@ -1407,6 +1407,87 @@ GST_START_TEST (test_push_big_gap) GST_END_TEST; +typedef struct +{ + guint seqnum_offset; + guint late_buffer; +} TestLateArrivalInput; + +static const TestLateArrivalInput + test_considered_lost_packet_in_large_gap_arrives_input[] = { + {0, 1}, {0, 2}, {65535, 1}, {65535, 2}, {65534, 1}, {65534, 2} +}; + +GST_START_TEST (test_considered_lost_packet_in_large_gap_arrives) +{ + GstHarness *h = gst_harness_new ("rtpjitterbuffer"); + GstTestClock *testclock; + GstClockID id; + GstBuffer *buffer; + gint jb_latency_ms = 20; + GstEvent *event; + const TestLateArrivalInput *test_input = + &test_considered_lost_packet_in_large_gap_arrives_input[__i__]; + guint seq_offset = test_input->seqnum_offset; + guint late_buffer = test_input->late_buffer; + + gst_harness_set_src_caps (h, generate_caps ()); + testclock = gst_harness_get_testclock (h); + g_object_set (h->element, "do-lost", TRUE, "latency", jb_latency_ms, NULL); + + /* first push buffer 0 */ + fail_unless_equals_int (GST_FLOW_OK, + gst_harness_push (h, generate_test_buffer_full (0 * PCMU_BUF_DURATION, + TRUE, 0 + seq_offset, 0 * PCMU_RTP_TS_DURATION))); + fail_unless (gst_harness_crank_single_clock_wait (h)); + gst_buffer_unref (gst_harness_pull (h)); + + /* drop GstEventStreamStart & GstEventCaps & GstEventSegment */ + for (gint i = 0; i < 3; i++) + gst_event_unref (gst_harness_pull_event (h)); + + /* hop over 3 packets, and push buffer 4 (gap of 3) */ + fail_unless_equals_int (GST_FLOW_OK, + gst_harness_push (h, generate_test_buffer_full (4 * PCMU_BUF_DURATION, + TRUE, 4 + seq_offset, 4 * PCMU_RTP_TS_DURATION))); + + /* the jitterbuffer should be waiting for the timeout of a "large gap timer" + * for buffer 1 and 2 */ + gst_test_clock_wait_for_next_pending_id (testclock, &id); + fail_unless_equals_uint64 (1 * PCMU_BUF_DURATION + + jb_latency_ms * GST_MSECOND, gst_clock_id_get_time (id)); + gst_clock_id_unref (id); + + /* now buffer 1 sneaks in before the lost event for buffer 1 and 2 is + * processed */ + fail_unless_equals_int (GST_FLOW_OK, + gst_harness_push (h, + generate_test_buffer_full (late_buffer * PCMU_BUF_DURATION, TRUE, + late_buffer + seq_offset, late_buffer * PCMU_RTP_TS_DURATION))); + + /* time out for lost packets 1 and 2 (one event, double duration) */ + fail_unless (gst_harness_crank_single_clock_wait (h)); + event = gst_harness_pull_event (h); + verify_lost_event (event, 1 + seq_offset, 1 * PCMU_BUF_DURATION, + 2 * PCMU_BUF_DURATION); + + /* time out for lost packets 3 */ + fail_unless (gst_harness_crank_single_clock_wait (h)); + event = gst_harness_pull_event (h); + verify_lost_event (event, 3 + seq_offset, 3 * PCMU_BUF_DURATION, + 1 * PCMU_BUF_DURATION); + + /* buffer 4 is pushed as normal */ + buffer = gst_harness_pull (h); + fail_unless_equals_int ((4 + seq_offset) & 0xffff, get_rtp_seq_num (buffer)); + gst_buffer_unref (buffer); + + gst_object_unref (testclock); + gst_harness_teardown (h); +} + +GST_END_TEST; + static Suite * rtpjitterbuffer_suite (void) { @@ -1431,6 +1512,10 @@ rtpjitterbuffer_suite (void) tcase_add_test (tc_chain, test_dts_gap_larger_than_latency); tcase_add_test (tc_chain, test_push_big_gap); + tcase_add_loop_test (tc_chain, + test_considered_lost_packet_in_large_gap_arrives, 0, + G_N_ELEMENTS (test_considered_lost_packet_in_large_gap_arrives_input)); + return s; }