From 747cd87da1fc7b2acabfd32283c6f692c62e65dc Mon Sep 17 00:00:00 2001 From: "marpan@webrtc.org" Date: Tue, 22 May 2012 16:50:00 +0000 Subject: [PATCH] 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 --- .../source/forward_error_correction.cc | 23 ++++--- .../source/forward_error_correction.h | 6 ++ src/modules/rtp_rtcp/source/producer_fec.cc | 62 ++++++++++++++++--- src/modules/rtp_rtcp/source/producer_fec.h | 5 ++ .../rtp_rtcp/source/producer_fec_unittest.cc | 18 +++++- 5 files changed, 95 insertions(+), 19 deletions(-) diff --git a/src/modules/rtp_rtcp/source/forward_error_correction.cc b/src/modules/rtp_rtcp/source/forward_error_correction.cc index e756e7d4c..c252c0191 100644 --- a/src/modules/rtp_rtcp/source/forward_error_correction.cc +++ b/src/modules/rtp_rtcp/source/forward_error_correction.cc @@ -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, diff --git a/src/modules/rtp_rtcp/source/forward_error_correction.h b/src/modules/rtp_rtcp/source/forward_error_correction.h index 1dcb5b48b..2fb59a63b 100644 --- a/src/modules/rtp_rtcp/source/forward_error_correction.h +++ b/src/modules/rtp_rtcp/source/forward_error_correction.h @@ -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. diff --git a/src/modules/rtp_rtcp/source/producer_fec.cc b/src/modules/rtp_rtcp/source/producer_fec.cc index 864bb5b4a..a2ec0e3ff 100644 --- a/src/modules/rtp_rtcp/source/producer_fec.cc +++ b/src/modules/rtp_rtcp/source/producer_fec.cc @@ -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(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(media_packets_fec_.size() / + num_frames_); + if (avg_num_packets_frame < 2.0f) { + return (static_cast(media_packets_fec_.size()) >= + minimum_media_packets_fec_); + } else { + // For larger rates (more packets/frame), increase the threshold. + return (static_cast(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(); } diff --git a/src/modules/rtp_rtcp/source/producer_fec.h b/src/modules/rtp_rtcp/source/producer_fec.h index f0fa54e15..663ec3446 100644 --- a/src/modules/rtp_rtcp/source/producer_fec.h +++ b/src/modules/rtp_rtcp/source/producer_fec.h @@ -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_; }; diff --git a/src/modules/rtp_rtcp/source/producer_fec_unittest.cc b/src/modules/rtp_rtcp/source/producer_fec_unittest.cc index 89d2dbca2..a8c2b004f 100644 --- a/src/modules/rtp_rtcp/source/producer_fec_unittest.cc +++ b/src/modules/rtp_rtcp/source/producer_fec_unittest.cc @@ -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 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 rtp_packets; producer_->SetFecParameters(¶ms, 0); // Expecting one FEC packet. uint32_t last_timestamp = 0;