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
|
* 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
|
* 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.
|
* be found in the AUTHORS file in the root of the source tree.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
#include <iterator>
|
||||||
|
|
||||||
#include "forward_error_correction.h"
|
#include "modules/rtp_rtcp/source/forward_error_correction.h"
|
||||||
#include "forward_error_correction_internal.h"
|
#include "modules/rtp_rtcp/source/forward_error_correction_internal.h"
|
||||||
#include "rtp_utility.h"
|
#include "modules/rtp_rtcp/source/rtp_utility.h"
|
||||||
#include "trace.h"
|
#include "system_wrappers/interface/trace.h"
|
||||||
|
|
||||||
namespace webrtc {
|
namespace webrtc {
|
||||||
|
|
||||||
@ -33,42 +35,39 @@ const uint8_t kUlpHeaderSizeLBitClear = (2 + kMaskSizeLBitClear);
|
|||||||
// Transport header size in bytes. Assume UDP/IPv4 as a reasonable minimum.
|
// Transport header size in bytes. Assume UDP/IPv4 as a reasonable minimum.
|
||||||
const uint8_t kTransportOverhead = 28;
|
const uint8_t kTransportOverhead = 28;
|
||||||
|
|
||||||
|
enum { kMaxFecPackets = ForwardErrorCorrection::kMaxMediaPackets };
|
||||||
|
|
||||||
// Used to link media packets to their protecting FEC packets.
|
// Used to link media packets to their protecting FEC packets.
|
||||||
//
|
//
|
||||||
struct ProtectedPacket {
|
// TODO(holmer): Refactor into a proper class.
|
||||||
uint16_t seqNum;
|
class ProtectedPacket : public ForwardErrorCorrection::SortablePacket {
|
||||||
ForwardErrorCorrection::Packet* pkt;
|
public:
|
||||||
|
scoped_refptr<ForwardErrorCorrection::Packet> pkt;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
typedef std::list<ProtectedPacket*> ProtectedPacketList;
|
||||||
|
|
||||||
//
|
//
|
||||||
// Used for internal storage of FEC packets in a list.
|
// Used for internal storage of FEC packets in a list.
|
||||||
//
|
//
|
||||||
struct FecPacket {
|
// TODO(holmer): Refactor into a proper class.
|
||||||
std::list<ProtectedPacket*> protectedPktList;
|
class FecPacket : public ForwardErrorCorrection::SortablePacket {
|
||||||
uint16_t seqNum;
|
public:
|
||||||
|
ProtectedPacketList protectedPktList;
|
||||||
uint32_t ssrc; // SSRC of the current frame.
|
uint32_t ssrc; // SSRC of the current frame.
|
||||||
ForwardErrorCorrection::Packet* pkt;
|
scoped_refptr<ForwardErrorCorrection::Packet> pkt;
|
||||||
};
|
};
|
||||||
|
|
||||||
bool ForwardErrorCorrection::CompareRecoveredPackets(RecoveredPacket* first,
|
bool ForwardErrorCorrection::SortablePacket::LessThan(
|
||||||
RecoveredPacket* second) {
|
const SortablePacket* first,
|
||||||
if ((first->seqNum > second->seqNum &&
|
const SortablePacket* second) {
|
||||||
(first->seqNum - kMaxMediaPackets) < second->seqNum) ||
|
return (first->seqNum != second->seqNum &&
|
||||||
// We have a wrap in sequence number if the first sequence number is low,
|
LatestSequenceNumber(first->seqNum, second->seqNum) == second->seqNum);
|
||||||
// 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ForwardErrorCorrection::ForwardErrorCorrection(int32_t id)
|
ForwardErrorCorrection::ForwardErrorCorrection(int32_t id)
|
||||||
: _id(id),
|
: _id(id),
|
||||||
_generatedFecPackets(kMaxMediaPackets),
|
_generatedFecPackets(kMaxMediaPackets),
|
||||||
_seqNumBase(0),
|
|
||||||
_lastMediaPacketReceived(false),
|
|
||||||
_fecPacketReceived(false) {
|
_fecPacketReceived(false) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -93,11 +92,11 @@ ForwardErrorCorrection::~ForwardErrorCorrection() {
|
|||||||
// | |
|
// | |
|
||||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
int32_t ForwardErrorCorrection::GenerateFEC(
|
int32_t ForwardErrorCorrection::GenerateFEC(
|
||||||
const std::list<Packet*>& mediaPacketList,
|
const PacketList& mediaPacketList,
|
||||||
uint8_t protectionFactor,
|
uint8_t protectionFactor,
|
||||||
int numImportantPackets,
|
int numImportantPackets,
|
||||||
bool useUnequalProtection,
|
bool useUnequalProtection,
|
||||||
std::list<Packet*>* fecPacketList) {
|
PacketList* fecPacketList) {
|
||||||
if (mediaPacketList.empty()) {
|
if (mediaPacketList.empty()) {
|
||||||
WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id,
|
WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id,
|
||||||
"%s media packet list is empty", __FUNCTION__);
|
"%s media packet list is empty", __FUNCTION__);
|
||||||
@ -135,7 +134,7 @@ int32_t ForwardErrorCorrection::GenerateFEC(
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
// Do some error checking on the media packets.
|
// 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()) {
|
while (mediaListIt != mediaPacketList.end()) {
|
||||||
Packet* mediaPacket = *mediaListIt;
|
Packet* mediaPacket = *mediaListIt;
|
||||||
assert(mediaPacket);
|
assert(mediaPacket);
|
||||||
@ -192,7 +191,7 @@ int32_t ForwardErrorCorrection::GenerateFEC(
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ForwardErrorCorrection::GenerateFecBitStrings(
|
void ForwardErrorCorrection::GenerateFecBitStrings(
|
||||||
const std::list<Packet*>& mediaPacketList,
|
const PacketList& mediaPacketList,
|
||||||
uint8_t* packetMask,
|
uint8_t* packetMask,
|
||||||
uint32_t numFecPackets) {
|
uint32_t numFecPackets) {
|
||||||
uint8_t mediaPayloadLength[2];
|
uint8_t mediaPayloadLength[2];
|
||||||
@ -204,7 +203,7 @@ void ForwardErrorCorrection::GenerateFecBitStrings(
|
|||||||
const uint16_t fecRtpOffset = kFecHeaderSize + ulpHeaderSize - kRtpHeaderSize;
|
const uint16_t fecRtpOffset = kFecHeaderSize + ulpHeaderSize - kRtpHeaderSize;
|
||||||
|
|
||||||
for (uint32_t i = 0; i < numFecPackets; i++) {
|
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 pktMaskIdx = i * numMaskBytes;
|
||||||
uint32_t mediaPktIdx = 0;
|
uint32_t mediaPktIdx = 0;
|
||||||
uint16_t fecPacketLength = 0;
|
uint16_t fecPacketLength = 0;
|
||||||
@ -271,7 +270,7 @@ void ForwardErrorCorrection::GenerateFecBitStrings(
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ForwardErrorCorrection::GenerateFecUlpHeaders(
|
void ForwardErrorCorrection::GenerateFecUlpHeaders(
|
||||||
const std::list<Packet*>& mediaPacketList,
|
const PacketList& mediaPacketList,
|
||||||
uint8_t* packetMask,
|
uint8_t* packetMask,
|
||||||
uint32_t numFecPackets) {
|
uint32_t numFecPackets) {
|
||||||
// -- Generate FEC and ULP headers --
|
// -- Generate FEC and ULP headers --
|
||||||
@ -295,7 +294,7 @@ void ForwardErrorCorrection::GenerateFecUlpHeaders(
|
|||||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
// | mask cont. (present only when L = 1) |
|
// | mask cont. (present only when L = 1) |
|
||||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
std::list<Packet*>::const_iterator mediaListIt = mediaPacketList.begin();
|
PacketList::const_iterator mediaListIt = mediaPacketList.begin();
|
||||||
Packet* mediaPacket = *mediaListIt;
|
Packet* mediaPacket = *mediaListIt;
|
||||||
assert(mediaPacket != NULL);
|
assert(mediaPacket != NULL);
|
||||||
const uint8_t lBit = mediaPacketList.size() > 16 ? 1 : 0;
|
const uint8_t lBit = mediaPacketList.size() > 16 ? 1 : 0;
|
||||||
@ -330,35 +329,28 @@ void ForwardErrorCorrection::GenerateFecUlpHeaders(
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ForwardErrorCorrection::ResetState(
|
void ForwardErrorCorrection::ResetState(
|
||||||
std::list<RecoveredPacket*>* recoveredPacketList) {
|
RecoveredPacketList* recoveredPacketList) {
|
||||||
_seqNumBase = 0;
|
|
||||||
_lastMediaPacketReceived = false;
|
|
||||||
_fecPacketReceived = false;
|
_fecPacketReceived = false;
|
||||||
|
|
||||||
// Free the memory for any existing recovered packets, if the user hasn't.
|
// Free the memory for any existing recovered packets, if the user hasn't.
|
||||||
while (!recoveredPacketList->empty()) {
|
while (!recoveredPacketList->empty()) {
|
||||||
std::list<RecoveredPacket*>::iterator recoveredPacketListIt =
|
delete recoveredPacketList->front();
|
||||||
recoveredPacketList->begin();
|
|
||||||
RecoveredPacket* recPacket = *recoveredPacketListIt;
|
|
||||||
delete recPacket->pkt;
|
|
||||||
delete recPacket;
|
|
||||||
recoveredPacketList->pop_front();
|
recoveredPacketList->pop_front();
|
||||||
}
|
}
|
||||||
assert(recoveredPacketList->empty());
|
assert(recoveredPacketList->empty());
|
||||||
|
|
||||||
// Free the FEC packet list.
|
// Free the FEC packet list.
|
||||||
while (!_fecPacketList.empty()) {
|
while (!_fecPacketList.empty()) {
|
||||||
std::list<FecPacket*>::iterator fecPacketListIt = _fecPacketList.begin();
|
FecPacketList::iterator fecPacketListIt = _fecPacketList.begin();
|
||||||
FecPacket* fecPacket = *fecPacketListIt;
|
FecPacket* fecPacket = *fecPacketListIt;
|
||||||
std::list<ProtectedPacket*>::iterator protectedPacketListIt;
|
ProtectedPacketList::iterator protectedPacketListIt;
|
||||||
protectedPacketListIt = fecPacket->protectedPktList.begin();
|
protectedPacketListIt = fecPacket->protectedPktList.begin();
|
||||||
while (protectedPacketListIt != fecPacket->protectedPktList.end()) {
|
while (protectedPacketListIt != fecPacket->protectedPktList.end()) {
|
||||||
delete *protectedPacketListIt;
|
delete *protectedPacketListIt;
|
||||||
protectedPacketListIt++;
|
protectedPacketListIt =
|
||||||
fecPacket->protectedPktList.pop_front();
|
fecPacket->protectedPktList.erase(protectedPacketListIt);
|
||||||
}
|
}
|
||||||
assert(fecPacket->protectedPktList.empty());
|
assert(fecPacket->protectedPktList.empty());
|
||||||
delete fecPacket->pkt;
|
|
||||||
delete fecPacket;
|
delete fecPacket;
|
||||||
_fecPacketList.pop_front();
|
_fecPacketList.pop_front();
|
||||||
}
|
}
|
||||||
@ -367,74 +359,75 @@ void ForwardErrorCorrection::ResetState(
|
|||||||
|
|
||||||
void ForwardErrorCorrection::InsertMediaPacket(
|
void ForwardErrorCorrection::InsertMediaPacket(
|
||||||
ReceivedPacket* rxPacket,
|
ReceivedPacket* rxPacket,
|
||||||
std::list<RecoveredPacket*>* recoveredPacketList) {
|
RecoveredPacketList* recoveredPacketList) {
|
||||||
if (rxPacket->lastMediaPktInFrame) {
|
RecoveredPacketList::iterator recoveredPacketListIt =
|
||||||
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->begin();
|
recoveredPacketList->begin();
|
||||||
|
|
||||||
|
// Search for duplicate packets.
|
||||||
while (recoveredPacketListIt != recoveredPacketList->end()) {
|
while (recoveredPacketListIt != recoveredPacketList->end()) {
|
||||||
RecoveredPacket* recPacket = *recoveredPacketListIt;
|
if (rxPacket->seqNum == (*recoveredPacketListIt)->seqNum) {
|
||||||
if (rxPacket->seqNum == recPacket->seqNum) {
|
|
||||||
// Duplicate packet, no need to add to list.
|
// Duplicate packet, no need to add to list.
|
||||||
duplicatePacket = true;
|
// Delete duplicate media packet data.
|
||||||
break;
|
rxPacket->pkt = NULL;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
recoveredPacketListIt++;
|
recoveredPacketListIt++;
|
||||||
}
|
}
|
||||||
if (duplicatePacket) {
|
|
||||||
// Delete duplicate media packet data.
|
|
||||||
delete rxPacket->pkt;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
RecoveredPacket* recoverdPacketToInsert = new RecoveredPacket;
|
RecoveredPacket* recoverdPacketToInsert = new RecoveredPacket;
|
||||||
recoverdPacketToInsert->wasRecovered = false;
|
recoverdPacketToInsert->wasRecovered = false;
|
||||||
|
recoverdPacketToInsert->returned = false;
|
||||||
recoverdPacketToInsert->seqNum = rxPacket->seqNum;
|
recoverdPacketToInsert->seqNum = rxPacket->seqNum;
|
||||||
recoverdPacketToInsert->pkt = rxPacket->pkt;
|
recoverdPacketToInsert->pkt = rxPacket->pkt;
|
||||||
recoverdPacketToInsert->pkt->length = rxPacket->pkt->length;
|
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->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;
|
_fecPacketReceived = true;
|
||||||
|
|
||||||
// Check for duplicate.
|
// Check for duplicate.
|
||||||
bool duplicatePacket = false;
|
FecPacketList::iterator fecPacketListIt = _fecPacketList.begin();
|
||||||
std::list<FecPacket*>::iterator fecPacketListIt = _fecPacketList.begin();
|
|
||||||
while (fecPacketListIt != _fecPacketList.end()) {
|
while (fecPacketListIt != _fecPacketList.end()) {
|
||||||
FecPacket* fecPacket = *fecPacketListIt;
|
if (rxPacket->seqNum == (*fecPacketListIt)->seqNum) {
|
||||||
if (rxPacket->seqNum == fecPacket->seqNum) {
|
// Delete duplicate FEC packet data.
|
||||||
duplicatePacket = true;
|
rxPacket->pkt = NULL;
|
||||||
break;
|
return;
|
||||||
}
|
}
|
||||||
fecPacketListIt++;
|
fecPacketListIt++;
|
||||||
}
|
}
|
||||||
if (duplicatePacket) {
|
|
||||||
// Delete duplicate FEC packet data.
|
|
||||||
delete rxPacket->pkt;
|
|
||||||
rxPacket->pkt = NULL;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
FecPacket* fecPacket = new FecPacket;
|
FecPacket* fecPacket = new FecPacket;
|
||||||
fecPacket->pkt = rxPacket->pkt;
|
fecPacket->pkt = rxPacket->pkt;
|
||||||
fecPacket->seqNum = rxPacket->seqNum;
|
fecPacket->seqNum = rxPacket->seqNum;
|
||||||
fecPacket->ssrc = rxPacket->ssrc;
|
fecPacket->ssrc = rxPacket->ssrc;
|
||||||
|
|
||||||
// We store this for determining frame completion later.
|
const uint16_t seqNumBase = ModuleRTPUtility::BufferToUWord16(
|
||||||
_seqNumBase = ModuleRTPUtility::BufferToUWord16(
|
|
||||||
&fecPacket->pkt->data[2]);
|
&fecPacket->pkt->data[2]);
|
||||||
|
|
||||||
const uint16_t maskSizeBytes = (fecPacket->pkt->data[0] & 0x40) ?
|
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++) {
|
for (uint16_t byteIdx = 0; byteIdx < maskSizeBytes; byteIdx++) {
|
||||||
uint8_t packetMask = fecPacket->pkt->data[12 + byteIdx];
|
uint8_t packetMask = fecPacket->pkt->data[12 + byteIdx];
|
||||||
@ -443,7 +436,7 @@ void ForwardErrorCorrection::InsertFECPacket(ReceivedPacket* rxPacket) {
|
|||||||
ProtectedPacket* protectedPacket = new ProtectedPacket;
|
ProtectedPacket* protectedPacket = new ProtectedPacket;
|
||||||
fecPacket->protectedPktList.push_back(protectedPacket);
|
fecPacket->protectedPktList.push_back(protectedPacket);
|
||||||
// This wraps naturally with the sequence number.
|
// This wraps naturally with the sequence number.
|
||||||
protectedPacket->seqNum = static_cast<uint16_t>(_seqNumBase +
|
protectedPacket->seqNum = static_cast<uint16_t>(seqNumBase +
|
||||||
(byteIdx << 3) + bitIdx);
|
(byteIdx << 3) + bitIdx);
|
||||||
protectedPacket->pkt = NULL;
|
protectedPacket->pkt = NULL;
|
||||||
}
|
}
|
||||||
@ -451,24 +444,60 @@ void ForwardErrorCorrection::InsertFECPacket(ReceivedPacket* rxPacket) {
|
|||||||
}
|
}
|
||||||
if (fecPacket->protectedPktList.empty()) {
|
if (fecPacket->protectedPktList.empty()) {
|
||||||
// All-zero packet mask; we can discard this FEC packet.
|
// 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;
|
delete fecPacket;
|
||||||
} else {
|
} 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.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(
|
void ForwardErrorCorrection::InsertPackets(
|
||||||
std::list<ReceivedPacket*>* receivedPacketList,
|
ReceivedPacketList* receivedPacketList,
|
||||||
std::list<RecoveredPacket*>* recoveredPacketList) {
|
RecoveredPacketList* recoveredPacketList) {
|
||||||
|
|
||||||
while (!receivedPacketList->empty()) {
|
while (!receivedPacketList->empty()) {
|
||||||
ReceivedPacket* rxPacket = receivedPacketList->front();
|
ReceivedPacket* rxPacket = receivedPacketList->front();
|
||||||
|
|
||||||
if (rxPacket->isFec) {
|
if (rxPacket->isFec) {
|
||||||
InsertFECPacket(rxPacket);
|
InsertFECPacket(rxPacket, recoveredPacketList);
|
||||||
} else {
|
} else {
|
||||||
// Insert packet in end of list.
|
// Insert packet at the end of |recoveredPacketList|.
|
||||||
InsertMediaPacket(rxPacket, recoveredPacketList);
|
InsertMediaPacket(rxPacket, recoveredPacketList);
|
||||||
}
|
}
|
||||||
// Delete the received packet "wrapper", but not the packet data.
|
// Delete the received packet "wrapper", but not the packet data.
|
||||||
@ -476,247 +505,195 @@ void ForwardErrorCorrection::InsertPackets(
|
|||||||
receivedPacketList->pop_front();
|
receivedPacketList->pop_front();
|
||||||
}
|
}
|
||||||
assert(receivedPacketList->empty());
|
assert(receivedPacketList->empty());
|
||||||
// Sort our recovered packet list.
|
DiscardOldPackets(recoveredPacketList);
|
||||||
recoveredPacketList->sort(CompareRecoveredPackets);
|
}
|
||||||
|
|
||||||
|
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(
|
void ForwardErrorCorrection::RecoverPacket(
|
||||||
const FecPacket& fecPacket,
|
const FecPacket* fecPacket,
|
||||||
RecoveredPacket* recPacketToInsert) {
|
RecoveredPacket* recPacketToInsert) {
|
||||||
uint8_t lengthRecovery[2];
|
InitRecovery(fecPacket, recPacketToInsert);
|
||||||
const uint16_t ulpHeaderSize = fecPacket.pkt->data[0] & 0x40 ?
|
ProtectedPacketList::const_iterator protected_it =
|
||||||
kUlpHeaderSizeLBitSet : kUlpHeaderSizeLBitClear; // L bit set?
|
fecPacket->protectedPktList.begin();
|
||||||
|
while (protected_it != fecPacket->protectedPktList.end()) {
|
||||||
recPacketToInsert->wasRecovered = true;
|
if ((*protected_it)->pkt == NULL) {
|
||||||
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) {
|
|
||||||
// This is the packet we're recovering.
|
// This is the packet we're recovering.
|
||||||
recPacketToInsert->seqNum = protectedPacket->seqNum;
|
recPacketToInsert->seqNum = (*protected_it)->seqNum;
|
||||||
} else {
|
} else {
|
||||||
// XOR with the first 2 bytes of the RTP header.
|
XorPackets((*protected_it)->pkt, recPacketToInsert);
|
||||||
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];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
protectedPacketListIt++;
|
++protected_it;
|
||||||
}
|
}
|
||||||
// Set the RTP version to 2.
|
FinishRecovery(recPacketToInsert);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ForwardErrorCorrection::AttemptRecover(
|
void ForwardErrorCorrection::AttemptRecover(
|
||||||
std::list<RecoveredPacket*>* recoveredPacketList) {
|
RecoveredPacketList* recoveredPacketList) {
|
||||||
std::list<FecPacket*>::iterator fecPacketListIt = _fecPacketList.begin();
|
FecPacketList::iterator fecPacketListIt = _fecPacketList.begin();
|
||||||
while (fecPacketListIt != _fecPacketList.end()) {
|
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.
|
// Search for each FEC packet's protected media packets.
|
||||||
FecPacket* fecPacket = *fecPacketListIt;
|
int packets_missing = NumCoveredPacketsMissing(*fecPacketListIt);
|
||||||
uint32_t protectedPacketsFound =
|
|
||||||
NumberOfProtectedPackets(*fecPacket, recoveredPacketList);
|
|
||||||
|
|
||||||
if (protectedPacketsFound == fecPacket->protectedPktList.size() - 1) {
|
// We can only recover one packet with an FEC packet.
|
||||||
|
if (packets_missing == 1) {
|
||||||
// Recovery possible.
|
// Recovery possible.
|
||||||
RecoveredPacket* packetToInsert = new RecoveredPacket;
|
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);
|
recoveredPacketList->push_back(packetToInsert);
|
||||||
|
recoveredPacketList->sort(SortablePacket::LessThan);
|
||||||
// Sort our recovered packet list.
|
UpdateCoveringFECPackets(packetToInsert);
|
||||||
recoveredPacketList->sort(CompareRecoveredPackets);
|
DiscardOldPackets(recoveredPacketList);
|
||||||
|
DiscardFECPacket(*fecPacketListIt);
|
||||||
protectedPacketsFound++;
|
fecPacketListIt = _fecPacketList.erase(fecPacketListIt);
|
||||||
assert(protectedPacketsFound == fecPacket->protectedPktList.size());
|
|
||||||
|
|
||||||
// A packet has been recovered. We need to check the FEC list again, as
|
// A packet has been recovered. We need to check the FEC list again, as
|
||||||
// this may allow additional packets to be recovered.
|
// this may allow additional packets to be recovered.
|
||||||
// Restart for first FEC packet.
|
// Restart for first FEC packet.
|
||||||
fecPacketListIt = _fecPacketList.begin();
|
fecPacketListIt = _fecPacketList.begin();
|
||||||
if (_fecPacketList.begin() == fecPacketListItToDiscard) {
|
} else if (packets_missing == 0) {
|
||||||
// If we're deleting the first item, we need to get the next first.
|
// Either all protected packets arrived or have been recovered. We can
|
||||||
fecPacketListIt++;
|
// discard this FEC packet.
|
||||||
}
|
DiscardFECPacket(*fecPacketListIt);
|
||||||
|
fecPacketListIt = _fecPacketList.erase(fecPacketListIt);
|
||||||
} else {
|
} else {
|
||||||
fecPacketListIt++;
|
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(
|
int ForwardErrorCorrection::NumCoveredPacketsMissing(
|
||||||
std::list<ReceivedPacket*>* receivedPacketList,
|
const FecPacket* fec_packet) {
|
||||||
std::list<RecoveredPacket*>* recoveredPacketList,
|
int packets_missing = 0;
|
||||||
uint16_t lastFECSeqNum,
|
ProtectedPacketList::const_iterator it = fec_packet->protectedPktList.begin();
|
||||||
bool& frameComplete) {
|
for (; it != fec_packet->protectedPktList.end(); ++it) {
|
||||||
// TODO: can we check for multiple ULP headers, and return an error?
|
if ((*it)->pkt == NULL) {
|
||||||
|
++packets_missing;
|
||||||
// Allow an empty received packet list when complete is true as a teardown
|
if (packets_missing > 1) {
|
||||||
// indicator.
|
break; // We can't recover more than one packet.
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t ForwardErrorCorrection::PacketOverhead() {
|
uint16_t ForwardErrorCorrection::PacketOverhead() {
|
||||||
return kFecHeaderSize + kUlpHeaderSizeLBitSet;
|
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
|
} // namespace webrtc
|
||||||
|
@ -14,13 +14,15 @@
|
|||||||
#include <list>
|
#include <list>
|
||||||
#include <vector>
|
#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 "typedefs.h"
|
||||||
#include "rtp_rtcp_defines.h"
|
|
||||||
|
|
||||||
namespace webrtc {
|
namespace webrtc {
|
||||||
|
|
||||||
// Forward declaration.
|
// Forward declaration.
|
||||||
struct FecPacket;
|
class FecPacket;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Performs codec-independent forward error correction (FEC), based on RFC 5109.
|
* 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
|
// Maximum number of media packets we can protect
|
||||||
static const int kMaxMediaPackets = 48;
|
static const int kMaxMediaPackets = 48;
|
||||||
|
|
||||||
struct Packet {
|
// TODO(holmer): As a next step all these struct-like packet classes should be
|
||||||
uint16_t length; /**> Length of packet in bytes. */
|
// refactored into proper classes, and their members should be made private.
|
||||||
uint8_t data[IP_PACKET_SIZE]; /**> Packet data. */
|
// 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
|
* media packets, but in the case of an FEC packet protecting a single
|
||||||
* missing media packet, we have no other means of obtaining it.
|
* missing media packet, we have no other means of obtaining it.
|
||||||
*/
|
*/
|
||||||
struct ReceivedPacket {
|
// TODO(holmer): Refactor into a proper class.
|
||||||
uint16_t seqNum; /**> Sequence number of packet. */
|
class ReceivedPacket : public SortablePacket {
|
||||||
uint32_t ssrc; /**> SSRC of the current frame. Must be set for FEC
|
public:
|
||||||
packets, but not required for media packets. */
|
uint32_t ssrc; // SSRC of the current frame. Must be set for FEC
|
||||||
bool isFec; /**> Set to true if this is an FEC packet and false
|
// packets, but not required for media packets.
|
||||||
otherwise. */
|
bool isFec; // Set to true if this is an FEC packet and false
|
||||||
bool lastMediaPktInFrame; /**> Set to true to mark the last media packet in
|
// otherwise.
|
||||||
the frame and false otherwise. */
|
scoped_refptr<Packet> pkt; // Pointer to the packet storage.
|
||||||
Packet* pkt; /**> Pointer to the packet storage. */
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The recovered list parameter of #DecodeFEC() will reference structs of
|
* The recovered list parameter of #DecodeFEC() will reference structs of
|
||||||
* this type.
|
* this type.
|
||||||
*/
|
*/
|
||||||
struct RecoveredPacket {
|
// TODO(holmer): Refactor into a proper class.
|
||||||
bool wasRecovered; /**> Will be true if this packet was recovered by
|
class RecoveredPacket : public SortablePacket {
|
||||||
the FEC. Otherwise it was a media packet passed in
|
public:
|
||||||
through the received packet list. */
|
bool wasRecovered; // Will be true if this packet was recovered by
|
||||||
uint16_t seqNum; /**> Sequence number of the packet. This is mostly for
|
// the FEC. Otherwise it was a media packet passed in
|
||||||
implementation convenience but could be utilized
|
// through the received packet list.
|
||||||
by the user if so desired. */
|
bool returned; // True when the packet already has been returned to the
|
||||||
Packet* pkt; /**> Pointer to the packet storage. */
|
// 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
|
* \param[in] id Module ID
|
||||||
*/
|
*/
|
||||||
@ -118,17 +162,17 @@ class ForwardErrorCorrection {
|
|||||||
*
|
*
|
||||||
* \return 0 on success, -1 on failure.
|
* \return 0 on success, -1 on failure.
|
||||||
*/
|
*/
|
||||||
int32_t GenerateFEC(const std::list<Packet*>& mediaPacketList,
|
int32_t GenerateFEC(const PacketList& mediaPacketList,
|
||||||
uint8_t protectionFactor,
|
uint8_t protectionFactor,
|
||||||
int numImportantPackets,
|
int numImportantPackets,
|
||||||
bool useUnequalProtection,
|
bool useUnequalProtection,
|
||||||
std::list<Packet*>* fecPacketList);
|
PacketList* fecPacketList);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Decodes a list of media and FEC packets. It will parse the input received
|
* 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
|
* packet list, storing FEC packets internally and inserting media packets to
|
||||||
* the output recovered packet list. The recovered list will be sorted by
|
* 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
|
* should be called as new packets arrive, with the recovered list being
|
||||||
* progressively assembled with each call. The received packet list will be
|
* progressively assembled with each call. The received packet list will be
|
||||||
* empty at output.\n
|
* empty at output.\n
|
||||||
@ -139,12 +183,8 @@ class ForwardErrorCorrection {
|
|||||||
* packets, in which case they must remove deleted packets from the
|
* packets, in which case they must remove deleted packets from the
|
||||||
* recovered list.\n
|
* 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
|
* \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,
|
* frame. At output the list will be empty,
|
||||||
* with packets either stored internally,
|
* with packets either stored internally,
|
||||||
* or accessible through the recovered list.
|
* or accessible through the recovered list.
|
||||||
@ -152,24 +192,12 @@ class ForwardErrorCorrection {
|
|||||||
* #RecoveredPacket, belonging to a single
|
* #RecoveredPacket, belonging to a single
|
||||||
* frame. The memory available through the
|
* frame. The memory available through the
|
||||||
* list will be valid until the next call to
|
* list will be valid until the next call to
|
||||||
* DecodeFEC() in which the completion
|
* DecodeFEC().
|
||||||
* 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.
|
|
||||||
*
|
*
|
||||||
* \return 0 on success, -1 on failure.
|
* \return 0 on success, -1 on failure.
|
||||||
*/
|
*/
|
||||||
int32_t DecodeFEC(std::list<ReceivedPacket*>* receivedPacketList,
|
int32_t DecodeFEC(ReceivedPacketList* receivedPacketList,
|
||||||
std::list<RecoveredPacket*>* recoveredPacketList,
|
RecoveredPacketList* recoveredPacketList);
|
||||||
uint16_t lastFECSeqNum,
|
|
||||||
bool& frameComplete);
|
|
||||||
/**
|
/**
|
||||||
* Gets the size in bytes of the FEC/ULP headers, which must be accounted for
|
* Gets the size in bytes of the FEC/ULP headers, which must be accounted for
|
||||||
* as packet overhead.
|
* as packet overhead.
|
||||||
@ -177,55 +205,84 @@ class ForwardErrorCorrection {
|
|||||||
*/
|
*/
|
||||||
static uint16_t PacketOverhead();
|
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.
|
// 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.
|
// Insert received packets into FEC or recovered list.
|
||||||
void InsertPackets(std::list<ReceivedPacket*>* receivedPacketList,
|
void InsertPackets(ReceivedPacketList* receivedPacketList,
|
||||||
std::list<RecoveredPacket*>* recoveredPacketList);
|
RecoveredPacketList* recoveredPacketList);
|
||||||
|
|
||||||
// Insert media packet into recovered packet list. We delete duplicates.
|
// Insert media packet into recovered packet list. We delete duplicates.
|
||||||
void InsertMediaPacket(ReceivedPacket* rxPacket,
|
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.
|
// 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.
|
// Insert into recovered list in correct position.
|
||||||
void InsertRecoveredPacket(
|
void InsertRecoveredPacket(
|
||||||
RecoveredPacket* recPacketToInsert,
|
RecoveredPacket* recPacketToInsert,
|
||||||
std::list<RecoveredPacket*>* recoveredPacketList);
|
RecoveredPacketList* recoveredPacketList);
|
||||||
|
|
||||||
// Attempt to recover missing packets.
|
// 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.
|
// Recover a missing packet.
|
||||||
void RecoverPacket(const FecPacket& fecPacket,
|
void RecoverPacket(const FecPacket* fecPacket,
|
||||||
RecoveredPacket* recPacketToInsert);
|
RecoveredPacket* recPacketToInsert);
|
||||||
|
|
||||||
// Get number of protected packet in the fecPacket.
|
// Get the number of missing media packets which are covered by this
|
||||||
uint32_t NumberOfProtectedPackets(
|
// FEC packet. An FEC packet can recover at most one packet, and if zero
|
||||||
const FecPacket& fecPacket,
|
// packets are missing the FEC packet can be discarded.
|
||||||
std::list<RecoveredPacket*>* recoveredPacketList);
|
// 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;
|
int32_t _id;
|
||||||
std::vector<Packet> _generatedFecPackets;
|
std::vector<Packet> _generatedFecPackets;
|
||||||
std::list<FecPacket*> _fecPacketList;
|
FecPacketList _fecPacketList;
|
||||||
uint16_t _seqNumBase;
|
|
||||||
bool _lastMediaPacketReceived;
|
|
||||||
bool _fecPacketReceived;
|
bool _fecPacketReceived;
|
||||||
};
|
};
|
||||||
} // namespace webrtc
|
} // namespace webrtc
|
||||||
|
@ -8,13 +8,14 @@
|
|||||||
* be found in the AUTHORS file in the root of the source tree.
|
* 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 <cassert>
|
||||||
|
|
||||||
#include "rtp_receiver_video.h"
|
#include "modules/rtp_rtcp/source/rtp_receiver_video.h"
|
||||||
#include "rtp_utility.h"
|
#include "modules/rtp_rtcp/source/rtp_utility.h"
|
||||||
#include "trace.h"
|
#include "system_wrappers/interface/scoped_ptr.h"
|
||||||
|
#include "system_wrappers/interface/trace.h"
|
||||||
|
|
||||||
// RFC 5109
|
// RFC 5109
|
||||||
namespace webrtc {
|
namespace webrtc {
|
||||||
@ -22,9 +23,7 @@ ReceiverFEC::ReceiverFEC(const WebRtc_Word32 id, RTPReceiverVideo* owner)
|
|||||||
: _id(id),
|
: _id(id),
|
||||||
_owner(owner),
|
_owner(owner),
|
||||||
_fec(new ForwardErrorCorrection(id)),
|
_fec(new ForwardErrorCorrection(id)),
|
||||||
_payloadTypeFEC(-1),
|
_payloadTypeFEC(-1) {
|
||||||
_lastFECSeqNum(0),
|
|
||||||
_frameComplete(true) {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ReceiverFEC::~ReceiverFEC() {
|
ReceiverFEC::~ReceiverFEC() {
|
||||||
@ -32,16 +31,13 @@ ReceiverFEC::~ReceiverFEC() {
|
|||||||
while (!_receivedPacketList.empty()){
|
while (!_receivedPacketList.empty()){
|
||||||
ForwardErrorCorrection::ReceivedPacket* receivedPacket =
|
ForwardErrorCorrection::ReceivedPacket* receivedPacket =
|
||||||
_receivedPacketList.front();
|
_receivedPacketList.front();
|
||||||
delete receivedPacket->pkt;
|
|
||||||
delete receivedPacket;
|
delete receivedPacket;
|
||||||
_receivedPacketList.pop_front();
|
_receivedPacketList.pop_front();
|
||||||
}
|
}
|
||||||
assert(_receivedPacketList.empty());
|
assert(_receivedPacketList.empty());
|
||||||
|
|
||||||
if (_fec != NULL) {
|
if (_fec != NULL) {
|
||||||
bool frameComplete = true;
|
_fec->ResetState(&_recoveredPacketList);
|
||||||
_fec->DecodeFEC(&_receivedPacketList, &_recoveredPacketList,_lastFECSeqNum,
|
|
||||||
frameComplete);
|
|
||||||
delete _fec;
|
delete _fec;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -84,8 +80,7 @@ WebRtc_Word32 ReceiverFEC::AddReceivedFECPacket(
|
|||||||
const WebRtcRTPHeader* rtpHeader,
|
const WebRtcRTPHeader* rtpHeader,
|
||||||
const WebRtc_UWord8* incomingRtpPacket,
|
const WebRtc_UWord8* incomingRtpPacket,
|
||||||
const WebRtc_UWord16 payloadDataLength,
|
const WebRtc_UWord16 payloadDataLength,
|
||||||
bool& FECpacket,
|
bool& FECpacket) {
|
||||||
bool oldPacket) {
|
|
||||||
if (_payloadTypeFEC == -1) {
|
if (_payloadTypeFEC == -1) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@ -107,19 +102,10 @@ WebRtc_Word32 ReceiverFEC::AddReceivedFECPacket(
|
|||||||
if (_payloadTypeFEC == payloadType) {
|
if (_payloadTypeFEC == payloadType) {
|
||||||
receivedPacket->isFec = true;
|
receivedPacket->isFec = true;
|
||||||
FECpacket = 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 {
|
} else {
|
||||||
receivedPacket->isFec = false;
|
receivedPacket->isFec = false;
|
||||||
FECpacket = false;
|
FECpacket = false;
|
||||||
}
|
}
|
||||||
receivedPacket->lastMediaPktInFrame = rtpHeader->header.markerBit;
|
|
||||||
receivedPacket->seqNum = rtpHeader->header.sequenceNumber;
|
receivedPacket->seqNum = rtpHeader->header.sequenceNumber;
|
||||||
|
|
||||||
WebRtc_UWord16 blockLength = 0;
|
WebRtc_UWord16 blockLength = 0;
|
||||||
@ -135,7 +121,6 @@ WebRtc_Word32 ReceiverFEC::AddReceivedFECPacket(
|
|||||||
// location a corrupt payload can be caught, so don't assert.
|
// location a corrupt payload can be caught, so don't assert.
|
||||||
WEBRTC_TRACE(kTraceWarning, kTraceRtpRtcp, _id,
|
WEBRTC_TRACE(kTraceWarning, kTraceRtpRtcp, _id,
|
||||||
"Corrupt payload found in %s", __FUNCTION__);
|
"Corrupt payload found in %s", __FUNCTION__);
|
||||||
delete receivedPacket->pkt;
|
|
||||||
delete receivedPacket;
|
delete receivedPacket;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@ -147,14 +132,12 @@ WebRtc_Word32 ReceiverFEC::AddReceivedFECPacket(
|
|||||||
// check next RED header
|
// check next RED header
|
||||||
if(incomingRtpPacket[rtpHeader->header.headerLength+4] & 0x80) {
|
if(incomingRtpPacket[rtpHeader->header.headerLength+4] & 0x80) {
|
||||||
// more than 2 blocks in packet not supported
|
// more than 2 blocks in packet not supported
|
||||||
delete receivedPacket->pkt;
|
|
||||||
delete receivedPacket;
|
delete receivedPacket;
|
||||||
assert(false);
|
assert(false);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
if(blockLength > payloadDataLength - REDHeaderLength) {
|
if(blockLength > payloadDataLength - REDHeaderLength) {
|
||||||
// block length longer than packet
|
// block length longer than packet
|
||||||
delete receivedPacket->pkt;
|
|
||||||
delete receivedPacket;
|
delete receivedPacket;
|
||||||
assert(false);
|
assert(false);
|
||||||
return -1;
|
return -1;
|
||||||
@ -186,7 +169,6 @@ WebRtc_Word32 ReceiverFEC::AddReceivedFECPacket(
|
|||||||
secondReceivedPacket->pkt = new ForwardErrorCorrection::Packet;
|
secondReceivedPacket->pkt = new ForwardErrorCorrection::Packet;
|
||||||
|
|
||||||
secondReceivedPacket->isFec = true;
|
secondReceivedPacket->isFec = true;
|
||||||
secondReceivedPacket->lastMediaPktInFrame = false;
|
|
||||||
secondReceivedPacket->seqNum = rtpHeader->header.sequenceNumber;
|
secondReceivedPacket->seqNum = rtpHeader->header.sequenceNumber;
|
||||||
|
|
||||||
// copy the FEC payload data
|
// copy the FEC payload data
|
||||||
@ -226,105 +208,35 @@ WebRtc_Word32 ReceiverFEC::AddReceivedFECPacket(
|
|||||||
payloadDataLength - REDHeaderLength;
|
payloadDataLength - REDHeaderLength;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(receivedPacket->isFec) {
|
|
||||||
AddReceivedFECInfo(rtpHeader, NULL, FECpacket);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(receivedPacket->pkt->length == 0) {
|
if(receivedPacket->pkt->length == 0) {
|
||||||
if (secondReceivedPacket) {
|
|
||||||
delete secondReceivedPacket->pkt;
|
|
||||||
}
|
|
||||||
delete secondReceivedPacket;
|
delete secondReceivedPacket;
|
||||||
delete receivedPacket->pkt;
|
|
||||||
delete receivedPacket;
|
delete receivedPacket;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send any old media packets to jitter buffer, don't push them onto
|
_receivedPacketList.push_back(receivedPacket);
|
||||||
// received list for FEC decoding (we don't do FEC decoding on old packets).
|
if (secondReceivedPacket) {
|
||||||
if (oldPacket && receivedPacket->isFec == false) {
|
_receivedPacketList.push_back(secondReceivedPacket);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ReceiverFEC::AddReceivedFECInfo(const WebRtcRTPHeader* rtpHeader,
|
WebRtc_Word32 ReceiverFEC::ProcessReceivedFEC() {
|
||||||
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) {
|
|
||||||
if (!_receivedPacketList.empty()) {
|
if (!_receivedPacketList.empty()) {
|
||||||
if (_fec->DecodeFEC(&_receivedPacketList, &_recoveredPacketList,
|
if (_fec->DecodeFEC(&_receivedPacketList, &_recoveredPacketList) != 0) {
|
||||||
_lastFECSeqNum, _frameComplete) != 0) {
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
assert(_receivedPacketList.empty());
|
assert(_receivedPacketList.empty());
|
||||||
}
|
}
|
||||||
if (forceFrameDecode) {
|
ForwardErrorCorrection::RecoveredPacketList::iterator it =
|
||||||
_frameComplete = true;
|
_recoveredPacketList.begin();
|
||||||
}
|
for (; it != _recoveredPacketList.end(); ++it) {
|
||||||
if (_frameComplete) {
|
if ((*it)->returned) // Already sent to the VCM and the jitter buffer.
|
||||||
while (!_recoveredPacketList.empty()) {
|
continue;
|
||||||
ForwardErrorCorrection::RecoveredPacket* recoveredPacket =
|
if (ParseAndReceivePacket((*it)->pkt) != 0) {
|
||||||
_recoveredPacketList.front();
|
return -1;
|
||||||
|
|
||||||
if (ParseAndReceivePacket(recoveredPacket->pkt) != 0) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
delete recoveredPacket->pkt;
|
|
||||||
delete recoveredPacket;
|
|
||||||
_recoveredPacketList.pop_front();
|
|
||||||
}
|
}
|
||||||
assert(_recoveredPacketList.empty());
|
(*it)->returned = true;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -346,4 +258,5 @@ int ReceiverFEC::ParseAndReceivePacket(
|
|||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace webrtc
|
} // namespace webrtc
|
||||||
|
@ -11,8 +11,6 @@
|
|||||||
#ifndef WEBRTC_MODULES_RTP_RTCP_SOURCE_RECEIVER_FEC_H_
|
#ifndef WEBRTC_MODULES_RTP_RTCP_SOURCE_RECEIVER_FEC_H_
|
||||||
#define WEBRTC_MODULES_RTP_RTCP_SOURCE_RECEIVER_FEC_H_
|
#define WEBRTC_MODULES_RTP_RTCP_SOURCE_RECEIVER_FEC_H_
|
||||||
|
|
||||||
#include <list>
|
|
||||||
|
|
||||||
#include "rtp_rtcp_defines.h"
|
#include "rtp_rtcp_defines.h"
|
||||||
// This header is included to get the nested declaration of Packet structure.
|
// This header is included to get the nested declaration of Packet structure.
|
||||||
#include "forward_error_correction.h"
|
#include "forward_error_correction.h"
|
||||||
@ -31,14 +29,9 @@ public:
|
|||||||
WebRtc_Word32 AddReceivedFECPacket(const WebRtcRTPHeader* rtpHeader,
|
WebRtc_Word32 AddReceivedFECPacket(const WebRtcRTPHeader* rtpHeader,
|
||||||
const WebRtc_UWord8* incomingRtpPacket,
|
const WebRtc_UWord8* incomingRtpPacket,
|
||||||
const WebRtc_UWord16 payloadDataLength,
|
const WebRtc_UWord16 payloadDataLength,
|
||||||
bool& FECpacket,
|
bool& FECpacket);
|
||||||
bool oldPacket);
|
|
||||||
|
|
||||||
void AddReceivedFECInfo(const WebRtcRTPHeader* rtpHeader,
|
WebRtc_Word32 ProcessReceivedFEC();
|
||||||
const WebRtc_UWord8* incomingRtpPacket,
|
|
||||||
bool& FECpacket);
|
|
||||||
|
|
||||||
WebRtc_Word32 ProcessReceivedFEC(const bool forceFrameDecode);
|
|
||||||
|
|
||||||
void SetPayloadTypeFEC(const WebRtc_Word8 payloadType);
|
void SetPayloadTypeFEC(const WebRtc_Word8 payloadType);
|
||||||
|
|
||||||
@ -46,13 +39,14 @@ private:
|
|||||||
int ParseAndReceivePacket(const ForwardErrorCorrection::Packet* packet);
|
int ParseAndReceivePacket(const ForwardErrorCorrection::Packet* packet);
|
||||||
|
|
||||||
int _id;
|
int _id;
|
||||||
RTPReceiverVideo* _owner;
|
RTPReceiverVideo* _owner;
|
||||||
ForwardErrorCorrection* _fec;
|
ForwardErrorCorrection* _fec;
|
||||||
std::list<ForwardErrorCorrection::ReceivedPacket*> _receivedPacketList;
|
// TODO(holmer): In the current version _receivedPacketList is never more
|
||||||
std::list<ForwardErrorCorrection::RecoveredPacket*> _recoveredPacketList;
|
// than one packet, since we process FEC every time a new packet
|
||||||
WebRtc_Word8 _payloadTypeFEC;
|
// arrives. We should remove the list.
|
||||||
WebRtc_UWord16 _lastFECSeqNum;
|
ForwardErrorCorrection::ReceivedPacketList _receivedPacketList;
|
||||||
bool _frameComplete;
|
ForwardErrorCorrection::RecoveredPacketList _recoveredPacketList;
|
||||||
|
WebRtc_Word8 _payloadTypeFEC;
|
||||||
};
|
};
|
||||||
} // namespace webrtc
|
} // namespace webrtc
|
||||||
|
|
||||||
|
@ -124,6 +124,7 @@ class ReceiverFecTest : public ::testing::Test {
|
|||||||
fec_ = new ForwardErrorCorrection(0);
|
fec_ = new ForwardErrorCorrection(0);
|
||||||
receiver_fec_ = new ReceiverFEC(0, &rtp_receiver_video_);
|
receiver_fec_ = new ReceiverFEC(0, &rtp_receiver_video_);
|
||||||
generator_ = new FrameGenerator();
|
generator_ = new FrameGenerator();
|
||||||
|
receiver_fec_->SetPayloadTypeFEC(kFecPayloadType);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void TearDown() {
|
virtual void TearDown() {
|
||||||
@ -132,6 +133,32 @@ class ReceiverFecTest : public ::testing::Test {
|
|||||||
delete generator_;
|
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,
|
void GenerateFrame(int num_media_packets,
|
||||||
int frame_offset,
|
int frame_offset,
|
||||||
std::list<RtpPacket*>* media_rtp_packets,
|
std::list<RtpPacket*>* media_rtp_packets,
|
||||||
@ -163,8 +190,7 @@ class ReceiverFecTest : public ::testing::Test {
|
|||||||
red_packet->data,
|
red_packet->data,
|
||||||
red_packet->length -
|
red_packet->length -
|
||||||
kRtpHeaderSize,
|
kRtpHeaderSize,
|
||||||
is_fec,
|
is_fec));
|
||||||
false));
|
|
||||||
delete red_packet;
|
delete red_packet;
|
||||||
EXPECT_FALSE(is_fec);
|
EXPECT_FALSE(is_fec);
|
||||||
}
|
}
|
||||||
@ -176,8 +202,7 @@ class ReceiverFecTest : public ::testing::Test {
|
|||||||
red_packet->data,
|
red_packet->data,
|
||||||
red_packet->length -
|
red_packet->length -
|
||||||
kRtpHeaderSize,
|
kRtpHeaderSize,
|
||||||
is_fec,
|
is_fec));
|
||||||
false));
|
|
||||||
delete red_packet;
|
delete red_packet;
|
||||||
EXPECT_TRUE(is_fec);
|
EXPECT_TRUE(is_fec);
|
||||||
}
|
}
|
||||||
@ -197,20 +222,13 @@ void DeletePackets(std::list<Packet*>* packets) {
|
|||||||
|
|
||||||
TEST_F(ReceiverFecTest, TwoMediaOneFec) {
|
TEST_F(ReceiverFecTest, TwoMediaOneFec) {
|
||||||
const unsigned int kNumFecPackets = 1u;
|
const unsigned int kNumFecPackets = 1u;
|
||||||
const unsigned int kNumMediaPackets = 2u;
|
|
||||||
std::list<RtpPacket*> media_rtp_packets;
|
std::list<RtpPacket*> media_rtp_packets;
|
||||||
std::list<Packet*> media_packets;
|
std::list<Packet*> media_packets;
|
||||||
GenerateFrame(2, 0, &media_rtp_packets, &media_packets);
|
GenerateFrame(2, 0, &media_rtp_packets, &media_packets);
|
||||||
std::list<Packet*> fec_packets;
|
std::list<Packet*> fec_packets;
|
||||||
EXPECT_EQ(0, fec_->GenerateFEC(media_packets,
|
GenerateFEC(&media_packets, &fec_packets, kNumFecPackets);
|
||||||
kNumFecPackets * 255 / kNumMediaPackets,
|
|
||||||
0,
|
|
||||||
false,
|
|
||||||
&fec_packets));
|
|
||||||
ASSERT_EQ(kNumFecPackets, fec_packets.size());
|
|
||||||
|
|
||||||
// Recovery
|
// Recovery
|
||||||
receiver_fec_->SetPayloadTypeFEC(kFecPayloadType);
|
|
||||||
std::list<RtpPacket*>::iterator media_it = media_rtp_packets.begin();
|
std::list<RtpPacket*>::iterator media_it = media_rtp_packets.begin();
|
||||||
BuildAndAddRedMediaPacket(*media_it);
|
BuildAndAddRedMediaPacket(*media_it);
|
||||||
// Drop one media packet.
|
// Drop one media packet.
|
||||||
@ -223,27 +241,21 @@ TEST_F(ReceiverFecTest, TwoMediaOneFec) {
|
|||||||
++it;
|
++it;
|
||||||
VerifyReconstructedMediaPacket(*it, 1);
|
VerifyReconstructedMediaPacket(*it, 1);
|
||||||
}
|
}
|
||||||
EXPECT_EQ(0, receiver_fec_->ProcessReceivedFEC(false));
|
EXPECT_EQ(0, receiver_fec_->ProcessReceivedFEC());
|
||||||
|
|
||||||
DeletePackets(&media_packets);
|
DeletePackets(&media_packets);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ReceiverFecTest, TwoMediaTwoFec) {
|
TEST_F(ReceiverFecTest, TwoMediaTwoFec) {
|
||||||
const unsigned int kNumFecPackets = 2u;
|
const unsigned int kNumFecPackets = 2u;
|
||||||
const unsigned int kNumMediaPackets = 2u;
|
|
||||||
std::list<RtpPacket*> media_rtp_packets;
|
std::list<RtpPacket*> media_rtp_packets;
|
||||||
std::list<Packet*> media_packets;
|
std::list<Packet*> media_packets;
|
||||||
GenerateFrame(2, 0, &media_rtp_packets, &media_packets);
|
GenerateFrame(2, 0, &media_rtp_packets, &media_packets);
|
||||||
std::list<Packet*> fec_packets;
|
std::list<Packet*> fec_packets;
|
||||||
EXPECT_EQ(0, fec_->GenerateFEC(media_packets,
|
GenerateFEC(&media_packets, &fec_packets, kNumFecPackets);
|
||||||
kNumFecPackets * 255 / kNumMediaPackets,
|
|
||||||
0,
|
|
||||||
false, &fec_packets));
|
|
||||||
ASSERT_EQ(kNumFecPackets, fec_packets.size());
|
|
||||||
|
|
||||||
// Recovery
|
// Recovery
|
||||||
// Drop both media packets.
|
// Drop both media packets.
|
||||||
receiver_fec_->SetPayloadTypeFEC(kFecPayloadType);
|
|
||||||
std::list<Packet*>::iterator fec_it = fec_packets.begin();
|
std::list<Packet*>::iterator fec_it = fec_packets.begin();
|
||||||
BuildAndAddRedFecPacket(*fec_it);
|
BuildAndAddRedFecPacket(*fec_it);
|
||||||
++fec_it;
|
++fec_it;
|
||||||
@ -255,28 +267,21 @@ TEST_F(ReceiverFecTest, TwoMediaTwoFec) {
|
|||||||
++it;
|
++it;
|
||||||
VerifyReconstructedMediaPacket(*it, 1);
|
VerifyReconstructedMediaPacket(*it, 1);
|
||||||
}
|
}
|
||||||
EXPECT_EQ(0, receiver_fec_->ProcessReceivedFEC(false));
|
EXPECT_EQ(0, receiver_fec_->ProcessReceivedFEC());
|
||||||
|
|
||||||
DeletePackets(&media_packets);
|
DeletePackets(&media_packets);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ReceiverFecTest, TwoFramesOneFec) {
|
TEST_F(ReceiverFecTest, TwoFramesOneFec) {
|
||||||
const unsigned int kNumFecPackets = 1u;
|
const unsigned int kNumFecPackets = 1u;
|
||||||
const unsigned int kNumMediaPackets = 2u;
|
|
||||||
std::list<RtpPacket*> media_rtp_packets;
|
std::list<RtpPacket*> media_rtp_packets;
|
||||||
std::list<Packet*> media_packets;
|
std::list<Packet*> media_packets;
|
||||||
GenerateFrame(1, 0, &media_rtp_packets, &media_packets);
|
GenerateFrame(1, 0, &media_rtp_packets, &media_packets);
|
||||||
GenerateFrame(1, 1, &media_rtp_packets, &media_packets);
|
GenerateFrame(1, 1, &media_rtp_packets, &media_packets);
|
||||||
std::list<Packet*> fec_packets;
|
std::list<Packet*> fec_packets;
|
||||||
EXPECT_EQ(0, fec_->GenerateFEC(media_packets,
|
GenerateFEC(&media_packets, &fec_packets, kNumFecPackets);
|
||||||
kNumFecPackets * 255 / kNumMediaPackets,
|
|
||||||
0,
|
|
||||||
false,
|
|
||||||
&fec_packets));
|
|
||||||
ASSERT_EQ(kNumFecPackets, fec_packets.size());
|
|
||||||
|
|
||||||
// Recovery
|
// Recovery
|
||||||
receiver_fec_->SetPayloadTypeFEC(kFecPayloadType);
|
|
||||||
BuildAndAddRedMediaPacket(media_rtp_packets.front());
|
BuildAndAddRedMediaPacket(media_rtp_packets.front());
|
||||||
// Drop one media packet.
|
// Drop one media packet.
|
||||||
BuildAndAddRedFecPacket(fec_packets.front());
|
BuildAndAddRedFecPacket(fec_packets.front());
|
||||||
@ -287,7 +292,27 @@ TEST_F(ReceiverFecTest, TwoFramesOneFec) {
|
|||||||
++it;
|
++it;
|
||||||
VerifyReconstructedMediaPacket(*it, 1);
|
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);
|
DeletePackets(&media_packets);
|
||||||
}
|
}
|
||||||
@ -300,15 +325,9 @@ TEST_F(ReceiverFecTest, MaxFramesOneFec) {
|
|||||||
for (unsigned int i = 0; i < kNumMediaPackets; ++i)
|
for (unsigned int i = 0; i < kNumMediaPackets; ++i)
|
||||||
GenerateFrame(1, i, &media_rtp_packets, &media_packets);
|
GenerateFrame(1, i, &media_rtp_packets, &media_packets);
|
||||||
std::list<Packet*> fec_packets;
|
std::list<Packet*> fec_packets;
|
||||||
EXPECT_EQ(0, fec_->GenerateFEC(media_packets,
|
GenerateFEC(&media_packets, &fec_packets, kNumFecPackets);
|
||||||
kNumFecPackets * 255 / kNumMediaPackets,
|
|
||||||
0,
|
|
||||||
false,
|
|
||||||
&fec_packets));
|
|
||||||
ASSERT_EQ(kNumFecPackets, fec_packets.size());
|
|
||||||
|
|
||||||
// Recovery
|
// Recovery
|
||||||
receiver_fec_->SetPayloadTypeFEC(kFecPayloadType);
|
|
||||||
std::list<RtpPacket*>::iterator it = media_rtp_packets.begin();
|
std::list<RtpPacket*>::iterator it = media_rtp_packets.begin();
|
||||||
++it; // Drop first packet.
|
++it; // Drop first packet.
|
||||||
for (; it != media_rtp_packets.end(); ++it)
|
for (; it != media_rtp_packets.end(); ++it)
|
||||||
@ -320,7 +339,7 @@ TEST_F(ReceiverFecTest, MaxFramesOneFec) {
|
|||||||
for (; it != media_rtp_packets.end(); ++it)
|
for (; it != media_rtp_packets.end(); ++it)
|
||||||
VerifyReconstructedMediaPacket(*it, 1);
|
VerifyReconstructedMediaPacket(*it, 1);
|
||||||
}
|
}
|
||||||
EXPECT_EQ(0, receiver_fec_->ProcessReceivedFEC(false));
|
EXPECT_EQ(0, receiver_fec_->ProcessReceivedFEC());
|
||||||
|
|
||||||
DeletePackets(&media_packets);
|
DeletePackets(&media_packets);
|
||||||
}
|
}
|
||||||
@ -342,4 +361,163 @@ TEST_F(ReceiverFecTest, TooManyFrames) {
|
|||||||
DeletePackets(&media_packets);
|
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
|
} // namespace webrtc
|
||||||
|
@ -131,7 +131,6 @@ TEST_F(RtpFecTest, HandleIncorrectInputs) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(RtpFecTest, FecRecoveryNoLoss) {
|
TEST_F(RtpFecTest, FecRecoveryNoLoss) {
|
||||||
bool frame_complete = true;
|
|
||||||
const int num_important_packets = 0;
|
const int num_important_packets = 0;
|
||||||
const bool use_unequal_protection = false;
|
const bool use_unequal_protection = false;
|
||||||
const int num_media_packets = 4;
|
const int num_media_packets = 4;
|
||||||
@ -153,15 +152,14 @@ TEST_F(RtpFecTest, FecRecoveryNoLoss) {
|
|||||||
memset(fec_loss_mask_, 0, sizeof(fec_loss_mask_));
|
memset(fec_loss_mask_, 0, sizeof(fec_loss_mask_));
|
||||||
NetworkReceivedPackets();
|
NetworkReceivedPackets();
|
||||||
|
|
||||||
EXPECT_EQ(0, fec_->DecodeFEC(&received_packet_list_ , &recovered_packet_list_,
|
EXPECT_EQ(0, fec_->DecodeFEC(&received_packet_list_ ,
|
||||||
fec_seq_num_, frame_complete));
|
&recovered_packet_list_));
|
||||||
|
|
||||||
// No packets lost, expect complete recovery.
|
// No packets lost, expect complete recovery.
|
||||||
EXPECT_TRUE(IsRecoveryComplete());
|
EXPECT_TRUE(IsRecoveryComplete());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(RtpFecTest, FecRecoveryWithLoss) {
|
TEST_F(RtpFecTest, FecRecoveryWithLoss) {
|
||||||
bool frame_complete = true;
|
|
||||||
const int num_important_packets = 0;
|
const int num_important_packets = 0;
|
||||||
const bool use_unequal_protection = false;
|
const bool use_unequal_protection = false;
|
||||||
const int num_media_packets = 4;
|
const int num_media_packets = 4;
|
||||||
@ -184,8 +182,8 @@ TEST_F(RtpFecTest, FecRecoveryWithLoss) {
|
|||||||
media_loss_mask_[3] = 1;
|
media_loss_mask_[3] = 1;
|
||||||
NetworkReceivedPackets();
|
NetworkReceivedPackets();
|
||||||
|
|
||||||
EXPECT_EQ(0, fec_->DecodeFEC(&received_packet_list_ , &recovered_packet_list_,
|
EXPECT_EQ(0, fec_->DecodeFEC(&received_packet_list_ ,
|
||||||
fec_seq_num_, frame_complete));
|
&recovered_packet_list_));
|
||||||
|
|
||||||
// One packet lost, one FEC packet, expect complete recovery.
|
// One packet lost, one FEC packet, expect complete recovery.
|
||||||
EXPECT_TRUE(IsRecoveryComplete());
|
EXPECT_TRUE(IsRecoveryComplete());
|
||||||
@ -198,15 +196,14 @@ TEST_F(RtpFecTest, FecRecoveryWithLoss) {
|
|||||||
media_loss_mask_[3] = 1;
|
media_loss_mask_[3] = 1;
|
||||||
NetworkReceivedPackets();
|
NetworkReceivedPackets();
|
||||||
|
|
||||||
EXPECT_EQ(0, fec_->DecodeFEC(&received_packet_list_ , &recovered_packet_list_,
|
EXPECT_EQ(0, fec_->DecodeFEC(&received_packet_list_ ,
|
||||||
fec_seq_num_, frame_complete));
|
&recovered_packet_list_));
|
||||||
|
|
||||||
// 2 packets lost, one FEC packet, cannot get complete recovery.
|
// 2 packets lost, one FEC packet, cannot get complete recovery.
|
||||||
EXPECT_FALSE(IsRecoveryComplete());
|
EXPECT_FALSE(IsRecoveryComplete());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(RtpFecTest, FecRecoveryWithLoss50perc) {
|
TEST_F(RtpFecTest, FecRecoveryWithLoss50perc) {
|
||||||
bool frame_complete = true;
|
|
||||||
const int num_important_packets = 0;
|
const int num_important_packets = 0;
|
||||||
const bool use_unequal_protection = false;
|
const bool use_unequal_protection = false;
|
||||||
const int num_media_packets = 4;
|
const int num_media_packets = 4;
|
||||||
@ -243,8 +240,8 @@ TEST_F(RtpFecTest, FecRecoveryWithLoss50perc) {
|
|||||||
|
|
||||||
NetworkReceivedPackets();
|
NetworkReceivedPackets();
|
||||||
|
|
||||||
EXPECT_EQ(0, fec_->DecodeFEC(&received_packet_list_ , &recovered_packet_list_,
|
EXPECT_EQ(0, fec_->DecodeFEC(&received_packet_list_ ,
|
||||||
fec_seq_num_, frame_complete));
|
&recovered_packet_list_));
|
||||||
|
|
||||||
// With media packet#1 and FEC packets #0, #1, #3, expect complete recovery.
|
// With media packet#1 and FEC packets #0, #1, #3, expect complete recovery.
|
||||||
EXPECT_TRUE(IsRecoveryComplete());
|
EXPECT_TRUE(IsRecoveryComplete());
|
||||||
@ -259,15 +256,14 @@ TEST_F(RtpFecTest, FecRecoveryWithLoss50perc) {
|
|||||||
media_loss_mask_[3] = 1;
|
media_loss_mask_[3] = 1;
|
||||||
NetworkReceivedPackets();
|
NetworkReceivedPackets();
|
||||||
|
|
||||||
EXPECT_EQ(0, fec_->DecodeFEC(&received_packet_list_ , &recovered_packet_list_,
|
EXPECT_EQ(0, fec_->DecodeFEC(&received_packet_list_ ,
|
||||||
fec_seq_num_, frame_complete));
|
&recovered_packet_list_));
|
||||||
|
|
||||||
// Cannot get complete recovery for this loss configuration.
|
// Cannot get complete recovery for this loss configuration.
|
||||||
EXPECT_FALSE(IsRecoveryComplete());
|
EXPECT_FALSE(IsRecoveryComplete());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(RtpFecTest, FecRecoveryNoLossUep) {
|
TEST_F(RtpFecTest, FecRecoveryNoLossUep) {
|
||||||
bool frame_complete = true;
|
|
||||||
const int num_important_packets = 2;
|
const int num_important_packets = 2;
|
||||||
const bool use_unequal_protection = true;
|
const bool use_unequal_protection = true;
|
||||||
const int num_media_packets = 4;
|
const int num_media_packets = 4;
|
||||||
@ -289,15 +285,14 @@ TEST_F(RtpFecTest, FecRecoveryNoLossUep) {
|
|||||||
memset(fec_loss_mask_, 0, sizeof(fec_loss_mask_));
|
memset(fec_loss_mask_, 0, sizeof(fec_loss_mask_));
|
||||||
NetworkReceivedPackets();
|
NetworkReceivedPackets();
|
||||||
|
|
||||||
EXPECT_EQ(0, fec_->DecodeFEC(&received_packet_list_ , &recovered_packet_list_,
|
EXPECT_EQ(0, fec_->DecodeFEC(&received_packet_list_ ,
|
||||||
fec_seq_num_, frame_complete));
|
&recovered_packet_list_));
|
||||||
|
|
||||||
// No packets lost, expect complete recovery.
|
// No packets lost, expect complete recovery.
|
||||||
EXPECT_TRUE(IsRecoveryComplete());
|
EXPECT_TRUE(IsRecoveryComplete());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(RtpFecTest, FecRecoveryWithLossUep) {
|
TEST_F(RtpFecTest, FecRecoveryWithLossUep) {
|
||||||
bool frame_complete = true;
|
|
||||||
const int num_important_packets = 2;
|
const int num_important_packets = 2;
|
||||||
const bool use_unequal_protection = true;
|
const bool use_unequal_protection = true;
|
||||||
const int num_media_packets = 4;
|
const int num_media_packets = 4;
|
||||||
@ -320,8 +315,8 @@ TEST_F(RtpFecTest, FecRecoveryWithLossUep) {
|
|||||||
media_loss_mask_[3] = 1;
|
media_loss_mask_[3] = 1;
|
||||||
NetworkReceivedPackets();
|
NetworkReceivedPackets();
|
||||||
|
|
||||||
EXPECT_EQ(0, fec_->DecodeFEC(&received_packet_list_ , &recovered_packet_list_,
|
EXPECT_EQ(0, fec_->DecodeFEC(&received_packet_list_ ,
|
||||||
fec_seq_num_, frame_complete));
|
&recovered_packet_list_));
|
||||||
|
|
||||||
// One packet lost, one FEC packet, expect complete recovery.
|
// One packet lost, one FEC packet, expect complete recovery.
|
||||||
EXPECT_TRUE(IsRecoveryComplete());
|
EXPECT_TRUE(IsRecoveryComplete());
|
||||||
@ -334,15 +329,14 @@ TEST_F(RtpFecTest, FecRecoveryWithLossUep) {
|
|||||||
media_loss_mask_[3] = 1;
|
media_loss_mask_[3] = 1;
|
||||||
NetworkReceivedPackets();
|
NetworkReceivedPackets();
|
||||||
|
|
||||||
EXPECT_EQ(0, fec_->DecodeFEC(&received_packet_list_ , &recovered_packet_list_,
|
EXPECT_EQ(0, fec_->DecodeFEC(&received_packet_list_ ,
|
||||||
fec_seq_num_, frame_complete));
|
&recovered_packet_list_));
|
||||||
|
|
||||||
// 2 packets lost, one FEC packet, cannot get complete recovery.
|
// 2 packets lost, one FEC packet, cannot get complete recovery.
|
||||||
EXPECT_FALSE(IsRecoveryComplete());
|
EXPECT_FALSE(IsRecoveryComplete());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(RtpFecTest, FecRecoveryWithLoss50percUep) {
|
TEST_F(RtpFecTest, FecRecoveryWithLoss50percUep) {
|
||||||
bool frame_complete = true;
|
|
||||||
const int num_important_packets = 1;
|
const int num_important_packets = 1;
|
||||||
const bool use_unequal_protection = true;
|
const bool use_unequal_protection = true;
|
||||||
const int num_media_packets = 4;
|
const int num_media_packets = 4;
|
||||||
@ -378,8 +372,8 @@ TEST_F(RtpFecTest, FecRecoveryWithLoss50percUep) {
|
|||||||
media_loss_mask_[3] = 1;
|
media_loss_mask_[3] = 1;
|
||||||
NetworkReceivedPackets();
|
NetworkReceivedPackets();
|
||||||
|
|
||||||
EXPECT_EQ(0, fec_->DecodeFEC(&received_packet_list_ , &recovered_packet_list_,
|
EXPECT_EQ(0, fec_->DecodeFEC(&received_packet_list_ ,
|
||||||
fec_seq_num_, frame_complete));
|
&recovered_packet_list_));
|
||||||
|
|
||||||
// With media packet#1 and FEC packets #0, #2, #3, expect complete recovery.
|
// With media packet#1 and FEC packets #0, #2, #3, expect complete recovery.
|
||||||
EXPECT_TRUE(IsRecoveryComplete());
|
EXPECT_TRUE(IsRecoveryComplete());
|
||||||
@ -394,8 +388,8 @@ TEST_F(RtpFecTest, FecRecoveryWithLoss50percUep) {
|
|||||||
media_loss_mask_[3] = 1;
|
media_loss_mask_[3] = 1;
|
||||||
NetworkReceivedPackets();
|
NetworkReceivedPackets();
|
||||||
|
|
||||||
EXPECT_EQ(0, fec_->DecodeFEC(&received_packet_list_ , &recovered_packet_list_,
|
EXPECT_EQ(0, fec_->DecodeFEC(&received_packet_list_ ,
|
||||||
fec_seq_num_, frame_complete));
|
&recovered_packet_list_));
|
||||||
|
|
||||||
// Cannot get complete recovery for this loss configuration.
|
// Cannot get complete recovery for this loss configuration.
|
||||||
EXPECT_FALSE(IsRecoveryComplete());
|
EXPECT_FALSE(IsRecoveryComplete());
|
||||||
@ -404,11 +398,11 @@ TEST_F(RtpFecTest, FecRecoveryWithLoss50percUep) {
|
|||||||
// TODO(marpan): Add more test cases.
|
// TODO(marpan): Add more test cases.
|
||||||
|
|
||||||
void RtpFecTest::TearDown() {
|
void RtpFecTest::TearDown() {
|
||||||
|
fec_->ResetState(&recovered_packet_list_);
|
||||||
|
delete fec_;
|
||||||
FreeRecoveredPacketList();
|
FreeRecoveredPacketList();
|
||||||
ClearList(&media_packet_list_);
|
ClearList(&media_packet_list_);
|
||||||
EXPECT_TRUE(media_packet_list_.empty());
|
EXPECT_TRUE(media_packet_list_.empty());
|
||||||
|
|
||||||
fec_packet_list_.clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void RtpFecTest::FreeRecoveredPacketList() {
|
void RtpFecTest::FreeRecoveredPacketList() {
|
||||||
@ -482,18 +476,13 @@ void RtpFecTest:: ReceivedPackets(
|
|||||||
// obtained from RTP header. These were set in ConstructMediaPackets().
|
// obtained from RTP header. These were set in ConstructMediaPackets().
|
||||||
received_packet->seqNum =
|
received_packet->seqNum =
|
||||||
webrtc::ModuleRTPUtility::BufferToUWord16(&packet->data[2]);
|
webrtc::ModuleRTPUtility::BufferToUWord16(&packet->data[2]);
|
||||||
received_packet->lastMediaPktInFrame = (packet->data[1] & 0x80) != 0;
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// The sequence number, marker bit, and ssrc number are defined in the
|
// 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.
|
// 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
|
// So we set these values below based on the values generated in
|
||||||
// ConstructMediaPackets().
|
// ConstructMediaPackets().
|
||||||
|
|
||||||
received_packet->seqNum = seq_num;
|
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
|
// The ssrc value for FEC packets is set to the one used for the
|
||||||
// media packets in ConstructMediaPackets().
|
// media packets in ConstructMediaPackets().
|
||||||
received_packet->ssrc = ssrc_;
|
received_packet->ssrc = ssrc_;
|
||||||
|
@ -247,62 +247,14 @@ RTPReceiverVideo::ParseVideoCodecSpecific(WebRtcRTPHeader* rtpHeader,
|
|||||||
_criticalSectionReceiverVideo->Leave();
|
_criticalSectionReceiverVideo->Leave();
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
bool oldPacket = false;
|
|
||||||
bool FECpacket = false;
|
bool FECpacket = false;
|
||||||
bool wrapped = false; // Not used; just for OldTimeStamp().
|
retVal = _receiveFEC->AddReceivedFECPacket(
|
||||||
|
rtpHeader,
|
||||||
// Check for old packets.
|
incomingRtpPacket,
|
||||||
if (ModuleRTPUtility::OldTimestamp(rtpHeader->header.timestamp,
|
payloadDataLength,
|
||||||
TimeStamp(),
|
FECpacket);
|
||||||
&wrapped))
|
if (retVal != -1)
|
||||||
{
|
retVal = _receiveFEC->ProcessReceivedFEC();
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_criticalSectionReceiverVideo->Leave();
|
_criticalSectionReceiverVideo->Leave();
|
||||||
|
|
||||||
if(retVal == 0 && FECpacket)
|
if(retVal == 0 && FECpacket)
|
||||||
|
@ -31,8 +31,8 @@
|
|||||||
using namespace webrtc;
|
using namespace webrtc;
|
||||||
|
|
||||||
void ReceivePackets(
|
void ReceivePackets(
|
||||||
std::list<ForwardErrorCorrection::ReceivedPacket*>* toDecodeList,
|
ForwardErrorCorrection::ReceivedPacketList* toDecodeList,
|
||||||
std::list<ForwardErrorCorrection::ReceivedPacket*>* receivedPacketList,
|
ForwardErrorCorrection::ReceivedPacketList* receivedPacketList,
|
||||||
WebRtc_UWord32 numPacketsToDecode, float reorderRate, float duplicateRate);
|
WebRtc_UWord32 numPacketsToDecode, float reorderRate, float duplicateRate);
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
@ -48,11 +48,11 @@ int main() {
|
|||||||
WebRtc_UWord32 id = 0;
|
WebRtc_UWord32 id = 0;
|
||||||
ForwardErrorCorrection fec(id);
|
ForwardErrorCorrection fec(id);
|
||||||
|
|
||||||
std::list<ForwardErrorCorrection::Packet*> mediaPacketList;
|
ForwardErrorCorrection::PacketList mediaPacketList;
|
||||||
std::list<ForwardErrorCorrection::Packet*> fecPacketList;
|
ForwardErrorCorrection::PacketList fecPacketList;
|
||||||
std::list<ForwardErrorCorrection::ReceivedPacket*> toDecodeList;
|
ForwardErrorCorrection::ReceivedPacketList toDecodeList;
|
||||||
std::list<ForwardErrorCorrection::ReceivedPacket*> receivedPacketList;
|
ForwardErrorCorrection::ReceivedPacketList receivedPacketList;
|
||||||
std::list<ForwardErrorCorrection::RecoveredPacket*> recoveredPacketList;
|
ForwardErrorCorrection::RecoveredPacketList recoveredPacketList;
|
||||||
std::list<WebRtc_UWord8*> fecMaskList;
|
std::list<WebRtc_UWord8*> fecMaskList;
|
||||||
|
|
||||||
ForwardErrorCorrection::Packet* mediaPacket;
|
ForwardErrorCorrection::Packet* mediaPacket;
|
||||||
@ -226,7 +226,7 @@ int main() {
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
memset(mediaLossMask, 0, sizeof(mediaLossMask));
|
memset(mediaLossMask, 0, sizeof(mediaLossMask));
|
||||||
std::list<ForwardErrorCorrection::Packet*>::iterator
|
ForwardErrorCorrection::PacketList::iterator
|
||||||
mediaPacketListItem = mediaPacketList.begin();
|
mediaPacketListItem = mediaPacketList.begin();
|
||||||
ForwardErrorCorrection::ReceivedPacket* receivedPacket;
|
ForwardErrorCorrection::ReceivedPacket* receivedPacket;
|
||||||
WebRtc_UWord32 mediaPacketIdx = 0;
|
WebRtc_UWord32 mediaPacketIdx = 0;
|
||||||
@ -241,8 +241,7 @@ int main() {
|
|||||||
mediaLossMask[mediaPacketIdx] = 1;
|
mediaLossMask[mediaPacketIdx] = 1;
|
||||||
receivedPacket =
|
receivedPacket =
|
||||||
new ForwardErrorCorrection::ReceivedPacket;
|
new ForwardErrorCorrection::ReceivedPacket;
|
||||||
receivedPacket->pkt =
|
receivedPacket->pkt = new ForwardErrorCorrection::Packet;
|
||||||
new ForwardErrorCorrection::Packet;
|
|
||||||
receivedPacketList.push_back(receivedPacket);
|
receivedPacketList.push_back(receivedPacket);
|
||||||
|
|
||||||
receivedPacket->pkt->length = mediaPacket->length;
|
receivedPacket->pkt->length = mediaPacket->length;
|
||||||
@ -251,14 +250,12 @@ int main() {
|
|||||||
receivedPacket->seqNum =
|
receivedPacket->seqNum =
|
||||||
ModuleRTPUtility::BufferToUWord16(&mediaPacket->data[2]);
|
ModuleRTPUtility::BufferToUWord16(&mediaPacket->data[2]);
|
||||||
receivedPacket->isFec = false;
|
receivedPacket->isFec = false;
|
||||||
receivedPacket->lastMediaPktInFrame =
|
|
||||||
(mediaPacket->data[1] & 0x80) != 0;
|
|
||||||
}
|
}
|
||||||
mediaPacketIdx++;
|
mediaPacketIdx++;
|
||||||
++mediaPacketListItem;
|
++mediaPacketListItem;
|
||||||
}
|
}
|
||||||
memset(fecLossMask, 0, sizeof(fecLossMask));
|
memset(fecLossMask, 0, sizeof(fecLossMask));
|
||||||
std::list<ForwardErrorCorrection::Packet*>::iterator
|
ForwardErrorCorrection::PacketList::iterator
|
||||||
fecPacketListItem = fecPacketList.begin();
|
fecPacketListItem = fecPacketList.begin();
|
||||||
ForwardErrorCorrection::Packet* fecPacket;
|
ForwardErrorCorrection::Packet* fecPacket;
|
||||||
WebRtc_UWord32 fecPacketIdx = 0;
|
WebRtc_UWord32 fecPacketIdx = 0;
|
||||||
@ -270,8 +267,7 @@ int main() {
|
|||||||
fecLossMask[fecPacketIdx] = 1;
|
fecLossMask[fecPacketIdx] = 1;
|
||||||
receivedPacket =
|
receivedPacket =
|
||||||
new ForwardErrorCorrection::ReceivedPacket;
|
new ForwardErrorCorrection::ReceivedPacket;
|
||||||
receivedPacket->pkt =
|
receivedPacket->pkt = new ForwardErrorCorrection::Packet;
|
||||||
new ForwardErrorCorrection::Packet;
|
|
||||||
|
|
||||||
receivedPacketList.push_back(receivedPacket);
|
receivedPacketList.push_back(receivedPacket);
|
||||||
|
|
||||||
@ -281,7 +277,6 @@ int main() {
|
|||||||
|
|
||||||
receivedPacket->seqNum = seqNum;
|
receivedPacket->seqNum = seqNum;
|
||||||
receivedPacket->isFec = true;
|
receivedPacket->isFec = true;
|
||||||
receivedPacket->lastMediaPktInFrame = false;
|
|
||||||
receivedPacket->ssrc = ssrc;
|
receivedPacket->ssrc = ssrc;
|
||||||
|
|
||||||
fecMaskList.push_back(fecPacketMasks[fecPacketIdx]);
|
fecMaskList.push_back(fecPacketMasks[fecPacketIdx]);
|
||||||
@ -336,7 +331,6 @@ int main() {
|
|||||||
}
|
}
|
||||||
printf("\n\n");
|
printf("\n\n");
|
||||||
#endif
|
#endif
|
||||||
bool complete = true; // Marks start of new frame.
|
|
||||||
bool fecPacketReceived = false; // For error-checking frame completion.
|
bool fecPacketReceived = false; // For error-checking frame completion.
|
||||||
while (!receivedPacketList.empty()) {
|
while (!receivedPacketList.empty()) {
|
||||||
WebRtc_UWord32 numPacketsToDecode = static_cast<WebRtc_UWord32>
|
WebRtc_UWord32 numPacketsToDecode = static_cast<WebRtc_UWord32>
|
||||||
@ -349,7 +343,7 @@ int main() {
|
|||||||
numPacketsToDecode, reorderRate, duplicateRate);
|
numPacketsToDecode, reorderRate, duplicateRate);
|
||||||
|
|
||||||
if (fecPacketReceived == false) {
|
if (fecPacketReceived == false) {
|
||||||
std::list<ForwardErrorCorrection::ReceivedPacket*>::iterator
|
ForwardErrorCorrection::ReceivedPacketList::iterator
|
||||||
toDecodeIt = toDecodeList.begin();
|
toDecodeIt = toDecodeList.begin();
|
||||||
while (toDecodeIt != toDecodeList.end()) {
|
while (toDecodeIt != toDecodeList.end()) {
|
||||||
receivedPacket = *toDecodeIt;
|
receivedPacket = *toDecodeIt;
|
||||||
@ -359,8 +353,8 @@ int main() {
|
|||||||
++toDecodeIt;
|
++toDecodeIt;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (fec.DecodeFEC(&toDecodeList, &recoveredPacketList, seqNum,
|
if (fec.DecodeFEC(&toDecodeList, &recoveredPacketList)
|
||||||
complete) != 0) {
|
!= 0) {
|
||||||
printf("Error: DecodeFEC() failed\n");
|
printf("Error: DecodeFEC() failed\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@ -368,34 +362,13 @@ int main() {
|
|||||||
printf("Error: received packet list is not empty\n");
|
printf("Error: received packet list is not empty\n");
|
||||||
return -1;
|
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();
|
mediaPacketListItem = mediaPacketList.begin();
|
||||||
mediaPacketIdx = 0;
|
mediaPacketIdx = 0;
|
||||||
while (mediaPacketListItem != mediaPacketList.end()) {
|
while (mediaPacketListItem != mediaPacketList.end()) {
|
||||||
if (mediaLossMask[mediaPacketIdx] == 1) {
|
if (mediaLossMask[mediaPacketIdx] == 1) {
|
||||||
// Should have recovered this packet.
|
// Should have recovered this packet.
|
||||||
std::list<ForwardErrorCorrection::RecoveredPacket*>::iterator
|
ForwardErrorCorrection::RecoveredPacketList::iterator
|
||||||
recoveredPacketListItem = recoveredPacketList.begin();
|
recoveredPacketListItem = recoveredPacketList.begin();
|
||||||
|
|
||||||
if (recoveredPacketListItem == recoveredPacketList.end()) {
|
if (recoveredPacketListItem == recoveredPacketList.end()) {
|
||||||
@ -417,13 +390,13 @@ int main() {
|
|||||||
"original media packet\n");
|
"original media packet\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
delete recoveredPacket->pkt;
|
|
||||||
delete recoveredPacket;
|
delete recoveredPacket;
|
||||||
recoveredPacketList.pop_front();
|
recoveredPacketList.pop_front();
|
||||||
}
|
}
|
||||||
mediaPacketIdx++;
|
mediaPacketIdx++;
|
||||||
++mediaPacketListItem;
|
++mediaPacketListItem;
|
||||||
}
|
}
|
||||||
|
fec.ResetState(&recoveredPacketList);
|
||||||
if (!recoveredPacketList.empty()) {
|
if (!recoveredPacketList.empty()) {
|
||||||
printf("Error: excessive number of recovered packets.\n");
|
printf("Error: excessive number of recovered packets.\n");
|
||||||
printf("\t size is:%u\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
|
// Delete received packets we didn't pass to DecodeFEC(), due to early
|
||||||
// frame completion.
|
// frame completion.
|
||||||
std::list<ForwardErrorCorrection::ReceivedPacket*>::iterator
|
ForwardErrorCorrection::ReceivedPacketList::iterator
|
||||||
receivedPacketIt = receivedPacketList.begin();
|
receivedPacketIt = receivedPacketList.begin();
|
||||||
while (receivedPacketIt != receivedPacketList.end()) {
|
while (receivedPacketIt != receivedPacketList.end()) {
|
||||||
receivedPacket = *receivedPacketIt;
|
receivedPacket = *receivedPacketIt;
|
||||||
delete receivedPacket->pkt;
|
|
||||||
delete receivedPacket;
|
delete receivedPacket;
|
||||||
++receivedPacketIt;
|
++receivedPacketIt;
|
||||||
receivedPacketList.pop_front();
|
receivedPacketList.pop_front();
|
||||||
@ -469,8 +441,7 @@ int main() {
|
|||||||
} // loop over loss rates
|
} // loop over loss rates
|
||||||
|
|
||||||
// Have DecodeFEC free allocated memory.
|
// Have DecodeFEC free allocated memory.
|
||||||
bool complete = true;
|
fec.ResetState(&recoveredPacketList);
|
||||||
fec.DecodeFEC(&receivedPacketList, &recoveredPacketList, seqNum, complete);
|
|
||||||
if (!recoveredPacketList.empty()) {
|
if (!recoveredPacketList.empty()) {
|
||||||
printf("Error: recovered packet list is not empty\n");
|
printf("Error: recovered packet list is not empty\n");
|
||||||
return -1;
|
return -1;
|
||||||
@ -480,13 +451,13 @@ int main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ReceivePackets(
|
void ReceivePackets(
|
||||||
std::list<ForwardErrorCorrection::ReceivedPacket*>* toDecodeList,
|
ForwardErrorCorrection::ReceivedPacketList* toDecodeList,
|
||||||
std::list<ForwardErrorCorrection::ReceivedPacket*>* receivedPacketList,
|
ForwardErrorCorrection::ReceivedPacketList* receivedPacketList,
|
||||||
WebRtc_UWord32 numPacketsToDecode, float reorderRate, float duplicateRate) {
|
WebRtc_UWord32 numPacketsToDecode, float reorderRate, float duplicateRate) {
|
||||||
assert(toDecodeList->empty());
|
assert(toDecodeList->empty());
|
||||||
assert(numPacketsToDecode <= receivedPacketList->size());
|
assert(numPacketsToDecode <= receivedPacketList->size());
|
||||||
|
|
||||||
std::list<ForwardErrorCorrection::ReceivedPacket*>::iterator it;
|
ForwardErrorCorrection::ReceivedPacketList::iterator it;
|
||||||
for (WebRtc_UWord32 i = 0; i < numPacketsToDecode; i++) {
|
for (WebRtc_UWord32 i = 0; i < numPacketsToDecode; i++) {
|
||||||
it = receivedPacketList->begin();
|
it = receivedPacketList->begin();
|
||||||
// Reorder packets.
|
// Reorder packets.
|
||||||
@ -507,9 +478,7 @@ void ReceivePackets(
|
|||||||
while (randomVariable < duplicateRate) {
|
while (randomVariable < duplicateRate) {
|
||||||
ForwardErrorCorrection::ReceivedPacket* duplicatePacket =
|
ForwardErrorCorrection::ReceivedPacket* duplicatePacket =
|
||||||
new ForwardErrorCorrection::ReceivedPacket;
|
new ForwardErrorCorrection::ReceivedPacket;
|
||||||
memcpy(duplicatePacket, receivedPacket,
|
*duplicatePacket = *receivedPacket;
|
||||||
sizeof(ForwardErrorCorrection::ReceivedPacket));
|
|
||||||
|
|
||||||
duplicatePacket->pkt = new ForwardErrorCorrection::Packet;
|
duplicatePacket->pkt = new ForwardErrorCorrection::Packet;
|
||||||
memcpy(duplicatePacket->pkt->data, receivedPacket->pkt->data,
|
memcpy(duplicatePacket->pkt->data, receivedPacket->pkt->data,
|
||||||
receivedPacket->pkt->length);
|
receivedPacket->pkt->length);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user