The test works by randomly dropping small bursts of packets until enough NACKs have been sent back by the receiver. Retransmitted packets are never dropped in order to assure that all packets are eventually delivered. When enough NACK packets have been received and all dropped packets retransmitted, the test waits for the receiving side to send a number of RTCP packets without NACK lists to assure that the receiving side stops sending NACKs once packets have been retransmitted. BUG=2043 R=stefan@webrtc.org Review URL: https://webrtc-codereview.appspot.com/1934004 git-svn-id: http://webrtc.googlecode.com/svn/trunk@4482 4adac7df-926f-26a2-2b94-8c16560cd09d
285 lines
9.1 KiB
C++
285 lines
9.1 KiB
C++
/*
|
|
* 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 <map>
|
|
|
|
#include "testing/gtest/include/gtest/gtest.h"
|
|
|
|
#include "webrtc/modules/rtp_rtcp/interface/rtp_header_parser.h"
|
|
#include "webrtc/modules/rtp_rtcp/source/rtcp_utility.h"
|
|
#include "webrtc/system_wrappers/interface/critical_section_wrapper.h"
|
|
#include "webrtc/system_wrappers/interface/scoped_ptr.h"
|
|
#include "webrtc/system_wrappers/interface/event_wrapper.h"
|
|
#include "webrtc/video_engine/new_include/video_engine.h"
|
|
#include "webrtc/video_engine/test/common/frame_generator.h"
|
|
#include "webrtc/video_engine/test/common/frame_generator_capturer.h"
|
|
#include "webrtc/video_engine/test/common/generate_ssrcs.h"
|
|
|
|
namespace webrtc {
|
|
|
|
class NackObserver {
|
|
public:
|
|
class SenderTransport : public newapi::Transport {
|
|
public:
|
|
explicit SenderTransport(NackObserver* observer)
|
|
: receiver_(NULL), observer_(observer) {}
|
|
|
|
bool SendRTP(const uint8_t* packet, size_t length) {
|
|
{
|
|
CriticalSectionScoped lock(observer_->crit_.get());
|
|
if (observer_->DropSendPacket(packet, length))
|
|
return true;
|
|
++observer_->sent_rtp_packets_;
|
|
}
|
|
|
|
return receiver_->DeliverPacket(packet, length);
|
|
}
|
|
|
|
bool SendRTCP(const uint8_t* packet, size_t length) {
|
|
return receiver_->DeliverPacket(packet, length);
|
|
}
|
|
|
|
newapi::PacketReceiver* receiver_;
|
|
NackObserver* observer_;
|
|
} sender_transport_;
|
|
|
|
class ReceiverTransport : public newapi::Transport {
|
|
public:
|
|
explicit ReceiverTransport(NackObserver* observer)
|
|
: receiver_(NULL), observer_(observer) {}
|
|
|
|
bool SendRTP(const uint8_t* packet, size_t length) {
|
|
return receiver_->DeliverPacket(packet, length);
|
|
}
|
|
|
|
bool SendRTCP(const uint8_t* packet, size_t length) {
|
|
{
|
|
CriticalSectionScoped lock(observer_->crit_.get());
|
|
|
|
RTCPUtility::RTCPParserV2 parser(packet, length, true);
|
|
EXPECT_TRUE(parser.IsValid());
|
|
|
|
bool received_nack = false;
|
|
RTCPUtility::RTCPPacketTypes packet_type = parser.Begin();
|
|
while (packet_type != RTCPUtility::kRtcpNotValidCode) {
|
|
if (packet_type == RTCPUtility::kRtcpRtpfbNackCode)
|
|
received_nack = true;
|
|
|
|
packet_type = parser.Iterate();
|
|
}
|
|
|
|
if (received_nack) {
|
|
observer_->ReceivedNack();
|
|
} else {
|
|
observer_->RtcpWithoutNack();
|
|
}
|
|
}
|
|
return receiver_->DeliverPacket(packet, length);
|
|
}
|
|
|
|
newapi::PacketReceiver* receiver_;
|
|
NackObserver* observer_;
|
|
} receiver_transport_;
|
|
|
|
NackObserver()
|
|
: sender_transport_(this),
|
|
receiver_transport_(this),
|
|
crit_(CriticalSectionWrapper::CreateCriticalSection()),
|
|
received_all_retransmissions_(EventWrapper::Create()),
|
|
rtp_parser_(RtpHeaderParser::Create()),
|
|
drop_burst_count_(0),
|
|
sent_rtp_packets_(0),
|
|
nacks_left_(4) {}
|
|
|
|
EventTypeWrapper Wait() {
|
|
// 2 minutes should be more than enough time for the test to finish.
|
|
return received_all_retransmissions_->Wait(2 * 60 * 1000);
|
|
}
|
|
|
|
private:
|
|
// Decides whether a current packet should be dropped or not. A retransmitted
|
|
// packet will never be dropped. Packets are dropped in short bursts. When
|
|
// enough NACKs have been received, no further packets are dropped.
|
|
bool DropSendPacket(const uint8_t* packet, size_t length) {
|
|
EXPECT_FALSE(RtpHeaderParser::IsRtcp(packet, static_cast<int>(length)));
|
|
|
|
RTPHeader header;
|
|
EXPECT_TRUE(rtp_parser_->Parse(packet, static_cast<int>(length), &header));
|
|
|
|
// Never drop retransmitted packets.
|
|
if (dropped_packets_.find(header.sequenceNumber) !=
|
|
dropped_packets_.end()) {
|
|
retransmitted_packets_.insert(header.sequenceNumber);
|
|
return false;
|
|
}
|
|
|
|
// Enough NACKs received, stop dropping packets.
|
|
if (nacks_left_ == 0)
|
|
return false;
|
|
|
|
// Still dropping packets.
|
|
if (drop_burst_count_ > 0) {
|
|
--drop_burst_count_;
|
|
dropped_packets_.insert(header.sequenceNumber);
|
|
return true;
|
|
}
|
|
|
|
if (sent_rtp_packets_ > 0 && rand() % 20 == 0) {
|
|
drop_burst_count_ = rand() % 10;
|
|
dropped_packets_.insert(header.sequenceNumber);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void ReceivedNack() {
|
|
if (nacks_left_ > 0)
|
|
--nacks_left_;
|
|
rtcp_without_nack_count_ = 0;
|
|
}
|
|
|
|
void RtcpWithoutNack() {
|
|
if (nacks_left_ > 0)
|
|
return;
|
|
++rtcp_without_nack_count_;
|
|
|
|
// All packets retransmitted and no recent NACKs.
|
|
if (dropped_packets_.size() == retransmitted_packets_.size() &&
|
|
rtcp_without_nack_count_ >= kRequiredRtcpsWithoutNack) {
|
|
received_all_retransmissions_->Set();
|
|
}
|
|
}
|
|
|
|
scoped_ptr<CriticalSectionWrapper> crit_;
|
|
scoped_ptr<EventWrapper> received_all_retransmissions_;
|
|
|
|
scoped_ptr<RtpHeaderParser> rtp_parser_;
|
|
std::set<uint16_t> dropped_packets_;
|
|
std::set<uint16_t> retransmitted_packets_;
|
|
int drop_burst_count_;
|
|
uint64_t sent_rtp_packets_;
|
|
int nacks_left_;
|
|
int rtcp_without_nack_count_;
|
|
static const int kRequiredRtcpsWithoutNack = 2;
|
|
};
|
|
|
|
struct EngineTestParams {
|
|
size_t width, height;
|
|
struct {
|
|
unsigned int min, start, max;
|
|
} bitrate;
|
|
};
|
|
|
|
class EngineTest : public ::testing::TestWithParam<EngineTestParams> {
|
|
public:
|
|
virtual void SetUp() {
|
|
video_engine_.reset(
|
|
newapi::VideoEngine::Create(newapi::VideoEngineConfig()));
|
|
reserved_ssrcs_.clear();
|
|
}
|
|
|
|
protected:
|
|
newapi::VideoCall* CreateTestCall(newapi::Transport* transport) {
|
|
newapi::VideoCall::Config call_config;
|
|
call_config.send_transport = transport;
|
|
return video_engine_->CreateCall(call_config);
|
|
}
|
|
|
|
newapi::VideoSendStream::Config CreateTestSendConfig(
|
|
newapi::VideoCall* call,
|
|
EngineTestParams params) {
|
|
newapi::VideoSendStream::Config config = call->GetDefaultSendConfig();
|
|
|
|
test::GenerateRandomSsrcs(&config, &reserved_ssrcs_);
|
|
|
|
config.codec.width = static_cast<uint16_t>(params.width);
|
|
config.codec.height = static_cast<uint16_t>(params.height);
|
|
config.codec.minBitrate = params.bitrate.min;
|
|
config.codec.startBitrate = params.bitrate.start;
|
|
config.codec.maxBitrate = params.bitrate.max;
|
|
|
|
return config;
|
|
}
|
|
|
|
test::FrameGeneratorCapturer* CreateTestFrameGeneratorCapturer(
|
|
newapi::VideoSendStream* target,
|
|
EngineTestParams params) {
|
|
return test::FrameGeneratorCapturer::Create(
|
|
target->Input(),
|
|
test::FrameGenerator::Create(
|
|
params.width, params.height, Clock::GetRealTimeClock()),
|
|
30);
|
|
}
|
|
|
|
scoped_ptr<newapi::VideoEngine> video_engine_;
|
|
std::map<uint32_t, bool> reserved_ssrcs_;
|
|
};
|
|
|
|
// TODO(pbos): What are sane values here for bitrate? Are we missing any
|
|
// important resolutions?
|
|
EngineTestParams video_1080p = {1920, 1080, {300, 600, 800}};
|
|
EngineTestParams video_720p = {1280, 720, {300, 600, 800}};
|
|
EngineTestParams video_vga = {640, 480, {300, 600, 800}};
|
|
EngineTestParams video_qvga = {320, 240, {300, 600, 800}};
|
|
EngineTestParams video_4cif = {704, 576, {300, 600, 800}};
|
|
EngineTestParams video_cif = {352, 288, {300, 600, 800}};
|
|
EngineTestParams video_qcif = {176, 144, {300, 600, 800}};
|
|
|
|
TEST_P(EngineTest, ReceivesAndRetransmitsNack) {
|
|
EngineTestParams params = GetParam();
|
|
|
|
// Set up a video call per sender and receiver. Both send RTCP, and have a set
|
|
// RTP history > 0 to enable NACK and retransmissions.
|
|
NackObserver observer;
|
|
|
|
scoped_ptr<newapi::VideoCall> sender_call(
|
|
CreateTestCall(&observer.sender_transport_));
|
|
scoped_ptr<newapi::VideoCall> receiver_call(
|
|
CreateTestCall(&observer.receiver_transport_));
|
|
|
|
observer.receiver_transport_.receiver_ = sender_call->Receiver();
|
|
observer.sender_transport_.receiver_ = receiver_call->Receiver();
|
|
|
|
|
|
newapi::VideoSendStream::Config send_config =
|
|
CreateTestSendConfig(sender_call.get(), params);
|
|
send_config.rtp.nack.rtp_history_ms = 1000;
|
|
|
|
newapi::VideoReceiveStream::Config receive_config =
|
|
receiver_call->GetDefaultReceiveConfig();
|
|
receive_config.rtp.ssrc = send_config.rtp.ssrcs[0];
|
|
receive_config.rtp.nack.rtp_history_ms = send_config.rtp.nack.rtp_history_ms;
|
|
|
|
newapi::VideoSendStream* send_stream =
|
|
sender_call->CreateSendStream(send_config);
|
|
newapi::VideoReceiveStream* receive_stream =
|
|
receiver_call->CreateReceiveStream(receive_config);
|
|
|
|
scoped_ptr<test::FrameGeneratorCapturer> frame_generator_capturer(
|
|
CreateTestFrameGeneratorCapturer(send_stream, params));
|
|
ASSERT_TRUE(frame_generator_capturer.get() != NULL);
|
|
|
|
receive_stream->StartReceive();
|
|
send_stream->StartSend();
|
|
frame_generator_capturer->Start();
|
|
|
|
EXPECT_EQ(kEventSignaled, observer.Wait());
|
|
|
|
frame_generator_capturer->Stop();
|
|
send_stream->StopSend();
|
|
receive_stream->StopReceive();
|
|
|
|
receiver_call->DestroyReceiveStream(receive_stream);
|
|
receiver_call->DestroySendStream(send_stream);
|
|
}
|
|
|
|
INSTANTIATE_TEST_CASE_P(EngineTest, EngineTest, ::testing::Values(video_vga));
|
|
} // namespace webrtc
|