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