Reland of Fixes a bug where a video stream can get stuck in the suspended state. (patchset #1 id:1 of https://codereview.chromium.org/2703393002/ )

Reason for revert:
Downstream fixed

Original issue's description:
> Revert of Fixes a bug where a video stream can get stuck in the suspended state. (patchset #8 id:120001 of https://codereview.webrtc.org/2705603002/ )
>
> Reason for revert:
> Breaks downstream
>
> Original issue's description:
> > Fixes a bug where a video stream can get stuck in the suspended state.
> >
> > This happens if a lot of FEC is allocated when the stream becomes suspended. The required bitrate to unsuspend can then be too high so that the padding bitrate we are allowed to generate is not enough.
> >
> > This CL also switches the tests from using ISAC to OPUS as RampUpTest.UpDownUpAudioVideoTransportSequenceNumberRtx relies on audio BWE to work (which is only compatible with OPUS). I don't know why it didn't fail before.
> >
> > BUG=webrtc:7178
> >
> > Review-Url: https://codereview.webrtc.org/2705603002
> > Cr-Commit-Position: refs/heads/master@{#16739}
> > Committed: https://chromium.googlesource.com/external/webrtc/+/a518a39963d34616d8f0e94991c7f5fbb5affb38
>
> TBR=mflodman@webrtc.org,terelius@webrtc.org,stefan@webrtc.org
> # Skipping CQ checks because original CL landed less than 1 days ago.
> NOPRESUBMIT=true
> NOTREECHECKS=true
> NOTRY=true
> BUG=webrtc:7178
>
> Review-Url: https://codereview.webrtc.org/2703393002
> Cr-Commit-Position: refs/heads/master@{#16751}
> Committed: https://chromium.googlesource.com/external/webrtc/+/b80bdcafed6c529be140da7d9f3e95a00b94219e

TBR=mflodman@webrtc.org,terelius@webrtc.org,stefan@webrtc.org
# Skipping CQ checks because original CL landed less than 1 days ago.
NOPRESUBMIT=true
NOTREECHECKS=true
NOTRY=true
BUG=webrtc:7178

Review-Url: https://codereview.webrtc.org/2704323003
Cr-Commit-Position: refs/heads/master@{#16753}
diff --git a/webrtc/call/bitrate_allocator.cc b/webrtc/call/bitrate_allocator.cc
index 5bd1a2c..c3568df 100644
--- a/webrtc/call/bitrate_allocator.cc
+++ b/webrtc/call/bitrate_allocator.cc
@@ -54,7 +54,9 @@
       last_rtt_(0),
       num_pause_events_(0),
       clock_(Clock::GetRealTimeClock()),
-      last_bwe_log_time_(0) {
+      last_bwe_log_time_(0),
+      total_requested_padding_bitrate_(0),
+      total_requested_min_bitrate_(0) {
   sequenced_checker_.Detach();
 }
 
@@ -115,6 +117,7 @@
       config.media_ratio = MediaRatio(allocated_bitrate, protection_bitrate);
     config.allocated_bitrate_bps = allocated_bitrate;
   }
+  UpdateAllocationLimits();
 }
 
 void BitrateAllocator::AddObserver(BitrateAllocatorObserver* observer,
@@ -167,12 +170,24 @@
   uint32_t total_requested_min_bitrate = 0;
 
   for (const auto& config : bitrate_observer_configs_) {
+    uint32_t stream_padding = config.pad_up_bitrate_bps;
     if (config.enforce_min_bitrate) {
       total_requested_min_bitrate += config.min_bitrate_bps;
+    } else if (config.allocated_bitrate_bps == 0) {
+      stream_padding =
+          std::max(MinBitrateWithHysteresis(config), stream_padding);
     }
-    total_requested_padding_bitrate += config.pad_up_bitrate_bps;
+    total_requested_padding_bitrate += stream_padding;
   }
 
+  if (total_requested_padding_bitrate == total_requested_padding_bitrate_ &&
+      total_requested_min_bitrate == total_requested_min_bitrate_) {
+    return;
+  }
+
+  total_requested_min_bitrate_ = total_requested_min_bitrate;
+  total_requested_padding_bitrate_ = total_requested_padding_bitrate;
+
   LOG(LS_INFO) << "UpdateAllocationLimits : total_requested_min_bitrate: "
                << total_requested_min_bitrate
                << "bps, total_requested_padding_bitrate: "
@@ -416,8 +431,9 @@
       static_cast<uint32_t>(bitrate_observer_configs_.size());
   for (const auto& observer_config : bitrate_observer_configs_) {
     if (observer_config.min_bitrate_bps + extra_bitrate_per_observer <
-        MinBitrateWithHysteresis(observer_config))
+        MinBitrateWithHysteresis(observer_config)) {
       return false;
+    }
   }
   return true;
 }
diff --git a/webrtc/call/bitrate_allocator.h b/webrtc/call/bitrate_allocator.h
index cd3b2e1..f133fa1 100644
--- a/webrtc/call/bitrate_allocator.h
+++ b/webrtc/call/bitrate_allocator.h
@@ -165,6 +165,8 @@
   int num_pause_events_ GUARDED_BY(&sequenced_checker_);
   Clock* const clock_ GUARDED_BY(&sequenced_checker_);
   int64_t last_bwe_log_time_ GUARDED_BY(&sequenced_checker_);
+  uint32_t total_requested_padding_bitrate_ GUARDED_BY(&sequenced_checker_);
+  uint32_t total_requested_min_bitrate_ GUARDED_BY(&sequenced_checker_);
 };
 }  // namespace webrtc
 #endif  // WEBRTC_CALL_BITRATE_ALLOCATOR_H_
diff --git a/webrtc/call/bitrate_allocator_unittest.cc b/webrtc/call/bitrate_allocator_unittest.cc
index 751ec93..ead4cbd 100644
--- a/webrtc/call/bitrate_allocator_unittest.cc
+++ b/webrtc/call/bitrate_allocator_unittest.cc
@@ -98,8 +98,6 @@
                           true);
   EXPECT_EQ(4000000, allocator_->GetStartBitrate(&bitrate_observer));
 
