blob: 22e1a38081c2bfabb23fe100619b991088fd9a0f [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 <algorithm>
#include <cassert>
#include <cstring>
#include <iterator>
#include "modules/rtp_rtcp/source/forward_error_correction.h"
#include "modules/rtp_rtcp/source/forward_error_correction_internal.h"
#include "modules/rtp_rtcp/source/rtp_utility.h"
#include "system_wrappers/interface/trace.h"
namespace webrtc {
// Minimum RTP header size in bytes.
const uint8_t kRtpHeaderSize = 12;
// FEC header size in bytes.
const uint8_t kFecHeaderSize = 10;
// ULP header size in bytes (L bit is set).
const uint8_t kUlpHeaderSizeLBitSet = (2 + kMaskSizeLBitSet);
// ULP header size in bytes (L bit is cleared).
const uint8_t kUlpHeaderSizeLBitClear = (2 + kMaskSizeLBitClear);
// Transport header size in bytes. Assume UDP/IPv4 as a reasonable minimum.
const uint8_t kTransportOverhead = 28;
enum { kMaxFecPackets = ForwardErrorCorrection::kMaxMediaPackets };
// Used to link media packets to their protecting FEC packets.
//
// TODO(holmer): Refactor into a proper class.
class ProtectedPacket : public ForwardErrorCorrection::SortablePacket {
public:
scoped_refptr<ForwardErrorCorrection::Packet> pkt;
};
typedef std::list<ProtectedPacket*> ProtectedPacketList;
//
// Used for internal storage of FEC packets in a list.
//
// TODO(holmer): Refactor into a proper class.
class FecPacket : public ForwardErrorCorrection::SortablePacket {
public:
ProtectedPacketList protectedPktList;
uint32_t ssrc; // SSRC of the current frame.
scoped_refptr<ForwardErrorCorrection::Packet> pkt;
};
bool ForwardErrorCorrection::SortablePacket::LessThan(
const SortablePacket* first,
const SortablePacket* second) {
return (first->seqNum != second->seqNum &&
LatestSequenceNumber(first->seqNum, second->seqNum) == second->seqNum);
}
ForwardErrorCorrection::ForwardErrorCorrection(int32_t id)
: _id(id),
_generatedFecPackets(kMaxMediaPackets),
_fecPacketReceived(false) {
}
ForwardErrorCorrection::~ForwardErrorCorrection() {
}
// Input packet
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | RTP Header (12 octets) |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | RTP Payload |
// | |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// Output packet
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | FEC Header (10 octets) |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | FEC Level 0 Header |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | FEC Level 0 Payload |
// | |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
int32_t ForwardErrorCorrection::GenerateFEC(
const PacketList& mediaPacketList,
uint8_t protectionFactor,
int numImportantPackets,
bool useUnequalProtection,
PacketList* fecPacketList) {
if (mediaPacketList.empty()) {
WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id,
"%s media packet list is empty", __FUNCTION__);
return -1;
}
if (!fecPacketList->empty()) {
WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id,
"%s FEC packet list is not empty", __FUNCTION__);
return -1;
}
const uint16_t numMediaPackets = mediaPacketList.size();
const uint8_t lBit = numMediaPackets > 16 ? 1 : 0;
const uint16_t numMaskBytes = (lBit == 1)?
kMaskSizeLBitSet : kMaskSizeLBitClear;
if (numMediaPackets > kMaxMediaPackets) {
WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id,
"%s can only protect %d media packets per frame; %d requested",
__FUNCTION__, kMaxMediaPackets, numMediaPackets);
return -1;
}
// Error checking on the number of important packets.
// Can't have more important packets than media packets.
if (numImportantPackets > numMediaPackets) {
WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id,
"Number of important packets (%d) greater than number of media "
"packets (%d)", numImportantPackets, numMediaPackets);
return -1;
}
if (numImportantPackets < 0) {
WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id,
"Number of important packets (%d) less than zero",
numImportantPackets);
return -1;
}
// Do some error checking on the media packets.
PacketList::const_iterator mediaListIt = mediaPacketList.begin();
while (mediaListIt != mediaPacketList.end()) {
Packet* mediaPacket = *mediaListIt;
assert(mediaPacket);
if (mediaPacket->length < kRtpHeaderSize) {
WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id,
"%s media packet (%d bytes) is smaller than RTP header",
__FUNCTION__, mediaPacket->length);
return -1;
}
// Ensure our FEC packets will fit in a typical MTU.
if (mediaPacket->length + PacketOverhead() + kTransportOverhead >
IP_PACKET_SIZE) {
WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id,
"%s media packet (%d bytes) with overhead is larger than MTU(%d)",
__FUNCTION__, mediaPacket->length, IP_PACKET_SIZE);
return -1;
}
mediaListIt++;
}
// Result in Q0 with an unsigned round.
uint32_t numFecPackets = (numMediaPackets * protectionFactor + (1 << 7)) >> 8;
// Generate at least one FEC packet if we need protection.
if (protectionFactor > 0 && numFecPackets == 0) {
numFecPackets = 1;
}
if (numFecPackets == 0) {
return 0;
}
assert(numFecPackets <= numMediaPackets);
// Prepare FEC packets by setting them to 0.
for (uint32_t i = 0; i < numFecPackets; i++) {
memset(_generatedFecPackets[i].data, 0, IP_PACKET_SIZE);
_generatedFecPackets[i].length = 0; // Use this as a marker for untouched
// packets.
fecPacketList->push_back(&_generatedFecPackets[i]);
}
// -- Generate packet masks --
uint8_t* packetMask = new uint8_t[numFecPackets * numMaskBytes];
memset(packetMask, 0, numFecPackets * numMaskBytes);
internal::GeneratePacketMasks(numMediaPackets, numFecPackets,
numImportantPackets, useUnequalProtection,
packetMask);
GenerateFecBitStrings(mediaPacketList, packetMask, numFecPackets);
GenerateFecUlpHeaders(mediaPacketList, packetMask, numFecPackets);
delete [] packetMask;
return 0;
}
void ForwardErrorCorrection::GenerateFecBitStrings(
const PacketList& mediaPacketList,
uint8_t* packetMask,
uint32_t numFecPackets) {
uint8_t mediaPayloadLength[2];
const uint8_t lBit = mediaPacketList.size() > 16 ? 1 : 0;
const uint16_t numMaskBytes = (lBit == 1) ?
kMaskSizeLBitSet : kMaskSizeLBitClear;
const uint16_t ulpHeaderSize = (lBit == 1) ?
kUlpHeaderSizeLBitSet : kUlpHeaderSizeLBitClear;
const uint16_t fecRtpOffset = kFecHeaderSize + ulpHeaderSize - kRtpHeaderSize;
for (uint32_t i = 0; i < numFecPackets; i++) {
PacketList::const_iterator mediaListIt = mediaPacketList.begin();
uint32_t pktMaskIdx = i * numMaskBytes;
uint32_t mediaPktIdx = 0;
uint16_t fecPacketLength = 0;
while (mediaListIt != mediaPacketList.end()) {
// Each FEC packet has a multiple byte mask.
if (packetMask[pktMaskIdx] & (1 << (7 - mediaPktIdx))) {
Packet* mediaPacket = *mediaListIt;
// Assign network-ordered media payload length.
ModuleRTPUtility::AssignUWord16ToBuffer(
mediaPayloadLength,
mediaPacket->length - kRtpHeaderSize);
fecPacketLength = mediaPacket->length + fecRtpOffset;
// On the first protected packet, we don't need to XOR.
if (_generatedFecPackets[i].length == 0) {
// Copy the first 2 bytes of the RTP header.
memcpy(_generatedFecPackets[i].data, mediaPacket->data, 2);
// Copy the 5th to 8th bytes of the RTP header.
memcpy(&_generatedFecPackets[i].data[4], &mediaPacket->data[4], 4);
// Copy network-ordered payload size.
memcpy(&_generatedFecPackets[i].data[8], mediaPayloadLength, 2);
// Copy RTP payload, leaving room for the ULP header.
memcpy(&_generatedFecPackets[i].data[kFecHeaderSize + ulpHeaderSize],
&mediaPacket->data[kRtpHeaderSize],
mediaPacket->length - kRtpHeaderSize);
} else {
// XOR with the first 2 bytes of the RTP header.
_generatedFecPackets[i].data[0] ^= mediaPacket->data[0];
_generatedFecPackets[i].data[1] ^= mediaPacket->data[1];
// XOR with the 5th to 8th bytes of the RTP header.
for (uint32_t j = 4; j < 8; j++) {
_generatedFecPackets[i].data[j] ^= mediaPacket->data[j];
}
// XOR with the network-ordered payload size.
_generatedFecPackets[i].data[8] ^= mediaPayloadLength[0];
_generatedFecPackets[i].data[9] ^= mediaPayloadLength[1];
// XOR with RTP payload, leaving room for the ULP header.
for (int32_t j = kFecHeaderSize + ulpHeaderSize;
j < fecPacketLength; j++) {
_generatedFecPackets[i].data[j] ^=
mediaPacket->data[j - fecRtpOffset];
}
}
if (fecPacketLength > _generatedFecPackets[i].length) {
_generatedFecPackets[i].length = fecPacketLength;
}
}
mediaListIt++;
mediaPktIdx++;
if (mediaPktIdx == 8) {
// Switch to the next mask byte.
mediaPktIdx = 0;
pktMaskIdx++;
}
}
assert(_generatedFecPackets[i].length);
//Note: This shouldn't happen: means packet mask is wrong or poorly designed
}
}
void ForwardErrorCorrection::GenerateFecUlpHeaders(
const PacketList& mediaPacketList,
uint8_t* packetMask,
uint32_t numFecPackets) {
// -- 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) |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
PacketList::const_iterator mediaListIt = mediaPacketList.begin();
Packet* mediaPacket = *mediaListIt;
assert(mediaPacket != NULL);
const uint8_t lBit = mediaPacketList.size() > 16 ? 1 : 0;
const uint16_t numMaskBytes = (lBit == 1)?
kMaskSizeLBitSet : kMaskSizeLBitClear;
const uint16_t ulpHeaderSize = (lBit == 1)?
kUlpHeaderSizeLBitSet : kUlpHeaderSizeLBitClear;
for (uint32_t i = 0; i < numFecPackets; i++) {
// -- FEC header --
_generatedFecPackets[i].data[0] &= 0x7f; // Set E to zero.
if (lBit == 0) {
_generatedFecPackets[i].data[0] &= 0xbf; // Clear the L bit.
} else {
_generatedFecPackets[i].data[0] |= 0x40; // Set the L bit.
}
// Two byte sequence number from first RTP packet to SN base.
// We use the same sequence number base for every FEC packet,
// but that's not required in general.
memcpy(&_generatedFecPackets[i].data[2], &mediaPacket->data[2], 2);
// -- ULP header --
// Copy the payload size to the protection length field.
// (We protect the entire packet.)
ModuleRTPUtility::AssignUWord16ToBuffer(&_generatedFecPackets[i].data[10],
_generatedFecPackets[i].length - kFecHeaderSize - ulpHeaderSize);
// Copy the packet mask.
memcpy(&_generatedFecPackets[i].data[12], &packetMask[i * numMaskBytes],
numMaskBytes);
}
}
void ForwardErrorCorrection::ResetState(
RecoveredPacketList* recoveredPacketList) {
_fecPacketReceived = false;
// Free the memory for any existing recovered packets, if the user hasn't.
while (!recoveredPacketList->empty()) {
delete recoveredPacketList->front();
recoveredPacketList->pop_front();
}
assert(recoveredPacketList->empty());
// Free the FEC packet list.
while (!_fecPacketList.empty()) {
FecPacketList::iterator fecPacketListIt = _fecPacketList.begin();
FecPacket* fecPacket = *fecPacketListIt;
ProtectedPacketList::iterator protectedPacketListIt;
protectedPacketListIt = fecPacket->protectedPktList.begin();
while (protectedPacketListIt != fecPacket->protectedPktList.end()) {
delete *protectedPacketListIt;
protectedPacketListIt =
fecPacket->protectedPktList.erase(protectedPacketListIt);
}
assert(fecPacket->protectedPktList.empty());
delete fecPacket;
_fecPacketList.pop_front();
}
assert(_fecPacketList.empty());
}
void ForwardErrorCorrection::InsertMediaPacket(
ReceivedPacket* rxPacket,
RecoveredPacketList* recoveredPacketList) {
RecoveredPacketList::iterator recoveredPacketListIt =
recoveredPacketList->begin();
// Search for duplicate packets.
while (recoveredPacketListIt != recoveredPacketList->end()) {
if (rxPacket->seqNum == (*recoveredPacketListIt)->seqNum) {
// Duplicate packet, no need to add to list.
// Delete duplicate media packet data.
rxPacket->pkt = NULL;
return;
}
recoveredPacketListIt++;
}
RecoveredPacket* recoverdPacketToInsert = new RecoveredPacket;
recoverdPacketToInsert->wasRecovered = false;
recoverdPacketToInsert->returned = false;
recoverdPacketToInsert->seqNum = rxPacket->seqNum;
recoverdPacketToInsert->pkt = rxPacket->pkt;
recoverdPacketToInsert->pkt->length = rxPacket->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.
recoveredPacketList->push_back(recoverdPacketToInsert);
recoveredPacketList->sort(SortablePacket::LessThan);
UpdateCoveringFECPackets(recoverdPacketToInsert);
}
void ForwardErrorCorrection::UpdateCoveringFECPackets(RecoveredPacket* packet) {
for (FecPacketList::iterator it = _fecPacketList.begin();
it != _fecPacketList.end(); ++it) {
// Is this FEC packet protecting the media packet |packet|?
ProtectedPacketList::iterator protected_it = std::lower_bound(
(*it)->protectedPktList.begin(),
(*it)->protectedPktList.end(),
packet,
SortablePacket::LessThan);
if (protected_it != (*it)->protectedPktList.end() &&
(*protected_it)->seqNum == packet->seqNum) {
// Found an FEC packet which is protecting |packet|.
(*protected_it)->pkt = packet->pkt;
}
}
}
void ForwardErrorCorrection::InsertFECPacket(
ReceivedPacket* rxPacket,
const RecoveredPacketList* recoveredPacketList) {
_fecPacketReceived = true;
// Check for duplicate.
FecPacketList::iterator fecPacketListIt = _fecPacketList.begin();
while (fecPacketListIt != _fecPacketList.end()) {
if (rxPacket->seqNum == (*fecPacketListIt)->seqNum) {
// Delete duplicate FEC packet data.
rxPacket->pkt = NULL;
return;
}
fecPacketListIt++;
}
FecPacket* fecPacket = new FecPacket;
fecPacket->pkt = rxPacket->pkt;
fecPacket->seqNum = rxPacket->seqNum;
fecPacket->ssrc = rxPacket->ssrc;
const uint16_t seqNumBase = ModuleRTPUtility::BufferToUWord16(
&fecPacket->pkt->data[2]);
const uint16_t maskSizeBytes = (fecPacket->pkt->data[0] & 0x40) ?
kMaskSizeLBitSet : kMaskSizeLBitClear; // L bit set?
for (uint16_t byteIdx = 0; byteIdx < maskSizeBytes; byteIdx++) {
uint8_t packetMask = fecPacket->pkt->data[12 + byteIdx];
for (uint16_t bitIdx = 0; bitIdx < 8; bitIdx++) {
if (packetMask & (1 << (7 - bitIdx))) {
ProtectedPacket* protectedPacket = new ProtectedPacket;
fecPacket->protectedPktList.push_back(protectedPacket);
// This wraps naturally with the sequence number.
protectedPacket->seqNum = static_cast<uint16_t>(seqNumBase +
(byteIdx << 3) + bitIdx);
protectedPacket->pkt = NULL;
}
}
}
if (fecPacket->protectedPktList.empty()) {
// All-zero packet mask; we can discard this FEC packet.
WEBRTC_TRACE(kTraceWarning, kTraceRtpRtcp, _id,
"FEC packet %u has an all-zero packet mask.",
fecPacket->seqNum, __FUNCTION__);
delete fecPacket;
} else {
AssignRecoveredPackets(fecPacket,
recoveredPacketList);
// 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.
_fecPacketList.push_back(fecPacket);
_fecPacketList.sort(SortablePacket::LessThan);
if (_fecPacketList.size() > kMaxFecPackets) {
DiscardFECPacket(_fecPacketList.front());
_fecPacketList.pop_front();
}
assert(_fecPacketList.size() <= kMaxFecPackets);
}
}
void ForwardErrorCorrection::AssignRecoveredPackets(
FecPacket* fec_packet,
const RecoveredPacketList* recovered_packets) {
// Search for missing packets which have arrived or have been recovered by
// another FEC packet.
ProtectedPacketList* not_recovered = &fec_packet->protectedPktList;
RecoveredPacketList already_recovered;
std::set_intersection(
recovered_packets->begin(), recovered_packets->end(),
not_recovered->begin(), not_recovered->end(),
std::inserter(already_recovered, already_recovered.end()),
SortablePacket::LessThan);
// Set the FEC pointers to all recovered packets so that we don't have to
// search for them when we are doing recovery.
ProtectedPacketList::iterator not_recovered_it = not_recovered->begin();
for (RecoveredPacketList::iterator it = already_recovered.begin();
it != already_recovered.end(); ++it) {
// Search for the next recovered packet in |not_recovered|.
while ((*not_recovered_it)->seqNum != (*it)->seqNum)
++not_recovered_it;
(*not_recovered_it)->pkt = (*it)->pkt;
}
}
void ForwardErrorCorrection::InsertPackets(
ReceivedPacketList* receivedPacketList,
RecoveredPacketList* recoveredPacketList) {
while (!receivedPacketList->empty()) {
ReceivedPacket* rxPacket = receivedPacketList->front();
if (rxPacket->isFec) {
InsertFECPacket(rxPacket, recoveredPacketList);
} else {
// Insert packet at the end of |recoveredPacketList|.
InsertMediaPacket(rxPacket, recoveredPacketList);
}
// Delete the received packet "wrapper", but not the packet data.
delete rxPacket;
receivedPacketList->pop_front();
}
assert(receivedPacketList->empty());
DiscardOldPackets(recoveredPacketList);
}
void ForwardErrorCorrection::InitRecovery(
const FecPacket* fec_packet,
RecoveredPacket* recovered) {
// This is the first packet which we try to recover with.
const uint16_t ulpHeaderSize = fec_packet->pkt->data[0] & 0x40 ?
kUlpHeaderSizeLBitSet : kUlpHeaderSizeLBitClear; // L bit set?
recovered->pkt = new Packet;
memset(recovered->pkt->data, 0, IP_PACKET_SIZE);
recovered->returned = false;
recovered->wasRecovered = true;
uint8_t protectionLength[2];
// Copy the protection length from the ULP header.
memcpy(protectionLength, &fec_packet->pkt->data[10], 2);
// Copy FEC payload, skipping the ULP header.
memcpy(&recovered->pkt->data[kRtpHeaderSize],
&fec_packet->pkt->data[kFecHeaderSize + ulpHeaderSize],
ModuleRTPUtility::BufferToUWord16(protectionLength));
// Copy the length recovery field.
memcpy(recovered->length_recovery, &fec_packet->pkt->data[8], 2);
// Copy the first 2 bytes of the FEC header.
memcpy(recovered->pkt->data, fec_packet->pkt->data, 2);
// Copy the 5th to 8th bytes of the FEC header.
memcpy(&recovered->pkt->data[4], &fec_packet->pkt->data[4], 4);
// Set the SSRC field.
ModuleRTPUtility::AssignUWord32ToBuffer(&recovered->pkt->data[8],
fec_packet->ssrc);
}
void ForwardErrorCorrection::FinishRecovery(RecoveredPacket* recovered) {
// Set the RTP version to 2.
recovered->pkt->data[0] |= 0x80; // Set the 1st bit.
recovered->pkt->data[0] &= 0xbf; // Clear the 2nd bit.
// Set the SN field.
ModuleRTPUtility::AssignUWord16ToBuffer(&recovered->pkt->data[2],
recovered->seqNum);
// Recover the packet length.
recovered->pkt->length = ModuleRTPUtility::BufferToUWord16(
recovered->length_recovery) + kRtpHeaderSize;
}
void ForwardErrorCorrection::XorPackets(const Packet* src_packet,
RecoveredPacket* dst_packet) {
// XOR with the first 2 bytes of the RTP header.
for (uint32_t i = 0; i < 2; i++) {
dst_packet->pkt->data[i] ^= src_packet->data[i];
}
// XOR with the 5th to 8th bytes of the RTP header.
for (uint32_t i = 4; i < 8; i++) {
dst_packet->pkt->data[i] ^= src_packet->data[i];
}
// XOR with the network-ordered payload size.
uint8_t mediaPayloadLength[2];
ModuleRTPUtility::AssignUWord16ToBuffer(
mediaPayloadLength,
src_packet->length - kRtpHeaderSize);
dst_packet->length_recovery[0] ^= mediaPayloadLength[0];
dst_packet->length_recovery[1] ^= mediaPayloadLength[1];
// XOR with RTP payload.
// TODO(marpan/ajm): Are we doing more XORs than required here?
for (int32_t i = kRtpHeaderSize; i < src_packet->length; i++) {
dst_packet->pkt->data[i] ^= src_packet->data[i];
}
}
void ForwardErrorCorrection::RecoverPacket(
const FecPacket* fecPacket,
RecoveredPacket* recPacketToInsert) {
InitRecovery(fecPacket, recPacketToInsert);
ProtectedPacketList::const_iterator protected_it =
fecPacket->protectedPktList.begin();
while (protected_it != fecPacket->protectedPktList.end()) {
if ((*protected_it)->pkt == NULL) {
// This is the packet we're recovering.
recPacketToInsert->seqNum = (*protected_it)->seqNum;
} else {
XorPackets((*protected_it)->pkt, recPacketToInsert);
}
++protected_it;
}
FinishRecovery(recPacketToInsert);
}
void ForwardErrorCorrection::AttemptRecover(
RecoveredPacketList* recoveredPacketList) {
FecPacketList::iterator fecPacketListIt = _fecPacketList.begin();
while (fecPacketListIt != _fecPacketList.end()) {
// Search for each FEC packet's protected media packets.
int packets_missing = NumCoveredPacketsMissing(*fecPacketListIt);
// We can only recover one packet with an FEC packet.
if (packets_missing == 1) {
// Recovery possible.
RecoveredPacket* packetToInsert = new RecoveredPacket;
packetToInsert->pkt = NULL;
RecoverPacket(*fecPacketListIt, packetToInsert);
// 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.
recoveredPacketList->push_back(packetToInsert);
recoveredPacketList->sort(SortablePacket::LessThan);
UpdateCoveringFECPackets(packetToInsert);
DiscardOldPackets(recoveredPacketList);
DiscardFECPacket(*fecPacketListIt);
fecPacketListIt = _fecPacketList.erase(fecPacketListIt);
// 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.
fecPacketListIt = _fecPacketList.begin();
} else if (packets_missing == 0) {
// Either all protected packets arrived or have been recovered. We can
// discard this FEC packet.
DiscardFECPacket(*fecPacketListIt);
fecPacketListIt = _fecPacketList.erase(fecPacketListIt);
} else {
fecPacketListIt++;
}
}
}
int ForwardErrorCorrection::NumCoveredPacketsMissing(
const FecPacket* fec_packet) {
int packets_missing = 0;
ProtectedPacketList::const_iterator it = fec_packet->protectedPktList.begin();
for (; it != fec_packet->protectedPktList.end(); ++it) {
if ((*it)->pkt == NULL) {
++packets_missing;
if (packets_missing > 1) {
break; // We can't recover more than one packet.
}
}
}
return packets_missing;
}
void ForwardErrorCorrection::DiscardFECPacket(FecPacket* fec_packet) {
while (!fec_packet->protectedPktList.empty()) {
delete fec_packet->protectedPktList.front();
fec_packet->protectedPktList.pop_front();
}
assert(fec_packet->protectedPktList.empty());
delete fec_packet;
}
void ForwardErrorCorrection::DiscardOldPackets(
RecoveredPacketList* recoveredPacketList) {
while (recoveredPacketList->size() > kMaxMediaPackets) {
ForwardErrorCorrection::RecoveredPacket* packet =
recoveredPacketList->front();
delete packet;
recoveredPacketList->pop_front();
}
assert(recoveredPacketList->size() <= kMaxMediaPackets);
}
int32_t ForwardErrorCorrection::DecodeFEC(
ReceivedPacketList* receivedPacketList,
RecoveredPacketList* recoveredPacketList) {
// TODO(marpan/ajm): can we check for multiple ULP headers, and return an
// error?
InsertPackets(receivedPacketList, recoveredPacketList);
AttemptRecover(recoveredPacketList);
return 0;
}
uint16_t ForwardErrorCorrection::PacketOverhead() {
return kFecHeaderSize + kUlpHeaderSizeLBitSet;
}
uint16_t ForwardErrorCorrection::LatestSequenceNumber(uint16_t first,
uint16_t second) {
bool wrap = (first < 0x00ff && second > 0xff00) ||
(first > 0xff00 && second < 0x00ff);
if (second > first && !wrap)
return second;
else if (second <= first && !wrap)
return first;
else if (second < first && wrap)
return second;
else
return first;
}
} // namespace webrtc