Change ForwardErrorCorrection class to accept one received packet at a time.

BUG=None

Review-Url: https://codereview.webrtc.org/3012243002
Cr-Commit-Position: refs/heads/master@{#19893}
diff --git a/modules/rtp_rtcp/include/flexfec_receiver.h b/modules/rtp_rtcp/include/flexfec_receiver.h
index 7355262..024d691 100644
--- a/modules/rtp_rtcp/include/flexfec_receiver.h
+++ b/modules/rtp_rtcp/include/flexfec_receiver.h
@@ -39,8 +39,10 @@
 
   // Protected to aid testing.
  protected:
-  bool AddReceivedPacket(const RtpPacketReceived& packet);
-  bool ProcessReceivedPackets();
+  std::unique_ptr<ForwardErrorCorrection::ReceivedPacket> AddReceivedPacket(
+      const RtpPacketReceived& packet);
+  void ProcessReceivedPacket(
+      const ForwardErrorCorrection::ReceivedPacket& received_packet);
 
  private:
   // Config.
@@ -50,8 +52,6 @@
   // Erasure code interfacing and callback.
   std::unique_ptr<ForwardErrorCorrection> erasure_code_
       RTC_GUARDED_BY(sequence_checker_);
-  ForwardErrorCorrection::ReceivedPacketList received_packets_
-      RTC_GUARDED_BY(sequence_checker_);
   ForwardErrorCorrection::RecoveredPacketList recovered_packets_
       RTC_GUARDED_BY(sequence_checker_);
   RecoveredPacketReceiver* const recovered_packet_receiver_;
diff --git a/modules/rtp_rtcp/source/flexfec_receiver.cc b/modules/rtp_rtcp/source/flexfec_receiver.cc
index 5a625d8..ec35a2c0 100644
--- a/modules/rtp_rtcp/source/flexfec_receiver.cc
+++ b/modules/rtp_rtcp/source/flexfec_receiver.cc
@@ -48,10 +48,11 @@
 
 void FlexfecReceiver::OnRtpPacket(const RtpPacketReceived& packet) {
   RTC_DCHECK_CALLED_SEQUENTIALLY(&sequence_checker_);
-  if (!AddReceivedPacket(packet)) {
+  std::unique_ptr<ReceivedPacket> received_packet = AddReceivedPacket(packet);
+  if (!received_packet)
     return;
-  }
-  ProcessReceivedPackets();
+
+  ProcessReceivedPacket(*received_packet);
 }
 
 FecPacketCounter FlexfecReceiver::GetPacketCounter() const {
@@ -61,7 +62,8 @@
 
 // TODO(eladalon): Consider using packet.recovered() to avoid processing
 // recovered packets here.
-bool FlexfecReceiver::AddReceivedPacket(const RtpPacketReceived& packet) {
+std::unique_ptr<ReceivedPacket> FlexfecReceiver::AddReceivedPacket(
+    const RtpPacketReceived& packet) {
   RTC_DCHECK_CALLED_SEQUENTIALLY(&sequence_checker_);
 
   // RTP packets with a full base header (12 bytes), but without payload,
@@ -77,7 +79,7 @@
     // This is a FlexFEC packet.
     if (packet.payload_size() < kMinFlexfecHeaderSize) {
       LOG(LS_WARNING) << "Truncated FlexFEC packet, discarding.";
-      return false;
+      return nullptr;
     }
     received_packet->is_fec = true;
     ++packet_counter_.num_fec_packets;
@@ -93,7 +95,7 @@
     // This is a media packet, or a FlexFEC packet belonging to some
     // other FlexFEC stream.
     if (received_packet->ssrc != protected_media_ssrc_) {
-      return false;
+      return nullptr;
     }
     received_packet->is_fec = false;
 
@@ -104,10 +106,9 @@
     received_packet->pkt->length = packet.size();
   }
 
-  received_packets_.push_back(std::move(received_packet));
   ++packet_counter_.num_packets;
 
-  return true;
+  return received_packet;
 }
 
 // Note that the implementation of this member function and the implementation
@@ -119,16 +120,13 @@
 // Here, however, the received media pipeline is more decoupled from the
 // FlexFEC decoder, and we therefore do not interfere with the reception
 // of non-recovered media packets.
-bool FlexfecReceiver::ProcessReceivedPackets() {
+void FlexfecReceiver::ProcessReceivedPacket(
+    const ReceivedPacket& received_packet) {
   RTC_DCHECK_CALLED_SEQUENTIALLY(&sequence_checker_);
 
   // Decode.
-  if (!received_packets_.empty()) {
-    if (erasure_code_->DecodeFec(&received_packets_, &recovered_packets_) !=
-        0) {
-      return false;
-    }
-  }
+  erasure_code_->DecodeFec(received_packet, &recovered_packets_);
+
   // Return recovered packets through callback.
   for (const auto& recovered_packet : recovered_packets_) {
     if (recovered_packet->returned) {
@@ -150,7 +148,6 @@
       last_recovered_packet_ms_ = now_ms;
     }
   }
-  return true;
 }
 
 }  // namespace webrtc
diff --git a/modules/rtp_rtcp/source/flexfec_receiver_unittest.cc b/modules/rtp_rtcp/source/flexfec_receiver_unittest.cc
index ae2ec1d..1761e1d 100644
--- a/modules/rtp_rtcp/source/flexfec_receiver_unittest.cc
+++ b/modules/rtp_rtcp/source/flexfec_receiver_unittest.cc
@@ -54,7 +54,7 @@
   }
   // Expose methods for tests.
   using FlexfecReceiver::AddReceivedPacket;
-  using FlexfecReceiver::ProcessReceivedPackets;
+  using FlexfecReceiver::ProcessReceivedPacket;
 };
 
 class FlexfecReceiverTest : public ::testing::Test {
@@ -113,8 +113,10 @@
   std::unique_ptr<Packet> media_packet(
       packet_generator_.NextPacket(0, kPayloadLength));
 
-  EXPECT_TRUE(receiver_.AddReceivedPacket(ParsePacket(*media_packet)));
-  EXPECT_TRUE(receiver_.ProcessReceivedPackets());
+  std::unique_ptr<ForwardErrorCorrection::ReceivedPacket> received_packet =
+      receiver_.AddReceivedPacket(ParsePacket(*media_packet));
+  ASSERT_TRUE(received_packet);
+  receiver_.ProcessReceivedPacket(*received_packet);
 }
 
 TEST_F(FlexfecReceiverTest, ReceivesMediaAndFecPackets) {
@@ -127,10 +129,13 @@
   const auto& media_packet = media_packets.front();
   auto fec_packet = packet_generator_.BuildFlexfecPacket(*fec_packets.front());
 
-  EXPECT_TRUE(receiver_.AddReceivedPacket(ParsePacket(*media_packet)));
-  EXPECT_TRUE(receiver_.ProcessReceivedPackets());
-  EXPECT_TRUE(receiver_.AddReceivedPacket(ParsePacket(*fec_packet)));
-  EXPECT_TRUE(receiver_.ProcessReceivedPackets());
+  std::unique_ptr<ForwardErrorCorrection::ReceivedPacket> received_packet =
+      receiver_.AddReceivedPacket(ParsePacket(*media_packet));
+  ASSERT_TRUE(received_packet);
+  receiver_.ProcessReceivedPacket(*received_packet);
+  received_packet = receiver_.AddReceivedPacket(ParsePacket(*fec_packet));
+  ASSERT_TRUE(received_packet);
+  receiver_.ProcessReceivedPacket(*received_packet);
 }
 
 TEST_F(FlexfecReceiverTest, FailsOnTruncatedFecPacket) {
@@ -145,8 +150,10 @@
   fec_packets.front()->length = 1;
   auto fec_packet = packet_generator_.BuildFlexfecPacket(*fec_packets.front());
 
-  EXPECT_TRUE(receiver_.AddReceivedPacket(ParsePacket(*media_packet)));
-  EXPECT_TRUE(receiver_.ProcessReceivedPackets());
+  std::unique_ptr<ForwardErrorCorrection::ReceivedPacket> received_packet =
+      receiver_.AddReceivedPacket(ParsePacket(*media_packet));
+  ASSERT_TRUE(received_packet);
+  receiver_.ProcessReceivedPacket(*received_packet);
   EXPECT_FALSE(receiver_.AddReceivedPacket(ParsePacket(*fec_packet)));
 }
 
