Add new decoding statistics for muted output

This change adds a new statistic for logging how many calls to
NetEq::GetAudio resulted in a "muted output". A muted output happens
if the packet stream has been dead for some time (and the last decoded
packet was not comfort noise).

BUG=webrtc:5606
BUG=b/31256483

Review-Url: https://codereview.webrtc.org/2341293002
Cr-Commit-Position: refs/heads/master@{#14302}
diff --git a/webrtc/api/call/audio_receive_stream.h b/webrtc/api/call/audio_receive_stream.h
index 096cbc7..ec80a9e 100644
--- a/webrtc/api/call/audio_receive_stream.h
+++ b/webrtc/api/call/audio_receive_stream.h
@@ -57,6 +57,7 @@
     int32_t decoding_plc = 0;
     int32_t decoding_cng = 0;
     int32_t decoding_plc_cng = 0;
+    int32_t decoding_muted_output = 0;
     int64_t capture_start_ntp_time_ms = 0;
   };
 
diff --git a/webrtc/api/statscollector.cc b/webrtc/api/statscollector.cc
index d21fc30..4dd5305 100644
--- a/webrtc/api/statscollector.cc
+++ b/webrtc/api/statscollector.cc
@@ -143,6 +143,8 @@
     { StatsReport::kStatsValueNameDecodingCTN, info.decoding_calls_to_neteq },
     { StatsReport::kStatsValueNameDecodingCTSG,
       info.decoding_calls_to_silence_generator },
+    { StatsReport::kStatsValueNameDecodingMutedOutput,
+      info.decoding_muted_output },
     { StatsReport::kStatsValueNameDecodingNormal, info.decoding_normal },
     { StatsReport::kStatsValueNameDecodingPLC, info.decoding_plc },
     { StatsReport::kStatsValueNameDecodingPLCCNG, info.decoding_plc_cng },
diff --git a/webrtc/api/statscollector_unittest.cc b/webrtc/api/statscollector_unittest.cc
index f71a168..b2fb93c 100644
--- a/webrtc/api/statscollector_unittest.cc
+++ b/webrtc/api/statscollector_unittest.cc
@@ -348,8 +348,11 @@
   EXPECT_TRUE(GetValue(
       report, StatsReport::kStatsValueNameDecodingPLCCNG, &value_in_report));
   EXPECT_EQ(rtc::ToString<int>(info.decoding_plc_cng), value_in_report);
-  EXPECT_TRUE(GetValue(
-      report, StatsReport::kStatsValueNameCodecName, &value_in_report));
+  EXPECT_TRUE(GetValue(report, StatsReport::kStatsValueNameDecodingMutedOutput,
+                       &value_in_report));
+  EXPECT_EQ(rtc::ToString<int>(info.decoding_muted_output), value_in_report);
+  EXPECT_TRUE(GetValue(report, StatsReport::kStatsValueNameCodecName,
+                       &value_in_report));
 }
 
 
diff --git a/webrtc/api/statstypes.cc b/webrtc/api/statstypes.cc
index 9e5e176..eb6b30c 100644
--- a/webrtc/api/statstypes.cc
+++ b/webrtc/api/statstypes.cc
@@ -456,6 +456,8 @@
       return "googDecodingCTSG";
     case kStatsValueNameDecodingCTN:
       return "googDecodingCTN";
+    case kStatsValueNameDecodingMutedOutput:
+      return "googDecodingMuted";
     case kStatsValueNameDecodingNormal:
       return "googDecodingNormal";
     case kStatsValueNameDecodingPLC:
diff --git a/webrtc/api/statstypes.h b/webrtc/api/statstypes.h
index 093bdbf..a0a85c9 100644
--- a/webrtc/api/statstypes.h
+++ b/webrtc/api/statstypes.h
@@ -148,6 +148,7 @@
     kStatsValueNameDecodingCNG,
     kStatsValueNameDecodingCTN,
     kStatsValueNameDecodingCTSG,
+    kStatsValueNameDecodingMutedOutput,
     kStatsValueNameDecodingNormal,
     kStatsValueNameDecodingPLC,
     kStatsValueNameDecodingPLCCNG,
