Add more Audio Mixer and Fixed Gain Controller metrics.

We want to know how the AudioMixer is used and how FixedGainController
behaves.

The WebRTC.Audio.Agc2.FixedDigitalGainCurveRegion.* metrics measures
how often the input level hits different regions of the Fixed Gain
Controller gain curve (when the limiter is enabled). They also measure
how long the metrics stay in different regions. They are related to
WebRTC.Audio.ApmCaptureOutputLevelPeakRms, but the new metrics measure
the level before any processing done in APM.

The AudioMixer mixes incoming audio streams. Their number should be
mostly constant, and often some of them could be muted. The metrics
WebRTC.Audio.AudioMixer.NumIncomingStreams,
WebRTC.Audio.AudioMixer.NumIncomingActiveStreams log the number of
incoming stream and how many are not muted. We currently don't have
any stats related to that.

The metric WebRTC.Audio.AudioMixer.MixingRate logs the rate selected
for mixing. The rate can sometimes be inferred from
WebRTC.Audio.Encoder.CodecType. But that metric measures encoding and
not decoding, and codecs don't always map to rates.

See also accompanying Chromium CL
https://chromium-review.googlesource.com/c/chromium/src/+/939473

Bug: webrtc:8925
Change-Id: Ib1405877fc1b39e5d2f0ceccba04434813f20b0d
Reviewed-on: https://webrtc-review.googlesource.com/57740
Reviewed-by: Alessio Bazzica <alessiob@webrtc.org>
Commit-Queue: Alex Loiko <aleloi@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#22443}
diff --git a/modules/audio_mixer/BUILD.gn b/modules/audio_mixer/BUILD.gn
index 644c8ab..8cb4bfb 100644
--- a/modules/audio_mixer/BUILD.gn
+++ b/modules/audio_mixer/BUILD.gn
@@ -47,6 +47,7 @@
     "../../rtc_base:rtc_base_approved",
     "../../system_wrappers",
     "../../system_wrappers:field_trial_api",
+    "../../system_wrappers:metrics_api",
     "../audio_processing",
     "../audio_processing:apm_logging",
     "../audio_processing:audio_frame_view",
diff --git a/modules/audio_mixer/frame_combiner.cc b/modules/audio_mixer/frame_combiner.cc
index faca090..2c83e2e 100644
--- a/modules/audio_mixer/frame_combiner.cc
+++ b/modules/audio_mixer/frame_combiner.cc
@@ -20,8 +20,10 @@
 #include "modules/audio_mixer/audio_frame_manipulator.h"
 #include "modules/audio_mixer/audio_mixer_impl.h"
 #include "modules/audio_processing/logging/apm_data_dumper.h"
+#include "rtc_base/arraysize.h"
 #include "rtc_base/checks.h"
 #include "rtc_base/logging.h"
+#include "system_wrappers/include/metrics.h"
 
 namespace webrtc {
 namespace {
@@ -253,6 +255,36 @@
   }
 
   InterleaveToAudioFrame(mixing_buffer_view, audio_frame_for_mixing);
+
+  LogMixingStats(mix_list, sample_rate, number_of_streams);
+}
+
+void FrameCombiner::LogMixingStats(const std::vector<AudioFrame*>& mix_list,
+                                   int sample_rate,
+                                   size_t number_of_streams) const {
+  // Log every second.
+  uma_logging_counter_++;
+  if (uma_logging_counter_ > 1000 / AudioMixerImpl::kFrameDurationInMs) {
+    uma_logging_counter_ = 0;
+    RTC_HISTOGRAM_COUNTS_100("WebRTC.Audio.AudioMixer.NumIncomingStreams",
+                             static_cast<int>(number_of_streams));
+    RTC_HISTOGRAM_ENUMERATION(
+        "WebRTC.Audio.AudioMixer.NumIncomingActiveStreams",
+        static_cast<int>(mix_list.size()),
+        AudioMixerImpl::kMaximumAmountOfMixedAudioSources);
+
+    using NativeRate = AudioProcessing::NativeRate;
+    static constexpr NativeRate native_rates[] = {
+        NativeRate::kSampleRate8kHz, NativeRate::kSampleRate16kHz,
+        NativeRate::kSampleRate32kHz, NativeRate::kSampleRate48kHz};
+    const auto* rate_position = std::lower_bound(
+        std::begin(native_rates), std::end(native_rates), sample_rate);
+
+    RTC_HISTOGRAM_ENUMERATION(
+        "WebRTC.Audio.AudioMixer.MixingRate",
+        std::distance(std::begin(native_rates), rate_position),
+        arraysize(native_rates));
+  }
 }
 
 }  // namespace webrtc
diff --git a/modules/audio_mixer/frame_combiner.h b/modules/audio_mixer/frame_combiner.h
index 3d43128..14257d2 100644
--- a/modules/audio_mixer/frame_combiner.h
+++ b/modules/audio_mixer/frame_combiner.h
@@ -44,10 +44,15 @@
                AudioFrame* audio_frame_for_mixing);
 
  private:
+  void LogMixingStats(const std::vector<AudioFrame*>& mix_list,
+                      int sample_rate,
+                      size_t number_of_streams) const;
+
   LimiterType limiter_type_;
   std::unique_ptr<AudioProcessing> apm_agc_limiter_;
   std::unique_ptr<ApmDataDumper> data_dumper_;
   FixedGainController apm_agc2_limiter_;
