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