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/bind.h"
11 #include "base/bind_helpers.h"
12 #include "base/logging.h"
13 #include "base/mac/mac_logging.h"
14 #include "base/time/time.h"
15 #include "base/trace_event/trace_event.h"
16 #include "media/audio/mac/audio_manager_mac.h"
17 #include "media/base/audio_pull_fifo.h"
21 static void WrapBufferList(AudioBufferList
* buffer_list
,
26 const int channels
= bus
->channels();
27 const int buffer_list_channels
= buffer_list
->mNumberBuffers
;
28 CHECK_EQ(channels
, buffer_list_channels
);
30 // Copy pointers from AudioBufferList.
31 for (int i
= 0; i
< channels
; ++i
) {
33 i
, static_cast<float*>(buffer_list
->mBuffers
[i
].mData
));
36 // Finally set the actual length.
37 bus
->set_frames(frames
);
40 AUHALStream::AUHALStream(
41 AudioManagerMac
* manager
,
42 const AudioParameters
& params
,
46 output_channels_(params_
.channels()),
47 number_of_frames_(params_
.frames_per_buffer()),
52 hardware_latency_frames_(0),
54 current_hardware_pending_bytes_(0) {
55 // We must have a manager.
58 DVLOG(1) << "AUHALStream::AUHALStream()";
59 DVLOG(1) << "Device: " << device
;
60 DVLOG(1) << "Output channels: " << output_channels_
;
61 DVLOG(1) << "Sample rate: " << params_
.sample_rate();
62 DVLOG(1) << "Buffer size: " << number_of_frames_
;
65 AUHALStream::~AUHALStream() {
68 bool AUHALStream::Open() {
69 // Get the total number of output channels that the
71 int device_output_channels
;
72 bool got_output_channels
= AudioManagerMac::GetDeviceChannels(
74 kAudioDevicePropertyScopeOutput
,
75 &device_output_channels
);
77 // Sanity check the requested output channels.
78 if (!got_output_channels
||
79 output_channels_
<= 0 || output_channels_
> device_output_channels
) {
80 LOG(ERROR
) << "AudioDevice does not support requested output channels.";
84 // The requested sample-rate must match the hardware sample-rate.
85 int sample_rate
= AudioManagerMac::HardwareSampleRateForDevice(device_
);
87 if (sample_rate
!= params_
.sample_rate()) {
88 LOG(ERROR
) << "Requested sample-rate: " << params_
.sample_rate()
89 << " must match the hardware sample-rate: " << sample_rate
;
93 // The output bus will wrap the AudioBufferList given to us in
94 // the Render() callback.
95 DCHECK_GT(output_channels_
, 0);
96 output_bus_
= AudioBus::CreateWrapper(output_channels_
);
98 bool configured
= ConfigureAUHAL();
100 hardware_latency_frames_
= GetHardwareLatency();
105 void AUHALStream::Close() {
107 OSStatus result
= AudioUnitUninitialize(audio_unit_
);
108 OSSTATUS_DLOG_IF(ERROR
, result
!= noErr
, result
)
109 << "AudioUnitUninitialize() failed.";
110 result
= AudioComponentInstanceDispose(audio_unit_
);
111 OSSTATUS_DLOG_IF(ERROR
, result
!= noErr
, result
)
112 << "AudioComponentInstanceDispose() failed.";
115 // Inform the audio manager that we have been closed. This will cause our
117 manager_
->ReleaseOutputStream(this);
120 void AUHALStream::Start(AudioSourceCallback
* callback
) {
123 DLOG(ERROR
) << "Open() has not been called successfully";
127 // Check if we should defer Start() for http://crbug.com/160920.
128 if (manager_
->ShouldDeferStreamStart()) {
129 // Use a cancellable closure so that if Stop() is called before Start()
130 // actually runs, we can cancel the pending start.
131 deferred_start_cb_
.Reset(
132 base::Bind(&AUHALStream::Start
, base::Unretained(this), callback
));
133 manager_
->GetTaskRunner()->PostDelayedTask(
134 FROM_HERE
, deferred_start_cb_
.callback(), base::TimeDelta::FromSeconds(
135 AudioManagerMac::kStartDelayInSecsForPowerEvents
));
142 base::AutoLock
auto_lock(source_lock_
);
146 OSStatus result
= AudioOutputUnitStart(audio_unit_
);
151 OSSTATUS_DLOG(ERROR
, result
) << "AudioOutputUnitStart() failed.";
152 callback
->OnError(this);
155 void AUHALStream::Stop() {
156 deferred_start_cb_
.Cancel();
160 OSStatus result
= AudioOutputUnitStop(audio_unit_
);
161 OSSTATUS_DLOG_IF(ERROR
, result
!= noErr
, result
)
162 << "AudioOutputUnitStop() failed.";
164 source_
->OnError(this);
166 base::AutoLock
auto_lock(source_lock_
);
171 void AUHALStream::SetVolume(double volume
) {
172 volume_
= static_cast<float>(volume
);
175 void AUHALStream::GetVolume(double* volume
) {
179 // Pulls on our provider to get rendered audio stream.
180 // Note to future hackers of this function: Do not add locks which can
181 // be contended in the middle of stream processing here (starting and stopping
182 // the stream are ok) because this is running on a real-time thread.
183 OSStatus
AUHALStream::Render(
184 AudioUnitRenderActionFlags
* flags
,
185 const AudioTimeStamp
* output_time_stamp
,
187 UInt32 number_of_frames
,
188 AudioBufferList
* data
) {
189 TRACE_EVENT0("audio", "AUHALStream::Render");
191 // If the stream parameters change for any reason, we need to insert a FIFO
192 // since the OnMoreData() pipeline can't handle frame size changes.
193 if (number_of_frames
!= number_of_frames_
) {
194 // Create a FIFO on the fly to handle any discrepancies in callback rates.
196 DVLOG(1) << "Audio frame size changed from " << number_of_frames_
197 << " to " << number_of_frames
198 << "; adding FIFO to compensate.";
199 audio_fifo_
.reset(new AudioPullFifo(
202 base::Bind(&AUHALStream::ProvideInput
, base::Unretained(this))));
206 // Make |output_bus_| wrap the output AudioBufferList.
207 WrapBufferList(data
, output_bus_
.get(), number_of_frames
);
209 // Update the playout latency.
210 const double playout_latency_frames
= GetPlayoutLatency(output_time_stamp
);
211 current_hardware_pending_bytes_
= static_cast<uint32
>(
212 (playout_latency_frames
+ 0.5) * params_
.GetBytesPerFrame());
215 audio_fifo_
->Consume(output_bus_
.get(), output_bus_
->frames());
217 ProvideInput(0, output_bus_
.get());
222 void AUHALStream::ProvideInput(int frame_delay
, AudioBus
* dest
) {
223 base::AutoLock
auto_lock(source_lock_
);
229 // Supply the input data and render the output data.
232 current_hardware_pending_bytes_
+
233 frame_delay
* params_
.GetBytesPerFrame());
234 dest
->Scale(volume_
);
238 OSStatus
AUHALStream::InputProc(
240 AudioUnitRenderActionFlags
* flags
,
241 const AudioTimeStamp
* output_time_stamp
,
243 UInt32 number_of_frames
,
244 AudioBufferList
* io_data
) {
245 // Dispatch to our class method.
246 AUHALStream
* audio_output
=
247 static_cast<AUHALStream
*>(user_data
);
251 return audio_output
->Render(
259 double AUHALStream::GetHardwareLatency() {
260 if (!audio_unit_
|| device_
== kAudioObjectUnknown
) {
261 DLOG(WARNING
) << "AudioUnit is NULL or device ID is unknown";
265 // Get audio unit latency.
266 Float64 audio_unit_latency_sec
= 0.0;
267 UInt32 size
= sizeof(audio_unit_latency_sec
);
268 OSStatus result
= AudioUnitGetProperty(
270 kAudioUnitProperty_Latency
,
271 kAudioUnitScope_Global
,
273 &audio_unit_latency_sec
,
275 if (result
!= noErr
) {
276 OSSTATUS_DLOG(WARNING
, result
) << "Could not get AudioUnit latency";
280 // Get output audio device latency.
281 static const AudioObjectPropertyAddress property_address
= {
282 kAudioDevicePropertyLatency
,
283 kAudioDevicePropertyScopeOutput
,
284 kAudioObjectPropertyElementMaster
287 UInt32 device_latency_frames
= 0;
288 size
= sizeof(device_latency_frames
);
289 result
= AudioObjectGetPropertyData(
295 &device_latency_frames
);
296 if (result
!= noErr
) {
297 OSSTATUS_DLOG(WARNING
, result
) << "Could not get audio device latency";
301 return static_cast<double>((audio_unit_latency_sec
*
302 output_format_
.mSampleRate
) + device_latency_frames
);
305 double AUHALStream::GetPlayoutLatency(
306 const AudioTimeStamp
* output_time_stamp
) {
307 // Ensure mHostTime is valid.
308 if ((output_time_stamp
->mFlags
& kAudioTimeStampHostTimeValid
) == 0)
311 // Get the delay between the moment getting the callback and the scheduled
312 // time stamp that tells when the data is going to be played out.
313 UInt64 output_time_ns
= AudioConvertHostTimeToNanos(
314 output_time_stamp
->mHostTime
);
315 UInt64 now_ns
= AudioConvertHostTimeToNanos(AudioGetCurrentHostTime());
317 // Prevent overflow leading to huge delay information; occurs regularly on
318 // the bots, probably less so in the wild.
319 if (now_ns
> output_time_ns
)
322 double delay_frames
= static_cast<double>
323 (1e-9 * (output_time_ns
- now_ns
) * output_format_
.mSampleRate
);
325 return (delay_frames
+ hardware_latency_frames_
);
328 bool AUHALStream::SetStreamFormat(
329 AudioStreamBasicDescription
* desc
,
334 AudioStreamBasicDescription
& format
= *desc
;
336 format
.mSampleRate
= params_
.sample_rate();
337 format
.mFormatID
= kAudioFormatLinearPCM
;
338 format
.mFormatFlags
= kAudioFormatFlagsNativeFloatPacked
|
339 kLinearPCMFormatFlagIsNonInterleaved
;
340 format
.mBytesPerPacket
= sizeof(Float32
);
341 format
.mFramesPerPacket
= 1;
342 format
.mBytesPerFrame
= sizeof(Float32
);
343 format
.mChannelsPerFrame
= channels
;
344 format
.mBitsPerChannel
= 32;
345 format
.mReserved
= 0;
347 OSStatus result
= AudioUnitSetProperty(
349 kAudioUnitProperty_StreamFormat
,
354 return (result
== noErr
);
357 bool AUHALStream::ConfigureAUHAL() {
358 if (device_
== kAudioObjectUnknown
|| output_channels_
== 0)
361 AudioComponentDescription desc
= {
362 kAudioUnitType_Output
,
363 kAudioUnitSubType_HALOutput
,
364 kAudioUnitManufacturer_Apple
,
368 AudioComponent comp
= AudioComponentFindNext(0, &desc
);
372 OSStatus result
= AudioComponentInstanceNew(comp
, &audio_unit_
);
373 if (result
!= noErr
) {
374 OSSTATUS_DLOG(ERROR
, result
) << "AudioComponentInstanceNew() failed.";
378 // Enable output as appropriate.
379 // See Apple technote for details about the EnableIO property.
380 // Note that we use bus 1 for input and bus 0 for output:
381 // http://developer.apple.com/library/mac/#technotes/tn2091/_index.html
382 UInt32 enable_IO
= 1;
383 result
= AudioUnitSetProperty(
385 kAudioOutputUnitProperty_EnableIO
,
386 kAudioUnitScope_Output
,
393 // Set the device to be used with the AUHAL AudioUnit.
394 result
= AudioUnitSetProperty(
396 kAudioOutputUnitProperty_CurrentDevice
,
397 kAudioUnitScope_Global
,
400 sizeof(AudioDeviceID
));
404 // Set stream formats.
405 // See Apple's tech note for details on the peculiar way that
406 // inputs and outputs are handled in the AUHAL concerning scope and bus
407 // (element) numbers:
408 // http://developer.apple.com/library/mac/#technotes/tn2091/_index.html
410 if (!SetStreamFormat(&output_format_
,
412 kAudioUnitScope_Input
,
417 if (!manager_
->MaybeChangeBufferSize(
418 device_
, audio_unit_
, 0, number_of_frames_
))
422 AURenderCallbackStruct callback
;
423 callback
.inputProc
= InputProc
;
424 callback
.inputProcRefCon
= this;
425 result
= AudioUnitSetProperty(
427 kAudioUnitProperty_SetRenderCallback
,
428 kAudioUnitScope_Input
,
435 result
= AudioUnitInitialize(audio_unit_
);
436 if (result
!= noErr
) {
437 OSSTATUS_DLOG(ERROR
, result
) << "AudioUnitInitialize() failed.";