Revert "Refactors UlpFec and FlexFec to use a common interface."

This reverts commit 11af1d7444fd7438766b7bc52cbd64752d72e32e.

Reason for revert: Possible crash

Original change's description:
> Refactors UlpFec and FlexFec to use a common interface.
> 
> The new VideoFecGenerator is now injected into RtpSenderVideo,
> and generalizes the usage.
> This also prepares for being able to genera FEC in the RTP egress
> module.
> 
> Bug: webrtc:11340
> Change-Id: I8aa873129b2fb4131eb3399ee88f6ea2747155a3
> Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/168347
> Reviewed-by: Stefan Holmer <stefan@webrtc.org>
> Reviewed-by: Sebastian Jansson <srte@webrtc.org>
> Reviewed-by: Rasmus Brandt <brandtr@webrtc.org>
> Commit-Queue: Erik Språng <sprang@webrtc.org>
> Cr-Commit-Position: refs/heads/master@{#30515}

TBR=brandtr@webrtc.org,sprang@webrtc.org,stefan@webrtc.org,srte@webrtc.org

Change-Id: Iddf112d801621c8a4370b853cee3fa42bf2c7fba
No-Presubmit: true
No-Tree-Checks: true
No-Try: true
Bug: webrtc:11340
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/168603
Reviewed-by: Erik Språng <sprang@webrtc.org>
Commit-Queue: Erik Språng <sprang@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#30524}
diff --git a/call/rtp_video_sender.cc b/call/rtp_video_sender.cc
index ba39056..956ac0c 100644
--- a/call/rtp_video_sender.cc
+++ b/call/rtp_video_sender.cc
@@ -36,13 +36,9 @@
 
 namespace webrtc_internal_rtp_video_sender {
 
-RtpStreamSender::RtpStreamSender(
-    std::unique_ptr<RtpRtcp> rtp_rtcp,
-    std::unique_ptr<RTPSenderVideo> sender_video,
-    std::unique_ptr<VideoFecGenerator> fec_generator)
-    : rtp_rtcp(std::move(rtp_rtcp)),
-      sender_video(std::move(sender_video)),
-      fec_generator(std::move(fec_generator)) {}
+RtpStreamSender::RtpStreamSender(std::unique_ptr<RtpRtcp> rtp_rtcp,
+                                 std::unique_ptr<RTPSenderVideo> sender_video)
+    : rtp_rtcp(std::move(rtp_rtcp)), sender_video(std::move(sender_video)) {}
 
 RtpStreamSender::~RtpStreamSender() = default;
 
@@ -117,67 +113,6 @@
   return should_disable_red_and_ulpfec;
 }
 
-// TODO(brandtr): Update this function when we support multistream protection.
-std::unique_ptr<VideoFecGenerator> MaybeCreateFecGenerator(
-    Clock* clock,
-    const RtpConfig& rtp,
-    const std::map<uint32_t, RtpState>& suspended_ssrcs,
-    int simulcast_index) {
-  // If flexfec is configured that takes priority.
-  if (rtp.flexfec.payload_type >= 0) {
-    RTC_DCHECK_GE(rtp.flexfec.payload_type, 0);
-    RTC_DCHECK_LE(rtp.flexfec.payload_type, 127);
-    if (rtp.flexfec.ssrc == 0) {
-      RTC_LOG(LS_WARNING) << "FlexFEC is enabled, but no FlexFEC SSRC given. "
-                             "Therefore disabling FlexFEC.";
-      return nullptr;
-    }
-    if (rtp.flexfec.protected_media_ssrcs.empty()) {
-      RTC_LOG(LS_WARNING)
-          << "FlexFEC is enabled, but no protected media SSRC given. "
-             "Therefore disabling FlexFEC.";
-      return nullptr;
-    }
-
-    if (rtp.flexfec.protected_media_ssrcs.size() > 1) {
-      RTC_LOG(LS_WARNING)
-          << "The supplied FlexfecConfig contained multiple protected "
-             "media streams, but our implementation currently only "
-             "supports protecting a single media stream. "
-             "To avoid confusion, disabling FlexFEC completely.";
-      return nullptr;
-    }
-
-    if (absl::c_find(rtp.flexfec.protected_media_ssrcs,
-                     rtp.ssrcs[simulcast_index]) ==
-        rtp.flexfec.protected_media_ssrcs.end()) {
-      // Media SSRC not among flexfec protected SSRCs.
-      return nullptr;
-    }
-
-    const RtpState* rtp_state = nullptr;
-    auto it = suspended_ssrcs.find(rtp.flexfec.ssrc);
-    if (it != suspended_ssrcs.end()) {
-      rtp_state = &it->second;
-    }
-
-    RTC_DCHECK_EQ(1U, rtp.flexfec.protected_media_ssrcs.size());
-    return std::make_unique<FlexfecSender>(
-        rtp.flexfec.payload_type, rtp.flexfec.ssrc,
-        rtp.flexfec.protected_media_ssrcs[0], rtp.mid, rtp.extensions,
-        RTPSender::FecExtensionSizes(), rtp_state, clock);
-  } else if (rtp.ulpfec.red_payload_type >= 0 &&
-             rtp.ulpfec.ulpfec_payload_type >= 0 &&
-             !ShouldDisableRedAndUlpfec(/*flexfec_enabled=*/false, rtp)) {
-    // Flexfec not configured, but ulpfec is and is not disabled.
-    return std::make_unique<UlpfecGenerator>(
-        rtp.ulpfec.red_payload_type, rtp.ulpfec.ulpfec_payload_type, clock);
-  }
-
-  // Not a single FEC is given.
-  return nullptr;
-}
-
 std::vector<RtpStreamSender> CreateRtpStreamSenders(
     Clock* clock,
     const RtpConfig& rtp_config,
@@ -186,7 +121,7 @@
     Transport* send_transport,
     RtcpBandwidthObserver* bandwidth_callback,
     RtpTransportControllerSendInterface* transport,
-    const std::map<uint32_t, RtpState>& suspended_ssrcs,
+    FlexfecSender* flexfec_sender,
     RtcEventLog* event_log,
     RateLimiter* retransmission_rate_limiter,
     OverheadObserver* overhead_observer,
@@ -225,17 +160,18 @@
   configuration.rtcp_report_interval_ms = rtcp_report_interval_ms;
 
   std::vector<RtpStreamSender> rtp_streams;
-
+  const std::vector<uint32_t>& flexfec_protected_ssrcs =
+      rtp_config.flexfec.protected_media_ssrcs;
   RTC_DCHECK(rtp_config.rtx.ssrcs.empty() ||
              rtp_config.rtx.ssrcs.size() == rtp_config.rtx.ssrcs.size());
   for (size_t i = 0; i < rtp_config.ssrcs.size(); ++i) {
-    RTPSenderVideo::Config video_config;
     configuration.local_media_ssrc = rtp_config.ssrcs[i];
-
-    std::unique_ptr<VideoFecGenerator> fec_generator =
-        MaybeCreateFecGenerator(clock, rtp_config, suspended_ssrcs, i);
-    configuration.fec_generator = fec_generator.get();
-    video_config.fec_generator = fec_generator.get();
+    bool enable_flexfec = flexfec_sender != nullptr &&
+                          std::find(flexfec_protected_ssrcs.begin(),
+                                    flexfec_protected_ssrcs.end(),
+                                    configuration.local_media_ssrc) !=
+                              flexfec_protected_ssrcs.end();
+    configuration.flexfec_sender = enable_flexfec ? flexfec_sender : nullptr;
 
     if (rtp_config.rtx.ssrcs.size() > i) {
       configuration.rtx_send_ssrc = rtp_config.rtx.ssrcs[i];
@@ -251,31 +187,75 @@
     rtp_rtcp->SetStorePacketsStatus(true, kMinSendSidePacketHistorySize);
 
     FieldTrialBasedConfig field_trial_config;
+    RTPSenderVideo::Config video_config;
     video_config.clock = configuration.clock;
     video_config.rtp_sender = rtp_rtcp->RtpSender();
+    video_config.flexfec_sender = configuration.flexfec_sender;
     video_config.frame_encryptor = frame_encryptor;
     video_config.require_frame_encryption =
         crypto_options.sframe.require_frame_encryption;
     video_config.enable_retransmit_all_layers = false;
     video_config.field_trials = &field_trial_config;
-
-    const bool using_flexfec =
-        fec_generator &&
-        fec_generator->GetFecType() == VideoFecGenerator::FecType::kFlexFec;
     const bool should_disable_red_and_ulpfec =
-        ShouldDisableRedAndUlpfec(using_flexfec, rtp_config);
-    if (!should_disable_red_and_ulpfec &&
-        rtp_config.ulpfec.red_payload_type != -1) {
+        ShouldDisableRedAndUlpfec(enable_flexfec, rtp_config);
+    if (rtp_config.ulpfec.red_payload_type != -1 &&
+        !should_disable_red_and_ulpfec) {
       video_config.red_payload_type = rtp_config.ulpfec.red_payload_type;
     }
-
+    if (rtp_config.ulpfec.ulpfec_payload_type != -1 &&
+        !should_disable_red_and_ulpfec) {
+      video_config.ulpfec_payload_type = rtp_config.ulpfec.ulpfec_payload_type;
+    }
     auto sender_video = std::make_unique<RTPSenderVideo>(video_config);
-    rtp_streams.emplace_back(std::move(rtp_rtcp), std::move(sender_video),
-                             std::move(fec_generator));
+    rtp_streams.emplace_back(std::move(rtp_rtcp), std::move(sender_video));
   }
   return rtp_streams;
 }
 
+// TODO(brandtr): Update this function when we support multistream protection.
+std::unique_ptr<FlexfecSender> MaybeCreateFlexfecSender(
+    Clock* clock,
+    const RtpConfig& rtp,
+    const std::map<uint32_t, RtpState>& suspended_ssrcs) {
+  if (rtp.flexfec.payload_type < 0) {
+    return nullptr;
+  }
+  RTC_DCHECK_GE(rtp.flexfec.payload_type, 0);
+  RTC_DCHECK_LE(rtp.flexfec.payload_type, 127);
+  if (rtp.flexfec.ssrc == 0) {
+    RTC_LOG(LS_WARNING) << "FlexFEC is enabled, but no FlexFEC SSRC given. "
+                           "Therefore disabling FlexFEC.";
+    return nullptr;
+  }
+  if (rtp.flexfec.protected_media_ssrcs.empty()) {
+    RTC_LOG(LS_WARNING)
+        << "FlexFEC is enabled, but no protected media SSRC given. "
+           "Therefore disabling FlexFEC.";
+    return nullptr;
+  }
+
+  if (rtp.flexfec.protected_media_ssrcs.size() > 1) {
+    RTC_LOG(LS_WARNING)
+        << "The supplied FlexfecConfig contained multiple protected "
+           "media streams, but our implementation currently only "
+           "supports protecting a single media stream. "
+           "To avoid confusion, disabling FlexFEC completely.";
+    return nullptr;
+  }
+
+  const RtpState* rtp_state = nullptr;
+  auto it = suspended_ssrcs.find(rtp.flexfec.ssrc);
+  if (it != suspended_ssrcs.end()) {
+    rtp_state = &it->second;
+  }
+
+  RTC_DCHECK_EQ(1U, rtp.flexfec.protected_media_ssrcs.size());
+  return std::make_unique<FlexfecSender>(
+      rtp.flexfec.payload_type, rtp.flexfec.ssrc,
+      rtp.flexfec.protected_media_ssrcs[0], rtp.mid, rtp.extensions,
+      RTPSender::FecExtensionSizes(), rtp_state, clock);
+}
+
 DataRate CalculateOverheadRate(DataRate data_rate,
                                DataSize packet_size,
                                DataSize overhead_per_packet) {
@@ -322,6 +302,8 @@
       active_(false),
       module_process_thread_(nullptr),
       suspended_ssrcs_(std::move(suspended_ssrcs)),
+      flexfec_sender_(
+          MaybeCreateFlexfecSender(clock, rtp_config, suspended_ssrcs_)),
       fec_controller_(std::move(fec_controller)),
       fec_allowed_(true),
       rtp_streams_(CreateRtpStreamSenders(clock,
@@ -331,7 +313,7 @@
                                           send_transport,
                                           transport->GetBandwidthObserver(),
                                           transport,
-                                          suspended_ssrcs_,
+                                          flexfec_sender_.get(),
                                           event_log,
                                           retransmission_limiter,
                                           this,
@@ -393,7 +375,6 @@
     }
   }
 
-  bool fec_enabled = false;
   for (const RtpStreamSender& stream : rtp_streams_) {
     // Simulcast has one module for each layer. Set the CNAME on all modules.
     stream.rtp_rtcp->SetCNAME(rtp_config_.c_name.c_str());
@@ -403,13 +384,10 @@
     stream.rtp_rtcp->SetMaxRtpPacketSize(rtp_config_.max_packet_size);
     stream.rtp_rtcp->RegisterSendPayloadFrequency(rtp_config_.payload_type,
                                                   kVideoPayloadTypeFrequency);
-    if (stream.fec_generator != nullptr) {
-      fec_enabled = true;
-    }
   }
   // Currently, both ULPFEC and FlexFEC use the same FEC rate calculation logic,
   // so enable that logic if either of those FEC schemes are enabled.
-  fec_controller_->SetProtectionMethod(fec_enabled, NackEnabled());
+  fec_controller_->SetProtectionMethod(FecEnabled(), NackEnabled());
 
   fec_controller_->SetProtectionCallback(this);
   // Signal congestion controller this object is ready for OnPacket* callbacks.
@@ -567,6 +545,14 @@
   }
 }
 
+bool RtpVideoSender::FecEnabled() const {
+  const bool flexfec_enabled = (flexfec_sender_ != nullptr);
+  const bool ulpfec_enabled =
+      !webrtc::field_trial::IsEnabled("WebRTC-DisableUlpFecExperiment") &&
+      (rtp_config_.ulpfec.ulpfec_payload_type >= 0);
+  return flexfec_enabled || ulpfec_enabled;
+}
+
 bool RtpVideoSender::NackEnabled() const {
   const bool nack_enabled = rtp_config_.nack.rtp_history_ms > 0;
   return nack_enabled;
@@ -661,14 +647,6 @@
     uint32_t ssrc = rtp_config_.ssrcs[i];
     RTC_DCHECK_EQ(ssrc, rtp_streams_[i].rtp_rtcp->SSRC());
     rtp_states[ssrc] = rtp_streams_[i].rtp_rtcp->GetRtpState();
-
-    VideoFecGenerator* fec_generator = rtp_streams_[i].fec_generator.get();
-    if (fec_generator &&
-        fec_generator->GetFecType() == VideoFecGenerator::FecType::kFlexFec) {
-      auto* flexfec_sender = static_cast<FlexfecSender*>(fec_generator);
-      uint32_t ssrc = rtp_config_.flexfec.ssrc;
-      rtp_states[ssrc] = flexfec_sender->GetRtpState();
-    }
   }
 
   for (size_t i = 0; i < rtp_config_.rtx.ssrcs.size(); ++i) {
@@ -676,6 +654,11 @@
     rtp_states[ssrc] = rtp_streams_[i].rtp_rtcp->GetRtxState();
   }
 
+  if (flexfec_sender_) {
+    uint32_t ssrc = rtp_config_.flexfec.ssrc;
+    rtp_states[ssrc] = flexfec_sender_->GetRtpState();
+  }
+
   return rtp_states;
 }
 
diff --git a/call/rtp_video_sender.h b/call/rtp_video_sender.h
index ed89028..620c975 100644
--- a/call/rtp_video_sender.h
+++ b/call/rtp_video_sender.h
@@ -51,8 +51,7 @@
 // RtpVideoSender.
 struct RtpStreamSender {
   RtpStreamSender(std::unique_ptr<RtpRtcp> rtp_rtcp,
-                  std::unique_ptr<RTPSenderVideo> sender_video,
-                  std::unique_ptr<VideoFecGenerator> fec_generator);
+                  std::unique_ptr<RTPSenderVideo> sender_video);
   ~RtpStreamSender();
 
   RtpStreamSender(RtpStreamSender&&) = default;
@@ -61,7 +60,6 @@
   // Note: Needs pointer stability.
   std::unique_ptr<RtpRtcp> rtp_rtcp;
   std::unique_ptr<RTPSenderVideo> sender_video;
-  std::unique_ptr<VideoFecGenerator> fec_generator;
 };
 
 }  // namespace webrtc_internal_rtp_video_sender
