This source file includes following definitions.
- number_of_channels_in_frame_
- Open
- Start
- Stop
- Close
- GetMaxVolume
- SetVolume
- GetVolume
- InputProc
- Provide
- HardwareSampleRate
- GetHardwareLatency
- GetCaptureLatency
- GetNumberOfChannelsFromStream
- HandleError
- IsVolumeSettableOnChannel
#include "media/audio/mac/audio_low_latency_input_mac.h"
#include <CoreServices/CoreServices.h>
#include "base/basictypes.h"
#include "base/logging.h"
#include "base/mac/mac_logging.h"
#include "media/audio/mac/audio_manager_mac.h"
#include "media/base/data_buffer.h"
namespace media {
static std::ostream& operator<<(std::ostream& os,
const AudioStreamBasicDescription& format) {
os << "sample rate : " << format.mSampleRate << std::endl
<< "format ID : " << format.mFormatID << std::endl
<< "format flags : " << format.mFormatFlags << std::endl
<< "bytes per packet : " << format.mBytesPerPacket << std::endl
<< "frames per packet : " << format.mFramesPerPacket << std::endl
<< "bytes per frame : " << format.mBytesPerFrame << std::endl
<< "channels per frame: " << format.mChannelsPerFrame << std::endl
<< "bits per channel : " << format.mBitsPerChannel;
return os;
}
AUAudioInputStream::AUAudioInputStream(
AudioManagerMac* manager,
const AudioParameters& input_params,
const AudioParameters& output_params,
AudioDeviceID audio_device_id)
: manager_(manager),
sink_(NULL),
audio_unit_(0),
input_device_id_(audio_device_id),
started_(false),
hardware_latency_frames_(0),
fifo_delay_bytes_(0),
number_of_channels_in_frame_(0) {
DCHECK(manager_);
format_.mSampleRate = input_params.sample_rate();
format_.mFormatID = kAudioFormatLinearPCM;
format_.mFormatFlags = kLinearPCMFormatFlagIsPacked |
kLinearPCMFormatFlagIsSignedInteger;
format_.mBitsPerChannel = input_params.bits_per_sample();
format_.mChannelsPerFrame = input_params.channels();
format_.mFramesPerPacket = 1;
format_.mBytesPerPacket = (format_.mBitsPerChannel *
input_params.channels()) / 8;
format_.mBytesPerFrame = format_.mBytesPerPacket;
format_.mReserved = 0;
DVLOG(1) << "Desired ouput format: " << format_;
number_of_frames_ = output_params.frames_per_buffer();
DVLOG(1) << "Size of data buffer in frames : " << number_of_frames_;
UInt32 data_byte_size = number_of_frames_ * format_.mBytesPerFrame;
DVLOG(1) << "Size of data buffer in bytes : " << data_byte_size;
audio_data_buffer_.reset(new uint8[data_byte_size]);
audio_buffer_list_.mNumberBuffers = 1;
AudioBuffer* audio_buffer = audio_buffer_list_.mBuffers;
audio_buffer->mNumberChannels = input_params.channels();
audio_buffer->mDataByteSize = data_byte_size;
audio_buffer->mData = audio_data_buffer_.get();
size_t requested_size_frames =
input_params.GetBytesPerBuffer() / format_.mBytesPerPacket;
if (requested_size_frames < number_of_frames_) {
requested_size_frames = number_of_frames_;
}
requested_size_bytes_ = requested_size_frames * format_.mBytesPerFrame;
DVLOG(1) << "Requested buffer size in bytes : " << requested_size_bytes_;
DVLOG_IF(0, requested_size_frames > number_of_frames_) << "FIFO is used";
const int number_of_bytes = number_of_frames_ * format_.mBytesPerFrame;
fifo_delay_bytes_ = requested_size_bytes_ - number_of_bytes;
const int max_forward_capacity = number_of_bytes *
((requested_size_frames / number_of_frames_) + 1);
fifo_.reset(new media::SeekableBuffer(0, max_forward_capacity));
data_ = new media::DataBuffer(requested_size_bytes_);
}
AUAudioInputStream::~AUAudioInputStream() {}
bool AUAudioInputStream::Open() {
if (audio_unit_)
return false;
if (input_device_id_ == kAudioObjectUnknown) {
NOTREACHED() << "Device ID is unknown";
return false;
}
Component comp;
ComponentDescription desc;
desc.componentType = kAudioUnitType_Output;
desc.componentSubType = kAudioUnitSubType_HALOutput;
desc.componentManufacturer = kAudioUnitManufacturer_Apple;
desc.componentFlags = 0;
desc.componentFlagsMask = 0;
comp = FindNextComponent(0, &desc);
DCHECK(comp);
OSStatus result = OpenAComponent(comp, &audio_unit_);
if (result) {
HandleError(result);
return false;
}
UInt32 enableIO = 1;
result = AudioUnitSetProperty(audio_unit_,
kAudioOutputUnitProperty_EnableIO,
kAudioUnitScope_Input,
1,
&enableIO,
sizeof(enableIO));
if (result) {
HandleError(result);
return false;
}
enableIO = 0;
result = AudioUnitSetProperty(audio_unit_,
kAudioOutputUnitProperty_EnableIO,
kAudioUnitScope_Output,
0,
&enableIO,
sizeof(enableIO));
if (result) {
HandleError(result);
return false;
}
result = AudioUnitSetProperty(audio_unit_,
kAudioOutputUnitProperty_CurrentDevice,
kAudioUnitScope_Global,
0,
&input_device_id_,
sizeof(input_device_id_));
if (result) {
HandleError(result);
return false;
}
AURenderCallbackStruct callback;
callback.inputProc = InputProc;
callback.inputProcRefCon = this;
result = AudioUnitSetProperty(audio_unit_,
kAudioOutputUnitProperty_SetInputCallback,
kAudioUnitScope_Global,
0,
&callback,
sizeof(callback));
if (result) {
HandleError(result);
return false;
}
result = AudioUnitSetProperty(audio_unit_,
kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Output,
1,
&format_,
sizeof(format_));
if (result) {
HandleError(result);
return false;
}
result = AudioUnitSetProperty(audio_unit_,
kAudioDevicePropertyBufferFrameSize,
kAudioUnitScope_Output,
1,
&number_of_frames_,
sizeof(number_of_frames_));
if (result) {
HandleError(result);
return false;
}
result = AudioUnitInitialize(audio_unit_);
if (result) {
HandleError(result);
return false;
}
hardware_latency_frames_ = GetHardwareLatency();
number_of_channels_in_frame_ = GetNumberOfChannelsFromStream();
return true;
}
void AUAudioInputStream::Start(AudioInputCallback* callback) {
DCHECK(callback);
DLOG_IF(ERROR, !audio_unit_) << "Open() has not been called successfully";
if (started_ || !audio_unit_)
return;
sink_ = callback;
StartAgc();
OSStatus result = AudioOutputUnitStart(audio_unit_);
if (result == noErr) {
started_ = true;
}
OSSTATUS_DLOG_IF(ERROR, result != noErr, result)
<< "Failed to start acquiring data";
}
void AUAudioInputStream::Stop() {
if (!started_)
return;
StopAgc();
OSStatus result = AudioOutputUnitStop(audio_unit_);
DCHECK_EQ(result, noErr);
started_ = false;
sink_ = NULL;
OSSTATUS_DLOG_IF(ERROR, result != noErr, result)
<< "Failed to stop acquiring data";
}
void AUAudioInputStream::Close() {
if (started_) {
Stop();
}
if (audio_unit_) {
AudioUnitUninitialize(audio_unit_);
CloseComponent(audio_unit_);
audio_unit_ = 0;
}
manager_->ReleaseInputStream(this);
}
double AUAudioInputStream::GetMaxVolume() {
if (input_device_id_ == kAudioObjectUnknown) {
NOTREACHED() << "Device ID is unknown";
return 0.0;
}
for (int i = 0; i <= number_of_channels_in_frame_; ++i) {
if (IsVolumeSettableOnChannel(i))
return 1.0;
}
return 0.0;
}
void AUAudioInputStream::SetVolume(double volume) {
DVLOG(1) << "SetVolume(volume=" << volume << ")";
DCHECK_GE(volume, 0.0);
DCHECK_LE(volume, 1.0);
if (input_device_id_ == kAudioObjectUnknown) {
NOTREACHED() << "Device ID is unknown";
return;
}
Float32 volume_float32 = static_cast<Float32>(volume);
AudioObjectPropertyAddress property_address = {
kAudioDevicePropertyVolumeScalar,
kAudioDevicePropertyScopeInput,
kAudioObjectPropertyElementMaster
};
if (IsVolumeSettableOnChannel(kAudioObjectPropertyElementMaster)) {
OSStatus result = AudioObjectSetPropertyData(input_device_id_,
&property_address,
0,
NULL,
sizeof(volume_float32),
&volume_float32);
if (result != noErr) {
DLOG(WARNING) << "Failed to set volume to " << volume_float32;
}
return;
}
int successful_channels = 0;
for (int i = 1; i <= number_of_channels_in_frame_; ++i) {
property_address.mElement = static_cast<UInt32>(i);
if (IsVolumeSettableOnChannel(i)) {
OSStatus result = AudioObjectSetPropertyData(input_device_id_,
&property_address,
0,
NULL,
sizeof(volume_float32),
&volume_float32);
if (result == noErr)
++successful_channels;
}
}
DLOG_IF(WARNING, successful_channels == 0)
<< "Failed to set volume to " << volume_float32;
UpdateAgcVolume();
}
double AUAudioInputStream::GetVolume() {
if (input_device_id_ == kAudioObjectUnknown){
NOTREACHED() << "Device ID is unknown";
return 0.0;
}
AudioObjectPropertyAddress property_address = {
kAudioDevicePropertyVolumeScalar,
kAudioDevicePropertyScopeInput,
kAudioObjectPropertyElementMaster
};
if (AudioObjectHasProperty(input_device_id_, &property_address)) {
Float32 volume_float32 = 0.0;
UInt32 size = sizeof(volume_float32);
OSStatus result = AudioObjectGetPropertyData(input_device_id_,
&property_address,
0,
NULL,
&size,
&volume_float32);
if (result == noErr)
return static_cast<double>(volume_float32);
} else {
Float32 volume_float32 = 0.0;
int successful_channels = 0;
for (int i = 1; i <= number_of_channels_in_frame_; ++i) {
property_address.mElement = static_cast<UInt32>(i);
if (AudioObjectHasProperty(input_device_id_, &property_address)) {
Float32 channel_volume = 0;
UInt32 size = sizeof(channel_volume);
OSStatus result = AudioObjectGetPropertyData(input_device_id_,
&property_address,
0,
NULL,
&size,
&channel_volume);
if (result == noErr) {
volume_float32 += channel_volume;
++successful_channels;
}
}
}
if (successful_channels != 0)
return static_cast<double>(volume_float32 / successful_channels);
}
DLOG(WARNING) << "Failed to get volume";
return 0.0;
}
OSStatus AUAudioInputStream::InputProc(void* user_data,
AudioUnitRenderActionFlags* flags,
const AudioTimeStamp* time_stamp,
UInt32 bus_number,
UInt32 number_of_frames,
AudioBufferList* io_data) {
DCHECK_EQ(bus_number, static_cast<UInt32>(1));
AUAudioInputStream* audio_input =
reinterpret_cast<AUAudioInputStream*>(user_data);
DCHECK(audio_input);
if (!audio_input)
return kAudioUnitErr_InvalidElement;
OSStatus result = AudioUnitRender(audio_input->audio_unit(),
flags,
time_stamp,
bus_number,
number_of_frames,
audio_input->audio_buffer_list());
if (result)
return result;
return audio_input->Provide(number_of_frames,
audio_input->audio_buffer_list(),
time_stamp);
}
OSStatus AUAudioInputStream::Provide(UInt32 number_of_frames,
AudioBufferList* io_data,
const AudioTimeStamp* time_stamp) {
double capture_latency_frames = GetCaptureLatency(time_stamp);
double normalized_volume = 0.0;
GetAgcVolume(&normalized_volume);
AudioBuffer& buffer = io_data->mBuffers[0];
uint8* audio_data = reinterpret_cast<uint8*>(buffer.mData);
uint32 capture_delay_bytes = static_cast<uint32>
((capture_latency_frames + 0.5) * format_.mBytesPerFrame);
capture_delay_bytes += fifo_delay_bytes_;
DCHECK(audio_data);
if (!audio_data)
return kAudioUnitErr_InvalidElement;
fifo_->Append(audio_data, buffer.mDataByteSize);
if (fifo_->forward_bytes() >= requested_size_bytes_) {
fifo_->Read(data_->writable_data(), requested_size_bytes_);
sink_->OnData(this,
data_->data(),
requested_size_bytes_,
capture_delay_bytes,
normalized_volume);
}
return noErr;
}
int AUAudioInputStream::HardwareSampleRate() {
AudioDeviceID device_id = kAudioObjectUnknown;
UInt32 info_size = sizeof(device_id);
AudioObjectPropertyAddress default_input_device_address = {
kAudioHardwarePropertyDefaultInputDevice,
kAudioObjectPropertyScopeGlobal,
kAudioObjectPropertyElementMaster
};
OSStatus result = AudioObjectGetPropertyData(kAudioObjectSystemObject,
&default_input_device_address,
0,
0,
&info_size,
&device_id);
if (result != noErr)
return 0.0;
Float64 nominal_sample_rate;
info_size = sizeof(nominal_sample_rate);
AudioObjectPropertyAddress nominal_sample_rate_address = {
kAudioDevicePropertyNominalSampleRate,
kAudioObjectPropertyScopeGlobal,
kAudioObjectPropertyElementMaster
};
result = AudioObjectGetPropertyData(device_id,
&nominal_sample_rate_address,
0,
0,
&info_size,
&nominal_sample_rate);
if (result != noErr)
return 0.0;
return static_cast<int>(nominal_sample_rate);
}
double AUAudioInputStream::GetHardwareLatency() {
if (!audio_unit_ || input_device_id_ == kAudioObjectUnknown) {
DLOG(WARNING) << "Audio unit object 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);
OSSTATUS_DLOG_IF(WARNING, result != noErr, result)
<< "Could not get audio unit latency";
AudioObjectPropertyAddress property_address = {
kAudioDevicePropertyLatency,
kAudioDevicePropertyScopeInput,
kAudioObjectPropertyElementMaster
};
UInt32 device_latency_frames = 0;
size = sizeof(device_latency_frames);
result = AudioObjectGetPropertyData(input_device_id_,
&property_address,
0,
NULL,
&size,
&device_latency_frames);
DLOG_IF(WARNING, result != noErr) << "Could not get audio device latency.";
return static_cast<double>((audio_unit_latency_sec *
format_.mSampleRate) + device_latency_frames);
}
double AUAudioInputStream::GetCaptureLatency(
const AudioTimeStamp* input_time_stamp) {
UInt64 capture_time_ns = AudioConvertHostTimeToNanos(
input_time_stamp->mHostTime);
UInt64 now_ns = AudioConvertHostTimeToNanos(AudioGetCurrentHostTime());
double delay_frames = static_cast<double>
(1e-9 * (now_ns - capture_time_ns) * format_.mSampleRate);
return (delay_frames + hardware_latency_frames_);
}
int AUAudioInputStream::GetNumberOfChannelsFromStream() {
AudioObjectPropertyAddress property_address = {
kAudioDevicePropertyStreamFormat,
kAudioDevicePropertyScopeInput,
kAudioObjectPropertyElementMaster
};
AudioStreamBasicDescription stream_format;
UInt32 size = sizeof(stream_format);
OSStatus result = AudioObjectGetPropertyData(input_device_id_,
&property_address,
0,
NULL,
&size,
&stream_format);
if (result != noErr) {
DLOG(WARNING) << "Could not get stream format";
return 0;
}
return static_cast<int>(stream_format.mChannelsPerFrame);
}
void AUAudioInputStream::HandleError(OSStatus err) {
NOTREACHED() << "error " << GetMacOSStatusErrorString(err)
<< " (" << err << ")";
if (sink_)
sink_->OnError(this);
}
bool AUAudioInputStream::IsVolumeSettableOnChannel(int channel) {
Boolean is_settable = false;
AudioObjectPropertyAddress property_address = {
kAudioDevicePropertyVolumeScalar,
kAudioDevicePropertyScopeInput,
static_cast<UInt32>(channel)
};
OSStatus result = AudioObjectIsPropertySettable(input_device_id_,
&property_address,
&is_settable);
return (result == noErr) ? is_settable : false;
}
}