From 1e193faaf16ca34226f5a735db1d37347a57929a Mon Sep 17 00:00:00 2001 From: Ruslan Burakov Date: Wed, 15 May 2019 14:31:22 +0200 Subject: [PATCH] Add DecelerationTargetLevelOffset Field Trial. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This change allows NetEq to reach preferred jitter buffer size much faster for high target delays because it uses absolute units instead of relative ones during computation of lower_limit. More details can be found here: https://docs.google.com/document/d/12qPMJYFhGXrA_o_nvz9VshpzAJX6aULxFig1fTzBzDI/edit Change-Id: I21ce0e35e25166d935fdf0325c083bcf990899f5 Bug: webrtc:10619 Change-Id: I21ce0e35e25166d935fdf0325c083bcf990899f5 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/135745 Reviewed-by: Minyue Li Reviewed-by: Jakob Ivarsson‎ Commit-Queue: Ruslan Burakov Cr-Commit-Position: refs/heads/master@{#27970} --- modules/audio_coding/neteq/delay_manager.cc | 58 ++++++++++-- modules/audio_coding/neteq/delay_manager.h | 13 +++ .../neteq/delay_manager_unittest.cc | 90 +++++++++++++++++++ 3 files changed, 152 insertions(+), 9 deletions(-) diff --git a/modules/audio_coding/neteq/delay_manager.cc b/modules/audio_coding/neteq/delay_manager.cc index 9340b8192a..dd0d75925a 100644 --- a/modules/audio_coding/neteq/delay_manager.cc +++ b/modules/audio_coding/neteq/delay_manager.cc @@ -100,6 +100,29 @@ absl::optional GetDelayHistogramConfig() { return absl::nullopt; } +absl::optional GetDecelerationTargetLevelOffsetMs() { + constexpr char kDecelerationTargetLevelOffsetFieldTrial[] = + "WebRTC-Audio-NetEqDecelerationTargetLevelOffset"; + if (!webrtc::field_trial::IsEnabled( + kDecelerationTargetLevelOffsetFieldTrial)) { + return absl::nullopt; + } + + const auto field_trial_string = webrtc::field_trial::FindFullName( + kDecelerationTargetLevelOffsetFieldTrial); + int deceleration_target_level_offset_ms = -1; + sscanf(field_trial_string.c_str(), "Enabled-%d", + &deceleration_target_level_offset_ms); + if (deceleration_target_level_offset_ms >= 0) { + RTC_LOG(LS_INFO) << "NetEq deceleration_target_level_offset " + << "in milliseconds " + << deceleration_target_level_offset_ms; + // Convert into Q8. + return deceleration_target_level_offset_ms << 8; + } + return absl::nullopt; +} + } // namespace namespace webrtc { @@ -133,10 +156,14 @@ DelayManager::DelayManager(size_t max_packets_in_buffer, last_pack_cng_or_dtmf_(1), frame_length_change_experiment_( field_trial::IsEnabled("WebRTC-Audio-NetEqFramelengthExperiment")), - enable_rtx_handling_(enable_rtx_handling) { + enable_rtx_handling_(enable_rtx_handling), + deceleration_target_level_offset_ms_( + GetDecelerationTargetLevelOffsetMs()) { assert(peak_detector); // Should never be NULL. RTC_CHECK(histogram_); RTC_DCHECK_GE(base_minimum_delay_ms_, 0); + RTC_DCHECK(!deceleration_target_level_offset_ms_ || + *deceleration_target_level_offset_ms_ >= 0); Reset(); } @@ -404,26 +431,39 @@ void DelayManager::ResetPacketIatCount() { packet_iat_stopwatch_ = tick_timer_->GetNewStopwatch(); } +void DelayManager::BufferLimits(int* lower_limit, int* higher_limit) const { + BufferLimits(target_level_, lower_limit, higher_limit); +} + // Note that |low_limit| and |higher_limit| are not assigned to // |minimum_delay_ms_| and |maximum_delay_ms_| defined by the client of this -// class. They are computed from |target_level_| and used for decision making. -void DelayManager::BufferLimits(int* lower_limit, int* higher_limit) const { +// class. They are computed from |target_level| in Q8 and used for decision +// making. +void DelayManager::BufferLimits(int target_level, + int* lower_limit, + int* higher_limit) const { if (!lower_limit || !higher_limit) { RTC_LOG_F(LS_ERROR) << "NULL pointers supplied as input"; assert(false); return; } + // |target_level| is in Q8 already. + *lower_limit = (target_level * 3) / 4; + + if (deceleration_target_level_offset_ms_ && packet_len_ms_ > 0) { + *lower_limit = std::max( + *lower_limit, + target_level - *deceleration_target_level_offset_ms_ / packet_len_ms_); + } + int window_20ms = 0x7FFF; // Default large value for legacy bit-exactness. if (packet_len_ms_ > 0) { window_20ms = (20 << 8) / packet_len_ms_; } - - // |target_level_| is in Q8 already. - *lower_limit = (target_level_ * 3) / 4; - // |higher_limit| is equal to |target_level_|, but should at - // least be 20 ms higher than |lower_limit_|. - *higher_limit = std::max(target_level_, *lower_limit + window_20ms); + // |higher_limit| is equal to |target_level|, but should at + // least be 20 ms higher than |lower_limit|. + *higher_limit = std::max(target_level, *lower_limit + window_20ms); } int DelayManager::TargetLevel() const { diff --git a/modules/audio_coding/neteq/delay_manager.h b/modules/audio_coding/neteq/delay_manager.h index 9066f7fcf4..279e60842d 100644 --- a/modules/audio_coding/neteq/delay_manager.h +++ b/modules/audio_coding/neteq/delay_manager.h @@ -101,6 +101,9 @@ class DelayManager { // within to the corresponding pointers. The values are in (fractions of) // packets in Q8. virtual void BufferLimits(int* lower_limit, int* higher_limit) const; + virtual void BufferLimits(int target_level, + int* lower_limit, + int* higher_limit) const; // Gets the target buffer level, in (fractions of) packets in Q8. virtual int TargetLevel() const; @@ -130,6 +133,11 @@ class DelayManager { } // This accessor is only intended for testing purposes. + absl::optional deceleration_target_level_offset_ms() const { + return deceleration_target_level_offset_ms_; + } + + // 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(); } @@ -196,6 +204,11 @@ class DelayManager { const bool enable_rtx_handling_; int num_reordered_packets_ = 0; // Number of consecutive reordered packets. std::deque delay_history_; + // When current buffer level is more than + // |deceleration_target_level_offset_ms_| below the target level, NetEq will + // impose deceleration to increase the buffer level. The value is in Q8, and + // measured in milliseconds. + const absl::optional deceleration_target_level_offset_ms_; RTC_DISALLOW_COPY_AND_ASSIGN(DelayManager); }; diff --git a/modules/audio_coding/neteq/delay_manager_unittest.cc b/modules/audio_coding/neteq/delay_manager_unittest.cc index c57f074326..eb1fabc420 100644 --- a/modules/audio_coding/neteq/delay_manager_unittest.cc +++ b/modules/audio_coding/neteq/delay_manager_unittest.cc @@ -107,6 +107,7 @@ void DelayManagerTest::IncreaseTime(int inc_ms) { tick_timer_.Increment(); } } + void DelayManagerTest::TearDown() { EXPECT_CALL(detector_, Die()); } @@ -725,4 +726,93 @@ TEST_F(DelayManagerTest, RelativeArrivalDelayStatistic) { InsertNextPacket(); } +TEST_F(DelayManagerTest, DecelerationTargetLevelOffsetFieldTrial) { + { + test::ScopedFieldTrials field_trial( + "WebRTC-Audio-NetEqDecelerationTargetLevelOffset/Enabled-105/"); + RecreateDelayManager(); + EXPECT_EQ(dm_->deceleration_target_level_offset_ms().value(), 105 << 8); + } + { + // Negative number. + test::ScopedFieldTrials field_trial( + "WebRTC-Audio-NetEqDecelerationTargetLevelOffset/Enabled--105/"); + RecreateDelayManager(); + EXPECT_FALSE(dm_->deceleration_target_level_offset_ms().has_value()); + } + { + // Disabled. + test::ScopedFieldTrials field_trial( + "WebRTC-Audio-NetEqDecelerationTargetLevelOffset/Disabled/"); + RecreateDelayManager(); + EXPECT_FALSE(dm_->deceleration_target_level_offset_ms().has_value()); + } + { + // Float number. + test::ScopedFieldTrials field_trial( + "WebRTC-Audio-NetEqDecelerationTargetLevelOffset/Enabled-105.5/"); + RecreateDelayManager(); + EXPECT_EQ(dm_->deceleration_target_level_offset_ms().value(), 105 << 8); + } + { + // Several numbers. + test::ScopedFieldTrials field_trial( + "WebRTC-Audio-NetEqDecelerationTargetLevelOffset/Enabled-20-40/"); + RecreateDelayManager(); + EXPECT_EQ(dm_->deceleration_target_level_offset_ms().value(), 20 << 8); + } +} + +TEST_F(DelayManagerTest, DecelerationTargetLevelOffset) { + // Border value where 1/4 target buffer level meets + // WebRTC-Audio-NetEqDecelerationTargetLevelOffset. + constexpr int kBoarderTargetLevel = 100 * 4; + { + // Test that for a low target level, default behaviour is intact. + test::ScopedFieldTrials field_trial( + "WebRTC-Audio-NetEqDecelerationTargetLevelOffset/Enabled-100/"); + const int target_level_ms = ((kBoarderTargetLevel - 1) << 8) / kFrameSizeMs; + RecreateDelayManager(); + SetPacketAudioLength(kFrameSizeMs); + + int lower, higher; // In Q8. + dm_->BufferLimits(target_level_ms, &lower, &higher); + + // Default behaviour of taking 75% of target level. + EXPECT_EQ(target_level_ms * 3 / 4, lower); + EXPECT_EQ(target_level_ms, higher); + } + + { + // Test that for the high target level, |lower| is below target level by + // fixed constant (100 ms in this Field Trial setup). + test::ScopedFieldTrials field_trial( + "WebRTC-Audio-NetEqDecelerationTargetLevelOffset/Enabled-100/"); + const int target_level_ms = ((kBoarderTargetLevel + 1) << 8) / kFrameSizeMs; + RecreateDelayManager(); + SetPacketAudioLength(kFrameSizeMs); + + int lower, higher; // In Q8. + dm_->BufferLimits(target_level_ms, &lower, &higher); + + EXPECT_EQ(target_level_ms - ((100 << 8) / kFrameSizeMs), lower); + EXPECT_EQ(target_level_ms, higher); + } + + { + // Test that for the high target level, without Field Trial the behaviour + // will remain the same. + const int target_level_ms = ((kBoarderTargetLevel + 1) << 8) / kFrameSizeMs; + RecreateDelayManager(); + SetPacketAudioLength(kFrameSizeMs); + + int lower, higher; // In Q8. + dm_->BufferLimits(target_level_ms, &lower, &higher); + + // Default behaviour of taking 75% of target level. + EXPECT_EQ(target_level_ms * 3 / 4, lower); + EXPECT_EQ(target_level_ms, higher); + } +} + } // namespace webrtc