diff --git a/webrtc/modules/rtp_rtcp/include/fec_receiver.h b/webrtc/modules/rtp_rtcp/include/fec_receiver.h index 453ede406b..debf2d7eeb 100644 --- a/webrtc/modules/rtp_rtcp/include/fec_receiver.h +++ b/webrtc/modules/rtp_rtcp/include/fec_receiver.h @@ -35,6 +35,9 @@ class FecReceiver { // Takes a RED packet, strips the RED header, and adds the resulting // "virtual" RTP packet(s) into the internal buffer. + // + // TODO(brandtr): Set |ulpfec_payload_type| during constructor call, + // rather than as a parameter here. virtual int32_t AddReceivedRedPacket(const RTPHeader& rtp_header, const uint8_t* incoming_rtp_packet, size_t packet_length, diff --git a/webrtc/modules/rtp_rtcp/source/fec_receiver_impl.h b/webrtc/modules/rtp_rtcp/source/fec_receiver_impl.h index 94eb3ac3d4..bd3a79dd7a 100644 --- a/webrtc/modules/rtp_rtcp/source/fec_receiver_impl.h +++ b/webrtc/modules/rtp_rtcp/source/fec_receiver_impl.h @@ -46,6 +46,7 @@ class FecReceiverImpl : public FecReceiver { ForwardErrorCorrection::RecoveredPacketList recovered_packets_; FecPacketCounter packet_counter_; }; + } // namespace webrtc #endif // WEBRTC_MODULES_RTP_RTCP_SOURCE_FEC_RECEIVER_IMPL_H_ diff --git a/webrtc/modules/rtp_rtcp/source/fec_receiver_unittest.cc b/webrtc/modules/rtp_rtcp/source/fec_receiver_unittest.cc index 2c2b823977..0c4b4861cf 100644 --- a/webrtc/modules/rtp_rtcp/source/fec_receiver_unittest.cc +++ b/webrtc/modules/rtp_rtcp/source/fec_receiver_unittest.cc @@ -45,56 +45,32 @@ class ReceiverFecTest : public ::testing::Test { receiver_fec_(FecReceiver::Create(&rtp_data_callback_)), packet_generator_(kMediaSsrc) {} + // Generates |num_fec_packets| FEC packets, given |media_packets|. void EncodeFec(const ForwardErrorCorrection::PacketList& media_packets, size_t num_fec_packets, - std::list* fec_packets) { - uint8_t protection_factor = num_fec_packets * 255 / media_packets.size(); - EXPECT_EQ(0, fec_->EncodeFec(media_packets, protection_factor, 0, false, - kFecMaskBursty, fec_packets)); - ASSERT_EQ(num_fec_packets, fec_packets->size()); - } + std::list* fec_packets); - void GenerateFrame(size_t num_media_packets, - size_t frame_offset, - std::list* augmented_packets, - ForwardErrorCorrection::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, kRtpHeaderSize + 10)); - augmented_packets->push_back(next_packet.get()); - media_packets->push_back(std::move(next_packet)); - } - } + // Generates |num_media_packets| corresponding to a single frame. + void PacketizeFrame(size_t num_media_packets, + size_t frame_offset, + std::list* augmented_packets, + ForwardErrorCorrection::PacketList* packets); + // Build a media packet using |packet_generator_| and add it + // to the receiver. + void BuildAndAddRedMediaPacket(AugmentedPacket* packet); + + // Build a FEC packet using |packet_generator_| and add it + // to the receiver. + void BuildAndAddRedFecPacket(Packet* packet); + + // Ensure that |rtp_data_callback_| will be called correctly + // and that the recovered packet will be identical to the lost packet. void VerifyReconstructedMediaPacket(const AugmentedPacket& packet, - size_t times) { - // Verify that the content of the reconstructed packet is equal to the - // content of |packet|, and that the same content is received |times| number - // of times in a row. - EXPECT_CALL(rtp_data_callback_, OnRecoveredPacket(_, packet.length)) - .With(Args<0, 1>(ElementsAreArray(packet.data, packet.length))) - .Times(times) - .WillRepeatedly(Return(true)); - } - - void BuildAndAddRedMediaPacket(AugmentedPacket* packet) { - std::unique_ptr red_packet( - UlpfecPacketGenerator::BuildMediaRedPacket(*packet)); - EXPECT_EQ(0, receiver_fec_->AddReceivedRedPacket( - red_packet->header.header, red_packet->data, - red_packet->length, kFecPayloadType)); - } - - void BuildAndAddRedFecPacket(Packet* packet) { - std::unique_ptr red_packet( - packet_generator_.BuildUlpfecRedPacket(*packet)); - EXPECT_EQ(0, receiver_fec_->AddReceivedRedPacket( - red_packet->header.header, red_packet->data, - red_packet->length, kFecPayloadType)); - } + size_t times); void InjectGarbagePacketLength(size_t fec_garbage_offset); + static void SurvivesMaliciousPacket(const uint8_t* data, size_t length, uint8_t ulpfec_payload_type); @@ -105,30 +81,63 @@ class ReceiverFecTest : public ::testing::Test { UlpfecPacketGenerator packet_generator_; }; -TEST_F(ReceiverFecTest, TwoMediaOneFec) { - const size_t kNumFecPackets = 1; - std::list augmented_media_packets; - ForwardErrorCorrection::PacketList media_packets; - GenerateFrame(2, 0, &augmented_media_packets, &media_packets); - std::list fec_packets; - EncodeFec(media_packets, kNumFecPackets, &fec_packets); +void ReceiverFecTest::EncodeFec( + const ForwardErrorCorrection::PacketList& media_packets, + size_t num_fec_packets, + std::list* fec_packets) { + const uint8_t protection_factor = + num_fec_packets * 255 / media_packets.size(); + // Unequal protection is turned off, and the number of important + // packets is thus irrelevant. + constexpr int kNumImportantPackets = 0; + constexpr bool kUseUnequalProtection = false; + constexpr FecMaskType kFecMaskType = kFecMaskBursty; + EXPECT_EQ( + 0, fec_->EncodeFec(media_packets, protection_factor, kNumImportantPackets, + kUseUnequalProtection, kFecMaskType, fec_packets)); + ASSERT_EQ(num_fec_packets, fec_packets->size()); +} - // Recovery - auto it = augmented_media_packets.begin(); - BuildAndAddRedMediaPacket(*it); - VerifyReconstructedMediaPacket(**it, 1); - EXPECT_EQ(0, receiver_fec_->ProcessReceivedFec()); - // Drop one media packet. - auto fec_it = fec_packets.begin(); - BuildAndAddRedFecPacket(*fec_it); - ++it; - VerifyReconstructedMediaPacket(**it, 1); - EXPECT_EQ(0, receiver_fec_->ProcessReceivedFec()); +void ReceiverFecTest::PacketizeFrame( + size_t num_media_packets, + size_t frame_offset, + std::list* augmented_packets, + ForwardErrorCorrection::PacketList* 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, kRtpHeaderSize + 10)); + augmented_packets->push_back(next_packet.get()); + packets->push_back(std::move(next_packet)); + } +} - FecPacketCounter counter = receiver_fec_->GetPacketCounter(); - EXPECT_EQ(2U, counter.num_packets); - EXPECT_EQ(1U, counter.num_fec_packets); - EXPECT_EQ(1U, counter.num_recovered_packets); +void ReceiverFecTest::BuildAndAddRedMediaPacket(AugmentedPacket* packet) { + std::unique_ptr red_packet( + packet_generator_.BuildMediaRedPacket(*packet)); + EXPECT_EQ(0, receiver_fec_->AddReceivedRedPacket( + red_packet->header.header, red_packet->data, + red_packet->length, kFecPayloadType)); +} + +void ReceiverFecTest::BuildAndAddRedFecPacket(Packet* packet) { + std::unique_ptr red_packet( + packet_generator_.BuildUlpfecRedPacket(*packet)); + EXPECT_EQ(0, receiver_fec_->AddReceivedRedPacket( + red_packet->header.header, red_packet->data, + red_packet->length, kFecPayloadType)); +} + +void ReceiverFecTest::VerifyReconstructedMediaPacket( + const AugmentedPacket& packet, + size_t times) { + // Verify that the content of the reconstructed packet is equal to the + // content of |packet|, and that the same content is received |times| number + // of times in a row. + EXPECT_CALL(rtp_data_callback_, OnRecoveredPacket(_, packet.length)) + .With(Args<0, 1>(ElementsAreArray(packet.data, packet.length))) + .Times(times) + .WillRepeatedly(Return(true)); } void ReceiverFecTest::InjectGarbagePacketLength(size_t fec_garbage_offset) { @@ -138,7 +147,7 @@ void ReceiverFecTest::InjectGarbagePacketLength(size_t fec_garbage_offset) { const size_t kNumFecPackets = 1; std::list augmented_media_packets; ForwardErrorCorrection::PacketList media_packets; - GenerateFrame(2, 0, &augmented_media_packets, &media_packets); + PacketizeFrame(2, 0, &augmented_media_packets, &media_packets); std::list fec_packets; EncodeFec(media_packets, kNumFecPackets, &fec_packets); ByteWriter::WriteBigEndian( @@ -156,6 +165,47 @@ void ReceiverFecTest::InjectGarbagePacketLength(size_t fec_garbage_offset) { EXPECT_EQ(0U, counter.num_recovered_packets); } +void ReceiverFecTest::SurvivesMaliciousPacket(const uint8_t* data, + size_t length, + uint8_t ulpfec_payload_type) { + webrtc::RTPHeader header; + std::unique_ptr parser( + webrtc::RtpHeaderParser::Create()); + ASSERT_TRUE(parser->Parse(data, length, &header)); + + webrtc::NullRtpData null_callback; + std::unique_ptr receiver_fec( + webrtc::FecReceiver::Create(&null_callback)); + + receiver_fec->AddReceivedRedPacket(header, data, length, ulpfec_payload_type); +} + +TEST_F(ReceiverFecTest, TwoMediaOneFec) { + constexpr size_t kNumFecPackets = 1u; + std::list augmented_media_packets; + ForwardErrorCorrection::PacketList media_packets; + PacketizeFrame(2, 0, &augmented_media_packets, &media_packets); + std::list fec_packets; + EncodeFec(media_packets, kNumFecPackets, &fec_packets); + + // Recovery + auto it = augmented_media_packets.begin(); + BuildAndAddRedMediaPacket(*it); + VerifyReconstructedMediaPacket(**it, 1); + EXPECT_EQ(0, receiver_fec_->ProcessReceivedFec()); + // Drop one media packet. + auto fec_it = fec_packets.begin(); + BuildAndAddRedFecPacket(*fec_it); + ++it; + VerifyReconstructedMediaPacket(**it, 1); + EXPECT_EQ(0, receiver_fec_->ProcessReceivedFec()); + + FecPacketCounter counter = receiver_fec_->GetPacketCounter(); + EXPECT_EQ(2u, counter.num_packets); + EXPECT_EQ(1u, counter.num_fec_packets); + EXPECT_EQ(1u, counter.num_recovered_packets); +} + TEST_F(ReceiverFecTest, InjectGarbageFecHeaderLengthRecovery) { // Byte offset 8 is the 'length recovery' field of the FEC header. InjectGarbagePacketLength(8); @@ -171,7 +221,7 @@ TEST_F(ReceiverFecTest, TwoMediaTwoFec) { const size_t kNumFecPackets = 2; std::list augmented_media_packets; ForwardErrorCorrection::PacketList media_packets; - GenerateFrame(2, 0, &augmented_media_packets, &media_packets); + PacketizeFrame(2, 0, &augmented_media_packets, &media_packets); std::list fec_packets; EncodeFec(media_packets, kNumFecPackets, &fec_packets); @@ -193,8 +243,8 @@ TEST_F(ReceiverFecTest, TwoFramesOneFec) { const size_t kNumFecPackets = 1; std::list augmented_media_packets; ForwardErrorCorrection::PacketList media_packets; - GenerateFrame(1, 0, &augmented_media_packets, &media_packets); - GenerateFrame(1, 1, &augmented_media_packets, &media_packets); + PacketizeFrame(1, 0, &augmented_media_packets, &media_packets); + PacketizeFrame(1, 1, &augmented_media_packets, &media_packets); std::list fec_packets; EncodeFec(media_packets, kNumFecPackets, &fec_packets); @@ -214,8 +264,8 @@ TEST_F(ReceiverFecTest, OneCompleteOneUnrecoverableFrame) { const size_t kNumFecPackets = 1; std::list augmented_media_packets; ForwardErrorCorrection::PacketList media_packets; - GenerateFrame(1, 0, &augmented_media_packets, &media_packets); - GenerateFrame(2, 1, &augmented_media_packets, &media_packets); + PacketizeFrame(1, 0, &augmented_media_packets, &media_packets); + PacketizeFrame(2, 1, &augmented_media_packets, &media_packets); std::list fec_packets; EncodeFec(media_packets, kNumFecPackets, &fec_packets); @@ -237,7 +287,7 @@ TEST_F(ReceiverFecTest, MaxFramesOneFec) { std::list augmented_media_packets; ForwardErrorCorrection::PacketList media_packets; for (size_t i = 0; i < kNumMediaPackets; ++i) { - GenerateFrame(1, i, &augmented_media_packets, &media_packets); + PacketizeFrame(1, i, &augmented_media_packets, &media_packets); } std::list fec_packets; EncodeFec(media_packets, kNumFecPackets, &fec_packets); @@ -262,7 +312,7 @@ TEST_F(ReceiverFecTest, TooManyFrames) { std::list augmented_media_packets; ForwardErrorCorrection::PacketList media_packets; for (size_t i = 0; i < kNumMediaPackets; ++i) { - GenerateFrame(1, i, &augmented_media_packets, &media_packets); + PacketizeFrame(1, i, &augmented_media_packets, &media_packets); } std::list fec_packets; EXPECT_EQ(-1, fec_->EncodeFec(media_packets, @@ -278,8 +328,8 @@ TEST_F(ReceiverFecTest, PacketNotDroppedTooEarly) { const size_t kNumMediaPacketsBatch1 = 2; std::list augmented_media_packets_batch1; ForwardErrorCorrection::PacketList media_packets_batch1; - GenerateFrame(kNumMediaPacketsBatch1, 0, &augmented_media_packets_batch1, - &media_packets_batch1); + PacketizeFrame(kNumMediaPacketsBatch1, 0, &augmented_media_packets_batch1, + &media_packets_batch1); std::list fec_packets; EncodeFec(media_packets_batch1, kNumFecPacketsBatch1, &fec_packets); @@ -294,7 +344,8 @@ TEST_F(ReceiverFecTest, PacketNotDroppedTooEarly) { std::list augmented_media_packets_batch2; ForwardErrorCorrection::PacketList media_packets_batch2; for (size_t i = 0; i < kNumMediaPacketsBatch2; ++i) { - GenerateFrame(1, i, &augmented_media_packets_batch2, &media_packets_batch2); + PacketizeFrame(1, i, &augmented_media_packets_batch2, + &media_packets_batch2); } for (auto it = augmented_media_packets_batch2.begin(); it != augmented_media_packets_batch2.end(); ++it) { @@ -319,8 +370,8 @@ TEST_F(ReceiverFecTest, PacketDroppedWhenTooOld) { const size_t kNumMediaPacketsBatch1 = 2; std::list augmented_media_packets_batch1; ForwardErrorCorrection::PacketList media_packets_batch1; - GenerateFrame(kNumMediaPacketsBatch1, 0, &augmented_media_packets_batch1, - &media_packets_batch1); + PacketizeFrame(kNumMediaPacketsBatch1, 0, &augmented_media_packets_batch1, + &media_packets_batch1); std::list fec_packets; EncodeFec(media_packets_batch1, kNumFecPacketsBatch1, &fec_packets); @@ -335,7 +386,8 @@ TEST_F(ReceiverFecTest, PacketDroppedWhenTooOld) { std::list augmented_media_packets_batch2; ForwardErrorCorrection::PacketList media_packets_batch2; for (size_t i = 0; i < kNumMediaPacketsBatch2; ++i) { - GenerateFrame(1, i, &augmented_media_packets_batch2, &media_packets_batch2); + PacketizeFrame(1, i, &augmented_media_packets_batch2, + &media_packets_batch2); } for (auto it = augmented_media_packets_batch2.begin(); it != augmented_media_packets_batch2.end(); ++it) { @@ -363,7 +415,7 @@ TEST_F(ReceiverFecTest, OldFecPacketDropped) { std::list frame_augmented_media_packets; ForwardErrorCorrection::PacketList frame_media_packets; std::list fec_packets; - GenerateFrame(2, 0, &frame_augmented_media_packets, &frame_media_packets); + PacketizeFrame(2, 0, &frame_augmented_media_packets, &frame_media_packets); EncodeFec(frame_media_packets, 1, &fec_packets); for (auto it = fec_packets.begin(); it != fec_packets.end(); ++it) { // Only FEC packets inserted. No packets recoverable at this time. @@ -389,21 +441,6 @@ TEST_F(ReceiverFecTest, OldFecPacketDropped) { EXPECT_EQ(0, receiver_fec_->ProcessReceivedFec()); } -void ReceiverFecTest::SurvivesMaliciousPacket(const uint8_t* data, - size_t length, - uint8_t ulpfec_payload_type) { - webrtc::RTPHeader header; - std::unique_ptr parser( - webrtc::RtpHeaderParser::Create()); - ASSERT_TRUE(parser->Parse(data, length, &header)); - - webrtc::NullRtpData null_callback; - std::unique_ptr receiver_fec( - webrtc::FecReceiver::Create(&null_callback)); - - receiver_fec->AddReceivedRedPacket(header, data, length, ulpfec_payload_type); -} - TEST_F(ReceiverFecTest, TruncatedPacketWithFBitSet) { const uint8_t kTruncatedPacket[] = {0x80, 0x2a,