Expose `jitterBufferEmittedCount` in addition to the existing `jitterBufferDelay` for `getStats()`.

NetEq currently only passes `jitterBufferDelay` to `getStats()`. We need its paired `jitterBufferEmittedCount` denominator stat for the calculations to be accurate.

Bug: webrtc:10192
Change-Id: I655aea629026ce9101409c2e0f18c2fa57a1c3ab
Reviewed-on: https://webrtc-review.googlesource.com/c/117320
Reviewed-by: Karl Wiberg <kwiberg@webrtc.org>
Reviewed-by: Henrik Lundin <henrik.lundin@webrtc.org>
Commit-Queue: Chen Xing <chxg@google.com>
Cr-Commit-Position: refs/heads/master@{#26276}
diff --git a/api/stats/rtcstats_objects.h b/api/stats/rtcstats_objects.h
index e69bc8e..b27bf73 100644
--- a/api/stats/rtcstats_objects.h
+++ b/api/stats/rtcstats_objects.h
@@ -284,6 +284,7 @@
   // implemented for audio only).
   // https://crbug.com/webrtc/8318
   RTCStatsMember<double> jitter_buffer_delay;
+  RTCStatsMember<uint64_t> jitter_buffer_emitted_count;
   // Video-only members
   RTCStatsMember<uint32_t> frame_width;
   RTCStatsMember<uint32_t> frame_height;
diff --git a/audio/audio_receive_stream.cc b/audio/audio_receive_stream.cc
index 690bb90..58eca4f 100644
--- a/audio/audio_receive_stream.cc
+++ b/audio/audio_receive_stream.cc
@@ -207,6 +207,7 @@
   stats.jitter_buffer_delay_seconds =
       static_cast<double>(ns.jitterBufferDelayMs) /
       static_cast<double>(rtc::kNumMillisecsPerSec);
+  stats.jitter_buffer_emitted_count = ns.jitterBufferEmittedCount;
   stats.expand_rate = Q14ToFloat(ns.currentExpandRate);
   stats.speech_expand_rate = Q14ToFloat(ns.currentSpeechExpandRate);
   stats.secondary_decoded_rate = Q14ToFloat(ns.currentSecondaryDecodedRate);
diff --git a/audio/audio_receive_stream_unittest.cc b/audio/audio_receive_stream_unittest.cc
index 25ca528..cc1e9d4 100644
--- a/audio/audio_receive_stream_unittest.cc
+++ b/audio/audio_receive_stream_unittest.cc
@@ -67,8 +67,8 @@
 const std::pair<int, SdpAudioFormat> kReceiveCodec =
     {123, {"codec_name_recv", 96000, 0}};
 const NetworkStatistics kNetworkStats = {
-    123, 456, false, 789012, 3456, 123, 456, 0,  {}, 789, 12,
-    345, 678, 901,   0,      -1,   -1,  -1,  -1, -1, 0};
+    123, 456, false, 789012, 3456, 123, 456, 789, 0,  {}, 789,
+    12,  345, 678,   901,    0,    -1,  -1,  -1,  -1, -1, 0};
 const AudioDecodingCallStats kAudioDecodeStats = MakeAudioDecodeStatsForTest();
 
 struct ConfigHelper {
@@ -288,6 +288,8 @@
   EXPECT_EQ(static_cast<double>(kNetworkStats.jitterBufferDelayMs) /
                 static_cast<double>(rtc::kNumMillisecsPerSec),
             stats.jitter_buffer_delay_seconds);
+  EXPECT_EQ(kNetworkStats.jitterBufferEmittedCount,
+            stats.jitter_buffer_emitted_count);
   EXPECT_EQ(Q14ToFloat(kNetworkStats.currentExpandRate), stats.expand_rate);
   EXPECT_EQ(Q14ToFloat(kNetworkStats.currentSpeechExpandRate),
             stats.speech_expand_rate);
diff --git a/call/audio_receive_stream.h b/call/audio_receive_stream.h
index e850954..f1aeb88 100644
--- a/call/audio_receive_stream.h
+++ b/call/audio_receive_stream.h
@@ -56,6 +56,7 @@
     uint64_t concealed_samples = 0;
     uint64_t concealment_events = 0;
     double jitter_buffer_delay_seconds = 0.0;
+    uint64_t jitter_buffer_emitted_count = 0;
     // Stats below DO NOT correspond directly to anything in the WebRTC stats
     float expand_rate = 0.0f;
     float speech_expand_rate = 0.0f;
diff --git a/media/base/media_channel.h b/media/base/media_channel.h
index 8eecaa0..a55c191 100644
--- a/media/base/media_channel.h
+++ b/media/base/media_channel.h
@@ -449,7 +449,8 @@
   double total_output_duration = 0.0;
   uint64_t concealed_samples = 0;
   uint64_t concealment_events = 0;
-  double jitter_buffer_delay_seconds = 0;
+  double jitter_buffer_delay_seconds = 0.0;
+  uint64_t jitter_buffer_emitted_count = 0;
   // Stats below DO NOT correspond directly to anything in the WebRTC stats
   // fraction of synthesized audio inserted through expansion.
   float expand_rate = 0.0f;
diff --git a/media/engine/webrtc_voice_engine.cc b/media/engine/webrtc_voice_engine.cc
index 88fc4d8..9c84f7c 100644
--- a/media/engine/webrtc_voice_engine.cc
+++ b/media/engine/webrtc_voice_engine.cc
@@ -2244,6 +2244,7 @@
     rinfo.concealed_samples = stats.concealed_samples;
     rinfo.concealment_events = stats.concealment_events;
     rinfo.jitter_buffer_delay_seconds = stats.jitter_buffer_delay_seconds;
+    rinfo.jitter_buffer_emitted_count = stats.jitter_buffer_emitted_count;
     rinfo.expand_rate = stats.expand_rate;
     rinfo.speech_expand_rate = stats.speech_expand_rate;
     rinfo.secondary_decoded_rate = stats.secondary_decoded_rate;
diff --git a/media/engine/webrtc_voice_engine_unittest.cc b/media/engine/webrtc_voice_engine_unittest.cc
index 06bf3fd..8204010 100644
--- a/media/engine/webrtc_voice_engine_unittest.cc
+++ b/media/engine/webrtc_voice_engine_unittest.cc
@@ -655,6 +655,7 @@
     stats.concealed_samples = 234;
     stats.concealment_events = 12;
     stats.jitter_buffer_delay_seconds = 34;
+    stats.jitter_buffer_emitted_count = 77;
     stats.expand_rate = 5.67f;
     stats.speech_expand_rate = 8.90f;
     stats.secondary_decoded_rate = 1.23f;
@@ -702,6 +703,8 @@
     EXPECT_EQ(info.concealment_events, stats.concealment_events);
     EXPECT_EQ(info.jitter_buffer_delay_seconds,
               stats.jitter_buffer_delay_seconds);
+    EXPECT_EQ(info.jitter_buffer_emitted_count,
+              stats.jitter_buffer_emitted_count);
     EXPECT_EQ(info.expand_rate, stats.expand_rate);
     EXPECT_EQ(info.speech_expand_rate, stats.speech_expand_rate);
     EXPECT_EQ(info.secondary_decoded_rate, stats.secondary_decoded_rate);
diff --git a/modules/audio_coding/acm2/acm_receiver.cc b/modules/audio_coding/acm2/acm_receiver.cc
index 2810294..ba36278 100644
--- a/modules/audio_coding/acm2/acm_receiver.cc
+++ b/modules/audio_coding/acm2/acm_receiver.cc
@@ -245,6 +245,8 @@
   acm_stat->concealedSamples = neteq_lifetime_stat.concealed_samples;
   acm_stat->concealmentEvents = neteq_lifetime_stat.concealment_events;
   acm_stat->jitterBufferDelayMs = neteq_lifetime_stat.jitter_buffer_delay_ms;
+  acm_stat->jitterBufferEmittedCount =
+      neteq_lifetime_stat.jitter_buffer_emitted_count;
   acm_stat->delayedPacketOutageSamples =
       neteq_lifetime_stat.delayed_packet_outage_samples;
 
diff --git a/modules/audio_coding/include/audio_coding_module_typedefs.h b/modules/audio_coding/include/audio_coding_module_typedefs.h
index bafff72..0db423b 100644
--- a/modules/audio_coding/include/audio_coding_module_typedefs.h
+++ b/modules/audio_coding/include/audio_coding_module_typedefs.h
@@ -80,6 +80,7 @@
   uint64_t concealedSamples;
   uint64_t concealmentEvents;
   uint64_t jitterBufferDelayMs;
+  uint64_t jitterBufferEmittedCount;
   // Stats below DO NOT correspond directly to anything in the WebRTC stats
   // Loss rate (network + late); fraction between 0 and 1, scaled to Q14.
   uint16_t currentPacketLossRate;
diff --git a/modules/audio_coding/neteq/include/neteq.h b/modules/audio_coding/neteq/include/neteq.h
index 6eecc65..d4e8423 100644
--- a/modules/audio_coding/neteq/include/neteq.h
+++ b/modules/audio_coding/neteq/include/neteq.h
@@ -70,6 +70,7 @@
   uint64_t concealed_samples = 0;
   uint64_t concealment_events = 0;
   uint64_t jitter_buffer_delay_ms = 0;
+  uint64_t jitter_buffer_emitted_count = 0;
   // Below stat is not part of the spec.
   uint64_t voice_concealed_samples = 0;
   uint64_t delayed_packet_outage_samples = 0;
diff --git a/modules/audio_coding/neteq/neteq_unittest.cc b/modules/audio_coding/neteq/neteq_unittest.cc
index b562ede..b9b8d08 100644
--- a/modules/audio_coding/neteq/neteq_unittest.cc
+++ b/modules/audio_coding/neteq/neteq_unittest.cc
@@ -1656,6 +1656,7 @@
   int packets_sent = 0;
   int packets_received = 0;
   int expected_delay = 0;
+  uint64_t expected_emitted_count = 0;
   while (packets_received < kNumPackets) {
     // Insert packet.
     if (packets_sent < kNumPackets) {
@@ -1679,6 +1680,7 @@
       // number of samples that are sent for play out.
       int current_delay_ms = packets_delay * kPacketLenMs;
       expected_delay += current_delay_ms * kSamples;
+      expected_emitted_count += kSamples;
     }
   }
 
@@ -1690,6 +1692,7 @@
   // Check jitter buffer delay.
   NetEqLifetimeStatistics stats = neteq_->GetLifetimeStatistics();
   EXPECT_EQ(expected_delay, static_cast<int>(stats.jitter_buffer_delay_ms));
+  EXPECT_EQ(expected_emitted_count, stats.jitter_buffer_emitted_count);
 }
 
 TEST_F(NetEqDecodingTestFaxMode, TestJitterBufferDelayWithoutLoss) {
diff --git a/modules/audio_coding/neteq/statistics_calculator.cc b/modules/audio_coding/neteq/statistics_calculator.cc
index 5d94abd..7ad1a28 100644
--- a/modules/audio_coding/neteq/statistics_calculator.cc
+++ b/modules/audio_coding/neteq/statistics_calculator.cc
@@ -246,6 +246,7 @@
 void StatisticsCalculator::JitterBufferDelay(size_t num_samples,
                                              uint64_t waiting_time_ms) {
   lifetime_stats_.jitter_buffer_delay_ms += waiting_time_ms * num_samples;
+  lifetime_stats_.jitter_buffer_emitted_count += num_samples;
 }
 
 void StatisticsCalculator::SecondaryDecodedSamples(int num_samples) {
diff --git a/pc/rtc_stats_collector.cc b/pc/rtc_stats_collector.cc
index b1a44ee..245293a 100644
--- a/pc/rtc_stats_collector.cc
+++ b/pc/rtc_stats_collector.cc
@@ -445,6 +445,8 @@
   }
   audio_track_stats->jitter_buffer_delay =
       voice_receiver_info.jitter_buffer_delay_seconds;
+  audio_track_stats->jitter_buffer_emitted_count =
+      voice_receiver_info.jitter_buffer_emitted_count;
   audio_track_stats->total_audio_energy =
       voice_receiver_info.total_output_energy;
   audio_track_stats->total_samples_received =
diff --git a/pc/rtc_stats_collector_unittest.cc b/pc/rtc_stats_collector_unittest.cc
index 4635d0e..468e2c4 100644
--- a/pc/rtc_stats_collector_unittest.cc
+++ b/pc/rtc_stats_collector_unittest.cc
@@ -1425,6 +1425,7 @@
   voice_receiver_info.concealed_samples = 123;
   voice_receiver_info.concealment_events = 12;
   voice_receiver_info.jitter_buffer_delay_seconds = 3456;
+  voice_receiver_info.jitter_buffer_emitted_count = 13;
   voice_receiver_info.jitter_buffer_flushes = 7;
   voice_receiver_info.delayed_packet_outage_samples = 15;
 
@@ -1460,6 +1461,7 @@
   expected_remote_audio_track.concealed_samples = 123;
   expected_remote_audio_track.concealment_events = 12;
   expected_remote_audio_track.jitter_buffer_delay = 3456;
+  expected_remote_audio_track.jitter_buffer_emitted_count = 13;
   expected_remote_audio_track.jitter_buffer_flushes = 7;
   expected_remote_audio_track.delayed_packet_outage_samples = 15;
   ASSERT_TRUE(report->Get(expected_remote_audio_track.id()));
diff --git a/pc/rtc_stats_integrationtest.cc b/pc/rtc_stats_integrationtest.cc
index cae01ae..a9ebc80 100644
--- a/pc/rtc_stats_integrationtest.cc
+++ b/pc/rtc_stats_integrationtest.cc
@@ -612,6 +612,8 @@
       verifier.TestMemberIsNonNegative<double>(
           media_stream_track.jitter_buffer_delay);
       verifier.TestMemberIsNonNegative<uint64_t>(
+          media_stream_track.jitter_buffer_emitted_count);
+      verifier.TestMemberIsNonNegative<uint64_t>(
           media_stream_track.total_samples_received);
       verifier.TestMemberIsNonNegative<uint64_t>(
           media_stream_track.concealed_samples);
@@ -623,6 +625,8 @@
           media_stream_track.delayed_packet_outage_samples);
     } else {
       verifier.TestMemberIsUndefined(media_stream_track.jitter_buffer_delay);
+      verifier.TestMemberIsUndefined(
+          media_stream_track.jitter_buffer_emitted_count);
       verifier.TestMemberIsUndefined(media_stream_track.total_samples_received);
       verifier.TestMemberIsUndefined(media_stream_track.concealed_samples);
       verifier.TestMemberIsUndefined(media_stream_track.concealment_events);
diff --git a/stats/rtcstats_objects.cc b/stats/rtcstats_objects.cc
index 4ba0b28..2451e12 100644
--- a/stats/rtcstats_objects.cc
+++ b/stats/rtcstats_objects.cc
@@ -360,6 +360,7 @@
                      &detached,
                      &kind,
                      &jitter_buffer_delay,
+                     &jitter_buffer_emitted_count,
                      &frame_width,
                      &frame_height,
                      &frames_per_second,
@@ -398,6 +399,7 @@
       detached("detached"),
       kind("kind", kind),
       jitter_buffer_delay("jitterBufferDelay"),
+      jitter_buffer_emitted_count("jitterBufferEmittedCount"),
       frame_width("frameWidth"),
       frame_height("frameHeight"),
       frames_per_second("framesPerSecond"),
@@ -432,6 +434,7 @@
       detached(other.detached),
       kind(other.kind),
       jitter_buffer_delay(other.jitter_buffer_delay),
+      jitter_buffer_emitted_count(other.jitter_buffer_emitted_count),
       frame_width(other.frame_width),
       frame_height(other.frame_height),
       frames_per_second(other.frames_per_second),