Add test which verifies that the RTP header extensions are set correctly for FEC packets.

Also taking the opportunity to do a little bit of clean up.

BUG=webrtc:705
R=pbos@webrtc.org

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

Cr-Commit-Position: refs/heads/master@{#10927}
This commit is contained in:
Stefan Holmer
2015-12-08 09:10:43 +01:00
parent 03ef053202
commit 4654d204e4
4 changed files with 141 additions and 102 deletions

View File

@ -129,9 +129,7 @@ int32_t ForwardErrorCorrection::GenerateFEC(const PacketList& media_packet_list,
int num_maskBytes = l_bit ? kMaskSizeLBitSet : kMaskSizeLBitClear; int num_maskBytes = l_bit ? kMaskSizeLBitSet : kMaskSizeLBitClear;
// Do some error checking on the media packets. // Do some error checking on the media packets.
PacketList::const_iterator media_list_it = media_packet_list.begin(); for (Packet* media_packet : media_packet_list) {
while (media_list_it != media_packet_list.end()) {
Packet* media_packet = *media_list_it;
assert(media_packet); assert(media_packet);
if (media_packet->length < kRtpHeaderSize) { if (media_packet->length < kRtpHeaderSize) {
@ -146,7 +144,6 @@ int32_t ForwardErrorCorrection::GenerateFEC(const PacketList& media_packet_list,
LOG(LS_WARNING) << "Media packet " << media_packet->length << " bytes " LOG(LS_WARNING) << "Media packet " << media_packet->length << " bytes "
<< "with overhead is larger than " << IP_PACKET_SIZE; << "with overhead is larger than " << IP_PACKET_SIZE;
} }
media_list_it++;
} }
int num_fec_packets = int num_fec_packets =
@ -167,29 +164,30 @@ int32_t ForwardErrorCorrection::GenerateFEC(const PacketList& media_packet_list,
// -- Generate packet masks -- // -- Generate packet masks --
// Always allocate space for a large mask. // Always allocate space for a large mask.
uint8_t* packet_mask = new uint8_t[num_fec_packets * kMaskSizeLBitSet]; rtc::scoped_ptr<uint8_t[]> packet_mask(
memset(packet_mask, 0, num_fec_packets * num_maskBytes); new uint8_t[num_fec_packets * kMaskSizeLBitSet]);
memset(packet_mask.get(), 0, num_fec_packets * num_maskBytes);
internal::GeneratePacketMasks(num_media_packets, num_fec_packets, internal::GeneratePacketMasks(num_media_packets, num_fec_packets,
num_important_packets, use_unequal_protection, num_important_packets, use_unequal_protection,
mask_table, packet_mask); mask_table, packet_mask.get());
int num_maskBits = InsertZerosInBitMasks(media_packet_list, packet_mask, int num_mask_bits = InsertZerosInBitMasks(
num_maskBytes, num_fec_packets); media_packet_list, packet_mask.get(), num_maskBytes, num_fec_packets);
l_bit = (num_maskBits > 8 * kMaskSizeLBitClear); l_bit = (num_mask_bits > 8 * kMaskSizeLBitClear);
if (num_maskBits < 0) { if (num_mask_bits < 0) {
delete[] packet_mask;
return -1; return -1;
} }
if (l_bit) { if (l_bit) {
num_maskBytes = kMaskSizeLBitSet; num_maskBytes = kMaskSizeLBitSet;
} }
GenerateFecBitStrings(media_packet_list, packet_mask, num_fec_packets, l_bit); GenerateFecBitStrings(media_packet_list, packet_mask.get(), num_fec_packets,
GenerateFecUlpHeaders(media_packet_list, packet_mask, l_bit, num_fec_packets); l_bit);
GenerateFecUlpHeaders(media_packet_list, packet_mask.get(), l_bit,
num_fec_packets);
delete[] packet_mask;
return 0; return 0;
} }

View File

@ -42,13 +42,13 @@ RTPSenderVideo::RTPSenderVideo(Clock* clock, RTPSenderInterface* rtpSender)
_retransmissionSettings(kRetransmitBaseLayer), _retransmissionSettings(kRetransmitBaseLayer),
// Generic FEC // Generic FEC
_fec(), fec_(),
_fecEnabled(false), fec_enabled_(false),
_payloadTypeRED(-1), red_payload_type_(-1),
_payloadTypeFEC(-1), fec_payload_type_(-1),
delta_fec_params_(), delta_fec_params_(),
key_fec_params_(), key_fec_params_(),
producer_fec_(&_fec), producer_fec_(&fec_),
_fecOverheadRate(clock, NULL), _fecOverheadRate(clock, NULL),
_videoBitrate(clock, NULL) { _videoBitrate(clock, NULL) {
memset(&delta_fec_params_, 0, sizeof(delta_fec_params_)); memset(&delta_fec_params_, 0, sizeof(delta_fec_params_));
@ -130,7 +130,7 @@ void RTPSenderVideo::SendVideoPacketAsRed(uint8_t* data_buffer,
// Only protect while creating RED and FEC packets, not when sending. // Only protect while creating RED and FEC packets, not when sending.
CriticalSectionScoped cs(crit_.get()); CriticalSectionScoped cs(crit_.get());
red_packet.reset(producer_fec_.BuildRedPacket( red_packet.reset(producer_fec_.BuildRedPacket(
data_buffer, payload_length, rtp_header_length, _payloadTypeRED)); data_buffer, payload_length, rtp_header_length, red_payload_type_));
if (protect) { if (protect) {
producer_fec_.AddRtpPacketAndGenerateFec(data_buffer, payload_length, producer_fec_.AddRtpPacketAndGenerateFec(data_buffer, payload_length,
rtp_header_length); rtp_header_length);
@ -140,7 +140,7 @@ void RTPSenderVideo::SendVideoPacketAsRed(uint8_t* data_buffer,
next_fec_sequence_number = next_fec_sequence_number =
_rtpSender.AllocateSequenceNumber(num_fec_packets); _rtpSender.AllocateSequenceNumber(num_fec_packets);
fec_packets = producer_fec_.GetFecPackets( fec_packets = producer_fec_.GetFecPackets(
_payloadTypeRED, _payloadTypeFEC, next_fec_sequence_number, red_payload_type_, fec_payload_type_, next_fec_sequence_number,
rtp_header_length); rtp_header_length);
RTC_DCHECK_EQ(num_fec_packets, fec_packets.size()); RTC_DCHECK_EQ(num_fec_packets, fec_packets.size());
if (_retransmissionSettings & kRetransmitFECPackets) if (_retransmissionSettings & kRetransmitFECPackets)
@ -180,9 +180,9 @@ void RTPSenderVideo::SetGenericFECStatus(const bool enable,
const uint8_t payloadTypeRED, const uint8_t payloadTypeRED,
const uint8_t payloadTypeFEC) { const uint8_t payloadTypeFEC) {
CriticalSectionScoped cs(crit_.get()); CriticalSectionScoped cs(crit_.get());
_fecEnabled = enable; fec_enabled_ = enable;
_payloadTypeRED = payloadTypeRED; red_payload_type_ = payloadTypeRED;
_payloadTypeFEC = payloadTypeFEC; fec_payload_type_ = payloadTypeFEC;
memset(&delta_fec_params_, 0, sizeof(delta_fec_params_)); memset(&delta_fec_params_, 0, sizeof(delta_fec_params_));
memset(&key_fec_params_, 0, sizeof(key_fec_params_)); memset(&key_fec_params_, 0, sizeof(key_fec_params_));
delta_fec_params_.max_fec_frames = key_fec_params_.max_fec_frames = 1; delta_fec_params_.max_fec_frames = key_fec_params_.max_fec_frames = 1;
@ -194,14 +194,14 @@ void RTPSenderVideo::GenericFECStatus(bool& enable,
uint8_t& payloadTypeRED, uint8_t& payloadTypeRED,
uint8_t& payloadTypeFEC) const { uint8_t& payloadTypeFEC) const {
CriticalSectionScoped cs(crit_.get()); CriticalSectionScoped cs(crit_.get());
enable = _fecEnabled; enable = fec_enabled_;
payloadTypeRED = _payloadTypeRED; payloadTypeRED = red_payload_type_;
payloadTypeFEC = _payloadTypeFEC; payloadTypeFEC = fec_payload_type_;
} }
size_t RTPSenderVideo::FECPacketOverhead() const { size_t RTPSenderVideo::FECPacketOverhead() const {
CriticalSectionScoped cs(crit_.get()); CriticalSectionScoped cs(crit_.get());
if (_fecEnabled) { if (fec_enabled_) {
// Overhead is FEC headers plus RED for FEC header plus anything in RTP // Overhead is FEC headers plus RED for FEC header plus anything in RTP
// header beyond the 12 bytes base header (CSRC list, extensions...) // header beyond the 12 bytes base header (CSRC list, extensions...)
// This reason for the header extensions to be included here is that // This reason for the header extensions to be included here is that
@ -247,7 +247,7 @@ int32_t RTPSenderVideo::SendVideo(const RtpVideoCodecTypes videoType,
frameType == kVideoFrameKey ? &key_fec_params_ : &delta_fec_params_; frameType == kVideoFrameKey ? &key_fec_params_ : &delta_fec_params_;
producer_fec_.SetFecParameters(fec_params, 0); producer_fec_.SetFecParameters(fec_params, 0);
storage = packetizer->GetStorageType(_retransmissionSettings); storage = packetizer->GetStorageType(_retransmissionSettings);
fec_enabled = _fecEnabled; fec_enabled = fec_enabled_;
} }
// Register CVO rtp header extension at the first time when we receive a frame // Register CVO rtp header extension at the first time when we receive a frame

View File

@ -110,10 +110,10 @@ private:
int32_t _retransmissionSettings GUARDED_BY(crit_); int32_t _retransmissionSettings GUARDED_BY(crit_);
// FEC // FEC
ForwardErrorCorrection _fec; ForwardErrorCorrection fec_;
bool _fecEnabled GUARDED_BY(crit_); bool fec_enabled_ GUARDED_BY(crit_);
int8_t _payloadTypeRED GUARDED_BY(crit_); int8_t red_payload_type_ GUARDED_BY(crit_);
int8_t _payloadTypeFEC GUARDED_BY(crit_); int8_t fec_payload_type_ GUARDED_BY(crit_);
FecProtectionParams delta_fec_params_ GUARDED_BY(crit_); FecProtectionParams delta_fec_params_ GUARDED_BY(crit_);
FecProtectionParams key_fec_params_ GUARDED_BY(crit_); FecProtectionParams key_fec_params_ GUARDED_BY(crit_);
ProducerFec producer_fec_ GUARDED_BY(crit_); ProducerFec producer_fec_ GUARDED_BY(crit_);

View File

@ -127,12 +127,11 @@ TEST_F(VideoSendStreamTest, SupportsCName) {
} }
TEST_F(VideoSendStreamTest, SupportsAbsoluteSendTime) { TEST_F(VideoSendStreamTest, SupportsAbsoluteSendTime) {
static const uint8_t kAbsSendTimeExtensionId = 13;
class AbsoluteSendTimeObserver : public test::SendTest { class AbsoluteSendTimeObserver : public test::SendTest {
public: public:
AbsoluteSendTimeObserver() : SendTest(kDefaultTimeoutMs) { AbsoluteSendTimeObserver() : SendTest(kDefaultTimeoutMs) {
EXPECT_TRUE(parser_->RegisterRtpHeaderExtension( EXPECT_TRUE(parser_->RegisterRtpHeaderExtension(
kRtpExtensionAbsoluteSendTime, kAbsSendTimeExtensionId)); kRtpExtensionAbsoluteSendTime, test::kAbsSendTimeExtensionId));
} }
Action OnSendRtp(const uint8_t* packet, size_t length) override { Action OnSendRtp(const uint8_t* packet, size_t length) override {
@ -152,8 +151,8 @@ TEST_F(VideoSendStreamTest, SupportsAbsoluteSendTime) {
std::vector<VideoReceiveStream::Config>* receive_configs, std::vector<VideoReceiveStream::Config>* receive_configs,
VideoEncoderConfig* encoder_config) override { VideoEncoderConfig* encoder_config) override {
send_config->rtp.extensions.clear(); send_config->rtp.extensions.clear();
send_config->rtp.extensions.push_back( send_config->rtp.extensions.push_back(RtpExtension(
RtpExtension(RtpExtension::kAbsSendTime, kAbsSendTimeExtensionId)); RtpExtension::kAbsSendTime, test::kAbsSendTimeExtensionId));
} }
void PerformTest() override { void PerformTest() override {
@ -310,81 +309,123 @@ class FakeReceiveStatistics : public NullReceiveStatistics {
StatisticianMap stats_map_; StatisticianMap stats_map_;
}; };
TEST_F(VideoSendStreamTest, SupportsFec) { class FecObserver : public test::SendTest {
class FecObserver : public test::SendTest { public:
public: explicit FecObserver(bool header_extensions_enabled)
FecObserver() : SendTest(VideoSendStreamTest::kDefaultTimeoutMs),
: SendTest(kDefaultTimeoutMs), send_count_(0),
send_count_(0), received_media_(false),
received_media_(false), received_fec_(false),
received_fec_(false) { header_extensions_enabled_(header_extensions_enabled) {}
private:
Action OnSendRtp(const uint8_t* packet, size_t length) override {
RTPHeader header;
EXPECT_TRUE(parser_->Parse(packet, length, &header));
// Send lossy receive reports to trigger FEC enabling.
if (send_count_++ % 2 != 0) {
// Receive statistics reporting having lost 50% of the packets.
FakeReceiveStatistics lossy_receive_stats(
VideoSendStreamTest::kSendSsrcs[0], header.sequenceNumber,
send_count_ / 2, 127);
RTCPSender rtcp_sender(false, Clock::GetRealTimeClock(),
&lossy_receive_stats, nullptr,
transport_adapter_.get());
rtcp_sender.SetRTCPStatus(RtcpMode::kReducedSize);
rtcp_sender.SetRemoteSSRC(VideoSendStreamTest::kSendSsrcs[0]);
RTCPSender::FeedbackState feedback_state;
EXPECT_EQ(0, rtcp_sender.SendRTCP(feedback_state, kRtcpRr));
} }
private: int encapsulated_payload_type = -1;
Action OnSendRtp(const uint8_t* packet, size_t length) override { if (header.payloadType == VideoSendStreamTest::kRedPayloadType) {
RTPHeader header; encapsulated_payload_type = static_cast<int>(packet[header.headerLength]);
EXPECT_TRUE(parser_->Parse(packet, length, &header)); if (encapsulated_payload_type !=
VideoSendStreamTest::kFakeSendPayloadType)
EXPECT_EQ(VideoSendStreamTest::kUlpfecPayloadType,
encapsulated_payload_type);
} else {
EXPECT_EQ(VideoSendStreamTest::kFakeSendPayloadType, header.payloadType);
}
// Send lossy receive reports to trigger FEC enabling. if (header_extensions_enabled_) {
if (send_count_++ % 2 != 0) { EXPECT_TRUE(header.extension.hasAbsoluteSendTime);
// Receive statistics reporting having lost 50% of the packets. uint32_t kHalf24BitsSpace = 0xFFFFFF / 2;
FakeReceiveStatistics lossy_receive_stats( if (header.extension.absoluteSendTime <= kHalf24BitsSpace &&
kSendSsrcs[0], header.sequenceNumber, send_count_ / 2, 127); prev_header_.extension.absoluteSendTime > kHalf24BitsSpace) {
RTCPSender rtcp_sender(false, Clock::GetRealTimeClock(), // 24 bits wrap.
&lossy_receive_stats, nullptr, EXPECT_GT(prev_header_.extension.absoluteSendTime,
transport_adapter_.get()); header.extension.absoluteSendTime);
rtcp_sender.SetRTCPStatus(RtcpMode::kReducedSize);
rtcp_sender.SetRemoteSSRC(kSendSsrcs[0]);
RTCPSender::FeedbackState feedback_state;
EXPECT_EQ(0, rtcp_sender.SendRTCP(feedback_state, kRtcpRr));
}
int encapsulated_payload_type = -1;
if (header.payloadType == kRedPayloadType) {
encapsulated_payload_type =
static_cast<int>(packet[header.headerLength]);
if (encapsulated_payload_type != kFakeSendPayloadType)
EXPECT_EQ(kUlpfecPayloadType, encapsulated_payload_type);
} else { } else {
EXPECT_EQ(kFakeSendPayloadType, header.payloadType); EXPECT_GE(header.extension.absoluteSendTime,
prev_header_.extension.absoluteSendTime);
} }
EXPECT_TRUE(header.extension.hasTransportSequenceNumber);
uint16_t seq_num_diff = header.extension.transportSequenceNumber -
prev_header_.extension.transportSequenceNumber;
EXPECT_EQ(1, seq_num_diff);
}
if (encapsulated_payload_type != -1) { if (encapsulated_payload_type != -1) {
if (encapsulated_payload_type == kUlpfecPayloadType) { if (encapsulated_payload_type ==
received_fec_ = true; VideoSendStreamTest::kUlpfecPayloadType) {
} else { received_fec_ = true;
received_media_ = true; } else {
} received_media_ = true;
} }
if (received_media_ && received_fec_)
observation_complete_->Set();
return SEND_PACKET;
} }
void ModifyConfigs(VideoSendStream::Config* send_config, if (received_media_ && received_fec_ && send_count_ > 100)
std::vector<VideoReceiveStream::Config>* receive_configs, observation_complete_->Set();
VideoEncoderConfig* encoder_config) override {
transport_adapter_.reset(
new internal::TransportAdapter(send_config->send_transport));
transport_adapter_->Enable();
send_config->rtp.fec.red_payload_type = kRedPayloadType;
send_config->rtp.fec.ulpfec_payload_type = kUlpfecPayloadType;
}
void PerformTest() override { prev_header_ = header;
EXPECT_TRUE(Wait()) << "Timed out waiting for FEC and media packets.";
}
rtc::scoped_ptr<internal::TransportAdapter> transport_adapter_; return SEND_PACKET;
int send_count_; }
bool received_media_;
bool received_fec_; void ModifyConfigs(VideoSendStream::Config* send_config,
} test; std::vector<VideoReceiveStream::Config>* receive_configs,
VideoEncoderConfig* encoder_config) override {
transport_adapter_.reset(
new internal::TransportAdapter(send_config->send_transport));
transport_adapter_->Enable();
send_config->rtp.fec.red_payload_type =
VideoSendStreamTest::kRedPayloadType;
send_config->rtp.fec.ulpfec_payload_type =
VideoSendStreamTest::kUlpfecPayloadType;
if (header_extensions_enabled_) {
send_config->rtp.extensions.push_back(RtpExtension(
RtpExtension::kAbsSendTime, test::kAbsSendTimeExtensionId));
send_config->rtp.extensions.push_back(
RtpExtension(RtpExtension::kTransportSequenceNumber,
test::kTransportSequenceNumberExtensionId));
}
}
void PerformTest() override {
EXPECT_TRUE(Wait()) << "Timed out waiting for FEC and media packets.";
}
rtc::scoped_ptr<internal::TransportAdapter> transport_adapter_;
int send_count_;
bool received_media_;
bool received_fec_;
bool header_extensions_enabled_;
RTPHeader prev_header_;
};
TEST_F(VideoSendStreamTest, SupportsFecWithExtensions) {
FecObserver test(true);
RunBaseTest(&test, FakeNetworkPipe::Config());
}
TEST_F(VideoSendStreamTest, SupportsFecWithoutExtensions) {
FecObserver test(false);
RunBaseTest(&test, FakeNetworkPipe::Config()); RunBaseTest(&test, FakeNetworkPipe::Config());
} }