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