diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
commit | 26a029d407be480d791972afb5975cf62c9360a6 (patch) | |
tree | f435a8308119effd964b339f76abb83a57c29483 /third_party/libwebrtc/modules/rtp_rtcp/source/flexfec_receiver_unittest.cc | |
parent | Initial commit. (diff) | |
download | firefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz firefox-26a029d407be480d791972afb5975cf62c9360a6.zip |
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | third_party/libwebrtc/modules/rtp_rtcp/source/flexfec_receiver_unittest.cc | 691 |
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 |