// 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 #include #include "base/bind.h" #include "base/memory/scoped_ptr.h" #include "base/test/simple_test_tick_clock.h" #include "media/base/video_frame.h" #include "media/cast/cast_environment.h" #include "media/cast/logging/simple_event_subscriber.h" #include "media/cast/net/cast_transport_config.h" #include "media/cast/net/cast_transport_sender_impl.h" #include "media/cast/net/pacing/paced_sender.h" #include "media/cast/sender/video_sender.h" #include "media/cast/test/fake_single_thread_task_runner.h" #include "media/cast/test/fake_video_encode_accelerator.h" #include "media/cast/test/utility/default_config.h" #include "media/cast/test/utility/video_utility.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" namespace media { namespace cast { namespace { static const uint8 kPixelValue = 123; static const int kWidth = 320; static const int kHeight = 240; using testing::_; using testing::AtLeast; void CreateVideoEncodeAccelerator( const scoped_refptr& task_runner, scoped_ptr fake_vea, const ReceiveVideoEncodeAcceleratorCallback& callback) { callback.Run(task_runner, fake_vea.Pass()); } void CreateSharedMemory( size_t size, const ReceiveVideoEncodeMemoryCallback& callback) { scoped_ptr shm(new base::SharedMemory()); if (!shm->CreateAndMapAnonymous(size)) { NOTREACHED(); return; } callback.Run(shm.Pass()); } class TestPacketSender : public PacketSender { public: TestPacketSender() : number_of_rtp_packets_(0), number_of_rtcp_packets_(0), paused_(false) {} // A singular packet implies a RTCP packet. virtual bool SendPacket(PacketRef packet, const base::Closure& cb) OVERRIDE { if (paused_) { stored_packet_ = packet; callback_ = cb; return false; } if (Rtcp::IsRtcpPacket(&packet->data[0], packet->data.size())) { ++number_of_rtcp_packets_; } else { // Check that at least one RTCP packet was sent before the first RTP // packet. This confirms that the receiver will have the necessary lip // sync info before it has to calculate the playout time of the first // frame. if (number_of_rtp_packets_ == 0) EXPECT_LE(1, number_of_rtcp_packets_); ++number_of_rtp_packets_; } return true; } int number_of_rtp_packets() const { return number_of_rtp_packets_; } int number_of_rtcp_packets() const { return number_of_rtcp_packets_; } void SetPause(bool paused) { paused_ = paused; if (!paused && stored_packet_) { SendPacket(stored_packet_, callback_); callback_.Run(); } } private: int number_of_rtp_packets_; int number_of_rtcp_packets_; bool paused_; base::Closure callback_; PacketRef stored_packet_; DISALLOW_COPY_AND_ASSIGN(TestPacketSender); }; class PeerVideoSender : public VideoSender { public: PeerVideoSender( scoped_refptr cast_environment, const VideoSenderConfig& video_config, const CreateVideoEncodeAcceleratorCallback& create_vea_cb, const CreateVideoEncodeMemoryCallback& create_video_encode_mem_cb, CastTransportSender* const transport_sender) : VideoSender(cast_environment, video_config, create_vea_cb, create_video_encode_mem_cb, transport_sender) {} using VideoSender::OnReceivedCastFeedback; }; } // namespace class VideoSenderTest : public ::testing::Test { protected: VideoSenderTest() { testing_clock_ = new base::SimpleTestTickClock(); testing_clock_->Advance(base::TimeTicks::Now() - base::TimeTicks()); task_runner_ = new test::FakeSingleThreadTaskRunner(testing_clock_); cast_environment_ = new CastEnvironment(scoped_ptr(testing_clock_).Pass(), task_runner_, task_runner_, task_runner_); last_pixel_value_ = kPixelValue; net::IPEndPoint dummy_endpoint; transport_sender_.reset(new CastTransportSenderImpl( NULL, testing_clock_, dummy_endpoint, base::Bind(&UpdateCastTransportStatus), BulkRawEventsCallback(), base::TimeDelta(), task_runner_, &transport_)); } virtual ~VideoSenderTest() {} virtual void TearDown() OVERRIDE { video_sender_.reset(); task_runner_->RunTasks(); } static void UpdateCastTransportStatus(CastTransportStatus status) { EXPECT_EQ(TRANSPORT_VIDEO_INITIALIZED, status); } void InitEncoder(bool external) { VideoSenderConfig video_config; video_config.ssrc = 1; video_config.incoming_feedback_ssrc = 2; video_config.rtcp_c_name = "video_test@10.1.1.1"; video_config.rtp_payload_type = 127; video_config.use_external_encoder = external; video_config.width = kWidth; video_config.height = kHeight; video_config.max_bitrate = 5000000; video_config.min_bitrate = 1000000; video_config.start_bitrate = 1000000; video_config.max_qp = 56; video_config.min_qp = 0; video_config.max_frame_rate = 30; video_config.max_number_of_video_buffers_used = 1; video_config.codec = CODEC_VIDEO_VP8; if (external) { scoped_ptr fake_vea( new test::FakeVideoEncodeAccelerator(task_runner_)); video_sender_.reset( new PeerVideoSender(cast_environment_, video_config, base::Bind(&CreateVideoEncodeAccelerator, task_runner_, base::Passed(&fake_vea)), base::Bind(&CreateSharedMemory), transport_sender_.get())); } else { video_sender_.reset( new PeerVideoSender(cast_environment_, video_config, CreateDefaultVideoEncodeAcceleratorCallback(), CreateDefaultVideoEncodeMemoryCallback(), transport_sender_.get())); } ASSERT_EQ(STATUS_VIDEO_INITIALIZED, video_sender_->InitializationResult()); } scoped_refptr GetNewVideoFrame() { gfx::Size size(kWidth, kHeight); scoped_refptr video_frame = media::VideoFrame::CreateFrame( VideoFrame::I420, size, gfx::Rect(size), size, base::TimeDelta()); PopulateVideoFrame(video_frame, last_pixel_value_++); return video_frame; } scoped_refptr GetLargeNewVideoFrame() { gfx::Size size(kWidth, kHeight); scoped_refptr video_frame = media::VideoFrame::CreateFrame( VideoFrame::I420, size, gfx::Rect(size), size, base::TimeDelta()); PopulateVideoFrameWithNoise(video_frame); return video_frame; } void RunTasks(int during_ms) { task_runner_->Sleep(base::TimeDelta::FromMilliseconds(during_ms)); } base::SimpleTestTickClock* testing_clock_; // Owned by CastEnvironment. TestPacketSender transport_; scoped_ptr transport_sender_; scoped_refptr task_runner_; scoped_ptr video_sender_; scoped_refptr cast_environment_; int last_pixel_value_; DISALLOW_COPY_AND_ASSIGN(VideoSenderTest); }; TEST_F(VideoSenderTest, BuiltInEncoder) { InitEncoder(false); scoped_refptr video_frame = GetNewVideoFrame(); const base::TimeTicks capture_time = testing_clock_->NowTicks(); video_sender_->InsertRawVideoFrame(video_frame, capture_time); task_runner_->RunTasks(); EXPECT_LE(1, transport_.number_of_rtp_packets()); EXPECT_LE(1, transport_.number_of_rtcp_packets()); } TEST_F(VideoSenderTest, ExternalEncoder) { InitEncoder(true); task_runner_->RunTasks(); scoped_refptr video_frame = GetNewVideoFrame(); const base::TimeTicks capture_time = testing_clock_->NowTicks(); video_sender_->InsertRawVideoFrame(video_frame, capture_time); task_runner_->RunTasks(); // We need to run the task to cleanup the GPU instance. video_sender_.reset(NULL); task_runner_->RunTasks(); } TEST_F(VideoSenderTest, RtcpTimer) { InitEncoder(false); scoped_refptr video_frame = GetNewVideoFrame(); const base::TimeTicks capture_time = testing_clock_->NowTicks(); video_sender_->InsertRawVideoFrame(video_frame, capture_time); // Make sure that we send at least one RTCP packet. base::TimeDelta max_rtcp_timeout = base::TimeDelta::FromMilliseconds(1 + kDefaultRtcpIntervalMs * 3 / 2); RunTasks(max_rtcp_timeout.InMilliseconds()); EXPECT_LE(1, transport_.number_of_rtp_packets()); EXPECT_LE(1, transport_.number_of_rtcp_packets()); // Build Cast msg and expect RTCP packet. RtcpCastMessage cast_feedback(1); cast_feedback.media_ssrc_ = 2; cast_feedback.ack_frame_id_ = 0; video_sender_->OnReceivedCastFeedback(cast_feedback); RunTasks(max_rtcp_timeout.InMilliseconds()); EXPECT_LE(1, transport_.number_of_rtcp_packets()); } TEST_F(VideoSenderTest, ResendTimer) { InitEncoder(false); scoped_refptr video_frame = GetNewVideoFrame(); const base::TimeTicks capture_time = testing_clock_->NowTicks(); video_sender_->InsertRawVideoFrame(video_frame, capture_time); // ACK the key frame. RtcpCastMessage cast_feedback(1); cast_feedback.media_ssrc_ = 2; cast_feedback.ack_frame_id_ = 0; video_sender_->OnReceivedCastFeedback(cast_feedback); video_frame = GetNewVideoFrame(); video_sender_->InsertRawVideoFrame(video_frame, capture_time); base::TimeDelta max_resend_timeout = base::TimeDelta::FromMilliseconds(1 + kDefaultRtpMaxDelayMs); // Make sure that we do a re-send. RunTasks(max_resend_timeout.InMilliseconds()); // Should have sent at least 3 packets. EXPECT_LE( 3, transport_.number_of_rtp_packets() + transport_.number_of_rtcp_packets()); } TEST_F(VideoSenderTest, LogAckReceivedEvent) { InitEncoder(false); SimpleEventSubscriber event_subscriber; cast_environment_->Logging()->AddRawEventSubscriber(&event_subscriber); int num_frames = 10; for (int i = 0; i < num_frames; i++) { scoped_refptr video_frame = GetNewVideoFrame(); const base::TimeTicks capture_time = testing_clock_->NowTicks(); video_sender_->InsertRawVideoFrame(video_frame, capture_time); RunTasks(33); } task_runner_->RunTasks(); RtcpCastMessage cast_feedback(1); cast_feedback.ack_frame_id_ = num_frames - 1; video_sender_->OnReceivedCastFeedback(cast_feedback); std::vector frame_events; event_subscriber.GetFrameEventsAndReset(&frame_events); ASSERT_TRUE(!frame_events.empty()); EXPECT_EQ(FRAME_ACK_RECEIVED, frame_events.rbegin()->type); EXPECT_EQ(VIDEO_EVENT, frame_events.rbegin()->media_type); EXPECT_EQ(num_frames - 1u, frame_events.rbegin()->frame_id); cast_environment_->Logging()->RemoveRawEventSubscriber(&event_subscriber); } TEST_F(VideoSenderTest, StopSendingInTheAbsenceOfAck) { InitEncoder(false); // Send a stream of frames and don't ACK; by default we shouldn't have more // than 4 frames in flight. scoped_refptr video_frame = GetNewVideoFrame(); video_sender_->InsertRawVideoFrame(video_frame, testing_clock_->NowTicks()); RunTasks(33); // Send 3 more frames and record the number of packets sent. for (int i = 0; i < 3; ++i) { scoped_refptr video_frame = GetNewVideoFrame(); video_sender_->InsertRawVideoFrame(video_frame, testing_clock_->NowTicks()); RunTasks(33); } const int number_of_packets_sent = transport_.number_of_rtp_packets(); // Send 3 more frames - they should not be encoded, as we have not received // any acks. for (int i = 0; i < 3; ++i) { scoped_refptr video_frame = GetNewVideoFrame(); video_sender_->InsertRawVideoFrame(video_frame, testing_clock_->NowTicks()); RunTasks(33); } // We expect a frame to be retransmitted because of duplicated ACKs. // Only one packet of the frame is re-transmitted. EXPECT_EQ(number_of_packets_sent + 1, transport_.number_of_rtp_packets()); // Start acking and make sure we're back to steady-state. RtcpCastMessage cast_feedback(1); cast_feedback.media_ssrc_ = 2; cast_feedback.ack_frame_id_ = 0; video_sender_->OnReceivedCastFeedback(cast_feedback); EXPECT_LE( 4, transport_.number_of_rtp_packets() + transport_.number_of_rtcp_packets()); // Empty the pipeline. RunTasks(100); // Should have sent at least 7 packets. EXPECT_LE( 7, transport_.number_of_rtp_packets() + transport_.number_of_rtcp_packets()); } TEST_F(VideoSenderTest, DuplicateAckRetransmit) { InitEncoder(false); scoped_refptr video_frame = GetNewVideoFrame(); video_sender_->InsertRawVideoFrame(video_frame, testing_clock_->NowTicks()); RunTasks(33); RtcpCastMessage cast_feedback(1); cast_feedback.media_ssrc_ = 2; cast_feedback.ack_frame_id_ = 0; // Send 3 more frames but don't ACK. for (int i = 0; i < 3; ++i) { scoped_refptr video_frame = GetNewVideoFrame(); video_sender_->InsertRawVideoFrame(video_frame, testing_clock_->NowTicks()); RunTasks(33); } const int number_of_packets_sent = transport_.number_of_rtp_packets(); // Send duplicated ACKs and mix some invalid NACKs. for (int i = 0; i < 10; ++i) { RtcpCastMessage ack_feedback(1); ack_feedback.media_ssrc_ = 2; ack_feedback.ack_frame_id_ = 0; RtcpCastMessage nack_feedback(1); nack_feedback.media_ssrc_ = 2; nack_feedback.missing_frames_and_packets_[255] = PacketIdSet(); video_sender_->OnReceivedCastFeedback(ack_feedback); video_sender_->OnReceivedCastFeedback(nack_feedback); } EXPECT_EQ(number_of_packets_sent, transport_.number_of_rtp_packets()); // Re-transmit one packet because of duplicated ACKs. for (int i = 0; i < 3; ++i) { RtcpCastMessage ack_feedback(1); ack_feedback.media_ssrc_ = 2; ack_feedback.ack_frame_id_ = 0; video_sender_->OnReceivedCastFeedback(ack_feedback); } EXPECT_EQ(number_of_packets_sent + 1, transport_.number_of_rtp_packets()); } TEST_F(VideoSenderTest, DuplicateAckRetransmitDoesNotCancelRetransmits) { InitEncoder(false); scoped_refptr video_frame = GetNewVideoFrame(); video_sender_->InsertRawVideoFrame(video_frame, testing_clock_->NowTicks()); RunTasks(33); RtcpCastMessage cast_feedback(1); cast_feedback.media_ssrc_ = 2; cast_feedback.ack_frame_id_ = 0; // Send 2 more frames but don't ACK. for (int i = 0; i < 2; ++i) { scoped_refptr video_frame = GetNewVideoFrame(); video_sender_->InsertRawVideoFrame(video_frame, testing_clock_->NowTicks()); RunTasks(33); } // Pause the transport transport_.SetPause(true); // Insert one more video frame. video_frame = GetLargeNewVideoFrame(); video_sender_->InsertRawVideoFrame(video_frame, testing_clock_->NowTicks()); RunTasks(33); const int number_of_packets_sent = transport_.number_of_rtp_packets(); // Send duplicated ACKs and mix some invalid NACKs. for (int i = 0; i < 10; ++i) { RtcpCastMessage ack_feedback(1); ack_feedback.media_ssrc_ = 2; ack_feedback.ack_frame_id_ = 0; RtcpCastMessage nack_feedback(1); nack_feedback.media_ssrc_ = 2; nack_feedback.missing_frames_and_packets_[255] = PacketIdSet(); video_sender_->OnReceivedCastFeedback(ack_feedback); video_sender_->OnReceivedCastFeedback(nack_feedback); } EXPECT_EQ(number_of_packets_sent, transport_.number_of_rtp_packets()); // Re-transmit one packet because of duplicated ACKs. for (int i = 0; i < 3; ++i) { RtcpCastMessage ack_feedback(1); ack_feedback.media_ssrc_ = 2; ack_feedback.ack_frame_id_ = 0; video_sender_->OnReceivedCastFeedback(ack_feedback); } transport_.SetPause(false); RunTasks(100); EXPECT_LT(number_of_packets_sent + 1, transport_.number_of_rtp_packets()); } TEST_F(VideoSenderTest, AcksCancelRetransmits) { InitEncoder(false); transport_.SetPause(true); scoped_refptr video_frame = GetLargeNewVideoFrame(); video_sender_->InsertRawVideoFrame(video_frame, testing_clock_->NowTicks()); RunTasks(33); // Frame should be in buffer, waiting. Now let's ack it. RtcpCastMessage cast_feedback(1); cast_feedback.media_ssrc_ = 2; cast_feedback.ack_frame_id_ = 0; video_sender_->OnReceivedCastFeedback(cast_feedback); transport_.SetPause(false); RunTasks(33); EXPECT_EQ(0, transport_.number_of_rtp_packets()); } TEST_F(VideoSenderTest, NAcksCancelRetransmits) { InitEncoder(false); transport_.SetPause(true); // Send two video frames. scoped_refptr video_frame = GetLargeNewVideoFrame(); video_sender_->InsertRawVideoFrame(video_frame, testing_clock_->NowTicks()); RunTasks(33); video_frame = GetLargeNewVideoFrame(); video_sender_->InsertRawVideoFrame(video_frame, testing_clock_->NowTicks()); RunTasks(33); // Frames should be in buffer, waiting. Now let's ack the first one and nack // one packet in the second one. RtcpCastMessage cast_feedback(1); cast_feedback.media_ssrc_ = 2; cast_feedback.ack_frame_id_ = 0; PacketIdSet missing_packets; missing_packets.insert(0); cast_feedback.missing_frames_and_packets_[1] = missing_packets; video_sender_->OnReceivedCastFeedback(cast_feedback); transport_.SetPause(false); RunTasks(33); // Only one packet should be retransmitted. EXPECT_EQ(1, transport_.number_of_rtp_packets()); } } // namespace cast } // namespace media