From 0e63e767810c90138e41b250df3ce59ef04ae83b Mon Sep 17 00:00:00 2001 From: "pbos@webrtc.org" Date: Fri, 20 Sep 2013 11:56:26 +0000 Subject: [PATCH] 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 --- webrtc/video_engine/internal/call.cc | 8 +- .../internal/video_send_stream.cc | 21 +- webrtc/video_engine/test/send_stream_tests.cc | 209 +++++++++++++----- 3 files changed, 178 insertions(+), 60 deletions(-) diff --git a/webrtc/video_engine/internal/call.cc b/webrtc/video_engine/internal/call.cc index acb65bf031..94c5b7ed48 100644 --- a/webrtc/video_engine/internal/call.cc +++ b/webrtc/video_engine/internal/call.cc @@ -173,10 +173,8 @@ bool Call::DeliverRtcp(const uint8_t* packet, size_t length) { receive_ssrcs_.begin(); it != receive_ssrcs_.end(); ++it) { - if (it->second->DeliverRtcp(static_cast(packet), - length)) { + if (it->second->DeliverRtcp(packet, length)) rtcp_delivered = true; - } } } @@ -186,10 +184,8 @@ bool Call::DeliverRtcp(const uint8_t* packet, size_t length) { send_ssrcs_.begin(); it != send_ssrcs_.end(); ++it) { - if (it->second->DeliverRtcp(static_cast(packet), - length)) { + if (it->second->DeliverRtcp(packet, length)) rtcp_delivered = true; - } } } return rtcp_delivered; diff --git a/webrtc/video_engine/internal/video_send_stream.cc b/webrtc/video_engine/internal/video_send_stream.cc index 799cec7d43..34cafc87b6 100644 --- a/webrtc/video_engine/internal/video_send_stream.cc +++ b/webrtc/video_engine/internal/video_send_stream.cc @@ -103,7 +103,6 @@ VideoSendStream::VideoSendStream(newapi::Transport* transport, kViEStreamTypeNormal, i); } } - rtp_rtcp_->SetNACKStatus(channel_, config_.rtp.nack.rtp_history_ms > 0); rtp_rtcp_->SetTransmissionSmoothingStatus(channel_, config_.pacing); if (!config_.rtp.rtx.ssrcs.empty()) { 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(config_.rtp.fec.red_payload_type), + static_cast(config_.rtp.fec.ulpfec_payload_type)); + } else { + rtp_rtcp_->SetFECStatus( + channel_, + true, + static_cast(config_.rtp.fec.red_payload_type), + static_cast(config_.rtp.fec.ulpfec_payload_type)); + } + } else { + rtp_rtcp_->SetNACKStatus(channel_, config_.rtp.nack.rtp_history_ms > 0); + } + char rtcp_cname[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); diff --git a/webrtc/video_engine/test/send_stream_tests.cc b/webrtc/video_engine/test/send_stream_tests.cc index 10ad66dca6..c2e4b30255 100644 --- a/webrtc/video_engine/test/send_stream_tests.cc +++ b/webrtc/video_engine/test/send_stream_tests.cc @@ -15,11 +15,13 @@ #include "webrtc/system_wrappers/interface/scoped_ptr.h" #include "webrtc/system_wrappers/interface/sleep.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/frame_generator_capturer.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 { @@ -45,8 +47,6 @@ class VideoSendStreamTest : public ::testing::Test { VideoSendStreamTest() : fake_encoder_(Clock::GetRealTimeClock()) {} protected: - static const uint32_t kSendSsrc; - static const uint32_t kSendRtxSsrc; void RunSendTest(Call* call, const VideoSendStream::Config& config, SendTransportObserver* observer) { @@ -75,6 +75,9 @@ class VideoSendStreamTest : public ::testing::Test { void TestNackRetransmission(uint32_t retransmit_ssrc); + static const uint32_t kSendSsrc; + static const uint32_t kSendRtxSsrc; + test::FakeEncoder fake_encoder_; }; @@ -216,62 +219,151 @@ TEST_F(VideoSendStreamTest, SupportsTransmissionTimeOffset) { 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 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(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::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) { - class NackObserver : public SendTransportObserver, webrtc::Transport { + class NackObserver : public SendTransportObserver { public: NackObserver(uint32_t retransmit_ssrc) : SendTransportObserver(30 * 1000), - thread_(ThreadWrapper::CreateThread(NackProcess, this)), - send_call_receiver_(NULL), + transport_adapter_(&transport_), send_count_(0), retransmit_ssrc_(retransmit_ssrc), nacked_sequence_number_(0) {} - ~NackObserver() { - EXPECT_TRUE(thread_->Stop()); - } - - 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(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(data), static_cast(len))); - return len; + void SetReceiver(PacketReceiver* receiver) { + transport_.SetReceiver(receiver); } virtual bool SendRTP(const uint8_t* packet, size_t length) OVERRIDE { - EXPECT_TRUE(send_call_receiver_ != NULL); RTPHeader header; EXPECT_TRUE( rtp_header_parser_->Parse(packet, static_cast(length), &header)); @@ -279,8 +371,19 @@ void VideoSendStreamTest::TestNackRetransmission(uint32_t retransmit_ssrc) { // Nack second packet after receiving the third one. if (++send_count_ == 3) { nacked_sequence_number_ = header.sequenceNumber - 1; - unsigned int id; - EXPECT_TRUE(thread_->Start(id)); + NullReceiveStatistics null_stats; + 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; @@ -299,8 +402,8 @@ void VideoSendStreamTest::TestNackRetransmission(uint32_t retransmit_ssrc) { return true; } private: - scoped_ptr thread_; - PacketReceiver* send_call_receiver_; + internal::TransportAdapter transport_adapter_; + test::DirectTransport transport_; int send_count_; uint32_t retransmit_ssrc_; uint16_t nacked_sequence_number_;