From 78db1582e514b5ecd5bf9a38ef6a1613ba3a5752 Mon Sep 17 00:00:00 2001 From: Rasmus Brandt Date: Wed, 21 Sep 2016 09:19:34 +0200 Subject: [PATCH] Generalize FEC header formatting. - Split out reading/writing of FEC headers to classes separate from ForwardErrorCorrection. This makes ForwardErrorCorrection oblivious to what FEC header scheme is used, and lets it focus on encoding/decoding the FEC payloads. - Add unit tests for FEC header readers/writers. - Split ForwardErrorCorrection::XorPackets into XorHeaders and XorPayloads and reuse these functions for both encoding and decoding. - Rename AttemptRecover -> AttemptRecovery in ForwardErrorCorrection. BUG=webrtc:5654 R=danilchap@webrtc.org, stefan@webrtc.org Review URL: https://codereview.webrtc.org/2260803002 . Cr-Commit-Position: refs/heads/master@{#14316} --- webrtc/modules/BUILD.gn | 1 + webrtc/modules/rtp_rtcp/BUILD.gn | 2 + webrtc/modules/rtp_rtcp/rtp_rtcp.gypi | 2 + .../rtp_rtcp/source/fec_receiver_impl.cc | 7 +- .../rtp_rtcp/source/fec_receiver_impl.h | 4 +- .../rtp_rtcp/source/fec_receiver_unittest.cc | 16 +- .../source/forward_error_correction.cc | 614 ++++++++---------- .../source/forward_error_correction.h | 245 ++++--- .../forward_error_correction_internal.cc | 22 +- .../forward_error_correction_internal.h | 19 +- .../modules/rtp_rtcp/source/producer_fec.cc | 29 +- webrtc/modules/rtp_rtcp/source/producer_fec.h | 2 +- .../rtp_rtcp/source/rtp_fec_unittest.cc | 16 +- .../source/ulpfec_header_reader_writer.cc | 130 ++++ .../source/ulpfec_header_reader_writer.h | 66 ++ .../ulpfec_header_reader_writer_unittest.cc | 244 +++++++ .../modules/rtp_rtcp/test/testFec/test_fec.cc | 17 +- .../test/testFec/test_packet_masks_metrics.cc | 19 +- 18 files changed, 990 insertions(+), 465 deletions(-) create mode 100644 webrtc/modules/rtp_rtcp/source/ulpfec_header_reader_writer.cc create mode 100644 webrtc/modules/rtp_rtcp/source/ulpfec_header_reader_writer.h create mode 100644 webrtc/modules/rtp_rtcp/source/ulpfec_header_reader_writer_unittest.cc diff --git a/webrtc/modules/BUILD.gn b/webrtc/modules/BUILD.gn index 4a565c1a61..72af5792c1 100644 --- a/webrtc/modules/BUILD.gn +++ b/webrtc/modules/BUILD.gn @@ -446,6 +446,7 @@ if (rtc_include_tests) { "rtp_rtcp/source/rtp_rtcp_impl_unittest.cc", "rtp_rtcp/source/rtp_sender_unittest.cc", "rtp_rtcp/source/time_util_unittest.cc", + "rtp_rtcp/source/ulpfec_header_reader_writer_unittest.cc", "rtp_rtcp/source/vp8_partition_aggregator_unittest.cc", "rtp_rtcp/test/testAPI/test_api.cc", "rtp_rtcp/test/testAPI/test_api.h", diff --git a/webrtc/modules/rtp_rtcp/BUILD.gn b/webrtc/modules/rtp_rtcp/BUILD.gn index b06b9209f2..e448af07ca 100644 --- a/webrtc/modules/rtp_rtcp/BUILD.gn +++ b/webrtc/modules/rtp_rtcp/BUILD.gn @@ -149,6 +149,8 @@ rtc_source_set("rtp_rtcp") { "source/time_util.h", "source/tmmbr_help.cc", "source/tmmbr_help.h", + "source/ulpfec_header_reader_writer.cc", + "source/ulpfec_header_reader_writer.h", "source/video_codec_information.h", "source/vp8_partition_aggregator.cc", "source/vp8_partition_aggregator.h", diff --git a/webrtc/modules/rtp_rtcp/rtp_rtcp.gypi b/webrtc/modules/rtp_rtcp/rtp_rtcp.gypi index e03d56c09b..d5028f127c 100644 --- a/webrtc/modules/rtp_rtcp/rtp_rtcp.gypi +++ b/webrtc/modules/rtp_rtcp/rtp_rtcp.gypi @@ -158,6 +158,8 @@ 'source/rtp_format_vp9.h', 'source/rtp_format_video_generic.cc', 'source/rtp_format_video_generic.h', + 'source/ulpfec_header_reader_writer.cc', + 'source/ulpfec_header_reader_writer.h', 'source/vp8_partition_aggregator.cc', 'source/vp8_partition_aggregator.h', # Mocks diff --git a/webrtc/modules/rtp_rtcp/source/fec_receiver_impl.cc b/webrtc/modules/rtp_rtcp/source/fec_receiver_impl.cc index a4a6d2943b..c84ca5c9be 100644 --- a/webrtc/modules/rtp_rtcp/source/fec_receiver_impl.cc +++ b/webrtc/modules/rtp_rtcp/source/fec_receiver_impl.cc @@ -25,11 +25,12 @@ FecReceiver* FecReceiver::Create(RtpData* callback) { } FecReceiverImpl::FecReceiverImpl(RtpData* callback) - : recovered_packet_callback_(callback) {} + : recovered_packet_callback_(callback), + fec_(ForwardErrorCorrection::CreateUlpfec()) {} FecReceiverImpl::~FecReceiverImpl() { received_packets_.clear(); - fec_.ResetState(&recovered_packets_); + fec_->ResetState(&recovered_packets_); } FecPacketCounter FecReceiverImpl::GetPacketCounter() const { @@ -211,7 +212,7 @@ int32_t FecReceiverImpl::ProcessReceivedFec() { } crit_sect_.Enter(); } - if (fec_.DecodeFec(&received_packets_, &recovered_packets_) != 0) { + if (fec_->DecodeFec(&received_packets_, &recovered_packets_) != 0) { crit_sect_.Leave(); return -1; } diff --git a/webrtc/modules/rtp_rtcp/source/fec_receiver_impl.h b/webrtc/modules/rtp_rtcp/source/fec_receiver_impl.h index 2a3b8548bc..94eb3ac3d4 100644 --- a/webrtc/modules/rtp_rtcp/source/fec_receiver_impl.h +++ b/webrtc/modules/rtp_rtcp/source/fec_receiver_impl.h @@ -11,6 +11,8 @@ #ifndef WEBRTC_MODULES_RTP_RTCP_SOURCE_FEC_RECEIVER_IMPL_H_ #define WEBRTC_MODULES_RTP_RTCP_SOURCE_FEC_RECEIVER_IMPL_H_ +#include + #include "webrtc/base/criticalsection.h" #include "webrtc/modules/rtp_rtcp/include/fec_receiver.h" #include "webrtc/modules/rtp_rtcp/include/rtp_rtcp_defines.h" @@ -36,7 +38,7 @@ class FecReceiverImpl : public FecReceiver { private: rtc::CriticalSection crit_sect_; RtpData* recovered_packet_callback_; - ForwardErrorCorrection fec_; + std::unique_ptr fec_; // TODO(holmer): In the current version |received_packets_| is never more // than one packet, since we process FEC every time a new packet // arrives. We should remove the list. diff --git a/webrtc/modules/rtp_rtcp/source/fec_receiver_unittest.cc b/webrtc/modules/rtp_rtcp/source/fec_receiver_unittest.cc index 110e7c0b85..d2e1ce7e6b 100644 --- a/webrtc/modules/rtp_rtcp/source/fec_receiver_unittest.cc +++ b/webrtc/modules/rtp_rtcp/source/fec_receiver_unittest.cc @@ -39,14 +39,16 @@ using ::webrtc::test::fec::RawRtpPacket; class ReceiverFecTest : public ::testing::Test { protected: - ReceiverFecTest() : receiver_fec_(FecReceiver::Create(&rtp_data_callback_)) {} + ReceiverFecTest() + : fec_(ForwardErrorCorrection::CreateUlpfec()), + receiver_fec_(FecReceiver::Create(&rtp_data_callback_)) {} void EncodeFec(ForwardErrorCorrection::PacketList* media_packets, std::list* fec_packets, unsigned int num_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)); + EXPECT_EQ(0, fec_->EncodeFec(*media_packets, protection_factor, 0, false, + kFecMaskBursty, fec_packets)); ASSERT_EQ(num_fec_packets, fec_packets->size()); } @@ -95,7 +97,7 @@ class ReceiverFecTest : public ::testing::Test { uint8_t ulpfec_payload_type); MockRtpData rtp_data_callback_; - ForwardErrorCorrection fec_; + std::unique_ptr fec_; std::unique_ptr receiver_fec_; FrameGenerator generator_; }; @@ -260,9 +262,9 @@ TEST_F(ReceiverFecTest, TooManyFrames) { GenerateFrame(1, i, &media_rtp_packets, &media_packets); } std::list fec_packets; - EXPECT_EQ( - -1, fec_.EncodeFec(media_packets, kNumFecPackets * 255 / kNumMediaPackets, - 0, false, kFecMaskBursty, &fec_packets)); + EXPECT_EQ(-1, fec_->EncodeFec(media_packets, + kNumFecPackets * 255 / kNumMediaPackets, 0, + false, kFecMaskBursty, &fec_packets)); } TEST_F(ReceiverFecTest, PacketNotDroppedTooEarly) { diff --git a/webrtc/modules/rtp_rtcp/source/forward_error_correction.cc b/webrtc/modules/rtp_rtcp/source/forward_error_correction.cc index a89b71bf94..3106d6d38e 100644 --- a/webrtc/modules/rtp_rtcp/source/forward_error_correction.cc +++ b/webrtc/modules/rtp_rtcp/source/forward_error_correction.cc @@ -20,27 +20,17 @@ #include "webrtc/base/logging.h" #include "webrtc/modules/rtp_rtcp/include/rtp_rtcp_defines.h" #include "webrtc/modules/rtp_rtcp/source/byte_io.h" -#include "webrtc/modules/rtp_rtcp/source/forward_error_correction_internal.h" +#include "webrtc/modules/rtp_rtcp/source/ulpfec_header_reader_writer.h" namespace webrtc { -// FEC header size in bytes. -constexpr size_t kFecHeaderSize = 10; - -// ULP header size in bytes (L bit is set). -constexpr size_t kUlpHeaderSizeLBitSet = (2 + kMaskSizeLBitSet); - -// ULP header size in bytes (L bit is cleared). -constexpr size_t kUlpHeaderSizeLBitClear = (2 + kMaskSizeLBitClear); - +namespace { // Transport header size in bytes. Assume UDP/IPv4 as a reasonable minimum. constexpr size_t kTransportOverhead = 28; +} // namespace -// Maximum number of media packets that can be protected. -constexpr size_t ForwardErrorCorrection::kMaxMediaPackets; - -// Maximum number of FEC packets stored internally. -constexpr size_t kMaxFecPackets = ForwardErrorCorrection::kMaxMediaPackets; +ForwardErrorCorrection::Packet::Packet() : length(0), data(), ref_count_(0) {} +ForwardErrorCorrection::Packet::~Packet() = default; int32_t ForwardErrorCorrection::Packet::AddRef() { return ++ref_count_; @@ -65,35 +55,35 @@ bool ForwardErrorCorrection::SortablePacket::LessThan::operator() ( return IsNewerSequenceNumber(second->seq_num, first->seq_num); } -ForwardErrorCorrection::ReceivedPacket::ReceivedPacket() {} -ForwardErrorCorrection::ReceivedPacket::~ReceivedPacket() {} +ForwardErrorCorrection::ReceivedPacket::ReceivedPacket() = default; +ForwardErrorCorrection::ReceivedPacket::~ReceivedPacket() = default; -ForwardErrorCorrection::RecoveredPacket::RecoveredPacket() {} -ForwardErrorCorrection::RecoveredPacket::~RecoveredPacket() {} +ForwardErrorCorrection::RecoveredPacket::RecoveredPacket() = default; +ForwardErrorCorrection::RecoveredPacket::~RecoveredPacket() = default; -ForwardErrorCorrection::ForwardErrorCorrection() - : generated_fec_packets_(kMaxMediaPackets), received_fec_packets_(), - packet_mask_(), tmp_packet_mask_() {} -ForwardErrorCorrection::~ForwardErrorCorrection() {} +ForwardErrorCorrection::ProtectedPacket::ProtectedPacket() = default; +ForwardErrorCorrection::ProtectedPacket::~ProtectedPacket() = default; -// Input packet -// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -// | RTP Header (12 octets) | -// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -// | RTP Payload | -// | | -// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +ForwardErrorCorrection::ReceivedFecPacket::ReceivedFecPacket() = default; +ForwardErrorCorrection::ReceivedFecPacket::~ReceivedFecPacket() = default; + +ForwardErrorCorrection::ForwardErrorCorrection( + std::unique_ptr fec_header_reader, + std::unique_ptr fec_header_writer) + : fec_header_reader_(std::move(fec_header_reader)), + fec_header_writer_(std::move(fec_header_writer)), + generated_fec_packets_(fec_header_writer_->MaxFecPackets()), + packet_mask_size_(0) {} + +ForwardErrorCorrection::~ForwardErrorCorrection() = default; + +std::unique_ptr ForwardErrorCorrection::CreateUlpfec() { + std::unique_ptr fec_header_reader(new UlpfecHeaderReader()); + std::unique_ptr fec_header_writer(new UlpfecHeaderWriter()); + return std::unique_ptr(new ForwardErrorCorrection( + std::move(fec_header_reader), std::move(fec_header_writer))); +} -// Output packet -// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -// | FEC Header (10 octets) | -// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -// | FEC Level 0 Header | -// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -// | FEC Level 0 Payload | -// | | -// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -// int ForwardErrorCorrection::EncodeFec(const PacketList& media_packets, uint8_t protection_factor, int num_important_packets, @@ -107,17 +97,14 @@ int ForwardErrorCorrection::EncodeFec(const PacketList& media_packets, RTC_DCHECK_GE(num_important_packets, 0); RTC_DCHECK_LE(static_cast(num_important_packets), num_media_packets); RTC_DCHECK(fec_packets->empty()); - - if (num_media_packets > kMaxMediaPackets) { + const size_t max_media_packets = fec_header_writer_->MaxMediaPackets(); + if (num_media_packets > max_media_packets) { LOG(LS_WARNING) << "Can't protect " << num_media_packets - << " media packets per frame. Max is " << kMaxMediaPackets + << " media packets per frame. Max is " << max_media_packets << "."; return -1; } - bool l_bit = (num_media_packets > 8 * kMaskSizeLBitClear); - int num_mask_bytes = l_bit ? kMaskSizeLBitSet : kMaskSizeLBitClear; - // Error check the media packets. for (const auto& media_packet : media_packets) { RTC_DCHECK(media_packet); @@ -135,12 +122,11 @@ int ForwardErrorCorrection::EncodeFec(const PacketList& media_packets, } } + // Prepare generated FEC packets. int num_fec_packets = NumFecPackets(num_media_packets, protection_factor); if (num_fec_packets == 0) { return 0; } - - // Prepare generated FEC packets by setting them to 0. for (int i = 0; i < num_fec_packets; ++i) { memset(generated_fec_packets_[i].data, 0, IP_PACKET_SIZE); // Use this as a marker for untouched packets. @@ -149,26 +135,26 @@ int ForwardErrorCorrection::EncodeFec(const PacketList& media_packets, } const internal::PacketMaskTable mask_table(fec_mask_type, num_media_packets); - - // -- Generate packet masks -- - memset(packet_mask_, 0, num_fec_packets * num_mask_bytes); + packet_mask_size_ = internal::PacketMaskSize(num_media_packets); + memset(packet_masks_, 0, num_fec_packets * packet_mask_size_); internal::GeneratePacketMasks(num_media_packets, num_fec_packets, num_important_packets, use_unequal_protection, - mask_table, packet_mask_); - - int num_mask_bits = InsertZerosInBitMasks( - media_packets, packet_mask_, num_mask_bytes, num_fec_packets); + mask_table, packet_masks_); + // Adapt packet masks to missing media packets. + int num_mask_bits = InsertZerosInPacketMasks(media_packets, num_fec_packets); if (num_mask_bits < 0) { return -1; } - l_bit = (static_cast(num_mask_bits) > 8 * kMaskSizeLBitClear); - if (l_bit) { - num_mask_bytes = kMaskSizeLBitSet; - } + packet_mask_size_ = internal::PacketMaskSize(num_mask_bits); - GenerateFecBitStrings(media_packets, packet_mask_, num_fec_packets, l_bit); - GenerateFecUlpHeaders(media_packets, packet_mask_, num_fec_packets, l_bit); + // Write FEC packets to |generated_fec_packets_|. + GenerateFecPayloads(media_packets, num_fec_packets); + // TODO(brandtr): Generalize this when multistream protection support is + // added. + const uint16_t seq_num_base = + ParseSequenceNumber(media_packets.front().get()->data); + FinalizeFecHeaders(num_fec_packets, seq_num_base); return 0; } @@ -185,74 +171,53 @@ int ForwardErrorCorrection::NumFecPackets(int num_media_packets, return num_fec_packets; } -void ForwardErrorCorrection::GenerateFecBitStrings( +void ForwardErrorCorrection::GenerateFecPayloads( const PacketList& media_packets, - uint8_t* packet_mask, - int num_fec_packets, - bool l_bit) { + size_t num_fec_packets) { RTC_DCHECK(!media_packets.empty()); - uint8_t media_payload_length[2]; - const int num_mask_bytes = l_bit ? kMaskSizeLBitSet : kMaskSizeLBitClear; - const uint16_t ulp_header_size = - l_bit ? kUlpHeaderSizeLBitSet : kUlpHeaderSizeLBitClear; - const uint16_t fec_rtp_offset = - kFecHeaderSize + ulp_header_size - kRtpHeaderSize; - - for (int i = 0; i < num_fec_packets; ++i) { + for (size_t i = 0; i < num_fec_packets; ++i) { Packet* const fec_packet = &generated_fec_packets_[i]; + size_t pkt_mask_idx = i * packet_mask_size_; + const size_t min_packet_mask_size = fec_header_writer_->MinPacketMaskSize( + &packet_masks_[pkt_mask_idx], packet_mask_size_); + const size_t fec_header_size = + fec_header_writer_->FecHeaderSize(min_packet_mask_size); + + size_t media_pkt_idx = 0; auto media_packets_it = media_packets.cbegin(); - uint32_t pkt_mask_idx = i * num_mask_bytes; - uint32_t media_pkt_idx = 0; - uint16_t fec_packet_length = 0; uint16_t prev_seq_num = ParseSequenceNumber((*media_packets_it)->data); while (media_packets_it != media_packets.end()) { - // Each FEC packet has a multiple byte mask. Determine if this media - // packet should be included in FEC packet i. - if (packet_mask[pkt_mask_idx] & (1 << (7 - media_pkt_idx))) { - Packet* media_packet = media_packets_it->get(); + Packet* const media_packet = media_packets_it->get(); + // Should |media_packet| be protected by |fec_packet|? + if (packet_masks_[pkt_mask_idx] & (1 << (7 - media_pkt_idx))) { + size_t media_payload_length = media_packet->length - kRtpHeaderSize; - // Assign network-ordered media payload length. - ByteWriter::WriteBigEndian( - media_payload_length, media_packet->length - kRtpHeaderSize); - - fec_packet_length = media_packet->length + fec_rtp_offset; - // On the first protected packet, we don't need to XOR. - if (fec_packet->length == 0) { - // Copy the first 2 bytes of the RTP header. Note that the E and L - // bits are overwritten in GenerateFecUlpHeaders. - memcpy(&fec_packet->data[0], &media_packet->data[0], 2); - // Copy the 5th to 8th bytes of the RTP header (timestamp). - memcpy(&fec_packet->data[4], &media_packet->data[4], 4); - // Copy network-ordered payload size. - memcpy(&fec_packet->data[8], media_payload_length, 2); - - // Copy RTP payload, leaving room for the ULP header. - memcpy(&fec_packet->data[kFecHeaderSize + ulp_header_size], - &media_packet->data[kRtpHeaderSize], - media_packet->length - kRtpHeaderSize); - } else { - // XOR with the first 2 bytes of the RTP header. - fec_packet->data[0] ^= media_packet->data[0]; - fec_packet->data[1] ^= media_packet->data[1]; - - // XOR with the 5th to 8th bytes of the RTP header. - for (uint32_t j = 4; j < 8; ++j) { - fec_packet->data[j] ^= media_packet->data[j]; - } - - // XOR with the network-ordered payload size. - fec_packet->data[8] ^= media_payload_length[0]; - fec_packet->data[9] ^= media_payload_length[1]; - - // XOR with RTP payload, leaving room for the ULP header. - for (int32_t j = kFecHeaderSize + ulp_header_size; - j < fec_packet_length; j++) { - fec_packet->data[j] ^= media_packet->data[j - fec_rtp_offset]; - } - } + bool first_protected_packet = (fec_packet->length == 0); + size_t fec_packet_length = fec_header_size + media_payload_length; if (fec_packet_length > fec_packet->length) { + // Recall that XORing with zero (which the FEC packets are prefilled + // with) is the identity operator, thus all prior XORs are + // still correct even though we expand the packet length here. fec_packet->length = fec_packet_length; } + if (first_protected_packet) { + // Write P, X, CC, M, and PT recovery fields. + // Note that bits 0, 1, and 16 are overwritten in FinalizeFecHeaders. + memcpy(&fec_packet->data[0], &media_packet->data[0], 2); + // Write length recovery field. (This is a temporary location for + // ULPFEC.) + ByteWriter::WriteBigEndian(&fec_packet->data[2], + media_payload_length); + // Write timestamp recovery field. + memcpy(&fec_packet->data[4], &media_packet->data[4], 4); + // Write payload. + memcpy(&fec_packet->data[fec_header_size], + &media_packet->data[kRtpHeaderSize], media_payload_length); + } else { + XorHeaders(*media_packet, fec_packet); + XorPayloads(*media_packet, media_payload_length, fec_header_size, + fec_packet); + } } media_packets_it++; if (media_packets_it != media_packets.end()) { @@ -268,47 +233,44 @@ void ForwardErrorCorrection::GenerateFecBitStrings( } } -int ForwardErrorCorrection::InsertZerosInBitMasks( +int ForwardErrorCorrection::InsertZerosInPacketMasks( const PacketList& media_packets, - uint8_t* packet_mask, - int num_mask_bytes, - int num_fec_packets) { - if (media_packets.size() <= 1) { - return media_packets.size(); + size_t num_fec_packets) { + size_t num_media_packets = media_packets.size(); + if (num_media_packets <= 1) { + return num_media_packets; } - int last_seq_num = ParseSequenceNumber(media_packets.back()->data); - int first_seq_num = ParseSequenceNumber(media_packets.front()->data); - int total_missing_seq_nums = - static_cast(last_seq_num - first_seq_num) - - media_packets.size() + 1; + uint16_t last_seq_num = ParseSequenceNumber(media_packets.back()->data); + uint16_t first_seq_num = ParseSequenceNumber(media_packets.front()->data); + size_t total_missing_seq_nums = + static_cast(last_seq_num - first_seq_num) - num_media_packets + + 1; if (total_missing_seq_nums == 0) { - // All sequence numbers are covered by the packet mask. No zero insertion - // required. - return media_packets.size(); + // All sequence numbers are covered by the packet mask. + // No zero insertion required. + return num_media_packets; } - // We can only protect 8 * kMaskSizeLBitSet packets. - if (total_missing_seq_nums + media_packets.size() > 8 * kMaskSizeLBitSet) + const size_t max_media_packets = fec_header_writer_->MaxMediaPackets(); + if (total_missing_seq_nums + num_media_packets > max_media_packets) { return -1; - // Allocate the new mask. - int new_mask_bytes = kMaskSizeLBitClear; - if (media_packets.size() + - total_missing_seq_nums > 8 * kMaskSizeLBitClear) { - new_mask_bytes = kMaskSizeLBitSet; } - memset(tmp_packet_mask_, 0, num_fec_packets * kMaskSizeLBitSet); + // Allocate the new mask. + size_t tmp_packet_mask_size = + internal::PacketMaskSize(total_missing_seq_nums + num_media_packets); + memset(tmp_packet_masks_, 0, num_fec_packets * tmp_packet_mask_size); auto media_packets_it = media_packets.cbegin(); uint16_t prev_seq_num = first_seq_num; ++media_packets_it; // Insert the first column. - internal::CopyColumn(tmp_packet_mask_, new_mask_bytes, packet_mask_, - num_mask_bytes, num_fec_packets, 0, 0); + internal::CopyColumn(tmp_packet_masks_, tmp_packet_mask_size, packet_masks_, + packet_mask_size_, num_fec_packets, 0, 0); size_t new_bit_index = 1; size_t old_bit_index = 1; // Insert zeros in the bit mask for every hole in the sequence. while (media_packets_it != media_packets.end()) { - if (new_bit_index == 8 * kMaskSizeLBitSet) { + if (new_bit_index == max_media_packets) { // We can only cover up to 48 packets. break; } @@ -316,13 +278,13 @@ int ForwardErrorCorrection::InsertZerosInBitMasks( const int num_zeros_to_insert = static_cast(seq_num - prev_seq_num - 1); if (num_zeros_to_insert > 0) { - internal::InsertZeroColumns(num_zeros_to_insert, tmp_packet_mask_, - new_mask_bytes, num_fec_packets, + internal::InsertZeroColumns(num_zeros_to_insert, tmp_packet_masks_, + tmp_packet_mask_size, num_fec_packets, new_bit_index); } new_bit_index += num_zeros_to_insert; - internal::CopyColumn(tmp_packet_mask_, new_mask_bytes, packet_mask_, - num_mask_bytes, num_fec_packets, new_bit_index, + internal::CopyColumn(tmp_packet_masks_, tmp_packet_mask_size, packet_masks_, + packet_mask_size_, num_fec_packets, new_bit_index, old_bit_index); ++new_bit_index; ++old_bit_index; @@ -332,73 +294,22 @@ int ForwardErrorCorrection::InsertZerosInBitMasks( if (new_bit_index % 8 != 0) { // We didn't fill the last byte. Shift bits to correct position. for (uint16_t row = 0; row < num_fec_packets; ++row) { - int new_byte_index = row * new_mask_bytes + new_bit_index / 8; - tmp_packet_mask_[new_byte_index] <<= (7 - (new_bit_index % 8)); + int new_byte_index = row * tmp_packet_mask_size + new_bit_index / 8; + tmp_packet_masks_[new_byte_index] <<= (7 - (new_bit_index % 8)); } } // Replace the old mask with the new. - memcpy(packet_mask, tmp_packet_mask_, kMaskSizeLBitSet * num_fec_packets); + memcpy(packet_masks_, tmp_packet_masks_, + num_fec_packets * tmp_packet_mask_size); return new_bit_index; } -void ForwardErrorCorrection::GenerateFecUlpHeaders( - const PacketList& media_packets, - uint8_t* packet_mask, - int num_fec_packets, - bool l_bit) { - // -- Generate FEC and ULP headers -- - // - // FEC Header, 10 bytes - // 0 1 2 3 - // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - // |E|L|P|X| CC |M| PT recovery | SN base | - // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - // | TS recovery | - // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - // | length recovery | - // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - // - // ULP Header, 4 bytes (for L = 0) - // 0 1 2 3 - // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - // | Protection Length | mask | - // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - // | mask cont. (present only when L = 1) | - // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - int num_mask_bytes = l_bit ? kMaskSizeLBitSet : kMaskSizeLBitClear; - const uint16_t ulp_header_size = - l_bit ? kUlpHeaderSizeLBitSet : kUlpHeaderSizeLBitClear; - - RTC_DCHECK(!media_packets.empty()); - Packet* first_media_packet = media_packets.front().get(); - RTC_DCHECK(first_media_packet); - uint16_t seq_num = ParseSequenceNumber(first_media_packet->data); - for (int i = 0; i < num_fec_packets; ++i) { - Packet* const fec_packet = &generated_fec_packets_[i]; - // -- FEC header -- - fec_packet->data[0] &= 0x7f; // Set E to zero. - if (l_bit == 0) { - fec_packet->data[0] &= 0xbf; // Clear the L bit. - } else { - fec_packet->data[0] |= 0x40; // Set the L bit. - } - // Sequence number from first media packet used as SN base. - // We use the same sequence number base for every FEC packet, - // but that's not required in general. - ByteWriter::WriteBigEndian(&fec_packet->data[2], seq_num); - - // -- ULP header -- - // Copy the payload size to the protection length field. - // (We protect the entire packet.) - ByteWriter::WriteBigEndian( - &fec_packet->data[10], - fec_packet->length - kFecHeaderSize - ulp_header_size); - - // Copy the packet mask. - memcpy(&fec_packet->data[12], &packet_mask[i * num_mask_bytes], - num_mask_bytes); +void ForwardErrorCorrection::FinalizeFecHeaders(size_t num_fec_packets, + uint16_t seq_num_base) { + for (size_t i = 0; i < num_fec_packets; ++i) { + fec_header_writer_->FinalizeFecHeader( + seq_num_base, &packet_masks_[i * packet_mask_size_], packet_mask_size_, + &generated_fec_packets_[i]); } } @@ -410,9 +321,8 @@ void ForwardErrorCorrection::ResetState( } void ForwardErrorCorrection::InsertMediaPacket( - ReceivedPacket* received_packet, - RecoveredPacketList* recovered_packets) { - + RecoveredPacketList* recovered_packets, + ReceivedPacket* received_packet) { // Search for duplicate packets. for (const auto& recovered_packet : *recovered_packets) { if (received_packet->seq_num == recovered_packet->seq_num) { @@ -422,7 +332,6 @@ void ForwardErrorCorrection::InsertMediaPacket( return; } } - std::unique_ptr recovered_packet(new RecoveredPacket()); // This "recovered packet" was not recovered using parity packets. recovered_packet->was_recovered = false; @@ -431,33 +340,32 @@ void ForwardErrorCorrection::InsertMediaPacket( recovered_packet->seq_num = received_packet->seq_num; recovered_packet->pkt = received_packet->pkt; recovered_packet->pkt->length = received_packet->pkt->length; - - RecoveredPacket* recovered_packet_ptr = recovered_packet.get(); // TODO(holmer): Consider replacing this with a binary search for the right // position, and then just insert the new packet. Would get rid of the sort. + RecoveredPacket* recovered_packet_ptr = recovered_packet.get(); recovered_packets->push_back(std::move(recovered_packet)); recovered_packets->sort(SortablePacket::LessThan()); - UpdateCoveringFecPackets(recovered_packet_ptr); + UpdateCoveringFecPackets(*recovered_packet_ptr); } -void ForwardErrorCorrection::UpdateCoveringFecPackets(RecoveredPacket* packet) { +void ForwardErrorCorrection::UpdateCoveringFecPackets( + const RecoveredPacket& packet) { for (auto& fec_packet : received_fec_packets_) { // Is this FEC packet protecting the media packet |packet|? auto protected_it = std::lower_bound(fec_packet->protected_packets.begin(), fec_packet->protected_packets.end(), - packet, - SortablePacket::LessThan()); + &packet, SortablePacket::LessThan()); if (protected_it != fec_packet->protected_packets.end() && - (*protected_it)->seq_num == packet->seq_num) { + (*protected_it)->seq_num == packet.seq_num) { // Found an FEC packet which is protecting |packet|. - (*protected_it)->pkt = packet->pkt; + (*protected_it)->pkt = packet.pkt; } } } void ForwardErrorCorrection::InsertFecPacket( - ReceivedPacket* received_packet, - const RecoveredPacketList* recovered_packets) { + const RecoveredPacketList& recovered_packets, + ReceivedPacket* received_packet) { // Check for duplicate. for (const auto& existing_fec_packet : received_fec_packets_) { if (received_packet->seq_num == existing_fec_packet->seq_num) { @@ -466,31 +374,29 @@ void ForwardErrorCorrection::InsertFecPacket( return; } } - std::unique_ptr fec_packet(new ReceivedFecPacket()); fec_packet->pkt = received_packet->pkt; fec_packet->seq_num = received_packet->seq_num; fec_packet->ssrc = received_packet->ssrc; - - const uint16_t seq_num_base = - ByteReader::ReadBigEndian(&fec_packet->pkt->data[2]); - const uint16_t mask_size_bytes = (fec_packet->pkt->data[0] & 0x40) - ? kMaskSizeLBitSet - : kMaskSizeLBitClear; // L bit set? - - // Parse erasure code mask from ULP header and represent as protected packets. - for (uint16_t byte_idx = 0; byte_idx < mask_size_bytes; ++byte_idx) { - uint8_t packet_mask = fec_packet->pkt->data[12 + byte_idx]; + // Parse ULPFEC/FlexFEC header specific info. + bool ret = fec_header_reader_->ReadFecHeader(fec_packet.get()); + if (!ret) { + LOG(LS_WARNING) << "Malformed FEC header: dropping packet."; + return; + } + // Parse packet mask from header and represent as protected packets. + for (uint16_t byte_idx = 0; byte_idx < fec_packet->packet_mask_size; + ++byte_idx) { + uint8_t packet_mask = + fec_packet->pkt->data[fec_packet->packet_mask_offset + byte_idx]; for (uint16_t bit_idx = 0; bit_idx < 8; ++bit_idx) { if (packet_mask & (1 << (7 - bit_idx))) { std::unique_ptr protected_packet( new ProtectedPacket()); // This wraps naturally with the sequence number. - protected_packet->seq_num = - static_cast(seq_num_base + (byte_idx << 3) + bit_idx); + protected_packet->seq_num = static_cast( + fec_packet->seq_num_base + (byte_idx << 3) + bit_idx); protected_packet->pkt = nullptr; - // Note that |protected_pkt_list| is sorted (according to sequence - // number) by construction. fec_packet->protected_packets.push_back(std::move(protected_packet)); } } @@ -499,26 +405,27 @@ void ForwardErrorCorrection::InsertFecPacket( // All-zero packet mask; we can discard this FEC packet. LOG(LS_WARNING) << "Received FEC packet has an all-zero packet mask."; } else { - AssignRecoveredPackets(fec_packet.get(), recovered_packets); + AssignRecoveredPackets(recovered_packets, fec_packet.get()); // TODO(holmer): Consider replacing this with a binary search for the right // position, and then just insert the new packet. Would get rid of the sort. // - // For correct decoding, |fec_packet_list_| does not necessarily + // For correct decoding, |received_fec_packets_| does not necessarily // need to be sorted by sequence number (see decoding algorithm in - // AttemptRecover()), but by keeping it sorted we try to recover the - // oldest lost packets first. + // AttemptRecover()). By keeping it sorted we try to recover the + // oldest lost packets first, however. received_fec_packets_.push_back(std::move(fec_packet)); received_fec_packets_.sort(SortablePacket::LessThan()); - if (received_fec_packets_.size() > kMaxFecPackets) { + const size_t max_fec_packets = fec_header_reader_->MaxFecPackets(); + if (received_fec_packets_.size() > max_fec_packets) { received_fec_packets_.pop_front(); } - RTC_DCHECK_LE(received_fec_packets_.size(), kMaxFecPackets); + RTC_DCHECK_LE(received_fec_packets_.size(), max_fec_packets); } } void ForwardErrorCorrection::AssignRecoveredPackets( - ReceivedFecPacket* fec_packet, - const RecoveredPacketList* recovered_packets) { + const RecoveredPacketList& recovered_packets, + ReceivedFecPacket* fec_packet) { ProtectedPacketList* protected_packets = &fec_packet->protected_packets; std::vector recovered_protected_packets; @@ -527,9 +434,9 @@ void ForwardErrorCorrection::AssignRecoveredPackets( // been recovered. Update the corresponding protected packets to point to // the recovered packets. auto it_p = protected_packets->cbegin(); - auto it_r = recovered_packets->cbegin(); + auto it_r = recovered_packets.cbegin(); SortablePacket::LessThan less_than; - while (it_p != protected_packets->end() && it_r != recovered_packets->end()) { + while (it_p != protected_packets->end() && it_r != recovered_packets.end()) { if (less_than(*it_p, *it_r)) { ++it_p; } else if (less_than(*it_r, *it_p)) { @@ -566,9 +473,9 @@ void ForwardErrorCorrection::InsertPackets( } if (received_packet->is_fec) { - InsertFecPacket(received_packet, recovered_packets); + InsertFecPacket(*recovered_packets, received_packet); } else { - InsertMediaPacket(received_packet, recovered_packets); + InsertMediaPacket(recovered_packets, received_packet); } // Delete the received packet "wrapper". received_packets->pop_front(); @@ -578,137 +485,143 @@ void ForwardErrorCorrection::InsertPackets( } bool ForwardErrorCorrection::StartPacketRecovery( - const ReceivedFecPacket* fec_packet, - RecoveredPacket* recovered_packet) { - // This is the first packet which we try to recover with. - const uint16_t ulp_header_size = fec_packet->pkt->data[0] & 0x40 - ? kUlpHeaderSizeLBitSet - : kUlpHeaderSizeLBitClear; // L bit set? - if (fec_packet->pkt->length < - static_cast(kFecHeaderSize + ulp_header_size)) { + const ReceivedFecPacket& fec_packet, + RecoveredPacket* recovered_packet) { + // Sanity check packet length. + if (fec_packet.pkt->length < fec_packet.fec_header_size) { LOG(LS_WARNING) - << "Truncated FEC packet doesn't contain room for ULP header."; + << "The FEC packet is truncated: it does not contain enough room " + << "for its own header."; return false; } + // Initialize recovered packet data. recovered_packet->pkt = new Packet(); memset(recovered_packet->pkt->data, 0, IP_PACKET_SIZE); recovered_packet->returned = false; recovered_packet->was_recovered = true; - uint16_t protection_length = - ByteReader::ReadBigEndian(&fec_packet->pkt->data[10]); - if (protection_length > - std::min( - sizeof(recovered_packet->pkt->data) - kRtpHeaderSize, - sizeof(fec_packet->pkt->data) - kFecHeaderSize - ulp_header_size)) { - LOG(LS_WARNING) << "Incorrect FEC protection length, dropping."; + // Copy bytes corresponding to minimum RTP header size. + // Note that the sequence number and SSRC fields will be overwritten + // at the end of packet recovery. + memcpy(&recovered_packet->pkt->data, fec_packet.pkt->data, kRtpHeaderSize); + // Copy remaining FEC payload. + if (fec_packet.protection_length > + std::min(sizeof(recovered_packet->pkt->data) - kRtpHeaderSize, + sizeof(fec_packet.pkt->data) - fec_packet.fec_header_size)) { + LOG(LS_WARNING) << "Incorrect protection length, dropping FEC packet."; return false; } - // Copy FEC payload, skipping the ULP header. memcpy(&recovered_packet->pkt->data[kRtpHeaderSize], - &fec_packet->pkt->data[kFecHeaderSize + ulp_header_size], - protection_length); - // Copy the length recovery field. - memcpy(recovered_packet->length_recovery, &fec_packet->pkt->data[8], 2); - // Copy the first 2 bytes of the FEC header. - memcpy(recovered_packet->pkt->data, fec_packet->pkt->data, 2); - // Copy the 5th to 8th bytes of the FEC header. - memcpy(&recovered_packet->pkt->data[4], &fec_packet->pkt->data[4], 4); - // Set the SSRC field. - ByteWriter::WriteBigEndian(&recovered_packet->pkt->data[8], - fec_packet->ssrc); + &fec_packet.pkt->data[fec_packet.fec_header_size], + fec_packet.protection_length); return true; } bool ForwardErrorCorrection::FinishPacketRecovery( + const ReceivedFecPacket& fec_packet, RecoveredPacket* recovered_packet) { // Set the RTP version to 2. recovered_packet->pkt->data[0] |= 0x80; // Set the 1st bit. recovered_packet->pkt->data[0] &= 0xbf; // Clear the 2nd bit. - - // Set the SN field. - ByteWriter::WriteBigEndian(&recovered_packet->pkt->data[2], - recovered_packet->seq_num); - // Recover the packet length. + // Recover the packet length, from temporary location. recovered_packet->pkt->length = - ByteReader::ReadBigEndian(recovered_packet->length_recovery) + + ByteReader::ReadBigEndian(&recovered_packet->pkt->data[2]) + kRtpHeaderSize; if (recovered_packet->pkt->length > sizeof(recovered_packet->pkt->data) - kRtpHeaderSize) { + LOG(LS_WARNING) << "The recovered packet had a length larger than a " + << "typical IP packet, and is thus dropped."; return false; } - + // Set the SN field. + ByteWriter::WriteBigEndian(&recovered_packet->pkt->data[2], + recovered_packet->seq_num); + // Set the SSRC field. + ByteWriter::WriteBigEndian(&recovered_packet->pkt->data[8], + fec_packet.protected_ssrc); return true; } -void ForwardErrorCorrection::XorPackets(const Packet* src, - RecoveredPacket* dst) { - // XOR with the first 2 bytes of the RTP header. - for (uint32_t i = 0; i < 2; ++i) { - dst->pkt->data[i] ^= src->data[i]; - } - // XOR with the 5th to 8th bytes of the RTP header. - for (uint32_t i = 4; i < 8; ++i) { - dst->pkt->data[i] ^= src->data[i]; - } - // XOR with the network-ordered payload size. - uint8_t media_payload_length[2]; - ByteWriter::WriteBigEndian(media_payload_length, - src->length - kRtpHeaderSize); - dst->length_recovery[0] ^= media_payload_length[0]; - dst->length_recovery[1] ^= media_payload_length[1]; +void ForwardErrorCorrection::XorHeaders(const Packet& src, Packet* dst) { + // XOR the first 2 bytes of the header: V, P, X, CC, M, PT fields. + dst->data[0] ^= src.data[0]; + dst->data[1] ^= src.data[1]; - // XOR with RTP payload. - // TODO(marpan/ajm): Are we doing more XORs than required here? - for (size_t i = kRtpHeaderSize; i < src->length; ++i) { - dst->pkt->data[i] ^= src->data[i]; + // XOR the length recovery field. + uint8_t src_payload_length_network_order[2]; + ByteWriter::WriteBigEndian(src_payload_length_network_order, + src.length - kRtpHeaderSize); + dst->data[2] ^= src_payload_length_network_order[0]; + dst->data[3] ^= src_payload_length_network_order[1]; + + // XOR the 5th to 8th bytes of the header: the timestamp field. + dst->data[4] ^= src.data[4]; + dst->data[5] ^= src.data[5]; + dst->data[6] ^= src.data[6]; + dst->data[7] ^= src.data[7]; + + // Skip the 9th to 12th bytes of the header. +} + +void ForwardErrorCorrection::XorPayloads(const Packet& src, + size_t payload_length, + size_t dst_offset, + Packet* dst) { + // XOR the payload. + RTC_DCHECK_LE(kRtpHeaderSize + payload_length, sizeof(src.data)); + RTC_DCHECK_LE(dst_offset + payload_length, sizeof(dst->data)); + for (size_t i = 0; i < payload_length; ++i) { + dst->data[dst_offset + i] ^= src.data[kRtpHeaderSize + i]; } } -bool ForwardErrorCorrection::RecoverPacket( - const ReceivedFecPacket* fec_packet, - RecoveredPacket* rec_packet_to_insert) { - if (!StartPacketRecovery(fec_packet, rec_packet_to_insert)) +bool ForwardErrorCorrection::RecoverPacket(const ReceivedFecPacket& fec_packet, + RecoveredPacket* recovered_packet) { + if (!StartPacketRecovery(fec_packet, recovered_packet)) { return false; - for (const auto& protected_packet : fec_packet->protected_packets) { + } + for (const auto& protected_packet : fec_packet.protected_packets) { if (protected_packet->pkt == nullptr) { // This is the packet we're recovering. - rec_packet_to_insert->seq_num = protected_packet->seq_num; + recovered_packet->seq_num = protected_packet->seq_num; } else { - XorPackets(protected_packet->pkt, rec_packet_to_insert); + XorHeaders(*protected_packet->pkt, recovered_packet->pkt); + XorPayloads(*protected_packet->pkt, protected_packet->pkt->length, + kRtpHeaderSize, recovered_packet->pkt); } } - if (!FinishPacketRecovery(rec_packet_to_insert)) + if (!FinishPacketRecovery(fec_packet, recovered_packet)) { return false; + } return true; } -void ForwardErrorCorrection::AttemptRecover( +void ForwardErrorCorrection::AttemptRecovery( RecoveredPacketList* recovered_packets) { auto fec_packet_it = received_fec_packets_.begin(); while (fec_packet_it != received_fec_packets_.end()) { // Search for each FEC packet's protected media packets. - int packets_missing = NumCoveredPacketsMissing(fec_packet_it->get()); + int packets_missing = NumCoveredPacketsMissing(**fec_packet_it); // We can only recover one packet with an FEC packet. if (packets_missing == 1) { // Recovery possible. - std::unique_ptr packet_to_insert(new RecoveredPacket()); - packet_to_insert->pkt = nullptr; - if (!RecoverPacket(fec_packet_it->get(), packet_to_insert.get())) { + std::unique_ptr recovered_packet(new RecoveredPacket()); + recovered_packet->pkt = nullptr; + if (!RecoverPacket(**fec_packet_it, recovered_packet.get())) { // Can't recover using this packet, drop it. fec_packet_it = received_fec_packets_.erase(fec_packet_it); continue; } - auto packet_to_insert_ptr = packet_to_insert.get(); + auto recovered_packet_ptr = recovered_packet.get(); // Add recovered packet to the list of recovered packets and update any // FEC packets covering this packet with a pointer to the data. // TODO(holmer): Consider replacing this with a binary search for the // right position, and then just insert the new packet. Would get rid of // the sort. - recovered_packets->push_back(std::move(packet_to_insert)); + recovered_packets->push_back(std::move(recovered_packet)); recovered_packets->sort(SortablePacket::LessThan()); - UpdateCoveringFecPackets(packet_to_insert_ptr); + UpdateCoveringFecPackets(*recovered_packet_ptr); DiscardOldRecoveredPackets(recovered_packets); fec_packet_it = received_fec_packets_.erase(fec_packet_it); @@ -727,9 +640,9 @@ void ForwardErrorCorrection::AttemptRecover( } int ForwardErrorCorrection::NumCoveredPacketsMissing( - const ReceivedFecPacket* fec_packet) { + const ReceivedFecPacket& fec_packet) { int packets_missing = 0; - for (const auto& protected_packet : fec_packet->protected_packets) { + for (const auto& protected_packet : fec_packet.protected_packets) { if (protected_packet->pkt == nullptr) { ++packets_missing; if (packets_missing > 1) { @@ -742,37 +655,80 @@ int ForwardErrorCorrection::NumCoveredPacketsMissing( void ForwardErrorCorrection::DiscardOldRecoveredPackets( RecoveredPacketList* recovered_packets) { - while (recovered_packets->size() > kMaxMediaPackets) { + const size_t max_media_packets = fec_header_reader_->MaxMediaPackets(); + while (recovered_packets->size() > max_media_packets) { recovered_packets->pop_front(); } - RTC_DCHECK_LE(recovered_packets->size(), kMaxMediaPackets); + RTC_DCHECK_LE(recovered_packets->size(), max_media_packets); } uint16_t ForwardErrorCorrection::ParseSequenceNumber(uint8_t* packet) { return (packet[2] << 8) + packet[3]; } +uint32_t ForwardErrorCorrection::ParseSsrc(uint8_t* packet) { + return (packet[8] << 24) + (packet[9] << 16) + (packet[10] << 8) + packet[11]; +} + int ForwardErrorCorrection::DecodeFec( ReceivedPacketList* received_packets, RecoveredPacketList* recovered_packets) { // TODO(marpan/ajm): can we check for multiple ULP headers, and return an // error? - if (recovered_packets->size() == kMaxMediaPackets) { + const size_t max_media_packets = fec_header_reader_->MaxMediaPackets(); + if (recovered_packets->size() == max_media_packets) { const unsigned int seq_num_diff = abs(static_cast(received_packets->front()->seq_num) - static_cast(recovered_packets->back()->seq_num)); - if (seq_num_diff > kMaxMediaPackets) { + if (seq_num_diff > max_media_packets) { // A big gap in sequence numbers. The old recovered packets // are now useless, so it's safe to do a reset. ResetState(recovered_packets); } } InsertPackets(received_packets, recovered_packets); - AttemptRecover(recovered_packets); + AttemptRecovery(recovered_packets); return 0; } size_t ForwardErrorCorrection::MaxPacketOverhead() const { - return kFecHeaderSize + kUlpHeaderSizeLBitSet; + return fec_header_writer_->MaxPacketOverhead(); } + +FecHeaderReader::FecHeaderReader(size_t max_media_packets, + size_t max_fec_packets) + : max_media_packets_(max_media_packets), + max_fec_packets_(max_fec_packets) {} + +FecHeaderReader::~FecHeaderReader() = default; + +size_t FecHeaderReader::MaxMediaPackets() const { + return max_media_packets_; +} + +size_t FecHeaderReader::MaxFecPackets() const { + return max_fec_packets_; +} + +FecHeaderWriter::FecHeaderWriter(size_t max_media_packets, + size_t max_fec_packets, + size_t max_packet_overhead) + : max_media_packets_(max_media_packets), + max_fec_packets_(max_fec_packets), + max_packet_overhead_(max_packet_overhead) {} + +FecHeaderWriter::~FecHeaderWriter() = default; + +size_t FecHeaderWriter::MaxMediaPackets() const { + return max_media_packets_; +} + +size_t FecHeaderWriter::MaxFecPackets() const { + return max_fec_packets_; +} + +size_t FecHeaderWriter::MaxPacketOverhead() const { + return max_packet_overhead_; +} + } // namespace webrtc diff --git a/webrtc/modules/rtp_rtcp/source/forward_error_correction.h b/webrtc/modules/rtp_rtcp/source/forward_error_correction.h index 21f2afb2c0..a0600f05bf 100644 --- a/webrtc/modules/rtp_rtcp/source/forward_error_correction.h +++ b/webrtc/modules/rtp_rtcp/source/forward_error_correction.h @@ -17,31 +17,32 @@ #include #include +#include "webrtc/base/basictypes.h" +#include "webrtc/base/constructormagic.h" #include "webrtc/base/refcount.h" #include "webrtc/base/scoped_ref_ptr.h" #include "webrtc/modules/rtp_rtcp/include/rtp_rtcp_defines.h" #include "webrtc/modules/rtp_rtcp/source/forward_error_correction_internal.h" -#include "webrtc/typedefs.h" namespace webrtc { +class FecHeaderReader; +class FecHeaderWriter; + // Performs codec-independent forward error correction (FEC), based on RFC 5109. // Option exists to enable unequal protection (UEP) across packets. // This is not to be confused with protection within packets // (referred to as uneven level protection (ULP) in RFC 5109). class ForwardErrorCorrection { public: - // Maximum number of media packets we can protect - static constexpr size_t kMaxMediaPackets = 48u; - // TODO(holmer): As a next step all these struct-like packet classes should be // refactored into proper classes, and their members should be made private. // This will require parts of the functionality in forward_error_correction.cc // and receiver_fec.cc to be refactored into the packet classes. class Packet { public: - Packet() : length(0), data(), ref_count_(0) {} - virtual ~Packet() {} + Packet(); + virtual ~Packet(); // Add a reference. virtual int32_t AddRef(); @@ -89,7 +90,7 @@ class ForwardErrorCorrection { rtc::scoped_refptr pkt; // Pointer to the packet storage. }; - // The recovered list parameter of #DecodeFec() references structs of + // The recovered list parameter of DecodeFec() references structs of // this type. // TODO(holmer): Refactor into a proper class. class RecoveredPacket : public SortablePacket { @@ -102,19 +103,55 @@ class ForwardErrorCorrection { // through the received packet list. bool returned; // True when the packet already has been returned to the // caller through the callback. - uint8_t length_recovery[2]; // Two bytes used for recovering the packet - // length with XOR operations. rtc::scoped_refptr pkt; // Pointer to the packet storage. }; + // Used to link media packets to their protecting FEC packets. + // + // TODO(holmer): Refactor into a proper class. + class ProtectedPacket : public ForwardErrorCorrection::SortablePacket { + public: + ProtectedPacket(); + ~ProtectedPacket(); + + rtc::scoped_refptr pkt; + }; + + using ProtectedPacketList = std::list>; + + // Used for internal storage of received FEC packets in a list. + // + // TODO(holmer): Refactor into a proper class. + class ReceivedFecPacket : public ForwardErrorCorrection::SortablePacket { + public: + ReceivedFecPacket(); + ~ReceivedFecPacket(); + + // List of media packets that this FEC packet protects. + ProtectedPacketList protected_packets; + // RTP header fields. + uint32_t ssrc; + // FEC header fields. + size_t fec_header_size; + uint32_t protected_ssrc; + uint16_t seq_num_base; + size_t packet_mask_offset; // Relative start of FEC header. + size_t packet_mask_size; + size_t protection_length; + // Raw data. + rtc::scoped_refptr pkt; + }; + using PacketList = std::list>; using ReceivedPacketList = std::list>; using RecoveredPacketList = std::list>; + using ReceivedFecPacketList = std::list>; - ForwardErrorCorrection(); - virtual ~ForwardErrorCorrection(); + ~ForwardErrorCorrection(); + + // Creates a ForwardErrorCorrection tailored for a specific FEC scheme. + static std::unique_ptr CreateUlpfec(); - // // Generates a list of FEC packets from supplied media packets. // // Input: media_packets List of media packets to protect, of type @@ -158,7 +195,6 @@ class ForwardErrorCorrection { FecMaskType fec_mask_type, std::list* fec_packets); - // // Decodes a list of received media and FEC packets. It will parse the // |received_packets|, storing FEC packets internally, and move // media packets to |recovered_packets|. The recovered list will be @@ -198,47 +234,32 @@ class ForwardErrorCorrection { // Frees all memory allocated by this class. void ResetState(RecoveredPacketList* recovered_packets); + // TODO(brandtr): Remove these functions when the Packet classes + // have been refactored. + static uint16_t ParseSequenceNumber(uint8_t* packet); + static uint32_t ParseSsrc(uint8_t* packet); + + protected: + ForwardErrorCorrection(std::unique_ptr fec_header_reader, + std::unique_ptr fec_header_writer); + private: - // Used to link media packets to their protecting FEC packets. - // - // TODO(holmer): Refactor into a proper class. - class ProtectedPacket : public ForwardErrorCorrection::SortablePacket { - public: - rtc::scoped_refptr pkt; - }; - - using ProtectedPacketList = std::list>; - - // Used for internal storage of received FEC packets in a list. - // - // TODO(holmer): Refactor into a proper class. - class ReceivedFecPacket : public ForwardErrorCorrection::SortablePacket { - public: - ProtectedPacketList protected_packets; - uint32_t ssrc; // SSRC of the current frame. - rtc::scoped_refptr pkt; - }; - - using ReceivedFecPacketList = std::list>; - // Analyzes |media_packets| for holes in the sequence and inserts zero columns // into the |packet_mask| where those holes are found. Zero columns means that // those packets will have no protection. // Returns the number of bits used for one row of the new packet mask. // Requires that |packet_mask| has at least 6 * |num_fec_packets| bytes // allocated. - int InsertZerosInBitMasks(const PacketList& media_packets, - uint8_t* packet_mask, int num_mask_bytes, - int num_fec_packets); + int InsertZerosInPacketMasks(const PacketList& media_packets, + size_t num_fec_packets); + // Writes FEC payloads and some recovery fields in the FEC headers. + void GenerateFecPayloads(const PacketList& media_packets, + size_t num_fec_packets); - void GenerateFecUlpHeaders(const PacketList& media_packets, - uint8_t* packet_mask, int num_fec_packets, - bool l_bit); - - void GenerateFecBitStrings(const PacketList& media_packets, - uint8_t* packet_mask, int num_fec_packets, - bool l_bit); + // Writes the FEC header fields that are not written by GenerateFecPayloads. + // This includes writing the packet masks. + void FinalizeFecHeaders(size_t num_fec_packets, uint16_t seq_num_base); // Inserts the |received_packets| into the internal received FEC packet list // or into |recovered_packets|. @@ -246,67 +267,145 @@ class ForwardErrorCorrection { RecoveredPacketList* recovered_packets); // Inserts the |received_packet| into |recovered_packets|. Deletes duplicates. - void InsertMediaPacket(ReceivedPacket* received_packet, - RecoveredPacketList* recovered_packets); + void InsertMediaPacket(RecoveredPacketList* recovered_packets, + ReceivedPacket* received_packet); // Assigns pointers to the recovered packet from all FEC packets which cover // it. // Note: This reduces the complexity when we want to try to recover a packet // since we don't have to find the intersection between recovered packets and // packets covered by the FEC packet. - void UpdateCoveringFecPackets(RecoveredPacket* packet); + void UpdateCoveringFecPackets(const RecoveredPacket& packet); // Insert |received_packet| into internal FEC list. Deletes duplicates. - void InsertFecPacket(ReceivedPacket* received_packet, - const RecoveredPacketList* recovered_packets); + void InsertFecPacket(const RecoveredPacketList& recovered_packets, + ReceivedPacket* received_packet); // Assigns pointers to already recovered packets covered by |fec_packet|. static void AssignRecoveredPackets( - ReceivedFecPacket* fec_packet, - const RecoveredPacketList* recovered_packets); - - // Insert |rec_packet_to_insert| into |recovered_packets| in correct position. - void InsertRecoveredPacket(RecoveredPacket* rec_packet_to_insert, - RecoveredPacketList* recovered_packets); + const RecoveredPacketList& recovered_packets, + ReceivedFecPacket* fec_packet); // Attempt to recover missing packets, using the internally stored // received FEC packets. - void AttemptRecover(RecoveredPacketList* recovered_packets); + void AttemptRecovery(RecoveredPacketList* recovered_packets); - // Initializes packet recovery using the received |fec_packet|. - static bool StartPacketRecovery(const ReceivedFecPacket* fec_packet, + // Initializes headers and payload before the XOR operation + // that recovers a packet. + static bool StartPacketRecovery(const ReceivedFecPacket& fec_packet, RecoveredPacket* recovered_packet); - // Performs XOR between |src| and |dst| and stores the result in |dst|. - static void XorPackets(const Packet* src, RecoveredPacket* dst); + // Performs XOR between the first 8 bytes of |src| and |dst| and stores + // the result in |dst|. The 3rd and 4th bytes are used for storing + // the length recovery field. + static void XorHeaders(const Packet& src, Packet* dst); - // Finish up the recovery of a packet. - static bool FinishPacketRecovery(RecoveredPacket* recovered_packet); + // Performs XOR between the payloads of |src| and |dst| and stores the result + // in |dst|. The parameter |dst_offset| determines at what byte the + // XOR operation starts in |dst|. In total, |payload_length| bytes are XORed. + static void XorPayloads(const Packet& src, + size_t payload_length, + size_t dst_offset, + Packet* dst); + + // Finalizes recovery of packet by setting RTP header fields. + // This is not specific to the FEC scheme used. + static bool FinishPacketRecovery(const ReceivedFecPacket& fec_packet, + RecoveredPacket* recovered_packet); // Recover a missing packet. - bool RecoverPacket(const ReceivedFecPacket* fec_packet, - RecoveredPacket* rec_packet_to_insert); + static bool RecoverPacket(const ReceivedFecPacket& fec_packet, + RecoveredPacket* recovered_packet); // Get the number of missing media packets which are covered by |fec_packet|. // An FEC packet can recover at most one packet, and if zero packets are // missing the FEC packet can be discarded. This function returns 2 when two // or more packets are missing. - static int NumCoveredPacketsMissing(const ReceivedFecPacket* fec_packet); + static int NumCoveredPacketsMissing(const ReceivedFecPacket& fec_packet); // Discards old packets in |recovered_packets|, which are no longer relevant // for recovering lost packets. - static void DiscardOldRecoveredPackets( - RecoveredPacketList* recovered_packets); - static uint16_t ParseSequenceNumber(uint8_t* packet); + void DiscardOldRecoveredPackets(RecoveredPacketList* recovered_packets); + + std::unique_ptr fec_header_reader_; + std::unique_ptr fec_header_writer_; std::vector generated_fec_packets_; ReceivedFecPacketList received_fec_packets_; // Arrays used to avoid dynamically allocating memory when generating - // the packet masks in the ULPFEC headers. - // (There are never more than |kMaxMediaPackets| FEC packets generated.) - uint8_t packet_mask_[kMaxMediaPackets * kMaskSizeLBitSet]; - uint8_t tmp_packet_mask_[kMaxMediaPackets * kMaskSizeLBitSet]; + // the packet masks. + // (There are never more than |kUlpfecMaxMediaPackets| FEC packets generated.) + uint8_t packet_masks_[kUlpfecMaxMediaPackets * kUlpfecMaxPacketMaskSize]; + uint8_t tmp_packet_masks_[kUlpfecMaxMediaPackets * kUlpfecMaxPacketMaskSize]; + size_t packet_mask_size_; }; + +// Classes derived from FecHeader{Reader,Writer} encapsulate the +// specifics of reading and writing FEC header for, e.g., ULPFEC +// and FlexFEC. +class FecHeaderReader { + public: + virtual ~FecHeaderReader(); + + // The maximum number of media packets that can be covered by one FEC packet. + size_t MaxMediaPackets() const; + + // The maximum number of FEC packets that is supported, per call + // to ForwardErrorCorrection::EncodeFec(). + size_t MaxFecPackets() const; + + // Parses FEC header and stores information in ReceivedFecPacket members. + virtual bool ReadFecHeader( + ForwardErrorCorrection::ReceivedFecPacket* fec_packet) const = 0; + + protected: + FecHeaderReader(size_t max_media_packets, size_t max_fec_packets); + + const size_t max_media_packets_; + const size_t max_fec_packets_; +}; + +class FecHeaderWriter { + public: + virtual ~FecHeaderWriter(); + + // The maximum number of media packets that can be covered by one FEC packet. + size_t MaxMediaPackets() const; + + // The maximum number of FEC packets that is supported, per call + // to ForwardErrorCorrection::EncodeFec(). + size_t MaxFecPackets() const; + + // The maximum overhead (in bytes) per packet, due to FEC headers. + size_t MaxPacketOverhead() const; + + // Calculates the minimum packet mask size needed (in bytes), + // given the discrete options of the ULPFEC masks and the bits + // set in the current packet mask. + virtual size_t MinPacketMaskSize(const uint8_t* packet_mask, + size_t packet_mask_size) const = 0; + + // The header size (in bytes), given the packet mask size. + virtual size_t FecHeaderSize(size_t packet_mask_size) const = 0; + + // Writes FEC header. + virtual void FinalizeFecHeader( + uint16_t seq_num_base, + const uint8_t* packet_mask, + size_t packet_mask_size, + ForwardErrorCorrection::Packet* fec_packet) const = 0; + + protected: + FecHeaderWriter(size_t max_media_packets, + size_t max_fec_packets, + size_t max_packet_overhead); + + const size_t max_media_packets_; + const size_t max_fec_packets_; + const size_t max_packet_overhead_; +}; + } // namespace webrtc + #endif // WEBRTC_MODULES_RTP_RTCP_SOURCE_FORWARD_ERROR_CORRECTION_H_ diff --git a/webrtc/modules/rtp_rtcp/source/forward_error_correction_internal.cc b/webrtc/modules/rtp_rtcp/source/forward_error_correction_internal.cc index 790e705bc4..faef7716fd 100644 --- a/webrtc/modules/rtp_rtcp/source/forward_error_correction_internal.cc +++ b/webrtc/modules/rtp_rtcp/source/forward_error_correction_internal.cc @@ -15,6 +15,7 @@ #include +#include "webrtc/base/checks.h" #include "webrtc/modules/rtp_rtcp/source/fec_private_tables_bursty.h" #include "webrtc/modules/rtp_rtcp/source/fec_private_tables_random.h" @@ -202,11 +203,8 @@ void RemainingPacketProtection(int num_media_packets, if (mode == kModeNoOverlap) { // sub_mask21 - const int l_bit = - (num_media_packets - num_fec_for_imp_packets) > 16 ? 1 : 0; - const int res_mask_bytes = - (l_bit == 1) ? kMaskSizeLBitSet : kMaskSizeLBitClear; + PacketMaskSize(num_media_packets - num_fec_for_imp_packets); const uint8_t* packet_mask_sub_21 = mask_table.fec_packet_mask_table()[num_media_packets - @@ -245,9 +243,7 @@ void ImportantPacketProtection(int num_fec_for_imp_packets, int num_mask_bytes, uint8_t* packet_mask, const PacketMaskTable& mask_table) { - const int l_bit = num_imp_packets > 16 ? 1 : 0; - const int num_imp_mask_bytes = - (l_bit == 1) ? kMaskSizeLBitSet : kMaskSizeLBitClear; + const int num_imp_mask_bytes = PacketMaskSize(num_imp_packets); // Get sub_mask1 from table const uint8_t* packet_mask_sub_1 = @@ -375,9 +371,7 @@ void GeneratePacketMasks(int num_media_packets, assert(num_fec_packets <= num_media_packets && num_fec_packets > 0); assert(num_imp_packets <= num_media_packets && num_imp_packets >= 0); - int l_bit = num_media_packets > 16 ? 1 : 0; - const int num_mask_bytes = - (l_bit == 1) ? kMaskSizeLBitSet : kMaskSizeLBitClear; + const int num_mask_bytes = PacketMaskSize(num_media_packets); // Equal-protection for these cases. if (!use_unequal_protection || num_imp_packets == 0) { @@ -394,6 +388,14 @@ void GeneratePacketMasks(int num_media_packets, } // End of UEP modification } // End of GetPacketMasks +size_t PacketMaskSize(size_t num_sequence_numbers) { + RTC_DCHECK_LE(num_sequence_numbers, 8 * kUlpfecPacketMaskSizeLBitSet); + if (num_sequence_numbers > 8 * kUlpfecPacketMaskSizeLBitClear) { + return kUlpfecPacketMaskSizeLBitSet; + } + return kUlpfecPacketMaskSizeLBitClear; +} + void InsertZeroColumns(int num_zeros, uint8_t* new_mask, int new_mask_bytes, diff --git a/webrtc/modules/rtp_rtcp/source/forward_error_correction_internal.h b/webrtc/modules/rtp_rtcp/source/forward_error_correction_internal.h index 82f02c21e5..c61aea872c 100644 --- a/webrtc/modules/rtp_rtcp/source/forward_error_correction_internal.h +++ b/webrtc/modules/rtp_rtcp/source/forward_error_correction_internal.h @@ -16,10 +16,17 @@ namespace webrtc { -// Packet mask size in bytes (L bit is set). -constexpr size_t kMaskSizeLBitSet = 6; -// Packet mask size in bytes (L bit is cleared). -constexpr size_t kMaskSizeLBitClear = 2; +// Maximum number of media packets that can be protected +// by these packet masks. +constexpr size_t kUlpfecMaxMediaPackets = 48; + +// Packet mask size in bytes (given L bit). +constexpr size_t kUlpfecPacketMaskSizeLBitClear = 2; +constexpr size_t kUlpfecPacketMaskSizeLBitSet = 6; + +// Convenience constants. +constexpr size_t kUlpfecMinPacketMaskSize = kUlpfecPacketMaskSizeLBitClear; +constexpr size_t kUlpfecMaxPacketMaskSize = kUlpfecPacketMaskSizeLBitSet; namespace internal { @@ -65,6 +72,10 @@ void GeneratePacketMasks(int num_media_packets, int num_fec_packets, const PacketMaskTable& mask_table, uint8_t* packet_mask); +// Returns the required packet mask size, given the number of sequence numbers +// that will be covered. +size_t PacketMaskSize(size_t num_sequence_numbers); + // Inserts |num_zeros| zero columns into |new_mask| at position // |new_bit_index|. If the current byte of |new_mask| can't fit all zeros, the // byte will be filled with zeros from |new_bit_index|, but the next byte will diff --git a/webrtc/modules/rtp_rtcp/source/producer_fec.cc b/webrtc/modules/rtp_rtcp/source/producer_fec.cc index 06f15402ba..cdd898d733 100644 --- a/webrtc/modules/rtp_rtcp/source/producer_fec.cc +++ b/webrtc/modules/rtp_rtcp/source/producer_fec.cc @@ -93,9 +93,10 @@ size_t RedPacket::length() const { } ProducerFec::ProducerFec() - : num_protected_frames_(0), + : fec_(ForwardErrorCorrection::CreateUlpfec()), + num_protected_frames_(0), num_important_packets_(0), - min_num_media_packets_(1) { + min_num_media_packets_(1) { memset(¶ms_, 0, sizeof(params_)); memset(&new_params_, 0, sizeof(new_params_)); } @@ -124,9 +125,8 @@ void ProducerFec::SetFecParameters(const FecProtectionParams* params, // protection in 'unequal protection mode') cannot exceed kMaxMediaPackets. RTC_DCHECK_GE(params->fec_rate, 0); RTC_DCHECK_LE(params->fec_rate, 255); - if (num_important_packets > - static_cast(ForwardErrorCorrection::kMaxMediaPackets)) { - num_important_packets = ForwardErrorCorrection::kMaxMediaPackets; + if (num_important_packets > static_cast(kUlpfecMaxMediaPackets)) { + num_important_packets = kUlpfecMaxMediaPackets; } // Store the new params and apply them for the next set of FEC packets being // produced. @@ -148,8 +148,8 @@ int ProducerFec::AddRtpPacketAndGenerateFec(const uint8_t* data_buffer, } bool complete_frame = false; const bool marker_bit = (data_buffer[1] & kRtpMarkerBitMask) ? true : false; - if (media_packets_.size() < ForwardErrorCorrection::kMaxMediaPackets) { - // Generic FEC can only protect up to |kMaxMediaPackets| packets. + if (media_packets_.size() < kUlpfecMaxMediaPackets) { + // Generic FEC can only protect up to |kUlpfecMaxMediaPackets| packets. std::unique_ptr packet( new ForwardErrorCorrection::Packet()); packet->length = payload_length + rtp_header_length; @@ -168,16 +168,16 @@ int ProducerFec::AddRtpPacketAndGenerateFec(const uint8_t* data_buffer, (num_protected_frames_ == params_.max_fec_frames || (ExcessOverheadBelowMax() && MinimumMediaPacketsReached()))) { RTC_DCHECK_LE(num_important_packets_, - static_cast(ForwardErrorCorrection::kMaxMediaPackets)); + static_cast(kUlpfecMaxMediaPackets)); // TODO(pbos): Consider whether unequal protection should be enabled or not, // it is currently always disabled. // // Since unequal protection is disabled, the value of // |num_important_packets_| has no importance when calling GenerateFec(). constexpr bool kUseUnequalProtection = false; - int ret = fec_.EncodeFec(media_packets_, params_.fec_rate, - num_important_packets_, kUseUnequalProtection, - params_.fec_mask_type, &generated_fec_packets_); + int ret = fec_->EncodeFec(media_packets_, params_.fec_rate, + num_important_packets_, kUseUnequalProtection, + params_.fec_mask_type, &generated_fec_packets_); if (generated_fec_packets_.empty()) { num_protected_frames_ = 0; DeleteMediaPackets(); @@ -213,7 +213,7 @@ size_t ProducerFec::NumAvailableFecPackets() const { } size_t ProducerFec::MaxPacketOverhead() const { - return fec_.MaxPacketOverhead(); + return fec_->MaxPacketOverhead(); } std::vector> ProducerFec::GetFecPacketsAsRed( @@ -238,15 +238,12 @@ std::vector> ProducerFec::GetFecPacketsAsRed( red_packet->SetSeqNum(seq_num++); red_packet->ClearMarkerBit(); red_packet->AssignPayload(fec_packet->data, fec_packet->length); - red_packets.push_back(std::move(red_packet)); } - // Reset state. DeleteMediaPackets(); generated_fec_packets_.clear(); num_protected_frames_ = 0; - return red_packets; } @@ -257,7 +254,7 @@ int ProducerFec::Overhead() const { // generation is implemented. RTC_DCHECK(!media_packets_.empty()); int num_fec_packets = - fec_.NumFecPackets(media_packets_.size(), params_.fec_rate); + fec_->NumFecPackets(media_packets_.size(), params_.fec_rate); // Return the overhead in Q8. return (num_fec_packets << 8) / media_packets_.size(); } diff --git a/webrtc/modules/rtp_rtcp/source/producer_fec.h b/webrtc/modules/rtp_rtcp/source/producer_fec.h index f09f18100c..7ea07403a6 100644 --- a/webrtc/modules/rtp_rtcp/source/producer_fec.h +++ b/webrtc/modules/rtp_rtcp/source/producer_fec.h @@ -89,7 +89,7 @@ class ProducerFec { private: void DeleteMediaPackets(); int Overhead() const; - ForwardErrorCorrection fec_; + std::unique_ptr fec_; ForwardErrorCorrection::PacketList media_packets_; std::list generated_fec_packets_; int num_protected_frames_; diff --git a/webrtc/modules/rtp_rtcp/source/rtp_fec_unittest.cc b/webrtc/modules/rtp_rtcp/source/rtp_fec_unittest.cc index 9f6dee2be9..1334cb7be2 100644 --- a/webrtc/modules/rtp_rtcp/source/rtp_fec_unittest.cc +++ b/webrtc/modules/rtp_rtcp/source/rtp_fec_unittest.cc @@ -18,6 +18,7 @@ #include "webrtc/modules/rtp_rtcp/source/byte_io.h" #include "webrtc/modules/rtp_rtcp/source/fec_test_helper.h" #include "webrtc/modules/rtp_rtcp/source/forward_error_correction.h" +#include "webrtc/modules/rtp_rtcp/source/ulpfec_header_reader_writer.h" namespace webrtc { @@ -46,6 +47,15 @@ void DeepCopyEveryNthPacket(const ForwardErrorCorrection::PacketList& src, using ::testing::Types; +// Subclass ForwardErrorCorrection to use gTest typed tests. +class UlpfecForwardErrorCorrection : public ForwardErrorCorrection { + public: + UlpfecForwardErrorCorrection() + : ForwardErrorCorrection( + std::unique_ptr(new UlpfecHeaderReader()), + std::unique_ptr(new UlpfecHeaderWriter())) {} +}; + template class RtpFecTest : public ::testing::Test { protected: @@ -86,15 +96,15 @@ class RtpFecTest : public ::testing::Test { ForwardErrorCorrection::ReceivedPacketList received_packets_; ForwardErrorCorrection::RecoveredPacketList recovered_packets_; - int media_loss_mask_[ForwardErrorCorrection::kMaxMediaPackets]; - int fec_loss_mask_[ForwardErrorCorrection::kMaxMediaPackets]; + int media_loss_mask_[kUlpfecMaxMediaPackets]; + int fec_loss_mask_[kUlpfecMaxMediaPackets]; }; // Define gTest typed test to loop over both ULPFEC and FlexFEC. // Since the tests now are parameterized, we need to access // member variables using |this|, thereby enforcing runtime // resolution. -using FecTypes = Types; +using FecTypes = Types; TYPED_TEST_CASE(RtpFecTest, FecTypes); TYPED_TEST(RtpFecTest, FecRecoveryNoLoss) { diff --git a/webrtc/modules/rtp_rtcp/source/ulpfec_header_reader_writer.cc b/webrtc/modules/rtp_rtcp/source/ulpfec_header_reader_writer.cc new file mode 100644 index 0000000000..2913482554 --- /dev/null +++ b/webrtc/modules/rtp_rtcp/source/ulpfec_header_reader_writer.cc @@ -0,0 +1,130 @@ +/* + * 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 "webrtc/modules/rtp_rtcp/source/ulpfec_header_reader_writer.h" + +#include + +#include "webrtc/base/checks.h" +#include "webrtc/modules/rtp_rtcp/source/byte_io.h" +#include "webrtc/modules/rtp_rtcp/source/forward_error_correction_internal.h" + +namespace webrtc { + +namespace { + +// Maximum number of media packets that can be protected in one batch. +constexpr size_t kMaxMediaPackets = 48; + +// Maximum number of FEC packets stored inside ForwardErrorCorrection. +constexpr size_t kMaxFecPackets = kMaxMediaPackets; + +// FEC Level 0 header size in bytes. +constexpr size_t kFecLevel0HeaderSize = 10; + +// FEC Level 1 (ULP) header size in bytes (L bit is set). +constexpr size_t kFecLevel1HeaderSizeLBitSet = 2 + kUlpfecPacketMaskSizeLBitSet; + +// FEC Level 1 (ULP) header size in bytes (L bit is cleared). +constexpr size_t kFecLevel1HeaderSizeLBitClear = + 2 + kUlpfecPacketMaskSizeLBitClear; + +constexpr size_t kPacketMaskOffset = kFecLevel0HeaderSize + 2; + +size_t UlpfecHeaderSize(size_t packet_mask_size) { + RTC_DCHECK_LE(packet_mask_size, kUlpfecPacketMaskSizeLBitSet); + if (packet_mask_size <= kUlpfecPacketMaskSizeLBitClear) { + return kFecLevel0HeaderSize + kFecLevel1HeaderSizeLBitClear; + } else { + return kFecLevel0HeaderSize + kFecLevel1HeaderSizeLBitSet; + } +} + +} // namespace + +UlpfecHeaderReader::UlpfecHeaderReader() + : FecHeaderReader(kMaxMediaPackets, kMaxFecPackets) {} + +UlpfecHeaderReader::~UlpfecHeaderReader() = default; + +bool UlpfecHeaderReader::ReadFecHeader( + ForwardErrorCorrection::ReceivedFecPacket* fec_packet) const { + bool l_bit = (fec_packet->pkt->data[0] & 0x40) != 0u; + size_t packet_mask_size = + l_bit ? kUlpfecPacketMaskSizeLBitSet : kUlpfecPacketMaskSizeLBitClear; + fec_packet->fec_header_size = UlpfecHeaderSize(packet_mask_size); + uint16_t seq_num_base = + ByteReader::ReadBigEndian(&fec_packet->pkt->data[2]); + fec_packet->protected_ssrc = fec_packet->ssrc; // Due to RED. + fec_packet->seq_num_base = seq_num_base; + fec_packet->packet_mask_offset = kPacketMaskOffset; + fec_packet->packet_mask_size = packet_mask_size; + fec_packet->protection_length = + ByteReader::ReadBigEndian(&fec_packet->pkt->data[10]); + + // Store length recovery field in temporary location in header. + // This makes the header "compatible" with the corresponding + // FlexFEC location of the length recovery field, thus simplifying + // the XORing operations. + memcpy(&fec_packet->pkt->data[2], &fec_packet->pkt->data[8], 2); + + return true; +} + +UlpfecHeaderWriter::UlpfecHeaderWriter() + : FecHeaderWriter(kMaxMediaPackets, + kMaxFecPackets, + kFecLevel0HeaderSize + kFecLevel1HeaderSizeLBitSet) {} + +UlpfecHeaderWriter::~UlpfecHeaderWriter() = default; + +// TODO(brandtr): Consider updating this implementation (which actually +// returns a bound on the sequence number spread), if logic is added to +// UlpfecHeaderWriter::FinalizeFecHeader to truncate packet masks which end +// in a string of zeroes. (Similar to how it is done in the FlexFEC case.) +size_t UlpfecHeaderWriter::MinPacketMaskSize(const uint8_t* packet_mask, + size_t packet_mask_size) const { + return packet_mask_size; +} + +size_t UlpfecHeaderWriter::FecHeaderSize(size_t packet_mask_size) const { + return UlpfecHeaderSize(packet_mask_size); +} + +void UlpfecHeaderWriter::FinalizeFecHeader( + uint16_t seq_num_base, + const uint8_t* packet_mask, + size_t packet_mask_size, + ForwardErrorCorrection::Packet* fec_packet) const { + // Set E bit to zero. + fec_packet->data[0] &= 0x7f; + // Set L bit based on packet mask size. (Note that the packet mask + // can only take on two discrete values.) + bool l_bit = (packet_mask_size == kUlpfecPacketMaskSizeLBitSet); + if (l_bit) { + fec_packet->data[0] |= 0x40; // Set the L bit. + } else { + RTC_DCHECK_EQ(packet_mask_size, kUlpfecPacketMaskSizeLBitClear); + fec_packet->data[0] &= 0xbf; // Clear the L bit. + } + // Copy length recovery field from temporary location. + memcpy(&fec_packet->data[8], &fec_packet->data[2], 2); + // Write sequence number base. + ByteWriter::WriteBigEndian(&fec_packet->data[2], seq_num_base); + // Protection length is set to entire packet. (This is not + // required in general.) + const size_t fec_header_size = FecHeaderSize(packet_mask_size); + ByteWriter::WriteBigEndian(&fec_packet->data[10], + fec_packet->length - fec_header_size); + // Copy the packet mask. + memcpy(&fec_packet->data[12], packet_mask, packet_mask_size); +} + +} // namespace webrtc diff --git a/webrtc/modules/rtp_rtcp/source/ulpfec_header_reader_writer.h b/webrtc/modules/rtp_rtcp/source/ulpfec_header_reader_writer.h new file mode 100644 index 0000000000..10328adf56 --- /dev/null +++ b/webrtc/modules/rtp_rtcp/source/ulpfec_header_reader_writer.h @@ -0,0 +1,66 @@ +/* + * 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. + */ + +#ifndef WEBRTC_MODULES_RTP_RTCP_SOURCE_ULPFEC_HEADER_READER_WRITER_H_ +#define WEBRTC_MODULES_RTP_RTCP_SOURCE_ULPFEC_HEADER_READER_WRITER_H_ + +#include "webrtc/base/basictypes.h" +#include "webrtc/modules/rtp_rtcp/source/forward_error_correction.h" + +namespace webrtc { + +// FEC Level 0 Header, 10 bytes. +// 0 1 2 3 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// |E|L|P|X| CC |M| PT recovery | SN base | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | TS recovery | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | length recovery | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// +// FEC Level 1 Header, 4 bytes (L = 0) or 8 bytes (L = 1). +// 0 1 2 3 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Protection Length | mask | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | mask cont. (present only when L = 1) | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +class UlpfecHeaderReader : public FecHeaderReader { + public: + UlpfecHeaderReader(); + ~UlpfecHeaderReader() override; + + bool ReadFecHeader( + ForwardErrorCorrection::ReceivedFecPacket* fec_packet) const override; +}; + +class UlpfecHeaderWriter : public FecHeaderWriter { + public: + UlpfecHeaderWriter(); + ~UlpfecHeaderWriter() override; + + size_t MinPacketMaskSize(const uint8_t* packet_mask, + size_t packet_mask_size) const override; + + size_t FecHeaderSize(size_t packet_mask_row_size) const override; + + void FinalizeFecHeader( + uint16_t seq_num_base, + const uint8_t* packet_mask, + size_t packet_mask_size, + ForwardErrorCorrection::Packet* fec_packet) const override; +}; + +} // namespace webrtc + +#endif // WEBRTC_MODULES_RTP_RTCP_SOURCE_ULPFEC_HEADER_READER_WRITER_H_ diff --git a/webrtc/modules/rtp_rtcp/source/ulpfec_header_reader_writer_unittest.cc b/webrtc/modules/rtp_rtcp/source/ulpfec_header_reader_writer_unittest.cc new file mode 100644 index 0000000000..9eb2df0478 --- /dev/null +++ b/webrtc/modules/rtp_rtcp/source/ulpfec_header_reader_writer_unittest.cc @@ -0,0 +1,244 @@ +/* + * 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 + +#include "testing/gtest/include/gtest/gtest.h" +#include "webrtc/base/basictypes.h" +#include "webrtc/base/checks.h" +#include "webrtc/base/random.h" +#include "webrtc/base/scoped_ref_ptr.h" +#include "webrtc/modules/rtp_rtcp/source/byte_io.h" +#include "webrtc/modules/rtp_rtcp/source/forward_error_correction.h" +#include "webrtc/modules/rtp_rtcp/source/forward_error_correction_internal.h" +#include "webrtc/modules/rtp_rtcp/source/ulpfec_header_reader_writer.h" + +namespace webrtc { + +namespace { + +using Packet = ::webrtc::ForwardErrorCorrection::Packet; +using ReceivedFecPacket = ::webrtc::ForwardErrorCorrection::ReceivedFecPacket; + +constexpr uint32_t kMediaSsrc = 1254983; +constexpr uint16_t kMediaStartSeqNum = 825; +constexpr size_t kMediaPacketLength = 1234; + +constexpr size_t kUlpfecHeaderSizeLBitClear = 14; +constexpr size_t kUlpfecHeaderSizeLBitSet = 18; +constexpr size_t kUlpfecPacketMaskOffset = 12; + +std::unique_ptr GeneratePacketMask(size_t packet_mask_size, + uint64_t seed) { + Random random(seed); + std::unique_ptr packet_mask(new uint8_t[packet_mask_size]); + for (size_t i = 0; i < packet_mask_size; ++i) { + packet_mask[i] = random.Rand(); + } + return packet_mask; +} + +std::unique_ptr WriteHeader(const uint8_t* packet_mask, + size_t packet_mask_size) { + UlpfecHeaderWriter writer; + std::unique_ptr written_packet(new Packet()); + written_packet->length = kMediaPacketLength; + for (size_t i = 0; i < written_packet->length; ++i) { + written_packet->data[i] = i; // Actual content doesn't matter. + } + writer.FinalizeFecHeader(kMediaStartSeqNum, packet_mask, packet_mask_size, + written_packet.get()); + return written_packet; +} + +std::unique_ptr ReadHeader(const Packet& written_packet) { + UlpfecHeaderReader reader; + std::unique_ptr read_packet(new ReceivedFecPacket()); + read_packet->ssrc = kMediaSsrc; + read_packet->pkt = rtc::scoped_refptr(new Packet()); + memcpy(read_packet->pkt->data, written_packet.data, written_packet.length); + read_packet->pkt->length = written_packet.length; + EXPECT_TRUE(reader.ReadFecHeader(read_packet.get())); + return read_packet; +} + +void VerifyHeaders(size_t expected_fec_header_size, + const uint8_t* expected_packet_mask, + size_t expected_packet_mask_size, + const Packet& written_packet, + const ReceivedFecPacket& read_packet) { + EXPECT_EQ(kMediaSsrc, read_packet.ssrc); + EXPECT_EQ(expected_fec_header_size, read_packet.fec_header_size); + EXPECT_EQ(kMediaSsrc, read_packet.protected_ssrc); + EXPECT_EQ(kMediaStartSeqNum, read_packet.seq_num_base); + EXPECT_EQ(kUlpfecPacketMaskOffset, read_packet.packet_mask_offset); + ASSERT_EQ(expected_packet_mask_size, read_packet.packet_mask_size); + EXPECT_EQ(written_packet.length - expected_fec_header_size, + read_packet.protection_length); + EXPECT_EQ(0, memcmp(expected_packet_mask, + &read_packet.pkt->data[read_packet.packet_mask_offset], + read_packet.packet_mask_size)); + // Verify that the call to ReadFecHeader did not tamper with the payload. + EXPECT_EQ(0, memcmp(&written_packet.data[expected_fec_header_size], + &read_packet.pkt->data[expected_fec_header_size], + written_packet.length - expected_fec_header_size)); +} + +} // namespace + +TEST(UlpfecHeaderReaderTest, ReadsSmallHeader) { + const uint8_t packet[] = { + 0x00, 0x12, 0xab, 0xcd, // L bit clear, "random" payload type and SN base + 0x12, 0x34, 0x56, 0x78, // "random" TS recovery + 0xab, 0xcd, 0x11, 0x22, // "random" length recovery and protection length + 0x33, 0x44, // "random" packet mask + 0x00, 0x00, 0x00, 0x00 // payload + }; + const size_t packet_length = sizeof(packet); + ReceivedFecPacket read_packet; + read_packet.pkt = rtc::scoped_refptr(new Packet()); + memcpy(read_packet.pkt->data, packet, packet_length); + read_packet.pkt->length = packet_length; + + UlpfecHeaderReader reader; + EXPECT_TRUE(reader.ReadFecHeader(&read_packet)); + + EXPECT_EQ(14U, read_packet.fec_header_size); + EXPECT_EQ(0xabcdU, read_packet.seq_num_base); + EXPECT_EQ(12U, read_packet.packet_mask_offset); + EXPECT_EQ(2U, read_packet.packet_mask_size); + EXPECT_EQ(0x1122U, read_packet.protection_length); +} + +TEST(UlpfecHeaderReaderTest, ReadsLargeHeader) { + const uint8_t packet[] = { + 0x40, 0x12, 0xab, 0xcd, // L bit set, "random" payload type and SN base + 0x12, 0x34, 0x56, 0x78, // "random" TS recovery + 0xab, 0xcd, 0x11, 0x22, // "random" length recovery and protection length + 0x33, 0x44, 0x55, 0x66, // "random" packet mask + 0x77, 0x88, // + 0x00, 0x00, 0x00, 0x00 // payload + }; + const size_t packet_length = sizeof(packet); + ReceivedFecPacket read_packet; + read_packet.pkt = rtc::scoped_refptr(new Packet()); + memcpy(read_packet.pkt->data, packet, packet_length); + read_packet.pkt->length = packet_length; + + UlpfecHeaderReader reader; + EXPECT_TRUE(reader.ReadFecHeader(&read_packet)); + + EXPECT_EQ(18U, read_packet.fec_header_size); + EXPECT_EQ(0xabcdU, read_packet.seq_num_base); + EXPECT_EQ(12U, read_packet.packet_mask_offset); + EXPECT_EQ(6U, read_packet.packet_mask_size); + EXPECT_EQ(0x1122U, read_packet.protection_length); +} + +TEST(UlpfecHeaderWriterTest, FinalizesSmallHeader) { + const size_t packet_mask_size = kUlpfecPacketMaskSizeLBitClear; + auto packet_mask = GeneratePacketMask(packet_mask_size, 0xabcd); + Packet written_packet; + written_packet.length = kMediaPacketLength; + for (size_t i = 0; i < written_packet.length; ++i) { + written_packet.data[i] = i; + } + + UlpfecHeaderWriter writer; + writer.FinalizeFecHeader(kMediaStartSeqNum, packet_mask.get(), + packet_mask_size, &written_packet); + + const uint8_t* packet = written_packet.data; + EXPECT_EQ(0x00, packet[0] & 0x80); // E bit. + EXPECT_EQ(0x00, packet[0] & 0x40); // L bit. + EXPECT_EQ(kMediaStartSeqNum, ByteReader::ReadBigEndian(packet + 2)); + EXPECT_EQ( + static_cast(kMediaPacketLength - kUlpfecHeaderSizeLBitClear), + ByteReader::ReadBigEndian(packet + 10)); + EXPECT_EQ(0, memcmp(packet + kUlpfecPacketMaskOffset, packet_mask.get(), + packet_mask_size)); +} + +TEST(UlpfecHeaderWriterTest, FinalizesLargeHeader) { + const size_t packet_mask_size = kUlpfecPacketMaskSizeLBitSet; + auto packet_mask = GeneratePacketMask(packet_mask_size, 0xabcd); + Packet written_packet; + written_packet.length = kMediaPacketLength; + for (size_t i = 0; i < written_packet.length; ++i) { + written_packet.data[i] = i; + } + + UlpfecHeaderWriter writer; + writer.FinalizeFecHeader(kMediaStartSeqNum, packet_mask.get(), + packet_mask_size, &written_packet); + + const uint8_t* packet = written_packet.data; + EXPECT_EQ(0x00, packet[0] & 0x80); // E bit. + EXPECT_EQ(0x40, packet[0] & 0x40); // L bit. + EXPECT_EQ(kMediaStartSeqNum, ByteReader::ReadBigEndian(packet + 2)); + EXPECT_EQ( + static_cast(kMediaPacketLength - kUlpfecHeaderSizeLBitSet), + ByteReader::ReadBigEndian(packet + 10)); + EXPECT_EQ(0, memcmp(packet + kUlpfecPacketMaskOffset, packet_mask.get(), + packet_mask_size)); +} + +TEST(UlpfecHeaderWriterTest, CalculateSmallHeaderSize) { + const size_t packet_mask_size = kUlpfecPacketMaskSizeLBitClear; + auto packet_mask = GeneratePacketMask(packet_mask_size, 0xabcd); + + UlpfecHeaderWriter writer; + size_t min_packet_mask_size = + writer.MinPacketMaskSize(packet_mask.get(), packet_mask_size); + + EXPECT_EQ(kUlpfecPacketMaskSizeLBitClear, min_packet_mask_size); + EXPECT_EQ(kUlpfecHeaderSizeLBitClear, + writer.FecHeaderSize(min_packet_mask_size)); +} + +TEST(UlpfecHeaderWriterTest, CalculateLargeHeaderSize) { + const size_t packet_mask_size = kUlpfecPacketMaskSizeLBitSet; + auto packet_mask = GeneratePacketMask(packet_mask_size, 0xabcd); + + UlpfecHeaderWriter writer; + size_t min_packet_mask_size = + writer.MinPacketMaskSize(packet_mask.get(), packet_mask_size); + + EXPECT_EQ(kUlpfecPacketMaskSizeLBitSet, min_packet_mask_size); + EXPECT_EQ(kUlpfecHeaderSizeLBitSet, + writer.FecHeaderSize(min_packet_mask_size)); +} + +TEST(UlpfecHeaderReaderWriterTest, WriteAndReadSmallHeader) { + const size_t packet_mask_size = kUlpfecPacketMaskSizeLBitClear; + auto packet_mask = GeneratePacketMask(packet_mask_size, 0xabcd); + + auto written_packet = WriteHeader(packet_mask.get(), packet_mask_size); + auto read_packet = ReadHeader(*written_packet); + + VerifyHeaders(kUlpfecHeaderSizeLBitClear, packet_mask.get(), packet_mask_size, + *written_packet, *read_packet); +} + +TEST(UlpfecHeaderReaderWriterTest, WriteAndReadLargeHeader) { + const size_t packet_mask_size = kUlpfecPacketMaskSizeLBitSet; + auto packet_mask = GeneratePacketMask(packet_mask_size, 0xabcd); + + auto written_packet = WriteHeader(packet_mask.get(), packet_mask_size); + auto read_packet = ReadHeader(*written_packet); + + VerifyHeaders(kUlpfecHeaderSizeLBitSet, packet_mask.get(), packet_mask_size, + *written_packet, *read_packet); +} + +} // namespace webrtc diff --git a/webrtc/modules/rtp_rtcp/test/testFec/test_fec.cc b/webrtc/modules/rtp_rtcp/test/testFec/test_fec.cc index be010382a8..e3368e2b82 100644 --- a/webrtc/modules/rtp_rtcp/test/testFec/test_fec.cc +++ b/webrtc/modules/rtp_rtcp/test/testFec/test_fec.cc @@ -107,7 +107,8 @@ TEST(FecTest, MAYBE_FecTest) { ASSERT_EQ(12, kMaxMediaPackets[1]) << "Max media packets for bursty mode not " << "equal to 12."; - ForwardErrorCorrection fec; + std::unique_ptr fec = + ForwardErrorCorrection::CreateUlpfec(); ForwardErrorCorrection::PacketList media_packet_list; std::list fec_packet_list; ForwardErrorCorrection::ReceivedPacketList to_decode_list; @@ -238,7 +239,7 @@ TEST(FecTest, MAYBE_FecTest) { new ForwardErrorCorrection::Packet()); const uint32_t kMinPacketSize = 12; const uint32_t kMaxPacketSize = static_cast( - IP_PACKET_SIZE - 12 - 28 - fec.MaxPacketOverhead()); + IP_PACKET_SIZE - 12 - 28 - fec->MaxPacketOverhead()); media_packet->length = random.Rand(kMinPacketSize, kMaxPacketSize); @@ -277,9 +278,9 @@ TEST(FecTest, MAYBE_FecTest) { } media_packet_list.back()->data[1] |= 0x80; - ASSERT_EQ(0, fec.EncodeFec(media_packet_list, protection_factor, - num_imp_packets, kUseUnequalProtection, - fec_mask_type, &fec_packet_list)) + ASSERT_EQ(0, fec->EncodeFec(media_packet_list, protection_factor, + num_imp_packets, kUseUnequalProtection, + fec_mask_type, &fec_packet_list)) << "EncodeFec() failed"; ASSERT_EQ(num_fec_packets, fec_packet_list.size()) @@ -393,7 +394,7 @@ TEST(FecTest, MAYBE_FecTest) { } } ASSERT_EQ(0, - fec.DecodeFec(&to_decode_list, &recovered_packet_list)) + fec->DecodeFec(&to_decode_list, &recovered_packet_list)) << "DecodeFec() failed"; ASSERT_TRUE(to_decode_list.empty()) << "Received packet list is not empty."; @@ -421,7 +422,7 @@ TEST(FecTest, MAYBE_FecTest) { } ++media_packet_idx; } - fec.ResetState(&recovered_packet_list); + fec->ResetState(&recovered_packet_list); ASSERT_TRUE(recovered_packet_list.empty()) << "Excessive number of recovered packets.\t size is: " << recovered_packet_list.size(); @@ -447,7 +448,7 @@ TEST(FecTest, MAYBE_FecTest) { } // loop over mask types // Have DecodeFec clear the recovered packet list. - fec.ResetState(&recovered_packet_list); + fec->ResetState(&recovered_packet_list); ASSERT_TRUE(recovered_packet_list.empty()) << "Recovered packet list is not empty"; } diff --git a/webrtc/modules/rtp_rtcp/test/testFec/test_packet_masks_metrics.cc b/webrtc/modules/rtp_rtcp/test/testFec/test_packet_masks_metrics.cc index b7c4ef5506..0b4ac25949 100644 --- a/webrtc/modules/rtp_rtcp/test/testFec/test_packet_masks_metrics.cc +++ b/webrtc/modules/rtp_rtcp/test/testFec/test_packet_masks_metrics.cc @@ -730,24 +730,24 @@ class FecPacketMaskMetricsTest : public ::testing::Test { int code_index = 0; // Maximum number of media packets allowed for the mask type. const int packet_mask_max = kMaxMediaPackets[fec_mask_type]; - uint8_t* packet_mask = new uint8_t[packet_mask_max * kMaskSizeLBitSet]; + std::unique_ptr packet_mask( + new uint8_t[packet_mask_max * kUlpfecMaxPacketMaskSize]); // Loop through codes up to |kMaxMediaPacketsTest|. for (int num_media_packets = 1; num_media_packets <= kMaxMediaPacketsTest; num_media_packets++) { const int mask_bytes_fec_packet = - (num_media_packets > 16) ? kMaskSizeLBitSet : kMaskSizeLBitClear; + static_cast(internal::PacketMaskSize(num_media_packets)); internal::PacketMaskTable mask_table(fec_mask_type, num_media_packets); for (int num_fec_packets = 1; num_fec_packets <= num_media_packets; num_fec_packets++) { - memset(packet_mask, 0, num_media_packets * mask_bytes_fec_packet); - memcpy(packet_mask, mask_table.fec_packet_mask_table() - [num_media_packets - 1][num_fec_packets - 1], + memset(packet_mask.get(), 0, num_media_packets * mask_bytes_fec_packet); + memcpy(packet_mask.get(), + mask_table.fec_packet_mask_table()[num_media_packets - 1] + [num_fec_packets - 1], num_fec_packets * mask_bytes_fec_packet); // Convert to bit mask. - GetPacketMaskConvertToBitMask(packet_mask, - num_media_packets, - num_fec_packets, - mask_bytes_fec_packet, + GetPacketMaskConvertToBitMask(packet_mask.get(), num_media_packets, + num_fec_packets, mask_bytes_fec_packet, code_type); if (RejectInvalidMasks(num_media_packets, num_fec_packets) < 0) { return -1; @@ -759,7 +759,6 @@ class FecPacketMaskMetricsTest : public ::testing::Test { } } assert(code_index == kNumberCodes); - delete [] packet_mask; return 0; }