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:
@ -71,16 +71,6 @@ struct FecConfig {
|
||||
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.
|
||||
struct RtpExtension {
|
||||
static const char* kTOffset;
|
||||
|
@ -13,6 +13,8 @@
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
#include "testing/gtest/include/gtest/gtest.h"
|
||||
|
||||
#include "webrtc/modules/rtp_rtcp/interface/rtp_header_parser.h"
|
||||
#include "webrtc/typedefs.h"
|
||||
#include "webrtc/video_send_stream.h"
|
||||
@ -123,6 +125,7 @@ class RtpRtcpObserver {
|
||||
|
||||
private:
|
||||
virtual bool SendRtp(const uint8_t* packet, size_t length) OVERRIDE {
|
||||
EXPECT_FALSE(RtpHeaderParser::IsRtcp(packet, static_cast<int>(length)));
|
||||
Action action;
|
||||
{
|
||||
CriticalSectionScoped crit_(lock_);
|
||||
@ -139,6 +142,7 @@ class RtpRtcpObserver {
|
||||
}
|
||||
|
||||
virtual bool SendRtcp(const uint8_t* packet, size_t length) OVERRIDE {
|
||||
EXPECT_TRUE(RtpHeaderParser::IsRtcp(packet, static_cast<int>(length)));
|
||||
Action action;
|
||||
{
|
||||
CriticalSectionScoped crit_(lock_);
|
||||
|
@ -170,8 +170,9 @@ void CreateTraceDispatcher() {
|
||||
Call* Call::Create(const Call::Config& config) {
|
||||
CreateTraceDispatcher();
|
||||
|
||||
VideoEngine* video_engine = config.webrtc_config != NULL ?
|
||||
VideoEngine::Create(*config.webrtc_config) : VideoEngine::Create();
|
||||
VideoEngine* video_engine = config.webrtc_config != NULL
|
||||
? VideoEngine::Create(*config.webrtc_config)
|
||||
: VideoEngine::Create();
|
||||
assert(video_engine != NULL);
|
||||
|
||||
return new internal::Call(video_engine, config);
|
||||
@ -294,6 +295,12 @@ VideoReceiveStream* Call::CreateVideoReceiveStream(
|
||||
WriteLockScoped write_lock(*receive_lock_);
|
||||
assert(receive_ssrcs_.find(config.rtp.remote_ssrc) == receive_ssrcs_.end());
|
||||
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;
|
||||
}
|
||||
|
||||
@ -304,14 +311,16 @@ void Call::DestroyVideoReceiveStream(
|
||||
VideoReceiveStream* receive_stream_impl = NULL;
|
||||
{
|
||||
WriteLockScoped write_lock(*receive_lock_);
|
||||
for (std::map<uint32_t, VideoReceiveStream*>::iterator it =
|
||||
receive_ssrcs_.begin();
|
||||
it != receive_ssrcs_.end();
|
||||
++it) {
|
||||
// Remove all ssrcs pointing to a receive stream. As RTX retransmits on a
|
||||
// separate SSRC there can be either one or two.
|
||||
std::map<uint32_t, VideoReceiveStream*>::iterator it =
|
||||
receive_ssrcs_.begin();
|
||||
while (it != receive_ssrcs_.end()) {
|
||||
if (it->second == static_cast<VideoReceiveStream*>(receive_stream)) {
|
||||
receive_stream_impl = it->second;
|
||||
receive_ssrcs_.erase(it);
|
||||
break;
|
||||
receive_ssrcs_.erase(it++);
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -18,7 +18,6 @@
|
||||
|
||||
#include "webrtc/call.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/system_wrappers/interface/critical_section_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 kLongTimeoutMs = 120 * 1000;
|
||||
static const uint32_t kSendSsrc = 0x654321;
|
||||
static const uint32_t kSendRtxSsrc = 0x424242;
|
||||
static const uint32_t kReceiverLocalSsrc = 0x123456;
|
||||
static const uint8_t kSendPayloadType = 125;
|
||||
static const uint8_t kSendRtxPayloadType = 126;
|
||||
|
||||
class CallTest : public ::testing::Test {
|
||||
public:
|
||||
@ -125,6 +126,7 @@ class CallTest : public ::testing::Test {
|
||||
receive_stream_ = NULL;
|
||||
}
|
||||
|
||||
void DecodesRetransmittedFrame(bool retransmit_over_rtx);
|
||||
void ReceivesPliAndRecovers(int rtp_history_ms);
|
||||
void RespectsRtcpMode(newapi::RtcpMode rtcp_mode);
|
||||
void TestXrReceiverReferenceTimeReport(bool enable_rrtr);
|
||||
@ -159,8 +161,6 @@ class NackObserver : public test::RtpRtcpObserver {
|
||||
|
||||
private:
|
||||
virtual Action OnSendRtp(const uint8_t* packet, size_t length) OVERRIDE {
|
||||
EXPECT_FALSE(RtpHeaderParser::IsRtcp(packet, static_cast<int>(length)));
|
||||
|
||||
RTPHeader 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); }
|
||||
|
||||
private:
|
||||
virtual void FrameCallback(I420VideoFrame* frame) {
|
||||
virtual void FrameCallback(I420VideoFrame* frame) OVERRIDE {
|
||||
SleepMs(kDelayRenderCallbackMs);
|
||||
event_->Set();
|
||||
}
|
||||
@ -485,6 +485,99 @@ TEST_F(CallTest, ReceivesAndRetransmitsNack) {
|
||||
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) {
|
||||
static const int kWidth = 320;
|
||||
static const int kHeight = 240;
|
||||
@ -588,7 +681,6 @@ class PliObserver : public test::RtpRtcpObserver, public VideoRenderer {
|
||||
public:
|
||||
explicit PliObserver(bool nack_enabled)
|
||||
: test::RtpRtcpObserver(kLongTimeoutMs),
|
||||
rtp_header_parser_(RtpHeaderParser::Create()),
|
||||
nack_enabled_(nack_enabled),
|
||||
highest_dropped_timestamp_(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 {
|
||||
RTPHeader header;
|
||||
EXPECT_TRUE(
|
||||
rtp_header_parser_->Parse(packet, static_cast<int>(length), &header));
|
||||
EXPECT_TRUE(parser_->Parse(packet, static_cast<int>(length), &header));
|
||||
|
||||
// Drop all retransmitted packets to force a PLI.
|
||||
if (header.timestamp <= highest_dropped_timestamp_)
|
||||
@ -643,7 +734,6 @@ class PliObserver : public test::RtpRtcpObserver, public VideoRenderer {
|
||||
private:
|
||||
static const int kPacketsToDrop = 1;
|
||||
|
||||
scoped_ptr<RtpHeaderParser> rtp_header_parser_;
|
||||
bool nack_enabled_;
|
||||
uint32_t highest_dropped_timestamp_;
|
||||
int frames_to_drop_;
|
||||
@ -933,9 +1023,10 @@ TEST_F(CallTest, SendsAndReceivesMultipleStreams) {
|
||||
TEST_F(CallTest, ObserversEncodedFrames) {
|
||||
class EncodedFrameTestObserver : public EncodedFrameObserver {
|
||||
public:
|
||||
EncodedFrameTestObserver() : length_(0),
|
||||
frame_type_(kFrameEmpty),
|
||||
called_(EventWrapper::Create()) {}
|
||||
EncodedFrameTestObserver()
|
||||
: length_(0),
|
||||
frame_type_(kFrameEmpty),
|
||||
called_(EventWrapper::Create()) {}
|
||||
virtual ~EncodedFrameTestObserver() {}
|
||||
|
||||
virtual void EncodedFrameCallback(const EncodedFrame& encoded_frame) {
|
||||
@ -946,9 +1037,7 @@ TEST_F(CallTest, ObserversEncodedFrames) {
|
||||
called_->Set();
|
||||
}
|
||||
|
||||
EventTypeWrapper Wait() {
|
||||
return called_->Wait(kDefaultTimeoutMs);
|
||||
}
|
||||
EventTypeWrapper Wait() { return called_->Wait(kDefaultTimeoutMs); }
|
||||
|
||||
void ExpectEqualFrames(const EncodedFrameTestObserver& observer) {
|
||||
ASSERT_EQ(length_, observer.length_)
|
||||
@ -1064,6 +1153,7 @@ void CallTest::TestXrReceiverReferenceTimeReport(bool enable_rrtr) {
|
||||
sent_rtcp_rr_(0),
|
||||
sent_rtcp_rrtr_(0),
|
||||
sent_rtcp_dlrr_(0) {}
|
||||
|
||||
private:
|
||||
// Receive stream should send RR packets (and RRTR packets if enabled).
|
||||
virtual Action OnReceiveRtcp(const uint8_t* packet,
|
||||
@ -1075,8 +1165,8 @@ void CallTest::TestXrReceiverReferenceTimeReport(bool enable_rrtr) {
|
||||
while (packet_type != RTCPUtility::kRtcpNotValidCode) {
|
||||
if (packet_type == RTCPUtility::kRtcpRrCode) {
|
||||
++sent_rtcp_rr_;
|
||||
} else if (
|
||||
packet_type == RTCPUtility::kRtcpXrReceiverReferenceTimeCode) {
|
||||
} else if (packet_type ==
|
||||
RTCPUtility::kRtcpXrReceiverReferenceTimeCode) {
|
||||
++sent_rtcp_rrtr_;
|
||||
}
|
||||
EXPECT_NE(packet_type, RTCPUtility::kRtcpSrCode);
|
||||
@ -1122,8 +1212,7 @@ void CallTest::TestXrReceiverReferenceTimeReport(bool enable_rrtr) {
|
||||
|
||||
CreateCalls(Call::Config(observer.SendTransport()),
|
||||
Call::Config(observer.ReceiveTransport()));
|
||||
observer.SetReceivers(receiver_call_->Receiver(),
|
||||
sender_call_->Receiver());
|
||||
observer.SetReceivers(receiver_call_->Receiver(), sender_call_->Receiver());
|
||||
|
||||
CreateTestConfigs();
|
||||
receive_config_.rtp.rtcp_mode = newapi::kRtcpReducedSize;
|
||||
|
@ -36,8 +36,8 @@
|
||||
namespace webrtc {
|
||||
|
||||
namespace {
|
||||
static const int kAbsoluteSendTimeExtensionId = 7;
|
||||
static const int kMaxPacketSize = 1500;
|
||||
static const int kAbsoluteSendTimeExtensionId = 7;
|
||||
static const int kMaxPacketSize = 1500;
|
||||
}
|
||||
|
||||
class StreamObserver : public newapi::Transport, public RemoteBitrateObserver {
|
||||
@ -53,8 +53,9 @@ class StreamObserver : public newapi::Transport, public RemoteBitrateObserver {
|
||||
rtp_parser_(RtpHeaderParser::Create()),
|
||||
feedback_transport_(feedback_transport),
|
||||
receive_stats_(ReceiveStatistics::Create(clock)),
|
||||
payload_registry_(new RTPPayloadRegistry(
|
||||
-1, RTPPayloadStrategy::CreateStrategy(false))),
|
||||
payload_registry_(
|
||||
new RTPPayloadRegistry(-1,
|
||||
RTPPayloadStrategy::CreateStrategy(false))),
|
||||
clock_(clock),
|
||||
num_expected_ssrcs_(num_expected_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 (rtx_media_ssrcs_.empty() || rtx_media_sent_ > 0) {
|
||||
const ::testing::TestInfo* const test_info =
|
||||
::testing::UnitTest::GetInstance()->current_test_info();
|
||||
webrtc::test::PrintResult("total-sent", "", test_info->name(),
|
||||
total_sent_, "bytes", false);
|
||||
webrtc::test::PrintResult("padding-sent", "", test_info->name(),
|
||||
padding_sent_, "bytes", false);
|
||||
webrtc::test::PrintResult("rtx-media-sent", "", test_info->name(),
|
||||
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);
|
||||
::testing::UnitTest::GetInstance()->current_test_info();
|
||||
webrtc::test::PrintResult(
|
||||
"total-sent", "", test_info->name(), total_sent_, "bytes", false);
|
||||
webrtc::test::PrintResult("padding-sent",
|
||||
"",
|
||||
test_info->name(),
|
||||
padding_sent_,
|
||||
"bytes",
|
||||
false);
|
||||
webrtc::test::PrintResult("rtx-media-sent",
|
||||
"",
|
||||
test_info->name(),
|
||||
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();
|
||||
}
|
||||
}
|
||||
@ -132,13 +153,14 @@ class StreamObserver : public newapi::Transport, public RemoteBitrateObserver {
|
||||
uint8_t restored_packet[kMaxPacketSize];
|
||||
uint8_t* restored_packet_ptr = restored_packet;
|
||||
int restored_length = static_cast<int>(length);
|
||||
payload_registry_->RestoreOriginalPacket(
|
||||
&restored_packet_ptr, packet, &restored_length,
|
||||
rtx_media_ssrcs_[header.ssrc],
|
||||
header);
|
||||
payload_registry_->RestoreOriginalPacket(&restored_packet_ptr,
|
||||
packet,
|
||||
&restored_length,
|
||||
rtx_media_ssrcs_[header.ssrc],
|
||||
header);
|
||||
length = restored_length;
|
||||
EXPECT_TRUE(rtp_parser_->Parse(restored_packet, static_cast<int>(length),
|
||||
&header));
|
||||
EXPECT_TRUE(rtp_parser_->Parse(
|
||||
restored_packet, static_cast<int>(length), &header));
|
||||
} else {
|
||||
rtp_rtcp_->SetRemoteSSRC(header.ssrc);
|
||||
}
|
||||
@ -191,9 +213,10 @@ class RampUpTest : public ::testing::TestWithParam<bool> {
|
||||
}
|
||||
test::DirectTransport receiver_transport;
|
||||
int num_expected_ssrcs = kNumberOfStreams + (rtx ? 1 : 0);
|
||||
StreamObserver stream_observer(
|
||||
num_expected_ssrcs, rtx_ssrc_map, &receiver_transport,
|
||||
Clock::GetRealTimeClock());
|
||||
StreamObserver stream_observer(num_expected_ssrcs,
|
||||
rtx_ssrc_map,
|
||||
&receiver_transport,
|
||||
Clock::GetRealTimeClock());
|
||||
|
||||
Call::Config call_config(&stream_observer);
|
||||
webrtc::Config webrtc_config;
|
||||
@ -211,10 +234,10 @@ class RampUpTest : public ::testing::TestWithParam<bool> {
|
||||
send_config.codec.plType = 125;
|
||||
send_config.pacing = pacing;
|
||||
send_config.rtp.nack.rtp_history_ms = 1000;
|
||||
send_config.rtp.ssrcs.insert(send_config.rtp.ssrcs.begin(), ssrcs.begin(),
|
||||
ssrcs.end());
|
||||
send_config.rtp.ssrcs.insert(
|
||||
send_config.rtp.ssrcs.begin(), ssrcs.begin(), ssrcs.end());
|
||||
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(),
|
||||
kRtxSsrcs,
|
||||
kRtxSsrcs + kNumberOfStreams);
|
||||
@ -244,16 +267,10 @@ class RampUpTest : public ::testing::TestWithParam<bool> {
|
||||
std::map<uint32_t, bool> reserved_ssrcs_;
|
||||
};
|
||||
|
||||
TEST_F(RampUpTest, WithoutPacing) {
|
||||
RunRampUpTest(false, false);
|
||||
}
|
||||
TEST_F(RampUpTest, WithoutPacing) { RunRampUpTest(false, false); }
|
||||
|
||||
TEST_F(RampUpTest, WithPacing) {
|
||||
RunRampUpTest(true, false);
|
||||
}
|
||||
TEST_F(RampUpTest, WithPacing) { RunRampUpTest(true, false); }
|
||||
|
||||
TEST_F(RampUpTest, WithPacingAndRtx) {
|
||||
RunRampUpTest(true, true);
|
||||
}
|
||||
TEST_F(RampUpTest, WithPacingAndRtx) { RunRampUpTest(true, true); }
|
||||
|
||||
} // namespace webrtc
|
||||
|
@ -59,10 +59,21 @@ VideoReceiveStream::VideoReceiveStream(webrtc::VideoEngine* video_engine,
|
||||
}
|
||||
|
||||
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.remote_ssrc != 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);
|
||||
|
||||
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->decoder,
|
||||
decoder->renderer,
|
||||
decoder->expected_delay_ms) !=
|
||||
0) {
|
||||
decoder->expected_delay_ms) != 0) {
|
||||
// TODO(pbos): Abort gracefully? Can this be a runtime error?
|
||||
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) {
|
||||
return network_->ReceivedRTPPacket(
|
||||
channel_, packet, static_cast<int>(length),
|
||||
PacketTime()) == 0;
|
||||
channel_, packet, static_cast<int>(length), PacketTime()) == 0;
|
||||
}
|
||||
|
||||
int32_t VideoReceiveStream::RenderFrame(const uint32_t stream_id,
|
||||
|
@ -156,9 +156,11 @@ VideoSendStream::VideoSendStream(newapi::Transport* transport,
|
||||
|
||||
if (config.encoder) {
|
||||
external_codec_ = ViEExternalCodec::GetInterface(video_engine);
|
||||
if (external_codec_->RegisterExternalSendCodec(
|
||||
channel_, config.codec.plType, config.encoder,
|
||||
config.internal_source) != 0) {
|
||||
if (external_codec_->RegisterExternalSendCodec(channel_,
|
||||
config.codec.plType,
|
||||
config.encoder,
|
||||
config.internal_source) !=
|
||||
0) {
|
||||
abort();
|
||||
}
|
||||
}
|
||||
@ -168,9 +170,8 @@ VideoSendStream::VideoSendStream(newapi::Transport* transport,
|
||||
abort();
|
||||
|
||||
if (overuse_detection) {
|
||||
overuse_observer_.reset(
|
||||
new ResolutionAdaptor(codec_, channel_, config_.codec.width,
|
||||
config_.codec.height));
|
||||
overuse_observer_.reset(new ResolutionAdaptor(
|
||||
codec_, channel_, config_.codec.width, config_.codec.height));
|
||||
video_engine_base_->RegisterCpuOveruseObserver(channel_,
|
||||
overuse_observer_.get());
|
||||
}
|
||||
@ -187,8 +188,7 @@ VideoSendStream::VideoSendStream(newapi::Transport* transport,
|
||||
codec_->SuspendBelowMinBitrate(channel_);
|
||||
}
|
||||
|
||||
stats_proxy_.reset(
|
||||
new SendStatisticsProxy(config, this));
|
||||
stats_proxy_.reset(new SendStatisticsProxy(config, this));
|
||||
|
||||
rtp_rtcp_->RegisterSendChannelRtcpStatisticsCallback(channel_,
|
||||
stats_proxy_.get());
|
||||
@ -282,7 +282,9 @@ bool VideoSendStream::SetCodec(const VideoCodec& codec) {
|
||||
static_cast<unsigned char>(i));
|
||||
}
|
||||
|
||||
config_.codec = codec;
|
||||
if (&config_.codec != &codec)
|
||||
config_.codec = codec;
|
||||
|
||||
if (config_.rtp.rtx.ssrcs.empty())
|
||||
return true;
|
||||
|
||||
@ -295,10 +297,8 @@ bool VideoSendStream::SetCodec(const VideoCodec& codec) {
|
||||
static_cast<unsigned char>(i));
|
||||
}
|
||||
|
||||
if (config_.rtp.rtx.rtx_payload_type != 0) {
|
||||
rtp_rtcp_->SetRtxSendPayloadType(channel_,
|
||||
config_.rtp.rtx.rtx_payload_type);
|
||||
}
|
||||
if (config_.rtp.rtx.payload_type != 0)
|
||||
rtp_rtcp_->SetRtxSendPayloadType(channel_, config_.rtp.rtx.payload_type);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -93,8 +93,8 @@ const uint8_t VideoSendStreamTest::kSendPayloadType = 100;
|
||||
const uint8_t VideoSendStreamTest::kFakeSendPayloadType = 125;
|
||||
const uint8_t VideoSendStreamTest::kSendRtxPayloadType = 98;
|
||||
const uint32_t VideoSendStreamTest::kSendRtxSsrc = 0xBADCAFE;
|
||||
const uint32_t VideoSendStreamTest::kSendSsrcs[kNumSendSsrcs] = { 0xC0FFED,
|
||||
0xC0FFEE, 0xC0FFEF };
|
||||
const uint32_t VideoSendStreamTest::kSendSsrcs[kNumSendSsrcs] = {
|
||||
0xC0FFED, 0xC0FFEE, 0xC0FFEF};
|
||||
const uint32_t VideoSendStreamTest::kSendSsrc =
|
||||
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
|
||||
// set up and some frames are sent on a random-generated SSRC
|
||||
// before the correct SSRC gets set.
|
||||
//EXPECT_TRUE(valid_ssrcs_[header.ssrc])
|
||||
// EXPECT_TRUE(valid_ssrcs_[header.ssrc])
|
||||
// << "Received unknown SSRC: " << header.ssrc;
|
||||
//
|
||||
//if (!valid_ssrcs_[header.ssrc])
|
||||
// if (!valid_ssrcs_[header.ssrc])
|
||||
// observation_complete_->Set();
|
||||
|
||||
if (!is_observed_[header.ssrc]) {
|
||||
@ -271,8 +271,7 @@ TEST_F(VideoSendStreamTest, SupportsAbsoluteSendTime) {
|
||||
|
||||
virtual Action OnSendRtp(const uint8_t* packet, size_t length) OVERRIDE {
|
||||
RTPHeader header;
|
||||
EXPECT_TRUE(
|
||||
parser_->Parse(packet, static_cast<int>(length), &header));
|
||||
EXPECT_TRUE(parser_->Parse(packet, static_cast<int>(length), &header));
|
||||
|
||||
EXPECT_FALSE(header.extension.hasTransmissionTimeOffset);
|
||||
EXPECT_TRUE(header.extension.hasAbsoluteSendTime);
|
||||
@ -299,10 +298,10 @@ TEST_F(VideoSendStreamTest, SupportsTransmissionTimeOffset) {
|
||||
class DelayedEncoder : public test::FakeEncoder {
|
||||
public:
|
||||
explicit DelayedEncoder(Clock* clock) : test::FakeEncoder(clock) {}
|
||||
virtual int32_t Encode(
|
||||
const I420VideoFrame& input_image,
|
||||
const CodecSpecificInfo* codec_specific_info,
|
||||
const std::vector<VideoFrameType>* frame_types) OVERRIDE {
|
||||
virtual int32_t Encode(const I420VideoFrame& input_image,
|
||||
const CodecSpecificInfo* codec_specific_info,
|
||||
const std::vector<VideoFrameType>* frame_types)
|
||||
OVERRIDE {
|
||||
// A delay needs to be introduced to assure that we get a timestamp
|
||||
// offset.
|
||||
SleepMs(5);
|
||||
@ -319,8 +318,7 @@ TEST_F(VideoSendStreamTest, SupportsTransmissionTimeOffset) {
|
||||
|
||||
virtual Action OnSendRtp(const uint8_t* packet, size_t length) OVERRIDE {
|
||||
RTPHeader header;
|
||||
EXPECT_TRUE(
|
||||
parser_->Parse(packet, static_cast<int>(length), &header));
|
||||
EXPECT_TRUE(parser_->Parse(packet, static_cast<int>(length), &header));
|
||||
|
||||
EXPECT_TRUE(header.extension.hasTransmissionTimeOffset);
|
||||
EXPECT_FALSE(header.extension.hasAbsoluteSendTime);
|
||||
@ -439,8 +437,7 @@ TEST_F(VideoSendStreamTest, SupportsFec) {
|
||||
|
||||
virtual Action OnSendRtp(const uint8_t* packet, size_t length) OVERRIDE {
|
||||
RTPHeader header;
|
||||
EXPECT_TRUE(
|
||||
parser_->Parse(packet, static_cast<int>(length), &header));
|
||||
EXPECT_TRUE(parser_->Parse(packet, static_cast<int>(length), &header));
|
||||
|
||||
// Send lossy receive reports to trigger FEC enabling.
|
||||
if (send_count_++ % 2 != 0) {
|
||||
@ -511,8 +508,7 @@ void VideoSendStreamTest::TestNackRetransmission(
|
||||
|
||||
virtual Action OnSendRtp(const uint8_t* packet, size_t length) OVERRIDE {
|
||||
RTPHeader header;
|
||||
EXPECT_TRUE(
|
||||
parser_->Parse(packet, static_cast<int>(length), &header));
|
||||
EXPECT_TRUE(parser_->Parse(packet, static_cast<int>(length), &header));
|
||||
|
||||
// Nack second packet after receiving the third one.
|
||||
if (++send_count_ == 3) {
|
||||
@ -564,7 +560,7 @@ void VideoSendStreamTest::TestNackRetransmission(
|
||||
|
||||
VideoSendStream::Config send_config = GetSendTestConfig(call.get(), 1);
|
||||
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;
|
||||
if (retransmit_ssrc != kSendSsrc)
|
||||
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 {
|
||||
RTPHeader header;
|
||||
EXPECT_TRUE(
|
||||
parser_->Parse(packet, static_cast<int>(length), &header));
|
||||
EXPECT_TRUE(parser_->Parse(packet, static_cast<int>(length), &header));
|
||||
|
||||
EXPECT_LE(length, max_packet_size_);
|
||||
|
||||
@ -721,9 +716,7 @@ TEST_F(VideoSendStreamTest, CanChangeSendCodec) {
|
||||
return kEventSignaled;
|
||||
}
|
||||
|
||||
void SetSecondCodec(const VideoCodec& codec) {
|
||||
second_codec_ = codec;
|
||||
}
|
||||
void SetSecondCodec(const VideoCodec& codec) { second_codec_ = codec; }
|
||||
|
||||
private:
|
||||
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 {
|
||||
CriticalSectionScoped lock(crit_sect_.get());
|
||||
const int kVideoMutedThresholdMs = 10000;
|
||||
if (last_packet_time_ms_ > 0 && clock_->TimeInMilliseconds() -
|
||||
last_packet_time_ms_ > kVideoMutedThresholdMs)
|
||||
if (last_packet_time_ms_ > 0 &&
|
||||
clock_->TimeInMilliseconds() - last_packet_time_ms_ >
|
||||
kVideoMutedThresholdMs)
|
||||
observation_complete_->Set();
|
||||
// Receive statistics reporting having lost 50% of the packets.
|
||||
FakeReceiveStatistics receive_stats(kSendSsrcs[0], 1, 1, 0);
|
||||
|
@ -26,10 +26,7 @@ namespace webrtc {
|
||||
namespace newapi {
|
||||
// RTCP mode to use. Compound mode is described by RFC 4585 and reduced-size
|
||||
// RTCP mode is described by RFC 5506.
|
||||
enum RtcpMode {
|
||||
kRtcpCompound,
|
||||
kRtcpReducedSize
|
||||
};
|
||||
enum RtcpMode { kRtcpCompound, kRtcpReducedSize };
|
||||
} // namespace newapi
|
||||
|
||||
class VideoDecoder;
|
||||
@ -138,9 +135,21 @@ class VideoReceiveStream {
|
||||
// See FecConfig for description.
|
||||
FecConfig fec;
|
||||
|
||||
// RTX settings for video payloads that may be received. RTX is disabled
|
||||
// if there's no config present.
|
||||
std::map<int, RtxConfig> rtx;
|
||||
// RTX settings for incoming video payloads that may be received. RTX is
|
||||
// disabled if there's no config present.
|
||||
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.
|
||||
std::vector<RtpExtension> extensions;
|
||||
|
@ -96,8 +96,16 @@ class VideoSendStream {
|
||||
// See FecConfig for description.
|
||||
FecConfig fec;
|
||||
|
||||
// See RtxConfig for description.
|
||||
RtxConfig rtx;
|
||||
// Settings for RTP retransmission payload format, see RFC 4588 for
|
||||
// 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.
|
||||
std::string c_name;
|
||||
|
Reference in New Issue
Block a user