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:
parent
e1919f41b7
commit
747cd87da1
@ -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,
|
||||
|
@ -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.
|
||||
|
@ -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(¶ms_, 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();
|
||||
}
|
||||
|
@ -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_;
|
||||
};
|
||||
|
@ -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(¶ms, 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(¶ms, 0); // Expecting one FEC packet.
|
||||
uint32_t last_timestamp = 0;
|
||||
|
Loading…
x
Reference in New Issue
Block a user