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++;
|
mediaListIt++;
|
||||||
}
|
}
|
||||||
// Result in Q0 with an unsigned round.
|
|
||||||
uint32_t numFecPackets = (numMediaPackets * protectionFactor + (1 << 7)) >> 8;
|
int numFecPackets = GetNumberOfFecPackets(numMediaPackets, protectionFactor);
|
||||||
// Generate at least one FEC packet if we need protection.
|
|
||||||
if (protectionFactor > 0 && numFecPackets == 0) {
|
|
||||||
numFecPackets = 1;
|
|
||||||
}
|
|
||||||
if (numFecPackets == 0) {
|
if (numFecPackets == 0) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
assert(numFecPackets <= numMediaPackets);
|
|
||||||
|
|
||||||
// Prepare FEC packets by setting them to 0.
|
// 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);
|
memset(_generatedFecPackets[i].data, 0, IP_PACKET_SIZE);
|
||||||
_generatedFecPackets[i].length = 0; // Use this as a marker for untouched
|
_generatedFecPackets[i].length = 0; // Use this as a marker for untouched
|
||||||
// packets.
|
// packets.
|
||||||
@ -204,6 +199,18 @@ int32_t ForwardErrorCorrection::GenerateFEC(
|
|||||||
return 0;
|
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(
|
void ForwardErrorCorrection::GenerateFecBitStrings(
|
||||||
const PacketList& mediaPacketList,
|
const PacketList& mediaPacketList,
|
||||||
uint8_t* packetMask,
|
uint8_t* packetMask,
|
||||||
|
@ -198,6 +198,12 @@ class ForwardErrorCorrection {
|
|||||||
*/
|
*/
|
||||||
int32_t DecodeFEC(ReceivedPacketList* receivedPacketList,
|
int32_t DecodeFEC(ReceivedPacketList* receivedPacketList,
|
||||||
RecoveredPacketList* recoveredPacketList);
|
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
|
* Gets the size in bytes of the FEC/ULP headers, which must be accounted for
|
||||||
* as packet overhead.
|
* as packet overhead.
|
||||||
|
@ -18,7 +18,20 @@ namespace webrtc {
|
|||||||
// Minimum RTP header size in bytes.
|
// Minimum RTP header size in bytes.
|
||||||
enum { kRtpHeaderSize = 12 };
|
enum { kRtpHeaderSize = 12 };
|
||||||
enum { kREDForFECHeaderLength = 1 };
|
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 {
|
struct RtpPacket {
|
||||||
WebRtc_UWord16 rtpHeaderLength;
|
WebRtc_UWord16 rtpHeaderLength;
|
||||||
@ -77,6 +90,7 @@ ProducerFec::ProducerFec(ForwardErrorCorrection* fec)
|
|||||||
num_frames_(0),
|
num_frames_(0),
|
||||||
incomplete_frame_(false),
|
incomplete_frame_(false),
|
||||||
num_first_partition_(0),
|
num_first_partition_(0),
|
||||||
|
minimum_media_packets_fec_(1),
|
||||||
params_(),
|
params_(),
|
||||||
new_params_() {
|
new_params_() {
|
||||||
memset(¶ms_, 0, sizeof(params_));
|
memset(¶ms_, 0, sizeof(params_));
|
||||||
@ -100,6 +114,11 @@ void ProducerFec::SetFecParameters(const FecProtectionParams* params,
|
|||||||
// produced.
|
// produced.
|
||||||
new_params_ = *params;
|
new_params_ = *params;
|
||||||
num_first_partition_ = num_first_partition;
|
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,
|
RedPacket* ProducerFec::BuildRedPacket(const uint8_t* data_buffer,
|
||||||
@ -136,12 +155,13 @@ int ProducerFec::AddRtpPacketAndGenerateFec(const uint8_t* data_buffer,
|
|||||||
++num_frames_;
|
++num_frames_;
|
||||||
incomplete_frame_ = false;
|
incomplete_frame_ = false;
|
||||||
}
|
}
|
||||||
// Produce FEC over at most |params_.max_fec_frames| frames, or as soon as
|
// Produce FEC over at most |params_.max_fec_frames| frames, or as soon as:
|
||||||
// the wasted overhead (actual overhead - requested protection) is less than
|
// (1) the excess overhead (actual overhead - requested/target overhead) is
|
||||||
// |kMaxOverhead|.
|
// less than |kMaxExcessOverhead|, and
|
||||||
|
// (2) at least |minimum_media_packets_fec_| media packets is reached.
|
||||||
if (!incomplete_frame_ &&
|
if (!incomplete_frame_ &&
|
||||||
(num_frames_ == params_.max_fec_frames ||
|
(num_frames_ == params_.max_fec_frames ||
|
||||||
(Overhead() - params_.fec_rate) < kMaxOverhead)) {
|
(ExcessOverheadBelowMax() && MinimumMediaPacketsReached()))) {
|
||||||
assert(num_first_partition_ <=
|
assert(num_first_partition_ <=
|
||||||
static_cast<int>(ForwardErrorCorrection::kMaxMediaPackets));
|
static_cast<int>(ForwardErrorCorrection::kMaxMediaPackets));
|
||||||
int ret = fec_->GenerateFEC(media_packets_fec_,
|
int ret = fec_->GenerateFEC(media_packets_fec_,
|
||||||
@ -158,6 +178,32 @@ int ProducerFec::AddRtpPacketAndGenerateFec(const uint8_t* data_buffer,
|
|||||||
return 0;
|
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 {
|
bool ProducerFec::FecAvailable() const {
|
||||||
return (fec_packets_.size() > 0);
|
return (fec_packets_.size() > 0);
|
||||||
}
|
}
|
||||||
@ -196,10 +242,8 @@ int ProducerFec::Overhead() const {
|
|||||||
// protection factor produced by video_coding module and how the FEC
|
// protection factor produced by video_coding module and how the FEC
|
||||||
// generation is implemented.
|
// generation is implemented.
|
||||||
assert(!media_packets_fec_.empty());
|
assert(!media_packets_fec_.empty());
|
||||||
int num_fec_packets = params_.fec_rate * media_packets_fec_.size();
|
int num_fec_packets = fec_->GetNumberOfFecPackets(media_packets_fec_.size(),
|
||||||
// Ceil.
|
params_.fec_rate);
|
||||||
int rounding = (num_fec_packets % (1 << 8) > 0) ? (1 << 8) : 0;
|
|
||||||
num_fec_packets = (num_fec_packets + rounding) >> 8;
|
|
||||||
// Return the overhead in Q8.
|
// Return the overhead in Q8.
|
||||||
return (num_fec_packets << 8) / media_packets_fec_.size();
|
return (num_fec_packets << 8) / media_packets_fec_.size();
|
||||||
}
|
}
|
||||||
|
@ -54,6 +54,10 @@ class ProducerFec {
|
|||||||
int payload_length,
|
int payload_length,
|
||||||
int rtp_header_length);
|
int rtp_header_length);
|
||||||
|
|
||||||
|
bool ExcessOverheadBelowMax();
|
||||||
|
|
||||||
|
bool MinimumMediaPacketsReached();
|
||||||
|
|
||||||
bool FecAvailable() const;
|
bool FecAvailable() const;
|
||||||
|
|
||||||
RedPacket* GetFecPacket(int red_pl_type, int fec_pl_type,
|
RedPacket* GetFecPacket(int red_pl_type, int fec_pl_type,
|
||||||
@ -68,6 +72,7 @@ class ProducerFec {
|
|||||||
int num_frames_;
|
int num_frames_;
|
||||||
bool incomplete_frame_;
|
bool incomplete_frame_;
|
||||||
int num_first_partition_;
|
int num_first_partition_;
|
||||||
|
int minimum_media_packets_fec_;
|
||||||
FecProtectionParams params_;
|
FecProtectionParams params_;
|
||||||
FecProtectionParams new_params_;
|
FecProtectionParams new_params_;
|
||||||
};
|
};
|
||||||
|
@ -55,8 +55,14 @@ class ProducerFecTest : public ::testing::Test {
|
|||||||
};
|
};
|
||||||
|
|
||||||
TEST_F(ProducerFecTest, OneFrameFec) {
|
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;
|
const int kNumPackets = 4;
|
||||||
FecProtectionParams params = {5, false, 3};
|
FecProtectionParams params = {15, false, 3};
|
||||||
std::list<RtpPacket*> rtp_packets;
|
std::list<RtpPacket*> rtp_packets;
|
||||||
generator_->NewFrame(kNumPackets);
|
generator_->NewFrame(kNumPackets);
|
||||||
producer_->SetFecParameters(¶ms, 0); // Expecting one FEC packet.
|
producer_->SetFecParameters(¶ms, 0); // Expecting one FEC packet.
|
||||||
@ -86,9 +92,17 @@ TEST_F(ProducerFecTest, OneFrameFec) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ProducerFecTest, TwoFrameFec) {
|
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 kNumPackets = 2;
|
||||||
const int kNumFrames = 2;
|
const int kNumFrames = 2;
|
||||||
FecProtectionParams params = {5, 0, 3};
|
|
||||||
|
FecProtectionParams params = {15, 0, 3};
|
||||||
std::list<RtpPacket*> rtp_packets;
|
std::list<RtpPacket*> rtp_packets;
|
||||||
producer_->SetFecParameters(¶ms, 0); // Expecting one FEC packet.
|
producer_->SetFecParameters(¶ms, 0); // Expecting one FEC packet.
|
||||||
uint32_t last_timestamp = 0;
|
uint32_t last_timestamp = 0;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user