Wire up RTX in VideoReceiveStream.

Also adds a test to make sure that a retransmitted frame is actually
received and decoded on the remote side. The previous NACK test checked
retransmission, but not that the receiver actually takes care of the
retransmitted packet.

BUG=2399
R=mflodman@webrtc.org, stefan@webrtc.org

Review URL: https://webrtc-codereview.appspot.com/7469004

git-svn-id: http://webrtc.googlecode.com/svn/trunk@5422 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
pbos@webrtc.org
2014-01-24 09:30:53 +00:00
parent 8d375c95b7
commit c279a5d72c
10 changed files with 253 additions and 124 deletions

View File

@ -71,16 +71,6 @@ struct FecConfig {
int red_payload_type; int red_payload_type;
}; };
// Settings for RTP retransmission payload format, see RFC 4588 for details.
struct RtxConfig {
RtxConfig() : rtx_payload_type(0) {}
// SSRCs to use for the RTX streams.
std::vector<uint32_t> ssrcs;
// Payload type to use for the RTX stream.
int rtx_payload_type;
};
// RTP header extension to use for the video stream, see RFC 5285. // RTP header extension to use for the video stream, see RFC 5285.
struct RtpExtension { struct RtpExtension {
static const char* kTOffset; static const char* kTOffset;

View File

@ -13,6 +13,8 @@
#include <map> #include <map>
#include <vector> #include <vector>
#include "testing/gtest/include/gtest/gtest.h"
#include "webrtc/modules/rtp_rtcp/interface/rtp_header_parser.h" #include "webrtc/modules/rtp_rtcp/interface/rtp_header_parser.h"
#include "webrtc/typedefs.h" #include "webrtc/typedefs.h"
#include "webrtc/video_send_stream.h" #include "webrtc/video_send_stream.h"
@ -123,6 +125,7 @@ class RtpRtcpObserver {
private: private:
virtual bool SendRtp(const uint8_t* packet, size_t length) OVERRIDE { virtual bool SendRtp(const uint8_t* packet, size_t length) OVERRIDE {
EXPECT_FALSE(RtpHeaderParser::IsRtcp(packet, static_cast<int>(length)));
Action action; Action action;
{ {
CriticalSectionScoped crit_(lock_); CriticalSectionScoped crit_(lock_);
@ -139,6 +142,7 @@ class RtpRtcpObserver {
} }
virtual bool SendRtcp(const uint8_t* packet, size_t length) OVERRIDE { virtual bool SendRtcp(const uint8_t* packet, size_t length) OVERRIDE {
EXPECT_TRUE(RtpHeaderParser::IsRtcp(packet, static_cast<int>(length)));
Action action; Action action;
{ {
CriticalSectionScoped crit_(lock_); CriticalSectionScoped crit_(lock_);

View File

@ -170,8 +170,9 @@ void CreateTraceDispatcher() {
Call* Call::Create(const Call::Config& config) { Call* Call::Create(const Call::Config& config) {
CreateTraceDispatcher(); CreateTraceDispatcher();
VideoEngine* video_engine = config.webrtc_config != NULL ? VideoEngine* video_engine = config.webrtc_config != NULL
VideoEngine::Create(*config.webrtc_config) : VideoEngine::Create(); ? VideoEngine::Create(*config.webrtc_config)
: VideoEngine::Create();
assert(video_engine != NULL); assert(video_engine != NULL);
return new internal::Call(video_engine, config); return new internal::Call(video_engine, config);
@ -294,6 +295,12 @@ VideoReceiveStream* Call::CreateVideoReceiveStream(
WriteLockScoped write_lock(*receive_lock_); WriteLockScoped write_lock(*receive_lock_);
assert(receive_ssrcs_.find(config.rtp.remote_ssrc) == receive_ssrcs_.end()); assert(receive_ssrcs_.find(config.rtp.remote_ssrc) == receive_ssrcs_.end());
receive_ssrcs_[config.rtp.remote_ssrc] = receive_stream; receive_ssrcs_[config.rtp.remote_ssrc] = receive_stream;
// TODO(pbos): Configure different RTX payloads per receive payload.
VideoReceiveStream::Config::Rtp::RtxMap::const_iterator it =
config.rtp.rtx.begin();
if (it != config.rtp.rtx.end())
receive_ssrcs_[it->second.ssrc] = receive_stream;
return receive_stream; return receive_stream;
} }
@ -304,14 +311,16 @@ void Call::DestroyVideoReceiveStream(
VideoReceiveStream* receive_stream_impl = NULL; VideoReceiveStream* receive_stream_impl = NULL;
{ {
WriteLockScoped write_lock(*receive_lock_); WriteLockScoped write_lock(*receive_lock_);
for (std::map<uint32_t, VideoReceiveStream*>::iterator it = // Remove all ssrcs pointing to a receive stream. As RTX retransmits on a
receive_ssrcs_.begin(); // separate SSRC there can be either one or two.
it != receive_ssrcs_.end(); std::map<uint32_t, VideoReceiveStream*>::iterator it =
++it) { receive_ssrcs_.begin();
while (it != receive_ssrcs_.end()) {
if (it->second == static_cast<VideoReceiveStream*>(receive_stream)) { if (it->second == static_cast<VideoReceiveStream*>(receive_stream)) {
receive_stream_impl = it->second; receive_stream_impl = it->second;
receive_ssrcs_.erase(it); receive_ssrcs_.erase(it++);
break; } else {
++it;
} }
} }
} }

View File

@ -18,7 +18,6 @@
#include "webrtc/call.h" #include "webrtc/call.h"
#include "webrtc/frame_callback.h" #include "webrtc/frame_callback.h"
#include "webrtc/modules/rtp_rtcp/interface/rtp_header_parser.h"
#include "webrtc/modules/rtp_rtcp/source/rtcp_utility.h" #include "webrtc/modules/rtp_rtcp/source/rtcp_utility.h"
#include "webrtc/system_wrappers/interface/critical_section_wrapper.h" #include "webrtc/system_wrappers/interface/critical_section_wrapper.h"
#include "webrtc/system_wrappers/interface/event_wrapper.h" #include "webrtc/system_wrappers/interface/event_wrapper.h"
@ -41,8 +40,10 @@ namespace webrtc {
static unsigned int kDefaultTimeoutMs = 30 * 1000; static unsigned int kDefaultTimeoutMs = 30 * 1000;
static unsigned int kLongTimeoutMs = 120 * 1000; static unsigned int kLongTimeoutMs = 120 * 1000;
static const uint32_t kSendSsrc = 0x654321; static const uint32_t kSendSsrc = 0x654321;
static const uint32_t kSendRtxSsrc = 0x424242;
static const uint32_t kReceiverLocalSsrc = 0x123456; static const uint32_t kReceiverLocalSsrc = 0x123456;
static const uint8_t kSendPayloadType = 125; static const uint8_t kSendPayloadType = 125;
static const uint8_t kSendRtxPayloadType = 126;
class CallTest : public ::testing::Test { class CallTest : public ::testing::Test {
public: public:
@ -125,6 +126,7 @@ class CallTest : public ::testing::Test {
receive_stream_ = NULL; receive_stream_ = NULL;
} }
void DecodesRetransmittedFrame(bool retransmit_over_rtx);
void ReceivesPliAndRecovers(int rtp_history_ms); void ReceivesPliAndRecovers(int rtp_history_ms);
void RespectsRtcpMode(newapi::RtcpMode rtcp_mode); void RespectsRtcpMode(newapi::RtcpMode rtcp_mode);
void TestXrReceiverReferenceTimeReport(bool enable_rrtr); void TestXrReceiverReferenceTimeReport(bool enable_rrtr);
@ -159,8 +161,6 @@ class NackObserver : public test::RtpRtcpObserver {
private: private:
virtual Action OnSendRtp(const uint8_t* packet, size_t length) OVERRIDE { virtual Action OnSendRtp(const uint8_t* packet, size_t length) OVERRIDE {
EXPECT_FALSE(RtpHeaderParser::IsRtcp(packet, static_cast<int>(length)));
RTPHeader header; RTPHeader header;
EXPECT_TRUE(rtp_parser_->Parse(packet, static_cast<int>(length), &header)); EXPECT_TRUE(rtp_parser_->Parse(packet, static_cast<int>(length), &header));
@ -328,7 +328,7 @@ TEST_F(CallTest, RendersSingleDelayedFrame) {
EventTypeWrapper Wait() { return event_->Wait(kDefaultTimeoutMs); } EventTypeWrapper Wait() { return event_->Wait(kDefaultTimeoutMs); }
private: private:
virtual void FrameCallback(I420VideoFrame* frame) { virtual void FrameCallback(I420VideoFrame* frame) OVERRIDE {
SleepMs(kDelayRenderCallbackMs); SleepMs(kDelayRenderCallbackMs);
event_->Set(); event_->Set();
} }
@ -485,6 +485,99 @@ TEST_F(CallTest, ReceivesAndRetransmitsNack) {
DestroyStreams(); DestroyStreams();
} }
// This test drops second RTP packet with a marker bit set, makes sure it's
// retransmitted and renders. Retransmission SSRCs are also checked.
void CallTest::DecodesRetransmittedFrame(bool retransmit_over_rtx) {
static const int kDroppedFrameNumber = 2;
class RetransmissionObserver : public test::RtpRtcpObserver,
public I420FrameCallback {
public:
RetransmissionObserver(bool expect_rtx)
: RtpRtcpObserver(kDefaultTimeoutMs),
retransmission_ssrc_(expect_rtx ? kSendRtxSsrc : kSendSsrc),
retransmission_payload_type_(expect_rtx ? kSendRtxPayloadType
: kSendPayloadType),
marker_bits_observed_(0),
retransmitted_timestamp_(0),
frame_retransmitted_(false) {}
private:
virtual Action OnSendRtp(const uint8_t* packet, size_t length) OVERRIDE {
RTPHeader header;
EXPECT_TRUE(parser_->Parse(packet, static_cast<int>(length), &header));
if (header.timestamp == retransmitted_timestamp_) {
EXPECT_EQ(retransmission_ssrc_, header.ssrc);
EXPECT_EQ(retransmission_payload_type_, header.payloadType);
frame_retransmitted_ = true;
return SEND_PACKET;
}
EXPECT_EQ(kSendSsrc, header.ssrc);
EXPECT_EQ(kSendPayloadType, header.payloadType);
// Found the second frame's final packet, drop this and expect a
// retransmission.
if (header.markerBit && ++marker_bits_observed_ == kDroppedFrameNumber) {
retransmitted_timestamp_ = header.timestamp;
return DROP_PACKET;
}
return SEND_PACKET;
}
virtual void FrameCallback(I420VideoFrame* frame) OVERRIDE {
CriticalSectionScoped crit_(lock_.get());
if (frame->timestamp() == retransmitted_timestamp_) {
EXPECT_TRUE(frame_retransmitted_);
observation_complete_->Set();
}
}
const uint32_t retransmission_ssrc_;
const int retransmission_payload_type_;
int marker_bits_observed_;
uint32_t retransmitted_timestamp_;
bool frame_retransmitted_;
} observer(retransmit_over_rtx);
CreateCalls(Call::Config(observer.SendTransport()),
Call::Config(observer.ReceiveTransport()));
observer.SetReceivers(receiver_call_->Receiver(), sender_call_->Receiver());
CreateTestConfigs();
send_config_.rtp.nack.rtp_history_ms =
receive_config_.rtp.nack.rtp_history_ms = 1000;
if (retransmit_over_rtx) {
send_config_.rtp.rtx.ssrcs.push_back(kSendRtxSsrc);
send_config_.rtp.rtx.payload_type = kSendRtxPayloadType;
int payload_type = send_config_.codec.plType;
receive_config_.rtp.rtx[payload_type].ssrc = kSendRtxSsrc;
receive_config_.rtp.rtx[payload_type].payload_type = kSendRtxPayloadType;
}
receive_config_.pre_render_callback = &observer;
CreateStreams();
CreateFrameGenerator();
StartSending();
EXPECT_EQ(kEventSignaled, observer.Wait())
<< "Timed out while waiting for retransmission to render.";
StopSending();
observer.StopSending();
DestroyStreams();
}
TEST_F(CallTest, DecodesRetransmittedFrame) {
DecodesRetransmittedFrame(false);
}
TEST_F(CallTest, DecodesRetransmittedFrameOverRtx) {
DecodesRetransmittedFrame(true);
}
TEST_F(CallTest, UsesFrameCallbacks) { TEST_F(CallTest, UsesFrameCallbacks) {
static const int kWidth = 320; static const int kWidth = 320;
static const int kHeight = 240; static const int kHeight = 240;
@ -588,7 +681,6 @@ class PliObserver : public test::RtpRtcpObserver, public VideoRenderer {
public: public:
explicit PliObserver(bool nack_enabled) explicit PliObserver(bool nack_enabled)
: test::RtpRtcpObserver(kLongTimeoutMs), : test::RtpRtcpObserver(kLongTimeoutMs),
rtp_header_parser_(RtpHeaderParser::Create()),
nack_enabled_(nack_enabled), nack_enabled_(nack_enabled),
highest_dropped_timestamp_(0), highest_dropped_timestamp_(0),
frames_to_drop_(0), frames_to_drop_(0),
@ -596,8 +688,7 @@ class PliObserver : public test::RtpRtcpObserver, public VideoRenderer {
virtual Action OnSendRtp(const uint8_t* packet, size_t length) OVERRIDE { virtual Action OnSendRtp(const uint8_t* packet, size_t length) OVERRIDE {
RTPHeader header; RTPHeader header;
EXPECT_TRUE( EXPECT_TRUE(parser_->Parse(packet, static_cast<int>(length), &header));
rtp_header_parser_->Parse(packet, static_cast<int>(length), &header));
// Drop all retransmitted packets to force a PLI. // Drop all retransmitted packets to force a PLI.
if (header.timestamp <= highest_dropped_timestamp_) if (header.timestamp <= highest_dropped_timestamp_)
@ -643,7 +734,6 @@ class PliObserver : public test::RtpRtcpObserver, public VideoRenderer {
private: private:
static const int kPacketsToDrop = 1; static const int kPacketsToDrop = 1;
scoped_ptr<RtpHeaderParser> rtp_header_parser_;
bool nack_enabled_; bool nack_enabled_;
uint32_t highest_dropped_timestamp_; uint32_t highest_dropped_timestamp_;
int frames_to_drop_; int frames_to_drop_;
@ -933,9 +1023,10 @@ TEST_F(CallTest, SendsAndReceivesMultipleStreams) {
TEST_F(CallTest, ObserversEncodedFrames) { TEST_F(CallTest, ObserversEncodedFrames) {
class EncodedFrameTestObserver : public EncodedFrameObserver { class EncodedFrameTestObserver : public EncodedFrameObserver {
public: public:
EncodedFrameTestObserver() : length_(0), EncodedFrameTestObserver()
frame_type_(kFrameEmpty), : length_(0),
called_(EventWrapper::Create()) {} frame_type_(kFrameEmpty),
called_(EventWrapper::Create()) {}
virtual ~EncodedFrameTestObserver() {} virtual ~EncodedFrameTestObserver() {}
virtual void EncodedFrameCallback(const EncodedFrame& encoded_frame) { virtual void EncodedFrameCallback(const EncodedFrame& encoded_frame) {
@ -946,9 +1037,7 @@ TEST_F(CallTest, ObserversEncodedFrames) {
called_->Set(); called_->Set();
} }
EventTypeWrapper Wait() { EventTypeWrapper Wait() { return called_->Wait(kDefaultTimeoutMs); }
return called_->Wait(kDefaultTimeoutMs);
}
void ExpectEqualFrames(const EncodedFrameTestObserver& observer) { void ExpectEqualFrames(const EncodedFrameTestObserver& observer) {
ASSERT_EQ(length_, observer.length_) ASSERT_EQ(length_, observer.length_)
@ -1064,6 +1153,7 @@ void CallTest::TestXrReceiverReferenceTimeReport(bool enable_rrtr) {
sent_rtcp_rr_(0), sent_rtcp_rr_(0),
sent_rtcp_rrtr_(0), sent_rtcp_rrtr_(0),
sent_rtcp_dlrr_(0) {} sent_rtcp_dlrr_(0) {}
private: private:
// Receive stream should send RR packets (and RRTR packets if enabled). // Receive stream should send RR packets (and RRTR packets if enabled).
virtual Action OnReceiveRtcp(const uint8_t* packet, virtual Action OnReceiveRtcp(const uint8_t* packet,
@ -1075,8 +1165,8 @@ void CallTest::TestXrReceiverReferenceTimeReport(bool enable_rrtr) {
while (packet_type != RTCPUtility::kRtcpNotValidCode) { while (packet_type != RTCPUtility::kRtcpNotValidCode) {
if (packet_type == RTCPUtility::kRtcpRrCode) { if (packet_type == RTCPUtility::kRtcpRrCode) {
++sent_rtcp_rr_; ++sent_rtcp_rr_;
} else if ( } else if (packet_type ==
packet_type == RTCPUtility::kRtcpXrReceiverReferenceTimeCode) { RTCPUtility::kRtcpXrReceiverReferenceTimeCode) {
++sent_rtcp_rrtr_; ++sent_rtcp_rrtr_;
} }
EXPECT_NE(packet_type, RTCPUtility::kRtcpSrCode); EXPECT_NE(packet_type, RTCPUtility::kRtcpSrCode);
@ -1122,8 +1212,7 @@ void CallTest::TestXrReceiverReferenceTimeReport(bool enable_rrtr) {
CreateCalls(Call::Config(observer.SendTransport()), CreateCalls(Call::Config(observer.SendTransport()),
Call::Config(observer.ReceiveTransport())); Call::Config(observer.ReceiveTransport()));
observer.SetReceivers(receiver_call_->Receiver(), observer.SetReceivers(receiver_call_->Receiver(), sender_call_->Receiver());
sender_call_->Receiver());
CreateTestConfigs(); CreateTestConfigs();
receive_config_.rtp.rtcp_mode = newapi::kRtcpReducedSize; receive_config_.rtp.rtcp_mode = newapi::kRtcpReducedSize;

View File

@ -36,8 +36,8 @@
namespace webrtc { namespace webrtc {
namespace { namespace {
static const int kAbsoluteSendTimeExtensionId = 7; static const int kAbsoluteSendTimeExtensionId = 7;
static const int kMaxPacketSize = 1500; static const int kMaxPacketSize = 1500;
} }
class StreamObserver : public newapi::Transport, public RemoteBitrateObserver { class StreamObserver : public newapi::Transport, public RemoteBitrateObserver {
@ -53,8 +53,9 @@ class StreamObserver : public newapi::Transport, public RemoteBitrateObserver {
rtp_parser_(RtpHeaderParser::Create()), rtp_parser_(RtpHeaderParser::Create()),
feedback_transport_(feedback_transport), feedback_transport_(feedback_transport),
receive_stats_(ReceiveStatistics::Create(clock)), receive_stats_(ReceiveStatistics::Create(clock)),
payload_registry_(new RTPPayloadRegistry( payload_registry_(
-1, RTPPayloadStrategy::CreateStrategy(false))), new RTPPayloadRegistry(-1,
RTPPayloadStrategy::CreateStrategy(false))),
clock_(clock), clock_(clock),
num_expected_ssrcs_(num_expected_ssrcs), num_expected_ssrcs_(num_expected_ssrcs),
rtx_media_ssrcs_(rtx_media_ssrcs), rtx_media_ssrcs_(rtx_media_ssrcs),
@ -88,19 +89,39 @@ class StreamObserver : public newapi::Transport, public RemoteBitrateObserver {
if (ssrcs.size() == num_expected_ssrcs_ && bitrate >= kExpectedBitrateBps) { if (ssrcs.size() == num_expected_ssrcs_ && bitrate >= kExpectedBitrateBps) {
if (rtx_media_ssrcs_.empty() || rtx_media_sent_ > 0) { if (rtx_media_ssrcs_.empty() || rtx_media_sent_ > 0) {
const ::testing::TestInfo* const test_info = const ::testing::TestInfo* const test_info =
::testing::UnitTest::GetInstance()->current_test_info(); ::testing::UnitTest::GetInstance()->current_test_info();
webrtc::test::PrintResult("total-sent", "", test_info->name(), webrtc::test::PrintResult(
total_sent_, "bytes", false); "total-sent", "", test_info->name(), total_sent_, "bytes", false);
webrtc::test::PrintResult("padding-sent", "", test_info->name(), webrtc::test::PrintResult("padding-sent",
padding_sent_, "bytes", false); "",
webrtc::test::PrintResult("rtx-media-sent", "", test_info->name(), test_info->name(),
rtx_media_sent_, "bytes", false); padding_sent_,
webrtc::test::PrintResult("total-packets-sent", "", test_info->name(), "bytes",
total_packets_sent_, "packets", false); false);
webrtc::test::PrintResult("padding-packets-sent", "", test_info->name(), webrtc::test::PrintResult("rtx-media-sent",
padding_packets_sent_, "packets", false); "",
webrtc::test::PrintResult("rtx-packets-sent", "", test_info->name(), test_info->name(),
rtx_media_packets_sent_, "packets", false); rtx_media_sent_,
"bytes",
false);
webrtc::test::PrintResult("total-packets-sent",
"",
test_info->name(),
total_packets_sent_,
"packets",
false);
webrtc::test::PrintResult("padding-packets-sent",
"",
test_info->name(),
padding_packets_sent_,
"packets",
false);
webrtc::test::PrintResult("rtx-packets-sent",
"",
test_info->name(),
rtx_media_packets_sent_,
"packets",
false);
all_ssrcs_sent_->Set(); all_ssrcs_sent_->Set();
} }
} }
@ -132,13 +153,14 @@ class StreamObserver : public newapi::Transport, public RemoteBitrateObserver {
uint8_t restored_packet[kMaxPacketSize]; uint8_t restored_packet[kMaxPacketSize];
uint8_t* restored_packet_ptr = restored_packet; uint8_t* restored_packet_ptr = restored_packet;
int restored_length = static_cast<int>(length); int restored_length = static_cast<int>(length);
payload_registry_->RestoreOriginalPacket( payload_registry_->RestoreOriginalPacket(&restored_packet_ptr,
&restored_packet_ptr, packet, &restored_length, packet,
rtx_media_ssrcs_[header.ssrc], &restored_length,
header); rtx_media_ssrcs_[header.ssrc],
header);
length = restored_length; length = restored_length;
EXPECT_TRUE(rtp_parser_->Parse(restored_packet, static_cast<int>(length), EXPECT_TRUE(rtp_parser_->Parse(
&header)); restored_packet, static_cast<int>(length), &header));
} else { } else {
rtp_rtcp_->SetRemoteSSRC(header.ssrc); rtp_rtcp_->SetRemoteSSRC(header.ssrc);
} }
@ -191,9 +213,10 @@ class RampUpTest : public ::testing::TestWithParam<bool> {
} }
test::DirectTransport receiver_transport; test::DirectTransport receiver_transport;
int num_expected_ssrcs = kNumberOfStreams + (rtx ? 1 : 0); int num_expected_ssrcs = kNumberOfStreams + (rtx ? 1 : 0);
StreamObserver stream_observer( StreamObserver stream_observer(num_expected_ssrcs,
num_expected_ssrcs, rtx_ssrc_map, &receiver_transport, rtx_ssrc_map,
Clock::GetRealTimeClock()); &receiver_transport,
Clock::GetRealTimeClock());
Call::Config call_config(&stream_observer); Call::Config call_config(&stream_observer);
webrtc::Config webrtc_config; webrtc::Config webrtc_config;
@ -211,10 +234,10 @@ class RampUpTest : public ::testing::TestWithParam<bool> {
send_config.codec.plType = 125; send_config.codec.plType = 125;
send_config.pacing = pacing; send_config.pacing = pacing;
send_config.rtp.nack.rtp_history_ms = 1000; send_config.rtp.nack.rtp_history_ms = 1000;
send_config.rtp.ssrcs.insert(send_config.rtp.ssrcs.begin(), ssrcs.begin(), send_config.rtp.ssrcs.insert(
ssrcs.end()); send_config.rtp.ssrcs.begin(), ssrcs.begin(), ssrcs.end());
if (rtx) { if (rtx) {
send_config.rtp.rtx.rtx_payload_type = 96; send_config.rtp.rtx.payload_type = 96;
send_config.rtp.rtx.ssrcs.insert(send_config.rtp.rtx.ssrcs.begin(), send_config.rtp.rtx.ssrcs.insert(send_config.rtp.rtx.ssrcs.begin(),
kRtxSsrcs, kRtxSsrcs,
kRtxSsrcs + kNumberOfStreams); kRtxSsrcs + kNumberOfStreams);
@ -244,16 +267,10 @@ class RampUpTest : public ::testing::TestWithParam<bool> {
std::map<uint32_t, bool> reserved_ssrcs_; std::map<uint32_t, bool> reserved_ssrcs_;
}; };
TEST_F(RampUpTest, WithoutPacing) { TEST_F(RampUpTest, WithoutPacing) { RunRampUpTest(false, false); }
RunRampUpTest(false, false);
}
TEST_F(RampUpTest, WithPacing) { TEST_F(RampUpTest, WithPacing) { RunRampUpTest(true, false); }
RunRampUpTest(true, false);
}
TEST_F(RampUpTest, WithPacingAndRtx) { TEST_F(RampUpTest, WithPacingAndRtx) { RunRampUpTest(true, true); }
RunRampUpTest(true, true);
}
} // namespace webrtc } // namespace webrtc

View File

@ -59,10 +59,21 @@ VideoReceiveStream::VideoReceiveStream(webrtc::VideoEngine* video_engine,
} }
assert(config_.rtp.remote_ssrc != 0); assert(config_.rtp.remote_ssrc != 0);
// TODO(pbos): What's an appropriate local_ssrc for receive-only streams?
assert(config_.rtp.local_ssrc != 0); assert(config_.rtp.local_ssrc != 0);
assert(config_.rtp.remote_ssrc != config_.rtp.local_ssrc); assert(config_.rtp.remote_ssrc != config_.rtp.local_ssrc);
rtp_rtcp_->SetLocalSSRC(channel_, config_.rtp.local_ssrc); rtp_rtcp_->SetLocalSSRC(channel_, config_.rtp.local_ssrc);
// TODO(pbos): Support multiple RTX, per video payload.
Config::Rtp::RtxMap::const_iterator it = config_.rtp.rtx.begin();
if (it != config_.rtp.rtx.end()) {
assert(it->second.ssrc != 0);
assert(it->second.payload_type != 0);
rtp_rtcp_->SetRemoteSSRCType(channel_, kViEStreamTypeRtx, it->second.ssrc);
rtp_rtcp_->SetRtxReceivePayloadType(channel_, it->second.payload_type);
}
rtp_rtcp_->SetRembStatus(channel_, false, config_.rtp.remb); rtp_rtcp_->SetRembStatus(channel_, false, config_.rtp.remb);
for (size_t i = 0; i < config_.rtp.extensions.size(); ++i) { for (size_t i = 0; i < config_.rtp.extensions.size(); ++i) {
@ -102,8 +113,7 @@ VideoReceiveStream::VideoReceiveStream(webrtc::VideoEngine* video_engine,
decoder->payload_type, decoder->payload_type,
decoder->decoder, decoder->decoder,
decoder->renderer, decoder->renderer,
decoder->expected_delay_ms) != decoder->expected_delay_ms) != 0) {
0) {
// TODO(pbos): Abort gracefully? Can this be a runtime error? // TODO(pbos): Abort gracefully? Can this be a runtime error?
abort(); abort();
} }
@ -182,8 +192,7 @@ bool VideoReceiveStream::DeliverRtcp(const uint8_t* packet, size_t length) {
bool VideoReceiveStream::DeliverRtp(const uint8_t* packet, size_t length) { bool VideoReceiveStream::DeliverRtp(const uint8_t* packet, size_t length) {
return network_->ReceivedRTPPacket( return network_->ReceivedRTPPacket(
channel_, packet, static_cast<int>(length), channel_, packet, static_cast<int>(length), PacketTime()) == 0;
PacketTime()) == 0;
} }
int32_t VideoReceiveStream::RenderFrame(const uint32_t stream_id, int32_t VideoReceiveStream::RenderFrame(const uint32_t stream_id,

View File

@ -156,9 +156,11 @@ VideoSendStream::VideoSendStream(newapi::Transport* transport,
if (config.encoder) { if (config.encoder) {
external_codec_ = ViEExternalCodec::GetInterface(video_engine); external_codec_ = ViEExternalCodec::GetInterface(video_engine);
if (external_codec_->RegisterExternalSendCodec( if (external_codec_->RegisterExternalSendCodec(channel_,
channel_, config.codec.plType, config.encoder, config.codec.plType,
config.internal_source) != 0) { config.encoder,
config.internal_source) !=
0) {
abort(); abort();
} }
} }
@ -168,9 +170,8 @@ VideoSendStream::VideoSendStream(newapi::Transport* transport,
abort(); abort();
if (overuse_detection) { if (overuse_detection) {
overuse_observer_.reset( overuse_observer_.reset(new ResolutionAdaptor(
new ResolutionAdaptor(codec_, channel_, config_.codec.width, codec_, channel_, config_.codec.width, config_.codec.height));
config_.codec.height));
video_engine_base_->RegisterCpuOveruseObserver(channel_, video_engine_base_->RegisterCpuOveruseObserver(channel_,
overuse_observer_.get()); overuse_observer_.get());
} }
@ -187,8 +188,7 @@ VideoSendStream::VideoSendStream(newapi::Transport* transport,
codec_->SuspendBelowMinBitrate(channel_); codec_->SuspendBelowMinBitrate(channel_);
} }
stats_proxy_.reset( stats_proxy_.reset(new SendStatisticsProxy(config, this));
new SendStatisticsProxy(config, this));
rtp_rtcp_->RegisterSendChannelRtcpStatisticsCallback(channel_, rtp_rtcp_->RegisterSendChannelRtcpStatisticsCallback(channel_,
stats_proxy_.get()); stats_proxy_.get());
@ -282,7 +282,9 @@ bool VideoSendStream::SetCodec(const VideoCodec& codec) {
static_cast<unsigned char>(i)); static_cast<unsigned char>(i));
} }
config_.codec = codec; if (&config_.codec != &codec)
config_.codec = codec;
if (config_.rtp.rtx.ssrcs.empty()) if (config_.rtp.rtx.ssrcs.empty())
return true; return true;
@ -295,10 +297,8 @@ bool VideoSendStream::SetCodec(const VideoCodec& codec) {
static_cast<unsigned char>(i)); static_cast<unsigned char>(i));
} }
if (config_.rtp.rtx.rtx_payload_type != 0) { if (config_.rtp.rtx.payload_type != 0)
rtp_rtcp_->SetRtxSendPayloadType(channel_, rtp_rtcp_->SetRtxSendPayloadType(channel_, config_.rtp.rtx.payload_type);
config_.rtp.rtx.rtx_payload_type);
}
return true; return true;
} }

View File

@ -93,8 +93,8 @@ const uint8_t VideoSendStreamTest::kSendPayloadType = 100;
const uint8_t VideoSendStreamTest::kFakeSendPayloadType = 125; const uint8_t VideoSendStreamTest::kFakeSendPayloadType = 125;
const uint8_t VideoSendStreamTest::kSendRtxPayloadType = 98; const uint8_t VideoSendStreamTest::kSendRtxPayloadType = 98;
const uint32_t VideoSendStreamTest::kSendRtxSsrc = 0xBADCAFE; const uint32_t VideoSendStreamTest::kSendRtxSsrc = 0xBADCAFE;
const uint32_t VideoSendStreamTest::kSendSsrcs[kNumSendSsrcs] = { 0xC0FFED, const uint32_t VideoSendStreamTest::kSendSsrcs[kNumSendSsrcs] = {
0xC0FFEE, 0xC0FFEF }; 0xC0FFED, 0xC0FFEE, 0xC0FFEF};
const uint32_t VideoSendStreamTest::kSendSsrc = const uint32_t VideoSendStreamTest::kSendSsrc =
VideoSendStreamTest::kSendSsrcs[0]; VideoSendStreamTest::kSendSsrcs[0];
@ -121,10 +121,10 @@ void VideoSendStreamTest::SendsSetSsrcs(size_t num_ssrcs,
// to fail on TSan as the codec gets set before the SSRCs are // to fail on TSan as the codec gets set before the SSRCs are
// set up and some frames are sent on a random-generated SSRC // set up and some frames are sent on a random-generated SSRC
// before the correct SSRC gets set. // before the correct SSRC gets set.
//EXPECT_TRUE(valid_ssrcs_[header.ssrc]) // EXPECT_TRUE(valid_ssrcs_[header.ssrc])
// << "Received unknown SSRC: " << header.ssrc; // << "Received unknown SSRC: " << header.ssrc;
// //
//if (!valid_ssrcs_[header.ssrc]) // if (!valid_ssrcs_[header.ssrc])
// observation_complete_->Set(); // observation_complete_->Set();
if (!is_observed_[header.ssrc]) { if (!is_observed_[header.ssrc]) {
@ -271,8 +271,7 @@ TEST_F(VideoSendStreamTest, SupportsAbsoluteSendTime) {
virtual Action OnSendRtp(const uint8_t* packet, size_t length) OVERRIDE { virtual Action OnSendRtp(const uint8_t* packet, size_t length) OVERRIDE {
RTPHeader header; RTPHeader header;
EXPECT_TRUE( EXPECT_TRUE(parser_->Parse(packet, static_cast<int>(length), &header));
parser_->Parse(packet, static_cast<int>(length), &header));
EXPECT_FALSE(header.extension.hasTransmissionTimeOffset); EXPECT_FALSE(header.extension.hasTransmissionTimeOffset);
EXPECT_TRUE(header.extension.hasAbsoluteSendTime); EXPECT_TRUE(header.extension.hasAbsoluteSendTime);
@ -299,10 +298,10 @@ TEST_F(VideoSendStreamTest, SupportsTransmissionTimeOffset) {
class DelayedEncoder : public test::FakeEncoder { class DelayedEncoder : public test::FakeEncoder {
public: public:
explicit DelayedEncoder(Clock* clock) : test::FakeEncoder(clock) {} explicit DelayedEncoder(Clock* clock) : test::FakeEncoder(clock) {}
virtual int32_t Encode( virtual int32_t Encode(const I420VideoFrame& input_image,
const I420VideoFrame& input_image, const CodecSpecificInfo* codec_specific_info,
const CodecSpecificInfo* codec_specific_info, const std::vector<VideoFrameType>* frame_types)
const std::vector<VideoFrameType>* frame_types) OVERRIDE { OVERRIDE {
// A delay needs to be introduced to assure that we get a timestamp // A delay needs to be introduced to assure that we get a timestamp
// offset. // offset.
SleepMs(5); SleepMs(5);
@ -319,8 +318,7 @@ TEST_F(VideoSendStreamTest, SupportsTransmissionTimeOffset) {
virtual Action OnSendRtp(const uint8_t* packet, size_t length) OVERRIDE { virtual Action OnSendRtp(const uint8_t* packet, size_t length) OVERRIDE {
RTPHeader header; RTPHeader header;
EXPECT_TRUE( EXPECT_TRUE(parser_->Parse(packet, static_cast<int>(length), &header));
parser_->Parse(packet, static_cast<int>(length), &header));
EXPECT_TRUE(header.extension.hasTransmissionTimeOffset); EXPECT_TRUE(header.extension.hasTransmissionTimeOffset);
EXPECT_FALSE(header.extension.hasAbsoluteSendTime); EXPECT_FALSE(header.extension.hasAbsoluteSendTime);
@ -439,8 +437,7 @@ TEST_F(VideoSendStreamTest, SupportsFec) {
virtual Action OnSendRtp(const uint8_t* packet, size_t length) OVERRIDE { virtual Action OnSendRtp(const uint8_t* packet, size_t length) OVERRIDE {
RTPHeader header; RTPHeader header;
EXPECT_TRUE( EXPECT_TRUE(parser_->Parse(packet, static_cast<int>(length), &header));
parser_->Parse(packet, static_cast<int>(length), &header));
// Send lossy receive reports to trigger FEC enabling. // Send lossy receive reports to trigger FEC enabling.
if (send_count_++ % 2 != 0) { if (send_count_++ % 2 != 0) {
@ -511,8 +508,7 @@ void VideoSendStreamTest::TestNackRetransmission(
virtual Action OnSendRtp(const uint8_t* packet, size_t length) OVERRIDE { virtual Action OnSendRtp(const uint8_t* packet, size_t length) OVERRIDE {
RTPHeader header; RTPHeader header;
EXPECT_TRUE( EXPECT_TRUE(parser_->Parse(packet, static_cast<int>(length), &header));
parser_->Parse(packet, static_cast<int>(length), &header));
// Nack second packet after receiving the third one. // Nack second packet after receiving the third one.
if (++send_count_ == 3) { if (++send_count_ == 3) {
@ -564,7 +560,7 @@ void VideoSendStreamTest::TestNackRetransmission(
VideoSendStream::Config send_config = GetSendTestConfig(call.get(), 1); VideoSendStream::Config send_config = GetSendTestConfig(call.get(), 1);
send_config.rtp.nack.rtp_history_ms = 1000; send_config.rtp.nack.rtp_history_ms = 1000;
send_config.rtp.rtx.rtx_payload_type = retransmit_payload_type; send_config.rtp.rtx.payload_type = retransmit_payload_type;
send_config.pacing = enable_pacing; send_config.pacing = enable_pacing;
if (retransmit_ssrc != kSendSsrc) if (retransmit_ssrc != kSendSsrc)
send_config.rtp.rtx.ssrcs.push_back(retransmit_ssrc); send_config.rtp.rtx.ssrcs.push_back(retransmit_ssrc);
@ -611,8 +607,7 @@ TEST_F(VideoSendStreamTest, FragmentsAccordingToMaxPacketSize) {
virtual Action OnSendRtp(const uint8_t* packet, size_t length) OVERRIDE { virtual Action OnSendRtp(const uint8_t* packet, size_t length) OVERRIDE {
RTPHeader header; RTPHeader header;
EXPECT_TRUE( EXPECT_TRUE(parser_->Parse(packet, static_cast<int>(length), &header));
parser_->Parse(packet, static_cast<int>(length), &header));
EXPECT_LE(length, max_packet_size_); EXPECT_LE(length, max_packet_size_);
@ -721,9 +716,7 @@ TEST_F(VideoSendStreamTest, CanChangeSendCodec) {
return kEventSignaled; return kEventSignaled;
} }
void SetSecondCodec(const VideoCodec& codec) { void SetSecondCodec(const VideoCodec& codec) { second_codec_ = codec; }
second_codec_ = codec;
}
private: private:
scoped_ptr<EventWrapper> received_first_payload_; scoped_ptr<EventWrapper> received_first_payload_;
@ -913,8 +906,9 @@ TEST_F(VideoSendStreamTest, NoPaddingWhenVideoIsMuted) {
virtual Action OnSendRtcp(const uint8_t* packet, size_t length) OVERRIDE { virtual Action OnSendRtcp(const uint8_t* packet, size_t length) OVERRIDE {
CriticalSectionScoped lock(crit_sect_.get()); CriticalSectionScoped lock(crit_sect_.get());
const int kVideoMutedThresholdMs = 10000; const int kVideoMutedThresholdMs = 10000;
if (last_packet_time_ms_ > 0 && clock_->TimeInMilliseconds() - if (last_packet_time_ms_ > 0 &&
last_packet_time_ms_ > kVideoMutedThresholdMs) clock_->TimeInMilliseconds() - last_packet_time_ms_ >
kVideoMutedThresholdMs)
observation_complete_->Set(); observation_complete_->Set();
// Receive statistics reporting having lost 50% of the packets. // Receive statistics reporting having lost 50% of the packets.
FakeReceiveStatistics receive_stats(kSendSsrcs[0], 1, 1, 0); FakeReceiveStatistics receive_stats(kSendSsrcs[0], 1, 1, 0);

View File

@ -26,10 +26,7 @@ namespace webrtc {
namespace newapi { namespace newapi {
// RTCP mode to use. Compound mode is described by RFC 4585 and reduced-size // RTCP mode to use. Compound mode is described by RFC 4585 and reduced-size
// RTCP mode is described by RFC 5506. // RTCP mode is described by RFC 5506.
enum RtcpMode { enum RtcpMode { kRtcpCompound, kRtcpReducedSize };
kRtcpCompound,
kRtcpReducedSize
};
} // namespace newapi } // namespace newapi
class VideoDecoder; class VideoDecoder;
@ -138,9 +135,21 @@ class VideoReceiveStream {
// See FecConfig for description. // See FecConfig for description.
FecConfig fec; FecConfig fec;
// RTX settings for video payloads that may be received. RTX is disabled // RTX settings for incoming video payloads that may be received. RTX is
// if there's no config present. // disabled if there's no config present.
std::map<int, RtxConfig> rtx; struct Rtx {
Rtx() : ssrc(0), payload_type(0) {}
// SSRCs to use for the RTX streams.
uint32_t ssrc;
// Payload type to use for the RTX stream.
int payload_type;
};
// Map from video RTP payload type -> RTX config.
typedef std::map<int, Rtx> RtxMap;
RtxMap rtx;
// RTP header extensions used for the received stream. // RTP header extensions used for the received stream.
std::vector<RtpExtension> extensions; std::vector<RtpExtension> extensions;

View File

@ -96,8 +96,16 @@ class VideoSendStream {
// See FecConfig for description. // See FecConfig for description.
FecConfig fec; FecConfig fec;
// See RtxConfig for description. // Settings for RTP retransmission payload format, see RFC 4588 for
RtxConfig rtx; // details.
struct Rtx {
Rtx() : payload_type(0) {}
// SSRCs to use for the RTX streams.
std::vector<uint32_t> ssrcs;
// Payload type to use for the RTX stream.
int payload_type;
} rtx;
// RTCP CNAME, see RFC 3550. // RTCP CNAME, see RFC 3550.
std::string c_name; std::string c_name;