blob: 73f0885ae1631a30d40647804702f66403798887 [file] [log] [blame]
// Copyright (c) 2012 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 <vector>
#include "base/environment.h"
#include "base/file_util.h"
#include "base/files/file_path.h"
#include "base/path_service.h"
#include "base/strings/stringprintf.h"
#include "base/test/test_timeouts.h"
#include "content/renderer/media/webrtc_audio_capturer.h"
#include "content/renderer/media/webrtc_audio_device_impl.h"
#include "content/renderer/media/webrtc_audio_renderer.h"
#include "content/renderer/media/webrtc_local_audio_track.h"
#include "content/renderer/render_thread_impl.h"
#include "content/test/webrtc_audio_device_test.h"
#include "media/audio/audio_manager_base.h"
#include "media/base/audio_hardware_config.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "third_party/webrtc/voice_engine/include/voe_audio_processing.h"
#include "third_party/webrtc/voice_engine/include/voe_base.h"
#include "third_party/webrtc/voice_engine/include/voe_codec.h"
#include "third_party/webrtc/voice_engine/include/voe_external_media.h"
#include "third_party/webrtc/voice_engine/include/voe_file.h"
#include "third_party/webrtc/voice_engine/include/voe_network.h"
#if defined(OS_WIN)
#include "base/win/windows_version.h"
#endif
using media::AudioParameters;
using media::CHANNEL_LAYOUT_STEREO;
using testing::_;
using testing::AnyNumber;
using testing::InvokeWithoutArgs;
using testing::Return;
using testing::StrEq;
namespace content {
namespace {
const int kRenderViewId = 1;
// The number of packers that RunWebRtcLoopbackTimeTest() uses for measurement.
const int kNumberOfPacketsForLoopbackTest = 100;
// The hardware latency we feed to WebRtc.
const int kHardwareLatencyInMs = 50;
scoped_ptr<media::AudioHardwareConfig> CreateRealHardwareConfig(
media::AudioManager* manager) {
const AudioParameters output_parameters =
manager->GetDefaultOutputStreamParameters();
const AudioParameters input_parameters =
manager->GetInputStreamParameters(
media::AudioManagerBase::kDefaultDeviceId);
return make_scoped_ptr(new media::AudioHardwareConfig(
input_parameters, output_parameters));
}
// Return true if at least one element in the array matches |value|.
bool FindElementInArray(const int* array, int size, int value) {
return (std::find(&array[0], &array[0] + size, value) != &array[size]);
}
// This method returns false if a non-supported rate is detected on the
// input or output side.
// TODO(henrika): add support for automatic fallback to Windows Wave audio
// if a non-supported rate is detected. It is probably better to detect
// invalid audio settings by actually trying to open the audio streams instead
// of relying on hard coded conditions.
bool HardwareSampleRatesAreValid() {
// These are the currently supported hardware sample rates in both directions.
// The actual WebRTC client can limit these ranges further depending on
// platform but this is the maximum range we support today.
int valid_input_rates[] = {16000, 32000, 44100, 48000, 96000};
int valid_output_rates[] = {16000, 32000, 44100, 48000, 96000};
media::AudioHardwareConfig* hardware_config =
RenderThreadImpl::current()->GetAudioHardwareConfig();
// Verify the input sample rate.
int input_sample_rate = hardware_config->GetInputSampleRate();
if (!FindElementInArray(valid_input_rates, arraysize(valid_input_rates),
input_sample_rate)) {
LOG(WARNING) << "Non-supported input sample rate detected.";
return false;
}
// Given that the input rate was OK, verify the output rate as well.
int output_sample_rate = hardware_config->GetOutputSampleRate();
if (!FindElementInArray(valid_output_rates, arraysize(valid_output_rates),
output_sample_rate)) {
LOG(WARNING) << "Non-supported output sample rate detected.";
return false;
}
return true;
}
// Utility method which creates and initializes the audio capturer and adds it
// to WebRTC audio device. This method should be used in tests where
// HardwareSampleRatesAreValid() has been called and returned true.
bool CreateAndInitializeCapturer(WebRtcAudioDeviceImpl* webrtc_audio_device) {
DCHECK(webrtc_audio_device);
scoped_refptr<WebRtcAudioCapturer> capturer(
WebRtcAudioCapturer::CreateCapturer());
media::AudioHardwareConfig* hardware_config =
RenderThreadImpl::current()->GetAudioHardwareConfig();
// Use native capture sample rate and channel configuration to get some
// action in this test.
int sample_rate = hardware_config->GetInputSampleRate();
media::ChannelLayout channel_layout =
hardware_config->GetInputChannelLayout();
if (!capturer->Initialize(kRenderViewId, channel_layout, sample_rate, 0, 1,
media::AudioManagerBase::kDefaultDeviceId, 0 ,0)) {
return false;
}
// Add the capturer to the WebRtcAudioDeviceImpl.
webrtc_audio_device->AddAudioCapturer(capturer);
return true;
}
// Create and start a local audio track. Starting the audio track will connect
// the audio track to the capturer and also start the source of the capturer.
// Also, connect the sink to the audio track.
scoped_refptr<WebRtcLocalAudioTrack>
CreateAndStartLocalAudioTrack(WebRtcAudioCapturer* capturer,
PeerConnectionAudioSink* sink) {
scoped_refptr<WebRtcLocalAudioTrack> local_audio_track(
WebRtcLocalAudioTrack::Create(std::string(), capturer, NULL, NULL, NULL));
local_audio_track->AddSink(sink);
local_audio_track->Start();
return local_audio_track;
}
class WebRTCMediaProcessImpl : public webrtc::VoEMediaProcess {
public:
explicit WebRTCMediaProcessImpl(base::WaitableEvent* event)
: event_(event),
channel_id_(-1),
type_(webrtc::kPlaybackPerChannel),
packet_size_(0),
sample_rate_(0),
channels_(0) {
}
virtual ~WebRTCMediaProcessImpl() {}
// TODO(henrika): Refactor in WebRTC and convert to Chrome coding style.
virtual void Process(int channel,
webrtc::ProcessingTypes type,
int16_t audio_10ms[],
int length,
int sampling_freq,
bool is_stereo) OVERRIDE {
base::AutoLock auto_lock(lock_);
channel_id_ = channel;
type_ = type;
packet_size_ = length;
sample_rate_ = sampling_freq;
channels_ = (is_stereo ? 2 : 1);
if (event_) {
// Signal that a new callback has been received.
event_->Signal();
}
}
int channel_id() const {
base::AutoLock auto_lock(lock_);
return channel_id_;
}
int type() const {
base::AutoLock auto_lock(lock_);
return type_;
}
int packet_size() const {
base::AutoLock auto_lock(lock_);
return packet_size_;
}
int sample_rate() const {
base::AutoLock auto_lock(lock_);
return sample_rate_;
}
private:
base::WaitableEvent* event_;
int channel_id_;
webrtc::ProcessingTypes type_;
int packet_size_;
int sample_rate_;
int channels_;
mutable base::Lock lock_;
DISALLOW_COPY_AND_ASSIGN(WebRTCMediaProcessImpl);
};
// TODO(xians): Use MediaStreamAudioSink.
class MockMediaStreamAudioSink : public PeerConnectionAudioSink {
public:
explicit MockMediaStreamAudioSink(base::WaitableEvent* event)
: event_(event) {
DCHECK(event_);
}
virtual ~MockMediaStreamAudioSink() {}
// PeerConnectionAudioSink implementation.
virtual int OnData(const int16* audio_data,
int sample_rate,
int number_of_channels,
int number_of_frames,
const std::vector<int>& channels,
int audio_delay_milliseconds,
int current_volume,
bool need_audio_processing,
bool key_pressed) OVERRIDE {
// Signal that a callback has been received.
event_->Signal();
return 0;
}
// Set the format for the capture audio parameters.
virtual void OnSetFormat(
const media::AudioParameters& params) OVERRIDE {}
private:
base::WaitableEvent* event_;
DISALLOW_COPY_AND_ASSIGN(MockMediaStreamAudioSink);
};
class MockWebRtcAudioRendererSource : public WebRtcAudioRendererSource {
public:
explicit MockWebRtcAudioRendererSource(base::WaitableEvent* event)
: event_(event) {
DCHECK(event_);
}
virtual ~MockWebRtcAudioRendererSource() {}
// WebRtcAudioRendererSource implementation.
virtual void RenderData(uint8* audio_data,
int number_of_channels,
int number_of_frames,
int audio_delay_milliseconds) OVERRIDE {
// Signal that a callback has been received.
// Initialize the memory to zero to avoid uninitialized warning from
// Valgrind.
memset(audio_data, 0,
sizeof(int16) * number_of_channels * number_of_frames);
event_->Signal();
}
virtual void SetRenderFormat(const media::AudioParameters& params) OVERRIDE {
}
virtual void RemoveAudioRenderer(WebRtcAudioRenderer* renderer) OVERRIDE {};
private:
base::WaitableEvent* event_;
DISALLOW_COPY_AND_ASSIGN(MockWebRtcAudioRendererSource);
};
// Prints numerical information to stdout in a controlled format so we can plot
// the result.
void PrintPerfResultMs(const char* graph, const char* trace, float time_ms) {
std::string times;
base::StringAppendF(&times, "%.2f,", time_ms);
std::string result = base::StringPrintf(
"%sRESULT %s%s: %s= %s%s%s %s\n", "*", graph, "",
trace, "[", times.c_str(), "]", "ms");
fflush(stdout);
printf("%s", result.c_str());
fflush(stdout);
}
void ReadDataFromSpeechFile(char* data, int length) {
base::FilePath data_file;
CHECK(PathService::Get(base::DIR_SOURCE_ROOT, &data_file));
data_file =
data_file.Append(FILE_PATH_LITERAL("media"))
.Append(FILE_PATH_LITERAL("test"))
.Append(FILE_PATH_LITERAL("data"))
.Append(FILE_PATH_LITERAL("speech_16b_stereo_48kHz.raw"));
DCHECK(base::PathExists(data_file));
int64 data_file_size64 = 0;
DCHECK(base::GetFileSize(data_file, &data_file_size64));
EXPECT_EQ(length, base::ReadFile(data_file, data, length));
DCHECK(data_file_size64 > length);
}
void SetChannelCodec(webrtc::VoiceEngine* engine, int channel) {
// TODO(xians): move the codec as an input param to this function, and add
// tests for different codecs, also add support to Android and IOS.
#if !defined(OS_ANDROID) && !defined(OS_IOS)
webrtc::CodecInst isac;
strcpy(isac.plname, "ISAC");
isac.pltype = 104;
isac.pacsize = 960;
isac.plfreq = 32000;
isac.channels = 1;
isac.rate = -1;
ScopedWebRTCPtr<webrtc::VoECodec> codec(engine);
EXPECT_EQ(0, codec->SetRecPayloadType(channel, isac));
EXPECT_EQ(0, codec->SetSendCodec(channel, isac));
#endif
}
// Returns the time in millisecond for sending packets to WebRtc for encoding,
// signal processing, decoding and receiving them back.
int RunWebRtcLoopbackTimeTest(media::AudioManager* manager,
bool enable_apm) {
scoped_refptr<WebRtcAudioDeviceImpl> webrtc_audio_device(
new WebRtcAudioDeviceImpl());
WebRTCAutoDelete<webrtc::VoiceEngine> engine(webrtc::VoiceEngine::Create());
EXPECT_TRUE(engine.valid());
ScopedWebRTCPtr<webrtc::VoEBase> base(engine.get());
EXPECT_TRUE(base.valid());
int err = base->Init(webrtc_audio_device.get());
EXPECT_EQ(0, err);
// We use OnSetFormat() and SetRenderFormat() to configure the audio
// parameters so that this test can run on machine without hardware device.
const media::AudioParameters params = media::AudioParameters(
media::AudioParameters::AUDIO_PCM_LOW_LATENCY, CHANNEL_LAYOUT_STEREO,
48000, 2, 480);
PeerConnectionAudioSink* capturer_sink =
static_cast<PeerConnectionAudioSink*>(webrtc_audio_device.get());
WebRtcAudioRendererSource* renderer_source =
static_cast<WebRtcAudioRendererSource*>(webrtc_audio_device.get());
renderer_source->SetRenderFormat(params);
// Turn on/off all the signal processing components like AGC, AEC and NS.
ScopedWebRTCPtr<webrtc::VoEAudioProcessing> audio_processing(engine.get());
EXPECT_TRUE(audio_processing.valid());
audio_processing->SetAgcStatus(enable_apm);
audio_processing->SetNsStatus(enable_apm);
audio_processing->SetEcStatus(enable_apm);
// Create a voice channel for the WebRtc.
int channel = base->CreateChannel();
EXPECT_NE(-1, channel);
SetChannelCodec(engine.get(), channel);
// Use our fake network transmission and start playout and recording.
ScopedWebRTCPtr<webrtc::VoENetwork> network(engine.get());
EXPECT_TRUE(network.valid());
scoped_ptr<WebRTCTransportImpl> transport(
new WebRTCTransportImpl(network.get()));
EXPECT_EQ(0, network->RegisterExternalTransport(channel, *transport.get()));
EXPECT_EQ(0, base->StartPlayout(channel));
EXPECT_EQ(0, base->StartSend(channel));
// Read speech data from a speech test file.
const int input_packet_size =
params.frames_per_buffer() * 2 * params.channels();
const int num_output_channels = webrtc_audio_device->output_channels();
const int output_packet_size = webrtc_audio_device->output_buffer_size() * 2 *
num_output_channels;
const size_t length = input_packet_size * kNumberOfPacketsForLoopbackTest;
scoped_ptr<char[]> capture_data(new char[length]);
ReadDataFromSpeechFile(capture_data.get(), length);
// Start the timer.
scoped_ptr<uint8[]> buffer(new uint8[output_packet_size]);
base::Time start_time = base::Time::Now();
int delay = 0;
std::vector<int> voe_channels;
voe_channels.push_back(channel);
for (int j = 0; j < kNumberOfPacketsForLoopbackTest; ++j) {
// Sending fake capture data to WebRtc.
capturer_sink->OnData(
reinterpret_cast<int16*>(capture_data.get() + input_packet_size * j),
params.sample_rate(),
params.channels(),
params.frames_per_buffer(),
voe_channels,
kHardwareLatencyInMs,
1.0,
enable_apm,
false);
// Receiving data from WebRtc.
renderer_source->RenderData(
reinterpret_cast<uint8*>(buffer.get()),
num_output_channels, webrtc_audio_device->output_buffer_size(),
kHardwareLatencyInMs + delay);
delay = (base::Time::Now() - start_time).InMilliseconds();
}
int latency = (base::Time::Now() - start_time).InMilliseconds();
EXPECT_EQ(0, base->StopSend(channel));
EXPECT_EQ(0, base->StopPlayout(channel));
EXPECT_EQ(0, base->DeleteChannel(channel));
EXPECT_EQ(0, base->Terminate());
return latency;
}
} // namespace
// Trivial test which verifies that one part of the test harness
// (HardwareSampleRatesAreValid()) works as intended for all supported
// hardware input sample rates.
TEST_F(MAYBE_WebRTCAudioDeviceTest, TestValidInputRates) {
int valid_rates[] = {16000, 32000, 44100, 48000, 96000};
// Verify that we will approve all rates listed in |valid_rates|.
for (size_t i = 0; i < arraysize(valid_rates); ++i) {
EXPECT_TRUE(FindElementInArray(valid_rates, arraysize(valid_rates),
valid_rates[i]));
}
// Verify that any value outside the valid range results in negative
// find results.
int invalid_rates[] = {-1, 0, 8000, 11025, 22050, 192000};
for (size_t i = 0; i < arraysize(invalid_rates); ++i) {
EXPECT_FALSE(FindElementInArray(valid_rates, arraysize(valid_rates),
invalid_rates[i]));
}
}
// Trivial test which verifies that one part of the test harness
// (HardwareSampleRatesAreValid()) works as intended for all supported
// hardware output sample rates.
TEST_F(MAYBE_WebRTCAudioDeviceTest, TestValidOutputRates) {
int valid_rates[] = {44100, 48000, 96000};
// Verify that we will approve all rates listed in |valid_rates|.
for (size_t i = 0; i < arraysize(valid_rates); ++i) {
EXPECT_TRUE(FindElementInArray(valid_rates, arraysize(valid_rates),
valid_rates[i]));
}
// Verify that any value outside the valid range results in negative
// find results.
int invalid_rates[] = {-1, 0, 8000, 11025, 22050, 32000, 192000};
for (size_t i = 0; i < arraysize(invalid_rates); ++i) {
EXPECT_FALSE(FindElementInArray(valid_rates, arraysize(valid_rates),
invalid_rates[i]));
}
}
// Basic test that instantiates and initializes an instance of
// WebRtcAudioDeviceImpl.
TEST_F(MAYBE_WebRTCAudioDeviceTest, Construct) {
#if defined(OS_WIN)
// This test crashes on Win XP bots.
if (base::win::GetVersion() <= base::win::VERSION_XP)
return;
#endif
AudioParameters input_params(
AudioParameters::AUDIO_PCM_LOW_LATENCY,
media::CHANNEL_LAYOUT_MONO,
48000,
16,
480);
AudioParameters output_params(
AudioParameters::AUDIO_PCM_LOW_LATENCY,
media::CHANNEL_LAYOUT_STEREO,
48000,
16,
480);
media::AudioHardwareConfig audio_config(input_params, output_params);
SetAudioHardwareConfig(&audio_config);
scoped_refptr<WebRtcAudioDeviceImpl> webrtc_audio_device(
new WebRtcAudioDeviceImpl());
WebRTCAutoDelete<webrtc::VoiceEngine> engine(webrtc::VoiceEngine::Create());
ASSERT_TRUE(engine.valid());
ScopedWebRTCPtr<webrtc::VoEBase> base(engine.get());
int err = base->Init(webrtc_audio_device.get());
EXPECT_TRUE(CreateAndInitializeCapturer(webrtc_audio_device.get()));
EXPECT_EQ(0, err);
EXPECT_EQ(0, base->Terminate());
}
// Verify that a call to webrtc::VoEBase::StartPlayout() starts audio output
// with the correct set of parameters. A WebRtcAudioDeviceImpl instance will
// be utilized to implement the actual audio path. The test registers a
// webrtc::VoEExternalMedia implementation to hijack the output audio and
// verify that streaming starts correctly.
// TODO(henrika): include on Android as well as soon as alla race conditions
// in OpenSLES are resolved.
#if defined(OS_ANDROID)
#define MAYBE_StartPlayout DISABLED_StartPlayout
#else
#define MAYBE_StartPlayout StartPlayout
#endif
TEST_F(MAYBE_WebRTCAudioDeviceTest, MAYBE_StartPlayout) {
if (!has_output_devices_) {
LOG(WARNING) << "No output device detected.";
return;
}
scoped_ptr<media::AudioHardwareConfig> config =
CreateRealHardwareConfig(audio_manager_.get());
SetAudioHardwareConfig(config.get());
if (!HardwareSampleRatesAreValid())
return;
WebRTCAutoDelete<webrtc::VoiceEngine> engine(webrtc::VoiceEngine::Create());
ASSERT_TRUE(engine.valid());
ScopedWebRTCPtr<webrtc::VoEBase> base(engine.get());
ASSERT_TRUE(base.valid());
scoped_refptr<WebRtcAudioDeviceImpl> webrtc_audio_device(
new WebRtcAudioDeviceImpl());
int err = base->Init(webrtc_audio_device.get());
ASSERT_EQ(0, err);
ScopedWebRTCPtr<webrtc::VoEExternalMedia> external_media(engine.get());
ASSERT_TRUE(external_media.valid());
base::WaitableEvent event(false, false);
scoped_ptr<WebRTCMediaProcessImpl> media_process(
new WebRTCMediaProcessImpl(&event));
int ch = base->CreateChannel();
EXPECT_NE(-1, ch);
EXPECT_EQ(0, external_media->RegisterExternalMediaProcessing(
ch, webrtc::kPlaybackPerChannel, *media_process.get()));
EXPECT_EQ(0, base->StartPlayout(ch));
scoped_refptr<WebRtcAudioRenderer> renderer(
CreateDefaultWebRtcAudioRenderer(kRenderViewId));
scoped_refptr<MediaStreamAudioRenderer> proxy(
renderer->CreateSharedAudioRendererProxy());
EXPECT_TRUE(webrtc_audio_device->SetAudioRenderer(renderer.get()));
proxy->Start();
proxy->Play();
EXPECT_TRUE(event.TimedWait(TestTimeouts::action_timeout()));
WaitForIOThreadCompletion();
EXPECT_TRUE(webrtc_audio_device->Playing());
EXPECT_FALSE(webrtc_audio_device->Recording());
EXPECT_EQ(ch, media_process->channel_id());
EXPECT_EQ(webrtc::kPlaybackPerChannel, media_process->type());
EXPECT_EQ(80, media_process->packet_size());
EXPECT_EQ(8000, media_process->sample_rate());
EXPECT_EQ(0, external_media->DeRegisterExternalMediaProcessing(
ch, webrtc::kPlaybackPerChannel));
EXPECT_EQ(0, base->StopPlayout(ch));
proxy->Stop();
EXPECT_EQ(0, base->DeleteChannel(ch));
EXPECT_EQ(0, base->Terminate());
}
// Verify that a call to webrtc::VoEBase::StartRecording() starts audio input
// with the correct set of parameters. A WebRtcAudioDeviceImpl instance will
// be utilized to implement the actual audio path. The test registers a
// webrtc::VoEExternalMedia implementation to hijack the input audio and
// verify that streaming starts correctly. An external transport implementation
// is also required to ensure that "sending" can start without actually trying
// to send encoded packets to the network. Our main interest here is to ensure
// that the audio capturing starts as it should.
// Disabled when running headless since the bots don't have the required config.
// TODO(leozwang): Because ExternalMediaProcessing is disabled in webrtc,
// disable this unit test on Android for now.
#if defined(OS_ANDROID)
#define MAYBE_StartRecording DISABLED_StartRecording
#elif defined(OS_LINUX) && !defined(OS_CHROMEOS) && defined(ARCH_CPU_ARM_FAMILY)
// This test is failing on ARM linux: http://crbug.com/238490
#define MAYBE_StartRecording DISABLED_StartRecording
#else
// Flakily hangs on all other platforms as well: crbug.com/268376.
// When the flakiness has been fixed, you probably want to leave it disabled
// on the above platforms.
#define MAYBE_StartRecording DISABLED_StartRecording
#endif
TEST_F(MAYBE_WebRTCAudioDeviceTest, MAYBE_StartRecording) {
if (!has_input_devices_ || !has_output_devices_) {
LOG(WARNING) << "Missing audio devices.";
return;
}
scoped_ptr<media::AudioHardwareConfig> config =
CreateRealHardwareConfig(audio_manager_.get());
SetAudioHardwareConfig(config.get());
if (!HardwareSampleRatesAreValid())
return;
// TODO(tommi): extend MediaObserver and MockMediaObserver with support
// for new interfaces, like OnSetAudioStreamRecording(). When done, add
// EXPECT_CALL() macros here.
scoped_refptr<WebRtcAudioDeviceImpl> webrtc_audio_device(
new WebRtcAudioDeviceImpl());
WebRTCAutoDelete<webrtc::VoiceEngine> engine(webrtc::VoiceEngine::Create());
ASSERT_TRUE(engine.valid());
ScopedWebRTCPtr<webrtc::VoEBase> base(engine.get());
ASSERT_TRUE(base.valid());
int err = base->Init(webrtc_audio_device.get());
ASSERT_EQ(0, err);
int ch = base->CreateChannel();
EXPECT_NE(-1, ch);
ScopedWebRTCPtr<webrtc::VoEExternalMedia> external_media(engine.get());
ASSERT_TRUE(external_media.valid());
base::WaitableEvent event(false, false);
scoped_ptr<WebRTCMediaProcessImpl> media_process(
new WebRTCMediaProcessImpl(&event));
EXPECT_EQ(0, external_media->RegisterExternalMediaProcessing(
ch, webrtc::kRecordingPerChannel, *media_process.get()));
// We must add an external transport implementation to be able to start
// recording without actually sending encoded packets to the network. All
// we want to do here is to verify that audio capturing starts as it should.
ScopedWebRTCPtr<webrtc::VoENetwork> network(engine.get());
scoped_ptr<WebRTCTransportImpl> transport(
new WebRTCTransportImpl(network.get()));
EXPECT_EQ(0, network->RegisterExternalTransport(ch, *transport.get()));
EXPECT_EQ(0, base->StartSend(ch));
// Create and initialize the capturer which starts the source of the data
// flow.
EXPECT_TRUE(CreateAndInitializeCapturer(webrtc_audio_device.get()));
// Create and start a local audio track which is bridging the data flow
// between the capturer and WebRtcAudioDeviceImpl.
scoped_refptr<WebRtcLocalAudioTrack> local_audio_track(
CreateAndStartLocalAudioTrack(webrtc_audio_device->GetDefaultCapturer(),
webrtc_audio_device));
// connect the VoE voice channel to the audio track
static_cast<webrtc::AudioTrackInterface*>(local_audio_track.get())->
GetRenderer()->AddChannel(ch);
// Verify we get the data flow.
EXPECT_TRUE(event.TimedWait(TestTimeouts::action_timeout()));
WaitForIOThreadCompletion();
EXPECT_FALSE(webrtc_audio_device->Playing());
EXPECT_TRUE(webrtc_audio_device->Recording());
EXPECT_EQ(ch, media_process->channel_id());
EXPECT_EQ(webrtc::kRecordingPerChannel, media_process->type());
EXPECT_EQ(80, media_process->packet_size());
EXPECT_EQ(8000, media_process->sample_rate());
EXPECT_EQ(0, external_media->DeRegisterExternalMediaProcessing(
ch, webrtc::kRecordingPerChannel));
EXPECT_EQ(0, base->StopSend(ch));
webrtc_audio_device->GetDefaultCapturer()->Stop();
EXPECT_EQ(0, base->DeleteChannel(ch));
EXPECT_EQ(0, base->Terminate());
}
// Uses WebRtcAudioDeviceImpl to play a local wave file.
// TODO(henrika): include on Android as well as soon as alla race conditions
// in OpenSLES are resolved.
#if defined(OS_ANDROID)
#define MAYBE_PlayLocalFile DISABLED_PlayLocalFile
#else
#define MAYBE_PlayLocalFile PlayLocalFile
#endif
TEST_F(MAYBE_WebRTCAudioDeviceTest, MAYBE_PlayLocalFile) {
if (!has_output_devices_) {
LOG(WARNING) << "No output device detected.";
return;
}
std::string file_path(
GetTestDataPath(FILE_PATH_LITERAL("speechmusic_mono_16kHz.pcm")));
scoped_ptr<media::AudioHardwareConfig> config =
CreateRealHardwareConfig(audio_manager_.get());
SetAudioHardwareConfig(config.get());
if (!HardwareSampleRatesAreValid())
return;
WebRTCAutoDelete<webrtc::VoiceEngine> engine(webrtc::VoiceEngine::Create());
ASSERT_TRUE(engine.valid());
ScopedWebRTCPtr<webrtc::VoEBase> base(engine.get());
ASSERT_TRUE(base.valid());
scoped_refptr<WebRtcAudioDeviceImpl> webrtc_audio_device(
new WebRtcAudioDeviceImpl());
int err = base->Init(webrtc_audio_device.get());
ASSERT_EQ(0, err);
int ch = base->CreateChannel();
EXPECT_NE(-1, ch);
EXPECT_EQ(0, base->StartPlayout(ch));
scoped_refptr<WebRtcAudioRenderer> renderer(
CreateDefaultWebRtcAudioRenderer(kRenderViewId));
scoped_refptr<MediaStreamAudioRenderer> proxy(
renderer->CreateSharedAudioRendererProxy());
EXPECT_TRUE(webrtc_audio_device->SetAudioRenderer(renderer.get()));
proxy->Start();
proxy->Play();
ScopedWebRTCPtr<webrtc::VoEFile> file(engine.get());
ASSERT_TRUE(file.valid());
int duration = 0;
EXPECT_EQ(0, file->GetFileDuration(file_path.c_str(), duration,
webrtc::kFileFormatPcm16kHzFile));
EXPECT_NE(0, duration);
EXPECT_EQ(0, file->StartPlayingFileLocally(ch, file_path.c_str(), false,
webrtc::kFileFormatPcm16kHzFile));
// Play 2 seconds worth of audio and then quit.
message_loop_.PostDelayedTask(FROM_HERE,
base::MessageLoop::QuitClosure(),
base::TimeDelta::FromSeconds(2));
message_loop_.Run();
proxy->Stop();
EXPECT_EQ(0, base->StopSend(ch));
EXPECT_EQ(0, base->StopPlayout(ch));
EXPECT_EQ(0, base->DeleteChannel(ch));
EXPECT_EQ(0, base->Terminate());
}
// Uses WebRtcAudioDeviceImpl to play out recorded audio in loopback.
// An external transport implementation is utilized to feed back RTP packets
// which are recorded, encoded, packetized into RTP packets and finally
// "transmitted". The RTP packets are then fed back into the VoiceEngine
// where they are decoded and played out on the default audio output device.
// Disabled when running headless since the bots don't have the required config.
// TODO(henrika): improve quality by using a wideband codec, enabling noise-
// suppressions etc.
// FullDuplexAudioWithAGC is flaky on Android, disable it for now.
// Also flakily hangs on Windows: crbug.com/269348.
#if defined(OS_ANDROID) || defined(OS_WIN)
#define MAYBE_FullDuplexAudioWithAGC DISABLED_FullDuplexAudioWithAGC
#else
#define MAYBE_FullDuplexAudioWithAGC FullDuplexAudioWithAGC
#endif
TEST_F(MAYBE_WebRTCAudioDeviceTest, MAYBE_FullDuplexAudioWithAGC) {
if (!has_output_devices_ || !has_input_devices_) {
LOG(WARNING) << "Missing audio devices.";
return;
}
scoped_ptr<media::AudioHardwareConfig> config =
CreateRealHardwareConfig(audio_manager_.get());
SetAudioHardwareConfig(config.get());
if (!HardwareSampleRatesAreValid())
return;
WebRTCAutoDelete<webrtc::VoiceEngine> engine(webrtc::VoiceEngine::Create());
ASSERT_TRUE(engine.valid());
ScopedWebRTCPtr<webrtc::VoEBase> base(engine.get());
ASSERT_TRUE(base.valid());
scoped_refptr<WebRtcAudioDeviceImpl> webrtc_audio_device(
new WebRtcAudioDeviceImpl());
int err = base->Init(webrtc_audio_device.get());
ASSERT_EQ(0, err);
ScopedWebRTCPtr<webrtc::VoEAudioProcessing> audio_processing(engine.get());
ASSERT_TRUE(audio_processing.valid());
#if defined(OS_ANDROID)
// On Android, by default AGC is off.
bool enabled = true;
webrtc::AgcModes agc_mode = webrtc::kAgcDefault;
EXPECT_EQ(0, audio_processing->GetAgcStatus(enabled, agc_mode));
EXPECT_FALSE(enabled);
#else
bool enabled = false;
webrtc::AgcModes agc_mode = webrtc::kAgcDefault;
EXPECT_EQ(0, audio_processing->GetAgcStatus(enabled, agc_mode));
EXPECT_TRUE(enabled);
EXPECT_EQ(agc_mode, webrtc::kAgcAdaptiveAnalog);
#endif
int ch = base->CreateChannel();
EXPECT_NE(-1, ch);
EXPECT_TRUE(CreateAndInitializeCapturer(webrtc_audio_device.get()));
scoped_refptr<WebRtcLocalAudioTrack> local_audio_track(
CreateAndStartLocalAudioTrack(webrtc_audio_device->GetDefaultCapturer(),
webrtc_audio_device));
// connect the VoE voice channel to the audio track
static_cast<webrtc::AudioTrackInterface*>(local_audio_track.get())->
GetRenderer()->AddChannel(ch);
ScopedWebRTCPtr<webrtc::VoENetwork> network(engine.get());
ASSERT_TRUE(network.valid());
scoped_ptr<WebRTCTransportImpl> transport(
new WebRTCTransportImpl(network.get()));
EXPECT_EQ(0, network->RegisterExternalTransport(ch, *transport.get()));
EXPECT_EQ(0, base->StartPlayout(ch));
EXPECT_EQ(0, base->StartSend(ch));
scoped_refptr<WebRtcAudioRenderer> renderer(
CreateDefaultWebRtcAudioRenderer(kRenderViewId));
scoped_refptr<MediaStreamAudioRenderer> proxy(
renderer->CreateSharedAudioRendererProxy());
EXPECT_TRUE(webrtc_audio_device->SetAudioRenderer(renderer.get()));
proxy->Start();
proxy->Play();
VLOG(0) << ">> You should now be able to hear yourself in loopback...";
message_loop_.PostDelayedTask(FROM_HERE,
base::MessageLoop::QuitClosure(),
base::TimeDelta::FromSeconds(2));
message_loop_.Run();
webrtc_audio_device->GetDefaultCapturer()->Stop();
proxy->Stop();
EXPECT_EQ(0, base->StopSend(ch));
EXPECT_EQ(0, base->StopPlayout(ch));
EXPECT_EQ(0, base->DeleteChannel(ch));
EXPECT_EQ(0, base->Terminate());
}
// Test times out on bots, see http://crbug.com/247447
TEST_F(MAYBE_WebRTCAudioDeviceTest, DISABLED_WebRtcRecordingSetupTime) {
if (!has_input_devices_) {
LOG(WARNING) << "Missing audio capture devices.";
return;
}
scoped_ptr<media::AudioHardwareConfig> config =
CreateRealHardwareConfig(audio_manager_.get());
SetAudioHardwareConfig(config.get());
if (!HardwareSampleRatesAreValid())
return;
scoped_refptr<WebRtcAudioDeviceImpl> webrtc_audio_device(
new WebRtcAudioDeviceImpl());
WebRTCAutoDelete<webrtc::VoiceEngine> engine(webrtc::VoiceEngine::Create());
ASSERT_TRUE(engine.valid());
ScopedWebRTCPtr<webrtc::VoEBase> base(engine.get());
ASSERT_TRUE(base.valid());
int err = base->Init(webrtc_audio_device.get());
ASSERT_EQ(0, err);
int ch = base->CreateChannel();
EXPECT_NE(-1, ch);
EXPECT_TRUE(CreateAndInitializeCapturer(webrtc_audio_device.get()));
base::WaitableEvent event(false, false);
scoped_ptr<MockMediaStreamAudioSink> sink(
new MockMediaStreamAudioSink(&event));
// Create and start a local audio track. Starting the audio track will connect
// the audio track to the capturer and also start the source of the capturer.
scoped_refptr<WebRtcLocalAudioTrack> local_audio_track(
CreateAndStartLocalAudioTrack(
webrtc_audio_device->GetDefaultCapturer().get(), sink.get()));
// connect the VoE voice channel to the audio track.
static_cast<webrtc::AudioTrackInterface*>(local_audio_track.get())->
GetRenderer()->AddChannel(ch);
base::Time start_time = base::Time::Now();
EXPECT_EQ(0, base->StartSend(ch));
EXPECT_TRUE(event.TimedWait(TestTimeouts::action_timeout()));
int delay = (base::Time::Now() - start_time).InMilliseconds();
PrintPerfResultMs("webrtc_recording_setup_c", "t", delay);
webrtc_audio_device->GetDefaultCapturer()->Stop();
EXPECT_EQ(0, base->StopSend(ch));
EXPECT_EQ(0, base->DeleteChannel(ch));
EXPECT_EQ(0, base->Terminate());
}
// TODO(henrika): include on Android as well as soon as alla race conditions
// in OpenSLES are resolved.
#if defined(OS_ANDROID)
#define MAYBE_WebRtcPlayoutSetupTime DISABLED_WebRtcPlayoutSetupTime
#else
#define MAYBE_WebRtcPlayoutSetupTime WebRtcPlayoutSetupTime
#endif
TEST_F(MAYBE_WebRTCAudioDeviceTest, MAYBE_WebRtcPlayoutSetupTime) {
if (!has_output_devices_) {
LOG(WARNING) << "No output device detected.";
return;
}
scoped_ptr<media::AudioHardwareConfig> config =
CreateRealHardwareConfig(audio_manager_.get());
SetAudioHardwareConfig(config.get());
if (!HardwareSampleRatesAreValid())
return;
base::WaitableEvent event(false, false);
scoped_ptr<MockWebRtcAudioRendererSource> renderer_source(
new MockWebRtcAudioRendererSource(&event));
scoped_refptr<WebRtcAudioRenderer> renderer(
CreateDefaultWebRtcAudioRenderer(kRenderViewId));
renderer->Initialize(renderer_source.get());
scoped_refptr<MediaStreamAudioRenderer> proxy(
renderer->CreateSharedAudioRendererProxy());
proxy->Start();
// Start the timer and playout.
base::Time start_time = base::Time::Now();
proxy->Play();
EXPECT_TRUE(event.TimedWait(TestTimeouts::action_timeout()));
int delay = (base::Time::Now() - start_time).InMilliseconds();
PrintPerfResultMs("webrtc_playout_setup_c", "t", delay);
proxy->Stop();
}
#if defined(OS_LINUX) && !defined(OS_CHROMEOS) && defined(ARCH_CPU_ARM_FAMILY)
// Timing out on ARM linux bot: http://crbug.com/238490
#define MAYBE_WebRtcLoopbackTimeWithoutSignalProcessing \
DISABLED_WebRtcLoopbackTimeWithoutSignalProcessing
#else
#define MAYBE_WebRtcLoopbackTimeWithoutSignalProcessing \
WebRtcLoopbackTimeWithoutSignalProcessing
#endif
TEST_F(MAYBE_WebRTCAudioDeviceTest,
MAYBE_WebRtcLoopbackTimeWithoutSignalProcessing) {
#if defined(OS_WIN)
// This test hangs on WinXP: see http://crbug.com/318189.
if (base::win::GetVersion() <= base::win::VERSION_XP) {
LOG(WARNING) << "Test disabled due to the test hangs on WinXP.";
return;
}
#endif
int latency = RunWebRtcLoopbackTimeTest(audio_manager_.get(), false);
PrintPerfResultMs("webrtc_loopback_without_sigal_processing (100 packets)",
"t", latency);
}
#if defined(OS_LINUX) && !defined(OS_CHROMEOS) && defined(ARCH_CPU_ARM_FAMILY)
// Timing out on ARM linux bot: http://crbug.com/238490
#define MAYBE_WebRtcLoopbackTimeWithSignalProcessing \
DISABLED_WebRtcLoopbackTimeWithSignalProcessing
#else
#define MAYBE_WebRtcLoopbackTimeWithSignalProcessing \
WebRtcLoopbackTimeWithSignalProcessing
#endif
TEST_F(MAYBE_WebRTCAudioDeviceTest,
MAYBE_WebRtcLoopbackTimeWithSignalProcessing) {
#if defined(OS_WIN)
// This test hangs on WinXP: see http://crbug.com/318189.
if (base::win::GetVersion() <= base::win::VERSION_XP) {
LOG(WARNING) << "Test disabled due to the test hangs on WinXP.";
return;
}
#endif
int latency = RunWebRtcLoopbackTimeTest(audio_manager_.get(), true);
PrintPerfResultMs("webrtc_loopback_with_signal_processing (100 packets)",
"t", latency);
}
} // namespace content