diff --git a/gst/rtpmanager/gstrtpjitterbuffer.c b/gst/rtpmanager/gstrtpjitterbuffer.c index 8d300a9288..afedd03e1d 100644 --- a/gst/rtpmanager/gstrtpjitterbuffer.c +++ b/gst/rtpmanager/gstrtpjitterbuffer.c @@ -333,6 +333,8 @@ struct _GstRtpJitterBufferPrivate GstBuffer *last_sr; /* some accounting */ + guint64 num_pushed; + guint64 num_lost; guint64 num_late; guint64 num_duplicates; guint64 num_rtx_requests; @@ -710,6 +712,34 @@ gst_rtp_jitter_buffer_class_init (GstRtpJitterBufferClass * klass) * * * #guint64 + * "num-pushed": + * the number of packets pushed out. + * + * + * + * + * #guint64 + * "num-lost": + * the number of packets considered lost. + * + * + * + * + * #guint64 + * "num-late": + * the number of packets arriving too late. + * + * + * + * + * #guint64 + * "num-duplicates": + * the number of duplicate packets. + * + * + * + * + * #guint64 * "rtx-count": * the number of retransmissions requested. * @@ -3165,6 +3195,7 @@ pop_and_push_next (GstRtpJitterBuffer * jitterbuffer, guint seqnum) "Pushing buffer %d, dts %" GST_TIME_FORMAT ", pts %" GST_TIME_FORMAT, seqnum, GST_TIME_ARGS (GST_BUFFER_DTS (outbuf)), GST_TIME_ARGS (GST_BUFFER_PTS (outbuf))); + priv->num_pushed++; result = gst_pad_push (priv->srcpad, outbuf); JBUF_LOCK_CHECK (priv, out_flushing); @@ -3446,7 +3477,7 @@ do_lost_timeout (GstRtpJitterBuffer * jitterbuffer, TimerData * timer, else GST_DEBUG_OBJECT (jitterbuffer, "Packet #%d lost", seqnum); - priv->num_late += lost_packets; + priv->num_lost += lost_packets; priv->num_rtx_failed += num_rtx_retry; next_in_seqnum = (seqnum + lost_packets) & 0xffff; @@ -4340,15 +4371,21 @@ gst_rtp_jitter_buffer_get_property (GObject * object, static GstStructure * gst_rtp_jitter_buffer_create_stats (GstRtpJitterBuffer * jbuf) { + GstRtpJitterBufferPrivate *priv = jbuf->priv; GstStructure *s; - JBUF_LOCK (jbuf->priv); + JBUF_LOCK (priv); s = gst_structure_new ("application/x-rtp-jitterbuffer-stats", - "rtx-count", G_TYPE_UINT64, jbuf->priv->num_rtx_requests, - "rtx-success-count", G_TYPE_UINT64, jbuf->priv->num_rtx_success, - "rtx-per-packet", G_TYPE_DOUBLE, jbuf->priv->avg_rtx_num, - "rtx-rtt", G_TYPE_UINT64, jbuf->priv->avg_rtx_rtt, NULL); - JBUF_UNLOCK (jbuf->priv); + "num-pushed", G_TYPE_UINT64, priv->num_pushed, + "num-lost", G_TYPE_UINT64, priv->num_lost, + "num-late", G_TYPE_UINT64, priv->num_late, + "num-duplicates", G_TYPE_UINT64, priv->num_duplicates, + "avg-jitter", G_TYPE_UINT64, priv->avg_jitter, + "rtx-count", G_TYPE_UINT64, priv->num_rtx_requests, + "rtx-success-count", G_TYPE_UINT64, priv->num_rtx_success, + "rtx-per-packet", G_TYPE_DOUBLE, priv->avg_rtx_num, + "rtx-rtt", G_TYPE_UINT64, priv->avg_rtx_rtt, NULL); + JBUF_UNLOCK (priv); return s; } diff --git a/tests/check/elements/rtpjitterbuffer.c b/tests/check/elements/rtpjitterbuffer.c index 10810fa39f..5cb8510c82 100644 --- a/tests/check/elements/rtpjitterbuffer.c +++ b/tests/check/elements/rtpjitterbuffer.c @@ -520,6 +520,29 @@ verify_rtx_event (GstEvent * event, guint32 expected_seqnum, gst_event_unref (event); } +static gboolean +verify_jb_stats (GstElement * jb, GstStructure * expected) +{ + gboolean ret; + GstStructure *actual; + g_object_get (jb, "stats", &actual, NULL); + + ret = gst_structure_is_subset (actual, expected); + + if (!ret) { + gchar *e_str = gst_structure_to_string (expected); + gchar *a_str = gst_structure_to_string (actual); + fail_unless (ret, "%s is not a subset of %s", e_str, a_str); + g_free (e_str); + g_free (a_str); + } + + gst_structure_free (expected); + gst_structure_free (actual); + + return ret; +} + GST_START_TEST (test_only_one_lost_event_on_large_gaps) { GstHarness *h = gst_harness_new ("rtpjitterbuffer"); @@ -604,6 +627,10 @@ GST_START_TEST (test_only_one_lost_event_on_large_gaps) fail_unless_equals_uint64 (10 * GST_SECOND, GST_BUFFER_PTS (out_buf)); gst_buffer_unref (out_buf); + fail_unless (verify_jb_stats (h->element, + gst_structure_new ("application/x-rtp-jitterbuffer-stats", + "num-lost", G_TYPE_UINT64, (guint64) 499, NULL))); + gst_object_unref (testclock); gst_harness_teardown (h); } @@ -688,6 +715,11 @@ GST_START_TEST (test_two_lost_one_arrives_in_time) fail_unless_equals_int (5, get_rtp_seq_num (out_buf)); gst_buffer_unref (out_buf); + fail_unless (verify_jb_stats (h->element, + gst_structure_new ("application/x-rtp-jitterbuffer-stats", + "num-pushed", G_TYPE_UINT64, (guint64) 5, + "num-lost", G_TYPE_UINT64, (guint64) 1, NULL))); + gst_object_unref (testclock); gst_harness_teardown (h); } @@ -749,12 +781,76 @@ GST_START_TEST (test_late_packets_still_makes_lost_events) fail_unless_equals_int (5, get_rtp_seq_num (out_buf)); gst_buffer_unref (out_buf); + fail_unless (verify_jb_stats (h->element, + gst_structure_new ("application/x-rtp-jitterbuffer-stats", + "num-pushed", G_TYPE_UINT64, (guint64) 4, + "num-lost", G_TYPE_UINT64, (guint64) 2, NULL))); + gst_object_unref (testclock); gst_harness_teardown (h); } GST_END_TEST; + +GST_START_TEST (test_num_late_when_considered_lost_arrives) +{ + GstHarness *h = gst_harness_new ("rtpjitterbuffer"); + gboolean do_lost = __i__ != 0; + + gst_harness_set_src_caps (h, generate_caps ()); + g_object_set (h->element, "do-lost", do_lost, "latency", 100, NULL); + + /* push the first buffer through */ + fail_unless_equals_int (GST_FLOW_OK, + gst_harness_push (h, generate_test_buffer (0))); + /* sync on the first packet */ + gst_harness_crank_single_clock_wait (h); + + /* gap of 1 */ + fail_unless_equals_int (GST_FLOW_OK, + gst_harness_push (h, generate_test_buffer (2))); + + /* crank to output lost-event */ + gst_harness_crank_single_clock_wait (h); + + if (do_lost) { + /* drop GstEventStreamStart & GstEventCaps & GstEventSegment */ + for (gint i = 0; i < 3; i++) + gst_event_unref (gst_harness_pull_event (h)); + + /* we should now receive packet-lost-events for buffer 1 */ + verify_lost_event (gst_harness_pull_event (h), + 1, 1 * PCMU_BUF_DURATION, PCMU_BUF_DURATION); + } + + /* pull out buffers to ensure determinism */ + gst_buffer_unref (gst_harness_pull (h)); + gst_buffer_unref (gst_harness_pull (h)); + + /* we have one lost packet in the stats */ + fail_unless (verify_jb_stats (h->element, + gst_structure_new ("application/x-rtp-jitterbuffer-stats", + "num-pushed", G_TYPE_UINT64, (guint64) 2, + "num-lost", G_TYPE_UINT64, (guint64) 1, + "num-late", G_TYPE_UINT64, (guint64) 0, NULL))); + + /* buffer 1 now arrives (too late) */ + fail_unless_equals_int (GST_FLOW_OK, + gst_harness_push (h, generate_test_buffer (1))); + + /* and this increments num-late */ + fail_unless (verify_jb_stats (h->element, + gst_structure_new ("application/x-rtp-jitterbuffer-stats", + "num-pushed", G_TYPE_UINT64, (guint64) 2, + "num-lost", G_TYPE_UINT64, (guint64) 1, + "num-late", G_TYPE_UINT64, (guint64) 1, NULL))); + + gst_harness_teardown (h); +} + +GST_END_TEST; + GST_START_TEST (test_all_packets_are_timestamped_zero) { GstHarness *h = gst_harness_new ("rtpjitterbuffer"); @@ -809,6 +905,11 @@ GST_START_TEST (test_all_packets_are_timestamped_zero) fail_unless_equals_int (5, get_rtp_seq_num (out_buf)); gst_buffer_unref (out_buf); + fail_unless (verify_jb_stats (h->element, + gst_structure_new ("application/x-rtp-jitterbuffer-stats", + "num-pushed", G_TYPE_UINT64, (guint64) 4, + "num-lost", G_TYPE_UINT64, (guint64) 2, NULL))); + gst_object_unref (testclock); gst_harness_teardown (h); } @@ -903,8 +1004,6 @@ GST_START_TEST (test_rtx_two_missing) GstEvent *out_event; gint jb_latency_ms = 200; const GstClockTime rtx_retry_timeout = 40 * GST_MSECOND; - GstStructure *rtx_stats; - const GValue *rtx_stat; gint i; gst_harness_set_src_caps (h, generate_caps ()); @@ -1010,17 +1109,12 @@ GST_START_TEST (test_rtx_two_missing) so no events in the queue */ fail_unless_equals_int (0, gst_harness_events_in_queue (h)); - g_object_get (h->element, "stats", &rtx_stats, NULL); - - rtx_stat = gst_structure_get_value (rtx_stats, "rtx-count"); - fail_unless_equals_uint64 (5, g_value_get_uint64 (rtx_stat)); - - rtx_stat = gst_structure_get_value (rtx_stats, "rtx-success-count"); - fail_unless_equals_uint64 (1, g_value_get_uint64 (rtx_stat)); - - rtx_stat = gst_structure_get_value (rtx_stats, "rtx-rtt"); - fail_unless_equals_uint64 (0, g_value_get_uint64 (rtx_stat)); - gst_structure_free (rtx_stats); + fail_unless (verify_jb_stats (h->element, + gst_structure_new ("application/x-rtp-jitterbuffer-stats", + "num-lost", G_TYPE_UINT64, (guint64) 1, + "rtx-count", G_TYPE_UINT64, (guint64) 5, + "rtx-success-count", G_TYPE_UINT64, (guint64) 1, + "rtx-rtt", G_TYPE_UINT64, (guint64) 0, NULL))); gst_object_unref (testclock); gst_harness_teardown (h); @@ -1142,6 +1236,13 @@ GST_START_TEST (test_rtx_packet_delay) fail_unless_equals_int (0, gst_harness_events_in_queue (h)); fail_unless_equals_int (20, gst_harness_upstream_events_in_queue (h)); + fail_unless (verify_jb_stats (h->element, + gst_structure_new ("application/x-rtp-jitterbuffer-stats", + "num-lost", G_TYPE_UINT64, (guint64) 7, + "rtx-count", G_TYPE_UINT64, (guint64) 26, + "rtx-success-count", G_TYPE_UINT64, (guint64) 0, + "rtx-rtt", G_TYPE_UINT64, (guint64) 0, NULL))); + gst_object_unref (testclock); gst_harness_teardown (h); } @@ -1274,6 +1375,15 @@ GST_START_TEST (test_gap_exceeds_latency) fail_unless_equals_int (0, gst_harness_events_in_queue (h)); fail_unless_equals_int (0, gst_harness_buffers_in_queue (h)); + fail_unless (verify_jb_stats (h->element, + gst_structure_new ("application/x-rtp-jitterbuffer-stats", + "num-pushed", G_TYPE_UINT64, (guint64) 11, + "num-lost", G_TYPE_UINT64, (guint64)7, + "rtx-count", G_TYPE_UINT64, (guint64)21, + "rtx-success-count", G_TYPE_UINT64, (guint64)5, + "rtx-rtt", G_TYPE_UINT64, (guint64)0, + NULL))); + gst_object_unref (testclock); gst_harness_teardown (h); } @@ -1360,6 +1470,10 @@ GST_START_TEST (test_dts_gap_larger_than_latency) verify_lost_event (out_event, i, i * dur, dur); } + fail_unless (verify_jb_stats (h->element, + gst_structure_new ("application/x-rtp-jitterbuffer-stats", + "num-lost", G_TYPE_UINT64, (guint64) 4, NULL))); + gst_object_unref (testclock); gst_harness_teardown (h); } @@ -1488,6 +1602,13 @@ GST_START_TEST (test_considered_lost_packet_in_large_gap_arrives) fail_unless_equals_int ((4 + seq_offset) & 0xffff, get_rtp_seq_num (buffer)); gst_buffer_unref (buffer); + /* we have lost 3, and one of them arrived eventually, but too late */ + fail_unless (verify_jb_stats (h->element, + gst_structure_new ("application/x-rtp-jitterbuffer-stats", + "num-pushed", G_TYPE_UINT64, (guint64) 2, + "num-lost", G_TYPE_UINT64, (guint64) 3, + "num-late", G_TYPE_UINT64, (guint64) 1, NULL))); + gst_object_unref (testclock); gst_harness_teardown (h); } @@ -1510,6 +1631,8 @@ 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_loop_test (tc_chain, test_num_late_when_considered_lost_arrives, 0, + 2); tcase_add_test (tc_chain, test_rtx_expected_next); tcase_add_test (tc_chain, test_rtx_two_missing); tcase_add_test (tc_chain, test_rtx_packet_delay);