| Index: webrtc/modules/rtp_rtcp/source/forward_error_correction.cc
|
| diff --git a/webrtc/modules/rtp_rtcp/source/forward_error_correction.cc b/webrtc/modules/rtp_rtcp/source/forward_error_correction.cc
|
| index a89b71bf94301eaf1a7d36d47bdacf52dec94fa3..4c99e2f60de4a098c3cd1df5f27003ce5f454dc0 100644
|
| --- a/webrtc/modules/rtp_rtcp/source/forward_error_correction.cc
|
| +++ b/webrtc/modules/rtp_rtcp/source/forward_error_correction.cc
|
| @@ -21,26 +21,14 @@
|
| #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;
|
| -
|
| -// 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;
|
| +} // namespace
|
|
|
| int32_t ForwardErrorCorrection::Packet::AddRef() {
|
| return ++ref_count_;
|
| @@ -65,35 +53,21 @@ bool ForwardErrorCorrection::SortablePacket::LessThan::operator() (
|
| return IsNewerSequenceNumber(second->seq_num, first->seq_num);
|
| }
|
|
|
| -ForwardErrorCorrection::ReceivedPacket::ReceivedPacket() {}
|
| -ForwardErrorCorrection::ReceivedPacket::~ReceivedPacket() {}
|
| -
|
| -ForwardErrorCorrection::RecoveredPacket::RecoveredPacket() {}
|
| -ForwardErrorCorrection::RecoveredPacket::~RecoveredPacket() {}
|
| -
|
| -ForwardErrorCorrection::ForwardErrorCorrection()
|
| - : generated_fec_packets_(kMaxMediaPackets), received_fec_packets_(),
|
| - packet_mask_(), tmp_packet_mask_() {}
|
| -ForwardErrorCorrection::~ForwardErrorCorrection() {}
|
| -
|
| -// Input packet
|
| -// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
| -// | RTP Header (12 octets) |
|
| -// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
| -// | RTP Payload |
|
| -// | |
|
| -// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
| -
|
| -// Output packet
|
| -// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
| -// | FEC Header (10 octets) |
|
| -// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
| -// | FEC Level 0 Header |
|
| -// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
| -// | FEC Level 0 Payload |
|
| -// | |
|
| -// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
| -//
|
| +ForwardErrorCorrection::ForwardErrorCorrection(
|
| + std::unique_ptr<FecHeaderReader> fec_header_reader,
|
| + std::unique_ptr<FecHeaderWriter> 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()),
|
| + received_fec_packets_() {}
|
| +
|
| +std::unique_ptr<ForwardErrorCorrection> ForwardErrorCorrection::CreateUlpfec() {
|
| + std::unique_ptr<FecHeaderReader> fec_header_reader(new UlpfecHeaderReader());
|
| + std::unique_ptr<FecHeaderWriter> fec_header_writer(new UlpfecHeaderWriter());
|
| + return std::unique_ptr<ForwardErrorCorrection>(new ForwardErrorCorrection(
|
| + std::move(fec_header_reader), std::move(fec_header_writer)));
|
| +}
|
| +
|
| int ForwardErrorCorrection::EncodeFec(const PacketList& media_packets,
|
| uint8_t protection_factor,
|
| int num_important_packets,
|
| @@ -107,17 +81,14 @@ int ForwardErrorCorrection::EncodeFec(const PacketList& media_packets,
|
| RTC_DCHECK_GE(num_important_packets, 0);
|
| RTC_DCHECK_LE(static_cast<size_t>(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 +106,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.
|
| @@ -148,27 +118,26 @@ int ForwardErrorCorrection::EncodeFec(const PacketList& media_packets,
|
| fec_packets->push_back(&generated_fec_packets_[i]);
|
| }
|
|
|
| + // Unpack packet masks. Since the precomputed packet masks are tailored
|
| + // for ULPFEC, the required mask size is given by the two
|
| + // discrete ULPFEC options, i.e., 16 or 48 bits long.
|
| 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<size_t>(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);
|
| + FinalizeFecHeaders(media_packets, num_fec_packets);
|
|
|
| return 0;
|
| }
|
| @@ -185,73 +154,54 @@ 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];
|
| - auto media_packets_it = media_packets.cbegin();
|
| - uint32_t pkt_mask_idx = i * num_mask_bytes;
|
| + uint32_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);
|
| +
|
| uint32_t media_pkt_idx = 0;
|
| - uint16_t fec_packet_length = 0;
|
| + auto media_packets_it = media_packets.cbegin();
|
| 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();
|
| -
|
| - // Assign network-ordered media payload length.
|
| - ByteWriter<uint16_t>::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.
|
| + 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;
|
| + uint8_t media_payload_length_network_order[2];
|
| + ByteWriter<uint16_t>::WriteBigEndian(media_payload_length_network_order,
|
| + media_payload_length);
|
| +
|
| + 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);
|
| - // Copy the 5th to 8th bytes of the RTP header (timestamp).
|
| + // Write length recovery field. (This is a temporary location for
|
| + // ULPFEC.)
|
| + memcpy(&fec_packet->data[2], media_payload_length_network_order, 2);
|
| + // Write timestamp recovery field.
|
| 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);
|
| + // Write payload.
|
| + memcpy(&fec_packet->data[fec_header_size],
|
| + &media_packet->data[kRtpHeaderSize], media_payload_length);
|
| } 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];
|
| - }
|
| - }
|
| - if (fec_packet_length > fec_packet->length) {
|
| - fec_packet->length = fec_packet_length;
|
| + XorPackets(media_packet, kRtpHeaderSize,
|
| + fec_packet_length - fec_header_size, fec_header_size,
|
| + fec_packet);
|
| }
|
| }
|
| media_packets_it++;
|
| @@ -268,47 +218,43 @@ 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();
|
| - }
|
| - 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<uint16_t>(last_seq_num - first_seq_num) -
|
| - media_packets.size() + 1;
|
| + size_t num_fec_packets) {
|
| + size_t num_media_packets = media_packets.size();
|
| + if (num_media_packets <= 1) {
|
| + return num_media_packets;
|
| + }
|
| + uint16_t last_seq_num = ParseSequenceNumber(media_packets.back()->data);
|
| + uint16_t first_seq_num = ParseSequenceNumber(media_packets.front()->data);
|
| + uint16_t total_missing_seq_nums =
|
| + static_cast<uint16_t>(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)
|
| + if (total_missing_seq_nums + num_media_packets > kUlpfecMaxMediaPackets) {
|
| 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 == kUlpfecMaxMediaPackets) {
|
| // We can only cover up to 48 packets.
|
| break;
|
| }
|
| @@ -316,13 +262,13 @@ int ForwardErrorCorrection::InsertZerosInBitMasks(
|
| const int num_zeros_to_insert =
|
| static_cast<uint16_t>(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 +278,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<uint16_t>::WriteBigEndian(&fec_packet->data[2], seq_num);
|
| -
|
| - // -- ULP header --
|
| - // Copy the payload size to the protection length field.
|
| - // (We protect the entire packet.)
|
| - ByteWriter<uint16_t>::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(const PacketList& media_packets,
|
| + size_t num_fec_packets) {
|
| + for (size_t i = 0; i < num_fec_packets; ++i) {
|
| + fec_header_writer_->FinalizeFecHeader(
|
| + media_packets, &packet_masks_[i * packet_mask_size_], packet_mask_size_,
|
| + &generated_fec_packets_[i]);
|
| }
|
| }
|
|
|
| @@ -412,7 +307,6 @@ void ForwardErrorCorrection::ResetState(
|
| void ForwardErrorCorrection::InsertMediaPacket(
|
| ReceivedPacket* received_packet,
|
| RecoveredPacketList* recovered_packets) {
|
| -
|
| // Search for duplicate packets.
|
| for (const auto& recovered_packet : *recovered_packets) {
|
| if (received_packet->seq_num == recovered_packet->seq_num) {
|
| @@ -422,7 +316,6 @@ void ForwardErrorCorrection::InsertMediaPacket(
|
| return;
|
| }
|
| }
|
| -
|
| std::unique_ptr<RecoveredPacket> recovered_packet(new RecoveredPacket());
|
| // This "recovered packet" was not recovered using parity packets.
|
| recovered_packet->was_recovered = false;
|
| @@ -431,10 +324,9 @@ 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);
|
| @@ -466,32 +358,33 @@ void ForwardErrorCorrection::InsertFecPacket(
|
| return;
|
| }
|
| }
|
| -
|
| std::unique_ptr<ReceivedFecPacket> 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<uint16_t>::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];
|
| - for (uint16_t bit_idx = 0; bit_idx < 8; ++bit_idx) {
|
| - if (packet_mask & (1 << (7 - bit_idx))) {
|
| - std::unique_ptr<ProtectedPacket> protected_packet(
|
| - new ProtectedPacket());
|
| - // This wraps naturally with the sequence number.
|
| - protected_packet->seq_num =
|
| - static_cast<uint16_t>(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));
|
| + fec_packet->rtp_ssrc = received_packet->ssrc;
|
| + // 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 (const auto& packet_mask : fec_packet->packet_mask_info) {
|
| + const uint16_t seq_num_base = std::get<0>(packet_mask.second);
|
| + const size_t offset = std::get<1>(packet_mask.second);
|
| + const size_t packet_mask_size = std::get<2>(packet_mask.second);
|
| + for (uint16_t byte_idx = 0; byte_idx < packet_mask_size; ++byte_idx) {
|
| + uint8_t packet_mask = fec_packet->pkt->data[offset + byte_idx];
|
| + for (uint16_t bit_idx = 0; bit_idx < 8; ++bit_idx) {
|
| + if (packet_mask & (1 << (7 - bit_idx))) {
|
| + std::unique_ptr<ProtectedPacket> protected_packet(
|
| + new ProtectedPacket());
|
| + // This wraps naturally with the sequence number.
|
| + protected_packet->seq_num =
|
| + static_cast<uint16_t>(seq_num_base + (byte_idx << 3) + bit_idx);
|
| + protected_packet->pkt = nullptr;
|
| + fec_packet->protected_packets.push_back(std::move(protected_packet));
|
| + }
|
| }
|
| }
|
| }
|
| @@ -503,16 +396,17 @@ void ForwardErrorCorrection::InsertFecPacket(
|
| // 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);
|
| }
|
| }
|
|
|
| @@ -578,111 +472,116 @@ 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<size_t>(kFecHeaderSize + ulp_header_size)) {
|
| + ReceivedFecPacket* fec_packet,
|
| + RecoveredPacket* recovered_packet) const {
|
| + // 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<uint16_t>::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<uint32_t>::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<uint16_t>::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<uint16_t>::ReadBigEndian(recovered_packet->length_recovery) +
|
| + ByteReader<uint16_t>::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<uint16_t>::WriteBigEndian(&recovered_packet->pkt->data[2],
|
| + recovered_packet->seq_num);
|
| + // Set the SSRC field.
|
| + ByteWriter<uint32_t>::WriteBigEndian(&recovered_packet->pkt->data[8],
|
| + fec_packet->rtp_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<uint16_t>::WriteBigEndian(media_payload_length,
|
| + size_t src_offset,
|
| + size_t payload_length,
|
| + size_t dst_offset,
|
| + 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 the length recovery field.
|
| + uint8_t src_payload_length_network_order[2];
|
| + ByteWriter<uint16_t>::WriteBigEndian(src_payload_length_network_order,
|
| src->length - kRtpHeaderSize);
|
| - dst->length_recovery[0] ^= media_payload_length[0];
|
| - dst->length_recovery[1] ^= media_payload_length[1];
|
| + 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];
|
|
|
| - // 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];
|
| + // Skip the 9th to 12th bytes of the header.
|
| +
|
| + // XOR the payload.
|
| + RTC_DCHECK_LE(src_offset + 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[src_offset + i];
|
| }
|
| }
|
|
|
| -bool ForwardErrorCorrection::RecoverPacket(
|
| - const ReceivedFecPacket* fec_packet,
|
| - RecoveredPacket* rec_packet_to_insert) {
|
| - if (!StartPacketRecovery(fec_packet, rec_packet_to_insert))
|
| +bool ForwardErrorCorrection::RecoverPacket(ReceivedFecPacket* fec_packet,
|
| + RecoveredPacket* recovered_packet) {
|
| + if (!StartPacketRecovery(fec_packet, recovered_packet)) {
|
| return false;
|
| + }
|
| 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);
|
| + XorPackets(protected_packet->pkt, kRtpHeaderSize,
|
| + 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()) {
|
| @@ -692,23 +591,23 @@ void ForwardErrorCorrection::AttemptRecover(
|
| // We can only recover one packet with an FEC packet.
|
| if (packets_missing == 1) {
|
| // Recovery possible.
|
| - std::unique_ptr<RecoveredPacket> packet_to_insert(new RecoveredPacket());
|
| - packet_to_insert->pkt = nullptr;
|
| - if (!RecoverPacket(fec_packet_it->get(), packet_to_insert.get())) {
|
| + std::unique_ptr<RecoveredPacket> recovered_packet(new RecoveredPacket());
|
| + recovered_packet->pkt = nullptr;
|
| + if (!RecoverPacket(fec_packet_it->get(), 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);
|
|
|
| @@ -742,37 +641,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<int>(received_packets->front()->seq_num) -
|
| static_cast<int>(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
|
|
|