webrtc_m130/webrtc/modules/rtp_rtcp/source/nack_rtx_unittest.cc

341 lines
12 KiB
C++
Raw Normal View History

/*
* Copyright (c) 2013 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 <algorithm>
#include <iterator>
#include <list>
#include <set>
#include "testing/gtest/include/gtest/gtest.h"
#include "webrtc/base/scoped_ptr.h"
#include "webrtc/common_types.h"
#include "webrtc/modules/rtp_rtcp/include/receive_statistics.h"
#include "webrtc/modules/rtp_rtcp/include/rtp_header_parser.h"
#include "webrtc/modules/rtp_rtcp/include/rtp_payload_registry.h"
#include "webrtc/modules/rtp_rtcp/include/rtp_receiver.h"
#include "webrtc/modules/rtp_rtcp/include/rtp_rtcp.h"
#include "webrtc/modules/rtp_rtcp/include/rtp_rtcp_defines.h"
#include "webrtc/transport.h"
using namespace webrtc;
const int kVideoNackListSize = 30;
const uint32_t kTestSsrc = 3456;
const uint16_t kTestSequenceNumber = 2345;
const uint32_t kTestNumberOfPackets = 1350;
const int kTestNumberOfRtxPackets = 149;
const int kNumFrames = 30;
const int kPayloadType = 123;
const int kRtxPayloadType = 98;
class VerifyingRtxReceiver : public NullRtpData {
public:
VerifyingRtxReceiver() {}
int32_t OnReceivedPayloadData(
const uint8_t* data,
const size_t size,
const webrtc::WebRtcRTPHeader* rtp_header) override {
if (!sequence_numbers_.empty())
EXPECT_EQ(kTestSsrc, rtp_header->header.ssrc);
sequence_numbers_.push_back(rtp_header->header.sequenceNumber);
return 0;
}
std::list<uint16_t> sequence_numbers_;
};
class TestRtpFeedback : public NullRtpFeedback {
public:
TestRtpFeedback(RtpRtcp* rtp_rtcp) : rtp_rtcp_(rtp_rtcp) {}
virtual ~TestRtpFeedback() {}
void OnIncomingSSRCChanged(const uint32_t ssrc) override {
rtp_rtcp_->SetRemoteSSRC(ssrc);
}
private:
RtpRtcp* rtp_rtcp_;
};
class RtxLoopBackTransport : public webrtc::Transport {
public:
explicit RtxLoopBackTransport(uint32_t rtx_ssrc)
: count_(0),
packet_loss_(0),
consecutive_drop_start_(0),
consecutive_drop_end_(0),
rtx_ssrc_(rtx_ssrc),
count_rtx_ssrc_(0),
rtp_payload_registry_(NULL),
rtp_receiver_(NULL),
module_(NULL) {}
void SetSendModule(RtpRtcp* rtpRtcpModule,
RTPPayloadRegistry* rtp_payload_registry,
RtpReceiver* receiver) {
module_ = rtpRtcpModule;
rtp_payload_registry_ = rtp_payload_registry;
rtp_receiver_ = receiver;
}
void DropEveryNthPacket(int n) { packet_loss_ = n; }
void DropConsecutivePackets(int start, int total) {
consecutive_drop_start_ = start;
consecutive_drop_end_ = start + total;
packet_loss_ = 0;
}
bool SendRtp(const uint8_t* data,
size_t len,
const PacketOptions& options) override {
count_++;
const unsigned char* ptr = static_cast<const unsigned char*>(data);
uint32_t ssrc = (ptr[8] << 24) + (ptr[9] << 16) + (ptr[10] << 8) + ptr[11];
if (ssrc == rtx_ssrc_)
count_rtx_ssrc_++;
uint16_t sequence_number = (ptr[2] << 8) + ptr[3];
size_t packet_length = len;
uint8_t restored_packet[1500];
RTPHeader header;
rtc::scoped_ptr<RtpHeaderParser> parser(RtpHeaderParser::Create());
if (!parser->Parse(ptr, len, &header)) {
return false;
}
Changed loopback transport in RtxNackTest to not store sequence numbers for retransmitted packets. The unit test currently works as follows: RtxLoopBackTransport logs the sequence numbers for all sent packets in expected_sequence_numbers_. Since the transport is configured to drop some of the packets there will be requests for retransmissions and the RTX sequence numbers will also be stored in the same list. The (non-rtx) packets are received by VerifyingRtxReceiver which also stores the sequence numbers in a list sequence_numbers_. Both lists are then sorted and sequence_numbers_ is compared to whatever is in the start of expected_sequence_numbers_. This works assuming that the RTX sequence numbers are greater than the regular RTP sequence numbers. In the RTP sender, both RTP and RTX are set to start at "random" 15-bit sequence numbers. The RTP sequence number is then changed to 2345 in the unit test, which would imply that the RTX sequence number is lower than the ones for RTP with probability ~1%. The reason why the test works anyway is that the test sets up a fake clock, which is used to initialize the random number generator in RTPSender, and the fixed starting point for the clock happens to result in RTX sequence numbers greater than 2345. However, any change to the initialization of the sequence numbers, the seeding of the PRNG or the fake clock causes a test failure with probability ~1%. The new code omits the RTX sequence numbers from expected_sequence_numbers_, thus avoiding the problem with low RTX sequence numbers. The initialization of the sequence numbers in RTPSender is also bad, but I'll fix that in another CL. Review URL: https://codereview.webrtc.org/1263383002 Cr-Commit-Position: refs/heads/master@{#9967}
2015-09-17 03:19:45 -07:00
if (!rtp_payload_registry_->IsRtx(header)) {
// Don't store retransmitted packets since we compare it to the list
// created by the receiver.
expected_sequence_numbers_.insert(expected_sequence_numbers_.end(),
sequence_number);
}
if (packet_loss_ > 0) {
if ((count_ % packet_loss_) == 0) {
return true;
Changed loopback transport in RtxNackTest to not store sequence numbers for retransmitted packets. The unit test currently works as follows: RtxLoopBackTransport logs the sequence numbers for all sent packets in expected_sequence_numbers_. Since the transport is configured to drop some of the packets there will be requests for retransmissions and the RTX sequence numbers will also be stored in the same list. The (non-rtx) packets are received by VerifyingRtxReceiver which also stores the sequence numbers in a list sequence_numbers_. Both lists are then sorted and sequence_numbers_ is compared to whatever is in the start of expected_sequence_numbers_. This works assuming that the RTX sequence numbers are greater than the regular RTP sequence numbers. In the RTP sender, both RTP and RTX are set to start at "random" 15-bit sequence numbers. The RTP sequence number is then changed to 2345 in the unit test, which would imply that the RTX sequence number is lower than the ones for RTP with probability ~1%. The reason why the test works anyway is that the test sets up a fake clock, which is used to initialize the random number generator in RTPSender, and the fixed starting point for the clock happens to result in RTX sequence numbers greater than 2345. However, any change to the initialization of the sequence numbers, the seeding of the PRNG or the fake clock causes a test failure with probability ~1%. The new code omits the RTX sequence numbers from expected_sequence_numbers_, thus avoiding the problem with low RTX sequence numbers. The initialization of the sequence numbers in RTPSender is also bad, but I'll fix that in another CL. Review URL: https://codereview.webrtc.org/1263383002 Cr-Commit-Position: refs/heads/master@{#9967}
2015-09-17 03:19:45 -07:00
}
} else if (count_ >= consecutive_drop_start_ &&
count_ < consecutive_drop_end_) {
return true;
Changed loopback transport in RtxNackTest to not store sequence numbers for retransmitted packets. The unit test currently works as follows: RtxLoopBackTransport logs the sequence numbers for all sent packets in expected_sequence_numbers_. Since the transport is configured to drop some of the packets there will be requests for retransmissions and the RTX sequence numbers will also be stored in the same list. The (non-rtx) packets are received by VerifyingRtxReceiver which also stores the sequence numbers in a list sequence_numbers_. Both lists are then sorted and sequence_numbers_ is compared to whatever is in the start of expected_sequence_numbers_. This works assuming that the RTX sequence numbers are greater than the regular RTP sequence numbers. In the RTP sender, both RTP and RTX are set to start at "random" 15-bit sequence numbers. The RTP sequence number is then changed to 2345 in the unit test, which would imply that the RTX sequence number is lower than the ones for RTP with probability ~1%. The reason why the test works anyway is that the test sets up a fake clock, which is used to initialize the random number generator in RTPSender, and the fixed starting point for the clock happens to result in RTX sequence numbers greater than 2345. However, any change to the initialization of the sequence numbers, the seeding of the PRNG or the fake clock causes a test failure with probability ~1%. The new code omits the RTX sequence numbers from expected_sequence_numbers_, thus avoiding the problem with low RTX sequence numbers. The initialization of the sequence numbers in RTPSender is also bad, but I'll fix that in another CL. Review URL: https://codereview.webrtc.org/1263383002 Cr-Commit-Position: refs/heads/master@{#9967}
2015-09-17 03:19:45 -07:00
}
if (rtp_payload_registry_->IsRtx(header)) {
// Remove the RTX header and parse the original RTP header.
EXPECT_TRUE(rtp_payload_registry_->RestoreOriginalPacket(
restored_packet, ptr, &packet_length, rtp_receiver_->SSRC(), header));
if (!parser->Parse(restored_packet, packet_length, &header)) {
return false;
}
ptr = restored_packet;
} else {
rtp_payload_registry_->SetIncomingPayloadType(header);
}
PayloadUnion payload_specific;
if (!rtp_payload_registry_->GetPayloadSpecifics(header.payloadType,
&payload_specific)) {
return false;
}
if (!rtp_receiver_->IncomingRtpPacket(header, ptr + header.headerLength,
packet_length - header.headerLength,
payload_specific, true)) {
return false;
}
return true;
}
bool SendRtcp(const uint8_t* data, size_t len) override {
return module_->IncomingRtcpPacket((const uint8_t*)data, len) == 0;
}
int count_;
int packet_loss_;
int consecutive_drop_start_;
int consecutive_drop_end_;
uint32_t rtx_ssrc_;
int count_rtx_ssrc_;
RTPPayloadRegistry* rtp_payload_registry_;
RtpReceiver* rtp_receiver_;
RtpRtcp* module_;
std::set<uint16_t> expected_sequence_numbers_;
};
class RtpRtcpRtxNackTest : public ::testing::Test {
protected:
RtpRtcpRtxNackTest()
: rtp_payload_registry_(RTPPayloadStrategy::CreateStrategy(false)),
rtp_rtcp_module_(NULL),
transport_(kTestSsrc + 1),
receiver_(),
payload_data_length(sizeof(payload_data)),
fake_clock(123456) {}
~RtpRtcpRtxNackTest() {}
void SetUp() override {
RtpRtcp::Configuration configuration;
configuration.audio = false;
configuration.clock = &fake_clock;
receive_statistics_.reset(ReceiveStatistics::Create(&fake_clock));
configuration.receive_statistics = receive_statistics_.get();
configuration.outgoing_transport = &transport_;
rtp_rtcp_module_ = RtpRtcp::CreateRtpRtcp(configuration);
rtp_feedback_.reset(new TestRtpFeedback(rtp_rtcp_module_));
rtp_receiver_.reset(RtpReceiver::CreateVideoReceiver(
&fake_clock, &receiver_, rtp_feedback_.get(), &rtp_payload_registry_));
rtp_rtcp_module_->SetSSRC(kTestSsrc);
rtp_rtcp_module_->SetRTCPStatus(RtcpMode::kCompound);
rtp_receiver_->SetNACKStatus(kNackRtcp);
rtp_rtcp_module_->SetStorePacketsStatus(true, 600);
EXPECT_EQ(0, rtp_rtcp_module_->SetSendingStatus(true));
rtp_rtcp_module_->SetSequenceNumber(kTestSequenceNumber);
rtp_rtcp_module_->SetStartTimestamp(111111);
transport_.SetSendModule(rtp_rtcp_module_, &rtp_payload_registry_,
rtp_receiver_.get());
VideoCodec video_codec;
memset(&video_codec, 0, sizeof(video_codec));
video_codec.plType = kPayloadType;
memcpy(video_codec.plName, "I420", 5);
EXPECT_EQ(0, rtp_rtcp_module_->RegisterSendPayload(video_codec));
rtp_rtcp_module_->SetRtxSendPayloadType(kRtxPayloadType, kPayloadType);
EXPECT_EQ(0, rtp_receiver_->RegisterReceivePayload(
video_codec.plName, video_codec.plType, 90000, 0,
video_codec.maxBitrate));
rtp_payload_registry_.SetRtxPayloadType(kRtxPayloadType, kPayloadType);
for (size_t n = 0; n < payload_data_length; n++) {
payload_data[n] = n % 10;
}
}
int BuildNackList(uint16_t* nack_list) {
receiver_.sequence_numbers_.sort();
std::list<uint16_t> missing_sequence_numbers;
std::list<uint16_t>::iterator it = receiver_.sequence_numbers_.begin();
while (it != receiver_.sequence_numbers_.end()) {
uint16_t sequence_number_1 = *it;
++it;
if (it != receiver_.sequence_numbers_.end()) {
uint16_t sequence_number_2 = *it;
// Add all missing sequence numbers to list
for (uint16_t i = sequence_number_1 + 1; i < sequence_number_2; ++i) {
missing_sequence_numbers.push_back(i);
}
}
}
int n = 0;
for (it = missing_sequence_numbers.begin();
it != missing_sequence_numbers.end(); ++it) {
nack_list[n++] = (*it);
}
return n;
}
bool ExpectedPacketsReceived() {
std::list<uint16_t> received_sorted;
std::copy(receiver_.sequence_numbers_.begin(),
receiver_.sequence_numbers_.end(),
std::back_inserter(received_sorted));
received_sorted.sort();
Changed loopback transport in RtxNackTest to not store sequence numbers for retransmitted packets. The unit test currently works as follows: RtxLoopBackTransport logs the sequence numbers for all sent packets in expected_sequence_numbers_. Since the transport is configured to drop some of the packets there will be requests for retransmissions and the RTX sequence numbers will also be stored in the same list. The (non-rtx) packets are received by VerifyingRtxReceiver which also stores the sequence numbers in a list sequence_numbers_. Both lists are then sorted and sequence_numbers_ is compared to whatever is in the start of expected_sequence_numbers_. This works assuming that the RTX sequence numbers are greater than the regular RTP sequence numbers. In the RTP sender, both RTP and RTX are set to start at "random" 15-bit sequence numbers. The RTP sequence number is then changed to 2345 in the unit test, which would imply that the RTX sequence number is lower than the ones for RTP with probability ~1%. The reason why the test works anyway is that the test sets up a fake clock, which is used to initialize the random number generator in RTPSender, and the fixed starting point for the clock happens to result in RTX sequence numbers greater than 2345. However, any change to the initialization of the sequence numbers, the seeding of the PRNG or the fake clock causes a test failure with probability ~1%. The new code omits the RTX sequence numbers from expected_sequence_numbers_, thus avoiding the problem with low RTX sequence numbers. The initialization of the sequence numbers in RTPSender is also bad, but I'll fix that in another CL. Review URL: https://codereview.webrtc.org/1263383002 Cr-Commit-Position: refs/heads/master@{#9967}
2015-09-17 03:19:45 -07:00
return received_sorted.size() ==
transport_.expected_sequence_numbers_.size() &&
std::equal(received_sorted.begin(), received_sorted.end(),
transport_.expected_sequence_numbers_.begin());
}
void RunRtxTest(RtxMode rtx_method, int loss) {
rtp_payload_registry_.SetRtxSsrc(kTestSsrc + 1);
rtp_rtcp_module_->SetRtxSendStatus(rtx_method);
rtp_rtcp_module_->SetRtxSsrc(kTestSsrc + 1);
transport_.DropEveryNthPacket(loss);
uint32_t timestamp = 3000;
uint16_t nack_list[kVideoNackListSize];
for (int frame = 0; frame < kNumFrames; ++frame) {
EXPECT_EQ(0, rtp_rtcp_module_->SendOutgoingData(
webrtc::kVideoFrameDelta, kPayloadType, timestamp,
timestamp / 90, payload_data, payload_data_length));
// Min required delay until retransmit = 5 + RTT ms (RTT = 0).
fake_clock.AdvanceTimeMilliseconds(5);
int length = BuildNackList(nack_list);
if (length > 0)
rtp_rtcp_module_->SendNACK(nack_list, length);
fake_clock.AdvanceTimeMilliseconds(28); // 33ms - 5ms delay.
rtp_rtcp_module_->Process();
// Prepare next frame.
timestamp += 3000;
}
receiver_.sequence_numbers_.sort();
}
void TearDown() override { delete rtp_rtcp_module_; }
rtc::scoped_ptr<ReceiveStatistics> receive_statistics_;
RTPPayloadRegistry rtp_payload_registry_;
rtc::scoped_ptr<RtpReceiver> rtp_receiver_;
RtpRtcp* rtp_rtcp_module_;
rtc::scoped_ptr<TestRtpFeedback> rtp_feedback_;
RtxLoopBackTransport transport_;
VerifyingRtxReceiver receiver_;
uint8_t payload_data[65000];
size_t payload_data_length;
SimulatedClock fake_clock;
};
TEST_F(RtpRtcpRtxNackTest, LongNackList) {
const int kNumPacketsToDrop = 900;
const int kNumRequiredRtcp = 4;
uint32_t timestamp = 3000;
uint16_t nack_list[kNumPacketsToDrop];
// Disable StorePackets to be able to set a larger packet history.
rtp_rtcp_module_->SetStorePacketsStatus(false, 0);
// Enable StorePackets with a packet history of 2000 packets.
rtp_rtcp_module_->SetStorePacketsStatus(true, 2000);
// Drop 900 packets from the second one so that we get a NACK list which is
// big enough to require 4 RTCP packets to be fully transmitted to the sender.
transport_.DropConsecutivePackets(2, kNumPacketsToDrop);
// Send 30 frames which at the default size is roughly what we need to get
// enough packets.
for (int frame = 0; frame < kNumFrames; ++frame) {
EXPECT_EQ(0, rtp_rtcp_module_->SendOutgoingData(
webrtc::kVideoFrameDelta, kPayloadType, timestamp,
timestamp / 90, payload_data, payload_data_length));
// Prepare next frame.
timestamp += 3000;
fake_clock.AdvanceTimeMilliseconds(33);
rtp_rtcp_module_->Process();
}
EXPECT_FALSE(transport_.expected_sequence_numbers_.empty());
EXPECT_FALSE(receiver_.sequence_numbers_.empty());
size_t last_receive_count = receiver_.sequence_numbers_.size();
int length = BuildNackList(nack_list);
for (int i = 0; i < kNumRequiredRtcp - 1; ++i) {
rtp_rtcp_module_->SendNACK(nack_list, length);
EXPECT_GT(receiver_.sequence_numbers_.size(), last_receive_count);
last_receive_count = receiver_.sequence_numbers_.size();
EXPECT_FALSE(ExpectedPacketsReceived());
}
rtp_rtcp_module_->SendNACK(nack_list, length);
EXPECT_GT(receiver_.sequence_numbers_.size(), last_receive_count);
EXPECT_TRUE(ExpectedPacketsReceived());
}
TEST_F(RtpRtcpRtxNackTest, RtxNack) {
RunRtxTest(kRtxRetransmitted, 10);
EXPECT_EQ(kTestSequenceNumber, *(receiver_.sequence_numbers_.begin()));
EXPECT_EQ(kTestSequenceNumber + kTestNumberOfPackets - 1,
*(receiver_.sequence_numbers_.rbegin()));
EXPECT_EQ(kTestNumberOfPackets, receiver_.sequence_numbers_.size());
EXPECT_EQ(kTestNumberOfRtxPackets, transport_.count_rtx_ssrc_);
EXPECT_TRUE(ExpectedPacketsReceived());
}