blob: 2276e332b0a4511572404db97a47a5ee4a717df1 [file] [log] [blame]
// 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 <stdint.h>
#include <vector>
#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<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());
}
void SaveInitializationStatus(CastInitializationStatus* out_status,
CastInitializationStatus in_status) {
*out_status = in_status;
}
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.
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;
}
int64 GetBytesSent() override { return 0; }
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_.get()) {
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);
};
void IgnorePlayoutDelayChanges(base::TimeDelta unused_playout_delay) {
}
class PeerVideoSender : public VideoSender {
public:
PeerVideoSender(
scoped_refptr<CastEnvironment> cast_environment,
const VideoSenderConfig& video_config,
const CastInitializationCallback& initialization_cb,
const CreateVideoEncodeAcceleratorCallback& create_vea_cb,
const CreateVideoEncodeMemoryCallback& create_video_encode_mem_cb,
CastTransportSender* const transport_sender)
: VideoSender(cast_environment,
video_config,
initialization_cb,
create_vea_cb,
create_video_encode_mem_cb,
transport_sender,
base::Bind(&IgnorePlayoutDelayChanges)) {}
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<base::TickClock>(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,
make_scoped_ptr(new base::DictionaryValue),
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);
}
// If |external| is true then external video encoder (VEA) is used.
// |expect_init_sucess| is true if initialization is expected to succeed.
CastInitializationStatus InitEncoder(bool external,
bool expect_init_success) {
VideoSenderConfig video_config;
video_config.ssrc = 1;
video_config.incoming_feedback_ssrc = 2;
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;
CastInitializationStatus status = STATUS_VIDEO_UNINITIALIZED;
if (external) {
test::FakeVideoEncodeAccelerator* fake_vea =
new test::FakeVideoEncodeAccelerator(
task_runner_, &stored_bitrates_);
fake_vea->SetWillInitializationSucceed(expect_init_success);
scoped_ptr<VideoEncodeAccelerator> fake_vea_owner(fake_vea);
video_sender_.reset(
new PeerVideoSender(cast_environment_,
video_config,
base::Bind(&SaveInitializationStatus,
&status),
base::Bind(&CreateVideoEncodeAccelerator,
task_runner_,
base::Passed(&fake_vea_owner)),
base::Bind(&CreateSharedMemory),
transport_sender_.get()));
} else {
video_sender_.reset(
new PeerVideoSender(cast_environment_,
video_config,
base::Bind(&SaveInitializationStatus,
&status),
CreateDefaultVideoEncodeAcceleratorCallback(),
CreateDefaultVideoEncodeMemoryCallback(),
transport_sender_.get()));
}
task_runner_->RunTasks();
return status;
}
scoped_refptr<media::VideoFrame> GetNewVideoFrame() {
if (first_frame_timestamp_.is_null())
first_frame_timestamp_ = testing_clock_->NowTicks();
gfx::Size size(kWidth, kHeight);
scoped_refptr<media::VideoFrame> video_frame =
media::VideoFrame::CreateFrame(
VideoFrame::I420, size, gfx::Rect(size), size,
testing_clock_->NowTicks() - first_frame_timestamp_);
PopulateVideoFrame(video_frame.get(), last_pixel_value_++);
return video_frame;
}
scoped_refptr<media::VideoFrame> GetLargeNewVideoFrame() {
if (first_frame_timestamp_.is_null())
first_frame_timestamp_ = testing_clock_->NowTicks();
gfx::Size size(kWidth, kHeight);
scoped_refptr<media::VideoFrame> video_frame =
media::VideoFrame::CreateFrame(
VideoFrame::I420, size, gfx::Rect(size), size,
testing_clock_->NowTicks() - first_frame_timestamp_);
PopulateVideoFrameWithNoise(video_frame.get());
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<CastTransportSenderImpl> transport_sender_;
scoped_refptr<test::FakeSingleThreadTaskRunner> task_runner_;
scoped_ptr<PeerVideoSender> video_sender_;
std::vector<uint32> stored_bitrates_;
scoped_refptr<CastEnvironment> cast_environment_;
int last_pixel_value_;
base::TimeTicks first_frame_timestamp_;
DISALLOW_COPY_AND_ASSIGN(VideoSenderTest);
};
TEST_F(VideoSenderTest, BuiltInEncoder) {
EXPECT_EQ(STATUS_VIDEO_INITIALIZED, InitEncoder(false, true));
scoped_refptr<media::VideoFrame> video_frame = GetNewVideoFrame();
const base::TimeTicks reference_time = testing_clock_->NowTicks();
video_sender_->InsertRawVideoFrame(video_frame, reference_time);
task_runner_->RunTasks();
EXPECT_LE(1, transport_.number_of_rtp_packets());
EXPECT_LE(1, transport_.number_of_rtcp_packets());
}
TEST_F(VideoSenderTest, ExternalEncoder) {
EXPECT_EQ(STATUS_VIDEO_INITIALIZED, InitEncoder(true, true));
scoped_refptr<media::VideoFrame> video_frame = GetNewVideoFrame();
const base::TimeTicks reference_time = testing_clock_->NowTicks();
video_sender_->InsertRawVideoFrame(video_frame, reference_time);
task_runner_->RunTasks();
video_sender_->InsertRawVideoFrame(video_frame, reference_time);
task_runner_->RunTasks();
video_sender_->InsertRawVideoFrame(video_frame, reference_time);
task_runner_->RunTasks();
// Fixed bitrate is used for external encoder. Bitrate is only once
// to the encoder.
EXPECT_EQ(1u, stored_bitrates_.size());
video_sender_.reset(NULL);
task_runner_->RunTasks();
}
TEST_F(VideoSenderTest, ExternalEncoderInitFails) {
EXPECT_EQ(STATUS_HW_VIDEO_ENCODER_NOT_SUPPORTED,
InitEncoder(true, false));
video_sender_.reset(NULL);
task_runner_->RunTasks();
}
TEST_F(VideoSenderTest, RtcpTimer) {
EXPECT_EQ(STATUS_VIDEO_INITIALIZED, InitEncoder(false, true));
scoped_refptr<media::VideoFrame> video_frame = GetNewVideoFrame();
const base::TimeTicks reference_time = testing_clock_->NowTicks();
video_sender_->InsertRawVideoFrame(video_frame, reference_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) {
EXPECT_EQ(STATUS_VIDEO_INITIALIZED, InitEncoder(false, true));
scoped_refptr<media::VideoFrame> video_frame = GetNewVideoFrame();
const base::TimeTicks reference_time = testing_clock_->NowTicks();
video_sender_->InsertRawVideoFrame(video_frame, reference_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, reference_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) {
EXPECT_EQ(STATUS_VIDEO_INITIALIZED, InitEncoder(false, true));
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 reference_time = testing_clock_->NowTicks();
video_sender_->InsertRawVideoFrame(video_frame, reference_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) {
EXPECT_EQ(STATUS_VIDEO_INITIALIZED, InitEncoder(false, true));
// 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) {
EXPECT_EQ(STATUS_VIDEO_INITIALIZED, InitEncoder(false, true));
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) {
EXPECT_EQ(STATUS_VIDEO_INITIALIZED, InitEncoder(false, true));
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) {
EXPECT_EQ(STATUS_VIDEO_INITIALIZED, InitEncoder(false, true));
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());
}
} // namespace cast
} // namespace media