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&&);