@@ -156,6 +154,7 @@
   void ConfigureProtection();
   void ConfigureSsrcs();
   void ConfigureRids();
+  bool FecEnabled() const;
   bool NackEnabled() const;
   uint32_t GetPacketizationOverheadRate() const;
 
@@ -173,6 +172,8 @@
   rtc::ThreadChecker module_process_thread_checker_;
   std::map<uint32_t, RtpState> suspended_ssrcs_;
 
+  std::unique_ptr<FlexfecSender> flexfec_sender_;
+
   const std::unique_ptr<FecController> fec_controller_;
   bool fec_allowed_ RTC_GUARDED_BY(crit_);
 
diff --git a/modules/include/module_fec_types.h b/modules/include/module_fec_types.h
index f9b35cc..25d6bc5 100644
--- a/modules/include/module_fec_types.h
+++ b/modules/include/module_fec_types.h
@@ -24,9 +24,9 @@
 
 // Struct containing forward error correction settings.
 struct FecProtectionParams {
-  int fec_rate = 0;
-  int max_fec_frames = 0;
-  FecMaskType fec_mask_type = FecMaskType::kFecMaskRandom;
+  int fec_rate;
+  int max_fec_frames;
+  FecMaskType fec_mask_type;
 };
 
 }  // namespace webrtc
diff --git a/modules/rtp_rtcp/BUILD.gn b/modules/rtp_rtcp/BUILD.gn
index 447afd9..9005548 100644
--- a/modules/rtp_rtcp/BUILD.gn
+++ b/modules/rtp_rtcp/BUILD.gn
@@ -208,7 +208,6 @@
     "source/ulpfec_header_reader_writer.h",
     "source/ulpfec_receiver_impl.cc",
     "source/ulpfec_receiver_impl.h",
-    "source/video_fec_generator.h",
     "source/video_rtp_depacketizer.h",
     "source/video_rtp_depacketizer_av1.cc",
     "source/video_rtp_depacketizer_av1.h",
diff --git a/modules/rtp_rtcp/include/flexfec_sender.h b/modules/rtp_rtcp/include/flexfec_sender.h
index 4cc8f99..94f3502 100644
--- a/modules/rtp_rtcp/include/flexfec_sender.h
+++ b/modules/rtp_rtcp/include/flexfec_sender.h
@@ -21,9 +21,7 @@
 #include "modules/rtp_rtcp/include/rtp_rtcp_defines.h"
 #include "modules/rtp_rtcp/source/rtp_header_extension_size.h"
 #include "modules/rtp_rtcp/source/ulpfec_generator.h"
-#include "modules/rtp_rtcp/source/video_fec_generator.h"
 #include "rtc_base/random.h"
-#include "rtc_base/rate_statistics.h"
 
 namespace webrtc {
 
@@ -33,7 +31,7 @@
 // Note that this class is not thread safe, and thus requires external
 // synchronization. Currently, this is done using the lock in PayloadRouter.
 
-class FlexfecSender : public VideoFecGenerator {
+class FlexfecSender {
  public:
   FlexfecSender(int payload_type,
                 uint32_t ssrc,
@@ -45,28 +43,26 @@
                 Clock* clock);
   ~FlexfecSender();
 
-  FecType GetFecType() const override {
-    return VideoFecGenerator::FecType::kFlexFec;
-  }
-  absl::optional<uint32_t> FecSsrc() override { return ssrc_; }
+  uint32_t ssrc() const { return ssrc_; }
 
   // Sets the FEC rate, max frames sent before FEC packets are sent,
   // and what type of generator matrices are used.
-  void SetProtectionParameters(const FecProtectionParams& delta_params,
-                               const FecProtectionParams& key_params) override;
+  void SetFecParameters(const FecProtectionParams& params);
 
   // Adds a media packet to the internal buffer. When enough media packets
   // have been added, the FEC packets are generated and stored internally.
   // These FEC packets are then obtained by calling GetFecPackets().
-  void AddPacketAndGenerateFec(const RtpPacketToSend& packet) override;
+  // Returns true if the media packet was successfully added.
+  bool AddRtpPacketAndGenerateFec(const RtpPacketToSend& packet);
+
+  // Returns true if there are generated FEC packets available.
+  bool FecAvailable() const;
 
   // Returns generated FlexFEC packets.
-  std::vector<std::unique_ptr<RtpPacketToSend>> GetFecPackets() override;
+  std::vector<std::unique_ptr<RtpPacketToSend>> GetFecPackets();
 
   // Returns the overhead, per packet, for FlexFEC.
-  size_t MaxPacketOverhead() const override;
-
-  DataRate CurrentFecRate() const override;
+  size_t MaxPacketOverhead() const;
 
   // Only called on the VideoSendStream queue, after operation has shut down.
   RtpState GetRtpState();
@@ -91,9 +87,6 @@
   UlpfecGenerator ulpfec_generator_;
   const RtpHeaderExtensionMap rtp_header_extension_map_;
   const size_t header_extensions_size_;
-
-  rtc::CriticalSection crit_;
-  RateStatistics fec_bitrate_ RTC_GUARDED_BY(crit_);
 };
 
 }  // namespace webrtc
diff --git a/modules/rtp_rtcp/include/rtp_rtcp.h b/modules/rtp_rtcp/include/rtp_rtcp.h
index e771e2a..b256f38 100644
--- a/modules/rtp_rtcp/include/rtp_rtcp.h
+++ b/modules/rtp_rtcp/include/rtp_rtcp.h
@@ -22,13 +22,13 @@
 #include "api/transport/webrtc_key_value_config.h"
 #include "api/video/video_bitrate_allocation.h"
 #include "modules/include/module.h"
+#include "modules/rtp_rtcp/include/flexfec_sender.h"
 #include "modules/rtp_rtcp/include/receive_statistics.h"
 #include "modules/rtp_rtcp/include/report_block_data.h"
 #include "modules/rtp_rtcp/include/rtp_packet_sender.h"
 #include "modules/rtp_rtcp/include/rtp_rtcp_defines.h"
 #include "modules/rtp_rtcp/source/rtp_packet_to_send.h"
 #include "modules/rtp_rtcp/source/rtp_sequence_number_map.h"
-#include "modules/rtp_rtcp/source/video_fec_generator.h"
 #include "rtc_base/constructor_magic.h"
 #include "rtc_base/deprecation.h"
 
@@ -92,9 +92,9 @@
     // Spread any bursts of packets into smaller bursts to minimize packet loss.
     RtpPacketSender* paced_sender = nullptr;
 
-    // Generates FEC packets.
-    // TODO(sprang): Wire up to RtpSenderEgress.
-    VideoFecGenerator* fec_generator = nullptr;
+    // Generate FlexFEC packets.
+    // TODO(brandtr): Remove when FlexfecSender is wired up to PacedSender.
+    FlexfecSender* flexfec_sender = nullptr;
 
     BitrateStatisticsObserver* send_bitrate_observer = nullptr;
     SendSideDelayObserver* send_side_delay_observer = nullptr;
diff --git a/modules/rtp_rtcp/source/flexfec_sender.cc b/modules/rtp_rtcp/source/flexfec_sender.cc
index 874a0cc..de0d412 100644
--- a/modules/rtp_rtcp/source/flexfec_sender.cc
+++ b/modules/rtp_rtcp/source/flexfec_sender.cc
@@ -91,13 +91,11 @@
       seq_num_(rtp_state ? rtp_state->sequence_number
                          : random_.Rand(1, kMaxInitRtpSeqNumber)),
       ulpfec_generator_(
-          ForwardErrorCorrection::CreateFlexfec(ssrc, protected_media_ssrc),
-          clock_),
+          ForwardErrorCorrection::CreateFlexfec(ssrc, protected_media_ssrc)),
       rtp_header_extension_map_(
           RegisterSupportedExtensions(rtp_header_extensions)),
       header_extensions_size_(
-          RtpHeaderExtensionSize(extension_sizes, rtp_header_extension_map_)),
-      fec_bitrate_(/*max_window_size_ms=*/1000, RateStatistics::kBpsScale) {
+          RtpHeaderExtensionSize(extension_sizes, rtp_header_extension_map_)) {
   // This object should not have been instantiated if FlexFEC is disabled.
   RTC_DCHECK_GE(payload_type, 0);
   RTC_DCHECK_LE(payload_type, 127);
@@ -107,30 +105,30 @@
 
 // We are reusing the implementation from UlpfecGenerator for SetFecParameters,
 // AddRtpPacketAndGenerateFec, and FecAvailable.
-void FlexfecSender::SetProtectionParameters(
-    const FecProtectionParams& delta_params,
-    const FecProtectionParams& key_params) {
-  ulpfec_generator_.SetProtectionParameters(delta_params, key_params);
+void FlexfecSender::SetFecParameters(const FecProtectionParams& params) {
+  ulpfec_generator_.SetFecParameters(params);
 }
 
-void FlexfecSender::AddPacketAndGenerateFec(const RtpPacketToSend& packet) {
+bool FlexfecSender::AddRtpPacketAndGenerateFec(const RtpPacketToSend& packet) {
   // TODO(brandtr): Generalize this SSRC check when we support multistream
   // protection.
   RTC_DCHECK_EQ(packet.Ssrc(), protected_media_ssrc_);
-  ulpfec_generator_.AddPacketAndGenerateFec(packet);
+  return ulpfec_generator_.AddRtpPacketAndGenerateFec(
+             packet.Buffer(), packet.headers_size()) == 0;
+}
+
+bool FlexfecSender::FecAvailable() const {
+  return ulpfec_generator_.FecAvailable();
 }
 
 std::vector<std::unique_ptr<RtpPacketToSend>> FlexfecSender::GetFecPackets() {
-  RTC_CHECK_RUNS_SERIALIZED(&ulpfec_generator_.race_checker_);
   std::vector<std::unique_ptr<RtpPacketToSend>> fec_packets_to_send;
   fec_packets_to_send.reserve(ulpfec_generator_.generated_fec_packets_.size());
-  size_t total_fec_data_bytes = 0;
   for (const auto* fec_packet : ulpfec_generator_.generated_fec_packets_) {
     std::unique_ptr<RtpPacketToSend> fec_packet_to_send(
         new RtpPacketToSend(&rtp_header_extension_map_));
     fec_packet_to_send->set_packet_type(
         RtpPacketMediaType::kForwardErrorCorrection);
-    fec_packet_to_send->set_allow_retransmission(false);
 
     // RTP header.
     fec_packet_to_send->SetMarker(false);
@@ -159,13 +157,9 @@
         fec_packet_to_send->AllocatePayload(fec_packet->data.size());
     memcpy(payload, fec_packet->data.cdata(), fec_packet->data.size());
 
-    total_fec_data_bytes += fec_packet_to_send->size();
     fec_packets_to_send.push_back(std::move(fec_packet_to_send));
   }
-
-  if (!fec_packets_to_send.empty()) {
-    ulpfec_generator_.ResetState();
-  }
+  ulpfec_generator_.ResetState();
 
   int64_t now_ms = clock_->TimeInMilliseconds();
   if (!fec_packets_to_send.empty() &&
@@ -176,9 +170,6 @@
     last_generated_packet_ms_ = now_ms;
   }
 
-  rtc::CritScope cs(&crit_);
-  fec_bitrate_.Update(total_fec_data_bytes, now_ms);
-
   return fec_packets_to_send;
 }
 
@@ -187,12 +178,6 @@
   return header_extensions_size_ + kFlexfecMaxHeaderSize;
 }
 
-DataRate FlexfecSender::CurrentFecRate() const {
-  rtc::CritScope cs(&crit_);
-  return DataRate::bps(
-      fec_bitrate_.Rate(clock_->TimeInMilliseconds()).value_or(0));
-}
-
 RtpState FlexfecSender::GetRtpState() {
   RtpState rtp_state;
   rtp_state.sequence_number = seq_num_;
diff --git a/modules/rtp_rtcp/source/flexfec_sender_unittest.cc b/modules/rtp_rtcp/source/flexfec_sender_unittest.cc
index e4501c2..10ec2e7 100644
--- a/modules/rtp_rtcp/source/flexfec_sender_unittest.cc
+++ b/modules/rtp_rtcp/source/flexfec_sender_unittest.cc
@@ -55,7 +55,7 @@
   params.fec_mask_type = kFecMaskRandom;
   constexpr size_t kNumPackets = 4;
 
-  sender->SetProtectionParameters(params, params);
+  sender->SetFecParameters(params);
   AugmentedPacketGenerator packet_generator(kMediaSsrc);
   packet_generator.NewFrame(kNumPackets);
   for (size_t i = 0; i < kNumPackets; ++i) {
@@ -63,12 +63,13 @@
         packet_generator.NextPacket(i, kPayloadLength);
     RtpPacketToSend rtp_packet(nullptr);  // No header extensions.
     rtp_packet.Parse(packet->data);
-    sender->AddPacketAndGenerateFec(rtp_packet);
+    EXPECT_TRUE(sender->AddRtpPacketAndGenerateFec(rtp_packet));
   }
+  EXPECT_TRUE(sender->FecAvailable());
   std::vector<std::unique_ptr<RtpPacketToSend>> fec_packets =
       sender->GetFecPackets();
+  EXPECT_FALSE(sender->FecAvailable());
   EXPECT_EQ(1U, fec_packets.size());
-  EXPECT_TRUE(sender->GetFecPackets().empty());
 
   return std::move(fec_packets.front());
 }
@@ -81,7 +82,7 @@
                        kNoRtpHeaderExtensions, kNoRtpHeaderExtensionSizes,
                        nullptr /* rtp_state */, &clock);
 
-  EXPECT_EQ(kFlexfecSsrc, sender.FecSsrc());
+  EXPECT_EQ(kFlexfecSsrc, sender.ssrc());
 }
 
 TEST(FlexfecSenderTest, NoFecAvailableBeforeMediaAdded) {
@@ -90,7 +91,9 @@
                        kNoRtpHeaderExtensions, kNoRtpHeaderExtensionSizes,
                        nullptr /* rtp_state */, &clock);
 
