/* * 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 #include #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 EncodeFec(const PacketList& media_packets, size_t num_fec_packets); FlexfecReceiverForTest receiver_; std::unique_ptr erasure_code_; FlexfecPacketGenerator packet_generator_; ::testing::StrictMock 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 next_packet( packet_generator_.NextPacket(frame_offset + i, kPayloadLength)); media_packets->push_back(std::move(next_packet)); } } std::list 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 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 media_packet( packet_generator_.NextPacket(0, kPayloadLength)); std::unique_ptr 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 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 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 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 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 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 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 fec_packets = EncodeFec(media_packets, kNumFecPackets); // Receive all media packets. for (const auto& media_packet : media_packets) { std::unique_ptr 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_with_rtp_header = packet_generator_.BuildFlexfecPacket(*fec_packet); std::unique_ptr 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 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_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 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_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 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 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_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 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 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 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_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 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_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 fec_packets = EncodeFec(protected_media_packet, 1); EXPECT_EQ(1u, fec_packets.size()); std::unique_ptr 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 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_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 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_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 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_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 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::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 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