2013-03-15 23:21:52 +00:00
|
|
|
/*
|
|
|
|
|
* 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 <vector>
|
|
|
|
|
|
|
|
|
|
#include "testing/gtest/include/gtest/gtest.h"
|
|
|
|
|
#include "webrtc/common_types.h"
|
|
|
|
|
#include "webrtc/modules/rtp_rtcp/interface/rtp_rtcp.h"
|
|
|
|
|
#include "webrtc/modules/rtp_rtcp/interface/rtp_rtcp_defines.h"
|
|
|
|
|
|
|
|
|
|
namespace webrtc {
|
|
|
|
|
const int kVideoNackListSize = 10;
|
|
|
|
|
const int kTestId = 123;
|
2013-04-08 11:08:41 +00:00
|
|
|
const uint32_t kTestSsrc = 3456;
|
|
|
|
|
const uint16_t kTestSequenceNumber = 2345;
|
|
|
|
|
const uint32_t kTestNumberOfPackets = 450;
|
2013-03-15 23:21:52 +00:00
|
|
|
const int kTestNumberOfRtxPackets = 49;
|
|
|
|
|
|
|
|
|
|
class VerifyingRtxReceiver : public RtpData {
|
|
|
|
|
public:
|
|
|
|
|
VerifyingRtxReceiver() {}
|
|
|
|
|
|
2013-04-08 11:08:41 +00:00
|
|
|
virtual int32_t OnReceivedPayloadData(
|
|
|
|
|
const uint8_t* data,
|
|
|
|
|
const uint16_t size,
|
2013-03-15 23:21:52 +00:00
|
|
|
const webrtc::WebRtcRTPHeader* rtp_header) {
|
|
|
|
|
if (!sequence_numbers_.empty()) {
|
|
|
|
|
EXPECT_EQ(kTestSsrc, rtp_header->header.ssrc);
|
|
|
|
|
}
|
|
|
|
|
sequence_numbers_.push_back(rtp_header->header.sequenceNumber);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2013-04-08 11:08:41 +00:00
|
|
|
std::vector<uint16_t > sequence_numbers_;
|
2013-03-15 23:21:52 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
class RtxLoopBackTransport : public webrtc::Transport {
|
|
|
|
|
public:
|
|
|
|
|
explicit RtxLoopBackTransport(uint32_t rtx_ssrc)
|
|
|
|
|
: count_(0),
|
|
|
|
|
packet_loss_(0),
|
|
|
|
|
rtx_ssrc_(rtx_ssrc),
|
|
|
|
|
count_rtx_ssrc_(0),
|
|
|
|
|
module_(NULL) {
|
|
|
|
|
}
|
|
|
|
|
void SetSendModule(RtpRtcp* rtpRtcpModule) {
|
|
|
|
|
module_ = rtpRtcpModule;
|
|
|
|
|
}
|
|
|
|
|
void DropEveryNthPacket(int n) {
|
|
|
|
|
packet_loss_ = n;
|
|
|
|
|
}
|
|
|
|
|
virtual int SendPacket(int channel, const void *data, int len) {
|
|
|
|
|
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_++;
|
|
|
|
|
if (packet_loss_ > 0) {
|
|
|
|
|
if ((count_ % packet_loss_) == 0) {
|
|
|
|
|
return len;
|
|
|
|
|
}
|
|
|
|
|
}
|
2013-04-08 11:08:41 +00:00
|
|
|
if (module_->IncomingPacket((const uint8_t*)data, len) == 0) {
|
2013-03-15 23:21:52 +00:00
|
|
|
return len;
|
|
|
|
|
}
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
virtual int SendRTCPPacket(int channel, const void *data, int len) {
|
2013-04-08 11:08:41 +00:00
|
|
|
if (module_->IncomingPacket((const uint8_t*)data, len) == 0) {
|
2013-03-15 23:21:52 +00:00
|
|
|
return len;
|
|
|
|
|
}
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
int count_;
|
|
|
|
|
int packet_loss_;
|
|
|
|
|
uint32_t rtx_ssrc_;
|
|
|
|
|
int count_rtx_ssrc_;
|
|
|
|
|
RtpRtcp* module_;
|
|
|
|
|
};
|
|
|
|
|
|
2013-03-18 15:00:50 +00:00
|
|
|
class RtpRtcpRtxNackTest : public ::testing::Test {
|
2013-03-15 23:21:52 +00:00
|
|
|
protected:
|
2013-03-18 15:00:50 +00:00
|
|
|
RtpRtcpRtxNackTest()
|
2013-03-15 23:21:52 +00:00
|
|
|
: rtp_rtcp_module_(NULL),
|
|
|
|
|
transport_(kTestSsrc + 1),
|
|
|
|
|
receiver_(),
|
|
|
|
|
payload_data_length(sizeof(payload_data)),
|
|
|
|
|
fake_clock(123456) {}
|
2013-03-18 15:00:50 +00:00
|
|
|
~RtpRtcpRtxNackTest() {}
|
2013-03-15 23:21:52 +00:00
|
|
|
|
|
|
|
|
virtual void SetUp() {
|
|
|
|
|
RtpRtcp::Configuration configuration;
|
|
|
|
|
configuration.id = kTestId;
|
|
|
|
|
configuration.audio = false;
|
|
|
|
|
configuration.clock = &fake_clock;
|
|
|
|
|
configuration.incoming_data = &receiver_;
|
|
|
|
|
configuration.outgoing_transport = &transport_;
|
|
|
|
|
rtp_rtcp_module_ = RtpRtcp::CreateRtpRtcp(configuration);
|
|
|
|
|
|
|
|
|
|
EXPECT_EQ(0, rtp_rtcp_module_->SetSSRC(kTestSsrc));
|
|
|
|
|
EXPECT_EQ(0, rtp_rtcp_module_->SetRTCPStatus(kRtcpCompound));
|
|
|
|
|
EXPECT_EQ(0, rtp_rtcp_module_->SetNACKStatus(kNackRtcp, 450));
|
|
|
|
|
EXPECT_EQ(0, rtp_rtcp_module_->SetStorePacketsStatus(true, 600));
|
|
|
|
|
EXPECT_EQ(0, rtp_rtcp_module_->SetSendingStatus(true));
|
|
|
|
|
EXPECT_EQ(0, rtp_rtcp_module_->SetSequenceNumber(kTestSequenceNumber));
|
|
|
|
|
EXPECT_EQ(0, rtp_rtcp_module_->SetStartTimestamp(111111));
|
|
|
|
|
|
|
|
|
|
transport_.SetSendModule(rtp_rtcp_module_);
|
|
|
|
|
|
|
|
|
|
VideoCodec video_codec;
|
|
|
|
|
memset(&video_codec, 0, sizeof(video_codec));
|
|
|
|
|
video_codec.plType = 123;
|
|
|
|
|
memcpy(video_codec.plName, "I420", 5);
|
|
|
|
|
|
|
|
|
|
EXPECT_EQ(0, rtp_rtcp_module_->RegisterSendPayload(video_codec));
|
|
|
|
|
EXPECT_EQ(0, rtp_rtcp_module_->RegisterReceivePayload(video_codec));
|
|
|
|
|
|
|
|
|
|
for (int n = 0; n < payload_data_length; n++) {
|
|
|
|
|
payload_data[n] = n % 10;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
virtual void TearDown() {
|
|
|
|
|
delete rtp_rtcp_module_;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
RtpRtcp* rtp_rtcp_module_;
|
|
|
|
|
RtxLoopBackTransport transport_;
|
|
|
|
|
VerifyingRtxReceiver receiver_;
|
2013-04-08 11:08:41 +00:00
|
|
|
uint8_t payload_data[65000];
|
2013-03-15 23:21:52 +00:00
|
|
|
int payload_data_length;
|
|
|
|
|
SimulatedClock fake_clock;
|
|
|
|
|
};
|
|
|
|
|
|
2013-03-18 15:00:50 +00:00
|
|
|
TEST_F(RtpRtcpRtxNackTest, RTCP) {
|
2013-04-08 11:08:41 +00:00
|
|
|
uint32_t timestamp = 3000;
|
|
|
|
|
uint16_t nack_list[kVideoNackListSize];
|
2013-03-15 23:21:52 +00:00
|
|
|
transport_.DropEveryNthPacket(10);
|
|
|
|
|
|
|
|
|
|
for (int frame = 0; frame < 10; ++frame) {
|
|
|
|
|
EXPECT_EQ(0, rtp_rtcp_module_->SendOutgoingData(webrtc::kVideoFrameDelta,
|
|
|
|
|
123,
|
|
|
|
|
timestamp,
|
|
|
|
|
timestamp / 90,
|
|
|
|
|
payload_data,
|
|
|
|
|
payload_data_length));
|
|
|
|
|
|
|
|
|
|
std::sort(receiver_.sequence_numbers_.begin(),
|
|
|
|
|
receiver_.sequence_numbers_.end());
|
|
|
|
|
|
2013-04-08 11:08:41 +00:00
|
|
|
std::vector<uint16_t> missing_sequence_numbers;
|
|
|
|
|
std::vector<uint16_t>::iterator it =
|
2013-03-15 23:21:52 +00:00
|
|
|
receiver_.sequence_numbers_.begin();
|
|
|
|
|
|
|
|
|
|
while (it != receiver_.sequence_numbers_.end()) {
|
2013-04-08 11:08:41 +00:00
|
|
|
uint16_t sequence_number_1 = *it;
|
2013-03-15 23:21:52 +00:00
|
|
|
++it;
|
|
|
|
|
if (it != receiver_.sequence_numbers_.end()) {
|
2013-04-08 11:08:41 +00:00
|
|
|
uint16_t sequence_number_2 = *it;
|
2013-03-15 23:21:52 +00:00
|
|
|
// Add all missing sequence numbers to list.
|
2013-04-08 11:08:41 +00:00
|
|
|
for (uint16_t i = sequence_number_1 + 1; i < sequence_number_2;
|
2013-03-15 23:21:52 +00:00
|
|
|
++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);
|
|
|
|
|
}
|
|
|
|
|
rtp_rtcp_module_->SendNACK(nack_list, n);
|
|
|
|
|
fake_clock.AdvanceTimeMilliseconds(33);
|
|
|
|
|
rtp_rtcp_module_->Process();
|
|
|
|
|
|
|
|
|
|
// Prepare next frame.
|
|
|
|
|
timestamp += 3000;
|
|
|
|
|
}
|
|
|
|
|
std::sort(receiver_.sequence_numbers_.begin(),
|
|
|
|
|
receiver_.sequence_numbers_.end());
|
|
|
|
|
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(0, transport_.count_rtx_ssrc_);
|
|
|
|
|
}
|
|
|
|
|
|
2013-03-18 15:00:50 +00:00
|
|
|
TEST_F(RtpRtcpRtxNackTest, RTXNack) {
|
2013-03-15 23:21:52 +00:00
|
|
|
EXPECT_EQ(0, rtp_rtcp_module_->SetRTXReceiveStatus(true, kTestSsrc + 1));
|
2013-04-12 14:55:46 +00:00
|
|
|
rtp_rtcp_module_->SetRtxReceivePayloadType(119);
|
|
|
|
|
EXPECT_EQ(0, rtp_rtcp_module_->SetRTXSendStatus(kRtxRetransmitted, true,
|
|
|
|
|
kTestSsrc + 1));
|
|
|
|
|
rtp_rtcp_module_->SetRtxSendPayloadType(119);
|
2013-03-15 23:21:52 +00:00
|
|
|
|
|
|
|
|
transport_.DropEveryNthPacket(10);
|
|
|
|
|
|
2013-04-08 11:08:41 +00:00
|
|
|
uint32_t timestamp = 3000;
|
|
|
|
|
uint16_t nack_list[kVideoNackListSize];
|
2013-03-15 23:21:52 +00:00
|
|
|
|
|
|
|
|
for (int frame = 0; frame < 10; ++frame) {
|
|
|
|
|
EXPECT_EQ(0, rtp_rtcp_module_->SendOutgoingData(webrtc::kVideoFrameDelta,
|
|
|
|
|
123,
|
|
|
|
|
timestamp,
|
|
|
|
|
timestamp / 90,
|
|
|
|
|
payload_data,
|
|
|
|
|
payload_data_length));
|
|
|
|
|
|
|
|
|
|
std::sort(receiver_.sequence_numbers_.begin(),
|
|
|
|
|
receiver_.sequence_numbers_.end());
|
|
|
|
|
|
2013-04-08 11:08:41 +00:00
|
|
|
std::vector<uint16_t> missing_sequence_numbers;
|
2013-03-15 23:21:52 +00:00
|
|
|
|
|
|
|
|
|
2013-04-08 11:08:41 +00:00
|
|
|
std::vector<uint16_t>::iterator it =
|
2013-03-15 23:21:52 +00:00
|
|
|
receiver_.sequence_numbers_.begin();
|
|
|
|
|
while (it != receiver_.sequence_numbers_.end()) {
|
|
|
|
|
int sequence_number_1 = *it;
|
|
|
|
|
++it;
|
|
|
|
|
if (it != receiver_.sequence_numbers_.end()) {
|
|
|
|
|
int sequence_number_2 = *it;
|
|
|
|
|
// Add all missing sequence numbers to list.
|
|
|
|
|
for (int 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);
|
|
|
|
|
}
|
|
|
|
|
rtp_rtcp_module_->SendNACK(nack_list, n);
|
|
|
|
|
fake_clock.AdvanceTimeMilliseconds(33);
|
|
|
|
|
rtp_rtcp_module_->Process();
|
|
|
|
|
|
|
|
|
|
// Prepare next frame.
|
|
|
|
|
timestamp += 3000;
|
|
|
|
|
}
|
|
|
|
|
std::sort(receiver_.sequence_numbers_.begin(),
|
|
|
|
|
receiver_.sequence_numbers_.end());
|
|
|
|
|
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_);
|
|
|
|
|
}
|
|
|
|
|
|
2013-03-18 15:00:50 +00:00
|
|
|
TEST_F(RtpRtcpRtxNackTest, RTXAllNoLoss) {
|
2013-03-15 23:21:52 +00:00
|
|
|
EXPECT_EQ(0, rtp_rtcp_module_->SetRTXReceiveStatus(true, kTestSsrc + 1));
|
2013-04-12 14:55:46 +00:00
|
|
|
EXPECT_EQ(0, rtp_rtcp_module_->SetRTXSendStatus(kRtxAll, true,
|
|
|
|
|
kTestSsrc + 1));
|
2013-03-15 23:21:52 +00:00
|
|
|
transport_.DropEveryNthPacket(0);
|
|
|
|
|
|
2013-04-08 11:08:41 +00:00
|
|
|
uint32_t timestamp = 3000;
|
2013-03-15 23:21:52 +00:00
|
|
|
|
|
|
|
|
for (int frame = 0; frame < 10; ++frame) {
|
|
|
|
|
EXPECT_EQ(0, rtp_rtcp_module_->SendOutgoingData(webrtc::kVideoFrameDelta,
|
|
|
|
|
123,
|
|
|
|
|
timestamp,
|
|
|
|
|
timestamp / 90,
|
|
|
|
|
payload_data,
|
|
|
|
|
payload_data_length));
|
|
|
|
|
|
|
|
|
|
fake_clock.AdvanceTimeMilliseconds(33);
|
|
|
|
|
rtp_rtcp_module_->Process();
|
|
|
|
|
|
|
|
|
|
// Prepare next frame.
|
|
|
|
|
timestamp += 3000;
|
|
|
|
|
}
|
|
|
|
|
std::sort(receiver_.sequence_numbers_.begin(),
|
|
|
|
|
receiver_.sequence_numbers_.end());
|
|
|
|
|
EXPECT_EQ(kTestSequenceNumber, *(receiver_.sequence_numbers_.begin()));
|
|
|
|
|
EXPECT_EQ(kTestSequenceNumber + kTestNumberOfPackets - 1,
|
|
|
|
|
*(receiver_.sequence_numbers_.rbegin()));
|
|
|
|
|
// We have transmitted all packets twice, and loss was set to 0.
|
|
|
|
|
EXPECT_EQ(kTestNumberOfPackets * 2u, receiver_.sequence_numbers_.size());
|
|
|
|
|
// Half of the packets should be via RTX.
|
|
|
|
|
EXPECT_EQ(static_cast<int>(kTestNumberOfPackets),
|
|
|
|
|
transport_.count_rtx_ssrc_);
|
|
|
|
|
}
|
|
|
|
|
|
2013-03-18 15:00:50 +00:00
|
|
|
TEST_F(RtpRtcpRtxNackTest, RTXAllWithLoss) {
|
2013-03-15 23:21:52 +00:00
|
|
|
EXPECT_EQ(0, rtp_rtcp_module_->SetRTXReceiveStatus(true, kTestSsrc + 1));
|
2013-04-12 14:55:46 +00:00
|
|
|
EXPECT_EQ(0, rtp_rtcp_module_->SetRTXSendStatus(kRtxAll, true,
|
2013-03-15 23:21:52 +00:00
|
|
|
kTestSsrc + 1));
|
|
|
|
|
|
|
|
|
|
int loss = 10;
|
|
|
|
|
transport_.DropEveryNthPacket(loss);
|
|
|
|
|
|
2013-04-08 11:08:41 +00:00
|
|
|
uint32_t timestamp = 3000;
|
|
|
|
|
uint16_t nack_list[kVideoNackListSize];
|
2013-03-15 23:21:52 +00:00
|
|
|
|
|
|
|
|
for (int frame = 0; frame < 10; ++frame) {
|
|
|
|
|
EXPECT_EQ(0, rtp_rtcp_module_->SendOutgoingData(webrtc::kVideoFrameDelta,
|
|
|
|
|
123,
|
|
|
|
|
timestamp,
|
|
|
|
|
timestamp / 90,
|
|
|
|
|
payload_data,
|
|
|
|
|
payload_data_length));
|
|
|
|
|
std::sort(receiver_.sequence_numbers_.begin(),
|
|
|
|
|
receiver_.sequence_numbers_.end());
|
2013-04-08 11:08:41 +00:00
|
|
|
std::vector<uint16_t> missing_sequence_numbers;
|
2013-03-15 23:21:52 +00:00
|
|
|
|
2013-04-08 11:08:41 +00:00
|
|
|
std::vector<uint16_t>::iterator it =
|
2013-03-15 23:21:52 +00:00
|
|
|
receiver_.sequence_numbers_.begin();
|
|
|
|
|
while (it != receiver_.sequence_numbers_.end()) {
|
|
|
|
|
int sequence_number_1 = *it;
|
|
|
|
|
++it;
|
|
|
|
|
if (it != receiver_.sequence_numbers_.end()) {
|
|
|
|
|
int sequence_number_2 = *it;
|
|
|
|
|
// Add all missing sequence numbers to list.
|
|
|
|
|
for (int 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);
|
|
|
|
|
}
|
|
|
|
|
if (n > 0)
|
|
|
|
|
rtp_rtcp_module_->SendNACK(nack_list, n);
|
|
|
|
|
fake_clock.AdvanceTimeMilliseconds(33);
|
|
|
|
|
rtp_rtcp_module_->Process();
|
|
|
|
|
|
|
|
|
|
// Prepare next frame.
|
|
|
|
|
timestamp += 3000;
|
|
|
|
|
}
|
|
|
|
|
std::sort(receiver_.sequence_numbers_.begin(),
|
|
|
|
|
receiver_.sequence_numbers_.end());
|
|
|
|
|
EXPECT_EQ(kTestSequenceNumber, *(receiver_.sequence_numbers_.begin()));
|
|
|
|
|
EXPECT_EQ(kTestSequenceNumber + kTestNumberOfPackets - 1,
|
|
|
|
|
*(receiver_.sequence_numbers_.rbegin()));
|
|
|
|
|
// Got everything but 10% loss.
|
|
|
|
|
EXPECT_EQ(2u * (kTestNumberOfPackets - kTestNumberOfPackets / 10),
|
|
|
|
|
receiver_.sequence_numbers_.size());
|
|
|
|
|
EXPECT_EQ(static_cast<int>(kTestNumberOfPackets),
|
|
|
|
|
transport_.count_rtx_ssrc_);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace webrtc
|