mirror of
https://github.com/mollyim/webrtc.git
synced 2025-05-13 13:50:40 +01:00
Ignore old, duplicate and overlapping packets in packet arrival history.
This should mostly be a noop, but in a follow up cl we will insert all packets after splitting, which will allow for adapting the delay to FEC (both RED and codec inband) that is useful for decoding (i.e. not already covered by primary packets). A slight behavior change is that reordered packets are no longer included in max delay calculation. Implementation details: - A map ordered by RTP timestamp is used to store the arrivals. - When inserting new packets, we check if the timestamp is too old, already exists or if the packet is fully covered by another packet (based on timestamp and packet duration). - Separate deques are used to keep track of "min" and "max" arrivals (as defined by ordering operators). The queues maintain a strictly increasing/decreasing order so that min/max is always at begin(). Bug: webrtc:13322 Change-Id: I8b6cf5afff77b4adc3c29745b95627e955715b5a Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/337184 Reviewed-by: Henrik Lundin <henrik.lundin@webrtc.org> Commit-Queue: Jakob Ivarsson <jakobi@webrtc.org> Cr-Commit-Position: refs/heads/main@{#41656}
This commit is contained in:
parent
b5f2b17fd8
commit
53e41a2bc6
6 changed files with 188 additions and 108 deletions
|
@ -14,7 +14,6 @@
|
|||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "absl/types/optional.h"
|
||||
#include "api/neteq/neteq.h"
|
||||
|
@ -22,7 +21,6 @@
|
|||
#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/experiments/field_trial_parser.h"
|
||||
#include "rtc_base/experiments/struct_parameters_parser.h"
|
||||
#include "rtc_base/logging.h"
|
||||
#include "rtc_base/numerics/safe_conversions.h"
|
||||
|
@ -102,6 +100,7 @@ DecisionLogic::DecisionLogic(
|
|||
packet_arrival_history_(packet_arrival_history
|
||||
? std::move(packet_arrival_history)
|
||||
: std::make_unique<PacketArrivalHistory>(
|
||||
config.tick_timer,
|
||||
config_.packet_history_size_ms)),
|
||||
tick_timer_(config.tick_timer),
|
||||
disallow_time_stretching_(!config.allow_time_stretching),
|
||||
|
@ -221,14 +220,14 @@ absl::optional<int> DecisionLogic::PacketArrived(
|
|||
packet_length_samples_ = info.packet_length_samples;
|
||||
delay_manager_->SetPacketAudioLength(packet_length_samples_ * 1000 / fs_hz);
|
||||
}
|
||||
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) {
|
||||
bool inserted = packet_arrival_history_->Insert(info.main_timestamp,
|
||||
info.packet_length_samples);
|
||||
if (!inserted || 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);
|
||||
packet_arrival_history_->GetDelayMs(info.main_timestamp);
|
||||
bool reordered =
|
||||
!packet_arrival_history_->IsNewestRtpTimestamp(info.main_timestamp);
|
||||
delay_manager_->Update(arrival_delay_ms, reordered);
|
||||
|
@ -464,8 +463,7 @@ int DecisionLogic::GetPlayoutDelayMs(
|
|||
NetEqController::NetEqStatus status) const {
|
||||
uint32_t playout_timestamp =
|
||||
status.target_timestamp - status.sync_buffer_samples;
|
||||
return packet_arrival_history_->GetDelayMs(
|
||||
playout_timestamp, tick_timer_->ticks() * tick_timer_->ms_per_tick());
|
||||
return packet_arrival_history_->GetDelayMs(playout_timestamp);
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
|
|
|
@ -14,12 +14,10 @@
|
|||
|
||||
#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_manager.h"
|
||||
#include "modules/audio_coding/neteq/mock/mock_buffer_level_filter.h"
|
||||
#include "modules/audio_coding/neteq/mock/mock_delay_manager.h"
|
||||
#include "modules/audio_coding/neteq/mock/mock_packet_arrival_history.h"
|
||||
#include "test/field_trial.h"
|
||||
#include "test/gtest.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
@ -64,7 +62,8 @@ class DecisionLogicTest : public ::testing::Test {
|
|||
mock_delay_manager_ = delay_manager.get();
|
||||
auto buffer_level_filter = std::make_unique<MockBufferLevelFilter>();
|
||||
mock_buffer_level_filter_ = buffer_level_filter.get();
|
||||
auto packet_arrival_history = std::make_unique<MockPacketArrivalHistory>();
|
||||
auto packet_arrival_history =
|
||||
std::make_unique<MockPacketArrivalHistory>(&tick_timer_);
|
||||
mock_packet_arrival_history_ = packet_arrival_history.get();
|
||||
decision_logic_ = std::make_unique<DecisionLogic>(
|
||||
config, std::move(delay_manager), std::move(buffer_level_filter),
|
||||
|
@ -82,7 +81,7 @@ class DecisionLogicTest : public ::testing::Test {
|
|||
TEST_F(DecisionLogicTest, NormalOperation) {
|
||||
EXPECT_CALL(*mock_delay_manager_, TargetDelayMs())
|
||||
.WillRepeatedly(Return(100));
|
||||
EXPECT_CALL(*mock_packet_arrival_history_, GetDelayMs(_, _))
|
||||
EXPECT_CALL(*mock_packet_arrival_history_, GetDelayMs(_))
|
||||
.WillRepeatedly(Return(100));
|
||||
EXPECT_CALL(*mock_packet_arrival_history_, GetMaxDelayMs())
|
||||
.WillRepeatedly(Return(0));
|
||||
|
@ -98,7 +97,7 @@ TEST_F(DecisionLogicTest, NormalOperation) {
|
|||
TEST_F(DecisionLogicTest, Accelerate) {
|
||||
EXPECT_CALL(*mock_delay_manager_, TargetDelayMs())
|
||||
.WillRepeatedly(Return(100));
|
||||
EXPECT_CALL(*mock_packet_arrival_history_, GetDelayMs(_, _))
|
||||
EXPECT_CALL(*mock_packet_arrival_history_, GetDelayMs(_))
|
||||
.WillRepeatedly(Return(150));
|
||||
EXPECT_CALL(*mock_packet_arrival_history_, GetMaxDelayMs())
|
||||
.WillRepeatedly(Return(0));
|
||||
|
@ -114,7 +113,7 @@ TEST_F(DecisionLogicTest, Accelerate) {
|
|||
TEST_F(DecisionLogicTest, FastAccelerate) {
|
||||
EXPECT_CALL(*mock_delay_manager_, TargetDelayMs())
|
||||
.WillRepeatedly(Return(100));
|
||||
EXPECT_CALL(*mock_packet_arrival_history_, GetDelayMs(_, _))
|
||||
EXPECT_CALL(*mock_packet_arrival_history_, GetDelayMs(_))
|
||||
.WillRepeatedly(Return(500));
|
||||
EXPECT_CALL(*mock_packet_arrival_history_, GetMaxDelayMs())
|
||||
.WillRepeatedly(Return(0));
|
||||
|
@ -130,7 +129,7 @@ TEST_F(DecisionLogicTest, FastAccelerate) {
|
|||
TEST_F(DecisionLogicTest, PreemptiveExpand) {
|
||||
EXPECT_CALL(*mock_delay_manager_, TargetDelayMs())
|
||||
.WillRepeatedly(Return(100));
|
||||
EXPECT_CALL(*mock_packet_arrival_history_, GetDelayMs(_, _))
|
||||
EXPECT_CALL(*mock_packet_arrival_history_, GetDelayMs(_))
|
||||
.WillRepeatedly(Return(50));
|
||||
EXPECT_CALL(*mock_packet_arrival_history_, GetMaxDelayMs())
|
||||
.WillRepeatedly(Return(0));
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#ifndef MODULES_AUDIO_CODING_NETEQ_MOCK_MOCK_PACKET_ARRIVAL_HISTORY_H_
|
||||
#define MODULES_AUDIO_CODING_NETEQ_MOCK_MOCK_PACKET_ARRIVAL_HISTORY_H_
|
||||
|
||||
#include "api/neteq/tick_timer.h"
|
||||
#include "modules/audio_coding/neteq/packet_arrival_history.h"
|
||||
#include "test/gmock.h"
|
||||
|
||||
|
@ -18,12 +19,10 @@ namespace webrtc {
|
|||
|
||||
class MockPacketArrivalHistory : public PacketArrivalHistory {
|
||||
public:
|
||||
MockPacketArrivalHistory() : PacketArrivalHistory(0) {}
|
||||
MockPacketArrivalHistory(const TickTimer* tick_timer)
|
||||
: PacketArrivalHistory(tick_timer, 0) {}
|
||||
|
||||
MOCK_METHOD(int,
|
||||
GetDelayMs,
|
||||
(uint32_t rtp_timestamp, int64_t time_ms),
|
||||
(const override));
|
||||
MOCK_METHOD(int, GetDelayMs, (uint32_t rtp_timestamp), (const override));
|
||||
MOCK_METHOD(int, GetMaxDelayMs, (), (const override));
|
||||
};
|
||||
|
||||
|
|
|
@ -11,95 +11,122 @@
|
|||
#include "modules/audio_coding/neteq/packet_arrival_history.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
|
||||
#include "api/neteq/tick_timer.h"
|
||||
#include "rtc_base/checks.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
PacketArrivalHistory::PacketArrivalHistory(int window_size_ms)
|
||||
: window_size_ms_(window_size_ms) {}
|
||||
PacketArrivalHistory::PacketArrivalHistory(const TickTimer* tick_timer,
|
||||
int window_size_ms)
|
||||
: tick_timer_(tick_timer), window_size_ms_(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 = timestamp_unwrapper_.Unwrap(rtp_timestamp);
|
||||
if (!newest_rtp_timestamp_ ||
|
||||
unwrapped_rtp_timestamp > *newest_rtp_timestamp_) {
|
||||
newest_rtp_timestamp_ = unwrapped_rtp_timestamp;
|
||||
bool PacketArrivalHistory::Insert(uint32_t rtp_timestamp,
|
||||
int packet_length_samples) {
|
||||
int64_t arrival_timestamp =
|
||||
tick_timer_->ticks() * tick_timer_->ms_per_tick() * sample_rate_khz_;
|
||||
PacketArrival packet(timestamp_unwrapper_.Unwrap(rtp_timestamp),
|
||||
arrival_timestamp, packet_length_samples);
|
||||
if (IsObsolete(packet)) {
|
||||
return false;
|
||||
}
|
||||
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 / sample_rate_khz_) {
|
||||
if (&history_.front() == min_packet_arrival_) {
|
||||
min_packet_arrival_ = nullptr;
|
||||
if (Contains(packet)) {
|
||||
return false;
|
||||
}
|
||||
if (&history_.front() == max_packet_arrival_) {
|
||||
max_packet_arrival_ = nullptr;
|
||||
history_.emplace(packet.rtp_timestamp, packet);
|
||||
if (packet != history_.rbegin()->second) {
|
||||
// Packet was reordered.
|
||||
return true;
|
||||
}
|
||||
history_.pop_front();
|
||||
// Remove old packets.
|
||||
while (IsObsolete(history_.begin()->second)) {
|
||||
if (history_.begin()->second == min_packet_arrivals_.front()) {
|
||||
min_packet_arrivals_.pop_front();
|
||||
}
|
||||
if (!min_packet_arrival_ || !max_packet_arrival_) {
|
||||
for (const PacketArrival& packet : history_) {
|
||||
MaybeUpdateCachedArrivals(packet);
|
||||
if (history_.begin()->second == max_packet_arrivals_.front()) {
|
||||
max_packet_arrivals_.pop_front();
|
||||
}
|
||||
history_.erase(history_.begin());
|
||||
}
|
||||
}
|
||||
|
||||
void PacketArrivalHistory::MaybeUpdateCachedArrivals(
|
||||
const PacketArrival& packet_arrival) {
|
||||
if (!min_packet_arrival_ || packet_arrival <= *min_packet_arrival_) {
|
||||
min_packet_arrival_ = &packet_arrival;
|
||||
// Ensure ordering constraints.
|
||||
while (!min_packet_arrivals_.empty() &&
|
||||
packet <= min_packet_arrivals_.back()) {
|
||||
min_packet_arrivals_.pop_back();
|
||||
}
|
||||
if (!max_packet_arrival_ || packet_arrival >= *max_packet_arrival_) {
|
||||
max_packet_arrival_ = &packet_arrival;
|
||||
while (!max_packet_arrivals_.empty() &&
|
||||
packet >= max_packet_arrivals_.back()) {
|
||||
max_packet_arrivals_.pop_back();
|
||||
}
|
||||
min_packet_arrivals_.push_back(packet);
|
||||
max_packet_arrivals_.push_back(packet);
|
||||
return true;
|
||||
}
|
||||
|
||||
void PacketArrivalHistory::Reset() {
|
||||
history_.clear();
|
||||
min_packet_arrival_ = nullptr;
|
||||
max_packet_arrival_ = nullptr;
|
||||
min_packet_arrivals_.clear();
|
||||
max_packet_arrivals_.clear();
|
||||
timestamp_unwrapper_.Reset();
|
||||
newest_rtp_timestamp_ = absl::nullopt;
|
||||
}
|
||||
|
||||
int PacketArrivalHistory::GetDelayMs(uint32_t rtp_timestamp,
|
||||
int64_t time_ms) const {
|
||||
RTC_DCHECK(sample_rate_khz_ > 0);
|
||||
int64_t unwrapped_rtp_timestamp_ms =
|
||||
timestamp_unwrapper_.PeekUnwrap(rtp_timestamp) / sample_rate_khz_;
|
||||
PacketArrival packet(unwrapped_rtp_timestamp_ms, time_ms);
|
||||
int PacketArrivalHistory::GetDelayMs(uint32_t rtp_timestamp) const {
|
||||
int64_t unwrapped_rtp_timestamp =
|
||||
timestamp_unwrapper_.PeekUnwrap(rtp_timestamp);
|
||||
int64_t current_timestamp =
|
||||
tick_timer_->ticks() * tick_timer_->ms_per_tick() * sample_rate_khz_;
|
||||
PacketArrival packet(unwrapped_rtp_timestamp, current_timestamp,
|
||||
/*duration_ms=*/0);
|
||||
return GetPacketArrivalDelayMs(packet);
|
||||
}
|
||||
|
||||
int PacketArrivalHistory::GetMaxDelayMs() const {
|
||||
if (!max_packet_arrival_) {
|
||||
if (max_packet_arrivals_.empty()) {
|
||||
return 0;
|
||||
}
|
||||
return GetPacketArrivalDelayMs(*max_packet_arrival_);
|
||||
return GetPacketArrivalDelayMs(max_packet_arrivals_.front());
|
||||
}
|
||||
|
||||
bool PacketArrivalHistory::IsNewestRtpTimestamp(uint32_t rtp_timestamp) const {
|
||||
if (!newest_rtp_timestamp_) {
|
||||
return false;
|
||||
if (history_.empty()) {
|
||||
return true;
|
||||
}
|
||||
int64_t unwrapped_rtp_timestamp =
|
||||
timestamp_unwrapper_.PeekUnwrap(rtp_timestamp);
|
||||
return unwrapped_rtp_timestamp == *newest_rtp_timestamp_;
|
||||
return unwrapped_rtp_timestamp == history_.rbegin()->second.rtp_timestamp;
|
||||
}
|
||||
|
||||
int PacketArrivalHistory::GetPacketArrivalDelayMs(
|
||||
const PacketArrival& packet_arrival) const {
|
||||
if (!min_packet_arrival_) {
|
||||
if (min_packet_arrivals_.empty()) {
|
||||
return 0;
|
||||
}
|
||||
return std::max(static_cast<int>(packet_arrival.arrival_time_ms -
|
||||
min_packet_arrival_->arrival_time_ms -
|
||||
(packet_arrival.rtp_timestamp_ms -
|
||||
min_packet_arrival_->rtp_timestamp_ms)),
|
||||
RTC_DCHECK_NE(sample_rate_khz_, 0);
|
||||
// TODO(jakobi): Timestamps are first converted to millis for bit-exactness.
|
||||
return std::max<int>(
|
||||
packet_arrival.arrival_timestamp / sample_rate_khz_ -
|
||||
min_packet_arrivals_.front().arrival_timestamp / sample_rate_khz_ -
|
||||
(packet_arrival.rtp_timestamp / sample_rate_khz_ -
|
||||
min_packet_arrivals_.front().rtp_timestamp / sample_rate_khz_),
|
||||
0);
|
||||
}
|
||||
|
||||
bool PacketArrivalHistory::IsObsolete(
|
||||
const PacketArrival& packet_arrival) const {
|
||||
if (history_.empty()) {
|
||||
return false;
|
||||
}
|
||||
return packet_arrival.rtp_timestamp + window_size_ms_ * sample_rate_khz_ <
|
||||
history_.rbegin()->second.rtp_timestamp;
|
||||
}
|
||||
|
||||
bool PacketArrivalHistory::Contains(const PacketArrival& packet_arrival) const {
|
||||
auto it = history_.upper_bound(packet_arrival.rtp_timestamp);
|
||||
if (it == history_.begin()) {
|
||||
return false;
|
||||
}
|
||||
--it;
|
||||
return it->second.contains(packet_arrival);
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
|
|
|
@ -11,10 +11,11 @@
|
|||
#ifndef MODULES_AUDIO_CODING_NETEQ_PACKET_ARRIVAL_HISTORY_H_
|
||||
#define MODULES_AUDIO_CODING_NETEQ_PACKET_ARRIVAL_HISTORY_H_
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <deque>
|
||||
#include <map>
|
||||
|
||||
#include "absl/types/optional.h"
|
||||
#include "api/neteq/tick_timer.h"
|
||||
#include "rtc_base/numerics/sequence_number_unwrapper.h"
|
||||
|
||||
|
@ -25,19 +26,22 @@ namespace webrtc {
|
|||
// pruned.
|
||||
class PacketArrivalHistory {
|
||||
public:
|
||||
explicit PacketArrivalHistory(int window_size_ms);
|
||||
explicit PacketArrivalHistory(const TickTimer* tick_timer,
|
||||
int window_size_ms);
|
||||
virtual ~PacketArrivalHistory() = default;
|
||||
|
||||
// Insert packet with `rtp_timestamp` and `arrival_time_ms` into the history.
|
||||
void Insert(uint32_t rtp_timestamp, int64_t arrival_time_ms);
|
||||
// Insert packet with `rtp_timestamp` into the history. Returns true if the
|
||||
// packet was inserted, false if the timestamp is too old or if the timestamp
|
||||
// already exists.
|
||||
bool Insert(uint32_t rtp_timestamp, int packet_length_samples);
|
||||
|
||||
// The delay for `rtp_timestamp` at `time_ms` is calculated as
|
||||
// `(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.
|
||||
virtual int GetDelayMs(uint32_t rtp_timestamp, int64_t time_ms) const;
|
||||
// The delay for `rtp_timestamp` at time `now` is calculated as
|
||||
// `(now - p.arrival_timestamp) - (rtp_timestamp - p.rtp_timestamp)` where `p`
|
||||
// is chosen as the packet arrival in the history that maximizes the delay.
|
||||
virtual int GetDelayMs(uint32_t rtp_timestamp) const;
|
||||
|
||||
// Get the maximum packet arrival delay observed in the history.
|
||||
// Get the maximum packet arrival delay observed in the history, excluding
|
||||
// reordered packets.
|
||||
virtual int GetMaxDelayMs() const;
|
||||
|
||||
bool IsNewestRtpTimestamp(uint32_t rtp_timestamp) const;
|
||||
|
@ -52,30 +56,53 @@ class PacketArrivalHistory {
|
|||
|
||||
private:
|
||||
struct PacketArrival {
|
||||
PacketArrival(int64_t rtp_timestamp_ms, int64_t arrival_time_ms)
|
||||
: rtp_timestamp_ms(rtp_timestamp_ms),
|
||||
arrival_time_ms(arrival_time_ms) {}
|
||||
int64_t rtp_timestamp_ms;
|
||||
int64_t arrival_time_ms;
|
||||
PacketArrival(int64_t rtp_timestamp,
|
||||
int64_t arrival_timestamp,
|
||||
int length_samples)
|
||||
: rtp_timestamp(rtp_timestamp),
|
||||
arrival_timestamp(arrival_timestamp),
|
||||
length_samples(length_samples) {}
|
||||
PacketArrival() = default;
|
||||
int64_t rtp_timestamp;
|
||||
int64_t arrival_timestamp;
|
||||
int length_samples;
|
||||
bool operator==(const PacketArrival& other) const {
|
||||
return rtp_timestamp == other.rtp_timestamp &&
|
||||
arrival_timestamp == other.arrival_timestamp &&
|
||||
length_samples == other.length_samples;
|
||||
}
|
||||
bool operator!=(const PacketArrival& other) const {
|
||||
return !(*this == other);
|
||||
}
|
||||
bool operator<=(const PacketArrival& other) const {
|
||||
return arrival_time_ms - rtp_timestamp_ms <=
|
||||
other.arrival_time_ms - other.rtp_timestamp_ms;
|
||||
return arrival_timestamp - rtp_timestamp <=
|
||||
other.arrival_timestamp - other.rtp_timestamp;
|
||||
}
|
||||
bool operator>=(const PacketArrival& other) const {
|
||||
return arrival_time_ms - rtp_timestamp_ms >=
|
||||
other.arrival_time_ms - other.rtp_timestamp_ms;
|
||||
return arrival_timestamp - rtp_timestamp >=
|
||||
other.arrival_timestamp - other.rtp_timestamp;
|
||||
}
|
||||
bool contains(const PacketArrival& other) const {
|
||||
return rtp_timestamp <= other.rtp_timestamp &&
|
||||
rtp_timestamp + length_samples >=
|
||||
other.rtp_timestamp + other.length_samples;
|
||||
}
|
||||
};
|
||||
std::deque<PacketArrival> history_;
|
||||
int GetPacketArrivalDelayMs(const PacketArrival& packet_arrival) const;
|
||||
// Updates `min_packet_arrival_` and `max_packet_arrival_`.
|
||||
void MaybeUpdateCachedArrivals(const PacketArrival& packet);
|
||||
const PacketArrival* min_packet_arrival_ = nullptr;
|
||||
const PacketArrival* max_packet_arrival_ = nullptr;
|
||||
// Checks if the packet is older than the window size.
|
||||
bool IsObsolete(const PacketArrival& packet_arrival) const;
|
||||
// Check if the packet exists or fully overlaps with a packet in the history.
|
||||
bool Contains(const PacketArrival& packet_arrival) const;
|
||||
const TickTimer* tick_timer_;
|
||||
const int window_size_ms_;
|
||||
RtpTimestampUnwrapper timestamp_unwrapper_;
|
||||
absl::optional<int64_t> newest_rtp_timestamp_;
|
||||
int sample_rate_khz_ = 0;
|
||||
RtpTimestampUnwrapper timestamp_unwrapper_;
|
||||
// Packet history ordered by rtp timestamp.
|
||||
std::map<int64_t, PacketArrival> history_;
|
||||
// Tracks min/max packet arrivals in `history_` in ascending/descending order.
|
||||
// Reordered packets are excluded.
|
||||
std::deque<PacketArrival> min_packet_arrivals_;
|
||||
std::deque<PacketArrival> max_packet_arrivals_;
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
|
|
@ -21,32 +21,36 @@ namespace {
|
|||
constexpr int kFs = 8000;
|
||||
constexpr int kFsKhz = kFs / 1000;
|
||||
constexpr int kFrameSizeMs = 20;
|
||||
constexpr int kFrameSizeSamples = kFrameSizeMs * kFsKhz;
|
||||
constexpr int kWindowSizeMs = 1000;
|
||||
|
||||
class PacketArrivalHistoryTest : public testing::Test {
|
||||
public:
|
||||
PacketArrivalHistoryTest() : history_(kWindowSizeMs) {
|
||||
PacketArrivalHistoryTest() : history_(&tick_timer_, kWindowSizeMs) {
|
||||
history_.set_sample_rate(kFs);
|
||||
}
|
||||
void IncrementTime(int delta_ms) { time_ms_ += delta_ms; }
|
||||
void IncrementTime(int delta_ms) {
|
||||
tick_timer_.Increment(delta_ms / tick_timer_.ms_per_tick());
|
||||
}
|
||||
int InsertPacketAndGetDelay(int timestamp_delta_ms) {
|
||||
uint32_t timestamp = timestamp_ + timestamp_delta_ms * kFsKhz;
|
||||
if (timestamp_delta_ms > 0) {
|
||||
timestamp_ = timestamp;
|
||||
}
|
||||
history_.Insert(timestamp, time_ms_);
|
||||
EXPECT_TRUE(history_.Insert(timestamp, kFrameSizeSamples));
|
||||
EXPECT_EQ(history_.IsNewestRtpTimestamp(timestamp),
|
||||
timestamp_delta_ms >= 0);
|
||||
return history_.GetDelayMs(timestamp, time_ms_);
|
||||
return history_.GetDelayMs(timestamp);
|
||||
}
|
||||
|
||||
protected:
|
||||
int64_t time_ms_ = 0;
|
||||
TickTimer tick_timer_;
|
||||
PacketArrivalHistory history_;
|
||||
uint32_t timestamp_ = 0x12345678;
|
||||
};
|
||||
|
||||
TEST_F(PacketArrivalHistoryTest, RelativeArrivalDelay) {
|
||||
// Insert first packet.
|
||||
EXPECT_EQ(InsertPacketAndGetDelay(0), 0);
|
||||
|
||||
IncrementTime(kFrameSizeMs);
|
||||
|
@ -56,7 +60,7 @@ TEST_F(PacketArrivalHistoryTest, RelativeArrivalDelay) {
|
|||
EXPECT_EQ(InsertPacketAndGetDelay(kFrameSizeMs), 20);
|
||||
|
||||
// Reordered packet.
|
||||
EXPECT_EQ(InsertPacketAndGetDelay(-2 * kFrameSizeMs), 60);
|
||||
EXPECT_EQ(InsertPacketAndGetDelay(-3 * kFrameSizeMs), 80);
|
||||
|
||||
IncrementTime(2 * kFrameSizeMs);
|
||||
EXPECT_EQ(InsertPacketAndGetDelay(kFrameSizeMs), 40);
|
||||
|
@ -68,7 +72,7 @@ TEST_F(PacketArrivalHistoryTest, RelativeArrivalDelay) {
|
|||
EXPECT_EQ(InsertPacketAndGetDelay(kFrameSizeMs), 20);
|
||||
|
||||
// Earlier packet is now more delayed due to the new reference packet.
|
||||
EXPECT_EQ(history_.GetMaxDelayMs(), 100);
|
||||
EXPECT_EQ(history_.GetMaxDelayMs(), 80);
|
||||
}
|
||||
|
||||
TEST_F(PacketArrivalHistoryTest, ReorderedPackets) {
|
||||
|
@ -86,7 +90,7 @@ TEST_F(PacketArrivalHistoryTest, ReorderedPackets) {
|
|||
IncrementTime(4 * kFrameSizeMs);
|
||||
EXPECT_EQ(InsertPacketAndGetDelay(kFrameSizeMs), 60);
|
||||
|
||||
EXPECT_EQ(history_.GetMaxDelayMs(), 80);
|
||||
EXPECT_EQ(history_.GetMaxDelayMs(), 60);
|
||||
}
|
||||
|
||||
TEST_F(PacketArrivalHistoryTest, MaxHistorySize) {
|
||||
|
@ -117,7 +121,7 @@ TEST_F(PacketArrivalHistoryTest, TimestampWraparound) {
|
|||
// Insert another in-order packet after the wraparound.
|
||||
EXPECT_EQ(InsertPacketAndGetDelay(kFrameSizeMs), 0);
|
||||
|
||||
EXPECT_EQ(history_.GetMaxDelayMs(), 3 * kFrameSizeMs);
|
||||
EXPECT_EQ(history_.GetMaxDelayMs(), kFrameSizeMs);
|
||||
}
|
||||
|
||||
TEST_F(PacketArrivalHistoryTest, TimestampWraparoundBackwards) {
|
||||
|
@ -134,7 +138,33 @@ TEST_F(PacketArrivalHistoryTest, TimestampWraparoundBackwards) {
|
|||
// Insert another in-order packet after the wraparound.
|
||||
EXPECT_EQ(InsertPacketAndGetDelay(kFrameSizeMs), 0);
|
||||
|
||||
EXPECT_EQ(history_.GetMaxDelayMs(), 3 * kFrameSizeMs);
|
||||
EXPECT_EQ(history_.GetMaxDelayMs(), kFrameSizeMs);
|
||||
}
|
||||
|
||||
TEST_F(PacketArrivalHistoryTest, OldPacketShouldNotBeInserted) {
|
||||
// Insert first packet as reference.
|
||||
EXPECT_EQ(InsertPacketAndGetDelay(0), 0);
|
||||
// Insert packet with timestamp older than the window size compared to the
|
||||
// first packet.
|
||||
EXPECT_FALSE(history_.Insert(timestamp_ - kWindowSizeMs * kFsKhz - 1,
|
||||
kFrameSizeSamples));
|
||||
}
|
||||
|
||||
TEST_F(PacketArrivalHistoryTest, DuplicatePacketShouldNotBeInserted) {
|
||||
// Insert first packet as reference.
|
||||
uint32_t first_timestamp = timestamp_;
|
||||
EXPECT_EQ(InsertPacketAndGetDelay(0), 0);
|
||||
EXPECT_EQ(InsertPacketAndGetDelay(kFrameSizeMs), 0);
|
||||
// Same timestamp as the first packet.
|
||||
EXPECT_FALSE(history_.Insert(first_timestamp, kFrameSizeSamples));
|
||||
}
|
||||
|
||||
TEST_F(PacketArrivalHistoryTest, OverlappingPacketShouldNotBeInserted) {
|
||||
// Insert first packet as reference.
|
||||
EXPECT_EQ(InsertPacketAndGetDelay(0), 0);
|
||||
// 10 ms overlap with the previous packet.
|
||||
EXPECT_FALSE(history_.Insert(timestamp_ + kFrameSizeSamples / 2,
|
||||
kFrameSizeSamples / 2));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
|
Loading…
Reference in a new issue