Improve adaptation to reordered packets in delay manager.
This is done by adding a reorder optimizer that estimates the probability of receiving reordered packets. The optimal delay is decided by balancing the cost of increasing the delay against the probability of missing a reordered packet, resulting in a loss. This balance is decided using the `ms_per_loss_percent` parameter. The usage and parameters can be controlled via field trial. Bug: webrtc:10178 Change-Id: Ic484df0412af35610e74b3a6070f2bac7a926a63 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/231541 Reviewed-by: Ivo Creusen <ivoc@webrtc.org> Commit-Queue: Jakob Ivarsson <jakobi@webrtc.org> Cr-Commit-Position: refs/heads/main@{#34954}
This commit is contained in:
parent
1f38a38b6f
commit
58ed02eae3
@ -976,6 +976,8 @@ rtc_library("neteq") {
|
|||||||
"neteq/red_payload_splitter.h",
|
"neteq/red_payload_splitter.h",
|
||||||
"neteq/relative_arrival_delay_tracker.cc",
|
"neteq/relative_arrival_delay_tracker.cc",
|
||||||
"neteq/relative_arrival_delay_tracker.h",
|
"neteq/relative_arrival_delay_tracker.h",
|
||||||
|
"neteq/reorder_optimizer.cc",
|
||||||
|
"neteq/reorder_optimizer.h",
|
||||||
"neteq/statistics_calculator.cc",
|
"neteq/statistics_calculator.cc",
|
||||||
"neteq/statistics_calculator.h",
|
"neteq/statistics_calculator.h",
|
||||||
"neteq/sync_buffer.cc",
|
"neteq/sync_buffer.cc",
|
||||||
@ -2016,6 +2018,7 @@ if (rtc_include_tests) {
|
|||||||
"neteq/random_vector_unittest.cc",
|
"neteq/random_vector_unittest.cc",
|
||||||
"neteq/red_payload_splitter_unittest.cc",
|
"neteq/red_payload_splitter_unittest.cc",
|
||||||
"neteq/relative_arrival_delay_tracker_unittest.cc",
|
"neteq/relative_arrival_delay_tracker_unittest.cc",
|
||||||
|
"neteq/reorder_optimizer_unittest.cc",
|
||||||
"neteq/statistics_calculator_unittest.cc",
|
"neteq/statistics_calculator_unittest.cc",
|
||||||
"neteq/sync_buffer_unittest.cc",
|
"neteq/sync_buffer_unittest.cc",
|
||||||
"neteq/time_stretch_unittest.cc",
|
"neteq/time_stretch_unittest.cc",
|
||||||
|
|||||||
@ -32,6 +32,16 @@ constexpr int kMinBaseMinimumDelayMs = 0;
|
|||||||
constexpr int kMaxBaseMinimumDelayMs = 10000;
|
constexpr int kMaxBaseMinimumDelayMs = 10000;
|
||||||
constexpr int kStartDelayMs = 80;
|
constexpr int kStartDelayMs = 80;
|
||||||
|
|
||||||
|
std::unique_ptr<ReorderOptimizer> MaybeCreateReorderOptimizer(
|
||||||
|
const DelayManager::Config& config) {
|
||||||
|
if (!config.use_reorder_optimizer) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return std::make_unique<ReorderOptimizer>(
|
||||||
|
(1 << 15) * config.reorder_forget_factor, config.ms_per_loss_percent,
|
||||||
|
config.start_forget_weight);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
DelayManager::Config::Config() {
|
DelayManager::Config::Config() {
|
||||||
@ -47,7 +57,10 @@ void DelayManager::Config::Log() {
|
|||||||
<< " start_forget_weight=" << start_forget_weight.value_or(0)
|
<< " start_forget_weight=" << start_forget_weight.value_or(0)
|
||||||
<< " resample_interval_ms="
|
<< " resample_interval_ms="
|
||||||
<< resample_interval_ms.value_or(0)
|
<< resample_interval_ms.value_or(0)
|
||||||
<< " max_history_ms=" << max_history_ms;
|
<< " max_history_ms=" << max_history_ms
|
||||||
|
<< " use_reorder_optimizer=" << use_reorder_optimizer
|
||||||
|
<< " reorder_forget_factor=" << reorder_forget_factor
|
||||||
|
<< " ms_per_loss_percent=" << ms_per_loss_percent;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<StructParametersParser> DelayManager::Config::Parser() {
|
std::unique_ptr<StructParametersParser> DelayManager::Config::Parser() {
|
||||||
@ -56,7 +69,10 @@ std::unique_ptr<StructParametersParser> DelayManager::Config::Parser() {
|
|||||||
"forget_factor", &forget_factor, //
|
"forget_factor", &forget_factor, //
|
||||||
"start_forget_weight", &start_forget_weight, //
|
"start_forget_weight", &start_forget_weight, //
|
||||||
"resample_interval_ms", &resample_interval_ms, //
|
"resample_interval_ms", &resample_interval_ms, //
|
||||||
"max_history_ms", &max_history_ms);
|
"max_history_ms", &max_history_ms, //
|
||||||
|
"use_reorder_optimizer", &use_reorder_optimizer, //
|
||||||
|
"reorder_forget_factor", &reorder_forget_factor, //
|
||||||
|
"ms_per_loss_percent", &ms_per_loss_percent);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(jakobi): remove legacy field trial.
|
// TODO(jakobi): remove legacy field trial.
|
||||||
@ -90,6 +106,7 @@ DelayManager::DelayManager(const Config& config, const TickTimer* tick_timer)
|
|||||||
(1 << 15) * config.forget_factor,
|
(1 << 15) * config.forget_factor,
|
||||||
config.start_forget_weight,
|
config.start_forget_weight,
|
||||||
config.resample_interval_ms),
|
config.resample_interval_ms),
|
||||||
|
reorder_optimizer_(MaybeCreateReorderOptimizer(config)),
|
||||||
relative_arrival_delay_tracker_(tick_timer, config.max_history_ms),
|
relative_arrival_delay_tracker_(tick_timer, config.max_history_ms),
|
||||||
base_minimum_delay_ms_(config.base_minimum_delay_ms),
|
base_minimum_delay_ms_(config.base_minimum_delay_ms),
|
||||||
effective_minimum_delay_ms_(config.base_minimum_delay_ms),
|
effective_minimum_delay_ms_(config.base_minimum_delay_ms),
|
||||||
@ -115,9 +132,18 @@ absl::optional<int> DelayManager::Update(uint32_t timestamp,
|
|||||||
return absl::nullopt;
|
return absl::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool reordered =
|
||||||
|
relative_arrival_delay_tracker_.newest_timestamp() != timestamp;
|
||||||
|
if (!reorder_optimizer_ || !reordered) {
|
||||||
underrun_optimizer_.Update(*relative_delay);
|
underrun_optimizer_.Update(*relative_delay);
|
||||||
|
}
|
||||||
target_level_ms_ =
|
target_level_ms_ =
|
||||||
underrun_optimizer_.GetOptimalDelayMs().value_or(kStartDelayMs);
|
underrun_optimizer_.GetOptimalDelayMs().value_or(kStartDelayMs);
|
||||||
|
if (reorder_optimizer_) {
|
||||||
|
reorder_optimizer_->Update(*relative_delay, reordered, target_level_ms_);
|
||||||
|
target_level_ms_ = std::max(
|
||||||
|
target_level_ms_, reorder_optimizer_->GetOptimalDelayMs().value_or(0));
|
||||||
|
}
|
||||||
target_level_ms_ = std::max(target_level_ms_, effective_minimum_delay_ms_);
|
target_level_ms_ = std::max(target_level_ms_, effective_minimum_delay_ms_);
|
||||||
if (maximum_delay_ms_ > 0) {
|
if (maximum_delay_ms_ > 0) {
|
||||||
target_level_ms_ = std::min(target_level_ms_, maximum_delay_ms_);
|
target_level_ms_ = std::min(target_level_ms_, maximum_delay_ms_);
|
||||||
@ -148,6 +174,9 @@ void DelayManager::Reset() {
|
|||||||
underrun_optimizer_.Reset();
|
underrun_optimizer_.Reset();
|
||||||
relative_arrival_delay_tracker_.Reset();
|
relative_arrival_delay_tracker_.Reset();
|
||||||
target_level_ms_ = kStartDelayMs;
|
target_level_ms_ = kStartDelayMs;
|
||||||
|
if (reorder_optimizer_) {
|
||||||
|
reorder_optimizer_->Reset();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int DelayManager::TargetDelayMs() const {
|
int DelayManager::TargetDelayMs() const {
|
||||||
|
|||||||
@ -20,6 +20,7 @@
|
|||||||
#include "api/neteq/tick_timer.h"
|
#include "api/neteq/tick_timer.h"
|
||||||
#include "modules/audio_coding/neteq/histogram.h"
|
#include "modules/audio_coding/neteq/histogram.h"
|
||||||
#include "modules/audio_coding/neteq/relative_arrival_delay_tracker.h"
|
#include "modules/audio_coding/neteq/relative_arrival_delay_tracker.h"
|
||||||
|
#include "modules/audio_coding/neteq/reorder_optimizer.h"
|
||||||
#include "modules/audio_coding/neteq/underrun_optimizer.h"
|
#include "modules/audio_coding/neteq/underrun_optimizer.h"
|
||||||
#include "rtc_base/constructor_magic.h"
|
#include "rtc_base/constructor_magic.h"
|
||||||
#include "rtc_base/experiments/struct_parameters_parser.h"
|
#include "rtc_base/experiments/struct_parameters_parser.h"
|
||||||
@ -39,6 +40,10 @@ class DelayManager {
|
|||||||
absl::optional<int> resample_interval_ms;
|
absl::optional<int> resample_interval_ms;
|
||||||
int max_history_ms = 2000;
|
int max_history_ms = 2000;
|
||||||
|
|
||||||
|
bool use_reorder_optimizer = true;
|
||||||
|
double reorder_forget_factor = 0.9993;
|
||||||
|
int ms_per_loss_percent = 20;
|
||||||
|
|
||||||
// Options that are externally populated.
|
// Options that are externally populated.
|
||||||
int max_packets_in_buffer = 200;
|
int max_packets_in_buffer = 200;
|
||||||
int base_minimum_delay_ms = 0;
|
int base_minimum_delay_ms = 0;
|
||||||
@ -104,6 +109,7 @@ class DelayManager {
|
|||||||
// TODO(jakobi): set maximum buffer delay instead of number of packets.
|
// TODO(jakobi): set maximum buffer delay instead of number of packets.
|
||||||
const int max_packets_in_buffer_;
|
const int max_packets_in_buffer_;
|
||||||
UnderrunOptimizer underrun_optimizer_;
|
UnderrunOptimizer underrun_optimizer_;
|
||||||
|
std::unique_ptr<ReorderOptimizer> reorder_optimizer_;
|
||||||
RelativeArrivalDelayTracker relative_arrival_delay_tracker_;
|
RelativeArrivalDelayTracker relative_arrival_delay_tracker_;
|
||||||
|
|
||||||
int base_minimum_delay_ms_;
|
int base_minimum_delay_ms_;
|
||||||
|
|||||||
@ -42,7 +42,7 @@ class Histogram {
|
|||||||
virtual int NumBuckets() const;
|
virtual int NumBuckets() const;
|
||||||
|
|
||||||
// Returns the probability for each bucket in Q30.
|
// Returns the probability for each bucket in Q30.
|
||||||
std::vector<int> buckets() const { return buckets_; }
|
const std::vector<int>& buckets() const { return buckets_; }
|
||||||
|
|
||||||
// Accessors only intended for testing purposes.
|
// Accessors only intended for testing purposes.
|
||||||
int base_forget_factor_for_testing() const { return base_forget_factor_; }
|
int base_forget_factor_for_testing() const { return base_forget_factor_; }
|
||||||
|
|||||||
@ -12,6 +12,8 @@
|
|||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
|
#include "modules/include/module_common_types_public.h"
|
||||||
|
|
||||||
namespace webrtc {
|
namespace webrtc {
|
||||||
|
|
||||||
absl::optional<int> RelativeArrivalDelayTracker::Update(uint32_t timestamp,
|
absl::optional<int> RelativeArrivalDelayTracker::Update(uint32_t timestamp,
|
||||||
@ -23,6 +25,7 @@ absl::optional<int> RelativeArrivalDelayTracker::Update(uint32_t timestamp,
|
|||||||
// Restart relative delay esimation from this packet.
|
// Restart relative delay esimation from this packet.
|
||||||
delay_history_.clear();
|
delay_history_.clear();
|
||||||
packet_iat_stopwatch_ = tick_timer_->GetNewStopwatch();
|
packet_iat_stopwatch_ = tick_timer_->GetNewStopwatch();
|
||||||
|
newest_timestamp_ = timestamp;
|
||||||
last_timestamp_ = timestamp;
|
last_timestamp_ = timestamp;
|
||||||
return absl::nullopt;
|
return absl::nullopt;
|
||||||
}
|
}
|
||||||
@ -37,6 +40,9 @@ absl::optional<int> RelativeArrivalDelayTracker::Update(uint32_t timestamp,
|
|||||||
|
|
||||||
packet_iat_stopwatch_ = tick_timer_->GetNewStopwatch();
|
packet_iat_stopwatch_ = tick_timer_->GetNewStopwatch();
|
||||||
last_timestamp_ = timestamp;
|
last_timestamp_ = timestamp;
|
||||||
|
if (IsNewerTimestamp(timestamp, *newest_timestamp_)) {
|
||||||
|
newest_timestamp_ = timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
return relative_delay;
|
return relative_delay;
|
||||||
}
|
}
|
||||||
@ -44,6 +50,7 @@ absl::optional<int> RelativeArrivalDelayTracker::Update(uint32_t timestamp,
|
|||||||
void RelativeArrivalDelayTracker::Reset() {
|
void RelativeArrivalDelayTracker::Reset() {
|
||||||
delay_history_.clear();
|
delay_history_.clear();
|
||||||
packet_iat_stopwatch_ = tick_timer_->GetNewStopwatch();
|
packet_iat_stopwatch_ = tick_timer_->GetNewStopwatch();
|
||||||
|
newest_timestamp_ = absl::nullopt;
|
||||||
last_timestamp_ = absl::nullopt;
|
last_timestamp_ = absl::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -28,6 +28,10 @@ class RelativeArrivalDelayTracker {
|
|||||||
|
|
||||||
void Reset();
|
void Reset();
|
||||||
|
|
||||||
|
absl::optional<uint32_t> newest_timestamp() const {
|
||||||
|
return newest_timestamp_;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Updates `delay_history_`.
|
// Updates `delay_history_`.
|
||||||
void UpdateDelayHistory(int iat_delay_ms,
|
void UpdateDelayHistory(int iat_delay_ms,
|
||||||
@ -46,8 +50,8 @@ class RelativeArrivalDelayTracker {
|
|||||||
};
|
};
|
||||||
std::deque<PacketDelay> delay_history_;
|
std::deque<PacketDelay> delay_history_;
|
||||||
|
|
||||||
absl::optional<uint32_t>
|
absl::optional<uint32_t> newest_timestamp_;
|
||||||
last_timestamp_; // Timestamp for the last received packet.
|
absl::optional<uint32_t> last_timestamp_;
|
||||||
|
|
||||||
std::unique_ptr<TickTimer::Stopwatch>
|
std::unique_ptr<TickTimer::Stopwatch>
|
||||||
packet_iat_stopwatch_; // Time elapsed since last packet.
|
packet_iat_stopwatch_; // Time elapsed since last packet.
|
||||||
|
|||||||
@ -49,14 +49,17 @@ TEST(RelativeArrivalDelayTrackerTest, ReorderedPackets) {
|
|||||||
|
|
||||||
// Insert reordered packet.
|
// Insert reordered packet.
|
||||||
EXPECT_EQ(tracker.Update(kTs - 4 * kTsIncrement, kFs), 80);
|
EXPECT_EQ(tracker.Update(kTs - 4 * kTsIncrement, kFs), 80);
|
||||||
|
EXPECT_EQ(tracker.newest_timestamp(), kTs);
|
||||||
|
|
||||||
// Insert another reordered packet.
|
// Insert another reordered packet.
|
||||||
EXPECT_EQ(tracker.Update(kTs - kTsIncrement, kFs), 20);
|
EXPECT_EQ(tracker.Update(kTs - kTsIncrement, kFs), 20);
|
||||||
|
EXPECT_EQ(tracker.newest_timestamp(), kTs);
|
||||||
|
|
||||||
// Insert the next packet in order and verify that the relative delay is
|
// Insert the next packet in order and verify that the relative delay is
|
||||||
// estimated based on the first inserted packet.
|
// estimated based on the first inserted packet.
|
||||||
tick_timer.Increment(4 * kFrameSizeMs / tick_timer.ms_per_tick());
|
tick_timer.Increment(4 * kFrameSizeMs / tick_timer.ms_per_tick());
|
||||||
EXPECT_EQ(tracker.Update(kTs + kTsIncrement, kFs), 60);
|
EXPECT_EQ(tracker.Update(kTs + kTsIncrement, kFs), 60);
|
||||||
|
EXPECT_EQ(tracker.newest_timestamp(), kTs + kTsIncrement);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(RelativeArrivalDelayTrackerTest, MaxDelayHistory) {
|
TEST(RelativeArrivalDelayTrackerTest, MaxDelayHistory) {
|
||||||
|
|||||||
75
modules/audio_coding/neteq/reorder_optimizer.cc
Normal file
75
modules/audio_coding/neteq/reorder_optimizer.cc
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2021 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/reorder_optimizer.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <limits>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace webrtc {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
constexpr int kDelayBuckets = 100;
|
||||||
|
constexpr int kBucketSizeMs = 20;
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
ReorderOptimizer::ReorderOptimizer(int forget_factor,
|
||||||
|
int ms_per_loss_percent,
|
||||||
|
absl::optional<int> start_forget_weight)
|
||||||
|
: histogram_(kDelayBuckets, forget_factor, start_forget_weight),
|
||||||
|
ms_per_loss_percent_(ms_per_loss_percent) {}
|
||||||
|
|
||||||
|
void ReorderOptimizer::Update(int relative_delay_ms,
|
||||||
|
bool reordered,
|
||||||
|
int base_delay_ms) {
|
||||||
|
const int index = reordered ? relative_delay_ms / kBucketSizeMs : 0;
|
||||||
|
if (index < histogram_.NumBuckets()) {
|
||||||
|
// Maximum delay to register is 2000 ms.
|
||||||
|
histogram_.Add(index);
|
||||||
|
}
|
||||||
|
int bucket_index = MinimizeCostFunction(base_delay_ms);
|
||||||
|
optimal_delay_ms_ = (1 + bucket_index) * kBucketSizeMs;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ReorderOptimizer::Reset() {
|
||||||
|
histogram_.Reset();
|
||||||
|
optimal_delay_ms_.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
int ReorderOptimizer::MinimizeCostFunction(int base_delay_ms) const {
|
||||||
|
const std::vector<int>& buckets = histogram_.buckets();
|
||||||
|
|
||||||
|
// Values are calculated in Q30.
|
||||||
|
int64_t loss_probability = 1 << 30;
|
||||||
|
int64_t min_cost = std::numeric_limits<int64_t>::max();
|
||||||
|
int min_bucket = 0;
|
||||||
|
for (int i = 0; i < static_cast<int>(buckets.size()); ++i) {
|
||||||
|
loss_probability -= buckets[i];
|
||||||
|
int64_t delay_ms =
|
||||||
|
static_cast<int64_t>(std::max(0, i * kBucketSizeMs - base_delay_ms))
|
||||||
|
<< 30;
|
||||||
|
int64_t cost = delay_ms + 100 * ms_per_loss_percent_ * loss_probability;
|
||||||
|
|
||||||
|
if (cost < min_cost) {
|
||||||
|
min_cost = cost;
|
||||||
|
min_bucket = i;
|
||||||
|
}
|
||||||
|
if (loss_probability == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return min_bucket;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace webrtc
|
||||||
43
modules/audio_coding/neteq/reorder_optimizer.h
Normal file
43
modules/audio_coding/neteq/reorder_optimizer.h
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2021 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_REORDER_OPTIMIZER_H_
|
||||||
|
#define MODULES_AUDIO_CODING_NETEQ_REORDER_OPTIMIZER_H_
|
||||||
|
|
||||||
|
#include "absl/types/optional.h"
|
||||||
|
#include "modules/audio_coding/neteq/histogram.h"
|
||||||
|
|
||||||
|
namespace webrtc {
|
||||||
|
|
||||||
|
// Calculates an optimal delay to reduce the chance of missing reordered
|
||||||
|
// packets. The delay/loss trade-off can be tune using the `ms_per_loss_percent`
|
||||||
|
// parameter.
|
||||||
|
class ReorderOptimizer {
|
||||||
|
public:
|
||||||
|
ReorderOptimizer(int forget_factor,
|
||||||
|
int ms_per_loss_percent,
|
||||||
|
absl::optional<int> start_forget_weight);
|
||||||
|
|
||||||
|
void Update(int relative_delay_ms, bool reordered, int base_delay_ms);
|
||||||
|
|
||||||
|
absl::optional<int> GetOptimalDelayMs() const { return optimal_delay_ms_; }
|
||||||
|
|
||||||
|
void Reset();
|
||||||
|
|
||||||
|
private:
|
||||||
|
int MinimizeCostFunction(int base_delay_ms) const;
|
||||||
|
|
||||||
|
Histogram histogram_;
|
||||||
|
const int ms_per_loss_percent_;
|
||||||
|
absl::optional<int> optimal_delay_ms_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace webrtc
|
||||||
|
#endif // MODULES_AUDIO_CODING_NETEQ_REORDER_OPTIMIZER_H_
|
||||||
70
modules/audio_coding/neteq/reorder_optimizer_unittest.cc
Normal file
70
modules/audio_coding/neteq/reorder_optimizer_unittest.cc
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2021 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/reorder_optimizer.h"
|
||||||
|
|
||||||
|
#include "test/gtest.h"
|
||||||
|
|
||||||
|
namespace webrtc {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
constexpr int kForgetFactor = 32745; // 0.9993 in Q15.
|
||||||
|
constexpr int kMsPerLossPercent = 20;
|
||||||
|
constexpr int kStartForgetWeight = 1;
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
TEST(ReorderOptimizerTest, OnlyIncreaseDelayForReorderedPackets) {
|
||||||
|
ReorderOptimizer reorder_optimizer(kForgetFactor, kMsPerLossPercent,
|
||||||
|
kStartForgetWeight);
|
||||||
|
EXPECT_FALSE(reorder_optimizer.GetOptimalDelayMs());
|
||||||
|
|
||||||
|
// Delay should not increase for in-order packets.
|
||||||
|
reorder_optimizer.Update(60, /*reordered=*/false, 0);
|
||||||
|
EXPECT_EQ(reorder_optimizer.GetOptimalDelayMs(), 20);
|
||||||
|
|
||||||
|
reorder_optimizer.Update(100, /*reordered=*/false, 0);
|
||||||
|
EXPECT_EQ(reorder_optimizer.GetOptimalDelayMs(), 20);
|
||||||
|
|
||||||
|
reorder_optimizer.Update(80, /*reordered=*/true, 0);
|
||||||
|
EXPECT_EQ(reorder_optimizer.GetOptimalDelayMs(), 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ReorderOptimizerTest, AvoidIncreasingDelayWhenProbabilityIsLow) {
|
||||||
|
ReorderOptimizer reorder_optimizer(kForgetFactor, kMsPerLossPercent,
|
||||||
|
kStartForgetWeight);
|
||||||
|
|
||||||
|
reorder_optimizer.Update(40, /*reordered=*/true, 0);
|
||||||
|
reorder_optimizer.Update(40, /*reordered=*/true, 0);
|
||||||
|
reorder_optimizer.Update(40, /*reordered=*/true, 0);
|
||||||
|
EXPECT_EQ(reorder_optimizer.GetOptimalDelayMs(), 60);
|
||||||
|
|
||||||
|
// The cost of the delay is too high relative the probability.
|
||||||
|
reorder_optimizer.Update(600, /*reordered=*/true, 0);
|
||||||
|
EXPECT_EQ(reorder_optimizer.GetOptimalDelayMs(), 60);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ReorderOptimizerTest, BaseDelayIsSubtractedFromCost) {
|
||||||
|
constexpr int kBaseDelayMs = 200;
|
||||||
|
ReorderOptimizer reorder_optimizer(kForgetFactor, kMsPerLossPercent,
|
||||||
|
kStartForgetWeight);
|
||||||
|
|
||||||
|
reorder_optimizer.Update(40, /*reordered=*/true, kBaseDelayMs);
|
||||||
|
reorder_optimizer.Update(40, /*reordered=*/true, kBaseDelayMs);
|
||||||
|
reorder_optimizer.Update(40, /*reordered=*/true, kBaseDelayMs);
|
||||||
|
EXPECT_EQ(reorder_optimizer.GetOptimalDelayMs(), 60);
|
||||||
|
|
||||||
|
// The cost of the delay is too high relative the probability.
|
||||||
|
reorder_optimizer.Update(600, /*reordered=*/true, kBaseDelayMs);
|
||||||
|
EXPECT_EQ(reorder_optimizer.GetOptimalDelayMs(), 620);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace webrtc
|
||||||
Loading…
x
Reference in New Issue
Block a user