diff --git a/webrtc/test/fake_network_pipe.cc b/webrtc/test/fake_network_pipe.cc index ea4e551f7b..360b32ccf8 100644 --- a/webrtc/test/fake_network_pipe.cc +++ b/webrtc/test/fake_network_pipe.cc @@ -13,7 +13,9 @@ #include #include #include + #include +#include #include "webrtc/call.h" #include "webrtc/system_wrappers/include/clock.h" @@ -34,7 +36,27 @@ FakeNetworkPipe::FakeNetworkPipe(Clock* clock, dropped_packets_(0), sent_packets_(0), total_packet_delay_(0), - next_process_time_(clock_->TimeInMilliseconds()) {} + bursting_(false), + next_process_time_(clock_->TimeInMilliseconds()) { + double prob_loss = config.loss_percent / 100.0; + if (config_.avg_burst_loss_length == -1) { + // Uniform loss + prob_loss_bursting_ = prob_loss; + prob_start_bursting_ = prob_loss; + } else { + // Lose packets according to a gilbert-elliot model. + int avg_burst_loss_length = config.avg_burst_loss_length; + int min_avg_burst_loss_length = std::ceil(prob_loss / (1 - prob_loss)); + + RTC_CHECK_GT(avg_burst_loss_length, min_avg_burst_loss_length) + << "For a total packet loss of " << config.loss_percent << "%% then" + << " avg_burst_loss_length must be " << min_avg_burst_loss_length + 1 + << " or higher."; + + prob_loss_bursting_ = (1.0 - 1.0 / avg_burst_loss_length); + prob_start_bursting_ = prob_loss / (1 - prob_loss) / avg_burst_loss_length; + } +} FakeNetworkPipe::~FakeNetworkPipe() { while (!capacity_link_.empty()) { @@ -118,10 +140,15 @@ void FakeNetworkPipe::Process() { NetworkPacket* packet = capacity_link_.front(); capacity_link_.pop(); - // Packets are randomly dropped after being affected by the bottleneck. - if (random_.Rand(100) < static_cast(config_.loss_percent)) { + // Drop packets at an average rate of |config_.loss_percent| with + // and average loss burst length of |config_.avg_burst_loss_length|. + if ((bursting_ && random_.Rand() < prob_loss_bursting_) || + (!bursting_ && random_.Rand() < prob_start_bursting_)) { + bursting_ = true; delete packet; continue; + } else { + bursting_ = false; } int arrival_time_jitter = random_.Gaussian( diff --git a/webrtc/test/fake_network_pipe.h b/webrtc/test/fake_network_pipe.h index 608ff008c9..2dcbcf59d1 100644 --- a/webrtc/test/fake_network_pipe.h +++ b/webrtc/test/fake_network_pipe.h @@ -80,6 +80,8 @@ class FakeNetworkPipe { int loss_percent = 0; // If packets are allowed to be reordered. bool allow_reordering = false; + // The average length of a burst of lost packets. + int avg_burst_loss_length = -1; }; FakeNetworkPipe(Clock* clock, const FakeNetworkPipe::Config& config); @@ -133,6 +135,16 @@ class FakeNetworkPipe { size_t sent_packets_; int64_t total_packet_delay_; + // Are we currently dropping a burst of packets? + bool bursting_; + + // The probability to drop the packet if we are currently dropping a + // burst of packet + double prob_loss_bursting_; + + // The probability to drop a burst of packets. + double prob_start_bursting_; + int64_t next_process_time_; RTC_DISALLOW_COPY_AND_ASSIGN(FakeNetworkPipe); diff --git a/webrtc/test/fake_network_pipe_unittest.cc b/webrtc/test/fake_network_pipe_unittest.cc index 0bd46df7fc..fcc0c935d0 100644 --- a/webrtc/test/fake_network_pipe_unittest.cc +++ b/webrtc/test/fake_network_pipe_unittest.cc @@ -379,4 +379,45 @@ TEST_F(FakeNetworkPipeTest, DisallowReorderingThenAllowReordering) { } EXPECT_TRUE(reordering_has_occured); } + +TEST_F(FakeNetworkPipeTest, BurstLoss) { + const int kLossPercent = 5; + const int kAvgBurstLength = 3; + const int kNumPackets = 10000; + const int kPacketSize = 10; + + FakeNetworkPipe::Config config; + config.queue_length_packets = kNumPackets; + config.loss_percent = kLossPercent; + config.avg_burst_loss_length = kAvgBurstLength; + std::unique_ptr pipe( + new FakeNetworkPipe(&fake_clock_, config)); + ReorderTestReceiver* receiver = new ReorderTestReceiver(); + receiver_.reset(receiver); + pipe->SetReceiver(receiver_.get()); + + SendPackets(pipe.get(), kNumPackets, kPacketSize); + fake_clock_.AdvanceTimeMilliseconds(1000); + pipe->Process(); + + // Check that the average loss is |kLossPercent| percent. + int lost_packets = kNumPackets - receiver->delivered_sequence_numbers_.size(); + double loss_fraction = lost_packets / static_cast(kNumPackets); + + EXPECT_NEAR(kLossPercent / 100.0, loss_fraction, 0.05); + + // Find the number of bursts that has occurred. + size_t received_packets = receiver->delivered_sequence_numbers_.size(); + int num_bursts = 0; + for (size_t i = 0; i < received_packets - 1; ++i) { + int diff = receiver->delivered_sequence_numbers_[i + 1] - + receiver->delivered_sequence_numbers_[i]; + if (diff > 1) + ++num_bursts; + } + + double average_burst_length = static_cast(lost_packets) / num_bursts; + + EXPECT_NEAR(kAvgBurstLength, average_burst_length, 0.3); +} } // namespace webrtc diff --git a/webrtc/video/video_loopback.cc b/webrtc/video/video_loopback.cc index 0974d5869f..70a940db5f 100644 --- a/webrtc/video/video_loopback.cc +++ b/webrtc/video/video_loopback.cc @@ -101,6 +101,11 @@ int LossPercent() { return static_cast(FLAGS_loss_percent); } +DEFINE_int32(avg_burst_loss_length, 1, "Average burst length of lost packets."); +int AvgBurstLossLength() { + return static_cast(FLAGS_avg_burst_loss_length); +} + DEFINE_int32(link_capacity, 0, "Capacity (kbps) of the fake link. 0 means infinite."); @@ -201,6 +206,7 @@ std::string Clip() { void Loopback() { FakeNetworkPipe::Config pipe_config; pipe_config.loss_percent = flags::LossPercent(); + pipe_config.avg_burst_loss_length = flags::AvgBurstLossLength(); pipe_config.link_capacity_kbps = flags::LinkCapacityKbps(); pipe_config.queue_length_packets = flags::QueueSize(); pipe_config.queue_delay_ms = flags::AvgPropagationDelayMs();