// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include <stdint.h>
+
#include <vector>
#include "base/bind.h"
#include "base/test/simple_test_tick_clock.h"
#include "media/base/video_frame.h"
#include "media/cast/cast_environment.h"
-#include "media/cast/test/fake_gpu_video_accelerator_factories.h"
+#include "media/cast/logging/simple_event_subscriber.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 "media/cast/transport/cast_transport_config.h"
#include "media/cast/transport/cast_transport_sender_impl.h"
namespace cast {
namespace {
-static const int64 kStartMillisecond = GG_INT64_C(12345678900000);
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<base::SingleThreadTaskRunner>& task_runner,
+ scoped_ptr<VideoEncodeAccelerator> fake_vea,
+ const ReceiveVideoEncodeAcceleratorCallback& callback) {
+ callback.Run(task_runner, fake_vea.Pass());
+}
+
+void CreateSharedMemory(
+ size_t size, const ReceiveVideoEncodeMemoryCallback& callback) {
+ scoped_ptr<base::SharedMemory> shm(new base::SharedMemory());
+ if (!shm->CreateAndMapAnonymous(size)) {
+ NOTREACHED();
+ return;
+ }
+ callback.Run(shm.Pass());
+}
+
class TestPacketSender : public transport::PacketSender {
public:
- TestPacketSender() : number_of_rtp_packets_(0), number_of_rtcp_packets_(0) {}
+ TestPacketSender()
+ : number_of_rtp_packets_(0),
+ number_of_rtcp_packets_(0),
+ paused_(false) {}
// A singular packet implies a RTCP packet.
- virtual bool SendPacket(const Packet& packet) OVERRIDE {
- if (Rtcp::IsRtcpPacket(&packet[0], packet.size())) {
+ virtual bool SendPacket(transport::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_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_;
+ transport::PacketRef stored_packet_;
DISALLOW_COPY_AND_ASSIGN(TestPacketSender);
};
-namespace {
class PeerVideoSender : public VideoSender {
public:
PeerVideoSender(
scoped_refptr<CastEnvironment> cast_environment,
const VideoSenderConfig& video_config,
- const scoped_refptr<GpuVideoAcceleratorFactories>& gpu_factories,
- const CastInitializationCallback& initialization_status,
+ const CreateVideoEncodeAcceleratorCallback& create_vea_cb,
+ const CreateVideoEncodeMemoryCallback& create_video_encode_mem_cb,
transport::CastTransportSender* const transport_sender)
: VideoSender(cast_environment,
video_config,
- gpu_factories,
- initialization_status,
+ create_vea_cb,
+ create_video_encode_mem_cb,
transport_sender) {}
using VideoSender::OnReceivedCastFeedback;
};
protected:
VideoSenderTest() {
testing_clock_ = new base::SimpleTestTickClock();
- testing_clock_->Advance(
- base::TimeDelta::FromMilliseconds(kStartMillisecond));
+ testing_clock_->Advance(base::TimeTicks::Now() - base::TimeTicks());
task_runner_ = new test::FakeSingleThreadTaskRunner(testing_clock_);
cast_environment_ =
new CastEnvironment(scoped_ptr<base::TickClock>(testing_clock_).Pass(),
task_runner_,
task_runner_,
- task_runner_,
- task_runner_,
- task_runner_,
- task_runner_,
- GetDefaultCastSenderLoggingConfig());
- transport::CastTransportConfig transport_config;
+ task_runner_);
+ last_pixel_value_ = kPixelValue;
+ net::IPEndPoint dummy_endpoint;
transport_sender_.reset(new transport::CastTransportSenderImpl(
+ NULL,
testing_clock_,
- transport_config,
+ dummy_endpoint,
base::Bind(&UpdateCastTransportStatus),
+ transport::BulkRawEventsCallback(),
+ base::TimeDelta(),
task_runner_,
&transport_));
}
}
static void UpdateCastTransportStatus(transport::CastTransportStatus status) {
- EXPECT_EQ(status, transport::TRANSPORT_INITIALIZED);
+ EXPECT_EQ(transport::TRANSPORT_VIDEO_INITIALIZED, status);
}
void InitEncoder(bool external) {
VideoSenderConfig video_config;
- video_config.sender_ssrc = 1;
+ video_config.rtp_config.ssrc = 1;
video_config.incoming_feedback_ssrc = 2;
video_config.rtcp_c_name = "video_test@10.1.1.1";
video_config.rtp_config.payload_type = 127;
video_config.codec = transport::kVp8;
if (external) {
- video_sender_.reset(new PeerVideoSender(
- cast_environment_,
- video_config,
- new test::FakeGpuVideoAcceleratorFactories(task_runner_),
- base::Bind(&VideoSenderTest::InitializationResult,
- base::Unretained(this)),
- transport_sender_.get()));
+ scoped_ptr<VideoEncodeAccelerator> 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,
- NULL,
- base::Bind(&VideoSenderTest::InitializationResult,
- base::Unretained(this)),
+ CreateDefaultVideoEncodeAcceleratorCallback(),
+ CreateDefaultVideoEncodeMemoryCallback(),
transport_sender_.get()));
}
+ ASSERT_EQ(STATUS_VIDEO_INITIALIZED, video_sender_->InitializationResult());
}
scoped_refptr<media::VideoFrame> GetNewVideoFrame() {
scoped_refptr<media::VideoFrame> video_frame =
media::VideoFrame::CreateFrame(
VideoFrame::I420, size, gfx::Rect(size), size, base::TimeDelta());
- PopulateVideoFrame(video_frame, kPixelValue);
+ PopulateVideoFrame(video_frame, last_pixel_value_++);
return video_frame;
}
- void RunTasks(int during_ms) {
- for (int i = 0; i < during_ms; ++i) {
- // Call process the timers every 1 ms.
- testing_clock_->Advance(base::TimeDelta::FromMilliseconds(1));
- task_runner_->RunTasks();
- }
+ scoped_refptr<media::VideoFrame> GetLargeNewVideoFrame() {
+ gfx::Size size(kWidth, kHeight);
+ scoped_refptr<media::VideoFrame> video_frame =
+ media::VideoFrame::CreateFrame(
+ VideoFrame::I420, size, gfx::Rect(size), size, base::TimeDelta());
+ PopulateVideoFrameWithNoise(video_frame);
+ return video_frame;
}
- void InitializationResult(CastInitializationStatus result) {
- EXPECT_EQ(result, STATUS_INITIALIZED);
+ void RunTasks(int during_ms) {
+ task_runner_->Sleep(base::TimeDelta::FromMilliseconds(during_ms));
}
base::SimpleTestTickClock* testing_clock_; // Owned by CastEnvironment.
scoped_refptr<test::FakeSingleThreadTaskRunner> task_runner_;
scoped_ptr<PeerVideoSender> video_sender_;
scoped_refptr<CastEnvironment> cast_environment_;
+ int last_pixel_value_;
DISALLOW_COPY_AND_ASSIGN(VideoSenderTest);
};
InitEncoder(false);
scoped_refptr<media::VideoFrame> video_frame = GetNewVideoFrame();
- base::TimeTicks capture_time;
+ const base::TimeTicks capture_time = testing_clock_->NowTicks();
video_sender_->InsertRawVideoFrame(video_frame, capture_time);
task_runner_->RunTasks();
- EXPECT_GE(
- transport_.number_of_rtp_packets() + transport_.number_of_rtcp_packets(),
- 1);
+ EXPECT_LE(1, transport_.number_of_rtp_packets());
+ EXPECT_LE(1, transport_.number_of_rtcp_packets());
}
TEST_F(VideoSenderTest, ExternalEncoder) {
scoped_refptr<media::VideoFrame> video_frame = GetNewVideoFrame();
- base::TimeTicks capture_time;
+ const base::TimeTicks capture_time = testing_clock_->NowTicks();
video_sender_->InsertRawVideoFrame(video_frame, capture_time);
task_runner_->RunTasks();
scoped_refptr<media::VideoFrame> video_frame = GetNewVideoFrame();
- base::TimeTicks capture_time;
+ 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::FromMilliseconds(1 + kDefaultRtcpIntervalMs * 3 / 2);
RunTasks(max_rtcp_timeout.InMilliseconds());
- EXPECT_GE(transport_.number_of_rtp_packets(), 1);
- EXPECT_GE(transport_.number_of_rtcp_packets(), 1);
+ 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) {
scoped_refptr<media::VideoFrame> video_frame = GetNewVideoFrame();
- base::TimeTicks capture_time;
+ const base::TimeTicks capture_time = testing_clock_->NowTicks();
video_sender_->InsertRawVideoFrame(video_frame, capture_time);
// ACK the key frame.
// Make sure that we do a re-send.
RunTasks(max_resend_timeout.InMilliseconds());
// Should have sent at least 3 packets.
- EXPECT_GE(
- transport_.number_of_rtp_packets() + transport_.number_of_rtcp_packets(),
- 3);
+ 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<media::VideoFrame> 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<FrameEvent> 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<media::VideoFrame> 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<media::VideoFrame> 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<media::VideoFrame> 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<media::VideoFrame> 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<media::VideoFrame> 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<media::VideoFrame> 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<media::VideoFrame> 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<media::VideoFrame> 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<media::VideoFrame> 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