Adding RTX on source
Review URL: https://webrtc-codereview.appspot.com/1190004 git-svn-id: http://webrtc.googlecode.com/svn/trunk@3674 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
parent
73222cff1a
commit
bda7f305c5
@ -416,14 +416,14 @@ class RtpRtcp : public Module {
|
|||||||
/*
|
/*
|
||||||
* Turn on/off sending RTX (RFC 4588) on a specific SSRC.
|
* Turn on/off sending RTX (RFC 4588) on a specific SSRC.
|
||||||
*/
|
*/
|
||||||
virtual WebRtc_Word32 SetRTXSendStatus(const bool enable,
|
virtual WebRtc_Word32 SetRTXSendStatus(const RtxMode mode,
|
||||||
const bool setSSRC,
|
const bool setSSRC,
|
||||||
const WebRtc_UWord32 SSRC) = 0;
|
const WebRtc_UWord32 SSRC) = 0;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Get status of sending RTX (RFC 4588) on a specific SSRC.
|
* Get status of sending RTX (RFC 4588) on a specific SSRC.
|
||||||
*/
|
*/
|
||||||
virtual WebRtc_Word32 RTXSendStatus(bool* enable,
|
virtual WebRtc_Word32 RTXSendStatus(RtxMode* mode,
|
||||||
WebRtc_UWord32* SSRC) const = 0;
|
WebRtc_UWord32* SSRC) const = 0;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -107,6 +107,12 @@ enum RetransmissionMode {
|
|||||||
kRetransmitAllPackets = 0xFF
|
kRetransmitAllPackets = 0xFF
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum RtxMode {
|
||||||
|
kRtxOff = 0,
|
||||||
|
kRtxRetransmitted = 1, // Apply RTX only to retransmitted packets.
|
||||||
|
kRtxAll = 2 // Apply RTX to all packets (source + retransmissions).
|
||||||
|
};
|
||||||
|
|
||||||
struct RTCPSenderInfo
|
struct RTCPSenderInfo
|
||||||
{
|
{
|
||||||
WebRtc_UWord32 NTPseconds;
|
WebRtc_UWord32 NTPseconds;
|
||||||
|
@ -128,9 +128,10 @@ class MockRtpRtcp : public RtpRtcp {
|
|||||||
MOCK_METHOD1(SetCSRCStatus,
|
MOCK_METHOD1(SetCSRCStatus,
|
||||||
WebRtc_Word32(const bool include));
|
WebRtc_Word32(const bool include));
|
||||||
MOCK_METHOD3(SetRTXSendStatus,
|
MOCK_METHOD3(SetRTXSendStatus,
|
||||||
WebRtc_Word32(const bool enable, const bool setSSRC, const WebRtc_UWord32 SSRC));
|
WebRtc_Word32(const RtxMode mode, const bool setSSRC,
|
||||||
|
const WebRtc_UWord32 SSRC));
|
||||||
MOCK_CONST_METHOD2(RTXSendStatus,
|
MOCK_CONST_METHOD2(RTXSendStatus,
|
||||||
WebRtc_Word32(bool* enable, WebRtc_UWord32* SSRC));
|
WebRtc_Word32(RtxMode* mode, WebRtc_UWord32* SSRC));
|
||||||
MOCK_METHOD1(SetSendingStatus,
|
MOCK_METHOD1(SetSendingStatus,
|
||||||
WebRtc_Word32(const bool sending));
|
WebRtc_Word32(const bool sending));
|
||||||
MOCK_CONST_METHOD0(Sending,
|
MOCK_CONST_METHOD0(Sending,
|
||||||
|
347
webrtc/modules/rtp_rtcp/source/nack_rtx_unittest.cc
Normal file
347
webrtc/modules/rtp_rtcp/source/nack_rtx_unittest.cc
Normal file
@ -0,0 +1,347 @@
|
|||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
const WebRtc_UWord32 kTestSsrc = 3456;
|
||||||
|
const WebRtc_UWord16 kTestSequenceNumber = 2345;
|
||||||
|
const WebRtc_UWord32 kTestNumberOfPackets = 450;
|
||||||
|
const int kTestNumberOfRtxPackets = 49;
|
||||||
|
|
||||||
|
class VerifyingRtxReceiver : public RtpData {
|
||||||
|
public:
|
||||||
|
VerifyingRtxReceiver() {}
|
||||||
|
|
||||||
|
virtual WebRtc_Word32 OnReceivedPayloadData(
|
||||||
|
const WebRtc_UWord8* data,
|
||||||
|
const WebRtc_UWord16 size,
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
std::vector<WebRtc_UWord16 > sequence_numbers_;
|
||||||
|
};
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (module_->IncomingPacket((const WebRtc_UWord8*)data, len) == 0) {
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
virtual int SendRTCPPacket(int channel, const void *data, int len) {
|
||||||
|
if (module_->IncomingPacket((const WebRtc_UWord8*)data, len) == 0) {
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
int count_;
|
||||||
|
int packet_loss_;
|
||||||
|
uint32_t rtx_ssrc_;
|
||||||
|
int count_rtx_ssrc_;
|
||||||
|
RtpRtcp* module_;
|
||||||
|
};
|
||||||
|
|
||||||
|
class RtpRtcpNackTest : public ::testing::Test {
|
||||||
|
protected:
|
||||||
|
RtpRtcpNackTest()
|
||||||
|
: rtp_rtcp_module_(NULL),
|
||||||
|
transport_(kTestSsrc + 1),
|
||||||
|
receiver_(),
|
||||||
|
payload_data_length(sizeof(payload_data)),
|
||||||
|
fake_clock(123456) {}
|
||||||
|
~RtpRtcpNackTest() {}
|
||||||
|
|
||||||
|
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_;
|
||||||
|
WebRtc_UWord8 payload_data[65000];
|
||||||
|
int payload_data_length;
|
||||||
|
SimulatedClock fake_clock;
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST_F(RtpRtcpNackTest, RTCP) {
|
||||||
|
WebRtc_UWord32 timestamp = 3000;
|
||||||
|
WebRtc_UWord16 nack_list[kVideoNackListSize];
|
||||||
|
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());
|
||||||
|
|
||||||
|
std::vector<WebRtc_UWord16> missing_sequence_numbers;
|
||||||
|
std::vector<WebRtc_UWord16>::iterator it =
|
||||||
|
receiver_.sequence_numbers_.begin();
|
||||||
|
|
||||||
|
while (it != receiver_.sequence_numbers_.end()) {
|
||||||
|
WebRtc_UWord16 sequence_number_1 = *it;
|
||||||
|
++it;
|
||||||
|
if (it != receiver_.sequence_numbers_.end()) {
|
||||||
|
WebRtc_UWord16 sequence_number_2 = *it;
|
||||||
|
// Add all missing sequence numbers to list.
|
||||||
|
for (WebRtc_UWord16 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(0, transport_.count_rtx_ssrc_);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(RtpRtcpNackTest, RTXNack) {
|
||||||
|
EXPECT_EQ(0, rtp_rtcp_module_->SetRTXReceiveStatus(true, kTestSsrc + 1));
|
||||||
|
EXPECT_EQ(0, rtp_rtcp_module_->SetRTXSendStatus(kRtxRetransmitted,
|
||||||
|
true, kTestSsrc + 1));
|
||||||
|
|
||||||
|
transport_.DropEveryNthPacket(10);
|
||||||
|
|
||||||
|
WebRtc_UWord32 timestamp = 3000;
|
||||||
|
WebRtc_UWord16 nack_list[kVideoNackListSize];
|
||||||
|
|
||||||
|
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());
|
||||||
|
|
||||||
|
std::vector<WebRtc_UWord16> missing_sequence_numbers;
|
||||||
|
|
||||||
|
|
||||||
|
std::vector<WebRtc_UWord16>::iterator it =
|
||||||
|
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_);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(RtpRtcpNackTest, RTXAllNoLoss) {
|
||||||
|
EXPECT_EQ(0, rtp_rtcp_module_->SetRTXReceiveStatus(true, kTestSsrc + 1));
|
||||||
|
EXPECT_EQ(0, rtp_rtcp_module_->SetRTXSendStatus(kRtxAll,
|
||||||
|
true, kTestSsrc + 1));
|
||||||
|
transport_.DropEveryNthPacket(0);
|
||||||
|
|
||||||
|
WebRtc_UWord32 timestamp = 3000;
|
||||||
|
|
||||||
|
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_);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(RtpRtcpNackTest, RTXAllWithLoss) {
|
||||||
|
EXPECT_EQ(0, rtp_rtcp_module_->SetRTXReceiveStatus(true, kTestSsrc + 1));
|
||||||
|
EXPECT_EQ(0, rtp_rtcp_module_->SetRTXSendStatus(kRtxAll,
|
||||||
|
true,
|
||||||
|
kTestSsrc + 1));
|
||||||
|
|
||||||
|
int loss = 10;
|
||||||
|
transport_.DropEveryNthPacket(loss);
|
||||||
|
|
||||||
|
WebRtc_UWord32 timestamp = 3000;
|
||||||
|
WebRtc_UWord16 nack_list[kVideoNackListSize];
|
||||||
|
|
||||||
|
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());
|
||||||
|
std::vector<WebRtc_UWord16> missing_sequence_numbers;
|
||||||
|
|
||||||
|
std::vector<WebRtc_UWord16>::iterator it =
|
||||||
|
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
|
@ -519,16 +519,16 @@ WebRtc_Word32 ModuleRtpRtcpImpl::RemoteCSRCs(
|
|||||||
}
|
}
|
||||||
|
|
||||||
WebRtc_Word32 ModuleRtpRtcpImpl::SetRTXSendStatus(
|
WebRtc_Word32 ModuleRtpRtcpImpl::SetRTXSendStatus(
|
||||||
const bool enable,
|
const RtxMode mode,
|
||||||
const bool set_ssrc,
|
const bool set_ssrc,
|
||||||
const WebRtc_UWord32 ssrc) {
|
const WebRtc_UWord32 ssrc) {
|
||||||
rtp_sender_.SetRTXStatus(enable, set_ssrc, ssrc);
|
rtp_sender_.SetRTXStatus(mode, set_ssrc, ssrc);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
WebRtc_Word32 ModuleRtpRtcpImpl::RTXSendStatus(bool* enable,
|
WebRtc_Word32 ModuleRtpRtcpImpl::RTXSendStatus(RtxMode* mode,
|
||||||
WebRtc_UWord32* ssrc) const {
|
WebRtc_UWord32* ssrc) const {
|
||||||
rtp_sender_.RTXStatus(enable, ssrc);
|
rtp_sender_.RTXStatus(mode, ssrc);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -157,11 +157,11 @@ class ModuleRtpRtcpImpl : public RtpRtcp {
|
|||||||
|
|
||||||
virtual WebRtc_UWord32 ByteCountSent() const;
|
virtual WebRtc_UWord32 ByteCountSent() const;
|
||||||
|
|
||||||
virtual WebRtc_Word32 SetRTXSendStatus(const bool enable,
|
virtual WebRtc_Word32 SetRTXSendStatus(const RtxMode mode,
|
||||||
const bool set_ssrc,
|
const bool set_ssrc,
|
||||||
const WebRtc_UWord32 ssrc);
|
const WebRtc_UWord32 ssrc);
|
||||||
|
|
||||||
virtual WebRtc_Word32 RTXSendStatus(bool* enable,
|
virtual WebRtc_Word32 RTXSendStatus(RtxMode* mode,
|
||||||
WebRtc_UWord32* ssrc) const;
|
WebRtc_UWord32* ssrc) const;
|
||||||
|
|
||||||
// Sends kRtcpByeCode when going from true to false.
|
// Sends kRtcpByeCode when going from true to false.
|
||||||
|
@ -25,13 +25,13 @@
|
|||||||
'../test/testAPI/test_api.cc',
|
'../test/testAPI/test_api.cc',
|
||||||
'../test/testAPI/test_api.h',
|
'../test/testAPI/test_api.h',
|
||||||
'../test/testAPI/test_api_audio.cc',
|
'../test/testAPI/test_api_audio.cc',
|
||||||
'../test/testAPI/test_api_nack.cc',
|
|
||||||
'../test/testAPI/test_api_rtcp.cc',
|
'../test/testAPI/test_api_rtcp.cc',
|
||||||
'../test/testAPI/test_api_video.cc',
|
'../test/testAPI/test_api_video.cc',
|
||||||
'mock/mock_rtp_payload_strategy.h',
|
'mock/mock_rtp_payload_strategy.h',
|
||||||
'mock/mock_rtp_receiver_video.h',
|
'mock/mock_rtp_receiver_video.h',
|
||||||
'fec_test_helper.cc',
|
'fec_test_helper.cc',
|
||||||
'fec_test_helper.h',
|
'fec_test_helper.h',
|
||||||
|
'nack_rtx_unittest.cc',
|
||||||
'producer_fec_unittest.cc',
|
'producer_fec_unittest.cc',
|
||||||
'receiver_fec_unittest.cc',
|
'receiver_fec_unittest.cc',
|
||||||
'rtcp_format_remb_unittest.cc',
|
'rtcp_format_remb_unittest.cc',
|
||||||
|
@ -38,15 +38,19 @@ RTPSender::RTPSender(const WebRtc_Word32 id, const bool audio, Clock *clock,
|
|||||||
// Statistics
|
// Statistics
|
||||||
packets_sent_(0), payload_bytes_sent_(0), start_time_stamp_forced_(false),
|
packets_sent_(0), payload_bytes_sent_(0), start_time_stamp_forced_(false),
|
||||||
start_time_stamp_(0), ssrc_db_(*SSRCDatabase::GetSSRCDatabase()),
|
start_time_stamp_(0), ssrc_db_(*SSRCDatabase::GetSSRCDatabase()),
|
||||||
remote_ssrc_(0), sequence_number_forced_(false), sequence_number_(0),
|
remote_ssrc_(0), sequence_number_forced_(false), ssrc_forced_(false),
|
||||||
sequence_number_rtx_(0), ssrc_forced_(false), ssrc_(0), time_stamp_(0),
|
time_stamp_(0), csrcs_(0), csrc_(), include_csrcs_(true),
|
||||||
csrcs_(0), csrc_(), include_csrcs_(true), rtx_(false), ssrc_rtx_(0) {
|
rtx_(kRtxOff) {
|
||||||
memset(nack_byte_count_times_, 0, sizeof(nack_byte_count_times_));
|
memset(nack_byte_count_times_, 0, sizeof(nack_byte_count_times_));
|
||||||
memset(nack_byte_count_, 0, sizeof(nack_byte_count_));
|
memset(nack_byte_count_, 0, sizeof(nack_byte_count_));
|
||||||
memset(csrc_, 0, sizeof(csrc_));
|
memset(csrc_, 0, sizeof(csrc_));
|
||||||
// We need to seed the random generator.
|
// We need to seed the random generator.
|
||||||
srand(static_cast<WebRtc_UWord32>(clock_->TimeInMilliseconds()));
|
srand(static_cast<WebRtc_UWord32>(clock_->TimeInMilliseconds()));
|
||||||
ssrc_ = ssrc_db_.CreateSSRC(); // Can't be 0.
|
ssrc_ = ssrc_db_.CreateSSRC(); // Can't be 0.
|
||||||
|
ssrc_rtx_ = ssrc_db_.CreateSSRC(); // Can't be 0.
|
||||||
|
// Random start, 16 bits. Can't be 0.
|
||||||
|
sequence_number_rtx_ = static_cast<uint16_t>(rand() + 1) & 0x7FFF;
|
||||||
|
sequence_number_ = static_cast<uint16_t>(rand() + 1) & 0x7FFF;
|
||||||
|
|
||||||
if (audio) {
|
if (audio) {
|
||||||
audio_ = new RTPSenderAudio(id, clock_, this);
|
audio_ = new RTPSenderAudio(id, clock_, this);
|
||||||
@ -233,11 +237,11 @@ WebRtc_UWord16 RTPSender::MaxPayloadLength() const {
|
|||||||
|
|
||||||
WebRtc_UWord16 RTPSender::PacketOverHead() const { return packet_over_head_; }
|
WebRtc_UWord16 RTPSender::PacketOverHead() const { return packet_over_head_; }
|
||||||
|
|
||||||
void RTPSender::SetRTXStatus(const bool enable, const bool set_ssrc,
|
void RTPSender::SetRTXStatus(const RtxMode mode, const bool set_ssrc,
|
||||||
const WebRtc_UWord32 ssrc) {
|
const WebRtc_UWord32 ssrc) {
|
||||||
CriticalSectionScoped cs(send_critsect_);
|
CriticalSectionScoped cs(send_critsect_);
|
||||||
rtx_ = enable;
|
rtx_ = mode;
|
||||||
if (enable) {
|
if (rtx_ != kRtxOff) {
|
||||||
if (set_ssrc) {
|
if (set_ssrc) {
|
||||||
ssrc_rtx_ = ssrc;
|
ssrc_rtx_ = ssrc;
|
||||||
} else {
|
} else {
|
||||||
@ -246,9 +250,9 @@ void RTPSender::SetRTXStatus(const bool enable, const bool set_ssrc,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void RTPSender::RTXStatus(bool *enable, WebRtc_UWord32 *SSRC) const {
|
void RTPSender::RTXStatus(RtxMode* mode, WebRtc_UWord32 *SSRC) const {
|
||||||
CriticalSectionScoped cs(send_critsect_);
|
CriticalSectionScoped cs(send_critsect_);
|
||||||
*enable = rtx_;
|
*mode = rtx_;
|
||||||
*SSRC = ssrc_rtx_;
|
*SSRC = ssrc_rtx_;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -439,39 +443,11 @@ WebRtc_Word32 RTPSender::ReSendPacket(WebRtc_UWord16 packet_id,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
WebRtc_UWord8 data_buffer_rtx[IP_PACKET_SIZE];
|
WebRtc_UWord8 data_buffer_rtx[IP_PACKET_SIZE];
|
||||||
if (rtx_) {
|
if (rtx_ != kRtxOff) {
|
||||||
|
BuildRtxPacket(data_buffer, &length, data_buffer_rtx);
|
||||||
buffer_to_send_ptr = data_buffer_rtx;
|
buffer_to_send_ptr = data_buffer_rtx;
|
||||||
|
|
||||||
CriticalSectionScoped cs(send_critsect_);
|
|
||||||
// Add RTX header.
|
|
||||||
ModuleRTPUtility::RTPHeaderParser rtp_parser(
|
|
||||||
reinterpret_cast<const WebRtc_UWord8 *>(data_buffer), length);
|
|
||||||
|
|
||||||
WebRtcRTPHeader rtp_header;
|
|
||||||
rtp_parser.Parse(rtp_header);
|
|
||||||
|
|
||||||
// Add original RTP header.
|
|
||||||
memcpy(data_buffer_rtx, data_buffer, rtp_header.header.headerLength);
|
|
||||||
|
|
||||||
// Replace sequence number.
|
|
||||||
WebRtc_UWord8 *ptr = data_buffer_rtx + 2;
|
|
||||||
ModuleRTPUtility::AssignUWord16ToBuffer(ptr, sequence_number_rtx_++);
|
|
||||||
|
|
||||||
// Replace SSRC.
|
|
||||||
ptr += 6;
|
|
||||||
ModuleRTPUtility::AssignUWord32ToBuffer(ptr, ssrc_rtx_);
|
|
||||||
|
|
||||||
// Add OSN (original sequence number).
|
|
||||||
ptr = data_buffer_rtx + rtp_header.header.headerLength;
|
|
||||||
ModuleRTPUtility::AssignUWord16ToBuffer(ptr,
|
|
||||||
rtp_header.header.sequenceNumber);
|
|
||||||
ptr += 2;
|
|
||||||
|
|
||||||
// Add original payload data.
|
|
||||||
memcpy(ptr, data_buffer + rtp_header.header.headerLength,
|
|
||||||
length - rtp_header.header.headerLength);
|
|
||||||
length += 2;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
WebRtc_Word32 bytes_sent = ReSendToNetwork(buffer_to_send_ptr, length);
|
WebRtc_Word32 bytes_sent = ReSendToNetwork(buffer_to_send_ptr, length);
|
||||||
if (bytes_sent <= 0) {
|
if (bytes_sent <= 0) {
|
||||||
WEBRTC_TRACE(kTraceWarning, kTraceRtpRtcp, id_,
|
WEBRTC_TRACE(kTraceWarning, kTraceRtpRtcp, id_,
|
||||||
@ -682,6 +658,21 @@ WebRtc_Word32 RTPSender::SendToNetwork(
|
|||||||
storage) != 0) {
|
storage) != 0) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
WebRtc_Word32 bytes_sent = -1;
|
||||||
|
// Create and send RTX Packet.
|
||||||
|
if (rtx_ == kRtxAll && storage == kAllowRetransmission) {
|
||||||
|
WebRtc_UWord16 length_rtx = payload_length + rtp_header_length;
|
||||||
|
WebRtc_UWord8 data_buffer_rtx[IP_PACKET_SIZE];
|
||||||
|
BuildRtxPacket(buffer, &length_rtx, data_buffer_rtx);
|
||||||
|
if (transport_) {
|
||||||
|
bytes_sent += transport_->SendPacket(id_, data_buffer_rtx, length_rtx);
|
||||||
|
if (bytes_sent <= 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (paced_sender_) {
|
if (paced_sender_) {
|
||||||
if (!paced_sender_->SendPacket(
|
if (!paced_sender_->SendPacket(
|
||||||
PacedSender::kNormalPriority, rtp_header.header.ssrc,
|
PacedSender::kNormalPriority, rtp_header.header.ssrc,
|
||||||
@ -692,8 +683,8 @@ WebRtc_Word32 RTPSender::SendToNetwork(
|
|||||||
return payload_length + rtp_header_length;
|
return payload_length + rtp_header_length;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Send packet.
|
// Send data packet.
|
||||||
WebRtc_Word32 bytes_sent = -1;
|
bytes_sent = -1;
|
||||||
if (transport_) {
|
if (transport_) {
|
||||||
bytes_sent = transport_->SendPacket(id_, buffer,
|
bytes_sent = transport_->SendPacket(id_, buffer,
|
||||||
payload_length + rtp_header_length);
|
payload_length + rtp_header_length);
|
||||||
@ -1191,4 +1182,38 @@ WebRtc_Word32 RTPSender::SetFecParameters(
|
|||||||
return video_->SetFecParameters(delta_params, key_params);
|
return video_->SetFecParameters(delta_params, key_params);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RTPSender::BuildRtxPacket(WebRtc_UWord8* buffer, WebRtc_UWord16* length,
|
||||||
|
WebRtc_UWord8* buffer_rtx) {
|
||||||
|
CriticalSectionScoped cs(send_critsect_);
|
||||||
|
WebRtc_UWord8* data_buffer_rtx = buffer_rtx;
|
||||||
|
// Add RTX header.
|
||||||
|
ModuleRTPUtility::RTPHeaderParser rtp_parser(
|
||||||
|
reinterpret_cast<const WebRtc_UWord8 *>(buffer), *length);
|
||||||
|
|
||||||
|
WebRtcRTPHeader rtp_header;
|
||||||
|
rtp_parser.Parse(rtp_header);
|
||||||
|
|
||||||
|
// Add original RTP header.
|
||||||
|
memcpy(data_buffer_rtx, buffer, rtp_header.header.headerLength);
|
||||||
|
|
||||||
|
// Replace sequence number.
|
||||||
|
WebRtc_UWord8 *ptr = data_buffer_rtx + 2;
|
||||||
|
ModuleRTPUtility::AssignUWord16ToBuffer(ptr, sequence_number_rtx_++);
|
||||||
|
|
||||||
|
// Replace SSRC.
|
||||||
|
ptr += 6;
|
||||||
|
ModuleRTPUtility::AssignUWord32ToBuffer(ptr, ssrc_rtx_);
|
||||||
|
|
||||||
|
// Add OSN (original sequence number).
|
||||||
|
ptr = data_buffer_rtx + rtp_header.header.headerLength;
|
||||||
|
ModuleRTPUtility::AssignUWord16ToBuffer(ptr,
|
||||||
|
rtp_header.header.sequenceNumber);
|
||||||
|
ptr += 2;
|
||||||
|
|
||||||
|
// Add original payload data.
|
||||||
|
memcpy(ptr, buffer + rtp_header.header.headerLength,
|
||||||
|
*length - rtp_header.header.headerLength);
|
||||||
|
*length += 2;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace webrtc
|
} // namespace webrtc
|
||||||
|
@ -176,10 +176,10 @@ class RTPSender : public Bitrate, public RTPSenderInterface {
|
|||||||
bool ProcessNACKBitRate(const WebRtc_UWord32 now);
|
bool ProcessNACKBitRate(const WebRtc_UWord32 now);
|
||||||
|
|
||||||
// RTX.
|
// RTX.
|
||||||
void SetRTXStatus(const bool enable, const bool set_ssrc,
|
void SetRTXStatus(const RtxMode mode, const bool set_ssrc,
|
||||||
const WebRtc_UWord32 SSRC);
|
const WebRtc_UWord32 SSRC);
|
||||||
|
|
||||||
void RTXStatus(bool *enable, WebRtc_UWord32 *SSRC) const;
|
void RTXStatus(RtxMode* mode, WebRtc_UWord32 *SSRC) const;
|
||||||
|
|
||||||
// Functions wrapping RTPSenderInterface.
|
// Functions wrapping RTPSenderInterface.
|
||||||
virtual WebRtc_Word32 BuildRTPheader(
|
virtual WebRtc_Word32 BuildRTPheader(
|
||||||
@ -263,6 +263,9 @@ class RTPSender : public Bitrate, public RTPSenderInterface {
|
|||||||
WebRtc_UWord32 capture_timestamp,
|
WebRtc_UWord32 capture_timestamp,
|
||||||
int64_t capture_time_ms);
|
int64_t capture_time_ms);
|
||||||
|
|
||||||
|
void BuildRtxPacket(WebRtc_UWord8* buffer, WebRtc_UWord16* length,
|
||||||
|
WebRtc_UWord8* buffer_rtx);
|
||||||
|
|
||||||
WebRtc_Word32 id_;
|
WebRtc_Word32 id_;
|
||||||
const bool audio_configured_;
|
const bool audio_configured_;
|
||||||
RTPSenderAudio *audio_;
|
RTPSenderAudio *audio_;
|
||||||
@ -309,7 +312,7 @@ class RTPSender : public Bitrate, public RTPSenderInterface {
|
|||||||
WebRtc_UWord8 csrcs_;
|
WebRtc_UWord8 csrcs_;
|
||||||
WebRtc_UWord32 csrc_[kRtpCsrcSize];
|
WebRtc_UWord32 csrc_[kRtpCsrcSize];
|
||||||
bool include_csrcs_;
|
bool include_csrcs_;
|
||||||
bool rtx_;
|
RtxMode rtx_;
|
||||||
WebRtc_UWord32 ssrc_rtx_;
|
WebRtc_UWord32 ssrc_rtx_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -8,15 +8,10 @@
|
|||||||
* be found in the AUTHORS file in the root of the source tree.
|
* be found in the AUTHORS file in the root of the source tree.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "webrtc/modules/rtp_rtcp/test/testAPI/test_api.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <gtest/gtest.h>
|
|
||||||
|
|
||||||
#include "test_api.h"
|
|
||||||
|
|
||||||
#include "common_types.h"
|
|
||||||
#include "rtp_rtcp.h"
|
|
||||||
#include "rtp_rtcp_defines.h"
|
|
||||||
|
|
||||||
using namespace webrtc;
|
using namespace webrtc;
|
||||||
|
|
||||||
@ -112,3 +107,28 @@ TEST_F(RtpRtcpAPITest, RTCP) {
|
|||||||
EXPECT_EQ(0, module->SetNACKStatus(kNackRtcp, 450));
|
EXPECT_EQ(0, module->SetNACKStatus(kNackRtcp, 450));
|
||||||
EXPECT_EQ(kNackRtcp, module->NACK());
|
EXPECT_EQ(kNackRtcp, module->NACK());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(RtpRtcpAPITest, RTXSender) {
|
||||||
|
unsigned int ssrc = 0;
|
||||||
|
RtxMode rtx_mode = kRtxOff;
|
||||||
|
EXPECT_EQ(0, module->SetRTXSendStatus(kRtxRetransmitted, true, 1));
|
||||||
|
EXPECT_EQ(0, module->RTXSendStatus(&rtx_mode, &ssrc));
|
||||||
|
EXPECT_EQ(kRtxRetransmitted, rtx_mode);
|
||||||
|
EXPECT_EQ(1u, ssrc);
|
||||||
|
rtx_mode = kRtxOff;
|
||||||
|
EXPECT_EQ(0, module->SetRTXSendStatus(kRtxOff, true, 0));
|
||||||
|
EXPECT_EQ(0, module->RTXSendStatus(&rtx_mode, &ssrc));
|
||||||
|
EXPECT_EQ(kRtxOff, rtx_mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(RtpRtcpAPITest, RTXReceiver) {
|
||||||
|
bool enable = false;
|
||||||
|
unsigned int ssrc = 0;
|
||||||
|
EXPECT_EQ(0, module->SetRTXReceiveStatus(true, 1));
|
||||||
|
EXPECT_EQ(0, module->RTXReceiveStatus(&enable, &ssrc));
|
||||||
|
EXPECT_TRUE(enable);
|
||||||
|
EXPECT_EQ(1u, ssrc);
|
||||||
|
EXPECT_EQ(0, module->SetRTXReceiveStatus(false, 0));
|
||||||
|
EXPECT_EQ(0, module->RTXReceiveStatus(&enable, &ssrc));
|
||||||
|
EXPECT_FALSE(enable);
|
||||||
|
}
|
||||||
|
@ -8,9 +8,10 @@
|
|||||||
* be found in the AUTHORS file in the root of the source tree.
|
* be found in the AUTHORS file in the root of the source tree.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "common_types.h"
|
#include "testing/gtest/include/gtest/gtest.h"
|
||||||
#include "rtp_rtcp.h"
|
#include "webrtc/common_types.h"
|
||||||
#include "rtp_rtcp_defines.h"
|
#include "webrtc/modules/rtp_rtcp/interface/rtp_rtcp.h"
|
||||||
|
#include "webrtc/modules/rtp_rtcp/interface/rtp_rtcp_defines.h"
|
||||||
|
|
||||||
namespace webrtc {
|
namespace webrtc {
|
||||||
|
|
||||||
@ -87,4 +88,3 @@ class RtpReceiver : public RtpData {
|
|||||||
};
|
};
|
||||||
|
|
||||||
} // namespace webrtc
|
} // namespace webrtc
|
||||||
|
|
||||||
|
@ -1,333 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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 <algorithm>
|
|
||||||
#include <iterator>
|
|
||||||
#include <list>
|
|
||||||
#include <set>
|
|
||||||
#include <gtest/gtest.h>
|
|
||||||
|
|
||||||
#include "test_api.h"
|
|
||||||
|
|
||||||
#include "common_types.h"
|
|
||||||
#include "rtp_rtcp.h"
|
|
||||||
#include "rtp_rtcp_defines.h"
|
|
||||||
|
|
||||||
using namespace webrtc;
|
|
||||||
|
|
||||||
const int kVideoNackListSize = 10;
|
|
||||||
const int kTestId = 123;
|
|
||||||
const WebRtc_UWord32 kTestSsrc = 3456;
|
|
||||||
const WebRtc_UWord16 kTestSequenceNumber = 2345;
|
|
||||||
const WebRtc_UWord32 kTestNumberOfPackets = 450;
|
|
||||||
const int kTestNumberOfRtxPackets = 49;
|
|
||||||
|
|
||||||
class VerifyingNackReceiver : public RtpData
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
VerifyingNackReceiver() {}
|
|
||||||
|
|
||||||
virtual WebRtc_Word32 OnReceivedPayloadData(
|
|
||||||
const WebRtc_UWord8* data,
|
|
||||||
const WebRtc_UWord16 size,
|
|
||||||
const webrtc::WebRtcRTPHeader* rtp_header) {
|
|
||||||
|
|
||||||
EXPECT_EQ(kTestSsrc, rtp_header->header.ssrc);
|
|
||||||
bool already_received = std::find(
|
|
||||||
sequence_numbers_.begin(), sequence_numbers_.end(),
|
|
||||||
rtp_header->header.sequenceNumber) != sequence_numbers_.end();
|
|
||||||
EXPECT_FALSE(already_received);
|
|
||||||
sequence_numbers_.push_back(rtp_header->header.sequenceNumber);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
std::list<uint16_t> sequence_numbers_;
|
|
||||||
};
|
|
||||||
|
|
||||||
class NackLoopBackTransport : public webrtc::Transport {
|
|
||||||
public:
|
|
||||||
NackLoopBackTransport(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),
|
|
||||||
module_(NULL) {
|
|
||||||
}
|
|
||||||
void SetSendModule(RtpRtcp* rtpRtcpModule) {
|
|
||||||
module_ = rtpRtcpModule;
|
|
||||||
}
|
|
||||||
void DropEveryNthPacket(int n) {
|
|
||||||
packet_loss_ = n;
|
|
||||||
consecutive_drop_start_ = 0;
|
|
||||||
consecutive_drop_end_ = 0;
|
|
||||||
}
|
|
||||||
void DropConsecutivePackets(int start, int total) {
|
|
||||||
consecutive_drop_start_ = start;
|
|
||||||
consecutive_drop_end_ = start + total;
|
|
||||||
packet_loss_ = 0;
|
|
||||||
}
|
|
||||||
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_++;
|
|
||||||
uint16_t sequence_number = (ptr[2] << 8) + ptr[3];
|
|
||||||
expected_sequence_numbers_.insert(expected_sequence_numbers_.end(),
|
|
||||||
sequence_number);
|
|
||||||
|
|
||||||
if (packet_loss_ > 0) {
|
|
||||||
if ((count_ % packet_loss_) == 0) {
|
|
||||||
return len;
|
|
||||||
}
|
|
||||||
} else if (count_ >= consecutive_drop_start_ &&
|
|
||||||
count_ < consecutive_drop_end_) {
|
|
||||||
return len;
|
|
||||||
}
|
|
||||||
if (module_->IncomingPacket((const WebRtc_UWord8*)data, len) == 0) {
|
|
||||||
return len;
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
virtual int SendRTCPPacket(int channel, const void *data, int len) {
|
|
||||||
if (module_->IncomingPacket((const WebRtc_UWord8*)data, len) == 0) {
|
|
||||||
return len;
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
int count_;
|
|
||||||
int packet_loss_;
|
|
||||||
int consecutive_drop_start_;
|
|
||||||
int consecutive_drop_end_;
|
|
||||||
uint32_t rtx_ssrc_;
|
|
||||||
int count_rtx_ssrc_;
|
|
||||||
RtpRtcp* module_;
|
|
||||||
std::set<uint16_t> expected_sequence_numbers_;
|
|
||||||
};
|
|
||||||
|
|
||||||
class RtpRtcpNackTest : public ::testing::Test {
|
|
||||||
protected:
|
|
||||||
RtpRtcpNackTest()
|
|
||||||
: video_module_(NULL),
|
|
||||||
transport_(NULL),
|
|
||||||
nack_receiver_(NULL),
|
|
||||||
payload_data_length(sizeof(payload_data)),
|
|
||||||
fake_clock(123456) {}
|
|
||||||
~RtpRtcpNackTest() {}
|
|
||||||
|
|
||||||
virtual void SetUp() {
|
|
||||||
transport_ = new NackLoopBackTransport(kTestSsrc + 1);
|
|
||||||
nack_receiver_ = new VerifyingNackReceiver();
|
|
||||||
|
|
||||||
RtpRtcp::Configuration configuration;
|
|
||||||
configuration.id = kTestId;
|
|
||||||
configuration.audio = false;
|
|
||||||
configuration.clock = &fake_clock;
|
|
||||||
configuration.incoming_data = nack_receiver_;
|
|
||||||
configuration.outgoing_transport = transport_;
|
|
||||||
video_module_ = RtpRtcp::CreateRtpRtcp(configuration);
|
|
||||||
|
|
||||||
EXPECT_EQ(0, video_module_->SetRTCPStatus(kRtcpCompound));
|
|
||||||
EXPECT_EQ(0, video_module_->SetSSRC(kTestSsrc));
|
|
||||||
EXPECT_EQ(0, video_module_->SetNACKStatus(kNackRtcp, 450));
|
|
||||||
EXPECT_EQ(0, video_module_->SetStorePacketsStatus(true, 600));
|
|
||||||
EXPECT_EQ(0, video_module_->SetSendingStatus(true));
|
|
||||||
EXPECT_EQ(0, video_module_->SetSequenceNumber(kTestSequenceNumber));
|
|
||||||
EXPECT_EQ(0, video_module_->SetStartTimestamp(111111));
|
|
||||||
|
|
||||||
transport_->SetSendModule(video_module_);
|
|
||||||
|
|
||||||
VideoCodec video_codec;
|
|
||||||
memset(&video_codec, 0, sizeof(video_codec));
|
|
||||||
video_codec.plType = 123;
|
|
||||||
memcpy(video_codec.plName, "I420", 5);
|
|
||||||
|
|
||||||
EXPECT_EQ(0, video_module_->RegisterSendPayload(video_codec));
|
|
||||||
EXPECT_EQ(0, video_module_->RegisterReceivePayload(video_codec));
|
|
||||||
|
|
||||||
for (int n = 0; n < payload_data_length; n++) {
|
|
||||||
payload_data[n] = n % 10;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void TearDown() {
|
|
||||||
delete video_module_;
|
|
||||||
delete transport_;
|
|
||||||
delete nack_receiver_;
|
|
||||||
}
|
|
||||||
|
|
||||||
int BuildNackList(uint16_t* nack_list) const {
|
|
||||||
nack_receiver_->sequence_numbers_.sort();
|
|
||||||
|
|
||||||
std::list<uint16_t> missing_sequence_numbers;
|
|
||||||
std::list<uint16_t>::iterator it =
|
|
||||||
nack_receiver_->sequence_numbers_.begin();
|
|
||||||
|
|
||||||
while (it != nack_receiver_->sequence_numbers_.end()) {
|
|
||||||
WebRtc_UWord16 sequence_number_1 = *it;
|
|
||||||
++it;
|
|
||||||
if (it != nack_receiver_->sequence_numbers_.end()) {
|
|
||||||
WebRtc_UWord16 sequence_number_2 = *it;
|
|
||||||
// Add all missing sequence numbers to list
|
|
||||||
for (WebRtc_UWord16 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(nack_receiver_->sequence_numbers_.begin(),
|
|
||||||
nack_receiver_->sequence_numbers_.end(),
|
|
||||||
std::back_inserter(received_sorted));
|
|
||||||
received_sorted.sort();
|
|
||||||
return std::equal(received_sorted.begin(), received_sorted.end(),
|
|
||||||
transport_->expected_sequence_numbers_.begin());
|
|
||||||
}
|
|
||||||
|
|
||||||
RtpRtcp* video_module_;
|
|
||||||
NackLoopBackTransport* transport_;
|
|
||||||
VerifyingNackReceiver* nack_receiver_;
|
|
||||||
WebRtc_UWord8 payload_data[65000];
|
|
||||||
int payload_data_length;
|
|
||||||
SimulatedClock fake_clock;
|
|
||||||
};
|
|
||||||
|
|
||||||
TEST_F(RtpRtcpNackTest, RTCP) {
|
|
||||||
WebRtc_UWord32 timestamp = 3000;
|
|
||||||
uint16_t nack_list[kVideoNackListSize];
|
|
||||||
transport_->DropEveryNthPacket(10);
|
|
||||||
|
|
||||||
for (int frame = 0; frame < 10; ++frame) {
|
|
||||||
EXPECT_EQ(0, video_module_->SendOutgoingData(webrtc::kVideoFrameDelta, 123,
|
|
||||||
timestamp,
|
|
||||||
timestamp / 90,
|
|
||||||
payload_data,
|
|
||||||
payload_data_length));
|
|
||||||
|
|
||||||
int length = BuildNackList(nack_list);
|
|
||||||
video_module_->SendNACK(nack_list, length);
|
|
||||||
fake_clock.AdvanceTimeMilliseconds(33);
|
|
||||||
video_module_->Process();
|
|
||||||
|
|
||||||
// Prepare next frame.
|
|
||||||
timestamp += 3000;
|
|
||||||
}
|
|
||||||
nack_receiver_->sequence_numbers_.sort();
|
|
||||||
EXPECT_EQ(kTestSequenceNumber, *(nack_receiver_->sequence_numbers_.begin()));
|
|
||||||
EXPECT_EQ(kTestSequenceNumber + kTestNumberOfPackets - 1,
|
|
||||||
*(nack_receiver_->sequence_numbers_.rbegin()));
|
|
||||||
EXPECT_EQ(kTestNumberOfPackets, nack_receiver_->sequence_numbers_.size());
|
|
||||||
EXPECT_EQ(0, transport_->count_rtx_ssrc_);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(RtpRtcpNackTest, LongNackList) {
|
|
||||||
const int kNumPacketsToDrop = 900;
|
|
||||||
const int kNumFrames = 30;
|
|
||||||
const int kNumRequiredRtcp = 4;
|
|
||||||
WebRtc_UWord32 timestamp = 3000;
|
|
||||||
uint16_t nack_list[kNumPacketsToDrop];
|
|
||||||
// Disable StorePackets to be able to set a larger packet history.
|
|
||||||
EXPECT_EQ(0, video_module_->SetStorePacketsStatus(false, 0));
|
|
||||||
// Enable StorePackets with a packet history of 2000 packets.
|
|
||||||
EXPECT_EQ(0, video_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, video_module_->SendOutgoingData(webrtc::kVideoFrameDelta, 123,
|
|
||||||
timestamp,
|
|
||||||
timestamp / 90,
|
|
||||||
payload_data,
|
|
||||||
payload_data_length));
|
|
||||||
// Prepare next frame.
|
|
||||||
timestamp += 3000;
|
|
||||||
fake_clock.AdvanceTimeMilliseconds(33);
|
|
||||||
video_module_->Process();
|
|
||||||
}
|
|
||||||
EXPECT_FALSE(transport_->expected_sequence_numbers_.empty());
|
|
||||||
EXPECT_FALSE(nack_receiver_->sequence_numbers_.empty());
|
|
||||||
size_t last_receive_count = nack_receiver_->sequence_numbers_.size();
|
|
||||||
int length = BuildNackList(nack_list);
|
|
||||||
for (int i = 0; i < kNumRequiredRtcp - 1; ++i) {
|
|
||||||
video_module_->SendNACK(nack_list, length);
|
|
||||||
EXPECT_GT(nack_receiver_->sequence_numbers_.size(), last_receive_count);
|
|
||||||
last_receive_count = nack_receiver_->sequence_numbers_.size();
|
|
||||||
EXPECT_FALSE(ExpectedPacketsReceived());
|
|
||||||
}
|
|
||||||
video_module_->SendNACK(nack_list, length);
|
|
||||||
EXPECT_GT(nack_receiver_->sequence_numbers_.size(), last_receive_count);
|
|
||||||
EXPECT_TRUE(ExpectedPacketsReceived());
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(RtpRtcpNackTest, RTX) {
|
|
||||||
EXPECT_EQ(0, video_module_->SetRTXReceiveStatus(true, kTestSsrc + 1));
|
|
||||||
EXPECT_EQ(0, video_module_->SetRTXSendStatus(true, true, kTestSsrc + 1));
|
|
||||||
|
|
||||||
transport_->DropEveryNthPacket(10);
|
|
||||||
|
|
||||||
WebRtc_UWord32 timestamp = 3000;
|
|
||||||
WebRtc_UWord16 nack_list[kVideoNackListSize];
|
|
||||||
|
|
||||||
for (int frame = 0; frame < 10; ++frame) {
|
|
||||||
EXPECT_EQ(0, video_module_->SendOutgoingData(webrtc::kVideoFrameDelta,
|
|
||||||
123,
|
|
||||||
timestamp,
|
|
||||||
timestamp / 90,
|
|
||||||
payload_data,
|
|
||||||
payload_data_length));
|
|
||||||
|
|
||||||
nack_receiver_->sequence_numbers_.sort();
|
|
||||||
|
|
||||||
std::list<WebRtc_UWord16> missing_sequence_numbers;
|
|
||||||
|
|
||||||
|
|
||||||
std::list<WebRtc_UWord16>::iterator it =
|
|
||||||
nack_receiver_->sequence_numbers_.begin();
|
|
||||||
while (it != nack_receiver_->sequence_numbers_.end()) {
|
|
||||||
int sequence_number_1 = *it;
|
|
||||||
++it;
|
|
||||||
if (it != nack_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);
|
|
||||||
}
|
|
||||||
video_module_->SendNACK(nack_list, n);
|
|
||||||
fake_clock.AdvanceTimeMilliseconds(33);
|
|
||||||
video_module_->Process();
|
|
||||||
|
|
||||||
// Prepare next frame.
|
|
||||||
timestamp += 3000;
|
|
||||||
}
|
|
||||||
nack_receiver_->sequence_numbers_.sort();
|
|
||||||
EXPECT_EQ(kTestSequenceNumber, *(nack_receiver_->sequence_numbers_.begin()));
|
|
||||||
EXPECT_EQ(kTestSequenceNumber + kTestNumberOfPackets - 1,
|
|
||||||
*(nack_receiver_->sequence_numbers_.rbegin()));
|
|
||||||
EXPECT_EQ(kTestNumberOfPackets, nack_receiver_->sequence_numbers_.size());
|
|
||||||
EXPECT_EQ(kTestNumberOfRtxPackets, transport_->count_rtx_ssrc_);
|
|
||||||
}
|
|
@ -915,7 +915,7 @@ WebRtc_Word32 ViEChannel::SetSSRC(const WebRtc_UWord32 SSRC,
|
|||||||
}
|
}
|
||||||
RtpRtcp* rtp_rtcp = *it;
|
RtpRtcp* rtp_rtcp = *it;
|
||||||
if (usage == kViEStreamTypeRtx) {
|
if (usage == kViEStreamTypeRtx) {
|
||||||
return rtp_rtcp->SetRTXSendStatus(true, true, SSRC);
|
return rtp_rtcp->SetRTXSendStatus(kRtxRetransmitted, true, SSRC);
|
||||||
}
|
}
|
||||||
return rtp_rtcp->SetSSRC(SSRC);
|
return rtp_rtcp->SetSSRC(SSRC);
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user