audio_auhal_mac.cc revision 0529e5d033099cbfc42635f6f6183833b09dff6e
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. 4 5#include "media/audio/mac/audio_auhal_mac.h" 6 7#include <CoreServices/CoreServices.h> 8 9#include "base/basictypes.h" 10#include "base/bind.h" 11#include "base/bind_helpers.h" 12#include "base/debug/trace_event.h" 13#include "base/logging.h" 14#include "base/mac/mac_logging.h" 15#include "base/time/time.h" 16#include "media/audio/mac/audio_manager_mac.h" 17#include "media/base/audio_pull_fifo.h" 18 19namespace media { 20 21static void ZeroBufferList(AudioBufferList* buffer_list) { 22 for (size_t i = 0; i < buffer_list->mNumberBuffers; ++i) { 23 memset(buffer_list->mBuffers[i].mData, 24 0, 25 buffer_list->mBuffers[i].mDataByteSize); 26 } 27} 28 29static void WrapBufferList(AudioBufferList* buffer_list, 30 AudioBus* bus, 31 int frames) { 32 DCHECK(buffer_list); 33 DCHECK(bus); 34 const int channels = bus->channels(); 35 const int buffer_list_channels = buffer_list->mNumberBuffers; 36 CHECK_EQ(channels, buffer_list_channels); 37 38 // Copy pointers from AudioBufferList. 39 for (int i = 0; i < channels; ++i) { 40 bus->SetChannelData( 41 i, static_cast<float*>(buffer_list->mBuffers[i].mData)); 42 } 43 44 // Finally set the actual length. 45 bus->set_frames(frames); 46} 47 48AUHALStream::AUHALStream( 49 AudioManagerMac* manager, 50 const AudioParameters& params, 51 AudioDeviceID device) 52 : manager_(manager), 53 params_(params), 54 input_channels_(params_.input_channels()), 55 output_channels_(params_.channels()), 56 number_of_frames_(params_.frames_per_buffer()), 57 source_(NULL), 58 device_(device), 59 audio_unit_(0), 60 volume_(1), 61 hardware_latency_frames_(0), 62 stopped_(false), 63 input_buffer_list_(NULL), 64 current_hardware_pending_bytes_(0) { 65 // We must have a manager. 66 DCHECK(manager_); 67 68 VLOG(1) << "AUHALStream::AUHALStream()"; 69 VLOG(1) << "Device: " << device; 70 VLOG(1) << "Input channels: " << input_channels_; 71 VLOG(1) << "Output channels: " << output_channels_; 72 VLOG(1) << "Sample rate: " << params_.sample_rate(); 73 VLOG(1) << "Buffer size: " << number_of_frames_; 74} 75 76AUHALStream::~AUHALStream() { 77} 78 79bool AUHALStream::Open() { 80 // Get the total number of input and output channels that the 81 // hardware supports. 82 int device_input_channels; 83 bool got_input_channels = AudioManagerMac::GetDeviceChannels( 84 device_, 85 kAudioDevicePropertyScopeInput, 86 &device_input_channels); 87 88 int device_output_channels; 89 bool got_output_channels = AudioManagerMac::GetDeviceChannels( 90 device_, 91 kAudioDevicePropertyScopeOutput, 92 &device_output_channels); 93 94 // Sanity check the requested I/O channels. 95 if (!got_input_channels || 96 input_channels_ < 0 || input_channels_ > device_input_channels) { 97 LOG(ERROR) << "AudioDevice does not support requested input channels."; 98 return false; 99 } 100 101 if (!got_output_channels || 102 output_channels_ <= 0 || output_channels_ > device_output_channels) { 103 LOG(ERROR) << "AudioDevice does not support requested output channels."; 104 return false; 105 } 106 107 // The requested sample-rate must match the hardware sample-rate. 108 int sample_rate = AudioManagerMac::HardwareSampleRateForDevice(device_); 109 110 if (sample_rate != params_.sample_rate()) { 111 LOG(ERROR) << "Requested sample-rate: " << params_.sample_rate() 112 << " must match the hardware sample-rate: " << sample_rate; 113 return false; 114 } 115 116 CreateIOBusses(); 117 118 bool configured = ConfigureAUHAL(); 119 if (configured) 120 hardware_latency_frames_ = GetHardwareLatency(); 121 122 return configured; 123} 124 125void AUHALStream::Close() { 126 if (input_buffer_list_) { 127 input_buffer_list_storage_.reset(); 128 input_buffer_list_ = NULL; 129 input_bus_.reset(NULL); 130 output_bus_.reset(NULL); 131 } 132 133 if (audio_unit_) { 134 OSStatus result = AudioUnitUninitialize(audio_unit_); 135 OSSTATUS_DLOG_IF(ERROR, result != noErr, result) 136 << "AudioUnitUninitialize() failed."; 137 result = AudioComponentInstanceDispose(audio_unit_); 138 OSSTATUS_DLOG_IF(ERROR, result != noErr, result) 139 << "AudioComponentInstanceDispose() failed."; 140 } 141 142 // Inform the audio manager that we have been closed. This will cause our 143 // destruction. 144 manager_->ReleaseOutputStream(this); 145} 146 147void AUHALStream::Start(AudioSourceCallback* callback) { 148 DCHECK(callback); 149 if (!audio_unit_) { 150 DLOG(ERROR) << "Open() has not been called successfully"; 151 return; 152 } 153 154 // Check if we should defer Start() for http://crbug.com/160920. 155 if (manager_->ShouldDeferStreamStart()) { 156 // Use a cancellable closure so that if Stop() is called before Start() 157 // actually runs, we can cancel the pending start. 158 DCHECK(deferred_start_cb_.IsCancelled()); 159 deferred_start_cb_.Reset( 160 base::Bind(&AUHALStream::Start, base::Unretained(this), callback)); 161 manager_->GetTaskRunner()->PostDelayedTask( 162 FROM_HERE, deferred_start_cb_.callback(), base::TimeDelta::FromSeconds( 163 AudioManagerMac::kStartDelayInSecsForPowerEvents)); 164 return; 165 } 166 167 stopped_ = false; 168 audio_fifo_.reset(); 169 { 170 base::AutoLock auto_lock(source_lock_); 171 source_ = callback; 172 } 173 174 OSStatus result = AudioOutputUnitStart(audio_unit_); 175 if (result == noErr) 176 return; 177 178 Stop(); 179 OSSTATUS_DLOG(ERROR, result) << "AudioOutputUnitStart() failed."; 180 callback->OnError(this); 181} 182 183void AUHALStream::Stop() { 184 deferred_start_cb_.Cancel(); 185 if (stopped_) 186 return; 187 188 OSStatus result = AudioOutputUnitStop(audio_unit_); 189 OSSTATUS_DLOG_IF(ERROR, result != noErr, result) 190 << "AudioOutputUnitStop() failed."; 191 if (result != noErr) 192 source_->OnError(this); 193 194 base::AutoLock auto_lock(source_lock_); 195 source_ = NULL; 196 stopped_ = true; 197} 198 199void AUHALStream::SetVolume(double volume) { 200 volume_ = static_cast<float>(volume); 201} 202 203void AUHALStream::GetVolume(double* volume) { 204 *volume = volume_; 205} 206 207// Pulls on our provider to get rendered audio stream. 208// Note to future hackers of this function: Do not add locks which can 209// be contended in the middle of stream processing here (starting and stopping 210// the stream are ok) because this is running on a real-time thread. 211OSStatus AUHALStream::Render( 212 AudioUnitRenderActionFlags* flags, 213 const AudioTimeStamp* output_time_stamp, 214 UInt32 bus_number, 215 UInt32 number_of_frames, 216 AudioBufferList* io_data) { 217 TRACE_EVENT0("audio", "AUHALStream::Render"); 218 219 // If the stream parameters change for any reason, we need to insert a FIFO 220 // since the OnMoreData() pipeline can't handle frame size changes. 221 if (number_of_frames != number_of_frames_) { 222 // Create a FIFO on the fly to handle any discrepancies in callback rates. 223 if (!audio_fifo_) { 224 VLOG(1) << "Audio frame size changed from " << number_of_frames_ << " to " 225 << number_of_frames << "; adding FIFO to compensate."; 226 audio_fifo_.reset(new AudioPullFifo( 227 output_channels_, 228 number_of_frames_, 229 base::Bind(&AUHALStream::ProvideInput, base::Unretained(this)))); 230 } 231 232 // Synchronous IO is not supported in this state. 233 if (input_channels_ > 0) 234 input_bus_->Zero(); 235 } else { 236 if (input_channels_ > 0 && input_buffer_list_) { 237 // Get the input data. |input_buffer_list_| is wrapped 238 // to point to the data allocated in |input_bus_|. 239 OSStatus result = AudioUnitRender(audio_unit_, 240 flags, 241 output_time_stamp, 242 1, 243 number_of_frames, 244 input_buffer_list_); 245 if (result != noErr) 246 ZeroBufferList(input_buffer_list_); 247 } 248 } 249 250 // Make |output_bus_| wrap the output AudioBufferList. 251 WrapBufferList(io_data, output_bus_.get(), number_of_frames); 252 253 // Update the playout latency. 254 const double playout_latency_frames = GetPlayoutLatency(output_time_stamp); 255 current_hardware_pending_bytes_ = static_cast<uint32>( 256 (playout_latency_frames + 0.5) * params_.GetBytesPerFrame()); 257 258 if (audio_fifo_) 259 audio_fifo_->Consume(output_bus_.get(), output_bus_->frames()); 260 else 261 ProvideInput(0, output_bus_.get()); 262 263 return noErr; 264} 265 266void AUHALStream::ProvideInput(int frame_delay, AudioBus* dest) { 267 base::AutoLock auto_lock(source_lock_); 268 if (!source_) { 269 dest->Zero(); 270 return; 271 } 272 273 // Supply the input data and render the output data. 274 source_->OnMoreIOData( 275 input_bus_.get(), 276 dest, 277 AudioBuffersState(0, 278 current_hardware_pending_bytes_ + 279 frame_delay * params_.GetBytesPerFrame())); 280 dest->Scale(volume_); 281} 282 283// AUHAL callback. 284OSStatus AUHALStream::InputProc( 285 void* user_data, 286 AudioUnitRenderActionFlags* flags, 287 const AudioTimeStamp* output_time_stamp, 288 UInt32 bus_number, 289 UInt32 number_of_frames, 290 AudioBufferList* io_data) { 291 // Dispatch to our class method. 292 AUHALStream* audio_output = 293 static_cast<AUHALStream*>(user_data); 294 if (!audio_output) 295 return -1; 296 297 return audio_output->Render( 298 flags, 299 output_time_stamp, 300 bus_number, 301 number_of_frames, 302 io_data); 303} 304 305double AUHALStream::GetHardwareLatency() { 306 if (!audio_unit_ || device_ == kAudioObjectUnknown) { 307 DLOG(WARNING) << "AudioUnit is NULL or device ID is unknown"; 308 return 0.0; 309 } 310 311 // Get audio unit latency. 312 Float64 audio_unit_latency_sec = 0.0; 313 UInt32 size = sizeof(audio_unit_latency_sec); 314 OSStatus result = AudioUnitGetProperty( 315 audio_unit_, 316 kAudioUnitProperty_Latency, 317 kAudioUnitScope_Global, 318 0, 319 &audio_unit_latency_sec, 320 &size); 321 if (result != noErr) { 322 OSSTATUS_DLOG(WARNING, result) << "Could not get AudioUnit latency"; 323 return 0.0; 324 } 325 326 // Get output audio device latency. 327 static const AudioObjectPropertyAddress property_address = { 328 kAudioDevicePropertyLatency, 329 kAudioDevicePropertyScopeOutput, 330 kAudioObjectPropertyElementMaster 331 }; 332 333 UInt32 device_latency_frames = 0; 334 size = sizeof(device_latency_frames); 335 result = AudioObjectGetPropertyData( 336 device_, 337 &property_address, 338 0, 339 NULL, 340 &size, 341 &device_latency_frames); 342 if (result != noErr) { 343 OSSTATUS_DLOG(WARNING, result) << "Could not get audio device latency"; 344 return 0.0; 345 } 346 347 return static_cast<double>((audio_unit_latency_sec * 348 output_format_.mSampleRate) + device_latency_frames); 349} 350 351double AUHALStream::GetPlayoutLatency( 352 const AudioTimeStamp* output_time_stamp) { 353 // Ensure mHostTime is valid. 354 if ((output_time_stamp->mFlags & kAudioTimeStampHostTimeValid) == 0) 355 return 0; 356 357 // Get the delay between the moment getting the callback and the scheduled 358 // time stamp that tells when the data is going to be played out. 359 UInt64 output_time_ns = AudioConvertHostTimeToNanos( 360 output_time_stamp->mHostTime); 361 UInt64 now_ns = AudioConvertHostTimeToNanos(AudioGetCurrentHostTime()); 362 363 // Prevent overflow leading to huge delay information; occurs regularly on 364 // the bots, probably less so in the wild. 365 if (now_ns > output_time_ns) 366 return 0; 367 368 double delay_frames = static_cast<double> 369 (1e-9 * (output_time_ns - now_ns) * output_format_.mSampleRate); 370 371 return (delay_frames + hardware_latency_frames_); 372} 373 374void AUHALStream::CreateIOBusses() { 375 if (input_channels_ > 0) { 376 // Allocate storage for the AudioBufferList used for the 377 // input data from the input AudioUnit. 378 // We allocate enough space for with one AudioBuffer per channel. 379 size_t buffer_list_size = offsetof(AudioBufferList, mBuffers[0]) + 380 (sizeof(AudioBuffer) * input_channels_); 381 input_buffer_list_storage_.reset(new uint8[buffer_list_size]); 382 383 input_buffer_list_ = 384 reinterpret_cast<AudioBufferList*>(input_buffer_list_storage_.get()); 385 input_buffer_list_->mNumberBuffers = input_channels_; 386 387 // |input_bus_| allocates the storage for the PCM input data. 388 input_bus_ = AudioBus::Create(input_channels_, number_of_frames_); 389 390 // Make the AudioBufferList point to the memory in |input_bus_|. 391 UInt32 buffer_size_bytes = input_bus_->frames() * sizeof(Float32); 392 for (size_t i = 0; i < input_buffer_list_->mNumberBuffers; ++i) { 393 input_buffer_list_->mBuffers[i].mNumberChannels = 1; 394 input_buffer_list_->mBuffers[i].mDataByteSize = buffer_size_bytes; 395 input_buffer_list_->mBuffers[i].mData = input_bus_->channel(i); 396 } 397 } 398 399 // The output bus will wrap the AudioBufferList given to us in 400 // the Render() callback. 401 DCHECK_GT(output_channels_, 0); 402 output_bus_ = AudioBus::CreateWrapper(output_channels_); 403} 404 405bool AUHALStream::EnableIO(bool enable, UInt32 scope) { 406 // See Apple technote for details about the EnableIO property. 407 // Note that we use bus 1 for input and bus 0 for output: 408 // http://developer.apple.com/library/mac/#technotes/tn2091/_index.html 409 UInt32 enable_IO = enable ? 1 : 0; 410 OSStatus result = AudioUnitSetProperty( 411 audio_unit_, 412 kAudioOutputUnitProperty_EnableIO, 413 scope, 414 (scope == kAudioUnitScope_Input) ? 1 : 0, 415 &enable_IO, 416 sizeof(enable_IO)); 417 return (result == noErr); 418} 419 420bool AUHALStream::SetStreamFormat( 421 AudioStreamBasicDescription* desc, 422 int channels, 423 UInt32 scope, 424 UInt32 element) { 425 DCHECK(desc); 426 AudioStreamBasicDescription& format = *desc; 427 428 format.mSampleRate = params_.sample_rate(); 429 format.mFormatID = kAudioFormatLinearPCM; 430 format.mFormatFlags = kAudioFormatFlagsNativeFloatPacked | 431 kLinearPCMFormatFlagIsNonInterleaved; 432 format.mBytesPerPacket = sizeof(Float32); 433 format.mFramesPerPacket = 1; 434 format.mBytesPerFrame = sizeof(Float32); 435 format.mChannelsPerFrame = channels; 436 format.mBitsPerChannel = 32; 437 format.mReserved = 0; 438 439 OSStatus result = AudioUnitSetProperty( 440 audio_unit_, 441 kAudioUnitProperty_StreamFormat, 442 scope, 443 element, 444 &format, 445 sizeof(format)); 446 return (result == noErr); 447} 448 449bool AUHALStream::ConfigureAUHAL() { 450 if (device_ == kAudioObjectUnknown || 451 (input_channels_ == 0 && output_channels_ == 0)) 452 return false; 453 454 AudioComponentDescription desc = { 455 kAudioUnitType_Output, 456 kAudioUnitSubType_HALOutput, 457 kAudioUnitManufacturer_Apple, 458 0, 459 0 460 }; 461 AudioComponent comp = AudioComponentFindNext(0, &desc); 462 if (!comp) 463 return false; 464 465 OSStatus result = AudioComponentInstanceNew(comp, &audio_unit_); 466 if (result != noErr) { 467 OSSTATUS_DLOG(ERROR, result) << "AudioComponentInstanceNew() failed."; 468 return false; 469 } 470 471 // Enable input and output as appropriate. 472 if (!EnableIO(input_channels_ > 0, kAudioUnitScope_Input)) 473 return false; 474 if (!EnableIO(output_channels_ > 0, kAudioUnitScope_Output)) 475 return false; 476 477 // Set the device to be used with the AUHAL AudioUnit. 478 result = AudioUnitSetProperty( 479 audio_unit_, 480 kAudioOutputUnitProperty_CurrentDevice, 481 kAudioUnitScope_Global, 482 0, 483 &device_, 484 sizeof(AudioDeviceID)); 485 if (result != noErr) 486 return false; 487 488 // Set stream formats. 489 // See Apple's tech note for details on the peculiar way that 490 // inputs and outputs are handled in the AUHAL concerning scope and bus 491 // (element) numbers: 492 // http://developer.apple.com/library/mac/#technotes/tn2091/_index.html 493 494 if (input_channels_ > 0) { 495 if (!SetStreamFormat(&input_format_, 496 input_channels_, 497 kAudioUnitScope_Output, 498 1)) 499 return false; 500 } 501 502 if (output_channels_ > 0) { 503 if (!SetStreamFormat(&output_format_, 504 output_channels_, 505 kAudioUnitScope_Input, 506 0)) 507 return false; 508 } 509 510 // Set the buffer frame size. 511 // WARNING: Setting this value changes the frame size for all output audio 512 // units in the current process. As a result, the AURenderCallback must be 513 // able to handle arbitrary buffer sizes and FIFO appropriately. 514 UInt32 buffer_size = 0; 515 UInt32 property_size = sizeof(buffer_size); 516 result = AudioUnitGetProperty(audio_unit_, 517 kAudioDevicePropertyBufferFrameSize, 518 kAudioUnitScope_Output, 519 0, 520 &buffer_size, 521 &property_size); 522 if (result != noErr) { 523 OSSTATUS_DLOG(ERROR, result) 524 << "AudioUnitGetProperty(kAudioDevicePropertyBufferFrameSize) failed."; 525 return false; 526 } 527 528 // Only set the buffer size if we're the only active stream or the buffer size 529 // is lower than the current buffer size. 530 if (manager_->output_stream_count() == 1 || number_of_frames_ < buffer_size) { 531 buffer_size = number_of_frames_; 532 result = AudioUnitSetProperty(audio_unit_, 533 kAudioDevicePropertyBufferFrameSize, 534 kAudioUnitScope_Output, 535 0, 536 &buffer_size, 537 sizeof(buffer_size)); 538 if (result != noErr) { 539 OSSTATUS_DLOG(ERROR, result) << "AudioUnitSetProperty(" 540 "kAudioDevicePropertyBufferFrameSize) " 541 "failed. Size: " << number_of_frames_; 542 return false; 543 } 544 } 545 546 // Setup callback. 547 AURenderCallbackStruct callback; 548 callback.inputProc = InputProc; 549 callback.inputProcRefCon = this; 550 result = AudioUnitSetProperty( 551 audio_unit_, 552 kAudioUnitProperty_SetRenderCallback, 553 kAudioUnitScope_Input, 554 0, 555 &callback, 556 sizeof(callback)); 557 if (result != noErr) 558 return false; 559 560 result = AudioUnitInitialize(audio_unit_); 561 if (result != noErr) { 562 OSSTATUS_DLOG(ERROR, result) << "AudioUnitInitialize() failed."; 563 return false; 564 } 565 566 return true; 567} 568 569} // namespace media 570