-  EXPECT_TRUE(sender.GetFecPackets().empty());
+  EXPECT_FALSE(sender.FecAvailable());
+  auto fec_packets = sender.GetFecPackets();
+  EXPECT_EQ(0U, fec_packets.size());
 }
 
 TEST(FlexfecSenderTest, ProtectOneFrameWithOneFecPacket) {
@@ -121,7 +124,7 @@
   FlexfecSender sender(kFlexfecPayloadType, kFlexfecSsrc, kMediaSsrc, kNoMid,
                        kNoRtpHeaderExtensions, kNoRtpHeaderExtensionSizes,
                        nullptr /* rtp_state */, &clock);
-  sender.SetProtectionParameters(params, params);
+  sender.SetFecParameters(params);
 
   AugmentedPacketGenerator packet_generator(kMediaSsrc);
   for (size_t i = 0; i < kNumFrames; ++i) {
@@ -131,13 +134,14 @@
           packet_generator.NextPacket(i, kPayloadLength);
       RtpPacketToSend rtp_packet(nullptr);
       rtp_packet.Parse(packet->data);
-      sender.AddPacketAndGenerateFec(rtp_packet);
+      EXPECT_TRUE(sender.AddRtpPacketAndGenerateFec(rtp_packet));
     }
   }
+  EXPECT_TRUE(sender.FecAvailable());
   std::vector<std::unique_ptr<RtpPacketToSend>> fec_packets =
       sender.GetFecPackets();
+  EXPECT_FALSE(sender.FecAvailable());
   ASSERT_EQ(1U, fec_packets.size());
-  EXPECT_TRUE(sender.GetFecPackets().empty());
 
   RtpPacketToSend* fec_packet = fec_packets.front().get();
   EXPECT_EQ(kRtpHeaderSize, fec_packet->headers_size());
@@ -160,7 +164,7 @@
   FlexfecSender sender(kFlexfecPayloadType, kFlexfecSsrc, kMediaSsrc, kNoMid,
                        kNoRtpHeaderExtensions, kNoRtpHeaderExtensionSizes,
                        nullptr /* rtp_state */, &clock);
-  sender.SetProtectionParameters(params, params);
+  sender.SetFecParameters(params);
 
   AugmentedPacketGenerator packet_generator(kMediaSsrc);
   for (size_t i = 0; i < kNumFrames; ++i) {
@@ -170,12 +174,13 @@
           packet_generator.NextPacket(i, kPayloadLength);
       RtpPacketToSend rtp_packet(nullptr);
       rtp_packet.Parse(packet->data);
-      sender.AddPacketAndGenerateFec(rtp_packet);
+      EXPECT_TRUE(sender.AddRtpPacketAndGenerateFec(rtp_packet));
     }
+    EXPECT_TRUE(sender.FecAvailable());
     std::vector<std::unique_ptr<RtpPacketToSend>> fec_packets =
         sender.GetFecPackets();
+    EXPECT_FALSE(sender.FecAvailable());
     ASSERT_EQ(1U, fec_packets.size());
-    EXPECT_TRUE(sender.GetFecPackets().empty());
 
     RtpPacketToSend* fec_packet = fec_packets.front().get();
     EXPECT_EQ(kRtpHeaderSize, fec_packet->headers_size());
diff --git a/modules/rtp_rtcp/source/rtcp_receiver.cc b/modules/rtp_rtcp/source/rtcp_receiver.cc
index 26465ad..6b64473 100644
--- a/modules/rtp_rtcp/source/rtcp_receiver.cc
+++ b/modules/rtp_rtcp/source/rtcp_receiver.cc
@@ -72,11 +72,8 @@
   if (config.rtx_send_ssrc) {
     ssrcs.insert(*config.rtx_send_ssrc);
   }
-  if (config.fec_generator) {
-    absl::optional<uint32_t> flexfec_ssrc = config.fec_generator->FecSsrc();
-    if (flexfec_ssrc) {
-      ssrcs.insert(*flexfec_ssrc);
-    }
+  if (config.flexfec_sender) {
+    ssrcs.insert(config.flexfec_sender->ssrc());
   }
   return ssrcs;
 }
diff --git a/modules/rtp_rtcp/source/rtp_packet_to_send.h b/modules/rtp_rtcp/source/rtp_packet_to_send.h
index 8997bce..57493e3 100644
--- a/modules/rtp_rtcp/source/rtp_packet_to_send.h
+++ b/modules/rtp_rtcp/source/rtp_packet_to_send.h
@@ -98,15 +98,11 @@
         VideoTimingExtension::kNetwork2TimestampDeltaOffset);
   }
 
-  // Indicates if packet is the first packet of a video frame.
   void set_first_packet_of_frame(bool is_first_packet) {
     is_first_packet_of_frame_ = is_first_packet;
   }
-  bool is_first_packet_of_frame() const { return is_first_packet_of_frame_; }
 
-  // Indicates if packet contains payload for a video key-frame.
-  void set_is_key_frame(bool is_key_frame) { is_key_frame_ = is_key_frame; }
-  bool is_key_frame() const { return is_key_frame_; }
+  bool is_first_packet_of_frame() const { return is_first_packet_of_frame_; }
 
  private:
   int64_t capture_time_ms_ = 0;
@@ -115,7 +111,6 @@
   absl::optional<uint16_t> retransmitted_sequence_number_;
   std::vector<uint8_t> application_data_;
   bool is_first_packet_of_frame_ = false;
-  bool is_key_frame_ = false;
 };
 
 }  // namespace webrtc
diff --git a/modules/rtp_rtcp/source/rtp_sender.cc b/modules/rtp_rtcp/source/rtp_sender.cc
index c48a662..3277c67 100644
--- a/modules/rtp_rtcp/source/rtp_sender.cc
+++ b/modules/rtp_rtcp/source/rtp_sender.cc
@@ -102,8 +102,9 @@
       audio_configured_(config.audio),
       ssrc_(config.local_media_ssrc),
       rtx_ssrc_(config.rtx_send_ssrc),
-      flexfec_ssrc_(config.fec_generator ? config.fec_generator->FecSsrc()
-                                         : absl::nullopt),
+      flexfec_ssrc_(config.flexfec_sender
+                        ? absl::make_optional(config.flexfec_sender->ssrc())
+                        : absl::nullopt),
       packet_history_(packet_history),
       paced_sender_(packet_sender),
       sending_media_(true),                   // Default to sending media.
diff --git a/modules/rtp_rtcp/source/rtp_sender_egress.cc b/modules/rtp_rtcp/source/rtp_sender_egress.cc
index 91332f8..fafd3ca 100644
--- a/modules/rtp_rtcp/source/rtp_sender_egress.cc
+++ b/modules/rtp_rtcp/source/rtp_sender_egress.cc
@@ -57,8 +57,9 @@
                                  RtpPacketHistory* packet_history)
     : ssrc_(config.local_media_ssrc),
       rtx_ssrc_(config.rtx_send_ssrc),
-      flexfec_ssrc_(config.fec_generator ? config.fec_generator->FecSsrc()
-                                         : absl::nullopt),
+      flexfec_ssrc_(config.flexfec_sender
+                        ? absl::make_optional(config.flexfec_sender->ssrc())
+                        : absl::nullopt),
       populate_network2_timestamp_(config.populate_network2_timestamp),
       send_side_bwe_with_overhead_(
           IsEnabled("WebRTC-SendSideBwe-WithOverhead", config.field_trials)),