@@ -180,8 +187,10 @@
   fec_packet->data[10] = 6;
   fec_packet->data[11] = 7;
 
-  EXPECT_TRUE(receiver_.AddReceivedPacket(ParsePacket(*media_packet)));
-  EXPECT_TRUE(receiver_.ProcessReceivedPackets());
+  std::unique_ptr<ForwardErrorCorrection::ReceivedPacket> received_packet =
+      receiver_.AddReceivedPacket(ParsePacket(*media_packet));
+  ASSERT_TRUE(received_packet);
+  receiver_.ProcessReceivedPacket(*received_packet);
   EXPECT_FALSE(receiver_.AddReceivedPacket(ParsePacket(*fec_packet)));
 }
 
@@ -195,17 +204,20 @@
 
   // Receive all media packets.
   for (const auto& media_packet : media_packets) {
-    EXPECT_TRUE(receiver_.AddReceivedPacket(ParsePacket(*media_packet)));
-    EXPECT_TRUE(receiver_.ProcessReceivedPackets());
+    std::unique_ptr<ForwardErrorCorrection::ReceivedPacket> received_packet =
+        receiver_.AddReceivedPacket(ParsePacket(*media_packet));
+    ASSERT_TRUE(received_packet);
+    receiver_.ProcessReceivedPacket(*received_packet);
   }
 
   // Receive FEC packet.
   auto fec_packet = fec_packets.front();
   std::unique_ptr<Packet> packet_with_rtp_header =
       packet_generator_.BuildFlexfecPacket(*fec_packet);
