From 6f2fcb4962e9488e0c251c9315c5f2a5480acdde Mon Sep 17 00:00:00 2001 From: Alex Loiko Date: Wed, 14 Mar 2018 12:27:05 +0100 Subject: [PATCH] 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 Commit-Queue: Alex Loiko Cr-Commit-Position: refs/heads/master@{#22443} --- modules/audio_mixer/BUILD.gn | 1 + modules/audio_mixer/frame_combiner.cc | 32 +++++++++++++++++ modules/audio_mixer/frame_combiner.h | 5 +++ modules/audio_processing/agc2/BUILD.gn | 1 + .../agc2/interpolated_gain_curve.cc | 36 ++++++++++++++++++- .../agc2/interpolated_gain_curve.h | 12 +++++++ 6 files changed, 86 insertions(+), 1 deletion(-) diff --git a/modules/audio_mixer/BUILD.gn b/modules/audio_mixer/BUILD.gn index 644c8ab265..8cb4bfb60e 100644 --- a/modules/audio_mixer/BUILD.gn +++ b/modules/audio_mixer/BUILD.gn @@ -47,6 +47,7 @@ rtc_static_library("audio_mixer_impl") { "../../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 faca0908ae..2c83e2e0b7 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 @@ void FrameCombiner::Combine(const std::vector& mix_list, } InterleaveToAudioFrame(mixing_buffer_view, audio_frame_for_mixing); + + LogMixingStats(mix_list, sample_rate, number_of_streams); +} + +void FrameCombiner::LogMixingStats(const std::vector& 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(number_of_streams)); + RTC_HISTOGRAM_ENUMERATION( + "WebRTC.Audio.AudioMixer.NumIncomingActiveStreams", + static_cast(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 3d43128d6a..14257d27dd 100644 --- a/modules/audio_mixer/frame_combiner.h +++ b/modules/audio_mixer/frame_combiner.h @@ -44,10 +44,15 @@ class FrameCombiner { AudioFrame* audio_frame_for_mixing); private: + void LogMixingStats(const std::vector& mix_list, + int sample_rate, + size_t number_of_streams) const; + LimiterType limiter_type_; std::unique_ptr apm_agc_limiter_; std::unique_ptr 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 87101646cd..aca80d484f 100644 --- a/modules/audio_processing/agc2/BUILD.gn +++ b/modules/audio_processing/agc2/BUILD.gn @@ -32,6 +32,7 @@ rtc_source_set("agc2") { "../../../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 0cc8f9068d..69602b5919 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 InterpolatedGainCurve::approximation_params_x_; @@ -31,7 +50,6 @@ InterpolatedGainCurve::InterpolatedGainCurve(ApmDataDumper* apm_data_dumper) 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 @@ InterpolatedGainCurve::~InterpolatedGainCurve() { 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 533afe2828..ddceaaab93 100644 --- a/modules/audio_processing/agc2/interpolated_gain_curve.h +++ b/modules/audio_processing/agc2/interpolated_gain_curve.h @@ -33,6 +33,13 @@ constexpr float kMaxInputLevelLinear = static_cast(36766.300710566735); // 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 @@ class InterpolatedGainCurve { 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&&);