Add delay manager config options.

Add a new field trial with more flexible parsing and new options:
- Resample packet delays to only update histogram with maximum observed
 delay every X ms.
- Setting the maximum history size (in ms) used for calculating the
 relative arrival delay.

Legacy field trial used for configuration is maintained.

Bug: webrtc:10333
Change-Id: I35b004f5d8209c85b33cb49def3816db51650946
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/192789
Reviewed-by: Ivo Creusen <ivoc@webrtc.org>
Commit-Queue: Jakob Ivarsson <jakobi@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#32591}
This commit is contained in:
Jakob Ivarsson
2020-11-11 15:26:10 +01:00
committed by Commit Bot
parent 05f5d636e5
commit 7dff9f3a76
5 changed files with 125 additions and 41 deletions

View File

@ -64,7 +64,8 @@ class DecisionLogicTest : public ::testing::Test {
std::unique_ptr<Histogram> histogram =
std::make_unique<Histogram>(200, 12345, 2);
auto delay_manager = std::make_unique<MockDelayManager>(
200, 0, 12300, config.tick_timer, std::move(histogram));
200, 0, 12300, absl::nullopt, 2000, config.tick_timer,
std::move(histogram));
mock_delay_manager_ = delay_manager.get();
auto buffer_level_filter = std::make_unique<MockBufferLevelFilter>();
mock_buffer_level_filter_ = buffer_level_filter.get();

View File