+  mutable int uma_logging_counter_ = 0;
 };
 }  // namespace webrtc
 
diff --git a/modules/audio_processing/agc2/BUILD.gn b/modules/audio_processing/agc2/BUILD.gn
index 8710164..aca80d4 100644
--- a/modules/audio_processing/agc2/BUILD.gn
+++ b/modules/audio_processing/agc2/BUILD.gn
@@ -32,6 +32,7 @@
     "../../../rtc_base:gtest_prod",
     "../../../rtc_base:rtc_base_approved",
     "../../../rtc_base:safe_minmax",
+    "../../../system_wrappers:metrics_api",
   ]
 }
 
diff --git a/modules/audio_processing/agc2/interpolated_gain_curve.cc b/modules/audio_processing/agc2/interpolated_gain_curve.cc
index 0cc8f90..69602b5 100644
--- a/modules/audio_processing/agc2/interpolated_gain_curve.cc
+++ b/modules/audio_processing/agc2/interpolated_gain_curve.cc
@@ -14,8 +14,27 @@
 #include "modules/audio_processing/logging/apm_data_dumper.h"
 #include "rtc_base/checks.h"
 #include "rtc_base/logging.h"
+#include "system_wrappers/include/metrics.h"
 
 namespace webrtc {
+namespace {
+void LogRegionStats(const InterpolatedGainCurve::Stats& stats) {
+  using Region = InterpolatedGainCurve::GainCurveRegion;
+
+  std::string histogram_name = "WebRTC.Audio.AGC2.FixedDigitalGainCurveRegion.";
+  if (stats.region == Region::kIdentity) {
+    histogram_name += "Identity";
+  } else if (stats.region == Region::kKnee) {
+    histogram_name += "Knee";
+  } else if (stats.region == Region::kLimiter) {
+    histogram_name += "Limiter";
+  } else {
+    histogram_name += "Saturation";
+  }
+  RTC_HISTOGRAM_COUNTS_10000(histogram_name,
+                             stats.region_duration_frames / 100);
+}
+}  // namespace
 
 constexpr std::array<float, kInterpolatedGainCurveTotalPoints>
     InterpolatedGainCurve::approximation_params_x_;
@@ -31,7 +50,6 @@
 
 InterpolatedGainCurve::~InterpolatedGainCurve() {
   if (stats_.available) {
-    // TODO(alessiob): We might want to add these stats as RTC metrics.
     RTC_DCHECK(apm_data_dumper_);
     apm_data_dumper_->DumpRaw("agc2_interp_gain_curve_lookups_identity",
                               stats_.look_ups_identity_region);
@@ -41,21 +59,37 @@
                               stats_.look_ups_limiter_region);
     apm_data_dumper_->DumpRaw("agc2_interp_gain_curve_lookups_saturation",
                               stats_.look_ups_saturation_region);
+    LogRegionStats(stats_);
   }
 }
 
 void InterpolatedGainCurve::UpdateStats(float input_level) const {
   stats_.available = true;
 
+  GainCurveRegion region;
+
   if (input_level < approximation_params_x_[0]) {
     stats_.look_ups_identity_region++;
+    region = GainCurveRegion::kIdentity;
   } else if (input_level <
              approximation_params_x_[kInterpolatedGainCurveKneePoints - 1]) {
     stats_.look_ups_knee_region++;
+    region = GainCurveRegion::kKnee;
   } else if (input_level < kMaxInputLevelLinear) {
     stats_.look_ups_limiter_region++;
+    region = GainCurveRegion::kLimiter;
   } else {
     stats_.look_ups_saturation_region++;
+    region = GainCurveRegion::kSaturation;
+  }
+
+  if (region == stats_.region) {
+    ++stats_.region_duration_frames;
+  } else {
+    LogRegionStats(stats_);
+
+    stats_.region_duration_frames = 0;
+    stats_.region = region;
   }
 }
 
diff --git a/modules/audio_processing/agc2/interpolated_gain_curve.h b/modules/audio_processing/agc2/interpolated_gain_curve.h
index 533afe2..ddceaaa 100644
--- a/modules/audio_processing/agc2/interpolated_gain_curve.h
+++ b/modules/audio_processing/agc2/interpolated_gain_curve.h
@@ -33,6 +33,13 @@
 // estimates of the gain to apply given an estimated input level.
 class InterpolatedGainCurve {
  public:
+  enum class GainCurveRegion {
+    kIdentity = 0,
+    kKnee = 1,
+    kLimiter = 2,
+    kSaturation = 3
+  };
+
   struct Stats {
     // Region in which the output level equals the input one.
     size_t look_ups_identity_region = 0;
@@ -45,6 +52,11 @@
     size_t look_ups_saturation_region = 0;
     // True if stats have been populated.
     bool available = false;
+
+    // The current region, and for how many frames the level has been
+    // in that region.
+    GainCurveRegion region = GainCurveRegion::kIdentity;
+    int64_t region_duration_frames = 0;
   };
 
   // InterpolatedGainCurve(InterpolatedGainCurve&&);