diff --git a/modules/audio_coding/neteq/delay_manager.cc b/modules/audio_coding/neteq/delay_manager.cc index dd0d75925a..3a74896d62 100644 --- a/modules/audio_coding/neteq/delay_manager.cc +++ b/modules/audio_coding/neteq/delay_manager.cc @@ -72,6 +72,7 @@ absl::optional GetForcedLimitProbability() { struct DelayHistogramConfig { int quantile = 1020054733; // 0.95 in Q30. int forget_factor = 32745; // 0.9993 in Q15. + absl::optional start_forget_weight; }; absl::optional GetDelayHistogramConfig() { @@ -85,16 +86,22 @@ absl::optional GetDelayHistogramConfig() { DelayHistogramConfig config; double percentile = -1.0; double forget_factor = -1.0; - if (sscanf(field_trial_string.c_str(), "Enabled-%lf-%lf", &percentile, - &forget_factor) == 2 && + double start_forget_weight = -1.0; + if (sscanf(field_trial_string.c_str(), "Enabled-%lf-%lf-%lf", &percentile, + &forget_factor, &start_forget_weight) >= 2 && percentile >= 0.0 && percentile <= 100.0 && forget_factor >= 0.0 && forget_factor <= 1.0) { config.quantile = PercentileToQuantile(percentile); config.forget_factor = (1 << 15) * forget_factor; + if (start_forget_weight >= 1) { + config.start_forget_weight = start_forget_weight; + } } RTC_LOG(LS_INFO) << "Delay histogram config:" << " quantile=" << config.quantile - << " forget_factor=" << config.forget_factor; + << " forget_factor=" << config.forget_factor + << " start_forget_weight=" + << config.start_forget_weight.value_or(0); return absl::make_optional(config); } return absl::nullopt; @@ -182,8 +189,8 @@ std::unique_ptr DelayManager::Create( if (delay_histogram_config) { DelayHistogramConfig config = delay_histogram_config.value(); quantile = config.quantile; - histogram = - absl::make_unique(kDelayBuckets, config.forget_factor); + histogram = absl::make_unique( + kDelayBuckets, config.forget_factor, config.start_forget_weight); mode = RELATIVE_ARRIVAL_DELAY; } else { quantile = GetForcedLimitProbability().value_or(kLimitProbability); diff --git a/modules/audio_coding/neteq/delay_manager.h b/modules/audio_coding/neteq/delay_manager.h index 279e60842d..3075bfb341 100644 --- a/modules/audio_coding/neteq/delay_manager.h +++ b/modules/audio_coding/neteq/delay_manager.h @@ -140,7 +140,7 @@ class DelayManager { // These accessors are only intended for testing purposes. HistogramMode histogram_mode() const { return histogram_mode_; } int histogram_quantile() const { return histogram_quantile_; } - int histogram_forget_factor() const { return histogram_->forget_factor(); } + Histogram* histogram() const { return histogram_.get(); } private: // Provides value which minimum delay can't exceed based on current buffer diff --git a/modules/audio_coding/neteq/delay_manager_unittest.cc b/modules/audio_coding/neteq/delay_manager_unittest.cc index eb1fabc420..1004261ffe 100644 --- a/modules/audio_coding/neteq/delay_manager_unittest.cc +++ b/modules/audio_coding/neteq/delay_manager_unittest.cc @@ -643,7 +643,10 @@ TEST_F(DelayManagerTest, DelayHistogramFieldTrial) { EXPECT_EQ(DelayManager::HistogramMode::RELATIVE_ARRIVAL_DELAY, dm_->histogram_mode()); EXPECT_EQ(1030792151, dm_->histogram_quantile()); // 0.96 in Q30. - EXPECT_EQ(32702, dm_->histogram_forget_factor()); // 0.998 in Q15. + EXPECT_EQ( + 32702, + dm_->histogram()->base_forget_factor_for_testing()); // 0.998 in Q15. + EXPECT_FALSE(dm_->histogram()->start_forget_weight_for_testing()); } { test::ScopedFieldTrials field_trial( @@ -652,7 +655,10 @@ TEST_F(DelayManagerTest, DelayHistogramFieldTrial) { EXPECT_EQ(DelayManager::HistogramMode::RELATIVE_ARRIVAL_DELAY, dm_->histogram_mode()); EXPECT_EQ(1046898278, dm_->histogram_quantile()); // 0.975 in Q30. - EXPECT_EQ(32702, dm_->histogram_forget_factor()); // 0.998 in Q15. + EXPECT_EQ( + 32702, + dm_->histogram()->base_forget_factor_for_testing()); // 0.998 in Q15. + EXPECT_FALSE(dm_->histogram()->start_forget_weight_for_testing()); } { // NetEqDelayHistogram should take precedence over @@ -664,7 +670,10 @@ TEST_F(DelayManagerTest, DelayHistogramFieldTrial) { EXPECT_EQ(DelayManager::HistogramMode::RELATIVE_ARRIVAL_DELAY, dm_->histogram_mode()); EXPECT_EQ(1030792151, dm_->histogram_quantile()); // 0.96 in Q30. - EXPECT_EQ(32702, dm_->histogram_forget_factor()); // 0.998 in Q15. + EXPECT_EQ( + 32702, + dm_->histogram()->base_forget_factor_for_testing()); // 0.998 in Q15. + EXPECT_FALSE(dm_->histogram()->start_forget_weight_for_testing()); } { // Invalid parameters. @@ -675,7 +684,10 @@ TEST_F(DelayManagerTest, DelayHistogramFieldTrial) { dm_->histogram_mode()); EXPECT_EQ(kDefaultHistogramQuantile, dm_->histogram_quantile()); // 0.95 in Q30. - EXPECT_EQ(kForgetFactor, dm_->histogram_forget_factor()); // 0.9993 in Q15. + EXPECT_EQ( + kForgetFactor, + dm_->histogram()->base_forget_factor_for_testing()); // 0.9993 in Q15. + EXPECT_FALSE(dm_->histogram()->start_forget_weight_for_testing()); } { test::ScopedFieldTrials field_trial( @@ -685,7 +697,30 @@ TEST_F(DelayManagerTest, DelayHistogramFieldTrial) { dm_->histogram_mode()); EXPECT_EQ(kDefaultHistogramQuantile, dm_->histogram_quantile()); // 0.95 in Q30. - EXPECT_EQ(kForgetFactor, dm_->histogram_forget_factor()); // 0.9993 in Q15. + EXPECT_EQ( + kForgetFactor, + dm_->histogram()->base_forget_factor_for_testing()); // 0.9993 in Q15. + EXPECT_FALSE(dm_->histogram()->start_forget_weight_for_testing()); + } + + // Test parameter for new call start adaptation. + { + test::ScopedFieldTrials field_trial( + "WebRTC-Audio-NetEqDelayHistogram/Enabled-96-0.998-1/"); + RecreateDelayManager(); + EXPECT_EQ(dm_->histogram()->start_forget_weight_for_testing().value(), 1.0); + } + { + test::ScopedFieldTrials field_trial( + "WebRTC-Audio-NetEqDelayHistogram/Enabled-96-0.998-1.5/"); + RecreateDelayManager(); + EXPECT_EQ(dm_->histogram()->start_forget_weight_for_testing().value(), 1.5); + } + { + test::ScopedFieldTrials field_trial( + "WebRTC-Audio-NetEqDelayHistogram/Enabled-96-0.998-0.5/"); + RecreateDelayManager(); + EXPECT_FALSE(dm_->histogram()->start_forget_weight_for_testing()); } } diff --git a/modules/audio_coding/neteq/histogram.cc b/modules/audio_coding/neteq/histogram.cc index 2f7265321e..fc0801e482 100644 --- a/modules/audio_coding/neteq/histogram.cc +++ b/modules/audio_coding/neteq/histogram.cc @@ -12,23 +12,30 @@ #include #include +#include "absl/types/optional.h" #include "modules/audio_coding/neteq/histogram.h" #include "rtc_base/checks.h" #include "rtc_base/numerics/safe_conversions.h" namespace webrtc { -Histogram::Histogram(size_t num_buckets, int forget_factor) +Histogram::Histogram(size_t num_buckets, + int forget_factor, + absl::optional start_forget_weight) : buckets_(num_buckets, 0), forget_factor_(0), - base_forget_factor_(forget_factor) {} + base_forget_factor_(forget_factor), + add_count_(0), + start_forget_weight_(start_forget_weight) { + RTC_DCHECK_LT(base_forget_factor_, 1 << 15); +} Histogram::~Histogram() {} // Each element in the vector is first multiplied by the forgetting factor // |forget_factor_|. Then the vector element indicated by |iat_packets| is then // increased (additive) by 1 - |forget_factor_|. This way, the probability of -// |iat_packets| is slightly increased, while the sum of the histogram remains +// |value| is slightly increased, while the sum of the histogram remains // constant (=1). // Due to inaccuracies in the fixed-point arithmetic, the histogram may no // longer sum up to 1 (in Q30) after the update. To correct this, a correction @@ -37,7 +44,7 @@ Histogram::~Histogram() {} // The forgetting factor |forget_factor_| is also updated. When the DelayManager // is reset, the factor is set to 0 to facilitate rapid convergence in the // beginning. With each update of the histogram, the factor is increased towards -// the steady-state value |kIatFactor_|. +// the steady-state value |base_forget_factor_|. void Histogram::Add(int value) { RTC_DCHECK(value >= 0); RTC_DCHECK(value < static_cast(buckets_.size())); @@ -72,9 +79,28 @@ void Histogram::Add(int value) { } RTC_DCHECK(vector_sum == 0); // Verify that the above is correct. + ++add_count_; + // Update |forget_factor_| (changes only during the first seconds after a // reset). The factor converges to |base_forget_factor_|. - forget_factor_ += (base_forget_factor_ - forget_factor_ + 3) >> 2; + if (start_forget_weight_) { + if (forget_factor_ != base_forget_factor_) { + int old_forget_factor = forget_factor_; + int forget_factor = + (1 << 15) * (1 - start_forget_weight_.value() / (add_count_ + 1)); + forget_factor_ = + std::max(0, std::min(base_forget_factor_, forget_factor)); + // The histogram is updated recursively by forgetting the old histogram + // with |forget_factor_| and adding a new sample multiplied by |1 - + // forget_factor_|. We need to make sure that the effective weight on the + // new sample is no smaller than those on the old samples, i.e., to + // satisfy the following DCHECK. + RTC_DCHECK_GE((1 << 15) - forget_factor_, + ((1 << 15) - old_forget_factor) * forget_factor_ >> 15); + } + } else { + forget_factor_ += (base_forget_factor_ - forget_factor_ + 3) >> 2; + } } int Histogram::Quantile(int probability) { @@ -112,6 +138,7 @@ void Histogram::Reset() { bucket = temp_prob << 16; } forget_factor_ = 0; // Adapt the histogram faster for the first few packets. + add_count_ = 0; } int Histogram::NumBuckets() const { diff --git a/modules/audio_coding/neteq/histogram.h b/modules/audio_coding/neteq/histogram.h index fc8f612ac8..7eb90d97a3 100644 --- a/modules/audio_coding/neteq/histogram.h +++ b/modules/audio_coding/neteq/histogram.h @@ -15,12 +15,16 @@ #include +#include "absl/types/optional.h" + namespace webrtc { class Histogram { public: // Creates histogram with capacity |num_buckets| and |forget_factor| in Q15. - Histogram(size_t num_buckets, int forget_factor); + Histogram(size_t num_buckets, + int forget_factor, + absl::optional start_forget_weight = absl::nullopt); virtual ~Histogram(); @@ -43,17 +47,24 @@ class Histogram { // Returns the probability for each bucket in Q30. std::vector buckets() const { return buckets_; } - int forget_factor() const { return base_forget_factor_; } - // Made public for testing. static std::vector ScaleBuckets(const std::vector& buckets, int old_bucket_width, int new_bucket_width); + // Accessors only intended for testing purposes. + int base_forget_factor_for_testing() const { return base_forget_factor_; } + int forget_factor_for_testing() const { return forget_factor_; } + absl::optional start_forget_weight_for_testing() const { + return start_forget_weight_; + } + private: std::vector buckets_; int forget_factor_; // Q15 const int base_forget_factor_; + int add_count_; + const absl::optional start_forget_weight_; }; } // namespace webrtc diff --git a/modules/audio_coding/neteq/histogram_unittest.cc b/modules/audio_coding/neteq/histogram_unittest.cc index 7a887c80ce..6255a0c8d1 100644 --- a/modules/audio_coding/neteq/histogram_unittest.cc +++ b/modules/audio_coding/neteq/histogram_unittest.cc @@ -168,4 +168,15 @@ TEST(HistogramTest, OverflowTest) { EXPECT_EQ(scaled_buckets, expected_result); } +TEST(HistogramTest, ReachSteadyStateForgetFactor) { + static constexpr int kSteadyStateForgetFactor = (1 << 15) * 0.9993; + Histogram histogram(100, kSteadyStateForgetFactor, 1.0); + histogram.Reset(); + int n = (1 << 15) / ((1 << 15) - kSteadyStateForgetFactor); + for (int i = 0; i < n; ++i) { + histogram.Add(0); + } + EXPECT_EQ(histogram.forget_factor_for_testing(), kSteadyStateForgetFactor); +} + } // namespace webrtc