| /* |
| * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. |
| * |
| * Use of this source code is governed by a BSD-style license |
| * that can be found in the LICENSE file in the root of the source |
| * tree. An additional intellectual property rights grant can be found |
| * in the file PATENTS. All contributing project authors may |
| * be found in the AUTHORS file in the root of the source tree. |
| */ |
| #include <assert.h> |
| |
| #include <map> |
| |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| #include "webrtc/modules/remote_bitrate_estimator/include/remote_bitrate_estimator.h" |
| #include "webrtc/modules/rtp_rtcp/interface/receive_statistics.h" |
| #include "webrtc/modules/rtp_rtcp/interface/rtp_header_parser.h" |
| #include "webrtc/modules/rtp_rtcp/interface/rtp_rtcp.h" |
| #include "webrtc/modules/rtp_rtcp/source/rtcp_utility.h" |
| #include "webrtc/system_wrappers/interface/critical_section_wrapper.h" |
| #include "webrtc/system_wrappers/interface/scoped_ptr.h" |
| #include "webrtc/system_wrappers/interface/event_wrapper.h" |
| #include "webrtc/video_engine/new_include/video_call.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.h" |
| #include "webrtc/video_engine/test/common/frame_generator_capturer.h" |
| #include "webrtc/video_engine/test/common/generate_ssrcs.h" |
| #include "webrtc/video_engine/test/common/rtp_rtcp_observer.h" |
| |
| namespace webrtc { |
| |
| class StreamObserver : public newapi::Transport, public RemoteBitrateObserver { |
| public: |
| typedef std::map<uint32_t, int> BytesSentMap; |
| StreamObserver(int num_expected_ssrcs, newapi::Transport* feedback_transport, |
| Clock* clock) |
| : critical_section_(CriticalSectionWrapper::CreateCriticalSection()), |
| all_ssrcs_sent_(EventWrapper::Create()), |
| rtp_parser_(RtpHeaderParser::Create()), |
| feedback_transport_(new TransportWrapper(feedback_transport)), |
| receive_stats_(ReceiveStatistics::Create(clock)), |
| clock_(clock), |
| num_expected_ssrcs_(num_expected_ssrcs) { |
| // Ideally we would only have to instantiate an RtcpSender, an |
| // RtpHeaderParser and a RemoteBitrateEstimator here, but due to the current |
| // state of the RTP module we need a full module and receive statistics to |
| // be able to produce an RTCP with REMB. |
| RtpRtcp::Configuration config; |
| config.receive_statistics = receive_stats_.get(); |
| config.outgoing_transport = feedback_transport_.get(); |
| rtp_rtcp_.reset(RtpRtcp::CreateRtpRtcp(config)); |
| rtp_rtcp_->SetREMBStatus(true); |
| rtp_rtcp_->SetRTCPStatus(kRtcpNonCompound); |
| rtp_parser_->RegisterRtpHeaderExtension(kRtpExtensionTransmissionTimeOffset, |
| 1); |
| AbsoluteSendTimeRemoteBitrateEstimatorFactory rbe_factory; |
| remote_bitrate_estimator_.reset(rbe_factory.Create(this, clock)); |
| } |
| |
| virtual void OnReceiveBitrateChanged(const std::vector<unsigned int>& ssrcs, |
| unsigned int bitrate) { |
| CriticalSectionScoped lock(critical_section_.get()); |
| if (ssrcs.size() == num_expected_ssrcs_ && bitrate >= kExpectedBitrateBps) |
| all_ssrcs_sent_->Set(); |
| rtp_rtcp_->SetREMBData(bitrate, static_cast<uint8_t>(ssrcs.size()), |
| &ssrcs[0]); |
| rtp_rtcp_->Process(); |
| } |
| |
| virtual bool SendRTP(const uint8_t* packet, size_t length) OVERRIDE { |
| CriticalSectionScoped lock(critical_section_.get()); |
| RTPHeader header; |
| EXPECT_TRUE(rtp_parser_->Parse(packet, static_cast<int>(length), |
| &header)); |
| receive_stats_->IncomingPacket(header, length, false, true); |
| rtp_rtcp_->SetRemoteSSRC(header.ssrc); |
| remote_bitrate_estimator_->IncomingPacket(clock_->TimeInMilliseconds(), |
| static_cast<int>(length - 12), |
| header); |
| if (remote_bitrate_estimator_->TimeUntilNextProcess() <= 0) { |
| remote_bitrate_estimator_->Process(); |
| } |
| return true; |
| } |
| |
| virtual bool SendRTCP(const uint8_t* packet, size_t length) OVERRIDE { |
| return true; |
| } |
| |
| EventTypeWrapper Wait() { |
| return all_ssrcs_sent_->Wait(120 * 1000); |
| } |
| |
| private: |
| class TransportWrapper : public webrtc::Transport { |
| public: |
| explicit TransportWrapper(newapi::Transport* new_transport) |
| : new_transport_(new_transport) {} |
| |
| virtual int SendPacket(int channel, const void *data, int len) OVERRIDE { |
| return new_transport_->SendRTP(static_cast<const uint8_t*>(data), len) ? |
| len : -1; |
| } |
| |
| virtual int SendRTCPPacket(int channel, const void *data, |
| int len) OVERRIDE { |
| return new_transport_->SendRTCP(static_cast<const uint8_t*>(data), len) ? |
| len : -1; |
| } |
| |
| private: |
| newapi::Transport* new_transport_; |
| }; |
| |
| static const unsigned int kExpectedBitrateBps = 1200000; |
| |
| scoped_ptr<CriticalSectionWrapper> critical_section_; |
| scoped_ptr<EventWrapper> all_ssrcs_sent_; |
| scoped_ptr<RtpHeaderParser> rtp_parser_; |
| scoped_ptr<RtpRtcp> rtp_rtcp_; |
| scoped_ptr<TransportWrapper> feedback_transport_; |
| scoped_ptr<ReceiveStatistics> receive_stats_; |
| scoped_ptr<RemoteBitrateEstimator> remote_bitrate_estimator_; |
| Clock* clock_; |
| const size_t num_expected_ssrcs_; |
| }; |
| |
| class RampUpTest : public ::testing::TestWithParam<bool> { |
| public: |
| virtual void SetUp() { |
| reserved_ssrcs_.clear(); |
| } |
| |
| static void SetCodecStreamSettings(VideoCodec* video_codec) { |
| video_codec->width = 1280; |
| video_codec->height = 720; |
| video_codec->startBitrate = 300; |
| video_codec->minBitrate = 50; |
| video_codec->maxBitrate = 1800; |
| |
| video_codec->numberOfSimulcastStreams = 3; |
| video_codec->simulcastStream[0].width = 320; |
| video_codec->simulcastStream[0].height = 180; |
| video_codec->simulcastStream[0].numberOfTemporalLayers = 0; |
| video_codec->simulcastStream[0].maxBitrate = 150; |
| video_codec->simulcastStream[0].targetBitrate = 150; |
| video_codec->simulcastStream[0].minBitrate = 50; |
| video_codec->simulcastStream[0].qpMax = video_codec->qpMax; |
| |
| video_codec->simulcastStream[1].width = 640; |
| video_codec->simulcastStream[1].height = 360; |
| video_codec->simulcastStream[1].numberOfTemporalLayers = 0; |
| video_codec->simulcastStream[1].maxBitrate = 500; |
| video_codec->simulcastStream[1].targetBitrate = 500; |
| video_codec->simulcastStream[1].minBitrate = 150; |
| video_codec->simulcastStream[1].qpMax = video_codec->qpMax; |
| |
| video_codec->simulcastStream[2].width = 1280; |
| video_codec->simulcastStream[2].height = 720; |
| video_codec->simulcastStream[2].numberOfTemporalLayers = 0; |
| video_codec->simulcastStream[2].maxBitrate = 1200; |
| video_codec->simulcastStream[2].targetBitrate = 1200; |
| video_codec->simulcastStream[2].minBitrate = 600; |
| video_codec->simulcastStream[2].qpMax = video_codec->qpMax; |
| } |
| |
| protected: |
| std::map<uint32_t, bool> reserved_ssrcs_; |
| }; |
| |
| TEST_P(RampUpTest, RampUpWithPadding) { |
| test::DirectTransport receiver_transport; |
| StreamObserver stream_observer(3, &receiver_transport, |
| Clock::GetRealTimeClock()); |
| VideoCall::Config call_config(&stream_observer); |
| scoped_ptr<VideoCall> call(VideoCall::Create(call_config)); |
| VideoSendStream::Config send_config = |
| call->GetDefaultSendConfig(); |
| |
| receiver_transport.SetReceiver(call->Receiver()); |
| |
| FakeEncoder encoder(Clock::GetRealTimeClock()); |
| send_config.encoder = &encoder; |
| send_config.internal_source = false; |
| SetCodecStreamSettings(&send_config.codec); |
| send_config.codec.plType = 100; |
| send_config.pacing = GetParam(); |
| |
| test::GenerateRandomSsrcs(&send_config, &reserved_ssrcs_); |
| |
| VideoSendStream* send_stream = |
| call->CreateSendStream(send_config); |
| |
| VideoReceiveStream::Config receive_config; |
| receive_config.rtp.ssrc = send_config.rtp.ssrcs[0]; |
| receive_config.rtp.nack.rtp_history_ms = |
| send_config.rtp.nack.rtp_history_ms; |
| VideoReceiveStream* receive_stream = call->CreateReceiveStream( |
| receive_config); |
| |
| scoped_ptr<test::FrameGeneratorCapturer> frame_generator_capturer( |
| test::FrameGeneratorCapturer::Create( |
| send_stream->Input(), |
| test::FrameGenerator::Create( |
| send_config.codec.width, send_config.codec.height, |
| Clock::GetRealTimeClock()), |
| 30)); |
| |
| receive_stream->StartReceive(); |
| send_stream->StartSend(); |
| frame_generator_capturer->Start(); |
| |
| EXPECT_EQ(kEventSignaled, stream_observer.Wait()); |
| |
| frame_generator_capturer->Stop(); |
| send_stream->StopSend(); |
| receive_stream->StopReceive(); |
| |
| call->DestroyReceiveStream(receive_stream); |
| call->DestroySendStream(send_stream); |
| } |
| |
| INSTANTIATE_TEST_CASE_P(RampUpTest, RampUpTest, ::testing::Bool()); |
| |
| struct EngineTestParams { |
| size_t width, height; |
| struct { |
| unsigned int min, start, max; |
| } bitrate; |
| }; |
| |
| class EngineTest : public ::testing::TestWithParam<EngineTestParams> { |
| public: |
| EngineTest() : send_stream_(NULL), receive_stream_(NULL) {} |
| |
| ~EngineTest() { |
| EXPECT_EQ(NULL, send_stream_); |
| EXPECT_EQ(NULL, receive_stream_); |
| } |
| |
| protected: |
| void CreateCalls(newapi::Transport* sender_transport, |
| newapi::Transport* receiver_transport) { |
| VideoCall::Config sender_config(sender_transport); |
| VideoCall::Config receiver_config(receiver_transport); |
| sender_call_.reset(VideoCall::Create(sender_config)); |
| receiver_call_.reset(VideoCall::Create(receiver_config)); |
| } |
| |
| void CreateTestConfigs() { |
| EngineTestParams params = GetParam(); |
| send_config_ = sender_call_->GetDefaultSendConfig(); |
| receive_config_ = receiver_call_->GetDefaultReceiveConfig(); |
| |
| test::GenerateRandomSsrcs(&send_config_, &reserved_ssrcs_); |
| send_config_.codec.width = static_cast<uint16_t>(params.width); |
| send_config_.codec.height = static_cast<uint16_t>(params.height); |
| send_config_.codec.minBitrate = params.bitrate.min; |
| send_config_.codec.startBitrate = params.bitrate.start; |
| send_config_.codec.maxBitrate = params.bitrate.max; |
| |
| receive_config_.rtp.ssrc = send_config_.rtp.ssrcs[0]; |
| } |
| |
| void CreateStreams() { |
| assert(send_stream_ == NULL); |
| assert(receive_stream_ == NULL); |
| |
| send_stream_ = sender_call_->CreateSendStream(send_config_); |
| receive_stream_ = receiver_call_->CreateReceiveStream(receive_config_); |
| } |
| |
| void CreateFrameGenerator() { |
| EngineTestParams params = GetParam(); |
| frame_generator_capturer_.reset(test::FrameGeneratorCapturer::Create( |
| send_stream_->Input(), |
| test::FrameGenerator::Create( |
| params.width, params.height, Clock::GetRealTimeClock()), |
| 30)); |
| } |
| |
| void StartSending() { |
| receive_stream_->StartReceive(); |
| send_stream_->StartSend(); |
| frame_generator_capturer_->Start(); |
| } |
| |
| void StopSending() { |
| frame_generator_capturer_->Stop(); |
| send_stream_->StopSend(); |
| receive_stream_->StopReceive(); |
| } |
| |
| void DestroyStreams() { |
| sender_call_->DestroySendStream(send_stream_); |
| receiver_call_->DestroyReceiveStream(receive_stream_); |
| send_stream_= NULL; |
| receive_stream_ = NULL; |
| } |
| |
| void ReceivesPliAndRecovers(int rtp_history_ms); |
| |
| scoped_ptr<VideoCall> sender_call_; |
| scoped_ptr<VideoCall> receiver_call_; |
| |
| VideoSendStream::Config send_config_; |
| VideoReceiveStream::Config receive_config_; |
| |
| VideoSendStream* send_stream_; |
| VideoReceiveStream* receive_stream_; |
| |
| scoped_ptr<test::FrameGeneratorCapturer> frame_generator_capturer_; |
| |
| std::map<uint32_t, bool> reserved_ssrcs_; |
| }; |
| |
| // TODO(pbos): What are sane values here for bitrate? Are we missing any |
| // important resolutions? |
| EngineTestParams video_1080p = {1920, 1080, {300, 600, 800}}; |
| EngineTestParams video_720p = {1280, 720, {300, 600, 800}}; |
| EngineTestParams video_vga = {640, 480, {300, 600, 800}}; |
| EngineTestParams video_qvga = {320, 240, {300, 600, 800}}; |
| EngineTestParams video_4cif = {704, 576, {300, 600, 800}}; |
| EngineTestParams video_cif = {352, 288, {300, 600, 800}}; |
| EngineTestParams video_qcif = {176, 144, {300, 600, 800}}; |
| |
| class NackObserver : public test::RtpRtcpObserver { |
| static const int kNumberOfNacksToObserve = 4; |
| static const int kInverseProbabilityToStartLossBurst = 20; |
| static const int kMaxLossBurst = 10; |
| public: |
| NackObserver() |
| : received_all_retransmissions_(EventWrapper::Create()), |
| rtp_parser_(RtpHeaderParser::Create()), |
| drop_burst_count_(0), |
| sent_rtp_packets_(0), |
| nacks_left_(kNumberOfNacksToObserve) {} |
| |
| EventTypeWrapper Wait() { |
| // 2 minutes should be more than enough time for the test to finish. |
| return received_all_retransmissions_->Wait(2 * 60 * 1000); |
| } |
| |
| 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)); |
| |
| // Never drop retransmitted packets. |
| if (dropped_packets_.find(header.sequenceNumber) != |
| dropped_packets_.end()) { |
| retransmitted_packets_.insert(header.sequenceNumber); |
| return SEND_PACKET; |
| } |
| |
| // Enough NACKs received, stop dropping packets. |
| if (nacks_left_ == 0) { |
| ++sent_rtp_packets_; |
| return SEND_PACKET; |
| } |
| |
| // Still dropping packets. |
| if (drop_burst_count_ > 0) { |
| --drop_burst_count_; |
| dropped_packets_.insert(header.sequenceNumber); |
| return DROP_PACKET; |
| } |
| |
| // Should we start dropping packets? |
| if (sent_rtp_packets_ > 0 && |
| rand() % kInverseProbabilityToStartLossBurst == 0) { |
| drop_burst_count_ = rand() % kMaxLossBurst; |
| dropped_packets_.insert(header.sequenceNumber); |
| return DROP_PACKET; |
| } |
| |
| ++sent_rtp_packets_; |
| return SEND_PACKET; |
| } |
| |
| virtual Action OnReceiveRtcp(const uint8_t* packet, size_t length) OVERRIDE { |
| RTCPUtility::RTCPParserV2 parser(packet, length, true); |
| EXPECT_TRUE(parser.IsValid()); |
| |
| bool received_nack = false; |
| RTCPUtility::RTCPPacketTypes packet_type = parser.Begin(); |
| while (packet_type != RTCPUtility::kRtcpNotValidCode) { |
| if (packet_type == RTCPUtility::kRtcpRtpfbNackCode) |
| received_nack = true; |
| |
| packet_type = parser.Iterate(); |
| } |
| |
| if (received_nack) { |
| ReceivedNack(); |
| } else { |
| RtcpWithoutNack(); |
| } |
| return SEND_PACKET; |
| } |
| |
| private: |
| void ReceivedNack() { |
| if (nacks_left_ > 0) |
| --nacks_left_; |
| rtcp_without_nack_count_ = 0; |
| } |
| |
| void RtcpWithoutNack() { |
| if (nacks_left_ > 0) |
| return; |
| ++rtcp_without_nack_count_; |
| |
| // All packets retransmitted and no recent NACKs. |
| if (dropped_packets_.size() == retransmitted_packets_.size() && |
| rtcp_without_nack_count_ >= kRequiredRtcpsWithoutNack) { |
| received_all_retransmissions_->Set(); |
| } |
| } |
| |
| scoped_ptr<EventWrapper> received_all_retransmissions_; |
| |
| scoped_ptr<RtpHeaderParser> rtp_parser_; |
| std::set<uint16_t> dropped_packets_; |
| std::set<uint16_t> retransmitted_packets_; |
| int drop_burst_count_; |
| uint64_t sent_rtp_packets_; |
| int nacks_left_; |
| int rtcp_without_nack_count_; |
| static const int kRequiredRtcpsWithoutNack = 2; |
| }; |
| |
| TEST_P(EngineTest, ReceivesAndRetransmitsNack) { |
| NackObserver observer; |
| |
| CreateCalls(observer.SendTransport(), observer.ReceiveTransport()); |
| |
| observer.SetReceivers(receiver_call_->Receiver(), sender_call_->Receiver()); |
| |
| CreateTestConfigs(); |
| int rtp_history_ms = 1000; |
| send_config_.rtp.nack.rtp_history_ms = rtp_history_ms; |
| receive_config_.rtp.nack.rtp_history_ms = rtp_history_ms; |
| |
| CreateStreams(); |
| CreateFrameGenerator(); |
| |
| StartSending(); |
| |
| // Wait() waits for an event triggered when NACKs have been received, NACKed |
| // packets retransmitted and frames rendered again. |
| EXPECT_EQ(kEventSignaled, observer.Wait()); |
| |
| StopSending(); |
| |
| DestroyStreams(); |
| |
| observer.StopSending(); |
| } |
| |
| class PliObserver : public test::RtpRtcpObserver { |
| static const int kInverseDropProbability = 16; |
| public: |
| PliObserver(bool nack_enabled) : |
| renderer_(this), |
| rtp_header_parser_(RtpHeaderParser::Create()), |
| nack_enabled_(nack_enabled), |
| first_retransmitted_timestamp_(0), |
| last_send_timestamp_(0), |
| rendered_frame_(false), |
| received_pli_(false) {} |
| |
| 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)); |
| |
| // Drop all NACK retransmissions. This is to force transmission of a PLI. |
| if (header.timestamp < last_send_timestamp_) |
| return DROP_PACKET; |
| |
| if (received_pli_) { |
| if (first_retransmitted_timestamp_ == 0) { |
| first_retransmitted_timestamp_ = header.timestamp; |
| } |
| } else if (rendered_frame_ && rand() % kInverseDropProbability == 0) { |
| return DROP_PACKET; |
| } |
| |
| last_send_timestamp_ = header.timestamp; |
| return SEND_PACKET; |
| } |
| |
| virtual Action OnReceiveRtcp(const uint8_t* packet, size_t length) OVERRIDE { |
| RTCPUtility::RTCPParserV2 parser(packet, length, true); |
| EXPECT_TRUE(parser.IsValid()); |
| |
| for (RTCPUtility::RTCPPacketTypes packet_type = parser.Begin(); |
| packet_type != RTCPUtility::kRtcpNotValidCode; |
| packet_type = parser.Iterate()) { |
| if (!nack_enabled_) |
| EXPECT_NE(packet_type, RTCPUtility::kRtcpRtpfbNackCode); |
| |
| if (packet_type == RTCPUtility::kRtcpPsfbPliCode) { |
| received_pli_ = true; |
| break; |
| } |
| } |
| return SEND_PACKET; |
| } |
| |
| class ReceiverRenderer : public VideoRenderer { |
| public: |
| ReceiverRenderer(PliObserver* observer) |
| : rendered_retransmission_(EventWrapper::Create()), |
| observer_(observer) {} |
| |
| virtual void RenderFrame(const I420VideoFrame& video_frame, |
| int time_to_render_ms) { |
| CriticalSectionScoped crit_(observer_->lock_.get()); |
| if (observer_->first_retransmitted_timestamp_ != 0 && |
| video_frame.timestamp() > observer_->first_retransmitted_timestamp_) { |
| EXPECT_TRUE(observer_->received_pli_); |
| rendered_retransmission_->Set(); |
| } |
| observer_->rendered_frame_ = true; |
| } |
| scoped_ptr<EventWrapper> rendered_retransmission_; |
| PliObserver* observer_; |
| } renderer_; |
| |
| EventTypeWrapper Wait() { |
| // 120 seconds should be plenty of time. |
| return renderer_.rendered_retransmission_->Wait(2 * 60 * 1000); |
| } |
| |
| private: |
| scoped_ptr<RtpHeaderParser> rtp_header_parser_; |
| bool nack_enabled_; |
| |
| uint32_t first_retransmitted_timestamp_; |
| uint32_t last_send_timestamp_; |
| |
| bool rendered_frame_; |
| bool received_pli_; |
| }; |
| |
| void EngineTest::ReceivesPliAndRecovers(int rtp_history_ms) { |
| PliObserver observer(rtp_history_ms > 0); |
| |
| CreateCalls(observer.SendTransport(), observer.ReceiveTransport()); |
| |
| observer.SetReceivers(receiver_call_->Receiver(), sender_call_->Receiver()); |
| |
| CreateTestConfigs(); |
| send_config_.rtp.nack.rtp_history_ms = rtp_history_ms; |
| receive_config_.rtp.nack.rtp_history_ms = rtp_history_ms; |
| receive_config_.renderer = &observer.renderer_; |
| |
| CreateStreams(); |
| CreateFrameGenerator(); |
| |
| StartSending(); |
| |
| // Wait() waits for an event triggered when Pli has been received and frames |
| // have been rendered afterwards. |
| EXPECT_EQ(kEventSignaled, observer.Wait()); |
| |
| StopSending(); |
| |
| DestroyStreams(); |
| |
| observer.StopSending(); |
| } |
| |
| TEST_P(EngineTest, ReceivesPliAndRecoversWithNack) { |
| ReceivesPliAndRecovers(1000); |
| } |
| |
| // TODO(pbos): Enable this when 2250 is resolved. |
| TEST_P(EngineTest, DISABLED_ReceivesPliAndRecoversWithoutNack) { |
| ReceivesPliAndRecovers(0); |
| } |
| |
| INSTANTIATE_TEST_CASE_P(EngineTest, EngineTest, ::testing::Values(video_vga)); |
| } // namespace webrtc |