diff --git a/modules/rtp_rtcp/source/rtp_sender_unittest.cc b/modules/rtp_rtcp/source/rtp_sender_unittest.cc
index 3b85166..c3ae539 100644
--- a/modules/rtp_rtcp/source/rtp_sender_unittest.cc
+++ b/modules/rtp_rtcp/source/rtp_sender_unittest.cc
@@ -272,7 +272,7 @@
     config.outgoing_transport = &transport_;
     config.local_media_ssrc = kSsrc;
     config.rtx_send_ssrc = kRtxSsrc;
-    config.fec_generator = &flexfec_sender_;
+    config.flexfec_sender = &flexfec_sender_;
     config.event_log = &mock_rtc_event_log_;
     config.send_packet_observer = &send_packet_observer_;
     config.retransmission_rate_limiter = &retransmission_rate_limiter_;
@@ -1225,7 +1225,7 @@
   config.outgoing_transport = &transport_;
   config.paced_sender = &mock_paced_sender_;
   config.local_media_ssrc = kSsrc;
-  config.fec_generator = &flexfec_sender_;
+  config.flexfec_sender = &flexfec_sender_;
   config.event_log = &mock_rtc_event_log_;
   config.send_packet_observer = &send_packet_observer_;
   config.retransmission_rate_limiter = &retransmission_rate_limiter_;
@@ -1239,7 +1239,7 @@
   RTPSenderVideo::Config video_config;
   video_config.clock = &fake_clock_;
   video_config.rtp_sender = rtp_sender();
-  video_config.fec_generator = &flexfec_sender;
+  video_config.flexfec_sender = &flexfec_sender;
   video_config.field_trials = &field_trials;
   RTPSenderVideo rtp_sender_video(video_config);
 
@@ -1311,7 +1311,7 @@
   config.clock = &fake_clock_;
   config.outgoing_transport = &transport_;
   config.local_media_ssrc = kSsrc;
-  config.fec_generator = &flexfec_sender;
+  config.flexfec_sender = &flexfec_sender;
   config.event_log = &mock_rtc_event_log_;
   config.send_packet_observer = &send_packet_observer_;
   config.retransmission_rate_limiter = &retransmission_rate_limiter_;
@@ -1323,7 +1323,7 @@
   RTPSenderVideo::Config video_config;
   video_config.clock = &fake_clock_;
   video_config.rtp_sender = rtp_sender();
-  video_config.fec_generator = &flexfec_sender;
+  video_config.flexfec_sender = &flexfec_sender;
   video_config.field_trials = &field_trials;
   RTPSenderVideo rtp_sender_video(video_config);
 
@@ -1583,7 +1583,7 @@
   config.outgoing_transport = &transport_;
   config.paced_sender = &mock_paced_sender_;
   config.local_media_ssrc = kSsrc;
-  config.fec_generator = &flexfec_sender;
+  config.flexfec_sender = &flexfec_sender;
   config.event_log = &mock_rtc_event_log_;
   config.send_packet_observer = &send_packet_observer_;
   config.retransmission_rate_limiter = &retransmission_rate_limiter_;
@@ -1595,7 +1595,7 @@
   RTPSenderVideo::Config video_config;
   video_config.clock = &fake_clock_;
   video_config.rtp_sender = rtp_sender();
-  video_config.fec_generator = &flexfec_sender;
+  video_config.flexfec_sender = &flexfec_sender;
   video_config.field_trials = &field_trials;
   RTPSenderVideo rtp_sender_video(video_config);
   // Parameters selected to generate a single FEC packet per media packet.
@@ -1777,14 +1777,12 @@
   const uint8_t kPayloadType = 127;
   const VideoCodecType kCodecType = VideoCodecType::kVideoCodecGeneric;
   FieldTrialBasedConfig field_trials;
-  UlpfecGenerator ulpfec_generator(kRedPayloadType, kUlpfecPayloadType,
-                                   &fake_clock_);
   RTPSenderVideo::Config video_config;
   video_config.clock = &fake_clock_;
   video_config.rtp_sender = rtp_sender();
   video_config.field_trials = &field_trials;
   video_config.red_payload_type = kRedPayloadType;
