Use packet arrival history in delay manager.
It replaces the relative arrival delay tracker which is equivalent. This results in a slight bit-exactness change but nothing that should affect quality. Bug: webrtc:13322 Change-Id: I6ed5d6fdfa724859122928a8838acce27ac2e5d0 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/263380 Commit-Queue: Jakob Ivarsson <jakobi@webrtc.org> Reviewed-by: Minyue Li <minyue@webrtc.org> Cr-Commit-Position: refs/heads/main@{#37004}
This commit is contained in:

committed by
WebRTC LUCI CQ

parent
c1fb359b2a
commit
01ab7d501b
@ -986,8 +986,6 @@ rtc_library("neteq") {
|
||||
"neteq/random_vector.h",
|
||||
"neteq/red_payload_splitter.cc",
|
||||
"neteq/red_payload_splitter.h",
|
||||
"neteq/relative_arrival_delay_tracker.cc",
|
||||
"neteq/relative_arrival_delay_tracker.h",
|
||||
"neteq/reorder_optimizer.cc",
|
||||
"neteq/reorder_optimizer.h",
|
||||
"neteq/statistics_calculator.cc",
|
||||
@ -2037,7 +2035,6 @@ if (rtc_include_tests) {
|
||||
"neteq/post_decode_vad_unittest.cc",
|
||||
"neteq/random_vector_unittest.cc",
|
||||
"neteq/red_payload_splitter_unittest.cc",
|
||||
"neteq/relative_arrival_delay_tracker_unittest.cc",
|
||||
"neteq/reorder_optimizer_unittest.cc",
|
||||
"neteq/statistics_calculator_unittest.cc",
|
||||
"neteq/sync_buffer_unittest.cc",
|
||||
|
@ -12,6 +12,7 @@
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
@ -106,7 +107,6 @@ void DecisionLogic::SoftReset() {
|
||||
packet_length_samples_ = 0;
|
||||
sample_memory_ = 0;
|
||||
prev_time_scale_ = false;
|
||||
last_pack_cng_or_dtmf_ = true;
|
||||
timescale_countdown_ =
|
||||
tick_timer_->GetNewCountdown(kMinTimescaleInterval + 1);
|
||||
time_stretched_cn_samples_ = 0;
|
||||
@ -239,11 +239,7 @@ absl::optional<int> DecisionLogic::PacketArrived(
|
||||
bool should_update_stats,
|
||||
const PacketArrivedInfo& info) {
|
||||
buffer_flush_ = buffer_flush_ || info.buffer_flush;
|
||||
if (info.is_cng_or_dtmf) {
|
||||
last_pack_cng_or_dtmf_ = true;
|
||||
return absl::nullopt;
|
||||
}
|
||||
if (!should_update_stats) {
|
||||
if (!should_update_stats || info.is_cng_or_dtmf) {
|
||||
return absl::nullopt;
|
||||
}
|
||||
if (info.packet_length_samples > 0 && fs_hz > 0 &&
|
||||
@ -251,12 +247,18 @@ absl::optional<int> DecisionLogic::PacketArrived(
|
||||
packet_length_samples_ = info.packet_length_samples;
|
||||
delay_manager_->SetPacketAudioLength(packet_length_samples_ * 1000 / fs_hz);
|
||||
}
|
||||
packet_arrival_history_.Insert(
|
||||
info.main_timestamp, tick_timer_->ticks() * tick_timer_->ms_per_tick());
|
||||
auto relative_delay = delay_manager_->Update(
|
||||
info.main_timestamp, fs_hz, /*reset=*/last_pack_cng_or_dtmf_);
|
||||
last_pack_cng_or_dtmf_ = false;
|
||||
return relative_delay;
|
||||
int64_t time_now_ms = tick_timer_->ticks() * tick_timer_->ms_per_tick();
|
||||
packet_arrival_history_.Insert(info.main_timestamp, time_now_ms);
|
||||
if (packet_arrival_history_.size() < 2) {
|
||||
// No meaningful delay estimate unless at least 2 packets have arrived.
|
||||
return absl::nullopt;
|
||||
}
|
||||
int arrival_delay_ms =
|
||||
packet_arrival_history_.GetDelayMs(info.main_timestamp, time_now_ms);
|
||||
bool reordered =
|
||||
!packet_arrival_history_.IsNewestRtpTimestamp(info.main_timestamp);
|
||||
delay_manager_->Update(arrival_delay_ms, reordered);
|
||||
return arrival_delay_ms;
|
||||
}
|
||||
|
||||
void DecisionLogic::FilterBufferLevel(size_t buffer_size_samples) {
|
||||
|
@ -191,7 +191,6 @@ class DecisionLogic : public NetEqController {
|
||||
std::unique_ptr<TickTimer::Countdown> timescale_countdown_;
|
||||
int num_consecutive_expands_ = 0;
|
||||
int time_stretched_cn_samples_ = 0;
|
||||
bool last_pack_cng_or_dtmf_ = true;
|
||||
bool buffer_flush_ = false;
|
||||
int last_playout_delay_ms_ = 0;
|
||||
};
|
||||
|
@ -51,7 +51,6 @@ DelayManager::Config::Config() {
|
||||
"forget_factor", &forget_factor, //
|
||||
"start_forget_weight", &start_forget_weight, //
|
||||
"resample_interval_ms", &resample_interval_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)
|
||||
@ -66,7 +65,6 @@ void DelayManager::Config::Log() {
|
||||
<< " start_forget_weight=" << start_forget_weight.value_or(0)
|
||||
<< " resample_interval_ms="
|
||||
<< resample_interval_ms.value_or(0)
|
||||
<< " 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;
|
||||
@ -80,7 +78,6 @@ DelayManager::DelayManager(const Config& config, const TickTimer* tick_timer)
|
||||
config.start_forget_weight,
|
||||
config.resample_interval_ms),
|
||||
reorder_optimizer_(MaybeCreateReorderOptimizer(config)),
|
||||
relative_arrival_delay_tracker_(tick_timer, config.max_history_ms),
|
||||
base_minimum_delay_ms_(config.base_minimum_delay_ms),
|
||||
effective_minimum_delay_ms_(config.base_minimum_delay_ms),
|
||||
minimum_delay_ms_(0),
|
||||
@ -93,27 +90,14 @@ DelayManager::DelayManager(const Config& config, const TickTimer* tick_timer)
|
||||
|
||||
DelayManager::~DelayManager() {}
|
||||
|
||||
absl::optional<int> DelayManager::Update(uint32_t timestamp,
|
||||
int sample_rate_hz,
|
||||
bool reset) {
|
||||
if (reset) {
|
||||
relative_arrival_delay_tracker_.Reset();
|
||||
}
|
||||
absl::optional<int> relative_delay =
|
||||
relative_arrival_delay_tracker_.Update(timestamp, sample_rate_hz);
|
||||
if (!relative_delay) {
|
||||
return absl::nullopt;
|
||||
}
|
||||
|
||||
bool reordered =
|
||||
relative_arrival_delay_tracker_.newest_timestamp() != timestamp;
|
||||
void DelayManager::Update(int arrival_delay_ms, bool reordered) {
|
||||
if (!reorder_optimizer_ || !reordered) {
|
||||
underrun_optimizer_.Update(*relative_delay);
|
||||
underrun_optimizer_.Update(arrival_delay_ms);
|
||||
}
|
||||
target_level_ms_ =
|
||||
underrun_optimizer_.GetOptimalDelayMs().value_or(kStartDelayMs);
|
||||
if (reorder_optimizer_) {
|
||||
reorder_optimizer_->Update(*relative_delay, reordered, target_level_ms_);
|
||||
reorder_optimizer_->Update(arrival_delay_ms, reordered, target_level_ms_);
|
||||
target_level_ms_ = std::max(
|
||||
target_level_ms_, reorder_optimizer_->GetOptimalDelayMs().value_or(0));
|
||||
}
|
||||
@ -126,11 +110,8 @@ absl::optional<int> DelayManager::Update(uint32_t timestamp,
|
||||
target_level_ms_ = std::min(
|
||||
target_level_ms_, 3 * max_packets_in_buffer_ * packet_len_ms_ / 4);
|
||||
}
|
||||
|
||||
return relative_delay;
|
||||
}
|
||||
|
||||
|
||||
int DelayManager::SetPacketAudioLength(int length_ms) {
|
||||
if (length_ms <= 0) {
|
||||
RTC_LOG_F(LS_ERROR) << "length_ms = " << length_ms;
|
||||
@ -143,7 +124,6 @@ int DelayManager::SetPacketAudioLength(int length_ms) {
|
||||
void DelayManager::Reset() {
|
||||
packet_len_ms_ = 0;
|
||||
underrun_optimizer_.Reset();
|
||||
relative_arrival_delay_tracker_.Reset();
|
||||
target_level_ms_ = kStartDelayMs;
|
||||
if (reorder_optimizer_) {
|
||||
reorder_optimizer_->Reset();
|
||||
|
@ -19,7 +19,6 @@
|
||||
#include "absl/types/optional.h"
|
||||
#include "api/neteq/tick_timer.h"
|
||||
#include "modules/audio_coding/neteq/histogram.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"
|
||||
|
||||
@ -36,7 +35,6 @@ class DelayManager {
|
||||
double forget_factor = 0.983;
|
||||
absl::optional<double> start_forget_weight = 2;
|
||||
absl::optional<int> resample_interval_ms = 500;
|
||||
int max_history_ms = 2000;
|
||||
|
||||
bool use_reorder_optimizer = true;
|
||||
double reorder_forget_factor = 0.9993;
|
||||
@ -54,14 +52,11 @@ class DelayManager {
|
||||
DelayManager(const DelayManager&) = delete;
|
||||
DelayManager& operator=(const DelayManager&) = delete;
|
||||
|
||||
// Updates the delay manager with a new incoming packet, with `timestamp` from
|
||||
// the RTP header. This updates the statistics and a new target buffer level
|
||||
// is calculated. Returns the relative delay if it can be calculated. If
|
||||
// `reset` is true, restarts the relative arrival delay calculation from this
|
||||
// packet.
|
||||
virtual absl::optional<int> Update(uint32_t timestamp,
|
||||
int sample_rate_hz,
|
||||
bool reset = false);
|
||||
// Updates the delay manager that a new packet arrived with delay
|
||||
// `arrival_delay_ms`. This updates the statistics and a new target buffer
|
||||
// level is calculated. The `reordered` flag indicates if the packet was
|
||||
// reordered.
|
||||
virtual void Update(int arrival_delay_ms, bool reordered);
|
||||
|
||||
// Resets all state.
|
||||
virtual void Reset();
|
||||
@ -105,7 +100,6 @@ class DelayManager {
|
||||
const int max_packets_in_buffer_;
|
||||
UnderrunOptimizer underrun_optimizer_;
|
||||
std::unique_ptr<ReorderOptimizer> reorder_optimizer_;
|
||||
RelativeArrivalDelayTracker relative_arrival_delay_tracker_;
|
||||
|
||||
int base_minimum_delay_ms_;
|
||||
int effective_minimum_delay_ms_; // Used as lower bound for target delay.
|
||||
|
@ -30,9 +30,7 @@ namespace webrtc {
|
||||
namespace {
|
||||
constexpr int kMaxNumberOfPackets = 200;
|
||||
constexpr int kTimeStepMs = 10;
|
||||
constexpr int kFs = 8000;
|
||||
constexpr int kFrameSizeMs = 20;
|
||||
constexpr int kTsIncrement = kFrameSizeMs * kFs / 1000;
|
||||
constexpr int kMaxBufferSizeMs = kMaxNumberOfPackets * kFrameSizeMs;
|
||||
|
||||
} // namespace
|
||||
@ -41,25 +39,22 @@ class DelayManagerTest : public ::testing::Test {
|
||||
protected:
|
||||
DelayManagerTest();
|
||||
virtual void SetUp();
|
||||
absl::optional<int> InsertNextPacket();
|
||||
void Update(int delay);
|
||||
void IncreaseTime(int inc_ms);
|
||||
|
||||
TickTimer tick_timer_;
|
||||
DelayManager dm_;
|
||||
uint32_t ts_;
|
||||
};
|
||||
|
||||
DelayManagerTest::DelayManagerTest()
|
||||
: dm_(DelayManager::Config(), &tick_timer_), ts_(0x12345678) {}
|
||||
: dm_(DelayManager::Config(), &tick_timer_) {}
|
||||
|
||||
void DelayManagerTest::SetUp() {
|
||||
dm_.SetPacketAudioLength(kFrameSizeMs);
|
||||
}
|
||||
|
||||
absl::optional<int> DelayManagerTest::InsertNextPacket() {
|
||||
auto relative_delay = dm_.Update(ts_, kFs);
|
||||
ts_ += kTsIncrement;
|
||||
return relative_delay;
|
||||
void DelayManagerTest::Update(int delay) {
|
||||
dm_.Update(delay, false);
|
||||
}
|
||||
|
||||
void DelayManagerTest::IncreaseTime(int inc_ms) {
|
||||
@ -75,28 +70,28 @@ TEST_F(DelayManagerTest, CreateAndDestroy) {
|
||||
|
||||
TEST_F(DelayManagerTest, UpdateNormal) {
|
||||
for (int i = 0; i < 50; ++i) {
|
||||
InsertNextPacket();
|
||||
Update(0);
|
||||
IncreaseTime(kFrameSizeMs);
|
||||
}
|
||||
EXPECT_EQ(20, dm_.TargetDelayMs());
|
||||
}
|
||||
|
||||
TEST_F(DelayManagerTest, MaxDelay) {
|
||||
InsertNextPacket();
|
||||
Update(0);
|
||||
const int kMaxDelayMs = 60;
|
||||
EXPECT_GT(dm_.TargetDelayMs(), kMaxDelayMs);
|
||||
EXPECT_TRUE(dm_.SetMaximumDelay(kMaxDelayMs));
|
||||
InsertNextPacket();
|
||||
Update(0);
|
||||
EXPECT_EQ(kMaxDelayMs, dm_.TargetDelayMs());
|
||||
}
|
||||
|
||||
TEST_F(DelayManagerTest, MinDelay) {
|
||||
InsertNextPacket();
|
||||
Update(0);
|
||||
int kMinDelayMs = 7 * kFrameSizeMs;
|
||||
EXPECT_LT(dm_.TargetDelayMs(), kMinDelayMs);
|
||||
dm_.SetMinimumDelay(kMinDelayMs);
|
||||
IncreaseTime(kFrameSizeMs);
|
||||
InsertNextPacket();
|
||||
Update(0);
|
||||
EXPECT_EQ(kMinDelayMs, dm_.TargetDelayMs());
|
||||
}
|
||||
|
||||
@ -220,7 +215,7 @@ TEST_F(DelayManagerTest, MinimumDelayMemorization) {
|
||||
|
||||
TEST_F(DelayManagerTest, BaseMinimumDelay) {
|
||||
// First packet arrival.
|
||||
InsertNextPacket();
|
||||
Update(0);
|
||||
|
||||
constexpr int kBaseMinimumDelayMs = 7 * kFrameSizeMs;
|
||||
EXPECT_LT(dm_.TargetDelayMs(), kBaseMinimumDelayMs);
|
||||
@ -228,14 +223,12 @@ TEST_F(DelayManagerTest, BaseMinimumDelay) {
|
||||
EXPECT_EQ(dm_.GetBaseMinimumDelay(), kBaseMinimumDelayMs);
|
||||
|
||||
IncreaseTime(kFrameSizeMs);
|
||||
InsertNextPacket();
|
||||
Update(0);
|
||||
EXPECT_EQ(dm_.GetBaseMinimumDelay(), kBaseMinimumDelayMs);
|
||||
EXPECT_EQ(kBaseMinimumDelayMs, dm_.TargetDelayMs());
|
||||
}
|
||||
|
||||
TEST_F(DelayManagerTest, Failures) {
|
||||
// Wrong sample rate.
|
||||
EXPECT_EQ(absl::nullopt, dm_.Update(0, -1));
|
||||
// Wrong packet size.
|
||||
EXPECT_EQ(-1, dm_.SetPacketAudioLength(0));
|
||||
EXPECT_EQ(-1, dm_.SetPacketAudioLength(-1));
|
||||
@ -250,13 +243,4 @@ TEST_F(DelayManagerTest, Failures) {
|
||||
EXPECT_FALSE(dm_.SetMaximumDelay(60));
|
||||
}
|
||||
|
||||
TEST_F(DelayManagerTest, RelativeArrivalDelayStatistic) {
|
||||
EXPECT_EQ(absl::nullopt, InsertNextPacket());
|
||||
IncreaseTime(kFrameSizeMs);
|
||||
EXPECT_EQ(0, InsertNextPacket());
|
||||
IncreaseTime(2 * kFrameSizeMs);
|
||||
|
||||
EXPECT_EQ(20, InsertNextPacket());
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
|
@ -57,10 +57,10 @@ TEST_F(NetEqDecodingTest, MAYBE_TestBitExactness) {
|
||||
webrtc::test::ResourcePath("audio_coding/neteq_universal_new", "rtp");
|
||||
|
||||
const std::string output_checksum =
|
||||
"5e56fabfacd6fa202f3a00bcb4e034d6d817e6b3";
|
||||
"dee7a10ab92526876a70a85bc48a4906901af3df";
|
||||
|
||||
const std::string network_stats_checksum =
|
||||
"dfbf60f913a25a1f2f1066f85b4b08c24eed0ef2";
|
||||
"911dbf5fd97f48d25b8f0967286eb73c9d6f6158";
|
||||
|
||||
DecodeAndCompare(input_rtp_file, output_checksum, network_stats_checksum,
|
||||
absl::GetFlag(FLAGS_gen_ref));
|
||||
|
@ -23,12 +23,16 @@ PacketArrivalHistory::PacketArrivalHistory(int window_size_ms)
|
||||
void PacketArrivalHistory::Insert(uint32_t rtp_timestamp,
|
||||
int64_t arrival_time_ms) {
|
||||
RTC_DCHECK(sample_rate_khz_ > 0);
|
||||
int64_t unwrapped_rtp_timestamp_ms =
|
||||
timestamp_unwrapper_.Unwrap(rtp_timestamp) / sample_rate_khz_;
|
||||
history_.emplace_back(unwrapped_rtp_timestamp_ms, arrival_time_ms);
|
||||
int64_t unwrapped_rtp_timestamp = timestamp_unwrapper_.Unwrap(rtp_timestamp);
|
||||
if (!newest_rtp_timestamp_ ||
|
||||
unwrapped_rtp_timestamp > *newest_rtp_timestamp_) {
|
||||
newest_rtp_timestamp_ = unwrapped_rtp_timestamp;
|
||||
}
|
||||
history_.emplace_back(unwrapped_rtp_timestamp / sample_rate_khz_,
|
||||
arrival_time_ms);
|
||||
MaybeUpdateCachedArrivals(history_.back());
|
||||
while (history_.front().rtp_timestamp_ms + window_size_ms_ <
|
||||
unwrapped_rtp_timestamp_ms) {
|
||||
unwrapped_rtp_timestamp / sample_rate_khz_) {
|
||||
if (&history_.front() == min_packet_arrival_) {
|
||||
min_packet_arrival_ = nullptr;
|
||||
}
|
||||
@ -59,6 +63,7 @@ void PacketArrivalHistory::Reset() {
|
||||
min_packet_arrival_ = nullptr;
|
||||
max_packet_arrival_ = nullptr;
|
||||
timestamp_unwrapper_ = TimestampUnwrapper();
|
||||
newest_rtp_timestamp_ = absl::nullopt;
|
||||
}
|
||||
|
||||
int PacketArrivalHistory::GetDelayMs(uint32_t rtp_timestamp,
|
||||
@ -78,6 +83,15 @@ int PacketArrivalHistory::GetMaxDelayMs() const {
|
||||
return GetPacketArrivalDelayMs(*max_packet_arrival_);
|
||||
}
|
||||
|
||||
bool PacketArrivalHistory::IsNewestRtpTimestamp(uint32_t rtp_timestamp) const {
|
||||
if (!newest_rtp_timestamp_) {
|
||||
return false;
|
||||
}
|
||||
int64_t unwrapped_rtp_timestamp =
|
||||
timestamp_unwrapper_.UnwrapWithoutUpdate(rtp_timestamp);
|
||||
return unwrapped_rtp_timestamp == *newest_rtp_timestamp_;
|
||||
}
|
||||
|
||||
int PacketArrivalHistory::GetPacketArrivalDelayMs(
|
||||
const PacketArrival& packet_arrival) const {
|
||||
if (!min_packet_arrival_) {
|
||||
|
@ -13,8 +13,8 @@
|
||||
|
||||
#include <cstdint>
|
||||
#include <deque>
|
||||
#include <memory>
|
||||
|
||||
#include "absl/types/optional.h"
|
||||
#include "api/neteq/tick_timer.h"
|
||||
#include "modules/include/module_common_types_public.h"
|
||||
|
||||
@ -34,17 +34,21 @@ class PacketArrivalHistory {
|
||||
// `(time_ms - p.arrival_time_ms) - (rtp_timestamp - p.rtp_timestamp)`
|
||||
// where `p` is chosen as the packet arrival in the history that maximizes the
|
||||
// delay.
|
||||
int GetDelayMs(uint32_t rtp_timestamp, int64_t times_ms) const;
|
||||
int GetDelayMs(uint32_t rtp_timestamp, int64_t time_ms) const;
|
||||
|
||||
// Get the maximum packet arrival delay observed in the history.
|
||||
int GetMaxDelayMs() const;
|
||||
|
||||
bool IsNewestRtpTimestamp(uint32_t rtp_timestamp) const;
|
||||
|
||||
void Reset();
|
||||
|
||||
void set_sample_rate(int sample_rate) {
|
||||
sample_rate_khz_ = sample_rate / 1000;
|
||||
}
|
||||
|
||||
size_t size() const { return history_.size(); }
|
||||
|
||||
private:
|
||||
struct PacketArrival {
|
||||
PacketArrival(int64_t rtp_timestamp_ms, int64_t arrival_time_ms)
|
||||
@ -69,6 +73,7 @@ class PacketArrivalHistory {
|
||||
const PacketArrival* max_packet_arrival_ = nullptr;
|
||||
const int window_size_ms_;
|
||||
TimestampUnwrapper timestamp_unwrapper_;
|
||||
absl::optional<int64_t> newest_rtp_timestamp_;
|
||||
int sample_rate_khz_ = 0;
|
||||
};
|
||||
|
||||
|
@ -35,6 +35,8 @@ class PacketArrivalHistoryTest : public testing::Test {
|
||||
timestamp_ = timestamp;
|
||||
}
|
||||
history_.Insert(timestamp, time_ms_);
|
||||
EXPECT_EQ(history_.IsNewestRtpTimestamp(timestamp),
|
||||
timestamp_delta_ms >= 0);
|
||||
return history_.GetDelayMs(timestamp, time_ms_);
|
||||
}
|
||||
|
||||
|
@ -1,83 +0,0 @@
|
||||
/*
|
||||
* 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/relative_arrival_delay_tracker.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "modules/include/module_common_types_public.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
absl::optional<int> RelativeArrivalDelayTracker::Update(uint32_t timestamp,
|
||||
int sample_rate_hz) {
|
||||
if (sample_rate_hz <= 0) {
|
||||
return absl::nullopt;
|
||||
}
|
||||
if (!last_timestamp_) {
|
||||
// Restart relative delay esimation from this packet.
|
||||
delay_history_.clear();
|
||||
packet_iat_stopwatch_ = tick_timer_->GetNewStopwatch();
|
||||
newest_timestamp_ = timestamp;
|
||||
last_timestamp_ = timestamp;
|
||||
return absl::nullopt;
|
||||
}
|
||||
|
||||
const int expected_iat_ms =
|
||||
1000ll * 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;
|
||||
UpdateDelayHistory(iat_delay_ms, timestamp, sample_rate_hz);
|
||||
int relative_delay = CalculateRelativePacketArrivalDelay();
|
||||
|
||||
packet_iat_stopwatch_ = tick_timer_->GetNewStopwatch();
|
||||
last_timestamp_ = timestamp;
|
||||
if (IsNewerTimestamp(timestamp, *newest_timestamp_)) {
|
||||
newest_timestamp_ = timestamp;
|
||||
}
|
||||
|
||||
return relative_delay;
|
||||
}
|
||||
|
||||
void RelativeArrivalDelayTracker::Reset() {
|
||||
delay_history_.clear();
|
||||
packet_iat_stopwatch_.reset();
|
||||
newest_timestamp_ = absl::nullopt;
|
||||
last_timestamp_ = absl::nullopt;
|
||||
}
|
||||
|
||||
void RelativeArrivalDelayTracker::UpdateDelayHistory(int iat_delay_ms,
|
||||
uint32_t timestamp,
|
||||
int sample_rate_hz) {
|
||||
PacketDelay delay;
|
||||
delay.iat_delay_ms = iat_delay_ms;
|
||||
delay.timestamp = timestamp;
|
||||
delay_history_.push_back(delay);
|
||||
while (static_cast<int32_t>(timestamp - delay_history_.front().timestamp) >
|
||||
max_history_ms_ * sample_rate_hz / 1000) {
|
||||
delay_history_.pop_front();
|
||||
}
|
||||
}
|
||||
|
||||
int RelativeArrivalDelayTracker::CalculateRelativePacketArrivalDelay() const {
|
||||
// This effectively calculates arrival delay of a packet relative to the
|
||||
// packet preceding the history window. If the arrival delay ever becomes
|
||||
// smaller than zero, it means the reference packet is invalid, and we
|
||||
// move the reference.
|
||||
int relative_delay = 0;
|
||||
for (const PacketDelay& delay : delay_history_) {
|
||||
relative_delay += delay.iat_delay_ms;
|
||||
relative_delay = std::max(relative_delay, 0);
|
||||
}
|
||||
return relative_delay;
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
@ -1,61 +0,0 @@
|
||||
/*
|
||||
* 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_RELATIVE_ARRIVAL_DELAY_TRACKER_H_
|
||||
#define MODULES_AUDIO_CODING_NETEQ_RELATIVE_ARRIVAL_DELAY_TRACKER_H_
|
||||
|
||||
#include <deque>
|
||||
#include <memory>
|
||||
|
||||
#include "absl/types/optional.h"
|
||||
#include "api/neteq/tick_timer.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
class RelativeArrivalDelayTracker {
|
||||
public:
|
||||
RelativeArrivalDelayTracker(const TickTimer* tick_timer, int max_history_ms)
|
||||
: tick_timer_(tick_timer), max_history_ms_(max_history_ms) {}
|
||||
|
||||
absl::optional<int> Update(uint32_t timestamp, int sample_rate_hz);
|
||||
|
||||
void Reset();
|
||||
|
||||
absl::optional<uint32_t> newest_timestamp() const {
|
||||
return newest_timestamp_;
|
||||
}
|
||||
|
||||
private:
|
||||
// Updates `delay_history_`.
|
||||
void UpdateDelayHistory(int iat_delay_ms,
|
||||
uint32_t timestamp,
|
||||
int sample_rate_hz);
|
||||
|
||||
// Calculate relative packet arrival delay from `delay_history_`.
|
||||
int CalculateRelativePacketArrivalDelay() const;
|
||||
|
||||
const TickTimer* tick_timer_;
|
||||
const int max_history_ms_;
|
||||
|
||||
struct PacketDelay {
|
||||
int iat_delay_ms;
|
||||
uint32_t timestamp;
|
||||
};
|
||||
std::deque<PacketDelay> delay_history_;
|
||||
|
||||
absl::optional<uint32_t> newest_timestamp_;
|
||||
absl::optional<uint32_t> last_timestamp_;
|
||||
|
||||
std::unique_ptr<TickTimer::Stopwatch>
|
||||
packet_iat_stopwatch_; // Time elapsed since last packet.
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
#endif // MODULES_AUDIO_CODING_NETEQ_RELATIVE_ARRIVAL_DELAY_TRACKER_H_
|
@ -1,84 +0,0 @@
|
||||
/*
|
||||
* 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/relative_arrival_delay_tracker.h"
|
||||
|
||||
#include "test/gtest.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
namespace {
|
||||
constexpr int kMaxHistoryMs = 2000;
|
||||
constexpr int kFs = 8000;
|
||||
constexpr int kFrameSizeMs = 20;
|
||||
constexpr int kTsIncrement = kFrameSizeMs * kFs / 1000;
|
||||
constexpr uint32_t kTs = 0x12345678;
|
||||
} // namespace
|
||||
|
||||
TEST(RelativeArrivalDelayTrackerTest, RelativeArrivalDelay) {
|
||||
TickTimer tick_timer;
|
||||
RelativeArrivalDelayTracker tracker(&tick_timer, kMaxHistoryMs);
|
||||
|
||||
EXPECT_FALSE(tracker.Update(kTs, kFs));
|
||||
|
||||
tick_timer.Increment(kFrameSizeMs / tick_timer.ms_per_tick());
|
||||
EXPECT_EQ(tracker.Update(kTs + kTsIncrement, kFs), 0);
|
||||
|
||||
tick_timer.Increment(2 * kFrameSizeMs / tick_timer.ms_per_tick());
|
||||
EXPECT_EQ(tracker.Update(kTs + 2 * kTsIncrement, kFs), 20);
|
||||
|
||||
EXPECT_EQ(tracker.Update(kTs, kFs), 60); // Reordered, 60ms delayed.
|
||||
|
||||
tick_timer.Increment(2 * kFrameSizeMs / tick_timer.ms_per_tick());
|
||||
EXPECT_EQ(tracker.Update(kTs + 3 * kTsIncrement, kFs), 40);
|
||||
}
|
||||
|
||||
TEST(RelativeArrivalDelayTrackerTest, ReorderedPackets) {
|
||||
TickTimer tick_timer;
|
||||
RelativeArrivalDelayTracker tracker(&tick_timer, kMaxHistoryMs);
|
||||
|
||||
// Insert first packet.
|
||||
EXPECT_FALSE(tracker.Update(kTs, kFs));
|
||||
|
||||
// Insert reordered packet.
|
||||
EXPECT_EQ(tracker.Update(kTs - 4 * kTsIncrement, kFs), 80);
|
||||
EXPECT_EQ(tracker.newest_timestamp(), kTs);
|
||||
|
||||
// Insert another reordered packet.
|
||||
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
|
||||
// estimated based on the first inserted packet.
|
||||
tick_timer.Increment(4 * kFrameSizeMs / tick_timer.ms_per_tick());
|
||||
EXPECT_EQ(tracker.Update(kTs + kTsIncrement, kFs), 60);
|
||||
EXPECT_EQ(tracker.newest_timestamp(), kTs + kTsIncrement);
|
||||
}
|
||||
|
||||
TEST(RelativeArrivalDelayTrackerTest, MaxDelayHistory) {
|
||||
TickTimer tick_timer;
|
||||
RelativeArrivalDelayTracker tracker(&tick_timer, kMaxHistoryMs);
|
||||
|
||||
EXPECT_FALSE(tracker.Update(kTs, kFs));
|
||||
|
||||
// Insert 20 ms iat delay in the delay history.
|
||||
tick_timer.Increment(2 * kFrameSizeMs / tick_timer.ms_per_tick());
|
||||
EXPECT_EQ(tracker.Update(kTs + kTsIncrement, kFs), 20);
|
||||
|
||||
// Insert next packet with a timestamp difference larger than maximum history
|
||||
// size. This removes the previously inserted iat delay from the history.
|
||||
tick_timer.Increment((kMaxHistoryMs + kFrameSizeMs) /
|
||||
tick_timer.ms_per_tick());
|
||||
EXPECT_EQ(
|
||||
tracker.Update(kTs + 2 * kTsIncrement + kFs * kMaxHistoryMs / 1000, kFs),
|
||||
0);
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
Reference in New Issue
Block a user