Separate Simple Backend creation from initialization.
[chromium-blink-merge.git] / media / audio / mac / audio_auhal_mac.cc
blobb2a8828fec12f6548a47a40cd24a99aa1f976a42
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"
18 namespace media {
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;
30 return os;
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,
42 AudioBus* bus,
43 int frames) {
44 DCHECK(buffer_list);
45 DCHECK(bus);
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) {
52 bus->SetChannelData(
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,
63 AudioDeviceID device)
64 : manager_(manager),
65 params_(params),
66 input_channels_(params_.input_channels()),
67 output_channels_(params_.channels()),
68 number_of_frames_(params_.frames_per_buffer()),
69 source_(NULL),
70 device_(device),
71 audio_unit_(0),
72 volume_(1),
73 hardware_latency_frames_(0),
74 stopped_(false),
75 input_buffer_list_(NULL) {
76 // We must have a manager.
77 DCHECK(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
90 // hardware supports.
91 int device_input_channels;
92 bool got_input_channels = AudioManagerMac::GetDeviceChannels(
93 device_,
94 kAudioDevicePropertyScopeInput,
95 &device_input_channels);
97 int device_output_channels;
98 bool got_output_channels = AudioManagerMac::GetDeviceChannels(
99 device_,
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.";
107 return false;
110 if (!got_output_channels ||
111 output_channels_ <= 0 || output_channels_ > device_output_channels) {
112 LOG(ERROR) << "AudioDevice does not support requested output channels.";
113 return false;
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;
122 return false;
125 CreateIOBusses();
127 bool configured = ConfigureAUHAL();
128 if (configured)
129 hardware_latency_frames_ = GetHardwareLatency();
131 return configured;
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);
142 if (audio_unit_) {
143 AudioUnitUninitialize(audio_unit_);
144 AudioComponentInstanceDispose(audio_unit_);
147 // Inform the audio manager that we have been closed. This will cause our
148 // destruction.
149 manager_->ReleaseOutputStream(this);
152 void AUHALStream::Start(AudioSourceCallback* callback) {
153 DCHECK(callback);
154 if (!audio_unit_) {
155 DLOG(ERROR) << "Open() has not been called successfully";
156 return;
159 stopped_ = false;
161 base::AutoLock auto_lock(source_lock_);
162 source_ = callback;
165 AudioOutputUnitStart(audio_unit_);
168 void AUHALStream::Stop() {
169 if (stopped_)
170 return;
172 AudioOutputUnitStop(audio_unit_);
174 base::AutoLock auto_lock(source_lock_);
175 source_ = NULL;
176 stopped_ = true;
179 void AUHALStream::SetVolume(double volume) {
180 volume_ = static_cast<float>(volume);
183 void AUHALStream::GetVolume(double* volume) {
184 *volume = 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,
194 UInt32 bus_number,
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);
210 return noErr;
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(
217 audio_unit_,
218 flags,
219 output_time_stamp,
221 number_of_frames,
222 input_buffer_list_);
223 if (result != noErr)
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_);
242 if (!source_) {
243 ZeroBufferList(io_data);
244 return noErr;
247 // Supply the input data and render the output data.
248 source_->OnMoreIOData(
249 input_bus_.get(),
250 output_bus_.get(),
251 AudioBuffersState(0, hardware_pending_bytes));
252 output_bus_->Scale(volume_);
255 return noErr;
258 // AUHAL callback.
259 OSStatus AUHALStream::InputProc(
260 void* user_data,
261 AudioUnitRenderActionFlags* flags,
262 const AudioTimeStamp* output_time_stamp,
263 UInt32 bus_number,
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);
269 if (!audio_output)
270 return -1;
272 return audio_output->Render(
273 flags,
274 output_time_stamp,
275 bus_number,
276 number_of_frames,
277 io_data);
280 double AUHALStream::GetHardwareLatency() {
281 if (!audio_unit_ || device_ == kAudioObjectUnknown) {
282 DLOG(WARNING) << "AudioUnit is NULL or device ID is unknown";
283 return 0.0;
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(
290 audio_unit_,
291 kAudioUnitProperty_Latency,
292 kAudioUnitScope_Global,
294 &audio_unit_latency_sec,
295 &size);
296 if (result != noErr) {
297 OSSTATUS_DLOG(WARNING, result) << "Could not get AudioUnit latency";
298 return 0.0;
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(
311 device_,
312 &property_address,
314 NULL,
315 &size,
316 &device_latency_frames);
317 if (result != noErr) {
318 OSSTATUS_DLOG(WARNING, result) << "Could not get audio device latency";
319 return 0.0;
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)
330 return 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)
341 return 0;
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]);
358 input_buffer_list_ =
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(
386 audio_unit_,
387 kAudioOutputUnitProperty_EnableIO,
388 scope,
389 (scope == kAudioUnitScope_Input) ? 1 : 0,
390 &enable_IO,
391 sizeof(enable_IO));
392 return (result == noErr);
395 bool AUHALStream::SetStreamFormat(
396 AudioStreamBasicDescription* desc,
397 int channels,
398 UInt32 scope,
399 UInt32 element) {
400 DCHECK(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(
415 audio_unit_,
416 kAudioUnitProperty_StreamFormat,
417 scope,
418 element,
419 &format,
420 sizeof(format));
421 return (result == noErr);
424 bool AUHALStream::ConfigureAUHAL() {
425 if (device_ == kAudioObjectUnknown ||
426 (input_channels_ == 0 && output_channels_ == 0))
427 return false;
429 AudioComponentDescription desc = {
430 kAudioUnitType_Output,
431 kAudioUnitSubType_HALOutput,
432 kAudioUnitManufacturer_Apple,
436 AudioComponent comp = AudioComponentFindNext(0, &desc);
437 if (!comp)
438 return false;
440 OSStatus result = AudioComponentInstanceNew(comp, &audio_unit_);
441 if (result != noErr) {
442 OSSTATUS_DLOG(WARNING, result) << "AudioComponentInstanceNew() failed.";
443 return false;
446 result = AudioUnitInitialize(audio_unit_);
447 if (result != noErr) {
448 OSSTATUS_DLOG(WARNING, result) << "AudioUnitInitialize() failed.";
449 return false;
452 // Enable input and output as appropriate.
453 if (!EnableIO(input_channels_ > 0, kAudioUnitScope_Input))
454 return false;
455 if (!EnableIO(output_channels_ > 0, kAudioUnitScope_Output))
456 return false;
458 // Set the device to be used with the AUHAL AudioUnit.
459 result = AudioUnitSetProperty(
460 audio_unit_,
461 kAudioOutputUnitProperty_CurrentDevice,
462 kAudioUnitScope_Global,
464 &device_,
465 sizeof(AudioDeviceID));
466 if (result != noErr)
467 return false;
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_,
477 input_channels_,
478 kAudioUnitScope_Output,
480 return false;
483 if (output_channels_ > 0) {
484 if (!SetStreamFormat(&output_format_,
485 output_channels_,
486 kAudioUnitScope_Input,
488 return false;
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(
499 audio_unit_,
500 kAudioDevicePropertyBufferFrameSize,
501 kAudioUnitScope_Output,
503 &buffer_size,
504 sizeof(buffer_size));
505 if (result != noErr) {
506 OSSTATUS_DLOG(WARNING, result)
507 << "AudioUnitSetProperty(kAudioDevicePropertyBufferFrameSize) failed.";
508 return false;
511 // Setup callback.
512 AURenderCallbackStruct callback;
513 callback.inputProc = InputProc;
514 callback.inputProcRefCon = this;
515 result = AudioUnitSetProperty(
516 audio_unit_,
517 kAudioUnitProperty_SetRenderCallback,
518 kAudioUnitScope_Input,
520 &callback,
521 sizeof(callback));
522 if (result != noErr)
523 return false;
525 return true;
528 } // namespace media