Refactor NetEq delay constraint logic.

This makes the delay manager interface significantly simpler and easier to expose.

Bug: None
Change-Id: Ie3d37c3b869eb17ca421a76e9d1af8f0a1a36ee5
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/364781
Commit-Queue: Jakob Ivarsson‎ <jakobi@webrtc.org>
Reviewed-by: Henrik Lundin <henrik.lundin@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#43186}
This commit is contained in:
Jakob Ivarsson 2024-10-07 13:02:08 +00:00 committed by WebRTC LUCI CQ
parent 1accaf91b5
commit b507daf411
10 changed files with 424 additions and 392 deletions

View File

@ -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",

View File

@ -15,13 +15,16 @@
#include <cstdint>
#include <memory>
#include <optional>
#include <utility>
#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<DelayManager> 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<DelayManager>(config, neteq_config.tick_timer);
}
@ -76,6 +77,8 @@ DecisionLogic::DecisionLogic(
std::unique_ptr<BufferLevelFilter> buffer_level_filter,
std::unique_ptr<PacketArrivalHistory> 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<int> 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);

View File

@ -11,16 +11,19 @@
#ifndef MODULES_AUDIO_CODING_NETEQ_DECISION_LOGIC_H_
#define MODULES_AUDIO_CODING_NETEQ_DECISION_LOGIC_H_
#include <cstddef>
#include <cstdint>
#include <memory>
#include <optional>
#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<DelayManager> delay_manager_;
DelayConstraints delay_constraints_;
std::unique_ptr<BufferLevelFilter> buffer_level_filter_;
std::unique_ptr<PacketArrivalHistory> packet_arrival_history_;
const TickTimer* tick_timer_;

View File

@ -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<MockDelayManager>(
DelayManager::Config(ExplicitKeyValueConfig("")), config.tick_timer);
mock_delay_manager_ = delay_manager.get();

View File

@ -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 <algorithm>
#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

View File

@ -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_

View File

@ -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

View File

@ -15,22 +15,16 @@
#include <algorithm>
#include <memory>
#include <numeric>
#include <string>
#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<ReorderOptimizer> 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

View File

@ -11,15 +11,11 @@
#ifndef MODULES_AUDIO_CODING_NETEQ_DELAY_MANAGER_H_
#define MODULES_AUDIO_CODING_NETEQ_DELAY_MANAGER_H_
#include <string.h> // Provide access to size_t.
#include <deque>
#include <memory>
#include <optional>
#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<ReorderOptimizer> 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

View File

@ -12,238 +12,25 @@
#include "modules/audio_coding/neteq/delay_manager.h"
#include <math.h>
#include <memory>
#include <optional>
#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