diff options
author | xians <xians@chromium.org> | 2014-08-27 07:44:43 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2014-08-27 14:46:06 +0000 |
commit | c72c70da1229625cb54ecf683a909f173043d5b1 (patch) | |
tree | 11448d2661d19389333133f086462e9de914637f /media/audio/mac | |
parent | 9da9612482b2028dff0f1caca783878e8aa82abc (diff) | |
download | chromium_src-c72c70da1229625cb54ecf683a909f173043d5b1.zip chromium_src-c72c70da1229625cb54ecf683a909f173043d5b1.tar.gz chromium_src-c72c70da1229625cb54ecf683a909f173043d5b1.tar.bz2 |
Used native deinterleaved and float point format for the input streams.
If we call GetProperty of kAudioUnitProperty_StreamFormat before setting the format, the device will report kAudioFormatFlagsNativeFloatPacked | kLinearPCMFormatFlagIsNonInterleaved as the native format of the device, which is the same as the output.
This patch changes the format to use kAudioFormatFlagsNativeFloatPacked | kLinearPCMFormatFlagIsNonInterleaved to open the device, so that we will avoid format flipping back and forth. Hope this optimization will help increase the stability of the input audio on Mac.
BUG=404884
TEST=media_unittests && https://webrtc.googlecode.com/svn-history/r5497/trunk/samples/js/demos/html/pc1.html, https://www.google.com/intl/en/chrome/demos/speech.html
Review URL: https://codereview.chromium.org/501823002
Cr-Commit-Position: refs/heads/master@{#292151}
Diffstat (limited to 'media/audio/mac')
-rw-r--r-- | media/audio/mac/audio_low_latency_input_mac.cc | 200 | ||||
-rw-r--r-- | media/audio/mac/audio_low_latency_input_mac.h | 21 |
2 files changed, 115 insertions, 106 deletions
diff --git a/media/audio/mac/audio_low_latency_input_mac.cc b/media/audio/mac/audio_low_latency_input_mac.cc index f1dbdf7..fd835d7 100644 --- a/media/audio/mac/audio_low_latency_input_mac.cc +++ b/media/audio/mac/audio_low_latency_input_mac.cc @@ -10,6 +10,7 @@ #include "base/logging.h" #include "base/mac/mac_logging.h" #include "media/audio/mac/audio_manager_mac.h" +#include "media/base/audio_block_fifo.h" #include "media/base/audio_bus.h" #include "media/base/data_buffer.h" @@ -46,43 +47,45 @@ AUAudioInputStream::AUAudioInputStream(AudioManagerMac* manager, started_(false), hardware_latency_frames_(0), number_of_channels_in_frame_(0), - fifo_(input_params.channels(), - number_of_frames_, - kNumberOfBlocksBufferInFifo) { + output_bus_(AudioBus::Create(input_params)) { DCHECK(manager_); // Set up the desired (output) format specified by the client. format_.mSampleRate = input_params.sample_rate(); format_.mFormatID = kAudioFormatLinearPCM; - format_.mFormatFlags = kLinearPCMFormatFlagIsPacked | - kLinearPCMFormatFlagIsSignedInteger; - format_.mBitsPerChannel = input_params.bits_per_sample(); + format_.mFormatFlags = + kAudioFormatFlagsNativeFloatPacked | kLinearPCMFormatFlagIsNonInterleaved; + size_t bytes_per_sample = sizeof(Float32); + format_.mBitsPerChannel = bytes_per_sample * 8; format_.mChannelsPerFrame = input_params.channels(); - format_.mFramesPerPacket = 1; // uncompressed audio - format_.mBytesPerPacket = (format_.mBitsPerChannel * - input_params.channels()) / 8; - format_.mBytesPerFrame = format_.mBytesPerPacket; + format_.mFramesPerPacket = 1; + format_.mBytesPerFrame = bytes_per_sample; + format_.mBytesPerPacket = format_.mBytesPerFrame * format_.mFramesPerPacket; format_.mReserved = 0; DVLOG(1) << "Desired ouput format: " << format_; - // Derive size (in bytes) of the buffers that we will render to. - UInt32 data_byte_size = number_of_frames_ * format_.mBytesPerFrame; - DVLOG(1) << "Size of data buffer in bytes : " << data_byte_size; + // Allocate AudioBufferList based on the number of channels. + audio_buffer_list_.reset(static_cast<AudioBufferList*>( + malloc(sizeof(UInt32) + input_params.channels() * sizeof(AudioBuffer)))); + audio_buffer_list_->mNumberBuffers = input_params.channels(); // Allocate AudioBuffers to be used as storage for the received audio. // The AudioBufferList structure works as a placeholder for the // AudioBuffer structure, which holds a pointer to the actual data buffer. - 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(); + UInt32 data_byte_size = number_of_frames_ * format_.mBytesPerFrame; + CHECK_LE(static_cast<int>(data_byte_size * input_params.channels()), + media::AudioBus::CalculateMemorySize(input_params)); + AudioBuffer* audio_buffer = audio_buffer_list_->mBuffers; + for (UInt32 i = 0; i < audio_buffer_list_->mNumberBuffers; ++i) { + audio_buffer[i].mNumberChannels = 1; + audio_buffer[i].mDataByteSize = data_byte_size; + audio_buffer[i].mData = output_bus_->channel(i); + } } -AUAudioInputStream::~AUAudioInputStream() {} +AUAudioInputStream::~AUAudioInputStream() { +} // Obtain and open the AUHAL AudioOutputUnit for recording. bool AUAudioInputStream::Open() { @@ -165,23 +168,6 @@ bool AUAudioInputStream::Open() { return false; } - // Register the input procedure for the AUHAL. - // This procedure will be called when the AUHAL has received new data - // from the input device. - 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; - } - // Set up the the desired (output) format. // For obtaining input from a device, the device format is always expressed // on the output scope of the AUHAL's Element 1. @@ -229,6 +215,23 @@ bool AUAudioInputStream::Open() { } } + // Register the input procedure for the AUHAL. + // This procedure will be called when the AUHAL has received new data + // from the input device. + 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; + } + // Finally, initialize the audio unit and ensure that it is ready to render. // Allocates memory according to the maximum number of audio frames // it can produce in response to a single render call. @@ -342,9 +345,9 @@ void AUAudioInputStream::SetVolume(double volume) { Float32 volume_float32 = static_cast<Float32>(volume); AudioObjectPropertyAddress property_address = { - kAudioDevicePropertyVolumeScalar, - kAudioDevicePropertyScopeInput, - kAudioObjectPropertyElementMaster + kAudioDevicePropertyVolumeScalar, + kAudioDevicePropertyScopeInput, + kAudioObjectPropertyElementMaster }; // Try to set the volume for master volume channel. @@ -390,15 +393,15 @@ void AUAudioInputStream::SetVolume(double volume) { double AUAudioInputStream::GetVolume() { // Verify that we have a valid device. - if (input_device_id_ == kAudioObjectUnknown){ + if (input_device_id_ == kAudioObjectUnknown) { NOTREACHED() << "Device ID is unknown"; return 0.0; } AudioObjectPropertyAddress property_address = { - kAudioDevicePropertyVolumeScalar, - kAudioDevicePropertyScopeInput, - kAudioObjectPropertyElementMaster + kAudioDevicePropertyVolumeScalar, + kAudioDevicePropertyScopeInput, + kAudioObjectPropertyElementMaster }; if (AudioObjectHasProperty(input_device_id_, &property_address)) { @@ -406,12 +409,8 @@ double AUAudioInputStream::GetVolume() { // master channel. Float32 volume_float32 = 0.0; UInt32 size = sizeof(volume_float32); - OSStatus result = AudioObjectGetPropertyData(input_device_id_, - &property_address, - 0, - NULL, - &size, - &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 { @@ -472,9 +471,8 @@ OSStatus AUAudioInputStream::InputProc(void* user_data, return result; // Deliver recorded data to the consumer as a callback. - return audio_input->Provide(number_of_frames, - audio_input->audio_buffer_list(), - time_stamp); + return audio_input->Provide( + number_of_frames, audio_input->audio_buffer_list(), time_stamp); } OSStatus AUAudioInputStream::Provide(UInt32 number_of_frames, @@ -491,22 +489,39 @@ OSStatus AUAudioInputStream::Provide(UInt32 number_of_frames, 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); + uint32 capture_delay_bytes = static_cast<uint32>( + (capture_latency_frames + 0.5) * format_.mBytesPerFrame); DCHECK(audio_data); if (!audio_data) return kAudioUnitErr_InvalidElement; - // Copy captured (and interleaved) data into FIFO. - fifo_.Push(audio_data, number_of_frames, format_.mBitsPerChannel / 8); + // If the stream parameters change for any reason, we need to insert a FIFO + // since the OnMoreData() pipeline can't handle frame size changes. + if (number_of_frames != number_of_frames_) { + // Create a FIFO on the fly to handle any discrepancies in callback rates. + if (!fifo_) { + fifo_.reset(new AudioBlockFifo(output_bus_->channels(), + number_of_frames_, + kNumberOfBlocksBufferInFifo)); + } + } + // When FIFO does not kick in, data will be directly passed to the callback. + if (!fifo_) { + CHECK_EQ(output_bus_->frames(), static_cast<int>(number_of_frames_)); + sink_->OnData( + this, output_bus_.get(), capture_delay_bytes, normalized_volume); + return noErr; + } + + // Compensate the audio delay caused by the FIFO. + capture_delay_bytes += fifo_->GetAvailableFrames() * format_.mBytesPerFrame; + + fifo_->Push(output_bus_.get()); // Consume and deliver the data when the FIFO has a block of available data. - while (fifo_.available_blocks()) { - const AudioBus* audio_bus = fifo_.Consume(); + while (fifo_->available_blocks()) { + const AudioBus* audio_bus = fifo_->Consume(); DCHECK_EQ(audio_bus->frames(), static_cast<int>(number_of_frames_)); - - // Compensate the audio delay caused by the FIFO. - capture_delay_bytes += fifo_.GetAvailableFrames() * format_.mBytesPerFrame; sink_->OnData(this, audio_bus, capture_delay_bytes, normalized_volume); } @@ -519,9 +534,9 @@ int AUAudioInputStream::HardwareSampleRate() { UInt32 info_size = sizeof(device_id); AudioObjectPropertyAddress default_input_device_address = { - kAudioHardwarePropertyDefaultInputDevice, - kAudioObjectPropertyScopeGlobal, - kAudioObjectPropertyElementMaster + kAudioHardwarePropertyDefaultInputDevice, + kAudioObjectPropertyScopeGlobal, + kAudioObjectPropertyElementMaster }; OSStatus result = AudioObjectGetPropertyData(kAudioObjectSystemObject, &default_input_device_address, @@ -536,10 +551,8 @@ int AUAudioInputStream::HardwareSampleRate() { info_size = sizeof(nominal_sample_rate); AudioObjectPropertyAddress nominal_sample_rate_address = { - kAudioDevicePropertyNominalSampleRate, - kAudioObjectPropertyScopeGlobal, - kAudioObjectPropertyElementMaster - }; + kAudioDevicePropertyNominalSampleRate, kAudioObjectPropertyScopeGlobal, + kAudioObjectPropertyElementMaster}; result = AudioObjectGetPropertyData(device_id, &nominal_sample_rate_address, 0, @@ -572,9 +585,9 @@ double AUAudioInputStream::GetHardwareLatency() { // Get input audio device latency. AudioObjectPropertyAddress property_address = { - kAudioDevicePropertyLatency, - kAudioDevicePropertyScopeInput, - kAudioObjectPropertyElementMaster + kAudioDevicePropertyLatency, + kAudioDevicePropertyScopeInput, + kAudioObjectPropertyElementMaster }; UInt32 device_latency_frames = 0; size = sizeof(device_latency_frames); @@ -586,19 +599,19 @@ double AUAudioInputStream::GetHardwareLatency() { &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); + return static_cast<double>((audio_unit_latency_sec * format_.mSampleRate) + + device_latency_frames); } double AUAudioInputStream::GetCaptureLatency( const AudioTimeStamp* input_time_stamp) { // Get the delay between between the actual recording instant and the time // when the data packet is provided as a callback. - UInt64 capture_time_ns = AudioConvertHostTimeToNanos( - input_time_stamp->mHostTime); + 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); + double delay_frames = static_cast<double>(1e-9 * (now_ns - capture_time_ns) * + format_.mSampleRate); // Total latency is composed by the dynamic latency and the fixed // hardware latency. @@ -608,18 +621,14 @@ double AUAudioInputStream::GetCaptureLatency( int AUAudioInputStream::GetNumberOfChannelsFromStream() { // Get the stream format, to be able to read the number of channels. AudioObjectPropertyAddress property_address = { - kAudioDevicePropertyStreamFormat, - kAudioDevicePropertyScopeInput, - kAudioObjectPropertyElementMaster + kAudioDevicePropertyStreamFormat, + kAudioDevicePropertyScopeInput, + kAudioObjectPropertyElementMaster }; AudioStreamBasicDescription stream_format; UInt32 size = sizeof(stream_format); - OSStatus result = AudioObjectGetPropertyData(input_device_id_, - &property_address, - 0, - NULL, - &size, - &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; @@ -629,8 +638,8 @@ int AUAudioInputStream::GetNumberOfChannelsFromStream() { } void AUAudioInputStream::HandleError(OSStatus err) { - NOTREACHED() << "error " << GetMacOSStatusErrorString(err) - << " (" << err << ")"; + NOTREACHED() << "error " << GetMacOSStatusErrorString(err) << " (" << err + << ")"; if (sink_) sink_->OnError(this); } @@ -638,13 +647,12 @@ void AUAudioInputStream::HandleError(OSStatus err) { bool AUAudioInputStream::IsVolumeSettableOnChannel(int channel) { Boolean is_settable = false; AudioObjectPropertyAddress property_address = { - kAudioDevicePropertyVolumeScalar, - kAudioDevicePropertyScopeInput, - static_cast<UInt32>(channel) + kAudioDevicePropertyVolumeScalar, + kAudioDevicePropertyScopeInput, + static_cast<UInt32>(channel) }; - OSStatus result = AudioObjectIsPropertySettable(input_device_id_, - &property_address, - &is_settable); + OSStatus result = AudioObjectIsPropertySettable( + input_device_id_, &property_address, &is_settable); return (result == noErr) ? is_settable : false; } diff --git a/media/audio/mac/audio_low_latency_input_mac.h b/media/audio/mac/audio_low_latency_input_mac.h index c8e43fa..db444aa 100644 --- a/media/audio/mac/audio_low_latency_input_mac.h +++ b/media/audio/mac/audio_low_latency_input_mac.h @@ -45,10 +45,10 @@ #include "media/audio/agc_audio_stream.h" #include "media/audio/audio_io.h" #include "media/audio/audio_parameters.h" -#include "media/base/audio_block_fifo.h" namespace media { +class AudioBlockFifo; class AudioBus; class AudioManagerMac; class DataBuffer; @@ -78,7 +78,7 @@ class AUAudioInputStream : public AgcAudioStream<AudioInputStream> { bool started() const { return started_; } AudioUnit audio_unit() { return audio_unit_; } - AudioBufferList* audio_buffer_list() { return &audio_buffer_list_; } + AudioBufferList* audio_buffer_list() { return audio_buffer_list_.get(); } private: // AudioOutputUnit callback. @@ -90,7 +90,8 @@ class AUAudioInputStream : public AgcAudioStream<AudioInputStream> { AudioBufferList* io_data); // Pushes recorded data to consumer of the input audio stream. - OSStatus Provide(UInt32 number_of_frames, AudioBufferList* io_data, + OSStatus Provide(UInt32 number_of_frames, + AudioBufferList* io_data, const AudioTimeStamp* time_stamp); // Gets the fixed capture hardware latency and store it during initialization. @@ -132,11 +133,7 @@ class AUAudioInputStream : public AgcAudioStream<AudioInputStream> { AudioDeviceID input_device_id_; // Provides a mechanism for encapsulating one or more buffers of audio data. - AudioBufferList audio_buffer_list_; - - // Temporary storage for recorded data. The InputProc() renders into this - // array as soon as a frame of the desired buffer size has been recorded. - scoped_ptr<uint8[]> audio_data_buffer_; + scoped_ptr<AudioBufferList, base::FreeDeleter> audio_buffer_list_; // True after successfull Start(), false after successful Stop(). bool started_; @@ -148,8 +145,12 @@ class AUAudioInputStream : public AgcAudioStream<AudioInputStream> { // when querying the volume of each channel. int number_of_channels_in_frame_; - // FIFO used to accumulates recorded data. - media::AudioBlockFifo fifo_; + // Dynamically allocated FIFO used when CoreAudio asks for unexpected frame + // sizes. + scoped_ptr<AudioBlockFifo> fifo_; + + // AudioBus for delievering data via AudioSourceCallback::OnData(). + scoped_ptr<AudioBus> output_bus_; // Used to defer Start() to workaround http://crbug.com/160920. base::CancelableClosure deferred_start_cb_; |