blob: 4992d29b3f98eeaac0d991aec29010bb0595b711 [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 "media/cast/net/cast_transport_impl.h"
#include <gtest/gtest.h>
#include <stdint.h>
#include <memory>
#include <utility>
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/macros.h"
#include "base/memory/ptr_util.h"
#include "base/test/simple_test_tick_clock.h"
#include "base/values.h"
#include "media/base/fake_single_thread_task_runner.h"
#include "media/cast/net/cast_transport_config.h"
#include "media/cast/net/rtcp/rtcp_defines.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;
class StubRtcpObserver : public RtcpObserver {
public:
StubRtcpObserver() = default;
void OnReceivedCastMessage(const RtcpCastMessage& cast_message) final {}
void OnReceivedRtt(base::TimeDelta round_trip_time) final {}
void OnReceivedPli() final {}
DISALLOW_COPY_AND_ASSIGN(StubRtcpObserver);
};
} // namespace
class FakePacketSender : public PacketTransport {
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 CastTransportImplTest : public ::testing::Test {
public:
void ReceivedLoggingEvents() { num_times_logging_callback_called_++; }
protected:
CastTransportImplTest() : num_times_logging_callback_called_(0) {
testing_clock_.Advance(
base::TimeDelta::FromMilliseconds(kStartMillisecond));
task_runner_ = new FakeSingleThreadTaskRunner(&testing_clock_);
}
~CastTransportImplTest() override = default;
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 = RtpPayloadType::VIDEO_VP8;
transport_sender_->InitializeStream(rtp_config,
std::make_unique<StubRtcpObserver>());
}
void InitializeAudio() {
CastTransportRtpConfig rtp_config;
rtp_config.ssrc = kAudioSsrc;
rtp_config.feedback_ssrc = 3;
rtp_config.rtp_payload_type = RtpPayloadType::AUDIO_OPUS;
transport_sender_->InitializeStream(rtp_config,
std::make_unique<StubRtcpObserver>());
}
base::SimpleTestTickClock testing_clock_;
scoped_refptr<FakeSingleThreadTaskRunner> task_runner_;
std::unique_ptr<CastTransportImpl> transport_sender_;
FakePacketSender* transport_; // Owned by CastTransport.
int num_times_logging_callback_called_;
};
namespace {
class TransportClient : public CastTransport::Client {
public:
explicit TransportClient(
CastTransportImplTest* cast_transport_sender_impl_test)
: cast_transport_sender_impl_test_(cast_transport_sender_impl_test) {}
void OnStatusChanged(CastTransportStatus status) final{};
void OnLoggingEventsReceived(
std::unique_ptr<std::vector<FrameEvent>> frame_events,
std::unique_ptr<std::vector<PacketEvent>> packet_events) final {
CHECK(cast_transport_sender_impl_test_);
cast_transport_sender_impl_test_->ReceivedLoggingEvents();
};
void ProcessRtpPacket(std::unique_ptr<Packet> packet) final {}
private:
CastTransportImplTest* const cast_transport_sender_impl_test_;
DISALLOW_COPY_AND_ASSIGN(TransportClient);
};
} // namespace
void CastTransportImplTest::InitWithoutLogging() {
transport_ = new FakePacketSender();
transport_sender_.reset(
new CastTransportImpl(&testing_clock_, base::TimeDelta(),
std::make_unique<TransportClient>(nullptr),
base::WrapUnique(transport_), task_runner_));
task_runner_->RunTasks();
}
void CastTransportImplTest::InitWithOptions() {
std::unique_ptr<base::DictionaryValue> 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 CastTransportImpl(&testing_clock_, base::TimeDelta(),
std::make_unique<TransportClient>(nullptr),
base::WrapUnique(transport_), task_runner_));
transport_sender_->SetOptions(*options);
task_runner_->RunTasks();
}
void CastTransportImplTest::InitWithLogging() {
transport_ = new FakePacketSender();
transport_sender_.reset(new CastTransportImpl(
&testing_clock_, base::TimeDelta::FromMilliseconds(10),
std::make_unique<TransportClient>(this), base::WrapUnique(transport_),
task_runner_));
task_runner_->RunTasks();
}
TEST_F(CastTransportImplTest, InitWithoutLogging) {
InitWithoutLogging();
task_runner_->Sleep(base::TimeDelta::FromMilliseconds(50));
EXPECT_EQ(0, num_times_logging_callback_called_);
}
TEST_F(CastTransportImplTest, InitWithOptions) {
InitWithOptions();
task_runner_->Sleep(base::TimeDelta::FromMilliseconds(50));
EXPECT_EQ(0, num_times_logging_callback_called_);
}
TEST_F(CastTransportImplTest, 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 = FrameId::first() + 1;
fake_frame.referenced_frame_id = FrameId::first() + 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[fake_frame.frame_id].insert(0);
missing_packets[fake_frame.frame_id].insert(1);
missing_packets[fake_frame.frame_id].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.remote_ssrc = kVideoSsrc;
cast_message.ack_frame_id = FrameId::first() + 1;
cast_message.missing_frames_and_packets[fake_frame.frame_id].insert(3);
transport_sender_->OnReceivedCastMessage(kVideoSsrc, 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(CastTransportImplTest, 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 = FrameId::first() + 1;
fake_frame.referenced_frame_id = FrameId::first() + 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[fake_frame.frame_id].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<FrameId> cancel_sending_frames;
cancel_sending_frames.push_back(fake_frame.frame_id);
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(CastTransportImplTest, 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 = FrameId::first() + 1;
fake_frame.referenced_frame_id = FrameId::first() + 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, fake_frame.frame_id);
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[fake_frame.frame_id].insert(0);
missing_packets[fake_frame.frame_id].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, fake_frame.frame_id);
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(CastTransportImplTest, 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 = FrameId::first() + 1;
fake_audio.referenced_frame_id = FrameId::first() + 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 = FrameId::first() + 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.remote_ssrc = kAudioSsrc;
cast_message.ack_frame_id = FrameId::first() + 1;
transport_sender_->OnReceivedCastMessage(kAudioSsrc, 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 = FrameId::first() + 1;
fake_video.referenced_frame_id = FrameId::first() + 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.remote_ssrc = kVideoSsrc;
cast_message.ack_frame_id = FrameId::first();
cast_message.missing_frames_and_packets[fake_video.frame_id].insert(3);
task_runner_->Sleep(base::TimeDelta::FromMilliseconds(10));
transport_sender_->OnReceivedCastMessage(kVideoSsrc, 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.remote_ssrc = kAudioSsrc;
cast_message.ack_frame_id = FrameId::first() + 2;
cast_message.missing_frames_and_packets.clear();
task_runner_->Sleep(base::TimeDelta::FromMilliseconds(2));
transport_sender_->OnReceivedCastMessage(kAudioSsrc, 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.remote_ssrc = kVideoSsrc;
cast_message.ack_frame_id = FrameId::first() + 1;
cast_message.missing_frames_and_packets[fake_video.frame_id].insert(3);
task_runner_->Sleep(base::TimeDelta::FromMilliseconds(2));
transport_sender_->OnReceivedCastMessage(kVideoSsrc, 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