Restricting NetEq postpone decoding after expand.

Bug: webrtc:9289
Change-Id: I923f304e6c12423fe5323c62484a27346033b19a
Reviewed-on: https://webrtc-review.googlesource.com/c/98320
Commit-Queue: Minyue Li <minyue@webrtc.org>
Reviewed-by: Henrik Lundin <henrik.lundin@webrtc.org>
Reviewed-by: Ivo Creusen <ivoc@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#24966}
diff --git a/modules/audio_coding/neteq/decision_logic.cc b/modules/audio_coding/neteq/decision_logic.cc
index 6b61555..349fdab 100644
--- a/modules/audio_coding/neteq/decision_logic.cc
+++ b/modules/audio_coding/neteq/decision_logic.cc
@@ -21,8 +21,38 @@
 #include "modules/audio_coding/neteq/packet_buffer.h"
 #include "modules/audio_coding/neteq/sync_buffer.h"
 #include "modules/include/module_common_types.h"
+#include "rtc_base/logging.h"
 #include "system_wrappers/include/field_trial.h"
 
+namespace {
+constexpr char kPostponeDecodingFieldTrial[] =
+    "WebRTC-Audio-NetEqPostponeDecodingAfterExpand";
+
+int GetPostponeDecodingLevel() {
+  const bool enabled =
+      webrtc::field_trial::IsEnabled(kPostponeDecodingFieldTrial);
+  if (!enabled)
+    return 0;
+
+  constexpr int kDefaultPostponeDecodingLevel = 50;
+  const std::string field_trial_string =
+      webrtc::field_trial::FindFullName(kPostponeDecodingFieldTrial);
+  int value = -1;
+  if (sscanf(field_trial_string.c_str(), "Enabled-%d", &value) == 1) {
+    if (value >= 0 && value <= 100) {
+      return value;
+    } else {
+      RTC_LOG(LS_WARNING)
+          << "Wrong value (" << value
+          << ") for postpone decoding after expand, using default ("
+          << kDefaultPostponeDecodingLevel << ")";
+    }
+  }
+  return kDefaultPostponeDecodingLevel;
+}
+
+}  // namespace
+
 namespace webrtc {
 
 DecisionLogic* DecisionLogic::Create(int fs_hz,
@@ -59,8 +89,7 @@
       timescale_countdown_(
           tick_timer_->GetNewCountdown(kMinTimescaleInterval + 1)),
       num_consecutive_expands_(0),
-      postpone_decoding_after_expand_(field_trial::IsEnabled(
-          "WebRTC-Audio-NetEqPostponeDecodingAfterExpand")) {
+      postpone_decoding_level_(GetPostponeDecodingLevel()) {
   delay_manager_->set_streaming_mode(false);
   SetSampleRate(fs_hz, output_size_samples);
 }
@@ -164,12 +193,13 @@
   // if the mute factor is low enough (otherwise the expansion was short enough
   // to not be noticable).
   // Note that the MuteFactor is in Q14, so a value of 16384 corresponds to 1.
-  if (postpone_decoding_after_expand_ &&
-      (prev_mode == kModeExpand || prev_mode == kModeCodecPlc) &&
-      !packet_buffer_.ContainsDtxOrCngPacket(decoder_database_) &&
-      cur_size_samples<static_cast<size_t>(delay_manager_->TargetLevel() *
-                                           packet_length_samples_)>> 8 &&
-      expand.MuteFactor(0) < 16384 / 2) {
+  if ((prev_mode == kModeExpand || prev_mode == kModeCodecPlc) &&
+      expand.MuteFactor(0) < 16384 / 2 &&
+      cur_size_samples < static_cast<size_t>(
+              delay_manager_->TargetLevel() * packet_length_samples_ *
+              postpone_decoding_level_ / 100) >> 8 &&
+      !packet_buffer_.ContainsDtxOrCngPacket(decoder_database_)) {
+    RTC_DCHECK(webrtc::field_trial::IsEnabled(kPostponeDecodingFieldTrial));
     return kExpand;
   }
 
diff --git a/modules/audio_coding/neteq/decision_logic.h b/modules/audio_coding/neteq/decision_logic.h
index 00b8620..39761da 100644
--- a/modules/audio_coding/neteq/decision_logic.h
+++ b/modules/audio_coding/neteq/decision_logic.h
@@ -109,6 +109,10 @@
   }
   void set_prev_time_scale(bool value) { prev_time_scale_ = value; }
 
+  int postpone_decoding_level_for_test() const {
+    return postpone_decoding_level_;
+  }
+
  private:
   // The value 5 sets maximum time-stretch rate to about 100 ms/s.
   static const int kMinTimescaleInterval = 5;
@@ -181,7 +185,7 @@
   bool disallow_time_stretching_;
   std::unique_ptr<TickTimer::Countdown> timescale_countdown_;
   int num_consecutive_expands_;
-  const bool postpone_decoding_after_expand_;
+  const int postpone_decoding_level_;
 
   RTC_DISALLOW_COPY_AND_ASSIGN(DecisionLogic);
 };
diff --git a/modules/audio_coding/neteq/decision_logic_unittest.cc b/modules/audio_coding/neteq/decision_logic_unittest.cc
index 6929daa..08720d1 100644
--- a/modules/audio_coding/neteq/decision_logic_unittest.cc
+++ b/modules/audio_coding/neteq/decision_logic_unittest.cc
@@ -17,6 +17,7 @@
 #include "modules/audio_coding/neteq/delay_peak_detector.h"
 #include "modules/audio_coding/neteq/packet_buffer.h"
 #include "modules/audio_coding/neteq/tick_timer.h"
+#include "test/field_trial.h"
 #include "test/gtest.h"
 #include "test/mock_audio_decoder_factory.h"
 
@@ -38,6 +39,62 @@
   delete logic;
 }
 
+TEST(DecisionLogic, PostponeDecodingAfterExpansionSettings) {
+  constexpr int kDefaultPostponeDecodingLevel = 50;
+  constexpr int kFsHz = 8000;
+  constexpr int kOutputSizeSamples = kFsHz / 100;  // Samples per 10 ms.
+  DecoderDatabase decoder_database(
+      new rtc::RefCountedObject<MockAudioDecoderFactory>, absl::nullopt);
+  TickTimer tick_timer;
+  PacketBuffer packet_buffer(10, &tick_timer);
+  DelayPeakDetector delay_peak_detector(&tick_timer);
+  DelayManager delay_manager(240, &delay_peak_detector, &tick_timer);
+  BufferLevelFilter buffer_level_filter;
+  {
+    test::ScopedFieldTrials field_trial(
+        "WebRTC-Audio-NetEqPostponeDecodingAfterExpand/Enabled/");
+    DecisionLogic logic(kFsHz, kOutputSizeSamples, false, &decoder_database,
+                        packet_buffer, &delay_manager, &buffer_level_filter,
+                        &tick_timer);
+    EXPECT_EQ(kDefaultPostponeDecodingLevel,
+              logic.postpone_decoding_level_for_test());
+  }
+  {
+    test::ScopedFieldTrials field_trial(
+        "WebRTC-Audio-NetEqPostponeDecodingAfterExpand/Enabled-65/");
+    DecisionLogic logic(kFsHz, kOutputSizeSamples, false, &decoder_database,
+                        packet_buffer, &delay_manager, &buffer_level_filter,
+                        &tick_timer);
+    EXPECT_EQ(65, logic.postpone_decoding_level_for_test());
+  }
+  {
+    test::ScopedFieldTrials field_trial(
+        "WebRTC-Audio-NetEqPostponeDecodingAfterExpand/Disabled/");
+    DecisionLogic logic(kFsHz, kOutputSizeSamples, false, &decoder_database,
+                        packet_buffer, &delay_manager, &buffer_level_filter,
+                        &tick_timer);
+    EXPECT_EQ(0, logic.postpone_decoding_level_for_test());
+  }
+  {
+    test::ScopedFieldTrials field_trial(
+        "WebRTC-Audio-NetEqPostponeDecodingAfterExpand/Enabled--1/");
+    DecisionLogic logic(kFsHz, kOutputSizeSamples, false, &decoder_database,
+                        packet_buffer, &delay_manager, &buffer_level_filter,
+                        &tick_timer);
+    EXPECT_EQ(kDefaultPostponeDecodingLevel,
+              logic.postpone_decoding_level_for_test());
+  }
+  {
+    test::ScopedFieldTrials field_trial(
+        "WebRTC-Audio-NetEqPostponeDecodingAfterExpand/Enabled-101/");
+    DecisionLogic logic(kFsHz, kOutputSizeSamples, false, &decoder_database,
+                        packet_buffer, &delay_manager, &buffer_level_filter,
+                        &tick_timer);
+    EXPECT_EQ(kDefaultPostponeDecodingLevel,
+              logic.postpone_decoding_level_for_test());
+  }
+}
+
 // TODO(hlundin): Write more tests.
 
 }  // namespace webrtc