blob: c3ef160808f2464e57b69a3b5d71614943463d98 [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 "content/renderer/media/webrtc_audio_device_impl.h"
#include "base/bind.h"
#include "base/metrics/histogram.h"
#include "base/string_util.h"
#include "base/win/windows_version.h"
#include "content/renderer/media/audio_device_factory.h"
#include "content/renderer/media/audio_hardware.h"
#include "content/renderer/render_thread_impl.h"
#include "media/audio/audio_util.h"
#include "media/audio/audio_parameters.h"
#include "media/audio/sample_rates.h"
using content::AudioDeviceFactory;
using media::AudioParameters;
static const int64 kMillisecondsBetweenProcessCalls = 5000;
static const double kMaxVolumeLevel = 255.0;
// Supported hardware sample rates for input and output sides.
#if defined(OS_WIN) || defined(OS_MACOSX)
// media::GetAudioInput[Output]HardwareSampleRate() asks the audio layer
// for its current sample rate (set by the user) on Windows and Mac OS X.
// The listed rates below adds restrictions and WebRtcAudioDeviceImpl::Init()
// will fail if the user selects any rate outside these ranges.
static int kValidInputRates[] = {96000, 48000, 44100, 32000, 16000, 8000};
static int kValidOutputRates[] = {96000, 48000, 44100};
#elif defined(OS_LINUX) || defined(OS_OPENBSD)
// media::GetAudioInput[Output]HardwareSampleRate() is hardcoded to return
// 48000 in both directions on Linux.
static int kValidInputRates[] = {48000};
static int kValidOutputRates[] = {48000};
#endif
namespace {
// Helper enum used for histogramming buffer sizes expressed in number of
// audio frames. This enumerator covers all supported sizes for all platforms.
// Example: k480 <=> 480 audio frames <=> 10ms@48kHz.
// TODO(henrika): can be moved to the media namespace if more clients need it.
// TODO(henrika): add support for k80 as well. Will be listed as unexpected for
// now. Very rare case though and most likeley only on Mac OS X.
enum AudioFramesPerBuffer {
k160,
k320,
k440, // WebRTC works internally with 440 audio frames at 44.1kHz.
k480,
k640,
k880,
k960,
k1440,
k1920,
kUnexpectedAudioBufferSize // Must always be last!
};
enum HistogramDirection {
kAudioOutput,
kAudioInput
};
} // anonymous namespace
// Helper method to convert integral values to their respective enum values
// above, or kUnexpectedAudioBufferSize if no match exists.
// TODO(henrika): add support for k80 as well given that 8000Hz input now has
// been added.
static AudioFramesPerBuffer AsAudioFramesPerBuffer(int frames_per_buffer) {
switch (frames_per_buffer) {
case 160: return k160;
case 320: return k320;
case 440: return k440;
case 480: return k480;
case 640: return k640;
case 880: return k880;
case 960: return k960;
case 1440: return k1440;
case 1920: return k1920;
}
return kUnexpectedAudioBufferSize;
}
// Helper method which adds histogram data to be uploaded as part of an
// UMA logging event. Names: "WebRTC.Audio[Output|Input]SampleRate".
static void AddHistogramSampleRate(HistogramDirection dir, int param) {
media::AudioSampleRate asr = media::AsAudioSampleRate(param);
if (asr != media::kUnexpectedAudioSampleRate) {
if (dir == kAudioOutput) {
UMA_HISTOGRAM_ENUMERATION("WebRTC.AudioOutputSampleRate",
asr, media::kUnexpectedAudioSampleRate);
} else {
UMA_HISTOGRAM_ENUMERATION("WebRTC.AudioInputSampleRate",
asr, media::kUnexpectedAudioSampleRate);
}
} else {
// Report unexpected sample rates using a unique histogram name.
if (dir == kAudioOutput) {
UMA_HISTOGRAM_COUNTS("WebRTC.AudioOutputSampleRateUnexpected", param);
} else {
UMA_HISTOGRAM_COUNTS("WebRTC.AudioInputSampleRateUnexpected", param);
}
}
}
// Helper method which adds histogram data to be uploaded as part of an
// UMA logging event. Names: "WebRTC.Audio[Output|Input]FramesPerBuffer".
static void AddHistogramFramesPerBuffer(HistogramDirection dir, int param) {
AudioFramesPerBuffer afpb = AsAudioFramesPerBuffer(param);
if (afpb != kUnexpectedAudioBufferSize) {
if (dir == kAudioOutput) {
UMA_HISTOGRAM_ENUMERATION("WebRTC.AudioOutputFramesPerBuffer",
afpb, kUnexpectedAudioBufferSize);
} else {
UMA_HISTOGRAM_ENUMERATION("WebRTC.AudioInputFramesPerBuffer",
afpb, kUnexpectedAudioBufferSize);
}
} else {
// Report unexpected sample rates using a unique histogram name.
if (dir == kAudioOutput) {
UMA_HISTOGRAM_COUNTS("WebRTC.AudioOutputFramesPerBufferUnexpected",
param);
} else {
UMA_HISTOGRAM_COUNTS("WebRTC.AudioInputFramesPerBufferUnexpected", param);
}
}
}
WebRtcAudioDeviceImpl::WebRtcAudioDeviceImpl()
: ref_count_(0),
render_loop_(base::MessageLoopProxy::current()),
audio_transport_callback_(NULL),
input_delay_ms_(0),
output_delay_ms_(0),
last_error_(AudioDeviceModule::kAdmErrNone),
last_process_time_(base::TimeTicks::Now()),
session_id_(0),
bytes_per_sample_(0),
initialized_(false),
playing_(false),
recording_(false),
agc_is_enabled_(false) {
DVLOG(1) << "WebRtcAudioDeviceImpl::WebRtcAudioDeviceImpl()";
// TODO(henrika): remove this restriction when factory is used for the
// input side as well.
DCHECK(RenderThreadImpl::current()) <<
"WebRtcAudioDeviceImpl must be constructed on the render thread";
audio_output_device_ = AudioDeviceFactory::NewOutputDevice();
DCHECK(audio_output_device_);
}
WebRtcAudioDeviceImpl::~WebRtcAudioDeviceImpl() {
DVLOG(1) << "WebRtcAudioDeviceImpl::~WebRtcAudioDeviceImpl()";
if (playing_)
StopPlayout();
if (recording_)
StopRecording();
if (initialized_)
Terminate();
}
int32_t WebRtcAudioDeviceImpl::AddRef() {
return base::subtle::Barrier_AtomicIncrement(&ref_count_, 1);
}
int32_t WebRtcAudioDeviceImpl::Release() {
int ret = base::subtle::Barrier_AtomicIncrement(&ref_count_, -1);
if (ret == 0) {
delete this;
}
return ret;
}
int WebRtcAudioDeviceImpl::Render(
const std::vector<float*>& audio_data,
int number_of_frames,
int audio_delay_milliseconds) {
DCHECK_LE(number_of_frames, output_buffer_size());
{
base::AutoLock auto_lock(lock_);
// Store the reported audio delay locally.
output_delay_ms_ = audio_delay_milliseconds;
}
const int channels = audio_data.size();
DCHECK_LE(channels, output_channels());
int samples_per_sec = output_sample_rate();
if (samples_per_sec == 44100) {
// Even if the hardware runs at 44.1kHz, we use 44.0 internally.
samples_per_sec = 44000;
}
int samples_per_10_msec = (samples_per_sec / 100);
const int bytes_per_10_msec =
channels * samples_per_10_msec * bytes_per_sample_;
uint32_t num_audio_samples = 0;
int accumulated_audio_samples = 0;
char* audio_byte_buffer = reinterpret_cast<char*>(output_buffer_.get());
// Get audio samples in blocks of 10 milliseconds from the registered
// webrtc::AudioTransport source. Keep reading until our internal buffer
// is full.
while (accumulated_audio_samples < number_of_frames) {
// Get 10ms and append output to temporary byte buffer.
audio_transport_callback_->NeedMorePlayData(samples_per_10_msec,
bytes_per_sample_,
channels,
samples_per_sec,
audio_byte_buffer,
num_audio_samples);
accumulated_audio_samples += num_audio_samples;
audio_byte_buffer += bytes_per_10_msec;
}
// Deinterleave each channel and convert to 32-bit floating-point
// with nominal range -1.0 -> +1.0 to match the callback format.
for (int channel_index = 0; channel_index < channels; ++channel_index) {
media::DeinterleaveAudioChannel(
output_buffer_.get(),
audio_data[channel_index],
channels,
channel_index,
bytes_per_sample_,
number_of_frames);
}
return number_of_frames;
}
void WebRtcAudioDeviceImpl::OnRenderError() {
DCHECK_EQ(MessageLoop::current(), ChildProcess::current()->io_message_loop());
// TODO(henrika): Implement error handling.
LOG(ERROR) << "OnRenderError()";
}
void WebRtcAudioDeviceImpl::Capture(const std::vector<float*>& audio_data,
int number_of_frames,
int audio_delay_milliseconds,
double volume) {
DCHECK_LE(number_of_frames, input_buffer_size());
#if defined(OS_WIN) || defined(OS_MACOSX)
DCHECK_LE(volume, 1.0);
#elif defined(OS_LINUX) || defined(OS_OPENBSD)
// We have a special situation on Linux where the microphone volume can be
// "higher than maximum". The input volume slider in the sound preference
// allows the user to set a scaling that is higher than 100%. It means that
// even if the reported maximum levels is N, the actual microphone level can
// go up to 1.5*N and that corresponds to a normalized |volume| of 1.5.
DCHECK_LE(volume, 1.5);
#endif
int output_delay_ms = 0;
{
base::AutoLock auto_lock(lock_);
// Store the reported audio delay locally.
input_delay_ms_ = audio_delay_milliseconds;
output_delay_ms = output_delay_ms_;
}
const int channels = audio_data.size();
DCHECK_LE(channels, input_channels());
uint32_t new_mic_level = 0;
// Interleave, scale, and clip input to int and store result in
// a local byte buffer.
media::InterleaveFloatToInt(audio_data,
input_buffer_.get(),
number_of_frames,
input_audio_parameters_.bits_per_sample() / 8);
int samples_per_sec = input_sample_rate();
if (samples_per_sec == 44100) {
// Even if the hardware runs at 44.1kHz, we use 44.0 internally.
samples_per_sec = 44000;
}
const int samples_per_10_msec = (samples_per_sec / 100);
const int bytes_per_10_msec =
channels * samples_per_10_msec * bytes_per_sample_;
int accumulated_audio_samples = 0;
char* audio_byte_buffer = reinterpret_cast<char*>(input_buffer_.get());
// Map internal volume range of [0.0, 1.0] into [0, 255] used by the
// webrtc::VoiceEngine.
uint32_t current_mic_level = static_cast<uint32_t>(volume * kMaxVolumeLevel);
// Write audio samples in blocks of 10 milliseconds to the registered
// webrtc::AudioTransport sink. Keep writing until our internal byte
// buffer is empty.
while (accumulated_audio_samples < number_of_frames) {
// Deliver 10ms of recorded 16-bit linear PCM audio.
audio_transport_callback_->RecordedDataIsAvailable(
audio_byte_buffer,
samples_per_10_msec,
bytes_per_sample_,
channels,
samples_per_sec,
input_delay_ms_ + output_delay_ms,
0, // TODO(henrika): |clock_drift| parameter is not utilized today.
current_mic_level,
new_mic_level);
accumulated_audio_samples += samples_per_10_msec;
audio_byte_buffer += bytes_per_10_msec;
}
// The AGC returns a non-zero microphone level if it has been decided
// that a new level should be set.
if (new_mic_level != 0) {
// Use IPC and set the new level. Note that, it will take some time
// before the new level is effective due to the IPC scheme.
// During this time, |current_mic_level| will contain "non-valid" values
// and it might reduce the AGC performance. Measurements on Windows 7 have
// shown that we might receive old volume levels for one or two callbacks.
SetMicrophoneVolume(new_mic_level);
}
}
void WebRtcAudioDeviceImpl::OnCaptureError() {
DCHECK_EQ(MessageLoop::current(), ChildProcess::current()->io_message_loop());
// TODO(henrika): Implement error handling.
LOG(ERROR) << "OnCaptureError()";
}
void WebRtcAudioDeviceImpl::OnDeviceStarted(const std::string& device_id) {
DVLOG(1) << "OnDeviceStarted (device_id=" << device_id << ")";
// Empty string is an invalid device id. Do nothing if a valid device has
// been started. Otherwise update the |recording_| state to false.
if (!device_id.empty())
return;
base::AutoLock auto_lock(lock_);
if (recording_)
recording_ = false;
}
void WebRtcAudioDeviceImpl::OnDeviceStopped() {
DVLOG(1) << "OnDeviceStopped";
base::AutoLock auto_lock(lock_);
if (recording_)
recording_ = false;
}
int32_t WebRtcAudioDeviceImpl::ChangeUniqueId(const int32_t id) {
NOTIMPLEMENTED();
return -1;
}
int32_t WebRtcAudioDeviceImpl::TimeUntilNextProcess() {
// Calculate the number of milliseconds until this module wants its
// Process method to be called.
base::TimeDelta delta_time = (base::TimeTicks::Now() - last_process_time_);
int64 time_until_next =
kMillisecondsBetweenProcessCalls - delta_time.InMilliseconds();
return static_cast<int32_t>(time_until_next);
}
int32_t WebRtcAudioDeviceImpl::Process() {
// TODO(henrika): it is possible to add functionality in this method, which
// is called periodically. The idea is that we should call one of the methods
// in the registered AudioDeviceObserver to inform the user about warnings
// or error states. Leave it empty for now.
last_process_time_ = base::TimeTicks::Now();
return 0;
}
int32_t WebRtcAudioDeviceImpl::ActiveAudioLayer(AudioLayer* audio_layer) const {
NOTIMPLEMENTED();
return -1;
}
webrtc::AudioDeviceModule::ErrorCode WebRtcAudioDeviceImpl::LastError() const {
return last_error_;
}
int32_t WebRtcAudioDeviceImpl::RegisterEventObserver(
webrtc::AudioDeviceObserver* event_callback) {
DVLOG(2) << "WARNING: WebRtcAudioDeviceImpl::RegisterEventObserver() "
<< "NOT IMPLEMENTED";
return -1;
}
int32_t WebRtcAudioDeviceImpl::RegisterAudioCallback(
webrtc::AudioTransport* audio_callback) {
DVLOG(1) << "RegisterAudioCallback()";
if (playing_ || recording_) {
LOG(ERROR) << "Unable to (de)register transport during active media";
return -1;
}
audio_transport_callback_ = audio_callback;
return 0;
}
int32_t WebRtcAudioDeviceImpl::Init() {
DVLOG(1) << "Init()";
// TODO(henrika): After switching to using the AudioDeviceFactory for
// instantiating the input device, maybe this isn't a requirement anymore?
if (!render_loop_->BelongsToCurrentThread()) {
int32_t error = 0;
base::WaitableEvent event(false, false);
// Ensure that we call Init() from the main render thread since
// the audio clients can only be created on this thread.
render_loop_->PostTask(
FROM_HERE,
base::Bind(&WebRtcAudioDeviceImpl::InitOnRenderThread,
this, &error, &event));
event.Wait();
return error;
}
// Calling Init() multiple times in a row is OK.
if (initialized_)
return 0;
DCHECK(!audio_input_device_);
DCHECK(!input_buffer_.get());
DCHECK(!output_buffer_.get());
// TODO(henrika): it could be possible to allow one of the directions (input
// or output) to use a non-supported rate. As an example: if only the
// output rate is OK, we could finalize Init() and only set up an
// AudioOutputDevice.
// Ask the browser for the default audio output hardware sample-rate.
// This request is based on a synchronous IPC message.
int out_sample_rate = audio_hardware::GetOutputSampleRate();
DVLOG(1) << "Audio output hardware sample rate: " << out_sample_rate;
AddHistogramSampleRate(kAudioOutput, out_sample_rate);
// Verify that the reported output hardware sample rate is supported
// on the current platform.
if (std::find(&kValidOutputRates[0],
&kValidOutputRates[0] + arraysize(kValidOutputRates),
out_sample_rate) ==
&kValidOutputRates[arraysize(kValidOutputRates)]) {
DLOG(ERROR) << out_sample_rate << " is not a supported output rate.";
return -1;
}
// Ask the browser for the default audio input hardware sample-rate.
// This request is based on a synchronous IPC message.
int in_sample_rate = audio_hardware::GetInputSampleRate();
DVLOG(1) << "Audio input hardware sample rate: " << in_sample_rate;
AddHistogramSampleRate(kAudioInput, in_sample_rate);
// Verify that the reported input hardware sample rate is supported
// on the current platform.
if (std::find(&kValidInputRates[0],
&kValidInputRates[0] + arraysize(kValidInputRates),
in_sample_rate) ==
&kValidInputRates[arraysize(kValidInputRates)]) {
DLOG(ERROR) << in_sample_rate << " is not a supported input rate.";
return -1;
}
// Ask the browser for the default number of audio input channels.
// This request is based on a synchronous IPC message.
ChannelLayout in_channel_layout = audio_hardware::GetInputChannelLayout();
DVLOG(1) << "Audio input hardware channels: " << in_channel_layout;
ChannelLayout out_channel_layout = CHANNEL_LAYOUT_MONO;
AudioParameters::Format in_format = AudioParameters::AUDIO_PCM_LINEAR;
int in_buffer_size = 0;
int out_buffer_size = 0;
// TODO(henrika): factor out all platform specific parts in separate
// functions. Code is a bit messy right now.
// Windows
#if defined(OS_WIN)
// Always use stereo rendering on Windows.
out_channel_layout = CHANNEL_LAYOUT_STEREO;
DVLOG(1) << "Using AUDIO_PCM_LOW_LATENCY as input mode on Windows.";
in_format = AudioParameters::AUDIO_PCM_LOW_LATENCY;
// Capture side: AUDIO_PCM_LOW_LATENCY is based on the Core Audio (WASAPI)
// API which was introduced in Windows Vista. For lower Windows versions,
// a callback-driven Wave implementation is used instead. An input buffer
// size of 10ms works well for both these implementations.
// Use different buffer sizes depending on the current hardware sample rate.
if (in_sample_rate == 44100) {
// We do run at 44.1kHz at the actual audio layer, but ask for frames
// at 44.0kHz to ensure that we can feed them to the webrtc::VoiceEngine.
in_buffer_size = 440;
} else {
in_buffer_size = (in_sample_rate / 100);
DCHECK_EQ(in_buffer_size * 100, in_sample_rate) <<
"Sample rate not supported. Should have been caught in Init().";
}
// Render side: AUDIO_PCM_LOW_LATENCY is based on the Core Audio (WASAPI)
// API which was introduced in Windows Vista. For lower Windows versions,
// a callback-driven Wave implementation is used instead. An output buffer
// size of 10ms works well for WASAPI but 30ms is needed for Wave.
// Use different buffer sizes depending on the current hardware sample rate.
if (out_sample_rate == 96000 || out_sample_rate == 48000) {
out_buffer_size = (out_sample_rate / 100);
} else {
// We do run at 44.1kHz at the actual audio layer, but ask for frames
// at 44.0kHz to ensure that we can feed them to the webrtc::VoiceEngine.
// TODO(henrika): figure out why we seem to need 20ms here for glitch-
// free audio.
out_buffer_size = 2 * 440;
}
// Windows XP and lower can't cope with 10 ms output buffer size.
// It must be extended to 30 ms (60 ms will be used internally by WaveOut).
if (!media::IsWASAPISupported()) {
out_buffer_size = 3 * out_buffer_size;
DLOG(WARNING) << "Extending the output buffer size by a factor of three "
<< "since Windows XP has been detected.";
}
// Mac OS X
#elif defined(OS_MACOSX)
out_channel_layout = CHANNEL_LAYOUT_MONO;
DVLOG(1) << "Using AUDIO_PCM_LOW_LATENCY as input mode on Mac OS X.";
in_format = AudioParameters::AUDIO_PCM_LOW_LATENCY;
// Capture side: AUDIO_PCM_LOW_LATENCY on Mac OS X is based on a callback-
// driven Core Audio implementation. Tests have shown that 10ms is a suitable
// frame size to use, both for 48kHz and 44.1kHz.
// Use different buffer sizes depending on the current hardware sample rate.
if (in_sample_rate == 44100) {
// We do run at 44.1kHz at the actual audio layer, but ask for frames
// at 44.0kHz to ensure that we can feed them to the webrtc::VoiceEngine.
in_buffer_size = 440;
} else {
in_buffer_size = (in_sample_rate / 100);
DCHECK_EQ(in_buffer_size * 100, in_sample_rate) <<
"Sample rate not supported. Should have been caught in Init().";
}
// Render side: AUDIO_PCM_LOW_LATENCY on Mac OS X is based on a callback-
// driven Core Audio implementation. Tests have shown that 10ms is a suitable
// frame size to use, both for 48kHz and 44.1kHz.
// Use different buffer sizes depending on the current hardware sample rate.
if (out_sample_rate == 48000) {
out_buffer_size = 480;
} else {
// We do run at 44.1kHz at the actual audio layer, but ask for frames
// at 44.0kHz to ensure that we can feed them to the webrtc::VoiceEngine.
out_buffer_size = 440;
}
// Linux
#elif defined(OS_LINUX) || defined(OS_OPENBSD)
in_channel_layout = CHANNEL_LAYOUT_STEREO;
out_channel_layout = CHANNEL_LAYOUT_MONO;
// Based on tests using the current ALSA implementation in Chrome, we have
// found that the best combination is 20ms on the input side and 10ms on the
// output side.
// TODO(henrika): It might be possible to reduce the input buffer
// size and reduce the delay even more.
in_buffer_size = 2 * 480;
out_buffer_size = 480;
#else
DLOG(ERROR) << "Unsupported platform";
return -1;
#endif
// Store utilized parameters to ensure that we can check them
// after a successful initialization.
output_audio_parameters_.Reset(
AudioParameters::AUDIO_PCM_LOW_LATENCY, out_channel_layout,
out_sample_rate, 16, out_buffer_size);
input_audio_parameters_.Reset(
in_format, in_channel_layout, in_sample_rate,
16, in_buffer_size);
// Create and configure the audio capturing client.
audio_input_device_ = AudioDeviceFactory::NewInputDevice();
audio_input_device_->Initialize(input_audio_parameters_, this, this);
UMA_HISTOGRAM_ENUMERATION("WebRTC.AudioOutputChannelLayout",
out_channel_layout, CHANNEL_LAYOUT_MAX);
UMA_HISTOGRAM_ENUMERATION("WebRTC.AudioInputChannelLayout",
in_channel_layout, CHANNEL_LAYOUT_MAX);
AddHistogramFramesPerBuffer(kAudioOutput, out_buffer_size);
AddHistogramFramesPerBuffer(kAudioInput, in_buffer_size);
// Configure the audio rendering client.
audio_output_device_->Initialize(output_audio_parameters_, this);
DCHECK(audio_input_device_);
// Allocate local audio buffers based on the parameters above.
// It is assumed that each audio sample contains 16 bits and each
// audio frame contains one or two audio samples depending on the
// number of channels.
input_buffer_.reset(new int16[input_buffer_size() * input_channels()]);
output_buffer_.reset(new int16[output_buffer_size() * output_channels()]);
DCHECK(input_buffer_.get());
DCHECK(output_buffer_.get());
bytes_per_sample_ = sizeof(*input_buffer_.get());
initialized_ = true;
DVLOG(1) << "Capture parameters (size/channels/rate): ("
<< input_buffer_size() << "/" << input_channels() << "/"
<< input_sample_rate() << ")";
DVLOG(1) << "Render parameters (size/channels/rate): ("
<< output_buffer_size() << "/" << output_channels() << "/"
<< output_sample_rate() << ")";
return 0;
}
void WebRtcAudioDeviceImpl::InitOnRenderThread(int32_t* error,
base::WaitableEvent* event) {
DCHECK(render_loop_->BelongsToCurrentThread());
*error = Init();
event->Signal();
}
int32_t WebRtcAudioDeviceImpl::Terminate() {
DVLOG(1) << "Terminate()";
// Calling Terminate() multiple times in a row is OK.
if (!initialized_)
return 0;
DCHECK(audio_input_device_);
DCHECK(input_buffer_.get());
DCHECK(output_buffer_.get());
// Release all resources allocated in Init().
audio_input_device_ = NULL;
input_buffer_.reset();
output_buffer_.reset();
initialized_ = false;
return 0;
}
bool WebRtcAudioDeviceImpl::Initialized() const {
return initialized_;
}
int16_t WebRtcAudioDeviceImpl::PlayoutDevices() {
NOTIMPLEMENTED();
return -1;
}
int16_t WebRtcAudioDeviceImpl::RecordingDevices() {
NOTIMPLEMENTED();
return -1;
}
int32_t WebRtcAudioDeviceImpl::PlayoutDeviceName(
uint16_t index,
char name[webrtc::kAdmMaxDeviceNameSize],
char guid[webrtc::kAdmMaxGuidSize]) {
NOTIMPLEMENTED();
return -1;
}
int32_t WebRtcAudioDeviceImpl::RecordingDeviceName(
uint16_t index,
char name[webrtc::kAdmMaxDeviceNameSize],
char guid[webrtc::kAdmMaxGuidSize]) {
NOTIMPLEMENTED();
return -1;
}
int32_t WebRtcAudioDeviceImpl::SetPlayoutDevice(uint16_t index) {
DVLOG(2) << "WARNING: WebRtcAudioDeviceImpl::SetPlayoutDevice() "
<< "NOT IMPLEMENTED";
return 0;
}
int32_t WebRtcAudioDeviceImpl::SetPlayoutDevice(WindowsDeviceType device) {
DVLOG(2) << "WARNING: WebRtcAudioDeviceImpl::SetPlayoutDevice() "
<< "NOT IMPLEMENTED";
return 0;
}
int32_t WebRtcAudioDeviceImpl::SetRecordingDevice(uint16_t index) {
DVLOG(2) << "WARNING: WebRtcAudioDeviceImpl::SetRecordingDevice() "
<< "NOT IMPLEMENTED";
return 0;
}
int32_t WebRtcAudioDeviceImpl::SetRecordingDevice(WindowsDeviceType device) {
DVLOG(2) << "WARNING: WebRtcAudioDeviceImpl::SetRecordingDevice() "
<< "NOT IMPLEMENTED";
return 0;
}
int32_t WebRtcAudioDeviceImpl::PlayoutIsAvailable(bool* available) {
DVLOG(1) << "PlayoutIsAvailable()";
*available = initialized();
return 0;
}
int32_t WebRtcAudioDeviceImpl::InitPlayout() {
DVLOG(2) << "WARNING: WebRtcAudioDeviceImpl::InitPlayout() "
<< "NOT IMPLEMENTED";
return 0;
}
bool WebRtcAudioDeviceImpl::PlayoutIsInitialized() const {
DVLOG(1) << "PlayoutIsInitialized()";
return initialized();
}
int32_t WebRtcAudioDeviceImpl::RecordingIsAvailable(bool* available) {
DVLOG(1) << "RecordingIsAvailable()";
*available = (audio_input_device_ != NULL);
return 0;
}
int32_t WebRtcAudioDeviceImpl::InitRecording() {
DVLOG(2) << "WARNING: WebRtcAudioDeviceImpl::InitRecording() "
<< "NOT IMPLEMENTED";
return 0;
}
bool WebRtcAudioDeviceImpl::RecordingIsInitialized() const {
DVLOG(1) << "RecordingIsInitialized()";
return (audio_input_device_ != NULL);
}
int32_t WebRtcAudioDeviceImpl::StartPlayout() {
DVLOG(1) << "StartPlayout()";
LOG_IF(ERROR, !audio_transport_callback_) << "Audio transport is missing";
if (!audio_transport_callback_) {
return -1;
}
if (playing_) {
// webrtc::VoiceEngine assumes that it is OK to call Start() twice and
// that the call is ignored the second time.
return 0;
}
start_render_time_ = base::Time::Now();
audio_output_device_->Start();
playing_ = true;
return 0;
}
int32_t WebRtcAudioDeviceImpl::StopPlayout() {
DVLOG(1) << "StopPlayout()";
if (!playing_) {
// webrtc::VoiceEngine assumes that it is OK to call Stop() just in case.
return 0;
}
// Add histogram data to be uploaded as part of an UMA logging event.
// This histogram keeps track of total playout times.
if (!start_render_time_.is_null()) {
base::TimeDelta render_time = base::Time::Now() - start_render_time_;
UMA_HISTOGRAM_LONG_TIMES("WebRTC.AudioRenderTime", render_time);
}
audio_output_device_->Stop();
playing_ = false;
return 0;
}
bool WebRtcAudioDeviceImpl::Playing() const {
return playing_;
}
int32_t WebRtcAudioDeviceImpl::StartRecording() {
DVLOG(1) << "StartRecording()";
LOG_IF(ERROR, !audio_transport_callback_) << "Audio transport is missing";
if (!audio_transport_callback_) {
return -1;
}
if (session_id_ <= 0) {
LOG(WARNING) << session_id_ << " is an invalid session id.";
// TODO(xians): enable the return -1 when MediaStreamManager can handle
// AudioInputDeviceManager.
// return -1;
}
base::AutoLock auto_lock(lock_);
if (recording_) {
// webrtc::VoiceEngine assumes that it is OK to call Start() twice and
// that the call is ignored the second time.
return 0;
}
start_capture_time_ = base::Time::Now();
// Specify the session_id which is mapped to a certain device.
audio_input_device_->SetDevice(session_id_);
audio_input_device_->Start();
recording_ = true;
return 0;
}
int32_t WebRtcAudioDeviceImpl::StopRecording() {
DVLOG(1) << "StopRecording()";
{
base::AutoLock auto_lock(lock_);
if (!recording_) {
// webrtc::VoiceEngine assumes that it is OK to call Stop()
// more than once.
return 0;
}
}
// Add histogram data to be uploaded as part of an UMA logging event.
// This histogram keeps track of total recording times.
if (!start_capture_time_.is_null()) {
base::TimeDelta capture_time = base::Time::Now() - start_capture_time_;
UMA_HISTOGRAM_LONG_TIMES("WebRTC.AudioCaptureTime", capture_time);
}
audio_input_device_->Stop();
base::AutoLock auto_lock(lock_);
recording_ = false;
return 0;
}
bool WebRtcAudioDeviceImpl::Recording() const {
return recording_;
}
int32_t WebRtcAudioDeviceImpl::SetAGC(bool enable) {
DVLOG(1) << "SetAGC(enable=" << enable << ")";
// The current implementation does not support changing the AGC state while
// recording. Using this approach simplifies the design and it is also
// inline with the latest WebRTC standard.
DCHECK(initialized_);
DCHECK(!recording_) << "Unable to set AGC state while recording is active.";
if (recording_) {
return -1;
}
audio_input_device_->SetAutomaticGainControl(enable);
agc_is_enabled_ = enable;
return 0;
}
bool WebRtcAudioDeviceImpl::AGC() const {
// To reduce the usage of IPC messages, an internal AGC state is used.
// TODO(henrika): investigate if there is a need for a "deeper" getter.
return agc_is_enabled_;
}
int32_t WebRtcAudioDeviceImpl::SetWaveOutVolume(uint16_t volume_left,
uint16_t volume_right) {
NOTIMPLEMENTED();
return -1;
}
int32_t WebRtcAudioDeviceImpl::WaveOutVolume(
uint16_t* volume_left,
uint16_t* volume_right) const {
NOTIMPLEMENTED();
return -1;
}
int32_t WebRtcAudioDeviceImpl::SpeakerIsAvailable(bool* available) {
DVLOG(2) << "WARNING: WebRtcAudioDeviceImpl::SpeakerIsAvailable() "
<< "NOT IMPLEMENTED";
*available = true;
return 0;
}
int32_t WebRtcAudioDeviceImpl::InitSpeaker() {
DVLOG(2) << "WARNING: WebRtcAudioDeviceImpl::InitSpeaker() "
<< "NOT IMPLEMENTED";
return 0;
}
bool WebRtcAudioDeviceImpl::SpeakerIsInitialized() const {
DVLOG(2) << "WARNING: WebRtcAudioDeviceImpl::SpeakerIsInitialized() "
<< "NOT IMPLEMENTED";
return true;
}
int32_t WebRtcAudioDeviceImpl::MicrophoneIsAvailable(bool* available) {
DVLOG(2) << "WARNING: WebRtcAudioDeviceImpl::MicrophoneIsAvailable() "
<< "NOT IMPLEMENTED";
*available = true;
return 0;
}
int32_t WebRtcAudioDeviceImpl::InitMicrophone() {
DVLOG(2) << "WARNING: WebRtcAudioDeviceImpl::InitMicrophone() "
<< "NOT IMPLEMENTED";
return 0;
}
bool WebRtcAudioDeviceImpl::MicrophoneIsInitialized() const {
NOTIMPLEMENTED();
return true;
}
int32_t WebRtcAudioDeviceImpl::SpeakerVolumeIsAvailable(
bool* available) {
NOTIMPLEMENTED();
return -1;
}
int32_t WebRtcAudioDeviceImpl::SetSpeakerVolume(uint32_t volume) {
NOTIMPLEMENTED();
return -1;
}
int32_t WebRtcAudioDeviceImpl::SpeakerVolume(uint32_t* volume) const {
NOTIMPLEMENTED();
return -1;
}
int32_t WebRtcAudioDeviceImpl::MaxSpeakerVolume(uint32_t* max_volume) const {
NOTIMPLEMENTED();
return -1;
}
int32_t WebRtcAudioDeviceImpl::MinSpeakerVolume(uint32_t* min_volume) const {
NOTIMPLEMENTED();
return -1;
}
int32_t WebRtcAudioDeviceImpl::SpeakerVolumeStepSize(
uint16_t* step_size) const {
NOTIMPLEMENTED();
return -1;
}
int32_t WebRtcAudioDeviceImpl::MicrophoneVolumeIsAvailable(bool* available) {
NOTIMPLEMENTED();
return -1;
}
int32_t WebRtcAudioDeviceImpl::SetMicrophoneVolume(uint32_t volume) {
DVLOG(1) << "SetMicrophoneVolume(" << volume << ")";
if (volume > kMaxVolumeLevel)
return -1;
// WebRTC uses a range of [0, 255] to represent the level of the microphone
// volume. The IPC channel between the renderer and browser process works
// with doubles in the [0.0, 1.0] range and we have to compensate for that.
double normalized_volume = static_cast<double>(volume / kMaxVolumeLevel);
audio_input_device_->SetVolume(normalized_volume);
return 0;
}
int32_t WebRtcAudioDeviceImpl::MicrophoneVolume(uint32_t* volume) const {
// The microphone level is fed to this class using the Capture() callback
// and this external API should not be used. Additional IPC messages are
// required if support for this API is ever needed.
NOTREACHED();
return -1;
}
int32_t WebRtcAudioDeviceImpl::MaxMicrophoneVolume(uint32_t* max_volume) const {
*max_volume = kMaxVolumeLevel;
return 0;
}
int32_t WebRtcAudioDeviceImpl::MinMicrophoneVolume(uint32_t* min_volume) const {
*min_volume = 0;
return 0;
}
int32_t WebRtcAudioDeviceImpl::MicrophoneVolumeStepSize(
uint16_t* step_size) const {
NOTREACHED();
return -1;
}
int32_t WebRtcAudioDeviceImpl::SpeakerMuteIsAvailable(bool* available) {
NOTIMPLEMENTED();
return -1;
}
int32_t WebRtcAudioDeviceImpl::SetSpeakerMute(bool enable) {
NOTIMPLEMENTED();
return -1;
}
int32_t WebRtcAudioDeviceImpl::SpeakerMute(bool* enabled) const {
NOTIMPLEMENTED();
return -1;
}
int32_t WebRtcAudioDeviceImpl::MicrophoneMuteIsAvailable(
bool* available) {
NOTIMPLEMENTED();
return -1;
}
int32_t WebRtcAudioDeviceImpl::SetMicrophoneMute(bool enable) {
NOTIMPLEMENTED();
return -1;
}
int32_t WebRtcAudioDeviceImpl::MicrophoneMute(bool* enabled) const {
NOTIMPLEMENTED();
return -1;
}
int32_t WebRtcAudioDeviceImpl::MicrophoneBoostIsAvailable(bool* available) {
NOTIMPLEMENTED();
return -1;
}
int32_t WebRtcAudioDeviceImpl::SetMicrophoneBoost(bool enable) {
NOTIMPLEMENTED();
return -1;
}
int32_t WebRtcAudioDeviceImpl::MicrophoneBoost(bool* enabled) const {
NOTIMPLEMENTED();
return -1;
}
int32_t WebRtcAudioDeviceImpl::StereoPlayoutIsAvailable(bool* available) const {
DCHECK(initialized_) << "Init() must be called first.";
*available = (output_channels() == 2);
return 0;
}
int32_t WebRtcAudioDeviceImpl::SetStereoPlayout(bool enable) {
DVLOG(2) << "WARNING: WebRtcAudioDeviceImpl::SetStereoPlayout() "
<< "NOT IMPLEMENTED";
return 0;
}
int32_t WebRtcAudioDeviceImpl::StereoPlayout(bool* enabled) const {
DVLOG(2) << "WARNING: WebRtcAudioDeviceImpl::StereoPlayout() "
<< "NOT IMPLEMENTED";
return 0;
}
int32_t WebRtcAudioDeviceImpl::StereoRecordingIsAvailable(
bool* available) const {
DCHECK(initialized_) << "Init() must be called first.";
*available = (input_channels() == 2);
return 0;
}
int32_t WebRtcAudioDeviceImpl::SetStereoRecording(bool enable) {
DVLOG(2) << "WARNING: WebRtcAudioDeviceImpl::SetStereoRecording() "
<< "NOT IMPLEMENTED";
return -1;
}
int32_t WebRtcAudioDeviceImpl::StereoRecording(bool* enabled) const {
DVLOG(2) << "WARNING: WebRtcAudioDeviceImpl::StereoRecording() "
<< "NOT IMPLEMENTED";
return -1;
}
int32_t WebRtcAudioDeviceImpl::SetRecordingChannel(const ChannelType channel) {
DVLOG(2) << "WARNING: WebRtcAudioDeviceImpl::SetRecordingChannel() "
<< "NOT IMPLEMENTED";
return -1;
}
int32_t WebRtcAudioDeviceImpl::RecordingChannel(ChannelType* channel) const {
DVLOG(2) << "WARNING: WebRtcAudioDeviceImpl::RecordingChannel() "
<< "NOT IMPLEMENTED";
return -1;
}
int32_t WebRtcAudioDeviceImpl::SetPlayoutBuffer(const BufferType type,
uint16_t size_ms) {
NOTIMPLEMENTED();
return -1;
}
int32_t WebRtcAudioDeviceImpl::PlayoutBuffer(BufferType* type,
uint16_t* size_ms) const {
NOTIMPLEMENTED();
return -1;
}
int32_t WebRtcAudioDeviceImpl::PlayoutDelay(uint16_t* delay_ms) const {
// Report the cached output delay value.
base::AutoLock auto_lock(lock_);
*delay_ms = static_cast<uint16_t>(output_delay_ms_);
return 0;
}
int32_t WebRtcAudioDeviceImpl::RecordingDelay(uint16_t* delay_ms) const {
// Report the cached output delay value.
base::AutoLock auto_lock(lock_);
*delay_ms = static_cast<uint16_t>(input_delay_ms_);
return 0;
}
int32_t WebRtcAudioDeviceImpl::CPULoad(uint16_t* load) const {
NOTIMPLEMENTED();
return -1;
}
int32_t WebRtcAudioDeviceImpl::StartRawOutputFileRecording(
const char pcm_file_name_utf8[webrtc::kAdmMaxFileNameSize]) {
NOTIMPLEMENTED();
return -1;
}
int32_t WebRtcAudioDeviceImpl::StopRawOutputFileRecording() {
NOTIMPLEMENTED();
return -1;
}
int32_t WebRtcAudioDeviceImpl::StartRawInputFileRecording(
const char pcm_file_name_utf8[webrtc::kAdmMaxFileNameSize]) {
NOTIMPLEMENTED();
return -1;
}
int32_t WebRtcAudioDeviceImpl::StopRawInputFileRecording() {
NOTIMPLEMENTED();
return -1;
}
int32_t WebRtcAudioDeviceImpl::SetRecordingSampleRate(
const uint32_t samples_per_sec) {
// Sample rate should only be set at construction.
NOTIMPLEMENTED();
return -1;
}
int32_t WebRtcAudioDeviceImpl::RecordingSampleRate(
uint32_t* samples_per_sec) const {
// Returns the sample rate set at construction.
*samples_per_sec = static_cast<uint32_t>(input_sample_rate());
return 0;
}
int32_t WebRtcAudioDeviceImpl::SetPlayoutSampleRate(
const uint32_t samples_per_sec) {
// Sample rate should only be set at construction.
NOTIMPLEMENTED();
return -1;
}
int32_t WebRtcAudioDeviceImpl::PlayoutSampleRate(
uint32_t* samples_per_sec) const {
// Returns the sample rate set at construction.
*samples_per_sec = static_cast<uint32_t>(output_sample_rate());
return 0;
}
int32_t WebRtcAudioDeviceImpl::ResetAudioDevice() {
NOTIMPLEMENTED();
return -1;
}
int32_t WebRtcAudioDeviceImpl::SetLoudspeakerStatus(bool enable) {
NOTIMPLEMENTED();
return -1;
}
int32_t WebRtcAudioDeviceImpl::GetLoudspeakerStatus(bool* enabled) const {
NOTIMPLEMENTED();
return -1;
}
void WebRtcAudioDeviceImpl::SetSessionId(int session_id) {
session_id_ = session_id;
}