blob: 0e3e888322ae1c08acce95f636cc0314a19efb0d [file] [log] [blame]
// Copyright 2013 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.
//
// This test generate synthetic data. For audio it's a sinusoid waveform with
// frequency kSoundFrequency and different amplitudes. For video it's a pattern
// that is shifting by one pixel per frame, each pixels neighbors right and down
// is this pixels value +1, since the pixel value is 8 bit it will wrap
// frequently within the image. Visually this will create diagonally color bands
// that moves across the screen
#include <math.h>
#include <list>
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/test/simple_test_tick_clock.h"
#include "base/time/tick_clock.h"
#include "media/base/video_frame.h"
#include "media/cast/cast_config.h"
#include "media/cast/cast_environment.h"
#include "media/cast/cast_receiver.h"
#include "media/cast/cast_sender.h"
#include "media/cast/test/audio_utility.h"
#include "media/cast/test/crypto_utility.h"
#include "media/cast/test/fake_task_runner.h"
#include "media/cast/test/video_utility.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace media {
namespace cast {
static const int64 kStartMillisecond = GG_INT64_C(1245);
static const int kAudioChannels = 2;
static const double kSoundFrequency = 314.15926535897; // Freq of sine wave.
static const float kSoundVolume = 0.5f;
static const int kVideoWidth = 1280;
static const int kVideoHeight = 720;
static const int kCommonRtpHeaderLength = 12;
static const uint8 kCastReferenceFrameIdBitReset = 0xDF; // Mask is 0x40.
// Since the video encoded and decoded an error will be introduced; when
// comparing individual pixels the error can be quite large; we allow a PSNR of
// at least |kVideoAcceptedPSNR|.
static const double kVideoAcceptedPSNR = 38.0;
// The tests are commonly implemented with |kFrameTimerMs| RunTask function;
// a normal video is 30 fps hence the 33 ms between frames.
static const int kFrameTimerMs = 33;
// The packets pass through the pacer which can delay the beginning of the
// frame by 10 ms if there is packets belonging to the previous frame being
// retransmitted.
static const int kTimerErrorMs = 15;
namespace {
// Dummy callback function that does nothing except to accept ownership of
// |audio_bus| for destruction.
void OwnThatAudioBus(scoped_ptr<AudioBus> audio_bus) {
}
} // namespace
// Class that sends the packet direct from sender into the receiver with the
// ability to drop packets between the two.
class LoopBackTransport : public PacketSender {
public:
explicit LoopBackTransport(scoped_refptr<CastEnvironment> cast_environment)
: packet_receiver_(NULL),
send_packets_(true),
drop_packets_belonging_to_odd_frames_(false),
reset_reference_frame_id_(false),
cast_environment_(cast_environment) {
}
void RegisterPacketReceiver(PacketReceiver* packet_receiver) {
DCHECK(packet_receiver);
packet_receiver_ = packet_receiver;
}
virtual bool SendPacket(const Packet& packet) OVERRIDE {
DCHECK(packet_receiver_);
DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
if (!send_packets_) return false;
uint8* packet_copy = new uint8[packet.size()];
memcpy(packet_copy, packet.data(), packet.size());
packet_receiver_->ReceivedPacket(packet_copy, packet.size(),
base::Bind(PacketReceiver::DeletePacket, packet_copy));
return true;
}
virtual bool SendPackets(const PacketList& packets) OVERRIDE {
DCHECK(packet_receiver_);
DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
if (!send_packets_) return false;
for (size_t i = 0; i < packets.size(); ++i) {
const Packet& packet = packets[i];
if (drop_packets_belonging_to_odd_frames_) {
uint32 frame_id = packet[13];
if (frame_id % 2 == 1) continue;
}
uint8* packet_copy = new uint8[packet.size()];
memcpy(packet_copy, packet.data(), packet.size());
if (reset_reference_frame_id_) {
// Reset the is_reference bit in the cast header.
packet_copy[kCommonRtpHeaderLength] &= kCastReferenceFrameIdBitReset;
}
packet_receiver_->ReceivedPacket(packet_copy, packet.size(),
base::Bind(PacketReceiver::DeletePacket, packet_copy));
}
return true;
}
void SetSendPackets(bool send_packets) {
send_packets_ = send_packets;
}
void DropAllPacketsBelongingToOddFrames() {
drop_packets_belonging_to_odd_frames_ = true;
}
void AlwaysResetReferenceFrameId() {
reset_reference_frame_id_ = true;
}
private:
PacketReceiver* packet_receiver_;
bool send_packets_;
bool drop_packets_belonging_to_odd_frames_;
bool reset_reference_frame_id_;
scoped_refptr<CastEnvironment> cast_environment_;
};
// Class that verifies the audio frames coming out of the receiver.
class TestReceiverAudioCallback :
public base::RefCountedThreadSafe<TestReceiverAudioCallback> {
public:
struct ExpectedAudioFrame {
PcmAudioFrame audio_frame;
int num_10ms_blocks;
base::TimeTicks record_time;
};
TestReceiverAudioCallback()
: num_called_(0) {}
void SetExpectedSamplingFrequency(int expected_sampling_frequency) {
expected_sampling_frequency_ = expected_sampling_frequency;
}
void AddExpectedResult(scoped_ptr<PcmAudioFrame> audio_frame,
int expected_num_10ms_blocks,
const base::TimeTicks& record_time) {
ExpectedAudioFrame expected_audio_frame;
expected_audio_frame.audio_frame = *audio_frame;
expected_audio_frame.num_10ms_blocks = expected_num_10ms_blocks;
expected_audio_frame.record_time = record_time;
expected_frame_.push_back(expected_audio_frame);
}
void IgnoreAudioFrame(scoped_ptr<PcmAudioFrame> audio_frame,
const base::TimeTicks& playout_time) {}
// Check the audio frame parameters but not the audio samples.
void CheckBasicAudioFrame(const scoped_ptr<PcmAudioFrame>& audio_frame,
const base::TimeTicks& playout_time) {
EXPECT_FALSE(expected_frame_.empty()); // Test for bug in test code.
ExpectedAudioFrame expected_audio_frame = expected_frame_.front();
EXPECT_EQ(audio_frame->channels, kAudioChannels);
EXPECT_EQ(audio_frame->frequency, expected_sampling_frequency_);
EXPECT_EQ(static_cast<int>(audio_frame->samples.size()),
expected_audio_frame.num_10ms_blocks * kAudioChannels *
expected_sampling_frequency_ / 100);
const base::TimeTicks upper_bound = expected_audio_frame.record_time +
base::TimeDelta::FromMilliseconds(kDefaultRtpMaxDelayMs +
kTimerErrorMs);
EXPECT_GE(upper_bound, playout_time)
<< "playout_time - upper_bound == "
<< (playout_time - upper_bound).InMicroseconds() << " usec";
EXPECT_LT(expected_audio_frame.record_time, playout_time)
<< "playout_time - expected == "
<< (playout_time - expected_audio_frame.record_time).InMilliseconds()
<< " mS";
EXPECT_EQ(audio_frame->samples.size(),
expected_audio_frame.audio_frame.samples.size());
}
void CheckPcmAudioFrame(scoped_ptr<PcmAudioFrame> audio_frame,
const base::TimeTicks& playout_time) {
++num_called_;
CheckBasicAudioFrame(audio_frame, playout_time);
ExpectedAudioFrame expected_audio_frame = expected_frame_.front();
expected_frame_.pop_front();
if (audio_frame->samples.size() == 0) return; // No more checks needed.
EXPECT_NEAR(CountZeroCrossings(expected_audio_frame.audio_frame.samples),
CountZeroCrossings(audio_frame->samples),
1);
}
void CheckCodedPcmAudioFrame(scoped_ptr<EncodedAudioFrame> audio_frame,
const base::TimeTicks& playout_time) {
++num_called_;
EXPECT_FALSE(expected_frame_.empty()); // Test for bug in test code.
ExpectedAudioFrame expected_audio_frame = expected_frame_.front();
expected_frame_.pop_front();
EXPECT_EQ(static_cast<int>(audio_frame->data.size()),
2 * kAudioChannels * expected_sampling_frequency_ / 100);
base::TimeDelta time_since_recording =
playout_time - expected_audio_frame.record_time;
EXPECT_LE(time_since_recording, base::TimeDelta::FromMilliseconds(
kDefaultRtpMaxDelayMs + kTimerErrorMs));
EXPECT_LT(expected_audio_frame.record_time, playout_time);
if (audio_frame->data.size() == 0) return; // No more checks needed.
// We need to convert our "coded" audio frame to our raw format.
std::vector<int16> output_audio_samples;
size_t number_of_samples = audio_frame->data.size() / 2;
for (size_t i = 0; i < number_of_samples; ++i) {
uint16 sample =
static_cast<uint8>(audio_frame->data[1 + i * sizeof(uint16)]) +
(static_cast<uint16>(audio_frame->data[i * sizeof(uint16)]) << 8);
output_audio_samples.push_back(static_cast<int16>(sample));
}
EXPECT_NEAR(CountZeroCrossings(expected_audio_frame.audio_frame.samples),
CountZeroCrossings(output_audio_samples),
1);
}
int number_times_called() const {
return num_called_;
}
protected:
virtual ~TestReceiverAudioCallback() {}
private:
friend class base::RefCountedThreadSafe<TestReceiverAudioCallback>;
int num_called_;
int expected_sampling_frequency_;
std::list<ExpectedAudioFrame> expected_frame_;
};
// Class that verifies the video frames coming out of the receiver.
class TestReceiverVideoCallback :
public base::RefCountedThreadSafe<TestReceiverVideoCallback> {
public:
struct ExpectedVideoFrame {
int start_value;
int width;
int height;
base::TimeTicks capture_time;
};
TestReceiverVideoCallback()
: num_called_(0) {}
void AddExpectedResult(int start_value,
int width,
int height,
const base::TimeTicks& capture_time) {
ExpectedVideoFrame expected_video_frame;
expected_video_frame.start_value = start_value;
expected_video_frame.capture_time = capture_time;
expected_video_frame.width = width;
expected_video_frame.height = height;
expected_frame_.push_back(expected_video_frame);
}
void CheckVideoFrame(const scoped_refptr<media::VideoFrame>& video_frame,
const base::TimeTicks& render_time) {
++num_called_;
EXPECT_FALSE(expected_frame_.empty()); // Test for bug in test code.
ExpectedVideoFrame expected_video_frame = expected_frame_.front();
expected_frame_.pop_front();
base::TimeDelta time_since_capture =
render_time - expected_video_frame.capture_time;
const base::TimeDelta upper_bound = base::TimeDelta::FromMilliseconds(
kDefaultRtpMaxDelayMs + kTimerErrorMs);
EXPECT_GE(upper_bound, time_since_capture)
<< "time_since_capture - upper_bound == "
<< (time_since_capture - upper_bound).InMilliseconds() << " mS";
EXPECT_LE(expected_video_frame.capture_time, render_time);
EXPECT_EQ(expected_video_frame.width, video_frame->coded_size().width());
EXPECT_EQ(expected_video_frame.height, video_frame->coded_size().height());
gfx::Size size(expected_video_frame.width, expected_video_frame.height);
scoped_refptr<media::VideoFrame> expected_I420_frame =
media::VideoFrame::CreateFrame(
VideoFrame::I420, size, gfx::Rect(size), size, base::TimeDelta());
PopulateVideoFrame(expected_I420_frame, expected_video_frame.start_value);
EXPECT_GE(I420PSNR(expected_I420_frame, video_frame), kVideoAcceptedPSNR);
}
int number_times_called() { return num_called_;}
protected:
virtual ~TestReceiverVideoCallback() {}
private:
friend class base::RefCountedThreadSafe<TestReceiverVideoCallback>;
int num_called_;
std::list<ExpectedVideoFrame> expected_frame_;
};
CastLoggingConfig EnableCastLoggingConfig() {
CastLoggingConfig config;
config.enable_data_collection = true;
return config;
}
// The actual test class, generate synthetic data for both audio and video and
// send those through the sender and receiver and analyzes the result.
class End2EndTest : public ::testing::Test {
protected:
End2EndTest()
: task_runner_(new test::FakeTaskRunner(&testing_clock_)),
cast_environment_(new CastEnvironment(&testing_clock_, task_runner_,
task_runner_, task_runner_, task_runner_, task_runner_,
EnableCastLoggingConfig())),
start_time_(),
sender_to_receiver_(cast_environment_),
receiver_to_sender_(cast_environment_),
test_receiver_audio_callback_(new TestReceiverAudioCallback()),
test_receiver_video_callback_(new TestReceiverVideoCallback()) {
testing_clock_.Advance(
base::TimeDelta::FromMilliseconds(kStartMillisecond));
}
void SetupConfig(AudioCodec audio_codec,
int audio_sampling_frequency,
// TODO(miu): 3rd arg is meaningless?!?
bool external_audio_decoder,
int max_number_of_video_buffers_used) {
audio_sender_config_.sender_ssrc = 1;
audio_sender_config_.incoming_feedback_ssrc = 2;
audio_sender_config_.rtp_payload_type = 96;
audio_sender_config_.use_external_encoder = false;
audio_sender_config_.frequency = audio_sampling_frequency;
audio_sender_config_.channels = kAudioChannels;
audio_sender_config_.bitrate = kDefaultAudioEncoderBitrate;
audio_sender_config_.codec = audio_codec;
audio_receiver_config_.feedback_ssrc =
audio_sender_config_.incoming_feedback_ssrc;
audio_receiver_config_.incoming_ssrc =
audio_sender_config_.sender_ssrc;
audio_receiver_config_.rtp_payload_type =
audio_sender_config_.rtp_payload_type;
audio_receiver_config_.use_external_decoder = external_audio_decoder;
audio_receiver_config_.frequency = audio_sender_config_.frequency;
audio_receiver_config_.channels = kAudioChannels;
audio_receiver_config_.codec = audio_sender_config_.codec;
test_receiver_audio_callback_->SetExpectedSamplingFrequency(
audio_receiver_config_.frequency);
video_sender_config_.sender_ssrc = 3;
video_sender_config_.incoming_feedback_ssrc = 4;
video_sender_config_.rtp_payload_type = 97;
video_sender_config_.use_external_encoder = false;
video_sender_config_.width = kVideoWidth;
video_sender_config_.height = kVideoHeight;
video_sender_config_.max_bitrate = 5000000;
video_sender_config_.min_bitrate = 1000000;
video_sender_config_.start_bitrate = 5000000;
video_sender_config_.max_qp = 30;
video_sender_config_.min_qp = 4;
video_sender_config_.max_frame_rate = 30;
video_sender_config_.max_number_of_video_buffers_used =
max_number_of_video_buffers_used;
video_sender_config_.codec = kVp8;
video_sender_config_.number_of_cores = 1;
video_receiver_config_.feedback_ssrc =
video_sender_config_.incoming_feedback_ssrc;
video_receiver_config_.incoming_ssrc =
video_sender_config_.sender_ssrc;
video_receiver_config_.rtp_payload_type =
video_sender_config_.rtp_payload_type;
video_receiver_config_.use_external_decoder = false;
video_receiver_config_.codec = video_sender_config_.codec;
}
void Create() {
cast_receiver_.reset(CastReceiver::CreateCastReceiver(cast_environment_,
audio_receiver_config_, video_receiver_config_, &receiver_to_sender_));
cast_sender_.reset(CastSender::CreateCastSender(cast_environment_,
audio_sender_config_,
video_sender_config_,
NULL,
&sender_to_receiver_));
receiver_to_sender_.RegisterPacketReceiver(cast_sender_->packet_receiver());
sender_to_receiver_.RegisterPacketReceiver(
cast_receiver_->packet_receiver());
frame_input_ = cast_sender_->frame_input();
frame_receiver_ = cast_receiver_->frame_receiver();
audio_bus_factory_.reset(new TestAudioBusFactory(
audio_sender_config_.channels, audio_sender_config_.frequency,
kSoundFrequency, kSoundVolume));
}
virtual ~End2EndTest() {}
void SendVideoFrame(int start_value, const base::TimeTicks& capture_time) {
if (start_time_.is_null())
start_time_ = testing_clock_.NowTicks();
start_time_ = testing_clock_.NowTicks();
base::TimeDelta time_diff = testing_clock_.NowTicks() - start_time_;
gfx::Size size(kVideoWidth, kVideoHeight);
EXPECT_TRUE(VideoFrame::IsValidConfig(VideoFrame::I420,
size, gfx::Rect(size), size));
scoped_refptr<media::VideoFrame> video_frame =
media::VideoFrame::CreateFrame(
VideoFrame::I420, size, gfx::Rect(size), size, time_diff);
PopulateVideoFrame(video_frame, start_value);
frame_input_->InsertRawVideoFrame(video_frame, capture_time);
}
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();
}
}
AudioReceiverConfig audio_receiver_config_;
VideoReceiverConfig video_receiver_config_;
AudioSenderConfig audio_sender_config_;
VideoSenderConfig video_sender_config_;
base::SimpleTestTickClock testing_clock_;
scoped_refptr<test::FakeTaskRunner> task_runner_;
scoped_refptr<CastEnvironment> cast_environment_;
base::TimeTicks start_time_;
LoopBackTransport sender_to_receiver_;
LoopBackTransport receiver_to_sender_;
scoped_ptr<CastReceiver> cast_receiver_;
scoped_ptr<CastSender> cast_sender_;
scoped_refptr<FrameInput> frame_input_;
scoped_refptr<FrameReceiver> frame_receiver_;
scoped_refptr<TestReceiverAudioCallback> test_receiver_audio_callback_;
scoped_refptr<TestReceiverVideoCallback> test_receiver_video_callback_;
scoped_ptr<TestAudioBusFactory> audio_bus_factory_;
};
// Audio and video test without packet loss using raw PCM 16 audio "codec";
TEST_F(End2EndTest, LoopNoLossPcm16) {
SetupConfig(kPcm16, 32000, false, 1);
Create();
int video_start = 1;
int audio_diff = kFrameTimerMs;
int i = 0;
std::cout << "Progress ";
for (; i < 300; ++i) {
int num_10ms_blocks = audio_diff / 10;
audio_diff -= num_10ms_blocks * 10;
base::TimeTicks send_time = testing_clock_.NowTicks();
test_receiver_video_callback_->AddExpectedResult(video_start,
video_sender_config_.width, video_sender_config_.height, send_time);
scoped_ptr<AudioBus> audio_bus(audio_bus_factory_->NextAudioBus(
base::TimeDelta::FromMilliseconds(10) * num_10ms_blocks));
if (i != 0) {
// Due to the re-sampler and NetEq in the webrtc AudioCodingModule the
// first samples will be 0 and then slowly ramp up to its real amplitude;
// ignore the first frame.
test_receiver_audio_callback_->AddExpectedResult(
ToPcmAudioFrame(*audio_bus, audio_sender_config_.frequency),
num_10ms_blocks, send_time);
}
AudioBus* const audio_bus_ptr = audio_bus.get();
frame_input_->InsertAudio(audio_bus_ptr, send_time,
base::Bind(&OwnThatAudioBus, base::Passed(&audio_bus)));
SendVideoFrame(video_start, send_time);
RunTasks(kFrameTimerMs);
audio_diff += kFrameTimerMs;
if (i == 0) {
frame_receiver_->GetRawAudioFrame(num_10ms_blocks,
audio_sender_config_.frequency,
base::Bind(&TestReceiverAudioCallback::IgnoreAudioFrame,
test_receiver_audio_callback_));
} else {
frame_receiver_->GetRawAudioFrame(num_10ms_blocks,
audio_sender_config_.frequency,
base::Bind(&TestReceiverAudioCallback::CheckPcmAudioFrame,
test_receiver_audio_callback_));
}
frame_receiver_->GetRawVideoFrame(
base::Bind(&TestReceiverVideoCallback::CheckVideoFrame,
test_receiver_video_callback_));
std::cout << " " << i << std::flush;
video_start++;
}
std::cout << std::endl;
RunTasks(2 * kFrameTimerMs + 1); // Empty the receiver pipeline.
EXPECT_EQ(i - 1, test_receiver_audio_callback_->number_times_called());
EXPECT_EQ(i, test_receiver_video_callback_->number_times_called());
}
// This tests our external decoder interface for Audio.
// Audio test without packet loss using raw PCM 16 audio "codec";
TEST_F(End2EndTest, LoopNoLossPcm16ExternalDecoder) {
SetupConfig(kPcm16, 32000, true, 1);
Create();
int i = 0;
for (; i < 100; ++i) {
base::TimeTicks send_time = testing_clock_.NowTicks();
scoped_ptr<AudioBus> audio_bus(audio_bus_factory_->NextAudioBus(
base::TimeDelta::FromMilliseconds(10)));
test_receiver_audio_callback_->AddExpectedResult(
ToPcmAudioFrame(*audio_bus, audio_sender_config_.frequency),
1, send_time);
AudioBus* const audio_bus_ptr = audio_bus.get();
frame_input_->InsertAudio(audio_bus_ptr, send_time,
base::Bind(&OwnThatAudioBus, base::Passed(&audio_bus)));
RunTasks(10);
frame_receiver_->GetCodedAudioFrame(
base::Bind(&TestReceiverAudioCallback::CheckCodedPcmAudioFrame,
test_receiver_audio_callback_));
}
RunTasks(2 * kFrameTimerMs + 1); // Empty the receiver pipeline.
EXPECT_EQ(100, test_receiver_audio_callback_->number_times_called());
}
// This tests our Opus audio codec without video.
TEST_F(End2EndTest, LoopNoLossOpus) {
SetupConfig(kOpus, kDefaultAudioSamplingRate, false, 1);
Create();
int i = 0;
for (; i < 100; ++i) {
int num_10ms_blocks = 3;
base::TimeTicks send_time = testing_clock_.NowTicks();
scoped_ptr<AudioBus> audio_bus(audio_bus_factory_->NextAudioBus(
base::TimeDelta::FromMilliseconds(10) * num_10ms_blocks));
if (i != 0) {
test_receiver_audio_callback_->AddExpectedResult(
ToPcmAudioFrame(*audio_bus, audio_sender_config_.frequency),
num_10ms_blocks, send_time);
}
AudioBus* const audio_bus_ptr = audio_bus.get();
frame_input_->InsertAudio(audio_bus_ptr, send_time,
base::Bind(&OwnThatAudioBus, base::Passed(&audio_bus)));
RunTasks(30);
if (i == 0) {
frame_receiver_->GetRawAudioFrame(num_10ms_blocks,
audio_sender_config_.frequency,
base::Bind(&TestReceiverAudioCallback::IgnoreAudioFrame,
test_receiver_audio_callback_));
} else {
frame_receiver_->GetRawAudioFrame(num_10ms_blocks,
audio_sender_config_.frequency,
base::Bind(&TestReceiverAudioCallback::CheckPcmAudioFrame,
test_receiver_audio_callback_));
}
}
RunTasks(2 * kFrameTimerMs + 1); // Empty the receiver pipeline.
EXPECT_EQ(i - 1, test_receiver_audio_callback_->number_times_called());
}
// This tests start sending audio and video before the receiver is ready.
//
// TODO(miu): Test disabled because of non-determinism.
// http://crbug.com/314233
TEST_F(End2EndTest, DISABLED_StartSenderBeforeReceiver) {
SetupConfig(kOpus, kDefaultAudioSamplingRate, false, 1);
Create();
int video_start = 1;
int audio_diff = kFrameTimerMs;
sender_to_receiver_.SetSendPackets(false);
for (int i = 0; i < 3; ++i) {
int num_10ms_blocks = audio_diff / 10;
audio_diff -= num_10ms_blocks * 10;
base::TimeTicks send_time = testing_clock_.NowTicks();
scoped_ptr<AudioBus> audio_bus(audio_bus_factory_->NextAudioBus(
base::TimeDelta::FromMilliseconds(10) * num_10ms_blocks));
AudioBus* const audio_bus_ptr = audio_bus.get();
frame_input_->InsertAudio(audio_bus_ptr, send_time,
base::Bind(&OwnThatAudioBus, base::Passed(&audio_bus)));
SendVideoFrame(video_start, send_time);
RunTasks(kFrameTimerMs);
audio_diff += kFrameTimerMs;
video_start++;
}
RunTasks(100);
sender_to_receiver_.SetSendPackets(true);
int j = 0;
const int number_of_audio_frames_to_ignore = 3;
for (; j < 10; ++j) {
int num_10ms_blocks = audio_diff / 10;
audio_diff -= num_10ms_blocks * 10;
base::TimeTicks send_time = testing_clock_.NowTicks();
scoped_ptr<AudioBus> audio_bus(audio_bus_factory_->NextAudioBus(
base::TimeDelta::FromMilliseconds(10) * num_10ms_blocks));
if (j >= number_of_audio_frames_to_ignore) {
test_receiver_audio_callback_->AddExpectedResult(
ToPcmAudioFrame(*audio_bus, audio_sender_config_.frequency),
num_10ms_blocks, send_time);
}
AudioBus* const audio_bus_ptr = audio_bus.get();
frame_input_->InsertAudio(audio_bus_ptr, send_time,
base::Bind(&OwnThatAudioBus, base::Passed(&audio_bus)));
test_receiver_video_callback_->AddExpectedResult(video_start,
video_sender_config_.width, video_sender_config_.height, send_time);
SendVideoFrame(video_start, send_time);
RunTasks(kFrameTimerMs);
audio_diff += kFrameTimerMs;
if (j < number_of_audio_frames_to_ignore) {
frame_receiver_->GetRawAudioFrame(num_10ms_blocks,
audio_sender_config_.frequency,
base::Bind(&TestReceiverAudioCallback::IgnoreAudioFrame,
test_receiver_audio_callback_));
} else {
frame_receiver_->GetRawAudioFrame(num_10ms_blocks,
audio_sender_config_.frequency,
base::Bind(&TestReceiverAudioCallback::CheckPcmAudioFrame,
test_receiver_audio_callback_));
}
frame_receiver_->GetRawVideoFrame(
base::Bind(&TestReceiverVideoCallback::CheckVideoFrame,
test_receiver_video_callback_));
video_start++;
}
RunTasks(2 * kFrameTimerMs + 1); // Empty the receiver pipeline.
EXPECT_EQ(j - number_of_audio_frames_to_ignore,
test_receiver_audio_callback_->number_times_called());
EXPECT_EQ(j, test_receiver_video_callback_->number_times_called());
}
// This tests a network glitch lasting for 10 video frames.
TEST_F(End2EndTest, GlitchWith3Buffers) {
SetupConfig(kOpus, kDefaultAudioSamplingRate, false, 3);
video_sender_config_.rtp_max_delay_ms = 67;
video_receiver_config_.rtp_max_delay_ms = 67;
Create();
int video_start = 50;
base::TimeTicks send_time = testing_clock_.NowTicks();
SendVideoFrame(video_start, send_time);
RunTasks(kFrameTimerMs);
test_receiver_video_callback_->AddExpectedResult(video_start,
video_sender_config_.width, video_sender_config_.height, send_time);
frame_receiver_->GetRawVideoFrame(
base::Bind(&TestReceiverVideoCallback::CheckVideoFrame,
test_receiver_video_callback_));
RunTasks(750); // Make sure that we send a RTCP packet.
video_start++;
// Introduce a glitch lasting for 10 frames.
sender_to_receiver_.SetSendPackets(false);
for (int i = 0; i < 10; ++i) {
send_time = testing_clock_.NowTicks();
// First 3 will be sent and lost.
SendVideoFrame(video_start, send_time);
RunTasks(kFrameTimerMs);
video_start++;
}
sender_to_receiver_.SetSendPackets(true);
RunTasks(100);
send_time = testing_clock_.NowTicks();
// Frame 1 should be acked by now and we should have an opening to send 4.
SendVideoFrame(video_start, send_time);
RunTasks(kFrameTimerMs);
test_receiver_video_callback_->AddExpectedResult(video_start,
video_sender_config_.width, video_sender_config_.height, send_time);
frame_receiver_->GetRawVideoFrame(
base::Bind(&TestReceiverVideoCallback::CheckVideoFrame,
test_receiver_video_callback_));
RunTasks(2 * kFrameTimerMs + 1); // Empty the receiver pipeline.
EXPECT_EQ(2, test_receiver_video_callback_->number_times_called());
}
TEST_F(End2EndTest, DropEveryOtherFrame3Buffers) {
SetupConfig(kOpus, kDefaultAudioSamplingRate, false, 3);
video_sender_config_.rtp_max_delay_ms = 67;
video_receiver_config_.rtp_max_delay_ms = 67;
Create();
sender_to_receiver_.DropAllPacketsBelongingToOddFrames();
int video_start = 50;
base::TimeTicks send_time;
std::cout << "Progress ";
int i = 0;
for (; i < 20; ++i) {
send_time = testing_clock_.NowTicks();
SendVideoFrame(video_start, send_time);
if (i % 2 == 0) {
test_receiver_video_callback_->AddExpectedResult(video_start,
video_sender_config_.width, video_sender_config_.height, send_time);
// GetRawVideoFrame will not return the frame until we are close in
// time before we should render the frame.
frame_receiver_->GetRawVideoFrame(
base::Bind(&TestReceiverVideoCallback::CheckVideoFrame,
test_receiver_video_callback_));
}
RunTasks(kFrameTimerMs);
std::cout << " " << i << std::flush;
video_start++;
}
std::cout << std::endl;
RunTasks(2 * kFrameTimerMs + 1); // Empty the pipeline.
EXPECT_EQ(i / 2, test_receiver_video_callback_->number_times_called());
}
TEST_F(End2EndTest, ResetReferenceFrameId) {
SetupConfig(kOpus, kDefaultAudioSamplingRate, false, 3);
video_sender_config_.rtp_max_delay_ms = 67;
video_receiver_config_.rtp_max_delay_ms = 67;
Create();
sender_to_receiver_.AlwaysResetReferenceFrameId();
int frames_counter = 0;
for (; frames_counter < 20; ++frames_counter) {
const base::TimeTicks send_time = testing_clock_.NowTicks();
SendVideoFrame(frames_counter, send_time);
test_receiver_video_callback_->AddExpectedResult(frames_counter,
video_sender_config_.width, video_sender_config_.height, send_time);
// GetRawVideoFrame will not return the frame until we are close to the
// time in which we should render the frame.
frame_receiver_->GetRawVideoFrame(
base::Bind(&TestReceiverVideoCallback::CheckVideoFrame,
test_receiver_video_callback_));
RunTasks(kFrameTimerMs);
}
RunTasks(2 * kFrameTimerMs + 1); // Empty the pipeline.
EXPECT_EQ(frames_counter,
test_receiver_video_callback_->number_times_called());
}
TEST_F(End2EndTest, CryptoVideo) {
SetupConfig(kPcm16, 32000, false, 1);
video_sender_config_.aes_iv_mask =
ConvertFromBase16String("1234567890abcdeffedcba0987654321");
video_sender_config_.aes_key =
ConvertFromBase16String("deadbeefcafeb0b0b0b0cafedeadbeef");
video_receiver_config_.aes_iv_mask = video_sender_config_.aes_iv_mask;
video_receiver_config_.aes_key = video_sender_config_.aes_key;
Create();
int frames_counter = 0;
for (; frames_counter < 20; ++frames_counter) {
const base::TimeTicks send_time = testing_clock_.NowTicks();
SendVideoFrame(frames_counter, send_time);
test_receiver_video_callback_->AddExpectedResult(frames_counter,
video_sender_config_.width, video_sender_config_.height, send_time);
// GetRawVideoFrame will not return the frame until we are close to the
// time in which we should render the frame.
frame_receiver_->GetRawVideoFrame(
base::Bind(&TestReceiverVideoCallback::CheckVideoFrame,
test_receiver_video_callback_));
RunTasks(kFrameTimerMs);
}
RunTasks(2 * kFrameTimerMs + 1); // Empty the pipeline.
EXPECT_EQ(frames_counter,
test_receiver_video_callback_->number_times_called());
}
TEST_F(End2EndTest, CryptoAudio) {
SetupConfig(kPcm16, 32000, false, 1);
audio_sender_config_.aes_iv_mask =
ConvertFromBase16String("abcdeffedcba12345678900987654321");
audio_sender_config_.aes_key =
ConvertFromBase16String("deadbeefcafecafedeadbeefb0b0b0b0");
audio_receiver_config_.aes_iv_mask = audio_sender_config_.aes_iv_mask;
audio_receiver_config_.aes_key = audio_sender_config_.aes_key;
Create();
int frames_counter = 0;
for (; frames_counter < 20; ++frames_counter) {
int num_10ms_blocks = 2;
const base::TimeTicks send_time = testing_clock_.NowTicks();
scoped_ptr<AudioBus> audio_bus(audio_bus_factory_->NextAudioBus(
base::TimeDelta::FromMilliseconds(10) * num_10ms_blocks));
if (frames_counter != 0) {
// Due to the re-sampler and NetEq in the webrtc AudioCodingModule the
// first samples will be 0 and then slowly ramp up to its real amplitude;
// ignore the first frame.
test_receiver_audio_callback_->AddExpectedResult(
ToPcmAudioFrame(*audio_bus, audio_sender_config_.frequency),
num_10ms_blocks, send_time);
}
AudioBus* const audio_bus_ptr = audio_bus.get();
frame_input_->InsertAudio(audio_bus_ptr, send_time,
base::Bind(&OwnThatAudioBus, base::Passed(&audio_bus)));
RunTasks(num_10ms_blocks * 10);
if (frames_counter == 0) {
frame_receiver_->GetRawAudioFrame(num_10ms_blocks,
32000,
base::Bind(&TestReceiverAudioCallback::IgnoreAudioFrame,
test_receiver_audio_callback_));
} else {
frame_receiver_->GetRawAudioFrame(num_10ms_blocks,
32000,
base::Bind(&TestReceiverAudioCallback::CheckPcmAudioFrame,
test_receiver_audio_callback_));
}
}
RunTasks(2 * kFrameTimerMs + 1); // Empty the pipeline.
EXPECT_EQ(frames_counter - 1,
test_receiver_audio_callback_->number_times_called());
}
// Video test without packet loss; This test is targeted at testing the logging
// aspects of the end2end, but is basically equivalent to LoopNoLossPcm16.
TEST_F(End2EndTest, VideoLogging) {
SetupConfig(kPcm16, 32000, false, 1);
Create();
int video_start = 1;
int i = 0;
for (; i < 1; ++i) {
base::TimeTicks send_time = testing_clock_.NowTicks();
test_receiver_video_callback_->AddExpectedResult(video_start,
video_sender_config_.width, video_sender_config_.height, send_time);
SendVideoFrame(video_start, send_time);
RunTasks(kFrameTimerMs);
frame_receiver_->GetRawVideoFrame(
base::Bind(&TestReceiverVideoCallback::CheckVideoFrame,
test_receiver_video_callback_));
video_start++;
}
// Basic tests.
RunTasks(2 * kFrameTimerMs + 1); // Empty the receiver pipeline.
EXPECT_EQ(i, test_receiver_video_callback_->number_times_called());
// Logging tests.
LoggingImpl* logging = cast_environment_->Logging();
// Frame logging.
// Verify that all frames and all required events were logged.
FrameRawMap frame_raw_log = logging->GetFrameRawData();
// Every frame should have only one entry.
EXPECT_EQ(static_cast<unsigned int>(i), frame_raw_log.size());
FrameRawMap::const_iterator frame_it = frame_raw_log.begin();
// Choose a video frame, and verify that all events were logged.
std::vector<CastLoggingEvent> event_log = frame_it->second.type;
EXPECT_TRUE((std::find(event_log.begin(), event_log.end(),
kVideoFrameReceived)) != event_log.end());
EXPECT_TRUE((std::find(event_log.begin(), event_log.end(),
kVideoFrameSentToEncoder)) != event_log.end());
EXPECT_TRUE((std::find(event_log.begin(), event_log.end(),
kVideoFrameEncoded)) != event_log.end());
EXPECT_TRUE((std::find(event_log.begin(), event_log.end(),
kVideoRenderDelay)) != event_log.end());
// TODO(mikhal): Plumb this one through.
EXPECT_TRUE((std::find(event_log.begin(), event_log.end(),
kVideoFrameDecoded)) == event_log.end());
// Verify that there were no other events logged with respect to this frame.
EXPECT_EQ(4u, event_log.size());
// Packet logging.
// Verify that all packet related events were logged.
PacketRawMap packet_raw_log = logging->GetPacketRawData();
// Every rtp_timestamp should have only one entry.
EXPECT_EQ(static_cast<unsigned int>(i), packet_raw_log.size());
PacketRawMap::const_iterator packet_it = packet_raw_log.begin();
// Choose a packet, and verify that all events were logged.
event_log = (++(packet_it->second.packet_map.begin()))->second.type;
EXPECT_TRUE((std::find(event_log.begin(), event_log.end(),
kPacketSentToPacer)) != event_log.end());
EXPECT_TRUE((std::find(event_log.begin(), event_log.end(),
kPacketSentToNetwork)) != event_log.end());
EXPECT_TRUE((std::find(event_log.begin(), event_log.end(),
kPacketReceived)) != event_log.end());
// Verify that there were no other events logged with respect to this frame.
EXPECT_EQ(3u, event_log.size());
}
// Audio test without packet loss; This test is targeted at testing the logging
// aspects of the end2end, but is basically equivalent to LoopNoLossPcm16.
TEST_F(End2EndTest, AudioLogging) {
SetupConfig(kPcm16, 32000, false, 1);
Create();
int audio_diff = kFrameTimerMs;
int i = 0;
for (; i < 10; ++i) {
int num_10ms_blocks = audio_diff / 10;
audio_diff -= num_10ms_blocks * 10;
base::TimeTicks send_time = testing_clock_.NowTicks();
scoped_ptr<AudioBus> audio_bus(audio_bus_factory_->NextAudioBus(
base::TimeDelta::FromMilliseconds(10) * num_10ms_blocks));
if (i != 0) {
// Due to the re-sampler and NetEq in the webrtc AudioCodingModule the
// first samples will be 0 and then slowly ramp up to its real amplitude;
// ignore the first frame.
test_receiver_audio_callback_->AddExpectedResult(
ToPcmAudioFrame(*audio_bus, audio_sender_config_.frequency),
num_10ms_blocks, send_time);
}
AudioBus* const audio_bus_ptr = audio_bus.get();
frame_input_->InsertAudio(audio_bus_ptr, send_time,
base::Bind(&OwnThatAudioBus, base::Passed(&audio_bus)));
RunTasks(kFrameTimerMs);
audio_diff += kFrameTimerMs;
if (i == 0) {
frame_receiver_->GetRawAudioFrame(num_10ms_blocks,
audio_sender_config_.frequency,
base::Bind(&TestReceiverAudioCallback::IgnoreAudioFrame,
test_receiver_audio_callback_));
} else {
frame_receiver_->GetRawAudioFrame(num_10ms_blocks,
audio_sender_config_.frequency,
base::Bind(&TestReceiverAudioCallback::CheckPcmAudioFrame,
test_receiver_audio_callback_));
}
}
// Basic tests.
RunTasks(2 * kFrameTimerMs + 1); // Empty the receiver pipeline.
//EXPECT_EQ(i - 1, test_receiver_audio_callback_->number_times_called());
EXPECT_EQ(i - 1, test_receiver_audio_callback_->number_times_called());
// Logging tests.
LoggingImpl* logging = cast_environment_->Logging();
// Verify that all frames and all required events were logged.
FrameRawMap frame_raw_log = logging->GetFrameRawData();
// TODO(mikhal): Results are wrong. Need to resolve passing/calculation of
// rtp_timestamp for audio for this to work.
// Should have logged both audio and video. Every frame should have only one
// entry.
//EXPECT_EQ(static_cast<unsigned int>(i - 1), frame_raw_log.size());
FrameRawMap::const_iterator frame_it = frame_raw_log.begin();
// Choose a video frame, and verify that all events were logged.
std::vector<CastLoggingEvent> event_log = frame_it->second.type;
EXPECT_TRUE((std::find(event_log.begin(), event_log.end(),
kAudioFrameReceived)) != event_log.end());
EXPECT_TRUE((std::find(event_log.begin(), event_log.end(),
kAudioFrameEncoded)) != event_log.end());
// EXPECT_TRUE((std::find(event_log.begin(), event_log.end(),
// kAudioPlayoutDelay)) != event_log.end());
// TODO(mikhal): Plumb this one through.
EXPECT_TRUE((std::find(event_log.begin(), event_log.end(),
kAudioFrameDecoded)) == event_log.end());
// Verify that there were no other events logged with respect to this frame.
EXPECT_EQ(2u, event_log.size());
}
// TODO(pwestin): Add repeatable packet loss test.
// TODO(pwestin): Add test for misaligned send get calls.
// TODO(pwestin): Add more tests that does not resample.
// TODO(pwestin): Add test when we have starvation for our RunTask.
} // namespace cast
} // namespace media