This removes the knowledge of frame completeness from the FEC decoder.
Therefore, with this change a recovered packet is only considered old, and will be removed, if more than 48 recovered packets are stored. Packets are immediately reconstructed and sent to the jitter buffer. Before this CL packets were processed on a frame-by-frame basis, and all packets belonging to a frame was sent to the jitter buffer at the same time. The number of FEC packets is also limited to 48. An FEC packet is removed if all protected packets have been recovered or if the FEC packet is considered old. Lot's of tests added. Patch-set 2: - Fixed rtp_fec_unittest.cc to work with the new reconstruction. - Added reference counting of Packet to be able to keep references to them from FecPacket between different reconstruction runs. - Rewrote the packet search to use std::set_intersection. BUG= TEST= Review URL: https://webrtc-codereview.appspot.com/379005 git-svn-id: http://webrtc.googlecode.com/svn/trunk@1651 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
parent
cde1c0b519
commit
7adab0922d
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2011 The WebRTC project authors. All Rights Reserved.
|
||||
* 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
|
||||
@ -8,13 +8,15 @@
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
#include <iterator>
|
||||
|
||||
#include "forward_error_correction.h"
|
||||
#include "forward_error_correction_internal.h"
|
||||
#include "rtp_utility.h"
|
||||
#include "trace.h"
|
||||
#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 {
|
||||
|
||||
@ -33,42 +35,39 @@ 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.
|
||||
//
|
||||
struct ProtectedPacket {
|
||||
uint16_t seqNum;
|
||||
ForwardErrorCorrection::Packet* pkt;
|
||||
// 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.
|
||||
//
|
||||
struct FecPacket {
|
||||
std::list<ProtectedPacket*> protectedPktList;
|
||||
uint16_t seqNum;
|
||||
// TODO(holmer): Refactor into a proper class.
|
||||
class FecPacket : public ForwardErrorCorrection::SortablePacket {
|
||||
public:
|
||||
ProtectedPacketList protectedPktList;
|
||||
uint32_t ssrc; // SSRC of the current frame.
|
||||
ForwardErrorCorrection::Packet* pkt;
|
||||
scoped_refptr<ForwardErrorCorrection::Packet> pkt;
|
||||
};
|
||||
|
||||
bool ForwardErrorCorrection::CompareRecoveredPackets(RecoveredPacket* first,
|
||||
RecoveredPacket* second) {
|
||||
if ((first->seqNum > second->seqNum &&
|
||||
(first->seqNum - kMaxMediaPackets) < second->seqNum) ||
|
||||
// We have a wrap in sequence number if the first sequence number is low,
|
||||
// defined and lower than kMaxMediaPackets and second sequence number is
|
||||
// high defined as max sequence number (65535) - kMaxMediaPackets.
|
||||
(first->seqNum < kMaxMediaPackets && // Wrap guard.
|
||||
second->seqNum > (65535 - kMaxMediaPackets))) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
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),
|
||||
_seqNumBase(0),
|
||||
_lastMediaPacketReceived(false),
|
||||
_fecPacketReceived(false) {
|
||||
}
|
||||
|
||||
@ -93,11 +92,11 @@ ForwardErrorCorrection::~ForwardErrorCorrection() {
|
||||
// | |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
int32_t ForwardErrorCorrection::GenerateFEC(
|
||||
const std::list<Packet*>& mediaPacketList,
|
||||
const PacketList& mediaPacketList,
|
||||
uint8_t protectionFactor,
|
||||
int numImportantPackets,
|
||||
bool useUnequalProtection,
|
||||
std::list<Packet*>* fecPacketList) {
|
||||
PacketList* fecPacketList) {
|
||||
if (mediaPacketList.empty()) {
|
||||
WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id,
|
||||
"%s media packet list is empty", __FUNCTION__);
|
||||
@ -135,7 +134,7 @@ int32_t ForwardErrorCorrection::GenerateFEC(
|
||||
return -1;
|
||||
}
|
||||
// Do some error checking on the media packets.
|
||||
std::list<Packet*>::const_iterator mediaListIt = mediaPacketList.begin();
|
||||
PacketList::const_iterator mediaListIt = mediaPacketList.begin();
|
||||
while (mediaListIt != mediaPacketList.end()) {
|
||||
Packet* mediaPacket = *mediaListIt;
|
||||
assert(mediaPacket);
|
||||
@ -192,7 +191,7 @@ int32_t ForwardErrorCorrection::GenerateFEC(
|
||||
}
|
||||
|
||||
void ForwardErrorCorrection::GenerateFecBitStrings(
|
||||
const std::list<Packet*>& mediaPacketList,
|
||||
const PacketList& mediaPacketList,
|
||||
uint8_t* packetMask,
|
||||
uint32_t numFecPackets) {
|
||||
uint8_t mediaPayloadLength[2];
|
||||
@ -204,7 +203,7 @@ void ForwardErrorCorrection::GenerateFecBitStrings(
|
||||
const uint16_t fecRtpOffset = kFecHeaderSize + ulpHeaderSize - kRtpHeaderSize;
|
||||
|
||||
for (uint32_t i = 0; i < numFecPackets; i++) {
|
||||
std::list<Packet*>::const_iterator mediaListIt = mediaPacketList.begin();
|
||||
PacketList::const_iterator mediaListIt = mediaPacketList.begin();
|
||||
uint32_t pktMaskIdx = i * numMaskBytes;
|
||||
uint32_t mediaPktIdx = 0;
|
||||
uint16_t fecPacketLength = 0;
|
||||
@ -271,7 +270,7 @@ void ForwardErrorCorrection::GenerateFecBitStrings(
|
||||
}
|
||||
|
||||
void ForwardErrorCorrection::GenerateFecUlpHeaders(
|
||||
const std::list<Packet*>& mediaPacketList,
|
||||
const PacketList& mediaPacketList,
|
||||
uint8_t* packetMask,
|
||||
uint32_t numFecPackets) {
|
||||
// -- Generate FEC and ULP headers --
|
||||
@ -295,7 +294,7 @@ void ForwardErrorCorrection::GenerateFecUlpHeaders(
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | mask cont. (present only when L = 1) |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
std::list<Packet*>::const_iterator mediaListIt = mediaPacketList.begin();
|
||||
PacketList::const_iterator mediaListIt = mediaPacketList.begin();
|
||||
Packet* mediaPacket = *mediaListIt;
|
||||
assert(mediaPacket != NULL);
|
||||
const uint8_t lBit = mediaPacketList.size() > 16 ? 1 : 0;
|
||||
@ -330,35 +329,28 @@ void ForwardErrorCorrection::GenerateFecUlpHeaders(
|
||||
}
|
||||
|
||||
void ForwardErrorCorrection::ResetState(
|
||||
std::list<RecoveredPacket*>* recoveredPacketList) {
|
||||
_seqNumBase = 0;
|
||||
_lastMediaPacketReceived = false;
|
||||
RecoveredPacketList* recoveredPacketList) {
|
||||
_fecPacketReceived = false;
|
||||
|
||||
// Free the memory for any existing recovered packets, if the user hasn't.
|
||||
while (!recoveredPacketList->empty()) {
|
||||
std::list<RecoveredPacket*>::iterator recoveredPacketListIt =
|
||||
recoveredPacketList->begin();
|
||||
RecoveredPacket* recPacket = *recoveredPacketListIt;
|
||||
delete recPacket->pkt;
|
||||
delete recPacket;
|
||||
delete recoveredPacketList->front();
|
||||
recoveredPacketList->pop_front();
|
||||
}
|
||||
assert(recoveredPacketList->empty());
|
||||
|
||||
// Free the FEC packet list.
|
||||
while (!_fecPacketList.empty()) {
|
||||
std::list<FecPacket*>::iterator fecPacketListIt = _fecPacketList.begin();
|
||||
FecPacketList::iterator fecPacketListIt = _fecPacketList.begin();
|
||||
FecPacket* fecPacket = *fecPacketListIt;
|
||||
std::list<ProtectedPacket*>::iterator protectedPacketListIt;
|
||||
ProtectedPacketList::iterator protectedPacketListIt;
|
||||
protectedPacketListIt = fecPacket->protectedPktList.begin();
|
||||
while (protectedPacketListIt != fecPacket->protectedPktList.end()) {
|
||||
delete *protectedPacketListIt;
|
||||
protectedPacketListIt++;
|
||||
fecPacket->protectedPktList.pop_front();
|
||||
protectedPacketListIt =
|
||||
fecPacket->protectedPktList.erase(protectedPacketListIt);
|
||||
}
|
||||
assert(fecPacket->protectedPktList.empty());
|
||||
delete fecPacket->pkt;
|
||||
delete fecPacket;
|
||||
_fecPacketList.pop_front();
|
||||
}
|
||||
@ -367,74 +359,75 @@ void ForwardErrorCorrection::ResetState(
|
||||
|
||||
void ForwardErrorCorrection::InsertMediaPacket(
|
||||
ReceivedPacket* rxPacket,
|
||||
std::list<RecoveredPacket*>* recoveredPacketList) {
|
||||
if (rxPacket->lastMediaPktInFrame) {
|
||||
if (_lastMediaPacketReceived) {
|
||||
// We already received the last packet.
|
||||
WEBRTC_TRACE(kTraceWarning, kTraceRtpRtcp, _id,
|
||||
"%s last media packet marked more than once per frame",
|
||||
__FUNCTION__);
|
||||
}
|
||||
_lastMediaPacketReceived = true;
|
||||
}
|
||||
bool duplicatePacket = false;
|
||||
std::list<RecoveredPacket*>::iterator recoveredPacketListIt =
|
||||
RecoveredPacketList* recoveredPacketList) {
|
||||
RecoveredPacketList::iterator recoveredPacketListIt =
|
||||
recoveredPacketList->begin();
|
||||
|
||||
// Search for duplicate packets.
|
||||
while (recoveredPacketListIt != recoveredPacketList->end()) {
|
||||
RecoveredPacket* recPacket = *recoveredPacketListIt;
|
||||
if (rxPacket->seqNum == recPacket->seqNum) {
|
||||
if (rxPacket->seqNum == (*recoveredPacketListIt)->seqNum) {
|
||||
// Duplicate packet, no need to add to list.
|
||||
duplicatePacket = true;
|
||||
break;
|
||||
// Delete duplicate media packet data.
|
||||
rxPacket->pkt = NULL;
|
||||
return;
|
||||
}
|
||||
recoveredPacketListIt++;
|
||||
}
|
||||
if (duplicatePacket) {
|
||||
// Delete duplicate media packet data.
|
||||
delete rxPacket->pkt;
|
||||
return;
|
||||
}
|
||||
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::InsertFECPacket(ReceivedPacket* rxPacket) {
|
||||
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.
|
||||
bool duplicatePacket = false;
|
||||
std::list<FecPacket*>::iterator fecPacketListIt = _fecPacketList.begin();
|
||||
FecPacketList::iterator fecPacketListIt = _fecPacketList.begin();
|
||||
while (fecPacketListIt != _fecPacketList.end()) {
|
||||
FecPacket* fecPacket = *fecPacketListIt;
|
||||
if (rxPacket->seqNum == fecPacket->seqNum) {
|
||||
duplicatePacket = true;
|
||||
break;
|
||||
if (rxPacket->seqNum == (*fecPacketListIt)->seqNum) {
|
||||
// Delete duplicate FEC packet data.
|
||||
rxPacket->pkt = NULL;
|
||||
return;
|
||||
}
|
||||
fecPacketListIt++;
|
||||
}
|
||||
if (duplicatePacket) {
|
||||
// Delete duplicate FEC packet data.
|
||||
delete rxPacket->pkt;
|
||||
rxPacket->pkt = NULL;
|
||||
return;
|
||||
}
|
||||
FecPacket* fecPacket = new FecPacket;
|
||||
fecPacket->pkt = rxPacket->pkt;
|
||||
fecPacket->seqNum = rxPacket->seqNum;
|
||||
fecPacket->ssrc = rxPacket->ssrc;
|
||||
|
||||
// We store this for determining frame completion later.
|
||||
_seqNumBase = ModuleRTPUtility::BufferToUWord16(
|
||||
const uint16_t seqNumBase = ModuleRTPUtility::BufferToUWord16(
|
||||
&fecPacket->pkt->data[2]);
|
||||
|
||||
const uint16_t maskSizeBytes = (fecPacket->pkt->data[0] & 0x40) ?
|
||||
kMaskSizeLBitSet : kMaskSizeLBitClear; // L bit set?
|
||||
kMaskSizeLBitSet : kMaskSizeLBitClear; // L bit set?
|
||||
|
||||
for (uint16_t byteIdx = 0; byteIdx < maskSizeBytes; byteIdx++) {
|
||||
uint8_t packetMask = fecPacket->pkt->data[12 + byteIdx];
|
||||
@ -443,7 +436,7 @@ void ForwardErrorCorrection::InsertFECPacket(ReceivedPacket* rxPacket) {
|
||||
ProtectedPacket* protectedPacket = new ProtectedPacket;
|
||||
fecPacket->protectedPktList.push_back(protectedPacket);
|
||||
// This wraps naturally with the sequence number.
|
||||
protectedPacket->seqNum = static_cast<uint16_t>(_seqNumBase +
|
||||
protectedPacket->seqNum = static_cast<uint16_t>(seqNumBase +
|
||||
(byteIdx << 3) + bitIdx);
|
||||
protectedPacket->pkt = NULL;
|
||||
}
|
||||
@ -451,24 +444,60 @@ void ForwardErrorCorrection::InsertFECPacket(ReceivedPacket* rxPacket) {
|
||||
}
|
||||
if (fecPacket->protectedPktList.empty()) {
|
||||
// All-zero packet mask; we can discard this FEC packet.
|
||||
delete fecPacket->pkt;
|
||||
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(
|
||||
std::list<ReceivedPacket*>* receivedPacketList,
|
||||
std::list<RecoveredPacket*>* recoveredPacketList) {
|
||||
ReceivedPacketList* receivedPacketList,
|
||||
RecoveredPacketList* recoveredPacketList) {
|
||||
|
||||
while (!receivedPacketList->empty()) {
|
||||
ReceivedPacket* rxPacket = receivedPacketList->front();
|
||||
|
||||
if (rxPacket->isFec) {
|
||||
InsertFECPacket(rxPacket);
|
||||
InsertFECPacket(rxPacket, recoveredPacketList);
|
||||
} else {
|
||||
// Insert packet in end of list.
|
||||
// Insert packet at the end of |recoveredPacketList|.
|
||||
InsertMediaPacket(rxPacket, recoveredPacketList);
|
||||
}
|
||||
// Delete the received packet "wrapper", but not the packet data.
|
||||
@ -476,247 +505,195 @@ void ForwardErrorCorrection::InsertPackets(
|
||||
receivedPacketList->pop_front();
|
||||
}
|
||||
assert(receivedPacketList->empty());
|
||||
// Sort our recovered packet list.
|
||||
recoveredPacketList->sort(CompareRecoveredPackets);
|
||||
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,
|
||||
const FecPacket* fecPacket,
|
||||
RecoveredPacket* recPacketToInsert) {
|
||||
uint8_t lengthRecovery[2];
|
||||
const uint16_t ulpHeaderSize = fecPacket.pkt->data[0] & 0x40 ?
|
||||
kUlpHeaderSizeLBitSet : kUlpHeaderSizeLBitClear; // L bit set?
|
||||
|
||||
recPacketToInsert->wasRecovered = true;
|
||||
recPacketToInsert->pkt = new Packet;
|
||||
memset(recPacketToInsert->pkt->data, 0, IP_PACKET_SIZE);
|
||||
|
||||
uint8_t protectionLength[2];
|
||||
// Copy the protection length from the ULP header.
|
||||
memcpy(&protectionLength, &fecPacket.pkt->data[10], 2);
|
||||
|
||||
// Copy the first 2 bytes of the FEC header.
|
||||
memcpy(recPacketToInsert->pkt->data, fecPacket.pkt->data, 2);
|
||||
|
||||
// Copy the 5th to 8th bytes of the FEC header.
|
||||
memcpy(&recPacketToInsert->pkt->data[4], &fecPacket.pkt->data[4], 4);
|
||||
|
||||
// Set the SSRC field.
|
||||
ModuleRTPUtility::AssignUWord32ToBuffer(&recPacketToInsert->pkt->data[8],
|
||||
fecPacket.ssrc);
|
||||
|
||||
// Copy the length recovery field.
|
||||
memcpy(&lengthRecovery, &fecPacket.pkt->data[8], 2);
|
||||
|
||||
// Copy FEC payload, skipping the ULP header.
|
||||
memcpy(&recPacketToInsert->pkt->data[kRtpHeaderSize],
|
||||
&fecPacket.pkt->data[kFecHeaderSize + ulpHeaderSize],
|
||||
ModuleRTPUtility::BufferToUWord16(protectionLength));
|
||||
|
||||
std::list<ProtectedPacket*>::const_iterator protectedPacketListIt =
|
||||
fecPacket.protectedPktList.begin();
|
||||
|
||||
while (protectedPacketListIt != fecPacket.protectedPktList.end()) {
|
||||
ProtectedPacket* protectedPacket = *protectedPacketListIt;
|
||||
if (protectedPacket->pkt == NULL) {
|
||||
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 = protectedPacket->seqNum;
|
||||
recPacketToInsert->seqNum = (*protected_it)->seqNum;
|
||||
} else {
|
||||
// XOR with the first 2 bytes of the RTP header.
|
||||
for (uint32_t i = 0; i < 2; i++) {
|
||||
recPacketToInsert->pkt->data[i] ^= protectedPacket->pkt->data[i];
|
||||
}
|
||||
// XOR with the 5th to 8th bytes of the RTP header.
|
||||
for (uint32_t i = 4; i < 8; i++) {
|
||||
recPacketToInsert->pkt->data[i] ^= protectedPacket->pkt->data[i];
|
||||
}
|
||||
// XOR with the network-ordered payload size.
|
||||
uint8_t mediaPayloadLength[2];
|
||||
ModuleRTPUtility::AssignUWord16ToBuffer(
|
||||
mediaPayloadLength,
|
||||
protectedPacket->pkt->length - kRtpHeaderSize);
|
||||
lengthRecovery[0] ^= mediaPayloadLength[0];
|
||||
lengthRecovery[1] ^= mediaPayloadLength[1];
|
||||
|
||||
// XOR with RTP payload.
|
||||
// TODO: Are we doing more XORs than required here?
|
||||
for (int32_t i = kRtpHeaderSize;
|
||||
i < protectedPacket->pkt->length;
|
||||
i++) {
|
||||
recPacketToInsert->pkt->data[i] ^= protectedPacket->pkt->data[i];
|
||||
}
|
||||
XorPackets((*protected_it)->pkt, recPacketToInsert);
|
||||
}
|
||||
protectedPacketListIt++;
|
||||
++protected_it;
|
||||
}
|
||||
// Set the RTP version to 2.
|
||||
recPacketToInsert->pkt->data[0] |= 0x80; // Set the 1st bit.
|
||||
recPacketToInsert->pkt->data[0] &= 0xbf; // Clear the 2nd bit.
|
||||
|
||||
// Assume a recovered marker bit indicates the last media packet in a frame.
|
||||
if (recPacketToInsert->pkt->data[1] & 0x80) {
|
||||
if (_lastMediaPacketReceived) {
|
||||
// Multiple marker bits are illegal.
|
||||
WEBRTC_TRACE(kTraceWarning, kTraceRtpRtcp, _id,
|
||||
"%s recovered media packet contains a marker bit, but the last "
|
||||
"media packet in this frame has already been marked",
|
||||
__FUNCTION__);
|
||||
}
|
||||
_lastMediaPacketReceived = true;
|
||||
}
|
||||
// Set the SN field.
|
||||
ModuleRTPUtility::AssignUWord16ToBuffer(&recPacketToInsert->pkt->data[2],
|
||||
recPacketToInsert->seqNum);
|
||||
// Recover the packet length.
|
||||
recPacketToInsert->pkt->length =
|
||||
ModuleRTPUtility::BufferToUWord16(lengthRecovery) + kRtpHeaderSize;
|
||||
}
|
||||
|
||||
uint32_t ForwardErrorCorrection::NumberOfProtectedPackets(
|
||||
const FecPacket& fecPacket,
|
||||
std::list<RecoveredPacket*>* recoveredPacketList) {
|
||||
uint32_t protectedPacketsFound = 0;
|
||||
std::list<ProtectedPacket*>::const_iterator protectedPacketListIt =
|
||||
fecPacket.protectedPktList.begin();
|
||||
|
||||
while (protectedPacketListIt != fecPacket.protectedPktList.end()) {
|
||||
ProtectedPacket* protectedPacket = *protectedPacketListIt;
|
||||
if (protectedPacket->pkt != NULL) {
|
||||
// We already have the required packet.
|
||||
protectedPacketsFound++;
|
||||
} else {
|
||||
// Search for the required packet.
|
||||
std::list<RecoveredPacket*>::iterator recoveredPacketListIt =
|
||||
recoveredPacketList->begin();
|
||||
|
||||
while (recoveredPacketListIt != recoveredPacketList->end()) {
|
||||
RecoveredPacket* recPacket = *recoveredPacketListIt;
|
||||
recoveredPacketListIt++;
|
||||
if (protectedPacket->seqNum == recPacket->seqNum) {
|
||||
protectedPacket->pkt = recPacket->pkt;
|
||||
protectedPacketsFound++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Since the recovered packet list is already sorted, we don't need to
|
||||
// restart at the beginning of the list unless the previous protected
|
||||
// packet wasn't found.
|
||||
if (protectedPacket->pkt == NULL) {
|
||||
recoveredPacketListIt = recoveredPacketList->begin();
|
||||
}
|
||||
}
|
||||
protectedPacketListIt++;
|
||||
}
|
||||
return protectedPacketsFound;
|
||||
FinishRecovery(recPacketToInsert);
|
||||
}
|
||||
|
||||
void ForwardErrorCorrection::AttemptRecover(
|
||||
std::list<RecoveredPacket*>* recoveredPacketList) {
|
||||
std::list<FecPacket*>::iterator fecPacketListIt = _fecPacketList.begin();
|
||||
RecoveredPacketList* recoveredPacketList) {
|
||||
FecPacketList::iterator fecPacketListIt = _fecPacketList.begin();
|
||||
while (fecPacketListIt != _fecPacketList.end()) {
|
||||
// Store this in case a discard is required.
|
||||
std::list<FecPacket*>::iterator fecPacketListItToDiscard = fecPacketListIt;
|
||||
|
||||
// Search for each FEC packet's protected media packets.
|
||||
FecPacket* fecPacket = *fecPacketListIt;
|
||||
uint32_t protectedPacketsFound =
|
||||
NumberOfProtectedPackets(*fecPacket, recoveredPacketList);
|
||||
int packets_missing = NumCoveredPacketsMissing(*fecPacketListIt);
|
||||
|
||||
if (protectedPacketsFound == fecPacket->protectedPktList.size() - 1) {
|
||||
// We can only recover one packet with an FEC packet.
|
||||
if (packets_missing == 1) {
|
||||
// Recovery possible.
|
||||
RecoveredPacket* packetToInsert = new RecoveredPacket;
|
||||
RecoverPacket(*fecPacket, packetToInsert);
|
||||
packetToInsert->pkt = NULL;
|
||||
RecoverPacket(*fecPacketListIt, packetToInsert);
|
||||
|
||||
// Add recovered packet in back of list.
|
||||
// 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);
|
||||
|
||||
// Sort our recovered packet list.
|
||||
recoveredPacketList->sort(CompareRecoveredPackets);
|
||||
|
||||
protectedPacketsFound++;
|
||||
assert(protectedPacketsFound == fecPacket->protectedPktList.size());
|
||||
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();
|
||||
if (_fecPacketList.begin() == fecPacketListItToDiscard) {
|
||||
// If we're deleting the first item, we need to get the next first.
|
||||
fecPacketListIt++;
|
||||
}
|
||||
} 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++;
|
||||
}
|
||||
if (protectedPacketsFound == fecPacket->protectedPktList.size()) {
|
||||
// Either all protected packets arrived or have been recovered.
|
||||
// We can discard this FEC packet.
|
||||
std::list<ProtectedPacket*>::iterator protectedPacketListIt =
|
||||
fecPacket->protectedPktList.begin();
|
||||
while (protectedPacketListIt != fecPacket->protectedPktList.end()) {
|
||||
delete *protectedPacketListIt;
|
||||
protectedPacketListIt++;
|
||||
fecPacket->protectedPktList.pop_front();
|
||||
}
|
||||
assert(fecPacket->protectedPktList.empty());
|
||||
delete fecPacket->pkt;
|
||||
delete fecPacket;
|
||||
_fecPacketList.erase(fecPacketListItToDiscard);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int32_t ForwardErrorCorrection::DecodeFEC(
|
||||
std::list<ReceivedPacket*>* receivedPacketList,
|
||||
std::list<RecoveredPacket*>* recoveredPacketList,
|
||||
uint16_t lastFECSeqNum,
|
||||
bool& frameComplete) {
|
||||
// TODO: can we check for multiple ULP headers, and return an error?
|
||||
|
||||
// Allow an empty received packet list when complete is true as a teardown
|
||||
// indicator.
|
||||
if (receivedPacketList->empty() && !frameComplete) {
|
||||
WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id,
|
||||
"%s received packet list is empty, but we're not tearing down here",
|
||||
__FUNCTION__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (frameComplete) {
|
||||
// We have a new frame.
|
||||
ResetState(recoveredPacketList);
|
||||
}
|
||||
InsertPackets(receivedPacketList, recoveredPacketList);
|
||||
|
||||
AttemptRecover(recoveredPacketList);
|
||||
|
||||
// Check if we have a complete frame.
|
||||
frameComplete = false;
|
||||
|
||||
if (_lastMediaPacketReceived) {
|
||||
frameComplete = true;
|
||||
if(!_fecPacketReceived) {
|
||||
// best estimate we have if we have not received a FEC packet
|
||||
_seqNumBase = lastFECSeqNum + 1;
|
||||
}
|
||||
// With this we assume the user is attempting to decode a FEC stream.
|
||||
uint16_t seqNumIdx = 0;
|
||||
std::list<RecoveredPacket*>::iterator recPacketListIt =
|
||||
recoveredPacketList->begin();
|
||||
while (recPacketListIt != recoveredPacketList->end() &&
|
||||
frameComplete == true) {
|
||||
RecoveredPacket* recPacket = *recPacketListIt;
|
||||
if (recPacket->seqNum !=
|
||||
static_cast<uint16_t>(_seqNumBase + seqNumIdx)) {
|
||||
frameComplete = false;
|
||||
break;
|
||||
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.
|
||||
}
|
||||
recPacketListIt++;
|
||||
seqNumIdx++;
|
||||
}
|
||||
}
|
||||
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
|
||||
|
@ -14,13 +14,15 @@
|
||||
#include <list>
|
||||
#include <vector>
|
||||
|
||||
#include "modules/rtp_rtcp/interface/rtp_rtcp_defines.h"
|
||||
#include "system_wrappers/interface/ref_count.h"
|
||||
#include "system_wrappers/interface/scoped_refptr.h"
|
||||
#include "typedefs.h"
|
||||
#include "rtp_rtcp_defines.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// Forward declaration.
|
||||
struct FecPacket;
|
||||
class FecPacket;
|
||||
|
||||
/**
|
||||
* Performs codec-independent forward error correction (FEC), based on RFC 5109.
|
||||
@ -33,9 +35,45 @@ class ForwardErrorCorrection {
|
||||
// Maximum number of media packets we can protect
|
||||
static const int kMaxMediaPackets = 48;
|
||||
|
||||
struct Packet {
|
||||
uint16_t length; /**> Length of packet in bytes. */
|
||||
uint8_t data[IP_PACKET_SIZE]; /**> Packet data. */
|
||||
// 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() : ref_count_(0) {}
|
||||
virtual ~Packet() {}
|
||||
|
||||
// Add a reference.
|
||||
virtual int32_t AddRef() {
|
||||
return ++ref_count_;
|
||||
}
|
||||
|
||||
// Release a reference. Will delete the object if the reference count
|
||||
// reaches zero.
|
||||
virtual int32_t Release() {
|
||||
int32_t ref_count;
|
||||
ref_count = --ref_count_;
|
||||
if (ref_count == 0)
|
||||
delete this;
|
||||
return ref_count;
|
||||
}
|
||||
|
||||
uint16_t length; // Length of packet in bytes.
|
||||
uint8_t data[IP_PACKET_SIZE]; // Packet data.
|
||||
|
||||
private:
|
||||
int32_t ref_count_; // Counts the number of references to a packet.
|
||||
};
|
||||
|
||||
// TODO(holmer): Refactor into a proper class.
|
||||
class SortablePacket {
|
||||
public:
|
||||
// True if first is <= than second.
|
||||
static bool LessThan(const SortablePacket* first,
|
||||
const SortablePacket* second);
|
||||
|
||||
uint16_t seqNum;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -56,31 +94,37 @@ class ForwardErrorCorrection {
|
||||
* media packets, but in the case of an FEC packet protecting a single
|
||||
* missing media packet, we have no other means of obtaining it.
|
||||
*/
|
||||
struct ReceivedPacket {
|
||||
uint16_t seqNum; /**> Sequence number of packet. */
|
||||
uint32_t ssrc; /**> SSRC of the current frame. Must be set for FEC
|
||||
packets, but not required for media packets. */
|
||||
bool isFec; /**> Set to true if this is an FEC packet and false
|
||||
otherwise. */
|
||||
bool lastMediaPktInFrame; /**> Set to true to mark the last media packet in
|
||||
the frame and false otherwise. */
|
||||
Packet* pkt; /**> Pointer to the packet storage. */
|
||||
// TODO(holmer): Refactor into a proper class.
|
||||
class ReceivedPacket : public SortablePacket {
|
||||
public:
|
||||
uint32_t ssrc; // SSRC of the current frame. Must be set for FEC
|
||||
// packets, but not required for media packets.
|
||||
bool isFec; // Set to true if this is an FEC packet and false
|
||||
// otherwise.
|
||||
scoped_refptr<Packet> pkt; // Pointer to the packet storage.
|
||||
};
|
||||
|
||||
/**
|
||||
* The recovered list parameter of #DecodeFEC() will reference structs of
|
||||
* this type.
|
||||
*/
|
||||
struct RecoveredPacket {
|
||||
bool wasRecovered; /**> Will be true if this packet was recovered by
|
||||
the FEC. Otherwise it was a media packet passed in
|
||||
through the received packet list. */
|
||||
uint16_t seqNum; /**> Sequence number of the packet. This is mostly for
|
||||
implementation convenience but could be utilized
|
||||
by the user if so desired. */
|
||||
Packet* pkt; /**> Pointer to the packet storage. */
|
||||
// TODO(holmer): Refactor into a proper class.
|
||||
class RecoveredPacket : public SortablePacket {
|
||||
public:
|
||||
bool wasRecovered; // Will be true if this packet was recovered by
|
||||
// the FEC. Otherwise it was a media packet passed in
|
||||
// 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.
|
||||
scoped_refptr<Packet> pkt; // Pointer to the packet storage.
|
||||
};
|
||||
|
||||
typedef std::list<Packet*> PacketList;
|
||||
typedef std::list<ReceivedPacket*> ReceivedPacketList;
|
||||
typedef std::list<RecoveredPacket*> RecoveredPacketList;
|
||||
|
||||
/**
|
||||
* \param[in] id Module ID
|
||||
*/
|
||||
@ -118,17 +162,17 @@ class ForwardErrorCorrection {
|
||||
*
|
||||
* \return 0 on success, -1 on failure.
|
||||
*/
|
||||
int32_t GenerateFEC(const std::list<Packet*>& mediaPacketList,
|
||||
int32_t GenerateFEC(const PacketList& mediaPacketList,
|
||||
uint8_t protectionFactor,
|
||||
int numImportantPackets,
|
||||
bool useUnequalProtection,
|
||||
std::list<Packet*>* fecPacketList);
|
||||
PacketList* fecPacketList);
|
||||
|
||||
/**
|
||||
* Decodes a list of media and FEC packets. It will parse the input received
|
||||
* packet list, storing FEC packets internally and inserting media packets to
|
||||
* the output recovered packet list. The recovered list will be sorted by
|
||||
* as cending sequence number and have duplicates removed. The function
|
||||
* ascending sequence number and have duplicates removed. The function
|
||||
* should be called as new packets arrive, with the recovered list being
|
||||
* progressively assembled with each call. The received packet list will be
|
||||
* empty at output.\n
|
||||
@ -139,12 +183,8 @@ class ForwardErrorCorrection {
|
||||
* packets, in which case they must remove deleted packets from the
|
||||
* recovered list.\n
|
||||
*
|
||||
* Before deleting an instance of the class, call the function with an empty
|
||||
* received packet list and the completion parameter set to true. This will
|
||||
* free any outstanding memory.
|
||||
*
|
||||
* \param[in] receivedPacketList List of new received packets, of type
|
||||
* #ReceivedPacket, beloning to a single
|
||||
* #ReceivedPacket, belonging to a single
|
||||
* frame. At output the list will be empty,
|
||||
* with packets either stored internally,
|
||||
* or accessible through the recovered list.
|
||||
@ -152,24 +192,12 @@ class ForwardErrorCorrection {
|
||||
* #RecoveredPacket, belonging to a single
|
||||
* frame. The memory available through the
|
||||
* list will be valid until the next call to
|
||||
* DecodeFEC() in which the completion
|
||||
* parameter is set to true.
|
||||
* \param[in] lastFECSeqNum Estimated last seqNumber before this frame.
|
||||
* \param[in,out] frameComplete Set to true on input to indicate the start
|
||||
* of a new frame. On output, this will be
|
||||
* set to true if all media packets in the
|
||||
* frame have been recovered. Note that the
|
||||
* frame may be complete without this
|
||||
* parameter having been set, as it may not
|
||||
* always be possible to determine frame
|
||||
* completion.
|
||||
* DecodeFEC().
|
||||
*
|
||||
* \return 0 on success, -1 on failure.
|
||||
*/
|
||||
int32_t DecodeFEC(std::list<ReceivedPacket*>* receivedPacketList,
|
||||
std::list<RecoveredPacket*>* recoveredPacketList,
|
||||
uint16_t lastFECSeqNum,
|
||||
bool& frameComplete);
|
||||
int32_t DecodeFEC(ReceivedPacketList* receivedPacketList,
|
||||
RecoveredPacketList* recoveredPacketList);
|
||||
/**
|
||||
* Gets the size in bytes of the FEC/ULP headers, which must be accounted for
|
||||
* as packet overhead.
|
||||
@ -177,55 +205,84 @@ class ForwardErrorCorrection {
|
||||
*/
|
||||
static uint16_t PacketOverhead();
|
||||
|
||||
private:
|
||||
// True if first is <= than second.
|
||||
static bool CompareRecoveredPackets(RecoveredPacket* first,
|
||||
RecoveredPacket* second);
|
||||
|
||||
void GenerateFecUlpHeaders(const std::list<Packet*>& mediaPacketList,
|
||||
uint8_t* packetMask,
|
||||
uint32_t numFecPackets);
|
||||
|
||||
void GenerateFecBitStrings(const std::list<Packet*>& mediaPacketList,
|
||||
uint8_t* packetMask,
|
||||
uint32_t numFecPackets);
|
||||
|
||||
// Reset internal states from last frame and clear the recoveredPacketList.
|
||||
void ResetState(std::list<RecoveredPacket*>* recoveredPacketList);
|
||||
// Frees all memory allocated by this class.
|
||||
void ResetState(RecoveredPacketList* recoveredPacketList);
|
||||
|
||||
private:
|
||||
typedef std::list<FecPacket*> FecPacketList;
|
||||
|
||||
void GenerateFecUlpHeaders(const PacketList& mediaPacketList,
|
||||
uint8_t* packetMask,
|
||||
uint32_t numFecPackets);
|
||||
|
||||
void GenerateFecBitStrings(const PacketList& mediaPacketList,
|
||||
uint8_t* packetMask,
|
||||
uint32_t numFecPackets);
|
||||
|
||||
// Insert received packets into FEC or recovered list.
|
||||
void InsertPackets(std::list<ReceivedPacket*>* receivedPacketList,
|
||||
std::list<RecoveredPacket*>* recoveredPacketList);
|
||||
void InsertPackets(ReceivedPacketList* receivedPacketList,
|
||||
RecoveredPacketList* recoveredPacketList);
|
||||
|
||||
// Insert media packet into recovered packet list. We delete duplicates.
|
||||
void InsertMediaPacket(ReceivedPacket* rxPacket,
|
||||
std::list<RecoveredPacket*>* recoveredPacketList);
|
||||
RecoveredPacketList* recoveredPacketList);
|
||||
|
||||
// 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);
|
||||
|
||||
// Insert packet into FEC list. We delete duplicates.
|
||||
void InsertFECPacket(ReceivedPacket* rxPacket);
|
||||
void InsertFECPacket(ReceivedPacket* rxPacket,
|
||||
const RecoveredPacketList* recoveredPacketList);
|
||||
|
||||
// Assigns pointers to already recovered packets covered by this FEC packet.
|
||||
static void AssignRecoveredPackets(
|
||||
FecPacket* fec_packet,
|
||||
const RecoveredPacketList* recovered_packets);
|
||||
|
||||
// Insert into recovered list in correct position.
|
||||
void InsertRecoveredPacket(
|
||||
RecoveredPacket* recPacketToInsert,
|
||||
std::list<RecoveredPacket*>* recoveredPacketList);
|
||||
RecoveredPacketList* recoveredPacketList);
|
||||
|
||||
// Attempt to recover missing packets.
|
||||
void AttemptRecover(std::list<RecoveredPacket*>* recoveredPacketList);
|
||||
void AttemptRecover(RecoveredPacketList* recoveredPacketList);
|
||||
|
||||
// Initializes the packet recovery using the FEC packet.
|
||||
static void InitRecovery(const FecPacket* fec_packet,
|
||||
RecoveredPacket* recovered);
|
||||
|
||||
// Performs XOR between |src_packet| and |dst_packet| and stores the result
|
||||
// in |dst_packet|.
|
||||
static void XorPackets(const Packet* src_packet,
|
||||
RecoveredPacket* dst_packet);
|
||||
|
||||
// Finish up the recovery of a packet.
|
||||
static void FinishRecovery(RecoveredPacket* recovered);
|
||||
|
||||
// Recover a missing packet.
|
||||
void RecoverPacket(const FecPacket& fecPacket,
|
||||
void RecoverPacket(const FecPacket* fecPacket,
|
||||
RecoveredPacket* recPacketToInsert);
|
||||
|
||||
// Get number of protected packet in the fecPacket.
|
||||
uint32_t NumberOfProtectedPackets(
|
||||
const FecPacket& fecPacket,
|
||||
std::list<RecoveredPacket*>* recoveredPacketList);
|
||||
// Get the number of missing media packets which are covered by this
|
||||
// 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 FecPacket* fec_packet);
|
||||
|
||||
static uint16_t LatestSequenceNumber(uint16_t first,
|
||||
uint16_t second);
|
||||
|
||||
static void DiscardFECPacket(FecPacket* fec_packet);
|
||||
static void DiscardOldPackets(RecoveredPacketList* recoveredPacketList);
|
||||
|
||||
int32_t _id;
|
||||
std::vector<Packet> _generatedFecPackets;
|
||||
std::list<FecPacket*> _fecPacketList;
|
||||
uint16_t _seqNumBase;
|
||||
bool _lastMediaPacketReceived;
|
||||
FecPacketList _fecPacketList;
|
||||
bool _fecPacketReceived;
|
||||
};
|
||||
} // namespace webrtc
|
||||
|
@ -8,13 +8,14 @@
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "receiver_fec.h"
|
||||
#include "modules/rtp_rtcp/source/receiver_fec.h"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
#include "rtp_receiver_video.h"
|
||||
#include "rtp_utility.h"
|
||||
#include "trace.h"
|
||||
#include "modules/rtp_rtcp/source/rtp_receiver_video.h"
|
||||
#include "modules/rtp_rtcp/source/rtp_utility.h"
|
||||
#include "system_wrappers/interface/scoped_ptr.h"
|
||||
#include "system_wrappers/interface/trace.h"
|
||||
|
||||
// RFC 5109
|
||||
namespace webrtc {
|
||||
@ -22,9 +23,7 @@ ReceiverFEC::ReceiverFEC(const WebRtc_Word32 id, RTPReceiverVideo* owner)
|
||||
: _id(id),
|
||||
_owner(owner),
|
||||
_fec(new ForwardErrorCorrection(id)),
|
||||
_payloadTypeFEC(-1),
|
||||
_lastFECSeqNum(0),
|
||||
_frameComplete(true) {
|
||||
_payloadTypeFEC(-1) {
|
||||
}
|
||||
|
||||
ReceiverFEC::~ReceiverFEC() {
|
||||
@ -32,16 +31,13 @@ ReceiverFEC::~ReceiverFEC() {
|
||||
while (!_receivedPacketList.empty()){
|
||||
ForwardErrorCorrection::ReceivedPacket* receivedPacket =
|
||||
_receivedPacketList.front();
|
||||
delete receivedPacket->pkt;
|
||||
delete receivedPacket;
|
||||
_receivedPacketList.pop_front();
|
||||
}
|
||||
assert(_receivedPacketList.empty());
|
||||
|
||||
if (_fec != NULL) {
|
||||
bool frameComplete = true;
|
||||
_fec->DecodeFEC(&_receivedPacketList, &_recoveredPacketList,_lastFECSeqNum,
|
||||
frameComplete);
|
||||
_fec->ResetState(&_recoveredPacketList);
|
||||
delete _fec;
|
||||
}
|
||||
}
|
||||
@ -84,8 +80,7 @@ WebRtc_Word32 ReceiverFEC::AddReceivedFECPacket(
|
||||
const WebRtcRTPHeader* rtpHeader,
|
||||
const WebRtc_UWord8* incomingRtpPacket,
|
||||
const WebRtc_UWord16 payloadDataLength,
|
||||
bool& FECpacket,
|
||||
bool oldPacket) {
|
||||
bool& FECpacket) {
|
||||
if (_payloadTypeFEC == -1) {
|
||||
return -1;
|
||||
}
|
||||
@ -107,19 +102,10 @@ WebRtc_Word32 ReceiverFEC::AddReceivedFECPacket(
|
||||
if (_payloadTypeFEC == payloadType) {
|
||||
receivedPacket->isFec = true;
|
||||
FECpacket = true;
|
||||
// We don't need to parse old FEC packets.
|
||||
// Old FEC packets are sent to jitter buffer as empty packets in the
|
||||
// callback in rtp_receiver_video.
|
||||
if (oldPacket) {
|
||||
delete receivedPacket->pkt;
|
||||
delete receivedPacket;
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
receivedPacket->isFec = false;
|
||||
FECpacket = false;
|
||||
}
|
||||
receivedPacket->lastMediaPktInFrame = rtpHeader->header.markerBit;
|
||||
receivedPacket->seqNum = rtpHeader->header.sequenceNumber;
|
||||
|
||||
WebRtc_UWord16 blockLength = 0;
|
||||
@ -135,7 +121,6 @@ WebRtc_Word32 ReceiverFEC::AddReceivedFECPacket(
|
||||
// location a corrupt payload can be caught, so don't assert.
|
||||
WEBRTC_TRACE(kTraceWarning, kTraceRtpRtcp, _id,
|
||||
"Corrupt payload found in %s", __FUNCTION__);
|
||||
delete receivedPacket->pkt;
|
||||
delete receivedPacket;
|
||||
return -1;
|
||||
}
|
||||
@ -147,14 +132,12 @@ WebRtc_Word32 ReceiverFEC::AddReceivedFECPacket(
|
||||
// check next RED header
|
||||
if(incomingRtpPacket[rtpHeader->header.headerLength+4] & 0x80) {
|
||||
// more than 2 blocks in packet not supported
|
||||
delete receivedPacket->pkt;
|
||||
delete receivedPacket;
|
||||
assert(false);
|
||||
return -1;
|
||||
}
|
||||
if(blockLength > payloadDataLength - REDHeaderLength) {
|
||||
// block length longer than packet
|
||||
delete receivedPacket->pkt;
|
||||
delete receivedPacket;
|
||||
assert(false);
|
||||
return -1;
|
||||
@ -186,7 +169,6 @@ WebRtc_Word32 ReceiverFEC::AddReceivedFECPacket(
|
||||
secondReceivedPacket->pkt = new ForwardErrorCorrection::Packet;
|
||||
|
||||
secondReceivedPacket->isFec = true;
|
||||
secondReceivedPacket->lastMediaPktInFrame = false;
|
||||
secondReceivedPacket->seqNum = rtpHeader->header.sequenceNumber;
|
||||
|
||||
// copy the FEC payload data
|
||||
@ -226,105 +208,35 @@ WebRtc_Word32 ReceiverFEC::AddReceivedFECPacket(
|
||||
payloadDataLength - REDHeaderLength;
|
||||
}
|
||||
|
||||
if(receivedPacket->isFec) {
|
||||
AddReceivedFECInfo(rtpHeader, NULL, FECpacket);
|
||||
}
|
||||
|
||||
if(receivedPacket->pkt->length == 0) {
|
||||
if (secondReceivedPacket) {
|
||||
delete secondReceivedPacket->pkt;
|
||||
}
|
||||
delete secondReceivedPacket;
|
||||
delete receivedPacket->pkt;
|
||||
delete receivedPacket;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Send any old media packets to jitter buffer, don't push them onto
|
||||
// received list for FEC decoding (we don't do FEC decoding on old packets).
|
||||
if (oldPacket && receivedPacket->isFec == false) {
|
||||
if (ParseAndReceivePacket(receivedPacket->pkt) != 0) {
|
||||
if (secondReceivedPacket) {
|
||||
delete secondReceivedPacket->pkt;
|
||||
}
|
||||
delete secondReceivedPacket;
|
||||
delete receivedPacket->pkt;
|
||||
delete receivedPacket;
|
||||
return -1;
|
||||
}
|
||||
if (secondReceivedPacket) {
|
||||
delete secondReceivedPacket->pkt;
|
||||
}
|
||||
delete secondReceivedPacket;
|
||||
delete receivedPacket->pkt;
|
||||
delete receivedPacket;
|
||||
} else {
|
||||
_receivedPacketList.push_back(receivedPacket);
|
||||
if (secondReceivedPacket) {
|
||||
_receivedPacketList.push_back(secondReceivedPacket);
|
||||
}
|
||||
_receivedPacketList.push_back(receivedPacket);
|
||||
if (secondReceivedPacket) {
|
||||
_receivedPacketList.push_back(secondReceivedPacket);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ReceiverFEC::AddReceivedFECInfo(const WebRtcRTPHeader* rtpHeader,
|
||||
const WebRtc_UWord8* incomingRtpPacket,
|
||||
bool& FECpacket) {
|
||||
// store the highest FEC seq num received
|
||||
if (_lastFECSeqNum >= rtpHeader->header.sequenceNumber) {
|
||||
if (_lastFECSeqNum > 0xff00 && rtpHeader->header.sequenceNumber < 0x0ff ) {
|
||||
// wrap
|
||||
_lastFECSeqNum = rtpHeader->header.sequenceNumber;
|
||||
} else {
|
||||
// old seqNum
|
||||
}
|
||||
} else {
|
||||
// check for a wrap
|
||||
if(rtpHeader->header.sequenceNumber > 0xff00 && _lastFECSeqNum < 0x0ff ) {
|
||||
// old seqNum
|
||||
} else {
|
||||
_lastFECSeqNum = rtpHeader->header.sequenceNumber;
|
||||
}
|
||||
}
|
||||
|
||||
if (incomingRtpPacket) {
|
||||
// get payload type from RED header
|
||||
WebRtc_UWord8 payloadType =
|
||||
incomingRtpPacket[rtpHeader->header.headerLength] & 0x7f;
|
||||
|
||||
// use the payloadType to decide if it's FEC or coded data
|
||||
if(_payloadTypeFEC == payloadType) {
|
||||
FECpacket = true;
|
||||
} else {
|
||||
FECpacket = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
WebRtc_Word32 ReceiverFEC::ProcessReceivedFEC(const bool forceFrameDecode) {
|
||||
WebRtc_Word32 ReceiverFEC::ProcessReceivedFEC() {
|
||||
if (!_receivedPacketList.empty()) {
|
||||
if (_fec->DecodeFEC(&_receivedPacketList, &_recoveredPacketList,
|
||||
_lastFECSeqNum, _frameComplete) != 0) {
|
||||
if (_fec->DecodeFEC(&_receivedPacketList, &_recoveredPacketList) != 0) {
|
||||
return -1;
|
||||
}
|
||||
assert(_receivedPacketList.empty());
|
||||
}
|
||||
if (forceFrameDecode) {
|
||||
_frameComplete = true;
|
||||
}
|
||||
if (_frameComplete) {
|
||||
while (!_recoveredPacketList.empty()) {
|
||||
ForwardErrorCorrection::RecoveredPacket* recoveredPacket =
|
||||
_recoveredPacketList.front();
|
||||
|
||||
if (ParseAndReceivePacket(recoveredPacket->pkt) != 0) {
|
||||
return -1;
|
||||
}
|
||||
delete recoveredPacket->pkt;
|
||||
delete recoveredPacket;
|
||||
_recoveredPacketList.pop_front();
|
||||
ForwardErrorCorrection::RecoveredPacketList::iterator it =
|
||||
_recoveredPacketList.begin();
|
||||
for (; it != _recoveredPacketList.end(); ++it) {
|
||||
if ((*it)->returned) // Already sent to the VCM and the jitter buffer.
|
||||
continue;
|
||||
if (ParseAndReceivePacket((*it)->pkt) != 0) {
|
||||
return -1;
|
||||
}
|
||||
assert(_recoveredPacketList.empty());
|
||||
(*it)->returned = true;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@ -346,4 +258,5 @@ int ReceiverFEC::ParseAndReceivePacket(
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
|
@ -11,8 +11,6 @@
|
||||
#ifndef WEBRTC_MODULES_RTP_RTCP_SOURCE_RECEIVER_FEC_H_
|
||||
#define WEBRTC_MODULES_RTP_RTCP_SOURCE_RECEIVER_FEC_H_
|
||||
|
||||
#include <list>
|
||||
|
||||
#include "rtp_rtcp_defines.h"
|
||||
// This header is included to get the nested declaration of Packet structure.
|
||||
#include "forward_error_correction.h"
|
||||
@ -31,14 +29,9 @@ public:
|
||||
WebRtc_Word32 AddReceivedFECPacket(const WebRtcRTPHeader* rtpHeader,
|
||||
const WebRtc_UWord8* incomingRtpPacket,
|
||||
const WebRtc_UWord16 payloadDataLength,
|
||||
bool& FECpacket,
|
||||
bool oldPacket);
|
||||
bool& FECpacket);
|
||||
|
||||
void AddReceivedFECInfo(const WebRtcRTPHeader* rtpHeader,
|
||||
const WebRtc_UWord8* incomingRtpPacket,
|
||||
bool& FECpacket);
|
||||
|
||||
WebRtc_Word32 ProcessReceivedFEC(const bool forceFrameDecode);
|
||||
WebRtc_Word32 ProcessReceivedFEC();
|
||||
|
||||
void SetPayloadTypeFEC(const WebRtc_Word8 payloadType);
|
||||
|
||||
@ -46,13 +39,14 @@ private:
|
||||
int ParseAndReceivePacket(const ForwardErrorCorrection::Packet* packet);
|
||||
|
||||
int _id;
|
||||
RTPReceiverVideo* _owner;
|
||||
RTPReceiverVideo* _owner;
|
||||
ForwardErrorCorrection* _fec;
|
||||
std::list<ForwardErrorCorrection::ReceivedPacket*> _receivedPacketList;
|
||||
std::list<ForwardErrorCorrection::RecoveredPacket*> _recoveredPacketList;
|
||||
WebRtc_Word8 _payloadTypeFEC;
|
||||
WebRtc_UWord16 _lastFECSeqNum;
|
||||
bool _frameComplete;
|
||||
// TODO(holmer): In the current version _receivedPacketList is never more
|
||||
// than one packet, since we process FEC every time a new packet
|
||||
// arrives. We should remove the list.
|
||||
ForwardErrorCorrection::ReceivedPacketList _receivedPacketList;
|
||||
ForwardErrorCorrection::RecoveredPacketList _recoveredPacketList;
|
||||
WebRtc_Word8 _payloadTypeFEC;
|
||||
};
|
||||
} // namespace webrtc
|
||||
|
||||
|
@ -124,6 +124,7 @@ class ReceiverFecTest : public ::testing::Test {
|
||||
fec_ = new ForwardErrorCorrection(0);
|
||||
receiver_fec_ = new ReceiverFEC(0, &rtp_receiver_video_);
|
||||
generator_ = new FrameGenerator();
|
||||
receiver_fec_->SetPayloadTypeFEC(kFecPayloadType);
|
||||
}
|
||||
|
||||
virtual void TearDown() {
|
||||
@ -132,6 +133,32 @@ class ReceiverFecTest : public ::testing::Test {
|
||||
delete generator_;
|
||||
}
|
||||
|
||||
void GenerateAndAddFrames(int num_frames,
|
||||
int num_packets_per_frame,
|
||||
std::list<RtpPacket*>* media_rtp_packets,
|
||||
std::list<Packet*>* media_packets) {
|
||||
for (int i = 0; i < num_frames; ++i) {
|
||||
GenerateFrame(num_packets_per_frame, i, media_rtp_packets,
|
||||
media_packets);
|
||||
}
|
||||
for (std::list<RtpPacket*>::iterator it = media_rtp_packets->begin();
|
||||
it != media_rtp_packets->end(); ++it) {
|
||||
BuildAndAddRedMediaPacket(*it);
|
||||
}
|
||||
}
|
||||
|
||||
void GenerateFEC(std::list<Packet*>* media_packets,
|
||||
std::list<Packet*>* fec_packets,
|
||||
unsigned int num_fec_packets) {
|
||||
EXPECT_EQ(0, fec_->GenerateFEC(
|
||||
*media_packets,
|
||||
num_fec_packets * 255 / media_packets->size(),
|
||||
0,
|
||||
false,
|
||||
fec_packets));
|
||||
ASSERT_EQ(num_fec_packets, fec_packets->size());
|
||||
}
|
||||
|
||||
void GenerateFrame(int num_media_packets,
|
||||
int frame_offset,
|
||||
std::list<RtpPacket*>* media_rtp_packets,
|
||||
@ -163,8 +190,7 @@ class ReceiverFecTest : public ::testing::Test {
|
||||
red_packet->data,
|
||||
red_packet->length -
|
||||
kRtpHeaderSize,
|
||||
is_fec,
|
||||
false));
|
||||
is_fec));
|
||||
delete red_packet;
|
||||
EXPECT_FALSE(is_fec);
|
||||
}
|
||||
@ -176,8 +202,7 @@ class ReceiverFecTest : public ::testing::Test {
|
||||
red_packet->data,
|
||||
red_packet->length -
|
||||
kRtpHeaderSize,
|
||||
is_fec,
|
||||
false));
|
||||
is_fec));
|
||||
delete red_packet;
|
||||
EXPECT_TRUE(is_fec);
|
||||
}
|
||||
@ -197,20 +222,13 @@ void DeletePackets(std::list<Packet*>* packets) {
|
||||
|
||||
TEST_F(ReceiverFecTest, TwoMediaOneFec) {
|
||||
const unsigned int kNumFecPackets = 1u;
|
||||
const unsigned int kNumMediaPackets = 2u;
|
||||
std::list<RtpPacket*> media_rtp_packets;
|
||||
std::list<Packet*> media_packets;
|
||||
GenerateFrame(2, 0, &media_rtp_packets, &media_packets);
|
||||
std::list<Packet*> fec_packets;
|
||||
EXPECT_EQ(0, fec_->GenerateFEC(media_packets,
|
||||
kNumFecPackets * 255 / kNumMediaPackets,
|
||||
0,
|
||||
false,
|
||||
&fec_packets));
|
||||
ASSERT_EQ(kNumFecPackets, fec_packets.size());
|
||||
GenerateFEC(&media_packets, &fec_packets, kNumFecPackets);
|
||||
|
||||
// Recovery
|
||||
receiver_fec_->SetPayloadTypeFEC(kFecPayloadType);
|
||||
std::list<RtpPacket*>::iterator media_it = media_rtp_packets.begin();
|
||||
BuildAndAddRedMediaPacket(*media_it);
|
||||
// Drop one media packet.
|
||||
@ -223,27 +241,21 @@ TEST_F(ReceiverFecTest, TwoMediaOneFec) {
|
||||
++it;
|
||||
VerifyReconstructedMediaPacket(*it, 1);
|
||||
}
|
||||
EXPECT_EQ(0, receiver_fec_->ProcessReceivedFEC(false));
|
||||
EXPECT_EQ(0, receiver_fec_->ProcessReceivedFEC());
|
||||
|
||||
DeletePackets(&media_packets);
|
||||
}
|
||||
|
||||
TEST_F(ReceiverFecTest, TwoMediaTwoFec) {
|
||||
const unsigned int kNumFecPackets = 2u;
|
||||
const unsigned int kNumMediaPackets = 2u;
|
||||
std::list<RtpPacket*> media_rtp_packets;
|
||||
std::list<Packet*> media_packets;
|
||||
GenerateFrame(2, 0, &media_rtp_packets, &media_packets);
|
||||
std::list<Packet*> fec_packets;
|
||||
EXPECT_EQ(0, fec_->GenerateFEC(media_packets,
|
||||
kNumFecPackets * 255 / kNumMediaPackets,
|
||||
0,
|
||||
false, &fec_packets));
|
||||
ASSERT_EQ(kNumFecPackets, fec_packets.size());
|
||||
GenerateFEC(&media_packets, &fec_packets, kNumFecPackets);
|
||||
|
||||
// Recovery
|
||||
// Drop both media packets.
|
||||
receiver_fec_->SetPayloadTypeFEC(kFecPayloadType);
|
||||
std::list<Packet*>::iterator fec_it = fec_packets.begin();
|
||||
BuildAndAddRedFecPacket(*fec_it);
|
||||
++fec_it;
|
||||
@ -255,28 +267,21 @@ TEST_F(ReceiverFecTest, TwoMediaTwoFec) {
|
||||
++it;
|
||||
VerifyReconstructedMediaPacket(*it, 1);
|
||||
}
|
||||
EXPECT_EQ(0, receiver_fec_->ProcessReceivedFEC(false));
|
||||
EXPECT_EQ(0, receiver_fec_->ProcessReceivedFEC());
|
||||
|
||||
DeletePackets(&media_packets);
|
||||
}
|
||||
|
||||
TEST_F(ReceiverFecTest, TwoFramesOneFec) {
|
||||
const unsigned int kNumFecPackets = 1u;
|
||||
const unsigned int kNumMediaPackets = 2u;
|
||||
std::list<RtpPacket*> media_rtp_packets;
|
||||
std::list<Packet*> media_packets;
|
||||
GenerateFrame(1, 0, &media_rtp_packets, &media_packets);
|
||||
GenerateFrame(1, 1, &media_rtp_packets, &media_packets);
|
||||
std::list<Packet*> fec_packets;
|
||||
EXPECT_EQ(0, fec_->GenerateFEC(media_packets,
|
||||
kNumFecPackets * 255 / kNumMediaPackets,
|
||||
0,
|
||||
false,
|
||||
&fec_packets));
|
||||
ASSERT_EQ(kNumFecPackets, fec_packets.size());
|
||||
GenerateFEC(&media_packets, &fec_packets, kNumFecPackets);
|
||||
|
||||
// Recovery
|
||||
receiver_fec_->SetPayloadTypeFEC(kFecPayloadType);
|
||||
BuildAndAddRedMediaPacket(media_rtp_packets.front());
|
||||
// Drop one media packet.
|
||||
BuildAndAddRedFecPacket(fec_packets.front());
|
||||
@ -287,7 +292,27 @@ TEST_F(ReceiverFecTest, TwoFramesOneFec) {
|
||||
++it;
|
||||
VerifyReconstructedMediaPacket(*it, 1);
|
||||
}
|
||||
EXPECT_EQ(0, receiver_fec_->ProcessReceivedFEC(false));
|
||||
EXPECT_EQ(0, receiver_fec_->ProcessReceivedFEC());
|
||||
|
||||
DeletePackets(&media_packets);
|
||||
}
|
||||
|
||||
TEST_F(ReceiverFecTest, OneCompleteOneUnrecoverableFrame) {
|
||||
const unsigned int kNumFecPackets = 1u;
|
||||
std::list<RtpPacket*> media_rtp_packets;
|
||||
std::list<Packet*> media_packets;
|
||||
GenerateFrame(1, 0, &media_rtp_packets, &media_packets);
|
||||
GenerateFrame(2, 1, &media_rtp_packets, &media_packets);
|
||||
std::list<Packet*> fec_packets;
|
||||
GenerateFEC(&media_packets, &fec_packets, kNumFecPackets);
|
||||
|
||||
// Recovery
|
||||
std::list<RtpPacket*>::iterator it = media_rtp_packets.begin();
|
||||
BuildAndAddRedMediaPacket(*it); // First frame
|
||||
BuildAndAddRedMediaPacket(*it); // First packet of second frame.
|
||||
EXPECT_CALL(rtp_receiver_video_, ReceiveRecoveredPacketCallback(_, _, _))
|
||||
.Times(1);
|
||||
EXPECT_EQ(0, receiver_fec_->ProcessReceivedFEC());
|
||||
|
||||
DeletePackets(&media_packets);
|
||||
}
|
||||
@ -300,15 +325,9 @@ TEST_F(ReceiverFecTest, MaxFramesOneFec) {
|
||||
for (unsigned int i = 0; i < kNumMediaPackets; ++i)
|
||||
GenerateFrame(1, i, &media_rtp_packets, &media_packets);
|
||||
std::list<Packet*> fec_packets;
|
||||
EXPECT_EQ(0, fec_->GenerateFEC(media_packets,
|
||||
kNumFecPackets * 255 / kNumMediaPackets,
|
||||
0,
|
||||
false,
|
||||
&fec_packets));
|
||||
ASSERT_EQ(kNumFecPackets, fec_packets.size());
|
||||
GenerateFEC(&media_packets, &fec_packets, kNumFecPackets);
|
||||
|
||||
// Recovery
|
||||
receiver_fec_->SetPayloadTypeFEC(kFecPayloadType);
|
||||
std::list<RtpPacket*>::iterator it = media_rtp_packets.begin();
|
||||
++it; // Drop first packet.
|
||||
for (; it != media_rtp_packets.end(); ++it)
|
||||
@ -320,7 +339,7 @@ TEST_F(ReceiverFecTest, MaxFramesOneFec) {
|
||||
for (; it != media_rtp_packets.end(); ++it)
|
||||
VerifyReconstructedMediaPacket(*it, 1);
|
||||
}
|
||||
EXPECT_EQ(0, receiver_fec_->ProcessReceivedFEC(false));
|
||||
EXPECT_EQ(0, receiver_fec_->ProcessReceivedFEC());
|
||||
|
||||
DeletePackets(&media_packets);
|
||||
}
|
||||
@ -342,4 +361,163 @@ TEST_F(ReceiverFecTest, TooManyFrames) {
|
||||
DeletePackets(&media_packets);
|
||||
}
|
||||
|
||||
TEST_F(ReceiverFecTest, PacketNotDroppedTooEarly) {
|
||||
// 1 frame with 2 media packets and one FEC packet. One media packet missing.
|
||||
// Delay the FEC packet.
|
||||
Packet* delayed_fec = NULL;
|
||||
const unsigned int kNumFecPacketsBatch1 = 1u;
|
||||
const unsigned int kNumMediaPacketsBatch1 = 2u;
|
||||
std::list<RtpPacket*> media_rtp_packets_batch1;
|
||||
std::list<Packet*> media_packets_batch1;
|
||||
GenerateFrame(kNumMediaPacketsBatch1, 0, &media_rtp_packets_batch1,
|
||||
&media_packets_batch1);
|
||||
std::list<Packet*> fec_packets;
|
||||
GenerateFEC(&media_packets_batch1, &fec_packets, kNumFecPacketsBatch1);
|
||||
|
||||
BuildAndAddRedMediaPacket(media_rtp_packets_batch1.front());
|
||||
EXPECT_CALL(rtp_receiver_video_, ReceiveRecoveredPacketCallback(_,_,_))
|
||||
.Times(1);
|
||||
EXPECT_EQ(0, receiver_fec_->ProcessReceivedFEC());
|
||||
delayed_fec = fec_packets.front();
|
||||
|
||||
// Fill the FEC decoder. No packets should be dropped.
|
||||
const unsigned int kNumMediaPacketsBatch2 = 47u;
|
||||
std::list<RtpPacket*> media_rtp_packets_batch2;
|
||||
std::list<Packet*> media_packets_batch2;
|
||||
GenerateAndAddFrames(kNumMediaPacketsBatch2, 1, &media_rtp_packets_batch2,
|
||||
&media_packets_batch2);
|
||||
EXPECT_CALL(rtp_receiver_video_, ReceiveRecoveredPacketCallback(_,_,_))
|
||||
.Times(media_packets_batch2.size());
|
||||
EXPECT_EQ(0, receiver_fec_->ProcessReceivedFEC());
|
||||
|
||||
// Add the delayed FEC packet. One packet should be reconstructed.
|
||||
BuildAndAddRedFecPacket(delayed_fec);
|
||||
EXPECT_CALL(rtp_receiver_video_, ReceiveRecoveredPacketCallback(_,_,_))
|
||||
.Times(1);
|
||||
EXPECT_EQ(0, receiver_fec_->ProcessReceivedFEC());
|
||||
|
||||
DeletePackets(&media_packets_batch1);
|
||||
DeletePackets(&media_packets_batch2);
|
||||
}
|
||||
|
||||
TEST_F(ReceiverFecTest, PacketDroppedWhenTooOld) {
|
||||
// 1 frame with 2 media packets and one FEC packet. One media packet missing.
|
||||
// Delay the FEC packet.
|
||||
Packet* delayed_fec = NULL;
|
||||
const unsigned int kNumFecPacketsBatch1 = 1u;
|
||||
const unsigned int kNumMediaPacketsBatch1 = 2u;
|
||||
std::list<RtpPacket*> media_rtp_packets_batch1;
|
||||
std::list<Packet*> media_packets_batch1;
|
||||
GenerateFrame(kNumMediaPacketsBatch1, 0, &media_rtp_packets_batch1,
|
||||
&media_packets_batch1);
|
||||
std::list<Packet*> fec_packets;
|
||||
GenerateFEC(&media_packets_batch1, &fec_packets, kNumFecPacketsBatch1);
|
||||
|
||||
BuildAndAddRedMediaPacket(media_rtp_packets_batch1.front());
|
||||
EXPECT_CALL(rtp_receiver_video_, ReceiveRecoveredPacketCallback(_,_,_))
|
||||
.Times(1);
|
||||
EXPECT_EQ(0, receiver_fec_->ProcessReceivedFEC());
|
||||
delayed_fec = fec_packets.front();
|
||||
|
||||
// Fill the FEC decoder and force the last packet to be dropped.
|
||||
const unsigned int kNumMediaPacketsBatch2 = 48u;
|
||||
std::list<RtpPacket*> media_rtp_packets_batch2;
|
||||
std::list<Packet*> media_packets_batch2;
|
||||
GenerateAndAddFrames(kNumMediaPacketsBatch2, 1, &media_rtp_packets_batch2,
|
||||
&media_packets_batch2);
|
||||
EXPECT_CALL(rtp_receiver_video_, ReceiveRecoveredPacketCallback(_,_,_))
|
||||
.Times(media_packets_batch2.size());
|
||||
EXPECT_EQ(0, receiver_fec_->ProcessReceivedFEC());
|
||||
|
||||
// Add the delayed FEC packet. No packet should be reconstructed since the
|
||||
// first media packet of that frame has been dropped due to being too old.
|
||||
BuildAndAddRedFecPacket(delayed_fec);
|
||||
EXPECT_CALL(rtp_receiver_video_, ReceiveRecoveredPacketCallback(_,_,_))
|
||||
.Times(0);
|
||||
EXPECT_EQ(0, receiver_fec_->ProcessReceivedFEC());
|
||||
|
||||
DeletePackets(&media_packets_batch1);
|
||||
DeletePackets(&media_packets_batch2);
|
||||
}
|
||||
|
||||
TEST_F(ReceiverFecTest, OldFecPacketDropped) {
|
||||
// 49 frames with 2 media packets and one FEC packet. All media packets
|
||||
// missing.
|
||||
const unsigned int kNumMediaPackets = 49 * 2;
|
||||
std::list<RtpPacket*> media_rtp_packets;
|
||||
std::list<Packet*> media_packets;
|
||||
for (unsigned int i = 0; i < kNumMediaPackets / 2; ++i) {
|
||||
std::list<RtpPacket*> frame_media_rtp_packets;
|
||||
std::list<Packet*> frame_media_packets;
|
||||
std::list<Packet*> fec_packets;
|
||||
GenerateFrame(2, 0, &frame_media_rtp_packets, &frame_media_packets);
|
||||
GenerateFEC(&frame_media_packets, &fec_packets, 1);
|
||||
for (std::list<Packet*>::iterator it = fec_packets.begin();
|
||||
it != fec_packets.end(); ++it) {
|
||||
BuildAndAddRedFecPacket(*it);
|
||||
}
|
||||
media_packets.insert(media_packets.end(),
|
||||
frame_media_packets.begin(),
|
||||
frame_media_packets.end());
|
||||
media_rtp_packets.insert(media_rtp_packets.end(),
|
||||
frame_media_rtp_packets.begin(),
|
||||
frame_media_rtp_packets.end());
|
||||
}
|
||||
// Don't insert any media packets.
|
||||
// Only FEC packets inserted. No packets should be recoverable at this time.
|
||||
EXPECT_CALL(rtp_receiver_video_, ReceiveRecoveredPacketCallback(_,_,_))
|
||||
.Times(0);
|
||||
EXPECT_EQ(0, receiver_fec_->ProcessReceivedFEC());
|
||||
|
||||
// Insert the oldest media packet. The corresponding FEC packet is too old
|
||||
// and should've been dropped. Only the media packet we inserted will be
|
||||
// returned.
|
||||
BuildAndAddRedMediaPacket(media_rtp_packets.front());
|
||||
EXPECT_CALL(rtp_receiver_video_, ReceiveRecoveredPacketCallback(_,_,_))
|
||||
.Times(1);
|
||||
EXPECT_EQ(0, receiver_fec_->ProcessReceivedFEC());
|
||||
|
||||
DeletePackets(&media_packets);
|
||||
}
|
||||
|
||||
TEST_F(ReceiverFecTest, PacketsOnlyReturnedOnce) {
|
||||
const unsigned int kNumFecPackets = 1u;
|
||||
std::list<RtpPacket*> media_rtp_packets;
|
||||
std::list<Packet*> media_packets;
|
||||
GenerateFrame(1, 0, &media_rtp_packets, &media_packets);
|
||||
GenerateFrame(2, 1, &media_rtp_packets, &media_packets);
|
||||
std::list<Packet*> fec_packets;
|
||||
GenerateFEC(&media_packets, &fec_packets, kNumFecPackets);
|
||||
|
||||
// Recovery
|
||||
std::list<RtpPacket*>::iterator media_it = media_rtp_packets.begin();
|
||||
BuildAndAddRedMediaPacket(*media_it); // First frame.
|
||||
{
|
||||
std::list<RtpPacket*>::iterator verify_it = media_rtp_packets.begin();
|
||||
VerifyReconstructedMediaPacket(*verify_it, 1); // First frame
|
||||
}
|
||||
EXPECT_EQ(0, receiver_fec_->ProcessReceivedFEC());
|
||||
|
||||
++media_it;
|
||||
BuildAndAddRedMediaPacket(*media_it); // 1st packet of 2nd frame.
|
||||
BuildAndAddRedFecPacket(fec_packets.front()); // Insert FEC packet.
|
||||
{
|
||||
InSequence s;
|
||||
std::list<RtpPacket*>::iterator verify_it = media_rtp_packets.begin();
|
||||
++verify_it; // First frame has already been returned.
|
||||
VerifyReconstructedMediaPacket(*verify_it, 1); // 1st packet of 2nd frame.
|
||||
++verify_it;
|
||||
VerifyReconstructedMediaPacket(*verify_it, 1); // 2nd packet of 2nd frame.
|
||||
}
|
||||
EXPECT_EQ(0, receiver_fec_->ProcessReceivedFEC());
|
||||
|
||||
++media_it;
|
||||
BuildAndAddRedMediaPacket(*media_it); // 2nd packet of 2nd frame.
|
||||
EXPECT_CALL(rtp_receiver_video_, ReceiveRecoveredPacketCallback(_,_,_))
|
||||
.Times(0);
|
||||
EXPECT_EQ(0, receiver_fec_->ProcessReceivedFEC());
|
||||
|
||||
DeletePackets(&media_packets);
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
|
@ -131,7 +131,6 @@ TEST_F(RtpFecTest, HandleIncorrectInputs) {
|
||||
}
|
||||
|
||||
TEST_F(RtpFecTest, FecRecoveryNoLoss) {
|
||||
bool frame_complete = true;
|
||||
const int num_important_packets = 0;
|
||||
const bool use_unequal_protection = false;
|
||||
const int num_media_packets = 4;
|
||||
@ -153,15 +152,14 @@ TEST_F(RtpFecTest, FecRecoveryNoLoss) {
|
||||
memset(fec_loss_mask_, 0, sizeof(fec_loss_mask_));
|
||||
NetworkReceivedPackets();
|
||||
|
||||
EXPECT_EQ(0, fec_->DecodeFEC(&received_packet_list_ , &recovered_packet_list_,
|
||||
fec_seq_num_, frame_complete));
|
||||
EXPECT_EQ(0, fec_->DecodeFEC(&received_packet_list_ ,
|
||||
&recovered_packet_list_));
|
||||
|
||||
// No packets lost, expect complete recovery.
|
||||
EXPECT_TRUE(IsRecoveryComplete());
|
||||
}
|
||||
|
||||
TEST_F(RtpFecTest, FecRecoveryWithLoss) {
|
||||
bool frame_complete = true;
|
||||
const int num_important_packets = 0;
|
||||
const bool use_unequal_protection = false;
|
||||
const int num_media_packets = 4;
|
||||
@ -184,8 +182,8 @@ TEST_F(RtpFecTest, FecRecoveryWithLoss) {
|
||||
media_loss_mask_[3] = 1;
|
||||
NetworkReceivedPackets();
|
||||
|
||||
EXPECT_EQ(0, fec_->DecodeFEC(&received_packet_list_ , &recovered_packet_list_,
|
||||
fec_seq_num_, frame_complete));
|
||||
EXPECT_EQ(0, fec_->DecodeFEC(&received_packet_list_ ,
|
||||
&recovered_packet_list_));
|
||||
|
||||
// One packet lost, one FEC packet, expect complete recovery.
|
||||
EXPECT_TRUE(IsRecoveryComplete());
|
||||
@ -198,15 +196,14 @@ TEST_F(RtpFecTest, FecRecoveryWithLoss) {
|
||||
media_loss_mask_[3] = 1;
|
||||
NetworkReceivedPackets();
|
||||
|
||||
EXPECT_EQ(0, fec_->DecodeFEC(&received_packet_list_ , &recovered_packet_list_,
|
||||
fec_seq_num_, frame_complete));
|
||||
EXPECT_EQ(0, fec_->DecodeFEC(&received_packet_list_ ,
|
||||
&recovered_packet_list_));
|
||||
|
||||
// 2 packets lost, one FEC packet, cannot get complete recovery.
|
||||
EXPECT_FALSE(IsRecoveryComplete());
|
||||
}
|
||||
|
||||
TEST_F(RtpFecTest, FecRecoveryWithLoss50perc) {
|
||||
bool frame_complete = true;
|
||||
const int num_important_packets = 0;
|
||||
const bool use_unequal_protection = false;
|
||||
const int num_media_packets = 4;
|
||||
@ -243,8 +240,8 @@ TEST_F(RtpFecTest, FecRecoveryWithLoss50perc) {
|
||||
|
||||
NetworkReceivedPackets();
|
||||
|
||||
EXPECT_EQ(0, fec_->DecodeFEC(&received_packet_list_ , &recovered_packet_list_,
|
||||
fec_seq_num_, frame_complete));
|
||||
EXPECT_EQ(0, fec_->DecodeFEC(&received_packet_list_ ,
|
||||
&recovered_packet_list_));
|
||||
|
||||
// With media packet#1 and FEC packets #0, #1, #3, expect complete recovery.
|
||||
EXPECT_TRUE(IsRecoveryComplete());
|
||||
@ -259,15 +256,14 @@ TEST_F(RtpFecTest, FecRecoveryWithLoss50perc) {
|
||||
media_loss_mask_[3] = 1;
|
||||
NetworkReceivedPackets();
|
||||
|
||||
EXPECT_EQ(0, fec_->DecodeFEC(&received_packet_list_ , &recovered_packet_list_,
|
||||
fec_seq_num_, frame_complete));
|
||||
EXPECT_EQ(0, fec_->DecodeFEC(&received_packet_list_ ,
|
||||
&recovered_packet_list_));
|
||||
|
||||
// Cannot get complete recovery for this loss configuration.
|
||||
EXPECT_FALSE(IsRecoveryComplete());
|
||||
}
|
||||
|
||||
TEST_F(RtpFecTest, FecRecoveryNoLossUep) {
|
||||
bool frame_complete = true;
|
||||
const int num_important_packets = 2;
|
||||
const bool use_unequal_protection = true;
|
||||
const int num_media_packets = 4;
|
||||
@ -289,15 +285,14 @@ TEST_F(RtpFecTest, FecRecoveryNoLossUep) {
|
||||
memset(fec_loss_mask_, 0, sizeof(fec_loss_mask_));
|
||||
NetworkReceivedPackets();
|
||||
|
||||
EXPECT_EQ(0, fec_->DecodeFEC(&received_packet_list_ , &recovered_packet_list_,
|
||||
fec_seq_num_, frame_complete));
|
||||
EXPECT_EQ(0, fec_->DecodeFEC(&received_packet_list_ ,
|
||||
&recovered_packet_list_));
|
||||
|
||||
// No packets lost, expect complete recovery.
|
||||
EXPECT_TRUE(IsRecoveryComplete());
|
||||
}
|
||||
|
||||
TEST_F(RtpFecTest, FecRecoveryWithLossUep) {
|
||||
bool frame_complete = true;
|
||||
const int num_important_packets = 2;
|
||||
const bool use_unequal_protection = true;
|
||||
const int num_media_packets = 4;
|
||||
@ -320,8 +315,8 @@ TEST_F(RtpFecTest, FecRecoveryWithLossUep) {
|
||||
media_loss_mask_[3] = 1;
|
||||
NetworkReceivedPackets();
|
||||
|
||||
EXPECT_EQ(0, fec_->DecodeFEC(&received_packet_list_ , &recovered_packet_list_,
|
||||
fec_seq_num_, frame_complete));
|
||||
EXPECT_EQ(0, fec_->DecodeFEC(&received_packet_list_ ,
|
||||
&recovered_packet_list_));
|
||||
|
||||
// One packet lost, one FEC packet, expect complete recovery.
|
||||
EXPECT_TRUE(IsRecoveryComplete());
|
||||
@ -334,15 +329,14 @@ TEST_F(RtpFecTest, FecRecoveryWithLossUep) {
|
||||
media_loss_mask_[3] = 1;
|
||||
NetworkReceivedPackets();
|
||||
|
||||
EXPECT_EQ(0, fec_->DecodeFEC(&received_packet_list_ , &recovered_packet_list_,
|
||||
fec_seq_num_, frame_complete));
|
||||
EXPECT_EQ(0, fec_->DecodeFEC(&received_packet_list_ ,
|
||||
&recovered_packet_list_));
|
||||
|
||||
// 2 packets lost, one FEC packet, cannot get complete recovery.
|
||||
EXPECT_FALSE(IsRecoveryComplete());
|
||||
}
|
||||
|
||||
TEST_F(RtpFecTest, FecRecoveryWithLoss50percUep) {
|
||||
bool frame_complete = true;
|
||||
const int num_important_packets = 1;
|
||||
const bool use_unequal_protection = true;
|
||||
const int num_media_packets = 4;
|
||||
@ -378,8 +372,8 @@ TEST_F(RtpFecTest, FecRecoveryWithLoss50percUep) {
|
||||
media_loss_mask_[3] = 1;
|
||||
NetworkReceivedPackets();
|
||||
|
||||
EXPECT_EQ(0, fec_->DecodeFEC(&received_packet_list_ , &recovered_packet_list_,
|
||||
fec_seq_num_, frame_complete));
|
||||
EXPECT_EQ(0, fec_->DecodeFEC(&received_packet_list_ ,
|
||||
&recovered_packet_list_));
|
||||
|
||||
// With media packet#1 and FEC packets #0, #2, #3, expect complete recovery.
|
||||
EXPECT_TRUE(IsRecoveryComplete());
|
||||
@ -394,8 +388,8 @@ TEST_F(RtpFecTest, FecRecoveryWithLoss50percUep) {
|
||||
media_loss_mask_[3] = 1;
|
||||
NetworkReceivedPackets();
|
||||
|
||||
EXPECT_EQ(0, fec_->DecodeFEC(&received_packet_list_ , &recovered_packet_list_,
|
||||
fec_seq_num_, frame_complete));
|
||||
EXPECT_EQ(0, fec_->DecodeFEC(&received_packet_list_ ,
|
||||
&recovered_packet_list_));
|
||||
|
||||
// Cannot get complete recovery for this loss configuration.
|
||||
EXPECT_FALSE(IsRecoveryComplete());
|
||||
@ -404,11 +398,11 @@ TEST_F(RtpFecTest, FecRecoveryWithLoss50percUep) {
|
||||
// TODO(marpan): Add more test cases.
|
||||
|
||||
void RtpFecTest::TearDown() {
|
||||
fec_->ResetState(&recovered_packet_list_);
|
||||
delete fec_;
|
||||
FreeRecoveredPacketList();
|
||||
ClearList(&media_packet_list_);
|
||||
EXPECT_TRUE(media_packet_list_.empty());
|
||||
|
||||
fec_packet_list_.clear();
|
||||
}
|
||||
|
||||
void RtpFecTest::FreeRecoveredPacketList() {
|
||||
@ -482,18 +476,13 @@ void RtpFecTest:: ReceivedPackets(
|
||||
// obtained from RTP header. These were set in ConstructMediaPackets().
|
||||
received_packet->seqNum =
|
||||
webrtc::ModuleRTPUtility::BufferToUWord16(&packet->data[2]);
|
||||
received_packet->lastMediaPktInFrame = (packet->data[1] & 0x80) != 0;
|
||||
}
|
||||
else {
|
||||
// The sequence number, marker bit, and ssrc number are defined in the
|
||||
// RTP header of the FEC packet, which is not constructed in this test.
|
||||
// So we set these values below based on the values generated in
|
||||
// ConstructMediaPackets().
|
||||
|
||||
received_packet->seqNum = seq_num;
|
||||
// The marker bit (last media packet of frame) for FEC packets is
|
||||
// always zero.
|
||||
received_packet->lastMediaPktInFrame = false;
|
||||
// The ssrc value for FEC packets is set to the one used for the
|
||||
// media packets in ConstructMediaPackets().
|
||||
received_packet->ssrc = ssrc_;
|
||||
|
@ -247,62 +247,14 @@ RTPReceiverVideo::ParseVideoCodecSpecific(WebRtcRTPHeader* rtpHeader,
|
||||
_criticalSectionReceiverVideo->Leave();
|
||||
return -1;
|
||||
}
|
||||
bool oldPacket = false;
|
||||
bool FECpacket = false;
|
||||
bool wrapped = false; // Not used; just for OldTimeStamp().
|
||||
|
||||
// Check for old packets.
|
||||
if (ModuleRTPUtility::OldTimestamp(rtpHeader->header.timestamp,
|
||||
TimeStamp(),
|
||||
&wrapped))
|
||||
{
|
||||
// We have an old packet.
|
||||
// FEC receiver holds a list of packets with current timestamp.
|
||||
// Setting "oldPacket = true" will send old packets directly
|
||||
// to the jitter buffer.
|
||||
oldPacket = true;
|
||||
retVal = _receiveFEC->AddReceivedFECPacket(rtpHeader,
|
||||
incomingRtpPacket,
|
||||
payloadDataLength,
|
||||
FECpacket,
|
||||
oldPacket);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Check for future packets.
|
||||
if (rtpHeader->header.timestamp != TimeStamp())
|
||||
{
|
||||
// We have a packet from next frame.
|
||||
// Force a decode with the existing packets.
|
||||
retVal = _receiveFEC->ProcessReceivedFEC(true);
|
||||
_currentFecFrameDecoded = false;
|
||||
}
|
||||
if(retVal != -1)
|
||||
{
|
||||
if (!_currentFecFrameDecoded)
|
||||
{
|
||||
retVal = _receiveFEC->AddReceivedFECPacket(
|
||||
rtpHeader,
|
||||
incomingRtpPacket,
|
||||
payloadDataLength,
|
||||
FECpacket,
|
||||
oldPacket);
|
||||
|
||||
if (retVal != -1 && (FECpacket ||
|
||||
rtpHeader->header.markerBit))
|
||||
{
|
||||
// Only attempt a decode after receiving the
|
||||
// last media packet or an FEC packet.
|
||||
retVal = _receiveFEC->ProcessReceivedFEC(false);
|
||||
}
|
||||
}else
|
||||
{
|
||||
_receiveFEC->AddReceivedFECInfo(rtpHeader,
|
||||
incomingRtpPacket,
|
||||
FECpacket);
|
||||
}
|
||||
}
|
||||
}
|
||||
retVal = _receiveFEC->AddReceivedFECPacket(
|
||||
rtpHeader,
|
||||
incomingRtpPacket,
|
||||
payloadDataLength,
|
||||
FECpacket);
|
||||
if (retVal != -1)
|
||||
retVal = _receiveFEC->ProcessReceivedFEC();
|
||||
_criticalSectionReceiverVideo->Leave();
|
||||
|
||||
if(retVal == 0 && FECpacket)
|
||||
|
@ -31,8 +31,8 @@
|
||||
using namespace webrtc;
|
||||
|
||||
void ReceivePackets(
|
||||
std::list<ForwardErrorCorrection::ReceivedPacket*>* toDecodeList,
|
||||
std::list<ForwardErrorCorrection::ReceivedPacket*>* receivedPacketList,
|
||||
ForwardErrorCorrection::ReceivedPacketList* toDecodeList,
|
||||
ForwardErrorCorrection::ReceivedPacketList* receivedPacketList,
|
||||
WebRtc_UWord32 numPacketsToDecode, float reorderRate, float duplicateRate);
|
||||
|
||||
int main() {
|
||||
@ -48,11 +48,11 @@ int main() {
|
||||
WebRtc_UWord32 id = 0;
|
||||
ForwardErrorCorrection fec(id);
|
||||
|
||||
std::list<ForwardErrorCorrection::Packet*> mediaPacketList;
|
||||
std::list<ForwardErrorCorrection::Packet*> fecPacketList;
|
||||
std::list<ForwardErrorCorrection::ReceivedPacket*> toDecodeList;
|
||||
std::list<ForwardErrorCorrection::ReceivedPacket*> receivedPacketList;
|
||||
std::list<ForwardErrorCorrection::RecoveredPacket*> recoveredPacketList;
|
||||
ForwardErrorCorrection::PacketList mediaPacketList;
|
||||
ForwardErrorCorrection::PacketList fecPacketList;
|
||||
ForwardErrorCorrection::ReceivedPacketList toDecodeList;
|
||||
ForwardErrorCorrection::ReceivedPacketList receivedPacketList;
|
||||
ForwardErrorCorrection::RecoveredPacketList recoveredPacketList;
|
||||
std::list<WebRtc_UWord8*> fecMaskList;
|
||||
|
||||
ForwardErrorCorrection::Packet* mediaPacket;
|
||||
@ -226,7 +226,7 @@ int main() {
|
||||
return -1;
|
||||
}
|
||||
memset(mediaLossMask, 0, sizeof(mediaLossMask));
|
||||
std::list<ForwardErrorCorrection::Packet*>::iterator
|
||||
ForwardErrorCorrection::PacketList::iterator
|
||||
mediaPacketListItem = mediaPacketList.begin();
|
||||
ForwardErrorCorrection::ReceivedPacket* receivedPacket;
|
||||
WebRtc_UWord32 mediaPacketIdx = 0;
|
||||
@ -241,8 +241,7 @@ int main() {
|
||||
mediaLossMask[mediaPacketIdx] = 1;
|
||||
receivedPacket =
|
||||
new ForwardErrorCorrection::ReceivedPacket;
|
||||
receivedPacket->pkt =
|
||||
new ForwardErrorCorrection::Packet;
|
||||
receivedPacket->pkt = new ForwardErrorCorrection::Packet;
|
||||
receivedPacketList.push_back(receivedPacket);
|
||||
|
||||
receivedPacket->pkt->length = mediaPacket->length;
|
||||
@ -251,14 +250,12 @@ int main() {
|
||||
receivedPacket->seqNum =
|
||||
ModuleRTPUtility::BufferToUWord16(&mediaPacket->data[2]);
|
||||
receivedPacket->isFec = false;
|
||||
receivedPacket->lastMediaPktInFrame =
|
||||
(mediaPacket->data[1] & 0x80) != 0;
|
||||
}
|
||||
mediaPacketIdx++;
|
||||
++mediaPacketListItem;
|
||||
}
|
||||
memset(fecLossMask, 0, sizeof(fecLossMask));
|
||||
std::list<ForwardErrorCorrection::Packet*>::iterator
|
||||
ForwardErrorCorrection::PacketList::iterator
|
||||
fecPacketListItem = fecPacketList.begin();
|
||||
ForwardErrorCorrection::Packet* fecPacket;
|
||||
WebRtc_UWord32 fecPacketIdx = 0;
|
||||
@ -270,8 +267,7 @@ int main() {
|
||||
fecLossMask[fecPacketIdx] = 1;
|
||||
receivedPacket =
|
||||
new ForwardErrorCorrection::ReceivedPacket;
|
||||
receivedPacket->pkt =
|
||||
new ForwardErrorCorrection::Packet;
|
||||
receivedPacket->pkt = new ForwardErrorCorrection::Packet;
|
||||
|
||||
receivedPacketList.push_back(receivedPacket);
|
||||
|
||||
@ -281,7 +277,6 @@ int main() {
|
||||
|
||||
receivedPacket->seqNum = seqNum;
|
||||
receivedPacket->isFec = true;
|
||||
receivedPacket->lastMediaPktInFrame = false;
|
||||
receivedPacket->ssrc = ssrc;
|
||||
|
||||
fecMaskList.push_back(fecPacketMasks[fecPacketIdx]);
|
||||
@ -336,7 +331,6 @@ int main() {
|
||||
}
|
||||
printf("\n\n");
|
||||
#endif
|
||||
bool complete = true; // Marks start of new frame.
|
||||
bool fecPacketReceived = false; // For error-checking frame completion.
|
||||
while (!receivedPacketList.empty()) {
|
||||
WebRtc_UWord32 numPacketsToDecode = static_cast<WebRtc_UWord32>
|
||||
@ -349,7 +343,7 @@ int main() {
|
||||
numPacketsToDecode, reorderRate, duplicateRate);
|
||||
|
||||
if (fecPacketReceived == false) {
|
||||
std::list<ForwardErrorCorrection::ReceivedPacket*>::iterator
|
||||
ForwardErrorCorrection::ReceivedPacketList::iterator
|
||||
toDecodeIt = toDecodeList.begin();
|
||||
while (toDecodeIt != toDecodeList.end()) {
|
||||
receivedPacket = *toDecodeIt;
|
||||
@ -359,8 +353,8 @@ int main() {
|
||||
++toDecodeIt;
|
||||
}
|
||||
}
|
||||
if (fec.DecodeFEC(&toDecodeList, &recoveredPacketList, seqNum,
|
||||
complete) != 0) {
|
||||
if (fec.DecodeFEC(&toDecodeList, &recoveredPacketList)
|
||||
!= 0) {
|
||||
printf("Error: DecodeFEC() failed\n");
|
||||
return -1;
|
||||
}
|
||||
@ -368,34 +362,13 @@ int main() {
|
||||
printf("Error: received packet list is not empty\n");
|
||||
return -1;
|
||||
}
|
||||
if (recoveredPacketList.size() == numMediaPackets &&
|
||||
fecPacketReceived == true) {
|
||||
if (complete == true) {
|
||||
#ifdef VERBOSE_OUTPUT
|
||||
printf("Full frame recovery correctly marked\n\n");
|
||||
#endif
|
||||
break;
|
||||
} else {
|
||||
printf("Error: "
|
||||
"it should be possible to verify full frame recovery,"
|
||||
" but complete parameter was set to false\n");
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
if (complete) {
|
||||
printf("Error: "
|
||||
"it should not be possible to verify full frame recovery,"
|
||||
" but complete parameter was set to true\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
mediaPacketListItem = mediaPacketList.begin();
|
||||
mediaPacketIdx = 0;
|
||||
while (mediaPacketListItem != mediaPacketList.end()) {
|
||||
if (mediaLossMask[mediaPacketIdx] == 1) {
|
||||
// Should have recovered this packet.
|
||||
std::list<ForwardErrorCorrection::RecoveredPacket*>::iterator
|
||||
ForwardErrorCorrection::RecoveredPacketList::iterator
|
||||
recoveredPacketListItem = recoveredPacketList.begin();
|
||||
|
||||
if (recoveredPacketListItem == recoveredPacketList.end()) {
|
||||
@ -417,13 +390,13 @@ int main() {
|
||||
"original media packet\n");
|
||||
return -1;
|
||||
}
|
||||
delete recoveredPacket->pkt;
|
||||
delete recoveredPacket;
|
||||
recoveredPacketList.pop_front();
|
||||
}
|
||||
mediaPacketIdx++;
|
||||
++mediaPacketListItem;
|
||||
}
|
||||
fec.ResetState(&recoveredPacketList);
|
||||
if (!recoveredPacketList.empty()) {
|
||||
printf("Error: excessive number of recovered packets.\n");
|
||||
printf("\t size is:%u\n",
|
||||
@ -447,11 +420,10 @@ int main() {
|
||||
|
||||
// Delete received packets we didn't pass to DecodeFEC(), due to early
|
||||
// frame completion.
|
||||
std::list<ForwardErrorCorrection::ReceivedPacket*>::iterator
|
||||
ForwardErrorCorrection::ReceivedPacketList::iterator
|
||||
receivedPacketIt = receivedPacketList.begin();
|
||||
while (receivedPacketIt != receivedPacketList.end()) {
|
||||
receivedPacket = *receivedPacketIt;
|
||||
delete receivedPacket->pkt;
|
||||
delete receivedPacket;
|
||||
++receivedPacketIt;
|
||||
receivedPacketList.pop_front();
|
||||
@ -469,8 +441,7 @@ int main() {
|
||||
} // loop over loss rates
|
||||
|
||||
// Have DecodeFEC free allocated memory.
|
||||
bool complete = true;
|
||||
fec.DecodeFEC(&receivedPacketList, &recoveredPacketList, seqNum, complete);
|
||||
fec.ResetState(&recoveredPacketList);
|
||||
if (!recoveredPacketList.empty()) {
|
||||
printf("Error: recovered packet list is not empty\n");
|
||||
return -1;
|
||||
@ -480,13 +451,13 @@ int main() {
|
||||
}
|
||||
|
||||
void ReceivePackets(
|
||||
std::list<ForwardErrorCorrection::ReceivedPacket*>* toDecodeList,
|
||||
std::list<ForwardErrorCorrection::ReceivedPacket*>* receivedPacketList,
|
||||
ForwardErrorCorrection::ReceivedPacketList* toDecodeList,
|
||||
ForwardErrorCorrection::ReceivedPacketList* receivedPacketList,
|
||||
WebRtc_UWord32 numPacketsToDecode, float reorderRate, float duplicateRate) {
|
||||
assert(toDecodeList->empty());
|
||||
assert(numPacketsToDecode <= receivedPacketList->size());
|
||||
|
||||
std::list<ForwardErrorCorrection::ReceivedPacket*>::iterator it;
|
||||
ForwardErrorCorrection::ReceivedPacketList::iterator it;
|
||||
for (WebRtc_UWord32 i = 0; i < numPacketsToDecode; i++) {
|
||||
it = receivedPacketList->begin();
|
||||
// Reorder packets.
|
||||
@ -507,9 +478,7 @@ void ReceivePackets(
|
||||
while (randomVariable < duplicateRate) {
|
||||
ForwardErrorCorrection::ReceivedPacket* duplicatePacket =
|
||||
new ForwardErrorCorrection::ReceivedPacket;
|
||||
memcpy(duplicatePacket, receivedPacket,
|
||||
sizeof(ForwardErrorCorrection::ReceivedPacket));
|
||||
|
||||
*duplicatePacket = *receivedPacket;
|
||||
duplicatePacket->pkt = new ForwardErrorCorrection::Packet;
|
||||
memcpy(duplicatePacket->pkt->data, receivedPacket->pkt->data,
|
||||
receivedPacket->pkt->length);
|
||||
|
Loading…
x
Reference in New Issue
Block a user