Enable multi-frame FEC by default for temporal layers <= 2. For two temporal layers we currently only protect the base layer.

This also introduces zero column insertion into packet masks when some sequence numbers deliberately haven't been given to the FEC generator.

BUG=
TEST=

Review URL: https://webrtc-codereview.appspot.com/477001

git-svn-id: http://webrtc.googlecode.com/svn/trunk@2005 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
stefan@webrtc.org 2012-04-11 07:42:25 +00:00
parent 85b4a1b715
commit c35f5ced92
11 changed files with 505 additions and 54 deletions

View File

@ -8,12 +8,13 @@
* be found in the AUTHORS file in the root of the source tree.
*/
#include "modules/rtp_rtcp/source/forward_error_correction.h"
#include <algorithm>
#include <cassert>
#include <cstring>
#include <iterator>
#include "modules/rtp_rtcp/source/forward_error_correction.h"
#include "modules/rtp_rtcp/source/forward_error_correction_internal.h"
#include "modules/rtp_rtcp/source/rtp_utility.h"
#include "system_wrappers/interface/trace.h"
@ -108,8 +109,8 @@ int32_t ForwardErrorCorrection::GenerateFEC(
return -1;
}
const uint16_t numMediaPackets = mediaPacketList.size();
const uint8_t lBit = numMediaPackets > 16 ? 1 : 0;
const uint16_t numMaskBytes = (lBit == 1)?
bool lBit = (numMediaPackets > 8 * kMaskSizeLBitClear);
uint16_t numMaskBytes = lBit ?
kMaskSizeLBitSet : kMaskSizeLBitClear;
if (numMediaPackets > kMaxMediaPackets) {
@ -176,15 +177,28 @@ int32_t ForwardErrorCorrection::GenerateFEC(
}
// -- Generate packet masks --
uint8_t* packetMask = new uint8_t[numFecPackets * numMaskBytes];
// Always allocate space for a large mask.
uint8_t* packetMask = new uint8_t[numFecPackets * kMaskSizeLBitSet];
memset(packetMask, 0, numFecPackets * numMaskBytes);
internal::GeneratePacketMasks(numMediaPackets, numFecPackets,
numImportantPackets, useUnequalProtection,
packetMask);
GenerateFecBitStrings(mediaPacketList, packetMask, numFecPackets);
int numMaskBits = InsertZerosInBitMasks(mediaPacketList, packetMask,
numMaskBytes, numFecPackets);
GenerateFecUlpHeaders(mediaPacketList, packetMask, numFecPackets);
lBit = (numMaskBits > 8 * kMaskSizeLBitClear);
if (numMaskBits < 0) {
delete [] packetMask;
return -1;
}
if (lBit) {
numMaskBytes = kMaskSizeLBitSet;
}
GenerateFecBitStrings(mediaPacketList, packetMask, numFecPackets, lBit);
GenerateFecUlpHeaders(mediaPacketList, packetMask, lBit, numFecPackets);
delete [] packetMask;
return 0;
@ -193,12 +207,15 @@ int32_t ForwardErrorCorrection::GenerateFEC(
void ForwardErrorCorrection::GenerateFecBitStrings(
const PacketList& mediaPacketList,
uint8_t* packetMask,
uint32_t numFecPackets) {
uint32_t numFecPackets,
bool lBit) {
if (mediaPacketList.empty()) {
return;
}
uint8_t mediaPayloadLength[2];
const uint8_t lBit = mediaPacketList.size() > 16 ? 1 : 0;
const uint16_t numMaskBytes = (lBit == 1) ?
const uint16_t numMaskBytes = lBit ?
kMaskSizeLBitSet : kMaskSizeLBitClear;
const uint16_t ulpHeaderSize = (lBit == 1) ?
const uint16_t ulpHeaderSize = lBit ?
kUlpHeaderSizeLBitSet : kUlpHeaderSizeLBitClear;
const uint16_t fecRtpOffset = kFecHeaderSize + ulpHeaderSize - kRtpHeaderSize;
@ -207,6 +224,7 @@ void ForwardErrorCorrection::GenerateFecBitStrings(
uint32_t pktMaskIdx = i * numMaskBytes;
uint32_t mediaPktIdx = 0;
uint16_t fecPacketLength = 0;
uint16_t prevSeqNum = ParseSequenceNumber((*mediaListIt)->data);
while (mediaListIt != mediaPacketList.end()) {
// Each FEC packet has a multiple byte mask.
if (packetMask[pktMaskIdx] & (1 << (7 - mediaPktIdx))) {
@ -257,7 +275,11 @@ void ForwardErrorCorrection::GenerateFecBitStrings(
}
}
mediaListIt++;
mediaPktIdx++;
if (mediaListIt != mediaPacketList.end()) {
uint16_t seqNum = ParseSequenceNumber((*mediaListIt)->data);
mediaPktIdx += static_cast<uint16_t>(seqNum - prevSeqNum);
prevSeqNum = seqNum;
}
if (mediaPktIdx == 8) {
// Switch to the next mask byte.
mediaPktIdx = 0;
@ -269,9 +291,111 @@ void ForwardErrorCorrection::GenerateFecBitStrings(
}
}
int ForwardErrorCorrection::InsertZerosInBitMasks(
const PacketList& media_packets,
uint8_t* packet_mask,
uint16_t num_mask_bytes,
uint32_t num_fec_packets) {
uint8_t* new_mask = NULL;
if (media_packets.size() <= 1) {
return media_packets.size();
}
int last_seq_num = ParseSequenceNumber(media_packets.back()->data);
int first_seq_num = ParseSequenceNumber(media_packets.front()->data);
int total_missing_seq_nums = static_cast<uint16_t>(last_seq_num -
first_seq_num) -
media_packets.size() + 1;
if (total_missing_seq_nums == 0) {
// All sequence numbers are covered by the packet mask. No zero insertion
// required.
return media_packets.size();
}
// Allocate the new mask.
int new_mask_bytes = kMaskSizeLBitClear;
if (media_packets.size() + total_missing_seq_nums > 8 * kMaskSizeLBitClear) {
new_mask_bytes = kMaskSizeLBitSet;
}
new_mask = new uint8_t[num_fec_packets * kMaskSizeLBitSet];
memset(new_mask, 0, num_fec_packets * kMaskSizeLBitSet);
PacketList::const_iterator it = media_packets.begin();
uint16_t prev_seq_num = first_seq_num;
++it;
// Insert the first column.
CopyColumn(new_mask, new_mask_bytes, packet_mask, num_mask_bytes,
num_fec_packets, 0, 0);
int new_bit_index = 1;
int old_bit_index = 1;
// Insert zeros in the bit mask for every hole in the sequence.
for (; it != media_packets.end(); ++it) {
if (new_bit_index == 8 * kMaskSizeLBitSet) {
// We can only cover up to 48 packets.
break;
}
uint16_t seq_num = ParseSequenceNumber((*it)->data);
const int zeros_to_insert =
static_cast<uint16_t>(seq_num - prev_seq_num - 1);
if (zeros_to_insert > 0) {
InsertZeroColumns(zeros_to_insert, new_mask, new_mask_bytes,
num_fec_packets, new_bit_index);
}
new_bit_index += zeros_to_insert;
CopyColumn(new_mask, new_mask_bytes, packet_mask, num_mask_bytes,
num_fec_packets, new_bit_index, old_bit_index);
++new_bit_index;
++old_bit_index;
prev_seq_num = seq_num;
}
if (new_bit_index % 8 != 0) {
// We didn't fill the last byte. Shift bits to correct position.
for (uint16_t row = 0; row < num_fec_packets; ++row) {
int new_byte_index = row * new_mask_bytes + new_bit_index / 8;
new_mask[new_byte_index] <<= (7 - (new_bit_index % 8));
}
}
// Replace the old mask with the new.
memcpy(packet_mask, new_mask, kMaskSizeLBitSet * num_fec_packets);
delete [] new_mask;
return new_bit_index;
}
void ForwardErrorCorrection::InsertZeroColumns(int num_zeros,
uint8_t* new_mask,
int new_mask_bytes,
int num_fec_packets,
int new_bit_index) {
for (uint16_t row = 0; row < num_fec_packets; ++row) {
const int new_byte_index = row * new_mask_bytes + new_bit_index / 8;
const int max_shifts = (7 - (new_bit_index % 8));
new_mask[new_byte_index] <<= std::min(num_zeros, max_shifts);
}
}
void ForwardErrorCorrection::CopyColumn(uint8_t* new_mask,
int new_mask_bytes,
uint8_t* old_mask,
int old_mask_bytes,
int num_fec_packets,
int new_bit_index,
int old_bit_index) {
// Copy column from the old mask to the beginning of the new mask and shift it
// out from the old mask.
for (uint16_t row = 0; row < num_fec_packets; ++row) {
int new_byte_index = row * new_mask_bytes + new_bit_index / 8;
int old_byte_index = row * old_mask_bytes + old_bit_index / 8;
new_mask[new_byte_index] |= ((old_mask[old_byte_index] & 0x80) >> 7);
if (new_bit_index % 8 != 7) {
new_mask[new_byte_index] <<= 1;
}
old_mask[old_byte_index] <<= 1;
}
}
void ForwardErrorCorrection::GenerateFecUlpHeaders(
const PacketList& mediaPacketList,
uint8_t* packetMask,
bool lBit,
uint32_t numFecPackets) {
// -- Generate FEC and ULP headers --
//
@ -297,10 +421,9 @@ void ForwardErrorCorrection::GenerateFecUlpHeaders(
PacketList::const_iterator mediaListIt = mediaPacketList.begin();
Packet* mediaPacket = *mediaListIt;
assert(mediaPacket != NULL);
const uint8_t lBit = mediaPacketList.size() > 16 ? 1 : 0;
const uint16_t numMaskBytes = (lBit == 1)?
const uint16_t numMaskBytes = lBit ?
kMaskSizeLBitSet : kMaskSizeLBitClear;
const uint16_t ulpHeaderSize = (lBit == 1)?
const uint16_t ulpHeaderSize = lBit ?
kUlpHeaderSizeLBitSet : kUlpHeaderSizeLBitClear;
for (uint32_t i = 0; i < numFecPackets; i++) {
@ -669,6 +792,10 @@ void ForwardErrorCorrection::DiscardOldPackets(
assert(recoveredPacketList->size() <= kMaxMediaPackets);
}
uint16_t ForwardErrorCorrection::ParseSequenceNumber(uint8_t* packet) {
return (packet[2] << 8) + packet[3];
}
int32_t ForwardErrorCorrection::DecodeFEC(
ReceivedPacketList* receivedPacketList,
RecoveredPacketList* recoveredPacketList) {

View File

@ -214,11 +214,50 @@ class ForwardErrorCorrection {
void GenerateFecUlpHeaders(const PacketList& mediaPacketList,
uint8_t* packetMask,
bool lBit,
uint32_t numFecPackets);
// Analyzes |media_packets| for holes in the sequence and inserts zero columns
// into the |packet_mask| where those holes are found. Zero columns means that
// those packets will have no protection.
// Returns the number of bits used for one row of the new packet mask.
// Requires that |packet_mask| has at least 6 * |num_fec_packets| bytes
// allocated.
int InsertZerosInBitMasks(const PacketList& media_packets,
uint8_t* packet_mask,
uint16_t num_mask_bytes,
uint32_t num_fec_packets);
// Inserts |num_zeros| zero columns into |new_mask| at position
// |new_bit_index|. If the current byte of |new_mask| can't fit all zeros, the
// byte will be filled with zeros from |new_bit_index|, but the next byte will
// be untouched.
static void InsertZeroColumns(int num_zeros,
uint8_t* new_mask,
int new_mask_bytes,
int num_fec_packets,
int new_bit_index);
// Copies the left most bit column from the byte pointed to by
// |old_bit_index| in |old_mask| to the right most column of the byte pointed
// to by |new_bit_index| in |new_mask|. |old_mask_bytes| and |new_mask_bytes|
// represent the number of bytes used per row for each mask. |num_fec_packets|
// represent the number of rows of the masks.
// The copied bit is shifted out from |old_mask| and is shifted one step to
// the left in |new_mask|. |new_mask| will contain "xxxx xxn0" after this
// operation, where x are previously inserted bits and n is the new bit.
static void CopyColumn(uint8_t* new_mask,
int new_mask_bytes,
uint8_t* old_mask,
int old_mask_bytes,
int num_fec_packets,
int new_bit_index,
int old_bit_index);
void GenerateFecBitStrings(const PacketList& mediaPacketList,
uint8_t* packetMask,
uint32_t numFecPackets);
uint32_t numFecPackets,
bool lBit);
// Insert received packets into FEC or recovered list.
void InsertPackets(ReceivedPacketList* receivedPacketList,
@ -279,6 +318,7 @@ class ForwardErrorCorrection {
static void DiscardFECPacket(FecPacket* fec_packet);
static void DiscardOldPackets(RecoveredPacketList* recoveredPacketList);
static uint16_t ParseSequenceNumber(uint8_t* packet);
int32_t _id;
std::vector<Packet> _generatedFecPackets;

View File

@ -10,8 +10,6 @@
#include "modules/rtp_rtcp/source/producer_fec.h"
#include <stdio.h>
#include "modules/rtp_rtcp/source/forward_error_correction.h"
#include "modules/rtp_rtcp/source/rtp_utility.h"
@ -79,8 +77,10 @@ ProducerFec::ProducerFec(ForwardErrorCorrection* fec)
num_frames_(0),
incomplete_frame_(false),
num_first_partition_(0),
params_() {
params_(),
new_params_() {
memset(&params_, 0, sizeof(params_));
memset(&new_params_, 0, sizeof(new_params_));
}
ProducerFec::~ProducerFec() {
@ -96,7 +96,9 @@ void ProducerFec::SetFecParameters(const FecProtectionParams* params,
num_first_partition =
ForwardErrorCorrection::kMaxMediaPackets;
}
params_ = *params;
// Store the new params and apply them for the next set of FEC packets being
// produced.
new_params_ = *params;
num_first_partition_ = num_first_partition;
}
@ -118,6 +120,9 @@ int ProducerFec::AddRtpPacketAndGenerateFec(const uint8_t* data_buffer,
int payload_length,
int rtp_header_length) {
assert(fec_packets_.empty());
if (media_packets_fec_.empty()) {
params_ = new_params_;
}
incomplete_frame_ = true;
const bool marker_bit = (data_buffer[1] & kRtpMarkerBitMask) ? true : false;
if (media_packets_fec_.size() < ForwardErrorCorrection::kMaxMediaPackets) {
@ -131,6 +136,9 @@ 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|.
if (!incomplete_frame_ &&
(num_frames_ == params_.max_fec_frames ||
(Overhead() - params_.fec_rate) < kMaxOverhead)) {
@ -183,13 +191,17 @@ RedPacket* ProducerFec::GetFecPacket(int red_pl_type, int fec_pl_type,
}
int ProducerFec::Overhead() const {
// Overhead is defined as relative to the number of media packets, and not
// relative to total number of packets. This definition is inhereted from the
// 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;
// Return the overhead in Q8.
return (num_fec_packets << 8) /
(media_packets_fec_.size() + num_fec_packets);
return (num_fec_packets << 8) / media_packets_fec_.size();
}
void ProducerFec::DeletePackets() {

View File

@ -69,6 +69,7 @@ class ProducerFec {
bool incomplete_frame_;
int num_first_partition_;
FecProtectionParams params_;
FecProtectionParams new_params_;
};
} // namespace webrtc

View File

@ -55,7 +55,7 @@ class ProducerFecTest : public ::testing::Test {
};
TEST_F(ProducerFecTest, OneFrameFec) {
const int kNumPackets = 3;
const int kNumPackets = 4;
FecProtectionParams params = {5, false, 3};
std::list<RtpPacket*> rtp_packets;
generator_->NewFrame(kNumPackets);
@ -75,7 +75,7 @@ TEST_F(ProducerFecTest, OneFrameFec) {
kFecPayloadType,
seq_num);
EXPECT_FALSE(producer_->FecAvailable());
EXPECT_TRUE(packet != NULL);
ASSERT_TRUE(packet != NULL);
VerifyHeader(seq_num, last_timestamp,
kRedPayloadType, kFecPayloadType, packet, false);
while (!rtp_packets.empty()) {

View File

@ -26,6 +26,10 @@ const uint8_t kTransportOverhead = 28;
// Maximum number of media packets used in the FEC (RFC 5109).
const uint8_t kMaxNumberMediaPackets = ForwardErrorCorrection::kMaxMediaPackets;
typedef std::list<ForwardErrorCorrection::Packet*> PacketList;
typedef std::list<ForwardErrorCorrection::ReceivedPacket*> ReceivedPacketList;
typedef std::list<ForwardErrorCorrection::RecoveredPacket*> RecoveredPacketList;
template<typename T> void ClearList(std::list<T*>* my_list) {
T* packet = NULL;
while (!my_list->empty()) {
@ -47,10 +51,10 @@ class RtpFecTest : public ::testing::Test {
int ssrc_;
uint16_t fec_seq_num_;
std::list<ForwardErrorCorrection::Packet*> media_packet_list_;
std::list<ForwardErrorCorrection::Packet*> fec_packet_list_;
std::list<ForwardErrorCorrection::ReceivedPacket*> received_packet_list_;
std::list<ForwardErrorCorrection::RecoveredPacket*> recovered_packet_list_;
PacketList media_packet_list_;
PacketList fec_packet_list_;
ReceivedPacketList received_packet_list_;
RecoveredPacketList recovered_packet_list_;
// Media packet "i" is lost if media_loss_mask_[i] = 1,
// received if media_loss_mask_[i] = 0.
@ -63,6 +67,8 @@ class RtpFecTest : public ::testing::Test {
// Construct the media packet list, up to |num_media_packets| packets.
// Returns the next sequence number after the last media packet.
// (this will be the sequence of the first FEC packet)
int ConstructMediaPacketsSeqNum(int num_media_packets,
int start_seq_num);
int ConstructMediaPackets(int num_media_packets);
// Construct the received packet list: a subset of the media and FEC packets.
@ -73,7 +79,7 @@ class RtpFecTest : public ::testing::Test {
// The |packet_list| may be a media packet list (is_fec = false), or a
// FEC packet list (is_fec = true).
void ReceivedPackets(
const std::list<ForwardErrorCorrection::Packet*>& packet_list,
const PacketList& packet_list,
int* loss_mask,
bool is_fec);
@ -395,6 +401,221 @@ TEST_F(RtpFecTest, FecRecoveryWithLoss50percUep) {
EXPECT_FALSE(IsRecoveryComplete());
}
TEST_F(RtpFecTest, FecRecoveryNonConsecutivePackets) {
const int num_important_packets = 0;
const bool use_unequal_protection = false;
const int num_media_packets = 5;
uint8_t protection_factor = 60;
fec_seq_num_ = ConstructMediaPackets(num_media_packets);
// Create a new temporary packet list for generating FEC packets.
// This list should have every other packet removed.
PacketList protected_media_packets;
int i = 0;
for (PacketList::iterator it = media_packet_list_.begin();
it != media_packet_list_.end(); ++it, ++i) {
if (i % 2 == 0)
protected_media_packets.push_back(*it);
}
EXPECT_EQ(0, fec_->GenerateFEC(protected_media_packets,
protection_factor,
num_important_packets,
use_unequal_protection,
&fec_packet_list_));
// Expect 1 FEC packet.
EXPECT_EQ(1, static_cast<int>(fec_packet_list_.size()));
// 1 protected media packet lost
memset(media_loss_mask_, 0, sizeof(media_loss_mask_));
memset(fec_loss_mask_, 0, sizeof(fec_loss_mask_));
media_loss_mask_[2] = 1;
NetworkReceivedPackets();
EXPECT_EQ(0, fec_->DecodeFEC(&received_packet_list_ ,
&recovered_packet_list_));
// One packet lost, one FEC packet, expect complete recovery.
EXPECT_TRUE(IsRecoveryComplete());
FreeRecoveredPacketList();
// Unprotected packet lost.
memset(media_loss_mask_, 0, sizeof(media_loss_mask_));
memset(fec_loss_mask_, 0, sizeof(fec_loss_mask_));
media_loss_mask_[1] = 1;
NetworkReceivedPackets();
EXPECT_EQ(0, fec_->DecodeFEC(&received_packet_list_ ,
&recovered_packet_list_));
// Unprotected packet lost. Recovery not possible.
EXPECT_FALSE(IsRecoveryComplete());
FreeRecoveredPacketList();
// 2 media packets lost.
memset(media_loss_mask_, 0, sizeof(media_loss_mask_));
memset(fec_loss_mask_, 0, sizeof(fec_loss_mask_));
media_loss_mask_[0] = 1;
media_loss_mask_[2] = 1;
NetworkReceivedPackets();
EXPECT_EQ(0, fec_->DecodeFEC(&received_packet_list_ ,
&recovered_packet_list_));
// 2 protected packets lost, one FEC packet, cannot get complete recovery.
EXPECT_FALSE(IsRecoveryComplete());
}
TEST_F(RtpFecTest, FecRecoveryNonConsecutivePacketsExtension) {
const int num_important_packets = 0;
const bool use_unequal_protection = false;
const int num_media_packets = 21;
uint8_t protection_factor = 127;
fec_seq_num_ = ConstructMediaPackets(num_media_packets);
// Create a new temporary packet list for generating FEC packets.
// This list should have every other packet removed.
PacketList protected_media_packets;
int i = 0;
for (PacketList::iterator it = media_packet_list_.begin();
it != media_packet_list_.end(); ++it, ++i) {
if (i % 2 == 0)
protected_media_packets.push_back(*it);
}
// Zero column insertion will have to extend the size of the packet
// mask since the number of actual packets are 21, while the number
// of protected packets are 11.
EXPECT_EQ(0, fec_->GenerateFEC(protected_media_packets,
protection_factor,
num_important_packets,
use_unequal_protection,
&fec_packet_list_));
// Expect 5 FEC packet.
EXPECT_EQ(5, static_cast<int>(fec_packet_list_.size()));
// Last protected media packet lost
memset(media_loss_mask_, 0, sizeof(media_loss_mask_));
memset(fec_loss_mask_, 0, sizeof(fec_loss_mask_));
media_loss_mask_[num_media_packets - 1] = 1;
NetworkReceivedPackets();
EXPECT_EQ(0, fec_->DecodeFEC(&received_packet_list_ ,
&recovered_packet_list_));
// One packet lost, one FEC packet, expect complete recovery.
EXPECT_TRUE(IsRecoveryComplete());
FreeRecoveredPacketList();
// Last unprotected packet lost.
memset(media_loss_mask_, 0, sizeof(media_loss_mask_));
memset(fec_loss_mask_, 0, sizeof(fec_loss_mask_));
media_loss_mask_[num_media_packets - 2] = 1;
NetworkReceivedPackets();
EXPECT_EQ(0, fec_->DecodeFEC(&received_packet_list_ ,
&recovered_packet_list_));
// Unprotected packet lost. Recovery not possible.
EXPECT_FALSE(IsRecoveryComplete());
FreeRecoveredPacketList();
// 6 media packets lost.
memset(media_loss_mask_, 0, sizeof(media_loss_mask_));
memset(fec_loss_mask_, 0, sizeof(fec_loss_mask_));
media_loss_mask_[num_media_packets - 11] = 1;
media_loss_mask_[num_media_packets - 9] = 1;
media_loss_mask_[num_media_packets - 7] = 1;
media_loss_mask_[num_media_packets - 5] = 1;
media_loss_mask_[num_media_packets - 3] = 1;
media_loss_mask_[num_media_packets - 1] = 1;
NetworkReceivedPackets();
EXPECT_EQ(0, fec_->DecodeFEC(&received_packet_list_ ,
&recovered_packet_list_));
// 5 protected packets lost, one FEC packet, cannot get complete recovery.
EXPECT_FALSE(IsRecoveryComplete());
}
TEST_F(RtpFecTest, FecRecoveryNonConsecutivePacketsWrap) {
const int num_important_packets = 0;
const bool use_unequal_protection = false;
const int num_media_packets = 21;
uint8_t protection_factor = 127;
fec_seq_num_ = ConstructMediaPacketsSeqNum(num_media_packets, 0xFFFF - 5);
// Create a new temporary packet list for generating FEC packets.
// This list should have every other packet removed.
PacketList protected_media_packets;
int i = 0;
for (PacketList::iterator it = media_packet_list_.begin();
it != media_packet_list_.end(); ++it, ++i) {
if (i % 2 == 0)
protected_media_packets.push_back(*it);
}
// Zero column insertion will have to extend the size of the packet
// mask since the number of actual packets are 21, while the number
// of protected packets are 11.
EXPECT_EQ(0, fec_->GenerateFEC(protected_media_packets,
protection_factor,
num_important_packets,
use_unequal_protection,
&fec_packet_list_));
// Expect 5 FEC packet.
EXPECT_EQ(5, static_cast<int>(fec_packet_list_.size()));
// Last protected media packet lost
memset(media_loss_mask_, 0, sizeof(media_loss_mask_));
memset(fec_loss_mask_, 0, sizeof(fec_loss_mask_));
media_loss_mask_[num_media_packets - 1] = 1;
NetworkReceivedPackets();
EXPECT_EQ(0, fec_->DecodeFEC(&received_packet_list_ ,
&recovered_packet_list_));
// One packet lost, one FEC packet, expect complete recovery.
EXPECT_TRUE(IsRecoveryComplete());
FreeRecoveredPacketList();
// Last unprotected packet lost.
memset(media_loss_mask_, 0, sizeof(media_loss_mask_));
memset(fec_loss_mask_, 0, sizeof(fec_loss_mask_));
media_loss_mask_[num_media_packets - 2] = 1;
NetworkReceivedPackets();
EXPECT_EQ(0, fec_->DecodeFEC(&received_packet_list_ ,
&recovered_packet_list_));
// Unprotected packet lost. Recovery not possible.
EXPECT_FALSE(IsRecoveryComplete());
FreeRecoveredPacketList();
// 6 media packets lost.
memset(media_loss_mask_, 0, sizeof(media_loss_mask_));
memset(fec_loss_mask_, 0, sizeof(fec_loss_mask_));
media_loss_mask_[num_media_packets - 11] = 1;
media_loss_mask_[num_media_packets - 9] = 1;
media_loss_mask_[num_media_packets - 7] = 1;
media_loss_mask_[num_media_packets - 5] = 1;
media_loss_mask_[num_media_packets - 3] = 1;
media_loss_mask_[num_media_packets - 1] = 1;
NetworkReceivedPackets();
EXPECT_EQ(0, fec_->DecodeFEC(&received_packet_list_ ,
&recovered_packet_list_));
// 5 protected packets lost, one FEC packet, cannot get complete recovery.
EXPECT_FALSE(IsRecoveryComplete());
}
// TODO(marpan): Add more test cases.
void RtpFecTest::TearDown() {
@ -420,9 +641,9 @@ bool RtpFecTest::IsRecoveryComplete() {
bool recovery = true;
std::list<ForwardErrorCorrection::Packet*>::iterator
PacketList::iterator
media_packet_list_item = media_packet_list_.begin();
std::list<ForwardErrorCorrection::RecoveredPacket*>::iterator
RecoveredPacketList::iterator
recovered_packet_list_item = recovered_packet_list_.begin();
while (media_packet_list_item != media_packet_list_.end()) {
if (recovered_packet_list_item == recovered_packet_list_.end()) {
@ -450,7 +671,7 @@ void RtpFecTest::NetworkReceivedPackets() {
}
void RtpFecTest:: ReceivedPackets(
const std::list<ForwardErrorCorrection::Packet*>& packet_list,
const PacketList& packet_list,
int* loss_mask,
bool is_fec) {
ForwardErrorCorrection::Packet* packet;
@ -458,7 +679,7 @@ void RtpFecTest:: ReceivedPackets(
int seq_num = fec_seq_num_;
int packet_idx = 0;
std::list<ForwardErrorCorrection::Packet*>::const_iterator
PacketList::const_iterator
packet_list_item = packet_list.begin();
while (packet_list_item != packet_list.end()) {
@ -496,10 +717,11 @@ void RtpFecTest:: ReceivedPackets(
}
}
int RtpFecTest::ConstructMediaPackets(int num_media_packets) {
int RtpFecTest::ConstructMediaPacketsSeqNum(int num_media_packets,
int start_seq_num) {
assert(num_media_packets > 0);
ForwardErrorCorrection::Packet* media_packet = NULL;
int sequence_number = rand();
int sequence_number = start_seq_num;
int time_stamp = rand();
for (int i = 0; i < num_media_packets; i++) {
@ -548,3 +770,7 @@ int RtpFecTest::ConstructMediaPackets(int num_media_packets) {
media_packet->data[1] |= 0x80;
return sequence_number;
}
int RtpFecTest::ConstructMediaPackets(int num_media_packets) {
return ConstructMediaPacketsSeqNum(num_media_packets, rand());
}

View File

@ -131,7 +131,8 @@ WebRtc_Word32
RTPSenderVideo::SendVideoPacket(const WebRtc_UWord8* data_buffer,
const WebRtc_UWord16 payload_length,
const WebRtc_UWord16 rtp_header_length,
StorageType storage) {
StorageType storage,
bool protect) {
if(_fecEnabled) {
int ret = 0;
int fec_overhead_sent = 0;
@ -156,11 +157,13 @@ RTPSenderVideo::SendVideoPacket(const WebRtc_UWord8* data_buffer,
delete red_packet;
red_packet = NULL;
ret = producer_fec_.AddRtpPacketAndGenerateFec(data_buffer,
payload_length,
rtp_header_length);
if (ret != 0)
return ret;
if (protect) {
ret = producer_fec_.AddRtpPacketAndGenerateFec(data_buffer,
payload_length,
rtp_header_length);
if (ret != 0)
return ret;
}
while (producer_fec_.FecAvailable()) {
red_packet = producer_fec_.GetFecPacket(
@ -283,13 +286,6 @@ RTPSenderVideo::SendVideo(const RtpVideoCodecTypes videoType,
if (frameType == kVideoFrameKey) {
producer_fec_.SetFecParameters(&key_fec_params_,
_numberFirstPartition);
} else if (videoType == kRtpVp8Video && rtpTypeHdr->VP8.temporalIdx > 0) {
// In current version, we only apply FEC on the base layer.
FecProtectionParams params;
params.fec_rate = 0;
params.max_fec_frames = 0;
params.use_uep_protection = false;
producer_fec_.SetFecParameters(&params, _numberFirstPartition);
} else {
producer_fec_.SetFecParameters(&delta_fec_params_,
_numberFirstPartition);
@ -373,7 +369,8 @@ RTPSenderVideo::SendGeneric(const WebRtc_Word8 payloadType,
if(-1 == SendVideoPacket(dataBuffer,
payloadBytesInPacket,
rtpHeaderLength,
kAllowRetransmission))
kAllowRetransmission,
true))
{
return -1;
}
@ -433,6 +430,9 @@ RTPSenderVideo::SendVP8(const FrameType frameType,
bool last = false;
_numberFirstPartition = 0;
// |rtpTypeHdr->VP8.temporalIdx| is zero for base layers, or -1 if the field
// isn't used. We currently only protect base layers.
bool protect = (rtpTypeHdr->VP8.temporalIdx < 1);
while (!last)
{
// Write VP8 Payload Descriptor and VP8 payload.
@ -455,7 +455,7 @@ RTPSenderVideo::SendVP8(const FrameType frameType,
_rtpSender.BuildRTPheader(dataBuffer, payloadType, last,
captureTimeStamp);
if (-1 == SendVideoPacket(dataBuffer, payloadBytesInPacket,
rtpHeaderLength, storage))
rtpHeaderLength, storage, protect))
{
WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id,
"RTPSenderVideo::SendVP8 failed to send packet number"

View File

@ -95,7 +95,8 @@ protected:
virtual WebRtc_Word32 SendVideoPacket(const WebRtc_UWord8* dataBuffer,
const WebRtc_UWord16 payloadLength,
const WebRtc_UWord16 rtpHeaderLength,
StorageType storage);
StorageType storage,
bool protect);
private:
WebRtc_Word32 SendGeneric(const WebRtc_Word8 payloadType,

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2011 The WebRTC project authors. All Rights Reserved.
* Copyright (c) 2012 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
@ -10,6 +10,7 @@
#include "modules/video_coding/main/source/media_opt_util.h"
#include <algorithm>
#include <math.h>
#include <float.h>
#include <limits.h>
@ -55,7 +56,8 @@ VCMNackFecMethod::VCMNackFecMethod(int lowRttNackThresholdMs,
int highRttNackThresholdMs)
: VCMFecMethod(),
_lowRttNackMs(lowRttNackThresholdMs),
_highRttNackMs(highRttNackThresholdMs) {
_highRttNackMs(highRttNackThresholdMs),
_maxFramesFec(1) {
assert(lowRttNackThresholdMs >= -1 && highRttNackThresholdMs >= -1);
assert(highRttNackThresholdMs == -1 ||
lowRttNackThresholdMs <= highRttNackThresholdMs);
@ -110,6 +112,35 @@ VCMNackFecMethod::ProtectionFactor(const VCMProtectionParameters* parameters)
return true;
}
int VCMNackFecMethod::ComputeMaxFramesFec(
const VCMProtectionParameters* parameters) {
if (parameters->numLayers > 2) {
// For more than 2 temporal layers we will only have FEC on the base layer,
// and the base layers will be pretty far apart. Therefore we force one
// frame FEC.
return 1;
}
// We set the max number of frames to base the FEC on so that on average
// we will have complete frames in one RTT. Note that this is an upper
// bound, and that the actual number of frames used for FEC is decided by the
// RTP module based on the actual number of packets and the protection factor.
float base_layer_framerate = parameters->frameRate /
static_cast<float>(1 << (parameters->numLayers - 1));
int max_frames_fec = std::max(static_cast<int>(
2.0f * base_layer_framerate * parameters->rtt /
1000.0f + 0.5f), 1);
// |kUpperLimitFramesFec| is the upper limit on how many frames we
// allow any FEC to be based on.
if (max_frames_fec > kUpperLimitFramesFec) {
max_frames_fec = kUpperLimitFramesFec;
}
return max_frames_fec;
}
int VCMNackFecMethod::MaxFramesFec() const {
return _maxFramesFec;
}
bool
VCMNackFecMethod::EffectivePacketLoss(const VCMProtectionParameters* parameters)
{
@ -124,6 +155,7 @@ VCMNackFecMethod::UpdateParameters(const VCMProtectionParameters* parameters)
{
ProtectionFactor(parameters);
EffectivePacketLoss(parameters);
_maxFramesFec = ComputeMaxFramesFec(parameters);
// Efficiency computation is based on FEC and NACK

View File

@ -144,6 +144,8 @@ public:
// Return value : Required Unequal protection on/off state.
virtual bool RequiredUepProtectionD() { return _useUepProtectionD; }
virtual int MaxFramesFec() const { return 1; }
// Updates content metrics
void UpdateContentMetrics(const VideoContentMetrics* contentMetrics);
@ -198,6 +200,9 @@ public:
void UpdateProtectionFactorK(WebRtc_UWord8 protectionFactorK);
// Compute the bits per frame. Account for temporal layers when applicable.
int BitsPerFrame(const VCMProtectionParameters* parameters);
protected:
enum { kUpperLimitFramesFec = 6 };
};
@ -212,10 +217,15 @@ public:
bool EffectivePacketLoss(const VCMProtectionParameters* parameters);
// Get the protection factors
bool ProtectionFactor(const VCMProtectionParameters* parameters);
// Get the max number of frames the FEC is allowed to be based on.
int MaxFramesFec() const;
private:
int ComputeMaxFramesFec(const VCMProtectionParameters* parameters);
int _lowRttNackMs;
int _highRttNackMs;
int _maxFramesFec;
};
class VCMLossProtectionLogic

View File

@ -224,8 +224,10 @@ int VCMMediaOptimization::UpdateProtectionCallback(
delta_fec_params.use_uep_protection =
selected_method->RequiredUepProtectionD();
delta_fec_params.max_fec_frames = 1;
key_fec_params.max_fec_frames = 1;
// The RTP module currently requires the same |max_fec_frames| for both
// key and delta frames.
delta_fec_params.max_fec_frames = selected_method->MaxFramesFec();
key_fec_params.max_fec_frames = selected_method->MaxFramesFec();
// TODO(Marco): Pass FEC protection values per layer.
return _videoProtectionCallback->ProtectionRequest(&delta_fec_params,