Files
platform-external-webrtc/src/modules/rtp_rtcp/source/rtp_fec_unittest.cc
stefan@webrtc.org 7adab0922d This removes the knowledge of frame completeness from the FEC decoder.
Therefore, with this change a recovered packet is only considered old,
and will be removed, if more than 48 recovered packets are stored.

Packets are immediately reconstructed and sent to the jitter
buffer. Before this CL packets were processed on a frame-by-frame
basis, and all packets belonging to a frame was sent to the
jitter buffer at the same time.

The number of FEC packets is also limited to 48. An FEC packet is
removed if all protected packets have been recovered or if the
FEC packet is considered old.

Lot's of tests added.

Patch-set 2:
- Fixed rtp_fec_unittest.cc to work with the new reconstruction.
- Added reference counting of Packet to be able to keep references to them from FecPacket between different reconstruction runs.
- Rewrote the packet search to use std::set_intersection.

BUG=
TEST=

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

git-svn-id: http://webrtc.googlecode.com/svn/trunk@1651 4adac7df-926f-26a2-2b94-8c16560cd09d
2012-02-09 12:34:52 +00:00

551 lines
19 KiB
C++

/*
* Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "modules/rtp_rtcp/source/forward_error_correction.h"
#include <gtest/gtest.h>
#include <list>
#include "rtp_utility.h"
using webrtc::ForwardErrorCorrection;
// Minimum RTP header size in bytes.
const uint8_t kRtpHeaderSize = 12;
// Transport header size in bytes. Assume UDP/IPv4 as a reasonable minimum.
const uint8_t kTransportOverhead = 28;
// Maximum number of media packets used in the FEC (RFC 5109).
const uint8_t kMaxNumberMediaPackets = ForwardErrorCorrection::kMaxMediaPackets;
template<typename T> void ClearList(std::list<T*>* my_list) {
T* packet = NULL;
while (!my_list->empty()) {
packet = my_list->front();
delete packet;
my_list->pop_front();
}
}
class RtpFecTest : public ::testing::Test {
protected:
RtpFecTest()
: fec_(new ForwardErrorCorrection(0)),
ssrc_(rand()),
fec_seq_num_(0) {
}
ForwardErrorCorrection* fec_;
int ssrc_;
uint16_t fec_seq_num_;
std::list<ForwardErrorCorrection::Packet*> media_packet_list_;
std::list<ForwardErrorCorrection::Packet*> fec_packet_list_;
std::list<ForwardErrorCorrection::ReceivedPacket*> received_packet_list_;
std::list<ForwardErrorCorrection::RecoveredPacket*> recovered_packet_list_;
// Media packet "i" is lost if media_loss_mask_[i] = 1,
// received if media_loss_mask_[i] = 0.
int media_loss_mask_[kMaxNumberMediaPackets];
// FEC packet "i" is lost if fec_loss_mask_[i] = 1,
// received if fec_loss_mask_[i] = 0.
int fec_loss_mask_[kMaxNumberMediaPackets];
// Construct the media packet list, up to |num_media_packets| packets.
// Returns the next sequence number after the last media packet.
// (this will be the sequence of the first FEC packet)
int ConstructMediaPackets(int num_media_packets);
// Construct the received packet list: a subset of the media and FEC packets.
void NetworkReceivedPackets();
// Add packet from |packet_list| to list of received packets, using the
// |loss_mask|.
// The |packet_list| may be a media packet list (is_fec = false), or a
// FEC packet list (is_fec = true).
void ReceivedPackets(
const std::list<ForwardErrorCorrection::Packet*>& packet_list,
int* loss_mask,
bool is_fec);
// Check for complete recovery after FEC decoding.
bool IsRecoveryComplete();
// Delete the received packets.
void FreeRecoveredPacketList();
// Delete the media and FEC packets.
void TearDown();
};
TEST_F(RtpFecTest, HandleIncorrectInputs) {
int num_important_packets = 0;
bool use_unequal_protection = false;
uint8_t protection_factor = 60;
// Media packet list is empty.
EXPECT_EQ(-1, fec_->GenerateFEC(media_packet_list_,
protection_factor,
num_important_packets,
use_unequal_protection,
&fec_packet_list_));
int num_media_packets = 10;
ConstructMediaPackets(num_media_packets);
num_important_packets = -1;
// Number of important packets below 0.
EXPECT_EQ(-1, fec_->GenerateFEC(media_packet_list_,
protection_factor,
num_important_packets,
use_unequal_protection,
&fec_packet_list_));
num_important_packets = 12;
// Number of important packets greater than number of media packets.
EXPECT_EQ(-1, fec_->GenerateFEC(media_packet_list_,
protection_factor,
num_important_packets,
use_unequal_protection,
&fec_packet_list_));
num_media_packets = kMaxNumberMediaPackets + 1;
ConstructMediaPackets(num_media_packets);
num_important_packets = 0;
// Number of media packet is above maximum allowed (kMaxNumberMediaPackets).
EXPECT_EQ(-1, fec_->GenerateFEC(media_packet_list_,
protection_factor,
num_important_packets,
use_unequal_protection,
&fec_packet_list_));
}
TEST_F(RtpFecTest, FecRecoveryNoLoss) {
const int num_important_packets = 0;
const bool use_unequal_protection = false;
const int num_media_packets = 4;
uint8_t protection_factor = 60;
fec_seq_num_ = ConstructMediaPackets(num_media_packets);
EXPECT_EQ(0, fec_->GenerateFEC(media_packet_list_,
protection_factor,
num_important_packets,
use_unequal_protection,
&fec_packet_list_));
// Expect 1 FEC packet.
EXPECT_EQ(1, static_cast<int>(fec_packet_list_.size()));
// No packets lost.
memset(media_loss_mask_, 0, sizeof(media_loss_mask_));
memset(fec_loss_mask_, 0, sizeof(fec_loss_mask_));
NetworkReceivedPackets();
EXPECT_EQ(0, fec_->DecodeFEC(&received_packet_list_ ,
&recovered_packet_list_));
// No packets lost, expect complete recovery.
EXPECT_TRUE(IsRecoveryComplete());
}
TEST_F(RtpFecTest, FecRecoveryWithLoss) {
const int num_important_packets = 0;
const bool use_unequal_protection = false;
const int num_media_packets = 4;
uint8_t protection_factor = 60;
fec_seq_num_ = ConstructMediaPackets(num_media_packets);
EXPECT_EQ(0, fec_->GenerateFEC(media_packet_list_,
protection_factor,
num_important_packets,
use_unequal_protection,
&fec_packet_list_));
// Expect 1 FEC packet.
EXPECT_EQ(1, static_cast<int>(fec_packet_list_.size()));
// 1 media packet lost
memset(media_loss_mask_, 0, sizeof(media_loss_mask_));
memset(fec_loss_mask_, 0, sizeof(fec_loss_mask_));
media_loss_mask_[3] = 1;
NetworkReceivedPackets();
EXPECT_EQ(0, fec_->DecodeFEC(&received_packet_list_ ,
&recovered_packet_list_));
// One packet lost, one FEC packet, expect complete recovery.
EXPECT_TRUE(IsRecoveryComplete());
FreeRecoveredPacketList();
// 2 media packets lost.
memset(media_loss_mask_, 0, sizeof(media_loss_mask_));
memset(fec_loss_mask_, 0, sizeof(fec_loss_mask_));
media_loss_mask_[1] = 1;
media_loss_mask_[3] = 1;
NetworkReceivedPackets();
EXPECT_EQ(0, fec_->DecodeFEC(&received_packet_list_ ,
&recovered_packet_list_));
// 2 packets lost, one FEC packet, cannot get complete recovery.
EXPECT_FALSE(IsRecoveryComplete());
}
TEST_F(RtpFecTest, FecRecoveryWithLoss50perc) {
const int num_important_packets = 0;
const bool use_unequal_protection = false;
const int num_media_packets = 4;
const uint8_t protection_factor = 255;
// Packet Mask for (4,4,0) code:
// (num_media_packets = 4; num_fec_packets = 4, num_important_packets = 0)
// media#0 media#1 media#2 media#3
// fec#0: 1 1 0 0
// fec#1: 1 0 1 0
// fec#2: 0 1 0 1
// fec#3: 0 0 1 1
//
fec_seq_num_ = ConstructMediaPackets(num_media_packets);
EXPECT_EQ(0, fec_->GenerateFEC(media_packet_list_,
protection_factor,
num_important_packets,
use_unequal_protection,
&fec_packet_list_));
// Expect 4 FEC packets.
EXPECT_EQ(4, static_cast<int>(fec_packet_list_.size()));
// 4 packets lost: 3 media packets and one FEC packet#2 lost.
memset(media_loss_mask_, 0, sizeof(media_loss_mask_));
memset(fec_loss_mask_, 0, sizeof(fec_loss_mask_));
fec_loss_mask_[2] = 1;
media_loss_mask_[0] = 1;
media_loss_mask_[2] = 1;
media_loss_mask_[3] = 1;
NetworkReceivedPackets();
EXPECT_EQ(0, fec_->DecodeFEC(&received_packet_list_ ,
&recovered_packet_list_));
// With media packet#1 and FEC packets #0, #1, #3, expect complete recovery.
EXPECT_TRUE(IsRecoveryComplete());
FreeRecoveredPacketList();
// 4 packets lost: all media packets
memset(media_loss_mask_, 0, sizeof(media_loss_mask_));
memset(fec_loss_mask_, 1, sizeof(fec_loss_mask_));
media_loss_mask_[0] = 1;
media_loss_mask_[1] = 1;
media_loss_mask_[2] = 1;
media_loss_mask_[3] = 1;
NetworkReceivedPackets();
EXPECT_EQ(0, fec_->DecodeFEC(&received_packet_list_ ,
&recovered_packet_list_));
// Cannot get complete recovery for this loss configuration.
EXPECT_FALSE(IsRecoveryComplete());
}
TEST_F(RtpFecTest, FecRecoveryNoLossUep) {
const int num_important_packets = 2;
const bool use_unequal_protection = true;
const int num_media_packets = 4;
const uint8_t protection_factor = 60;
fec_seq_num_ = ConstructMediaPackets(num_media_packets);
EXPECT_EQ(0, fec_->GenerateFEC(media_packet_list_,
protection_factor,
num_important_packets,
use_unequal_protection,
&fec_packet_list_));
// Expect 1 FEC packet.
EXPECT_EQ(1, static_cast<int>(fec_packet_list_.size()));
// No packets lost.
memset(media_loss_mask_, 0, sizeof(media_loss_mask_));
memset(fec_loss_mask_, 0, sizeof(fec_loss_mask_));
NetworkReceivedPackets();
EXPECT_EQ(0, fec_->DecodeFEC(&received_packet_list_ ,
&recovered_packet_list_));
// No packets lost, expect complete recovery.
EXPECT_TRUE(IsRecoveryComplete());
}
TEST_F(RtpFecTest, FecRecoveryWithLossUep) {
const int num_important_packets = 2;
const bool use_unequal_protection = true;
const int num_media_packets = 4;
const uint8_t protection_factor = 60;
fec_seq_num_ = ConstructMediaPackets(num_media_packets);
EXPECT_EQ(0, fec_->GenerateFEC(media_packet_list_,
protection_factor,
num_important_packets,
use_unequal_protection,
&fec_packet_list_));
// Expect 1 FEC packet.
EXPECT_EQ(1, static_cast<int>(fec_packet_list_.size()));
// 1 media packet lost.
memset(media_loss_mask_, 0, sizeof(media_loss_mask_));
memset(fec_loss_mask_, 0, sizeof(fec_loss_mask_));
media_loss_mask_[3] = 1;
NetworkReceivedPackets();
EXPECT_EQ(0, fec_->DecodeFEC(&received_packet_list_ ,
&recovered_packet_list_));
// One packet lost, one FEC packet, expect complete recovery.
EXPECT_TRUE(IsRecoveryComplete());
FreeRecoveredPacketList();
// 2 media packets lost.
memset(media_loss_mask_, 0, sizeof(media_loss_mask_));
memset(fec_loss_mask_, 0, sizeof(fec_loss_mask_));
media_loss_mask_[1] = 1;
media_loss_mask_[3] = 1;
NetworkReceivedPackets();
EXPECT_EQ(0, fec_->DecodeFEC(&received_packet_list_ ,
&recovered_packet_list_));
// 2 packets lost, one FEC packet, cannot get complete recovery.
EXPECT_FALSE(IsRecoveryComplete());
}
TEST_F(RtpFecTest, FecRecoveryWithLoss50percUep) {
const int num_important_packets = 1;
const bool use_unequal_protection = true;
const int num_media_packets = 4;
const uint8_t protection_factor = 255;
// Packet Mask for (4,4,1) code:
// (num_media_packets = 4; num_fec_packets = 4, num_important_packets = 2)
// media#0 media#1 media#2 media#3
// fec#0: 1 0 0 0
// fec#1: 1 1 0 0
// fec#2: 1 0 1 1
// fec#3: 0 1 1 0
//
fec_seq_num_ = ConstructMediaPackets(num_media_packets);
EXPECT_EQ(0, fec_->GenerateFEC(media_packet_list_,
protection_factor,
num_important_packets,
use_unequal_protection,
&fec_packet_list_));
// Expect 4 FEC packets.
EXPECT_EQ(4, static_cast<int>(fec_packet_list_.size()));
// 4 packets lost: 3 media packets and FEC packet#1 lost.
memset(media_loss_mask_, 0, sizeof(media_loss_mask_));
memset(fec_loss_mask_, 0, sizeof(fec_loss_mask_));
fec_loss_mask_[1] = 1;
media_loss_mask_[0] = 1;
media_loss_mask_[2] = 1;
media_loss_mask_[3] = 1;
NetworkReceivedPackets();
EXPECT_EQ(0, fec_->DecodeFEC(&received_packet_list_ ,
&recovered_packet_list_));
// With media packet#1 and FEC packets #0, #2, #3, expect complete recovery.
EXPECT_TRUE(IsRecoveryComplete());
FreeRecoveredPacketList();
// 4 packets lost: 3 media packets and one FEC packet#2 lost.
memset(media_loss_mask_, 0, sizeof(media_loss_mask_));
memset(fec_loss_mask_, 0, sizeof(fec_loss_mask_));
fec_loss_mask_[2] = 1;
media_loss_mask_[0] = 1;
media_loss_mask_[2] = 1;
media_loss_mask_[3] = 1;
NetworkReceivedPackets();
EXPECT_EQ(0, fec_->DecodeFEC(&received_packet_list_ ,
&recovered_packet_list_));
// Cannot get complete recovery for this loss configuration.
EXPECT_FALSE(IsRecoveryComplete());
}
// TODO(marpan): Add more test cases.
void RtpFecTest::TearDown() {
fec_->ResetState(&recovered_packet_list_);
delete fec_;
FreeRecoveredPacketList();
ClearList(&media_packet_list_);
EXPECT_TRUE(media_packet_list_.empty());
}
void RtpFecTest::FreeRecoveredPacketList() {
ClearList(&recovered_packet_list_);
}
bool RtpFecTest::IsRecoveryComplete() {
// Check that the number of media and recovered packets are equal.
if (media_packet_list_.size() != recovered_packet_list_.size()) {
return false;
}
ForwardErrorCorrection::Packet* media_packet;
ForwardErrorCorrection::RecoveredPacket* recovered_packet;
bool recovery = true;
std::list<ForwardErrorCorrection::Packet*>::iterator
media_packet_list_item = media_packet_list_.begin();
std::list<ForwardErrorCorrection::RecoveredPacket*>::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()) {
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,
media_packet->length) != 0) {
return false;
}
media_packet_list_item++;
recovered_packet_list_item++;
}
return recovery;
}
void RtpFecTest::NetworkReceivedPackets() {
const bool kFecPacket = true;
ReceivedPackets(media_packet_list_, media_loss_mask_, !kFecPacket);
ReceivedPackets(fec_packet_list_, fec_loss_mask_, kFecPacket);
}
void RtpFecTest:: ReceivedPackets(
const std::list<ForwardErrorCorrection::Packet*>& 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;
std::list<ForwardErrorCorrection::Packet*>::const_iterator
packet_list_item = packet_list.begin();
while (packet_list_item != packet_list.end()) {
packet = *packet_list_item;
if (loss_mask[packet_idx] == 0) {
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);
received_packet->isFec = is_fec;
if (!is_fec) {
// For media packets, the sequence number and marker bit is
// obtained from RTP header. These were set in ConstructMediaPackets().
received_packet->seqNum =
webrtc::ModuleRTPUtility::BufferToUWord16(&packet->data[2]);
}
else {
// The sequence number, marker bit, and ssrc number are defined in the
// RTP header of the FEC packet, which is not constructed in this test.
// So we set these values below based on the values generated in
// ConstructMediaPackets().
received_packet->seqNum = seq_num;
// The ssrc value for FEC packets is set to the one used for the
// media packets in ConstructMediaPackets().
received_packet->ssrc = ssrc_;
}
}
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++;
}
}
int RtpFecTest::ConstructMediaPackets(int num_media_packets) {
assert(num_media_packets > 0);
ForwardErrorCorrection::Packet* media_packet = NULL;
int sequence_number = rand();
int time_stamp = rand();
for (int i = 0; i < num_media_packets; i++) {
media_packet = new ForwardErrorCorrection::Packet;
media_packet_list_.push_back(media_packet);
media_packet->length =
static_cast<uint16_t>((static_cast<float>(rand()) / RAND_MAX) *
(IP_PACKET_SIZE - kRtpHeaderSize - kTransportOverhead -
ForwardErrorCorrection::PacketOverhead()));
if (media_packet->length < kRtpHeaderSize) {
media_packet->length = kRtpHeaderSize;
}
// Generate random values for the first 2 bytes
media_packet->data[0] = static_cast<uint8_t>(rand() % 256);
media_packet->data[1] = static_cast<uint8_t>(rand() % 256);
// The first two bits are assumed to be 10 by the FEC encoder.
// In fact the FEC decoder will set the two first bits to 10 regardless of
// what they actually were. Set the first two bits to 10 so that a memcmp
// can be performed for the whole restored packet.
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.
media_packet->data[1] &= 0x7f;
webrtc::ModuleRTPUtility::AssignUWord16ToBuffer(&media_packet->data[2],
sequence_number);
webrtc::ModuleRTPUtility::AssignUWord32ToBuffer(&media_packet->data[4],
time_stamp);
webrtc::ModuleRTPUtility::AssignUWord32ToBuffer(&media_packet->data[8],
ssrc_);
// Generate random values for payload.
for (int j = 12; j < media_packet->length; j++) {
media_packet->data[j] = static_cast<uint8_t> (rand() % 256);
}
sequence_number++;
}
// Last packet, set marker bit.
assert(media_packet != NULL);
media_packet->data[1] |= 0x80;
return sequence_number;
}