#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"
namespace media {
namespace cast {
-static const int64 kStartMillisecond = INT64_C(12345678900000);
+namespace {
+const int64 kStartMillisecond = INT64_C(12345678900000);
+const uint32 kVideoSsrc = 1;
+const uint32 kAudioSsrc = 2;
+} // namespace
class FakePacketSender : public PacketSender {
public:
- FakePacketSender() {}
+ FakePacketSender()
+ : paused_(false), packets_sent_(0), bytes_sent_(0) {}
virtual 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;
}
+
+ virtual 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 {
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(),
NULL,
&testing_clock_,
net::IPEndPoint(),
+ make_scoped_ptr(new base::DictionaryValue),
base::Bind(&UpdateCastTransportStatus),
base::Bind(&CastTransportSenderImplTest::LogRawEvents,
base::Unretained(this)),
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_++;
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