blob: 7b951578e295e20857afe993651d1cb3e613f54a [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 <gtest/gtest.h>
#include <stdint.h>
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/memory/scoped_ptr.h"
#include "base/test/simple_test_tick_clock.h"
#include "base/values.h"
#include "media/cast/cast_config.h"
#include "media/cast/net/cast_transport_config.h"
#include "media/cast/net/cast_transport_sender_impl.h"
#include "media/cast/net/rtcp/rtcp.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 kStartMillisecond = INT64_C(12345678900000);
const uint32 kVideoSsrc = 1;
const uint32 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) override {
if (paused_) {
stored_packet_ = packet;
callback_ = cb;
return false;
}
++packets_sent_;
bytes_sent_ += packet->data.size();
return true;
}
int64 GetBytesSent() override { return bytes_sent_; }
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 bytes_sent_;
DISALLOW_COPY_AND_ASSIGN(FakePacketSender);
};
class CastTransportSenderImplTest : public ::testing::Test {
protected:
CastTransportSenderImplTest()
: num_times_callback_called_(0) {
testing_clock_.Advance(
base::TimeDelta::FromMilliseconds(kStartMillisecond));
task_runner_ = new test::FakeSingleThreadTaskRunner(&testing_clock_);
}
~CastTransportSenderImplTest() override {}
void InitWithoutLogging() {
transport_sender_.reset(
new CastTransportSenderImpl(NULL,
&testing_clock_,
net::IPEndPoint(),
make_scoped_ptr(new base::DictionaryValue),
base::Bind(&UpdateCastTransportStatus),
BulkRawEventsCallback(),
base::TimeDelta(),
task_runner_,
&transport_));
task_runner_->RunTasks();
}
void InitWithOptions() {
scoped_ptr<base::DictionaryValue> options(
new base::DictionaryValue);
options->SetBoolean("DHCP", true);
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_sender_.reset(
new CastTransportSenderImpl(NULL,
&testing_clock_,
net::IPEndPoint(),
options.Pass(),
base::Bind(&UpdateCastTransportStatus),
BulkRawEventsCallback(),
base::TimeDelta(),
task_runner_,
&transport_));
task_runner_->RunTasks();
}
void InitWithLogging() {
transport_sender_.reset(new CastTransportSenderImpl(
NULL,
&testing_clock_,
net::IPEndPoint(),
make_scoped_ptr(new base::DictionaryValue),
base::Bind(&UpdateCastTransportStatus),
base::Bind(&CastTransportSenderImplTest::LogRawEvents,
base::Unretained(this)),
base::TimeDelta::FromMilliseconds(10),
task_runner_,
&transport_));
task_runner_->RunTasks();
}
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());
}
void LogRawEvents(const std::vector<PacketEvent>& packet_events,
const std::vector<FrameEvent>& frame_events) {
num_times_callback_called_++;
}
static void UpdateCastTransportStatus(CastTransportStatus status) {
}
base::SimpleTestTickClock testing_clock_;
scoped_refptr<test::FakeSingleThreadTaskRunner> task_runner_;
scoped_ptr<CastTransportSenderImpl> transport_sender_;
FakePacketSender transport_;
int num_times_callback_called_;
};
TEST_F(CastTransportSenderImplTest, InitWithoutLogging) {
InitWithoutLogging();
task_runner_->Sleep(base::TimeDelta::FromMilliseconds(50));
EXPECT_EQ(0, num_times_callback_called_);
}
TEST_F(CastTransportSenderImplTest, InitWithLogging) {
InitWithLogging();
task_runner_->Sleep(base::TimeDelta::FromMilliseconds(50));
EXPECT_EQ(5, num_times_callback_called_);
}
TEST_F(CastTransportSenderImplTest, InitWithOptions) {
InitWithOptions();
task_runner_->Sleep(base::TimeDelta::FromMilliseconds(50));
EXPECT_EQ(0, num_times_callback_called_);
}
TEST_F(CastTransportSenderImplTest, NacksCancelRetransmits) {
InitWithoutLogging();
InitializeVideo();
task_runner_->Sleep(base::TimeDelta::FromMilliseconds(50));
// A fake frame that will be decomposed into 4 packets.
EncodedFrame fake_frame;
fake_frame.frame_id = 1;
fake_frame.rtp_timestamp = 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());
// 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));
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));
// Resend one packet in the socket when unpaused.
// Resend one more packet from NACK.
EXPECT_EQ(6, transport_.packets_sent());
}
TEST_F(CastTransportSenderImplTest, CancelRetransmits) {
InitWithoutLogging();
InitializeVideo();
task_runner_->Sleep(base::TimeDelta::FromMilliseconds(50));
// A fake frame that will be decomposed into 4 packets.
EncodedFrame fake_frame;
fake_frame.frame_id = 1;
fake_frame.rtp_timestamp = 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());
// 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));
std::vector<uint32> 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));
// Resend one packet in the socket when unpaused.
EXPECT_EQ(5, transport_.packets_sent());
}
TEST_F(CastTransportSenderImplTest, Kickstart) {
InitWithoutLogging();
InitializeVideo();
task_runner_->Sleep(base::TimeDelta::FromMilliseconds(50));
// A fake frame that will be decomposed into 4 packets.
EncodedFrame fake_frame;
fake_frame.frame_id = 1;
fake_frame.rtp_timestamp = 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());
// 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));
// Resend one packet in the socket when unpaused.
// Two more retransmission packets sent.
EXPECT_EQ(7, transport_.packets_sent());
}
TEST_F(CastTransportSenderImplTest, DedupRetransmissionWithAudio) {
InitWithoutLogging();
InitializeAudio();
InitializeVideo();
task_runner_->Sleep(base::TimeDelta::FromMilliseconds(50));
// 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());
// 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());
// 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());
// 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());
// 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());
}
} // namespace cast
} // namespace media