From e9a2ee263142589d936e02b97bbe8e58ddc73097 Mon Sep 17 00:00:00 2001 From: Jakob Ivarsson Date: Wed, 22 May 2019 16:54:09 +0200 Subject: [PATCH] Improve NetEq network adaptation in the beginning of the call. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change the way the forget factor converge to the steady state so that we don't overemphasize the first packets received. The logic is controlled by the delay histogram field trial which has an added parameter to control if emphasis should be even (c=1, default) or put on later packets (c>1) until we reach our steady state forget factor. Bug: webrtc:10411 Change-Id: Ia5d46c22d1a4a66994652f71c8cde664362bfacb Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/137050 Reviewed-by: Minyue Li Reviewed-by: Chen Xing Commit-Queue: Jakob Ivarsson‎ Cr-Commit-Position: refs/heads/master@{#28039} --- modules/audio_coding/neteq/delay_manager.cc | 17 ++++--- modules/audio_coding/neteq/delay_manager.h | 2 +- .../neteq/delay_manager_unittest.cc | 45 ++++++++++++++++--- modules/audio_coding/neteq/histogram.cc | 37 ++++++++++++--- modules/audio_coding/neteq/histogram.h | 17 +++++-- .../audio_coding/neteq/histogram_unittest.cc | 11 +++++ 6 files changed, 110 insertions(+), 19 deletions(-) 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