Modified the FEC to allow for option of unequal protection (UEP) across packets.

Added two files under testFec, removed old testFec.cpp, and added two
new files for generating packet masks: _internal.cc/h.
Review URL: http://webrtc-codereview.appspot.com/26003

git-svn-id: http://webrtc.googlecode.com/svn/trunk@93 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
marpan@google.com 2011-06-16 21:44:38 +00:00
parent e25b0148c9
commit ae0ad911a1
10 changed files with 1103 additions and 593 deletions

View File

@ -13,68 +13,48 @@
#include "rtp_utility.h"
#include "trace.h"
#include <cassert>
#include <cstring>
#define RtpHeaderSize 12 /**> Minimum RTP header size in bytes. */
#define FecHeaderSize 10 /**> FEC header size in bytes. */
#define MaskSizeLBitSet 6 /**> Packet mask size in bytes (L bit is set). */
#define MaskSizeLBitClear 2 /**> Packet mask size in bytes (L bit is cleared). */
#define UlpHeaderSizeLBitSet (2+MaskSizeLBitSet) /**> ULP header size in bytes (L bit is set). */
#define UlpHeaderSizeLBitClear (2 + MaskSizeLBitClear) /**> ULP header size in bytes (L bit is cleared). */
#define TransportOverhead 28 /**> Transport header size in bytes. Assume UDP/IPv4 as a reasonable minimum. */
#include "forward_error_correction_internal.h"
namespace webrtc {
/**
* Used for internal storage of FEC packets in a list.
*/
// Minimum RTP header size in bytes.
const WebRtc_UWord8 kRtpHeaderSize = 12;
// FEC header size in bytes.
const WebRtc_UWord8 kFecHeaderSize = 10;
// ULP header size in bytes (L bit is set).
const WebRtc_UWord8 kUlpHeaderSizeLBitSet = (2 + kMaskSizeLBitSet);
// ULP header size in bytes (L bit is cleared).
const WebRtc_UWord8 kUlpHeaderSizeLBitClear = (2 + kMaskSizeLBitClear);
//Transport header size in bytes. Assume UDP/IPv4 as a reasonable minimum.
const WebRtc_UWord8 kTransportOverhead = 28;
//
// Used for internal storage of FEC packets in a list.
//
struct FecPacket
{
ListWrapper protectedPktList; /**> List containing #ProtectedPacket types. */
ListWrapper protectedPktList; /**> List containing #ProtectedPacket types.*/
WebRtc_UWord16 seqNum; /**> Sequence number. */
WebRtc_UWord32 ssrc; /**> SSRC of the current frame. */
ForwardErrorCorrection::Packet* pkt; /**> Pointer to the packet storage. */
};
/**
* Used to link media packets to their protecting FEC packets.
*/
//
// Used to link media packets to their protecting FEC packets.
//
struct ProtectedPacket
{
WebRtc_UWord16 seqNum; /**> Sequence number. */
ForwardErrorCorrection::Packet* pkt; /**> Pointer to the packet storage. */
};
namespace // Unnamed namespace gives internal linkage.
{
/**
* Returns an array of packet masks. Every NumMaskBytes-bytes of the array
* corresponds to the mask of a single FEC packet. The mask indicates which
* media packets should be protected by the FEC packet.
*
* \param[out] packetMask A pointer to hold the packet mask array, of size
* numFecPackets * NumMaskBytes;
* \param[in] numMediaPackets The number of media packets to protect.
* [1, maxMediaPackets].
* \param[in] numFecPackets The number of FEC packets which will be generated.
* [1, numMediaPackets].
*/
void
GeneratePacketMasks(const WebRtc_UWord8*& packetMask,
const WebRtc_UWord32 numMediaPackets,
const WebRtc_UWord32 numFecPackets)
{
assert(numMediaPackets <= sizeof(packetMaskTbl)/sizeof(*packetMaskTbl) &&
numMediaPackets > 0);
assert(numFecPackets <= numMediaPackets && numFecPackets > 0);
// Retrieve corresponding mask table.
packetMask = packetMaskTbl[numMediaPackets - 1][numFecPackets - 1];
}
}
ForwardErrorCorrection::ForwardErrorCorrection(const WebRtc_Word32 id) :
_id(id),
_generatedFecPackets(NULL),
@ -112,8 +92,9 @@ ForwardErrorCorrection::~ForwardErrorCorrection()
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
WebRtc_Word32
ForwardErrorCorrection::GenerateFEC(const ListWrapper& mediaPacketList,
ListWrapper& fecPacketList,
WebRtc_UWord8 protectionFactor)
WebRtc_UWord8 protectionFactor,
WebRtc_UWord32 numImportantPackets,
ListWrapper& fecPacketList)
{
if (mediaPacketList.Empty())
{
@ -131,9 +112,12 @@ ForwardErrorCorrection::GenerateFEC(const ListWrapper& mediaPacketList,
const WebRtc_UWord16 numMediaPackets = mediaPacketList.GetSize();
const WebRtc_UWord8 lBit = numMediaPackets > 16 ? 1 : 0;
const WebRtc_UWord16 numMaskBytes = (lBit == 1)? MaskSizeLBitSet : MaskSizeLBitClear;
const WebRtc_UWord16 ulpHeaderSize = (lBit == 1)? UlpHeaderSizeLBitSet : UlpHeaderSizeLBitClear;
const WebRtc_UWord16 fecRtpOffset = FecHeaderSize + ulpHeaderSize - RtpHeaderSize;
const WebRtc_UWord16 numMaskBytes =
(lBit == 1)? kMaskSizeLBitSet : kMaskSizeLBitClear;
const WebRtc_UWord16 ulpHeaderSize =
(lBit == 1)? kUlpHeaderSizeLBitSet : kUlpHeaderSizeLBitClear;
const WebRtc_UWord16 fecRtpOffset =
kFecHeaderSize + ulpHeaderSize - kRtpHeaderSize;
const WebRtc_UWord16 maxMediaPackets = numMaskBytes * 8;
if (numMediaPackets > maxMediaPackets)
@ -144,6 +128,23 @@ ForwardErrorCorrection::GenerateFEC(const ListWrapper& mediaPacketList,
return -1;
}
// Error checking on the number of important packets.
// Can't have more important packets than media packets.
if (numImportantPackets > numMediaPackets)
{
WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id,
"Number of Important packet greater than number of Media Packets %d %d",
numImportantPackets, numMediaPackets);
return -1;
}
if (numImportantPackets < 0)
{
WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id,
"Number of Important packets less than zero %d %d",
numImportantPackets, numMediaPackets);
return -1;
}
// Do some error checking on the media packets.
Packet* mediaPacket;
ListItem* mediaListItem = mediaPacketList.First();
@ -151,7 +152,7 @@ ForwardErrorCorrection::GenerateFEC(const ListWrapper& mediaPacketList,
{
mediaPacket = static_cast<Packet*>(mediaListItem->GetItem());
if (mediaPacket->length < RtpHeaderSize)
if (mediaPacket->length < kRtpHeaderSize)
{
WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id,
"%s media packet (%d bytes) is smaller than RTP header",
@ -160,7 +161,7 @@ ForwardErrorCorrection::GenerateFEC(const ListWrapper& mediaPacketList,
}
// Ensure our FEC packets will fit in a typical MTU.
if (mediaPacket->length + PacketOverhead() + TransportOverhead >
if (mediaPacket->length + PacketOverhead() + kTransportOverhead >
IP_PACKET_SIZE)
{
WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id,
@ -197,8 +198,10 @@ ForwardErrorCorrection::GenerateFEC(const ListWrapper& mediaPacketList,
}
// -- Generate packet masks --
const WebRtc_UWord8* packetMask;
GeneratePacketMasks(packetMask, numMediaPackets, numFecPackets);
WebRtc_UWord8 packetMask[numFecPackets * numMaskBytes];
memset(packetMask, 0, numFecPackets * numMaskBytes);
internal::GeneratePacketMasks(numMediaPackets, numFecPackets,
numImportantPackets, packetMask);
// -- Generate FEC bit strings --
WebRtc_UWord8 mediaPayloadLength[2];
@ -218,7 +221,7 @@ ForwardErrorCorrection::GenerateFEC(const ListWrapper& mediaPacketList,
// Assign network-ordered media payload length.
ModuleRTPUtility::AssignUWord16ToBuffer(mediaPayloadLength,
mediaPacket->length - RtpHeaderSize);
mediaPacket->length - kRtpHeaderSize);
fecPacketLength = mediaPacket->length + fecRtpOffset;
// On the first protected packet, we don't need to XOR.
if (_generatedFecPackets[i].length == 0)
@ -231,9 +234,9 @@ ForwardErrorCorrection::GenerateFEC(const ListWrapper& mediaPacketList,
memcpy(&_generatedFecPackets[i].data[8], mediaPayloadLength, 2);
// Copy RTP payload, leaving room for the ULP header.
memcpy(&_generatedFecPackets[i].data[FecHeaderSize + ulpHeaderSize],
&mediaPacket->data[RtpHeaderSize],
mediaPacket->length - RtpHeaderSize);
memcpy(&_generatedFecPackets[i].data[kFecHeaderSize + ulpHeaderSize],
&mediaPacket->data[kRtpHeaderSize],
mediaPacket->length - kRtpHeaderSize);
}
else
{
@ -252,7 +255,7 @@ ForwardErrorCorrection::GenerateFEC(const ListWrapper& mediaPacketList,
_generatedFecPackets[i].data[9] ^= mediaPayloadLength[1];
// XOR with RTP payload, leaving room for the ULP header.
for (WebRtc_Word32 j = FecHeaderSize + ulpHeaderSize;
for (WebRtc_Word32 j = kFecHeaderSize + ulpHeaderSize;
j < fecPacketLength; j++)
{
_generatedFecPackets[i].data[j] ^=
@ -278,9 +281,12 @@ ForwardErrorCorrection::GenerateFEC(const ListWrapper& mediaPacketList,
if (_generatedFecPackets[i].length == 0)
{
// This will occur in the event of an all-zero mask.
// Ensure we still mark the length of the headers.
_generatedFecPackets[i].length = FecHeaderSize + ulpHeaderSize;
//Note: This shouldn't happen: means packet mask is wrong or poorly designed
WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id,
"Packet mask has row of zeros %d %d",
numMediaPackets, numFecPackets);
return -1;
}
}
@ -330,7 +336,7 @@ ForwardErrorCorrection::GenerateFEC(const ListWrapper& mediaPacketList,
// Copy the payload size to the protection length field.
// (We protect the entire packet.)
ModuleRTPUtility::AssignUWord16ToBuffer(&_generatedFecPackets[i].data[10],
_generatedFecPackets[i].length - FecHeaderSize - ulpHeaderSize);
_generatedFecPackets[i].length - kFecHeaderSize - ulpHeaderSize);
// Copy the packet mask.
memcpy(&_generatedFecPackets[i].data[12], &packetMask[i * numMaskBytes],
@ -517,7 +523,7 @@ ForwardErrorCorrection::DecodeFEC(ListWrapper& receivedPacketList,
// We store this for determining frame completion later.
_seqNumBase = ModuleRTPUtility::BufferToUWord16(&fecPacket->pkt->data[2]);
const WebRtc_UWord16 maskSizeBytes = (fecPacket->pkt->data[0] & 0x40) ?
MaskSizeLBitSet : MaskSizeLBitClear; // L bit set?
kMaskSizeLBitSet : kMaskSizeLBitClear; // L bit set?
for (WebRtc_UWord16 byteIdx = 0; byteIdx < maskSizeBytes; byteIdx++)
{
@ -617,7 +623,7 @@ ForwardErrorCorrection::DecodeFEC(ListWrapper& receivedPacketList,
// Recovery possible.
WebRtc_UWord8 lengthRecovery[2];
const WebRtc_UWord16 ulpHeaderSize = fecPacket->pkt->data[0] & 0x40 ?
UlpHeaderSizeLBitSet : UlpHeaderSizeLBitClear; // L bit set?
kUlpHeaderSizeLBitSet : kUlpHeaderSizeLBitClear; // L bit set?
RecoveredPacket* recPacketToInsert = new RecoveredPacket;
recPacketToInsert->wasRecovered = true;
@ -641,8 +647,8 @@ ForwardErrorCorrection::DecodeFEC(ListWrapper& receivedPacketList,
memcpy(&lengthRecovery, &fecPacket->pkt->data[8], 2);
// Copy FEC payload, skipping the ULP header.
memcpy(&recPacketToInsert->pkt->data[RtpHeaderSize],
&fecPacket->pkt->data[FecHeaderSize + ulpHeaderSize],
memcpy(&recPacketToInsert->pkt->data[kRtpHeaderSize],
&fecPacket->pkt->data[kFecHeaderSize + ulpHeaderSize],
ModuleRTPUtility::BufferToUWord16(protectionLength));
protectedPacketListItem = fecPacket->protectedPktList.First();
@ -672,13 +678,13 @@ ForwardErrorCorrection::DecodeFEC(ListWrapper& receivedPacketList,
// XOR with the network-ordered payload size.
ModuleRTPUtility::AssignUWord16ToBuffer(mediaPayloadLength,
protectedPacket->pkt->length - RtpHeaderSize);
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 (WebRtc_Word32 i = RtpHeaderSize; i < protectedPacket->pkt->length;
for (WebRtc_Word32 i = kRtpHeaderSize; i < protectedPacket->pkt->length;
i++)
{
recPacketToInsert->pkt->data[i] ^= protectedPacket->pkt->data[i];
@ -712,7 +718,7 @@ ForwardErrorCorrection::DecodeFEC(ListWrapper& receivedPacketList,
// Recover the packet length.
recPacketToInsert->pkt->length =
ModuleRTPUtility::BufferToUWord16(lengthRecovery) + RtpHeaderSize;
ModuleRTPUtility::BufferToUWord16(lengthRecovery) + kRtpHeaderSize;
// Insert into recovered list in correct position.
recPacketListItem = recoveredPacketList.Last();
@ -823,6 +829,6 @@ ForwardErrorCorrection::DecodeFEC(ListWrapper& receivedPacketList,
WebRtc_UWord16
ForwardErrorCorrection::PacketOverhead()
{
return FecHeaderSize + UlpHeaderSizeLBitSet;
return kFecHeaderSize + kUlpHeaderSizeLBitSet;
}
} // namespace webrtc

View File

@ -84,27 +84,35 @@ public:
/**
* Destructor. Before freeing an instance of the class, #DecodeFEC() must be called
* in a particular fashion to free oustanding memory. Refer to #DecodeFEC().
* in a particular fashion to free outstanding memory. Refer to #DecodeFEC().
*/
virtual ~ForwardErrorCorrection();
/**
* Generates a list of FEC packets from supplied media packets.
*
* \param[in] mediaPacketList List of media packets to protect, of type #Packet.
* All packets must belong to the same frame and the
* list must not be empty.
* \param[out] fecPacketList List of FEC packets, of type #Packet. Must be empty
* on entry. The memory available through the list
* will be valid until the next call to GenerateFEC().
* \param[in] protectionFactor FEC protection overhead in the [0, 255] domain. To
* obtain 100% overhead, or an equal number of FEC
* packets as media packets, use 255.
* \param[in] mediaPacketList List of media packets to protect, of type #Packet.
* All packets must belong to the same frame and the
* list must not be empty.
* \param[in] protectionFactor FEC protection overhead in the [0, 255] domain. To
* obtain 100% overhead, or an equal number of FEC
* packets as media packets, use 255.
* \param[in] numImportantPackets The number of "important" packets in the frame.
* These packets may receive greater protection than
* the remaining packets. The important packets must
* be located at the start of the media packet list.
* For codecs with data partitioning, the important
* packets may correspond to first partition packets.
* \param[out] fecPacketList List of FEC packets, of type #Packet. Must be empty
* on entry. The memory available through the list
* will be valid until the next call to GenerateFEC().
*
* \return 0 on success, -1 on failure.
*/
WebRtc_Word32 GenerateFEC(const ListWrapper& mediaPacketList, ListWrapper& fecPacketList,
WebRtc_UWord8 protectionFactor);
WebRtc_Word32 GenerateFEC(const ListWrapper& mediaPacketList,
WebRtc_UWord8 protectionFactor,
WebRtc_UWord32 numImportantPackets,
ListWrapper& fecPacketList);
/**
* Decodes a list of media and FEC packets. It will parse the input received packet

View File

@ -0,0 +1,340 @@
/*
* Copyright (c) 2011 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "forward_error_correction_internal.h"
#include "fec_private_tables.h"
#include <cassert>
#include <cstring>
namespace {
// This parameter enables/disables unequal protection (UEP) across packets.
// This is not to be confused with protection within packets (referred to as ULP).
// One use case of UEP across packets is for codecs with data partitioning,
// e.g., VP8, H264 XP profile, where important packets would be first partition.
// TODO (marpan): Pass this parameter from MediaOpt (VCM).
const bool kUseUnequalProtection = true;
// Allow for two different modes of protection for residual packets.
// The residual packets are the remaining packets beyond the important ones.
enum ResidualProtectionMode
{
kModeNoOverlap,
kModeOverlap,
};
/**
* Fits an input mask (subMask) to an output mask.
* The mask is a matrix where the rows are the FEC packets,
* and the columns are the source packets the FEC is applied to.
* Each row of the mask is represented by a number of mask bytes.
*
* \param[in] numMaskBytes The number of mask bytes of output mask.
* \param[in] numSubMaskBytes The number of mask bytes of input mask.
* \param[in] numRows The number of rows of the input mask.
* \param[in] subMask A pointer to hold the input mask, of size
* [0, numRows * numSubMaskBytes]
* \param[out] packetMask A pointer to hold the output mask, of size
* [0, x * numMaskBytes], where x >= numRows.
*/
void FitSubMask(const WebRtc_UWord16 numMaskBytes,
const WebRtc_UWord16 numSubMaskBytes,
const WebRtc_UWord16 numRows,
const WebRtc_UWord8* subMask,
WebRtc_UWord8* packetMask)
{
if (numMaskBytes == numSubMaskBytes)
{
memcpy(packetMask,subMask,
numRows * numSubMaskBytes);
}
else
{
for (WebRtc_UWord32 i = 0; i < numRows; i++)
{
WebRtc_UWord32 pktMaskIdx = i * numMaskBytes;
WebRtc_UWord32 pktMaskIdx2 = i * numSubMaskBytes;
for (WebRtc_UWord32 j = 0; j < numSubMaskBytes; j++)
{
packetMask[pktMaskIdx] = subMask[pktMaskIdx2];
pktMaskIdx++;
pktMaskIdx2++;
}
}
}
}
/**
* Shifts a mask by number of columns (bits), and fits it to an output mask.
* The mask is a matrix where the rows are the FEC packets,
* and the columns are the source packets the FEC is applied to.
* Each row of the mask is represented by a number of mask bytes.
*
* \param[in] numMaskBytes The number of mask bytes of output mask.
* \param[in] numSubMaskBytes The number of mask bytes of input mask.
* \param[in] numColumnShift The number columns to be shifted, and
* the starting row for the output mask.
* \param[in] endRow The ending row for the output mask.
* \param[in] subMask A pointer to hold the input mask, of size
* [0, (endRowFEC - startRowFec) * numSubMaskBytes]
* \param[out] packetMask A pointer to hold the output mask, of size
* [0, x * numMaskBytes], where x >= endRowFEC.
*/
// TODO (marpan): This function is doing three things at the same time:
// shift within a byte, byte shift and resizing.
// Split up into subroutines.
void ShiftFitSubMask(const WebRtc_UWord16 numMaskBytes,
const WebRtc_UWord16 resMaskBytes,
const WebRtc_UWord16 numColumnShift,
const WebRtc_UWord16 endRow,
const WebRtc_UWord8* subMask,
WebRtc_UWord8* packetMask)
{
// Number of bit shifts within a byte
const WebRtc_UWord8 numBitShifts = (numColumnShift % 8);
const WebRtc_UWord8 numByteShifts = numColumnShift >> 3;
// Modify new mask with sub-mask21.
// Loop over the remaining FEC packets.
for (WebRtc_UWord32 i = numColumnShift; i < endRow; i++)
{
// Byte index of new mask, for row i and column resMaskBytes,
// offset by the number of bytes shifts
WebRtc_UWord32 pktMaskIdx = i * numMaskBytes + resMaskBytes - 1
+ numByteShifts;
// Byte index of subMask, for row i and column resMaskBytes
WebRtc_UWord32 pktMaskIdx2 =
(i - numColumnShift) * resMaskBytes + resMaskBytes - 1;
WebRtc_UWord8 shiftRightCurrByte = 0;
WebRtc_UWord8 shiftLeftPrevByte = 0;
WebRtc_UWord8 combNewByte = 0;
// Handle case of numMaskBytes > resMaskBytes:
// For a given row, copy the rightmost "numBitShifts" bits
// of the last byte of subMask into output mask.
if (numMaskBytes > resMaskBytes)
{
shiftLeftPrevByte =
(subMask[pktMaskIdx2] << (8 - numBitShifts));
packetMask[pktMaskIdx + 1] = shiftLeftPrevByte;
}
// For each row i (FEC packet), shift the bit-mask of the subMask.
// Each row of the mask contains "resMaskBytes" of bytes.
// We start from the last byte of the subMask and move to first one.
for (WebRtc_Word32 j = resMaskBytes - 1; j > 0; j--)
{
// Shift current byte of sub21 to the right by "numBitShifts".
shiftRightCurrByte =
subMask[pktMaskIdx2] >> numBitShifts;
// Fill in shifted bits with bits from the previous (left) byte:
// First shift the previous byte to the left by "8-numBitShifts".
shiftLeftPrevByte =
(subMask[pktMaskIdx2 - 1] << (8 - numBitShifts));
// Then combine both shifted bytes into new mask byte.
combNewByte = shiftRightCurrByte | shiftLeftPrevByte;
// Assign to new mask.
packetMask[pktMaskIdx] = combNewByte;
pktMaskIdx--;
pktMaskIdx2--;
}
// For the first byte in the row (j=0 case).
shiftRightCurrByte = subMask[pktMaskIdx2] >> numBitShifts;
packetMask[pktMaskIdx] = shiftRightCurrByte;
}
}
} //namespace
namespace webrtc {
namespace internal {
// Residual protection for remaining packets
void ResidualPacketProtection(const WebRtc_UWord16 numMediaPackets,
const WebRtc_UWord16 numFecPackets,
const WebRtc_UWord16 numImpPackets,
const WebRtc_UWord16 numMaskBytes,
const ResidualProtectionMode mode,
WebRtc_UWord8* packetMask)
{
if (mode == kModeNoOverlap)
{
// subMask21
const WebRtc_UWord8 lBit =
(numMediaPackets - numImpPackets) > 16 ? 1 : 0;
const WebRtc_UWord16 resMaskBytes =
(lBit == 1)? kMaskSizeLBitSet : kMaskSizeLBitClear;
const WebRtc_UWord8* packetMaskSub21 =
packetMaskTbl[numMediaPackets - numImpPackets - 1]
[numFecPackets - numImpPackets - 1];
ShiftFitSubMask(numMaskBytes, resMaskBytes,
numImpPackets, numFecPackets,
packetMaskSub21, packetMask);
}
else if (mode == kModeOverlap)
{
// subMask22
const WebRtc_UWord16 numFecForResidual =
numFecPackets - numImpPackets;
const WebRtc_UWord8* packetMaskSub22 =
packetMaskTbl[numMediaPackets - 1]
[numFecForResidual - 1];
FitSubMask(numMaskBytes, numMaskBytes,
numFecForResidual,
packetMaskSub22,
&packetMask[numImpPackets * numMaskBytes]);
}
else
{
assert(false);
}
}
// Higher protection for numImpPackets
void ImportantPacketProtection(const WebRtc_UWord16 numFecPackets,
const WebRtc_UWord16 numImpPackets,
const WebRtc_UWord16 numMaskBytes,
WebRtc_UWord8* packetMask)
{
const WebRtc_UWord8 lBit = numImpPackets > 16 ? 1 : 0;
const WebRtc_UWord16 numImpMaskBytes =
(lBit == 1)? kMaskSizeLBitSet : kMaskSizeLBitClear;
WebRtc_UWord32 numFecForImpPackets = numImpPackets;
if (numFecPackets < numImpPackets)
{
numFecForImpPackets = numFecPackets;
}
// Get subMask1 from table
const WebRtc_UWord8* packetMaskSub1 =
packetMaskTbl[numImpPackets - 1][numFecForImpPackets - 1];
FitSubMask(numMaskBytes, numImpMaskBytes,
numFecForImpPackets,
packetMaskSub1,
packetMask);
}
// Modification for UEP: reuse the tables (designed for equal protection).
// First version is to build mask from two sub-masks.
// Longer-term, may add another set of tables for UEP cases for more
// flexibility in protection between important and residual packets.
// UEP scheme:
// First subMask is for higher protection for important packets.
// Other subMask is the residual protection for remaining packets.
// Mask is characterized as (#packets_to_protect, #fec_for_protection).
// Protection defined as: (#fec_for_protection / #packets_to_protect).
// So if k = numMediaPackets, n=total#packets, (n-k)=numFecPackets,
// and m=numImpPackets, then we will have the following:
// For important packets:
// subMask1 = (m, t): protection = m/(t), where t=min(m,n-k).
// For the residual protection, we currently have two options:
// Mode 0: subMask21 = (k-m,n-k-m): protection = (n-k-m)/(k-m):
// no protection overlap between the two partitions.
// Mode 1: subMask22 = (k, n-k-m), with protection (n-k-m)/(k):
// some protection overlap between the two partitions.
void UnequalProtectionMask(const WebRtc_UWord16 numMediaPackets,
const WebRtc_UWord16 numFecPackets,
const WebRtc_UWord16 numImpPackets,
const WebRtc_UWord16 numMaskBytes,
const ResidualProtectionMode mode,
WebRtc_UWord8* packetMask)
{
//
// Generate subMask1: higher protection for numImpPackets:
//
ImportantPacketProtection(numFecPackets, numImpPackets,
numMaskBytes, packetMask);
//
// Generate subMask2: left-over protection (for remaining partition data),
// if we still have some some FEC packets
//
if (numFecPackets > numImpPackets)
{
ResidualPacketProtection(numMediaPackets,numFecPackets,
numImpPackets, numMaskBytes,
mode, packetMask);
}
}
void GeneratePacketMasks(const WebRtc_UWord32 numMediaPackets,
const WebRtc_UWord32 numFecPackets,
const WebRtc_UWord32 numImpPackets,
WebRtc_UWord8* packetMask)
{
assert(numMediaPackets <= sizeof(packetMaskTbl)/sizeof(*packetMaskTbl) &&
numMediaPackets > 0);
assert(numFecPackets <= numMediaPackets && numFecPackets > 0);
assert(numImpPackets <= numMediaPackets && numImpPackets >= 0);
WebRtc_UWord8 lBit = numMediaPackets > 16 ? 1 : 0;
const WebRtc_UWord16 numMaskBytes =
(lBit == 1)? kMaskSizeLBitSet : kMaskSizeLBitClear;
// Default: use overlap mode for residual protection.
const ResidualProtectionMode kResidualProtectionMode = kModeOverlap;
// Force equal-protection for these cases.
// Equal protection is also used for: (numImpPackets == 1 && numFecPackets == 1).
// UEP=off would generally be more efficient than the UEP=on for this case.
// TODO (marpan): check/test this condition.
if (!kUseUnequalProtection || numImpPackets == 0 ||
(numImpPackets == 1 && numFecPackets == 1))
{
// Retrieve corresponding mask table directly: for equal-protection case.
// Mask = (k,n-k), with protection factor = (n-k)/k,
// where k = numMediaPackets, n=total#packets, (n-k)=numFecPackets.
memcpy(packetMask, packetMaskTbl[numMediaPackets - 1][numFecPackets - 1],
numFecPackets * numMaskBytes);
}
else //UEP case
{
UnequalProtectionMask(numMediaPackets, numFecPackets, numImpPackets,
numMaskBytes, kResidualProtectionMode,
packetMask);
} // End of UEP modification
} //End of GetPacketMasks
} // namespace internal
} // namespace webrtc

View File

@ -0,0 +1,44 @@
/*
* Copyright (c) 2011 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "typedefs.h"
namespace webrtc {
// Packet mask size in bytes (L bit is set).
const WebRtc_UWord8 kMaskSizeLBitSet = 6;
// Packet mask size in bytes (L bit is cleared).
const WebRtc_UWord8 kMaskSizeLBitClear = 2;
namespace internal {
/**
* Returns an array of packet masks. The mask of a single FEC packet
* corresponds to a number of mask bytes. The mask indicates which
* media packets should be protected by the FEC packet.
* \param[in] numMediaPackets The number of media packets to protect.
* [1, maxMediaPackets].
* \param[in] numFecPackets The number of FEC packets which will be generated.
* [1, numMediaPackets].
* \param[in] numImpPackets The number of important packets.
* [0, numMediaPackets].
* numImpPackets = 0 is the equal protection scenario.
* \param[out] packetMask A pointer to hold the packet mask array, of size
* numFecPackets * "number of mask bytes".
*/
void GeneratePacketMasks(const WebRtc_UWord32 numMediaPackets,
const WebRtc_UWord32 numFecPackets,
const WebRtc_UWord32 numImpPackets,
WebRtc_UWord8* packetMask);
} // namespace internal
} // namespace webrtc

View File

@ -65,6 +65,8 @@
'fec_private_tables.h',
'forward_error_correction.cc',
'forward_error_correction.h',
'forward_error_correction_internal.cc',
'forward_error_correction_internal.h',
'overuse_detector.cc',
'overuse_detector.h',
'h263_information.cc',

View File

@ -42,6 +42,7 @@ RTPSenderVideo::RTPSenderVideo(const WebRtc_Word32 id, RTPSenderInterface* rtpSe
_codeRateKey(0),
_codeRateDelta(0),
_fecProtectionFactor(0),
_numberFirstPartition(0),
// H263
_savedByte(0),
@ -69,6 +70,7 @@ RTPSenderVideo::Init()
_codeRateKey = 0;
_codeRateDelta = 0;
_fecProtectionFactor = 0;
_numberFirstPartition = 0;
return 0;
}
@ -176,7 +178,8 @@ RTPSenderVideo::SendVideoPacket(const FrameType frameType,
lastMediaRtpHeader.data[1] = _payloadTypeRED; // Replace payload and clear
// marker bit.
retVal = _fec.GenerateFEC(_mediaPacketListFec, fecPacketList,_fecProtectionFactor);
retVal = _fec.GenerateFEC(_mediaPacketListFec, _fecProtectionFactor,
_numberFirstPartition, fecPacketList);
while(!_rtpPacketListFec.Empty())
{
WebRtc_UWord8 newDataBuffer[IP_PACKET_SIZE];
@ -347,6 +350,10 @@ RTPSenderVideo::SendVideo(const RtpVideoCodecTypes videoType,
_fecProtectionFactor = _codeRateDelta;
}
// Default setting for number of first partition packets:
// Will be extracted in SendVP8 for VP8 codec; other codecs use 0
_numberFirstPartition = 0;
WebRtc_Word32 retVal = -1;
switch(videoType)
{
@ -1123,6 +1130,9 @@ RTPSenderVideo::SendVP8(const FrameType frameType,
_rtpSender.BuildRTPheader(dataBuffer, payloadType, last,
captureTimeStamp);
// TODO (marpan): Set _numberFirstPartition here:
// Equal to the first packet that contains last fragment of first partition
if (-1 == SendVideoPacket(frameType, dataBuffer, payloadBytesInPacket,
rtpHeaderLength))
{

View File

@ -152,8 +152,9 @@ private:
WebRtc_UWord8 _codeRateKey;
WebRtc_UWord8 _codeRateDelta;
WebRtc_UWord8 _fecProtectionFactor;
ListWrapper _mediaPacketListFec;
ListWrapper _rtpPacketListFec;
WebRtc_UWord32 _numberFirstPartition;
ListWrapper _mediaPacketListFec;
ListWrapper _rtpPacketListFec;
// H263
WebRtc_UWord8 _savedByte;

View File

@ -1,508 +0,0 @@
/*
* Copyright (c) 2011 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
/**
* \file testFec.cpp
* Test application for core FEC algorithm. Calls encoding and decoding functions in
* ForwardErrorCorrection directly.
*
*/
#include "forward_error_correction.h"
#include "list_wrapper.h"
#include "rtp_utility.h"
#include <cstdio>
#include <cstring>
#include <cassert>
#include <cstdlib>
#include <ctime>
#include <windows.h>
//#include "vld.h"
#include "fec_private_tables.h"
//#define VERBOSE_OUTPUT
void ReceivePackets(ListWrapper& toDecodeList, ListWrapper& receivedPacketList,
WebRtc_UWord32 numPacketsToDecode, float reorderRate, float duplicateRate)
{
assert(toDecodeList.Empty());
assert(numPacketsToDecode <= receivedPacketList.GetSize());
ListItem* listItem = receivedPacketList.First();
for (WebRtc_UWord32 i = 0; i < numPacketsToDecode; i++)
{
// Reorder packets.
float randomVariable = static_cast<float>(rand()) / RAND_MAX;
while (randomVariable < reorderRate)
{
ListItem* nextItem = receivedPacketList.Next(listItem);
if (nextItem == NULL)
{
break;
}
else
{
listItem = nextItem;
}
randomVariable = static_cast<float>(rand()) / RAND_MAX;
}
assert(listItem != NULL);
ForwardErrorCorrection::ReceivedPacket* receivedPacket =
static_cast<ForwardErrorCorrection::ReceivedPacket*>(listItem->GetItem());
toDecodeList.PushBack(receivedPacket);
// Duplicate packets.
randomVariable = static_cast<float>(rand()) / RAND_MAX;
while (randomVariable < duplicateRate)
{
ForwardErrorCorrection::ReceivedPacket* duplicatePacket =
new ForwardErrorCorrection::ReceivedPacket;
memcpy(duplicatePacket, receivedPacket,
sizeof(ForwardErrorCorrection::ReceivedPacket));
duplicatePacket->pkt = new ForwardErrorCorrection::Packet;
memcpy(duplicatePacket->pkt->data, receivedPacket->pkt->data,
receivedPacket->pkt->length);
duplicatePacket->pkt->length = receivedPacket->pkt->length;
toDecodeList.PushBack(duplicatePacket);
randomVariable = static_cast<float>(rand()) / RAND_MAX;
}
receivedPacketList.Erase(listItem);
listItem = receivedPacketList.First();
}
}
int main()
{
enum { MaxNumberMediaPackets = 48 };
enum { MaxNumberFecPackets = 48 };
WebRtc_UWord32 id = 0;
ForwardErrorCorrection fec(id);
ListWrapper mediaPacketList;
ListWrapper fecPacketList;
ListWrapper toDecodeList;
ListWrapper receivedPacketList;
ListWrapper recoveredPacketList;
ListWrapper fecMaskList;
ForwardErrorCorrection::Packet* mediaPacket;
const float lossRate[] = {0, 0.05f, 0.1f, 0.25f, 0.5f, 0.75f, 0.9f};
const WebRtc_UWord32 lossRateSize = sizeof(lossRate)/sizeof(*lossRate);
const float reorderRate = 0.1f;
const float duplicateRate = 0.1f;
WebRtc_UWord8 mediaLossMask[MaxNumberMediaPackets];
WebRtc_UWord8 fecLossMask[MaxNumberFecPackets];
WebRtc_UWord8 fecPacketMasks[MaxNumberFecPackets][MaxNumberMediaPackets];
// Seed the random number generator, storing the seed to file in order to reproduce
// past results.
const unsigned int randomSeed = static_cast<unsigned int>(time(NULL));
srand(randomSeed);
FILE* randomSeedFile = fopen("randomSeedLog.txt", "a");
fprintf(randomSeedFile, "%u\n", randomSeed);
fclose(randomSeedFile);
randomSeedFile = NULL;
WebRtc_UWord16 seqNum = static_cast<WebRtc_UWord16>(rand());
WebRtc_UWord32 timeStamp = static_cast<WebRtc_UWord32>(rand());
const WebRtc_UWord32 ssrc = static_cast<WebRtc_UWord32>(rand());
for (WebRtc_UWord32 lossRateIdx = 0; lossRateIdx < lossRateSize; lossRateIdx++)
{
printf("Loss rate: %.2f\n", lossRate[lossRateIdx]);
for (WebRtc_UWord32 numMediaPackets = 1; numMediaPackets <= MaxNumberMediaPackets;
numMediaPackets++)
{
for (WebRtc_UWord32 numFecPackets = 1; numFecPackets <= numMediaPackets &&
numFecPackets <= MaxNumberFecPackets; numFecPackets++)
{
#ifdef VERBOSE_OUTPUT
printf("%u media packets, %u FEC packets\n", numMediaPackets, numFecPackets);
printf("Packet mask matrix:\n");
#endif
// Transfer packet masks from bit-mask to byte-mask.
const WebRtc_UWord8* packetMask = packetMaskTbl[numMediaPackets - 1][numFecPackets - 1];
WebRtc_UWord32 maskBytesPerFecPacket = 2;
if (numMediaPackets > 16)
{
maskBytesPerFecPacket = 6;
}
for (WebRtc_UWord32 i = 0; i < numFecPackets; i++)
{
for (WebRtc_UWord32 j = 0; j < numMediaPackets; j++)
{
const WebRtc_UWord8 byteMask = packetMask[i * maskBytesPerFecPacket + j / 8];
const WebRtc_UWord32 bitPosition = (7 - j % 8);
fecPacketMasks[i][j] = (byteMask & (1 << bitPosition)) >> bitPosition;
#ifdef VERBOSE_OUTPUT
printf("%u ", fecPacketMasks[i][j]);
#endif
}
#ifdef VERBOSE_OUTPUT
printf("\n");
#endif
}
#ifdef VERBOSE_OUTPUT
printf("\n");
#endif
// Construct media packets.
for (WebRtc_UWord32 i = 0; i < numMediaPackets; i++)
{
mediaPacket = new ForwardErrorCorrection::Packet;
mediaPacketList.PushBack(mediaPacket);
mediaPacket->length = static_cast<WebRtc_UWord16>((static_cast<float>(rand()) /
RAND_MAX) * (IP_PACKET_SIZE - 12 - 28 -
ForwardErrorCorrection::PacketOverhead()));
if (mediaPacket->length < 12)
{
mediaPacket->length = 12;
}
// Set the RTP version to 2.
mediaPacket->data[0] |= 0x80; // Set the 1st bit.
mediaPacket->data[0] &= 0xbf; // Clear the 2nd bit.
mediaPacket->data[1] &= 0x7f; // Clear marker bit.
ModuleRTPUtility::AssignUWord16ToBuffer(&mediaPacket->data[2], seqNum);
ModuleRTPUtility::AssignUWord32ToBuffer(&mediaPacket->data[4], timeStamp);
ModuleRTPUtility::AssignUWord32ToBuffer(&mediaPacket->data[8], ssrc);
for (WebRtc_Word32 j = 12; j < mediaPacket->length; j++)
{
mediaPacket->data[j] = static_cast<WebRtc_UWord8>((static_cast<float>(rand()) /
RAND_MAX) * 255);
}
seqNum++;
}
mediaPacket->data[1] |= 0x80; // Set the marker bit of the last packet.
WebRtc_UWord8 protectionFactor = static_cast<WebRtc_UWord8>(numFecPackets * 255 / numMediaPackets);
if (fec.GenerateFEC(mediaPacketList, fecPacketList, protectionFactor) != 0)
{
printf("Error: GenerateFEC() failed\n");
return -1;
}
if (fecPacketList.GetSize() != numFecPackets)
{
printf("Error: we requested %u FEC packets, but GenerateFEC() produced %u\n",
numFecPackets, fecPacketList.GetSize());
return -1;
}
memset(mediaLossMask, 0, sizeof(mediaLossMask));
ListItem* mediaPacketListItem = mediaPacketList.First();
ForwardErrorCorrection::ReceivedPacket* receivedPacket;
WebRtc_UWord32 mediaPacketIdx = 0;
while (mediaPacketListItem != NULL)
{
mediaPacket = static_cast<ForwardErrorCorrection::Packet*>
(mediaPacketListItem->GetItem());
const float lossRandomVariable = (static_cast<float>(rand()) /
(RAND_MAX + 1)); // +1 to get [0, 1)
if (lossRandomVariable >= lossRate[lossRateIdx])
{
mediaLossMask[mediaPacketIdx] = 1;
receivedPacket = new ForwardErrorCorrection::ReceivedPacket;
receivedPacket->pkt = new ForwardErrorCorrection::Packet;
receivedPacketList.PushBack(receivedPacket);
receivedPacket->pkt->length = mediaPacket->length;
memcpy(receivedPacket->pkt->data, mediaPacket->data, mediaPacket->length);
receivedPacket->seqNum =
ModuleRTPUtility::BufferToUWord16(&mediaPacket->data[2]);
receivedPacket->isFec = false;
receivedPacket->lastMediaPktInFrame = mediaPacket->data[1] & 0x80 ?
true : false; // Check for marker bit.
}
mediaPacketIdx++;
mediaPacketListItem = mediaPacketList.Next(mediaPacketListItem);
}
memset(fecLossMask, 0, sizeof(fecLossMask));
ListItem* fecPacketListItem = fecPacketList.First();
ForwardErrorCorrection::Packet* fecPacket;
WebRtc_UWord32 fecPacketIdx = 0;
while (fecPacketListItem != NULL)
{
fecPacket = static_cast<ForwardErrorCorrection::Packet*>
(fecPacketListItem->GetItem());
const float lossRandomVariable = (static_cast<float>(rand()) /
(RAND_MAX + 1)); // +1 to get [0, 1)
if (lossRandomVariable >= lossRate[lossRateIdx])
{
fecLossMask[fecPacketIdx] = 1;
receivedPacket = new ForwardErrorCorrection::ReceivedPacket;
receivedPacket->pkt = new ForwardErrorCorrection::Packet;
receivedPacketList.PushBack(receivedPacket);
receivedPacket->pkt->length = fecPacket->length;
memcpy(receivedPacket->pkt->data, fecPacket->data, fecPacket->length);
receivedPacket->seqNum = seqNum;
receivedPacket->isFec = true;
receivedPacket->lastMediaPktInFrame = false;
receivedPacket->ssrc = ssrc;
fecMaskList.PushBack(fecPacketMasks[fecPacketIdx]);
}
fecPacketIdx++;
seqNum++;
fecPacketListItem = fecPacketList.Next(fecPacketListItem);
}
#ifdef VERBOSE_OUTPUT
printf("Media loss mask:\n");
for (WebRtc_UWord32 i = 0; i < numMediaPackets; i++)
{
printf("%u ", mediaLossMask[i]);
}
printf("\n\n");
printf("FEC loss mask:\n");
for (WebRtc_UWord32 i = 0; i < numFecPackets; i++)
{
printf("%u ", fecLossMask[i]);
}
printf("\n\n");
#endif
ListItem* listItem = fecMaskList.First();
WebRtc_UWord8* fecMask;
while (listItem != NULL)
{
fecMask = static_cast<WebRtc_UWord8*>(listItem->GetItem());
WebRtc_UWord32 hammingDist = 0;
WebRtc_UWord32 recoveryPosition = 0;
for (WebRtc_UWord32 i = 0; i < numMediaPackets; i++)
{
if (mediaLossMask[i] == 0 && fecMask[i] == 1)
{
recoveryPosition = i;
hammingDist++;
}
}
ListItem* itemToDelete = listItem;
listItem = fecMaskList.Next(listItem);
if (hammingDist == 1)
{
// Recovery possible. Restart search.
mediaLossMask[recoveryPosition] = 1;
listItem = fecMaskList.First();
}
else if (hammingDist == 0)
{
// FEC packet cannot provide further recovery.
fecMaskList.Erase(itemToDelete);
}
}
#ifdef VERBOSE_OUTPUT
printf("Recovery mask:\n");
for (WebRtc_UWord32 i = 0; i < numMediaPackets; i++)
{
printf("%u ", mediaLossMask[i]);
}
printf("\n\n");
#endif
bool complete = true; // Marks start of new frame.
bool fecPacketReceived = false; // For error-checking frame completion.
while (!receivedPacketList.Empty())
{
WebRtc_UWord32 numPacketsToDecode = static_cast<WebRtc_UWord32>
((static_cast<float>(rand()) / RAND_MAX) * receivedPacketList.GetSize() + 0.5);
if (numPacketsToDecode < 1)
{
numPacketsToDecode = 1;
}
ReceivePackets(toDecodeList, receivedPacketList, numPacketsToDecode,
reorderRate, duplicateRate);
if (fecPacketReceived == false)
{
listItem = toDecodeList.First();
while (listItem != NULL)
{
receivedPacket =
static_cast<ForwardErrorCorrection::ReceivedPacket*>
(listItem->GetItem());
if (receivedPacket->isFec)
{
fecPacketReceived = true;
}
listItem = toDecodeList.Next(listItem);
}
}
if (fec.DecodeFEC(toDecodeList, recoveredPacketList, seqNum, complete) != 0)
{
printf("Error: DecodeFEC() failed\n");
return -1;
}
if (!toDecodeList.Empty())
{
printf("Error: received packet list is not empty\n");
return -1;
}
if (recoveredPacketList.GetSize() == 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 == true)
{
printf("Error: it should not be possible to verify full frame recovery,"
" but complete parameter was set to true\n");
return -1;
}
}
}
mediaPacketListItem = mediaPacketList.First();
mediaPacketIdx = 0;
while (mediaPacketListItem != NULL)
{
if (mediaLossMask[mediaPacketIdx] == 1)
{
// Should have recovered this packet.
ListItem* recoveredPacketListItem = recoveredPacketList.First();
mediaPacket = static_cast<ForwardErrorCorrection::Packet*>
(mediaPacketListItem->GetItem());
if (recoveredPacketListItem == NULL)
{
printf("Error: insufficient number of recovered packets.\n");
return -1;
}
ForwardErrorCorrection::RecoveredPacket* recoveredPacket =
static_cast<ForwardErrorCorrection::RecoveredPacket*>
(recoveredPacketListItem->GetItem());
if (recoveredPacket->pkt->length != mediaPacket->length)
{
printf("Error: recovered packet length not identical to original media packet\n");
return -1;
}
if (memcmp(recoveredPacket->pkt->data, mediaPacket->data,
mediaPacket->length) != 0)
{
printf("Error: recovered packet payload not identical to original media packet\n");
return -1;
}
delete recoveredPacket->pkt;
delete recoveredPacket;
recoveredPacket = NULL;
recoveredPacketList.PopFront();
}
mediaPacketIdx++;
mediaPacketListItem = mediaPacketList.Next(mediaPacketListItem);
}
if (!recoveredPacketList.Empty())
{
printf("Error: excessive number of recovered packets.\n");
return -1;
}
// -- Teardown --
mediaPacketListItem = mediaPacketList.First();
while (mediaPacketListItem != NULL)
{
delete static_cast<ForwardErrorCorrection::Packet*>
(mediaPacketListItem->GetItem());
mediaPacketListItem = mediaPacketList.Next(mediaPacketListItem);
mediaPacketList.PopFront();
}
assert(mediaPacketList.Empty());
fecPacketListItem = fecPacketList.First();
while (fecPacketListItem != NULL)
{
fecPacketListItem = fecPacketList.Next(fecPacketListItem);
fecPacketList.PopFront();
}
// Delete received packets we didn't pass to DecodeFEC(), due to early
// frame completion.
listItem = receivedPacketList.First();
while (listItem != NULL)
{
receivedPacket =
static_cast<ForwardErrorCorrection::ReceivedPacket*>
(listItem->GetItem());
delete receivedPacket->pkt;
delete receivedPacket;
receivedPacket = NULL;
listItem = receivedPacketList.Next(listItem);
receivedPacketList.PopFront();
}
assert(receivedPacketList.Empty());
while (fecMaskList.First() != NULL)
{
fecMaskList.PopFront();
}
timeStamp += 90000 / 30;
}
}
}
// Have DecodeFEC free allocated memory.
bool complete = true;
fec.DecodeFEC(receivedPacketList, recoveredPacketList, seqNum, complete);
if (!recoveredPacketList.Empty())
{
printf("Error: recovered packet list is not empty\n");
return -1;
}
printf("\nAll tests passed successfully\n");
Sleep(5000);
return 0;
}

View File

@ -0,0 +1,569 @@
/**
* Test application for core FEC algorithm. Calls encoding and decoding functions in
* ForwardErrorCorrection directly.
*
*/
#include <cassert>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include "forward_error_correction.h"
#include "forward_error_correction_internal.h"
#include "list_wrapper.h"
#include "rtp_utility.h"
#define VERBOSE_OUTPUT
void ReceivePackets(webrtc::ListWrapper& toDecodeList, webrtc::ListWrapper& receivedPacketList,
WebRtc_UWord32 numPacketsToDecode, float reorderRate, float duplicateRate);
int main()
{
enum { kMaxNumberMediaPackets = 48 };
enum { kMaxNumberFecPackets = 48 };
// Use same values as set in forward_correction.cc
const WebRtc_UWord8 rtpHeaderSize = 12;
const bool kUEP = true;
const WebRtc_UWord32 kForceFecThr = 1;
WebRtc_UWord32 id = 0;
webrtc::ForwardErrorCorrection fec(id);
// FOR UEP test
WebRtc_UWord32 numImpPackets = 0;
webrtc::ListWrapper mediaPacketList;
webrtc::ListWrapper fecPacketList;
webrtc::ListWrapper toDecodeList;
webrtc::ListWrapper receivedPacketList;
webrtc::ListWrapper recoveredPacketList;
webrtc::ListWrapper fecMaskList;
webrtc::ForwardErrorCorrection::Packet* mediaPacket;
const float lossRate[] = {0, 0.05f, 0.1f, 0.25f, 0.5f, 0.75f, 0.9f};
const WebRtc_UWord32 lossRateSize = sizeof(lossRate)/sizeof(*lossRate);
const float reorderRate = 0.1f;
const float duplicateRate = 0.1f;
WebRtc_UWord8 mediaLossMask[kMaxNumberMediaPackets];
WebRtc_UWord8 fecLossMask[kMaxNumberFecPackets];
WebRtc_UWord8 fecPacketMasks[kMaxNumberFecPackets][kMaxNumberMediaPackets];
// Seed the random number generator, storing the seed to file in order to reproduce
// past results.
const unsigned int randomSeed = static_cast<unsigned int>(time(NULL));
srand(randomSeed);
FILE* randomSeedFile = fopen("randomSeedLog.txt", "a");
fprintf(randomSeedFile, "%u\n", randomSeed);
fclose(randomSeedFile);
randomSeedFile = NULL;
WebRtc_UWord16 seqNum = static_cast<WebRtc_UWord16>(rand());
WebRtc_UWord32 timeStamp = static_cast<WebRtc_UWord32>(rand());
const WebRtc_UWord32 ssrc = static_cast<WebRtc_UWord32>(rand());
for (WebRtc_UWord32 lossRateIdx = 0; lossRateIdx < lossRateSize; lossRateIdx++)
{
printf("Loss rate: %.2f\n", lossRate[lossRateIdx]);
for (WebRtc_UWord32 numMediaPackets = 1; numMediaPackets <= kMaxNumberMediaPackets;
numMediaPackets++)
{
for (WebRtc_UWord32 numFecPackets = 1; numFecPackets <= numMediaPackets &&
numFecPackets <= kMaxNumberFecPackets; numFecPackets++)
{
// loop over all possible numImpPackets
for (WebRtc_UWord32 numImpPackets = 0; numImpPackets <= numMediaPackets &&
numImpPackets <= kMaxNumberMediaPackets; numImpPackets++)
{
WebRtc_UWord8 protectionFactor = static_cast<WebRtc_UWord8>
(numFecPackets * 255 / numMediaPackets);
WebRtc_UWord32 maskBytesPerFecPacket = 2;
if (numMediaPackets > 16)
{
maskBytesPerFecPacket = 6;
}
// Transfer packet masks from bit-mask to byte-mask.
WebRtc_UWord8 packetMask[numFecPackets * maskBytesPerFecPacket];
memset(packetMask, 0, numFecPackets * maskBytesPerFecPacket);
webrtc::internal::GeneratePacketMasks(numMediaPackets,numFecPackets,
numImpPackets, packetMask);
#ifdef VERBOSE_OUTPUT
printf("%u media packets, %u FEC packets, %u numImpPackets, "
"loss rate = %.2f \n",
numMediaPackets, numFecPackets, numImpPackets, lossRate[lossRateIdx]);
printf("Packet mask matrix \n");
#endif
for (WebRtc_UWord32 i = 0; i < numFecPackets; i++)
{
for (WebRtc_UWord32 j = 0; j < numMediaPackets; j++)
{
const WebRtc_UWord8 byteMask =
packetMask[i * maskBytesPerFecPacket + j / 8];
const WebRtc_UWord32 bitPosition = (7 - j % 8);
fecPacketMasks[i][j] =
(byteMask & (1 << bitPosition)) >> bitPosition;
#ifdef VERBOSE_OUTPUT
printf("%u ", fecPacketMasks[i][j]);
#endif
}
#ifdef VERBOSE_OUTPUT
printf("\n");
#endif
}
#ifdef VERBOSE_OUTPUT
printf("\n");
#endif
// Check for all zero rows or columns: indicates incorrect mask
WebRtc_UWord32 rowLimit = numMediaPackets;
if (numFecPackets <= numImpPackets && kUEP == true)
{
rowLimit = numImpPackets;
}
for (WebRtc_UWord32 i = 0; i < numFecPackets; i++)
{
WebRtc_UWord32 rowSum = 0;
for (WebRtc_UWord32 j = 0; j < rowLimit; j++)
{
rowSum += fecPacketMasks[i][j];
}
if (rowSum == 0)
{
printf("ERROR: row is all zero %d \n",i);
return -1;
}
}
for (WebRtc_UWord32 j = 0; j < rowLimit; j++)
{
WebRtc_UWord32 columnSum = 0;
for (WebRtc_UWord32 i = 0; i < numFecPackets; i++)
{
columnSum += fecPacketMasks[i][j];
}
if (columnSum == 0)
{
printf("ERROR: column is all zero %d \n",j);
return -1;
}
}
// Construct media packets.
for (WebRtc_UWord32 i = 0; i < numMediaPackets; i++)
{
mediaPacket = new webrtc::ForwardErrorCorrection::Packet;
mediaPacketList.PushBack(mediaPacket);
mediaPacket->length =
static_cast<WebRtc_UWord16>((static_cast<float>(rand()) /
RAND_MAX) * (IP_PACKET_SIZE - 12 - 28 -
webrtc::ForwardErrorCorrection::PacketOverhead()));
if (mediaPacket->length < 12)
{
mediaPacket->length = 12;
}
// Set the RTP version to 2.
mediaPacket->data[0] |= 0x80; // Set the 1st bit.
mediaPacket->data[0] &= 0xbf; // Clear the 2nd bit.
mediaPacket->data[1] &= 0x7f; // Clear marker bit.
webrtc::ModuleRTPUtility::AssignUWord16ToBuffer(&mediaPacket->data[2],
seqNum);
webrtc::ModuleRTPUtility::AssignUWord32ToBuffer(&mediaPacket->data[4],
timeStamp);
webrtc::ModuleRTPUtility::AssignUWord32ToBuffer(&mediaPacket->data[8],
ssrc);
for (WebRtc_Word32 j = 12; j < mediaPacket->length; j++)
{
mediaPacket->data[j] =
static_cast<WebRtc_UWord8>((static_cast<float>(rand()) /
RAND_MAX) * 255);
}
seqNum++;
}
mediaPacket->data[1] |= 0x80; // Set the marker bit of the last packet.
if (fec.GenerateFEC(mediaPacketList, protectionFactor, numImpPackets,
fecPacketList) != 0)
{
printf("Error: GenerateFEC() failed\n");
return -1;
}
if (fecPacketList.GetSize() != numFecPackets)
{
printf("Error: we requested %u FEC packets, "
"but GenerateFEC() produced %u\n",
numFecPackets, fecPacketList.GetSize());
return -1;
}
memset(mediaLossMask, 0, sizeof(mediaLossMask));
webrtc::ListItem* mediaPacketListItem = mediaPacketList.First();
webrtc::ForwardErrorCorrection::ReceivedPacket* receivedPacket;
WebRtc_UWord32 mediaPacketIdx = 0;
while (mediaPacketListItem != NULL)
{
mediaPacket = static_cast<webrtc::ForwardErrorCorrection::Packet*>
(mediaPacketListItem->GetItem());
const float lossRandomVariable = (static_cast<float>(rand()) /
(RAND_MAX));
if (lossRandomVariable >= lossRate[lossRateIdx])
{
mediaLossMask[mediaPacketIdx] = 1;
receivedPacket =
new webrtc::ForwardErrorCorrection::ReceivedPacket;
receivedPacket->pkt =
new webrtc::ForwardErrorCorrection::Packet;
receivedPacketList.PushBack(receivedPacket);
receivedPacket->pkt->length = mediaPacket->length;
memcpy(receivedPacket->pkt->data, mediaPacket->data,
mediaPacket->length);
receivedPacket->seqNum =
webrtc::ModuleRTPUtility::BufferToUWord16(&mediaPacket->data[2]);
receivedPacket->isFec = false;
receivedPacket->lastMediaPktInFrame =
mediaPacket->data[1] & 0x80;
}
mediaPacketIdx++;
mediaPacketListItem = mediaPacketList.Next(mediaPacketListItem);
}
memset(fecLossMask, 0, sizeof(fecLossMask));
webrtc::ListItem* fecPacketListItem = fecPacketList.First();
webrtc::ForwardErrorCorrection::Packet* fecPacket;
WebRtc_UWord32 fecPacketIdx = 0;
while (fecPacketListItem != NULL)
{
fecPacket = static_cast<webrtc::ForwardErrorCorrection::Packet*>
(fecPacketListItem->GetItem());
const float lossRandomVariable =
(static_cast<float>(rand()) / (RAND_MAX));
if (lossRandomVariable >= lossRate[lossRateIdx])
{
fecLossMask[fecPacketIdx] = 1;
receivedPacket =
new webrtc::ForwardErrorCorrection::ReceivedPacket;
receivedPacket->pkt =
new webrtc::ForwardErrorCorrection::Packet;
receivedPacketList.PushBack(receivedPacket);
receivedPacket->pkt->length = fecPacket->length;
memcpy(receivedPacket->pkt->data, fecPacket->data,
fecPacket->length);
receivedPacket->seqNum = seqNum;
receivedPacket->isFec = true;
receivedPacket->lastMediaPktInFrame = false;
receivedPacket->ssrc = ssrc;
fecMaskList.PushBack(fecPacketMasks[fecPacketIdx]);
}
fecPacketIdx++;
seqNum++;
fecPacketListItem = fecPacketList.Next(fecPacketListItem);
}
#ifdef VERBOSE_OUTPUT
printf("Media loss mask:\n");
for (WebRtc_UWord32 i = 0; i < numMediaPackets; i++)
{
printf("%u ", mediaLossMask[i]);
}
printf("\n\n");
printf("FEC loss mask:\n");
for (WebRtc_UWord32 i = 0; i < numFecPackets; i++)
{
printf("%u ", fecLossMask[i]);
}
printf("\n\n");
#endif
webrtc::ListItem* listItem = fecMaskList.First();
WebRtc_UWord8* fecMask;
while (listItem != NULL)
{
fecMask = static_cast<WebRtc_UWord8*>(listItem->GetItem());
WebRtc_UWord32 hammingDist = 0;
WebRtc_UWord32 recoveryPosition = 0;
for (WebRtc_UWord32 i = 0; i < numMediaPackets; i++)
{
if (mediaLossMask[i] == 0 && fecMask[i] == 1)
{
recoveryPosition = i;
hammingDist++;
}
}
webrtc::ListItem* itemToDelete = listItem;
listItem = fecMaskList.Next(listItem);
if (hammingDist == 1)
{
// Recovery possible. Restart search.
mediaLossMask[recoveryPosition] = 1;
listItem = fecMaskList.First();
}
else if (hammingDist == 0)
{
// FEC packet cannot provide further recovery.
fecMaskList.Erase(itemToDelete);
}
}
#ifdef VERBOSE_OUTPUT
printf("Recovery mask:\n");
for (WebRtc_UWord32 i = 0; i < numMediaPackets; i++)
{
printf("%u ", mediaLossMask[i]);
}
printf("\n\n");
#endif
bool complete = true; // Marks start of new frame.
bool fecPacketReceived = false; // For error-checking frame completion.
while (!receivedPacketList.Empty())
{
WebRtc_UWord32 numPacketsToDecode = static_cast<WebRtc_UWord32>
((static_cast<float>(rand()) / RAND_MAX) *
receivedPacketList.GetSize() + 0.5);
if (numPacketsToDecode < 1)
{
numPacketsToDecode = 1;
}
ReceivePackets(toDecodeList, receivedPacketList, numPacketsToDecode,
reorderRate, duplicateRate);
if (fecPacketReceived == false)
{
listItem = toDecodeList.First();
while (listItem != NULL)
{
receivedPacket =
static_cast<webrtc::ForwardErrorCorrection::
ReceivedPacket*>(listItem->GetItem());
if (receivedPacket->isFec)
{
fecPacketReceived = true;
}
listItem = toDecodeList.Next(listItem);
}
}
if (fec.DecodeFEC(toDecodeList, recoveredPacketList, seqNum,
complete) != 0)
{
printf("Error: DecodeFEC() failed\n");
return -1;
}
if (!toDecodeList.Empty())
{
printf("Error: received packet list is not empty\n");
return -1;
}
if (recoveredPacketList.GetSize() == 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 == true)
{
printf("Error: "
"it should not be possible to verify full frame recovery,"
" but complete parameter was set to true\n");
return -1;
}
}
}
mediaPacketListItem = mediaPacketList.First();
mediaPacketIdx = 0;
while (mediaPacketListItem != NULL)
{
if (mediaLossMask[mediaPacketIdx] == 1)
{
// Should have recovered this packet.
webrtc::ListItem* recoveredPacketListItem =
recoveredPacketList.First();
mediaPacket =
static_cast<webrtc::ForwardErrorCorrection::Packet*>
(mediaPacketListItem->GetItem());
if (recoveredPacketListItem == NULL)
{
printf("Error: insufficient number of recovered packets.\n");
return -1;
}
webrtc::ForwardErrorCorrection::RecoveredPacket* recoveredPacket =
static_cast<webrtc::ForwardErrorCorrection::RecoveredPacket*>
(recoveredPacketListItem->GetItem());
if (recoveredPacket->pkt->length != mediaPacket->length)
{
printf("Error: recovered packet length not identical to "
"original media packet\n");
return -1;
}
if (memcmp(recoveredPacket->pkt->data, mediaPacket->data,
mediaPacket->length) != 0)
{
printf("Error: recovered packet payload not identical to "
"original media packet\n");
return -1;
}
delete recoveredPacket->pkt;
delete recoveredPacket;
recoveredPacket = NULL;
recoveredPacketList.PopFront();
}
mediaPacketIdx++;
mediaPacketListItem = mediaPacketList.Next(mediaPacketListItem);
}
if (!recoveredPacketList.Empty())
{
printf("Error: excessive number of recovered packets.\n");
return -1;
}
// -- Teardown --
mediaPacketListItem = mediaPacketList.First();
while (mediaPacketListItem != NULL)
{
delete static_cast<webrtc::ForwardErrorCorrection::Packet*>
(mediaPacketListItem->GetItem());
mediaPacketListItem = mediaPacketList.Next(mediaPacketListItem);
mediaPacketList.PopFront();
}
assert(mediaPacketList.Empty());
fecPacketListItem = fecPacketList.First();
while (fecPacketListItem != NULL)
{
fecPacketListItem = fecPacketList.Next(fecPacketListItem);
fecPacketList.PopFront();
}
// Delete received packets we didn't pass to DecodeFEC(), due to early
// frame completion.
listItem = receivedPacketList.First();
while (listItem != NULL)
{
receivedPacket =
static_cast<webrtc::ForwardErrorCorrection::ReceivedPacket*>
(listItem->GetItem());
delete receivedPacket->pkt;
delete receivedPacket;
receivedPacket = NULL;
listItem = receivedPacketList.Next(listItem);
receivedPacketList.PopFront();
}
assert(receivedPacketList.Empty());
while (fecMaskList.First() != NULL)
{
fecMaskList.PopFront();
}
timeStamp += 90000 / 30;
} //loop over numImpPackets
} //loop over FecPackets
} //loop over numMediaPackets
} // loop over loss rates
// Have DecodeFEC free allocated memory.
bool complete = true;
fec.DecodeFEC(receivedPacketList, recoveredPacketList, seqNum, complete);
if (!recoveredPacketList.Empty())
{
printf("Error: recovered packet list is not empty\n");
return -1;
}
printf("\nAll tests passed successfully\n");
return 0;
}
void ReceivePackets(webrtc::ListWrapper& toDecodeList, webrtc::ListWrapper& receivedPacketList,
WebRtc_UWord32 numPacketsToDecode, float reorderRate, float duplicateRate)
{
assert(toDecodeList.Empty());
assert(numPacketsToDecode <= receivedPacketList.GetSize());
webrtc::ListItem* listItem = receivedPacketList.First();
for (WebRtc_UWord32 i = 0; i < numPacketsToDecode; i++)
{
// Reorder packets.
float randomVariable = static_cast<float>(rand()) / RAND_MAX;
while (randomVariable < reorderRate)
{
webrtc::ListItem* nextItem = receivedPacketList.Next(listItem);
if (nextItem == NULL)
{
break;
}
else
{
listItem = nextItem;
}
randomVariable = static_cast<float>(rand()) / RAND_MAX;
}
assert(listItem != NULL);
webrtc::ForwardErrorCorrection::ReceivedPacket* receivedPacket =
static_cast<webrtc::ForwardErrorCorrection::ReceivedPacket*>(listItem->GetItem());
toDecodeList.PushBack(receivedPacket);
// Duplicate packets.
randomVariable = static_cast<float>(rand()) / RAND_MAX;
while (randomVariable < duplicateRate)
{
webrtc::ForwardErrorCorrection::ReceivedPacket* duplicatePacket =
new webrtc::ForwardErrorCorrection::ReceivedPacket;
memcpy(duplicatePacket, receivedPacket,
sizeof(webrtc::ForwardErrorCorrection::ReceivedPacket));
duplicatePacket->pkt = new webrtc::ForwardErrorCorrection::Packet;
memcpy(duplicatePacket->pkt->data, receivedPacket->pkt->data,
receivedPacket->pkt->length);
duplicatePacket->pkt->length = receivedPacket->pkt->length;
toDecodeList.PushBack(duplicatePacket);
randomVariable = static_cast<float>(rand()) / RAND_MAX;
}
receivedPacketList.Erase(listItem);
listItem = receivedPacketList.First();
}
}

View File

@ -0,0 +1,38 @@
# Copyright (c) 2011 The WebRTC project authors. All Rights Reserved.
#
# Use of this source code is governed by a BSD-style license
# that can be found in the LICENSE file in the root of the source
# tree. An additional intellectual property rights grant can be found
# in the file PATENTS. All contributing project authors may
# be found in the AUTHORS file in the root of the source tree.
{
'includes': [
'../../../../common_settings.gypi', # Common settings
],
'targets': [
{
'target_name': 'test_fec',
'type': 'executable',
'dependencies': [
'../../source/rtp_rtcp.gyp:rtp_rtcp',
],
'include_dirs': [
'../../source',
'../../../../system_wrappers/interface',
],
'sources': [
'test_fec.cc',
],
},
],
}
# Local Variables:
# tab-width:2
# indent-tabs-mode:nil
# End:
# vim: set expandtab tabstop=2 shiftwidth=2: