Enable FEC for VideoSendStream.
Test only checks for FEC without NACK. Test for FEC with NACK postponed until later. BUG=2230 R=stefan@webrtc.org Review URL: https://webrtc-codereview.appspot.com/2246004 git-svn-id: http://webrtc.googlecode.com/svn/trunk@4802 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
parent
9c74be7bd1
commit
0e63e76781
@ -173,10 +173,8 @@ bool Call::DeliverRtcp(const uint8_t* packet, size_t length) {
|
|||||||
receive_ssrcs_.begin();
|
receive_ssrcs_.begin();
|
||||||
it != receive_ssrcs_.end();
|
it != receive_ssrcs_.end();
|
||||||
++it) {
|
++it) {
|
||||||
if (it->second->DeliverRtcp(static_cast<const uint8_t*>(packet),
|
if (it->second->DeliverRtcp(packet, length))
|
||||||
length)) {
|
|
||||||
rtcp_delivered = true;
|
rtcp_delivered = true;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -186,10 +184,8 @@ bool Call::DeliverRtcp(const uint8_t* packet, size_t length) {
|
|||||||
send_ssrcs_.begin();
|
send_ssrcs_.begin();
|
||||||
it != send_ssrcs_.end();
|
it != send_ssrcs_.end();
|
||||||
++it) {
|
++it) {
|
||||||
if (it->second->DeliverRtcp(static_cast<const uint8_t*>(packet),
|
if (it->second->DeliverRtcp(packet, length))
|
||||||
length)) {
|
|
||||||
rtcp_delivered = true;
|
rtcp_delivered = true;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return rtcp_delivered;
|
return rtcp_delivered;
|
||||||
|
@ -103,7 +103,6 @@ VideoSendStream::VideoSendStream(newapi::Transport* transport,
|
|||||||
kViEStreamTypeNormal, i);
|
kViEStreamTypeNormal, i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
rtp_rtcp_->SetNACKStatus(channel_, config_.rtp.nack.rtp_history_ms > 0);
|
|
||||||
rtp_rtcp_->SetTransmissionSmoothingStatus(channel_, config_.pacing);
|
rtp_rtcp_->SetTransmissionSmoothingStatus(channel_, config_.pacing);
|
||||||
if (!config_.rtp.rtx.ssrcs.empty()) {
|
if (!config_.rtp.rtx.ssrcs.empty()) {
|
||||||
assert(config_.rtp.rtx.ssrcs.size() == config_.rtp.ssrcs.size());
|
assert(config_.rtp.rtx.ssrcs.size() == config_.rtp.ssrcs.size());
|
||||||
@ -132,6 +131,26 @@ VideoSendStream::VideoSendStream(newapi::Transport* transport,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Enable NACK, FEC or both.
|
||||||
|
if (config_.rtp.fec.red_payload_type != -1) {
|
||||||
|
assert(config_.rtp.fec.ulpfec_payload_type != -1);
|
||||||
|
if (config_.rtp.nack.rtp_history_ms > 0) {
|
||||||
|
rtp_rtcp_->SetHybridNACKFECStatus(
|
||||||
|
channel_,
|
||||||
|
true,
|
||||||
|
static_cast<unsigned char>(config_.rtp.fec.red_payload_type),
|
||||||
|
static_cast<unsigned char>(config_.rtp.fec.ulpfec_payload_type));
|
||||||
|
} else {
|
||||||
|
rtp_rtcp_->SetFECStatus(
|
||||||
|
channel_,
|
||||||
|
true,
|
||||||
|
static_cast<unsigned char>(config_.rtp.fec.red_payload_type),
|
||||||
|
static_cast<unsigned char>(config_.rtp.fec.ulpfec_payload_type));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
rtp_rtcp_->SetNACKStatus(channel_, config_.rtp.nack.rtp_history_ms > 0);
|
||||||
|
}
|
||||||
|
|
||||||
char rtcp_cname[ViERTP_RTCP::KMaxRTCPCNameLength];
|
char rtcp_cname[ViERTP_RTCP::KMaxRTCPCNameLength];
|
||||||
assert(config_.rtp.c_name.length() < ViERTP_RTCP::KMaxRTCPCNameLength);
|
assert(config_.rtp.c_name.length() < ViERTP_RTCP::KMaxRTCPCNameLength);
|
||||||
strncpy(rtcp_cname, config_.rtp.c_name.c_str(), sizeof(rtcp_cname) - 1);
|
strncpy(rtcp_cname, config_.rtp.c_name.c_str(), sizeof(rtcp_cname) - 1);
|
||||||
|
@ -15,11 +15,13 @@
|
|||||||
#include "webrtc/system_wrappers/interface/scoped_ptr.h"
|
#include "webrtc/system_wrappers/interface/scoped_ptr.h"
|
||||||
#include "webrtc/system_wrappers/interface/sleep.h"
|
#include "webrtc/system_wrappers/interface/sleep.h"
|
||||||
#include "webrtc/system_wrappers/interface/thread_wrapper.h"
|
#include "webrtc/system_wrappers/interface/thread_wrapper.h"
|
||||||
|
#include "webrtc/video_engine/internal/transport_adapter.h"
|
||||||
|
#include "webrtc/video_engine/new_include/call.h"
|
||||||
|
#include "webrtc/video_engine/new_include/video_send_stream.h"
|
||||||
|
#include "webrtc/video_engine/test/common/direct_transport.h"
|
||||||
#include "webrtc/video_engine/test/common/fake_encoder.h"
|
#include "webrtc/video_engine/test/common/fake_encoder.h"
|
||||||
#include "webrtc/video_engine/test/common/frame_generator_capturer.h"
|
#include "webrtc/video_engine/test/common/frame_generator_capturer.h"
|
||||||
#include "webrtc/video_engine/test/common/null_transport.h"
|
#include "webrtc/video_engine/test/common/null_transport.h"
|
||||||
#include "webrtc/video_engine/new_include/call.h"
|
|
||||||
#include "webrtc/video_engine/new_include/video_send_stream.h"
|
|
||||||
|
|
||||||
namespace webrtc {
|
namespace webrtc {
|
||||||
|
|
||||||
@ -45,8 +47,6 @@ class VideoSendStreamTest : public ::testing::Test {
|
|||||||
VideoSendStreamTest() : fake_encoder_(Clock::GetRealTimeClock()) {}
|
VideoSendStreamTest() : fake_encoder_(Clock::GetRealTimeClock()) {}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
static const uint32_t kSendSsrc;
|
|
||||||
static const uint32_t kSendRtxSsrc;
|
|
||||||
void RunSendTest(Call* call,
|
void RunSendTest(Call* call,
|
||||||
const VideoSendStream::Config& config,
|
const VideoSendStream::Config& config,
|
||||||
SendTransportObserver* observer) {
|
SendTransportObserver* observer) {
|
||||||
@ -75,6 +75,9 @@ class VideoSendStreamTest : public ::testing::Test {
|
|||||||
|
|
||||||
void TestNackRetransmission(uint32_t retransmit_ssrc);
|
void TestNackRetransmission(uint32_t retransmit_ssrc);
|
||||||
|
|
||||||
|
static const uint32_t kSendSsrc;
|
||||||
|
static const uint32_t kSendRtxSsrc;
|
||||||
|
|
||||||
test::FakeEncoder fake_encoder_;
|
test::FakeEncoder fake_encoder_;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -216,62 +219,151 @@ TEST_F(VideoSendStreamTest, SupportsTransmissionTimeOffset) {
|
|||||||
RunSendTest(call.get(), send_config, &observer);
|
RunSendTest(call.get(), send_config, &observer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class LossyReceiveStatistics : public NullReceiveStatistics {
|
||||||
|
public:
|
||||||
|
LossyReceiveStatistics(uint32_t send_ssrc,
|
||||||
|
uint32_t last_sequence_number,
|
||||||
|
uint32_t cumulative_lost,
|
||||||
|
uint8_t fraction_lost)
|
||||||
|
: lossy_stats_(new LossyStatistician(last_sequence_number,
|
||||||
|
cumulative_lost,
|
||||||
|
fraction_lost)) {
|
||||||
|
stats_map_[send_ssrc] = lossy_stats_.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual StatisticianMap GetActiveStatisticians() const OVERRIDE {
|
||||||
|
return stats_map_;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual StreamStatistician* GetStatistician(uint32_t ssrc) const OVERRIDE {
|
||||||
|
return lossy_stats_.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
class LossyStatistician : public StreamStatistician {
|
||||||
|
public:
|
||||||
|
LossyStatistician(uint32_t extended_max_sequence_number,
|
||||||
|
uint32_t cumulative_lost,
|
||||||
|
uint8_t fraction_lost) {
|
||||||
|
stats_.fraction_lost = fraction_lost;
|
||||||
|
stats_.cumulative_lost = cumulative_lost;
|
||||||
|
stats_.extended_max_sequence_number = extended_max_sequence_number;
|
||||||
|
}
|
||||||
|
virtual bool GetStatistics(Statistics* statistics, bool reset) OVERRIDE {
|
||||||
|
*statistics = stats_;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
virtual void GetDataCounters(uint32_t* bytes_received,
|
||||||
|
uint32_t* packets_received) const OVERRIDE {
|
||||||
|
*bytes_received = 0;
|
||||||
|
*packets_received = 0;
|
||||||
|
}
|
||||||
|
virtual uint32_t BitrateReceived() const OVERRIDE { return 0; }
|
||||||
|
virtual void ResetStatistics() OVERRIDE {}
|
||||||
|
virtual bool IsRetransmitOfOldPacket(const RTPHeader& header,
|
||||||
|
int min_rtt) const OVERRIDE {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual bool IsPacketInOrder(uint16_t sequence_number) const OVERRIDE {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
Statistics stats_;
|
||||||
|
};
|
||||||
|
|
||||||
|
scoped_ptr<LossyStatistician> lossy_stats_;
|
||||||
|
StatisticianMap stats_map_;
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST_F(VideoSendStreamTest, SupportsFec) {
|
||||||
|
static const int kRedPayloadType = 118;
|
||||||
|
static const int kUlpfecPayloadType = 119;
|
||||||
|
class FecObserver : public SendTransportObserver {
|
||||||
|
public:
|
||||||
|
FecObserver()
|
||||||
|
: SendTransportObserver(30 * 1000),
|
||||||
|
transport_adapter_(&transport_),
|
||||||
|
send_count_(0),
|
||||||
|
received_media_(false),
|
||||||
|
received_fec_(false) {}
|
||||||
|
|
||||||
|
void SetReceiver(PacketReceiver* receiver) {
|
||||||
|
transport_.SetReceiver(receiver);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual bool SendRTP(const uint8_t* packet, size_t length) OVERRIDE {
|
||||||
|
RTPHeader header;
|
||||||
|
EXPECT_TRUE(
|
||||||
|
rtp_header_parser_->Parse(packet, static_cast<int>(length), &header));
|
||||||
|
|
||||||
|
// Send lossy receive reports to trigger FEC enabling.
|
||||||
|
if (send_count_++ % 2 != 0) {
|
||||||
|
// Receive statistics reporting having lost 50% of the packets.
|
||||||
|
LossyReceiveStatistics lossy_receive_stats(
|
||||||
|
kSendSsrc, header.sequenceNumber, send_count_ / 2, 127);
|
||||||
|
RTCPSender rtcp_sender(
|
||||||
|
0, false, Clock::GetRealTimeClock(), &lossy_receive_stats);
|
||||||
|
EXPECT_EQ(0, rtcp_sender.RegisterSendTransport(&transport_adapter_));
|
||||||
|
|
||||||
|
rtcp_sender.SetRTCPStatus(kRtcpNonCompound);
|
||||||
|
rtcp_sender.SetRemoteSSRC(kSendSsrc);
|
||||||
|
|
||||||
|
RTCPSender::FeedbackState feedback_state;
|
||||||
|
|
||||||
|
EXPECT_EQ(0, rtcp_sender.SendRTCP(feedback_state, kRtcpRr));
|
||||||
|
}
|
||||||
|
|
||||||
|
EXPECT_EQ(kRedPayloadType, header.payloadType);
|
||||||
|
|
||||||
|
uint8_t encapsulated_payload_type = packet[header.headerLength];
|
||||||
|
|
||||||
|
if (encapsulated_payload_type == kUlpfecPayloadType) {
|
||||||
|
received_fec_ = true;
|
||||||
|
} else {
|
||||||
|
received_media_ = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (received_media_ && received_fec_)
|
||||||
|
send_test_complete_->Set();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
internal::TransportAdapter transport_adapter_;
|
||||||
|
test::DirectTransport transport_;
|
||||||
|
int send_count_;
|
||||||
|
bool received_media_;
|
||||||
|
bool received_fec_;
|
||||||
|
} observer;
|
||||||
|
|
||||||
|
Call::Config call_config(&observer);
|
||||||
|
scoped_ptr<Call> call(Call::Create(call_config));
|
||||||
|
|
||||||
|
observer.SetReceiver(call->Receiver());
|
||||||
|
|
||||||
|
VideoSendStream::Config send_config = GetSendTestConfig(call.get());
|
||||||
|
send_config.rtp.fec.red_payload_type = kRedPayloadType;
|
||||||
|
send_config.rtp.fec.ulpfec_payload_type = kUlpfecPayloadType;
|
||||||
|
|
||||||
|
RunSendTest(call.get(), send_config, &observer);
|
||||||
|
}
|
||||||
|
|
||||||
void VideoSendStreamTest::TestNackRetransmission(uint32_t retransmit_ssrc) {
|
void VideoSendStreamTest::TestNackRetransmission(uint32_t retransmit_ssrc) {
|
||||||
class NackObserver : public SendTransportObserver, webrtc::Transport {
|
class NackObserver : public SendTransportObserver {
|
||||||
public:
|
public:
|
||||||
NackObserver(uint32_t retransmit_ssrc)
|
NackObserver(uint32_t retransmit_ssrc)
|
||||||
: SendTransportObserver(30 * 1000),
|
: SendTransportObserver(30 * 1000),
|
||||||
thread_(ThreadWrapper::CreateThread(NackProcess, this)),
|
transport_adapter_(&transport_),
|
||||||
send_call_receiver_(NULL),
|
|
||||||
send_count_(0),
|
send_count_(0),
|
||||||
retransmit_ssrc_(retransmit_ssrc),
|
retransmit_ssrc_(retransmit_ssrc),
|
||||||
nacked_sequence_number_(0) {}
|
nacked_sequence_number_(0) {}
|
||||||
|
|
||||||
~NackObserver() {
|
void SetReceiver(PacketReceiver* receiver) {
|
||||||
EXPECT_TRUE(thread_->Stop());
|
transport_.SetReceiver(receiver);
|
||||||
}
|
|
||||||
|
|
||||||
void SetReceiver(PacketReceiver* send_call_receiver) {
|
|
||||||
send_call_receiver_ = send_call_receiver;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sending NACKs must be done from a different "network" thread to prevent
|
|
||||||
// violating locking orders. With this no locks are held prior to inserting
|
|
||||||
// packets back into the sender.
|
|
||||||
static bool NackProcess(void* observer) {
|
|
||||||
return static_cast<NackObserver*>(observer)->SendNack();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool SendNack() {
|
|
||||||
NullReceiveStatistics null_stats;
|
|
||||||
RTCPSender rtcp_sender(0, false, Clock::GetRealTimeClock(), &null_stats);
|
|
||||||
EXPECT_EQ(0, rtcp_sender.RegisterSendTransport(this));
|
|
||||||
|
|
||||||
rtcp_sender.SetRTCPStatus(kRtcpNonCompound);
|
|
||||||
rtcp_sender.SetRemoteSSRC(kSendSsrc);
|
|
||||||
|
|
||||||
RTCPSender::FeedbackState feedback_state;
|
|
||||||
EXPECT_EQ(0, rtcp_sender.SendRTCP(
|
|
||||||
feedback_state, kRtcpNack, 1, &nacked_sequence_number_));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual int SendPacket(int channel, const void* data, int len) OVERRIDE {
|
|
||||||
ADD_FAILURE()
|
|
||||||
<< "This should never be reached. Only a NACK should be sent.";
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual int SendRTCPPacket(int channel,
|
|
||||||
const void* data,
|
|
||||||
int len) OVERRIDE {
|
|
||||||
EXPECT_TRUE(send_call_receiver_->DeliverPacket(
|
|
||||||
static_cast<const uint8_t*>(data), static_cast<size_t>(len)));
|
|
||||||
return len;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual bool SendRTP(const uint8_t* packet, size_t length) OVERRIDE {
|
virtual bool SendRTP(const uint8_t* packet, size_t length) OVERRIDE {
|
||||||
EXPECT_TRUE(send_call_receiver_ != NULL);
|
|
||||||
RTPHeader header;
|
RTPHeader header;
|
||||||
EXPECT_TRUE(
|
EXPECT_TRUE(
|
||||||
rtp_header_parser_->Parse(packet, static_cast<int>(length), &header));
|
rtp_header_parser_->Parse(packet, static_cast<int>(length), &header));
|
||||||
@ -279,8 +371,19 @@ void VideoSendStreamTest::TestNackRetransmission(uint32_t retransmit_ssrc) {
|
|||||||
// Nack second packet after receiving the third one.
|
// Nack second packet after receiving the third one.
|
||||||
if (++send_count_ == 3) {
|
if (++send_count_ == 3) {
|
||||||
nacked_sequence_number_ = header.sequenceNumber - 1;
|
nacked_sequence_number_ = header.sequenceNumber - 1;
|
||||||
unsigned int id;
|
NullReceiveStatistics null_stats;
|
||||||
EXPECT_TRUE(thread_->Start(id));
|
RTCPSender rtcp_sender(
|
||||||
|
0, false, Clock::GetRealTimeClock(), &null_stats);
|
||||||
|
EXPECT_EQ(0, rtcp_sender.RegisterSendTransport(&transport_adapter_));
|
||||||
|
|
||||||
|
rtcp_sender.SetRTCPStatus(kRtcpNonCompound);
|
||||||
|
rtcp_sender.SetRemoteSSRC(kSendSsrc);
|
||||||
|
|
||||||
|
RTCPSender::FeedbackState feedback_state;
|
||||||
|
|
||||||
|
EXPECT_EQ(0,
|
||||||
|
rtcp_sender.SendRTCP(
|
||||||
|
feedback_state, kRtcpNack, 1, &nacked_sequence_number_));
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t sequence_number = header.sequenceNumber;
|
uint16_t sequence_number = header.sequenceNumber;
|
||||||
@ -299,8 +402,8 @@ void VideoSendStreamTest::TestNackRetransmission(uint32_t retransmit_ssrc) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
private:
|
private:
|
||||||
scoped_ptr<ThreadWrapper> thread_;
|
internal::TransportAdapter transport_adapter_;
|
||||||
PacketReceiver* send_call_receiver_;
|
test::DirectTransport transport_;
|
||||||
int send_count_;
|
int send_count_;
|
||||||
uint32_t retransmit_ssrc_;
|
uint32_t retransmit_ssrc_;
|
||||||
uint16_t nacked_sequence_number_;
|
uint16_t nacked_sequence_number_;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user