-  EXPECT_CALL(limit_observer_,
-              OnAllocationLimitsChanged(kMinSendBitrateBps, 0));
   allocator_->AddObserver(&bitrate_observer, kMinSendBitrateBps, 1500000, 0,
                           true);
   EXPECT_EQ(3000000, allocator_->GetStartBitrate(&bitrate_observer));
@@ -183,7 +181,7 @@
   TestBitrateObserver bitrate_observer_1;
   // Expect OnAllocationLimitsChanged with |min_send_bitrate_bps| = 0 since
   // AddObserver is called with |enforce_min_bitrate| = false.
-  EXPECT_CALL(limit_observer_, OnAllocationLimitsChanged(0, 0));
+  EXPECT_CALL(limit_observer_, OnAllocationLimitsChanged(0, 120000));
   allocator_->AddObserver(&bitrate_observer_1, 100000, 400000, 0, false);
   EXPECT_EQ(300000, allocator_->GetStartBitrate(&bitrate_observer_1));
 
@@ -264,7 +262,7 @@
   TestBitrateObserver bitrate_observer;
   // Expect OnAllocationLimitsChanged with |min_send_bitrate_bps| = 0 since
   // AddObserver is called with |enforce_min_bitrate| = false.
-  EXPECT_CALL(limit_observer_, OnAllocationLimitsChanged(0, 0));
+  EXPECT_CALL(limit_observer_, OnAllocationLimitsChanged(0, 168000));
   allocator_->AddObserver(
       &bitrate_observer, 100000, 400000, 0, false);
   EXPECT_EQ(300000, allocator_->GetStartBitrate(&bitrate_observer));
@@ -292,6 +290,7 @@
   EXPECT_EQ(0u, bitrate_observer.last_bitrate_bps_);
 
   // Just enough to enable video again.
+  EXPECT_CALL(limit_observer_, OnAllocationLimitsChanged(0, 0));
   allocator_->OnNetworkChanged(168000, 0, fraction_loss,
                                kDefaultProbingIntervalMs);
   EXPECT_EQ(168000u, bitrate_observer.last_bitrate_bps_);
@@ -304,7 +303,6 @@
   allocator_->OnNetworkChanged(139000, 0, 0, kDefaultProbingIntervalMs);
   EXPECT_EQ(139000u, bitrate_observer.last_bitrate_bps_);
 
-  EXPECT_CALL(limit_observer_, OnAllocationLimitsChanged(0, 0));
   allocator_->RemoveObserver(&bitrate_observer);
 }
 
