audio_manager_mac.cc revision 868fa2fe829687343ffae624259930155e16dbd8
1// Copyright (c) 2012 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_manager_mac.h" 6 7#include <CoreAudio/AudioHardware.h> 8#include <string> 9 10#include "base/bind.h" 11#include "base/command_line.h" 12#include "base/mac/mac_logging.h" 13#include "base/mac/scoped_cftyperef.h" 14#include "base/strings/sys_string_conversions.h" 15#include "media/audio/audio_parameters.h" 16#include "media/audio/audio_util.h" 17#include "media/audio/mac/audio_auhal_mac.h" 18#include "media/audio/mac/audio_input_mac.h" 19#include "media/audio/mac/audio_low_latency_input_mac.h" 20#include "media/audio/mac/audio_low_latency_output_mac.h" 21#include "media/audio/mac/audio_synchronized_mac.h" 22#include "media/audio/mac/audio_unified_mac.h" 23#include "media/base/bind_to_loop.h" 24#include "media/base/channel_layout.h" 25#include "media/base/limits.h" 26#include "media/base/media_switches.h" 27 28namespace media { 29 30// Maximum number of output streams that can be open simultaneously. 31static const int kMaxOutputStreams = 50; 32 33// Default buffer size in samples for low-latency input and output streams. 34static const int kDefaultLowLatencyBufferSize = 128; 35 36// Default sample-rate on most Apple hardware. 37static const int kFallbackSampleRate = 44100; 38 39static int ChooseBufferSize(int output_sample_rate) { 40 int buffer_size = kDefaultLowLatencyBufferSize; 41 const int user_buffer_size = GetUserBufferSize(); 42 if (user_buffer_size) { 43 buffer_size = user_buffer_size; 44 } else if (output_sample_rate > 48000) { 45 // The default buffer size is too small for higher sample rates and may lead 46 // to glitching. Adjust upwards by multiples of the default size. 47 if (output_sample_rate <= 96000) 48 buffer_size = 2 * kDefaultLowLatencyBufferSize; 49 else if (output_sample_rate <= 192000) 50 buffer_size = 4 * kDefaultLowLatencyBufferSize; 51 } 52 53 return buffer_size; 54} 55 56static bool HasAudioHardware(AudioObjectPropertySelector selector) { 57 AudioDeviceID output_device_id = kAudioObjectUnknown; 58 const AudioObjectPropertyAddress property_address = { 59 selector, 60 kAudioObjectPropertyScopeGlobal, // mScope 61 kAudioObjectPropertyElementMaster // mElement 62 }; 63 UInt32 output_device_id_size = static_cast<UInt32>(sizeof(output_device_id)); 64 OSStatus err = AudioObjectGetPropertyData(kAudioObjectSystemObject, 65 &property_address, 66 0, // inQualifierDataSize 67 NULL, // inQualifierData 68 &output_device_id_size, 69 &output_device_id); 70 return err == kAudioHardwareNoError && 71 output_device_id != kAudioObjectUnknown; 72} 73 74// Returns true if the default input device is the same as 75// the default output device. 76bool AudioManagerMac::HasUnifiedDefaultIO() { 77 AudioDeviceID input_id, output_id; 78 if (!GetDefaultInputDevice(&input_id) || !GetDefaultOutputDevice(&output_id)) 79 return false; 80 81 return input_id == output_id; 82} 83 84static void GetAudioDeviceInfo(bool is_input, 85 media::AudioDeviceNames* device_names) { 86 DCHECK(device_names); 87 device_names->clear(); 88 89 // Query the number of total devices. 90 AudioObjectPropertyAddress property_address = { 91 kAudioHardwarePropertyDevices, 92 kAudioObjectPropertyScopeGlobal, 93 kAudioObjectPropertyElementMaster 94 }; 95 UInt32 size = 0; 96 OSStatus result = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, 97 &property_address, 98 0, 99 NULL, 100 &size); 101 if (result || !size) 102 return; 103 104 int device_count = size / sizeof(AudioDeviceID); 105 106 // Get the array of device ids for all the devices, which includes both 107 // input devices and output devices. 108 scoped_ptr_malloc<AudioDeviceID> 109 devices(reinterpret_cast<AudioDeviceID*>(malloc(size))); 110 AudioDeviceID* device_ids = devices.get(); 111 result = AudioObjectGetPropertyData(kAudioObjectSystemObject, 112 &property_address, 113 0, 114 NULL, 115 &size, 116 device_ids); 117 if (result) 118 return; 119 120 // Iterate over all available devices to gather information. 121 for (int i = 0; i < device_count; ++i) { 122 // Get the number of input or output channels of the device. 123 property_address.mScope = is_input ? 124 kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput; 125 property_address.mSelector = kAudioDevicePropertyStreams; 126 size = 0; 127 result = AudioObjectGetPropertyDataSize(device_ids[i], 128 &property_address, 129 0, 130 NULL, 131 &size); 132 if (result || !size) 133 continue; 134 135 // Get device UID. 136 CFStringRef uid = NULL; 137 size = sizeof(uid); 138 property_address.mSelector = kAudioDevicePropertyDeviceUID; 139 property_address.mScope = kAudioObjectPropertyScopeGlobal; 140 result = AudioObjectGetPropertyData(device_ids[i], 141 &property_address, 142 0, 143 NULL, 144 &size, 145 &uid); 146 if (result) 147 continue; 148 149 // Get device name. 150 CFStringRef name = NULL; 151 property_address.mSelector = kAudioObjectPropertyName; 152 property_address.mScope = kAudioObjectPropertyScopeGlobal; 153 result = AudioObjectGetPropertyData(device_ids[i], 154 &property_address, 155 0, 156 NULL, 157 &size, 158 &name); 159 if (result) { 160 if (uid) 161 CFRelease(uid); 162 continue; 163 } 164 165 // Store the device name and UID. 166 media::AudioDeviceName device_name; 167 device_name.device_name = base::SysCFStringRefToUTF8(name); 168 device_name.unique_id = base::SysCFStringRefToUTF8(uid); 169 device_names->push_back(device_name); 170 171 // We are responsible for releasing the returned CFObject. See the 172 // comment in the AudioHardware.h for constant 173 // kAudioDevicePropertyDeviceUID. 174 if (uid) 175 CFRelease(uid); 176 if (name) 177 CFRelease(name); 178 } 179} 180 181static AudioDeviceID GetAudioDeviceIdByUId(bool is_input, 182 const std::string& device_id) { 183 AudioObjectPropertyAddress property_address = { 184 kAudioHardwarePropertyDevices, 185 kAudioObjectPropertyScopeGlobal, 186 kAudioObjectPropertyElementMaster 187 }; 188 AudioDeviceID audio_device_id = kAudioObjectUnknown; 189 UInt32 device_size = sizeof(audio_device_id); 190 OSStatus result = -1; 191 192 if (device_id == AudioManagerBase::kDefaultDeviceId) { 193 // Default Device. 194 property_address.mSelector = is_input ? 195 kAudioHardwarePropertyDefaultInputDevice : 196 kAudioHardwarePropertyDefaultOutputDevice; 197 198 result = AudioObjectGetPropertyData(kAudioObjectSystemObject, 199 &property_address, 200 0, 201 0, 202 &device_size, 203 &audio_device_id); 204 } else { 205 // Non-default device. 206 base::mac::ScopedCFTypeRef<CFStringRef> 207 uid(base::SysUTF8ToCFStringRef(device_id)); 208 AudioValueTranslation value; 209 value.mInputData = &uid; 210 value.mInputDataSize = sizeof(CFStringRef); 211 value.mOutputData = &audio_device_id; 212 value.mOutputDataSize = device_size; 213 UInt32 translation_size = sizeof(AudioValueTranslation); 214 215 property_address.mSelector = kAudioHardwarePropertyDeviceForUID; 216 result = AudioObjectGetPropertyData(kAudioObjectSystemObject, 217 &property_address, 218 0, 219 0, 220 &translation_size, 221 &value); 222 } 223 224 if (result) { 225 OSSTATUS_DLOG(WARNING, result) << "Unable to query device " << device_id 226 << " for AudioDeviceID"; 227 } 228 229 return audio_device_id; 230} 231 232AudioManagerMac::AudioManagerMac() 233 : current_sample_rate_(0) { 234 current_output_device_ = kAudioDeviceUnknown; 235 236 SetMaxOutputStreamsAllowed(kMaxOutputStreams); 237 238 // Task must be posted last to avoid races from handing out "this" to the 239 // audio thread. Always PostTask even if we're on the right thread since 240 // AudioManager creation is on the startup path and this may be slow. 241 GetMessageLoop()->PostTask(FROM_HERE, base::Bind( 242 &AudioManagerMac::CreateDeviceListener, base::Unretained(this))); 243} 244 245AudioManagerMac::~AudioManagerMac() { 246 if (GetMessageLoop()->BelongsToCurrentThread()) { 247 DestroyDeviceListener(); 248 } else { 249 // It's safe to post a task here since Shutdown() will wait for all tasks to 250 // complete before returning. 251 GetMessageLoop()->PostTask(FROM_HERE, base::Bind( 252 &AudioManagerMac::DestroyDeviceListener, base::Unretained(this))); 253 } 254 255 Shutdown(); 256} 257 258bool AudioManagerMac::HasAudioOutputDevices() { 259 return HasAudioHardware(kAudioHardwarePropertyDefaultOutputDevice); 260} 261 262bool AudioManagerMac::HasAudioInputDevices() { 263 return HasAudioHardware(kAudioHardwarePropertyDefaultInputDevice); 264} 265 266// TODO(crogers): There are several places on the OSX specific code which 267// could benefit from these helper functions. 268bool AudioManagerMac::GetDefaultInputDevice( 269 AudioDeviceID* device) { 270 return GetDefaultDevice(device, true); 271} 272 273bool AudioManagerMac::GetDefaultOutputDevice( 274 AudioDeviceID* device) { 275 return GetDefaultDevice(device, false); 276} 277 278bool AudioManagerMac::GetDefaultDevice( 279 AudioDeviceID* device, bool input) { 280 CHECK(device); 281 282 // Obtain the current output device selected by the user. 283 AudioObjectPropertyAddress pa; 284 pa.mSelector = input ? kAudioHardwarePropertyDefaultInputDevice : 285 kAudioHardwarePropertyDefaultOutputDevice; 286 pa.mScope = kAudioObjectPropertyScopeGlobal; 287 pa.mElement = kAudioObjectPropertyElementMaster; 288 289 UInt32 size = sizeof(*device); 290 291 OSStatus result = AudioObjectGetPropertyData( 292 kAudioObjectSystemObject, 293 &pa, 294 0, 295 0, 296 &size, 297 device); 298 299 if ((result != kAudioHardwareNoError) || (*device == kAudioDeviceUnknown)) { 300 DLOG(ERROR) << "Error getting default AudioDevice."; 301 return false; 302 } 303 304 return true; 305} 306 307bool AudioManagerMac::GetDefaultOutputChannels( 308 int* channels) { 309 AudioDeviceID device; 310 if (!GetDefaultOutputDevice(&device)) 311 return false; 312 313 return GetDeviceChannels(device, 314 kAudioDevicePropertyScopeOutput, 315 channels); 316} 317 318bool AudioManagerMac::GetDeviceChannels( 319 AudioDeviceID device, 320 AudioObjectPropertyScope scope, 321 int* channels) { 322 CHECK(channels); 323 324 // Get stream configuration. 325 AudioObjectPropertyAddress pa; 326 pa.mSelector = kAudioDevicePropertyStreamConfiguration; 327 pa.mScope = scope; 328 pa.mElement = kAudioObjectPropertyElementMaster; 329 330 UInt32 size; 331 OSStatus result = AudioObjectGetPropertyDataSize(device, &pa, 0, 0, &size); 332 if (result != noErr || !size) 333 return false; 334 335 // Allocate storage. 336 scoped_ptr<uint8[]> list_storage(new uint8[size]); 337 AudioBufferList& buffer_list = 338 *reinterpret_cast<AudioBufferList*>(list_storage.get()); 339 340 result = AudioObjectGetPropertyData( 341 device, 342 &pa, 343 0, 344 0, 345 &size, 346 &buffer_list); 347 if (result != noErr) 348 return false; 349 350 // Determine number of input channels. 351 int channels_per_frame = buffer_list.mNumberBuffers > 0 ? 352 buffer_list.mBuffers[0].mNumberChannels : 0; 353 if (channels_per_frame == 1 && buffer_list.mNumberBuffers > 1) { 354 // Non-interleaved. 355 *channels = buffer_list.mNumberBuffers; 356 } else { 357 // Interleaved. 358 *channels = channels_per_frame; 359 } 360 361 return true; 362} 363 364int AudioManagerMac::HardwareSampleRateForDevice(AudioDeviceID device_id) { 365 Float64 nominal_sample_rate; 366 UInt32 info_size = sizeof(nominal_sample_rate); 367 368 static const AudioObjectPropertyAddress kNominalSampleRateAddress = { 369 kAudioDevicePropertyNominalSampleRate, 370 kAudioObjectPropertyScopeGlobal, 371 kAudioObjectPropertyElementMaster 372 }; 373 OSStatus result = AudioObjectGetPropertyData( 374 device_id, 375 &kNominalSampleRateAddress, 376 0, 377 0, 378 &info_size, 379 &nominal_sample_rate); 380 if (result != noErr) { 381 OSSTATUS_DLOG(WARNING, result) 382 << "Could not get default sample rate for device: " << device_id; 383 return 0; 384 } 385 386 return static_cast<int>(nominal_sample_rate); 387} 388 389int AudioManagerMac::HardwareSampleRate() { 390 // Determine the default output device's sample-rate. 391 AudioDeviceID device_id = kAudioObjectUnknown; 392 if (!GetDefaultOutputDevice(&device_id)) 393 return kFallbackSampleRate; 394 395 return HardwareSampleRateForDevice(device_id); 396} 397 398void AudioManagerMac::GetAudioInputDeviceNames( 399 media::AudioDeviceNames* device_names) { 400 GetAudioDeviceInfo(true, device_names); 401 if (!device_names->empty()) { 402 // Prepend the default device to the list since we always want it to be 403 // on the top of the list for all platforms. There is no duplicate 404 // counting here since the default device has been abstracted out before. 405 media::AudioDeviceName name; 406 name.device_name = AudioManagerBase::kDefaultDeviceName; 407 name.unique_id = AudioManagerBase::kDefaultDeviceId; 408 device_names->push_front(name); 409 } 410} 411 412AudioParameters AudioManagerMac::GetInputStreamParameters( 413 const std::string& device_id) { 414 // Due to the sharing of the input and output buffer sizes, we need to choose 415 // the input buffer size based on the output sample rate. See 416 // http://crbug.com/154352. 417 const int buffer_size = ChooseBufferSize( 418 AUAudioOutputStream::HardwareSampleRate()); 419 420 AudioDeviceID device = GetAudioDeviceIdByUId(true, device_id); 421 if (device == kAudioObjectUnknown) { 422 DLOG(ERROR) << "Invalid device " << device_id; 423 return AudioParameters(); 424 } 425 426 int channels = 0; 427 ChannelLayout channel_layout = CHANNEL_LAYOUT_STEREO; 428 if (GetDeviceChannels(device, kAudioDevicePropertyScopeInput, &channels) && 429 channels <= 2) { 430 channel_layout = GuessChannelLayout(channels); 431 } else { 432 DLOG(ERROR) << "Failed to get the device channels, use stereo as default " 433 << "for device " << device_id; 434 } 435 436 int sample_rate = HardwareSampleRateForDevice(device); 437 if (!sample_rate) 438 sample_rate = kFallbackSampleRate; 439 440 // TODO(xians): query the native channel layout for the specific device. 441 return AudioParameters( 442 AudioParameters::AUDIO_PCM_LOW_LATENCY, channel_layout, 443 sample_rate, 16, buffer_size); 444} 445 446AudioOutputStream* AudioManagerMac::MakeLinearOutputStream( 447 const AudioParameters& params) { 448 return MakeLowLatencyOutputStream(params, std::string()); 449} 450 451AudioOutputStream* AudioManagerMac::MakeLowLatencyOutputStream( 452 const AudioParameters& params, const std::string& input_device_id) { 453 // Handle basic output with no input channels. 454 if (params.input_channels() == 0) { 455 AudioDeviceID device = kAudioObjectUnknown; 456 GetDefaultOutputDevice(&device); 457 return new AUHALStream(this, params, device); 458 } 459 460 // TODO(crogers): support more than stereo input. 461 if (params.input_channels() != 2) { 462 // WebAudio is currently hard-coded to 2 channels so we should not 463 // see this case. 464 NOTREACHED() << "Only stereo input is currently supported!"; 465 return NULL; 466 } 467 468 AudioDeviceID device = kAudioObjectUnknown; 469 if (HasUnifiedDefaultIO()) { 470 // For I/O, the simplest case is when the default input and output 471 // devices are the same. 472 GetDefaultOutputDevice(&device); 473 LOG(INFO) << "UNIFIED: default input and output devices are identical"; 474 } else { 475 // Some audio hardware is presented as separate input and output devices 476 // even though they are really the same physical hardware and 477 // share the same "clock domain" at the lowest levels of the driver. 478 // A common of example of this is the "built-in" audio hardware: 479 // "Built-in Line Input" 480 // "Built-in Output" 481 // We would like to use an "aggregate" device for these situations, since 482 // CoreAudio will make the most efficient use of the shared "clock domain" 483 // so we get the lowest latency and use fewer threads. 484 device = aggregate_device_manager_.GetDefaultAggregateDevice(); 485 if (device != kAudioObjectUnknown) 486 LOG(INFO) << "Using AGGREGATE audio device"; 487 } 488 489 if (device != kAudioObjectUnknown && 490 input_device_id == AudioManagerBase::kDefaultDeviceId) 491 return new AUHALStream(this, params, device); 492 493 // Fallback to AudioSynchronizedStream which will handle completely 494 // different and arbitrary combinations of input and output devices 495 // even running at different sample-rates. 496 // kAudioDeviceUnknown translates to "use default" here. 497 // TODO(crogers): consider tracking UMA stats on AUHALStream 498 // versus AudioSynchronizedStream. 499 AudioDeviceID audio_device_id = GetAudioDeviceIdByUId(true, input_device_id); 500 if (audio_device_id == kAudioObjectUnknown) 501 return NULL; 502 503 return new AudioSynchronizedStream(this, 504 params, 505 audio_device_id, 506 kAudioDeviceUnknown); 507} 508 509AudioInputStream* AudioManagerMac::MakeLinearInputStream( 510 const AudioParameters& params, const std::string& device_id) { 511 DCHECK_EQ(AudioParameters::AUDIO_PCM_LINEAR, params.format()); 512 return new PCMQueueInAudioInputStream(this, params); 513} 514 515AudioInputStream* AudioManagerMac::MakeLowLatencyInputStream( 516 const AudioParameters& params, const std::string& device_id) { 517 DCHECK_EQ(AudioParameters::AUDIO_PCM_LOW_LATENCY, params.format()); 518 // Gets the AudioDeviceID that refers to the AudioOutputDevice with the device 519 // unique id. This AudioDeviceID is used to set the device for Audio Unit. 520 AudioDeviceID audio_device_id = GetAudioDeviceIdByUId(true, device_id); 521 AudioInputStream* stream = NULL; 522 if (audio_device_id != kAudioObjectUnknown) 523 stream = new AUAudioInputStream(this, params, audio_device_id); 524 525 return stream; 526} 527 528AudioParameters AudioManagerMac::GetPreferredOutputStreamParameters( 529 const AudioParameters& input_params) { 530 int hardware_channels = 2; 531 if (!GetDefaultOutputChannels(&hardware_channels)) { 532 // Fallback to stereo. 533 hardware_channels = 2; 534 } 535 536 ChannelLayout channel_layout = GuessChannelLayout(hardware_channels); 537 538 const int hardware_sample_rate = AUAudioOutputStream::HardwareSampleRate(); 539 const int buffer_size = ChooseBufferSize(hardware_sample_rate); 540 541 int input_channels = 0; 542 if (input_params.IsValid()) { 543 input_channels = input_params.input_channels(); 544 545 if (input_channels > 0) { 546 // TODO(crogers): given the limitations of the AudioOutputStream 547 // back-ends used with synchronized I/O, we hard-code to stereo. 548 // Specifically, this is a limitation of AudioSynchronizedStream which 549 // can be removed as part of the work to consolidate these back-ends. 550 channel_layout = CHANNEL_LAYOUT_STEREO; 551 } 552 } 553 554 AudioParameters params( 555 AudioParameters::AUDIO_PCM_LOW_LATENCY, 556 channel_layout, 557 input_channels, 558 hardware_sample_rate, 559 16, 560 buffer_size); 561 562 if (channel_layout == CHANNEL_LAYOUT_UNSUPPORTED) 563 params.SetDiscreteChannels(hardware_channels); 564 565 return params; 566} 567 568void AudioManagerMac::CreateDeviceListener() { 569 DCHECK(GetMessageLoop()->BelongsToCurrentThread()); 570 571 // Get a baseline for the sample-rate and current device, 572 // so we can intelligently handle device notifications only when necessary. 573 current_sample_rate_ = HardwareSampleRate(); 574 if (!GetDefaultOutputDevice(¤t_output_device_)) 575 current_output_device_ = kAudioDeviceUnknown; 576 577 output_device_listener_.reset(new AudioDeviceListenerMac(base::Bind( 578 &AudioManagerMac::HandleDeviceChanges, base::Unretained(this)))); 579} 580 581void AudioManagerMac::DestroyDeviceListener() { 582 DCHECK(GetMessageLoop()->BelongsToCurrentThread()); 583 output_device_listener_.reset(); 584} 585 586void AudioManagerMac::HandleDeviceChanges() { 587 int new_sample_rate = HardwareSampleRate(); 588 AudioDeviceID new_output_device; 589 GetDefaultOutputDevice(&new_output_device); 590 591 if (current_sample_rate_ == new_sample_rate && 592 current_output_device_ == new_output_device) 593 return; 594 595 current_sample_rate_ = new_sample_rate; 596 current_output_device_ = new_output_device; 597 NotifyAllOutputDeviceChangeListeners(); 598} 599 600AudioManager* CreateAudioManager() { 601 return new AudioManagerMac(); 602} 603 604} // namespace media 605