-  video_config.fec_generator = &ulpfec_generator;
+  video_config.ulpfec_payload_type = kUlpfecPayloadType;
   RTPSenderVideo rtp_sender_video(video_config);
   uint8_t payload[] = {47, 11, 32, 93, 89};
   rtp_sender_context_->packet_history_.SetStorePacketsStatus(
@@ -2120,7 +2118,7 @@
   config.outgoing_transport = &transport_;
   config.local_media_ssrc = kSsrc;
   config.rtx_send_ssrc = kRtxSsrc;
-  config.fec_generator = &flexfec_sender_;
+  config.flexfec_sender = &flexfec_sender_;
   config.send_side_delay_observer = &send_side_delay_observer;
   config.event_log = &mock_rtc_event_log_;
   config.send_packet_observer = &send_packet_observer_;
diff --git a/modules/rtp_rtcp/source/rtp_sender_video.cc b/modules/rtp_rtcp/source/rtp_sender_video.cc
index 6556752..2696514 100644
--- a/modules/rtp_rtcp/source/rtp_sender_video.cc
+++ b/modules/rtp_rtcp/source/rtp_sender_video.cc
@@ -258,7 +258,11 @@
       current_playout_delay_{-1, -1},
       playout_delay_pending_(false),
       red_payload_type_(config.red_payload_type),
-      fec_generator_(config.fec_generator),
+      ulpfec_payload_type_(config.ulpfec_payload_type),
+      flexfec_sender_(config.flexfec_sender),
+      delta_fec_params_{0, 1, kFecMaskRandom},
+      key_fec_params_{0, 1, kFecMaskRandom},
+      fec_bitrate_(1000, RateStatistics::kBpsScale),
       video_bitrate_(1000, RateStatistics::kBpsScale),
       packetization_overhead_bitrate_(1000, RateStatistics::kBpsScale),
       frame_encryptor_(config.frame_encryptor),
@@ -274,6 +278,83 @@
 
 RTPSenderVideo::~RTPSenderVideo() {}
 
+void RTPSenderVideo::AppendAsRedMaybeWithUlpfec(
+    std::unique_ptr<RtpPacketToSend> media_packet,
+    bool protect_media_packet,
+    std::vector<std::unique_ptr<RtpPacketToSend>>* packets) {
+  std::unique_ptr<RtpPacketToSend> red_packet(
+      new RtpPacketToSend(*media_packet));
+  BuildRedPayload(*media_packet, red_packet.get());
+  red_packet->SetPayloadType(*red_payload_type_);
+
+  std::vector<std::unique_ptr<RedPacket>> fec_packets;
+  if (ulpfec_enabled()) {
+    if (protect_media_packet) {
+      if (exclude_transport_sequence_number_from_fec_experiment_) {
+        // See comments at the top of the file why experiment
+        // "WebRTC-kExcludeTransportSequenceNumberFromFec" is needed in
+        // conjunction with datagram transport.
+        // TODO(sukhanov): We may also need to implement it for flexfec_sender
+        // if we decide to keep this approach in the future.
+        uint16_t transport_senquence_number;
+        if (media_packet->GetExtension<webrtc::TransportSequenceNumber>(
+                &transport_senquence_number)) {
+          if (!media_packet->RemoveExtension(
+                  webrtc::TransportSequenceNumber::kId)) {
+            RTC_NOTREACHED()
+                << "Failed to remove transport sequence number, packet="
+                << media_packet->ToString();
+          }
+        }
+      }
+
+      ulpfec_generator_.AddRtpPacketAndGenerateFec(
+          media_packet->Buffer(), media_packet->headers_size());
+    }
+    uint16_t num_fec_packets = ulpfec_generator_.NumAvailableFecPackets();
+    if (num_fec_packets > 0) {
+      uint16_t first_fec_sequence_number =
+          rtp_sender_->AllocateSequenceNumber(num_fec_packets);
+      fec_packets = ulpfec_generator_.GetUlpfecPacketsAsRed(
+          *red_payload_type_, *ulpfec_payload_type_, first_fec_sequence_number);
+      RTC_DCHECK_EQ(num_fec_packets, fec_packets.size());
+    }
+  }
+
+  // Send |red_packet| instead of |packet| for allocated sequence number.
+  red_packet->set_packet_type(RtpPacketMediaType::kVideo);
+  red_packet->set_allow_retransmission(media_packet->allow_retransmission());
+  packets->emplace_back(std::move(red_packet));
+
+  for (const auto& fec_packet : fec_packets) {
+    // TODO(danilchap): Make ulpfec_generator_ generate RtpPacketToSend to avoid
+    // reparsing them.
+    std::unique_ptr<RtpPacketToSend> rtp_packet(
+        new RtpPacketToSend(*media_packet));
+    RTC_CHECK(rtp_packet->Parse(fec_packet->data(), fec_packet->length()));
+    rtp_packet->set_capture_time_ms(media_packet->capture_time_ms());
+    rtp_packet->set_packet_type(RtpPacketMediaType::kForwardErrorCorrection);
+    rtp_packet->set_allow_retransmission(false);
+    RTC_DCHECK_EQ(fec_packet->length(), rtp_packet->size());
+    packets->emplace_back(std::move(rtp_packet));
+  }
+}
+
+void RTPSenderVideo::GenerateAndAppendFlexfec(
+    std::vector<std::unique_ptr<RtpPacketToSend>>* packets) {
+  RTC_DCHECK(flexfec_sender_);
+
+  if (flexfec_sender_->FecAvailable()) {
+    std::vector<std::unique_ptr<RtpPacketToSend>> fec_packets =
+        flexfec_sender_->GetFecPackets();
+    for (auto& fec_packet : fec_packets) {
+      fec_packet->set_packet_type(RtpPacketMediaType::kForwardErrorCorrection);
+      fec_packet->set_allow_retransmission(false);
+      packets->emplace_back(std::move(fec_packet));
+    }
+  }
+}
+
 void RTPSenderVideo::LogAndSendToNetwork(
     std::vector<std::unique_ptr<RtpPacketToSend>> packets,
     size_t unpacketized_payload_size) {
@@ -292,9 +373,16 @@
     rtc::CritScope cs(&stats_crit_);
     size_t packetized_payload_size = 0;
     for (const auto& packet : packets) {
-      if (*packet->packet_type() == RtpPacketMediaType::kVideo) {
-        video_bitrate_.Update(packet->size(), now_ms);
-        packetized_payload_size += packet->payload_size();
+      switch (*packet->packet_type()) {
+        case RtpPacketMediaType::kVideo:
+          video_bitrate_.Update(packet->size(), now_ms);
+          packetized_payload_size += packet->payload_size();
+          break;
+        case RtpPacketMediaType::kForwardErrorCorrection:
+          fec_bitrate_.Update(packet->size(), clock_->TimeInMilliseconds());
+          break;
+        default:
+          continue;
       }
     }
     // AV1 packetizer may produce less packetized bytes than unpacketized.
@@ -309,31 +397,39 @@
 }
 
 size_t RTPSenderVideo::FecPacketOverhead() const {
-  size_t overhead = fec_generator_ ? fec_generator_->MaxPacketOverhead() : 0u;
+  if (flexfec_enabled())
+    return flexfec_sender_->MaxPacketOverhead();
+
+  size_t overhead = 0;
   if (red_enabled()) {
     // The RED overhead is due to a small header.
     overhead += kRedForFecHeaderLength;
-
-    // TODO(bugs.webrtc.org/11340): Move this into UlpfecGenerator.
-    if (fec_generator_ &&
-        fec_generator_->GetFecType() == VideoFecGenerator::FecType::kUlpFec) {
-      // For ULPFEC, the overhead is the FEC headers plus RED for FEC header
-      // (see above) plus anything in RTP header beyond the 12 bytes base header
-      // (CSRC list, extensions...)
-      // This reason for the header extensions to be included here is that
-      // from an FEC viewpoint, they are part of the payload to be protected.
-      // (The base RTP header is already protected by the FEC header.)
-      overhead += rtp_sender_->RtpHeaderLength() - kRtpHeaderSize;
-    }
+  }
+  if (ulpfec_enabled()) {
+    // For ULPFEC, the overhead is the FEC headers plus RED for FEC header
+    // (see above) plus anything in RTP header beyond the 12 bytes base header
+    // (CSRC list, extensions...)
+    // This reason for the header extensions to be included here is that
+    // from an FEC viewpoint, they are part of the payload to be protected.
+    // (The base RTP header is already protected by the FEC header.)
+    overhead += ulpfec_generator_.MaxPacketOverhead() +
+                (rtp_sender_->RtpHeaderLength() - kRtpHeaderSize);
   }
   return overhead;
 }
 
 void RTPSenderVideo::SetFecParameters(const FecProtectionParams& delta_params,
                                       const FecProtectionParams& key_params) {
-  if (fec_generator_) {
-    fec_generator_->SetProtectionParameters(delta_params, key_params);
+  rtc::CritScope cs(&crit_);
+  delta_fec_params_ = delta_params;
+  key_fec_params_ = key_params;
+}
+
+absl::optional<uint32_t> RTPSenderVideo::FlexfecSsrc() const {
+  if (flexfec_sender_) {
+    return flexfec_sender_->ssrc();
   }
+  return absl::nullopt;
 }
 
 void RTPSenderVideo::SetVideoStructure(
@@ -444,6 +540,19 @@
         transmit_color_space_next_frame_ ? !IsBaseLayer(video_header) : false;
   }
 
+  if (flexfec_enabled() || ulpfec_enabled()) {
+    rtc::CritScope cs(&crit_);
+    // FEC settings.
+    const FecProtectionParams& fec_params =
+        video_header.frame_type == VideoFrameType::kVideoFrameKey
+            ? key_fec_params_
+            : delta_fec_params_;
+    if (flexfec_enabled())
+      flexfec_sender_->SetFecParameters(fec_params);
+    if (ulpfec_enabled())
+      ulpfec_generator_.SetFecParameters(fec_params);
+  }
+
   // Maximum size of packet including rtp headers.
   // Extra space left in case packet will be resent using fec or rtx.
   int packet_capacity = rtp_sender_->MaxRtpPacketSize() - FecPacketOverhead() -
@@ -634,40 +743,21 @@
       packet->set_packetization_finish_time_ms(clock_->TimeInMilliseconds());
     }
 
-    if (protect_packet && fec_generator_) {
-      if (red_enabled() &&
-          exclude_transport_sequence_number_from_fec_experiment_) {
-        // See comments at the top of the file why experiment
-        // "WebRTC-kExcludeTransportSequenceNumberFromFec" is needed in
-        // conjunction with datagram transport.
-        // TODO(sukhanov): We may also need to implement it for flexfec_sender
-        // if we decide to keep this approach in the future.
-        uint16_t transport_senquence_number;
-        if (packet->GetExtension<webrtc::TransportSequenceNumber>(
-                &transport_senquence_number)) {
-          if (!packet->RemoveExtension(webrtc::TransportSequenceNumber::kId)) {
-            RTC_NOTREACHED()
-                << "Failed to remove transport sequence number, packet="
-                << packet->ToString();
-          }
-        }
-      }
-
-      fec_generator_->AddPacketAndGenerateFec(*packet);
-    }
-
     if (red_enabled()) {
-      std::unique_ptr<RtpPacketToSend> red_packet(new RtpPacketToSend(*packet));
-      BuildRedPayload(*packet, red_packet.get());
-      red_packet->SetPayloadType(*red_payload_type_);
-
-      // Send |red_packet| instead of |packet| for allocated sequence number.
-      red_packet->set_packet_type(RtpPacketMediaType::kVideo);
-      red_packet->set_allow_retransmission(packet->allow_retransmission());
-      rtp_packets.emplace_back(std::move(red_packet));
+      AppendAsRedMaybeWithUlpfec(std::move(packet), protect_packet,
+                                 &rtp_packets);
     } else {
       packet->set_packet_type(RtpPacketMediaType::kVideo);
+      const RtpPacketToSend& media_packet = *packet;
       rtp_packets.emplace_back(std::move(packet));
+      if (flexfec_enabled()) {
+        // TODO(brandtr): Remove the FlexFEC code path when FlexfecSender
+        // is wired up to PacedSender instead.
+        if (protect_packet) {
+          flexfec_sender_->AddRtpPacketAndGenerateFec(media_packet);
+        }
+        GenerateAndAppendFlexfec(&rtp_packets);
+      }
     }
 
     if (first_frame) {
@@ -682,22 +772,6 @@
     }
   }
 
-  if (fec_generator_) {
-    // Fetch any FEC packets generated from the media frame and add them to
-    // the list of packets to send.
-    auto fec_packets = fec_generator_->GetFecPackets();
-
-    // TODO(bugs.webrtc.org/11340): Move sequence number assignment into
-    // UlpfecGenerator.
-    const bool generate_sequence_numbers = !fec_generator_->FecSsrc();
-    for (auto& fec_packet : fec_packets) {
-      if (generate_sequence_numbers) {
-        rtp_sender_->AssignSequenceNumber(fec_packet.get());
-      }
-      rtp_packets.emplace_back(std::move(fec_packet));
-    }
-  }
-
   LogAndSendToNetwork(std::move(rtp_packets), unpacketized_payload_size);
 
   TRACE_EVENT_ASYNC_END1("webrtc", "Video", capture_time_ms, "timestamp",
@@ -711,7 +785,8 @@
 }
 
 uint32_t RTPSenderVideo::FecOverheadRate() const {
-  return fec_generator_ ? fec_generator_->CurrentFecRate().bps<uint32_t>() : 0u;
+  rtc::CritScope cs(&stats_crit_);
+  return fec_bitrate_.Rate(clock_->TimeInMilliseconds()).value_or(0);
 }
 
 uint32_t RTPSenderVideo::PacketizationOverheadBps() const {
diff --git a/modules/rtp_rtcp/source/rtp_sender_video.h b/modules/rtp_rtcp/source/rtp_sender_video.h
index 1b92f52..5c9657e 100644
--- a/modules/rtp_rtcp/source/rtp_sender_video.h
+++ b/modules/rtp_rtcp/source/rtp_sender_video.h
@@ -22,12 +22,13 @@
 #include "api/video/video_codec_type.h"
 #include "api/video/video_frame_type.h"
 #include "modules/include/module_common_types.h"
+#include "modules/rtp_rtcp/include/flexfec_sender.h"
 #include "modules/rtp_rtcp/include/rtp_rtcp_defines.h"
 #include "modules/rtp_rtcp/source/absolute_capture_time_sender.h"
 #include "modules/rtp_rtcp/source/rtp_rtcp_config.h"
 #include "modules/rtp_rtcp/source/rtp_sender.h"
 #include "modules/rtp_rtcp/source/rtp_video_header.h"
-#include "modules/rtp_rtcp/source/video_fec_generator.h"
+#include "modules/rtp_rtcp/source/ulpfec_generator.h"
 #include "rtc_base/critical_section.h"
 #include "rtc_base/one_time_event.h"
 #include "rtc_base/race_checker.h"
@@ -67,11 +68,11 @@
     Clock* clock = nullptr;
     RTPSender* rtp_sender = nullptr;
     FlexfecSender* flexfec_sender = nullptr;
-    VideoFecGenerator* fec_generator = nullptr;
     FrameEncryptorInterface* frame_encryptor = nullptr;
     bool require_frame_encryption = false;
     bool enable_retransmit_all_layers = false;
     absl::optional<int> red_payload_type;
+    absl::optional<int> ulpfec_payload_type;
     const WebRtcKeyValueConfig* field_trials = nullptr;
   };
 
@@ -98,9 +99,13 @@
 
   // FlexFEC/ULPFEC.
   // Set FEC rates, max frames before FEC is sent, and type of FEC masks.
+  // Returns false on failure.
   void SetFecParameters(const FecProtectionParams& delta_params,
                         const FecProtectionParams& key_params);
 
+  // FlexFEC.
+  absl::optional<uint32_t> FlexfecSsrc() const;
+
   uint32_t VideoBitrateSent() const;
   uint32_t FecOverheadRate() const;
 
@@ -129,12 +134,27 @@
 
   size_t FecPacketOverhead() const RTC_EXCLUSIVE_LOCKS_REQUIRED(send_checker_);
 
+  void AppendAsRedMaybeWithUlpfec(
+      std::unique_ptr<RtpPacketToSend> media_packet,
+      bool protect_media_packet,
+      std::vector<std::unique_ptr<RtpPacketToSend>>* packets)
+      RTC_EXCLUSIVE_LOCKS_REQUIRED(send_checker_);
+
+  // TODO(brandtr): Remove the FlexFEC functions when FlexfecSender has been
+  // moved to PacedSender.
+  void GenerateAndAppendFlexfec(
+      std::vector<std::unique_ptr<RtpPacketToSend>>* packets);
+
   void LogAndSendToNetwork(
       std::vector<std::unique_ptr<RtpPacketToSend>> packets,
       size_t unpacketized_payload_size);
 
   bool red_enabled() const { return red_payload_type_.has_value(); }
 
+  bool ulpfec_enabled() const { return ulpfec_payload_type_.has_value(); }
+
+  bool flexfec_enabled() const { return flexfec_sender_ != nullptr; }
+
   bool UpdateConditionalRetransmit(uint8_t temporal_id,
                                    int64_t expected_retransmission_time_ms)
       RTC_EXCLUSIVE_LOCKS_REQUIRED(stats_crit_);
@@ -165,10 +185,22 @@
   // Should never be held when calling out of this class.
   rtc::CriticalSection crit_;
 
+  // RED/ULPFEC.
   const absl::optional<int> red_payload_type_;
-  VideoFecGenerator* const fec_generator_;
+  const absl::optional<int> ulpfec_payload_type_;
+  UlpfecGenerator ulpfec_generator_ RTC_GUARDED_BY(send_checker_);
+
+  // FlexFEC.
+  FlexfecSender* const flexfec_sender_;
+
+  // FEC parameters, applicable to either ULPFEC or FlexFEC.
+  FecProtectionParams delta_fec_params_ RTC_GUARDED_BY(crit_);
+  FecProtectionParams key_fec_params_ RTC_GUARDED_BY(crit_);
 
   rtc::CriticalSection stats_crit_;
+  // Bitrate used for FEC payload, RED headers, RTP headers for FEC packets
+  // and any padding overhead.
+  RateStatistics fec_bitrate_ RTC_GUARDED_BY(stats_crit_);
   // Bitrate used for video payload and RTP headers.
   RateStatistics video_bitrate_ RTC_GUARDED_BY(stats_crit_);
   RateStatistics packetization_overhead_bitrate_ RTC_GUARDED_BY(stats_crit_);
diff --git a/modules/rtp_rtcp/source/rtp_sender_video_unittest.cc b/modules/rtp_rtcp/source/rtp_sender_video_unittest.cc
index 6065742..af235af 100644
--- a/modules/rtp_rtcp/source/rtp_sender_video_unittest.cc
+++ b/modules/rtp_rtcp/source/rtp_sender_video_unittest.cc
@@ -123,7 +123,7 @@
           Config config;
           config.clock = clock;
           config.rtp_sender = rtp_sender;
-          config.fec_generator = flexfec_sender;
+          config.flexfec_sender = flexfec_sender;
           config.field_trials = &field_trials;
           return config;
         }()) {}
diff --git a/modules/rtp_rtcp/source/ulpfec_generator.cc b/modules/rtp_rtcp/source/ulpfec_generator.cc
index 45bea87..92e65df 100644
--- a/modules/rtp_rtcp/source/ulpfec_generator.cc
+++ b/modules/rtp_rtcp/source/ulpfec_generator.cc
@@ -22,7 +22,6 @@
 #include "modules/rtp_rtcp/source/forward_error_correction_internal.h"
 #include "modules/rtp_rtcp/source/rtp_utility.h"
 #include "rtc_base/checks.h"
-#include "rtc_base/critical_section.h"
 
 namespace webrtc {
 
@@ -63,119 +62,128 @@
 
 }  // namespace
 
-UlpfecGenerator::Params::Params() = default;
-UlpfecGenerator::Params::Params(FecProtectionParams delta_params,
-                                FecProtectionParams keyframe_params)
-    : delta_params(delta_params), keyframe_params(keyframe_params) {}
+RedPacket::RedPacket(size_t length)
+    : data_(new uint8_t[length]), length_(length), header_length_(0) {}
 
-UlpfecGenerator::UlpfecGenerator(int red_payload_type,
-                                 int ulpfec_payload_type,
-                                 Clock* clock)
-    : red_payload_type_(red_payload_type),
-      ulpfec_payload_type_(ulpfec_payload_type),
-      clock_(clock),
-      fec_(ForwardErrorCorrection::CreateUlpfec(kUnknownSsrc)),
-      num_protected_frames_(0),
-      min_num_media_packets_(1),
-      keyframe_in_process_(false),
-      fec_bitrate_(/*max_window_size_ms=*/1000, RateStatistics::kBpsScale) {}
+RedPacket::~RedPacket() = default;
 