diff --git a/webrtc/call/rampup_tests.cc b/webrtc/call/rampup_tests.cc
index 46e97cd..94153b0 100644
--- a/webrtc/call/rampup_tests.cc
+++ b/webrtc/call/rampup_tests.cc
@@ -11,6 +11,7 @@
 #include "webrtc/call/rampup_tests.h"
 
 #include "webrtc/base/checks.h"
+#include "webrtc/base/logging.h"
 #include "webrtc/base/platform_thread.h"
 #include "webrtc/test/encoder_settings.h"
 #include "webrtc/test/gtest.h"
@@ -20,7 +21,7 @@
 namespace {
 
 static const int64_t kPollIntervalMs = 20;
-static const int kExpectedHighVideoBitrateBps = 60000;
+static const int kExpectedHighVideoBitrateBps = 80000;
 static const int kExpectedHighAudioBitrateBps = 30000;
 static const int kLowBandwidthLimitBps = 20000;
 static const int kExpectedLowBitrateBps = 20000;
@@ -35,6 +36,7 @@
 
 RampUpTester::RampUpTester(size_t num_video_streams,
                            size_t num_audio_streams,
+                           size_t num_flexfec_streams,
                            unsigned int start_bitrate_bps,
                            int64_t min_run_time_ms,
                            const std::string& extension_type,
@@ -46,6 +48,7 @@
       clock_(Clock::GetRealTimeClock()),
       num_video_streams_(num_video_streams),
       num_audio_streams_(num_audio_streams),
+      num_flexfec_streams_(num_flexfec_streams),
       rtx_(rtx),
       red_(red),
       sender_call_(nullptr),
@@ -63,6 +66,8 @@
       poller_thread_(&BitrateStatsPollingThread,
                      this,
                      "BitrateStatsPollingThread") {
+  if (red_)
+    EXPECT_EQ(0u, num_flexfec_streams_);
   EXPECT_LE(num_audio_streams_, 1u);
 }
 
@@ -100,6 +105,10 @@
   return num_audio_streams_;
 }
 
+size_t RampUpTester::GetNumFlexfecStreams() const {
+  return num_flexfec_streams_;
+}
+
 class RampUpTester::VideoStreamFactory
     : public VideoEncoderConfig::VideoStreamFactoryInterface {
  public:
@@ -209,6 +218,13 @@
     }
     ++i;
   }
+
+  RTC_DCHECK_LE(num_flexfec_streams_, 1);
+  if (num_flexfec_streams_ == 1) {
+    send_config->rtp.flexfec.payload_type = test::CallTest::kFlexfecPayloadType;
+    send_config->rtp.flexfec.ssrc = test::CallTest::kFlexfecSendSsrc;
+    send_config->rtp.flexfec.protected_media_ssrcs = {video_ssrcs_[0]};
+  }
 }
 
 void RampUpTester::ModifyAudioConfigs(
@@ -219,6 +235,8 @@
 
   EXPECT_NE(RtpExtension::kTimestampOffsetUri, extension_type_)
       << "Audio BWE not supported with toffset.";
+  EXPECT_NE(RtpExtension::kAbsSendTimeUri, extension_type_)
+      << "Audio BWE not supported with abs-send-time.";
 
   send_config->rtp.ssrc = audio_ssrcs_[0];
   send_config->rtp.extensions.clear();
@@ -227,11 +245,7 @@
   send_config->max_bitrate_bps = 60000;
 
   bool transport_cc = false;
-  if (extension_type_ == RtpExtension::kAbsSendTimeUri) {
-    transport_cc = false;
-    send_config->rtp.extensions.push_back(
-        RtpExtension(extension_type_.c_str(), kAbsSendTimeExtensionId));
-  } else if (extension_type_ == RtpExtension::kTransportSequenceNumberUri) {
+  if (extension_type_ == RtpExtension::kTransportSequenceNumberUri) {
     transport_cc = true;
     send_config->rtp.extensions.push_back(RtpExtension(
         extension_type_.c_str(), kTransportSequenceNumberExtensionId));
@@ -244,6 +258,26 @@
   }
 }
 
+void RampUpTester::ModifyFlexfecConfigs(
+    std::vector<FlexfecReceiveStream::Config>* receive_configs) {
+  if (num_flexfec_streams_ == 0)
+    return;
+  RTC_DCHECK_EQ(1, num_flexfec_streams_);
+  (*receive_configs)[0].payload_type = test::CallTest::kFlexfecPayloadType;
+  (*receive_configs)[0].remote_ssrc = test::CallTest::kFlexfecSendSsrc;
+  (*receive_configs)[0].protected_media_ssrcs = {video_ssrcs_[0]};
+  (*receive_configs)[0].local_ssrc = video_ssrcs_[0];
+  if (extension_type_ == RtpExtension::kAbsSendTimeUri) {
+    (*receive_configs)[0].transport_cc = false;
+    (*receive_configs)[0].rtp_header_extensions.push_back(
+        RtpExtension(extension_type_.c_str(), kAbsSendTimeExtensionId));
+  } else if (extension_type_ == RtpExtension::kTransportSequenceNumberUri) {
+    (*receive_configs)[0].transport_cc = true;
+    (*receive_configs)[0].rtp_header_extensions.push_back(RtpExtension(
+        extension_type_.c_str(), kTransportSequenceNumberExtensionId));
+  }
+}
+
 void RampUpTester::OnCallsCreated(Call* sender_call, Call* receiver_call) {
   sender_call_ = sender_call;
 }
@@ -351,23 +385,32 @@
 
 RampUpDownUpTester::RampUpDownUpTester(size_t num_video_streams,
                                        size_t num_audio_streams,
+                                       size_t num_flexfec_streams,
                                        unsigned int start_bitrate_bps,
                                        const std::string& extension_type,
                                        bool rtx,
-                                       bool red)
+                                       bool red,
+                                       const std::vector<int>& loss_rates)
     : RampUpTester(num_video_streams,
                    num_audio_streams,
+                   num_flexfec_streams,
                    start_bitrate_bps,
                    0,
                    extension_type,
                    rtx,
                    red,
                    true),
