FEC: For multi-frame FEC, allow for the size of the code to be increased,

under certain conditions. This generally improves the FEC recovery for 
     bursty loss under medium-high protection level.
Review URL: https://webrtc-codereview.appspot.com/566012

git-svn-id: http://webrtc.googlecode.com/svn/trunk@2271 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
marpan@webrtc.org 2012-05-22 16:50:00 +00:00
parent e1919f41b7
commit 747cd87da1
5 changed files with 95 additions and 19 deletions

View File

@ -157,19 +157,14 @@ int32_t ForwardErrorCorrection::GenerateFEC(
}
mediaListIt++;
}
// Result in Q0 with an unsigned round.
uint32_t numFecPackets = (numMediaPackets * protectionFactor + (1 << 7)) >> 8;
// Generate at least one FEC packet if we need protection.
if (protectionFactor > 0 && numFecPackets == 0) {
numFecPackets = 1;
}
int numFecPackets = GetNumberOfFecPackets(numMediaPackets, protectionFactor);
if (numFecPackets == 0) {
return 0;
}
assert(numFecPackets <= numMediaPackets);
// Prepare FEC packets by setting them to 0.
for (uint32_t i = 0; i < numFecPackets; i++) {
for (int i = 0; i < numFecPackets; i++) {
memset(_generatedFecPackets[i].data, 0, IP_PACKET_SIZE);
_generatedFecPackets[i].length = 0; // Use this as a marker for untouched
// packets.
@ -204,6 +199,18 @@ int32_t ForwardErrorCorrection::GenerateFEC(
return 0;
}
int ForwardErrorCorrection::GetNumberOfFecPackets(uint16_t numMediaPackets,
uint8_t protectionFactor) {
// Result in Q0 with an unsigned round.
int numFecPackets = (numMediaPackets * protectionFactor + (1 << 7)) >> 8;
// Generate at least one FEC packet if we need protection.
if (protectionFactor > 0 && numFecPackets == 0) {
numFecPackets = 1;
}
assert(numFecPackets <= numMediaPackets);
return numFecPackets;
}
void ForwardErrorCorrection::GenerateFecBitStrings(
const PacketList& mediaPacketList,
uint8_t* packetMask,

View File

@ -198,6 +198,12 @@ class ForwardErrorCorrection {
*/
int32_t DecodeFEC(ReceivedPacketList* receivedPacketList,
RecoveredPacketList* recoveredPacketList);
// Get the number of FEC packets, given the number of media packets and the
// protection factor.
int GetNumberOfFecPackets(uint16_t numMediaPackets,
uint8_t protectionFactor);
/**
* Gets the size in bytes of the FEC/ULP headers, which must be accounted for
* as packet overhead.

View File

@ -18,7 +18,20 @@ namespace webrtc {
// Minimum RTP header size in bytes.
enum { kRtpHeaderSize = 12 };
enum { kREDForFECHeaderLength = 1 };
enum { kMaxOverhead = 60 }; // Q8.
// This controls the maximum amount of excess overhead (actual - target)
// allowed in order to trigger GenerateFEC(), before |params_.max_fec_frames|
// is reached. Overhead here is defined as relative to number of media packets.
enum { kMaxExcessOverhead = 50 }; // Q8.
// This is the minimum number of media packets required (above some protection
// level) in order to trigger GenerateFEC(), before |params_.max_fec_frames| is
// reached.
enum { kMinimumMediaPackets = 4 };
// Threshold on the received FEC protection level, above which we enforce at
// least |kMinimumMediaPackets| packets for the FEC code. Below this
// threshold |kMinimumMediaPackets| is set to default value of 1.
enum { kHighProtectionThreshold = 80 }; // Corresponds to ~30 overhead, range
// is 0 to 255, where 255 corresponds to 100% overhead (relative to number of
// media packets).
struct RtpPacket {
WebRtc_UWord16 rtpHeaderLength;
@ -77,6 +90,7 @@ ProducerFec::ProducerFec(ForwardErrorCorrection* fec)
num_frames_(0),
incomplete_frame_(false),
num_first_partition_(0),
minimum_media_packets_fec_(1),
params_(),
new_params_() {
memset(&params_, 0, sizeof(params_));
@ -100,6 +114,11 @@ void ProducerFec::SetFecParameters(const FecProtectionParams* params,
// produced.
new_params_ = *params;
num_first_partition_ = num_first_partition;
if (params->fec_rate > kHighProtectionThreshold) {
minimum_media_packets_fec_ = kMinimumMediaPackets;
} else {
minimum_media_packets_fec_ = 1;
}
}
RedPacket* ProducerFec::BuildRedPacket(const uint8_t* data_buffer,
@ -136,12 +155,13 @@ int ProducerFec::AddRtpPacketAndGenerateFec(const uint8_t* data_buffer,
++num_frames_;
incomplete_frame_ = false;
}
// Produce FEC over at most |params_.max_fec_frames| frames, or as soon as
// the wasted overhead (actual overhead - requested protection) is less than
// |kMaxOverhead|.
// Produce FEC over at most |params_.max_fec_frames| frames, or as soon as:
// (1) the excess overhead (actual overhead - requested/target overhead) is
// less than |kMaxExcessOverhead|, and
// (2) at least |minimum_media_packets_fec_| media packets is reached.
if (!incomplete_frame_ &&
(num_frames_ == params_.max_fec_frames ||
(Overhead() - params_.fec_rate) < kMaxOverhead)) {
(ExcessOverheadBelowMax() && MinimumMediaPacketsReached()))) {
assert(num_first_partition_ <=
static_cast<int>(ForwardErrorCorrection::kMaxMediaPackets));
int ret = fec_->GenerateFEC(media_packets_fec_,
@ -158,6 +178,32 @@ int ProducerFec::AddRtpPacketAndGenerateFec(const uint8_t* data_buffer,
return 0;
}
// Returns true if the excess overhead (actual - target) for the FEC is below
// the amount |kMaxExcessOverhead|. This effects the lower protection level
// cases and low number of media packets/frame. The target overhead is given by
// |params_.fec_rate|, and is only achievable in the limit of large number of
// media packets.
bool ProducerFec::ExcessOverheadBelowMax() {
return ((Overhead() - params_.fec_rate) < kMaxExcessOverhead);
}
// Returns true if the media packet list for the FEC is at least
// |minimum_media_packets_fec_|. This condition tries to capture the effect
// that, for the same amount of protection/overhead, longer codes
// (e.g. (2k,2m) vs (k,m)) are generally more effective at recovering losses.
bool ProducerFec::MinimumMediaPacketsReached() {
float avg_num_packets_frame = static_cast<float>(media_packets_fec_.size() /
num_frames_);
if (avg_num_packets_frame < 2.0f) {
return (static_cast<int>(media_packets_fec_.size()) >=
minimum_media_packets_fec_);
} else {
// For larger rates (more packets/frame), increase the threshold.
return (static_cast<int>(media_packets_fec_.size()) >=
minimum_media_packets_fec_ + 1);
}
}
bool ProducerFec::FecAvailable() const {
return (fec_packets_.size() > 0);
}
@ -196,10 +242,8 @@ int ProducerFec::Overhead() const {
// protection factor produced by video_coding module and how the FEC
// generation is implemented.
assert(!media_packets_fec_.empty());
int num_fec_packets = params_.fec_rate * media_packets_fec_.size();
// Ceil.
int rounding = (num_fec_packets % (1 << 8) > 0) ? (1 << 8) : 0;
num_fec_packets = (num_fec_packets + rounding) >> 8;
int num_fec_packets = fec_->GetNumberOfFecPackets(media_packets_fec_.size(),
params_.fec_rate);
// Return the overhead in Q8.
return (num_fec_packets << 8) / media_packets_fec_.size();
}

View File

@ -54,6 +54,10 @@ class ProducerFec {
int payload_length,
int rtp_header_length);
bool ExcessOverheadBelowMax();
bool MinimumMediaPacketsReached();
bool FecAvailable() const;
RedPacket* GetFecPacket(int red_pl_type, int fec_pl_type,
@ -68,6 +72,7 @@ class ProducerFec {
int num_frames_;
bool incomplete_frame_;
int num_first_partition_;
int minimum_media_packets_fec_;
FecProtectionParams params_;
FecProtectionParams new_params_;
};

View File

@ -55,8 +55,14 @@ class ProducerFecTest : public ::testing::Test {
};
TEST_F(ProducerFecTest, OneFrameFec) {
// The number of media packets (|kNumPackets|), number of frames (one for
// this test), and the protection factor (|params->fec_rate|) are set to make
// sure the conditions for generating FEC are satisfied. This means:
// (1) protection factor is high enough so that actual overhead over 1 frame
// of packets is within |kMaxExcessOverhead|, and (2) the total number of
// media packets for 1 frame is at least |minimum_media_packets_fec_|.
const int kNumPackets = 4;
FecProtectionParams params = {5, false, 3};
FecProtectionParams params = {15, false, 3};
std::list<RtpPacket*> rtp_packets;
generator_->NewFrame(kNumPackets);
producer_->SetFecParameters(&params, 0); // Expecting one FEC packet.
@ -86,9 +92,17 @@ TEST_F(ProducerFecTest, OneFrameFec) {
}
TEST_F(ProducerFecTest, TwoFrameFec) {
// The number of media packets/frame (|kNumPackets|), the number of frames
// (|kNumFrames|), and the protection factor (|params->fec_rate|) are set to
// make sure the conditions for generating FEC are satisfied. This means:
// (1) protection factor is high enough so that actual overhead over
// |kNumFrames| is within |kMaxExcessOverhead|, and (2) the total number of
// media packets for |kNumFrames| frames is at least
// |minimum_media_packets_fec_|.
const int kNumPackets = 2;
const int kNumFrames = 2;
FecProtectionParams params = {5, 0, 3};
FecProtectionParams params = {15, 0, 3};
std::list<RtpPacket*> rtp_packets;
producer_->SetFecParameters(&params, 0); // Expecting one FEC packet.
uint32_t last_timestamp = 0;