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:
parent
1accaf91b5
commit
b507daf411
@ -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",
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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_;
|
||||
|
||||
@ -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();
|
||||
|
||||
118
modules/audio_coding/neteq/delay_constraints.cc
Normal file
118
modules/audio_coding/neteq/delay_constraints.cc
Normal 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
|
||||
70
modules/audio_coding/neteq/delay_constraints.h
Normal file
70
modules/audio_coding/neteq/delay_constraints.h
Normal 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_
|
||||
198
modules/audio_coding/neteq/delay_constraints_unittest.cc
Normal file
198
modules/audio_coding/neteq/delay_constraints_unittest.cc
Normal 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
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user