+      link_rates_({GetHighLinkCapacity(), kLowBandwidthLimitBps / 1000,
+                   GetHighLinkCapacity(), 0}),
       test_state_(kFirstRampup),
+      next_state_(kTransitionToNextState),
       state_start_ms_(clock_->TimeInMilliseconds()),
       interval_start_ms_(clock_->TimeInMilliseconds()),
-      sent_bytes_(0) {
-  forward_transport_config_.link_capacity_kbps = GetHighLinkCapacity();
+      sent_bytes_(0),
+      loss_rates_(loss_rates) {
+  forward_transport_config_.link_capacity_kbps = link_rates_[test_state_];
+  forward_transport_config_.queue_delay_ms = 100;
+  forward_transport_config_.loss_percent = loss_rates_[test_state_];
 }
 
 RampUpDownUpTester::~RampUpDownUpTester() {}
@@ -432,57 +475,79 @@
   return 4 * GetExpectedHighBitrate() / (3 * 1000);
 }
 
+size_t RampUpDownUpTester::GetFecBytes() const {
+  size_t flex_fec_bytes = 0;
+  if (num_flexfec_streams_ > 0) {
+    webrtc::VideoSendStream::Stats stats = send_stream_->GetStats();
+    for (const auto& kv : stats.substreams)
+      flex_fec_bytes += kv.second.rtp_stats.fec.TotalBytes();
+  }
+  return flex_fec_bytes;
+}
+
+bool RampUpDownUpTester::ExpectingFec() const {
+  return num_flexfec_streams_ > 0 && forward_transport_config_.loss_percent > 0;
+}
+
 void RampUpDownUpTester::EvolveTestState(int bitrate_bps, bool suspended) {
   int64_t now = clock_->TimeInMilliseconds();
   switch (test_state_) {
-    case kFirstRampup: {
+    case kFirstRampup:
       EXPECT_FALSE(suspended);
       if (bitrate_bps >= GetExpectedHighBitrate()) {
-        // The first ramp-up has reached the target bitrate. Change the
-        // channel limit, and move to the next test state.
-        forward_transport_config_.link_capacity_kbps =
-            kLowBandwidthLimitBps / 1000;
-        send_transport_->SetConfig(forward_transport_config_);
-        test_state_ = kLowRate;
         webrtc::test::PrintResult("ramp_up_down_up", GetModifierString(),
                                   "first_rampup", now - state_start_ms_, "ms",
                                   false);
-        state_start_ms_ = now;
-        interval_start_ms_ = now;
-        sent_bytes_ = 0;
+        // Apply loss during the transition between states if FEC is enabled.
+        forward_transport_config_.loss_percent = loss_rates_[test_state_];
+        test_state_ = kTransitionToNextState;
+        next_state_ = kLowRate;
       }
       break;
-    }
     case kLowRate: {
       // Audio streams are never suspended.
       bool check_suspend_state = num_video_streams_ > 0;
       if (bitrate_bps < kExpectedLowBitrateBps &&
           suspended == check_suspend_state) {
-        // The ramp-down was successful. Change the channel limit back to a
-        // high value, and move to the next test state.
-        forward_transport_config_.link_capacity_kbps = GetHighLinkCapacity();
-        send_transport_->SetConfig(forward_transport_config_);
-        test_state_ = kSecondRampup;
         webrtc::test::PrintResult("ramp_up_down_up", GetModifierString(),
                                   "rampdown", now - state_start_ms_, "ms",
                                   false);
-        state_start_ms_ = now;
-        interval_start_ms_ = now;
-        sent_bytes_ = 0;
+        // Apply loss during the transition between states if FEC is enabled.
+        forward_transport_config_.loss_percent = loss_rates_[test_state_];
+        test_state_ = kTransitionToNextState;
+        next_state_ = kSecondRampup;
       }
       break;
     }
-    case kSecondRampup: {
+    case kSecondRampup:
       if (bitrate_bps >= GetExpectedHighBitrate() && !suspended) {
         webrtc::test::PrintResult("ramp_up_down_up", GetModifierString(),
                                   "second_rampup", now - state_start_ms_, "ms",
                                   false);
         ReportResult("ramp-up-down-up-average-network-latency",
                      send_transport_->GetAverageDelayMs(), "milliseconds");
-        observation_complete_.Set();
+        // Apply loss during the transition between states if FEC is enabled.
+        forward_transport_config_.loss_percent = loss_rates_[test_state_];
+        test_state_ = kTransitionToNextState;
+        next_state_ = kTestEnd;
       }
       break;
-    }
+    case kTestEnd:
+      observation_complete_.Set();
+      break;
+    case kTransitionToNextState:
+      if (!ExpectingFec() || GetFecBytes() > 0) {
+        test_state_ = next_state_;
+        forward_transport_config_.link_capacity_kbps = link_rates_[test_state_];
+        // No loss while ramping up and down as it may affect the BWE
+        // negatively, making the test flaky.
+        forward_transport_config_.loss_percent = 0;
+        state_start_ms_ = now;
+        interval_start_ms_ = now;
+        sent_bytes_ = 0;
+        send_transport_->SetConfig(forward_transport_config_);
+      }
+      break;
   }
 }
 
