/* * 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 #include #include "webrtc/base/basictypes.h" #include "webrtc/modules/rtp_rtcp/include/flexfec_receiver.h" #include "webrtc/modules/rtp_rtcp/mocks/mock_recovered_packet_receiver.h" #include "webrtc/modules/rtp_rtcp/source/fec_test_helper.h" #include "webrtc/modules/rtp_rtcp/source/forward_error_correction.h" #include "webrtc/test/gmock.h" #include "webrtc/test/gtest.h" namespace webrtc { namespace { using ::testing::_; using ::testing::Args; using ::testing::ElementsAreArray; using ::testing::Return; 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; } // namespace class FlexfecReceiverTest : public ::testing::Test { protected: FlexfecReceiverTest() : receiver_(FlexfecReceiver::Create(kFlexfecSsrc, kMediaSsrc, &recovered_packet_receiver_)), erasure_code_(ForwardErrorCorrection::CreateFlexfec()), 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); std::unique_ptr 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)); EXPECT_TRUE(receiver_->AddAndProcessReceivedPacket(media_packet->data, media_packet->length)); } TEST_F(FlexfecReceiverTest, FailsOnTruncatedMediaPacket) { const size_t kNoPayload = 0; packet_generator_.NewFrame(1); std::unique_ptr media_packet( packet_generator_.NextPacket(0, kNoPayload)); // Simulate truncated media packet. media_packet->length = kRtpHeaderSize - 1; EXPECT_FALSE(receiver_->AddAndProcessReceivedPacket(media_packet->data, media_packet->length)); } 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); auto media_packet = media_packets.front().get(); auto fec_packet = packet_generator_.BuildFlexfecPacket(*fec_packets.front()); EXPECT_TRUE(receiver_->AddAndProcessReceivedPacket(media_packet->data, media_packet->length)); EXPECT_TRUE(receiver_->AddAndProcessReceivedPacket(fec_packet->data, fec_packet->length)); } 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); auto media_packet = media_packets.front().get(); // Simulate truncated FlexFEC payload. fec_packets.front()->length = 1; auto fec_packet = packet_generator_.BuildFlexfecPacket(*fec_packets.front()); EXPECT_TRUE(receiver_->AddAndProcessReceivedPacket(media_packet->data, media_packet->length)); EXPECT_FALSE(receiver_->AddAndProcessReceivedPacket(fec_packet->data, fec_packet->length)); } TEST_F(FlexfecReceiverTest, FailsOnUnknownMediaSsrc) { const size_t kNumMediaPackets = 1; PacketList media_packets; PacketizeFrame(kNumMediaPackets, 0, &media_packets); auto media_packet = media_packets.front().get(); // Corrupt the SSRC. media_packet->data[8] = 0; media_packet->data[9] = 1; media_packet->data[10] = 2; media_packet->data[11] = 3; EXPECT_FALSE(receiver_->AddAndProcessReceivedPacket(media_packet->data, media_packet->length)); } 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); auto media_packet = media_packets.front().get(); auto fec_packet = packet_generator_.BuildFlexfecPacket(*fec_packets.front()); // Corrupt the SSRC. fec_packet->data[8] = 4; fec_packet->data[9] = 5; fec_packet->data[10] = 6; fec_packet->data[11] = 7; EXPECT_TRUE(receiver_->AddAndProcessReceivedPacket(media_packet->data, media_packet->length)); EXPECT_FALSE(receiver_->AddAndProcessReceivedPacket(fec_packet->data, fec_packet->length)); } 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) { EXPECT_TRUE(receiver_->AddAndProcessReceivedPacket(media_packet->data, media_packet->length)); } // Receive FEC packet. auto fec_packet = fec_packets.front(); std::unique_ptr packet_with_rtp_header = packet_generator_.BuildFlexfecPacket(*fec_packet); EXPECT_TRUE(receiver_->AddAndProcessReceivedPacket( packet_with_rtp_header->data, packet_with_rtp_header->length)); } 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(); EXPECT_TRUE(receiver_->AddAndProcessReceivedPacket((*media_it)->data, (*media_it)->length)); // 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(_, (*media_it)->length)) .With( Args<0, 1>(ElementsAreArray((*media_it)->data, (*media_it)->length))) .WillOnce(Return(true)); EXPECT_TRUE(receiver_->AddAndProcessReceivedPacket( packet_with_rtp_header->data, packet_with_rtp_header->length)); } 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(_, (*media_it)->length)) .With( Args<0, 1>(ElementsAreArray((*media_it)->data, (*media_it)->length))) .WillOnce(Return(true)); EXPECT_TRUE(receiver_->AddAndProcessReceivedPacket( packet_with_rtp_header->data, packet_with_rtp_header->length)); // 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(_, (*media_it)->length)) .With( Args<0, 1>(ElementsAreArray((*media_it)->data, (*media_it)->length))) .WillOnce(Return(true)); EXPECT_TRUE(receiver_->AddAndProcessReceivedPacket( packet_with_rtp_header->data, packet_with_rtp_header->length)); } 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(); EXPECT_TRUE(receiver_->AddAndProcessReceivedPacket((*media_it)->data, (*media_it)->length)); // 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(); EXPECT_TRUE(receiver_->AddAndProcessReceivedPacket((*media_it)->data, (*media_it)->length)); // 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(_, (*media_it)->length)) .With( Args<0, 1>(ElementsAreArray((*media_it)->data, (*media_it)->length))) .WillOnce(Return(true)); EXPECT_TRUE(receiver_->AddAndProcessReceivedPacket( packet_with_rtp_header->data, packet_with_rtp_header->length)); // Receive FEC packet again. EXPECT_TRUE(receiver_->AddAndProcessReceivedPacket( packet_with_rtp_header->data, packet_with_rtp_header->length)); // Do not call back again. } // 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()) { EXPECT_TRUE(receiver_->AddAndProcessReceivedPacket((*media_it)->data, (*media_it)->length)); ++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(_, (*media_it)->length)) .With(Args<0, 1>( ElementsAreArray((*media_it)->data, (*media_it)->length))) .WillOnce(Return(true)); EXPECT_TRUE(receiver_->AddAndProcessReceivedPacket( fec_packet_with_rtp_header->data, fec_packet_with_rtp_header->length)); ++media_it; } } TEST_F(FlexfecReceiverTest, DelayedFecPacketDoesHelp) { // These values need to be updated if the underlying erasure code // implementation changes. const size_t kNumFrames = 48; 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()) { EXPECT_TRUE(receiver_->AddAndProcessReceivedPacket((*media_it)->data, (*media_it)->length)); ++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(_, (*media_it)->length)) .With( Args<0, 1>(ElementsAreArray((*media_it)->data, (*media_it)->length))) .WillOnce(Return(true)); EXPECT_TRUE(receiver_->AddAndProcessReceivedPacket( packet_with_rtp_header->data, packet_with_rtp_header->length)); } TEST_F(FlexfecReceiverTest, TooDelayedFecPacketDoesNotHelp) { // These values need to be updated if the underlying erasure code // implementation changes. const size_t kNumFrames = 49; 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()) { EXPECT_TRUE(receiver_->AddAndProcessReceivedPacket((*media_it)->data, (*media_it)->length)); ++media_it; } // Receive FEC packet. auto fec_it = fec_packets.begin(); std::unique_ptr packet_with_rtp_header = packet_generator_.BuildFlexfecPacket(**fec_it); EXPECT_TRUE(receiver_->AddAndProcessReceivedPacket( packet_with_rtp_header->data, packet_with_rtp_header->length)); // Do not expect a call back. } 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++; EXPECT_TRUE(receiver_->AddAndProcessReceivedPacket((*media_packet5)->data, (*media_packet5)->length)); EXPECT_TRUE(receiver_->AddAndProcessReceivedPacket((*media_packet2)->data, (*media_packet2)->length)); EXPECT_TRUE(receiver_->AddAndProcessReceivedPacket((*media_packet3)->data, (*media_packet3)->length)); EXPECT_TRUE(receiver_->AddAndProcessReceivedPacket((*media_packet0)->data, (*media_packet0)->length)); // Expect to recover lost media packets. EXPECT_CALL(recovered_packet_receiver_, OnRecoveredPacket(_, (*media_packet1)->length)) .With(Args<0, 1>( ElementsAreArray((*media_packet1)->data, (*media_packet1)->length))) .WillOnce(Return(true)); EXPECT_CALL(recovered_packet_receiver_, OnRecoveredPacket(_, (*media_packet4)->length)) .With(Args<0, 1>( ElementsAreArray((*media_packet4)->data, (*media_packet4)->length))) .WillOnce(Return(true)); // 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); EXPECT_TRUE(receiver_->AddAndProcessReceivedPacket( packet_with_rtp_header->data, packet_with_rtp_header->length)); ++fec_it; } } 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(); EXPECT_TRUE(receiver_->AddAndProcessReceivedPacket((*media_it)->data, (*media_it)->length)); // 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(_, (*media_it)->length)) .With( Args<0, 1>(ElementsAreArray((*media_it)->data, (*media_it)->length))) .WillOnce(Return(true)); EXPECT_TRUE(receiver_->AddAndProcessReceivedPacket( packet_with_rtp_header->data, packet_with_rtp_header->length)); // 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); } } // namespace webrtc