diff --git a/modules/audio_coding/BUILD.gn b/modules/audio_coding/BUILD.gn index 30d66566af..d74a595661 100644 --- a/modules/audio_coding/BUILD.gn +++ b/modules/audio_coding/BUILD.gn @@ -643,6 +643,8 @@ rtc_library("neteq") { "neteq/decision_logic.h", "neteq/decoder_database.cc", "neteq/decoder_database.h", + "neteq/delay_constraints.cc", + "neteq/delay_constraints.h", "neteq/delay_manager.cc", "neteq/delay_manager.h", "neteq/dsp_helper.cc", @@ -1601,6 +1603,7 @@ if (rtc_include_tests) { "neteq/comfort_noise_unittest.cc", "neteq/decision_logic_unittest.cc", "neteq/decoder_database_unittest.cc", + "neteq/delay_constraints_unittest.cc", "neteq/delay_manager_unittest.cc", "neteq/dsp_helper_unittest.cc", "neteq/dtmf_buffer_unittest.cc", diff --git a/modules/audio_coding/neteq/decision_logic.cc b/modules/audio_coding/neteq/decision_logic.cc index 7d9f3a9fce..d79a4e1663 100644 --- a/modules/audio_coding/neteq/decision_logic.cc +++ b/modules/audio_coding/neteq/decision_logic.cc @@ -15,13 +15,16 @@ #include #include #include +#include +#include "api/environment/environment.h" #include "api/neteq/neteq.h" #include "api/neteq/neteq_controller.h" +#include "modules/audio_coding/neteq/buffer_level_filter.h" +#include "modules/audio_coding/neteq/delay_manager.h" #include "modules/audio_coding/neteq/packet_arrival_history.h" #include "modules/audio_coding/neteq/packet_buffer.h" #include "rtc_base/checks.h" -#include "rtc_base/logging.h" #include "rtc_base/numerics/safe_conversions.h" namespace webrtc { @@ -40,8 +43,6 @@ std::unique_ptr CreateDelayManager( const Environment& env, const NetEqController::Config& neteq_config) { DelayManager::Config config(env.field_trials()); - config.max_packets_in_buffer = neteq_config.max_packets_in_buffer; - config.base_minimum_delay_ms = neteq_config.base_min_delay_ms; config.Log(); return std::make_unique(config, neteq_config.tick_timer); } @@ -76,6 +77,8 @@ DecisionLogic::DecisionLogic( std::unique_ptr buffer_level_filter, std::unique_ptr packet_arrival_history) : delay_manager_(std::move(delay_manager)), + delay_constraints_(config.max_packets_in_buffer, + config.base_min_delay_ms), buffer_level_filter_(std::move(buffer_level_filter)), packet_arrival_history_( packet_arrival_history @@ -160,11 +163,11 @@ NetEq::Operation DecisionLogic::GetDecision(const NetEqStatus& status, } int DecisionLogic::TargetLevelMs() const { - return delay_manager_->TargetDelayMs(); + return delay_constraints_.Clamp(UnlimitedTargetLevelMs()); } int DecisionLogic::UnlimitedTargetLevelMs() const { - return delay_manager_->UnlimitedTargetLevelMs(); + return delay_manager_->TargetDelayMs(); } int DecisionLogic::GetFilteredBufferLevel() const { @@ -181,7 +184,8 @@ std::optional DecisionLogic::PacketArrived(int fs_hz, if (info.packet_length_samples > 0 && fs_hz > 0 && info.packet_length_samples != packet_length_samples_) { packet_length_samples_ = info.packet_length_samples; - delay_manager_->SetPacketAudioLength(packet_length_samples_ * 1000 / fs_hz); + delay_constraints_.SetPacketAudioLength(packet_length_samples_ * 1000 / + fs_hz); } bool inserted = packet_arrival_history_->Insert(info.main_timestamp, info.packet_length_samples); diff --git a/modules/audio_coding/neteq/decision_logic.h b/modules/audio_coding/neteq/decision_logic.h index 21cbc56506..4b974c55b7 100644 --- a/modules/audio_coding/neteq/decision_logic.h +++ b/modules/audio_coding/neteq/decision_logic.h @@ -11,16 +11,19 @@ #ifndef MODULES_AUDIO_CODING_NETEQ_DECISION_LOGIC_H_ #define MODULES_AUDIO_CODING_NETEQ_DECISION_LOGIC_H_ +#include +#include #include +#include #include "api/environment/environment.h" #include "api/neteq/neteq.h" #include "api/neteq/neteq_controller.h" #include "api/neteq/tick_timer.h" #include "modules/audio_coding/neteq/buffer_level_filter.h" +#include "modules/audio_coding/neteq/delay_constraints.h" #include "modules/audio_coding/neteq/delay_manager.h" #include "modules/audio_coding/neteq/packet_arrival_history.h" -#include "rtc_base/experiments/field_trial_parser.h" namespace webrtc { @@ -77,16 +80,16 @@ class DecisionLogic : public NetEqController { void RegisterEmptyPacket() override {} bool SetMaximumDelay(int delay_ms) override { - return delay_manager_->SetMaximumDelay(delay_ms); + return delay_constraints_.SetMaximumDelay(delay_ms); } bool SetMinimumDelay(int delay_ms) override { - return delay_manager_->SetMinimumDelay(delay_ms); + return delay_constraints_.SetMinimumDelay(delay_ms); } bool SetBaseMinimumDelay(int delay_ms) override { - return delay_manager_->SetBaseMinimumDelay(delay_ms); + return delay_constraints_.SetBaseMinimumDelay(delay_ms); } int GetBaseMinimumDelay() const override { - return delay_manager_->GetBaseMinimumDelay(); + return delay_constraints_.GetBaseMinimumDelay(); } bool PeakFound() const override { return false; } @@ -148,6 +151,7 @@ class DecisionLogic : public NetEqController { int GetPlayoutDelayMs(NetEqController::NetEqStatus status) const; std::unique_ptr delay_manager_; + DelayConstraints delay_constraints_; std::unique_ptr buffer_level_filter_; std::unique_ptr packet_arrival_history_; const TickTimer* tick_timer_; diff --git a/modules/audio_coding/neteq/decision_logic_unittest.cc b/modules/audio_coding/neteq/decision_logic_unittest.cc index ecf353c741..2d3170b9ec 100644 --- a/modules/audio_coding/neteq/decision_logic_unittest.cc +++ b/modules/audio_coding/neteq/decision_logic_unittest.cc @@ -61,6 +61,8 @@ class DecisionLogicTest : public ::testing::Test { NetEqController::Config config; config.tick_timer = &tick_timer_; config.allow_time_stretching = true; + config.max_packets_in_buffer = 200; + config.base_min_delay_ms = 0; auto delay_manager = std::make_unique( DelayManager::Config(ExplicitKeyValueConfig("")), config.tick_timer); mock_delay_manager_ = delay_manager.get(); diff --git a/modules/audio_coding/neteq/delay_constraints.cc b/modules/audio_coding/neteq/delay_constraints.cc new file mode 100644 index 0000000000..e493155e5b --- /dev/null +++ b/modules/audio_coding/neteq/delay_constraints.cc @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2024 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_coding/neteq/delay_constraints.h" + +#include + +#include "rtc_base/logging.h" +#include "rtc_base/numerics/safe_minmax.h" + +namespace webrtc { + +constexpr int kMinBaseMinimumDelayMs = 0; +constexpr int kMaxBaseMinimumDelayMs = 10000; + +DelayConstraints::DelayConstraints(int max_packets_in_buffer, + int base_minimum_delay_ms) + : max_packets_in_buffer_(max_packets_in_buffer), + base_minimum_delay_ms_(base_minimum_delay_ms), + effective_minimum_delay_ms_(base_minimum_delay_ms), + minimum_delay_ms_(0), + maximum_delay_ms_(0) {} + +int DelayConstraints::Clamp(int delay_ms) const { + delay_ms = std::max(delay_ms, effective_minimum_delay_ms_); + if (maximum_delay_ms_ > 0) { + delay_ms = std::min(delay_ms, maximum_delay_ms_); + } + if (packet_len_ms_ > 0) { + // Limit to 75% of maximum buffer size. + delay_ms = + std::min(delay_ms, 3 * max_packets_in_buffer_ * packet_len_ms_ / 4); + } + return delay_ms; +} + +bool DelayConstraints::SetPacketAudioLength(int length_ms) { + if (length_ms <= 0) { + RTC_LOG_F(LS_ERROR) << "length_ms = " << length_ms; + return false; + } + packet_len_ms_ = length_ms; + return true; +} + +bool DelayConstraints::IsValidMinimumDelay(int delay_ms) const { + return 0 <= delay_ms && delay_ms <= MinimumDelayUpperBound(); +} + +bool DelayConstraints::IsValidBaseMinimumDelay(int delay_ms) const { + return kMinBaseMinimumDelayMs <= delay_ms && + delay_ms <= kMaxBaseMinimumDelayMs; +} + +bool DelayConstraints::SetMinimumDelay(int delay_ms) { + if (!IsValidMinimumDelay(delay_ms)) { + return false; + } + + minimum_delay_ms_ = delay_ms; + UpdateEffectiveMinimumDelay(); + return true; +} + +bool DelayConstraints::SetMaximumDelay(int delay_ms) { + // If `delay_ms` is zero then it unsets the maximum delay and target level is + // unconstrained by maximum delay. + if (delay_ms != 0 && delay_ms < minimum_delay_ms_) { + // Maximum delay shouldn't be less than minimum delay or less than a packet. + return false; + } + + maximum_delay_ms_ = delay_ms; + UpdateEffectiveMinimumDelay(); + return true; +} + +bool DelayConstraints::SetBaseMinimumDelay(int delay_ms) { + if (!IsValidBaseMinimumDelay(delay_ms)) { + return false; + } + + base_minimum_delay_ms_ = delay_ms; + UpdateEffectiveMinimumDelay(); + return true; +} + +int DelayConstraints::GetBaseMinimumDelay() const { + return base_minimum_delay_ms_; +} + +void DelayConstraints::UpdateEffectiveMinimumDelay() { + // Clamp `base_minimum_delay_ms_` into the range which can be effectively + // used. + const int base_minimum_delay_ms = + rtc::SafeClamp(base_minimum_delay_ms_, 0, MinimumDelayUpperBound()); + effective_minimum_delay_ms_ = + std::max(minimum_delay_ms_, base_minimum_delay_ms); +} + +int DelayConstraints::MinimumDelayUpperBound() const { + // Choose the lowest possible bound discarding 0 cases which mean the value + // is not set and unconstrained. + int q75 = max_packets_in_buffer_ * packet_len_ms_ * 3 / 4; + q75 = q75 > 0 ? q75 : kMaxBaseMinimumDelayMs; + const int maximum_delay_ms = + maximum_delay_ms_ > 0 ? maximum_delay_ms_ : kMaxBaseMinimumDelayMs; + return std::min(maximum_delay_ms, q75); +} + +} // namespace webrtc diff --git a/modules/audio_coding/neteq/delay_constraints.h b/modules/audio_coding/neteq/delay_constraints.h new file mode 100644 index 0000000000..5437d21fdf --- /dev/null +++ b/modules/audio_coding/neteq/delay_constraints.h @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2024 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_CODING_NETEQ_DELAY_CONSTRAINTS_H_ +#define MODULES_AUDIO_CODING_NETEQ_DELAY_CONSTRAINTS_H_ + +namespace webrtc { + +class DelayConstraints { + public: + DelayConstraints(int max_packets_in_buffer, int base_minimum_delay_ms); + + // Returns the delay (in ms) clamped to the range of valid delays. + int Clamp(int delay_ms) const; + + // Notifies the DelayManager of how much audio data is carried in each packet. + bool SetPacketAudioLength(int length_ms); + + // Accessors and mutators. + // Assuming `delay` is in valid range. + bool SetMinimumDelay(int delay_ms); + bool SetMaximumDelay(int delay_ms); + bool SetBaseMinimumDelay(int delay_ms); + int GetBaseMinimumDelay() const; + + // These accessors are only intended for testing purposes. + int effective_minimum_delay_ms_for_test() const { + return effective_minimum_delay_ms_; + } + + private: + // Provides value which minimum delay can't exceed based on current buffer + // size and given `maximum_delay_ms_`. Lower bound is a constant 0. + int MinimumDelayUpperBound() const; + + // Updates `effective_minimum_delay_ms_` delay based on current + // `minimum_delay_ms_`, `base_minimum_delay_ms_`, `maximum_delay_ms_` and + // buffer size. + void UpdateEffectiveMinimumDelay(); + + // Makes sure that `delay_ms` is less than maximum delay, if any maximum + // is set. Also, if possible check `delay_ms` to be less than 75% of + // `max_packets_in_buffer_`. + bool IsValidMinimumDelay(int delay_ms) const; + + // Checks that `delay_ms` is in the range of valid base minimum delays. + bool IsValidBaseMinimumDelay(int delay_ms) const; + + // TODO(jakobi): set maximum buffer delay instead of number of packets. + const int max_packets_in_buffer_; + + int base_minimum_delay_ms_; + int effective_minimum_delay_ms_; // Used as lower bound for target delay. + int minimum_delay_ms_; // Externally set minimum delay. + int maximum_delay_ms_; // Externally set maximum delay. No maximum + // delay is enforced if <= 0. + + int packet_len_ms_ = 0; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_CODING_NETEQ_DELAY_CONSTRAINTS_H_ diff --git a/modules/audio_coding/neteq/delay_constraints_unittest.cc b/modules/audio_coding/neteq/delay_constraints_unittest.cc new file mode 100644 index 0000000000..3cefb2172e --- /dev/null +++ b/modules/audio_coding/neteq/delay_constraints_unittest.cc @@ -0,0 +1,198 @@ +/* + * Copyright (c) 2024 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_coding/neteq/delay_constraints.h" + +#include "rtc_base/checks.h" +#include "test/gtest.h" + +namespace webrtc { +namespace { + +constexpr int kMaxNumberOfPackets = 200; +constexpr int kFrameSizeMs = 20; +constexpr int kMaxBufferSizeMs = kMaxNumberOfPackets * kFrameSizeMs; + +TEST(DelayConstraintsTest, NoConstraints) { + DelayConstraints constraints(kMaxNumberOfPackets, 0); + EXPECT_EQ(constraints.Clamp(100), 100); + EXPECT_EQ(constraints.Clamp(0), 0); +} + +TEST(DelayConstraintsTest, MaxDelay) { + DelayConstraints constraints(kMaxNumberOfPackets, 0); + constexpr int kMaxDelayMs = 60; + EXPECT_TRUE(constraints.SetMaximumDelay(kMaxDelayMs)); + EXPECT_EQ(constraints.Clamp(100), kMaxDelayMs); +} + +TEST(DelayConstraintsTest, MinDelay) { + DelayConstraints constraints(kMaxNumberOfPackets, 0); + constexpr int kMinDelayMs = 7 * kFrameSizeMs; + constraints.SetMinimumDelay(kMinDelayMs); + EXPECT_EQ(constraints.Clamp(20), kMinDelayMs); +} + +TEST(DelayConstraintsTest, BaseMinimumDelayCheckValidRange) { + DelayConstraints constraints(kMaxNumberOfPackets, 0); + // Base minimum delay should be between [0, 10000] milliseconds. + EXPECT_FALSE(constraints.SetBaseMinimumDelay(-1)); + EXPECT_FALSE(constraints.SetBaseMinimumDelay(10001)); + EXPECT_EQ(constraints.GetBaseMinimumDelay(), 0); + + EXPECT_TRUE(constraints.SetBaseMinimumDelay(7999)); + EXPECT_EQ(constraints.GetBaseMinimumDelay(), 7999); +} + +TEST(DelayConstraintsTest, BaseMinimumDelayLowerThanMinimumDelay) { + DelayConstraints constraints(kMaxNumberOfPackets, 0); + constexpr int kBaseMinimumDelayMs = 100; + constexpr int kMinimumDelayMs = 200; + + // Base minimum delay sets lower bound on minimum. That is why when base + // minimum delay is lower than minimum delay we use minimum delay. + RTC_DCHECK_LT(kBaseMinimumDelayMs, kMinimumDelayMs); + + EXPECT_TRUE(constraints.SetBaseMinimumDelay(kBaseMinimumDelayMs)); + EXPECT_TRUE(constraints.SetMinimumDelay(kMinimumDelayMs)); + EXPECT_EQ(constraints.effective_minimum_delay_ms_for_test(), kMinimumDelayMs); +} + +TEST(DelayConstraintsTest, BaseMinimumDelayGreaterThanMinimumDelay) { + DelayConstraints constraints(kMaxNumberOfPackets, 0); + constexpr int kBaseMinimumDelayMs = 70; + constexpr int kMinimumDelayMs = 30; + + // Base minimum delay sets lower bound on minimum. That is why when base + // minimum delay is greater than minimum delay we use base minimum delay. + RTC_DCHECK_GT(kBaseMinimumDelayMs, kMinimumDelayMs); + + EXPECT_TRUE(constraints.SetBaseMinimumDelay(kBaseMinimumDelayMs)); + EXPECT_TRUE(constraints.SetMinimumDelay(kMinimumDelayMs)); + EXPECT_EQ(constraints.effective_minimum_delay_ms_for_test(), + kBaseMinimumDelayMs); +} + +TEST(DelayConstraintsTest, BaseMinimumDelayGreaterThanBufferSize) { + DelayConstraints constraints(kMaxNumberOfPackets, 0); + constexpr int kBaseMinimumDelayMs = kMaxBufferSizeMs + 1; + constexpr int kMinimumDelayMs = 12; + constexpr int kMaximumDelayMs = 20; + constexpr int kMaxBufferSizeMsQ75 = 3 * kMaxBufferSizeMs / 4; + EXPECT_TRUE(constraints.SetPacketAudioLength(kFrameSizeMs)); + + EXPECT_TRUE(constraints.SetMaximumDelay(kMaximumDelayMs)); + + // Base minimum delay is greater than minimum delay, that is why we clamp + // it to current the highest possible value which is maximum delay. + RTC_DCHECK_GT(kBaseMinimumDelayMs, kMinimumDelayMs); + RTC_DCHECK_GT(kBaseMinimumDelayMs, kMaxBufferSizeMs); + RTC_DCHECK_GT(kBaseMinimumDelayMs, kMaximumDelayMs); + RTC_DCHECK_LT(kMaximumDelayMs, kMaxBufferSizeMsQ75); + + EXPECT_TRUE(constraints.SetMinimumDelay(kMinimumDelayMs)); + EXPECT_TRUE(constraints.SetBaseMinimumDelay(kBaseMinimumDelayMs)); + + // Unset maximum value. + EXPECT_TRUE(constraints.SetMaximumDelay(0)); + + // With maximum value unset, the highest possible value now is 75% of + // currently possible maximum buffer size. + EXPECT_EQ(constraints.effective_minimum_delay_ms_for_test(), + kMaxBufferSizeMsQ75); +} + +TEST(DelayConstraintsTest, BaseMinimumDelayGreaterThanMaximumDelay) { + DelayConstraints constraints(kMaxNumberOfPackets, 0); + constexpr int kMaximumDelayMs = 400; + constexpr int kBaseMinimumDelayMs = kMaximumDelayMs + 1; + constexpr int kMinimumDelayMs = 20; + + // Base minimum delay is greater than minimum delay, that is why we clamp + // it to current the highest possible value which is kMaximumDelayMs. + RTC_DCHECK_GT(kBaseMinimumDelayMs, kMinimumDelayMs); + RTC_DCHECK_GT(kBaseMinimumDelayMs, kMaximumDelayMs); + RTC_DCHECK_LT(kMaximumDelayMs, kMaxBufferSizeMs); + + EXPECT_TRUE(constraints.SetMaximumDelay(kMaximumDelayMs)); + EXPECT_TRUE(constraints.SetMinimumDelay(kMinimumDelayMs)); + EXPECT_TRUE(constraints.SetBaseMinimumDelay(kBaseMinimumDelayMs)); + EXPECT_EQ(constraints.effective_minimum_delay_ms_for_test(), kMaximumDelayMs); +} + +TEST(DelayConstraintsTest, BaseMinimumDelayLowerThanMaxSize) { + DelayConstraints constraints(kMaxNumberOfPackets, 0); + constexpr int kMaximumDelayMs = 400; + constexpr int kBaseMinimumDelayMs = kMaximumDelayMs - 1; + constexpr int kMinimumDelayMs = 20; + + // Base minimum delay is greater than minimum delay, and lower than maximum + // delays that is why it is used. + RTC_DCHECK_GT(kBaseMinimumDelayMs, kMinimumDelayMs); + RTC_DCHECK_LT(kBaseMinimumDelayMs, kMaximumDelayMs); + + EXPECT_TRUE(constraints.SetMaximumDelay(kMaximumDelayMs)); + EXPECT_TRUE(constraints.SetMinimumDelay(kMinimumDelayMs)); + EXPECT_TRUE(constraints.SetBaseMinimumDelay(kBaseMinimumDelayMs)); + EXPECT_EQ(constraints.effective_minimum_delay_ms_for_test(), + kBaseMinimumDelayMs); +} + +TEST(DelayConstraintsTest, MinimumDelayMemorization) { + DelayConstraints constraints(kMaxNumberOfPackets, 0); + // Check that when we increase base minimum delay to value higher than + // minimum delay then minimum delay is still memorized. This allows to + // restore effective minimum delay to memorized minimum delay value when we + // decrease base minimum delay. + constexpr int kBaseMinimumDelayMsLow = 10; + constexpr int kMinimumDelayMs = 20; + constexpr int kBaseMinimumDelayMsHigh = 30; + + EXPECT_TRUE(constraints.SetBaseMinimumDelay(kBaseMinimumDelayMsLow)); + EXPECT_TRUE(constraints.SetMinimumDelay(kMinimumDelayMs)); + // Minimum delay is used as it is higher than base minimum delay. + EXPECT_EQ(constraints.effective_minimum_delay_ms_for_test(), kMinimumDelayMs); + + EXPECT_TRUE(constraints.SetBaseMinimumDelay(kBaseMinimumDelayMsHigh)); + // Base minimum delay is used as it is now higher than minimum delay. + EXPECT_EQ(constraints.effective_minimum_delay_ms_for_test(), + kBaseMinimumDelayMsHigh); + + EXPECT_TRUE(constraints.SetBaseMinimumDelay(kBaseMinimumDelayMsLow)); + // Check that minimum delay is memorized and is used again. + EXPECT_EQ(constraints.effective_minimum_delay_ms_for_test(), kMinimumDelayMs); +} + +TEST(DelayConstraintsTest, BaseMinimumDelay) { + DelayConstraints constraints(kMaxNumberOfPackets, 0); + constexpr int kBaseMinimumDelayMs = 7 * kFrameSizeMs; + EXPECT_TRUE(constraints.SetBaseMinimumDelay(kBaseMinimumDelayMs)); + EXPECT_EQ(constraints.GetBaseMinimumDelay(), kBaseMinimumDelayMs); + EXPECT_EQ(constraints.Clamp(20), kBaseMinimumDelayMs); +} + +TEST(DelayConstraintsTest, Failures) { + DelayConstraints constraints(kMaxNumberOfPackets, 0); + // Wrong packet size. + EXPECT_FALSE(constraints.SetPacketAudioLength(0)); + EXPECT_FALSE(constraints.SetPacketAudioLength(-1)); + + // Minimum delay higher than a maximum delay is not accepted. + EXPECT_TRUE(constraints.SetMaximumDelay(20)); + EXPECT_FALSE(constraints.SetMinimumDelay(40)); + + // Maximum delay less than minimum delay is not accepted. + EXPECT_TRUE(constraints.SetMaximumDelay(100)); + EXPECT_TRUE(constraints.SetMinimumDelay(80)); + EXPECT_FALSE(constraints.SetMaximumDelay(60)); +} + +} // namespace +} // namespace webrtc diff --git a/modules/audio_coding/neteq/delay_manager.cc b/modules/audio_coding/neteq/delay_manager.cc index f5c56249ad..8c814c802f 100644 --- a/modules/audio_coding/neteq/delay_manager.cc +++ b/modules/audio_coding/neteq/delay_manager.cc @@ -15,22 +15,16 @@ #include #include -#include -#include #include "api/field_trials_view.h" -#include "modules/include/module_common_types_public.h" -#include "rtc_base/checks.h" +#include "api/neteq/tick_timer.h" +#include "modules/audio_coding/neteq/reorder_optimizer.h" #include "rtc_base/experiments/struct_parameters_parser.h" #include "rtc_base/logging.h" -#include "rtc_base/numerics/safe_conversions.h" -#include "rtc_base/numerics/safe_minmax.h" namespace webrtc { namespace { -constexpr int kMinBaseMinimumDelayMs = 0; -constexpr int kMaxBaseMinimumDelayMs = 10000; constexpr int kStartDelayMs = 80; std::unique_ptr MaybeCreateReorderOptimizer( @@ -70,20 +64,13 @@ void DelayManager::Config::Log() { } DelayManager::DelayManager(const Config& config, const TickTimer* tick_timer) - : max_packets_in_buffer_(config.max_packets_in_buffer), - underrun_optimizer_(tick_timer, + : underrun_optimizer_(tick_timer, (1 << 30) * config.quantile, (1 << 15) * config.forget_factor, config.start_forget_weight, config.resample_interval_ms), reorder_optimizer_(MaybeCreateReorderOptimizer(config)), - base_minimum_delay_ms_(config.base_minimum_delay_ms), - effective_minimum_delay_ms_(config.base_minimum_delay_ms), - minimum_delay_ms_(0), - maximum_delay_ms_(0), target_level_ms_(kStartDelayMs) { - RTC_DCHECK_GE(base_minimum_delay_ms_, 0); - Reset(); } @@ -100,29 +87,9 @@ void DelayManager::Update(int arrival_delay_ms, bool reordered) { target_level_ms_ = std::max( target_level_ms_, reorder_optimizer_->GetOptimalDelayMs().value_or(0)); } - unlimited_target_level_ms_ = target_level_ms_; - target_level_ms_ = std::max(target_level_ms_, effective_minimum_delay_ms_); - if (maximum_delay_ms_ > 0) { - target_level_ms_ = std::min(target_level_ms_, maximum_delay_ms_); - } - if (packet_len_ms_ > 0) { - // Limit to 75% of maximum buffer size. - target_level_ms_ = std::min( - target_level_ms_, 3 * max_packets_in_buffer_ * packet_len_ms_ / 4); - } -} - -int DelayManager::SetPacketAudioLength(int length_ms) { - if (length_ms <= 0) { - RTC_LOG_F(LS_ERROR) << "length_ms = " << length_ms; - return -1; - } - packet_len_ms_ = length_ms; - return 0; } void DelayManager::Reset() { - packet_len_ms_ = 0; underrun_optimizer_.Reset(); target_level_ms_ = kStartDelayMs; if (reorder_optimizer_) { @@ -134,73 +101,5 @@ int DelayManager::TargetDelayMs() const { return target_level_ms_; } -int DelayManager::UnlimitedTargetLevelMs() const { - return unlimited_target_level_ms_; -} - -bool DelayManager::IsValidMinimumDelay(int delay_ms) const { - return 0 <= delay_ms && delay_ms <= MinimumDelayUpperBound(); -} - -bool DelayManager::IsValidBaseMinimumDelay(int delay_ms) const { - return kMinBaseMinimumDelayMs <= delay_ms && - delay_ms <= kMaxBaseMinimumDelayMs; -} - -bool DelayManager::SetMinimumDelay(int delay_ms) { - if (!IsValidMinimumDelay(delay_ms)) { - return false; - } - - minimum_delay_ms_ = delay_ms; - UpdateEffectiveMinimumDelay(); - return true; -} - -bool DelayManager::SetMaximumDelay(int delay_ms) { - // If `delay_ms` is zero then it unsets the maximum delay and target level is - // unconstrained by maximum delay. - if (delay_ms != 0 && delay_ms < minimum_delay_ms_) { - // Maximum delay shouldn't be less than minimum delay or less than a packet. - return false; - } - - maximum_delay_ms_ = delay_ms; - UpdateEffectiveMinimumDelay(); - return true; -} - -bool DelayManager::SetBaseMinimumDelay(int delay_ms) { - if (!IsValidBaseMinimumDelay(delay_ms)) { - return false; - } - - base_minimum_delay_ms_ = delay_ms; - UpdateEffectiveMinimumDelay(); - return true; -} - -int DelayManager::GetBaseMinimumDelay() const { - return base_minimum_delay_ms_; -} - -void DelayManager::UpdateEffectiveMinimumDelay() { - // Clamp `base_minimum_delay_ms_` into the range which can be effectively - // used. - const int base_minimum_delay_ms = - rtc::SafeClamp(base_minimum_delay_ms_, 0, MinimumDelayUpperBound()); - effective_minimum_delay_ms_ = - std::max(minimum_delay_ms_, base_minimum_delay_ms); -} - -int DelayManager::MinimumDelayUpperBound() const { - // Choose the lowest possible bound discarding 0 cases which mean the value - // is not set and unconstrained. - int q75 = max_packets_in_buffer_ * packet_len_ms_ * 3 / 4; - q75 = q75 > 0 ? q75 : kMaxBaseMinimumDelayMs; - const int maximum_delay_ms = - maximum_delay_ms_ > 0 ? maximum_delay_ms_ : kMaxBaseMinimumDelayMs; - return std::min(maximum_delay_ms, q75); -} } // namespace webrtc diff --git a/modules/audio_coding/neteq/delay_manager.h b/modules/audio_coding/neteq/delay_manager.h index 8cee36a423..2e00328084 100644 --- a/modules/audio_coding/neteq/delay_manager.h +++ b/modules/audio_coding/neteq/delay_manager.h @@ -11,15 +11,11 @@ #ifndef MODULES_AUDIO_CODING_NETEQ_DELAY_MANAGER_H_ #define MODULES_AUDIO_CODING_NETEQ_DELAY_MANAGER_H_ -#include // Provide access to size_t. - -#include #include #include #include "api/field_trials_view.h" #include "api/neteq/tick_timer.h" -#include "modules/audio_coding/neteq/histogram.h" #include "modules/audio_coding/neteq/reorder_optimizer.h" #include "modules/audio_coding/neteq/underrun_optimizer.h" @@ -40,10 +36,6 @@ class DelayManager { bool use_reorder_optimizer = true; double reorder_forget_factor = 0.9993; int ms_per_loss_percent = 20; - - // Options that are externally populated. - int max_packets_in_buffer = 200; - int base_minimum_delay_ms = 0; }; DelayManager(const Config& config, const TickTimer* tick_timer); @@ -67,55 +59,10 @@ class DelayManager { // min/max delay. virtual int TargetDelayMs() const; - // Reports the target delay that would be used if no minimum/maximum delay - // would be set. - virtual int UnlimitedTargetLevelMs() const; - - // Notifies the DelayManager of how much audio data is carried in each packet. - virtual int SetPacketAudioLength(int length_ms); - - // Accessors and mutators. - // Assuming `delay` is in valid range. - virtual bool SetMinimumDelay(int delay_ms); - virtual bool SetMaximumDelay(int delay_ms); - virtual bool SetBaseMinimumDelay(int delay_ms); - virtual int GetBaseMinimumDelay() const; - - // These accessors are only intended for testing purposes. - int effective_minimum_delay_ms_for_test() const { - return effective_minimum_delay_ms_; - } - private: - // Provides value which minimum delay can't exceed based on current buffer - // size and given `maximum_delay_ms_`. Lower bound is a constant 0. - int MinimumDelayUpperBound() const; - - // Updates `effective_minimum_delay_ms_` delay based on current - // `minimum_delay_ms_`, `base_minimum_delay_ms_` and `maximum_delay_ms_` - // and buffer size. - void UpdateEffectiveMinimumDelay(); - - // Makes sure that `delay_ms` is less than maximum delay, if any maximum - // is set. Also, if possible check `delay_ms` to be less than 75% of - // `max_packets_in_buffer_`. - bool IsValidMinimumDelay(int delay_ms) const; - - bool IsValidBaseMinimumDelay(int delay_ms) const; - - // TODO(jakobi): set maximum buffer delay instead of number of packets. - const int max_packets_in_buffer_; UnderrunOptimizer underrun_optimizer_; std::unique_ptr reorder_optimizer_; - - int base_minimum_delay_ms_; - int effective_minimum_delay_ms_; // Used as lower bound for target delay. - int minimum_delay_ms_; // Externally set minimum delay. - int maximum_delay_ms_; // Externally set maximum allowed delay. - - int packet_len_ms_ = 0; int target_level_ms_ = 0; // Currently preferred buffer level. - int unlimited_target_level_ms_ = 0; }; } // namespace webrtc diff --git a/modules/audio_coding/neteq/delay_manager_unittest.cc b/modules/audio_coding/neteq/delay_manager_unittest.cc index d59a038207..169e2ab950 100644 --- a/modules/audio_coding/neteq/delay_manager_unittest.cc +++ b/modules/audio_coding/neteq/delay_manager_unittest.cc @@ -12,238 +12,25 @@ #include "modules/audio_coding/neteq/delay_manager.h" -#include - -#include -#include - -#include "modules/audio_coding/neteq/histogram.h" -#include "modules/audio_coding/neteq/mock/mock_histogram.h" -#include "modules/audio_coding/neteq/mock/mock_statistics_calculator.h" -#include "rtc_base/checks.h" +#include "api/neteq/tick_timer.h" #include "test/explicit_key_value_config.h" -#include "test/gmock.h" #include "test/gtest.h" namespace webrtc { - namespace { using test::ExplicitKeyValueConfig; -constexpr int kMaxNumberOfPackets = 200; -constexpr int kTimeStepMs = 10; -constexpr int kFrameSizeMs = 20; -constexpr int kMaxBufferSizeMs = kMaxNumberOfPackets * kFrameSizeMs; +TEST(DelayManagerTest, UpdateNormal) { + TickTimer tick_timer; + DelayManager dm(DelayManager::Config(ExplicitKeyValueConfig("")), + &tick_timer); + for (int i = 0; i < 50; ++i) { + dm.Update(0, false); + tick_timer.Increment(2); + } + EXPECT_EQ(20, dm.TargetDelayMs()); +} } // namespace - -class DelayManagerTest : public ::testing::Test { - protected: - DelayManagerTest(); - virtual void SetUp(); - void Update(int delay); - void IncreaseTime(int inc_ms); - - TickTimer tick_timer_; - DelayManager dm_; -}; - -DelayManagerTest::DelayManagerTest() - : dm_(DelayManager::Config(ExplicitKeyValueConfig("")), &tick_timer_) {} - -void DelayManagerTest::SetUp() { - dm_.SetPacketAudioLength(kFrameSizeMs); -} - -void DelayManagerTest::Update(int delay) { - dm_.Update(delay, false); -} - -void DelayManagerTest::IncreaseTime(int inc_ms) { - for (int t = 0; t < inc_ms; t += kTimeStepMs) { - tick_timer_.Increment(); - } -} - -TEST_F(DelayManagerTest, CreateAndDestroy) { - // Nothing to do here. The test fixture creates and destroys the DelayManager - // object. -} - -TEST_F(DelayManagerTest, UpdateNormal) { - for (int i = 0; i < 50; ++i) { - Update(0); - IncreaseTime(kFrameSizeMs); - } - EXPECT_EQ(20, dm_.TargetDelayMs()); -} - -TEST_F(DelayManagerTest, MaxDelay) { - Update(0); - const int kMaxDelayMs = 60; - EXPECT_GT(dm_.TargetDelayMs(), kMaxDelayMs); - EXPECT_TRUE(dm_.SetMaximumDelay(kMaxDelayMs)); - Update(0); - EXPECT_EQ(kMaxDelayMs, dm_.TargetDelayMs()); -} - -TEST_F(DelayManagerTest, MinDelay) { - Update(0); - int kMinDelayMs = 7 * kFrameSizeMs; - EXPECT_LT(dm_.TargetDelayMs(), kMinDelayMs); - dm_.SetMinimumDelay(kMinDelayMs); - IncreaseTime(kFrameSizeMs); - Update(0); - EXPECT_EQ(kMinDelayMs, dm_.TargetDelayMs()); -} - -TEST_F(DelayManagerTest, BaseMinimumDelayCheckValidRange) { - // Base minimum delay should be between [0, 10000] milliseconds. - EXPECT_FALSE(dm_.SetBaseMinimumDelay(-1)); - EXPECT_FALSE(dm_.SetBaseMinimumDelay(10001)); - EXPECT_EQ(dm_.GetBaseMinimumDelay(), 0); - - EXPECT_TRUE(dm_.SetBaseMinimumDelay(7999)); - EXPECT_EQ(dm_.GetBaseMinimumDelay(), 7999); -} - -TEST_F(DelayManagerTest, BaseMinimumDelayLowerThanMinimumDelay) { - constexpr int kBaseMinimumDelayMs = 100; - constexpr int kMinimumDelayMs = 200; - - // Base minimum delay sets lower bound on minimum. That is why when base - // minimum delay is lower than minimum delay we use minimum delay. - RTC_DCHECK_LT(kBaseMinimumDelayMs, kMinimumDelayMs); - - EXPECT_TRUE(dm_.SetBaseMinimumDelay(kBaseMinimumDelayMs)); - EXPECT_TRUE(dm_.SetMinimumDelay(kMinimumDelayMs)); - EXPECT_EQ(dm_.effective_minimum_delay_ms_for_test(), kMinimumDelayMs); -} - -TEST_F(DelayManagerTest, BaseMinimumDelayGreaterThanMinimumDelay) { - constexpr int kBaseMinimumDelayMs = 70; - constexpr int kMinimumDelayMs = 30; - - // Base minimum delay sets lower bound on minimum. That is why when base - // minimum delay is greater than minimum delay we use base minimum delay. - RTC_DCHECK_GT(kBaseMinimumDelayMs, kMinimumDelayMs); - - EXPECT_TRUE(dm_.SetBaseMinimumDelay(kBaseMinimumDelayMs)); - EXPECT_TRUE(dm_.SetMinimumDelay(kMinimumDelayMs)); - EXPECT_EQ(dm_.effective_minimum_delay_ms_for_test(), kBaseMinimumDelayMs); -} - -TEST_F(DelayManagerTest, BaseMinimumDelayGreaterThanBufferSize) { - constexpr int kBaseMinimumDelayMs = kMaxBufferSizeMs + 1; - constexpr int kMinimumDelayMs = 12; - constexpr int kMaximumDelayMs = 20; - constexpr int kMaxBufferSizeMsQ75 = 3 * kMaxBufferSizeMs / 4; - - EXPECT_TRUE(dm_.SetMaximumDelay(kMaximumDelayMs)); - - // Base minimum delay is greater than minimum delay, that is why we clamp - // it to current the highest possible value which is maximum delay. - RTC_DCHECK_GT(kBaseMinimumDelayMs, kMinimumDelayMs); - RTC_DCHECK_GT(kBaseMinimumDelayMs, kMaxBufferSizeMs); - RTC_DCHECK_GT(kBaseMinimumDelayMs, kMaximumDelayMs); - RTC_DCHECK_LT(kMaximumDelayMs, kMaxBufferSizeMsQ75); - - EXPECT_TRUE(dm_.SetMinimumDelay(kMinimumDelayMs)); - EXPECT_TRUE(dm_.SetBaseMinimumDelay(kBaseMinimumDelayMs)); - - // Unset maximum value. - EXPECT_TRUE(dm_.SetMaximumDelay(0)); - - // With maximum value unset, the highest possible value now is 75% of - // currently possible maximum buffer size. - EXPECT_EQ(dm_.effective_minimum_delay_ms_for_test(), kMaxBufferSizeMsQ75); -} - -TEST_F(DelayManagerTest, BaseMinimumDelayGreaterThanMaximumDelay) { - constexpr int kMaximumDelayMs = 400; - constexpr int kBaseMinimumDelayMs = kMaximumDelayMs + 1; - constexpr int kMinimumDelayMs = 20; - - // Base minimum delay is greater than minimum delay, that is why we clamp - // it to current the highest possible value which is kMaximumDelayMs. - RTC_DCHECK_GT(kBaseMinimumDelayMs, kMinimumDelayMs); - RTC_DCHECK_GT(kBaseMinimumDelayMs, kMaximumDelayMs); - RTC_DCHECK_LT(kMaximumDelayMs, kMaxBufferSizeMs); - - EXPECT_TRUE(dm_.SetMaximumDelay(kMaximumDelayMs)); - EXPECT_TRUE(dm_.SetMinimumDelay(kMinimumDelayMs)); - EXPECT_TRUE(dm_.SetBaseMinimumDelay(kBaseMinimumDelayMs)); - EXPECT_EQ(dm_.effective_minimum_delay_ms_for_test(), kMaximumDelayMs); -} - -TEST_F(DelayManagerTest, BaseMinimumDelayLowerThanMaxSize) { - constexpr int kMaximumDelayMs = 400; - constexpr int kBaseMinimumDelayMs = kMaximumDelayMs - 1; - constexpr int kMinimumDelayMs = 20; - - // Base minimum delay is greater than minimum delay, and lower than maximum - // delays that is why it is used. - RTC_DCHECK_GT(kBaseMinimumDelayMs, kMinimumDelayMs); - RTC_DCHECK_LT(kBaseMinimumDelayMs, kMaximumDelayMs); - - EXPECT_TRUE(dm_.SetMaximumDelay(kMaximumDelayMs)); - EXPECT_TRUE(dm_.SetMinimumDelay(kMinimumDelayMs)); - EXPECT_TRUE(dm_.SetBaseMinimumDelay(kBaseMinimumDelayMs)); - EXPECT_EQ(dm_.effective_minimum_delay_ms_for_test(), kBaseMinimumDelayMs); -} - -TEST_F(DelayManagerTest, MinimumDelayMemorization) { - // Check that when we increase base minimum delay to value higher than - // minimum delay then minimum delay is still memorized. This allows to - // restore effective minimum delay to memorized minimum delay value when we - // decrease base minimum delay. - constexpr int kBaseMinimumDelayMsLow = 10; - constexpr int kMinimumDelayMs = 20; - constexpr int kBaseMinimumDelayMsHigh = 30; - - EXPECT_TRUE(dm_.SetBaseMinimumDelay(kBaseMinimumDelayMsLow)); - EXPECT_TRUE(dm_.SetMinimumDelay(kMinimumDelayMs)); - // Minimum delay is used as it is higher than base minimum delay. - EXPECT_EQ(dm_.effective_minimum_delay_ms_for_test(), kMinimumDelayMs); - - EXPECT_TRUE(dm_.SetBaseMinimumDelay(kBaseMinimumDelayMsHigh)); - // Base minimum delay is used as it is now higher than minimum delay. - EXPECT_EQ(dm_.effective_minimum_delay_ms_for_test(), kBaseMinimumDelayMsHigh); - - EXPECT_TRUE(dm_.SetBaseMinimumDelay(kBaseMinimumDelayMsLow)); - // Check that minimum delay is memorized and is used again. - EXPECT_EQ(dm_.effective_minimum_delay_ms_for_test(), kMinimumDelayMs); -} - -TEST_F(DelayManagerTest, BaseMinimumDelay) { - // First packet arrival. - Update(0); - - constexpr int kBaseMinimumDelayMs = 7 * kFrameSizeMs; - EXPECT_LT(dm_.TargetDelayMs(), kBaseMinimumDelayMs); - EXPECT_TRUE(dm_.SetBaseMinimumDelay(kBaseMinimumDelayMs)); - EXPECT_EQ(dm_.GetBaseMinimumDelay(), kBaseMinimumDelayMs); - - IncreaseTime(kFrameSizeMs); - Update(0); - EXPECT_EQ(dm_.GetBaseMinimumDelay(), kBaseMinimumDelayMs); - EXPECT_EQ(kBaseMinimumDelayMs, dm_.TargetDelayMs()); -} - -TEST_F(DelayManagerTest, Failures) { - // Wrong packet size. - EXPECT_EQ(-1, dm_.SetPacketAudioLength(0)); - EXPECT_EQ(-1, dm_.SetPacketAudioLength(-1)); - - // Minimum delay higher than a maximum delay is not accepted. - EXPECT_TRUE(dm_.SetMaximumDelay(20)); - EXPECT_FALSE(dm_.SetMinimumDelay(40)); - - // Maximum delay less than minimum delay is not accepted. - EXPECT_TRUE(dm_.SetMaximumDelay(100)); - EXPECT_TRUE(dm_.SetMinimumDelay(80)); - EXPECT_FALSE(dm_.SetMaximumDelay(60)); -} - } // namespace webrtc