@@ -499,70 +564,83 @@
 static const uint32_t kStartBitrateBps = 60000;
 
 TEST_F(RampUpTest, UpDownUpAbsSendTimeSimulcastRedRtx) {
-  RampUpDownUpTester test(3, 0, kStartBitrateBps, RtpExtension::kAbsSendTimeUri,
-                          true, true);
+  std::vector<int> loss_rates = {0, 0, 0, 0};
+  RampUpDownUpTester test(3, 0, 0, kStartBitrateBps,
+                          RtpExtension::kAbsSendTimeUri, true, true,
+                          loss_rates);
   RunBaseTest(&test);
 }
 
 TEST_F(RampUpTest, UpDownUpTransportSequenceNumberRtx) {
-  RampUpDownUpTester test(3, 0, kStartBitrateBps,
+  std::vector<int> loss_rates = {0, 0, 0, 0};
+  RampUpDownUpTester test(3, 0, 0, kStartBitrateBps,
                           RtpExtension::kTransportSequenceNumberUri, true,
-                          false);
+                          false, loss_rates);
+  RunBaseTest(&test);
+}
+
+TEST_F(RampUpTest, UpDownUpTransportSequenceNumberPacketLoss) {
+  std::vector<int> loss_rates = {20, 0, 0, 0};
+  RampUpDownUpTester test(1, 0, 1, kStartBitrateBps,
+                          RtpExtension::kTransportSequenceNumberUri, true,
+                          false, loss_rates);
   RunBaseTest(&test);
 }
 
 TEST_F(RampUpTest, UpDownUpAudioVideoTransportSequenceNumberRtx) {
-  RampUpDownUpTester test(3, 1, kStartBitrateBps,
+  std::vector<int> loss_rates = {0, 0, 0, 0};
+  RampUpDownUpTester test(3, 1, 0, kStartBitrateBps,
                           RtpExtension::kTransportSequenceNumberUri, true,
-                          false);
+                          false, loss_rates);
   RunBaseTest(&test);
 }
 
 TEST_F(RampUpTest, UpDownUpAudioTransportSequenceNumberRtx) {
-  RampUpDownUpTester test(0, 1, kStartBitrateBps,
+  std::vector<int> loss_rates = {0, 0, 0, 0};
+  RampUpDownUpTester test(0, 1, 0, kStartBitrateBps,
                           RtpExtension::kTransportSequenceNumberUri, true,
-                          false);
+                          false, loss_rates);
   RunBaseTest(&test);
 }
 
 TEST_F(RampUpTest, TOffsetSimulcastRedRtx) {
-  RampUpTester test(3, 0, 0, 0, RtpExtension::kTimestampOffsetUri, true, true,
-                    true);
+  RampUpTester test(3, 0, 0, 0, 0, RtpExtension::kTimestampOffsetUri, true,
+                    true, true);
   RunBaseTest(&test);
 }
 
 TEST_F(RampUpTest, AbsSendTime) {
-  RampUpTester test(1, 0, 0, 0, RtpExtension::kAbsSendTimeUri, false, false,
+  RampUpTester test(1, 0, 0, 0, 0, RtpExtension::kAbsSendTimeUri, false, false,
                     true);
   RunBaseTest(&test);
 }
 
 TEST_F(RampUpTest, AbsSendTimeSimulcastRedRtx) {
-  RampUpTester test(3, 0, 0, 0, RtpExtension::kAbsSendTimeUri, true, true,
+  RampUpTester test(3, 0, 0, 0, 0, RtpExtension::kAbsSendTimeUri, true, true,
                     true);
   RunBaseTest(&test);
 }
 
 TEST_F(RampUpTest, TransportSequenceNumber) {
-  RampUpTester test(1, 0, 0, 0, RtpExtension::kTransportSequenceNumberUri,
+  RampUpTester test(1, 0, 0, 0, 0, RtpExtension::kTransportSequenceNumberUri,
                     false, false, true);
   RunBaseTest(&test);
 }
 
 TEST_F(RampUpTest, TransportSequenceNumberSimulcast) {
-  RampUpTester test(3, 0, 0, 0, RtpExtension::kTransportSequenceNumberUri,
+  RampUpTester test(3, 0, 0, 0, 0, RtpExtension::kTransportSequenceNumberUri,
                     false, false, true);
   RunBaseTest(&test);
 }
 
 TEST_F(RampUpTest, TransportSequenceNumberSimulcastRedRtx) {
-  RampUpTester test(3, 0, 0, 0, RtpExtension::kTransportSequenceNumberUri,
+  RampUpTester test(3, 0, 0, 0, 0, RtpExtension::kTransportSequenceNumberUri,
                     true, true, true);
   RunBaseTest(&test);
 }
 
 TEST_F(RampUpTest, AudioTransportSequenceNumber) {
-  RampUpTester test(0, 1, 300000, 10000,
+  RampUpTester test(0, 1, 0, 300000, 10000,
                     RtpExtension::kTransportSequenceNumberUri, false, false,
                     false);
   RunBaseTest(&test);
diff --git a/webrtc/call/rampup_tests.h b/webrtc/call/rampup_tests.h
index 5cf4c4f..65276ea 100644
--- a/webrtc/call/rampup_tests.h
+++ b/webrtc/call/rampup_tests.h
@@ -33,6 +33,7 @@
  public:
   RampUpTester(size_t num_video_streams,
                size_t num_audio_streams,
+               size_t num_flexfec_streams,
                unsigned int start_bitrate_bps,
                int64_t min_run_time_ms,
                const std::string& extension_type,
@@ -43,6 +44,7 @@
 
   size_t GetNumVideoStreams() const override;
   size_t GetNumAudioStreams() const override;
+  size_t GetNumFlexfecStreams() const override;
 
   void PerformTest() override;
 
@@ -66,6 +68,7 @@
   FakeNetworkPipe::Config forward_transport_config_;
   const size_t num_video_streams_;
   const size_t num_audio_streams_;
+  const size_t num_flexfec_streams_;
   const bool rtx_;
   const bool red_;
   Call* sender_call_;
@@ -88,6 +91,8 @@
   void ModifyAudioConfigs(
       AudioSendStream::Config* send_config,
       std::vector<AudioReceiveStream::Config>* receive_configs) override;
+  void ModifyFlexfecConfigs(
+      std::vector<FlexfecReceiveStream::Config>* receive_configs) override;
   void OnCallsCreated(Call* sender_call, Call* receiver_call) override;
 
   static bool BitrateStatsPollingThread(void* obj);
@@ -111,29 +116,42 @@
  public:
   RampUpDownUpTester(size_t num_video_streams,
                      size_t num_audio_streams,
+                     size_t num_flexfec_streams,
                      unsigned int start_bitrate_bps,
                      const std::string& extension_type,
                      bool rtx,
-                     bool red);
+                     bool red,
+                     const std::vector<int>& loss_rates);
   ~RampUpDownUpTester() override;
 
  protected:
   bool PollStats() override;
 
  private:
-  enum TestStates { kFirstRampup, kLowRate, kSecondRampup };
+  enum TestStates {
+    kFirstRampup = 0,
+    kLowRate,
+    kSecondRampup,
+    kTestEnd,
+    kTransitionToNextState,
+  };
 
   Call::Config GetReceiverCallConfig() override;
 
   std::string GetModifierString() const;
   int GetExpectedHighBitrate() const;
   int GetHighLinkCapacity() const;
+  size_t GetFecBytes() const;
+  bool ExpectingFec() const;
   void EvolveTestState(int bitrate_bps, bool suspended);
 
+  const std::vector<int> link_rates_;
   TestStates test_state_;
+  TestStates next_state_;
   int64_t state_start_ms_;
   int64_t interval_start_ms_;
   int sent_bytes_;
+  std::vector<int> loss_rates_;
 };
 }  // namespace webrtc
 #endif  // WEBRTC_CALL_RAMPUP_TESTS_H_
diff --git a/webrtc/test/call_test.cc b/webrtc/test/call_test.cc
index 833e53c..68d6857 100644
--- a/webrtc/test/call_test.cc
+++ b/webrtc/test/call_test.cc
@@ -215,7 +215,7 @@
     audio_send_config_.voe_channel_id = voe_send_.channel_id;
     audio_send_config_.rtp.ssrc = kAudioSendSsrc;
     audio_send_config_.send_codec_spec.codec_inst =
-        CodecInst{kAudioSendPayloadType, "ISAC", 16000, 480, 1, 32000};
+        CodecInst{kAudioSendPayloadType, "OPUS", 48000, 960, 2, 64000};
   }
 
   // TODO(brandtr): Update this when we support multistream protection.
diff --git a/webrtc/test/encoder_settings.cc b/webrtc/test/encoder_settings.cc
index 061fafc..570fff5 100644
--- a/webrtc/test/encoder_settings.cc
+++ b/webrtc/test/encoder_settings.cc
@@ -24,7 +24,7 @@
 const int DefaultVideoStreamFactory::kMaxBitratePerStream[] = {150000, 450000,
                                                                1500000};
 const int DefaultVideoStreamFactory::kDefaultMinBitratePerStream[] = {
-    50000, 200000, 700000};
+    30000, 200000, 700000};
 
 // static
 std::vector<VideoStream> CreateVideoStreams(
diff --git a/webrtc/test/fake_network_pipe.cc b/webrtc/test/fake_network_pipe.cc
index 0b3aa6a..d594ab8 100644
--- a/webrtc/test/fake_network_pipe.cc
+++ b/webrtc/test/fake_network_pipe.cc
@@ -33,31 +33,14 @@
     : clock_(clock),
       packet_receiver_(NULL),
       random_(seed),
-      config_(config),
+      config_(),
       dropped_packets_(0),
       sent_packets_(0),
       total_packet_delay_(0),
       bursting_(false),
       next_process_time_(clock_->TimeInMilliseconds()),
       last_log_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;
-  }
+  SetConfig(config);
 }
 
 FakeNetworkPipe::~FakeNetworkPipe() {
@@ -78,6 +61,24 @@
 void FakeNetworkPipe::SetConfig(const FakeNetworkPipe::Config& config) {
   rtc::CritScope crit(&lock_);
   config_ = config;  // Shallow copy of the struct.
+  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;
+  }
 }
 
 void FakeNetworkPipe::SendPacket(const uint8_t* data, size_t data_length) {