@ -22,6 +22,7 @@
#include "modules/audio_coding/neteq/histogram.h"
#include "modules/include/module_common_types_public.h"
#include "rtc_base/checks.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"
@ -32,29 +33,34 @@ namespace {
constexpr int kMinBaseMinimumDelayMs = 0;
constexpr int kMaxBaseMinimumDelayMs = 10000;
constexpr int kMaxHistoryMs = 2000; // Oldest packet to include in history to
// calculate relative packet arrival delay.
constexpr int kDelayBuckets = 100;
constexpr int kBucketSizeMs = 20;
constexpr int kStartDelayMs = 80;
constexpr int kMaxNumReorderedPackets = 5;
int PercentileToQuantile(double percentile) {
return static_cast<int>((1 << 30) * percentile / 100.0 + 0.5);
}
struct DelayHistogramConfig {
int quantile = 1041529569; // 0.97 in Q30.
int forget_factor = 32745; // 0.9993 in Q15.
struct DelayManagerConfig {
double quantile = 0.97;
double forget_factor = 0.9993;
absl::optional<double> start_forget_weight = 2;
};
absl::optional<int> resample_interval_ms;
int max_history_ms = 2000;
// TODO(jakobi): Remove legacy field trial.
DelayHistogramConfig GetDelayHistogramConfig() {
constexpr char kDelayHistogramFieldTrial[] =
"WebRTC-Audio-NetEqDelayHistogram";
DelayHistogramConfig config;
if (webrtc::field_trial::IsEnabled(kDelayHistogramFieldTrial)) {
std::unique_ptr<webrtc::StructParametersParser> Parser() {
return webrtc::StructParametersParser::Create( //
"quantile", &quantile, //
"forget_factor", &forget_factor, //
"start_forget_weight", &start_forget_weight, //
"resample_interval_ms", &resample_interval_ms, //
"max_history_ms", &max_history_ms);
}
// TODO(jakobi): remove legacy field trial.
void MaybeUpdateFromLegacyFieldTrial() {
constexpr char kDelayHistogramFieldTrial[] =
"WebRTC-Audio-NetEqDelayHistogram";
if (!webrtc::field_trial::IsEnabled(kDelayHistogramFieldTrial)) {
return;
}
const auto field_trial_string =
webrtc::field_trial::FindFullName(kDelayHistogramFieldTrial);
double percentile = -1.0;
@ -64,27 +70,36 @@ DelayHistogramConfig GetDelayHistogramConfig() {
&forget_factor, &start_forget_weight) >= 2 &&
percentile >= 0.0 && percentile <= 100.0 && forget_factor >= 0.0 &&
forget_factor <= 1.0) {
config.quantile = PercentileToQuantile(percentile);
config.forget_factor = (1 << 15) * forget_factor;
config.start_forget_weight =
start_forget_weight >= 1 ? absl::make_optional(start_forget_weight)
: absl::nullopt;
this->quantile = percentile / 100;
this->forget_factor = forget_factor;
this->start_forget_weight = start_forget_weight >= 1
? absl::make_optional(start_forget_weight)
: absl::nullopt;
}
}
RTC_LOG(LS_INFO) << "Delay histogram config:"
" quantile="
<< config.quantile
<< " forget_factor=" << config.forget_factor
<< " start_forget_weight="
<< config.start_forget_weight.value_or(0);
return config;
}
explicit DelayManagerConfig() {
Parser()->Parse(webrtc::field_trial::FindFullName(
"WebRTC-Audio-NetEqDelayManagerConfig"));
MaybeUpdateFromLegacyFieldTrial();
RTC_LOG(LS_INFO) << "Delay manager config:"
" quantile="
<< quantile << " forget_factor=" << forget_factor
<< " start_forget_weight="
<< start_forget_weight.value_or(0)
<< " resample_interval_ms="
<< resample_interval_ms.value_or(0)
<< " max_history_ms=" << max_history_ms;
}
};
} // namespace
DelayManager::DelayManager(int max_packets_in_buffer,
int base_minimum_delay_ms,
int histogram_quantile,
absl::optional<int> resample_interval_ms,
int max_history_ms,
const TickTimer* tick_timer,
std::unique_ptr<Histogram> histogram)
: first_packet_received_(false),
@ -92,6 +107,8 @@ DelayManager::DelayManager(int max_packets_in_buffer,
histogram_(std::move(histogram)),
histogram_quantile_(histogram_quantile),
tick_timer_(tick_timer),
resample_interval_ms_(resample_interval_ms),
max_history_ms_(max_history_ms),
base_minimum_delay_ms_(base_minimum_delay_ms),
effective_minimum_delay_ms_(base_minimum_delay_ms),
minimum_delay_ms_(0),
@ -108,12 +125,15 @@ std::unique_ptr<DelayManager> DelayManager::Create(
int max_packets_in_buffer,
int base_minimum_delay_ms,
const TickTimer* tick_timer) {
auto config = GetDelayHistogramConfig();
DelayManagerConfig config;
int forget_factor_q15 = (1 << 15) * config.forget_factor;
int quantile_q30 = (1 << 30) * config.quantile;
std::unique_ptr<Histogram> histogram = std::make_unique<Histogram>(
kDelayBuckets, config.forget_factor, config.start_forget_weight);
return std::make_unique<DelayManager>(max_packets_in_buffer,
base_minimum_delay_ms, config.quantile,
tick_timer, std::move(histogram));
kDelayBuckets, forget_factor_q15, config.start_forget_weight);
return std::make_unique<DelayManager>(
max_packets_in_buffer, base_minimum_delay_ms, quantile_q30,
config.resample_interval_ms, config.max_history_ms, tick_timer,
std::move(histogram));
}
DelayManager::~DelayManager() {}
@ -132,6 +152,8 @@ absl::optional<int> DelayManager::Update(uint32_t timestamp,
last_timestamp_ = timestamp;
first_packet_received_ = true;
num_reordered_packets_ = 0;
resample_stopwatch_ = tick_timer_->GetNewStopwatch();
max_delay_in_interval_ms_ = 0;
return absl::nullopt;
}
@ -139,7 +161,7 @@ absl::optional<int> DelayManager::Update(uint32_t timestamp,
1000 * static_cast<int32_t>(timestamp - last_timestamp_) / sample_rate_hz;
const int iat_ms = packet_iat_stopwatch_->ElapsedMs();
const int iat_delay_ms = iat_ms - expected_iat_ms;
absl::optional<int> relative_delay;
int relative_delay;
bool reordered = !IsNewerTimestamp(timestamp, last_timestamp_);
if (reordered) {
relative_delay = std::max(iat_delay_ms, 0);
@ -147,11 +169,28 @@ absl::optional<int> DelayManager::Update(uint32_t timestamp,
UpdateDelayHistory(iat_delay_ms, timestamp, sample_rate_hz);
relative_delay = CalculateRelativePacketArrivalDelay();
}
const int index = relative_delay.value() / kBucketSizeMs;
if (index < histogram_->NumBuckets()) {
// Maximum delay to register is 2000 ms.
histogram_->Add(index);
absl::optional<int> histogram_update;
if (resample_interval_ms_) {
if (static_cast<int>(resample_stopwatch_->ElapsedMs()) >
*resample_interval_ms_) {
histogram_update = max_delay_in_interval_ms_;
resample_stopwatch_ = tick_timer_->GetNewStopwatch();
max_delay_in_interval_ms_ = 0;
}
max_delay_in_interval_ms_ =
std::max(max_delay_in_interval_ms_, relative_delay);
} else {
histogram_update = relative_delay;
}
if (histogram_update) {
const int index = *histogram_update / kBucketSizeMs;
if (index < histogram_->NumBuckets()) {
// Maximum delay to register is 2000 ms.
histogram_->Add(index);
}
}
// Calculate new |target_level_ms_| based on updated statistics.
int bucket_index = histogram_->Quantile(histogram_quantile_);
target_level_ms_ = (1 + bucket_index) * kBucketSizeMs;
@ -191,7 +230,7 @@ void DelayManager::UpdateDelayHistory(int iat_delay_ms,
delay.timestamp = timestamp;
delay_history_.push_back(delay);
while (timestamp - delay_history_.front().timestamp >
static_cast<uint32_t>(kMaxHistoryMs * sample_rate_hz / 1000)) {
static_cast<uint32_t>(max_history_ms_ * sample_rate_hz / 1000)) {
delay_history_.pop_front();
}
}
@ -226,6 +265,8 @@ void DelayManager::Reset() {
packet_iat_stopwatch_ = tick_timer_->GetNewStopwatch();
first_packet_received_ = false;
num_reordered_packets_ = 0;
resample_stopwatch_ = tick_timer_->GetNewStopwatch();
max_delay_in_interval_ms_ = 0;
}
int DelayManager::TargetDelayMs() const {

View File

@ -28,6 +28,8 @@ class DelayManager {
DelayManager(int max_packets_in_buffer,
int base_minimum_delay_ms,
int histogram_quantile,
absl::optional<int> resample_interval_ms,
int max_history_ms,
const TickTimer* tick_timer,
std::unique_ptr<Histogram> histogram);
@ -105,6 +107,9 @@ class DelayManager {
std::unique_ptr<Histogram> histogram_;
const int histogram_quantile_;
const TickTimer* tick_timer_;
const absl::optional<int> resample_interval_ms_;
const int max_history_ms_;
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.
@ -116,6 +121,8 @@ class DelayManager {
int target_level_ms_; // Currently preferred buffer level.
uint32_t last_timestamp_; // Timestamp for the last received packet.
int num_reordered_packets_ = 0;
int max_delay_in_interval_ms_ = 0;
std::unique_ptr<TickTimer::Stopwatch> resample_stopwatch_;
struct PacketDelay {
int iat_delay_ms;

View File

@ -16,6 +16,7 @@
#include <memory>
#include "absl/types/optional.h"
#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"
@ -29,6 +30,7 @@ namespace webrtc {
namespace {
constexpr int kMaxNumberOfPackets = 240;
constexpr int kMinDelayMs = 0;
constexpr int kMaxHistoryMs = 2000;
constexpr int kTimeStepMs = 10;
constexpr int kFs = 8000;
constexpr int kFrameSizeMs = 20;
@ -53,6 +55,7 @@ class DelayManagerTest : public ::testing::Test {
MockHistogram* mock_histogram_;
uint32_t ts_;
bool use_mock_histogram_ = false;
absl::optional<int> resample_interval_ms_;
};
DelayManagerTest::DelayManagerTest()
@ -69,6 +72,7 @@ void DelayManagerTest::RecreateDelayManager() {
std::unique_ptr<Histogram> histogram(mock_histogram_);
dm_ = std::make_unique<DelayManager>(kMaxNumberOfPackets, kMinDelayMs,
kDefaultHistogramQuantile,
resample_interval_ms_, kMaxHistoryMs,
&tick_timer_, std::move(histogram));
} else {
dm_ = DelayManager::Create(kMaxNumberOfPackets, kMinDelayMs, &tick_timer_);
@ -455,4 +459,31 @@ TEST_F(DelayManagerTest, RelativeArrivalDelayStatistic) {
EXPECT_EQ(20, InsertNextPacket());
}
TEST_F(DelayManagerTest, ResamplePacketDelays) {
use_mock_histogram_ = true;
resample_interval_ms_ = 500;
RecreateDelayManager();
// The histogram should be updated once with the maximum delay observed for
// the following sequence of packets.
EXPECT_CALL(*mock_histogram_, Add(5)).Times(1);
EXPECT_EQ(absl::nullopt, InsertNextPacket());
IncreaseTime(kFrameSizeMs);
EXPECT_EQ(0, InsertNextPacket());
IncreaseTime(3 * kFrameSizeMs);
EXPECT_EQ(2 * kFrameSizeMs, InsertNextPacket());
IncreaseTime(4 * kFrameSizeMs);
EXPECT_EQ(5 * kFrameSizeMs, InsertNextPacket());
for (int i = 4; i >= 0; --i) {
EXPECT_EQ(i * kFrameSizeMs, InsertNextPacket());
}
for (int i = 0; i < *resample_interval_ms_ / kFrameSizeMs; ++i) {
IncreaseTime(kFrameSizeMs);
EXPECT_EQ(0, InsertNextPacket());
}
}
} // namespace webrtc

View File

@ -25,11 +25,15 @@ class MockDelayManager : public DelayManager {
MockDelayManager(size_t max_packets_in_buffer,
int base_minimum_delay_ms,
int histogram_quantile,
absl::optional<int> resample_interval_ms,
int max_history_ms,
const TickTimer* tick_timer,
std::unique_ptr<Histogram> histogram)
: DelayManager(max_packets_in_buffer,
base_minimum_delay_ms,
histogram_quantile,
resample_interval_ms,
max_history_ms,
tick_timer,
std::move(histogram)) {}
MOCK_METHOD(int, TargetDelayMs, (), (const));