blob: 0217c165d20a11430e3f011db9c148921c05766b [file] [log] [blame]
/*
* 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 {
// Allow for different modes of protection for packets in UEP case.
enum ProtectionMode
{
kModeNoOverlap,
kModeOverlap,
kModeBiasFirstPacket,
};
/**
* 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(WebRtc_UWord16 numMaskBytes,
WebRtc_UWord16 numSubMaskBytes,
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(WebRtc_UWord16 numMaskBytes,
WebRtc_UWord16 resMaskBytes,
WebRtc_UWord16 numColumnShift,
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 {
// Remaining protection after important (first partition) packet protection
void RemainingPacketProtection(WebRtc_UWord16 numMediaPackets,
WebRtc_UWord16 numFecRemaining,
WebRtc_UWord16 numFecForImpPackets,
WebRtc_UWord16 numMaskBytes,
ProtectionMode mode,
WebRtc_UWord8* packetMask)
{
if (mode == kModeNoOverlap)
{
// subMask21
const WebRtc_UWord8 lBit =
(numMediaPackets - numFecForImpPackets) > 16 ? 1 : 0;
const WebRtc_UWord16 resMaskBytes =
(lBit == 1)? kMaskSizeLBitSet : kMaskSizeLBitClear;
const WebRtc_UWord8* packetMaskSub21 =
packetMaskTbl[numMediaPackets - numFecForImpPackets - 1]
[numFecRemaining - 1];
ShiftFitSubMask(numMaskBytes, resMaskBytes, numFecForImpPackets,
(numFecForImpPackets + numFecRemaining),
packetMaskSub21, packetMask);
}
else if (mode == kModeOverlap || mode == kModeBiasFirstPacket)
{
// subMask22
const WebRtc_UWord8* packetMaskSub22 =
packetMaskTbl[numMediaPackets - 1][numFecRemaining - 1];
FitSubMask(numMaskBytes, numMaskBytes, numFecRemaining, packetMaskSub22,
&packetMask[numFecForImpPackets * numMaskBytes]);
if (mode == kModeBiasFirstPacket)
{
for (WebRtc_UWord32 i = 0; i < numFecRemaining; i++)
{
WebRtc_UWord32 pktMaskIdx = i * numMaskBytes;
packetMask[pktMaskIdx] = packetMask[pktMaskIdx] | (1 << 7);
}
}
}
else
{
assert(false);
}
}
// Protection for important (first partition) packets
void ImportantPacketProtection(WebRtc_UWord16 numFecForImpPackets,
WebRtc_UWord16 numImpPackets,
WebRtc_UWord16 numMaskBytes,
WebRtc_UWord8* packetMask)
{
const WebRtc_UWord8 lBit = numImpPackets > 16 ? 1 : 0;
const WebRtc_UWord16 numImpMaskBytes =
(lBit == 1)? kMaskSizeLBitSet : kMaskSizeLBitClear;
// Get subMask1 from table
const WebRtc_UWord8* packetMaskSub1 =
packetMaskTbl[numImpPackets - 1][numFecForImpPackets - 1];
FitSubMask(numMaskBytes, numImpMaskBytes,
numFecForImpPackets, packetMaskSub1, packetMask);
}
// This function sets the protection allocation: i.e., how many FEC packets
// to use for numImp (1st partition) packets, given the: number of media
// packets, number of FEC packets, and number of 1st partition packets.
WebRtc_UWord32 SetProtectionAllocation(const WebRtc_UWord16 numMediaPackets,
const WebRtc_UWord16 numFecPackets,
const WebRtc_UWord16 numImpPackets)
{
// TODO (marpan): test different cases for protection allocation:
// Use at most (allocPar * numFecPackets) for important packets.
float allocPar = 0.5;
WebRtc_UWord16 maxNumFecForImp = static_cast<WebRtc_UWord16>
(allocPar * numFecPackets);
WebRtc_UWord16 numFecForImpPackets = (numImpPackets < maxNumFecForImp) ?
numImpPackets : maxNumFecForImp;
// Fall back to equal protection in this case
if (numFecPackets == 1 && (numMediaPackets > 2 * numImpPackets))
{
numFecForImpPackets = 0;
}
return numFecForImpPackets;
}
// Modification for UEP: reuse the off-line tables for the packet masks.
// Note: these masks were designed for equal packet protection case,
// assuming random packet loss.
// Current version has 3 modes (options) to build UEP mask from existing ones.
// Various other combinations may be added in future versions.
// Longer-term, we may add another set of tables specifically for UEP cases.
// TODO (marpan): also consider modification of masks for bursty loss cases.
// Mask is characterized as (#packets_to_protect, #fec_for_protection).
// Protection factor defined as: (#fec_for_protection / #packets_to_protect).
// Let k=numMediaPackets, n=total#packets, (n-k)=numFecPackets, m=numImpPackets.
// For ProtectionMode 0 and 1:
// one mask (subMask1) is used for 1st partition packets,
// the other mask (subMask21/22, for 0/1) is for the remaining FEC packets.
// In both mode 0 and 1, the packets of 1st partition (numImpPackets) are
// treated equally important, and are afforded more protection than the
// residual partition packets.
// For numImpPackets:
// subMask1 = (m, t): protection = t/(m), where t=F(k,n-k,m).
// t=F(k,n-k,m) is the number of packets used to protect first partition in
// subMask1. This is determined from the function SetProtectionAllocation().
// For the left-over protection:
// Mode 0: subMask21 = (k-m,n-k-t): protection = (n-k-t)/(k-m)
// mode 0 has no protection overlap between the two partitions.
// For mode 0, we would typically set t = min(m, n-k).
// Mode 1: subMask22 = (k, n-k-t), with protection (n-k-t)/(k)
// mode 1 has protection overlap between the two partitions (preferred).
// For ProtectionMode 2:
// This gives 1st packet of list (which is 1st packet of 1st partition) more
// protection. In mode 2, the equal protection mask (which is obtained from
// mode 1 for t=0) is modified (more "1s" added in 1st column of packet mask)
// to bias higher protection for the 1st source packet.
// Protection Mode 2 may be extended for a sort of sliding protection
// (i.e., vary the number/density of "1s" across columns) across packets.
void UnequalProtectionMask(const WebRtc_UWord16 numMediaPackets,
const WebRtc_UWord16 numFecPackets,
const WebRtc_UWord16 numImpPackets,
const WebRtc_UWord16 numMaskBytes,
WebRtc_UWord8* packetMask)
{
// Set Protection type and allocation
// TODO (marpan): test/update for best mode and some combinations thereof.
ProtectionMode mode = kModeOverlap;
WebRtc_UWord16 numFecForImpPackets = 0;
if (mode != kModeBiasFirstPacket)
{
numFecForImpPackets = SetProtectionAllocation(numMediaPackets,
numFecPackets,
numImpPackets);
}
WebRtc_UWord16 numFecRemaining = numFecPackets - numFecForImpPackets;
// Done with setting protection type and allocation
//
// Generate subMask1
//
if (numFecForImpPackets > 0)
{
ImportantPacketProtection(numFecForImpPackets, numImpPackets,
numMaskBytes, packetMask);
}
//
// Generate subMask2
//
if (numFecRemaining > 0)
{
RemainingPacketProtection(numMediaPackets, numFecRemaining,
numFecForImpPackets, numMaskBytes,
mode, packetMask);
}
}
void GeneratePacketMasks(int numMediaPackets,
int numFecPackets,
int numImpPackets,
bool useUnequalProtection,
WebRtc_UWord8* packetMask)
{
assert(numMediaPackets <= static_cast<int>(sizeof(packetMaskTbl) /
sizeof(*packetMaskTbl)));
assert(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;
// Equal-protection for these cases
if (!useUnequalProtection || numImpPackets == 0)
{
// 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, packetMask);
} // End of UEP modification
} //End of GetPacketMasks
} // namespace internal
} // namespace webrtc