This source file includes following definitions.
- ZeroBufferList
- WrapBufferList
- current_hardware_pending_bytes_
- Open
- Close
- Start
- Stop
- SetVolume
- GetVolume
- Render
- ProvideInput
- InputProc
- GetHardwareLatency
- GetPlayoutLatency
- CreateIOBusses
- EnableIO
- SetStreamFormat
- ConfigureAUHAL
#include "media/audio/mac/audio_auhal_mac.h"
#include <CoreServices/CoreServices.h>
#include "base/basictypes.h"
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/debug/trace_event.h"
#include "base/logging.h"
#include "base/mac/mac_logging.h"
#include "base/time/time.h"
#include "media/audio/mac/audio_manager_mac.h"
#include "media/base/audio_pull_fifo.h"
namespace media {
static void ZeroBufferList(AudioBufferList* buffer_list) {
for (size_t i = 0; i < buffer_list->mNumberBuffers; ++i) {
memset(buffer_list->mBuffers[i].mData,
0,
buffer_list->mBuffers[i].mDataByteSize);
}
}
static void WrapBufferList(AudioBufferList* buffer_list,
AudioBus* bus,
int frames) {
DCHECK(buffer_list);
DCHECK(bus);
const int channels = bus->channels();
const int buffer_list_channels = buffer_list->mNumberBuffers;
CHECK_EQ(channels, buffer_list_channels);
for (int i = 0; i < channels; ++i) {
bus->SetChannelData(
i, static_cast<float*>(buffer_list->mBuffers[i].mData));
}
bus->set_frames(frames);
}
AUHALStream::AUHALStream(
AudioManagerMac* manager,
const AudioParameters& params,
AudioDeviceID device)
: manager_(manager),
params_(params),
input_channels_(params_.input_channels()),
output_channels_(params_.channels()),
number_of_frames_(params_.frames_per_buffer()),
source_(NULL),
device_(device),
audio_unit_(0),
volume_(1),
hardware_latency_frames_(0),
stopped_(false),
input_buffer_list_(NULL),
current_hardware_pending_bytes_(0) {
DCHECK(manager_);
VLOG(1) << "AUHALStream::AUHALStream()";
VLOG(1) << "Device: " << device;
VLOG(1) << "Input channels: " << input_channels_;
VLOG(1) << "Output channels: " << output_channels_;
VLOG(1) << "Sample rate: " << params_.sample_rate();
VLOG(1) << "Buffer size: " << number_of_frames_;
}
AUHALStream::~AUHALStream() {
}
bool AUHALStream::Open() {
int device_input_channels;
bool got_input_channels = AudioManagerMac::GetDeviceChannels(
device_,
kAudioDevicePropertyScopeInput,
&device_input_channels);
int device_output_channels;
bool got_output_channels = AudioManagerMac::GetDeviceChannels(
device_,
kAudioDevicePropertyScopeOutput,
&device_output_channels);
if (!got_input_channels ||
input_channels_ < 0 || input_channels_ > device_input_channels) {
LOG(ERROR) << "AudioDevice does not support requested input channels.";
return false;
}
if (!got_output_channels ||
output_channels_ <= 0 || output_channels_ > device_output_channels) {
LOG(ERROR) << "AudioDevice does not support requested output channels.";
return false;
}
int sample_rate = AudioManagerMac::HardwareSampleRateForDevice(device_);
if (sample_rate != params_.sample_rate()) {
LOG(ERROR) << "Requested sample-rate: " << params_.sample_rate()
<< " must match the hardware sample-rate: " << sample_rate;
return false;
}
CreateIOBusses();
bool configured = ConfigureAUHAL();
if (configured)
hardware_latency_frames_ = GetHardwareLatency();
return configured;
}
void AUHALStream::Close() {
if (input_buffer_list_) {
input_buffer_list_storage_.reset();
input_buffer_list_ = NULL;
input_bus_.reset(NULL);
output_bus_.reset(NULL);
}
if (audio_unit_) {
OSStatus result = AudioUnitUninitialize(audio_unit_);
OSSTATUS_DLOG_IF(ERROR, result != noErr, result)
<< "AudioUnitUninitialize() failed.";
result = AudioComponentInstanceDispose(audio_unit_);
OSSTATUS_DLOG_IF(ERROR, result != noErr, result)
<< "AudioComponentInstanceDispose() failed.";
}
manager_->ReleaseOutputStream(this);
}
void AUHALStream::Start(AudioSourceCallback* callback) {
DCHECK(callback);
if (!audio_unit_) {
DLOG(ERROR) << "Open() has not been called successfully";
return;
}
if (manager_->ShouldDeferOutputStreamStart()) {
DCHECK(deferred_start_cb_.IsCancelled());
deferred_start_cb_.Reset(
base::Bind(&AUHALStream::Start, base::Unretained(this), callback));
manager_->GetTaskRunner()->PostDelayedTask(
FROM_HERE, deferred_start_cb_.callback(), base::TimeDelta::FromSeconds(
AudioManagerMac::kStartDelayInSecsForPowerEvents));
return;
}
stopped_ = false;
audio_fifo_.reset();
{
base::AutoLock auto_lock(source_lock_);
source_ = callback;
}
OSStatus result = AudioOutputUnitStart(audio_unit_);
if (result == noErr)
return;
Stop();
OSSTATUS_DLOG(ERROR, result) << "AudioOutputUnitStart() failed.";
callback->OnError(this);
}
void AUHALStream::Stop() {
deferred_start_cb_.Cancel();
if (stopped_)
return;
OSStatus result = AudioOutputUnitStop(audio_unit_);
OSSTATUS_DLOG_IF(ERROR, result != noErr, result)
<< "AudioOutputUnitStop() failed.";
if (result != noErr)
source_->OnError(this);
base::AutoLock auto_lock(source_lock_);
source_ = NULL;
stopped_ = true;
}
void AUHALStream::SetVolume(double volume) {
volume_ = static_cast<float>(volume);
}
void AUHALStream::GetVolume(double* volume) {
*volume = volume_;
}
OSStatus AUHALStream::Render(
AudioUnitRenderActionFlags* flags,
const AudioTimeStamp* output_time_stamp,
UInt32 bus_number,
UInt32 number_of_frames,
AudioBufferList* io_data) {
TRACE_EVENT0("audio", "AUHALStream::Render");
if (number_of_frames != number_of_frames_) {
if (!audio_fifo_) {
VLOG(1) << "Audio frame size change detected; adding FIFO to compensate.";
audio_fifo_.reset(new AudioPullFifo(
output_channels_,
number_of_frames_,
base::Bind(&AUHALStream::ProvideInput, base::Unretained(this))));
}
if (input_channels_ > 0)
input_bus_->Zero();
} else {
if (input_channels_ > 0 && input_buffer_list_) {
OSStatus result = AudioUnitRender(audio_unit_,
flags,
output_time_stamp,
1,
number_of_frames,
input_buffer_list_);
if (result != noErr)
ZeroBufferList(input_buffer_list_);
}
}
WrapBufferList(io_data, output_bus_.get(), number_of_frames);
const double playout_latency_frames = GetPlayoutLatency(output_time_stamp);
current_hardware_pending_bytes_ = static_cast<uint32>(
(playout_latency_frames + 0.5) * params_.GetBytesPerFrame());
if (audio_fifo_)
audio_fifo_->Consume(output_bus_.get(), output_bus_->frames());
else
ProvideInput(0, output_bus_.get());
return noErr;
}
void AUHALStream::ProvideInput(int frame_delay, AudioBus* dest) {
base::AutoLock auto_lock(source_lock_);
if (!source_) {
dest->Zero();
return;
}
source_->OnMoreIOData(
input_bus_.get(),
dest,
AudioBuffersState(0,
current_hardware_pending_bytes_ +
frame_delay * params_.GetBytesPerFrame()));
dest->Scale(volume_);
}
OSStatus AUHALStream::InputProc(
void* user_data,
AudioUnitRenderActionFlags* flags,
const AudioTimeStamp* output_time_stamp,
UInt32 bus_number,
UInt32 number_of_frames,
AudioBufferList* io_data) {
AUHALStream* audio_output =
static_cast<AUHALStream*>(user_data);
if (!audio_output)
return -1;
return audio_output->Render(
flags,
output_time_stamp,
bus_number,
number_of_frames,
io_data);
}
double AUHALStream::GetHardwareLatency() {
if (!audio_unit_ || device_ == kAudioObjectUnknown) {
DLOG(WARNING) << "AudioUnit is NULL or device ID is unknown";
return 0.0;
}
Float64 audio_unit_latency_sec = 0.0;
UInt32 size = sizeof(audio_unit_latency_sec);
OSStatus result = AudioUnitGetProperty(
audio_unit_,
kAudioUnitProperty_Latency,
kAudioUnitScope_Global,
0,
&audio_unit_latency_sec,
&size);
if (result != noErr) {
OSSTATUS_DLOG(WARNING, result) << "Could not get AudioUnit latency";
return 0.0;
}
static const AudioObjectPropertyAddress property_address = {
kAudioDevicePropertyLatency,
kAudioDevicePropertyScopeOutput,
kAudioObjectPropertyElementMaster
};
UInt32 device_latency_frames = 0;
size = sizeof(device_latency_frames);
result = AudioObjectGetPropertyData(
device_,
&property_address,
0,
NULL,
&size,
&device_latency_frames);
if (result != noErr) {
OSSTATUS_DLOG(WARNING, result) << "Could not get audio device latency";
return 0.0;
}
return static_cast<double>((audio_unit_latency_sec *
output_format_.mSampleRate) + device_latency_frames);
}
double AUHALStream::GetPlayoutLatency(
const AudioTimeStamp* output_time_stamp) {
if ((output_time_stamp->mFlags & kAudioTimeStampHostTimeValid) == 0)
return 0;
UInt64 output_time_ns = AudioConvertHostTimeToNanos(
output_time_stamp->mHostTime);
UInt64 now_ns = AudioConvertHostTimeToNanos(AudioGetCurrentHostTime());
if (now_ns > output_time_ns)
return 0;
double delay_frames = static_cast<double>
(1e-9 * (output_time_ns - now_ns) * output_format_.mSampleRate);
return (delay_frames + hardware_latency_frames_);
}
void AUHALStream::CreateIOBusses() {
if (input_channels_ > 0) {
size_t buffer_list_size = offsetof(AudioBufferList, mBuffers[0]) +
(sizeof(AudioBuffer) * input_channels_);
input_buffer_list_storage_.reset(new uint8[buffer_list_size]);
input_buffer_list_ =
reinterpret_cast<AudioBufferList*>(input_buffer_list_storage_.get());
input_buffer_list_->mNumberBuffers = input_channels_;
input_bus_ = AudioBus::Create(input_channels_, number_of_frames_);
UInt32 buffer_size_bytes = input_bus_->frames() * sizeof(Float32);
for (size_t i = 0; i < input_buffer_list_->mNumberBuffers; ++i) {
input_buffer_list_->mBuffers[i].mNumberChannels = 1;
input_buffer_list_->mBuffers[i].mDataByteSize = buffer_size_bytes;
input_buffer_list_->mBuffers[i].mData = input_bus_->channel(i);
}
}
DCHECK_GT(output_channels_, 0);
output_bus_ = AudioBus::CreateWrapper(output_channels_);
}
bool AUHALStream::EnableIO(bool enable, UInt32 scope) {
UInt32 enable_IO = enable ? 1 : 0;
OSStatus result = AudioUnitSetProperty(
audio_unit_,
kAudioOutputUnitProperty_EnableIO,
scope,
(scope == kAudioUnitScope_Input) ? 1 : 0,
&enable_IO,
sizeof(enable_IO));
return (result == noErr);
}
bool AUHALStream::SetStreamFormat(
AudioStreamBasicDescription* desc,
int channels,
UInt32 scope,
UInt32 element) {
DCHECK(desc);
AudioStreamBasicDescription& format = *desc;
format.mSampleRate = params_.sample_rate();
format.mFormatID = kAudioFormatLinearPCM;
format.mFormatFlags = kAudioFormatFlagsNativeFloatPacked |
kLinearPCMFormatFlagIsNonInterleaved;
format.mBytesPerPacket = sizeof(Float32);
format.mFramesPerPacket = 1;
format.mBytesPerFrame = sizeof(Float32);
format.mChannelsPerFrame = channels;
format.mBitsPerChannel = 32;
format.mReserved = 0;
OSStatus result = AudioUnitSetProperty(
audio_unit_,
kAudioUnitProperty_StreamFormat,
scope,
element,
&format,
sizeof(format));
return (result == noErr);
}
bool AUHALStream::ConfigureAUHAL() {
if (device_ == kAudioObjectUnknown ||
(input_channels_ == 0 && output_channels_ == 0))
return false;
AudioComponentDescription desc = {
kAudioUnitType_Output,
kAudioUnitSubType_HALOutput,
kAudioUnitManufacturer_Apple,
0,
0
};
AudioComponent comp = AudioComponentFindNext(0, &desc);
if (!comp)
return false;
OSStatus result = AudioComponentInstanceNew(comp, &audio_unit_);
if (result != noErr) {
OSSTATUS_DLOG(ERROR, result) << "AudioComponentInstanceNew() failed.";
return false;
}
if (!EnableIO(input_channels_ > 0, kAudioUnitScope_Input))
return false;
if (!EnableIO(output_channels_ > 0, kAudioUnitScope_Output))
return false;
result = AudioUnitSetProperty(
audio_unit_,
kAudioOutputUnitProperty_CurrentDevice,
kAudioUnitScope_Global,
0,
&device_,
sizeof(AudioDeviceID));
if (result != noErr)
return false;
if (input_channels_ > 0) {
if (!SetStreamFormat(&input_format_,
input_channels_,
kAudioUnitScope_Output,
1))
return false;
}
if (output_channels_ > 0) {
if (!SetStreamFormat(&output_format_,
output_channels_,
kAudioUnitScope_Input,
0))
return false;
}
UInt32 buffer_size = number_of_frames_;
result = AudioUnitSetProperty(
audio_unit_,
kAudioDevicePropertyBufferFrameSize,
kAudioUnitScope_Output,
0,
&buffer_size,
sizeof(buffer_size));
if (result != noErr) {
OSSTATUS_DLOG(ERROR, result)
<< "AudioUnitSetProperty(kAudioDevicePropertyBufferFrameSize) failed.";
return false;
}
AURenderCallbackStruct callback;
callback.inputProc = InputProc;
callback.inputProcRefCon = this;
result = AudioUnitSetProperty(
audio_unit_,
kAudioUnitProperty_SetRenderCallback,
kAudioUnitScope_Input,
0,
&callback,
sizeof(callback));
if (result != noErr)
return false;
result = AudioUnitInitialize(audio_unit_);
if (result != noErr) {
OSSTATUS_DLOG(ERROR, result) << "AudioUnitInitialize() failed.";
return false;
}
return true;
}
}