1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "media/audio/mac/audio_auhal_mac.h"
7 #include <CoreServices/CoreServices.h>
9 #include "base/basictypes.h"
10 #include "base/command_line.h"
11 #include "base/debug/trace_event.h"
12 #include "base/logging.h"
13 #include "base/mac/mac_logging.h"
14 #include "media/audio/audio_util.h"
15 #include "media/audio/mac/audio_manager_mac.h"
16 #include "media/base/media_switches.h"
20 static std::ostream
& operator<<(std::ostream
& os
,
21 const AudioStreamBasicDescription
& format
) {
22 os
<< "sample rate : " << format
.mSampleRate
<< std::endl
23 << "format ID : " << format
.mFormatID
<< std::endl
24 << "format flags : " << format
.mFormatFlags
<< std::endl
25 << "bytes per packet : " << format
.mBytesPerPacket
<< std::endl
26 << "frames per packet : " << format
.mFramesPerPacket
<< std::endl
27 << "bytes per frame : " << format
.mBytesPerFrame
<< std::endl
28 << "channels per frame: " << format
.mChannelsPerFrame
<< std::endl
29 << "bits per channel : " << format
.mBitsPerChannel
;
33 static void ZeroBufferList(AudioBufferList
* buffer_list
) {
34 for (size_t i
= 0; i
< buffer_list
->mNumberBuffers
; ++i
) {
35 memset(buffer_list
->mBuffers
[i
].mData
,
37 buffer_list
->mBuffers
[i
].mDataByteSize
);
41 static void WrapBufferList(AudioBufferList
* buffer_list
,
46 const int channels
= bus
->channels();
47 const int buffer_list_channels
= buffer_list
->mNumberBuffers
;
48 DCHECK_EQ(channels
, buffer_list_channels
);
50 // Copy pointers from AudioBufferList.
51 for (int i
= 0; i
< channels
; ++i
) {
53 i
, static_cast<float*>(buffer_list
->mBuffers
[i
].mData
));
56 // Finally set the actual length.
57 bus
->set_frames(frames
);
60 AUHALStream::AUHALStream(
61 AudioManagerMac
* manager
,
62 const AudioParameters
& params
,
66 input_channels_(params_
.input_channels()),
67 output_channels_(params_
.channels()),
68 number_of_frames_(params_
.frames_per_buffer()),
73 hardware_latency_frames_(0),
75 input_buffer_list_(NULL
) {
76 // We must have a manager.
79 DVLOG(1) << "Input channels: " << input_channels_
;
80 DVLOG(1) << "Output channels: " << output_channels_
;
81 DVLOG(1) << "Sample rate: " << params_
.sample_rate();
82 DVLOG(1) << "Buffer size: " << number_of_frames_
;
85 AUHALStream::~AUHALStream() {
88 bool AUHALStream::Open() {
89 // Get the total number of input and output channels that the
91 int device_input_channels
;
92 bool got_input_channels
= AudioManagerMac::GetDeviceChannels(
94 kAudioDevicePropertyScopeInput
,
95 &device_input_channels
);
97 int device_output_channels
;
98 bool got_output_channels
= AudioManagerMac::GetDeviceChannels(
100 kAudioDevicePropertyScopeOutput
,
101 &device_output_channels
);
103 // Sanity check the requested I/O channels.
104 if (!got_input_channels
||
105 input_channels_
< 0 || input_channels_
> device_input_channels
) {
106 LOG(ERROR
) << "AudioDevice does not support requested input channels.";
110 if (!got_output_channels
||
111 output_channels_
<= 0 || output_channels_
> device_output_channels
) {
112 LOG(ERROR
) << "AudioDevice does not support requested output channels.";
116 // The requested sample-rate must match the hardware sample-rate.
117 int sample_rate
= AudioManagerMac::HardwareSampleRateForDevice(device_
);
119 if (sample_rate
!= params_
.sample_rate()) {
120 LOG(ERROR
) << "Requested sample-rate: " << params_
.sample_rate()
121 << " must match the hardware sample-rate: " << sample_rate
;
127 bool configured
= ConfigureAUHAL();
129 hardware_latency_frames_
= GetHardwareLatency();
134 void AUHALStream::Close() {
135 if (input_buffer_list_
) {
136 input_buffer_list_storage_
.reset();
137 input_buffer_list_
= NULL
;
138 input_bus_
.reset(NULL
);
139 output_bus_
.reset(NULL
);
143 AudioUnitUninitialize(audio_unit_
);
144 AudioComponentInstanceDispose(audio_unit_
);
147 // Inform the audio manager that we have been closed. This will cause our
149 manager_
->ReleaseOutputStream(this);
152 void AUHALStream::Start(AudioSourceCallback
* callback
) {
155 DLOG(ERROR
) << "Open() has not been called successfully";
161 base::AutoLock
auto_lock(source_lock_
);
165 AudioOutputUnitStart(audio_unit_
);
168 void AUHALStream::Stop() {
172 AudioOutputUnitStop(audio_unit_
);
174 base::AutoLock
auto_lock(source_lock_
);
179 void AUHALStream::SetVolume(double volume
) {
180 volume_
= static_cast<float>(volume
);
183 void AUHALStream::GetVolume(double* volume
) {
187 // Pulls on our provider to get rendered audio stream.
188 // Note to future hackers of this function: Do not add locks which can
189 // be contended in the middle of stream processing here (starting and stopping
190 // the stream are ok) because this is running on a real-time thread.
191 OSStatus
AUHALStream::Render(
192 AudioUnitRenderActionFlags
* flags
,
193 const AudioTimeStamp
* output_time_stamp
,
195 UInt32 number_of_frames
,
196 AudioBufferList
* io_data
) {
197 TRACE_EVENT0("audio", "AUHALStream::Render");
199 if (number_of_frames
!= number_of_frames_
) {
200 // This can happen if we've suddenly changed sample-rates.
201 // The stream should be stopping very soon.
203 // Unfortunately AUAudioInputStream and AUHALStream share the frame
204 // size set by kAudioDevicePropertyBufferFrameSize above on a per process
205 // basis. What this means is that the |number_of_frames| value may be
206 // larger or smaller than the value set during ConfigureAUHAL().
207 // In this case either audio input or audio output will be broken,
208 // so just output silence.
209 ZeroBufferList(io_data
);
213 if (input_channels_
> 0 && input_buffer_list_
) {
214 // Get the input data. |input_buffer_list_| is wrapped
215 // to point to the data allocated in |input_bus_|.
216 OSStatus result
= AudioUnitRender(
224 ZeroBufferList(input_buffer_list_
);
227 // Make |output_bus_| wrap the output AudioBufferList.
228 WrapBufferList(io_data
, output_bus_
.get(), number_of_frames
);
230 // Update the playout latency.
231 double playout_latency_frames
= GetPlayoutLatency(output_time_stamp
);
233 uint32 hardware_pending_bytes
= static_cast<uint32
>
234 ((playout_latency_frames
+ 0.5) * output_format_
.mBytesPerFrame
);
237 // Render() shouldn't be called except between AudioOutputUnitStart() and
238 // AudioOutputUnitStop() calls, but crash reports have shown otherwise:
239 // http://crbug.com/178765. We use |source_lock_| to prevent races and
240 // crashes in Render() when |source_| is cleared.
241 base::AutoLock
auto_lock(source_lock_
);
243 ZeroBufferList(io_data
);
247 // Supply the input data and render the output data.
248 source_
->OnMoreIOData(
251 AudioBuffersState(0, hardware_pending_bytes
));
252 output_bus_
->Scale(volume_
);
259 OSStatus
AUHALStream::InputProc(
261 AudioUnitRenderActionFlags
* flags
,
262 const AudioTimeStamp
* output_time_stamp
,
264 UInt32 number_of_frames
,
265 AudioBufferList
* io_data
) {
266 // Dispatch to our class method.
267 AUHALStream
* audio_output
=
268 static_cast<AUHALStream
*>(user_data
);
272 return audio_output
->Render(
280 double AUHALStream::GetHardwareLatency() {
281 if (!audio_unit_
|| device_
== kAudioObjectUnknown
) {
282 DLOG(WARNING
) << "AudioUnit is NULL or device ID is unknown";
286 // Get audio unit latency.
287 Float64 audio_unit_latency_sec
= 0.0;
288 UInt32 size
= sizeof(audio_unit_latency_sec
);
289 OSStatus result
= AudioUnitGetProperty(
291 kAudioUnitProperty_Latency
,
292 kAudioUnitScope_Global
,
294 &audio_unit_latency_sec
,
296 if (result
!= noErr
) {
297 OSSTATUS_DLOG(WARNING
, result
) << "Could not get AudioUnit latency";
301 // Get output audio device latency.
302 static const AudioObjectPropertyAddress property_address
= {
303 kAudioDevicePropertyLatency
,
304 kAudioDevicePropertyScopeOutput
,
305 kAudioObjectPropertyElementMaster
308 UInt32 device_latency_frames
= 0;
309 size
= sizeof(device_latency_frames
);
310 result
= AudioObjectGetPropertyData(
316 &device_latency_frames
);
317 if (result
!= noErr
) {
318 OSSTATUS_DLOG(WARNING
, result
) << "Could not get audio device latency";
322 return static_cast<double>((audio_unit_latency_sec
*
323 output_format_
.mSampleRate
) + device_latency_frames
);
326 double AUHALStream::GetPlayoutLatency(
327 const AudioTimeStamp
* output_time_stamp
) {
328 // Ensure mHostTime is valid.
329 if ((output_time_stamp
->mFlags
& kAudioTimeStampHostTimeValid
) == 0)
332 // Get the delay between the moment getting the callback and the scheduled
333 // time stamp that tells when the data is going to be played out.
334 UInt64 output_time_ns
= AudioConvertHostTimeToNanos(
335 output_time_stamp
->mHostTime
);
336 UInt64 now_ns
= AudioConvertHostTimeToNanos(AudioGetCurrentHostTime());
338 // Prevent overflow leading to huge delay information; occurs regularly on
339 // the bots, probably less so in the wild.
340 if (now_ns
> output_time_ns
)
343 double delay_frames
= static_cast<double>
344 (1e-9 * (output_time_ns
- now_ns
) * output_format_
.mSampleRate
);
346 return (delay_frames
+ hardware_latency_frames_
);
349 void AUHALStream::CreateIOBusses() {
350 if (input_channels_
> 0) {
351 // Allocate storage for the AudioBufferList used for the
352 // input data from the input AudioUnit.
353 // We allocate enough space for with one AudioBuffer per channel.
354 size_t buffer_list_size
= offsetof(AudioBufferList
, mBuffers
[0]) +
355 (sizeof(AudioBuffer
) * input_channels_
);
356 input_buffer_list_storage_
.reset(new uint8
[buffer_list_size
]);
359 reinterpret_cast<AudioBufferList
*>(input_buffer_list_storage_
.get());
360 input_buffer_list_
->mNumberBuffers
= input_channels_
;
362 // |input_bus_| allocates the storage for the PCM input data.
363 input_bus_
= AudioBus::Create(input_channels_
, number_of_frames_
);
365 // Make the AudioBufferList point to the memory in |input_bus_|.
366 UInt32 buffer_size_bytes
= input_bus_
->frames() * sizeof(Float32
);
367 for (size_t i
= 0; i
< input_buffer_list_
->mNumberBuffers
; ++i
) {
368 input_buffer_list_
->mBuffers
[i
].mNumberChannels
= 1;
369 input_buffer_list_
->mBuffers
[i
].mDataByteSize
= buffer_size_bytes
;
370 input_buffer_list_
->mBuffers
[i
].mData
= input_bus_
->channel(i
);
374 // The output bus will wrap the AudioBufferList given to us in
375 // the Render() callback.
376 DCHECK_GT(output_channels_
, 0);
377 output_bus_
= AudioBus::CreateWrapper(output_channels_
);
380 bool AUHALStream::EnableIO(bool enable
, UInt32 scope
) {
381 // See Apple technote for details about the EnableIO property.
382 // Note that we use bus 1 for input and bus 0 for output:
383 // http://developer.apple.com/library/mac/#technotes/tn2091/_index.html
384 UInt32 enable_IO
= enable
? 1 : 0;
385 OSStatus result
= AudioUnitSetProperty(
387 kAudioOutputUnitProperty_EnableIO
,
389 (scope
== kAudioUnitScope_Input
) ? 1 : 0,
392 return (result
== noErr
);
395 bool AUHALStream::SetStreamFormat(
396 AudioStreamBasicDescription
* desc
,
401 AudioStreamBasicDescription
& format
= *desc
;
403 format
.mSampleRate
= params_
.sample_rate();
404 format
.mFormatID
= kAudioFormatLinearPCM
;
405 format
.mFormatFlags
= kAudioFormatFlagsNativeFloatPacked
|
406 kLinearPCMFormatFlagIsNonInterleaved
;
407 format
.mBytesPerPacket
= sizeof(Float32
);
408 format
.mFramesPerPacket
= 1;
409 format
.mBytesPerFrame
= sizeof(Float32
);
410 format
.mChannelsPerFrame
= channels
;
411 format
.mBitsPerChannel
= 32;
412 format
.mReserved
= 0;
414 OSStatus result
= AudioUnitSetProperty(
416 kAudioUnitProperty_StreamFormat
,
421 return (result
== noErr
);
424 bool AUHALStream::ConfigureAUHAL() {
425 if (device_
== kAudioObjectUnknown
||
426 (input_channels_
== 0 && output_channels_
== 0))
429 AudioComponentDescription desc
= {
430 kAudioUnitType_Output
,
431 kAudioUnitSubType_HALOutput
,
432 kAudioUnitManufacturer_Apple
,
436 AudioComponent comp
= AudioComponentFindNext(0, &desc
);
440 OSStatus result
= AudioComponentInstanceNew(comp
, &audio_unit_
);
441 if (result
!= noErr
) {
442 OSSTATUS_DLOG(WARNING
, result
) << "AudioComponentInstanceNew() failed.";
446 result
= AudioUnitInitialize(audio_unit_
);
447 if (result
!= noErr
) {
448 OSSTATUS_DLOG(WARNING
, result
) << "AudioUnitInitialize() failed.";
452 // Enable input and output as appropriate.
453 if (!EnableIO(input_channels_
> 0, kAudioUnitScope_Input
))
455 if (!EnableIO(output_channels_
> 0, kAudioUnitScope_Output
))
458 // Set the device to be used with the AUHAL AudioUnit.
459 result
= AudioUnitSetProperty(
461 kAudioOutputUnitProperty_CurrentDevice
,
462 kAudioUnitScope_Global
,
465 sizeof(AudioDeviceID
));
469 // Set stream formats.
470 // See Apple's tech note for details on the peculiar way that
471 // inputs and outputs are handled in the AUHAL concerning scope and bus
472 // (element) numbers:
473 // http://developer.apple.com/library/mac/#technotes/tn2091/_index.html
475 if (input_channels_
> 0) {
476 if (!SetStreamFormat(&input_format_
,
478 kAudioUnitScope_Output
,
483 if (output_channels_
> 0) {
484 if (!SetStreamFormat(&output_format_
,
486 kAudioUnitScope_Input
,
491 // Set the buffer frame size.
492 // WARNING: Setting this value changes the frame size for all audio units in
493 // the current process. It's imperative that the input and output frame sizes
494 // be the same as the frames_per_buffer() returned by
495 // GetDefaultOutputStreamParameters().
496 // See http://crbug.com/154352 for details.
497 UInt32 buffer_size
= number_of_frames_
;
498 result
= AudioUnitSetProperty(
500 kAudioDevicePropertyBufferFrameSize
,
501 kAudioUnitScope_Output
,
504 sizeof(buffer_size
));
505 if (result
!= noErr
) {
506 OSSTATUS_DLOG(WARNING
, result
)
507 << "AudioUnitSetProperty(kAudioDevicePropertyBufferFrameSize) failed.";
512 AURenderCallbackStruct callback
;
513 callback
.inputProc
= InputProc
;
514 callback
.inputProcRefCon
= this;
515 result
= AudioUnitSetProperty(
517 kAudioUnitProperty_SetRenderCallback
,
518 kAudioUnitScope_Input
,