Adding total duration and more test cases to VoipStatistics.

- Introduced IngressStatistics to cover total_duration which
comes from AudioLevel.

Bug: webrtc:11989
Change-Id: Iba52d3722b5fe6286b048ab5690e32a4f75e972a
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/190940
Commit-Queue: Tim Na <natim@webrtc.org>
Reviewed-by: Ivo Creusen <ivoc@webrtc.org>
Reviewed-by: Karl Wiberg <kwiberg@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#32538}
diff --git a/api/voip/voip_statistics.h b/api/voip/voip_statistics.h
index 5f4e174..cf01e95 100644
--- a/api/voip/voip_statistics.h
+++ b/api/voip/voip_statistics.h
@@ -16,13 +16,23 @@
 
 namespace webrtc {
 
+struct IngressStatistics {
+  // Stats included from api/neteq/neteq.h.
+  NetEqLifetimeStatistics neteq_stats;
+
+  // Represents the total duration in seconds of all samples that have been
+  // received.
+  // https://w3c.github.io/webrtc-stats/#dom-rtcinboundrtpstreamstats-totalsamplesduration
+  double total_duration = 0.0;
+};
+
 // VoipStatistics interface provides the interfaces for querying metrics around
 // the jitter buffer (NetEq) performance.
 class VoipStatistics {
  public:
-  // Gets the statistics from NetEq. Returns absl::nullopt when channel_id is
+  // Gets the audio ingress statistics. Returns absl::nullopt when channel_id is
   // invalid.
-  virtual absl::optional<NetEqLifetimeStatistics> GetNetEqStatistics(
+  virtual absl::optional<IngressStatistics> GetIngressStatistics(
       ChannelId channel_id) = 0;
 
  protected:
diff --git a/audio/voip/audio_channel.cc b/audio/voip/audio_channel.cc
index 28bd270..926130d 100644
--- a/audio/voip/audio_channel.cc
+++ b/audio/voip/audio_channel.cc
@@ -129,29 +129,34 @@
   }
 }
 
-NetEqLifetimeStatistics AudioChannel::GetNetEqStatistics() {
-  NetEqLifetimeStatistics neteq_stats;
+IngressStatistics AudioChannel::GetIngressStatistics() {
+  IngressStatistics ingress_stats;
   NetworkStatistics stats = ingress_->GetNetworkStatistics();
-  neteq_stats.total_samples_received = stats.totalSamplesReceived;
-  neteq_stats.concealed_samples = stats.concealedSamples;
-  neteq_stats.concealment_events = stats.concealmentEvents;
-  neteq_stats.jitter_buffer_delay_ms = stats.jitterBufferDelayMs;
-  neteq_stats.jitter_buffer_emitted_count = stats.jitterBufferEmittedCount;
-  neteq_stats.jitter_buffer_target_delay_ms = stats.jitterBufferTargetDelayMs;
-  neteq_stats.inserted_samples_for_deceleration =
+  ingress_stats.neteq_stats.total_samples_received = stats.totalSamplesReceived;
+  ingress_stats.neteq_stats.concealed_samples = stats.concealedSamples;
+  ingress_stats.neteq_stats.concealment_events = stats.concealmentEvents;
+  ingress_stats.neteq_stats.jitter_buffer_delay_ms = stats.jitterBufferDelayMs;
+  ingress_stats.neteq_stats.jitter_buffer_emitted_count =
+      stats.jitterBufferEmittedCount;
+  ingress_stats.neteq_stats.jitter_buffer_target_delay_ms =
+      stats.jitterBufferTargetDelayMs;
+  ingress_stats.neteq_stats.inserted_samples_for_deceleration =
       stats.insertedSamplesForDeceleration;
-  neteq_stats.removed_samples_for_acceleration =
+  ingress_stats.neteq_stats.removed_samples_for_acceleration =
       stats.removedSamplesForAcceleration;
-  neteq_stats.silent_concealed_samples = stats.silentConcealedSamples;
-  neteq_stats.fec_packets_received = stats.fecPacketsReceived;
-  neteq_stats.fec_packets_discarded = stats.fecPacketsDiscarded;
-  neteq_stats.delayed_packet_outage_samples = stats.delayedPacketOutageSamples;
-  neteq_stats.relative_packet_arrival_delay_ms =
+  ingress_stats.neteq_stats.silent_concealed_samples =
+      stats.silentConcealedSamples;
+  ingress_stats.neteq_stats.fec_packets_received = stats.fecPacketsReceived;
+  ingress_stats.neteq_stats.fec_packets_discarded = stats.fecPacketsDiscarded;
+  ingress_stats.neteq_stats.delayed_packet_outage_samples =
+      stats.delayedPacketOutageSamples;
+  ingress_stats.neteq_stats.relative_packet_arrival_delay_ms =
       stats.relativePacketArrivalDelayMs;
-  neteq_stats.interruption_count = stats.interruptionCount;
-  neteq_stats.total_interruption_duration_ms =
+  ingress_stats.neteq_stats.interruption_count = stats.interruptionCount;
+  ingress_stats.neteq_stats.total_interruption_duration_ms =
       stats.totalInterruptionDurationMs;
-  return neteq_stats;
+  ingress_stats.total_duration = ingress_->GetTotalDuration();
+  return ingress_stats;
 }
 
 }  // namespace webrtc
diff --git a/audio/voip/audio_channel.h b/audio/voip/audio_channel.h
index 9d0e707..a8946a7 100644
--- a/audio/voip/audio_channel.h
+++ b/audio/voip/audio_channel.h
@@ -82,7 +82,7 @@
   void SetReceiveCodecs(const std::map<int, SdpAudioFormat>& codecs) {
     ingress_->SetReceiveCodecs(codecs);
   }
-  NetEqLifetimeStatistics GetNetEqStatistics();
+  IngressStatistics GetIngressStatistics();
 
  private:
   // ChannelId that this audio channel belongs for logging purpose.
diff --git a/audio/voip/audio_ingress.h b/audio/voip/audio_ingress.h
index beff6cd..acb84c0 100644
--- a/audio/voip/audio_ingress.h
+++ b/audio/voip/audio_ingress.h
@@ -75,6 +75,11 @@
   int GetSpeechOutputLevelFullRange() const {
     return output_audio_level_.LevelFullRange();
   }
+  // Retrieves the total duration for all samples played so far as explained in
+  // audio/AudioLevel.h.
+  double GetTotalDuration() const {
+    return output_audio_level_.TotalDuration();
+  }
 
   // Returns network round trip time (RTT) measued by RTCP exchange with
   // remote media endpoint. RTT value -1 indicates that it's not initialized.
diff --git a/audio/voip/test/audio_channel_unittest.cc b/audio/voip/test/audio_channel_unittest.cc
index 601545b..34b595c 100644
--- a/audio/voip/test/audio_channel_unittest.cc
+++ b/audio/voip/test/audio_channel_unittest.cc
@@ -140,34 +140,86 @@
 }
 
 // Check metrics after processing an RTP packet.