diff --git a/webrtc/audio/audio_receive_stream.cc b/webrtc/audio/audio_receive_stream.cc
index 91237c3..57e62f5 100644
--- a/webrtc/audio/audio_receive_stream.cc
+++ b/webrtc/audio/audio_receive_stream.cc
@@ -211,6 +211,7 @@
   stats.decoding_plc = ds.decoded_plc;
   stats.decoding_cng = ds.decoded_cng;
   stats.decoding_plc_cng = ds.decoded_plc_cng;
+  stats.decoding_muted_output = ds.decoded_muted_output;
 
   return stats;
 }
diff --git a/webrtc/audio/audio_receive_stream_unittest.cc b/webrtc/audio/audio_receive_stream_unittest.cc
index dd66cc6..9276c28 100644
--- a/webrtc/audio/audio_receive_stream_unittest.cc
+++ b/webrtc/audio/audio_receive_stream_unittest.cc
@@ -43,6 +43,7 @@
   audio_decode_stats.decoded_plc = 123;
   audio_decode_stats.decoded_cng = 456;
   audio_decode_stats.decoded_plc_cng = 789;
+  audio_decode_stats.decoded_muted_output = 987;
   return audio_decode_stats;
 }
 
@@ -356,6 +357,8 @@
   EXPECT_EQ(kAudioDecodeStats.decoded_plc, stats.decoding_plc);
   EXPECT_EQ(kAudioDecodeStats.decoded_cng, stats.decoding_cng);
   EXPECT_EQ(kAudioDecodeStats.decoded_plc_cng, stats.decoding_plc_cng);
+  EXPECT_EQ(kAudioDecodeStats.decoded_muted_output,
+            stats.decoding_muted_output);
   EXPECT_EQ(kCallStats.capture_start_ntp_time_ms_,
             stats.capture_start_ntp_time_ms);
 }
diff --git a/webrtc/common_types.h b/webrtc/common_types.h
index 6d30719..b8c0df4 100644
--- a/webrtc/common_types.h
+++ b/webrtc/common_types.h
@@ -385,7 +385,8 @@
         decoded_normal(0),
         decoded_plc(0),
         decoded_cng(0),
-        decoded_plc_cng(0) {}
+        decoded_plc_cng(0),
+        decoded_muted_output(0) {}
 
   int calls_to_silence_generator;  // Number of calls where silence generated,
                                    // and NetEq was disengaged from decoding.
@@ -394,6 +395,7 @@
   int decoded_plc;     // Number of calls resulted in PLC.
   int decoded_cng;  // Number of calls where comfort noise generated due to DTX.
   int decoded_plc_cng;  // Number of calls resulted where PLC faded to CNG.
+  int decoded_muted_output;  // Number of calls returning a muted state output.
 };
 
 // Type of Noise Suppression.
diff --git a/webrtc/media/base/mediachannel.h b/webrtc/media/base/mediachannel.h
index 1355b36..b71a309 100644
--- a/webrtc/media/base/mediachannel.h
+++ b/webrtc/media/base/mediachannel.h
@@ -620,6 +620,7 @@
         decoding_plc(0),
         decoding_cng(0),
         decoding_plc_cng(0),
+        decoding_muted_output(0),
         capture_start_ntp_time_ms(-1) {}
 
   int ext_seqnum;
@@ -644,6 +645,7 @@
   int decoding_plc;
   int decoding_cng;
   int decoding_plc_cng;
+  int decoding_muted_output;
   // Estimated capture start time in NTP time in ms.
   int64_t capture_start_ntp_time_ms;
 };
diff --git a/webrtc/media/engine/webrtcvoiceengine.cc b/webrtc/media/engine/webrtcvoiceengine.cc
index 235b9b3..e066f30 100644
--- a/webrtc/media/engine/webrtcvoiceengine.cc
+++ b/webrtc/media/engine/webrtcvoiceengine.cc
@@ -2611,6 +2611,7 @@
     rinfo.decoding_plc = stats.decoding_plc;
     rinfo.decoding_cng = stats.decoding_cng;
     rinfo.decoding_plc_cng = stats.decoding_plc_cng;
+    rinfo.decoding_muted_output = stats.decoding_muted_output;
     rinfo.capture_start_ntp_time_ms = stats.capture_start_ntp_time_ms;
     info->receivers.push_back(rinfo);
   }
