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:
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user