// Copyright 2014 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "media/cast/net/cast_transport_sender_impl.h" #include #include #include #include "base/bind.h" #include "base/bind_helpers.h" #include "base/macros.h" #include "base/memory/scoped_ptr.h" #include "base/test/simple_test_tick_clock.h" #include "base/values.h" #include "media/cast/net/cast_transport_config.h" #include "media/cast/net/rtcp/rtcp_defines.h" #include "media/cast/test/fake_single_thread_task_runner.h" #include "testing/gtest/include/gtest/gtest.h" namespace media { namespace cast { namespace { const int64_t kStartMillisecond = INT64_C(12345678900000); const uint32_t kVideoSsrc = 1; const uint32_t kAudioSsrc = 2; } // namespace class FakePacketSender : public PacketSender { public: FakePacketSender() : paused_(false), packets_sent_(0), bytes_sent_(0) {} bool SendPacket(PacketRef packet, const base::Closure& cb) final { if (paused_) { stored_packet_ = packet; callback_ = cb; return false; } ++packets_sent_; bytes_sent_ += packet->data.size(); return true; } int64_t GetBytesSent() final { return bytes_sent_; } void StartReceiving( const PacketReceiverCallbackWithStatus& packet_receiver) final {} void StopReceiving() final {} void SetPaused(bool paused) { paused_ = paused; if (!paused && stored_packet_.get()) { SendPacket(stored_packet_, callback_); callback_.Run(); } } int packets_sent() const { return packets_sent_; } private: bool paused_; base::Closure callback_; PacketRef stored_packet_; int packets_sent_; int64_t bytes_sent_; DISALLOW_COPY_AND_ASSIGN(FakePacketSender); }; class CastTransportSenderImplTest : public ::testing::Test { public: void ReceivedLoggingEvents() { num_times_logging_callback_called_++; } protected: CastTransportSenderImplTest() : num_times_logging_callback_called_(0) { testing_clock_.Advance( base::TimeDelta::FromMilliseconds(kStartMillisecond)); task_runner_ = new test::FakeSingleThreadTaskRunner(&testing_clock_); } ~CastTransportSenderImplTest() override {} void InitWithoutLogging(); void InitWithOptions(); void InitWithLogging(); void InitializeVideo() { CastTransportRtpConfig rtp_config; rtp_config.ssrc = kVideoSsrc; rtp_config.feedback_ssrc = 2; rtp_config.rtp_payload_type = 3; transport_sender_->InitializeVideo(rtp_config, RtcpCastMessageCallback(), RtcpRttCallback()); } void InitializeAudio() { CastTransportRtpConfig rtp_config; rtp_config.ssrc = kAudioSsrc; rtp_config.feedback_ssrc = 3; rtp_config.rtp_payload_type = 4; transport_sender_->InitializeAudio(rtp_config, RtcpCastMessageCallback(), RtcpRttCallback()); } base::SimpleTestTickClock testing_clock_; scoped_refptr task_runner_; scoped_ptr transport_sender_; FakePacketSender* transport_; // Owned by CastTransportSender. int num_times_logging_callback_called_; }; namespace { class TransportClient : public CastTransportSender::Client { public: explicit TransportClient( CastTransportSenderImplTest* cast_transport_sender_impl_test) : cast_transport_sender_impl_test_(cast_transport_sender_impl_test) {} void OnStatusChanged(CastTransportStatus status) final{}; void OnLoggingEventsReceived( scoped_ptr> frame_events, scoped_ptr> packet_events) final { CHECK(cast_transport_sender_impl_test_); cast_transport_sender_impl_test_->ReceivedLoggingEvents(); }; void ProcessRtpPacket(scoped_ptr packet) final{}; private: CastTransportSenderImplTest* const cast_transport_sender_impl_test_; DISALLOW_COPY_AND_ASSIGN(TransportClient); }; } // namespace void CastTransportSenderImplTest::InitWithoutLogging() { transport_ = new FakePacketSender(); transport_sender_.reset( new CastTransportSenderImpl(&testing_clock_, base::TimeDelta(), make_scoped_ptr(new TransportClient(nullptr)), make_scoped_ptr(transport_), task_runner_)); task_runner_->RunTasks(); } void CastTransportSenderImplTest::InitWithOptions() { scoped_ptr options(new base::DictionaryValue); options->SetBoolean("disable_wifi_scan", true); options->SetBoolean("media_streaming_mode", true); options->SetInteger("pacer_target_burst_size", 20); options->SetInteger("pacer_max_burst_size", 100); transport_ = new FakePacketSender(); transport_sender_.reset( new CastTransportSenderImpl(&testing_clock_, base::TimeDelta(), make_scoped_ptr(new TransportClient(nullptr)), make_scoped_ptr(transport_), task_runner_)); transport_sender_->SetOptions(*options); task_runner_->RunTasks(); } void CastTransportSenderImplTest::InitWithLogging() { transport_ = new FakePacketSender(); transport_sender_.reset(new CastTransportSenderImpl( &testing_clock_, base::TimeDelta::FromMilliseconds(10), make_scoped_ptr(new TransportClient(this)), make_scoped_ptr(transport_), task_runner_)); task_runner_->RunTasks(); } TEST_F(CastTransportSenderImplTest, InitWithoutLogging) { InitWithoutLogging(); task_runner_->Sleep(base::TimeDelta::FromMilliseconds(50)); EXPECT_EQ(0, num_times_logging_callback_called_); } TEST_F(CastTransportSenderImplTest, InitWithOptions) { InitWithOptions(); task_runner_->Sleep(base::TimeDelta::FromMilliseconds(50)); EXPECT_EQ(0, num_times_logging_callback_called_); } TEST_F(CastTransportSenderImplTest, NacksCancelRetransmits) { InitWithLogging(); InitializeVideo(); task_runner_->Sleep(base::TimeDelta::FromMilliseconds(50)); EXPECT_EQ(0, num_times_logging_callback_called_); // A fake frame that will be decomposed into 4 packets. EncodedFrame fake_frame; fake_frame.frame_id = 1; fake_frame.rtp_timestamp = RtpTimeTicks().Expand(UINT32_C(1)); fake_frame.dependency = EncodedFrame::KEY; fake_frame.data.resize(5000, ' '); transport_sender_->InsertFrame(kVideoSsrc, fake_frame); task_runner_->Sleep(base::TimeDelta::FromMilliseconds(10)); EXPECT_EQ(4, transport_->packets_sent()); EXPECT_EQ(1, num_times_logging_callback_called_); // Resend packet 0. MissingFramesAndPacketsMap missing_packets; missing_packets[1].insert(0); missing_packets[1].insert(1); missing_packets[1].insert(2); transport_->SetPaused(true); DedupInfo dedup_info; dedup_info.resend_interval = base::TimeDelta::FromMilliseconds(10); transport_sender_->ResendPackets( kVideoSsrc, missing_packets, true, dedup_info); task_runner_->Sleep(base::TimeDelta::FromMilliseconds(10)); EXPECT_EQ(2, num_times_logging_callback_called_); RtcpCastMessage cast_message; cast_message.media_ssrc = kVideoSsrc; cast_message.ack_frame_id = 1; cast_message.missing_frames_and_packets[1].insert(3); transport_sender_->OnReceivedCastMessage(kVideoSsrc, RtcpCastMessageCallback(), cast_message); transport_->SetPaused(false); task_runner_->Sleep(base::TimeDelta::FromMilliseconds(10)); EXPECT_EQ(3, num_times_logging_callback_called_); // Resend one packet in the socket when unpaused. // Resend one more packet from NACK. EXPECT_EQ(6, transport_->packets_sent()); } TEST_F(CastTransportSenderImplTest, CancelRetransmits) { InitWithLogging(); InitializeVideo(); task_runner_->Sleep(base::TimeDelta::FromMilliseconds(50)); EXPECT_EQ(0, num_times_logging_callback_called_); // A fake frame that will be decomposed into 4 packets. EncodedFrame fake_frame; fake_frame.frame_id = 1; fake_frame.rtp_timestamp = RtpTimeTicks().Expand(UINT32_C(1)); fake_frame.dependency = EncodedFrame::KEY; fake_frame.data.resize(5000, ' '); transport_sender_->InsertFrame(kVideoSsrc, fake_frame); task_runner_->Sleep(base::TimeDelta::FromMilliseconds(10)); EXPECT_EQ(4, transport_->packets_sent()); EXPECT_EQ(1, num_times_logging_callback_called_); // Resend all packets for frame 1. MissingFramesAndPacketsMap missing_packets; missing_packets[1].insert(kRtcpCastAllPacketsLost); transport_->SetPaused(true); DedupInfo dedup_info; dedup_info.resend_interval = base::TimeDelta::FromMilliseconds(10); transport_sender_->ResendPackets( kVideoSsrc, missing_packets, true, dedup_info); task_runner_->Sleep(base::TimeDelta::FromMilliseconds(10)); EXPECT_EQ(2, num_times_logging_callback_called_); std::vector cancel_sending_frames; cancel_sending_frames.push_back(1); transport_sender_->CancelSendingFrames(kVideoSsrc, cancel_sending_frames); transport_->SetPaused(false); task_runner_->Sleep(base::TimeDelta::FromMilliseconds(10)); EXPECT_EQ(2, num_times_logging_callback_called_); // Resend one packet in the socket when unpaused. EXPECT_EQ(5, transport_->packets_sent()); } TEST_F(CastTransportSenderImplTest, Kickstart) { InitWithLogging(); InitializeVideo(); task_runner_->Sleep(base::TimeDelta::FromMilliseconds(50)); EXPECT_EQ(0, num_times_logging_callback_called_); // A fake frame that will be decomposed into 4 packets. EncodedFrame fake_frame; fake_frame.frame_id = 1; fake_frame.rtp_timestamp = RtpTimeTicks().Expand(UINT32_C(1)); fake_frame.dependency = EncodedFrame::KEY; fake_frame.data.resize(5000, ' '); transport_->SetPaused(true); transport_sender_->InsertFrame(kVideoSsrc, fake_frame); transport_sender_->ResendFrameForKickstart(kVideoSsrc, 1); transport_->SetPaused(false); task_runner_->Sleep(base::TimeDelta::FromMilliseconds(10)); EXPECT_EQ(4, transport_->packets_sent()); EXPECT_EQ(1, num_times_logging_callback_called_); // Resend 2 packets for frame 1. MissingFramesAndPacketsMap missing_packets; missing_packets[1].insert(0); missing_packets[1].insert(1); transport_->SetPaused(true); DedupInfo dedup_info; dedup_info.resend_interval = base::TimeDelta::FromMilliseconds(10); transport_sender_->ResendPackets( kVideoSsrc, missing_packets, true, dedup_info); transport_sender_->ResendFrameForKickstart(kVideoSsrc, 1); transport_->SetPaused(false); task_runner_->Sleep(base::TimeDelta::FromMilliseconds(10)); EXPECT_EQ(2, num_times_logging_callback_called_); // Resend one packet in the socket when unpaused. // Two more retransmission packets sent. EXPECT_EQ(7, transport_->packets_sent()); } TEST_F(CastTransportSenderImplTest, DedupRetransmissionWithAudio) { InitWithLogging(); InitializeAudio(); InitializeVideo(); task_runner_->Sleep(base::TimeDelta::FromMilliseconds(50)); EXPECT_EQ(0, num_times_logging_callback_called_); // Send two audio frames. EncodedFrame fake_audio; fake_audio.frame_id = 1; fake_audio.reference_time = testing_clock_.NowTicks(); fake_audio.dependency = EncodedFrame::KEY; fake_audio.data.resize(100, ' '); transport_sender_->InsertFrame(kAudioSsrc, fake_audio); task_runner_->Sleep(base::TimeDelta::FromMilliseconds(2)); fake_audio.frame_id = 2; fake_audio.reference_time = testing_clock_.NowTicks(); transport_sender_->InsertFrame(kAudioSsrc, fake_audio); task_runner_->Sleep(base::TimeDelta::FromMilliseconds(2)); EXPECT_EQ(2, transport_->packets_sent()); // Ack the first audio frame. RtcpCastMessage cast_message; cast_message.media_ssrc = kAudioSsrc; cast_message.ack_frame_id = 1; transport_sender_->OnReceivedCastMessage(kAudioSsrc, RtcpCastMessageCallback(), cast_message); task_runner_->RunTasks(); EXPECT_EQ(2, transport_->packets_sent()); EXPECT_EQ(0, num_times_logging_callback_called_); // Only 4 ms since last. // Send a fake video frame that will be decomposed into 4 packets. EncodedFrame fake_video; fake_video.frame_id = 1; fake_video.dependency = EncodedFrame::KEY; fake_video.data.resize(5000, ' '); transport_sender_->InsertFrame(kVideoSsrc, fake_video); task_runner_->RunTasks(); EXPECT_EQ(6, transport_->packets_sent()); EXPECT_EQ(0, num_times_logging_callback_called_); // Only 4 ms since last. // Retransmission is reject because audio is not acked yet. cast_message.media_ssrc = kVideoSsrc; cast_message.ack_frame_id = 0; cast_message.missing_frames_and_packets[1].insert(3); task_runner_->Sleep(base::TimeDelta::FromMilliseconds(10)); transport_sender_->OnReceivedCastMessage(kVideoSsrc, RtcpCastMessageCallback(), cast_message); task_runner_->RunTasks(); EXPECT_EQ(6, transport_->packets_sent()); EXPECT_EQ(1, num_times_logging_callback_called_); // Ack the second audio frame. cast_message.media_ssrc = kAudioSsrc; cast_message.ack_frame_id = 2; cast_message.missing_frames_and_packets.clear(); task_runner_->Sleep(base::TimeDelta::FromMilliseconds(2)); transport_sender_->OnReceivedCastMessage(kAudioSsrc, RtcpCastMessageCallback(), cast_message); task_runner_->RunTasks(); EXPECT_EQ(6, transport_->packets_sent()); EXPECT_EQ(1, num_times_logging_callback_called_); // Only 6 ms since last. // Retransmission of video packet now accepted. cast_message.media_ssrc = kVideoSsrc; cast_message.ack_frame_id = 1; cast_message.missing_frames_and_packets[1].insert(3); task_runner_->Sleep(base::TimeDelta::FromMilliseconds(2)); transport_sender_->OnReceivedCastMessage(kVideoSsrc, RtcpCastMessageCallback(), cast_message); task_runner_->RunTasks(); EXPECT_EQ(7, transport_->packets_sent()); EXPECT_EQ(1, num_times_logging_callback_called_); // Only 8 ms since last. task_runner_->Sleep(base::TimeDelta::FromMilliseconds(2)); EXPECT_EQ(2, num_times_logging_callback_called_); } } // namespace cast } // namespace media