Remove locks and dependency on ProcessThread+Module from NackModule2.
Change-Id: I39975e7812d7722fd231ac57e261fd6add9de000 Bug: webrtc:11594 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/175341 Reviewed-by: Philip Eliasson <philipel@webrtc.org> Commit-Queue: Tommi <tommi@webrtc.org> Cr-Commit-Position: refs/heads/master@{#31367}
This commit is contained in:
@ -84,7 +84,11 @@ rtc_library("nack_module") {
|
||||
"../../rtc_base:checks",
|
||||
"../../rtc_base:rtc_base_approved",
|
||||
"../../rtc_base:rtc_numerics",
|
||||
"../../rtc_base:rtc_task_queue",
|
||||
"../../rtc_base/experiments:field_trial_parser",
|
||||
"../../rtc_base/synchronization:sequence_checker",
|
||||
"../../rtc_base/task_utils:pending_task_safety_flag",
|
||||
"../../rtc_base/task_utils:repeating_task",
|
||||
"../../system_wrappers",
|
||||
"../../system_wrappers:field_trial",
|
||||
"../utility",
|
||||
|
||||
@ -14,10 +14,10 @@
|
||||
#include <limits>
|
||||
|
||||
#include "api/units/timestamp.h"
|
||||
#include "modules/utility/include/process_thread.h"
|
||||
#include "rtc_base/checks.h"
|
||||
#include "rtc_base/experiments/field_trial_parser.h"
|
||||
#include "rtc_base/logging.h"
|
||||
#include "rtc_base/task_queue.h"
|
||||
#include "system_wrappers/include/field_trial.h"
|
||||
|
||||
namespace webrtc {
|
||||
@ -27,8 +27,6 @@ const int kMaxPacketAge = 10000;
|
||||
const int kMaxNackPackets = 1000;
|
||||
const int kDefaultRttMs = 100;
|
||||
const int kMaxNackRetries = 10;
|
||||
const int kProcessFrequency = 50;
|
||||
const int kProcessIntervalMs = 1000 / kProcessFrequency;
|
||||
const int kMaxReorderedPackets = 128;
|
||||
const int kNumReorderingBuckets = 10;
|
||||
const int kDefaultSendNackDelayMs = 0;
|
||||
@ -45,6 +43,8 @@ int64_t GetSendNackDelay() {
|
||||
}
|
||||
} // namespace
|
||||
|
||||
constexpr TimeDelta NackModule2::kUpdateInterval;
|
||||
|
||||
NackModule2::NackInfo::NackInfo()
|
||||
: seq_num(0), send_at_seq_num(0), sent_at_time(-1), retries(0) {}
|
||||
|
||||
@ -88,32 +88,58 @@ NackModule2::BackoffSettings::ParseFromFieldTrials() {
|
||||
return absl::nullopt;
|
||||
}
|
||||
|
||||
NackModule2::NackModule2(Clock* clock,
|
||||
NackModule2::NackModule2(TaskQueueBase* current_queue,
|
||||
Clock* clock,
|
||||
NackSender* nack_sender,
|
||||
KeyFrameRequestSender* keyframe_request_sender)
|
||||
: clock_(clock),
|
||||
KeyFrameRequestSender* keyframe_request_sender,
|
||||
TimeDelta update_interval /*= kUpdateInterval*/)
|
||||
: worker_thread_(current_queue),
|
||||
update_interval_(update_interval),
|
||||
clock_(clock),
|
||||
nack_sender_(nack_sender),
|
||||
keyframe_request_sender_(keyframe_request_sender),
|
||||
reordering_histogram_(kNumReorderingBuckets, kMaxReorderedPackets),
|
||||
initialized_(false),
|
||||
rtt_ms_(kDefaultRttMs),
|
||||
newest_seq_num_(0),
|
||||
next_process_time_ms_(-1),
|
||||
send_nack_delay_ms_(GetSendNackDelay()),
|
||||
backoff_settings_(BackoffSettings::ParseFromFieldTrials()) {
|
||||
RTC_DCHECK(clock_);
|
||||
RTC_DCHECK(nack_sender_);
|
||||
RTC_DCHECK(keyframe_request_sender_);
|
||||
RTC_DCHECK_GT(update_interval.ms(), 0);
|
||||
RTC_DCHECK(worker_thread_);
|
||||
RTC_DCHECK(worker_thread_->IsCurrent());
|
||||
|
||||
repeating_task_ = RepeatingTaskHandle::DelayedStart(
|
||||
TaskQueueBase::Current(), update_interval_,
|
||||
[this]() {
|
||||
RTC_DCHECK_RUN_ON(worker_thread_);
|
||||
std::vector<uint16_t> nack_batch = GetNackBatch(kTimeOnly);
|
||||
if (!nack_batch.empty()) {
|
||||
// This batch of NACKs is triggered externally; there is no external
|
||||
// initiator who can batch them with other feedback messages.
|
||||
nack_sender_->SendNack(nack_batch, /*buffering_allowed=*/false);
|
||||
}
|
||||
return update_interval_;
|
||||
},
|
||||
clock_);
|
||||
}
|
||||
|
||||
NackModule2::~NackModule2() {
|
||||
RTC_DCHECK_RUN_ON(worker_thread_);
|
||||
repeating_task_.Stop();
|
||||
}
|
||||
|
||||
int NackModule2::OnReceivedPacket(uint16_t seq_num, bool is_keyframe) {
|
||||
RTC_DCHECK_RUN_ON(worker_thread_);
|
||||
return OnReceivedPacket(seq_num, is_keyframe, false);
|
||||
}
|
||||
|
||||
int NackModule2::OnReceivedPacket(uint16_t seq_num,
|
||||
bool is_keyframe,
|
||||
bool is_recovered) {
|
||||
rtc::CritScope lock(&crit_);
|
||||
RTC_DCHECK_RUN_ON(worker_thread_);
|
||||
// TODO(philipel): When the packet includes information whether it is
|
||||
// retransmitted or not, use that value instead. For
|
||||
// now set it to true, which will cause the reordering
|
||||
@ -182,61 +208,24 @@ int NackModule2::OnReceivedPacket(uint16_t seq_num,
|
||||
}
|
||||
|
||||
void NackModule2::ClearUpTo(uint16_t seq_num) {
|
||||
rtc::CritScope lock(&crit_);
|
||||
nack_list_.erase(nack_list_.begin(), nack_list_.lower_bound(seq_num));
|
||||
keyframe_list_.erase(keyframe_list_.begin(),
|
||||
keyframe_list_.lower_bound(seq_num));
|
||||
recovered_list_.erase(recovered_list_.begin(),
|
||||
recovered_list_.lower_bound(seq_num));
|
||||
// Called via RtpVideoStreamReceiver2::FrameContinuous on the network thread.
|
||||
worker_thread_->PostTask(ToQueuedTask(task_safety_, [seq_num, this]() {
|
||||
RTC_DCHECK_RUN_ON(worker_thread_);
|
||||
nack_list_.erase(nack_list_.begin(), nack_list_.lower_bound(seq_num));
|
||||
keyframe_list_.erase(keyframe_list_.begin(),
|
||||
keyframe_list_.lower_bound(seq_num));
|
||||
recovered_list_.erase(recovered_list_.begin(),
|
||||
recovered_list_.lower_bound(seq_num));
|
||||
}));
|
||||
}
|
||||
|
||||
void NackModule2::UpdateRtt(int64_t rtt_ms) {
|
||||
rtc::CritScope lock(&crit_);
|
||||
RTC_DCHECK_RUN_ON(worker_thread_);
|
||||
rtt_ms_ = rtt_ms;
|
||||
}
|
||||
|
||||
void NackModule2::Clear() {
|
||||
rtc::CritScope lock(&crit_);
|
||||
nack_list_.clear();
|
||||
keyframe_list_.clear();
|
||||
recovered_list_.clear();
|
||||
}
|
||||
|
||||
int64_t NackModule2::TimeUntilNextProcess() {
|
||||
return std::max<int64_t>(next_process_time_ms_ - clock_->TimeInMilliseconds(),
|
||||
0);
|
||||
}
|
||||
|
||||
void NackModule2::Process() {
|
||||
if (nack_sender_) {
|
||||
std::vector<uint16_t> nack_batch;
|
||||
{
|
||||
rtc::CritScope lock(&crit_);
|
||||
nack_batch = GetNackBatch(kTimeOnly);
|
||||
}
|
||||
|
||||
if (!nack_batch.empty()) {
|
||||
// This batch of NACKs is triggered externally; there is no external
|
||||
// initiator who can batch them with other feedback messages.
|
||||
nack_sender_->SendNack(nack_batch, /*buffering_allowed=*/false);
|
||||
}
|
||||
}
|
||||
|
||||
// Update the next_process_time_ms_ in intervals to achieve
|
||||
// the targeted frequency over time. Also add multiple intervals
|
||||
// in case of a skip in time as to not make uneccessary
|
||||
// calls to Process in order to catch up.
|
||||
int64_t now_ms = clock_->TimeInMilliseconds();
|
||||
if (next_process_time_ms_ == -1) {
|
||||
next_process_time_ms_ = now_ms + kProcessIntervalMs;
|
||||
} else {
|
||||
next_process_time_ms_ = next_process_time_ms_ + kProcessIntervalMs +
|
||||
(now_ms - next_process_time_ms_) /
|
||||
kProcessIntervalMs * kProcessIntervalMs;
|
||||
}
|
||||
}
|
||||
|
||||
bool NackModule2::RemovePacketsUntilKeyFrame() {
|
||||
// Called on worker_thread_.
|
||||
while (!keyframe_list_.empty()) {
|
||||
auto it = nack_list_.lower_bound(*keyframe_list_.begin());
|
||||
|
||||
@ -256,6 +245,7 @@ bool NackModule2::RemovePacketsUntilKeyFrame() {
|
||||
|
||||
void NackModule2::AddPacketsToNack(uint16_t seq_num_start,
|
||||
uint16_t seq_num_end) {
|
||||
// Called on worker_thread_.
|
||||
// Remove old packets.
|
||||
auto it = nack_list_.lower_bound(seq_num_end - kMaxPacketAge);
|
||||
nack_list_.erase(nack_list_.begin(), it);
|
||||
@ -290,6 +280,8 @@ void NackModule2::AddPacketsToNack(uint16_t seq_num_start,
|
||||
}
|
||||
|
||||
std::vector<uint16_t> NackModule2::GetNackBatch(NackFilterOptions options) {
|
||||
// Called on worker_thread_.
|
||||
|
||||
bool consider_seq_num = options != kTimeOnly;
|
||||
bool consider_timestamp = options != kSeqNumOnly;
|
||||
Timestamp now = clock_->CurrentTime();
|
||||
@ -335,12 +327,14 @@ std::vector<uint16_t> NackModule2::GetNackBatch(NackFilterOptions options) {
|
||||
}
|
||||
|
||||
void NackModule2::UpdateReorderingStatistics(uint16_t seq_num) {
|
||||
// Running on worker_thread_.
|
||||
RTC_DCHECK(AheadOf(newest_seq_num_, seq_num));
|
||||
uint16_t diff = ReverseDiff(newest_seq_num_, seq_num);
|
||||
reordering_histogram_.Add(diff);
|
||||
}
|
||||
|
||||
int NackModule2::WaitNumberOfPackets(float probability) const {
|
||||
// Called on worker_thread_;
|
||||
if (reordering_histogram_.NumValues() == 0)
|
||||
return 0;
|
||||
return reordering_histogram_.InverseCdf(probability);
|
||||
|
||||
@ -18,32 +18,37 @@
|
||||
#include <vector>
|
||||
|
||||
#include "api/units/time_delta.h"
|
||||
#include "modules/include/module.h"
|
||||
#include "modules/include/module_common_types.h"
|
||||
#include "modules/video_coding/histogram.h"
|
||||
#include "rtc_base/critical_section.h"
|
||||
#include "rtc_base/numerics/sequence_number_util.h"
|
||||
#include "rtc_base/synchronization/sequence_checker.h"
|
||||
#include "rtc_base/task_queue.h"
|
||||
#include "rtc_base/task_utils/pending_task_safety_flag.h"
|
||||
#include "rtc_base/task_utils/repeating_task.h"
|
||||
#include "rtc_base/thread_annotations.h"
|
||||
#include "system_wrappers/include/clock.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
class NackModule2 final : public Module {
|
||||
// TODO(bugs.webrtc.org/11594): This class no longer implements the Module
|
||||
// interface and therefore "NackModule" may not be a descriptive name anymore.
|
||||
// Consider renaming to e.g. NackTracker or NackRequester.
|
||||
class NackModule2 final {
|
||||
public:
|
||||
NackModule2(Clock* clock,
|
||||
static constexpr TimeDelta kUpdateInterval = TimeDelta::Millis(20);
|
||||
|
||||
NackModule2(TaskQueueBase* current_queue,
|
||||
Clock* clock,
|
||||
NackSender* nack_sender,
|
||||
KeyFrameRequestSender* keyframe_request_sender);
|
||||
KeyFrameRequestSender* keyframe_request_sender,
|
||||
TimeDelta update_interval = kUpdateInterval);
|
||||
~NackModule2();
|
||||
|
||||
int OnReceivedPacket(uint16_t seq_num, bool is_keyframe);
|
||||
int OnReceivedPacket(uint16_t seq_num, bool is_keyframe, bool is_recovered);
|
||||
|
||||
void ClearUpTo(uint16_t seq_num);
|
||||
void UpdateRtt(int64_t rtt_ms);
|
||||
void Clear();
|
||||
|
||||
// Module implementation
|
||||
int64_t TimeUntilNextProcess() override;
|
||||
void Process() override;
|
||||
|
||||
private:
|
||||
// Which fields to consider when deciding which packet to nack in
|
||||
@ -79,24 +84,30 @@ class NackModule2 final : public Module {
|
||||
};
|
||||
|
||||
void AddPacketsToNack(uint16_t seq_num_start, uint16_t seq_num_end)
|
||||
RTC_EXCLUSIVE_LOCKS_REQUIRED(crit_);
|
||||
RTC_EXCLUSIVE_LOCKS_REQUIRED(worker_thread_);
|
||||
|
||||
// Removes packets from the nack list until the next keyframe. Returns true
|
||||
// if packets were removed.
|
||||
bool RemovePacketsUntilKeyFrame() RTC_EXCLUSIVE_LOCKS_REQUIRED(crit_);
|
||||
bool RemovePacketsUntilKeyFrame()
|
||||
RTC_EXCLUSIVE_LOCKS_REQUIRED(worker_thread_);
|
||||
std::vector<uint16_t> GetNackBatch(NackFilterOptions options)
|
||||
RTC_EXCLUSIVE_LOCKS_REQUIRED(crit_);
|
||||
RTC_EXCLUSIVE_LOCKS_REQUIRED(worker_thread_);
|
||||
|
||||
// Update the reordering distribution.
|
||||
void UpdateReorderingStatistics(uint16_t seq_num)
|
||||
RTC_EXCLUSIVE_LOCKS_REQUIRED(crit_);
|
||||
RTC_EXCLUSIVE_LOCKS_REQUIRED(worker_thread_);
|
||||
|
||||
// Returns how many packets we have to wait in order to receive the packet
|
||||
// with probability |probabilty| or higher.
|
||||
int WaitNumberOfPackets(float probability) const
|
||||
RTC_EXCLUSIVE_LOCKS_REQUIRED(crit_);
|
||||
RTC_EXCLUSIVE_LOCKS_REQUIRED(worker_thread_);
|
||||
|
||||
TaskQueueBase* const worker_thread_;
|
||||
|
||||
// Used to regularly call SendNack if needed.
|
||||
RepeatingTaskHandle repeating_task_ RTC_GUARDED_BY(worker_thread_);
|
||||
const TimeDelta update_interval_;
|
||||
|
||||
rtc::CriticalSection crit_;
|
||||
Clock* const clock_;
|
||||
NackSender* const nack_sender_;
|
||||
KeyFrameRequestSender* const keyframe_request_sender_;
|
||||
@ -105,23 +116,23 @@ class NackModule2 final : public Module {
|
||||
// known thread (e.g. see |initialized_|). Those probably do not need
|
||||
// synchronized access.
|
||||
std::map<uint16_t, NackInfo, DescendingSeqNumComp<uint16_t>> nack_list_
|
||||
RTC_GUARDED_BY(crit_);
|
||||
RTC_GUARDED_BY(worker_thread_);
|
||||
std::set<uint16_t, DescendingSeqNumComp<uint16_t>> keyframe_list_
|
||||
RTC_GUARDED_BY(crit_);
|
||||
RTC_GUARDED_BY(worker_thread_);
|
||||
std::set<uint16_t, DescendingSeqNumComp<uint16_t>> recovered_list_
|
||||
RTC_GUARDED_BY(crit_);
|
||||
video_coding::Histogram reordering_histogram_ RTC_GUARDED_BY(crit_);
|
||||
bool initialized_ RTC_GUARDED_BY(crit_);
|
||||
int64_t rtt_ms_ RTC_GUARDED_BY(crit_);
|
||||
uint16_t newest_seq_num_ RTC_GUARDED_BY(crit_);
|
||||
|
||||
// Only touched on the process thread.
|
||||
int64_t next_process_time_ms_;
|
||||
RTC_GUARDED_BY(worker_thread_);
|
||||
video_coding::Histogram reordering_histogram_ RTC_GUARDED_BY(worker_thread_);
|
||||
bool initialized_ RTC_GUARDED_BY(worker_thread_);
|
||||
int64_t rtt_ms_ RTC_GUARDED_BY(worker_thread_);
|
||||
uint16_t newest_seq_num_ RTC_GUARDED_BY(worker_thread_);
|
||||
|
||||
// Adds a delay before send nack on packet received.
|
||||
const int64_t send_nack_delay_ms_;
|
||||
|
||||
const absl::optional<BackoffSettings> backoff_settings_;
|
||||
|
||||
// Used to signal destruction to potentially pending tasks.
|
||||
ScopedTaskSafety task_safety_;
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
@ -18,8 +18,12 @@
|
||||
#include "system_wrappers/include/clock.h"
|
||||
#include "test/field_trial.h"
|
||||
#include "test/gtest.h"
|
||||
#include "test/run_loop.h"
|
||||
|
||||
namespace webrtc {
|
||||
// TODO(bugs.webrtc.org/11594): Use the use the GlobalSimulatedTimeController
|
||||
// instead of RunLoop. At the moment we mix use of the Clock and the underlying
|
||||
// implementation of RunLoop, which is realtime.
|
||||
class TestNackModule2 : public ::testing::TestWithParam<bool>,
|
||||
public NackSender,
|
||||
public KeyFrameRequestSender {
|
||||
@ -29,68 +33,116 @@ class TestNackModule2 : public ::testing::TestWithParam<bool>,
|
||||
field_trial_(GetParam()
|
||||
? "WebRTC-ExponentialNackBackoff/enabled:true/"
|
||||
: "WebRTC-ExponentialNackBackoff/enabled:false/"),
|
||||
nack_module_(clock_.get(), this, this),
|
||||
keyframes_requested_(0) {}
|
||||
|
||||
void SetUp() override { nack_module_.UpdateRtt(kDefaultRttMs); }
|
||||
void SetUp() override {}
|
||||
|
||||
void SendNack(const std::vector<uint16_t>& sequence_numbers,
|
||||
bool buffering_allowed) override {
|
||||
sent_nacks_.insert(sent_nacks_.end(), sequence_numbers.begin(),
|
||||
sequence_numbers.end());
|
||||
if (waiting_for_send_nack_) {
|
||||
waiting_for_send_nack_ = false;
|
||||
loop_.Quit();
|
||||
}
|
||||
}
|
||||
|
||||
void RequestKeyFrame() override { ++keyframes_requested_; }
|
||||
|
||||
void Flush() {
|
||||
// nack_module.Process();
|
||||
loop_.Flush();
|
||||
}
|
||||
|
||||
bool WaitForSendNack() {
|
||||
if (timed_out_) {
|
||||
RTC_NOTREACHED();
|
||||
return false;
|
||||
}
|
||||
|
||||
RTC_DCHECK(!waiting_for_send_nack_);
|
||||
|
||||
waiting_for_send_nack_ = true;
|
||||
loop_.PostDelayedTask(
|
||||
[this]() {
|
||||
timed_out_ = true;
|
||||
loop_.Quit();
|
||||
},
|
||||
1000);
|
||||
|
||||
loop_.Run();
|
||||
|
||||
if (timed_out_)
|
||||
return false;
|
||||
|
||||
RTC_DCHECK(!waiting_for_send_nack_);
|
||||
return true;
|
||||
}
|
||||
|
||||
NackModule2& CreateNackModule(
|
||||
TimeDelta interval = NackModule2::kUpdateInterval) {
|
||||
RTC_DCHECK(!nack_module_.get());
|
||||
nack_module_ = std::make_unique<NackModule2>(
|
||||
TaskQueueBase::Current(), clock_.get(), this, this, interval);
|
||||
nack_module_->UpdateRtt(kDefaultRttMs);
|
||||
return *nack_module_.get();
|
||||
}
|
||||
|
||||
static constexpr int64_t kDefaultRttMs = 20;
|
||||
test::RunLoop loop_;
|
||||
std::unique_ptr<SimulatedClock> clock_;
|
||||
test::ScopedFieldTrials field_trial_;
|
||||
NackModule2 nack_module_;
|
||||
std::unique_ptr<NackModule2> nack_module_;
|
||||
std::vector<uint16_t> sent_nacks_;
|
||||
int keyframes_requested_;
|
||||
bool waiting_for_send_nack_ = false;
|
||||
bool timed_out_ = false;
|
||||
};
|
||||
|
||||
TEST_P(TestNackModule2, NackOnePacket) {
|
||||
nack_module_.OnReceivedPacket(1, false, false);
|
||||
nack_module_.OnReceivedPacket(3, false, false);
|
||||
EXPECT_EQ(1u, sent_nacks_.size());
|
||||
NackModule2& nack_module = CreateNackModule();
|
||||
nack_module.OnReceivedPacket(1, false, false);
|
||||
nack_module.OnReceivedPacket(3, false, false);
|
||||
ASSERT_EQ(1u, sent_nacks_.size());
|
||||
EXPECT_EQ(2, sent_nacks_[0]);
|
||||
}
|
||||
|
||||
TEST_P(TestNackModule2, WrappingSeqNum) {
|
||||
nack_module_.OnReceivedPacket(0xfffe, false, false);
|
||||
nack_module_.OnReceivedPacket(1, false, false);
|
||||
EXPECT_EQ(2u, sent_nacks_.size());
|
||||
NackModule2& nack_module = CreateNackModule();
|
||||
nack_module.OnReceivedPacket(0xfffe, false, false);
|
||||
nack_module.OnReceivedPacket(1, false, false);
|
||||
ASSERT_EQ(2u, sent_nacks_.size());
|
||||
EXPECT_EQ(0xffff, sent_nacks_[0]);
|
||||
EXPECT_EQ(0, sent_nacks_[1]);
|
||||
}
|
||||
|
||||
TEST_P(TestNackModule2, WrappingSeqNumClearToKeyframe) {
|
||||
nack_module_.OnReceivedPacket(0xfffe, false, false);
|
||||
nack_module_.OnReceivedPacket(1, false, false);
|
||||
EXPECT_EQ(2u, sent_nacks_.size());
|
||||
NackModule2& nack_module = CreateNackModule(TimeDelta::Millis(10));
|
||||
nack_module.OnReceivedPacket(0xfffe, false, false);
|
||||
nack_module.OnReceivedPacket(1, false, false);
|
||||
ASSERT_EQ(2u, sent_nacks_.size());
|
||||
EXPECT_EQ(0xffff, sent_nacks_[0]);
|
||||
EXPECT_EQ(0, sent_nacks_[1]);
|
||||
|
||||
sent_nacks_.clear();
|
||||
nack_module_.OnReceivedPacket(2, true, false);
|
||||
EXPECT_EQ(0u, sent_nacks_.size());
|
||||
nack_module.OnReceivedPacket(2, true, false);
|
||||
ASSERT_EQ(0u, sent_nacks_.size());
|
||||
|
||||
nack_module_.OnReceivedPacket(501, true, false);
|
||||
EXPECT_EQ(498u, sent_nacks_.size());
|
||||
nack_module.OnReceivedPacket(501, true, false);
|
||||
ASSERT_EQ(498u, sent_nacks_.size());
|
||||
for (int seq_num = 3; seq_num < 501; ++seq_num)
|
||||
EXPECT_EQ(seq_num, sent_nacks_[seq_num - 3]);
|
||||
|
||||
sent_nacks_.clear();
|
||||
nack_module_.OnReceivedPacket(1001, false, false);
|
||||
nack_module.OnReceivedPacket(1001, false, false);
|
||||
EXPECT_EQ(499u, sent_nacks_.size());
|
||||
for (int seq_num = 502; seq_num < 1001; ++seq_num)
|
||||
EXPECT_EQ(seq_num, sent_nacks_[seq_num - 502]);
|
||||
|
||||
sent_nacks_.clear();
|
||||
clock_->AdvanceTimeMilliseconds(100);
|
||||
nack_module_.Process();
|
||||
EXPECT_EQ(999u, sent_nacks_.size());
|
||||
ASSERT_TRUE(WaitForSendNack());
|
||||
ASSERT_EQ(999u, sent_nacks_.size());
|
||||
EXPECT_EQ(0xffff, sent_nacks_[0]);
|
||||
EXPECT_EQ(0, sent_nacks_[1]);
|
||||
for (int seq_num = 3; seq_num < 501; ++seq_num)
|
||||
@ -102,15 +154,15 @@ TEST_P(TestNackModule2, WrappingSeqNumClearToKeyframe) {
|
||||
// It will then clear all nacks up to the next keyframe (seq num 2),
|
||||
// thus removing 0xffff and 0 from the nack list.
|
||||
sent_nacks_.clear();
|
||||
nack_module_.OnReceivedPacket(1004, false, false);
|
||||
EXPECT_EQ(2u, sent_nacks_.size());
|
||||
nack_module.OnReceivedPacket(1004, false, false);
|
||||
ASSERT_EQ(2u, sent_nacks_.size());
|
||||
EXPECT_EQ(1002, sent_nacks_[0]);
|
||||
EXPECT_EQ(1003, sent_nacks_[1]);
|
||||
|
||||
sent_nacks_.clear();
|
||||
clock_->AdvanceTimeMilliseconds(100);
|
||||
nack_module_.Process();
|
||||
EXPECT_EQ(999u, sent_nacks_.size());
|
||||
ASSERT_TRUE(WaitForSendNack());
|
||||
ASSERT_EQ(999u, sent_nacks_.size());
|
||||
for (int seq_num = 3; seq_num < 501; ++seq_num)
|
||||
EXPECT_EQ(seq_num, sent_nacks_[seq_num - 3]);
|
||||
for (int seq_num = 502; seq_num < 1001; ++seq_num)
|
||||
@ -118,65 +170,39 @@ TEST_P(TestNackModule2, WrappingSeqNumClearToKeyframe) {
|
||||
|
||||
// Adding packet 1007 will cause the nack module to overflow again, thus
|
||||
// clearing everything up to 501 which is the next keyframe.
|
||||
nack_module_.OnReceivedPacket(1007, false, false);
|
||||
nack_module.OnReceivedPacket(1007, false, false);
|
||||
sent_nacks_.clear();
|
||||
clock_->AdvanceTimeMilliseconds(100);
|
||||
nack_module_.Process();
|
||||
EXPECT_EQ(503u, sent_nacks_.size());
|
||||
ASSERT_TRUE(WaitForSendNack());
|
||||
ASSERT_EQ(503u, sent_nacks_.size());
|
||||
for (int seq_num = 502; seq_num < 1001; ++seq_num)
|
||||
EXPECT_EQ(seq_num, sent_nacks_[seq_num - 502]);
|
||||
EXPECT_EQ(1005, sent_nacks_[501]);
|
||||
EXPECT_EQ(1006, sent_nacks_[502]);
|
||||
}
|
||||
|
||||
TEST_P(TestNackModule2, DontBurstOnTimeSkip) {
|
||||
nack_module_.Process();
|
||||
clock_->AdvanceTimeMilliseconds(20);
|
||||
EXPECT_EQ(0, nack_module_.TimeUntilNextProcess());
|
||||
nack_module_.Process();
|
||||
|
||||
clock_->AdvanceTimeMilliseconds(100);
|
||||
EXPECT_EQ(0, nack_module_.TimeUntilNextProcess());
|
||||
nack_module_.Process();
|
||||
EXPECT_EQ(20, nack_module_.TimeUntilNextProcess());
|
||||
|
||||
clock_->AdvanceTimeMilliseconds(19);
|
||||
EXPECT_EQ(1, nack_module_.TimeUntilNextProcess());
|
||||
clock_->AdvanceTimeMilliseconds(2);
|
||||
nack_module_.Process();
|
||||
EXPECT_EQ(19, nack_module_.TimeUntilNextProcess());
|
||||
|
||||
clock_->AdvanceTimeMilliseconds(19);
|
||||
EXPECT_EQ(0, nack_module_.TimeUntilNextProcess());
|
||||
nack_module_.Process();
|
||||
|
||||
clock_->AdvanceTimeMilliseconds(21);
|
||||
EXPECT_EQ(0, nack_module_.TimeUntilNextProcess());
|
||||
nack_module_.Process();
|
||||
EXPECT_EQ(19, nack_module_.TimeUntilNextProcess());
|
||||
}
|
||||
|
||||
TEST_P(TestNackModule2, ResendNack) {
|
||||
nack_module_.OnReceivedPacket(1, false, false);
|
||||
nack_module_.OnReceivedPacket(3, false, false);
|
||||
NackModule2& nack_module = CreateNackModule(TimeDelta::Millis(1));
|
||||
nack_module.OnReceivedPacket(1, false, false);
|
||||
nack_module.OnReceivedPacket(3, false, false);
|
||||
size_t expected_nacks_sent = 1;
|
||||
EXPECT_EQ(expected_nacks_sent, sent_nacks_.size());
|
||||
ASSERT_EQ(expected_nacks_sent, sent_nacks_.size());
|
||||
EXPECT_EQ(2, sent_nacks_[0]);
|
||||
|
||||
if (GetParam()) {
|
||||
// Retry has to wait at least 5ms by default.
|
||||
nack_module_.UpdateRtt(1);
|
||||
nack_module.UpdateRtt(1);
|
||||
clock_->AdvanceTimeMilliseconds(4);
|
||||
nack_module_.Process(); // Too early.
|
||||
Flush(); // Too early.
|
||||
EXPECT_EQ(expected_nacks_sent, sent_nacks_.size());
|
||||
|
||||
clock_->AdvanceTimeMilliseconds(1);
|
||||
nack_module_.Process(); // Now allowed.
|
||||
WaitForSendNack(); // Now allowed.
|
||||
EXPECT_EQ(++expected_nacks_sent, sent_nacks_.size());
|
||||
} else {
|
||||
nack_module_.UpdateRtt(1);
|
||||
nack_module.UpdateRtt(1);
|
||||
clock_->AdvanceTimeMilliseconds(1);
|
||||
nack_module_.Process(); // Fast retransmit allowed.
|
||||
WaitForSendNack(); // Fast retransmit allowed.
|
||||
EXPECT_EQ(++expected_nacks_sent, sent_nacks_.size());
|
||||
}
|
||||
|
||||
@ -185,7 +211,7 @@ TEST_P(TestNackModule2, ResendNack) {
|
||||
for (int i = 2; i < 10; ++i) {
|
||||
// Change RTT, above the 40ms max for exponential backoff.
|
||||
TimeDelta rtt = TimeDelta::Millis(160); // + (i * 10 - 40)
|
||||
nack_module_.UpdateRtt(rtt.ms());
|
||||
nack_module.UpdateRtt(rtt.ms());
|
||||
|
||||
// RTT gets capped at 160ms in backoff calculations.
|
||||
TimeDelta expected_backoff_delay =
|
||||
@ -193,26 +219,27 @@ TEST_P(TestNackModule2, ResendNack) {
|
||||
|
||||
// Move to one millisecond before next allowed NACK.
|
||||
clock_->AdvanceTimeMilliseconds(expected_backoff_delay.ms() - 1);
|
||||
nack_module_.Process();
|
||||
Flush();
|
||||
EXPECT_EQ(expected_nacks_sent, sent_nacks_.size());
|
||||
|
||||
// Move to one millisecond after next allowed NACK.
|
||||
// After rather than on to avoid rounding errors.
|
||||
clock_->AdvanceTimeMilliseconds(2);
|
||||
nack_module_.Process(); // Now allowed.
|
||||
WaitForSendNack(); // Now allowed.
|
||||
EXPECT_EQ(++expected_nacks_sent, sent_nacks_.size());
|
||||
}
|
||||
|
||||
// Giving up after 10 tries.
|
||||
clock_->AdvanceTimeMilliseconds(3000);
|
||||
nack_module_.Process();
|
||||
Flush();
|
||||
EXPECT_EQ(expected_nacks_sent, sent_nacks_.size());
|
||||
}
|
||||
|
||||
TEST_P(TestNackModule2, ResendPacketMaxRetries) {
|
||||
nack_module_.OnReceivedPacket(1, false, false);
|
||||
nack_module_.OnReceivedPacket(3, false, false);
|
||||
EXPECT_EQ(1u, sent_nacks_.size());
|
||||
NackModule2& nack_module = CreateNackModule(TimeDelta::Millis(1));
|
||||
nack_module.OnReceivedPacket(1, false, false);
|
||||
nack_module.OnReceivedPacket(3, false, false);
|
||||
ASSERT_EQ(1u, sent_nacks_.size());
|
||||
EXPECT_EQ(2, sent_nacks_[0]);
|
||||
|
||||
int backoff_factor = 1;
|
||||
@ -220,111 +247,124 @@ TEST_P(TestNackModule2, ResendPacketMaxRetries) {
|
||||
// Exponential backoff, so that we don't reject NACK because of time.
|
||||
clock_->AdvanceTimeMilliseconds(backoff_factor * kDefaultRttMs);
|
||||
backoff_factor *= 2;
|
||||
nack_module_.Process();
|
||||
WaitForSendNack();
|
||||
EXPECT_EQ(retries + 1, sent_nacks_.size());
|
||||
}
|
||||
|
||||
clock_->AdvanceTimeMilliseconds(backoff_factor * kDefaultRttMs);
|
||||
nack_module_.Process();
|
||||
Flush();
|
||||
EXPECT_EQ(10u, sent_nacks_.size());
|
||||
}
|
||||
|
||||
TEST_P(TestNackModule2, TooLargeNackList) {
|
||||
nack_module_.OnReceivedPacket(0, false, false);
|
||||
nack_module_.OnReceivedPacket(1001, false, false);
|
||||
NackModule2& nack_module = CreateNackModule();
|
||||
nack_module.OnReceivedPacket(0, false, false);
|
||||
nack_module.OnReceivedPacket(1001, false, false);
|
||||
EXPECT_EQ(1000u, sent_nacks_.size());
|
||||
EXPECT_EQ(0, keyframes_requested_);
|
||||
nack_module_.OnReceivedPacket(1003, false, false);
|
||||
nack_module.OnReceivedPacket(1003, false, false);
|
||||
EXPECT_EQ(1000u, sent_nacks_.size());
|
||||
EXPECT_EQ(1, keyframes_requested_);
|
||||
nack_module_.OnReceivedPacket(1004, false, false);
|
||||
nack_module.OnReceivedPacket(1004, false, false);
|
||||
EXPECT_EQ(1000u, sent_nacks_.size());
|
||||
EXPECT_EQ(1, keyframes_requested_);
|
||||
}
|
||||
|
||||
TEST_P(TestNackModule2, TooLargeNackListWithKeyFrame) {
|
||||
nack_module_.OnReceivedPacket(0, false, false);
|
||||
nack_module_.OnReceivedPacket(1, true, false);
|
||||
nack_module_.OnReceivedPacket(1001, false, false);
|
||||
NackModule2& nack_module = CreateNackModule();
|
||||
nack_module.OnReceivedPacket(0, false, false);
|
||||
nack_module.OnReceivedPacket(1, true, false);
|
||||
nack_module.OnReceivedPacket(1001, false, false);
|
||||
EXPECT_EQ(999u, sent_nacks_.size());
|
||||
EXPECT_EQ(0, keyframes_requested_);
|
||||
nack_module_.OnReceivedPacket(1003, false, false);
|
||||
nack_module.OnReceivedPacket(1003, false, false);
|
||||
EXPECT_EQ(1000u, sent_nacks_.size());
|
||||
EXPECT_EQ(0, keyframes_requested_);
|
||||
nack_module_.OnReceivedPacket(1005, false, false);
|
||||
nack_module.OnReceivedPacket(1005, false, false);
|
||||
EXPECT_EQ(1000u, sent_nacks_.size());
|
||||
EXPECT_EQ(1, keyframes_requested_);
|
||||
}
|
||||
|
||||
TEST_P(TestNackModule2, ClearUpTo) {
|
||||
nack_module_.OnReceivedPacket(0, false, false);
|
||||
nack_module_.OnReceivedPacket(100, false, false);
|
||||
NackModule2& nack_module = CreateNackModule(TimeDelta::Millis(1));
|
||||
nack_module.OnReceivedPacket(0, false, false);
|
||||
nack_module.OnReceivedPacket(100, false, false);
|
||||
EXPECT_EQ(99u, sent_nacks_.size());
|
||||
|
||||
sent_nacks_.clear();
|
||||
clock_->AdvanceTimeMilliseconds(100);
|
||||
nack_module_.ClearUpTo(50);
|
||||
nack_module_.Process();
|
||||
EXPECT_EQ(50u, sent_nacks_.size());
|
||||
nack_module.ClearUpTo(50);
|
||||
WaitForSendNack();
|
||||
ASSERT_EQ(50u, sent_nacks_.size());
|
||||
EXPECT_EQ(50, sent_nacks_[0]);
|
||||
}
|
||||
|
||||
TEST_P(TestNackModule2, ClearUpToWrap) {
|
||||
nack_module_.OnReceivedPacket(0xfff0, false, false);
|
||||
nack_module_.OnReceivedPacket(0xf, false, false);
|
||||
NackModule2& nack_module = CreateNackModule();
|
||||
nack_module.OnReceivedPacket(0xfff0, false, false);
|
||||
nack_module.OnReceivedPacket(0xf, false, false);
|
||||
EXPECT_EQ(30u, sent_nacks_.size());
|
||||
|
||||
sent_nacks_.clear();
|
||||
clock_->AdvanceTimeMilliseconds(100);
|
||||
nack_module_.ClearUpTo(0);
|
||||
nack_module_.Process();
|
||||
EXPECT_EQ(15u, sent_nacks_.size());
|
||||
nack_module.ClearUpTo(0);
|
||||
WaitForSendNack();
|
||||
ASSERT_EQ(15u, sent_nacks_.size());
|
||||
EXPECT_EQ(0, sent_nacks_[0]);
|
||||
}
|
||||
|
||||
TEST_P(TestNackModule2, PacketNackCount) {
|
||||
EXPECT_EQ(0, nack_module_.OnReceivedPacket(0, false, false));
|
||||
EXPECT_EQ(0, nack_module_.OnReceivedPacket(2, false, false));
|
||||
EXPECT_EQ(1, nack_module_.OnReceivedPacket(1, false, false));
|
||||
NackModule2& nack_module = CreateNackModule(TimeDelta::Millis(1));
|
||||
EXPECT_EQ(0, nack_module.OnReceivedPacket(0, false, false));
|
||||
EXPECT_EQ(0, nack_module.OnReceivedPacket(2, false, false));
|
||||
EXPECT_EQ(1, nack_module.OnReceivedPacket(1, false, false));
|
||||
|
||||
sent_nacks_.clear();
|
||||
nack_module_.UpdateRtt(100);
|
||||
EXPECT_EQ(0, nack_module_.OnReceivedPacket(5, false, false));
|
||||
nack_module.UpdateRtt(100);
|
||||
EXPECT_EQ(0, nack_module.OnReceivedPacket(5, false, false));
|
||||
clock_->AdvanceTimeMilliseconds(100);
|
||||
nack_module_.Process();
|
||||
WaitForSendNack();
|
||||
EXPECT_EQ(4u, sent_nacks_.size());
|
||||
|
||||
clock_->AdvanceTimeMilliseconds(125);
|
||||
nack_module_.Process();
|
||||
EXPECT_EQ(3, nack_module_.OnReceivedPacket(3, false, false));
|
||||
EXPECT_EQ(3, nack_module_.OnReceivedPacket(4, false, false));
|
||||
EXPECT_EQ(0, nack_module_.OnReceivedPacket(4, false, false));
|
||||
WaitForSendNack();
|
||||
|
||||
EXPECT_EQ(6u, sent_nacks_.size());
|
||||
|
||||
EXPECT_EQ(3, nack_module.OnReceivedPacket(3, false, false));
|
||||
EXPECT_EQ(3, nack_module.OnReceivedPacket(4, false, false));
|
||||
EXPECT_EQ(0, nack_module.OnReceivedPacket(4, false, false));
|
||||
}
|
||||
|
||||
TEST_P(TestNackModule2, NackListFullAndNoOverlapWithKeyframes) {
|
||||
NackModule2& nack_module = CreateNackModule();
|
||||
const int kMaxNackPackets = 1000;
|
||||
const unsigned int kFirstGap = kMaxNackPackets - 20;
|
||||
const unsigned int kSecondGap = 200;
|
||||
uint16_t seq_num = 0;
|
||||
nack_module_.OnReceivedPacket(seq_num++, true, false);
|
||||
nack_module.OnReceivedPacket(seq_num++, true, false);
|
||||
seq_num += kFirstGap;
|
||||
nack_module_.OnReceivedPacket(seq_num++, true, false);
|
||||
nack_module.OnReceivedPacket(seq_num++, true, false);
|
||||
EXPECT_EQ(kFirstGap, sent_nacks_.size());
|
||||
sent_nacks_.clear();
|
||||
seq_num += kSecondGap;
|
||||
nack_module_.OnReceivedPacket(seq_num, true, false);
|
||||
nack_module.OnReceivedPacket(seq_num, true, false);
|
||||
EXPECT_EQ(kSecondGap, sent_nacks_.size());
|
||||
}
|
||||
|
||||
TEST_P(TestNackModule2, HandleFecRecoveredPacket) {
|
||||
nack_module_.OnReceivedPacket(1, false, false);
|
||||
nack_module_.OnReceivedPacket(4, false, true);
|
||||
NackModule2& nack_module = CreateNackModule();
|
||||
nack_module.OnReceivedPacket(1, false, false);
|
||||
nack_module.OnReceivedPacket(4, false, true);
|
||||
EXPECT_EQ(0u, sent_nacks_.size());
|
||||
nack_module_.OnReceivedPacket(5, false, false);
|
||||
nack_module.OnReceivedPacket(5, false, false);
|
||||
EXPECT_EQ(2u, sent_nacks_.size());
|
||||
}
|
||||
|
||||
TEST_P(TestNackModule2, SendNackWithoutDelay) {
|
||||
nack_module_.OnReceivedPacket(0, false, false);
|
||||
nack_module_.OnReceivedPacket(100, false, false);
|
||||
NackModule2& nack_module = CreateNackModule();
|
||||
nack_module.OnReceivedPacket(0, false, false);
|
||||
nack_module.OnReceivedPacket(100, false, false);
|
||||
EXPECT_EQ(99u, sent_nacks_.size());
|
||||
}
|
||||
|
||||
@ -339,7 +379,7 @@ class TestNackModule2WithFieldTrial : public ::testing::Test,
|
||||
TestNackModule2WithFieldTrial()
|
||||
: nack_delay_field_trial_("WebRTC-SendNackDelayMs/10/"),
|
||||
clock_(new SimulatedClock(0)),
|
||||
nack_module_(clock_.get(), this, this),
|
||||
nack_module_(TaskQueueBase::Current(), clock_.get(), this, this),
|
||||
keyframes_requested_(0) {}
|
||||
|
||||
void SendNack(const std::vector<uint16_t>& sequence_numbers,
|
||||
|
||||
Reference in New Issue
Block a user