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:
parent
e25b0148c9
commit
ae0ad911a1
@ -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
|
||||
|
@ -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
|
||||
|
340
modules/rtp_rtcp/source/forward_error_correction_internal.cc
Normal file
340
modules/rtp_rtcp/source/forward_error_correction_internal.cc
Normal 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
|
44
modules/rtp_rtcp/source/forward_error_correction_internal.h
Normal file
44
modules/rtp_rtcp/source/forward_error_correction_internal.h
Normal 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
|
@ -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',
|
||||
|
@ -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))
|
||||
{
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
569
modules/rtp_rtcp/test/testFec/test_fec.cc
Normal file
569
modules/rtp_rtcp/test/testFec/test_fec.cc
Normal 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();
|
||||
}
|
||||
}
|
38
modules/rtp_rtcp/test/testFec/test_fec.gyp
Normal file
38
modules/rtp_rtcp/test/testFec/test_fec.gyp
Normal 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:
|
Loading…
x
Reference in New Issue
Block a user