-TEST_F(AudioChannelTest, TestAudioStatistics) {
-  rtc::Event event;
+TEST_F(AudioChannelTest, TestIngressStatistics) {
+  auto event = std::make_unique<rtc::Event>();
   auto loop_rtp = [&](const uint8_t* packet, size_t length, Unused) {
     audio_channel_->ReceivedRTPPacket(
         rtc::ArrayView<const uint8_t>(packet, length));
-    event.Set();
+    event->Set();
     return true;
   };
-  EXPECT_CALL(transport_, SendRtp).WillOnce(Invoke(loop_rtp));
+  EXPECT_CALL(transport_, SendRtp).WillRepeatedly(Invoke(loop_rtp));
 
   auto audio_sender = audio_channel_->GetAudioSender();
   audio_sender->SendAudioData(GetAudioFrame(0));
   audio_sender->SendAudioData(GetAudioFrame(1));
-
-  event.Wait(/*give_up_after_ms=*/1000);
+  event->Wait(/*give_up_after_ms=*/1000);
 
   AudioFrame audio_frame;
   audio_mixer_->Mix(/*number_of_channels=*/1, &audio_frame);
+  audio_mixer_->Mix(/*number_of_channels=*/1, &audio_frame);
 
-  // Check a few fields as we wouldn't have enough samples verify most of them
-  // here.
-  absl::optional<NetEqLifetimeStatistics> neteq_stats =
-      audio_channel_->GetNetEqStatistics();
-  EXPECT_TRUE(neteq_stats);
-  EXPECT_EQ(neteq_stats->total_samples_received, 80ULL);
-  EXPECT_EQ(neteq_stats->concealed_samples, 0ULL);
-  EXPECT_EQ(neteq_stats->jitter_buffer_delay_ms, 1600ULL);
-  EXPECT_EQ(neteq_stats->interruption_count, 0);
+  absl::optional<IngressStatistics> ingress_stats =
+      audio_channel_->GetIngressStatistics();
+  EXPECT_TRUE(ingress_stats);
+  EXPECT_EQ(ingress_stats->neteq_stats.total_samples_received, 160ULL);
+  EXPECT_EQ(ingress_stats->neteq_stats.concealed_samples, 0ULL);
+  EXPECT_EQ(ingress_stats->neteq_stats.concealment_events, 0ULL);
+  EXPECT_EQ(ingress_stats->neteq_stats.inserted_samples_for_deceleration, 0ULL);
+  EXPECT_EQ(ingress_stats->neteq_stats.removed_samples_for_acceleration, 0ULL);
+  EXPECT_EQ(ingress_stats->neteq_stats.silent_concealed_samples, 0ULL);
+  // To extract the jitter buffer length in millisecond, jitter_buffer_delay_ms
+  // needs to be divided by jitter_buffer_emitted_count (number of samples).
+  EXPECT_EQ(ingress_stats->neteq_stats.jitter_buffer_delay_ms, 1600ULL);
+  EXPECT_EQ(ingress_stats->neteq_stats.jitter_buffer_emitted_count, 160ULL);
+  EXPECT_GT(ingress_stats->neteq_stats.jitter_buffer_target_delay_ms, 0ULL);
+  EXPECT_EQ(ingress_stats->neteq_stats.interruption_count, 0);
+  EXPECT_EQ(ingress_stats->neteq_stats.total_interruption_duration_ms, 0);
+  EXPECT_DOUBLE_EQ(ingress_stats->total_duration, 0.02);
+
+  // Now without any RTP pending in jitter buffer pull more.
+  audio_mixer_->Mix(/*number_of_channels=*/1, &audio_frame);
+  audio_mixer_->Mix(/*number_of_channels=*/1, &audio_frame);
+
+  // Send another RTP packet to intentionally break PLC.
+  event = std::make_unique<rtc::Event>();
+  audio_sender->SendAudioData(GetAudioFrame(2));
+  audio_sender->SendAudioData(GetAudioFrame(3));
+  event->Wait(/*give_up_after_ms=*/1000);
+
+  ingress_stats = audio_channel_->GetIngressStatistics();
+  EXPECT_TRUE(ingress_stats);
+  EXPECT_EQ(ingress_stats->neteq_stats.total_samples_received, 320ULL);
+  EXPECT_EQ(ingress_stats->neteq_stats.concealed_samples, 168ULL);
+  EXPECT_EQ(ingress_stats->neteq_stats.concealment_events, 1ULL);
+  EXPECT_EQ(ingress_stats->neteq_stats.inserted_samples_for_deceleration, 0ULL);
+  EXPECT_EQ(ingress_stats->neteq_stats.removed_samples_for_acceleration, 0ULL);
+  EXPECT_EQ(ingress_stats->neteq_stats.silent_concealed_samples, 0ULL);
+  EXPECT_EQ(ingress_stats->neteq_stats.jitter_buffer_delay_ms, 1600ULL);
+  EXPECT_EQ(ingress_stats->neteq_stats.jitter_buffer_emitted_count, 160ULL);
+  EXPECT_GT(ingress_stats->neteq_stats.jitter_buffer_target_delay_ms, 0ULL);
+  EXPECT_EQ(ingress_stats->neteq_stats.interruption_count, 0);
+  EXPECT_EQ(ingress_stats->neteq_stats.total_interruption_duration_ms, 0);
+  EXPECT_DOUBLE_EQ(ingress_stats->total_duration, 0.04);
+
+  // Pull the last RTP packet.
+  audio_mixer_->Mix(/*number_of_channels=*/1, &audio_frame);
+  audio_mixer_->Mix(/*number_of_channels=*/1, &audio_frame);
+
+  ingress_stats = audio_channel_->GetIngressStatistics();
+  EXPECT_TRUE(ingress_stats);
+  EXPECT_EQ(ingress_stats->neteq_stats.total_samples_received, 480ULL);
+  EXPECT_EQ(ingress_stats->neteq_stats.concealed_samples, 168ULL);
+  EXPECT_EQ(ingress_stats->neteq_stats.concealment_events, 1ULL);
+  EXPECT_EQ(ingress_stats->neteq_stats.inserted_samples_for_deceleration, 0ULL);
+  EXPECT_EQ(ingress_stats->neteq_stats.removed_samples_for_acceleration, 0ULL);
+  EXPECT_EQ(ingress_stats->neteq_stats.silent_concealed_samples, 0ULL);
+  EXPECT_EQ(ingress_stats->neteq_stats.jitter_buffer_delay_ms, 3200ULL);
+  EXPECT_EQ(ingress_stats->neteq_stats.jitter_buffer_emitted_count, 320ULL);
+  EXPECT_GT(ingress_stats->neteq_stats.jitter_buffer_target_delay_ms, 0ULL);
+  EXPECT_EQ(ingress_stats->neteq_stats.interruption_count, 0);
+  EXPECT_EQ(ingress_stats->neteq_stats.total_interruption_duration_ms, 0);
+  EXPECT_DOUBLE_EQ(ingress_stats->total_duration, 0.06);
 }
 
 }  // namespace
diff --git a/audio/voip/voip_core.cc b/audio/voip/voip_core.cc
index 2c066e1..a93df73 100644
--- a/audio/voip/voip_core.cc
+++ b/audio/voip/voip_core.cc
@@ -382,10 +382,10 @@
   return false;
 }
 
-absl::optional<NetEqLifetimeStatistics> VoipCore::GetNetEqStatistics(
+absl::optional<IngressStatistics> VoipCore::GetIngressStatistics(
     ChannelId channel) {
   if (auto audio_channel = GetChannel(channel)) {
-    return audio_channel->GetNetEqStatistics();
+    return audio_channel->GetIngressStatistics();
   }
   return absl::nullopt;
 }
diff --git a/audio/voip/voip_core.h b/audio/voip/voip_core.h
index 1993fbe..8c1a44c 100644
--- a/audio/voip/voip_core.h
+++ b/audio/voip/voip_core.h
@@ -107,7 +107,7 @@
                      int duration_ms) override;
 
   // Implements VoipStatistics interfaces.
-  absl::optional<NetEqLifetimeStatistics> GetNetEqStatistics(
+  absl::optional<IngressStatistics> GetIngressStatistics(
       ChannelId channel) override;
 
  private: