Style updates for ForwardErrorCorrection and related classes.

This CL mainly updates the FEC code to use more C++11 features and
to be more in line with the style guide. These changes should
have no impact on the functionality provided by the FEC.

Summary of style fixes:
- Use range-based for loops, where applicable.
- Use auto type deduction for iterator type names.
- Use RTC_DCHECK instead of assert.
- Rename FEC to Fec, where applicable.
- Update test_fec.cc to use variable_names rather than variableNames.
- Avoid redefining the PacketList types outside ForwardErrorCorrection.

Another minor change is that storage for the packet masks, as these
are generated, now is provided by a member variable, rather than
being dynamically allocated on every call to GenerateFec.

BUG=webrtc:5654
R=danilchap@webrtc.org, stefan@webrtc.org

Review URL: https://codereview.webrtc.org/2080553003 .

Cr-Commit-Position: refs/heads/master@{#13403}
This commit is contained in:
Rasmus Brandt 2016-07-07 09:40:51 +02:00
parent 9b522f8d04
commit ae4f7674e4
10 changed files with 532 additions and 577 deletions

View File

@ -10,10 +10,9 @@
#include "webrtc/modules/rtp_rtcp/source/fec_receiver_impl.h"
#include <assert.h>
#include <memory>
#include "webrtc/base/checks.h"
#include "webrtc/base/logging.h"
#include "webrtc/modules/rtp_rtcp/source/byte_io.h"
#include "webrtc/modules/rtp_rtcp/source/rtp_receiver_video.h"
@ -89,8 +88,8 @@ int32_t FecReceiverImpl::AddReceivedRedPacket(
// we remove the RED header
std::unique_ptr<ForwardErrorCorrection::ReceivedPacket> received_packet(
new ForwardErrorCorrection::ReceivedPacket);
received_packet->pkt = new ForwardErrorCorrection::Packet;
new ForwardErrorCorrection::ReceivedPacket());
received_packet->pkt = new ForwardErrorCorrection::Packet();
// get payload type from RED header
uint8_t payload_type =
@ -159,8 +158,8 @@ int32_t FecReceiverImpl::AddReceivedRedPacket(
received_packet->pkt->length = blockLength;
second_received_packet.reset(new ForwardErrorCorrection::ReceivedPacket);
second_received_packet->pkt = new ForwardErrorCorrection::Packet;
second_received_packet.reset(new ForwardErrorCorrection::ReceivedPacket());
second_received_packet->pkt = new ForwardErrorCorrection::Packet();
second_received_packet->is_fec = true;
second_received_packet->seq_num = header.sequenceNumber;
@ -231,19 +230,19 @@ int32_t FecReceiverImpl::ProcessReceivedFec() {
}
crit_sect_.Enter();
}
if (fec_->DecodeFEC(&received_packet_list_, &recovered_packet_list_) != 0) {
if (fec_->DecodeFec(&received_packet_list_, &recovered_packet_list_) != 0) {
crit_sect_.Leave();
return -1;
}
assert(received_packet_list_.empty());
RTC_DCHECK(received_packet_list_.empty());
}
// Send any recovered media packets to VCM.
ForwardErrorCorrection::RecoveredPacketList::iterator it =
recovered_packet_list_.begin();
for (; it != recovered_packet_list_.end(); ++it) {
if ((*it)->returned) // Already sent to the VCM and the jitter buffer.
for(auto* recovered_packet : recovered_packet_list_) {
if (recovered_packet->returned) {
// Already sent to the VCM and the jitter buffer.
continue;
ForwardErrorCorrection::Packet* packet = (*it)->pkt;
}
ForwardErrorCorrection::Packet* packet = recovered_packet->pkt;
++packet_counter_.num_recovered_packets;
crit_sect_.Leave();
if (!recovered_packet_callback_->OnRecoveredPacket(packet->data,
@ -251,7 +250,7 @@ int32_t FecReceiverImpl::ProcessReceivedFec() {
return -1;
}
crit_sect_.Enter();
(*it)->returned = true;
recovered_packet->returned = true;
}
crit_sect_.Leave();
return 0;

View File

@ -38,11 +38,11 @@ class ReceiverFecTest : public ::testing::Test {
generator_.reset(new FrameGenerator());
}
void GenerateFEC(std::list<Packet*>* media_packets,
std::list<Packet*>* fec_packets,
void GenerateFec(ForwardErrorCorrection::PacketList* media_packets,
ForwardErrorCorrection::PacketList* fec_packets,
unsigned int num_fec_packets) {
uint8_t protection_factor = num_fec_packets * 255 / media_packets->size();
EXPECT_EQ(0, fec_->GenerateFEC(*media_packets, protection_factor,
EXPECT_EQ(0, fec_->GenerateFec(*media_packets, protection_factor,
0, false, kFecMaskBursty, fec_packets));
ASSERT_EQ(num_fec_packets, fec_packets->size());
}
@ -50,7 +50,7 @@ class ReceiverFecTest : public ::testing::Test {
void GenerateFrame(int num_media_packets,
int frame_offset,
std::list<test::RawRtpPacket*>* media_rtp_packets,
std::list<Packet*>* media_packets) {
ForwardErrorCorrection::PacketList* media_packets) {
generator_->NewFrame(num_media_packets);
for (int i = 0; i < num_media_packets; ++i) {
media_rtp_packets->push_back(
@ -97,7 +97,7 @@ class ReceiverFecTest : public ::testing::Test {
std::unique_ptr<FrameGenerator> generator_;
};
void DeletePackets(std::list<Packet*>* packets) {
void DeletePackets(ForwardErrorCorrection::PacketList* packets) {
while (!packets->empty()) {
delete packets->front();
packets->pop_front();
@ -107,18 +107,18 @@ void DeletePackets(std::list<Packet*>* packets) {
TEST_F(ReceiverFecTest, TwoMediaOneFec) {
const unsigned int kNumFecPackets = 1u;
std::list<test::RawRtpPacket*> media_rtp_packets;
std::list<Packet*> media_packets;
ForwardErrorCorrection::PacketList media_packets;
GenerateFrame(2, 0, &media_rtp_packets, &media_packets);
std::list<Packet*> fec_packets;
GenerateFEC(&media_packets, &fec_packets, kNumFecPackets);
ForwardErrorCorrection::PacketList fec_packets;
GenerateFec(&media_packets, &fec_packets, kNumFecPackets);
// Recovery
std::list<test::RawRtpPacket*>::iterator it = media_rtp_packets.begin();
auto it = media_rtp_packets.begin();
BuildAndAddRedMediaPacket(*it);
VerifyReconstructedMediaPacket(*it, 1);
EXPECT_EQ(0, receiver_fec_->ProcessReceivedFec());
// Drop one media packet.
std::list<Packet*>::iterator fec_it = fec_packets.begin();
auto fec_it = fec_packets.begin();
BuildAndAddRedFecPacket(*fec_it);
++it;
VerifyReconstructedMediaPacket(*it, 1);
@ -138,10 +138,10 @@ void ReceiverFecTest::InjectGarbagePacketLength(size_t fec_garbage_offset) {
const unsigned int kNumFecPackets = 1u;
std::list<test::RawRtpPacket*> media_rtp_packets;
std::list<Packet*> media_packets;
ForwardErrorCorrection::PacketList media_packets;
GenerateFrame(2, 0, &media_rtp_packets, &media_packets);
std::list<Packet*> fec_packets;
GenerateFEC(&media_packets, &fec_packets, kNumFecPackets);
ForwardErrorCorrection::PacketList fec_packets;
GenerateFec(&media_packets, &fec_packets, kNumFecPackets);
ByteWriter<uint16_t>::WriteBigEndian(
&fec_packets.front()->data[fec_garbage_offset], 0x4711);
@ -173,15 +173,15 @@ TEST_F(ReceiverFecTest, InjectGarbageFecLevelHeaderProtectionLength) {
TEST_F(ReceiverFecTest, TwoMediaTwoFec) {
const unsigned int kNumFecPackets = 2u;
std::list<test::RawRtpPacket*> media_rtp_packets;
std::list<Packet*> media_packets;
ForwardErrorCorrection::PacketList media_packets;
GenerateFrame(2, 0, &media_rtp_packets, &media_packets);
std::list<Packet*> fec_packets;
GenerateFEC(&media_packets, &fec_packets, kNumFecPackets);
ForwardErrorCorrection::PacketList fec_packets;
GenerateFec(&media_packets, &fec_packets, kNumFecPackets);
// Recovery
// Drop both media packets.
std::list<test::RawRtpPacket*>::iterator it = media_rtp_packets.begin();
std::list<Packet*>::iterator fec_it = fec_packets.begin();
auto it = media_rtp_packets.begin();
auto fec_it = fec_packets.begin();
BuildAndAddRedFecPacket(*fec_it);
VerifyReconstructedMediaPacket(*it, 1);
EXPECT_EQ(0, receiver_fec_->ProcessReceivedFec());
@ -197,14 +197,14 @@ TEST_F(ReceiverFecTest, TwoMediaTwoFec) {
TEST_F(ReceiverFecTest, TwoFramesOneFec) {
const unsigned int kNumFecPackets = 1u;
std::list<test::RawRtpPacket*> media_rtp_packets;
std::list<Packet*> media_packets;
ForwardErrorCorrection::PacketList media_packets;
GenerateFrame(1, 0, &media_rtp_packets, &media_packets);
GenerateFrame(1, 1, &media_rtp_packets, &media_packets);
std::list<Packet*> fec_packets;
GenerateFEC(&media_packets, &fec_packets, kNumFecPackets);
ForwardErrorCorrection::PacketList fec_packets;
GenerateFec(&media_packets, &fec_packets, kNumFecPackets);
// Recovery
std::list<test::RawRtpPacket*>::iterator it = media_rtp_packets.begin();
auto it = media_rtp_packets.begin();
BuildAndAddRedMediaPacket(media_rtp_packets.front());
VerifyReconstructedMediaPacket(*it, 1);
EXPECT_EQ(0, receiver_fec_->ProcessReceivedFec());
@ -220,15 +220,15 @@ TEST_F(ReceiverFecTest, TwoFramesOneFec) {
TEST_F(ReceiverFecTest, OneCompleteOneUnrecoverableFrame) {
const unsigned int kNumFecPackets = 1u;
std::list<test::RawRtpPacket*> media_rtp_packets;
std::list<Packet*> media_packets;
ForwardErrorCorrection::PacketList media_packets;
GenerateFrame(1, 0, &media_rtp_packets, &media_packets);
GenerateFrame(2, 1, &media_rtp_packets, &media_packets);
std::list<Packet*> fec_packets;
GenerateFEC(&media_packets, &fec_packets, kNumFecPackets);
ForwardErrorCorrection::PacketList fec_packets;
GenerateFec(&media_packets, &fec_packets, kNumFecPackets);
// Recovery
std::list<test::RawRtpPacket*>::iterator it = media_rtp_packets.begin();
auto it = media_rtp_packets.begin();
BuildAndAddRedMediaPacket(*it); // First frame: one packet.
VerifyReconstructedMediaPacket(*it, 1);
EXPECT_EQ(0, receiver_fec_->ProcessReceivedFec());
@ -244,15 +244,15 @@ TEST_F(ReceiverFecTest, MaxFramesOneFec) {
const unsigned int kNumFecPackets = 1u;
const unsigned int kNumMediaPackets = 48u;
std::list<test::RawRtpPacket*> media_rtp_packets;
std::list<Packet*> media_packets;
ForwardErrorCorrection::PacketList media_packets;
for (unsigned int i = 0; i < kNumMediaPackets; ++i) {
GenerateFrame(1, i, &media_rtp_packets, &media_packets);
}
std::list<Packet*> fec_packets;
GenerateFEC(&media_packets, &fec_packets, kNumFecPackets);
ForwardErrorCorrection::PacketList fec_packets;
GenerateFec(&media_packets, &fec_packets, kNumFecPackets);
// Recovery
std::list<test::RawRtpPacket*>::iterator it = media_rtp_packets.begin();
auto it = media_rtp_packets.begin();
++it; // Drop first packet.
for (; it != media_rtp_packets.end(); ++it) {
BuildAndAddRedMediaPacket(*it);
@ -271,12 +271,12 @@ TEST_F(ReceiverFecTest, TooManyFrames) {
const unsigned int kNumFecPackets = 1u;
const unsigned int kNumMediaPackets = 49u;
std::list<test::RawRtpPacket*> media_rtp_packets;
std::list<Packet*> media_packets;
ForwardErrorCorrection::PacketList media_packets;
for (unsigned int i = 0; i < kNumMediaPackets; ++i) {
GenerateFrame(1, i, &media_rtp_packets, &media_packets);
}
std::list<Packet*> fec_packets;
EXPECT_EQ(-1, fec_->GenerateFEC(media_packets,
ForwardErrorCorrection::PacketList fec_packets;
EXPECT_EQ(-1, fec_->GenerateFec(media_packets,
kNumFecPackets * 255 / kNumMediaPackets, 0,
false, kFecMaskBursty, &fec_packets));
@ -290,11 +290,11 @@ TEST_F(ReceiverFecTest, PacketNotDroppedTooEarly) {
const unsigned int kNumFecPacketsBatch1 = 1u;
const unsigned int kNumMediaPacketsBatch1 = 2u;
std::list<test::RawRtpPacket*> media_rtp_packets_batch1;
std::list<Packet*> media_packets_batch1;
ForwardErrorCorrection::PacketList media_packets_batch1;
GenerateFrame(kNumMediaPacketsBatch1, 0, &media_rtp_packets_batch1,
&media_packets_batch1);
std::list<Packet*> fec_packets;
GenerateFEC(&media_packets_batch1, &fec_packets, kNumFecPacketsBatch1);
ForwardErrorCorrection::PacketList fec_packets;
GenerateFec(&media_packets_batch1, &fec_packets, kNumFecPacketsBatch1);
BuildAndAddRedMediaPacket(media_rtp_packets_batch1.front());
EXPECT_CALL(rtp_data_callback_, OnRecoveredPacket(_, _))
@ -305,12 +305,11 @@ TEST_F(ReceiverFecTest, PacketNotDroppedTooEarly) {
// Fill the FEC decoder. No packets should be dropped.
const unsigned int kNumMediaPacketsBatch2 = 46u;
std::list<test::RawRtpPacket*> media_rtp_packets_batch2;
std::list<Packet*> media_packets_batch2;
ForwardErrorCorrection::PacketList media_packets_batch2;
for (unsigned int i = 0; i < kNumMediaPacketsBatch2; ++i) {
GenerateFrame(1, i, &media_rtp_packets_batch2, &media_packets_batch2);
}
for (std::list<test::RawRtpPacket*>::iterator it =
media_rtp_packets_batch2.begin();
for (auto it = media_rtp_packets_batch2.begin();
it != media_rtp_packets_batch2.end(); ++it) {
BuildAndAddRedMediaPacket(*it);
EXPECT_CALL(rtp_data_callback_, OnRecoveredPacket(_, _))
@ -335,11 +334,11 @@ TEST_F(ReceiverFecTest, PacketDroppedWhenTooOld) {
const unsigned int kNumFecPacketsBatch1 = 1u;
const unsigned int kNumMediaPacketsBatch1 = 2u;
std::list<test::RawRtpPacket*> media_rtp_packets_batch1;
std::list<Packet*> media_packets_batch1;
ForwardErrorCorrection::PacketList media_packets_batch1;
GenerateFrame(kNumMediaPacketsBatch1, 0, &media_rtp_packets_batch1,
&media_packets_batch1);
std::list<Packet*> fec_packets;
GenerateFEC(&media_packets_batch1, &fec_packets, kNumFecPacketsBatch1);
ForwardErrorCorrection::PacketList fec_packets;
GenerateFec(&media_packets_batch1, &fec_packets, kNumFecPacketsBatch1);
BuildAndAddRedMediaPacket(media_rtp_packets_batch1.front());
EXPECT_CALL(rtp_data_callback_, OnRecoveredPacket(_, _))
@ -350,12 +349,11 @@ TEST_F(ReceiverFecTest, PacketDroppedWhenTooOld) {
// Fill the FEC decoder and force the last packet to be dropped.
const unsigned int kNumMediaPacketsBatch2 = 48u;
std::list<test::RawRtpPacket*> media_rtp_packets_batch2;
std::list<Packet*> media_packets_batch2;
ForwardErrorCorrection::PacketList media_packets_batch2;
for (unsigned int i = 0; i < kNumMediaPacketsBatch2; ++i) {
GenerateFrame(1, i, &media_rtp_packets_batch2, &media_packets_batch2);
}
for (std::list<test::RawRtpPacket*>::iterator it =
media_rtp_packets_batch2.begin();
for (auto it = media_rtp_packets_batch2.begin();
it != media_rtp_packets_batch2.end(); ++it) {
BuildAndAddRedMediaPacket(*it);
EXPECT_CALL(rtp_data_callback_, OnRecoveredPacket(_, _))
@ -379,15 +377,14 @@ TEST_F(ReceiverFecTest, OldFecPacketDropped) {
// missing.
const unsigned int kNumMediaPackets = 49 * 2;
std::list<test::RawRtpPacket*> media_rtp_packets;
std::list<Packet*> media_packets;
ForwardErrorCorrection::PacketList media_packets;
for (unsigned int i = 0; i < kNumMediaPackets / 2; ++i) {
std::list<test::RawRtpPacket*> frame_media_rtp_packets;
std::list<Packet*> frame_media_packets;
std::list<Packet*> fec_packets;
ForwardErrorCorrection::PacketList frame_media_packets;
ForwardErrorCorrection::PacketList fec_packets;
GenerateFrame(2, 0, &frame_media_rtp_packets, &frame_media_packets);
GenerateFEC(&frame_media_packets, &fec_packets, 1);
for (std::list<Packet*>::iterator it = fec_packets.begin();
it != fec_packets.end(); ++it) {
GenerateFec(&frame_media_packets, &fec_packets, 1);
for (auto it = fec_packets.begin(); it != fec_packets.end(); ++it) {
// Only FEC packets inserted. No packets recoverable at this time.
BuildAndAddRedFecPacket(*it);
EXPECT_CALL(rtp_data_callback_, OnRecoveredPacket(_, _))

View File

@ -10,7 +10,6 @@
#include "webrtc/modules/rtp_rtcp/source/forward_error_correction.h"
#include <stdlib.h>
#include <string.h>
#include <algorithm>
@ -26,18 +25,19 @@
namespace webrtc {
// FEC header size in bytes.
const uint8_t kFecHeaderSize = 10;
constexpr size_t kFecHeaderSize = 10;
// ULP header size in bytes (L bit is set).
const uint8_t kUlpHeaderSizeLBitSet = (2 + kMaskSizeLBitSet);
constexpr size_t kUlpHeaderSizeLBitSet = (2 + kMaskSizeLBitSet);
// ULP header size in bytes (L bit is cleared).
const uint8_t kUlpHeaderSizeLBitClear = (2 + kMaskSizeLBitClear);
constexpr size_t kUlpHeaderSizeLBitClear = (2 + kMaskSizeLBitClear);
// Transport header size in bytes. Assume UDP/IPv4 as a reasonable minimum.
const uint8_t kTransportOverhead = 28;
constexpr size_t kTransportOverhead = 28;
enum { kMaxFecPackets = ForwardErrorCorrection::kMaxMediaPackets };
// Maximum number of FEC packets stored internally.
constexpr size_t kMaxFecPackets = ForwardErrorCorrection::kMaxMediaPackets;
int32_t ForwardErrorCorrection::Packet::AddRef() {
return ++ref_count_;
@ -85,8 +85,8 @@ ForwardErrorCorrection::RecoveredPacket::RecoveredPacket() {}
ForwardErrorCorrection::RecoveredPacket::~RecoveredPacket() {}
ForwardErrorCorrection::ForwardErrorCorrection()
: generated_fec_packets_(kMaxMediaPackets), fec_packet_received_(false) {}
: generated_fec_packets_(kMaxMediaPackets), fec_packet_list_(),
packet_mask_(), tmp_packet_mask_() {}
ForwardErrorCorrection::~ForwardErrorCorrection() {}
// Input packet
@ -106,18 +106,21 @@ ForwardErrorCorrection::~ForwardErrorCorrection() {}
// | FEC Level 0 Payload |
// | |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
int32_t ForwardErrorCorrection::GenerateFEC(const PacketList& media_packet_list,
uint8_t protection_factor,
int num_important_packets,
bool use_unequal_protection,
FecMaskType fec_mask_type,
PacketList* fec_packet_list) {
//
// Note that any potential RED headers are added/removed before calling
// GenerateFec() or DecodeFec().
int ForwardErrorCorrection::GenerateFec(const PacketList& media_packet_list,
uint8_t protection_factor,
int num_important_packets,
bool use_unequal_protection,
FecMaskType fec_mask_type,
PacketList* fec_packet_list) {
const uint16_t num_media_packets = media_packet_list.size();
// Sanity check arguments.
assert(num_media_packets > 0);
assert(num_important_packets >= 0 &&
num_important_packets <= num_media_packets);
assert(fec_packet_list->empty());
RTC_DCHECK_GT(num_media_packets, 0);
RTC_DCHECK_GE(num_important_packets, 0);
RTC_DCHECK_LE(num_important_packets, num_media_packets);
RTC_DCHECK(fec_packet_list->empty());
if (num_media_packets > kMaxMediaPackets) {
LOG(LS_WARNING) << "Can't protect " << num_media_packets
@ -130,7 +133,7 @@ int32_t ForwardErrorCorrection::GenerateFEC(const PacketList& media_packet_list,
// Do some error checking on the media packets.
for (Packet* media_packet : media_packet_list) {
assert(media_packet);
RTC_DCHECK(media_packet);
if (media_packet->length < kRtpHeaderSize) {
LOG(LS_WARNING) << "Media packet " << media_packet->length << " bytes "
@ -163,29 +166,26 @@ int32_t ForwardErrorCorrection::GenerateFEC(const PacketList& media_packet_list,
const internal::PacketMaskTable mask_table(fec_mask_type, num_media_packets);
// -- Generate packet masks --
// Always allocate space for a large mask.
std::unique_ptr<uint8_t[]> packet_mask(
new uint8_t[num_fec_packets * kMaskSizeLBitSet]);
memset(packet_mask.get(), 0, num_fec_packets * num_mask_bytes);
memset(packet_mask_, 0, num_fec_packets * num_mask_bytes);
internal::GeneratePacketMasks(num_media_packets, num_fec_packets,
num_important_packets, use_unequal_protection,
mask_table, packet_mask.get());
mask_table, packet_mask_);
int num_mask_bits = InsertZerosInBitMasks(
media_packet_list, packet_mask.get(), num_mask_bytes, num_fec_packets);
media_packet_list, packet_mask_, num_mask_bytes, num_fec_packets);
if (num_mask_bits < 0) {
return -1;
}
l_bit = (num_mask_bits > 8 * kMaskSizeLBitClear);
l_bit = (static_cast<size_t>(num_mask_bits) > 8 * kMaskSizeLBitClear);
if (l_bit) {
num_mask_bytes = kMaskSizeLBitSet;
}
GenerateFecBitStrings(media_packet_list, packet_mask.get(), num_fec_packets,
l_bit);
GenerateFecUlpHeaders(media_packet_list, packet_mask.get(), l_bit,
num_fec_packets);
GenerateFecBitStrings(media_packet_list, packet_mask_,
num_fec_packets, l_bit);
GenerateFecUlpHeaders(media_packet_list, packet_mask_,
num_fec_packets, l_bit);
return 0;
}
@ -198,7 +198,7 @@ int ForwardErrorCorrection::GetNumberOfFecPackets(int num_media_packets,
if (protection_factor > 0 && num_fec_packets == 0) {
num_fec_packets = 1;
}
assert(num_fec_packets <= num_media_packets);
RTC_DCHECK_LE(num_fec_packets, num_media_packets);
return num_fec_packets;
}
@ -207,9 +207,7 @@ void ForwardErrorCorrection::GenerateFecBitStrings(
uint8_t* packet_mask,
int num_fec_packets,
bool l_bit) {
if (media_packet_list.empty()) {
return;
}
RTC_DCHECK(!media_packet_list.empty());
uint8_t media_payload_length[2];
const int num_mask_bytes = l_bit ? kMaskSizeLBitSet : kMaskSizeLBitClear;
const uint16_t ulp_header_size =
@ -219,7 +217,7 @@ void ForwardErrorCorrection::GenerateFecBitStrings(
for (int i = 0; i < num_fec_packets; ++i) {
Packet* const fec_packet = &generated_fec_packets_[i];
PacketList::const_iterator media_list_it = media_packet_list.begin();
auto media_list_it = media_packet_list.cbegin();
uint32_t pkt_mask_idx = i * num_mask_bytes;
uint32_t media_pkt_idx = 0;
uint16_t fec_packet_length = 0;
@ -237,9 +235,10 @@ void ForwardErrorCorrection::GenerateFecBitStrings(
fec_packet_length = media_packet->length + fec_rtp_offset;
// On the first protected packet, we don't need to XOR.
if (fec_packet->length == 0) {
// Copy the first 2 bytes of the RTP header.
memcpy(fec_packet->data, media_packet->data, 2);
// Copy the 5th to 8th bytes of the RTP header.
// Copy the first 2 bytes of the RTP header. Note that the E and L
// bits are overwritten in GenerateFecUlpHeaders.
memcpy(&fec_packet->data[0], &media_packet->data[0], 2);
// Copy the 5th to 8th bytes of the RTP header (timestamp).
memcpy(&fec_packet->data[4], &media_packet->data[4], 4);
// Copy network-ordered payload size.
memcpy(&fec_packet->data[8], media_payload_length, 2);
@ -291,7 +290,6 @@ int ForwardErrorCorrection::InsertZerosInBitMasks(
uint8_t* packet_mask,
int num_mask_bytes,
int num_fec_packets) {
uint8_t* new_mask = NULL;
if (media_packets.size() <= 1) {
return media_packets.size();
}
@ -313,15 +311,14 @@ int ForwardErrorCorrection::InsertZerosInBitMasks(
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);
memset(tmp_packet_mask_, 0, num_fec_packets * kMaskSizeLBitSet);
PacketList::const_iterator it = media_packets.begin();
auto it = media_packets.cbegin();
uint16_t prev_seq_num = first_seq_num;
++it;
// Insert the first column.
CopyColumn(new_mask, new_mask_bytes, packet_mask, num_mask_bytes,
CopyColumn(tmp_packet_mask_, new_mask_bytes, packet_mask, num_mask_bytes,
num_fec_packets, 0, 0);
int new_bit_index = 1;
int old_bit_index = 1;
@ -335,11 +332,11 @@ int ForwardErrorCorrection::InsertZerosInBitMasks(
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,
InsertZeroColumns(zeros_to_insert, tmp_packet_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,
CopyColumn(tmp_packet_mask_, new_mask_bytes, packet_mask, num_mask_bytes,
num_fec_packets, new_bit_index, old_bit_index);
++new_bit_index;
++old_bit_index;
@ -349,12 +346,11 @@ int ForwardErrorCorrection::InsertZerosInBitMasks(
// 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));
tmp_packet_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;
memcpy(packet_mask, tmp_packet_mask_, kMaskSizeLBitSet * num_fec_packets);
return new_bit_index;
}
@ -393,8 +389,8 @@ void ForwardErrorCorrection::CopyColumn(uint8_t* new_mask,
void ForwardErrorCorrection::GenerateFecUlpHeaders(
const PacketList& media_packet_list,
uint8_t* packet_mask,
bool l_bit,
int num_fec_packets) {
int num_fec_packets,
bool l_bit) {
// -- Generate FEC and ULP headers --
//
// FEC Header, 10 bytes
@ -416,13 +412,14 @@ void ForwardErrorCorrection::GenerateFecUlpHeaders(
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | mask cont. (present only when L = 1) |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
PacketList::const_iterator media_list_it = media_packet_list.begin();
Packet* media_packet = *media_list_it;
assert(media_packet != NULL);
int num_mask_bytes = l_bit ? kMaskSizeLBitSet : kMaskSizeLBitClear;
const uint16_t ulp_header_size =
l_bit ? kUlpHeaderSizeLBitSet : kUlpHeaderSizeLBitClear;
RTC_DCHECK(!media_packet_list.empty());
Packet* first_media_packet = media_packet_list.front();
RTC_DCHECK(first_media_packet);
uint16_t seq_num = ParseSequenceNumber(first_media_packet->data);
for (int i = 0; i < num_fec_packets; ++i) {
Packet* const fec_packet = &generated_fec_packets_[i];
// -- FEC header --
@ -432,10 +429,10 @@ void ForwardErrorCorrection::GenerateFecUlpHeaders(
} else {
fec_packet->data[0] |= 0x40; // Set the L bit.
}
// Two byte sequence number from first RTP packet to SN base.
// Sequence number from first media packet used as SN base.
// We use the same sequence number base for every FEC packet,
// but that's not required in general.
memcpy(&fec_packet->data[2], &media_packet->data[2], 2);
ByteWriter<uint16_t>::WriteBigEndian(&fec_packet->data[2], seq_num);
// -- ULP header --
// Copy the payload size to the protection length field.
@ -452,72 +449,67 @@ void ForwardErrorCorrection::GenerateFecUlpHeaders(
void ForwardErrorCorrection::ResetState(
RecoveredPacketList* recovered_packet_list) {
fec_packet_received_ = false;
// Free the memory for any existing recovered packets, if the user hasn't.
while (!recovered_packet_list->empty()) {
delete recovered_packet_list->front();
recovered_packet_list->pop_front();
}
assert(recovered_packet_list->empty());
RTC_DCHECK(recovered_packet_list->empty());
// Free the FEC packet list.
while (!fec_packet_list_.empty()) {
FecPacketList::iterator fec_packet_list_it = fec_packet_list_.begin();
FecPacket* fec_packet = *fec_packet_list_it;
ProtectedPacketList::iterator protected_packet_list_it;
protected_packet_list_it = fec_packet->protected_pkt_list.begin();
FecPacket* fec_packet = fec_packet_list_.front();
auto protected_packet_list_it = fec_packet->protected_pkt_list.begin();
while (protected_packet_list_it != fec_packet->protected_pkt_list.end()) {
delete *protected_packet_list_it;
protected_packet_list_it =
fec_packet->protected_pkt_list.erase(protected_packet_list_it);
}
assert(fec_packet->protected_pkt_list.empty());
RTC_DCHECK(fec_packet->protected_pkt_list.empty());
delete fec_packet;
fec_packet_list_.pop_front();
}
assert(fec_packet_list_.empty());
RTC_DCHECK(fec_packet_list_.empty());
}
void ForwardErrorCorrection::InsertMediaPacket(
ReceivedPacket* rx_packet,
RecoveredPacketList* recovered_packet_list) {
RecoveredPacketList::iterator recovered_packet_list_it =
recovered_packet_list->begin();
auto recovered_packet_list_it = recovered_packet_list->cbegin();
// Search for duplicate packets.
while (recovered_packet_list_it != recovered_packet_list->end()) {
if (rx_packet->seq_num == (*recovered_packet_list_it)->seq_num) {
// Duplicate packet, no need to add to list.
// Delete duplicate media packet data.
rx_packet->pkt = NULL;
rx_packet->pkt = nullptr;
return;
}
recovered_packet_list_it++;
++recovered_packet_list_it;
}
RecoveredPacket* recoverd_packet_to_insert = new RecoveredPacket;
recoverd_packet_to_insert->was_recovered = false;
RecoveredPacket* recovered_packet_to_insert = new RecoveredPacket();
recovered_packet_to_insert->was_recovered = false;
// Inserted Media packet is already sent to VCM.
recoverd_packet_to_insert->returned = true;
recoverd_packet_to_insert->seq_num = rx_packet->seq_num;
recoverd_packet_to_insert->pkt = rx_packet->pkt;
recoverd_packet_to_insert->pkt->length = rx_packet->pkt->length;
recovered_packet_to_insert->returned = true;
recovered_packet_to_insert->seq_num = rx_packet->seq_num;
recovered_packet_to_insert->pkt = rx_packet->pkt;
recovered_packet_to_insert->pkt->length = rx_packet->pkt->length;
// TODO(holmer): Consider replacing this with a binary search for the right
// position, and then just insert the new packet. Would get rid of the sort.
recovered_packet_list->push_back(recoverd_packet_to_insert);
recovered_packet_list->push_back(recovered_packet_to_insert);
recovered_packet_list->sort(SortablePacket::LessThan);
UpdateCoveringFECPackets(recoverd_packet_to_insert);
UpdateCoveringFecPackets(recovered_packet_to_insert);
}
void ForwardErrorCorrection::UpdateCoveringFECPackets(RecoveredPacket* packet) {
for (FecPacketList::iterator it = fec_packet_list_.begin();
it != fec_packet_list_.end(); ++it) {
void ForwardErrorCorrection::UpdateCoveringFecPackets(RecoveredPacket* packet) {
for (auto* fec_packet : fec_packet_list_) {
// Is this FEC packet protecting the media packet |packet|?
ProtectedPacketList::iterator protected_it = std::lower_bound(
(*it)->protected_pkt_list.begin(), (*it)->protected_pkt_list.end(),
packet, SortablePacket::LessThan);
if (protected_it != (*it)->protected_pkt_list.end() &&
auto protected_it = std::lower_bound(fec_packet->protected_pkt_list.begin(),
fec_packet->protected_pkt_list.end(),
packet,
SortablePacket::LessThan);
if (protected_it != fec_packet->protected_pkt_list.end() &&
(*protected_it)->seq_num == packet->seq_num) {
// Found an FEC packet which is protecting |packet|.
(*protected_it)->pkt = packet->pkt;
@ -525,22 +517,18 @@ void ForwardErrorCorrection::UpdateCoveringFECPackets(RecoveredPacket* packet) {
}
}
void ForwardErrorCorrection::InsertFECPacket(
void ForwardErrorCorrection::InsertFecPacket(
ReceivedPacket* rx_packet,
const RecoveredPacketList* recovered_packet_list) {
fec_packet_received_ = true;
// Check for duplicate.
FecPacketList::iterator fec_packet_list_it = fec_packet_list_.begin();
while (fec_packet_list_it != fec_packet_list_.end()) {
if (rx_packet->seq_num == (*fec_packet_list_it)->seq_num) {
for (auto* fec_packet : fec_packet_list_) {
if (rx_packet->seq_num == fec_packet->seq_num) {
// Delete duplicate FEC packet data.
rx_packet->pkt = NULL;
rx_packet->pkt = nullptr;
return;
}
fec_packet_list_it++;
}
FecPacket* fec_packet = new FecPacket;
FecPacket* fec_packet = new FecPacket();
fec_packet->pkt = rx_packet->pkt;
fec_packet->seq_num = rx_packet->seq_num;
fec_packet->ssrc = rx_packet->ssrc;
@ -555,12 +543,12 @@ void ForwardErrorCorrection::InsertFECPacket(
uint8_t packet_mask = fec_packet->pkt->data[12 + byte_idx];
for (uint16_t bit_idx = 0; bit_idx < 8; ++bit_idx) {
if (packet_mask & (1 << (7 - bit_idx))) {
ProtectedPacket* protected_packet = new ProtectedPacket;
ProtectedPacket* protected_packet = new ProtectedPacket();
fec_packet->protected_pkt_list.push_back(protected_packet);
// This wraps naturally with the sequence number.
protected_packet->seq_num =
static_cast<uint16_t>(seq_num_base + (byte_idx << 3) + bit_idx);
protected_packet->pkt = NULL;
protected_packet->pkt = nullptr;
}
}
}
@ -575,10 +563,10 @@ void ForwardErrorCorrection::InsertFECPacket(
fec_packet_list_.push_back(fec_packet);
fec_packet_list_.sort(SortablePacket::LessThan);
if (fec_packet_list_.size() > kMaxFecPackets) {
DiscardFECPacket(fec_packet_list_.front());
DiscardFecPacket(fec_packet_list_.front());
fec_packet_list_.pop_front();
}
assert(fec_packet_list_.size() <= kMaxFecPackets);
RTC_DCHECK_LE(fec_packet_list_.size(), kMaxFecPackets);
}
}
@ -590,14 +578,14 @@ void ForwardErrorCorrection::AssignRecoveredPackets(
ProtectedPacketList* not_recovered = &fec_packet->protected_pkt_list;
RecoveredPacketList already_recovered;
std::set_intersection(
recovered_packets->begin(), recovered_packets->end(),
not_recovered->begin(), not_recovered->end(),
recovered_packets->cbegin(), recovered_packets->cend(),
not_recovered->cbegin(), not_recovered->cend(),
std::inserter(already_recovered, already_recovered.end()),
SortablePacket::LessThan);
// Set the FEC pointers to all recovered packets so that we don't have to
// search for them when we are doing recovery.
ProtectedPacketList::iterator not_recovered_it = not_recovered->begin();
for (RecoveredPacketList::iterator it = already_recovered.begin();
auto not_recovered_it = not_recovered->cbegin();
for (auto it = already_recovered.cbegin();
it != already_recovered.end(); ++it) {
// Search for the next recovered packet in |not_recovered|.
while ((*not_recovered_it)->seq_num != (*it)->seq_num)
@ -624,13 +612,13 @@ void ForwardErrorCorrection::InsertPackets(
abs(static_cast<int>(rx_packet->seq_num) -
static_cast<int>(fec_packet_list_.front()->seq_num));
if (seq_num_diff > 0x3fff) {
DiscardFECPacket(fec_packet_list_.front());
DiscardFecPacket(fec_packet_list_.front());
fec_packet_list_.pop_front();
}
}
if (rx_packet->is_fec) {
InsertFECPacket(rx_packet, recovered_packet_list);
InsertFecPacket(rx_packet, recovered_packet_list);
} else {
// Insert packet at the end of |recoveredPacketList|.
InsertMediaPacket(rx_packet, recovered_packet_list);
@ -639,12 +627,12 @@ void ForwardErrorCorrection::InsertPackets(
delete rx_packet;
received_packet_list->pop_front();
}
assert(received_packet_list->empty());
RTC_DCHECK(received_packet_list->empty());
DiscardOldPackets(recovered_packet_list);
}
bool ForwardErrorCorrection::InitRecovery(const FecPacket* fec_packet,
RecoveredPacket* recovered) {
bool ForwardErrorCorrection::StartPacketRecovery(const FecPacket* fec_packet,
RecoveredPacket* recovered) {
// This is the first packet which we try to recover with.
const uint16_t ulp_header_size = fec_packet->pkt->data[0] & 0x40
? kUlpHeaderSizeLBitSet
@ -655,7 +643,7 @@ bool ForwardErrorCorrection::InitRecovery(const FecPacket* fec_packet,
<< "Truncated FEC packet doesn't contain room for ULP header.";
return false;
}
recovered->pkt = new Packet;
recovered->pkt = new Packet();
memset(recovered->pkt->data, 0, IP_PACKET_SIZE);
recovered->returned = false;
recovered->was_recovered = true;
@ -684,7 +672,7 @@ bool ForwardErrorCorrection::InitRecovery(const FecPacket* fec_packet,
return true;
}
bool ForwardErrorCorrection::FinishRecovery(RecoveredPacket* recovered) {
bool ForwardErrorCorrection::FinishPacketRecovery(RecoveredPacket* recovered) {
// Set the RTP version to 2.
recovered->pkt->data[0] |= 0x80; // Set the 1st bit.
recovered->pkt->data[0] &= 0xbf; // Clear the 2nd bit.
@ -729,27 +717,24 @@ void ForwardErrorCorrection::XorPackets(const Packet* src_packet,
bool ForwardErrorCorrection::RecoverPacket(
const FecPacket* fec_packet,
RecoveredPacket* rec_packet_to_insert) {
if (!InitRecovery(fec_packet, rec_packet_to_insert))
if (!StartPacketRecovery(fec_packet, rec_packet_to_insert))
return false;
ProtectedPacketList::const_iterator protected_it =
fec_packet->protected_pkt_list.begin();
while (protected_it != fec_packet->protected_pkt_list.end()) {
if ((*protected_it)->pkt == NULL) {
for (const auto* protected_packet : fec_packet->protected_pkt_list) {
if (protected_packet->pkt == nullptr) {
// This is the packet we're recovering.
rec_packet_to_insert->seq_num = (*protected_it)->seq_num;
rec_packet_to_insert->seq_num = protected_packet->seq_num;
} else {
XorPackets((*protected_it)->pkt, rec_packet_to_insert);
XorPackets(protected_packet->pkt, rec_packet_to_insert);
}
++protected_it;
}
if (!FinishRecovery(rec_packet_to_insert))
if (!FinishPacketRecovery(rec_packet_to_insert))
return false;
return true;
}
void ForwardErrorCorrection::AttemptRecover(
RecoveredPacketList* recovered_packet_list) {
FecPacketList::iterator fec_packet_list_it = fec_packet_list_.begin();
auto fec_packet_list_it = fec_packet_list_.begin();
while (fec_packet_list_it != fec_packet_list_.end()) {
// Search for each FEC packet's protected media packets.
int packets_missing = NumCoveredPacketsMissing(*fec_packet_list_it);
@ -757,11 +742,11 @@ void ForwardErrorCorrection::AttemptRecover(
// We can only recover one packet with an FEC packet.
if (packets_missing == 1) {
// Recovery possible.
RecoveredPacket* packet_to_insert = new RecoveredPacket;
packet_to_insert->pkt = NULL;
RecoveredPacket* packet_to_insert = new RecoveredPacket();
packet_to_insert->pkt = nullptr;
if (!RecoverPacket(*fec_packet_list_it, packet_to_insert)) {
// Can't recover using this packet, drop it.
DiscardFECPacket(*fec_packet_list_it);
DiscardFecPacket(*fec_packet_list_it);
fec_packet_list_it = fec_packet_list_.erase(fec_packet_list_it);
delete packet_to_insert;
continue;
@ -774,9 +759,9 @@ void ForwardErrorCorrection::AttemptRecover(
// the sort.
recovered_packet_list->push_back(packet_to_insert);
recovered_packet_list->sort(SortablePacket::LessThan);
UpdateCoveringFECPackets(packet_to_insert);
UpdateCoveringFecPackets(packet_to_insert);
DiscardOldPackets(recovered_packet_list);
DiscardFECPacket(*fec_packet_list_it);
DiscardFecPacket(*fec_packet_list_it);
fec_packet_list_it = fec_packet_list_.erase(fec_packet_list_it);
// A packet has been recovered. We need to check the FEC list again, as
@ -786,7 +771,7 @@ void ForwardErrorCorrection::AttemptRecover(
} else if (packets_missing == 0) {
// Either all protected packets arrived or have been recovered. We can
// discard this FEC packet.
DiscardFECPacket(*fec_packet_list_it);
DiscardFecPacket(*fec_packet_list_it);
fec_packet_list_it = fec_packet_list_.erase(fec_packet_list_it);
} else {
fec_packet_list_it++;
@ -797,10 +782,8 @@ void ForwardErrorCorrection::AttemptRecover(
int ForwardErrorCorrection::NumCoveredPacketsMissing(
const FecPacket* fec_packet) {
int packets_missing = 0;
ProtectedPacketList::const_iterator it =
fec_packet->protected_pkt_list.begin();
for (; it != fec_packet->protected_pkt_list.end(); ++it) {
if ((*it)->pkt == NULL) {
for (const auto* protected_packet : fec_packet->protected_pkt_list) {
if (protected_packet->pkt == nullptr) {
++packets_missing;
if (packets_missing > 1) {
break; // We can't recover more than one packet.
@ -810,12 +793,12 @@ int ForwardErrorCorrection::NumCoveredPacketsMissing(
return packets_missing;
}
void ForwardErrorCorrection::DiscardFECPacket(FecPacket* fec_packet) {
void ForwardErrorCorrection::DiscardFecPacket(FecPacket* fec_packet) {
while (!fec_packet->protected_pkt_list.empty()) {
delete fec_packet->protected_pkt_list.front();
fec_packet->protected_pkt_list.pop_front();
}
assert(fec_packet->protected_pkt_list.empty());
RTC_DCHECK(fec_packet->protected_pkt_list.empty());
delete fec_packet;
}
@ -827,14 +810,14 @@ void ForwardErrorCorrection::DiscardOldPackets(
delete packet;
recovered_packet_list->pop_front();
}
assert(recovered_packet_list->size() <= kMaxMediaPackets);
RTC_DCHECK(recovered_packet_list->size() <= kMaxMediaPackets);
}
uint16_t ForwardErrorCorrection::ParseSequenceNumber(uint8_t* packet) {
return (packet[2] << 8) + packet[3];
}
int32_t ForwardErrorCorrection::DecodeFEC(
int ForwardErrorCorrection::DecodeFec(
ReceivedPacketList* received_packet_list,
RecoveredPacketList* recovered_packet_list) {
// TODO(marpan/ajm): can we check for multiple ULP headers, and return an

View File

@ -11,12 +11,15 @@
#ifndef WEBRTC_MODULES_RTP_RTCP_SOURCE_FORWARD_ERROR_CORRECTION_H_
#define WEBRTC_MODULES_RTP_RTCP_SOURCE_FORWARD_ERROR_CORRECTION_H_
#include <stdint.h>
#include <list>
#include <vector>
#include "webrtc/base/refcount.h"
#include "webrtc/base/scoped_ref_ptr.h"
#include "webrtc/modules/rtp_rtcp/include/rtp_rtcp_defines.h"
#include "webrtc/modules/rtp_rtcp/source/forward_error_correction_internal.h"
#include "webrtc/typedefs.h"
namespace webrtc {
@ -31,7 +34,7 @@ class FecPacket;
class ForwardErrorCorrection {
public:
// Maximum number of media packets we can protect
static const unsigned int kMaxMediaPackets = 48u;
static constexpr size_t kMaxMediaPackets = 48u;
// TODO(holmer): As a next step all these struct-like packet classes should be
// refactored into proper classes, and their members should be made private.
@ -49,7 +52,7 @@ class ForwardErrorCorrection {
// reaches zero.
virtual int32_t Release();
size_t length; // Length of packet in bytes.
size_t length; // Length of packet in bytes.
uint8_t data[IP_PACKET_SIZE]; // Packet data.
private:
@ -66,17 +69,17 @@ class ForwardErrorCorrection {
uint16_t seq_num;
};
// The received list parameter of #DecodeFEC() must reference structs of this
// The received list parameter of #DecodeFec() must reference structs of this
// type. The last_media_pkt_in_frame is not required to be used for correct
// recovery, but will reduce delay by allowing #DecodeFEC() to pre-emptively
// recovery, but will reduce delay by allowing #DecodeFec() to pre-emptively
// determine frame completion. If set, we assume a FEC stream, and the
// following assumptions must hold:\n
// following assumptions must hold:
//
// 1. The media packets in a frame have contiguous sequence numbers, i.e. the
// frame's FEC packets have sequence numbers either lower than the first
// media packet or higher than the last media packet.\n
// media packet or higher than the last media packet.
// 2. All FEC packets have a sequence number base equal to the first media
// packet in the corresponding frame.\n
// packet in the corresponding frame.
//
// The ssrc member is needed to ensure we can restore the SSRC field of
// recovered packets. In most situations this could be retrieved from other
@ -95,7 +98,7 @@ class ForwardErrorCorrection {
rtc::scoped_refptr<Packet> pkt; // Pointer to the packet storage.
};
// The recovered list parameter of #DecodeFEC() will reference structs of
// The recovered list parameter of #DecodeFec() will reference structs of
// this type.
// TODO(holmer): Refactor into a proper class.
class RecoveredPacket : public SortablePacket {
@ -118,7 +121,6 @@ class ForwardErrorCorrection {
typedef std::list<RecoveredPacket*> RecoveredPacketList;
ForwardErrorCorrection();
virtual ~ForwardErrorCorrection();
/**
@ -153,14 +155,14 @@ class ForwardErrorCorrection {
* \param[out] fecPacketList List of FEC packets, of type #Packet. Must
* be empty on entry. The memory available
* through the list will be valid until the
* next call to GenerateFEC().
* next call to GenerateFec().
*
* \return 0 on success, -1 on failure.
*/
int32_t GenerateFEC(const PacketList& media_packet_list,
uint8_t protection_factor, int num_important_packets,
bool use_unequal_protection, FecMaskType fec_mask_type,
PacketList* fec_packet_list);
int GenerateFec(const PacketList& media_packet_list,
uint8_t protection_factor, int num_important_packets,
bool use_unequal_protection, FecMaskType fec_mask_type,
PacketList* fec_packet_list);
/**
* Decodes a list of media and FEC packets. It will parse the input received
@ -169,13 +171,13 @@ class ForwardErrorCorrection {
* ascending sequence number and have duplicates removed. The function
* should be called as new packets arrive, with the recovered list being
* progressively assembled with each call. The received packet list will be
* empty at output.\n
* empty at output.
*
* The user will allocate packets submitted through the received list. The
* function will handle allocation of recovered packets and optionally
* deleting of all packet memory. The user may delete the recovered list
* packets, in which case they must remove deleted packets from the
* recovered list.\n
* recovered list.
*
* \param[in] receivedPacketList List of new received packets, of type
* #ReceivedPacket, belonging to a single
@ -186,12 +188,12 @@ class ForwardErrorCorrection {
* #RecoveredPacket, belonging to a single
* frame. The memory available through the
* list will be valid until the next call to
* DecodeFEC().
* DecodeFec().
*
* \return 0 on success, -1 on failure.
*/
int32_t DecodeFEC(ReceivedPacketList* received_packet_list,
RecoveredPacketList* recovered_packet_list);
int DecodeFec(ReceivedPacketList* received_packet_list,
RecoveredPacketList* recovered_packet_list);
// Get the number of FEC packets, given the number of media packets and the
// protection factor.
@ -209,10 +211,6 @@ class ForwardErrorCorrection {
private:
typedef std::list<FecPacket*> FecPacketList;
void GenerateFecUlpHeaders(const PacketList& media_packet_list,
uint8_t* packet_mask, bool l_bit,
int num_fec_packets);
// 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.
@ -244,6 +242,10 @@ class ForwardErrorCorrection {
int num_fec_packets, int new_bit_index,
int old_bit_index);
void GenerateFecUlpHeaders(const PacketList& media_packet_list,
uint8_t* packet_mask, int num_fec_packets,
bool l_bit);
void GenerateFecBitStrings(const PacketList& media_packet_list,
uint8_t* packet_mask, int num_fec_packets,
bool l_bit);
@ -261,10 +263,10 @@ class ForwardErrorCorrection {
// Note: This reduces the complexity when we want to try to recover a packet
// since we don't have to find the intersection between recovered packets and
// packets covered by the FEC packet.
void UpdateCoveringFECPackets(RecoveredPacket* packet);
void UpdateCoveringFecPackets(RecoveredPacket* packet);
// Insert packet into FEC list. We delete duplicates.
void InsertFECPacket(ReceivedPacket* rx_packet,
void InsertFecPacket(ReceivedPacket* rx_packet,
const RecoveredPacketList* recovered_packet_list);
// Assigns pointers to already recovered packets covered by this FEC packet.
@ -279,15 +281,15 @@ class ForwardErrorCorrection {
void AttemptRecover(RecoveredPacketList* recovered_packet_list);
// Initializes the packet recovery using the FEC packet.
static bool InitRecovery(const FecPacket* fec_packet,
RecoveredPacket* recovered);
static bool StartPacketRecovery(const FecPacket* fec_packet,
RecoveredPacket* recovered);
// Performs XOR between |src_packet| and |dst_packet| and stores the result
// in |dst_packet|.
static void XorPackets(const Packet* src_packet, RecoveredPacket* dst_packet);
// Finish up the recovery of a packet.
static bool FinishRecovery(RecoveredPacket* recovered);
static bool FinishPacketRecovery(RecoveredPacket* recovered);
// Recover a missing packet.
bool RecoverPacket(const FecPacket* fec_packet,
@ -299,13 +301,18 @@ class ForwardErrorCorrection {
// This function returns 2 when two or more packets are missing.
static int NumCoveredPacketsMissing(const FecPacket* fec_packet);
static void DiscardFECPacket(FecPacket* fec_packet);
static void DiscardFecPacket(FecPacket* fec_packet);
static void DiscardOldPackets(RecoveredPacketList* recovered_packet_list);
static uint16_t ParseSequenceNumber(uint8_t* packet);
std::vector<Packet> generated_fec_packets_;
FecPacketList fec_packet_list_;
bool fec_packet_received_;
// Arrays used to avoid dynamically allocating memory when generating
// the packet masks in the ULPFEC headers.
// (There are never more than |kMaxMediaPackets| FEC packets generated.)
uint8_t packet_mask_[kMaxMediaPackets * kMaskSizeLBitSet];
uint8_t tmp_packet_mask_[kMaxMediaPackets * kMaskSizeLBitSet];
};
} // namespace webrtc
#endif // WEBRTC_MODULES_RTP_RTCP_SOURCE_FORWARD_ERROR_CORRECTION_H_

View File

@ -11,15 +11,15 @@
#ifndef WEBRTC_MODULES_RTP_RTCP_SOURCE_FORWARD_ERROR_CORRECTION_INTERNAL_H_
#define WEBRTC_MODULES_RTP_RTCP_SOURCE_FORWARD_ERROR_CORRECTION_INTERNAL_H_
#include "webrtc/modules/rtp_rtcp/source/forward_error_correction.h"
#include "webrtc/modules/include/module_common_types.h"
#include "webrtc/typedefs.h"
namespace webrtc {
// Packet mask size in bytes (L bit is set).
static const int kMaskSizeLBitSet = 6;
constexpr size_t kMaskSizeLBitSet = 6;
// Packet mask size in bytes (L bit is cleared).
static const int kMaskSizeLBitClear = 2;
constexpr size_t kMaskSizeLBitClear = 2;
namespace internal {

View File

@ -18,11 +18,11 @@ namespace webrtc {
enum { kREDForFECHeaderLength = 1 };
// This controls the maximum amount of excess overhead (actual - target)
// allowed in order to trigger GenerateFEC(), before |params_.max_fec_frames|
// 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
// 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
@ -139,7 +139,8 @@ int ProducerFec::AddRtpPacketAndGenerateFec(const uint8_t* data_buffer,
const bool marker_bit = (data_buffer[1] & kRtpMarkerBitMask) ? true : false;
if (media_packets_fec_.size() < ForwardErrorCorrection::kMaxMediaPackets) {
// Generic FEC can only protect up to kMaxMediaPackets packets.
ForwardErrorCorrection::Packet* packet = new ForwardErrorCorrection::Packet;
ForwardErrorCorrection::Packet* packet =
new ForwardErrorCorrection::Packet();
packet->length = payload_length + rtp_header_length;
memcpy(packet->data, data_buffer, packet->length);
media_packets_fec_.push_back(packet);
@ -159,7 +160,7 @@ int ProducerFec::AddRtpPacketAndGenerateFec(const uint8_t* data_buffer,
static_cast<int>(ForwardErrorCorrection::kMaxMediaPackets));
// TODO(pbos): Consider whether unequal protection should be enabled or not,
// it is currently always disabled.
int ret = fec_->GenerateFEC(media_packets_fec_, params_.fec_rate,
int ret = fec_->GenerateFec(media_packets_fec_, params_.fec_rate,
num_first_partition_, false,
params_.fec_mask_type, &fec_packets_);
if (fec_packets_.empty()) {

View File

@ -11,7 +11,6 @@
#ifndef WEBRTC_MODULES_RTP_RTCP_SOURCE_PRODUCER_FEC_H_
#define WEBRTC_MODULES_RTP_RTCP_SOURCE_PRODUCER_FEC_H_
#include <list>
#include <vector>
#include "webrtc/modules/rtp_rtcp/source/forward_error_correction.h"
@ -72,8 +71,8 @@ class ProducerFec {
void DeletePackets();
int Overhead() const;
ForwardErrorCorrection* fec_;
std::list<ForwardErrorCorrection::Packet*> media_packets_fec_;
std::list<ForwardErrorCorrection::Packet*> fec_packets_;
ForwardErrorCorrection::PacketList media_packets_fec_;
ForwardErrorCorrection::PacketList fec_packets_;
int num_frames_;
int num_first_partition_;
int minimum_media_packets_fec_;

View File

@ -44,7 +44,7 @@ class ProducerFecTest : public ::testing::Test {
virtual void SetUp() {
fec_ = new ForwardErrorCorrection();
producer_ = new ProducerFec(fec_);
generator_ = new FrameGenerator;
generator_ = new FrameGenerator();
}
virtual void TearDown() {

View File

@ -8,6 +8,7 @@
* be found in the AUTHORS file in the root of the source tree.
*/
#include <algorithm>
#include <list>
#include "testing/gtest/include/gtest/gtest.h"
@ -99,7 +100,7 @@ TEST_F(RtpFecTest, FecRecoveryNoLoss) {
fec_seq_num_ = ConstructMediaPackets(kNumMediaPackets);
EXPECT_EQ(0, fec_->GenerateFEC(media_packet_list_, kProtectionFactor,
EXPECT_EQ(0, fec_->GenerateFec(media_packet_list_, kProtectionFactor,
kNumImportantPackets, kUseUnequalProtection,
webrtc::kFecMaskBursty, &fec_packet_list_));
@ -111,7 +112,7 @@ TEST_F(RtpFecTest, FecRecoveryNoLoss) {
memset(fec_loss_mask_, 0, sizeof(fec_loss_mask_));
NetworkReceivedPackets();
EXPECT_EQ(0, fec_->DecodeFEC(&received_packet_list_,
EXPECT_EQ(0, fec_->DecodeFec(&received_packet_list_,
&recovered_packet_list_));
// No packets lost, expect complete recovery.
@ -126,7 +127,7 @@ TEST_F(RtpFecTest, FecRecoveryWithLoss) {
fec_seq_num_ = ConstructMediaPackets(kNumMediaPackets);
EXPECT_EQ(0, fec_->GenerateFEC(media_packet_list_, kProtectionFactor,
EXPECT_EQ(0, fec_->GenerateFec(media_packet_list_, kProtectionFactor,
kNumImportantPackets, kUseUnequalProtection,
webrtc::kFecMaskBursty, &fec_packet_list_));
@ -140,7 +141,7 @@ TEST_F(RtpFecTest, FecRecoveryWithLoss) {
NetworkReceivedPackets();
EXPECT_EQ(0,
fec_->DecodeFEC(&received_packet_list_, &recovered_packet_list_));
fec_->DecodeFec(&received_packet_list_, &recovered_packet_list_));
// One packet lost, one FEC packet, expect complete recovery.
EXPECT_TRUE(IsRecoveryComplete());
@ -153,7 +154,7 @@ TEST_F(RtpFecTest, FecRecoveryWithLoss) {
media_loss_mask_[3] = 1;
NetworkReceivedPackets();
EXPECT_EQ(0, fec_->DecodeFEC(&received_packet_list_,
EXPECT_EQ(0, fec_->DecodeFec(&received_packet_list_,
&recovered_packet_list_));
// 2 packets lost, one FEC packet, cannot get complete recovery.
@ -176,7 +177,7 @@ TEST_F(RtpFecTest, FecRecoveryWithSeqNumGapTwoFrames) {
// Construct media packets for first frame, starting at sequence number 0.
fec_seq_num_ = ConstructMediaPacketsSeqNum(2, 0);
EXPECT_EQ(0, fec_->GenerateFEC(media_packet_list_, kProtectionFactor,
EXPECT_EQ(0, fec_->GenerateFec(media_packet_list_, kProtectionFactor,
kNumImportantPackets, kUseUnequalProtection,
webrtc::kFecMaskBursty, &fec_packet_list_));
// Expect 1 FEC packet.
@ -200,7 +201,7 @@ TEST_F(RtpFecTest, FecRecoveryWithSeqNumGapTwoFrames) {
ReceivedPackets(media_packet_list_, media_loss_mask_, false);
EXPECT_EQ(0,
fec_->DecodeFEC(&received_packet_list_, &recovered_packet_list_));
fec_->DecodeFec(&received_packet_list_, &recovered_packet_list_));
// Expect that no decoding is done to get missing packet (seq#0) of second
// frame, using old FEC packet (seq#2) from first (old) frame. So number of
@ -222,7 +223,7 @@ TEST_F(RtpFecTest, FecRecoveryWithSeqNumGapOneFrameRecovery) {
// #65534(media) #65535(media) #0(media) #1(FEC).
fec_seq_num_ = ConstructMediaPacketsSeqNum(3, 65534);
EXPECT_EQ(0, fec_->GenerateFEC(media_packet_list_, kProtectionFactor,
EXPECT_EQ(0, fec_->GenerateFec(media_packet_list_, kProtectionFactor,
kNumImportantPackets, kUseUnequalProtection,
webrtc::kFecMaskBursty, &fec_packet_list_));
@ -238,7 +239,7 @@ TEST_F(RtpFecTest, FecRecoveryWithSeqNumGapOneFrameRecovery) {
ReceivedPackets(fec_packet_list_, fec_loss_mask_, true);
EXPECT_EQ(0,
fec_->DecodeFEC(&received_packet_list_, &recovered_packet_list_));
fec_->DecodeFec(&received_packet_list_, &recovered_packet_list_));
// Expect 3 media packets in recovered list, and complete recovery.
// Wrap-around won't remove FEC packet, as it follows the wrap.
@ -264,7 +265,7 @@ TEST_F(RtpFecTest, FecRecoveryWithSeqNumGapOneFrameNoRecovery) {
// #65532(media) #65533(media) #65534(media) #65535(FEC) #0(FEC).
fec_seq_num_ = ConstructMediaPacketsSeqNum(3, 65532);
EXPECT_EQ(0, fec_->GenerateFEC(media_packet_list_, kProtectionFactor,
EXPECT_EQ(0, fec_->GenerateFec(media_packet_list_, kProtectionFactor,
kNumImportantPackets, kUseUnequalProtection,
webrtc::kFecMaskBursty, &fec_packet_list_));
@ -280,7 +281,7 @@ TEST_F(RtpFecTest, FecRecoveryWithSeqNumGapOneFrameNoRecovery) {
ReceivedPackets(fec_packet_list_, fec_loss_mask_, true);
EXPECT_EQ(0,
fec_->DecodeFEC(&received_packet_list_, &recovered_packet_list_));
fec_->DecodeFec(&received_packet_list_, &recovered_packet_list_));
// The two FEC packets are received and should allow for complete recovery,
// but because of the wrap the second FEC packet will be discarded, and only
@ -303,7 +304,7 @@ TEST_F(RtpFecTest, FecRecoveryWithFecOutOfOrder) {
// #0(media) #1(media) #2(media) #3(FEC).
fec_seq_num_ = ConstructMediaPacketsSeqNum(3, 0);
EXPECT_EQ(0, fec_->GenerateFEC(media_packet_list_, kProtectionFactor,
EXPECT_EQ(0, fec_->GenerateFec(media_packet_list_, kProtectionFactor,
kNumImportantPackets, kUseUnequalProtection,
webrtc::kFecMaskBursty, &fec_packet_list_));
@ -320,7 +321,7 @@ TEST_F(RtpFecTest, FecRecoveryWithFecOutOfOrder) {
ReceivedPackets(media_packet_list_, media_loss_mask_, false);
EXPECT_EQ(0,
fec_->DecodeFEC(&received_packet_list_, &recovered_packet_list_));
fec_->DecodeFec(&received_packet_list_, &recovered_packet_list_));
// Expect 3 media packets in recovered list, and complete recovery.
EXPECT_EQ(3, static_cast<int>(recovered_packet_list_.size()));
@ -349,7 +350,7 @@ TEST_F(RtpFecTest, FecRecoveryWithLoss50percRandomMask) {
fec_seq_num_ = ConstructMediaPackets(kNumMediaPackets);
EXPECT_EQ(0, fec_->GenerateFEC(media_packet_list_, kProtectionFactor,
EXPECT_EQ(0, fec_->GenerateFec(media_packet_list_, kProtectionFactor,
kNumImportantPackets, kUseUnequalProtection,
webrtc::kFecMaskRandom, &fec_packet_list_));
@ -366,7 +367,7 @@ TEST_F(RtpFecTest, FecRecoveryWithLoss50percRandomMask) {
NetworkReceivedPackets();
EXPECT_EQ(0,
fec_->DecodeFEC(&received_packet_list_, &recovered_packet_list_));
fec_->DecodeFec(&received_packet_list_, &recovered_packet_list_));
// With media packet#1 and FEC packets #1, #2, #3, expect complete recovery.
EXPECT_TRUE(IsRecoveryComplete());
@ -381,7 +382,7 @@ TEST_F(RtpFecTest, FecRecoveryWithLoss50percRandomMask) {
media_loss_mask_[3] = 1;
NetworkReceivedPackets();
EXPECT_EQ(0, fec_->DecodeFEC(&received_packet_list_,
EXPECT_EQ(0, fec_->DecodeFec(&received_packet_list_,
&recovered_packet_list_));
// Cannot get complete recovery for this loss configuration with random mask.
@ -409,7 +410,7 @@ TEST_F(RtpFecTest, FecRecoveryWithLoss50percBurstyMask) {
fec_seq_num_ = ConstructMediaPackets(kNumMediaPackets);
EXPECT_EQ(0, fec_->GenerateFEC(media_packet_list_, kProtectionFactor,
EXPECT_EQ(0, fec_->GenerateFec(media_packet_list_, kProtectionFactor,
kNumImportantPackets, kUseUnequalProtection,
webrtc::kFecMaskBursty, &fec_packet_list_));
@ -425,7 +426,7 @@ TEST_F(RtpFecTest, FecRecoveryWithLoss50percBurstyMask) {
media_loss_mask_[3] = 1;
NetworkReceivedPackets();
EXPECT_EQ(0, fec_->DecodeFEC(&received_packet_list_,
EXPECT_EQ(0, fec_->DecodeFec(&received_packet_list_,
&recovered_packet_list_));
// Expect complete recovery for consecutive packet loss <= 50%.
@ -441,7 +442,7 @@ TEST_F(RtpFecTest, FecRecoveryWithLoss50percBurstyMask) {
media_loss_mask_[3] = 1;
NetworkReceivedPackets();
EXPECT_EQ(0, fec_->DecodeFEC(&received_packet_list_,
EXPECT_EQ(0, fec_->DecodeFec(&received_packet_list_,
&recovered_packet_list_));
// Expect complete recovery for consecutive packet loss <= 50%.
@ -457,7 +458,7 @@ TEST_F(RtpFecTest, FecRecoveryWithLoss50percBurstyMask) {
media_loss_mask_[3] = 1;
NetworkReceivedPackets();
EXPECT_EQ(0, fec_->DecodeFEC(&received_packet_list_,
EXPECT_EQ(0, fec_->DecodeFec(&received_packet_list_,
&recovered_packet_list_));
// Cannot get complete recovery for this loss configuration.
@ -472,7 +473,7 @@ TEST_F(RtpFecTest, FecRecoveryNoLossUep) {
fec_seq_num_ = ConstructMediaPackets(kNumMediaPackets);
EXPECT_EQ(0, fec_->GenerateFEC(media_packet_list_, kProtectionFactor,
EXPECT_EQ(0, fec_->GenerateFec(media_packet_list_, kProtectionFactor,
kNumImportantPackets, kUseUnequalProtection,
webrtc::kFecMaskBursty, &fec_packet_list_));
@ -485,7 +486,7 @@ TEST_F(RtpFecTest, FecRecoveryNoLossUep) {
NetworkReceivedPackets();
EXPECT_EQ(0,
fec_->DecodeFEC(&received_packet_list_, &recovered_packet_list_));
fec_->DecodeFec(&received_packet_list_, &recovered_packet_list_));
// No packets lost, expect complete recovery.
EXPECT_TRUE(IsRecoveryComplete());
@ -499,7 +500,7 @@ TEST_F(RtpFecTest, FecRecoveryWithLossUep) {
fec_seq_num_ = ConstructMediaPackets(kNumMediaPackets);
EXPECT_EQ(0, fec_->GenerateFEC(media_packet_list_, kProtectionFactor,
EXPECT_EQ(0, fec_->GenerateFec(media_packet_list_, kProtectionFactor,
kNumImportantPackets, kUseUnequalProtection,
webrtc::kFecMaskBursty, &fec_packet_list_));
@ -512,7 +513,7 @@ TEST_F(RtpFecTest, FecRecoveryWithLossUep) {
media_loss_mask_[3] = 1;
NetworkReceivedPackets();
EXPECT_EQ(0, fec_->DecodeFEC(&received_packet_list_,
EXPECT_EQ(0, fec_->DecodeFec(&received_packet_list_,
&recovered_packet_list_));
// One packet lost, one FEC packet, expect complete recovery.
@ -526,7 +527,7 @@ TEST_F(RtpFecTest, FecRecoveryWithLossUep) {
media_loss_mask_[3] = 1;
NetworkReceivedPackets();
EXPECT_EQ(0, fec_->DecodeFEC(&received_packet_list_,
EXPECT_EQ(0, fec_->DecodeFec(&received_packet_list_,
&recovered_packet_list_));
// 2 packets lost, one FEC packet, cannot get complete recovery.
@ -552,7 +553,7 @@ TEST_F(RtpFecTest, FecRecoveryWithLoss50percUepRandomMask) {
fec_seq_num_ = ConstructMediaPackets(kNumMediaPackets);
EXPECT_EQ(0, fec_->GenerateFEC(media_packet_list_, kProtectionFactor,
EXPECT_EQ(0, fec_->GenerateFec(media_packet_list_, kProtectionFactor,
kNumImportantPackets, kUseUnequalProtection,
webrtc::kFecMaskRandom, &fec_packet_list_));
@ -568,7 +569,7 @@ TEST_F(RtpFecTest, FecRecoveryWithLoss50percUepRandomMask) {
media_loss_mask_[3] = 1;
NetworkReceivedPackets();
EXPECT_EQ(0, fec_->DecodeFEC(&received_packet_list_,
EXPECT_EQ(0, fec_->DecodeFec(&received_packet_list_,
&recovered_packet_list_));
// With media packet#3 and FEC packets #0, #1, #3, expect complete recovery.
@ -585,7 +586,7 @@ TEST_F(RtpFecTest, FecRecoveryWithLoss50percUepRandomMask) {
media_loss_mask_[3] = 1;
NetworkReceivedPackets();
EXPECT_EQ(0, fec_->DecodeFEC(&received_packet_list_,
EXPECT_EQ(0, fec_->DecodeFec(&received_packet_list_,
&recovered_packet_list_));
// Cannot get complete recovery for this loss configuration.
@ -604,12 +605,12 @@ TEST_F(RtpFecTest, FecRecoveryNonConsecutivePackets) {
// 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) {
for (auto 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, kProtectionFactor,
EXPECT_EQ(0, fec_->GenerateFec(protected_media_packets, kProtectionFactor,
kNumImportantPackets, kUseUnequalProtection,
webrtc::kFecMaskBursty, &fec_packet_list_));
@ -622,7 +623,7 @@ TEST_F(RtpFecTest, FecRecoveryNonConsecutivePackets) {
media_loss_mask_[2] = 1;
NetworkReceivedPackets();
EXPECT_EQ(0, fec_->DecodeFEC(&received_packet_list_,
EXPECT_EQ(0, fec_->DecodeFec(&received_packet_list_,
&recovered_packet_list_));
// One packet lost, one FEC packet, expect complete recovery.
@ -635,7 +636,7 @@ TEST_F(RtpFecTest, FecRecoveryNonConsecutivePackets) {
media_loss_mask_[1] = 1;
NetworkReceivedPackets();
EXPECT_EQ(0, fec_->DecodeFEC(&received_packet_list_,
EXPECT_EQ(0, fec_->DecodeFec(&received_packet_list_,
&recovered_packet_list_));
// Unprotected packet lost. Recovery not possible.
@ -649,7 +650,7 @@ TEST_F(RtpFecTest, FecRecoveryNonConsecutivePackets) {
media_loss_mask_[2] = 1;
NetworkReceivedPackets();
EXPECT_EQ(0, fec_->DecodeFEC(&received_packet_list_,
EXPECT_EQ(0, fec_->DecodeFec(&received_packet_list_,
&recovered_packet_list_));
// 2 protected packets lost, one FEC packet, cannot get complete recovery.
@ -668,15 +669,15 @@ TEST_F(RtpFecTest, FecRecoveryNonConsecutivePacketsExtension) {
// 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) {
for (auto 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, kProtectionFactor,
EXPECT_EQ(0, fec_->GenerateFec(protected_media_packets, kProtectionFactor,
kNumImportantPackets, kUseUnequalProtection,
webrtc::kFecMaskBursty, &fec_packet_list_));
@ -689,7 +690,7 @@ TEST_F(RtpFecTest, FecRecoveryNonConsecutivePacketsExtension) {
media_loss_mask_[kNumMediaPackets - 1] = 1;
NetworkReceivedPackets();
EXPECT_EQ(0, fec_->DecodeFEC(&received_packet_list_,
EXPECT_EQ(0, fec_->DecodeFec(&received_packet_list_,
&recovered_packet_list_));
// One packet lost, one FEC packet, expect complete recovery.
@ -702,7 +703,7 @@ TEST_F(RtpFecTest, FecRecoveryNonConsecutivePacketsExtension) {
media_loss_mask_[kNumMediaPackets - 2] = 1;
NetworkReceivedPackets();
EXPECT_EQ(0, fec_->DecodeFEC(&received_packet_list_,
EXPECT_EQ(0, fec_->DecodeFec(&received_packet_list_,
&recovered_packet_list_));
// Unprotected packet lost. Recovery not possible.
@ -720,7 +721,7 @@ TEST_F(RtpFecTest, FecRecoveryNonConsecutivePacketsExtension) {
media_loss_mask_[kNumMediaPackets - 1] = 1;
NetworkReceivedPackets();
EXPECT_EQ(0, fec_->DecodeFEC(&received_packet_list_,
EXPECT_EQ(0, fec_->DecodeFec(&received_packet_list_,
&recovered_packet_list_));
// 5 protected packets lost, one FEC packet, cannot get complete recovery.
@ -739,15 +740,15 @@ TEST_F(RtpFecTest, FecRecoveryNonConsecutivePacketsWrap) {
// 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) {
for (auto 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, kProtectionFactor,
EXPECT_EQ(0, fec_->GenerateFec(protected_media_packets, kProtectionFactor,
kNumImportantPackets, kUseUnequalProtection,
webrtc::kFecMaskBursty, &fec_packet_list_));
@ -760,7 +761,7 @@ TEST_F(RtpFecTest, FecRecoveryNonConsecutivePacketsWrap) {
media_loss_mask_[kNumMediaPackets - 1] = 1;
NetworkReceivedPackets();
EXPECT_EQ(0, fec_->DecodeFEC(&received_packet_list_,
EXPECT_EQ(0, fec_->DecodeFec(&received_packet_list_,
&recovered_packet_list_));
// One packet lost, one FEC packet, expect complete recovery.
@ -773,7 +774,7 @@ TEST_F(RtpFecTest, FecRecoveryNonConsecutivePacketsWrap) {
media_loss_mask_[kNumMediaPackets - 2] = 1;
NetworkReceivedPackets();
EXPECT_EQ(0, fec_->DecodeFEC(&received_packet_list_,
EXPECT_EQ(0, fec_->DecodeFec(&received_packet_list_,
&recovered_packet_list_));
// Unprotected packet lost. Recovery not possible.
@ -791,7 +792,7 @@ TEST_F(RtpFecTest, FecRecoveryNonConsecutivePacketsWrap) {
media_loss_mask_[kNumMediaPackets - 1] = 1;
NetworkReceivedPackets();
EXPECT_EQ(0, fec_->DecodeFEC(&received_packet_list_,
EXPECT_EQ(0, fec_->DecodeFec(&received_packet_list_,
&recovered_packet_list_));
// 5 protected packets lost, one FEC packet, cannot get complete recovery.
@ -811,36 +812,27 @@ void RtpFecTest::FreeRecoveredPacketList() {
}
bool RtpFecTest::IsRecoveryComplete() {
// Check that the number of media and recovered packets are equal.
if (media_packet_list_.size() != recovered_packet_list_.size()) {
// We must have equally many recovered packets as original packets.
if (recovered_packet_list_.size() != media_packet_list_.size()) {
return false;
}
ForwardErrorCorrection::Packet* media_packet;
ForwardErrorCorrection::RecoveredPacket* recovered_packet;
bool recovery = true;
PacketList::iterator media_packet_list_item = media_packet_list_.begin();
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()) {
// All recovered packets must be identical to the corresponding
// original packets.
auto cmp = [](ForwardErrorCorrection::Packet* media_packet,
ForwardErrorCorrection::RecoveredPacket* recovered_packet) {
if (media_packet->length != recovered_packet->pkt->length) {
return false;
}
media_packet = *media_packet_list_item;
recovered_packet = *recovered_packet_list_item;
if (recovered_packet->pkt->length != media_packet->length) {
return false;
}
if (memcmp(recovered_packet->pkt->data, media_packet->data,
if (memcmp(media_packet->data,
recovered_packet->pkt->data,
media_packet->length) != 0) {
return false;
}
media_packet_list_item++;
recovered_packet_list_item++;
}
return recovery;
return true;
};
return std::equal(media_packet_list_.cbegin(), media_packet_list_.cend(),
recovered_packet_list_.cbegin(), cmp);
}
void RtpFecTest::NetworkReceivedPackets() {
@ -851,18 +843,13 @@ void RtpFecTest::NetworkReceivedPackets() {
void RtpFecTest::ReceivedPackets(const PacketList& packet_list, int* loss_mask,
bool is_fec) {
ForwardErrorCorrection::Packet* packet;
ForwardErrorCorrection::ReceivedPacket* received_packet;
int seq_num = fec_seq_num_;
int packet_idx = 0;
PacketList::const_iterator packet_list_item = packet_list.begin();
while (packet_list_item != packet_list.end()) {
packet = *packet_list_item;
for (const auto* packet : packet_list) {
if (loss_mask[packet_idx] == 0) {
received_packet = new ForwardErrorCorrection::ReceivedPacket;
received_packet->pkt = new ForwardErrorCorrection::Packet;
auto received_packet = new ForwardErrorCorrection::ReceivedPacket();
received_packet->pkt = new ForwardErrorCorrection::Packet();
received_packet_list_.push_back(received_packet);
received_packet->pkt->length = packet->length;
memcpy(received_packet->pkt->data, packet->data, packet->length);
@ -884,7 +871,6 @@ void RtpFecTest::ReceivedPackets(const PacketList& packet_list, int* loss_mask,
}
}
packet_idx++;
packet_list_item++;
// Sequence number of FEC packets are defined as increment by 1 from
// last media packet in frame.
if (is_fec) seq_num++;
@ -893,13 +879,13 @@ void RtpFecTest::ReceivedPackets(const PacketList& packet_list, int* loss_mask,
int RtpFecTest::ConstructMediaPacketsSeqNum(int num_media_packets,
int start_seq_num) {
assert(num_media_packets > 0);
RTC_DCHECK_GT(num_media_packets, 0);
ForwardErrorCorrection::Packet* media_packet = NULL;
int sequence_number = start_seq_num;
int time_stamp = random_.Rand<int>();
for (int i = 0; i < num_media_packets; ++i) {
media_packet = new ForwardErrorCorrection::Packet;
media_packet = new ForwardErrorCorrection::Packet();
media_packet_list_.push_back(media_packet);
const uint32_t kMinPacketSize = kRtpHeaderSize;
const uint32_t kMaxPacketSize = IP_PACKET_SIZE - kRtpHeaderSize -
@ -937,7 +923,7 @@ int RtpFecTest::ConstructMediaPacketsSeqNum(int num_media_packets,
sequence_number++;
}
// Last packet, set marker bit.
assert(media_packet != NULL);
RTC_DCHECK(media_packet);
media_packet->data[1] |= 0x80;
return sequence_number;
}

View File

@ -13,9 +13,6 @@
* functions in ForwardErrorCorrection directly.
*/
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
@ -38,46 +35,45 @@ namespace test {
using fec_private_tables::kPacketMaskBurstyTbl;
void ReceivePackets(
ForwardErrorCorrection::ReceivedPacketList* toDecodeList,
ForwardErrorCorrection::ReceivedPacketList* receivedPacketList,
size_t numPacketsToDecode,
float reorderRate,
float duplicateRate,
ForwardErrorCorrection::ReceivedPacketList* to_decode_list,
ForwardErrorCorrection::ReceivedPacketList* received_packet_list,
size_t num_packets_to_decode,
float reorder_rate,
float duplicate_rate,
Random* random) {
assert(toDecodeList->empty());
assert(numPacketsToDecode <= receivedPacketList->size());
RTC_DCHECK(to_decode_list->empty());
RTC_DCHECK_LE(num_packets_to_decode, received_packet_list->size());
ForwardErrorCorrection::ReceivedPacketList::iterator it;
for (size_t i = 0; i < numPacketsToDecode; i++) {
it = receivedPacketList->begin();
for (size_t i = 0; i < num_packets_to_decode; i++) {
auto it = received_packet_list->begin();
// Reorder packets.
float randomVariable = random->Rand<float>();
while (randomVariable < reorderRate) {
float random_variable = random->Rand<float>();
while (random_variable < reorder_rate) {
++it;
if (it == receivedPacketList->end()) {
if (it == received_packet_list->end()) {
--it;
break;
}
randomVariable = random->Rand<float>();
random_variable = random->Rand<float>();
}
ForwardErrorCorrection::ReceivedPacket* receivedPacket = *it;
toDecodeList->push_back(receivedPacket);
ForwardErrorCorrection::ReceivedPacket* received_packet = *it;
to_decode_list->push_back(received_packet);
// Duplicate packets.
randomVariable = random->Rand<float>();
while (randomVariable < duplicateRate) {
ForwardErrorCorrection::ReceivedPacket* duplicatePacket =
new ForwardErrorCorrection::ReceivedPacket;
*duplicatePacket = *receivedPacket;
duplicatePacket->pkt = new ForwardErrorCorrection::Packet;
memcpy(duplicatePacket->pkt->data, receivedPacket->pkt->data,
receivedPacket->pkt->length);
duplicatePacket->pkt->length = receivedPacket->pkt->length;
random_variable = random->Rand<float>();
while (random_variable < duplicate_rate) {
ForwardErrorCorrection::ReceivedPacket* duplicate_packet =
new ForwardErrorCorrection::ReceivedPacket();
*duplicate_packet = *received_packet;
duplicate_packet->pkt = new ForwardErrorCorrection::Packet();
memcpy(duplicate_packet->pkt->data, received_packet->pkt->data,
received_packet->pkt->length);
duplicate_packet->pkt->length = received_packet->pkt->length;
toDecodeList->push_back(duplicatePacket);
randomVariable = random->Rand<float>();
to_decode_list->push_back(duplicate_packet);
random_variable = random->Rand<float>();
}
receivedPacketList->erase(it);
received_packet_list->erase(it);
}
}
@ -111,94 +107,100 @@ TEST(FecTest, MAYBE_FecTest) {
<< "equal to 12.";
ForwardErrorCorrection fec;
ForwardErrorCorrection::PacketList mediaPacketList;
ForwardErrorCorrection::PacketList fecPacketList;
ForwardErrorCorrection::ReceivedPacketList toDecodeList;
ForwardErrorCorrection::ReceivedPacketList receivedPacketList;
ForwardErrorCorrection::RecoveredPacketList recoveredPacketList;
std::list<uint8_t*> fecMaskList;
ForwardErrorCorrection::PacketList media_packet_list;
ForwardErrorCorrection::PacketList fec_packet_list;
ForwardErrorCorrection::ReceivedPacketList to_decode_list;
ForwardErrorCorrection::ReceivedPacketList received_packet_list;
ForwardErrorCorrection::RecoveredPacketList recovered_packet_list;
std::list<uint8_t*> fec_mask_list;
ForwardErrorCorrection::Packet* mediaPacket = NULL;
ForwardErrorCorrection::Packet* media_packet = nullptr;
// Running over only one loss rate to limit execution time.
const float lossRate[] = {0.5f};
const uint32_t lossRateSize = sizeof(lossRate) / sizeof(*lossRate);
const float reorderRate = 0.1f;
const float duplicateRate = 0.1f;
const float loss_rate[] = {0.5f};
const uint32_t loss_rate_size = sizeof(loss_rate) / sizeof(*loss_rate);
const float reorder_rate = 0.1f;
const float duplicate_rate = 0.1f;
uint8_t mediaLossMask[kMaxNumberMediaPackets];
uint8_t fecLossMask[kMaxNumberFecPackets];
uint8_t fecPacketMasks[kMaxNumberFecPackets][kMaxNumberMediaPackets];
uint8_t media_loss_mask[kMaxNumberMediaPackets];
uint8_t fec_loss_mask[kMaxNumberFecPackets];
uint8_t fec_packet_masks[kMaxNumberFecPackets][kMaxNumberMediaPackets];
// Seed the random number generator, storing the seed to file in order to
// reproduce past results.
const unsigned int randomSeed = static_cast<unsigned int>(time(NULL));
Random random(randomSeed);
const unsigned int random_seed = static_cast<unsigned int>(time(nullptr));
Random random(random_seed);
std::string filename = webrtc::test::OutputPath() + "randomSeedLog.txt";
FILE* randomSeedFile = fopen(filename.c_str(), "a");
fprintf(randomSeedFile, "%u\n", randomSeed);
fclose(randomSeedFile);
randomSeedFile = NULL;
FILE* random_seed_file = fopen(filename.c_str(), "a");
fprintf(random_seed_file, "%u\n", random_seed);
fclose(random_seed_file);
random_seed_file = nullptr;
uint16_t seqNum = 0;
uint32_t timeStamp = random.Rand<uint32_t>();
uint16_t seq_num = 0;
uint32_t timestamp = random.Rand<uint32_t>();
const uint32_t ssrc = random.Rand(1u, 0xfffffffe);
// Loop over the mask types: random and bursty.
for (int mask_type_idx = 0; mask_type_idx < kNumFecMaskTypes;
++mask_type_idx) {
for (uint32_t lossRateIdx = 0; lossRateIdx < lossRateSize; ++lossRateIdx) {
printf("Loss rate: %.2f, Mask type %d \n", lossRate[lossRateIdx],
for (uint32_t loss_rate_idx = 0; loss_rate_idx < loss_rate_size;
++loss_rate_idx) {
printf("Loss rate: %.2f, Mask type %d \n", loss_rate[loss_rate_idx],
mask_type_idx);
const uint32_t packetMaskMax = kMaxMediaPackets[mask_type_idx];
uint8_t* packetMask = new uint8_t[packetMaskMax * kNumMaskBytesL1];
const uint32_t packet_mask_max = kMaxMediaPackets[mask_type_idx];
std::unique_ptr<uint8_t[]> packet_mask(
new uint8_t[packet_mask_max * kNumMaskBytesL1]);
FecMaskType fec_mask_type = kMaskTypes[mask_type_idx];
for (uint32_t numMediaPackets = 1; numMediaPackets <= packetMaskMax;
numMediaPackets++) {
internal::PacketMaskTable mask_table(fec_mask_type, numMediaPackets);
for (uint32_t num_media_packets = 1; num_media_packets <= packet_mask_max;
num_media_packets++) {
internal::PacketMaskTable mask_table(fec_mask_type, num_media_packets);
for (uint32_t numFecPackets = 1;
numFecPackets <= numMediaPackets && numFecPackets <= packetMaskMax;
numFecPackets++) {
// Loop over numImpPackets: usually <= (0.3*numMediaPackets).
// For this test we check up to ~ (numMediaPackets / 4).
uint32_t maxNumImpPackets = numMediaPackets / 4 + 1;
for (uint32_t numImpPackets = 0; numImpPackets <= maxNumImpPackets &&
numImpPackets <= packetMaskMax;
numImpPackets++) {
uint8_t protectionFactor =
static_cast<uint8_t>(numFecPackets * 255 / numMediaPackets);
for (uint32_t num_fec_packets = 1;
num_fec_packets <= num_media_packets &&
num_fec_packets <= packet_mask_max;
num_fec_packets++) {
// Loop over num_imp_packets: usually <= (0.3*num_media_packets).
// For this test we check up to ~ (num_media_packets / 4).
uint32_t max_num_imp_packets = num_media_packets / 4 + 1;
for (uint32_t num_imp_packets = 0;
num_imp_packets <= max_num_imp_packets &&
num_imp_packets <= packet_mask_max;
num_imp_packets++) {
uint8_t protection_factor =
static_cast<uint8_t>(num_fec_packets * 255 / num_media_packets);
const uint32_t maskBytesPerFecPacket =
(numMediaPackets > 16) ? kNumMaskBytesL1 : kNumMaskBytesL0;
const uint32_t mask_bytes_per_fec_packet =
(num_media_packets > 16) ? kNumMaskBytesL1 : kNumMaskBytesL0;
memset(packetMask, 0, numMediaPackets * maskBytesPerFecPacket);
memset(packet_mask.get(), 0,
num_media_packets * mask_bytes_per_fec_packet);
// Transfer packet masks from bit-mask to byte-mask.
internal::GeneratePacketMasks(numMediaPackets, numFecPackets,
numImpPackets, kUseUnequalProtection,
mask_table, packetMask);
internal::GeneratePacketMasks(num_media_packets, num_fec_packets,
num_imp_packets,
kUseUnequalProtection,
mask_table, packet_mask.get());
#ifdef VERBOSE_OUTPUT
printf(
"%u media packets, %u FEC packets, %u numImpPackets, "
"%u media packets, %u FEC packets, %u num_imp_packets, "
"loss rate = %.2f \n",
numMediaPackets, numFecPackets, numImpPackets,
lossRate[lossRateIdx]);
num_media_packets, num_fec_packets, num_imp_packets,
loss_rate[loss_rate_idx]);
printf("Packet mask matrix \n");
#endif
for (uint32_t i = 0; i < numFecPackets; i++) {
for (uint32_t j = 0; j < numMediaPackets; j++) {
const uint8_t byteMask =
packetMask[i * maskBytesPerFecPacket + j / 8];
const uint32_t bitPosition = (7 - j % 8);
fecPacketMasks[i][j] =
(byteMask & (1 << bitPosition)) >> bitPosition;
for (uint32_t i = 0; i < num_fec_packets; i++) {
for (uint32_t j = 0; j < num_media_packets; j++) {
const uint8_t byte_mask =
packet_mask[i * mask_bytes_per_fec_packet + j / 8];
const uint32_t bit_position = (7 - j % 8);
fec_packet_masks[i][j] =
(byte_mask & (1 << bit_position)) >> bit_position;
#ifdef VERBOSE_OUTPUT
printf("%u ", fecPacketMasks[i][j]);
printf("%u ", fec_packet_masks[i][j]);
#endif
}
#ifdef VERBOSE_OUTPUT
@ -209,20 +211,20 @@ TEST(FecTest, MAYBE_FecTest) {
printf("\n");
#endif
// Check for all zero rows or columns: indicates incorrect mask.
uint32_t rowLimit = numMediaPackets;
for (uint32_t i = 0; i < numFecPackets; ++i) {
uint32_t rowSum = 0;
for (uint32_t j = 0; j < rowLimit; ++j) {
rowSum += fecPacketMasks[i][j];
uint32_t row_limit = num_media_packets;
for (uint32_t i = 0; i < num_fec_packets; ++i) {
uint32_t row_sum = 0;
for (uint32_t j = 0; j < row_limit; ++j) {
row_sum += fec_packet_masks[i][j];
}
ASSERT_NE(0u, rowSum) << "Row is all zero " << i;
ASSERT_NE(0u, row_sum) << "Row is all zero " << i;
}
for (uint32_t j = 0; j < rowLimit; ++j) {
uint32_t columnSum = 0;
for (uint32_t i = 0; i < numFecPackets; ++i) {
columnSum += fecPacketMasks[i][j];
for (uint32_t j = 0; j < row_limit; ++j) {
uint32_t column_sum = 0;
for (uint32_t i = 0; i < num_fec_packets; ++i) {
column_sum += fec_packet_masks[i][j];
}
ASSERT_NE(0u, columnSum) << "Column is all zero " << j;
ASSERT_NE(0u, column_sum) << "Column is all zero " << j;
}
// Construct media packets.
@ -230,19 +232,20 @@ TEST(FecTest, MAYBE_FecTest) {
// below, to avoid sequence number wrap-around. In actual decoding,
// old FEC packets in list are dropped if sequence number wrap
// around is detected. This case is currently not handled below.
seqNum = 0;
for (uint32_t i = 0; i < numMediaPackets; ++i) {
mediaPacket = new ForwardErrorCorrection::Packet;
mediaPacketList.push_back(mediaPacket);
seq_num = 0;
for (uint32_t i = 0; i < num_media_packets; ++i) {
media_packet = new ForwardErrorCorrection::Packet();
media_packet_list.push_back(media_packet);
const uint32_t kMinPacketSize = 12;
const uint32_t kMaxPacketSize = static_cast<uint32_t>(
IP_PACKET_SIZE - 12 - 28 -
ForwardErrorCorrection::PacketOverhead());
mediaPacket->length = random.Rand(kMinPacketSize, kMaxPacketSize);
media_packet->length = random.Rand(kMinPacketSize,
kMaxPacketSize);
// Generate random values for the first 2 bytes.
mediaPacket->data[0] = random.Rand<uint8_t>();
mediaPacket->data[1] = random.Rand<uint8_t>();
media_packet->data[0] = random.Rand<uint8_t>();
media_packet->data[1] = random.Rand<uint8_t>();
// The first two bits are assumed to be 10 by the
// FEC encoder. In fact the FEC decoder will set the
@ -250,237 +253,217 @@ TEST(FecTest, MAYBE_FecTest) {
// actually were. Set the first two bits to 10
// so that a memcmp can be performed for the
// whole restored packet.
mediaPacket->data[0] |= 0x80;
mediaPacket->data[0] &= 0xbf;
media_packet->data[0] |= 0x80;
media_packet->data[0] &= 0xbf;
// FEC is applied to a whole frame.
// A frame is signaled by multiple packets without
// the marker bit set followed by the last packet of
// the frame for which the marker bit is set.
// Only push one (fake) frame to the FEC.
mediaPacket->data[1] &= 0x7f;
media_packet->data[1] &= 0x7f;
ByteWriter<uint16_t>::WriteBigEndian(&mediaPacket->data[2],
seqNum);
ByteWriter<uint32_t>::WriteBigEndian(&mediaPacket->data[4],
timeStamp);
ByteWriter<uint32_t>::WriteBigEndian(&mediaPacket->data[8], ssrc);
ByteWriter<uint16_t>::WriteBigEndian(&media_packet->data[2],
seq_num);
ByteWriter<uint32_t>::WriteBigEndian(&media_packet->data[4],
timestamp);
ByteWriter<uint32_t>::WriteBigEndian(&media_packet->data[8],
ssrc);
// Generate random values for payload
for (size_t j = 12; j < mediaPacket->length; ++j) {
mediaPacket->data[j] = random.Rand<uint8_t>();
for (size_t j = 12; j < media_packet->length; ++j) {
media_packet->data[j] = random.Rand<uint8_t>();
}
seqNum++;
seq_num++;
}
mediaPacket->data[1] |= 0x80;
media_packet->data[1] |= 0x80;
ASSERT_EQ(0, fec.GenerateFEC(mediaPacketList, protectionFactor,
numImpPackets, kUseUnequalProtection,
fec_mask_type, &fecPacketList))
<< "GenerateFEC() failed";
ASSERT_EQ(0, fec.GenerateFec(media_packet_list, protection_factor,
num_imp_packets, kUseUnequalProtection,
fec_mask_type, &fec_packet_list))
<< "GenerateFec() failed";
ASSERT_EQ(numFecPackets, fecPacketList.size())
<< "We requested " << numFecPackets << " FEC packets, but "
<< "GenerateFEC() produced " << fecPacketList.size();
memset(mediaLossMask, 0, sizeof(mediaLossMask));
ForwardErrorCorrection::PacketList::iterator mediaPacketListItem =
mediaPacketList.begin();
ForwardErrorCorrection::ReceivedPacket* receivedPacket;
uint32_t mediaPacketIdx = 0;
ASSERT_EQ(num_fec_packets, fec_packet_list.size())
<< "We requested " << num_fec_packets << " FEC packets, but "
<< "GenerateFec() produced " << fec_packet_list.size();
while (mediaPacketListItem != mediaPacketList.end()) {
mediaPacket = *mediaPacketListItem;
memset(media_loss_mask, 0, sizeof(media_loss_mask));
uint32_t media_packet_idx = 0;
for (auto* media_packet : media_packet_list) {
// We want a value between 0 and 1.
const float lossRandomVariable = random.Rand<float>();
const float loss_random_variable = random.Rand<float>();
if (lossRandomVariable >= lossRate[lossRateIdx]) {
mediaLossMask[mediaPacketIdx] = 1;
receivedPacket = new ForwardErrorCorrection::ReceivedPacket;
receivedPacket->pkt = new ForwardErrorCorrection::Packet;
receivedPacketList.push_back(receivedPacket);
if (loss_random_variable >= loss_rate[loss_rate_idx]) {
media_loss_mask[media_packet_idx] = 1;
auto received_packet =
new ForwardErrorCorrection::ReceivedPacket();
received_packet->pkt = new ForwardErrorCorrection::Packet();
received_packet_list.push_back(received_packet);
receivedPacket->pkt->length = mediaPacket->length;
memcpy(receivedPacket->pkt->data, mediaPacket->data,
mediaPacket->length);
receivedPacket->seq_num =
ByteReader<uint16_t>::ReadBigEndian(&mediaPacket->data[2]);
receivedPacket->is_fec = false;
received_packet->pkt->length = media_packet->length;
memcpy(received_packet->pkt->data, media_packet->data,
media_packet->length);
received_packet->seq_num =
ByteReader<uint16_t>::ReadBigEndian(&media_packet->data[2]);
received_packet->is_fec = false;
}
mediaPacketIdx++;
++mediaPacketListItem;
media_packet_idx++;
}
memset(fecLossMask, 0, sizeof(fecLossMask));
ForwardErrorCorrection::PacketList::iterator fecPacketListItem =
fecPacketList.begin();
ForwardErrorCorrection::Packet* fecPacket;
uint32_t fecPacketIdx = 0;
while (fecPacketListItem != fecPacketList.end()) {
fecPacket = *fecPacketListItem;
const float lossRandomVariable = random.Rand<float>();
if (lossRandomVariable >= lossRate[lossRateIdx]) {
fecLossMask[fecPacketIdx] = 1;
receivedPacket = new ForwardErrorCorrection::ReceivedPacket;
receivedPacket->pkt = new ForwardErrorCorrection::Packet;
receivedPacketList.push_back(receivedPacket);
memset(fec_loss_mask, 0, sizeof(fec_loss_mask));
uint32_t fec_packet_idx = 0;
for (auto* fec_packet : fec_packet_list) {
const float loss_random_variable = random.Rand<float>();
if (loss_random_variable >= loss_rate[loss_rate_idx]) {
fec_loss_mask[fec_packet_idx] = 1;
auto received_packet =
new ForwardErrorCorrection::ReceivedPacket();
received_packet->pkt = new ForwardErrorCorrection::Packet();
receivedPacket->pkt->length = fecPacket->length;
memcpy(receivedPacket->pkt->data, fecPacket->data,
fecPacket->length);
received_packet_list.push_back(received_packet);
receivedPacket->seq_num = seqNum;
receivedPacket->is_fec = true;
receivedPacket->ssrc = ssrc;
received_packet->pkt->length = fec_packet->length;
memcpy(received_packet->pkt->data, fec_packet->data,
fec_packet->length);
fecMaskList.push_back(fecPacketMasks[fecPacketIdx]);
received_packet->seq_num = seq_num;
received_packet->is_fec = true;
received_packet->ssrc = ssrc;
fec_mask_list.push_back(fec_packet_masks[fec_packet_idx]);
}
++fecPacketIdx;
++seqNum;
++fecPacketListItem;
++fec_packet_idx;
++seq_num;
}
#ifdef VERBOSE_OUTPUT
printf("Media loss mask:\n");
for (uint32_t i = 0; i < numMediaPackets; i++) {
printf("%u ", mediaLossMask[i]);
for (uint32_t i = 0; i < num_media_packets; i++) {
printf("%u ", media_loss_mask[i]);
}
printf("\n\n");
printf("FEC loss mask:\n");
for (uint32_t i = 0; i < numFecPackets; i++) {
printf("%u ", fecLossMask[i]);
for (uint32_t i = 0; i < num_fec_packets; i++) {
printf("%u ", fec_loss_mask[i]);
}
printf("\n\n");
#endif
std::list<uint8_t*>::iterator fecMaskIt = fecMaskList.begin();
uint8_t* fecMask;
while (fecMaskIt != fecMaskList.end()) {
fecMask = *fecMaskIt;
uint32_t hammingDist = 0;
uint32_t recoveryPosition = 0;
for (uint32_t i = 0; i < numMediaPackets; i++) {
if (mediaLossMask[i] == 0 && fecMask[i] == 1) {
recoveryPosition = i;
++hammingDist;
auto fec_mask_it = fec_mask_list.begin();
while (fec_mask_it != fec_mask_list.end()) {
uint32_t hamming_dist = 0;
uint32_t recovery_position = 0;
for (uint32_t i = 0; i < num_media_packets; i++) {
if (media_loss_mask[i] == 0 && (*fec_mask_it)[i] == 1) {
recovery_position = i;
++hamming_dist;
}
}
std::list<uint8_t*>::iterator itemToDelete = fecMaskIt;
++fecMaskIt;
auto item_to_delete = fec_mask_it;
++fec_mask_it;
if (hammingDist == 1) {
if (hamming_dist == 1) {
// Recovery possible. Restart search.
mediaLossMask[recoveryPosition] = 1;
fecMaskIt = fecMaskList.begin();
} else if (hammingDist == 0) {
media_loss_mask[recovery_position] = 1;
fec_mask_it = fec_mask_list.begin();
} else if (hamming_dist == 0) {
// FEC packet cannot provide further recovery.
fecMaskList.erase(itemToDelete);
fec_mask_list.erase(item_to_delete);
}
}
#ifdef VERBOSE_OUTPUT
printf("Recovery mask:\n");
for (uint32_t i = 0; i < numMediaPackets; ++i) {
printf("%u ", mediaLossMask[i]);
for (uint32_t i = 0; i < num_media_packets; ++i) {
printf("%u ", media_loss_mask[i]);
}
printf("\n\n");
#endif
// For error-checking frame completion.
bool fecPacketReceived = false;
while (!receivedPacketList.empty()) {
size_t numPacketsToDecode = random.Rand(
1u, static_cast<uint32_t>(receivedPacketList.size()));
ReceivePackets(&toDecodeList, &receivedPacketList,
numPacketsToDecode, reorderRate, duplicateRate,
&random);
bool fec_packet_received = false;
while (!received_packet_list.empty()) {
size_t num_packets_to_decode = random.Rand(
1u, static_cast<uint32_t>(received_packet_list.size()));
ReceivePackets(&to_decode_list, &received_packet_list,
num_packets_to_decode, reorder_rate,
duplicate_rate, &random);
if (fecPacketReceived == false) {
ForwardErrorCorrection::ReceivedPacketList::iterator
toDecodeIt = toDecodeList.begin();
while (toDecodeIt != toDecodeList.end()) {
receivedPacket = *toDecodeIt;
if (receivedPacket->is_fec) {
fecPacketReceived = true;
if (fec_packet_received == false) {
for (auto* received_packet : to_decode_list) {
if (received_packet->is_fec) {
fec_packet_received = true;
}
++toDecodeIt;
}
}
ASSERT_EQ(0, fec.DecodeFEC(&toDecodeList, &recoveredPacketList))
<< "DecodeFEC() failed";
ASSERT_TRUE(toDecodeList.empty())
ASSERT_EQ(0, fec.DecodeFec(&to_decode_list,
&recovered_packet_list))
<< "DecodeFec() failed";
ASSERT_TRUE(to_decode_list.empty())
<< "Received packet list is not empty.";
}
mediaPacketListItem = mediaPacketList.begin();
mediaPacketIdx = 0;
while (mediaPacketListItem != mediaPacketList.end()) {
if (mediaLossMask[mediaPacketIdx] == 1) {
media_packet_idx = 0;
for (auto* media_packet : media_packet_list) {
if (media_loss_mask[media_packet_idx] == 1) {
// Should have recovered this packet.
ForwardErrorCorrection::RecoveredPacketList::iterator
recoveredPacketListItem = recoveredPacketList.begin();
auto recovered_packet_list_it = recovered_packet_list.cbegin();
ASSERT_FALSE(recoveredPacketListItem ==
recoveredPacketList.end())
ASSERT_FALSE(recovered_packet_list_it ==
recovered_packet_list.end())
<< "Insufficient number of recovered packets.";
mediaPacket = *mediaPacketListItem;
ForwardErrorCorrection::RecoveredPacket* recoveredPacket =
*recoveredPacketListItem;
ForwardErrorCorrection::RecoveredPacket* recovered_packet =
*recovered_packet_list_it;
ASSERT_EQ(recoveredPacket->pkt->length, mediaPacket->length)
ASSERT_EQ(recovered_packet->pkt->length, media_packet->length)
<< "Recovered packet length not identical to original "
<< "media packet";
ASSERT_EQ(0, memcmp(recoveredPacket->pkt->data,
mediaPacket->data, mediaPacket->length))
ASSERT_EQ(0, memcmp(recovered_packet->pkt->data,
media_packet->data, media_packet->length))
<< "Recovered packet payload not identical to original "
<< "media packet";
delete recoveredPacket;
recoveredPacketList.pop_front();
delete recovered_packet;
recovered_packet_list.pop_front();
}
++mediaPacketIdx;
++mediaPacketListItem;
++media_packet_idx;
}
fec.ResetState(&recoveredPacketList);
ASSERT_TRUE(recoveredPacketList.empty())
fec.ResetState(&recovered_packet_list);
ASSERT_TRUE(recovered_packet_list.empty())
<< "Excessive number of recovered packets.\t size is: "
<< recoveredPacketList.size();
<< recovered_packet_list.size();
// -- Teardown --
mediaPacketListItem = mediaPacketList.begin();
while (mediaPacketListItem != mediaPacketList.end()) {
delete *mediaPacketListItem;
++mediaPacketListItem;
mediaPacketList.pop_front();
auto media_packet_list_it = media_packet_list.begin();
while (media_packet_list_it != media_packet_list.end()) {
delete *media_packet_list_it;
++media_packet_list_it;
media_packet_list.pop_front();
}
assert(mediaPacketList.empty());
RTC_DCHECK(media_packet_list.empty());
fecPacketListItem = fecPacketList.begin();
while (fecPacketListItem != fecPacketList.end()) {
++fecPacketListItem;
fecPacketList.pop_front();
}
// Clear FEC packet list, so we don't pass in a non-empty
// list in the next call to DecodeFec().
fec_packet_list.clear();
// Delete received packets we didn't pass to DecodeFEC(), due to
// Delete received packets we didn't pass to DecodeFec(), due to
// early frame completion.
ForwardErrorCorrection::ReceivedPacketList::iterator
receivedPacketIt = receivedPacketList.begin();
while (receivedPacketIt != receivedPacketList.end()) {
receivedPacket = *receivedPacketIt;
delete receivedPacket;
++receivedPacketIt;
receivedPacketList.pop_front();
auto received_packet_it = received_packet_list.cbegin();
while (received_packet_it != received_packet_list.end()) {
delete *received_packet_it;
++received_packet_it;
received_packet_list.pop_front();
}
assert(receivedPacketList.empty());
RTC_DCHECK(received_packet_list.empty());
while (!fecMaskList.empty()) {
fecMaskList.pop_front();
while (!fec_mask_list.empty()) {
fec_mask_list.pop_front();
}
timeStamp += 90000 / 30;
} // loop over numImpPackets
timestamp += 90000 / 30;
} // loop over num_imp_packets
} // loop over FecPackets
} // loop over numMediaPackets
delete[] packetMask;
} // loop over num_media_packets
} // loop over loss rates
} // loop over mask types
// Have DecodeFEC free allocated memory.
fec.ResetState(&recoveredPacketList);
ASSERT_TRUE(recoveredPacketList.empty())
// Have DecodeFec free allocated memory.
fec.ResetState(&recovered_packet_list);
ASSERT_TRUE(recovered_packet_list.empty())
<< "Recovered packet list is not empty";
}