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