diff --git a/webrtc/media/engine/webrtcvoiceengine_unittest.cc b/webrtc/media/engine/webrtcvoiceengine_unittest.cc
index ead706f..e40bc77 100644
--- a/webrtc/media/engine/webrtcvoiceengine_unittest.cc
+++ b/webrtc/media/engine/webrtcvoiceengine_unittest.cc
@@ -450,7 +450,8 @@
     stats.decoding_plc = 1234;
     stats.decoding_cng = 5678;
     stats.decoding_plc_cng = 9012;
-    stats.capture_start_ntp_time_ms = 3456;
+    stats.decoding_muted_output = 3456;
+    stats.capture_start_ntp_time_ms = 7890;
     return stats;
   }
   void SetAudioReceiveStreamStats() {
@@ -485,6 +486,7 @@
     EXPECT_EQ(info.decoding_plc, stats.decoding_plc);
     EXPECT_EQ(info.decoding_cng, stats.decoding_cng);
     EXPECT_EQ(info.decoding_plc_cng, stats.decoding_plc_cng);
+    EXPECT_EQ(info.decoding_muted_output, stats.decoding_muted_output);
     EXPECT_EQ(info.capture_start_ntp_time_ms, stats.capture_start_ntp_time_ms);
   }
 
diff --git a/webrtc/modules/audio_coding/acm2/acm_receiver.cc b/webrtc/modules/audio_coding/acm2/acm_receiver.cc
index 9a882aa..417a346 100644
--- a/webrtc/modules/audio_coding/acm2/acm_receiver.cc
+++ b/webrtc/modules/audio_coding/acm2/acm_receiver.cc
@@ -135,6 +135,7 @@
 int AcmReceiver::GetAudio(int desired_freq_hz,
                           AudioFrame* audio_frame,
                           bool* muted) {
+  RTC_DCHECK(muted);
   // Accessing members, take the lock.
   rtc::CritScope lock(&crit_sect_);
 
@@ -191,7 +192,7 @@
          sizeof(int16_t) * audio_frame->samples_per_channel_ *
              audio_frame->num_channels_);
 
-  call_stats_.DecodedByNetEq(audio_frame->speech_type_);
+  call_stats_.DecodedByNetEq(audio_frame->speech_type_, *muted);
   return 0;
 }
 
diff --git a/webrtc/modules/audio_coding/acm2/audio_coding_module_unittest_oldapi.cc b/webrtc/modules/audio_coding/acm2/audio_coding_module_unittest_oldapi.cc
index 6220c05..2a8b834 100644
--- a/webrtc/modules/audio_coding/acm2/audio_coding_module_unittest_oldapi.cc
+++ b/webrtc/modules/audio_coding/acm2/audio_coding_module_unittest_oldapi.cc
@@ -253,6 +253,7 @@
   EXPECT_EQ(0, stats.decoded_cng);
   EXPECT_EQ(0, stats.decoded_plc);
   EXPECT_EQ(0, stats.decoded_plc_cng);
+  EXPECT_EQ(0, stats.decoded_muted_output);
 }
 
 // Insert some packets and pull audio. Check statistics are valid. Then,
@@ -278,6 +279,7 @@
   EXPECT_EQ(0, stats.decoded_cng);
   EXPECT_EQ(0, stats.decoded_plc);
   EXPECT_EQ(0, stats.decoded_plc_cng);
+  EXPECT_EQ(0, stats.decoded_muted_output);
 
   const int kNumPlc = 3;
   const int kNumPlcCng = 5;
@@ -293,6 +295,8 @@
   EXPECT_EQ(0, stats.decoded_cng);
   EXPECT_EQ(kNumPlc, stats.decoded_plc);
   EXPECT_EQ(kNumPlcCng, stats.decoded_plc_cng);
