OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "media/audio/mac/audio_manager_mac.h" | 5 #include "media/audio/mac/audio_manager_mac.h" |
6 | 6 |
7 #include <CoreAudio/AudioHardware.h> | 7 #include <CoreAudio/AudioHardware.h> |
8 #include <string> | 8 #include <string> |
9 | 9 |
10 #include "base/bind.h" | 10 #include "base/bind.h" |
(...skipping 168 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
179 const std::string& device_id) { | 179 const std::string& device_id) { |
180 AudioObjectPropertyAddress property_address = { | 180 AudioObjectPropertyAddress property_address = { |
181 kAudioHardwarePropertyDevices, | 181 kAudioHardwarePropertyDevices, |
182 kAudioObjectPropertyScopeGlobal, | 182 kAudioObjectPropertyScopeGlobal, |
183 kAudioObjectPropertyElementMaster | 183 kAudioObjectPropertyElementMaster |
184 }; | 184 }; |
185 AudioDeviceID audio_device_id = kAudioObjectUnknown; | 185 AudioDeviceID audio_device_id = kAudioObjectUnknown; |
186 UInt32 device_size = sizeof(audio_device_id); | 186 UInt32 device_size = sizeof(audio_device_id); |
187 OSStatus result = -1; | 187 OSStatus result = -1; |
188 | 188 |
189 if (device_id == AudioManagerBase::kDefaultDeviceId) { | 189 if (device_id == AudioManagerBase::kDefaultDeviceId || device_id.empty()) { |
190 // Default Device. | 190 // Default Device. |
191 property_address.mSelector = is_input ? | 191 property_address.mSelector = is_input ? |
192 kAudioHardwarePropertyDefaultInputDevice : | 192 kAudioHardwarePropertyDefaultInputDevice : |
193 kAudioHardwarePropertyDefaultOutputDevice; | 193 kAudioHardwarePropertyDefaultOutputDevice; |
194 | 194 |
195 result = AudioObjectGetPropertyData(kAudioObjectSystemObject, | 195 result = AudioObjectGetPropertyData(kAudioObjectSystemObject, |
196 &property_address, | 196 &property_address, |
197 0, | 197 0, |
198 0, | 198 0, |
199 &device_size, | 199 &device_size, |
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
253 } | 253 } |
254 | 254 |
255 bool AudioManagerMac::HasAudioOutputDevices() { | 255 bool AudioManagerMac::HasAudioOutputDevices() { |
256 return HasAudioHardware(kAudioHardwarePropertyDefaultOutputDevice); | 256 return HasAudioHardware(kAudioHardwarePropertyDefaultOutputDevice); |
257 } | 257 } |
258 | 258 |
259 bool AudioManagerMac::HasAudioInputDevices() { | 259 bool AudioManagerMac::HasAudioInputDevices() { |
260 return HasAudioHardware(kAudioHardwarePropertyDefaultInputDevice); | 260 return HasAudioHardware(kAudioHardwarePropertyDefaultInputDevice); |
261 } | 261 } |
262 | 262 |
263 // TODO(crogers): There are several places on the OSX specific code which | 263 // TODO(xians): There are several places on the OSX specific code which |
264 // could benefit from these helper functions. | 264 // could benefit from these helper functions. |
265 bool AudioManagerMac::GetDefaultInputDevice( | 265 bool AudioManagerMac::GetDefaultInputDevice( |
266 AudioDeviceID* device) { | 266 AudioDeviceID* device) { |
267 return GetDefaultDevice(device, true); | 267 return GetDefaultDevice(device, true); |
268 } | 268 } |
269 | 269 |
270 bool AudioManagerMac::GetDefaultOutputDevice( | 270 bool AudioManagerMac::GetDefaultOutputDevice( |
271 AudioDeviceID* device) { | 271 AudioDeviceID* device) { |
272 return GetDefaultDevice(device, false); | 272 return GetDefaultDevice(device, false); |
273 } | 273 } |
(...skipping 160 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
434 int sample_rate = HardwareSampleRateForDevice(device); | 434 int sample_rate = HardwareSampleRateForDevice(device); |
435 if (!sample_rate) | 435 if (!sample_rate) |
436 sample_rate = kFallbackSampleRate; | 436 sample_rate = kFallbackSampleRate; |
437 | 437 |
438 // TODO(xians): query the native channel layout for the specific device. | 438 // TODO(xians): query the native channel layout for the specific device. |
439 return AudioParameters( | 439 return AudioParameters( |
440 AudioParameters::AUDIO_PCM_LOW_LATENCY, channel_layout, | 440 AudioParameters::AUDIO_PCM_LOW_LATENCY, channel_layout, |
441 sample_rate, 16, buffer_size); | 441 sample_rate, 16, buffer_size); |
442 } | 442 } |
443 | 443 |
| 444 std::string AudioManagerMac::GetAssociatedOutputDeviceID( |
| 445 const std::string& input_device_id) { |
| 446 AudioDeviceID device = GetAudioDeviceIdByUId(true, input_device_id); |
| 447 if (device == kAudioObjectUnknown) |
| 448 return std::string(); |
| 449 |
| 450 UInt32 size = 0; |
| 451 AudioObjectPropertyAddress pa = { |
| 452 kAudioDevicePropertyRelatedDevices, |
| 453 kAudioDevicePropertyScopeOutput, |
| 454 kAudioObjectPropertyElementMaster |
| 455 }; |
| 456 OSStatus result = AudioObjectGetPropertyDataSize(device, &pa, 0, 0, &size); |
| 457 if (result || !size) |
| 458 return std::string(); |
| 459 |
| 460 int device_count = size / sizeof(AudioDeviceID); |
| 461 scoped_ptr_malloc<AudioDeviceID> |
| 462 devices(reinterpret_cast<AudioDeviceID*>(malloc(size))); |
| 463 result = AudioObjectGetPropertyData( |
| 464 device, &pa, 0, NULL, &size, devices.get()); |
| 465 if (result) |
| 466 return std::string(); |
| 467 |
| 468 for (int i = 0; i < device_count; ++i) { |
| 469 // Get the number of output channels of the device. |
| 470 pa.mSelector = kAudioDevicePropertyStreams; |
| 471 size = 0; |
| 472 result = AudioObjectGetPropertyDataSize(devices.get()[i], |
| 473 &pa, |
| 474 0, |
| 475 NULL, |
| 476 &size); |
| 477 if (result || !size) |
| 478 continue; // Skip if there aren't any output channels. |
| 479 |
| 480 // Get device UID. |
| 481 CFStringRef uid = NULL; |
| 482 size = sizeof(uid); |
| 483 pa.mSelector = kAudioDevicePropertyDeviceUID; |
| 484 result = AudioObjectGetPropertyData(devices.get()[i], |
| 485 &pa, |
| 486 0, |
| 487 NULL, |
| 488 &size, |
| 489 &uid); |
| 490 if (result || !uid) |
| 491 continue; |
| 492 |
| 493 std::string ret(base::SysCFStringRefToUTF8(uid)); |
| 494 CFRelease(uid); |
| 495 return ret; |
| 496 } |
| 497 |
| 498 // No matching device found. |
| 499 return std::string(); |
| 500 } |
| 501 |
444 AudioOutputStream* AudioManagerMac::MakeLinearOutputStream( | 502 AudioOutputStream* AudioManagerMac::MakeLinearOutputStream( |
445 const AudioParameters& params) { | 503 const AudioParameters& params) { |
446 return MakeLowLatencyOutputStream(params, std::string(), std::string()); | 504 return MakeLowLatencyOutputStream(params, std::string(), std::string()); |
447 } | 505 } |
448 | 506 |
449 AudioOutputStream* AudioManagerMac::MakeLowLatencyOutputStream( | 507 AudioOutputStream* AudioManagerMac::MakeLowLatencyOutputStream( |
450 const AudioParameters& params, | 508 const AudioParameters& params, |
451 const std::string& device_id, | 509 const std::string& device_id, |
452 const std::string& input_device_id) { | 510 const std::string& input_device_id) { |
453 DLOG_IF(ERROR, !device_id.empty()) << "Not implemented!"; | |
454 // Handle basic output with no input channels. | 511 // Handle basic output with no input channels. |
455 if (params.input_channels() == 0) { | 512 if (params.input_channels() == 0) { |
456 AudioDeviceID device = kAudioObjectUnknown; | 513 AudioDeviceID device = GetAudioDeviceIdByUId(false, device_id); |
457 GetDefaultOutputDevice(&device); | 514 if (device == kAudioObjectUnknown) { |
| 515 DLOG(ERROR) << "Failed to open output device: " << device_id; |
| 516 return NULL; |
| 517 } |
458 return new AUHALStream(this, params, device); | 518 return new AUHALStream(this, params, device); |
459 } | 519 } |
460 | 520 |
461 // TODO(crogers): support more than stereo input. | 521 DLOG_IF(ERROR, !device_id.empty()) << "Not implemented!"; |
| 522 |
| 523 // TODO(xians): support more than stereo input. |
462 if (params.input_channels() != 2) { | 524 if (params.input_channels() != 2) { |
463 // WebAudio is currently hard-coded to 2 channels so we should not | 525 // WebAudio is currently hard-coded to 2 channels so we should not |
464 // see this case. | 526 // see this case. |
465 NOTREACHED() << "Only stereo input is currently supported!"; | 527 NOTREACHED() << "Only stereo input is currently supported!"; |
466 return NULL; | 528 return NULL; |
467 } | 529 } |
468 | 530 |
469 AudioDeviceID device = kAudioObjectUnknown; | 531 AudioDeviceID device = kAudioObjectUnknown; |
470 if (HasUnifiedDefaultIO()) { | 532 if (HasUnifiedDefaultIO()) { |
471 // For I/O, the simplest case is when the default input and output | 533 // For I/O, the simplest case is when the default input and output |
(...skipping 16 matching lines...) Expand all Loading... |
488 } | 550 } |
489 | 551 |
490 if (device != kAudioObjectUnknown && | 552 if (device != kAudioObjectUnknown && |
491 input_device_id == AudioManagerBase::kDefaultDeviceId) | 553 input_device_id == AudioManagerBase::kDefaultDeviceId) |
492 return new AUHALStream(this, params, device); | 554 return new AUHALStream(this, params, device); |
493 | 555 |
494 // Fallback to AudioSynchronizedStream which will handle completely | 556 // Fallback to AudioSynchronizedStream which will handle completely |
495 // different and arbitrary combinations of input and output devices | 557 // different and arbitrary combinations of input and output devices |
496 // even running at different sample-rates. | 558 // even running at different sample-rates. |
497 // kAudioDeviceUnknown translates to "use default" here. | 559 // kAudioDeviceUnknown translates to "use default" here. |
498 // TODO(crogers): consider tracking UMA stats on AUHALStream | 560 // TODO(xians): consider tracking UMA stats on AUHALStream |
499 // versus AudioSynchronizedStream. | 561 // versus AudioSynchronizedStream. |
500 AudioDeviceID audio_device_id = GetAudioDeviceIdByUId(true, input_device_id); | 562 AudioDeviceID audio_device_id = GetAudioDeviceIdByUId(true, input_device_id); |
501 if (audio_device_id == kAudioObjectUnknown) | 563 if (audio_device_id == kAudioObjectUnknown) |
502 return NULL; | 564 return NULL; |
503 | 565 |
504 return new AudioSynchronizedStream(this, | 566 return new AudioSynchronizedStream(this, |
505 params, | 567 params, |
506 audio_device_id, | 568 audio_device_id, |
507 kAudioDeviceUnknown); | 569 kAudioDeviceUnknown); |
508 } | 570 } |
509 | 571 |
| 572 std::string AudioManagerMac::GetDefaultOutputDeviceID() { |
| 573 AudioDeviceID device_id = kAudioObjectUnknown; |
| 574 if (!GetDefaultOutputDevice(&device_id)) |
| 575 return std::string(); |
| 576 |
| 577 const AudioObjectPropertyAddress property_address = { |
| 578 kAudioDevicePropertyDeviceUID, |
| 579 kAudioObjectPropertyScopeGlobal, |
| 580 kAudioObjectPropertyElementMaster |
| 581 }; |
| 582 CFStringRef device_uid = NULL; |
| 583 UInt32 size = sizeof(device_uid); |
| 584 OSStatus status = AudioObjectGetPropertyData(device_id, |
| 585 &property_address, |
| 586 0, |
| 587 NULL, |
| 588 &size, |
| 589 &device_uid); |
| 590 if (status != kAudioHardwareNoError || !device_uid) |
| 591 return std::string(); |
| 592 |
| 593 std::string ret(base::SysCFStringRefToUTF8(device_uid)); |
| 594 CFRelease(device_uid); |
| 595 |
| 596 return ret; |
| 597 } |
| 598 |
510 AudioInputStream* AudioManagerMac::MakeLinearInputStream( | 599 AudioInputStream* AudioManagerMac::MakeLinearInputStream( |
511 const AudioParameters& params, const std::string& device_id) { | 600 const AudioParameters& params, const std::string& device_id) { |
512 DCHECK_EQ(AudioParameters::AUDIO_PCM_LINEAR, params.format()); | 601 DCHECK_EQ(AudioParameters::AUDIO_PCM_LINEAR, params.format()); |
513 return new PCMQueueInAudioInputStream(this, params); | 602 return new PCMQueueInAudioInputStream(this, params); |
514 } | 603 } |
515 | 604 |
516 AudioInputStream* AudioManagerMac::MakeLowLatencyInputStream( | 605 AudioInputStream* AudioManagerMac::MakeLowLatencyInputStream( |
517 const AudioParameters& params, const std::string& device_id) { | 606 const AudioParameters& params, const std::string& device_id) { |
518 DCHECK_EQ(AudioParameters::AUDIO_PCM_LOW_LATENCY, params.format()); | 607 DCHECK_EQ(AudioParameters::AUDIO_PCM_LOW_LATENCY, params.format()); |
519 // Gets the AudioDeviceID that refers to the AudioOutputDevice with the device | 608 // Gets the AudioDeviceID that refers to the AudioInputDevice with the device |
520 // unique id. This AudioDeviceID is used to set the device for Audio Unit. | 609 // unique id. This AudioDeviceID is used to set the device for Audio Unit. |
521 AudioDeviceID audio_device_id = GetAudioDeviceIdByUId(true, device_id); | 610 AudioDeviceID audio_device_id = GetAudioDeviceIdByUId(true, device_id); |
522 AudioInputStream* stream = NULL; | 611 AudioInputStream* stream = NULL; |
523 if (audio_device_id != kAudioObjectUnknown) | 612 if (audio_device_id != kAudioObjectUnknown) { |
524 stream = new AUAudioInputStream(this, params, audio_device_id); | 613 // AUAudioInputStream needs to be fed the preferred audio output parameters |
| 614 // of the matching device so that the buffer size of both input and output |
| 615 // can be matched. See constructor of AUAudioInputStream for more. |
| 616 const std::string associated_output_device( |
| 617 GetAssociatedOutputDeviceID(device_id)); |
| 618 const AudioParameters output_params = |
| 619 GetPreferredOutputStreamParameters( |
| 620 associated_output_device.empty() ? |
| 621 AudioManagerBase::kDefaultDeviceId : associated_output_device, |
| 622 params); |
| 623 stream = new AUAudioInputStream(this, params, output_params, |
| 624 audio_device_id); |
| 625 } |
525 | 626 |
526 return stream; | 627 return stream; |
527 } | 628 } |
528 | 629 |
529 AudioParameters AudioManagerMac::GetPreferredOutputStreamParameters( | 630 AudioParameters AudioManagerMac::GetPreferredOutputStreamParameters( |
530 const std::string& output_device_id, | 631 const std::string& output_device_id, |
531 const AudioParameters& input_params) { | 632 const AudioParameters& input_params) { |
532 // TODO(tommi): Support |output_device_id|. | 633 AudioDeviceID device = GetAudioDeviceIdByUId(false, output_device_id); |
533 DLOG_IF(ERROR, !output_device_id.empty()) << "Not implemented!"; | 634 if (device == kAudioObjectUnknown) { |
| 635 DLOG(ERROR) << "Invalid output device " << output_device_id; |
| 636 return AudioParameters(); |
| 637 } |
| 638 |
534 int hardware_channels = 2; | 639 int hardware_channels = 2; |
535 if (!GetDefaultOutputChannels(&hardware_channels)) { | 640 if (!GetDeviceChannels(device, kAudioDevicePropertyScopeOutput, |
| 641 &hardware_channels)) { |
536 // Fallback to stereo. | 642 // Fallback to stereo. |
537 hardware_channels = 2; | 643 hardware_channels = 2; |
538 } | 644 } |
539 | 645 |
540 ChannelLayout channel_layout = GuessChannelLayout(hardware_channels); | 646 ChannelLayout channel_layout = GuessChannelLayout(hardware_channels); |
541 | 647 |
542 const int hardware_sample_rate = AUAudioOutputStream::HardwareSampleRate(); | 648 const int hardware_sample_rate = HardwareSampleRateForDevice(device); |
543 const int buffer_size = ChooseBufferSize(hardware_sample_rate); | 649 const int buffer_size = ChooseBufferSize(hardware_sample_rate); |
544 | 650 |
545 int input_channels = 0; | 651 int input_channels = 0; |
546 if (input_params.IsValid()) { | 652 if (input_params.IsValid()) { |
547 input_channels = input_params.input_channels(); | 653 input_channels = input_params.input_channels(); |
548 | 654 |
549 if (input_channels > 0) { | 655 if (input_channels > 0) { |
550 // TODO(crogers): given the limitations of the AudioOutputStream | 656 // TODO(xians): given the limitations of the AudioOutputStream |
551 // back-ends used with synchronized I/O, we hard-code to stereo. | 657 // back-ends used with synchronized I/O, we hard-code to stereo. |
552 // Specifically, this is a limitation of AudioSynchronizedStream which | 658 // Specifically, this is a limitation of AudioSynchronizedStream which |
553 // can be removed as part of the work to consolidate these back-ends. | 659 // can be removed as part of the work to consolidate these back-ends. |
554 channel_layout = CHANNEL_LAYOUT_STEREO; | 660 channel_layout = CHANNEL_LAYOUT_STEREO; |
555 } | 661 } |
556 } | 662 } |
557 | 663 |
558 AudioParameters params( | 664 AudioParameters params( |
559 AudioParameters::AUDIO_PCM_LOW_LATENCY, | 665 AudioParameters::AUDIO_PCM_LOW_LATENCY, |
560 channel_layout, | 666 channel_layout, |
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
605 current_sample_rate_ = new_sample_rate; | 711 current_sample_rate_ = new_sample_rate; |
606 current_output_device_ = new_output_device; | 712 current_output_device_ = new_output_device; |
607 NotifyAllOutputDeviceChangeListeners(); | 713 NotifyAllOutputDeviceChangeListeners(); |
608 } | 714 } |
609 | 715 |
610 AudioManager* CreateAudioManager() { | 716 AudioManager* CreateAudioManager() { |
611 return new AudioManagerMac(); | 717 return new AudioManagerMac(); |
612 } | 718 } |
613 | 719 |
614 } // namespace media | 720 } // namespace media |
OLD | NEW |