blob: 7e524bf95a00421492d44256b4a8a7f2f8da1f05 [file] [log] [blame]
/*
* Copyright (c) 2012 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/forward_error_correction.h"
#include <string.h>
#include <algorithm>
#include <iterator>
#include <utility>
#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/flexfec_header_reader_writer.h"
#include "webrtc/modules/rtp_rtcp/source/forward_error_correction_internal.h"
#include "webrtc/modules/rtp_rtcp/source/ulpfec_header_reader_writer.h"
#include "webrtc/rtc_base/checks.h"
#include "webrtc/rtc_base/logging.h"
namespace webrtc {
namespace {
// Transport header size in bytes. Assume UDP/IPv4 as a reasonable minimum.
constexpr size_t kTransportOverhead = 28;
} // namespace
ForwardErrorCorrection::Packet::Packet() : length(0), data(), ref_count_(0) {}
ForwardErrorCorrection::Packet::~Packet() = default;
int32_t ForwardErrorCorrection::Packet::AddRef() {
return ++ref_count_;
}
int32_t ForwardErrorCorrection::Packet::Release() {
int32_t ref_count;
ref_count = --ref_count_;
if (ref_count == 0)
delete this;
return ref_count;
}
// This comparator is used to compare std::unique_ptr's pointing to
// subclasses of SortablePackets. It needs to be parametric since
// the std::unique_ptr's are not covariant w.r.t. the types that
// they are pointing to.
template <typename S, typename T>
bool ForwardErrorCorrection::SortablePacket::LessThan::operator() (
const S& first,
const T& second) {
RTC_DCHECK_EQ(first->ssrc, second->ssrc);
return IsNewerSequenceNumber(second->seq_num, first->seq_num);
}
ForwardErrorCorrection::ReceivedPacket::ReceivedPacket() = default;
ForwardErrorCorrection::ReceivedPacket::~ReceivedPacket() = default;
ForwardErrorCorrection::RecoveredPacket::RecoveredPacket() = default;
ForwardErrorCorrection::RecoveredPacket::~RecoveredPacket() = default;
ForwardErrorCorrection::ProtectedPacket::ProtectedPacket() = default;
ForwardErrorCorrection::ProtectedPacket::~ProtectedPacket() = default;
ForwardErrorCorrection::ReceivedFecPacket::ReceivedFecPacket() = default;
ForwardErrorCorrection::ReceivedFecPacket::~ReceivedFecPacket() = default;
ForwardErrorCorrection::ForwardErrorCorrection(
std::unique_ptr<FecHeaderReader> fec_header_reader,
std::unique_ptr<FecHeaderWriter> fec_header_writer,
uint32_t ssrc,
uint32_t protected_media_ssrc)
: ssrc_(ssrc),
protected_media_ssrc_(protected_media_ssrc),
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> ForwardErrorCorrection::CreateUlpfec(
uint32_t ssrc) {
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), ssrc, ssrc));
}
std::unique_ptr<ForwardErrorCorrection> ForwardErrorCorrection::CreateFlexfec(
uint32_t ssrc,
uint32_t protected_media_ssrc) {
std::unique_ptr<FecHeaderReader> fec_header_reader(new FlexfecHeaderReader());
std::unique_ptr<FecHeaderWriter> fec_header_writer(new FlexfecHeaderWriter());
return std::unique_ptr<ForwardErrorCorrection>(new ForwardErrorCorrection(
std::move(fec_header_reader), std::move(fec_header_writer), ssrc,
protected_media_ssrc));
}
int ForwardErrorCorrection::EncodeFec(const PacketList& media_packets,
uint8_t protection_factor,
int num_important_packets,
bool use_unequal_protection,
FecMaskType fec_mask_type,
std::list<Packet*>* fec_packets) {
const size_t num_media_packets = media_packets.size();
// Sanity check arguments.
RTC_DCHECK_GT(num_media_packets, 0);
RTC_DCHECK_GE(num_important_packets, 0);
RTC_DCHECK_LE(num_important_packets, num_media_packets);
RTC_DCHECK(fec_packets->empty());
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 " << max_media_packets
<< ".";
return -1;
}
// Error check the media packets.
for (const auto& media_packet : media_packets) {
RTC_DCHECK(media_packet);
if (media_packet->length < kRtpHeaderSize) {
LOG(LS_WARNING) << "Media packet " << media_packet->length << " bytes "
<< "is smaller than RTP header.";
return -1;
}
// Ensure the FEC packets will fit in a typical MTU.
if (media_packet->length + MaxPacketOverhead() + kTransportOverhead >
IP_PACKET_SIZE) {
LOG(LS_WARNING) << "Media packet " << media_packet->length << " bytes "
<< "with overhead is larger than " << IP_PACKET_SIZE
<< " bytes.";
}
}
// Prepare generated FEC packets.
int num_fec_packets = NumFecPackets(num_media_packets, protection_factor);
if (num_fec_packets == 0) {
return 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.
generated_fec_packets_[i].length = 0;
fec_packets->push_back(&generated_fec_packets_[i]);
}
const internal::PacketMaskTable mask_table(fec_mask_type, num_media_packets);
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_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;
}
packet_mask_size_ = internal::PacketMaskSize(num_mask_bits);
// Write FEC packets to |generated_fec_packets_|.
GenerateFecPayloads(media_packets, num_fec_packets);
// TODO(brandtr): Generalize this when multistream protection support is
// added.
const uint32_t media_ssrc = ParseSsrc(media_packets.front()->data);
const uint16_t seq_num_base =
ParseSequenceNumber(media_packets.front()->data);
FinalizeFecHeaders(num_fec_packets, media_ssrc, seq_num_base);
return 0;
}
int ForwardErrorCorrection::NumFecPackets(int num_media_packets,
int protection_factor) {
// Result in Q0 with an unsigned round.
int num_fec_packets = (num_media_packets * protection_factor + (1 << 7)) >> 8;
// Generate at least one FEC packet if we need protection.
if (protection_factor > 0 && num_fec_packets == 0) {
num_fec_packets = 1;
}
RTC_DCHECK_LE(num_fec_packets, num_media_packets);
return num_fec_packets;
}
void ForwardErrorCorrection::GenerateFecPayloads(
const PacketList& media_packets,
size_t num_fec_packets) {
RTC_DCHECK(!media_packets.empty());
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();
uint16_t prev_seq_num = ParseSequenceNumber((*media_packets_it)->data);
while (media_packets_it != media_packets.end()) {
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;
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<uint16_t>::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()) {
uint16_t seq_num = ParseSequenceNumber((*media_packets_it)->data);
media_pkt_idx += static_cast<uint16_t>(seq_num - prev_seq_num);
prev_seq_num = seq_num;
}
pkt_mask_idx += media_pkt_idx / 8;
media_pkt_idx %= 8;
}
RTC_DCHECK_GT(fec_packet->length, 0)
<< "Packet mask is wrong or poorly designed.";
}
}
int ForwardErrorCorrection::InsertZerosInPacketMasks(
const PacketList& media_packets,
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);
size_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 num_media_packets;
}
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.
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_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 == max_media_packets) {
// We can only cover up to 48 packets.
break;
}
uint16_t seq_num = ParseSequenceNumber((*media_packets_it)->data);
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_masks_,
tmp_packet_mask_size, num_fec_packets,
new_bit_index);
}
new_bit_index += num_zeros_to_insert;
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;
prev_seq_num = seq_num;
++media_packets_it;
}
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 * 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_masks_, tmp_packet_masks_,
num_fec_packets * tmp_packet_mask_size);
return new_bit_index;
}
void ForwardErrorCorrection::FinalizeFecHeaders(size_t num_fec_packets,
uint32_t media_ssrc,
uint16_t seq_num_base) {
for (size_t i = 0; i < num_fec_packets; ++i) {
fec_header_writer_->FinalizeFecHeader(
media_ssrc, seq_num_base, &packet_masks_[i * packet_mask_size_],
packet_mask_size_, &generated_fec_packets_[i]);
}
}
void ForwardErrorCorrection::ResetState(
RecoveredPacketList* recovered_packets) {
// Free the memory for any existing recovered packets, if the caller hasn't.
recovered_packets->clear();
received_fec_packets_.clear();
}
void ForwardErrorCorrection::InsertMediaPacket(
RecoveredPacketList* recovered_packets,
ReceivedPacket* received_packet) {
RTC_DCHECK_EQ(received_packet->ssrc, protected_media_ssrc_);
// Search for duplicate packets.
for (const auto& recovered_packet : *recovered_packets) {
RTC_DCHECK_EQ(recovered_packet->ssrc, received_packet->ssrc);
if (recovered_packet->seq_num == received_packet->seq_num) {
// Duplicate packet, no need to add to list.
// Delete duplicate media packet data.
received_packet->pkt = nullptr;
return;
}
}
std::unique_ptr<RecoveredPacket> recovered_packet(new RecoveredPacket());
// This "recovered packet" was not recovered using parity packets.
recovered_packet->was_recovered = false;
// This media packet has already been passed on.
recovered_packet->returned = true;
recovered_packet->ssrc = received_packet->ssrc;
recovered_packet->seq_num = received_packet->seq_num;
recovered_packet->pkt = received_packet->pkt;
recovered_packet->pkt->length = received_packet->pkt->length;
// 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);
}
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());
if (protected_it != fec_packet->protected_packets.end() &&
(*protected_it)->seq_num == packet.seq_num) {
// Found an FEC packet which is protecting |packet|.
(*protected_it)->pkt = packet.pkt;
}
}
}
void ForwardErrorCorrection::InsertFecPacket(
const RecoveredPacketList& recovered_packets,
ReceivedPacket* received_packet) {
RTC_DCHECK_EQ(received_packet->ssrc, ssrc_);
// Check for duplicate.
for (const auto& existing_fec_packet : received_fec_packets_) {
RTC_DCHECK_EQ(existing_fec_packet->ssrc, received_packet->ssrc);
if (existing_fec_packet->seq_num == received_packet->seq_num) {
// Delete duplicate FEC packet data.
received_packet->pkt = nullptr;
return;
}
}
std::unique_ptr<ReceivedFecPacket> fec_packet(new ReceivedFecPacket());
fec_packet->pkt = received_packet->pkt;
fec_packet->ssrc = received_packet->ssrc;
fec_packet->seq_num = received_packet->seq_num;
// Parse ULPFEC/FlexFEC header specific info.
bool ret = fec_header_reader_->ReadFecHeader(fec_packet.get());
if (!ret) {
return;
}
// TODO(brandtr): Update here when we support multistream protection.
if (fec_packet->protected_ssrc != protected_media_ssrc_) {
LOG(LS_INFO)
<< "Received FEC packet is protecting an unknown media SSRC; dropping.";
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<ProtectedPacket> protected_packet(
new ProtectedPacket());
// This wraps naturally with the sequence number.
protected_packet->ssrc = protected_media_ssrc_;
protected_packet->seq_num = static_cast<uint16_t>(
fec_packet->seq_num_base + (byte_idx << 3) + bit_idx);
protected_packet->pkt = nullptr;
fec_packet->protected_packets.push_back(std::move(protected_packet));
}
}
}
if (fec_packet->protected_packets.empty()) {
// All-zero packet mask; we can discard this FEC packet.
LOG(LS_WARNING) << "Received FEC packet has an all-zero packet mask.";
} else {
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.
received_fec_packets_.push_back(std::move(fec_packet));
received_fec_packets_.sort(SortablePacket::LessThan());
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(), max_fec_packets);
}
}
void ForwardErrorCorrection::AssignRecoveredPackets(
const RecoveredPacketList& recovered_packets,
ReceivedFecPacket* fec_packet) {
ProtectedPacketList* protected_packets = &fec_packet->protected_packets;
std::vector<RecoveredPacket*> recovered_protected_packets;
// Find intersection between the (sorted) containers |protected_packets|
// and |recovered_packets|, i.e. all protected packets that have already
// 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();
SortablePacket::LessThan less_than;
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)) {
++it_r;
} else { // *it_p == *it_r.
// This protected packet has already been recovered.
(*it_p)->pkt = (*it_r)->pkt;
++it_p;
++it_r;
}
}
}
void ForwardErrorCorrection::InsertPackets(
ReceivedPacketList* received_packets,
RecoveredPacketList* recovered_packets) {
while (!received_packets->empty()) {
ReceivedPacket* received_packet = received_packets->front().get();
// Discard old FEC packets such that the sequence numbers in
// |received_fec_packets_| span at most 1/2 of the sequence number space.
// This is important for keeping |received_fec_packets_| sorted, and may
// also reduce the possibility of incorrect decoding due to sequence number
// wrap-around.
// TODO(marpan/holmer): We should be able to improve detection/discarding of
// old FEC packets based on timestamp information or better sequence number
// thresholding (e.g., to distinguish between wrap-around and reordering).
if (!received_fec_packets_.empty() &&
received_packet->ssrc == received_fec_packets_.front()->ssrc) {
// It only makes sense to detect wrap-around when |received_packet|
// and |front_received_fec_packet| belong to the same sequence number
// space, i.e., the same SSRC. This happens when |received_packet|
// is a FEC packet, or if |received_packet| is a media packet and
// RED+ULPFEC is used.
auto it = received_fec_packets_.begin();
while (it != received_fec_packets_.end()) {
uint16_t seq_num_diff = abs(static_cast<int>(received_packet->seq_num) -
static_cast<int>((*it)->seq_num));
if (seq_num_diff > 0x3fff) {
it = received_fec_packets_.erase(it);
} else {
// No need to keep iterating, since |received_fec_packets_| is sorted.
break;
}
}
}
if (received_packet->is_fec) {
InsertFecPacket(*recovered_packets, received_packet);
} else {
InsertMediaPacket(recovered_packets, received_packet);
}
// Delete the received packet "wrapper".
received_packets->pop_front();
}
RTC_DCHECK(received_packets->empty());
DiscardOldRecoveredPackets(recovered_packets);
}
bool ForwardErrorCorrection::StartPacketRecovery(
const ReceivedFecPacket& fec_packet,
RecoveredPacket* recovered_packet) {
// Sanity check packet length.
if (fec_packet.pkt->length < fec_packet.fec_header_size) {
LOG(LS_WARNING)
<< "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;
// 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;
}
memcpy(&recovered_packet->pkt->data[kRtpHeaderSize],
&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.
// Recover the packet length, from temporary location.
recovered_packet->pkt->length =
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.protected_ssrc);
recovered_packet->ssrc = fec_packet.protected_ssrc;
return true;
}
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 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->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* 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.
recovered_packet->seq_num = protected_packet->seq_num;
} else {
XorHeaders(*protected_packet->pkt, recovered_packet->pkt);
XorPayloads(*protected_packet->pkt, protected_packet->pkt->length,
kRtpHeaderSize, recovered_packet->pkt);
}
}
if (!FinishPacketRecovery(fec_packet, recovered_packet)) {
return false;
}
return true;
}
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);
// We can only recover one packet with an FEC packet.
if (packets_missing == 1) {
// Recovery possible.
std::unique_ptr<RecoveredPacket> 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 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(recovered_packet));
recovered_packets->sort(SortablePacket::LessThan());
UpdateCoveringFecPackets(*recovered_packet_ptr);
DiscardOldRecoveredPackets(recovered_packets);
fec_packet_it = received_fec_packets_.erase(fec_packet_it);
// A packet has been recovered. We need to check the FEC list again, as
// this may allow additional packets to be recovered.
// Restart for first FEC packet.
fec_packet_it = received_fec_packets_.begin();
} else if (packets_missing == 0) {
// Either all protected packets arrived or have been recovered. We can
// discard this FEC packet.
fec_packet_it = received_fec_packets_.erase(fec_packet_it);
} else {
fec_packet_it++;
}
}
}
int ForwardErrorCorrection::NumCoveredPacketsMissing(
const ReceivedFecPacket& fec_packet) {
int packets_missing = 0;
for (const auto& protected_packet : fec_packet.protected_packets) {
if (protected_packet->pkt == nullptr) {
++packets_missing;
if (packets_missing > 1) {
break; // We can't recover more than one packet.
}
}
}
return packets_missing;
}
void ForwardErrorCorrection::DiscardOldRecoveredPackets(
RecoveredPacketList* recovered_packets) {
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(), 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) {
RTC_DCHECK(received_packets);
RTC_DCHECK(recovered_packets);
const size_t max_media_packets = fec_header_reader_->MaxMediaPackets();
if (recovered_packets->size() == max_media_packets) {
const RecoveredPacket* back_recovered_packet =
recovered_packets->back().get();
for (const auto& received_packet : *received_packets) {
if (received_packet->ssrc == back_recovered_packet->ssrc) {
const unsigned int seq_num_diff =
abs(static_cast<int>(received_packet->seq_num) -
static_cast<int>(back_recovered_packet->seq_num));
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.
LOG(LS_INFO) << "Big gap in media/ULPFEC sequence numbers. No need "
"to keep the old packets in the FEC buffers, thus "
"resetting them.";
ResetState(recovered_packets);
break;
}
}
}
}
InsertPackets(received_packets, recovered_packets);
AttemptRecovery(recovered_packets);
return 0;
}
size_t ForwardErrorCorrection::MaxPacketOverhead() const {
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