-// Used by FlexFecSender, payload types are unused.
-UlpfecGenerator::UlpfecGenerator(std::unique_ptr<ForwardErrorCorrection> fec,
-                                 Clock* clock)
-    : red_payload_type_(0),
-      ulpfec_payload_type_(0),
-      clock_(clock),
-      fec_(std::move(fec)),
+void RedPacket::CreateHeader(const uint8_t* rtp_header,
+                             size_t header_length,
+                             int red_payload_type,
+                             int payload_type) {
+  RTC_DCHECK_LE(header_length + kRedForFecHeaderLength, length_);
+  memcpy(data_.get(), rtp_header, header_length);
+  // Replace payload type.
+  data_[1] &= 0x80;
+  data_[1] += red_payload_type;
+  // Add RED header
+  // f-bit always 0
+  data_[header_length] = static_cast<uint8_t>(payload_type);
+  header_length_ = header_length + kRedForFecHeaderLength;
+}
+
+void RedPacket::SetSeqNum(int seq_num) {
+  RTC_DCHECK_GE(seq_num, 0);
+  RTC_DCHECK_LT(seq_num, 1 << 16);
+
+  ByteWriter<uint16_t>::WriteBigEndian(&data_[2], seq_num);
+}
+
+void RedPacket::AssignPayload(const uint8_t* payload, size_t length) {
+  RTC_DCHECK_LE(header_length_ + length, length_);
+  memcpy(data_.get() + header_length_, payload, length);
+}
+
+void RedPacket::ClearMarkerBit() {
+  data_[1] &= 0x7F;
+}
+
+uint8_t* RedPacket::data() const {
+  return data_.get();
+}
+
+size_t RedPacket::length() const {
+  return length_;
+}
+
+UlpfecGenerator::UlpfecGenerator()
+    : UlpfecGenerator(ForwardErrorCorrection::CreateUlpfec(kUnknownSsrc)) {}
+
+UlpfecGenerator::UlpfecGenerator(std::unique_ptr<ForwardErrorCorrection> fec)
+    : fec_(std::move(fec)),
+      last_media_packet_rtp_header_length_(0),
       num_protected_frames_(0),
-      min_num_media_packets_(1),
-      keyframe_in_process_(false),
-      fec_bitrate_(/*max_window_size_ms=*/1000, RateStatistics::kBpsScale) {}
+      min_num_media_packets_(1) {
+  memset(&params_, 0, sizeof(params_));
+  memset(&new_params_, 0, sizeof(new_params_));
+}
 
 UlpfecGenerator::~UlpfecGenerator() = default;
 
