WebRTC AGC Hybrid experiment: wire-up AGC2 params (+ refactoring)

ConfigAutomaticGainControl now supports more AGC2 params that are
needed for experimentation.

The CL includes a refactoring to avoid base::Optional boilerplate and
it improves the docstring for `ConfigAutomaticGainControl()`.

Tested: checked feature activation via AECdump using appr.tc
$ out/Default/Chromium.app/Contents/MacOS/Chromium \
  --enable-features=WebrtcHybridAgc

(cherry picked from commit bdf52e4e5685e5c71668f05d5dee1b4d394587b6)

Bug: chromium:1135177
Change-Id: I0d226b9e7e6f17acb8408af8ded80d80db7cabe0
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2446149
Commit-Queue: Ale Bzk <[email protected]>
Reviewed-by: Henrik Boström <[email protected]>
Cr-Original-Commit-Position: refs/heads/master@{#813736}
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2462082
Cr-Commit-Position: refs/branch-heads/4280@{#186}
Cr-Branched-From: ea420fb963f9658c9969b6513c56b8f47efa1a2a-refs/heads/master@{#812852}
diff --git a/third_party/blink/renderer/modules/mediastream/media_stream_audio_processor.cc b/third_party/blink/renderer/modules/mediastream/media_stream_audio_processor.cc
index 8b1962f..baa1e425 100644
--- a/third_party/blink/renderer/modules/mediastream/media_stream_audio_processor.cc
+++ b/third_party/blink/renderer/modules/mediastream/media_stream_audio_processor.cc
@@ -581,24 +581,37 @@
 
   if (properties.goog_auto_gain_control ||
       properties.goog_experimental_auto_gain_control) {
-    bool use_hybrid_agc = false;
-    base::Optional<bool> use_peaks_not_rms;
-    base::Optional<int> saturation_margin;
-    if (properties.goog_experimental_auto_gain_control) {
-      use_hybrid_agc = base::FeatureList::IsEnabled(features::kWebRtcHybridAgc);
-      if (use_hybrid_agc) {
-        DCHECK(properties.goog_auto_gain_control)
-            << "Cannot enable hybrid AGC when AGC is disabled.";
-      }
-      use_peaks_not_rms = base::GetFieldTrialParamByFeatureAsBool(
-          features::kWebRtcHybridAgc, "use_peaks_not_rms", false);
-      saturation_margin = base::GetFieldTrialParamByFeatureAsInt(
-          features::kWebRtcHybridAgc, "saturation_margin", -1);
+    base::Optional<blink::AdaptiveGainController2Properties> agc2_properties;
+    if (properties.goog_experimental_auto_gain_control &&
+        base::FeatureList::IsEnabled(features::kWebRtcHybridAgc)) {
+      DCHECK(properties.goog_auto_gain_control)
+          << "Cannot enable hybrid AGC when AGC is disabled.";
+      agc2_properties = blink::AdaptiveGainController2Properties{};
+      agc2_properties->vad_probability_attack =
+          base::GetFieldTrialParamByFeatureAsDouble(
+              features::kWebRtcHybridAgc, "vad_probability_attack", 1.0);
+      agc2_properties->use_peaks_not_rms =
+          base::GetFieldTrialParamByFeatureAsBool(features::kWebRtcHybridAgc,
+                                                  "use_peaks_not_rms", false);
+      agc2_properties->level_estimator_speech_frames_threshold =
+          base::GetFieldTrialParamByFeatureAsInt(
+              features::kWebRtcHybridAgc,
+              "level_estimator_speech_frames_threshold", 1);
+      agc2_properties->initial_saturation_margin_db =
+          base::GetFieldTrialParamByFeatureAsInt(
+              features::kWebRtcHybridAgc, "initial_saturation_margin", 20);
+      agc2_properties->extra_saturation_margin_db =
+          base::GetFieldTrialParamByFeatureAsInt(features::kWebRtcHybridAgc,
+                                                 "extra_saturation_margin", 5);
+      agc2_properties->gain_applier_speech_frames_threshold =
+          base::GetFieldTrialParamByFeatureAsInt(
+              features::kWebRtcHybridAgc,
+              "gain_applier_speech_frames_threshold", 1);
     }
     blink::ConfigAutomaticGainControl(
-        &apm_config, properties.goog_auto_gain_control,
-        properties.goog_experimental_auto_gain_control, use_hybrid_agc,
-        use_peaks_not_rms, saturation_margin, gain_control_compression_gain_db);
+        properties.goog_auto_gain_control,
+        properties.goog_experimental_auto_gain_control, agc2_properties,
+        gain_control_compression_gain_db, apm_config);
   }
 
   if (goog_typing_detection) {
diff --git a/third_party/blink/renderer/platform/mediastream/media_stream_audio_processor_options.cc b/third_party/blink/renderer/platform/mediastream/media_stream_audio_processor_options.cc
index 32b1d40..1f7ad74 100644
--- a/third_party/blink/renderer/platform/mediastream/media_stream_audio_processor_options.cc
+++ b/third_party/blink/renderer/platform/mediastream/media_stream_audio_processor_options.cc
@@ -185,23 +185,22 @@
 }
 
 void ConfigAutomaticGainControl(
-    AudioProcessing::Config* apm_config,
     bool agc_enabled,
     bool experimental_agc_enabled,
-    bool use_hybrid_agc,
-    base::Optional<bool> hybrid_agc_use_peaks_not_rms,
-    base::Optional<int> hybrid_agc_saturation_margin,
-    base::Optional<double> compression_gain_db) {
+    base::Optional<AdaptiveGainController2Properties> agc2_properties,
+    base::Optional<double> compression_gain_db,
+    AudioProcessing::Config& apm_config) {
   const bool use_fixed_digital_agc2 = agc_enabled &&
                                       !experimental_agc_enabled &&
                                       compression_gain_db.has_value();
+  const bool use_hybrid_agc = agc2_properties.has_value();
   const bool agc1_enabled =
       agc_enabled && (use_hybrid_agc || !use_fixed_digital_agc2);
 
   // Configure AGC1.
   if (agc1_enabled) {
-    apm_config->gain_controller1.enabled = true;
-    apm_config->gain_controller1.mode =
+    apm_config.gain_controller1.enabled = true;
+    apm_config.gain_controller1.mode =
 #if defined(OS_ANDROID)
         AudioProcessing::Config::GainController1::Mode::kFixedDigital;
 #else
@@ -211,32 +210,44 @@
 
   // Configure AGC2.
   if (experimental_agc_enabled) {
-    DCHECK(hybrid_agc_use_peaks_not_rms.has_value() &&
-           hybrid_agc_saturation_margin.has_value());
     // Experimental AGC is enabled. Hybrid AGC may or may not be enabled. Config
     // AGC2 with adaptive mode and the given options, while ignoring
     // |use_fixed_digital_agc2|.
-    apm_config->gain_controller2.enabled = use_hybrid_agc;
-    apm_config->gain_controller2.fixed_digital.gain_db = 0.f;
-    apm_config->gain_controller2.adaptive_digital.enabled = true;
+    apm_config.gain_controller2.enabled = use_hybrid_agc;
+    apm_config.gain_controller2.fixed_digital.gain_db = 0.f;
+    apm_config.gain_controller2.adaptive_digital.enabled = true;
 
-    using LevelEstimator =
-        AudioProcessing::Config::GainController2::LevelEstimator;
-    apm_config->gain_controller2.adaptive_digital.level_estimator =
-        hybrid_agc_use_peaks_not_rms.value() ? LevelEstimator::kPeak
+    if (use_hybrid_agc) {
+      auto& adaptive_digital = apm_config.gain_controller2.adaptive_digital;
+
+      adaptive_digital.vad_probability_attack =
+          agc2_properties->vad_probability_attack;
+
+      using LevelEstimator =
+          AudioProcessing::Config::GainController2::LevelEstimator;
+      adaptive_digital.level_estimator = agc2_properties->use_peaks_not_rms
+                                             ? LevelEstimator::kPeak
                                              : LevelEstimator::kRms;
 
-    if (hybrid_agc_saturation_margin.value() != -1) {
-      apm_config->gain_controller2.adaptive_digital.extra_saturation_margin_db =
-          hybrid_agc_saturation_margin.value();
+      adaptive_digital.level_estimator_adjacent_speech_frames_threshold =
+          agc2_properties->level_estimator_speech_frames_threshold;
+
+      adaptive_digital.initial_saturation_margin_db =
+          agc2_properties->initial_saturation_margin_db;
+
+      adaptive_digital.extra_saturation_margin_db =
+          agc2_properties->extra_saturation_margin_db;
+
+      adaptive_digital.gain_applier_adjacent_speech_frames_threshold =
+          agc2_properties->gain_applier_speech_frames_threshold;
     }
   } else if (use_fixed_digital_agc2) {
     // Experimental AGC is disabled, thus hybrid AGC is disabled. Config AGC2
     // with fixed gain mode.
-    apm_config->gain_controller2.enabled = true;
-    apm_config->gain_controller2.fixed_digital.gain_db =
+    apm_config.gain_controller2.enabled = true;
+    apm_config.gain_controller2.fixed_digital.gain_db =
         compression_gain_db.value();
-    apm_config->gain_controller2.adaptive_digital.enabled = false;
+    apm_config.gain_controller2.adaptive_digital.enabled = false;
   }
 }
 
diff --git a/third_party/blink/renderer/platform/mediastream/media_stream_audio_processor_options.h b/third_party/blink/renderer/platform/mediastream/media_stream_audio_processor_options.h
index f25d1e2..15779e8c 100644
--- a/third_party/blink/renderer/platform/mediastream/media_stream_audio_processor_options.h
+++ b/third_party/blink/renderer/platform/mediastream/media_stream_audio_processor_options.h
@@ -115,15 +115,27 @@
 PLATFORM_EXPORT void StopEchoCancellationDump(
     AudioProcessing* audio_processing);
 
-// Enables automatic gain control with flags and optional configures.
+// Adaptive Gain Controller 2 (aka AGC2) properties.
+struct PLATFORM_EXPORT AdaptiveGainController2Properties {
+  float vad_probability_attack;
+  bool use_peaks_not_rms;
+  int level_estimator_speech_frames_threshold;
+  int initial_saturation_margin_db;
+  int extra_saturation_margin_db;
+  int gain_applier_speech_frames_threshold;
+};
+
+// Configures automatic gain control in `apm_config`. If `agc_enabled` is true
+// and `agc2_properties` is specified, the AGC2 adaptive digital replaces the
+// adaptive digital controller of AGC1 - i.e., hybrid configuration (AGC1 analog
+// plus AGC2 adaptive digital).
+// TODO(crbug.com/webrtc/7494): Clean up once hybrid AGC experiment finalized.
 PLATFORM_EXPORT void ConfigAutomaticGainControl(
-    AudioProcessing::Config* apm_config,
     bool agc_enabled,
     bool experimental_agc_enabled,
-    bool use_hybrid_agc,
-    base::Optional<bool> hybrid_agc_use_peaks_not_rms,
-    base::Optional<int> hybrid_agc_saturation_margin,
-    base::Optional<double> compression_gain_db);
+    base::Optional<AdaptiveGainController2Properties> agc2_properties,
+    base::Optional<double> compression_gain_db,
+    AudioProcessing::Config& apm_config);
 
 PLATFORM_EXPORT void PopulateApmConfig(
     AudioProcessing::Config* apm_config,
diff --git a/third_party/blink/renderer/platform/mediastream/media_stream_audio_processor_options_test.cc b/third_party/blink/renderer/platform/mediastream/media_stream_audio_processor_options_test.cc
index 8dcdcd482..84d942f 100644
--- a/third_party/blink/renderer/platform/mediastream/media_stream_audio_processor_options_test.cc
+++ b/third_party/blink/renderer/platform/mediastream/media_stream_audio_processor_options_test.cc
@@ -11,11 +11,11 @@
 
 TEST(ConfigAutomaticGainControlTest, EnableDefaultAGC1) {
   webrtc::AudioProcessing::Config apm_config;
-  ConfigAutomaticGainControl(&apm_config,
-                             true,   // |agc_enabled|.
-                             false,  // |experimental_agc_enabled|.
-                             false,  // |use_hybrid_agc|.
-                             base::nullopt, base::nullopt, base::nullopt);
+  ConfigAutomaticGainControl(
+      /*agc_enabled=*/true,
+      /*experimental_agc_enabled=*/false,
+      /*agc2_properties=*/base::nullopt,
+      /*compression_gain_db=*/base::nullopt, apm_config);
   EXPECT_TRUE(apm_config.gain_controller1.enabled);
   EXPECT_EQ(
       apm_config.gain_controller1.mode,
@@ -29,11 +29,10 @@
 TEST(ConfigAutomaticGainControlTest, EnableFixedDigitalAGC2) {
   webrtc::AudioProcessing::Config apm_config;
   const double compression_gain_db = 10.0;
-  ConfigAutomaticGainControl(&apm_config,
-                             true,   // |agc_enabled|.
-                             false,  // |experimental_agc_enabled|.
-                             false,  // |use_hybrid_agc|.
-                             base::nullopt, base::nullopt, compression_gain_db);
+  ConfigAutomaticGainControl(
+      /*agc_enabled=*/true,
+      /*experimental_agc_enabled=*/false,
+      /*agc2_properties=*/base::nullopt, compression_gain_db, apm_config);
   EXPECT_FALSE(apm_config.gain_controller1.enabled);
   EXPECT_TRUE(apm_config.gain_controller2.enabled);
   EXPECT_FALSE(apm_config.gain_controller2.adaptive_digital.enabled);
@@ -43,15 +42,19 @@
 
 TEST(ConfigAutomaticGainControlTest, EnableHybridAGC) {
   webrtc::AudioProcessing::Config apm_config;
-  const bool use_peaks_not_rms = true;
-  const int saturation_margin = 10;
-  const double compression_gain_db = 10.0;  // Will test that it has no effect.
-  ConfigAutomaticGainControl(&apm_config,
-                             true,  // |agc_enabled|.
-                             true,  // |experimental_agc_enabled|.
-                             true,  // |use_hybrid_agc|.
-                             use_peaks_not_rms, saturation_margin,
-                             compression_gain_db);
+  blink::AdaptiveGainController2Properties agc2_properties;
+  agc2_properties.vad_probability_attack = 0.2f;
+  agc2_properties.use_peaks_not_rms = true;
+  agc2_properties.level_estimator_speech_frames_threshold = 3;
+  agc2_properties.initial_saturation_margin_db = 10;
+  agc2_properties.extra_saturation_margin_db = 10;
+  agc2_properties.gain_applier_speech_frames_threshold = 5;
+  const double compression_gain_db = 10.0;
+
+  ConfigAutomaticGainControl(
+      /*agc_enabled=*/true,
+      /*experimental_agc_enabled=*/true, agc2_properties, compression_gain_db,
+      apm_config);
   EXPECT_TRUE(apm_config.gain_controller1.enabled);
   EXPECT_EQ(
       apm_config.gain_controller1.mode,
@@ -61,14 +64,24 @@
       webrtc::AudioProcessing::Config::GainController1::Mode::kAdaptiveAnalog);
 #endif  // defined(OS_ANDROID)
   EXPECT_TRUE(apm_config.gain_controller2.enabled);
+  // `compression_gain_db` has no effect when hybrid AGC is active.
   EXPECT_EQ(apm_config.gain_controller2.fixed_digital.gain_db, 0);
-  EXPECT_TRUE(apm_config.gain_controller2.adaptive_digital.enabled);
+
+  const auto& adaptive_digital = apm_config.gain_controller2.adaptive_digital;
+  EXPECT_TRUE(adaptive_digital.enabled);
+  EXPECT_FLOAT_EQ(adaptive_digital.vad_probability_attack,
+                  agc2_properties.vad_probability_attack);
   EXPECT_EQ(
-      apm_config.gain_controller2.adaptive_digital.level_estimator,
+      adaptive_digital.level_estimator,
       webrtc::AudioProcessing::Config::GainController2::LevelEstimator::kPeak);
-  EXPECT_EQ(
-      apm_config.gain_controller2.adaptive_digital.extra_saturation_margin_db,
-      saturation_margin);
+  EXPECT_EQ(adaptive_digital.level_estimator_adjacent_speech_frames_threshold,
+            agc2_properties.level_estimator_speech_frames_threshold);
+  EXPECT_FLOAT_EQ(adaptive_digital.initial_saturation_margin_db,
+                  agc2_properties.initial_saturation_margin_db);
+  EXPECT_FLOAT_EQ(adaptive_digital.extra_saturation_margin_db,
+                  agc2_properties.extra_saturation_margin_db);
+  EXPECT_EQ(adaptive_digital.gain_applier_adjacent_speech_frames_threshold,
+            agc2_properties.gain_applier_speech_frames_threshold);
 }
 
 TEST(PopulateApmConfigTest, DefaultWithoutConfigJson) {