audio_manager_mac.cc revision 0529e5d033099cbfc42635f6f6183833b09dff6e
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 27namespace media { 28 29// Maximum number of output streams that can be open simultaneously. 30static const int kMaxOutputStreams = 50; 31 32// Define bounds for for low-latency input and output streams. 33static const int kMinimumInputOutputBufferSize = 128; 34static const int kMaximumInputOutputBufferSize = 4096; 35 36// Default sample-rate on most Apple hardware. 37static const int kFallbackSampleRate = 44100; 38 39static 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. 59static 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 163static 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 214template <class T> 215void 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 226class 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 274AudioManagerMac::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 287AudioManagerMac::~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 300bool AudioManagerMac::HasAudioOutputDevices() { 301 return HasAudioHardware(kAudioHardwarePropertyDefaultOutputDevice); 302} 303 304bool 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. 310bool AudioManagerMac::GetDefaultInputDevice( 311 AudioDeviceID* device) { 312 return GetDefaultDevice(device, true); 313} 314 315bool AudioManagerMac::GetDefaultOutputDevice( 316 AudioDeviceID* device) { 317 return GetDefaultDevice(device, false); 318} 319 320bool AudioManagerMac::GetDefaultDevice( 321 AudioDeviceID* device, bool input) { 322 CHECK(device); 323 324 // Obtain the current output device selected by the user. 325 AudioObjectPropertyAddress pa; 326 pa.mSelector = input ? kAudioHardwarePropertyDefaultInputDevice : 327 kAudioHardwarePropertyDefaultOutputDevice; 328 pa.mScope = kAudioObjectPropertyScopeGlobal; 329 pa.mElement = kAudioObjectPropertyElementMaster; 330 331 UInt32 size = sizeof(*device); 332 333 OSStatus result = AudioObjectGetPropertyData( 334 kAudioObjectSystemObject, 335 &pa, 336 0, 337 0, 338 &size, 339 device); 340 341 if ((result != kAudioHardwareNoError) || (*device == kAudioDeviceUnknown)) { 342 DLOG(ERROR) << "Error getting default AudioDevice."; 343 return false; 344 } 345 346 return true; 347} 348 349bool AudioManagerMac::GetDefaultOutputChannels( 350 int* channels) { 351 AudioDeviceID device; 352 if (!GetDefaultOutputDevice(&device)) 353 return false; 354 355 return GetDeviceChannels(device, 356 kAudioDevicePropertyScopeOutput, 357 channels); 358} 359 360bool AudioManagerMac::GetDeviceChannels( 361 AudioDeviceID device, 362 AudioObjectPropertyScope scope, 363 int* channels) { 364 CHECK(channels); 365 366 // Get stream configuration. 367 AudioObjectPropertyAddress pa; 368 pa.mSelector = kAudioDevicePropertyStreamConfiguration; 369 pa.mScope = scope; 370 pa.mElement = kAudioObjectPropertyElementMaster; 371 372 UInt32 size; 373 OSStatus result = AudioObjectGetPropertyDataSize(device, &pa, 0, 0, &size); 374 if (result != noErr || !size) 375 return false; 376 377 // Allocate storage. 378 scoped_ptr<uint8[]> list_storage(new uint8[size]); 379 AudioBufferList& buffer_list = 380 *reinterpret_cast<AudioBufferList*>(list_storage.get()); 381 382 result = AudioObjectGetPropertyData( 383 device, 384 &pa, 385 0, 386 0, 387 &size, 388 &buffer_list); 389 if (result != noErr) 390 return false; 391 392 // Determine number of input channels. 393 int channels_per_frame = buffer_list.mNumberBuffers > 0 ? 394 buffer_list.mBuffers[0].mNumberChannels : 0; 395 if (channels_per_frame == 1 && buffer_list.mNumberBuffers > 1) { 396 // Non-interleaved. 397 *channels = buffer_list.mNumberBuffers; 398 } else { 399 // Interleaved. 400 *channels = channels_per_frame; 401 } 402 403 return true; 404} 405 406int AudioManagerMac::HardwareSampleRateForDevice(AudioDeviceID device_id) { 407 Float64 nominal_sample_rate; 408 UInt32 info_size = sizeof(nominal_sample_rate); 409 410 static const AudioObjectPropertyAddress kNominalSampleRateAddress = { 411 kAudioDevicePropertyNominalSampleRate, 412 kAudioObjectPropertyScopeGlobal, 413 kAudioObjectPropertyElementMaster 414 }; 415 OSStatus result = AudioObjectGetPropertyData( 416 device_id, 417 &kNominalSampleRateAddress, 418 0, 419 0, 420 &info_size, 421 &nominal_sample_rate); 422 if (result != noErr) { 423 OSSTATUS_DLOG(WARNING, result) 424 << "Could not get default sample rate for device: " << device_id; 425 return 0; 426 } 427 428 return static_cast<int>(nominal_sample_rate); 429} 430 431int AudioManagerMac::HardwareSampleRate() { 432 // Determine the default output device's sample-rate. 433 AudioDeviceID device_id = kAudioObjectUnknown; 434 if (!GetDefaultOutputDevice(&device_id)) 435 return kFallbackSampleRate; 436 437 return HardwareSampleRateForDevice(device_id); 438} 439 440void AudioManagerMac::GetAudioInputDeviceNames( 441 media::AudioDeviceNames* device_names) { 442 DCHECK(device_names->empty()); 443 GetAudioDeviceInfo(true, device_names); 444} 445 446void AudioManagerMac::GetAudioOutputDeviceNames( 447 media::AudioDeviceNames* device_names) { 448 DCHECK(device_names->empty()); 449 GetAudioDeviceInfo(false, device_names); 450} 451 452AudioParameters AudioManagerMac::GetInputStreamParameters( 453 const std::string& device_id) { 454 AudioDeviceID device = GetAudioDeviceIdByUId(true, device_id); 455 if (device == kAudioObjectUnknown) { 456 DLOG(ERROR) << "Invalid device " << device_id; 457 return AudioParameters( 458 AudioParameters::AUDIO_PCM_LOW_LATENCY, CHANNEL_LAYOUT_STEREO, 459 kFallbackSampleRate, 16, ChooseBufferSize(kFallbackSampleRate)); 460 } 461 462 int channels = 0; 463 ChannelLayout channel_layout = CHANNEL_LAYOUT_STEREO; 464 if (GetDeviceChannels(device, kAudioDevicePropertyScopeInput, &channels) && 465 channels <= 2) { 466 channel_layout = GuessChannelLayout(channels); 467 } else { 468 DLOG(ERROR) << "Failed to get the device channels, use stereo as default " 469 << "for device " << device_id; 470 } 471 472 int sample_rate = HardwareSampleRateForDevice(device); 473 if (!sample_rate) 474 sample_rate = kFallbackSampleRate; 475 476 // Due to the sharing of the input and output buffer sizes, we need to choose 477 // the input buffer size based on the output sample rate. See 478 // http://crbug.com/154352. 479 const int buffer_size = ChooseBufferSize(sample_rate); 480 481 // TODO(xians): query the native channel layout for the specific device. 482 return AudioParameters( 483 AudioParameters::AUDIO_PCM_LOW_LATENCY, channel_layout, 484 sample_rate, 16, buffer_size); 485} 486 487std::string AudioManagerMac::GetAssociatedOutputDeviceID( 488 const std::string& input_device_id) { 489 AudioDeviceID device = GetAudioDeviceIdByUId(true, input_device_id); 490 if (device == kAudioObjectUnknown) 491 return std::string(); 492 493 UInt32 size = 0; 494 AudioObjectPropertyAddress pa = { 495 kAudioDevicePropertyRelatedDevices, 496 kAudioDevicePropertyScopeOutput, 497 kAudioObjectPropertyElementMaster 498 }; 499 OSStatus result = AudioObjectGetPropertyDataSize(device, &pa, 0, 0, &size); 500 if (result || !size) 501 return std::string(); 502 503 int device_count = size / sizeof(AudioDeviceID); 504 scoped_ptr<AudioDeviceID, base::FreeDeleter> 505 devices(static_cast<AudioDeviceID*>(malloc(size))); 506 result = AudioObjectGetPropertyData( 507 device, &pa, 0, NULL, &size, devices.get()); 508 if (result) 509 return std::string(); 510 511 std::vector<std::string> associated_devices; 512 for (int i = 0; i < device_count; ++i) { 513 // Get the number of output channels of the device. 514 pa.mSelector = kAudioDevicePropertyStreams; 515 size = 0; 516 result = AudioObjectGetPropertyDataSize(devices.get()[i], 517 &pa, 518 0, 519 NULL, 520 &size); 521 if (result || !size) 522 continue; // Skip if there aren't any output channels. 523 524 // Get device UID. 525 CFStringRef uid = NULL; 526 size = sizeof(uid); 527 pa.mSelector = kAudioDevicePropertyDeviceUID; 528 result = AudioObjectGetPropertyData(devices.get()[i], 529 &pa, 530 0, 531 NULL, 532 &size, 533 &uid); 534 if (result || !uid) 535 continue; 536 537 std::string ret(base::SysCFStringRefToUTF8(uid)); 538 CFRelease(uid); 539 associated_devices.push_back(ret); 540 } 541 542 // No matching device found. 543 if (associated_devices.empty()) 544 return std::string(); 545 546 // Return the device if there is only one associated device. 547 if (associated_devices.size() == 1) 548 return associated_devices[0]; 549 550 // When there are multiple associated devices, we currently do not have a way 551 // to detect if a device (e.g. a digital output device) is actually connected 552 // to an endpoint, so we cannot randomly pick a device. 553 // We pick the device iff the associated device is the default output device. 554 const std::string default_device = GetDefaultOutputDeviceID(); 555 for (std::vector<std::string>::const_iterator iter = 556 associated_devices.begin(); 557 iter != associated_devices.end(); ++iter) { 558 if (default_device == *iter) 559 return *iter; 560 } 561 562 // Failed to figure out which is the matching device, return an emtpy string. 563 return std::string(); 564} 565 566AudioOutputStream* AudioManagerMac::MakeLinearOutputStream( 567 const AudioParameters& params) { 568 return MakeLowLatencyOutputStream(params, std::string()); 569} 570 571AudioOutputStream* AudioManagerMac::MakeLowLatencyOutputStream( 572 const AudioParameters& params, 573 const std::string& device_id) { 574 AudioDeviceID device = GetAudioDeviceIdByUId(false, device_id); 575 if (device == kAudioObjectUnknown) { 576 DLOG(ERROR) << "Failed to open output device: " << device_id; 577 return NULL; 578 } 579 580 // Lazily create the audio device listener on the first stream creation. 581 if (!output_device_listener_) { 582 // NOTE: Use BindToCurrentLoop() to ensure the callback is always PostTask'd 583 // even if OSX calls us on the right thread. Some CoreAudio drivers will 584 // fire the callbacks during stream creation, leading to re-entrancy issues 585 // otherwise. See http://crbug.com/349604 586 output_device_listener_.reset( 587 new AudioDeviceListenerMac(BindToCurrentLoop(base::Bind( 588 &AudioManagerMac::HandleDeviceChanges, base::Unretained(this))))); 589 // Only set the current output device for the default device. 590 if (device_id == AudioManagerBase::kDefaultDeviceId || device_id.empty()) 591 current_output_device_ = device; 592 // Just use the current sample rate since we don't allow non-native sample 593 // rates on OSX. 594 current_sample_rate_ = params.sample_rate(); 595 } 596 597 AudioOutputStream* stream = new AUHALStream(this, params, device); 598 output_streams_.push_back(stream); 599 return stream; 600} 601 602std::string AudioManagerMac::GetDefaultOutputDeviceID() { 603 AudioDeviceID device_id = kAudioObjectUnknown; 604 if (!GetDefaultOutputDevice(&device_id)) 605 return std::string(); 606 607 const AudioObjectPropertyAddress property_address = { 608 kAudioDevicePropertyDeviceUID, 609 kAudioObjectPropertyScopeGlobal, 610 kAudioObjectPropertyElementMaster 611 }; 612 CFStringRef device_uid = NULL; 613 UInt32 size = sizeof(device_uid); 614 OSStatus status = AudioObjectGetPropertyData(device_id, 615 &property_address, 616 0, 617 NULL, 618 &size, 619 &device_uid); 620 if (status != kAudioHardwareNoError || !device_uid) 621 return std::string(); 622 623 std::string ret(base::SysCFStringRefToUTF8(device_uid)); 624 CFRelease(device_uid); 625 626 return ret; 627} 628 629AudioInputStream* AudioManagerMac::MakeLinearInputStream( 630 const AudioParameters& params, const std::string& device_id) { 631 DCHECK_EQ(AudioParameters::AUDIO_PCM_LINEAR, params.format()); 632 AudioInputStream* stream = new PCMQueueInAudioInputStream(this, params); 633 input_streams_.push_back(stream); 634 return stream; 635} 636 637AudioInputStream* AudioManagerMac::MakeLowLatencyInputStream( 638 const AudioParameters& params, const std::string& device_id) { 639 DCHECK_EQ(AudioParameters::AUDIO_PCM_LOW_LATENCY, params.format()); 640 // Gets the AudioDeviceID that refers to the AudioInputDevice with the device 641 // unique id. This AudioDeviceID is used to set the device for Audio Unit. 642 AudioDeviceID audio_device_id = GetAudioDeviceIdByUId(true, device_id); 643 AudioInputStream* stream = NULL; 644 if (audio_device_id != kAudioObjectUnknown) { 645 // AUAudioInputStream needs to be fed the preferred audio output parameters 646 // of the matching device so that the buffer size of both input and output 647 // can be matched. See constructor of AUAudioInputStream for more. 648 const std::string associated_output_device( 649 GetAssociatedOutputDeviceID(device_id)); 650 const AudioParameters output_params = 651 GetPreferredOutputStreamParameters( 652 associated_output_device.empty() ? 653 AudioManagerBase::kDefaultDeviceId : associated_output_device, 654 params); 655 stream = new AUAudioInputStream(this, params, output_params, 656 audio_device_id); 657 input_streams_.push_back(stream); 658 } 659 660 return stream; 661} 662 663AudioParameters AudioManagerMac::GetPreferredOutputStreamParameters( 664 const std::string& output_device_id, 665 const AudioParameters& input_params) { 666 const AudioDeviceID device = GetAudioDeviceIdByUId(false, output_device_id); 667 if (device == kAudioObjectUnknown) { 668 DLOG(ERROR) << "Invalid output device " << output_device_id; 669 return input_params.IsValid() ? input_params : AudioParameters( 670 AudioParameters::AUDIO_PCM_LOW_LATENCY, CHANNEL_LAYOUT_STEREO, 671 kFallbackSampleRate, 16, ChooseBufferSize(kFallbackSampleRate)); 672 } 673 674 const bool has_valid_input_params = input_params.IsValid(); 675 const int hardware_sample_rate = HardwareSampleRateForDevice(device); 676 677 // Allow pass through buffer sizes. If concurrent input and output streams 678 // exist, they will use the smallest buffer size amongst them. As such, each 679 // stream must be able to FIFO requests appropriately when this happens. 680 int buffer_size = ChooseBufferSize(hardware_sample_rate); 681 if (has_valid_input_params) { 682 buffer_size = 683 std::min(kMaximumInputOutputBufferSize, 684 std::max(input_params.frames_per_buffer(), buffer_size)); 685 } 686 687 int hardware_channels; 688 if (!GetDeviceChannels(device, kAudioDevicePropertyScopeOutput, 689 &hardware_channels)) { 690 hardware_channels = 2; 691 } 692 693 // Use the input channel count and channel layout if possible. Let OSX take 694 // care of remapping the channels; this lets user specified channel layouts 695 // work correctly. 696 int output_channels = input_params.channels(); 697 ChannelLayout channel_layout = input_params.channel_layout(); 698 if (!has_valid_input_params || output_channels > hardware_channels) { 699 output_channels = hardware_channels; 700 channel_layout = GuessChannelLayout(output_channels); 701 if (channel_layout == CHANNEL_LAYOUT_UNSUPPORTED) 702 channel_layout = CHANNEL_LAYOUT_DISCRETE; 703 } 704 705 const int input_channels = 706 has_valid_input_params ? input_params.input_channels() : 0; 707 if (input_channels > 0) { 708 // TODO(xians): given the limitations of the AudioOutputStream 709 // back-ends used with synchronized I/O, we hard-code to stereo. 710 // Specifically, this is a limitation of AudioSynchronizedStream which 711 // can be removed as part of the work to consolidate these back-ends. 712 channel_layout = CHANNEL_LAYOUT_STEREO; 713 } 714 715 return AudioParameters( 716 AudioParameters::AUDIO_PCM_LOW_LATENCY, channel_layout, output_channels, 717 input_channels, hardware_sample_rate, 16, buffer_size, 718 AudioParameters::NO_EFFECTS); 719} 720 721void AudioManagerMac::InitializeOnAudioThread() { 722 DCHECK(GetTaskRunner()->BelongsToCurrentThread()); 723 power_observer_.reset(new AudioPowerObserver()); 724} 725 726void AudioManagerMac::ShutdownOnAudioThread() { 727 DCHECK(GetTaskRunner()->BelongsToCurrentThread()); 728 output_device_listener_.reset(); 729 power_observer_.reset(); 730 731 // Since CoreAudio calls have to run on the UI thread and browser shutdown 732 // doesn't wait for outstanding tasks to complete, we may have input/output 733 // streams still running at shutdown. 734 // 735 // To avoid calls into destructed classes, we need to stop the OS callbacks 736 // by stopping the streams. Note: The streams are leaked since process 737 // destruction is imminent. 738 // 739 // See http://crbug.com/354139 for crash details. 740 StopStreams(&input_streams_); 741 StopStreams(&output_streams_); 742} 743 744void AudioManagerMac::HandleDeviceChanges() { 745 DCHECK(GetTaskRunner()->BelongsToCurrentThread()); 746 const int new_sample_rate = HardwareSampleRate(); 747 AudioDeviceID new_output_device; 748 GetDefaultOutputDevice(&new_output_device); 749 750 if (current_sample_rate_ == new_sample_rate && 751 current_output_device_ == new_output_device) 752 return; 753 754 current_sample_rate_ = new_sample_rate; 755 current_output_device_ = new_output_device; 756 NotifyAllOutputDeviceChangeListeners(); 757} 758 759int AudioManagerMac::ChooseBufferSize(int output_sample_rate) { 760 int buffer_size = kMinimumInputOutputBufferSize; 761 const int user_buffer_size = GetUserBufferSize(); 762 if (user_buffer_size) { 763 buffer_size = user_buffer_size; 764 } else if (output_sample_rate > 48000) { 765 // The default buffer size is too small for higher sample rates and may lead 766 // to glitching. Adjust upwards by multiples of the default size. 767 if (output_sample_rate <= 96000) 768 buffer_size = 2 * kMinimumInputOutputBufferSize; 769 else if (output_sample_rate <= 192000) 770 buffer_size = 4 * kMinimumInputOutputBufferSize; 771 } 772 773 return buffer_size; 774} 775 776bool AudioManagerMac::ShouldDeferStreamStart() { 777 DCHECK(GetTaskRunner()->BelongsToCurrentThread()); 778 return power_observer_->ShouldDeferStreamStart(); 779} 780 781void AudioManagerMac::ReleaseOutputStream(AudioOutputStream* stream) { 782 output_streams_.remove(stream); 783 AudioManagerBase::ReleaseOutputStream(stream); 784} 785 786void AudioManagerMac::ReleaseInputStream(AudioInputStream* stream) { 787 input_streams_.remove(stream); 788 AudioManagerBase::ReleaseInputStream(stream); 789} 790 791AudioManager* CreateAudioManager(AudioLogFactory* audio_log_factory) { 792 return new AudioManagerMac(audio_log_factory); 793} 794 795} // namespace media 796