+  EXPECT_EQ(0, stats.decoded_muted_output);
+  // TODO(henrik.lundin) Add a test with muted state enabled.
 }
 
 TEST_F(AudioCodingModuleTestOldApi, VerifyOutputFrame) {
diff --git a/webrtc/modules/audio_coding/acm2/call_statistics.cc b/webrtc/modules/audio_coding/acm2/call_statistics.cc
index 4441932..7ac9fc4 100644
--- a/webrtc/modules/audio_coding/acm2/call_statistics.cc
+++ b/webrtc/modules/audio_coding/acm2/call_statistics.cc
@@ -10,14 +10,18 @@
 
 #include "webrtc/modules/audio_coding/acm2/call_statistics.h"
 
-#include <assert.h>
+#include "webrtc/base/checks.h"
 
 namespace webrtc {
 
 namespace acm2 {
 
-void CallStatistics::DecodedByNetEq(AudioFrame::SpeechType speech_type) {
+void CallStatistics::DecodedByNetEq(AudioFrame::SpeechType speech_type,
+                                    bool muted) {
   ++decoding_stat_.calls_to_neteq;
+  if (muted) {
+    ++decoding_stat_.decoded_muted_output;
+  }
   switch (speech_type) {
     case AudioFrame::kNormalSpeech: {
       ++decoding_stat_.decoded_normal;
@@ -37,7 +41,7 @@
     }
     case AudioFrame::kUndefined: {
       // If the audio is decoded by NetEq, |kUndefined| is not an option.
-      assert(false);
+      RTC_NOTREACHED();
     }
   }
 }
diff --git a/webrtc/modules/audio_coding/acm2/call_statistics.h b/webrtc/modules/audio_coding/acm2/call_statistics.h
index 888afea..3993319 100644
--- a/webrtc/modules/audio_coding/acm2/call_statistics.h
+++ b/webrtc/modules/audio_coding/acm2/call_statistics.h
@@ -37,8 +37,9 @@
   ~CallStatistics() {}
 
   // Call this method to indicate that NetEq engaged in decoding. |speech_type|
-  // is the audio-type according to NetEq.
-  void DecodedByNetEq(AudioFrame::SpeechType speech_type);
+  // is the audio-type according to NetEq, and |muted| indicates if the decoded
+  // frame was produced in muted state.
+  void DecodedByNetEq(AudioFrame::SpeechType speech_type, bool muted);
 
   // Call this method to indicate that a decoding call resulted in generating
   // silence, i.e. call to NetEq is bypassed and the output audio is zero.
diff --git a/webrtc/modules/audio_coding/acm2/call_statistics_unittest.cc b/webrtc/modules/audio_coding/acm2/call_statistics_unittest.cc
index 9ba0774..284532f 100644
--- a/webrtc/modules/audio_coding/acm2/call_statistics_unittest.cc
+++ b/webrtc/modules/audio_coding/acm2/call_statistics_unittest.cc
@@ -26,6 +26,7 @@
   EXPECT_EQ(0, stats.decoded_cng);
   EXPECT_EQ(0, stats.decoded_plc);
   EXPECT_EQ(0, stats.decoded_plc_cng);
+  EXPECT_EQ(0, stats.decoded_muted_output);
 }
 
 TEST(CallStatisticsTest, AllCalls) {
@@ -33,10 +34,10 @@
   AudioDecodingCallStats stats;
 
   call_stats.DecodedBySilenceGenerator();
-  call_stats.DecodedByNetEq(AudioFrame::kNormalSpeech);
-  call_stats.DecodedByNetEq(AudioFrame::kPLC);
-  call_stats.DecodedByNetEq(AudioFrame::kPLCCNG);
-  call_stats.DecodedByNetEq(AudioFrame::kCNG);
+  call_stats.DecodedByNetEq(AudioFrame::kNormalSpeech, false);
+  call_stats.DecodedByNetEq(AudioFrame::kPLC, false);
+  call_stats.DecodedByNetEq(AudioFrame::kPLCCNG, true);  // Let this be muted.
+  call_stats.DecodedByNetEq(AudioFrame::kCNG, false);
 
   stats = call_stats.GetDecodingStatistics();
   EXPECT_EQ(4, stats.calls_to_neteq);
@@ -45,6 +46,7 @@
   EXPECT_EQ(1, stats.decoded_cng);
   EXPECT_EQ(1, stats.decoded_plc);
   EXPECT_EQ(1, stats.decoded_plc_cng);
+  EXPECT_EQ(1, stats.decoded_muted_output);
 }
 
 }  // namespace acm2