-  EXPECT_TRUE(
-      receiver_.AddReceivedPacket(ParsePacket(*packet_with_rtp_header)));
-  EXPECT_TRUE(receiver_.ProcessReceivedPackets());
+  std::unique_ptr<ForwardErrorCorrection::ReceivedPacket> received_packet =
+      receiver_.AddReceivedPacket(ParsePacket(*packet_with_rtp_header));
+  ASSERT_TRUE(received_packet);
+  receiver_.ProcessReceivedPacket(*received_packet);
 }
 
 TEST_F(FlexfecReceiverTest, RecoversFromSingleMediaLoss) {
diff --git a/modules/rtp_rtcp/source/forward_error_correction.cc b/modules/rtp_rtcp/source/forward_error_correction.cc
index fe348b5..3c1b1b4 100644
--- a/modules/rtp_rtcp/source/forward_error_correction.cc
+++ b/modules/rtp_rtcp/source/forward_error_correction.cc
@@ -342,16 +342,14 @@
 
 void ForwardErrorCorrection::InsertMediaPacket(
     RecoveredPacketList* recovered_packets,
-    ReceivedPacket* received_packet) {
-  RTC_DCHECK_EQ(received_packet->ssrc, protected_media_ssrc_);
+    const ReceivedPacket& received_packet) {
+  RTC_DCHECK_EQ(received_packet.ssrc, protected_media_ssrc_);
 
   // Search for duplicate packets.
   for (const auto& recovered_packet : *recovered_packets) {
-    RTC_DCHECK_EQ(recovered_packet->ssrc, received_packet->ssrc);
-    if (recovered_packet->seq_num == received_packet->seq_num) {
+    RTC_DCHECK_EQ(recovered_packet->ssrc, received_packet.ssrc);
+    if (recovered_packet->seq_num == received_packet.seq_num) {
       // Duplicate packet, no need to add to list.
-      // Delete duplicate media packet data.
-      received_packet->pkt = nullptr;
       return;
     }
   }
@@ -361,10 +359,10 @@
   recovered_packet->was_recovered = false;
   // This media packet has already been passed on.
   recovered_packet->returned = true;
-  recovered_packet->ssrc = received_packet->ssrc;
-  recovered_packet->seq_num = received_packet->seq_num;
-  recovered_packet->pkt = received_packet->pkt;
-  recovered_packet->pkt->length = received_packet->pkt->length;
+  recovered_packet->ssrc = received_packet.ssrc;
+  recovered_packet->seq_num = received_packet.seq_num;
+  recovered_packet->pkt = received_packet.pkt;
+  recovered_packet->pkt->length = received_packet.pkt->length;
   // TODO(holmer): Consider replacing this with a binary search for the right
   // position, and then just insert the new packet. Would get rid of the sort.
   RecoveredPacket* recovered_packet_ptr = recovered_packet.get();
@@ -390,23 +388,22 @@
 
 void ForwardErrorCorrection::InsertFecPacket(
     const RecoveredPacketList& recovered_packets,
-    ReceivedPacket* received_packet) {
-  RTC_DCHECK_EQ(received_packet->ssrc, ssrc_);
+    const ReceivedPacket& received_packet) {
+  RTC_DCHECK_EQ(received_packet.ssrc, ssrc_);
 
   // Check for duplicate.
   for (const auto& existing_fec_packet : received_fec_packets_) {
-    RTC_DCHECK_EQ(existing_fec_packet->ssrc, received_packet->ssrc);
-    if (existing_fec_packet->seq_num == received_packet->seq_num) {
-      // Delete duplicate FEC packet data.
-      received_packet->pkt = nullptr;
+    RTC_DCHECK_EQ(existing_fec_packet->ssrc, received_packet.ssrc);
+    if (existing_fec_packet->seq_num == received_packet.seq_num) {
+      // Drop duplicate FEC packet data.
       return;
     }
   }
 
   std::unique_ptr<ReceivedFecPacket> fec_packet(new ReceivedFecPacket());
-  fec_packet->pkt = received_packet->pkt;
-  fec_packet->ssrc = received_packet->ssrc;
-  fec_packet->seq_num = received_packet->seq_num;
+  fec_packet->pkt = received_packet.pkt;
+  fec_packet->ssrc = received_packet.ssrc;
+  fec_packet->seq_num = received_packet.seq_num;
   // Parse ULPFEC/FlexFEC header specific info.
   bool ret = fec_header_reader_->ReadFecHeader(fec_packet.get());
   if (!ret) {
@@ -483,49 +480,46 @@
   }
 }
 
-void ForwardErrorCorrection::InsertPackets(
-    ReceivedPacketList* received_packets,
+void ForwardErrorCorrection::InsertPacket(
+    const ReceivedPacket& received_packet,
     RecoveredPacketList* recovered_packets) {
-  while (!received_packets->empty()) {
-    ReceivedPacket* received_packet = received_packets->front().get();
-
-    // Discard old FEC packets such that the sequence numbers in
-    // |received_fec_packets_| span at most 1/2 of the sequence number space.
-    // This is important for keeping |received_fec_packets_| sorted, and may
-    // also reduce the possibility of incorrect decoding due to sequence number
-    // wrap-around.
-    // TODO(marpan/holmer): We should be able to improve detection/discarding of
-    // old FEC packets based on timestamp information or better sequence number
-    // thresholding (e.g., to distinguish between wrap-around and reordering).
-    if (!received_fec_packets_.empty() &&
-        received_packet->ssrc == received_fec_packets_.front()->ssrc) {
-      // It only makes sense to detect wrap-around when |received_packet|
-      // and |front_received_fec_packet| belong to the same sequence number
-      // space, i.e., the same SSRC. This happens when |received_packet|
-      // is a FEC packet, or if |received_packet| is a media packet and
-      // RED+ULPFEC is used.
-      auto it = received_fec_packets_.begin();
-      while (it != received_fec_packets_.end()) {
-        uint16_t seq_num_diff = abs(static_cast<int>(received_packet->seq_num) -
-                                    static_cast<int>((*it)->seq_num));
-        if (seq_num_diff > 0x3fff) {
-          it = received_fec_packets_.erase(it);
-        } else {
-          // No need to keep iterating, since |received_fec_packets_| is sorted.
-          break;
-        }
+  // Discard old FEC packets such that the sequence numbers in
+  // |received_fec_packets_| span at most 1/2 of the sequence number space.
+  // This is important for keeping |received_fec_packets_| sorted, and may
+  // also reduce the possibility of incorrect decoding due to sequence number
+  // wrap-around.
+  // TODO(marpan/holmer): We should be able to improve detection/discarding of
+  // old FEC packets based on timestamp information or better sequence number
+  // thresholding (e.g., to distinguish between wrap-around and reordering).
+  if (!received_fec_packets_.empty() &&
+      received_packet.ssrc == received_fec_packets_.front()->ssrc) {
+    // It only makes sense to detect wrap-around when |received_packet|
+    // and |front_received_fec_packet| belong to the same sequence number
+    // space, i.e., the same SSRC. This happens when |received_packet|
+    // is a FEC packet, or if |received_packet| is a media packet and
+    // RED+ULPFEC is used.
+    auto it = received_fec_packets_.begin();
+    while (it != received_fec_packets_.end()) {
+      // TODO(nisse): This handling of wraparound appears broken, should be
+      // static_cast<int16_t>(
+      //      received_packet.seq_num - back_recovered_packet->seq_num)
+      uint16_t seq_num_diff = abs(static_cast<int>(received_packet.seq_num) -
+                                  static_cast<int>((*it)->seq_num));
+      if (seq_num_diff > 0x3fff) {
+        it = received_fec_packets_.erase(it);
+      } else {
+        // No need to keep iterating, since |received_fec_packets_| is sorted.
+        break;
       }
     }
-
-    if (received_packet->is_fec) {
-      InsertFecPacket(*recovered_packets, received_packet);
-    } else {
-      InsertMediaPacket(recovered_packets, received_packet);
-    }
-    // Delete the received packet "wrapper".
-    received_packets->pop_front();
   }
-  RTC_DCHECK(received_packets->empty());
+
+  if (received_packet.is_fec) {
+    InsertFecPacket(*recovered_packets, received_packet);
+  } else {
+    InsertMediaPacket(recovered_packets, received_packet);
+  }
+
   DiscardOldRecoveredPackets(recovered_packets);
 }
 
@@ -716,38 +710,35 @@
   return (packet[8] << 24) + (packet[9] << 16) + (packet[10] << 8) + packet[11];
 }
 
-int ForwardErrorCorrection::DecodeFec(
-    ReceivedPacketList* received_packets,
-    RecoveredPacketList* recovered_packets) {
-  RTC_DCHECK(received_packets);
+void ForwardErrorCorrection::DecodeFec(const ReceivedPacket& received_packet,
+                                       RecoveredPacketList* recovered_packets) {
   RTC_DCHECK(recovered_packets);
 
   const size_t max_media_packets = fec_header_reader_->MaxMediaPackets();
   if (recovered_packets->size() == max_media_packets) {
     const RecoveredPacket* back_recovered_packet =
         recovered_packets->back().get();
-    for (const auto& received_packet : *received_packets) {
-      if (received_packet->ssrc == back_recovered_packet->ssrc) {
-        const unsigned int seq_num_diff =
-            abs(static_cast<int>(received_packet->seq_num) -
-                static_cast<int>(back_recovered_packet->seq_num));
-        if (seq_num_diff > max_media_packets) {
-          // A big gap in sequence numbers. The old recovered packets
-          // are now useless, so it's safe to do a reset.
-          LOG(LS_INFO) << "Big gap in media/ULPFEC sequence numbers. No need "
-                          "to keep the old packets in the FEC buffers, thus "
-                          "resetting them.";
-          ResetState(recovered_packets);
-          break;
-        }
+
+    if (received_packet.ssrc == back_recovered_packet->ssrc) {
+      // TODO(nisse): This handling of wraparound appears broken, should be
+      // static_cast<int16_t>(
+      //      received_packet.seq_num - back_recovered_packet->seq_num)
+      const unsigned int seq_num_diff =
+          abs(static_cast<int>(received_packet.seq_num) -
+              static_cast<int>(back_recovered_packet->seq_num));
+      if (seq_num_diff > max_media_packets) {
+        // A big gap in sequence numbers. The old recovered packets
+        // are now useless, so it's safe to do a reset.
+        LOG(LS_INFO) << "Big gap in media/ULPFEC sequence numbers. No need "
+                        "to keep the old packets in the FEC buffers, thus "
+                        "resetting them.";
+        ResetState(recovered_packets);
       }
     }
   }
 
-  InsertPackets(received_packets, recovered_packets);
+  InsertPacket(received_packet, recovered_packets);
   AttemptRecovery(recovered_packets);
-
-  return 0;
 }
 
 size_t ForwardErrorCorrection::MaxPacketOverhead() const {
diff --git a/modules/rtp_rtcp/source/forward_error_correction.h b/modules/rtp_rtcp/source/forward_error_correction.h
index f821f6f..97acb39 100644
--- a/modules/rtp_rtcp/source/forward_error_correction.h
+++ b/modules/rtp_rtcp/source/forward_error_correction.h
@@ -75,9 +75,10 @@
     uint16_t seq_num;
   };
 
-  // The received list parameter of DecodeFec() references structs of this type.
+  // Used for the input to DecodeFec().
   //
-  // TODO(holmer): Refactor into a proper class.
+  // TODO(nisse): Delete class, instead passing |is_fec| and |pkt| as separate
+  // arguments.
   class ReceivedPacket : public SortablePacket {
    public:
     ReceivedPacket();
@@ -141,7 +142,6 @@
   };
 
   using PacketList = std::list<std::unique_ptr<Packet>>;
-  using ReceivedPacketList = std::list<std::unique_ptr<ReceivedPacket>>;
   using RecoveredPacketList = std::list<std::unique_ptr<RecoveredPacket>>;
   using ReceivedFecPacketList = std::list<std::unique_ptr<ReceivedFecPacket>>;
 
@@ -218,10 +218,8 @@
   //                            list will be valid until the next call to
   //                            DecodeFec().
   //
-  // Returns 0 on success, -1 on failure.
-  //
-  int DecodeFec(ReceivedPacketList* received_packets,
-                RecoveredPacketList* recovered_packets);
+  void DecodeFec(const ReceivedPacket& received_packet,
+                 RecoveredPacketList* recovered_packets);
 
   // Get the number of generated FEC packets, given the number of media packets
   // and the protection factor.
@@ -266,14 +264,14 @@
                           uint32_t media_ssrc,
                           uint16_t seq_num_base);
 
-  // Inserts the |received_packets| into the internal received FEC packet list
+  // Inserts the |received_packet| into the internal received FEC packet list
   // or into |recovered_packets|.
-  void InsertPackets(ReceivedPacketList* received_packets,
-                     RecoveredPacketList* recovered_packets);
+  void InsertPacket(const ReceivedPacket& received_packet,
+                    RecoveredPacketList* recovered_packets);
 
   // Inserts the |received_packet| into |recovered_packets|. Deletes duplicates.
   void InsertMediaPacket(RecoveredPacketList* recovered_packets,
-                         ReceivedPacket* received_packet);
+                         const ReceivedPacket& received_packet);
 
   // Assigns pointers to the recovered packet from all FEC packets which cover
   // it.
@@ -284,7 +282,7 @@
 
   // Insert |received_packet| into internal FEC list. Deletes duplicates.
   void InsertFecPacket(const RecoveredPacketList& recovered_packets,
-                       ReceivedPacket* received_packet);
+                       const ReceivedPacket& received_packet);
 
   // Assigns pointers to already recovered packets covered by |fec_packet|.
   static void AssignRecoveredPackets(
diff --git a/modules/rtp_rtcp/source/rtp_fec_unittest.cc b/modules/rtp_rtcp/source/rtp_fec_unittest.cc
index 5f84af5..d01aa55 100644
--- a/modules/rtp_rtcp/source/rtp_fec_unittest.cc
+++ b/modules/rtp_rtcp/source/rtp_fec_unittest.cc
@@ -86,7 +86,8 @@
 
   ForwardErrorCorrection::PacketList media_packets_;
   std::list<ForwardErrorCorrection::Packet*> generated_fec_packets_;
-  ForwardErrorCorrection::ReceivedPacketList received_packets_;
+  std::vector<std::unique_ptr<ForwardErrorCorrection::ReceivedPacket>>
+      received_packets_;
   ForwardErrorCorrection::RecoveredPacketList recovered_packets_;
 
   int media_loss_mask_[kUlpfecMaxMediaPackets];
@@ -98,6 +99,7 @@
     int* media_loss_mask,
     int* fec_loss_mask) {
   constexpr bool kFecPacket = true;
+  this->received_packets_.clear();
   ReceivedPackets(media_packets_, media_loss_mask, !kFecPacket);
   ReceivedPackets(generated_fec_packets_, fec_loss_mask, kFecPacket);
 }
@@ -237,8 +239,10 @@
   memset(this->fec_loss_mask_, 0, sizeof(this->fec_loss_mask_));
   this->NetworkReceivedPackets(this->media_loss_mask_, this->fec_loss_mask_);
 
-  EXPECT_EQ(0, this->fec_.DecodeFec(&this->received_packets_,
-                                    &this->recovered_packets_));
+  for (const auto& received_packet : this->received_packets_) {
+    this->fec_.DecodeFec(*received_packet,
+                         &this->recovered_packets_);
+  }
 
   // No packets lost, expect complete recovery.
   EXPECT_TRUE(this->IsRecoveryComplete());
@@ -267,8 +271,10 @@
   this->media_loss_mask_[3] = 1;
   this->NetworkReceivedPackets(this->media_loss_mask_, this->fec_loss_mask_);
 
-  EXPECT_EQ(0, this->fec_.DecodeFec(&this->received_packets_,
-                                    &this->recovered_packets_));
+  for (const auto& received_packet : this->received_packets_) {
+    this->fec_.DecodeFec(*received_packet,
+                         &this->recovered_packets_);
+  }
 
   // One packet lost, one FEC packet, expect complete recovery.
   EXPECT_TRUE(this->IsRecoveryComplete());
@@ -281,8 +287,10 @@
   this->media_loss_mask_[3] = 1;
   this->NetworkReceivedPackets(this->media_loss_mask_, this->fec_loss_mask_);
 
-  EXPECT_EQ(0, this->fec_.DecodeFec(&this->received_packets_,
-                                    &this->recovered_packets_));
+  for (const auto& received_packet : this->received_packets_) {
+    this->fec_.DecodeFec(*received_packet,
+                         &this->recovered_packets_);
+  }
 
   // 2 packets lost, one FEC packet, cannot get complete recovery.
   EXPECT_FALSE(this->IsRecoveryComplete());
@@ -330,8 +338,10 @@
   // Add packets #65535, and #1 to received list.
   this->ReceivedPackets(this->media_packets_, this->media_loss_mask_, false);
 
-  EXPECT_EQ(0, this->fec_.DecodeFec(&this->received_packets_,
-                                    &this->recovered_packets_));
+  for (const auto& received_packet : this->received_packets_) {
+    this->fec_.DecodeFec(*received_packet,
+                         &this->recovered_packets_);
+  }
 
   // Expect that no decoding is done to get missing packet (seq#0) of second
   // frame, using old FEC packet (seq#2) from first (old) frame. So number of
@@ -370,8 +380,10 @@
   this->ReceivedPackets(this->generated_fec_packets_, this->fec_loss_mask_,
                         true);
 
-  EXPECT_EQ(0, this->fec_.DecodeFec(&this->received_packets_,
-                                    &this->recovered_packets_));
+  for (const auto& received_packet : this->received_packets_) {
+    this->fec_.DecodeFec(*received_packet,
+                         &this->recovered_packets_);
+  }
 
   // Expect 3 media packets in recovered list, and complete recovery.
   // Wrap-around won't remove FEC packet, as it follows the wrap.
@@ -380,13 +392,18 @@
 }
 
 // Sequence number wrap occurs within the ULPFEC packets for the frame.
-// In this case we will discard ULPFEC packet and full recovery is not expected.
 // Same problem will occur if wrap is within media packets but ULPFEC packet is
 // received before the media packets. This may be improved if timing information
 // is used to detect old ULPFEC packets.
-// TODO(marpan): Update test if wrap-around handling changes in FEC decoding.
+
+// TODO(nisse): There's some logic to discard ULPFEC packets at wrap-around,
+// however, that is not actually exercised by this test: When the first FEC
+// packet is processed, it results in full recovery of one media packet and the
+// FEC packet is forgotten. And then the wraparound isn't noticed when the next
+// FEC packet is received. We should fix wraparound handling, which currently
+// appears broken, and then figure out how to test it properly.
 using RtpFecTestUlpfecOnly = RtpFecTest<UlpfecForwardErrorCorrection>;
-TEST_F(RtpFecTestUlpfecOnly, FecRecoveryWithSeqNumGapOneFrameNoRecovery) {
+TEST_F(RtpFecTestUlpfecOnly, FecRecoveryWithSeqNumGapOneFrameRecovery) {
   constexpr int kNumImportantPackets = 0;
   constexpr bool kUseUnequalProtection = false;
   constexpr uint8_t kProtectionFactor = 200;
@@ -415,22 +432,28 @@
   this->ReceivedPackets(this->generated_fec_packets_, this->fec_loss_mask_,
                         true);
 
-  EXPECT_EQ(0, this->fec_.DecodeFec(&this->received_packets_,
-                                    &this->recovered_packets_));
+  for (const auto& received_packet : this->received_packets_) {
+    this->fec_.DecodeFec(*received_packet,
+                         &this->recovered_packets_);
+  }
 
   // The two FEC packets are received and should allow for complete recovery,
   // but because of the wrap the first FEC packet will be discarded, and only
   // one media packet is recoverable. So expect 2 media packets on recovered
   // list and no complete recovery.
-  EXPECT_EQ(2u, this->recovered_packets_.size());
-  EXPECT_TRUE(this->recovered_packets_.size() != this->media_packets_.size());
-  EXPECT_FALSE(this->IsRecoveryComplete());
+  EXPECT_EQ(3u, this->recovered_packets_.size());
+  EXPECT_EQ(this->recovered_packets_.size(), this->media_packets_.size());
+  EXPECT_TRUE(this->IsRecoveryComplete());
 }
 
 // TODO(brandtr): This test mimics the one above, ensuring that the recovery
 // strategy of FlexFEC matches the recovery strategy of ULPFEC. Since FlexFEC
 // does not share the sequence number space with the media, however, having a
 // matching recovery strategy may be suboptimal. Study this further.
+// TODO(nisse): In this test, recovery based on the first FEC packet fails with
+// the log message "The recovered packet had a length larger than a typical IP
+// packet, and is thus dropped." This is probably not intended, and needs
+// investigation.
 using RtpFecTestFlexfecOnly = RtpFecTest<FlexfecForwardErrorCorrection>;
 TEST_F(RtpFecTestFlexfecOnly, FecRecoveryWithSeqNumGapOneFrameNoRecovery) {
   constexpr int kNumImportantPackets = 0;
@@ -468,8 +491,10 @@
   this->ReceivedPackets(this->generated_fec_packets_, this->fec_loss_mask_,
                         true);
 
-  EXPECT_EQ(0, this->fec_.DecodeFec(&this->received_packets_,
-                                    &this->recovered_packets_));
+  for (const auto& received_packet : this->received_packets_) {
+    this->fec_.DecodeFec(*received_packet,
+                         &this->recovered_packets_);
+  }
 
   // The two FEC packets are received and should allow for complete recovery,
   // but because of the wrap the first FEC packet will be discarded, and only
@@ -512,8 +537,10 @@
   it1++;
   std::swap(*it0, *it1);
 
-  EXPECT_EQ(0, this->fec_.DecodeFec(&this->received_packets_,
-                                    &this->recovered_packets_));
+  for (const auto& received_packet : this->received_packets_) {
+    this->fec_.DecodeFec(*received_packet,
+                         &this->recovered_packets_);
+  }
 
   // Expect 3 media packets in recovered list, and complete recovery.
   EXPECT_EQ(3u, this->recovered_packets_.size());
@@ -550,8 +577,10 @@
   // Add media packets to received list.
   this->ReceivedPackets(this->media_packets_, this->media_loss_mask_, false);
 
-  EXPECT_EQ(0, this->fec_.DecodeFec(&this->received_packets_,
-                                    &this->recovered_packets_));
+  for (const auto& received_packet : this->received_packets_) {
+    this->fec_.DecodeFec(*received_packet,
+                         &this->recovered_packets_);
+  }
 
   // Expect 3 media packets in recovered list, and complete recovery.
   EXPECT_EQ(3u, this->recovered_packets_.size());
@@ -597,8 +626,10 @@
   this->media_loss_mask_[3] = 1;
   this->NetworkReceivedPackets(this->media_loss_mask_, this->fec_loss_mask_);
 
-  EXPECT_EQ(0, this->fec_.DecodeFec(&this->received_packets_,
-                                    &this->recovered_packets_));
+  for (const auto& received_packet : this->received_packets_) {
+    this->fec_.DecodeFec(*received_packet,
+                         &this->recovered_packets_);
+  }
 
   // With media packet#1 and FEC packets #1, #2, #3, expect complete recovery.
   EXPECT_TRUE(this->IsRecoveryComplete());
@@ -613,8 +644,10 @@
   this->media_loss_mask_[3] = 1;
   this->NetworkReceivedPackets(this->media_loss_mask_, this->fec_loss_mask_);
 
-  EXPECT_EQ(0, this->fec_.DecodeFec(&this->received_packets_,
-                                    &this->recovered_packets_));
+  for (const auto& received_packet : this->received_packets_) {
+    this->fec_.DecodeFec(*received_packet,
+                         &this->recovered_packets_);
+  }
 
   // Cannot get complete recovery for this loss configuration with random mask.
   EXPECT_FALSE(this->IsRecoveryComplete());
@@ -659,8 +692,10 @@
   this->media_loss_mask_[3] = 1;
   this->NetworkReceivedPackets(this->media_loss_mask_, this->fec_loss_mask_);
 
-  EXPECT_EQ(0, this->fec_.DecodeFec(&this->received_packets_,
-                                    &this->recovered_packets_));
+  for (const auto& received_packet : this->received_packets_) {
+    this->fec_.DecodeFec(*received_packet,
+                         &this->recovered_packets_);
+  }
 
   // Expect complete recovery for consecutive packet loss <= 50%.
   EXPECT_TRUE(this->IsRecoveryComplete());
@@ -675,8 +710,10 @@
   this->media_loss_mask_[3] = 1;
   this->NetworkReceivedPackets(this->media_loss_mask_, this->fec_loss_mask_);
 
-  EXPECT_EQ(0, this->fec_.DecodeFec(&this->received_packets_,
-                                    &this->recovered_packets_));
+  for (const auto& received_packet : this->received_packets_) {
+    this->fec_.DecodeFec(*received_packet,
+                         &this->recovered_packets_);
+  }
 
   // Expect complete recovery for consecutive packet loss <= 50%.
   EXPECT_TRUE(this->IsRecoveryComplete());
@@ -691,8 +728,10 @@
   this->media_loss_mask_[3] = 1;
   this->NetworkReceivedPackets(this->media_loss_mask_, this->fec_loss_mask_);
 
-  EXPECT_EQ(0, this->fec_.DecodeFec(&this->received_packets_,
-                                    &this->recovered_packets_));
+  for (const auto& received_packet : this->received_packets_) {
+    this->fec_.DecodeFec(*received_packet,
+                         &this->recovered_packets_);
+  }
 
   // Cannot get complete recovery for this loss configuration.
   EXPECT_FALSE(this->IsRecoveryComplete());
@@ -720,8 +759,10 @@
   memset(this->fec_loss_mask_, 0, sizeof(this->fec_loss_mask_));
   this->NetworkReceivedPackets(this->media_loss_mask_, this->fec_loss_mask_);
 
-  EXPECT_EQ(0, this->fec_.DecodeFec(&this->received_packets_,
-                                    &this->recovered_packets_));
+  for (const auto& received_packet : this->received_packets_) {
+    this->fec_.DecodeFec(*received_packet,
+                         &this->recovered_packets_);
+  }
 
   // No packets lost, expect complete recovery.
   EXPECT_TRUE(this->IsRecoveryComplete());
@@ -750,8 +791,10 @@
   this->media_loss_mask_[3] = 1;
   this->NetworkReceivedPackets(this->media_loss_mask_, this->fec_loss_mask_);
 
-  EXPECT_EQ(0, this->fec_.DecodeFec(&this->received_packets_,
-                                    &this->recovered_packets_));
+  for (const auto& received_packet : this->received_packets_) {
+    this->fec_.DecodeFec(*received_packet,
+                         &this->recovered_packets_);
+  }
 
   // One packet lost, one FEC packet, expect complete recovery.
   EXPECT_TRUE(this->IsRecoveryComplete());
@@ -764,8 +807,10 @@
   this->media_loss_mask_[3] = 1;
   this->NetworkReceivedPackets(this->media_loss_mask_, this->fec_loss_mask_);
 
-  EXPECT_EQ(0, this->fec_.DecodeFec(&this->received_packets_,
-                                    &this->recovered_packets_));
+  for (const auto& received_packet : this->received_packets_) {
+    this->fec_.DecodeFec(*received_packet,
+                         &this->recovered_packets_);
+  }
 
   // 2 packets lost, one FEC packet, cannot get complete recovery.
   EXPECT_FALSE(this->IsRecoveryComplete());
@@ -808,8 +853,10 @@
   this->media_loss_mask_[3] = 1;
   this->NetworkReceivedPackets(this->media_loss_mask_, this->fec_loss_mask_);
 
-  EXPECT_EQ(0, this->fec_.DecodeFec(&this->received_packets_,
-                                    &this->recovered_packets_));
+  for (const auto& received_packet : this->received_packets_) {
+    this->fec_.DecodeFec(*received_packet,
+                         &this->recovered_packets_);
+  }
 
   // With media packet#3 and FEC packets #0, #1, #3, expect complete recovery.
   EXPECT_TRUE(this->IsRecoveryComplete());
@@ -825,8 +872,10 @@
   this->media_loss_mask_[3] = 1;
   this->NetworkReceivedPackets(this->media_loss_mask_, this->fec_loss_mask_);
 
-  EXPECT_EQ(0, this->fec_.DecodeFec(&this->received_packets_,
-                                    &this->recovered_packets_));
+  for (const auto& received_packet : this->received_packets_) {
+    this->fec_.DecodeFec(*received_packet,
+                         &this->recovered_packets_);
+  }
 
   // Cannot get complete recovery for this loss configuration.
   EXPECT_FALSE(this->IsRecoveryComplete());
@@ -860,8 +909,10 @@
   this->media_loss_mask_[2] = 1;
   this->NetworkReceivedPackets(this->media_loss_mask_, this->fec_loss_mask_);
 
-  EXPECT_EQ(0, this->fec_.DecodeFec(&this->received_packets_,
-                                    &this->recovered_packets_));
+  for (const auto& received_packet : this->received_packets_) {
+    this->fec_.DecodeFec(*received_packet,
+                         &this->recovered_packets_);
+  }
 
   // One packet lost, one FEC packet, expect complete recovery.
   EXPECT_TRUE(this->IsRecoveryComplete());
@@ -873,8 +924,10 @@
   this->media_loss_mask_[1] = 1;
   this->NetworkReceivedPackets(this->media_loss_mask_, this->fec_loss_mask_);
 
-  EXPECT_EQ(0, this->fec_.DecodeFec(&this->received_packets_,
-                                    &this->recovered_packets_));
+  for (const auto& received_packet : this->received_packets_) {
+    this->fec_.DecodeFec(*received_packet,
+                         &this->recovered_packets_);
+  }
 
   // Unprotected packet lost. Recovery not possible.
   EXPECT_FALSE(this->IsRecoveryComplete());
@@ -887,8 +940,10 @@
   this->media_loss_mask_[2] = 1;
   this->NetworkReceivedPackets(this->media_loss_mask_, this->fec_loss_mask_);
 
-  EXPECT_EQ(0, this->fec_.DecodeFec(&this->received_packets_,
-                                    &this->recovered_packets_));
+  for (const auto& received_packet : this->received_packets_) {
+    this->fec_.DecodeFec(*received_packet,
+                         &this->recovered_packets_);
+  }
 
   // 2 protected packets lost, one FEC packet, cannot get complete recovery.
   EXPECT_FALSE(this->IsRecoveryComplete());
@@ -925,8 +980,10 @@
   this->media_loss_mask_[kNumMediaPackets - 1] = 1;
   this->NetworkReceivedPackets(this->media_loss_mask_, this->fec_loss_mask_);
 
-  EXPECT_EQ(0, this->fec_.DecodeFec(&this->received_packets_,
-                                    &this->recovered_packets_));
+  for (const auto& received_packet : this->received_packets_) {
+    this->fec_.DecodeFec(*received_packet,
+                         &this->recovered_packets_);
+  }
 
   // One packet lost, one FEC packet, expect complete recovery.
   EXPECT_TRUE(this->IsRecoveryComplete());
@@ -938,8 +995,10 @@
   this->media_loss_mask_[kNumMediaPackets - 2] = 1;
   this->NetworkReceivedPackets(this->media_loss_mask_, this->fec_loss_mask_);
 
-  EXPECT_EQ(0, this->fec_.DecodeFec(&this->received_packets_,
-                                    &this->recovered_packets_));
+  for (const auto& received_packet : this->received_packets_) {
+    this->fec_.DecodeFec(*received_packet,
+                         &this->recovered_packets_);
+  }
 
   // Unprotected packet lost. Recovery not possible.
   EXPECT_FALSE(this->IsRecoveryComplete());
@@ -956,8 +1015,10 @@
   this->media_loss_mask_[kNumMediaPackets - 1] = 1;
   this->NetworkReceivedPackets(this->media_loss_mask_, this->fec_loss_mask_);
 
-  EXPECT_EQ(0, this->fec_.DecodeFec(&this->received_packets_,
-                                    &this->recovered_packets_));
+  for (const auto& received_packet : this->received_packets_) {
+    this->fec_.DecodeFec(*received_packet,
+                         &this->recovered_packets_);
+  }
 
   // 5 protected packets lost, one FEC packet, cannot get complete recovery.
   EXPECT_FALSE(this->IsRecoveryComplete());
@@ -994,8 +1055,10 @@
   this->media_loss_mask_[kNumMediaPackets - 1] = 1;
   this->NetworkReceivedPackets(this->media_loss_mask_, this->fec_loss_mask_);
 
-  EXPECT_EQ(0, this->fec_.DecodeFec(&this->received_packets_,
-                                    &this->recovered_packets_));
+  for (const auto& received_packet : this->received_packets_) {
+    this->fec_.DecodeFec(*received_packet,
+                         &this->recovered_packets_);
+  }
 
   // One packet lost, one FEC packet, expect complete recovery.
   EXPECT_TRUE(this->IsRecoveryComplete());
@@ -1007,8 +1070,10 @@
   this->media_loss_mask_[kNumMediaPackets - 2] = 1;
   this->NetworkReceivedPackets(this->media_loss_mask_, this->fec_loss_mask_);
 
-  EXPECT_EQ(0, this->fec_.DecodeFec(&this->received_packets_,
-                                    &this->recovered_packets_));
+  for (const auto& received_packet : this->received_packets_) {
+    this->fec_.DecodeFec(*received_packet,
+                         &this->recovered_packets_);
+  }
 
   // Unprotected packet lost. Recovery not possible.
   EXPECT_FALSE(this->IsRecoveryComplete());
@@ -1025,8 +1090,10 @@
   this->media_loss_mask_[kNumMediaPackets - 1] = 1;
   this->NetworkReceivedPackets(this->media_loss_mask_, this->fec_loss_mask_);
 
-  EXPECT_EQ(0, this->fec_.DecodeFec(&this->received_packets_,
-                                    &this->recovered_packets_));
+  for (const auto& received_packet : this->received_packets_) {
+    this->fec_.DecodeFec(*received_packet,
+                         &this->recovered_packets_);
+  }
 
   // 5 protected packets lost, one FEC packet, cannot get complete recovery.
   EXPECT_FALSE(this->IsRecoveryComplete());
diff --git a/modules/rtp_rtcp/source/ulpfec_receiver_impl.cc b/modules/rtp_rtcp/source/ulpfec_receiver_impl.cc
index a35393d..4292f3c 100644
--- a/modules/rtp_rtcp/source/ulpfec_receiver_impl.cc
+++ b/modules/rtp_rtcp/source/ulpfec_receiver_impl.cc
@@ -216,23 +216,22 @@
   return 0;
 }
 
+// TODO(nisse): Drop always-zero return value.
 int32_t UlpfecReceiverImpl::ProcessReceivedFec() {
   crit_sect_.Enter();
-  if (!received_packets_.empty()) {
+  for (const auto& received_packet : received_packets_) {
     // Send received media packet to VCM.
-    if (!received_packets_.front()->is_fec) {
-      ForwardErrorCorrection::Packet* packet = received_packets_.front()->pkt;
+    if (!received_packet->is_fec) {
+      ForwardErrorCorrection::Packet* packet = received_packet->pkt;
       crit_sect_.Leave();
       recovered_packet_callback_->OnRecoveredPacket(packet->data,
                                                     packet->length);
       crit_sect_.Enter();
     }
-    if (fec_->DecodeFec(&received_packets_, &recovered_packets_) != 0) {
-      crit_sect_.Leave();
-      return -1;
-    }
-    RTC_DCHECK(received_packets_.empty());
+    fec_->DecodeFec(*received_packet, &recovered_packets_);
   }
+  received_packets_.clear();
+
   // Send any recovered media packets to VCM.
   for (const auto& recovered_packet : recovered_packets_) {
     if (recovered_packet->returned) {
diff --git a/modules/rtp_rtcp/source/ulpfec_receiver_impl.h b/modules/rtp_rtcp/source/ulpfec_receiver_impl.h
index 8025729..edc3d31 100644
--- a/modules/rtp_rtcp/source/ulpfec_receiver_impl.h
+++ b/modules/rtp_rtcp/source/ulpfec_receiver_impl.h
@@ -12,6 +12,7 @@
 #define MODULES_RTP_RTCP_SOURCE_ULPFEC_RECEIVER_IMPL_H_
 
 #include <memory>
+#include <vector>
 
 #include "modules/rtp_rtcp/include/rtp_rtcp_defines.h"
 #include "modules/rtp_rtcp/include/ulpfec_receiver.h"
@@ -41,10 +42,12 @@
   rtc::CriticalSection crit_sect_;
   RecoveredPacketReceiver* recovered_packet_callback_;
   std::unique_ptr<ForwardErrorCorrection> fec_;
-  // TODO(holmer): In the current version |received_packets_| is never more
-  // than one packet, since we process FEC every time a new packet
-  // arrives. We should remove the list.
-  ForwardErrorCorrection::ReceivedPacketList received_packets_;
+  // TODO(nisse): The AddReceivedRedPacket method adds one or two packets to
+  // this list at a time, after which it is emptied by ProcessReceivedFec. It
+  // will make things simpler to merge AddReceivedRedPacket and
+  // ProcessReceivedFec into a single method, and we can then delete this list.
+  std::vector<std::unique_ptr<ForwardErrorCorrection::ReceivedPacket>>
+      received_packets_;
   ForwardErrorCorrection::RecoveredPacketList recovered_packets_;
   FecPacketCounter packet_counter_;
 };
diff --git a/modules/rtp_rtcp/test/testFec/test_fec.cc b/modules/rtp_rtcp/test/testFec/test_fec.cc
index c65a2b5..32dc374 100644
--- a/modules/rtp_rtcp/test/testFec/test_fec.cc
+++ b/modules/rtp_rtcp/test/testFec/test_fec.cc
@@ -35,8 +35,10 @@
 using fec_private_tables::kPacketMaskBurstyTbl;
 
 void ReceivePackets(
-    ForwardErrorCorrection::ReceivedPacketList* to_decode_list,
-    ForwardErrorCorrection::ReceivedPacketList* received_packet_list,
+    std::vector<std::unique_ptr<ForwardErrorCorrection::ReceivedPacket>>*
+        to_decode_list,
+    std::vector<std::unique_ptr<ForwardErrorCorrection::ReceivedPacket>>*
+        received_packet_list,
     size_t num_packets_to_decode,
     float reorder_rate,
     float duplicate_rate,
@@ -103,8 +105,10 @@
 
   ForwardErrorCorrection::PacketList media_packet_list;
   std::list<ForwardErrorCorrection::Packet*> fec_packet_list;
-  ForwardErrorCorrection::ReceivedPacketList to_decode_list;
-  ForwardErrorCorrection::ReceivedPacketList received_packet_list;
+  std::vector<std::unique_ptr<ForwardErrorCorrection::ReceivedPacket>>
+      to_decode_list;
+  std::vector<std::unique_ptr<ForwardErrorCorrection::ReceivedPacket>>
+      received_packet_list;
   ForwardErrorCorrection::RecoveredPacketList recovered_packet_list;
   std::list<uint8_t*> fec_mask_list;
 
@@ -403,11 +407,10 @@
                   }
                 }
               }
-              ASSERT_EQ(0,
-                        fec->DecodeFec(&to_decode_list, &recovered_packet_list))
-                  << "DecodeFec() failed";
-              ASSERT_TRUE(to_decode_list.empty())
-                  << "Received packet list is not empty.";
+              for (const auto& received_packet : to_decode_list) {
+                fec->DecodeFec(*received_packet, &recovered_packet_list);
+              }
+              to_decode_list.clear();
             }
             media_packet_idx = 0;
             for (const auto& media_packet : media_packet_list) {