-void UlpfecGenerator::SetProtectionParameters(
-    const FecProtectionParams& delta_params,
-    const FecProtectionParams& key_params) {
-  RTC_DCHECK_GE(delta_params.fec_rate, 0);
-  RTC_DCHECK_LE(delta_params.fec_rate, 255);
-  RTC_DCHECK_GE(key_params.fec_rate, 0);
-  RTC_DCHECK_LE(key_params.fec_rate, 255);
+void UlpfecGenerator::SetFecParameters(const FecProtectionParams& params) {
+  RTC_DCHECK_GE(params.fec_rate, 0);
+  RTC_DCHECK_LE(params.fec_rate, 255);
   // Store the new params and apply them for the next set of FEC packets being
   // produced.
-  rtc::CritScope cs(&crit_);
-  pending_params_.emplace(delta_params, key_params);
+  new_params_ = params;
+  if (params.fec_rate > kHighProtectionThreshold) {
+    min_num_media_packets_ = kMinMediaPackets;
+  } else {
+    min_num_media_packets_ = 1;
+  }
 }
 
-void UlpfecGenerator::AddPacketAndGenerateFec(const RtpPacketToSend& packet) {
-  RTC_DCHECK_RUNS_SERIALIZED(&race_checker_);
+int UlpfecGenerator::AddRtpPacketAndGenerateFec(
+    const rtc::CopyOnWriteBuffer& data_buffer,
+    size_t rtp_header_length) {
   RTC_DCHECK(generated_fec_packets_.empty());
-
   if (media_packets_.empty()) {
-    rtc::CritScope cs(&crit_);
-    if (pending_params_) {
-      current_params_ = *pending_params_;
-      pending_params_.reset();
-
-      if (CurrentParams().fec_rate > kHighProtectionThreshold) {
-        min_num_media_packets_ = kMinMediaPackets;
-      } else {
-        min_num_media_packets_ = 1;
-      }
-    }
-
-    keyframe_in_process_ = packet.is_key_frame();
+    params_ = new_params_;
   }
-  RTC_DCHECK_EQ(packet.is_key_frame(), keyframe_in_process_);
-
   bool complete_frame = false;
-  const bool marker_bit = packet.Marker();
+  const bool marker_bit = (data_buffer[1] & kRtpMarkerBitMask) ? true : false;
   if (media_packets_.size() < kUlpfecMaxMediaPackets) {
     // Our packet masks can only protect up to |kUlpfecMaxMediaPackets| packets.
-    auto fec_packet = std::make_unique<ForwardErrorCorrection::Packet>();
-    fec_packet->data = packet.Buffer();
-    media_packets_.push_back(std::move(fec_packet));
-
-    // Keep a copy of the last RTP packet, so we can copy the RTP header
-    // from it when creating newly generated ULPFEC+RED packets.
-    RTC_DCHECK_GE(packet.headers_size(), kRtpHeaderSize);
-    last_media_packet_ = packet;
+    std::unique_ptr<ForwardErrorCorrection::Packet> packet(
+        new ForwardErrorCorrection::Packet());
+    RTC_DCHECK_GE(data_buffer.size(), rtp_header_length);
+    packet->data = data_buffer;
+    media_packets_.push_back(std::move(packet));
+    // Keep track of the RTP header length, so we can copy the RTP header
+    // from |packet| to newly generated ULPFEC+RED packets.
+    RTC_DCHECK_GE(rtp_header_length, kRtpHeaderSize);
+    last_media_packet_rtp_header_length_ = rtp_header_length;
   }
-
   if (marker_bit) {
     ++num_protected_frames_;
     complete_frame = true;
   }
-
-  auto params = CurrentParams();
-
   // Produce FEC over at most |params_.max_fec_frames| frames, or as soon as:
   // (1) the excess overhead (actual overhead - requested/target overhead) is
   // less than |kMaxExcessOverhead|, and
   // (2) at least |min_num_media_packets_| media packets is reached.
   if (complete_frame &&
-      (num_protected_frames_ == params.max_fec_frames ||
+      (num_protected_frames_ == params_.max_fec_frames ||
        (ExcessOverheadBelowMax() && MinimumMediaPacketsReached()))) {
     // We are not using Unequal Protection feature of the parity erasure code.
     constexpr int kNumImportantPackets = 0;
     constexpr bool kUseUnequalProtection = false;
-    fec_->EncodeFec(media_packets_, params.fec_rate, kNumImportantPackets,
-                    kUseUnequalProtection, params.fec_mask_type,
-                    &generated_fec_packets_);
+    int ret = fec_->EncodeFec(media_packets_, params_.fec_rate,
+                              kNumImportantPackets, kUseUnequalProtection,
+                              params_.fec_mask_type, &generated_fec_packets_);
     if (generated_fec_packets_.empty()) {
       ResetState();
     }
+    return ret;
   }
+  return 0;
 }
 
 bool UlpfecGenerator::ExcessOverheadBelowMax() const {
-  RTC_DCHECK_RUNS_SERIALIZED(&race_checker_);
-
-  return ((Overhead() - CurrentParams().fec_rate) < kMaxExcessOverhead);
+  return ((Overhead() - params_.fec_rate) < kMaxExcessOverhead);
 }
 
 bool UlpfecGenerator::MinimumMediaPacketsReached() const {
-  RTC_DCHECK_RUNS_SERIALIZED(&race_checker_);
   float average_num_packets_per_frame =
       static_cast<float>(media_packets_.size()) / num_protected_frames_;
   int num_media_packets = static_cast<int>(media_packets_.size());
@@ -188,79 +196,61 @@
   }
 }
 
-const FecProtectionParams& UlpfecGenerator::CurrentParams() const {
-  RTC_DCHECK_RUNS_SERIALIZED(&race_checker_);
-  return keyframe_in_process_ ? current_params_.keyframe_params
-                              : current_params_.delta_params;
+bool UlpfecGenerator::FecAvailable() const {
+  return !generated_fec_packets_.empty();
+}
+
+size_t UlpfecGenerator::NumAvailableFecPackets() const {
+  return generated_fec_packets_.size();
 }
 
 size_t UlpfecGenerator::MaxPacketOverhead() const {
-  RTC_DCHECK_RUNS_SERIALIZED(&race_checker_);
   return fec_->MaxPacketOverhead();
 }
 
-std::vector<std::unique_ptr<RtpPacketToSend>> UlpfecGenerator::GetFecPackets() {
-  RTC_DCHECK_RUNS_SERIALIZED(&race_checker_);
-  if (generated_fec_packets_.empty()) {
-    return std::vector<std::unique_ptr<RtpPacketToSend>>();
-  }
-
-  // Wrap FEC packet (including FEC headers) in a RED packet. Since the
-  // FEC packets in |generated_fec_packets_| don't have RTP headers, we
-  // reuse the header from the last media packet.
-  RTC_CHECK(last_media_packet_.has_value());
-  last_media_packet_->SetPayloadSize(0);
-
-  std::vector<std::unique_ptr<RtpPacketToSend>> fec_packets;
-  fec_packets.reserve(generated_fec_packets_.size());
-
-  size_t total_fec_size_bytes = 0;
+std::vector<std::unique_ptr<RedPacket>> UlpfecGenerator::GetUlpfecPacketsAsRed(
+    int red_payload_type,
+    int ulpfec_payload_type,
+    uint16_t first_seq_num) {
+  std::vector<std::unique_ptr<RedPacket>> red_packets;
+  red_packets.reserve(generated_fec_packets_.size());
+  RTC_DCHECK(!media_packets_.empty());
+  ForwardErrorCorrection::Packet* last_media_packet =
+      media_packets_.back().get();
+  uint16_t seq_num = first_seq_num;
   for (const auto* fec_packet : generated_fec_packets_) {
-    std::unique_ptr<RtpPacketToSend> red_packet =
-        std::make_unique<RtpPacketToSend>(*last_media_packet_);
-    red_packet->SetPayloadType(red_payload_type_);
-    red_packet->SetMarker(false);
-    uint8_t* payload_buffer = red_packet->SetPayloadSize(
-        kRedForFecHeaderLength + fec_packet->data.size());
-    // Primary RED header with F bit unset.
-    // See https://tools.ietf.org/html/rfc2198#section-3
-    payload_buffer[0] = ulpfec_payload_type_;  // RED header.
-    memcpy(&payload_buffer[1], fec_packet->data.data(),
-           fec_packet->data.size());
-    total_fec_size_bytes += red_packet->size();
-    red_packet->set_packet_type(RtpPacketMediaType::kForwardErrorCorrection);
-    red_packet->set_allow_retransmission(false);
-    fec_packets.push_back(std::move(red_packet));
+    // Wrap FEC packet (including FEC headers) in a RED packet. Since the
+    // FEC packets in |generated_fec_packets_| don't have RTP headers, we
+    // reuse the header from the last media packet.
+    RTC_DCHECK_GT(last_media_packet_rtp_header_length_, 0);
+    std::unique_ptr<RedPacket> red_packet(
+        new RedPacket(last_media_packet_rtp_header_length_ +
+                      kRedForFecHeaderLength + fec_packet->data.size()));
+    red_packet->CreateHeader(last_media_packet->data.data(),
+                             last_media_packet_rtp_header_length_,
+                             red_payload_type, ulpfec_payload_type);
+    red_packet->SetSeqNum(seq_num++);
+    red_packet->ClearMarkerBit();
+    red_packet->AssignPayload(fec_packet->data.data(), fec_packet->data.size());
+    red_packets.push_back(std::move(red_packet));
   }
 
   ResetState();
 
-  rtc::CritScope cs(&crit_);
-  fec_bitrate_.Update(total_fec_size_bytes, clock_->TimeInMilliseconds());
-
-  return fec_packets;
-}
-
-DataRate UlpfecGenerator::CurrentFecRate() const {
-  rtc::CritScope cs(&crit_);
-  return DataRate::bps(
-      fec_bitrate_.Rate(clock_->TimeInMilliseconds()).value_or(0));
+  return red_packets;
 }
 
 int UlpfecGenerator::Overhead() const {
-  RTC_DCHECK_RUNS_SERIALIZED(&race_checker_);
   RTC_DCHECK(!media_packets_.empty());
   int num_fec_packets =
-      fec_->NumFecPackets(media_packets_.size(), CurrentParams().fec_rate);
-
+      fec_->NumFecPackets(media_packets_.size(), params_.fec_rate);
   // Return the overhead in Q8.
   return (num_fec_packets << 8) / media_packets_.size();
 }
 
 void UlpfecGenerator::ResetState() {
-  RTC_DCHECK_RUNS_SERIALIZED(&race_checker_);
   media_packets_.clear();
-  last_media_packet_.reset();
+  last_media_packet_rtp_header_length_ = 0;
   generated_fec_packets_.clear();
   num_protected_frames_ = 0;
 }
diff --git a/modules/rtp_rtcp/source/ulpfec_generator.h b/modules/rtp_rtcp/source/ulpfec_generator.h
index 6c65f5f..cdfa1ff 100644
--- a/modules/rtp_rtcp/source/ulpfec_generator.h
+++ b/modules/rtp_rtcp/source/ulpfec_generator.h
@@ -20,54 +20,63 @@
 
 #include "modules/include/module_fec_types.h"
 #include "modules/rtp_rtcp/source/forward_error_correction.h"
-#include "modules/rtp_rtcp/source/video_fec_generator.h"
-#include "rtc_base/critical_section.h"
-#include "rtc_base/race_checker.h"
-#include "rtc_base/rate_statistics.h"
 
 namespace webrtc {
 
 class FlexfecSender;
 
-class UlpfecGenerator : public VideoFecGenerator {
+class RedPacket {
+ public:
+  explicit RedPacket(size_t length);
+  ~RedPacket();
+
+  void CreateHeader(const uint8_t* rtp_header,
+                    size_t header_length,
+                    int red_payload_type,
+                    int payload_type);
+  void SetSeqNum(int seq_num);
+  void AssignPayload(const uint8_t* payload, size_t length);
+  void ClearMarkerBit();
+  uint8_t* data() const;
+  size_t length() const;
+
+ private:
+  std::unique_ptr<uint8_t[]> data_;
+  size_t length_;
+  size_t header_length_;
+};
+
+class UlpfecGenerator {
   friend class FlexfecSender;
 
  public:
-  UlpfecGenerator(int red_payload_type, int ulpfec_payload_type, Clock* clock);
+  UlpfecGenerator();
   ~UlpfecGenerator();
 
-  FecType GetFecType() const override {
-    return VideoFecGenerator::FecType::kUlpFec;
-  }
-  absl::optional<uint32_t> FecSsrc() override { return absl::nullopt; }
-
-  void SetProtectionParameters(const FecProtectionParams& delta_params,
-                               const FecProtectionParams& key_params) override;
+  void SetFecParameters(const FecProtectionParams& params);
 
   // Adds a media packet to the internal buffer. When enough media packets
   // have been added, the FEC packets are generated and stored internally.
   // These FEC packets are then obtained by calling GetFecPacketsAsRed().
-  void AddPacketAndGenerateFec(const RtpPacketToSend& packet) override;
+  int AddRtpPacketAndGenerateFec(const rtc::CopyOnWriteBuffer& data_buffer,
+                                 size_t rtp_header_length);
+
+  // Returns true if there are generated FEC packets available.
+  bool FecAvailable() const;
+
+  size_t NumAvailableFecPackets() const;
 
   // Returns the overhead, per packet, for FEC (and possibly RED).
-  size_t MaxPacketOverhead() const override;
+  size_t MaxPacketOverhead() const;
 
-  std::vector<std::unique_ptr<RtpPacketToSend>> GetFecPackets() override;
-
-  // Current rate of FEC packets generated, including all RTP-level headers.
-  DataRate CurrentFecRate() const override;
+  // Returns generated FEC packets with RED headers added.
+  std::vector<std::unique_ptr<RedPacket>> GetUlpfecPacketsAsRed(
+      int red_payload_type,
+      int ulpfec_payload_type,
+      uint16_t first_seq_num);
 
  private:
-  struct Params {
-    Params();
-    Params(FecProtectionParams delta_params,
-           FecProtectionParams keyframe_params);
-
-    FecProtectionParams delta_params;
-    FecProtectionParams keyframe_params;
-  };
-
-  UlpfecGenerator(std::unique_ptr<ForwardErrorCorrection> fec, Clock* clock);
+  explicit UlpfecGenerator(std::unique_ptr<ForwardErrorCorrection> fec);
 
   // Overhead is defined as relative to the number of media packets, and not
   // relative to total number of packets. This definition is inherited from the
@@ -88,31 +97,16 @@
   // (e.g. (2k,2m) vs (k,m)) are generally more effective at recovering losses.
   bool MinimumMediaPacketsReached() const;
 
-  const FecProtectionParams& CurrentParams() const;
-
   void ResetState();
 
-  const int red_payload_type_;
-  const int ulpfec_payload_type_;
-  Clock* const clock_;
-
-  rtc::RaceChecker race_checker_;
-  const std::unique_ptr<ForwardErrorCorrection> fec_
-      RTC_GUARDED_BY(race_checker_);
-  ForwardErrorCorrection::PacketList media_packets_
-      RTC_GUARDED_BY(race_checker_);
-  absl::optional<RtpPacketToSend> last_media_packet_
-      RTC_GUARDED_BY(race_checker_);
-  std::list<ForwardErrorCorrection::Packet*> generated_fec_packets_
-      RTC_GUARDED_BY(race_checker_);
-  int num_protected_frames_ RTC_GUARDED_BY(race_checker_);
-  int min_num_media_packets_ RTC_GUARDED_BY(race_checker_);
-  Params current_params_ RTC_GUARDED_BY(race_checker_);
-  bool keyframe_in_process_ RTC_GUARDED_BY(race_checker_);
-
-  rtc::CriticalSection crit_;
-  absl::optional<Params> pending_params_ RTC_GUARDED_BY(crit_);
-  RateStatistics fec_bitrate_ RTC_GUARDED_BY(crit_);
+  std::unique_ptr<ForwardErrorCorrection> fec_;
+  ForwardErrorCorrection::PacketList media_packets_;
+  size_t last_media_packet_rtp_header_length_;
+  std::list<ForwardErrorCorrection::Packet*> generated_fec_packets_;
+  int num_protected_frames_;
+  int min_num_media_packets_;
+  FecProtectionParams params_;
+  FecProtectionParams new_params_;
 };
 
 }  // namespace webrtc
diff --git a/modules/rtp_rtcp/source/ulpfec_generator_unittest.cc b/modules/rtp_rtcp/source/ulpfec_generator_unittest.cc
index db005dd..8c1c7ea 100644
--- a/modules/rtp_rtcp/source/ulpfec_generator_unittest.cc
+++ b/modules/rtp_rtcp/source/ulpfec_generator_unittest.cc
@@ -35,8 +35,11 @@
                   uint32_t timestamp,
                   int red_payload_type,
                   int fec_payload_type,
-                  bool marker_bit,
-                  const rtc::CopyOnWriteBuffer& data) {
+                  RedPacket* packet,
+                  bool marker_bit) {
+  EXPECT_GT(packet->length(), kRtpHeaderSize);
+  EXPECT_TRUE(packet->data() != NULL);
+  uint8_t* data = packet->data();
   // Marker bit not set.
   EXPECT_EQ(marker_bit ? 0x80 : 0, data[1] & 0x80);
   EXPECT_EQ(red_payload_type, data[1] & 0x7F);
@@ -49,12 +52,8 @@
 
 class UlpfecGeneratorTest : public ::testing::Test {
  protected:
-  UlpfecGeneratorTest()
-      : fake_clock_(1),
-        ulpfec_generator_(kRedPayloadType, kFecPayloadType, &fake_clock_),
-        packet_generator_(kMediaSsrc) {}
+  UlpfecGeneratorTest() : packet_generator_(kMediaSsrc) {}
 
-  SimulatedClock fake_clock_;
   UlpfecGenerator ulpfec_generator_;
   AugmentedPacketGenerator packet_generator_;
 };
@@ -82,22 +81,24 @@
   protected_packets.push_back({21, 0, 55, 0});
   protected_packets.push_back({13, 3, 57, 1});
   FecProtectionParams params = {117, 3, kFecMaskBursty};
-  ulpfec_generator_.SetProtectionParameters(params, params);
+  ulpfec_generator_.SetFecParameters(params);
+  uint8_t packet[28] = {0};
   for (Packet p : protected_packets) {
-    RtpPacketToSend packet(nullptr);
-    packet.SetMarker(p.marker_bit);
-    packet.AllocateExtension(RTPExtensionType::kRtpExtensionMid,
-                             p.header_size - packet.headers_size());
-    packet.SetSequenceNumber(p.seq_num);
-    packet.AllocatePayload(p.payload_size);
-    ulpfec_generator_.AddPacketAndGenerateFec(packet);
-
-    std::vector<std::unique_ptr<RtpPacketToSend>> fec_packets =
-        ulpfec_generator_.GetFecPackets();
-    if (!p.marker_bit) {
-      EXPECT_TRUE(fec_packets.empty());
+    if (p.marker_bit) {
+      packet[1] |= 0x80;
     } else {
-      EXPECT_FALSE(fec_packets.empty());
+      packet[1] &= ~0x80;
+    }
+    ByteWriter<uint16_t>::WriteBigEndian(&packet[2], p.seq_num);
+    ulpfec_generator_.AddRtpPacketAndGenerateFec(
+        rtc::CopyOnWriteBuffer(packet, p.payload_size + p.header_size),
+        p.header_size);
+    size_t num_fec_packets = ulpfec_generator_.NumAvailableFecPackets();
+    if (num_fec_packets > 0) {
+      std::vector<std::unique_ptr<RedPacket>> fec_packets =
+          ulpfec_generator_.GetUlpfecPacketsAsRed(kRedPayloadType,
+                                                  kFecPayloadType, 100);
+      EXPECT_EQ(num_fec_packets, fec_packets.size());
     }
   }
 }
@@ -112,28 +113,24 @@
   constexpr size_t kNumPackets = 4;
   FecProtectionParams params = {15, 3, kFecMaskRandom};
   packet_generator_.NewFrame(kNumPackets);
-  // Expecting one FEC packet.
-  ulpfec_generator_.SetProtectionParameters(params, params);
+  ulpfec_generator_.SetFecParameters(params);  // Expecting one FEC packet.
   uint32_t last_timestamp = 0;
   for (size_t i = 0; i < kNumPackets; ++i) {
     std::unique_ptr<AugmentedPacket> packet =
         packet_generator_.NextPacket(i, 10);
-    RtpPacketToSend rtp_packet(nullptr);
-    EXPECT_TRUE(rtp_packet.Parse(packet->data.data(), packet->data.size()));
-    ulpfec_generator_.AddPacketAndGenerateFec(rtp_packet);
+    EXPECT_EQ(0, ulpfec_generator_.AddRtpPacketAndGenerateFec(packet->data,
+                                                              kRtpHeaderSize));
     last_timestamp = packet->header.timestamp;
   }
-  std::vector<std::unique_ptr<RtpPacketToSend>> fec_packets =
-      ulpfec_generator_.GetFecPackets();
-  EXPECT_EQ(fec_packets.size(), 1u);
-  uint16_t seq_num = packet_generator_.NextPacketSeqNum();
-  fec_packets[0]->SetSequenceNumber(seq_num);
-  EXPECT_TRUE(ulpfec_generator_.GetFecPackets().empty());
-
-  EXPECT_EQ(fec_packets[0]->headers_size(), kRtpHeaderSize);
-
-  VerifyHeader(seq_num, last_timestamp, kRedPayloadType, kFecPayloadType, false,
-               fec_packets[0]->Buffer());
+  EXPECT_TRUE(ulpfec_generator_.FecAvailable());
+  const uint16_t seq_num = packet_generator_.NextPacketSeqNum();
+  std::vector<std::unique_ptr<RedPacket>> red_packets =
+      ulpfec_generator_.GetUlpfecPacketsAsRed(kRedPayloadType, kFecPayloadType,
+                                              seq_num);
+  EXPECT_FALSE(ulpfec_generator_.FecAvailable());
+  ASSERT_EQ(1u, red_packets.size());
+  VerifyHeader(seq_num, last_timestamp, kRedPayloadType, kFecPayloadType,
+               red_packets.front().get(), false);
 }
 
 TEST_F(UlpfecGeneratorTest, TwoFrameFec) {
@@ -148,27 +145,27 @@
   constexpr size_t kNumFrames = 2;
 
   FecProtectionParams params = {15, 3, kFecMaskRandom};
-  // Expecting one FEC packet.
-  ulpfec_generator_.SetProtectionParameters(params, params);
+  ulpfec_generator_.SetFecParameters(params);  // Expecting one FEC packet.
   uint32_t last_timestamp = 0;
   for (size_t i = 0; i < kNumFrames; ++i) {
     packet_generator_.NewFrame(kNumPackets);
     for (size_t j = 0; j < kNumPackets; ++j) {
       std::unique_ptr<AugmentedPacket> packet =
           packet_generator_.NextPacket(i * kNumPackets + j, 10);
-      RtpPacketToSend rtp_packet(nullptr);
-      EXPECT_TRUE(rtp_packet.Parse(packet->data.data(), packet->data.size()));
-      ulpfec_generator_.AddPacketAndGenerateFec(rtp_packet);
+      EXPECT_EQ(0, ulpfec_generator_.AddRtpPacketAndGenerateFec(
+                       packet->data, kRtpHeaderSize));
       last_timestamp = packet->header.timestamp;
     }
   }
-  std::vector<std::unique_ptr<RtpPacketToSend>> fec_packets =
-      ulpfec_generator_.GetFecPackets();
-  EXPECT_EQ(fec_packets.size(), 1u);
+  EXPECT_TRUE(ulpfec_generator_.FecAvailable());
   const uint16_t seq_num = packet_generator_.NextPacketSeqNum();
-  fec_packets[0]->SetSequenceNumber(seq_num);
-  VerifyHeader(seq_num, last_timestamp, kRedPayloadType, kFecPayloadType, false,
-               fec_packets[0]->Buffer());
+  std::vector<std::unique_ptr<RedPacket>> red_packets =
+      ulpfec_generator_.GetUlpfecPacketsAsRed(kRedPayloadType, kFecPayloadType,
+                                              seq_num);
+  EXPECT_FALSE(ulpfec_generator_.FecAvailable());
+  ASSERT_EQ(1u, red_packets.size());
+  VerifyHeader(seq_num, last_timestamp, kRedPayloadType, kFecPayloadType,
+               red_packets.front().get(), false);
 }
 
 TEST_F(UlpfecGeneratorTest, MixedMediaRtpHeaderLengths) {
@@ -177,43 +174,34 @@
 
   // Only one frame required to generate FEC.
   FecProtectionParams params = {127, 1, kFecMaskRandom};
-  ulpfec_generator_.SetProtectionParameters(params, params);
+  ulpfec_generator_.SetFecParameters(params);
 
   // Fill up internal buffer with media packets with short RTP header length.
   packet_generator_.NewFrame(kUlpfecMaxMediaPackets + 1);
   for (size_t i = 0; i < kUlpfecMaxMediaPackets; ++i) {
     std::unique_ptr<AugmentedPacket> packet =
         packet_generator_.NextPacket(i, 10);
-    RtpPacketToSend rtp_packet(nullptr);
-    EXPECT_TRUE(rtp_packet.Parse(packet->data.data(), packet->data.size()));
-    EXPECT_EQ(rtp_packet.headers_size(), kShortRtpHeaderLength);
-    ulpfec_generator_.AddPacketAndGenerateFec(rtp_packet);
-    EXPECT_TRUE(ulpfec_generator_.GetFecPackets().empty());
+    EXPECT_EQ(0, ulpfec_generator_.AddRtpPacketAndGenerateFec(
+                     packet->data, kShortRtpHeaderLength));
+    EXPECT_FALSE(ulpfec_generator_.FecAvailable());
   }
 
   // Kick off FEC generation with media packet with long RTP header length.
   // Since the internal buffer is full, this packet will not be protected.
   std::unique_ptr<AugmentedPacket> packet =
       packet_generator_.NextPacket(kUlpfecMaxMediaPackets, 10);
-  RtpPacketToSend rtp_packet(nullptr);
-  EXPECT_TRUE(rtp_packet.Parse(packet->data.data(), packet->data.size()));
-  EXPECT_TRUE(rtp_packet.SetPayloadSize(0) != nullptr);
-  const uint32_t csrcs[]{1};
-  rtp_packet.SetCsrcs(csrcs);
-
-  EXPECT_EQ(rtp_packet.headers_size(), kLongRtpHeaderLength);
-
-  ulpfec_generator_.AddPacketAndGenerateFec(rtp_packet);
-  std::vector<std::unique_ptr<RtpPacketToSend>> fec_packets =
-      ulpfec_generator_.GetFecPackets();
-  EXPECT_FALSE(fec_packets.empty());
+  EXPECT_EQ(0, ulpfec_generator_.AddRtpPacketAndGenerateFec(
+                   packet->data, kLongRtpHeaderLength));
+  EXPECT_TRUE(ulpfec_generator_.FecAvailable());
 
   // Ensure that the RED header is placed correctly, i.e. the correct
   // RTP header length was used in the RED packet creation.
-  uint16_t seq_num = packet_generator_.NextPacketSeqNum();
-  for (const auto& fec_packet : fec_packets) {
-    fec_packet->SetSequenceNumber(seq_num++);
-    EXPECT_EQ(kFecPayloadType, fec_packet->data()[kShortRtpHeaderLength]);
+  const uint16_t seq_num = packet_generator_.NextPacketSeqNum();
+  std::vector<std::unique_ptr<RedPacket>> red_packets =
+      ulpfec_generator_.GetUlpfecPacketsAsRed(kRedPayloadType, kFecPayloadType,
+                                              seq_num);
+  for (const auto& red_packet : red_packets) {
+    EXPECT_EQ(kFecPayloadType, red_packet->data()[kShortRtpHeaderLength]);
   }
 }
 
diff --git a/modules/rtp_rtcp/source/video_fec_generator.h b/modules/rtp_rtcp/source/video_fec_generator.h
deleted file mode 100644
index 3731449..0000000
--- a/modules/rtp_rtcp/source/video_fec_generator.h
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- *  Copyright (c) 2019 The WebRTC project authors. All Rights Reserved.
- *
- *  Use of this source code is governed by a BSD-style license
- *  that can be found in the LICENSE file in the root of the source
- *  tree. An additional intellectual property rights grant can be found
- *  in the file PATENTS.  All contributing project authors may
- *  be found in the AUTHORS file in the root of the source tree.
- */
-
-#ifndef MODULES_RTP_RTCP_SOURCE_VIDEO_FEC_GENERATOR_H_
-#define MODULES_RTP_RTCP_SOURCE_VIDEO_FEC_GENERATOR_H_
-
-#include <memory>
-#include <vector>
-
-#include "api/units/data_rate.h"
-#include "modules/include/module_fec_types.h"
-#include "modules/rtp_rtcp/source/rtp_packet_to_send.h"
-
-namespace webrtc {
-
-class VideoFecGenerator {
- public:
-  VideoFecGenerator() = default;
-  virtual ~VideoFecGenerator() = default;
-
-  enum class FecType { kFlexFec, kUlpFec };
-  virtual FecType GetFecType() const = 0;
-  // Returns the SSRC used for FEC packets (i.e. FlexFec SSRC).
-  virtual absl::optional<uint32_t> FecSsrc() = 0;
-  // Returns the overhead, in bytes per packet, for FEC (and possibly RED).
-  virtual size_t MaxPacketOverhead() const = 0;
-  // Current rate of FEC packets generated, including all RTP-level headers.
-  virtual DataRate CurrentFecRate() const = 0;
-  // Set FEC rates, max frames before FEC is sent, and type of FEC masks.
-  virtual void SetProtectionParameters(
-      const FecProtectionParams& delta_params,
-      const FecProtectionParams& key_params) = 0;
-  // Called on new media packet to be protected. The generator may choose
-  // to generate FEC packets at this time, if so they will be stored in an
-  // internal buffer.
-  virtual void AddPacketAndGenerateFec(const RtpPacketToSend& packet) = 0;
-  // Get (and remove) and FEC packets pending in the generator. These packets
-  // will lack sequence numbers, that needs to be set externally.
-  // TODO(bugs.webrtc.org/11340): Actually FlexFec sets seq#, fix that!
-  virtual std::vector<std::unique_ptr<RtpPacketToSend>> GetFecPackets() = 0;
-};
-
-}  // namespace webrtc
-#endif  // MODULES_RTP_RTCP_SOURCE_VIDEO_FEC_GENERATOR_H_
diff --git a/test/fuzzers/BUILD.gn b/test/fuzzers/BUILD.gn
index 6dd8173..a15e5f0 100644
--- a/test/fuzzers/BUILD.gn
+++ b/test/fuzzers/BUILD.gn
@@ -148,7 +148,6 @@
     "../../modules/rtp_rtcp:rtp_rtcp_format",
     "../../rtc_base:checks",
     "../../rtc_base:rtc_base_approved",
-    "../../system_wrappers",
   ]
 }
 
diff --git a/test/fuzzers/flexfec_sender_fuzzer.cc b/test/fuzzers/flexfec_sender_fuzzer.cc
index 8ddd1c0..4882f7d 100644
--- a/test/fuzzers/flexfec_sender_fuzzer.cc
+++ b/test/fuzzers/flexfec_sender_fuzzer.cc
@@ -41,7 +41,7 @@
   FecProtectionParams params = {
       data[i++], static_cast<int>(data[i++] % 100),
       data[i++] <= 127 ? kFecMaskRandom : kFecMaskBursty};
-  sender.SetProtectionParameters(params, params);
+  sender.SetFecParameters(params);
   uint16_t seq_num = data[i++];
 
   while (i + 1 < size) {
@@ -59,8 +59,11 @@
     RtpPacketToSend rtp_packet(nullptr);
     if (!rtp_packet.Parse(packet.get(), kRtpHeaderSize + payload_size))
       break;
-    sender.AddPacketAndGenerateFec(rtp_packet);
-    sender.GetFecPackets();
+    sender.AddRtpPacketAndGenerateFec(rtp_packet);
+    if (sender.FecAvailable()) {
+      std::vector<std::unique_ptr<RtpPacketToSend>> fec_packets =
+          sender.GetFecPackets();
+    }
   }
 }
 
diff --git a/test/fuzzers/ulpfec_generator_fuzzer.cc b/test/fuzzers/ulpfec_generator_fuzzer.cc
index bcc801a..306f7a0 100644
--- a/test/fuzzers/ulpfec_generator_fuzzer.cc
+++ b/test/fuzzers/ulpfec_generator_fuzzer.cc
@@ -16,7 +16,6 @@
 #include "modules/rtp_rtcp/source/ulpfec_generator.h"
 #include "rtc_base/checks.h"
 #include "rtc_base/copy_on_write_buffer.h"
-#include "system_wrappers/include/clock.h"
 
 namespace webrtc {
 
@@ -26,14 +25,13 @@
 }  // namespace
 
 void FuzzOneInput(const uint8_t* data, size_t size) {
-  SimulatedClock clock(1);
-  UlpfecGenerator generator(kRedPayloadType, kFecPayloadType, &clock);
+  UlpfecGenerator generator;
   size_t i = 0;
   if (size < 4)
     return;
   FecProtectionParams params = {
       data[i++] % 128, static_cast<int>(data[i++] % 10), kFecMaskBursty};
-  generator.SetProtectionParameters(params, params);
+  generator.SetFecParameters(params);
   uint16_t seq_num = data[i++];
   uint16_t prev_seq_num = 0;
   while (i + 3 < size) {
@@ -53,13 +51,16 @@
     // number became out of order.
     if (protect && IsNewerSequenceNumber(seq_num, prev_seq_num) &&
         seq_num < prev_seq_num + kUlpfecMaxMediaPackets) {
-      RtpPacketToSend rtp_packet(nullptr);
-      rtp_packet.Parse(data, rtp_header_length + payload_size);
-      generator.AddPacketAndGenerateFec(rtp_packet);
+      generator.AddRtpPacketAndGenerateFec(packet, rtp_header_length);
       prev_seq_num = seq_num;
     }
-
-    generator.GetFecPackets();
+    const size_t num_fec_packets = generator.NumAvailableFecPackets();
+    if (num_fec_packets > 0) {
+      std::vector<std::unique_ptr<RedPacket>> fec_packets =
+          generator.GetUlpfecPacketsAsRed(kRedPayloadType, kFecPayloadType,
+                                          100);
+      RTC_CHECK_EQ(num_fec_packets, fec_packets.size());
+    }
   }
 }
 }  // namespace webrtc