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/power_monitor/power_monitor.h" 15 #include "base/power_monitor/power_observer.h" 16 #include "base/strings/sys_string_conversions.h" 17 #include "base/threading/thread_checker.h" 18 #include "media/audio/audio_parameters.h" 19 #include "media/audio/mac/audio_auhal_mac.h" 20 #include "media/audio/mac/audio_input_mac.h" 21 #include "media/audio/mac/audio_low_latency_input_mac.h" 22 #include "media/base/bind_to_current_loop.h" 23 #include "media/base/channel_layout.h" 24 #include "media/base/limits.h" 25 #include "media/base/media_switches.h" 26 27 namespace media { 28 29 // Maximum number of output streams that can be open simultaneously. 30 static const int kMaxOutputStreams = 50; 31 32 // Define bounds for for low-latency input and output streams. 33 static const int kMinimumInputOutputBufferSize = 128; 34 static const int kMaximumInputOutputBufferSize = 4096; 35 36 // Default sample-rate on most Apple hardware. 37 static const int kFallbackSampleRate = 44100; 38 39 static bool HasAudioHardware(AudioObjectPropertySelector selector) { 40 AudioDeviceID output_device_id = kAudioObjectUnknown; 41 const AudioObjectPropertyAddress property_address = { 42 selector, 43 kAudioObjectPropertyScopeGlobal, // mScope 44 kAudioObjectPropertyElementMaster // mElement 45 }; 46 UInt32 output_device_id_size = static_cast<UInt32>(sizeof(output_device_id)); 47 OSStatus err = AudioObjectGetPropertyData(kAudioObjectSystemObject, 48 &property_address, 49 0, // inQualifierDataSize 50 NULL, // inQualifierData 51 &output_device_id_size, 52 &output_device_id); 53 return err == kAudioHardwareNoError && 54 output_device_id != kAudioObjectUnknown; 55 } 56 57 // Retrieves information on audio devices, and prepends the default 58 // device to the list if the list is non-empty. 59 static void GetAudioDeviceInfo(bool is_input, 60 media::AudioDeviceNames* device_names) { 61 // Query the number of total devices. 62 AudioObjectPropertyAddress property_address = { 63 kAudioHardwarePropertyDevices, 64 kAudioObjectPropertyScopeGlobal, 65 kAudioObjectPropertyElementMaster 66 }; 67 UInt32 size = 0; 68 OSStatus result = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, 69 &property_address, 70 0, 71 NULL, 72 &size); 73 if (result || !size) 74 return; 75 76 int device_count = size / sizeof(AudioDeviceID); 77 78 // Get the array of device ids for all the devices, which includes both 79 // input devices and output devices. 80 scoped_ptr<AudioDeviceID, base::FreeDeleter> 81 devices(static_cast<AudioDeviceID*>(malloc(size))); 82 AudioDeviceID* device_ids = devices.get(); 83 result = AudioObjectGetPropertyData(kAudioObjectSystemObject, 84 &property_address, 85 0, 86 NULL, 87 &size, 88 device_ids); 89 if (result) 90 return; 91 92 // Iterate over all available devices to gather information. 93 for (int i = 0; i < device_count; ++i) { 94 // Get the number of input or output channels of the device. 95 property_address.mScope = is_input ? 96 kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput; 97 property_address.mSelector = kAudioDevicePropertyStreams; 98 size = 0; 99 result = AudioObjectGetPropertyDataSize(device_ids[i], 100 &property_address, 101 0, 102 NULL, 103 &size); 104 if (result || !size) 105 continue; 106 107 // Get device UID. 108 CFStringRef uid = NULL; 109 size = sizeof(uid); 110 property_address.mSelector = kAudioDevicePropertyDeviceUID; 111 property_address.mScope = kAudioObjectPropertyScopeGlobal; 112 result = AudioObjectGetPropertyData(device_ids[i], 113 &property_address, 114 0, 115 NULL, 116 &size, 117 &uid); 118 if (result) 119 continue; 120 121 // Get device name. 122 CFStringRef name = NULL; 123 property_address.mSelector = kAudioObjectPropertyName; 124 property_address.mScope = kAudioObjectPropertyScopeGlobal; 125 result = AudioObjectGetPropertyData(device_ids[i], 126 &property_address, 127 0, 128 NULL, 129 &size, 130 &name); 131 if (result) { 132 if (uid) 133 CFRelease(uid); 134 continue; 135 } 136 137 // Store the device name and UID. 138 media::AudioDeviceName device_name; 139 device_name.device_name = base::SysCFStringRefToUTF8(name); 140 device_name.unique_id = base::SysCFStringRefToUTF8(uid); 141 device_names->push_back(device_name); 142 143 // We are responsible for releasing the returned CFObject. See the 144 // comment in the AudioHardware.h for constant 145 // kAudioDevicePropertyDeviceUID. 146 if (uid) 147 CFRelease(uid); 148 if (name) 149 CFRelease(name); 150 } 151 152 if (!device_names->empty()) { 153 // Prepend the default device to the list since we always want it to be 154 // on the top of the list for all platforms. There is no duplicate 155 // counting here since the default device has been abstracted out before. 156 media::AudioDeviceName name; 157 name.device_name = AudioManagerBase::kDefaultDeviceName; 158 name.unique_id = AudioManagerBase::kDefaultDeviceId; 159 device_names->push_front(name); 160 } 161 } 162 163 static AudioDeviceID GetAudioDeviceIdByUId(bool is_input, 164 const std::string& device_id) { 165 AudioObjectPropertyAddress property_address = { 166 kAudioHardwarePropertyDevices, 167 kAudioObjectPropertyScopeGlobal, 168 kAudioObjectPropertyElementMaster 169 }; 170 AudioDeviceID audio_device_id = kAudioObjectUnknown; 171 UInt32 device_size = sizeof(audio_device_id); 172 OSStatus result = -1; 173 174 if (device_id == AudioManagerBase::kDefaultDeviceId || device_id.empty()) { 175 // Default Device. 176 property_address.mSelector = is_input ? 177 kAudioHardwarePropertyDefaultInputDevice : 178 kAudioHardwarePropertyDefaultOutputDevice; 179 180 result = AudioObjectGetPropertyData(kAudioObjectSystemObject, 181 &property_address, 182 0, 183 0, 184 &device_size, 185 &audio_device_id); 186 } else { 187 // Non-default device. 188 base::ScopedCFTypeRef<CFStringRef> uid( 189 base::SysUTF8ToCFStringRef(device_id)); 190 AudioValueTranslation value; 191 value.mInputData = &uid; 192 value.mInputDataSize = sizeof(CFStringRef); 193 value.mOutputData = &audio_device_id; 194 value.mOutputDataSize = device_size; 195 UInt32 translation_size = sizeof(AudioValueTranslation); 196 197 property_address.mSelector = kAudioHardwarePropertyDeviceForUID; 198 result = AudioObjectGetPropertyData(kAudioObjectSystemObject, 199 &property_address, 200 0, 201 0, 202 &translation_size, 203 &value); 204 } 205 206 if (result) { 207 OSSTATUS_DLOG(WARNING, result) << "Unable to query device " << device_id 208 << " for AudioDeviceID"; 209 } 210 211 return audio_device_id; 212 } 213 214 template <class T> 215 void StopStreams(std::list<T*>* streams) { 216 for (typename std::list<T*>::iterator it = streams->begin(); 217 it != streams->end(); 218 ++it) { 219 // Stop() is safe to call multiple times, so it doesn't matter if a stream 220 // has already been stopped. 221 (*it)->Stop(); 222 } 223 streams->clear(); 224 } 225 226 class AudioManagerMac::AudioPowerObserver : public base::PowerObserver { 227 public: 228 AudioPowerObserver() 229 : is_suspending_(false), 230 is_monitoring_(base::PowerMonitor::Get()) { 231 // The PowerMonitor requires signifcant setup (a CFRunLoop and preallocated 232 // IO ports) so it's not available under unit tests. See the OSX impl of 233 // base::PowerMonitorDeviceSource for more details. 234 if (!is_monitoring_) 235 return; 236 base::PowerMonitor::Get()->AddObserver(this); 237 } 238 239 virtual ~AudioPowerObserver() { 240 DCHECK(thread_checker_.CalledOnValidThread()); 241 if (!is_monitoring_) 242 return; 243 base::PowerMonitor::Get()->RemoveObserver(this); 244 } 245 246 bool ShouldDeferStreamStart() { 247 DCHECK(thread_checker_.CalledOnValidThread()); 248 // Start() should be deferred if the system is in the middle of a suspend or 249 // has recently started the process of resuming. 250 return is_suspending_ || base::TimeTicks::Now() < earliest_start_time_; 251 } 252 253 private: 254 virtual void OnSuspend() OVERRIDE { 255 DCHECK(thread_checker_.CalledOnValidThread()); 256 is_suspending_ = true; 257 } 258 259 virtual void OnResume() OVERRIDE { 260 DCHECK(thread_checker_.CalledOnValidThread()); 261 is_suspending_ = false; 262 earliest_start_time_ = base::TimeTicks::Now() + 263 base::TimeDelta::FromSeconds(kStartDelayInSecsForPowerEvents); 264 } 265 266 bool is_suspending_; 267 const bool is_monitoring_; 268 base::TimeTicks earliest_start_time_; 269 base::ThreadChecker thread_checker_; 270 271 DISALLOW_COPY_AND_ASSIGN(AudioPowerObserver); 272 }; 273 274 AudioManagerMac::AudioManagerMac(AudioLogFactory* audio_log_factory) 275 : AudioManagerBase(audio_log_factory), 276 current_sample_rate_(0), 277 current_output_device_(kAudioDeviceUnknown) { 278 SetMaxOutputStreamsAllowed(kMaxOutputStreams); 279 280 // Task must be posted last to avoid races from handing out "this" to the 281 // audio thread. Always PostTask even if we're on the right thread since 282 // AudioManager creation is on the startup path and this may be slow. 283 GetTaskRunner()->PostTask(FROM_HERE, base::Bind( 284 &AudioManagerMac::InitializeOnAudioThread, base::Unretained(this))); 285 } 286 287 AudioManagerMac::~AudioManagerMac() { 288 if (GetTaskRunner()->BelongsToCurrentThread()) { 289 ShutdownOnAudioThread(); 290 } else { 291 // It's safe to post a task here since Shutdown() will wait for all tasks to 292 // complete before returning. 293 GetTaskRunner()->PostTask(FROM_HERE, base::Bind( 294 &AudioManagerMac::ShutdownOnAudioThread, base::Unretained(this))); 295 } 296 297 Shutdown(); 298 } 299 300 bool AudioManagerMac::HasAudioOutputDevices() { 301 return HasAudioHardware(kAudioHardwarePropertyDefaultOutputDevice); 302 } 303 304 bool AudioManagerMac::HasAudioInputDevices() { 305 return HasAudioHardware(kAudioHardwarePropertyDefaultInputDevice); 306 } 307 308 // TODO(xians): There are several places on the OSX specific code which 309 // could benefit from these helper functions. 310 bool AudioManagerMac::GetDefaultInputDevice(AudioDeviceID* device) { 311 return GetDefaultDevice(device, true); 312 } 313 314 bool AudioManagerMac::GetDefaultOutputDevice(AudioDeviceID* device) { 315 return GetDefaultDevice(device, false); 316 } 317 318 bool AudioManagerMac::GetDefaultDevice(AudioDeviceID* device, bool input) { 319 CHECK(device); 320 321 // Obtain the current output device selected by the user. 322 AudioObjectPropertyAddress pa; 323 pa.mSelector = input ? kAudioHardwarePropertyDefaultInputDevice : 324 kAudioHardwarePropertyDefaultOutputDevice; 325 pa.mScope = kAudioObjectPropertyScopeGlobal; 326 pa.mElement = kAudioObjectPropertyElementMaster; 327 328 UInt32 size = sizeof(*device); 329 OSStatus result = AudioObjectGetPropertyData(kAudioObjectSystemObject, 330 &pa, 331 0, 332 0, 333 &size, 334 device); 335 336 if ((result != kAudioHardwareNoError) || (*device == kAudioDeviceUnknown)) { 337 DLOG(ERROR) << "Error getting default AudioDevice."; 338 return false; 339 } 340 341 return true; 342 } 343 344 bool AudioManagerMac::GetDefaultOutputChannels(int* channels) { 345 AudioDeviceID device; 346 if (!GetDefaultOutputDevice(&device)) 347 return false; 348 return GetDeviceChannels(device, kAudioDevicePropertyScopeOutput, channels); 349 } 350 351 bool AudioManagerMac::GetDeviceChannels(AudioDeviceID device, 352 AudioObjectPropertyScope scope, 353 int* channels) { 354 CHECK(channels); 355 356 // Get stream configuration. 357 AudioObjectPropertyAddress pa; 358 pa.mSelector = kAudioDevicePropertyStreamConfiguration; 359 pa.mScope = scope; 360 pa.mElement = kAudioObjectPropertyElementMaster; 361 362 UInt32 size; 363 OSStatus result = AudioObjectGetPropertyDataSize(device, &pa, 0, 0, &size); 364 if (result != noErr || !size) 365 return false; 366 367 // Allocate storage. 368 scoped_ptr<uint8[]> list_storage(new uint8[size]); 369 AudioBufferList& buffer_list = 370 *reinterpret_cast<AudioBufferList*>(list_storage.get()); 371 372 result = AudioObjectGetPropertyData(device, &pa, 0, 0, &size, &buffer_list); 373 if (result != noErr) 374 return false; 375 376 // Determine number of input channels. 377 int channels_per_frame = buffer_list.mNumberBuffers > 0 ? 378 buffer_list.mBuffers[0].mNumberChannels : 0; 379 if (channels_per_frame == 1 && buffer_list.mNumberBuffers > 1) { 380 // Non-interleaved. 381 *channels = buffer_list.mNumberBuffers; 382 } else { 383 // Interleaved. 384 *channels = channels_per_frame; 385 } 386 387 return true; 388 } 389 390 int AudioManagerMac::HardwareSampleRateForDevice(AudioDeviceID device_id) { 391 Float64 nominal_sample_rate; 392 UInt32 info_size = sizeof(nominal_sample_rate); 393 394 static const AudioObjectPropertyAddress kNominalSampleRateAddress = { 395 kAudioDevicePropertyNominalSampleRate, 396 kAudioObjectPropertyScopeGlobal, 397 kAudioObjectPropertyElementMaster 398 }; 399 OSStatus result = AudioObjectGetPropertyData(device_id, 400 &kNominalSampleRateAddress, 401 0, 402 0, 403 &info_size, 404 &nominal_sample_rate); 405 if (result != noErr) { 406 OSSTATUS_DLOG(WARNING, result) 407 << "Could not get default sample rate for device: " << device_id; 408 return 0; 409 } 410 411 return static_cast<int>(nominal_sample_rate); 412 } 413 414 int AudioManagerMac::HardwareSampleRate() { 415 // Determine the default output device's sample-rate. 416 AudioDeviceID device_id = kAudioObjectUnknown; 417 if (!GetDefaultOutputDevice(&device_id)) 418 return kFallbackSampleRate; 419 420 return HardwareSampleRateForDevice(device_id); 421 } 422 423 void AudioManagerMac::GetAudioInputDeviceNames( 424 media::AudioDeviceNames* device_names) { 425 DCHECK(device_names->empty()); 426 GetAudioDeviceInfo(true, device_names); 427 } 428 429 void AudioManagerMac::GetAudioOutputDeviceNames( 430 media::AudioDeviceNames* device_names) { 431 DCHECK(device_names->empty()); 432 GetAudioDeviceInfo(false, device_names); 433 } 434 435 AudioParameters AudioManagerMac::GetInputStreamParameters( 436 const std::string& device_id) { 437 AudioDeviceID device = GetAudioDeviceIdByUId(true, device_id); 438 if (device == kAudioObjectUnknown) { 439 DLOG(ERROR) << "Invalid device " << device_id; 440 return AudioParameters( 441 AudioParameters::AUDIO_PCM_LOW_LATENCY, CHANNEL_LAYOUT_STEREO, 442 kFallbackSampleRate, 16, ChooseBufferSize(true, kFallbackSampleRate)); 443 } 444 445 int channels = 0; 446 ChannelLayout channel_layout = CHANNEL_LAYOUT_STEREO; 447 if (GetDeviceChannels(device, kAudioDevicePropertyScopeInput, &channels) && 448 channels <= 2) { 449 channel_layout = GuessChannelLayout(channels); 450 } else { 451 DLOG(ERROR) << "Failed to get the device channels, use stereo as default " 452 << "for device " << device_id; 453 } 454 455 int sample_rate = HardwareSampleRateForDevice(device); 456 if (!sample_rate) 457 sample_rate = kFallbackSampleRate; 458 459 // Due to the sharing of the input and output buffer sizes, we need to choose 460 // the input buffer size based on the output sample rate. See 461 // http://crbug.com/154352. 462 const int buffer_size = ChooseBufferSize(true, sample_rate); 463 464 // TODO(xians): query the native channel layout for the specific device. 465 return AudioParameters( 466 AudioParameters::AUDIO_PCM_LOW_LATENCY, channel_layout, 467 sample_rate, 16, buffer_size); 468 } 469 470 std::string AudioManagerMac::GetAssociatedOutputDeviceID( 471 const std::string& input_device_id) { 472 AudioDeviceID device = GetAudioDeviceIdByUId(true, input_device_id); 473 if (device == kAudioObjectUnknown) 474 return std::string(); 475 476 UInt32 size = 0; 477 AudioObjectPropertyAddress pa = { 478 kAudioDevicePropertyRelatedDevices, 479 kAudioDevicePropertyScopeOutput, 480 kAudioObjectPropertyElementMaster 481 }; 482 OSStatus result = AudioObjectGetPropertyDataSize(device, &pa, 0, 0, &size); 483 if (result || !size) 484 return std::string(); 485 486 int device_count = size / sizeof(AudioDeviceID); 487 scoped_ptr<AudioDeviceID, base::FreeDeleter> 488 devices(static_cast<AudioDeviceID*>(malloc(size))); 489 result = AudioObjectGetPropertyData( 490 device, &pa, 0, NULL, &size, devices.get()); 491 if (result) 492 return std::string(); 493 494 std::vector<std::string> associated_devices; 495 for (int i = 0; i < device_count; ++i) { 496 // Get the number of output channels of the device. 497 pa.mSelector = kAudioDevicePropertyStreams; 498 size = 0; 499 result = AudioObjectGetPropertyDataSize(devices.get()[i], 500 &pa, 501 0, 502 NULL, 503 &size); 504 if (result || !size) 505 continue; // Skip if there aren't any output channels. 506 507 // Get device UID. 508 CFStringRef uid = NULL; 509 size = sizeof(uid); 510 pa.mSelector = kAudioDevicePropertyDeviceUID; 511 result = AudioObjectGetPropertyData(devices.get()[i], 512 &pa, 513 0, 514 NULL, 515 &size, 516 &uid); 517 if (result || !uid) 518 continue; 519 520 std::string ret(base::SysCFStringRefToUTF8(uid)); 521 CFRelease(uid); 522 associated_devices.push_back(ret); 523 } 524 525 // No matching device found. 526 if (associated_devices.empty()) 527 return std::string(); 528 529 // Return the device if there is only one associated device. 530 if (associated_devices.size() == 1) 531 return associated_devices[0]; 532 533 // When there are multiple associated devices, we currently do not have a way 534 // to detect if a device (e.g. a digital output device) is actually connected 535 // to an endpoint, so we cannot randomly pick a device. 536 // We pick the device iff the associated device is the default output device. 537 const std::string default_device = GetDefaultOutputDeviceID(); 538 for (std::vector<std::string>::const_iterator iter = 539 associated_devices.begin(); 540 iter != associated_devices.end(); ++iter) { 541 if (default_device == *iter) 542 return *iter; 543 } 544 545 // Failed to figure out which is the matching device, return an emtpy string. 546 return std::string(); 547 } 548 549 AudioOutputStream* AudioManagerMac::MakeLinearOutputStream( 550 const AudioParameters& params) { 551 return MakeLowLatencyOutputStream(params, std::string()); 552 } 553 554 AudioOutputStream* AudioManagerMac::MakeLowLatencyOutputStream( 555 const AudioParameters& params, 556 const std::string& device_id) { 557 AudioDeviceID device = GetAudioDeviceIdByUId(false, device_id); 558 if (device == kAudioObjectUnknown) { 559 DLOG(ERROR) << "Failed to open output device: " << device_id; 560 return NULL; 561 } 562 563 // Lazily create the audio device listener on the first stream creation. 564 if (!output_device_listener_) { 565 // NOTE: Use BindToCurrentLoop() to ensure the callback is always PostTask'd 566 // even if OSX calls us on the right thread. Some CoreAudio drivers will 567 // fire the callbacks during stream creation, leading to re-entrancy issues 568 // otherwise. See http://crbug.com/349604 569 output_device_listener_.reset( 570 new AudioDeviceListenerMac(BindToCurrentLoop(base::Bind( 571 &AudioManagerMac::HandleDeviceChanges, base::Unretained(this))))); 572 // Only set the current output device for the default device. 573 if (device_id == AudioManagerBase::kDefaultDeviceId || device_id.empty()) 574 current_output_device_ = device; 575 // Just use the current sample rate since we don't allow non-native sample 576 // rates on OSX. 577 current_sample_rate_ = params.sample_rate(); 578 } 579 580 AudioOutputStream* stream = new AUHALStream(this, params, device); 581 output_streams_.push_back(stream); 582 return stream; 583 } 584 585 std::string AudioManagerMac::GetDefaultOutputDeviceID() { 586 AudioDeviceID device_id = kAudioObjectUnknown; 587 if (!GetDefaultOutputDevice(&device_id)) 588 return std::string(); 589 590 const AudioObjectPropertyAddress property_address = { 591 kAudioDevicePropertyDeviceUID, 592 kAudioObjectPropertyScopeGlobal, 593 kAudioObjectPropertyElementMaster 594 }; 595 CFStringRef device_uid = NULL; 596 UInt32 size = sizeof(device_uid); 597 OSStatus status = AudioObjectGetPropertyData(device_id, 598 &property_address, 599 0, 600 NULL, 601 &size, 602 &device_uid); 603 if (status != kAudioHardwareNoError || !device_uid) 604 return std::string(); 605 606 std::string ret(base::SysCFStringRefToUTF8(device_uid)); 607 CFRelease(device_uid); 608 609 return ret; 610 } 611 612 AudioInputStream* AudioManagerMac::MakeLinearInputStream( 613 const AudioParameters& params, const std::string& device_id) { 614 DCHECK_EQ(AudioParameters::AUDIO_PCM_LINEAR, params.format()); 615 AudioInputStream* stream = new PCMQueueInAudioInputStream(this, params); 616 input_streams_.push_back(stream); 617 return stream; 618 } 619 620 AudioInputStream* AudioManagerMac::MakeLowLatencyInputStream( 621 const AudioParameters& params, const std::string& device_id) { 622 DCHECK_EQ(AudioParameters::AUDIO_PCM_LOW_LATENCY, params.format()); 623 // Gets the AudioDeviceID that refers to the AudioInputDevice with the device 624 // unique id. This AudioDeviceID is used to set the device for Audio Unit. 625 AudioDeviceID audio_device_id = GetAudioDeviceIdByUId(true, device_id); 626 AudioInputStream* stream = NULL; 627 if (audio_device_id != kAudioObjectUnknown) { 628 stream = new AUAudioInputStream(this, params, audio_device_id); 629 input_streams_.push_back(stream); 630 } 631 632 return stream; 633 } 634 635 AudioParameters AudioManagerMac::GetPreferredOutputStreamParameters( 636 const std::string& output_device_id, 637 const AudioParameters& input_params) { 638 const AudioDeviceID device = GetAudioDeviceIdByUId(false, output_device_id); 639 if (device == kAudioObjectUnknown) { 640 DLOG(ERROR) << "Invalid output device " << output_device_id; 641 return input_params.IsValid() ? input_params : AudioParameters( 642 AudioParameters::AUDIO_PCM_LOW_LATENCY, CHANNEL_LAYOUT_STEREO, 643 kFallbackSampleRate, 16, ChooseBufferSize(false, kFallbackSampleRate)); 644 } 645 646 const bool has_valid_input_params = input_params.IsValid(); 647 const int hardware_sample_rate = HardwareSampleRateForDevice(device); 648 649 // Allow pass through buffer sizes. If concurrent input and output streams 650 // exist, they will use the smallest buffer size amongst them. As such, each 651 // stream must be able to FIFO requests appropriately when this happens. 652 int buffer_size = ChooseBufferSize(false, hardware_sample_rate); 653 if (has_valid_input_params) { 654 buffer_size = 655 std::min(kMaximumInputOutputBufferSize, 656 std::max(input_params.frames_per_buffer(), buffer_size)); 657 } 658 659 int hardware_channels; 660 if (!GetDeviceChannels(device, kAudioDevicePropertyScopeOutput, 661 &hardware_channels)) { 662 hardware_channels = 2; 663 } 664 665 // Use the input channel count and channel layout if possible. Let OSX take 666 // care of remapping the channels; this lets user specified channel layouts 667 // work correctly. 668 int output_channels = input_params.channels(); 669 ChannelLayout channel_layout = input_params.channel_layout(); 670 if (!has_valid_input_params || output_channels > hardware_channels) { 671 output_channels = hardware_channels; 672 channel_layout = GuessChannelLayout(output_channels); 673 if (channel_layout == CHANNEL_LAYOUT_UNSUPPORTED) 674 channel_layout = CHANNEL_LAYOUT_DISCRETE; 675 } 676 677 return AudioParameters( 678 AudioParameters::AUDIO_PCM_LOW_LATENCY, channel_layout, output_channels, 679 hardware_sample_rate, 16, buffer_size, AudioParameters::NO_EFFECTS); 680 } 681 682 void AudioManagerMac::InitializeOnAudioThread() { 683 DCHECK(GetTaskRunner()->BelongsToCurrentThread()); 684 power_observer_.reset(new AudioPowerObserver()); 685 } 686 687 void AudioManagerMac::ShutdownOnAudioThread() { 688 DCHECK(GetTaskRunner()->BelongsToCurrentThread()); 689 output_device_listener_.reset(); 690 power_observer_.reset(); 691 692 // Since CoreAudio calls have to run on the UI thread and browser shutdown 693 // doesn't wait for outstanding tasks to complete, we may have input/output 694 // streams still running at shutdown. 695 // 696 // To avoid calls into destructed classes, we need to stop the OS callbacks 697 // by stopping the streams. Note: The streams are leaked since process 698 // destruction is imminent. 699 // 700 // See http://crbug.com/354139 for crash details. 701 StopStreams(&input_streams_); 702 StopStreams(&output_streams_); 703 } 704 705 void AudioManagerMac::HandleDeviceChanges() { 706 DCHECK(GetTaskRunner()->BelongsToCurrentThread()); 707 const int new_sample_rate = HardwareSampleRate(); 708 AudioDeviceID new_output_device; 709 GetDefaultOutputDevice(&new_output_device); 710 711 if (current_sample_rate_ == new_sample_rate && 712 current_output_device_ == new_output_device) 713 return; 714 715 current_sample_rate_ = new_sample_rate; 716 current_output_device_ = new_output_device; 717 NotifyAllOutputDeviceChangeListeners(); 718 } 719 720 int AudioManagerMac::ChooseBufferSize(bool is_input, int sample_rate) { 721 // kMinimumInputOutputBufferSize is too small for the output side because 722 // CoreAudio can get into under-run if the renderer fails delivering data 723 // to the browser within the allowed time by the OS. The workaround is to 724 // use 256 samples as the default output buffer size for sample rates 725 // smaller than 96KHz. 726 // TODO(xians): Remove this workaround after WebAudio supports user defined 727 // buffer size. See https://github.com/WebAudio/web-audio-api/issues/348 728 // for details. 729 int buffer_size = is_input ? 730 kMinimumInputOutputBufferSize : 2 * kMinimumInputOutputBufferSize; 731 const int user_buffer_size = GetUserBufferSize(); 732 if (user_buffer_size) { 733 buffer_size = user_buffer_size; 734 } else if (sample_rate > 48000) { 735 // The default buffer size is too small for higher sample rates and may lead 736 // to glitching. Adjust upwards by multiples of the default size. 737 if (sample_rate <= 96000) 738 buffer_size = 2 * kMinimumInputOutputBufferSize; 739 else if (sample_rate <= 192000) 740 buffer_size = 4 * kMinimumInputOutputBufferSize; 741 } 742 743 return buffer_size; 744 } 745 746 bool AudioManagerMac::ShouldDeferStreamStart() { 747 DCHECK(GetTaskRunner()->BelongsToCurrentThread()); 748 return power_observer_->ShouldDeferStreamStart(); 749 } 750 751 void AudioManagerMac::ReleaseOutputStream(AudioOutputStream* stream) { 752 output_streams_.remove(stream); 753 AudioManagerBase::ReleaseOutputStream(stream); 754 } 755 756 void AudioManagerMac::ReleaseInputStream(AudioInputStream* stream) { 757 input_streams_.remove(stream); 758 AudioManagerBase::ReleaseInputStream(stream); 759 } 760 761 AudioManager* CreateAudioManager(AudioLogFactory* audio_log_factory) { 762 return new AudioManagerMac(audio_log_factory); 763 } 764 765 } // namespace media 766