summaryrefslogtreecommitdiffstats
path: root/third_party/libwebrtc/modules/rtp_rtcp/source/flexfec_receiver_unittest.cc
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--third_party/libwebrtc/modules/rtp_rtcp/source/flexfec_receiver_unittest.cc691
1 files changed, 691 insertions, 0 deletions
diff --git a/third_party/libwebrtc/modules/rtp_rtcp/source/flexfec_receiver_unittest.cc b/third_party/libwebrtc/modules/rtp_rtcp/source/flexfec_receiver_unittest.cc
new file mode 100644
index 0000000000..1243858b6b
--- /dev/null
+++ b/third_party/libwebrtc/modules/rtp_rtcp/source/flexfec_receiver_unittest.cc
@@ -0,0 +1,691 @@
+/*
+ * Copyright (c) 2016 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.
+ */
+
+#include "modules/rtp_rtcp/include/flexfec_receiver.h"
+
+#include <algorithm>
+#include <memory>
+
+#include "modules/rtp_rtcp/mocks/mock_recovered_packet_receiver.h"
+#include "modules/rtp_rtcp/source/fec_test_helper.h"
+#include "modules/rtp_rtcp/source/forward_error_correction.h"
+#include "modules/rtp_rtcp/source/rtp_packet_received.h"
+#include "test/gmock.h"
+#include "test/gtest.h"
+
+namespace webrtc {
+
+namespace {
+
+using ::testing::_;
+using ::testing::Eq;
+using ::testing::Property;
+
+using test::fec::FlexfecPacketGenerator;
+using Packet = ForwardErrorCorrection::Packet;
+using PacketList = ForwardErrorCorrection::PacketList;
+
+constexpr size_t kPayloadLength = 500;
+constexpr uint32_t kFlexfecSsrc = 42984;
+constexpr uint32_t kMediaSsrc = 8353;
+
+RtpPacketReceived ParsePacket(const Packet& packet) {
+ RtpPacketReceived parsed_packet;
+ EXPECT_TRUE(parsed_packet.Parse(packet.data));
+ return parsed_packet;
+}
+
+} // namespace
+
+class FlexfecReceiverForTest : public FlexfecReceiver {
+ public:
+ FlexfecReceiverForTest(uint32_t ssrc,
+ uint32_t protected_media_ssrc,
+ RecoveredPacketReceiver* recovered_packet_receiver)
+ : FlexfecReceiver(Clock::GetRealTimeClock(),
+ ssrc,
+ protected_media_ssrc,
+ recovered_packet_receiver) {}
+ // Expose methods for tests.
+ using FlexfecReceiver::AddReceivedPacket;
+ using FlexfecReceiver::ProcessReceivedPacket;
+};
+
+class FlexfecReceiverTest : public ::testing::Test {
+ protected:
+ FlexfecReceiverTest()
+ : receiver_(kFlexfecSsrc, kMediaSsrc, &recovered_packet_receiver_),
+ erasure_code_(
+ ForwardErrorCorrection::CreateFlexfec(kFlexfecSsrc, kMediaSsrc)),
+ packet_generator_(kMediaSsrc, kFlexfecSsrc) {}
+
+ // Generates `num_media_packets` corresponding to a single frame.
+ void PacketizeFrame(size_t num_media_packets,
+ size_t frame_offset,
+ PacketList* media_packets);
+
+ // Generates `num_fec_packets` FEC packets, given `media_packets`.
+ std::list<Packet*> EncodeFec(const PacketList& media_packets,
+ size_t num_fec_packets);
+
+ FlexfecReceiverForTest receiver_;
+ std::unique_ptr<ForwardErrorCorrection> erasure_code_;
+
+ FlexfecPacketGenerator packet_generator_;
+ ::testing::StrictMock<MockRecoveredPacketReceiver> recovered_packet_receiver_;
+};
+
+void FlexfecReceiverTest::PacketizeFrame(size_t num_media_packets,
+ size_t frame_offset,
+ PacketList* media_packets) {
+ packet_generator_.NewFrame(num_media_packets);
+ for (size_t i = 0; i < num_media_packets; ++i) {
+ std::unique_ptr<Packet> next_packet(
+ packet_generator_.NextPacket(frame_offset + i, kPayloadLength));
+ media_packets->push_back(std::move(next_packet));
+ }
+}
+
+std::list<Packet*> FlexfecReceiverTest::EncodeFec(
+ const PacketList& media_packets,
+ size_t num_fec_packets) {
+ const uint8_t protection_factor =
+ num_fec_packets * 255 / media_packets.size();
+ constexpr int kNumImportantPackets = 0;
+ constexpr bool kUseUnequalProtection = false;
+ constexpr FecMaskType kFecMaskType = kFecMaskRandom;
+ std::list<Packet*> fec_packets;
+ EXPECT_EQ(0, erasure_code_->EncodeFec(
+ media_packets, protection_factor, kNumImportantPackets,
+ kUseUnequalProtection, kFecMaskType, &fec_packets));
+ EXPECT_EQ(num_fec_packets, fec_packets.size());
+ return fec_packets;
+}
+
+TEST_F(FlexfecReceiverTest, ReceivesMediaPacket) {
+ packet_generator_.NewFrame(1);
+ std::unique_ptr<Packet> media_packet(
+ packet_generator_.NextPacket(0, kPayloadLength));
+
+ std::unique_ptr<ForwardErrorCorrection::ReceivedPacket> received_packet =
+ receiver_.AddReceivedPacket(ParsePacket(*media_packet));
+ ASSERT_TRUE(received_packet);
+ receiver_.ProcessReceivedPacket(*received_packet);
+}
+
+TEST_F(FlexfecReceiverTest, ReceivesMediaAndFecPackets) {
+ const size_t kNumMediaPackets = 1;
+ const size_t kNumFecPackets = 1;
+
+ PacketList media_packets;
+ PacketizeFrame(kNumMediaPackets, 0, &media_packets);
+ std::list<Packet*> fec_packets = EncodeFec(media_packets, kNumFecPackets);
+ const auto& media_packet = media_packets.front();
+ auto fec_packet = packet_generator_.BuildFlexfecPacket(*fec_packets.front());
+
+ 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) {
+ const size_t kNumMediaPackets = 1;
+ const size_t kNumFecPackets = 1;
+
+ PacketList media_packets;
+ PacketizeFrame(kNumMediaPackets, 0, &media_packets);
+ std::list<Packet*> fec_packets = EncodeFec(media_packets, kNumFecPackets);
+ const auto& media_packet = media_packets.front();
+ // Simulate truncated FlexFEC payload.
+ fec_packets.front()->data.SetSize(1);
+ auto fec_packet = packet_generator_.BuildFlexfecPacket(*fec_packets.front());
+
+ 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)));
+}
+
+TEST_F(FlexfecReceiverTest, FailsOnUnknownMediaSsrc) {
+ const size_t kNumMediaPackets = 1;
+
+ PacketList media_packets;
+ PacketizeFrame(kNumMediaPackets, 0, &media_packets);
+ auto& media_packet = media_packets.front();
+ // Corrupt the SSRC.
+ media_packet->data.MutableData()[8] = 0;
+ media_packet->data.MutableData()[9] = 1;
+ media_packet->data.MutableData()[10] = 2;
+ media_packet->data.MutableData()[11] = 3;
+
+ EXPECT_FALSE(receiver_.AddReceivedPacket(ParsePacket(*media_packet)));
+}
+
+TEST_F(FlexfecReceiverTest, FailsOnUnknownFecSsrc) {
+ const size_t kNumMediaPackets = 1;
+ const size_t kNumFecPackets = 1;
+
+ PacketList media_packets;
+ PacketizeFrame(kNumMediaPackets, 0, &media_packets);
+ std::list<Packet*> fec_packets = EncodeFec(media_packets, kNumFecPackets);
+ const auto& media_packet = media_packets.front();
+ auto fec_packet = packet_generator_.BuildFlexfecPacket(*fec_packets.front());
+ // Corrupt the SSRC.
+ fec_packet->data.MutableData()[8] = 4;
+ fec_packet->data.MutableData()[9] = 5;
+ fec_packet->data.MutableData()[10] = 6;
+ fec_packet->data.MutableData()[11] = 7;
+
+ 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)));
+}
+
+TEST_F(FlexfecReceiverTest, ReceivesMultiplePackets) {
+ const size_t kNumMediaPackets = 2;
+ const size_t kNumFecPackets = 1;
+
+ PacketList media_packets;
+ PacketizeFrame(kNumMediaPackets, 0, &media_packets);
+ std::list<Packet*> fec_packets = EncodeFec(media_packets, kNumFecPackets);
+
+ // Receive all media packets.
+ for (const auto& media_packet : media_packets) {
+ 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);
+ 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) {
+ const size_t kNumMediaPackets = 2;
+ const size_t kNumFecPackets = 1;
+
+ PacketList media_packets;
+ PacketizeFrame(kNumMediaPackets, 0, &media_packets);
+ std::list<Packet*> fec_packets = EncodeFec(media_packets, kNumFecPackets);
+
+ // Receive first media packet but drop second.
+ auto media_it = media_packets.begin();
+ receiver_.OnRtpPacket(ParsePacket(**media_it));
+
+ // Receive FEC packet and ensure recovery of lost media packet.
+ auto fec_it = fec_packets.begin();
+ std::unique_ptr<Packet> packet_with_rtp_header =
+ packet_generator_.BuildFlexfecPacket(**fec_it);
+ media_it++;
+ EXPECT_CALL(recovered_packet_receiver_,
+ OnRecoveredPacket(
+ Property(&RtpPacketReceived::Buffer, Eq((*media_it)->data))));
+ receiver_.OnRtpPacket(ParsePacket(*packet_with_rtp_header));
+}
+
+TEST_F(FlexfecReceiverTest, RecoversFromDoubleMediaLoss) {
+ const size_t kNumMediaPackets = 2;
+ const size_t kNumFecPackets = 2;
+
+ PacketList media_packets;
+ PacketizeFrame(kNumMediaPackets, 0, &media_packets);
+ std::list<Packet*> fec_packets = EncodeFec(media_packets, kNumFecPackets);
+
+ // Drop both media packets.
+
+ // Receive first FEC packet and recover first lost media packet.
+ auto fec_it = fec_packets.begin();
+ std::unique_ptr<Packet> packet_with_rtp_header =
+ packet_generator_.BuildFlexfecPacket(**fec_it);
+ auto media_it = media_packets.begin();
+ EXPECT_CALL(recovered_packet_receiver_,
+ OnRecoveredPacket(
+ Property(&RtpPacketReceived::Buffer, Eq((*media_it)->data))));
+ receiver_.OnRtpPacket(ParsePacket(*packet_with_rtp_header));
+
+ // Receive second FEC packet and recover second lost media packet.
+ fec_it++;
+ packet_with_rtp_header = packet_generator_.BuildFlexfecPacket(**fec_it);
+ media_it++;
+ EXPECT_CALL(recovered_packet_receiver_,
+ OnRecoveredPacket(
+ Property(&RtpPacketReceived::Buffer, Eq((*media_it)->data))));
+
+ receiver_.OnRtpPacket(ParsePacket(*packet_with_rtp_header));
+}
+
+TEST_F(FlexfecReceiverTest, DoesNotRecoverFromMediaAndFecLoss) {
+ const size_t kNumMediaPackets = 2;
+ const size_t kNumFecPackets = 1;
+
+ PacketList media_packets;
+ PacketizeFrame(kNumMediaPackets, 0, &media_packets);
+ std::list<Packet*> fec_packets = EncodeFec(media_packets, kNumFecPackets);
+
+ // Receive first media packet.
+ auto media_it = media_packets.begin();
+ receiver_.OnRtpPacket(ParsePacket(**media_it));
+
+ // Drop second media packet and FEC packet. Do not expect call back.
+}
+
+TEST_F(FlexfecReceiverTest, DoesNotCallbackTwice) {
+ const size_t kNumMediaPackets = 2;
+ const size_t kNumFecPackets = 1;
+
+ PacketList media_packets;
+ PacketizeFrame(kNumMediaPackets, 0, &media_packets);
+ std::list<Packet*> fec_packets = EncodeFec(media_packets, kNumFecPackets);
+
+ // Receive first media packet but drop second.
+ auto media_it = media_packets.begin();
+ receiver_.OnRtpPacket(ParsePacket(**media_it));
+
+ // Receive FEC packet and ensure recovery of lost media packet.
+ auto fec_it = fec_packets.begin();
+ std::unique_ptr<Packet> packet_with_rtp_header =
+ packet_generator_.BuildFlexfecPacket(**fec_it);
+ media_it++;
+ EXPECT_CALL(recovered_packet_receiver_,
+ OnRecoveredPacket(
+ Property(&RtpPacketReceived::Buffer, Eq((*media_it)->data))));
+ receiver_.OnRtpPacket(ParsePacket(*packet_with_rtp_header));
+
+ // Receive the FEC packet again, but do not call back.
+ receiver_.OnRtpPacket(ParsePacket(*packet_with_rtp_header));
+
+ // Receive the first media packet again, but do not call back.
+ media_it = media_packets.begin();
+ receiver_.OnRtpPacket(ParsePacket(**media_it));
+
+ // Receive the second media packet again (the one recovered above),
+ // but do not call back again.
+ media_it++;
+ receiver_.OnRtpPacket(ParsePacket(**media_it));
+}
+
+// Here we are implicitly assuming packet masks that are suitable for
+// this type of 50% correlated loss. If we are changing our precomputed
+// packet masks, this test might need to be updated.
+TEST_F(FlexfecReceiverTest, RecoversFrom50PercentLoss) {
+ const size_t kNumFecPackets = 5;
+ const size_t kNumFrames = 2 * kNumFecPackets;
+ const size_t kNumMediaPacketsPerFrame = 1;
+
+ PacketList media_packets;
+ for (size_t i = 0; i < kNumFrames; ++i) {
+ PacketizeFrame(kNumMediaPacketsPerFrame, i, &media_packets);
+ }
+ std::list<Packet*> fec_packets = EncodeFec(media_packets, kNumFecPackets);
+
+ // Drop every second media packet.
+ auto media_it = media_packets.begin();
+ while (media_it != media_packets.end()) {
+ receiver_.OnRtpPacket(ParsePacket(**media_it));
+ ++media_it;
+ if (media_it == media_packets.end()) {
+ break;
+ }
+ ++media_it;
+ }
+
+ // Receive all FEC packets.
+ media_it = media_packets.begin();
+ for (const auto* fec_packet : fec_packets) {
+ std::unique_ptr<Packet> fec_packet_with_rtp_header =
+ packet_generator_.BuildFlexfecPacket(*fec_packet);
+ ++media_it;
+ if (media_it == media_packets.end()) {
+ break;
+ }
+ EXPECT_CALL(recovered_packet_receiver_,
+ OnRecoveredPacket(Property(&RtpPacketReceived::Buffer,
+ Eq((*media_it)->data))));
+ receiver_.OnRtpPacket(ParsePacket(*fec_packet_with_rtp_header));
+ ++media_it;
+ }
+}
+
+TEST_F(FlexfecReceiverTest, DelayedFecPacketDoesHelp) {
+ // These values need to be updated if the underlying erasure code
+ // implementation changes.
+ // Delay FEC packet by maximum number of media packets tracked by receiver.
+ const size_t kNumFrames = 192;
+ const size_t kNumMediaPacketsPerFrame = 1;
+ const size_t kNumFecPackets = 1;
+
+ PacketList media_packets;
+ PacketizeFrame(kNumMediaPacketsPerFrame, 0, &media_packets);
+ PacketizeFrame(kNumMediaPacketsPerFrame, 1, &media_packets);
+ // Protect two first frames.
+ std::list<Packet*> fec_packets = EncodeFec(media_packets, kNumFecPackets);
+ for (size_t i = 2; i < kNumFrames; ++i) {
+ PacketizeFrame(kNumMediaPacketsPerFrame, i, &media_packets);
+ }
+
+ // Drop first media packet and delay FEC packet.
+ auto media_it = media_packets.begin();
+ ++media_it;
+
+ // Receive all other media packets.
+ while (media_it != media_packets.end()) {
+ receiver_.OnRtpPacket(ParsePacket(**media_it));
+ ++media_it;
+ }
+
+ // Receive FEC packet and recover first media packet.
+ auto fec_it = fec_packets.begin();
+ std::unique_ptr<Packet> packet_with_rtp_header =
+ packet_generator_.BuildFlexfecPacket(**fec_it);
+ media_it = media_packets.begin();
+ EXPECT_CALL(recovered_packet_receiver_,
+ OnRecoveredPacket(
+ Property(&RtpPacketReceived::Buffer, Eq((*media_it)->data))));
+ receiver_.OnRtpPacket(ParsePacket(*packet_with_rtp_header));
+}
+
+TEST_F(FlexfecReceiverTest, TooDelayedFecPacketDoesNotHelp) {
+ // These values need to be updated if the underlying erasure code
+ // implementation changes.
+ // Delay FEC packet by one more than maximum number of media packets
+ // tracked by receiver.
+ const size_t kNumFrames = 193;
+ const size_t kNumMediaPacketsPerFrame = 1;
+ const size_t kNumFecPackets = 1;
+
+ PacketList media_packets;
+ PacketizeFrame(kNumMediaPacketsPerFrame, 0, &media_packets);
+ PacketizeFrame(kNumMediaPacketsPerFrame, 1, &media_packets);
+ // Protect first two frames.
+ std::list<Packet*> fec_packets = EncodeFec(media_packets, kNumFecPackets);
+ for (size_t i = 2; i < kNumFrames; ++i) {
+ PacketizeFrame(kNumMediaPacketsPerFrame, i, &media_packets);
+ }
+
+ // Drop first media packet and delay FEC packet.
+ auto media_it = media_packets.begin();
+ ++media_it;
+
+ // Receive all other media packets.
+ while (media_it != media_packets.end()) {
+ receiver_.OnRtpPacket(ParsePacket(**media_it));
+ ++media_it;
+ }
+
+ // Receive FEC packet.
+ auto fec_it = fec_packets.begin();
+ std::unique_ptr<Packet> packet_with_rtp_header =
+ packet_generator_.BuildFlexfecPacket(**fec_it);
+ receiver_.OnRtpPacket(ParsePacket(*packet_with_rtp_header));
+
+ // Do not expect a call back.
+}
+
+TEST_F(FlexfecReceiverTest, SurvivesOldRecoveredPacketBeingReinserted) {
+ // Simulates the behaviour of the
+ // Call->FlexfecReceiveStream->FlexfecReceiver->Call loop in production code.
+ class LoopbackRecoveredPacketReceiver : public RecoveredPacketReceiver {
+ public:
+ LoopbackRecoveredPacketReceiver() : receiver_(nullptr) {}
+
+ void SetReceiver(FlexfecReceiver* receiver) { receiver_ = receiver; }
+
+ // Implements RecoveredPacketReceiver.
+ void OnRecoveredPacket(const RtpPacketReceived& packet) override {
+ EXPECT_TRUE(packet.recovered());
+ RTC_DCHECK(receiver_);
+ receiver_->OnRtpPacket(packet);
+ }
+
+ private:
+ FlexfecReceiver* receiver_;
+ } loopback_recovered_packet_receiver;
+
+ // Feed recovered packets back into `receiver`.
+ FlexfecReceiver receiver(Clock::GetRealTimeClock(), kFlexfecSsrc, kMediaSsrc,
+ &loopback_recovered_packet_receiver);
+ loopback_recovered_packet_receiver.SetReceiver(&receiver);
+
+ // Receive first set of packets.
+ PacketList first_media_packets;
+ for (int i = 0; i < 46; ++i) {
+ PacketizeFrame(1, 0, &first_media_packets);
+ }
+ for (const auto& media_packet : first_media_packets) {
+ receiver.OnRtpPacket(ParsePacket(*media_packet));
+ }
+
+ // Protect one media packet. Lose the media packet,
+ // but do not receive FEC packet yet.
+ PacketList protected_media_packet;
+ PacketizeFrame(1, 0, &protected_media_packet);
+ const std::list<Packet*> fec_packets = EncodeFec(protected_media_packet, 1);
+ EXPECT_EQ(1u, fec_packets.size());
+ std::unique_ptr<Packet> fec_packet_with_rtp_header =
+ packet_generator_.BuildFlexfecPacket(*fec_packets.front());
+
+ // Lose some packets, thus introducing a sequence number gap.
+ PacketList lost_packets;
+ for (int i = 0; i < 100; ++i) {
+ PacketizeFrame(1, 0, &lost_packets);
+ }
+
+ // Receive one more packet.
+ PacketList second_media_packets;
+ PacketizeFrame(1, 0, &second_media_packets);
+ for (const auto& media_packet : second_media_packets) {
+ receiver.OnRtpPacket(ParsePacket(*media_packet));
+ }
+
+ // Receive delayed FEC packet.
+ receiver.OnRtpPacket(ParsePacket(*fec_packet_with_rtp_header));
+
+ // Expect no crash.
+}
+
+TEST_F(FlexfecReceiverTest, RecoversWithMediaPacketsOutOfOrder) {
+ const size_t kNumMediaPackets = 6;
+ const size_t kNumFecPackets = 2;
+
+ PacketList media_packets;
+ PacketizeFrame(kNumMediaPackets, 0, &media_packets);
+ std::list<Packet*> fec_packets = EncodeFec(media_packets, kNumFecPackets);
+
+ // Lose two media packets, and receive the others out of order.
+ auto media_it = media_packets.begin();
+ auto media_packet0 = media_it++;
+ auto media_packet1 = media_it++;
+ auto media_packet2 = media_it++;
+ auto media_packet3 = media_it++;
+ auto media_packet4 = media_it++;
+ auto media_packet5 = media_it++;
+ receiver_.OnRtpPacket(ParsePacket(**media_packet5));
+ receiver_.OnRtpPacket(ParsePacket(**media_packet2));
+ receiver_.OnRtpPacket(ParsePacket(**media_packet3));
+ receiver_.OnRtpPacket(ParsePacket(**media_packet0));
+
+ // Expect to recover lost media packets.
+ EXPECT_CALL(recovered_packet_receiver_,
+ OnRecoveredPacket(Property(&RtpPacketReceived::Buffer,
+ Eq((*media_packet1)->data))));
+ EXPECT_CALL(recovered_packet_receiver_,
+ OnRecoveredPacket(Property(&RtpPacketReceived::Buffer,
+ Eq((*media_packet4)->data))));
+ // Add FEC packets.
+ auto fec_it = fec_packets.begin();
+ std::unique_ptr<Packet> packet_with_rtp_header;
+ while (fec_it != fec_packets.end()) {
+ packet_with_rtp_header = packet_generator_.BuildFlexfecPacket(**fec_it);
+ receiver_.OnRtpPacket(ParsePacket(*packet_with_rtp_header));
+ ++fec_it;
+ }
+}
+
+// Recovered media packets may be fed back into the FlexfecReceiver by the
+// callback. This test ensures the idempotency of such a situation.
+TEST_F(FlexfecReceiverTest, RecoveryCallbackDoesNotLoopInfinitely) {
+ class LoopbackRecoveredPacketReceiver : public RecoveredPacketReceiver {
+ public:
+ const int kMaxRecursionDepth = 10;
+
+ LoopbackRecoveredPacketReceiver()
+ : receiver_(nullptr),
+ did_receive_call_back_(false),
+ recursion_depth_(0),
+ deep_recursion_(false) {}
+
+ void SetReceiver(FlexfecReceiver* receiver) { receiver_ = receiver; }
+ bool DidReceiveCallback() const { return did_receive_call_back_; }
+ bool DeepRecursion() const { return deep_recursion_; }
+
+ // Implements RecoveredPacketReceiver.
+ void OnRecoveredPacket(const RtpPacketReceived& packet) override {
+ did_receive_call_back_ = true;
+
+ if (recursion_depth_ > kMaxRecursionDepth) {
+ deep_recursion_ = true;
+ return;
+ }
+ ++recursion_depth_;
+ RTC_DCHECK(receiver_);
+ receiver_->OnRtpPacket(packet);
+ --recursion_depth_;
+ }
+
+ private:
+ FlexfecReceiver* receiver_;
+ bool did_receive_call_back_;
+ int recursion_depth_;
+ bool deep_recursion_;
+ } loopback_recovered_packet_receiver;
+
+ // Feed recovered packets back into `receiver`.
+ FlexfecReceiver receiver(Clock::GetRealTimeClock(), kFlexfecSsrc, kMediaSsrc,
+ &loopback_recovered_packet_receiver);
+ loopback_recovered_packet_receiver.SetReceiver(&receiver);
+
+ const size_t kNumMediaPackets = 2;
+ const size_t kNumFecPackets = 1;
+
+ PacketList media_packets;
+ PacketizeFrame(kNumMediaPackets, 0, &media_packets);
+ std::list<Packet*> fec_packets = EncodeFec(media_packets, kNumFecPackets);
+
+ // Receive first media packet but drop second.
+ auto media_it = media_packets.begin();
+ receiver.OnRtpPacket(ParsePacket(**media_it));
+
+ // Receive FEC packet and verify that a packet was recovered.
+ auto fec_it = fec_packets.begin();
+ std::unique_ptr<Packet> packet_with_rtp_header =
+ packet_generator_.BuildFlexfecPacket(**fec_it);
+ receiver.OnRtpPacket(ParsePacket(*packet_with_rtp_header));
+ EXPECT_TRUE(loopback_recovered_packet_receiver.DidReceiveCallback());
+ EXPECT_FALSE(loopback_recovered_packet_receiver.DeepRecursion());
+}
+
+TEST_F(FlexfecReceiverTest, CalculatesNumberOfPackets) {
+ const size_t kNumMediaPackets = 2;
+ const size_t kNumFecPackets = 1;
+
+ PacketList media_packets;
+ PacketizeFrame(kNumMediaPackets, 0, &media_packets);
+ std::list<Packet*> fec_packets = EncodeFec(media_packets, kNumFecPackets);
+
+ // Receive first media packet but drop second.
+ auto media_it = media_packets.begin();
+ receiver_.OnRtpPacket(ParsePacket(**media_it));
+
+ // Receive FEC packet and ensure recovery of lost media packet.
+ auto fec_it = fec_packets.begin();
+ std::unique_ptr<Packet> packet_with_rtp_header =
+ packet_generator_.BuildFlexfecPacket(**fec_it);
+ media_it++;
+ EXPECT_CALL(recovered_packet_receiver_,
+ OnRecoveredPacket(
+ Property(&RtpPacketReceived::Buffer, Eq((*media_it)->data))));
+ receiver_.OnRtpPacket(ParsePacket(*packet_with_rtp_header));
+
+ // Check stats calculations.
+ FecPacketCounter packet_counter = receiver_.GetPacketCounter();
+ EXPECT_EQ(2U, packet_counter.num_packets);
+ EXPECT_EQ(1U, packet_counter.num_fec_packets);
+ EXPECT_EQ(1U, packet_counter.num_recovered_packets);
+}
+
+TEST_F(FlexfecReceiverTest, DoesNotDecodeWrappedMediaSequenceUsingOldFec) {
+ const size_t kFirstFrameNumMediaPackets = 2;
+ const size_t kFirstFrameNumFecPackets = 1;
+
+ PacketList media_packets;
+ PacketizeFrame(kFirstFrameNumMediaPackets, 0, &media_packets);
+
+ // Protect first frame (sequences 0 and 1) with 1 FEC packet.
+ std::list<Packet*> fec_packets =
+ EncodeFec(media_packets, kFirstFrameNumFecPackets);
+
+ // Generate enough media packets to simulate media sequence number wraparound.
+ // Use no FEC for these frames to make sure old FEC is not purged due to age.
+ const size_t kNumFramesSequenceWrapAround =
+ std::numeric_limits<uint16_t>::max();
+ const size_t kNumMediaPacketsPerFrame = 1;
+
+ for (size_t i = 1; i <= kNumFramesSequenceWrapAround; ++i) {
+ PacketizeFrame(kNumMediaPacketsPerFrame, i, &media_packets);
+ }
+
+ // Receive first (`kFirstFrameNumMediaPackets` + 192) media packets.
+ // Simulate an old FEC packet by separating it from its encoded media
+ // packets by at least 192 packets.
+ auto media_it = media_packets.begin();
+ for (size_t i = 0; i < (kFirstFrameNumMediaPackets + 192); i++) {
+ if (i == 1) {
+ // Drop the second packet of the first frame.
+ media_it++;
+ } else {
+ receiver_.OnRtpPacket(ParsePacket(**media_it++));
+ }
+ }
+
+ // Receive FEC packet. Although a protected packet was dropped,
+ // expect no recovery callback since it is delayed from first frame
+ // by more than 192 packets.
+ auto fec_it = fec_packets.begin();
+ std::unique_ptr<Packet> fec_packet_with_rtp_header =
+ packet_generator_.BuildFlexfecPacket(**fec_it);
+ receiver_.OnRtpPacket(ParsePacket(*fec_packet_with_rtp_header));
+
+ // Receive remaining media packets.
+ // NOTE: Because we sent enough to simulate wrap around, sequence 0 is
+ // received again, but is a different packet than the original first
+ // packet of first frame.
+ while (media_it != media_packets.end()) {
+ receiver_.OnRtpPacket(ParsePacket(**media_it++));
+ }
+
+ // Do not expect a recovery callback, the FEC packet is old
+ // and should not decode wrapped around media sequences.
+}
+
+} // namespace webrtc