Allow FakeNetworkPipe to drop packets in bursts.

The fake network pipe will still only drop packets at an average rate of
|loss_percent| but in bursts at an average length specified by
|avg_burst_loss_length|.

Also added the flag -avg_burst_loss_length to video loopback.

BUG=

Review-Url: https://codereview.webrtc.org/1995683003
Cr-Commit-Position: refs/heads/master@{#12969}
diff --git a/webrtc/test/fake_network_pipe.cc b/webrtc/test/fake_network_pipe.cc
index ea4e551..360b32c 100644
--- a/webrtc/test/fake_network_pipe.cc
+++ b/webrtc/test/fake_network_pipe.cc
@@ -13,7 +13,9 @@
 #include <assert.h>
 #include <math.h>
 #include <string.h>
+
 #include <algorithm>
+#include <cmath>
 
 #include "webrtc/call.h"
 #include "webrtc/system_wrappers/include/clock.h"
@@ -34,7 +36,27 @@
       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 @@
       NetworkPacket* packet = capacity_link_.front();
       capacity_link_.pop();
 
-      // Packets are randomly dropped after being affected by the bottleneck.
-      if (random_.Rand(100) < static_cast<uint32_t>(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<double>() < prob_loss_bursting_) ||
+          (!bursting_ && random_.Rand<double>() < 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 608ff00..2dcbcf5 100644
--- a/webrtc/test/fake_network_pipe.h
+++ b/webrtc/test/fake_network_pipe.h
@@ -80,6 +80,8 @@
     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 @@
   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 0bd46df..fcc0c93 100644
--- a/webrtc/test/fake_network_pipe_unittest.cc
+++ b/webrtc/test/fake_network_pipe_unittest.cc
@@ -379,4 +379,45 @@
   }
   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<FakeNetworkPipe> 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<double>(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<double>(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 0974d58..70a940d 100644
--- a/webrtc/video/video_loopback.cc
+++ b/webrtc/video/video_loopback.cc
@@ -101,6 +101,11 @@
   return static_cast<int>(FLAGS_loss_percent);
 }
 
+DEFINE_int32(avg_burst_loss_length, 1, "Average burst length of lost packets.");
+int AvgBurstLossLength() {
+  return static_cast<int>(FLAGS_avg_burst_loss_length);
+}
+
 DEFINE_int32(link_capacity,
              0,
              "Capacity (kbps) of the fake link. 0 means infinite.");
@@ -201,6 +206,7 @@
 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();