From da964d7559766c4bbf27b197ad8cbe2c9c94d072 Mon Sep 17 00:00:00 2001 From: Alessio Bazzica Date: Thu, 8 Dec 2022 15:33:01 +0100 Subject: [PATCH] `InputVolumeStatsReporter`: replace `WebRTC.Audio.AgcSetLevel` The `WebRTC.Audio.AgcSetLevel` name is misleading and the histogram is logged for each channel - but the input volume is one for all the channels. Changes: - `WebRTC.Audio.Apm.RecommendedInputVolume.OnChangeToMatchTarget` is the new name - Now available not only in `AgcManagerDirect` (AGC1), but also in `InputVolumeController` (AGC2) - Logged once and not for each channel - Also add the following AGC implementation agnostic histograms - `WebRTC.Audio.Apm.AppliedInputVolume.OnChange` - `WebRTC.Audio.Apm.RecommendedInputVolume.OnChange` - Fix `SpeechSamplesReader::Feed()` in the unit tests, which did not set the applied input volume and apply the recommended one The histogram definitions are updated in crrev.com/c/4087426. Bug: webrtc:7494 Change-Id: I03c5dfb08165805215ca2c4bb6509b16de8d68da Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/287081 Commit-Queue: Alessio Bazzica Reviewed-by: Hanna Silen Cr-Commit-Position: refs/heads/main@{#38852} --- .../agc/agc_manager_direct.cc | 11 +- .../agc2/input_volume_controller.cc | 8 + .../agc2/input_volume_controller_unittest.cc | 145 ++++++++++++------ .../agc2/input_volume_stats_reporter.cc | 16 +- .../agc2/input_volume_stats_reporter.h | 6 +- .../input_volume_stats_reporter_unittest.cc | 41 ++++- 6 files changed, 169 insertions(+), 58 deletions(-) diff --git a/modules/audio_processing/agc/agc_manager_direct.cc b/modules/audio_processing/agc/agc_manager_direct.cc index 267c209512..67c9c6ddf9 100644 --- a/modules/audio_processing/agc/agc_manager_direct.cc +++ b/modules/audio_processing/agc/agc_manager_direct.cc @@ -407,9 +407,6 @@ void MonoAgc::UpdateGain(int rms_error_db) { int old_level = level_; SetLevel(LevelFromGainError(residual_gain, level_, min_mic_level_)); if (old_level != level_) { - // level_ was updated by SetLevel; log the new value. - RTC_HISTOGRAM_COUNTS_LINEAR("WebRTC.Audio.AgcSetLevel", level_, 1, - kMaxMicLevel, 50); // Reset the AGC since the level has changed. agc_->Reset(); } @@ -627,6 +624,7 @@ void AgcManagerDirect::Process(const AudioBuffer& audio_buffer, absl::optional speech_probability, absl::optional speech_level_dbfs) { AggregateChannelLevels(); + const int volume_after_clipping_handling = recommended_input_volume_; if (!capture_output_used_) { return; @@ -649,6 +647,13 @@ void AgcManagerDirect::Process(const AudioBuffer& audio_buffer, } AggregateChannelLevels(); + if (volume_after_clipping_handling != recommended_input_volume_) { + // The recommended input volume was adjusted in order to match the target + // level. + RTC_HISTOGRAM_COUNTS_LINEAR( + "WebRTC.Audio.Apm.RecommendedInputVolume.OnChangeToMatchTarget", + recommended_input_volume_, 1, kMaxMicLevel, 50); + } } absl::optional AgcManagerDirect::GetDigitalComressionGain() { diff --git a/modules/audio_processing/agc2/input_volume_controller.cc b/modules/audio_processing/agc2/input_volume_controller.cc index a428db8a62..49ecdf22aa 100644 --- a/modules/audio_processing/agc2/input_volume_controller.cc +++ b/modules/audio_processing/agc2/input_volume_controller.cc @@ -509,6 +509,7 @@ void InputVolumeController::AnalyzePreProcess(const AudioBuffer& audio_buffer) { void InputVolumeController::Process(float speech_probability, absl::optional speech_level_dbfs) { AggregateChannelLevels(); + const int volume_after_clipping_handling = recommended_input_volume_; if (!capture_output_used_) { return; @@ -526,6 +527,13 @@ void InputVolumeController::Process(float speech_probability, } AggregateChannelLevels(); + if (volume_after_clipping_handling != recommended_input_volume_) { + // The recommended input volume was adjusted in order to match the target + // level. + RTC_HISTOGRAM_COUNTS_LINEAR( + "WebRTC.Audio.Apm.RecommendedInputVolume.OnChangeToMatchTarget", + recommended_input_volume_, 1, kMaxInputVolume, 50); + } } void InputVolumeController::HandleCaptureOutputUsedChange( diff --git a/modules/audio_processing/agc2/input_volume_controller_unittest.cc b/modules/audio_processing/agc2/input_volume_controller_unittest.cc index ac443e6573..37c1fda9bc 100644 --- a/modules/audio_processing/agc2/input_volume_controller_unittest.cc +++ b/modules/audio_processing/agc2/input_volume_controller_unittest.cc @@ -18,6 +18,7 @@ #include "rtc_base/numerics/safe_minmax.h" #include "rtc_base/strings/string_builder.h" +#include "system_wrappers/include/metrics.h" #include "test/field_trial.h" #include "test/gmock.h" #include "test/gtest.h" @@ -59,9 +60,9 @@ constexpr InputVolumeControllerConfig kDefaultInputVolumeControllerConfig{}; constexpr ClippingPredictorConfig kDefaultClippingPredictorConfig{}; std::unique_ptr CreateInputVolumeController( - int clipped_level_step, - float clipped_ratio_threshold, - int clipped_wait_frames, + int clipped_level_step = kClippedLevelStep, + float clipped_ratio_threshold = kClippedRatioThreshold, + int clipped_wait_frames = kClippedWaitFrames, bool enable_clipping_predictor = false, int update_input_volume_wait_frames = 0) { InputVolumeControllerConfig config{ @@ -194,6 +195,7 @@ class SpeechSamplesReader { // does not loop. `speech_probability` and `speech_level_dbfs` are passed to // `Process()`. void Feed(int num_frames, + int applied_input_volume, int gain_db, float speech_probability, absl::optional speech_level_dbfs, @@ -214,9 +216,10 @@ class SpeechSamplesReader { return rtc::SafeClamp(static_cast(v) * gain, kMinSample, kMaxSample); }); - + controller.set_stream_analog_level(applied_input_volume); controller.AnalyzePreProcess(audio_buffer_); controller.Process(speech_probability, speech_level_dbfs); + applied_input_volume = controller.recommended_analog_level(); } } @@ -1250,19 +1253,16 @@ TEST_P(InputVolumeControllerParametrizedTest, EmptyRmsErrorHasNoEffect) { GetInputVolumeControllerTestConfig()); controller.Initialize(); - constexpr int kInputVolume = kInitialInputVolume; - controller.set_stream_analog_level(kInputVolume); - // Feed speech with low energy that would trigger an upward adapation of // the analog level if an speech level was not low and the RMS level empty. constexpr int kNumFrames = 125; constexpr int kGainDb = -20; SpeechSamplesReader reader; - reader.Feed(kNumFrames, kGainDb, kLowSpeechProbability, absl::nullopt, - controller); + reader.Feed(kNumFrames, kInitialInputVolume, kGainDb, kLowSpeechProbability, + absl::nullopt, controller); // Check that no adaptation occurs. - ASSERT_EQ(controller.recommended_analog_level(), kInputVolume); + ASSERT_EQ(controller.recommended_analog_level(), kInitialInputVolume); } // Checks that the recommended input volume is not updated unless enough @@ -1281,23 +1281,26 @@ TEST(InputVolumeControllerTest, UpdateInputVolumeWaitFramesIsEffective) { /*update_input_volume_wait_frames=*/100); controller_wait_0->Initialize(); controller_wait_100->Initialize(); - controller_wait_0->set_stream_analog_level(kInputVolume); - controller_wait_100->set_stream_analog_level(kInputVolume); SpeechSamplesReader reader_1; SpeechSamplesReader reader_2; - reader_1.Feed(/*num_frames=*/99, /*gain_db=*/0, kHighSpeechProbability, + reader_1.Feed(/*num_frames=*/99, kInputVolume, /*gain_db=*/0, + kHighSpeechProbability, /*speech_level_dbfs=*/-42.0f, *controller_wait_0); - reader_2.Feed(/*num_frames=*/99, /*gain_db=*/0, kHighSpeechProbability, + reader_2.Feed(/*num_frames=*/99, kInputVolume, /*gain_db=*/0, + kHighSpeechProbability, /*speech_level_dbfs=*/-42.0f, *controller_wait_100); // Check that adaptation only occurs if enough frames have been processed. ASSERT_GT(controller_wait_0->recommended_analog_level(), kInputVolume); ASSERT_EQ(controller_wait_100->recommended_analog_level(), kInputVolume); - reader_1.Feed(/*num_frames=*/1, /*gain_db=*/0, kHighSpeechProbability, + reader_1.Feed(/*num_frames=*/1, controller_wait_0->recommended_analog_level(), + /*gain_db=*/0, kHighSpeechProbability, /*speech_level_dbfs=*/-42.0f, *controller_wait_0); - reader_2.Feed(/*num_frames=*/1, /*gain_db=*/0, kHighSpeechProbability, + reader_2.Feed(/*num_frames=*/1, + controller_wait_100->recommended_analog_level(), /*gain_db=*/0, + kHighSpeechProbability, /*speech_level_dbfs=*/-42.0f, *controller_wait_100); // Check that adaptation only occurs when enough frames have been processed. @@ -1321,38 +1324,36 @@ TEST(InputVolumeControllerTest, SpeechRatioThresholdIsEffective) { /*update_input_volume_wait_frames=*/10); controller_1->Initialize(); controller_2->Initialize(); - controller_1->set_stream_analog_level(kInputVolume); - controller_2->set_stream_analog_level(kInputVolume); SpeechSamplesReader reader_1; SpeechSamplesReader reader_2; - reader_1.Feed(/*num_frames=*/1, /*gain_db=*/0, + reader_1.Feed(/*num_frames=*/1, kInputVolume, /*gain_db=*/0, /*speech_probability=*/0.7f, /*speech_level_dbfs=*/-42.0f, *controller_1); - reader_2.Feed(/*num_frames=*/1, /*gain_db=*/0, + reader_2.Feed(/*num_frames=*/1, kInputVolume, /*gain_db=*/0, /*speech_probability=*/0.4f, /*speech_level_dbfs=*/-42.0f, *controller_2); ASSERT_EQ(controller_1->recommended_analog_level(), kInputVolume); ASSERT_EQ(controller_2->recommended_analog_level(), kInputVolume); - reader_1.Feed(/*num_frames=*/2, /*gain_db=*/0, - /*speech_probability=*/0.4f, /*speech_level_dbfs=*/-42.0f, - *controller_1); - reader_2.Feed(/*num_frames=*/2, /*gain_db=*/0, - /*speech_probability=*/0.4f, /*speech_level_dbfs=*/-42.0f, - *controller_2); + reader_1.Feed( + /*num_frames=*/2, controller_1->recommended_analog_level(), /*gain_db=*/0, + /*speech_probability=*/0.4f, /*speech_level_dbfs=*/-42.0f, *controller_1); + reader_2.Feed( + /*num_frames=*/2, controller_2->recommended_analog_level(), /*gain_db=*/0, + /*speech_probability=*/0.4f, /*speech_level_dbfs=*/-42.0f, *controller_2); ASSERT_EQ(controller_1->recommended_analog_level(), kInputVolume); ASSERT_EQ(controller_2->recommended_analog_level(), kInputVolume); - reader_1.Feed(/*num_frames=*/7, /*gain_db=*/0, - /*speech_probability=*/0.7f, /*speech_level_dbfs=*/-42.0f, - *controller_1); - reader_2.Feed(/*num_frames=*/7, /*gain_db=*/0, - /*speech_probability=*/0.7f, /*speech_level_dbfs=*/-42.0f, - *controller_2); + reader_1.Feed( + /*num_frames=*/7, controller_1->recommended_analog_level(), /*gain_db=*/0, + /*speech_probability=*/0.7f, /*speech_level_dbfs=*/-42.0f, *controller_1); + reader_2.Feed( + /*num_frames=*/7, controller_2->recommended_analog_level(), /*gain_db=*/0, + /*speech_probability=*/0.7f, /*speech_level_dbfs=*/-42.0f, *controller_2); ASSERT_GT(controller_1->recommended_analog_level(), kInputVolume); ASSERT_EQ(controller_2->recommended_analog_level(), kInputVolume); @@ -1374,8 +1375,6 @@ TEST(InputVolumeControllerTest, SpeechProbabilityThresholdIsEffective) { /*update_input_volume_wait_frames=*/10); controller_1->Initialize(); controller_2->Initialize(); - controller_1->set_stream_analog_level(kInputVolume); - controller_2->set_stream_analog_level(kInputVolume); SpeechSamplesReader reader_1; SpeechSamplesReader reader_2; @@ -1384,37 +1383,95 @@ TEST(InputVolumeControllerTest, SpeechProbabilityThresholdIsEffective) { // that make the volume to be adjusted after enough frames have been // processsed and `reader_2` to process inputs that won't make the volume // to be adjusted. - reader_1.Feed(/*num_frames=*/1, /*gain_db=*/0, + reader_1.Feed(/*num_frames=*/1, kInputVolume, /*gain_db=*/0, /*speech_probability=*/0.5f, /*speech_level_dbfs=*/-42.0f, *controller_1); - reader_2.Feed(/*num_frames=*/1, /*gain_db=*/0, + reader_2.Feed(/*num_frames=*/1, kInputVolume, /*gain_db=*/0, /*speech_probability=*/0.49f, /*speech_level_dbfs=*/-42.0f, *controller_2); ASSERT_EQ(controller_1->recommended_analog_level(), kInputVolume); ASSERT_EQ(controller_2->recommended_analog_level(), kInputVolume); - reader_1.Feed(/*num_frames=*/2, /*gain_db=*/0, + reader_1.Feed(/*num_frames=*/2, controller_1->recommended_analog_level(), + /*gain_db=*/0, /*speech_probability=*/0.49f, /*speech_level_dbfs=*/-42.0f, *controller_1); - reader_2.Feed(/*num_frames=*/2, /*gain_db=*/0, + reader_2.Feed(/*num_frames=*/2, controller_2->recommended_analog_level(), + /*gain_db=*/0, /*speech_probability=*/0.49f, /*speech_level_dbfs=*/-42.0f, *controller_2); ASSERT_EQ(controller_1->recommended_analog_level(), kInputVolume); ASSERT_EQ(controller_2->recommended_analog_level(), kInputVolume); - reader_1.Feed(/*num_frames=*/7, /*gain_db=*/0, - /*speech_probability=*/0.5f, /*speech_level_dbfs=*/-42.0f, - *controller_1); - reader_2.Feed(/*num_frames=*/7, /*gain_db=*/0, - /*speech_probability=*/0.5f, /*speech_level_dbfs=*/-42.0f, - *controller_2); + reader_1.Feed( + /*num_frames=*/7, controller_1->recommended_analog_level(), /*gain_db=*/0, + /*speech_probability=*/0.5f, /*speech_level_dbfs=*/-42.0f, *controller_1); + reader_2.Feed( + /*num_frames=*/7, controller_2->recommended_analog_level(), /*gain_db=*/0, + /*speech_probability=*/0.5f, /*speech_level_dbfs=*/-42.0f, *controller_2); ASSERT_GT(controller_1->recommended_analog_level(), kInputVolume); ASSERT_EQ(controller_2->recommended_analog_level(), kInputVolume); } +TEST(InputVolumeControllerTest, + DoNotLogRecommendedInputVolumeOnChangeToMatchTarget) { + SpeechSamplesReader reader; + auto controller = CreateInputVolumeController(); + controller->Initialize(); + // Trigger a downward volume change by inputting audio that clips. Pass a + // speech level that falls in the target range to make sure that the + // adaptation is not made to match the target range. + constexpr int kStartupVolume = 255; + reader.Feed(/*num_frames=*/14, kStartupVolume, /*gain_db=*/50, + kHighSpeechProbability, + /*speech_level_dbfs=*/-20.0f, *controller); + ASSERT_LT(controller->recommended_analog_level(), kStartupVolume); + EXPECT_METRIC_THAT( + metrics::Samples( + "WebRTC.Audio.Apm.RecommendedInputVolume.OnChangeToMatchTarget"), + ::testing::IsEmpty()); +} + +TEST(InputVolumeControllerTest, + LogRecommendedInputVolumeOnUpwardChangeToMatchTarget) { + SpeechSamplesReader reader; + auto controller = CreateInputVolumeController(); + controller->Initialize(); + constexpr int kStartupVolume = 100; + // Trigger an upward volume change by inputting audio that does not clip and + // by passing a speech level below the target range. + reader.Feed(/*num_frames=*/14, kStartupVolume, /*gain_db=*/-6, + kHighSpeechProbability, + /*speech_level_dbfs=*/-50.0f, *controller); + ASSERT_GT(controller->recommended_analog_level(), kStartupVolume); + EXPECT_METRIC_THAT( + metrics::Samples( + "WebRTC.Audio.Apm.RecommendedInputVolume.OnChangeToMatchTarget"), + ::testing::Not(::testing::IsEmpty())); +} + +TEST(InputVolumeControllerTest, + LogRecommendedInputVolumeOnDownwardChangeToMatchTarget) { + SpeechSamplesReader reader; + auto controller = CreateInputVolumeController(); + controller->Initialize(); + constexpr int kStartupVolume = 100; + controller->set_stream_analog_level(kStartupVolume); + // Trigger a downward volume change by inputting audio that does not clip and + // by passing a speech level above the target range. + reader.Feed(/*num_frames=*/14, kStartupVolume, /*gain_db=*/-6, + kHighSpeechProbability, + /*speech_level_dbfs=*/-5.0f, *controller); + ASSERT_LT(controller->recommended_analog_level(), kStartupVolume); + EXPECT_METRIC_THAT( + metrics::Samples( + "WebRTC.Audio.Apm.RecommendedInputVolume.OnChangeToMatchTarget"), + ::testing::Not(::testing::IsEmpty())); +} + TEST(MonoInputVolumeControllerTest, CheckHandleClippingLowersVolume) { constexpr int kInitialInputVolume = 100; constexpr int kInputVolumeStep = 29; diff --git a/modules/audio_processing/agc2/input_volume_stats_reporter.cc b/modules/audio_processing/agc2/input_volume_stats_reporter.cc index cf6149eb49..dfeb2185a9 100644 --- a/modules/audio_processing/agc2/input_volume_stats_reporter.cc +++ b/modules/audio_processing/agc2/input_volume_stats_reporter.cc @@ -50,6 +50,16 @@ constexpr absl::string_view MetricNamePrefix( } } +metrics::Histogram* CreateVolumeHistogram(InputVolumeType input_volume_type) { + char buffer[64]; + rtc::SimpleStringBuilder builder(buffer); + builder << MetricNamePrefix(input_volume_type) << "OnChange"; + return metrics::HistogramFactoryGetCountsLinear(/*name=*/builder.str(), + /*min=*/1, + /*max=*/kMaxInputVolume, + /*bucket_count=*/50); +} + metrics::Histogram* CreateRateHistogram(InputVolumeType input_volume_type, absl::string_view name) { char buffer[64]; @@ -76,7 +86,8 @@ metrics::Histogram* CreateAverageHistogram(InputVolumeType input_volume_type, InputVolumeStatsReporter::InputVolumeStatsReporter(InputVolumeType type) : histograms_( - {.decrease_rate = CreateRateHistogram(type, "DecreaseRate"), + {.on_volume_change = CreateVolumeHistogram(type), + .decrease_rate = CreateRateHistogram(type, "DecreaseRate"), .decrease_average = CreateAverageHistogram(type, "DecreaseAverage"), .increase_rate = CreateRateHistogram(type, "IncreaseRate"), .increase_average = CreateAverageHistogram(type, "IncreaseAverage"), @@ -101,6 +112,9 @@ void InputVolumeStatsReporter::UpdateStatistics(int input_volume) { RTC_DCHECK_LE(input_volume, kMaxInputVolume); if (previous_input_volume_.has_value() && input_volume != previous_input_volume_.value()) { + // Update stats when the input volume changes. + metrics::HistogramAdd(histograms_.on_volume_change, input_volume); + // Update stats that are periodically logged. const int volume_change = input_volume - previous_input_volume_.value(); if (volume_change < 0) { ++volume_update_stats_.num_decreases; diff --git a/modules/audio_processing/agc2/input_volume_stats_reporter.h b/modules/audio_processing/agc2/input_volume_stats_reporter.h index 4df5a85a0c..0037a9a5ab 100644 --- a/modules/audio_processing/agc2/input_volume_stats_reporter.h +++ b/modules/audio_processing/agc2/input_volume_stats_reporter.h @@ -65,6 +65,7 @@ class InputVolumeStatsReporter { // Histograms. struct Histograms { + metrics::Histogram* const on_volume_change; metrics::Histogram* const decrease_rate; metrics::Histogram* const decrease_average; metrics::Histogram* const increase_rate; @@ -72,8 +73,9 @@ class InputVolumeStatsReporter { metrics::Histogram* const update_rate; metrics::Histogram* const update_average; bool AllPointersSet() const { - return !!decrease_rate && !!decrease_average && !!increase_rate && - !!increase_average && !!update_rate && !!update_average; + return !!on_volume_change && !!decrease_rate && !!decrease_average && + !!increase_rate && !!increase_average && !!update_rate && + !!update_average; } } histograms_; diff --git a/modules/audio_processing/agc2/input_volume_stats_reporter_unittest.cc b/modules/audio_processing/agc2/input_volume_stats_reporter_unittest.cc index a3e2ccaf91..e762c1fb59 100644 --- a/modules/audio_processing/agc2/input_volume_stats_reporter_unittest.cc +++ b/modules/audio_processing/agc2/input_volume_stats_reporter_unittest.cc @@ -31,6 +31,10 @@ class InputVolumeStatsReporterTest protected: InputVolumeType InputVolumeType() const { return GetParam(); } + std::string VolumeLabel() const { + return (rtc::StringBuilder(kLabelPrefix) << VolumeTypeLabel() << "OnChange") + .str(); + } std::string DecreaseRateLabel() const { return (rtc::StringBuilder(kLabelPrefix) << VolumeTypeLabel() << "DecreaseRate") @@ -73,7 +77,13 @@ class InputVolumeStatsReporterTest } }; -TEST_P(InputVolumeStatsReporterTest, CheckLogVolumeUpdateStatsEmpty) { +TEST_P(InputVolumeStatsReporterTest, CheckVolumeOnChangeIsEmpty) { + InputVolumeStatsReporter stats_reporter(InputVolumeType()); + stats_reporter.UpdateStatistics(10); + EXPECT_METRIC_THAT(metrics::Samples(VolumeLabel()), ::testing::ElementsAre()); +} + +TEST_P(InputVolumeStatsReporterTest, CheckRateAverageStatsEmpty) { InputVolumeStatsReporter stats_reporter(InputVolumeType()); constexpr int kInputVolume = 10; stats_reporter.UpdateStatistics(kInputVolume); @@ -96,20 +106,33 @@ TEST_P(InputVolumeStatsReporterTest, CheckLogVolumeUpdateStatsEmpty) { ::testing::ElementsAre()); } -TEST_P(InputVolumeStatsReporterTest, CheckLogVolumeUpdateStatsNotEmpty) { +TEST_P(InputVolumeStatsReporterTest, CheckSamples) { InputVolumeStatsReporter stats_reporter(InputVolumeType()); - constexpr int kInputVolume = 10; - stats_reporter.UpdateStatistics(kInputVolume); + + constexpr int kInputVolume1 = 10; + stats_reporter.UpdateStatistics(kInputVolume1); // Update until periodic logging. + constexpr int kInputVolume2 = 12; for (int i = 0; i < kFramesIn60Seconds; i += 2) { - stats_reporter.UpdateStatistics(kInputVolume + 2); - stats_reporter.UpdateStatistics(kInputVolume); + stats_reporter.UpdateStatistics(kInputVolume2); + stats_reporter.UpdateStatistics(kInputVolume1); } // Update until periodic logging. + constexpr int kInputVolume3 = 13; for (int i = 0; i < kFramesIn60Seconds; i += 2) { - stats_reporter.UpdateStatistics(kInputVolume + 3); - stats_reporter.UpdateStatistics(kInputVolume); + stats_reporter.UpdateStatistics(kInputVolume3); + stats_reporter.UpdateStatistics(kInputVolume1); } + + // Check volume changes stats. + EXPECT_METRIC_THAT( + metrics::Samples(VolumeLabel()), + ::testing::ElementsAre( + ::testing::Pair(kInputVolume1, kFramesIn60Seconds), + ::testing::Pair(kInputVolume2, kFramesIn60Seconds / 2), + ::testing::Pair(kInputVolume3, kFramesIn60Seconds / 2))); + + // Check volume change rate stats. EXPECT_METRIC_THAT( metrics::Samples(UpdateRateLabel()), ::testing::ElementsAre(::testing::Pair(kFramesIn60Seconds - 1, 1), @@ -121,6 +144,8 @@ TEST_P(InputVolumeStatsReporterTest, CheckLogVolumeUpdateStatsNotEmpty) { EXPECT_METRIC_THAT( metrics::Samples(IncreaseRateLabel()), ::testing::ElementsAre(::testing::Pair(kFramesIn60Seconds / 2, 2))); + + // Check volume change average stats. EXPECT_METRIC_THAT( metrics::Samples(UpdateAverageLabel()), ::testing::ElementsAre(::testing::Pair(2, 1), ::testing::Pair(3, 1)));