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 <stdint.h> | 7 #include <stdint.h> |
8 | 8 |
9 #include "base/bind.h" | 9 #include "base/bind.h" |
10 #include "base/command_line.h" | 10 #include "base/command_line.h" |
(...skipping 352 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
363 | 363 |
364 DISALLOW_COPY_AND_ASSIGN(AudioPowerObserver); | 364 DISALLOW_COPY_AND_ASSIGN(AudioPowerObserver); |
365 }; | 365 }; |
366 | 366 |
367 AudioManagerMac::AudioManagerMac(AudioLogFactory* audio_log_factory) | 367 AudioManagerMac::AudioManagerMac(AudioLogFactory* audio_log_factory) |
368 : AudioManagerBase(audio_log_factory), | 368 : AudioManagerBase(audio_log_factory), |
369 current_sample_rate_(0), | 369 current_sample_rate_(0), |
370 current_output_device_(kAudioDeviceUnknown) { | 370 current_output_device_(kAudioDeviceUnknown) { |
371 SetMaxOutputStreamsAllowed(kMaxOutputStreams); | 371 SetMaxOutputStreamsAllowed(kMaxOutputStreams); |
372 | 372 |
| 373 // CoreAudio calls must occur on the main thread of the process, which in our |
| 374 // case is sadly the browser UI thread. Failure to execute calls on the right |
| 375 // thread leads to crashes and odd behavior. See http://crbug.com/158170. |
| 376 // TODO(dalecurtis): We should require the message loop to be passed in. |
| 377 task_runner_ = base::MessageLoopForUI::IsCurrent() |
| 378 ? base::ThreadTaskRunnerHandle::Get() |
| 379 : AudioManagerBase::GetTaskRunner(); |
| 380 |
373 // Task must be posted last to avoid races from handing out "this" to the | 381 // Task must be posted last to avoid races from handing out "this" to the |
374 // audio thread. Always PostTask even if we're on the right thread since | 382 // audio thread. Always PostTask even if we're on the right thread since |
375 // AudioManager creation is on the startup path and this may be slow. | 383 // AudioManager creation is on the startup path and this may be slow. |
376 GetTaskRunner()->PostTask(FROM_HERE, base::Bind( | 384 task_runner_->PostTask(FROM_HERE, |
377 &AudioManagerMac::InitializeOnAudioThread, base::Unretained(this))); | 385 base::Bind(&AudioManagerMac::InitializeOnAudioThread, |
| 386 base::Unretained(this))); |
378 } | 387 } |
379 | 388 |
380 AudioManagerMac::~AudioManagerMac() { | 389 AudioManagerMac::~AudioManagerMac() { |
381 if (GetTaskRunner()->BelongsToCurrentThread()) { | 390 if (task_runner_->BelongsToCurrentThread()) { |
382 ShutdownOnAudioThread(); | 391 ShutdownOnAudioThread(); |
383 } else { | 392 } else { |
384 // It's safe to post a task here since Shutdown() will wait for all tasks to | 393 // It's safe to post a task here since Shutdown() will wait for all tasks to |
385 // complete before returning. | 394 // complete before returning. |
386 GetTaskRunner()->PostTask(FROM_HERE, base::Bind( | 395 task_runner_->PostTask(FROM_HERE, |
387 &AudioManagerMac::ShutdownOnAudioThread, base::Unretained(this))); | 396 base::Bind(&AudioManagerMac::ShutdownOnAudioThread, |
| 397 base::Unretained(this))); |
388 } | 398 } |
389 | 399 |
390 Shutdown(); | 400 Shutdown(); |
391 } | 401 } |
392 | 402 |
| 403 scoped_refptr<base::SingleThreadTaskRunner> AudioManagerMac::GetTaskRunner() { |
| 404 return task_runner_; |
| 405 } |
| 406 |
| 407 scoped_refptr<base::SingleThreadTaskRunner> |
| 408 AudioManagerMac::GetWorkerTaskRunner() { |
| 409 if (!worker_thread_) { |
| 410 worker_thread_.reset(new base::Thread("AudioWorkerThread")); |
| 411 CHECK(worker_thread_->Start()); |
| 412 } |
| 413 return worker_thread_->task_runner(); |
| 414 } |
| 415 |
393 bool AudioManagerMac::HasAudioOutputDevices() { | 416 bool AudioManagerMac::HasAudioOutputDevices() { |
394 return HasAudioHardware(kAudioHardwarePropertyDefaultOutputDevice); | 417 return HasAudioHardware(kAudioHardwarePropertyDefaultOutputDevice); |
395 } | 418 } |
396 | 419 |
397 bool AudioManagerMac::HasAudioInputDevices() { | 420 bool AudioManagerMac::HasAudioInputDevices() { |
398 return HasAudioHardware(kAudioHardwarePropertyDefaultInputDevice); | 421 return HasAudioHardware(kAudioHardwarePropertyDefaultInputDevice); |
399 } | 422 } |
400 | 423 |
| 424 // static |
401 bool AudioManagerMac::GetDeviceChannels(AudioDeviceID device, | 425 bool AudioManagerMac::GetDeviceChannels(AudioDeviceID device, |
402 AudioObjectPropertyScope scope, | 426 AudioObjectPropertyScope scope, |
403 int* channels) { | 427 int* channels) { |
404 DCHECK(AudioManager::Get()->GetTaskRunner()->BelongsToCurrentThread()); | 428 DCHECK(AudioManager::Get()->GetTaskRunner()->BelongsToCurrentThread()); |
405 CHECK(channels); | 429 CHECK(channels); |
406 const bool is_input = (scope == kAudioDevicePropertyScopeInput); | 430 const bool is_input = (scope == kAudioDevicePropertyScopeInput); |
407 DVLOG(1) << "GetDeviceChannels(id=0x" << std::hex << device | 431 DVLOG(1) << "GetDeviceChannels(id=0x" << std::hex << device |
408 << ", is_input=" << is_input << ")"; | 432 << ", is_input=" << is_input << ")"; |
409 | 433 |
410 // Get the stream configuration of the device in an AudioBufferList (with the | 434 // Get the stream configuration of the device in an AudioBufferList (with the |
(...skipping 23 matching lines...) Expand all Loading... |
434 // TODO(henrika): add UMA stats to track utilized hardware configurations. | 458 // TODO(henrika): add UMA stats to track utilized hardware configurations. |
435 int num_channels = 0; | 459 int num_channels = 0; |
436 for (UInt32 i = 0; i < buffer_list.mNumberBuffers; ++i) { | 460 for (UInt32 i = 0; i < buffer_list.mNumberBuffers; ++i) { |
437 num_channels += buffer_list.mBuffers[i].mNumberChannels; | 461 num_channels += buffer_list.mBuffers[i].mNumberChannels; |
438 } | 462 } |
439 *channels = num_channels; | 463 *channels = num_channels; |
440 DVLOG(1) << "#channels: " << *channels; | 464 DVLOG(1) << "#channels: " << *channels; |
441 return true; | 465 return true; |
442 } | 466 } |
443 | 467 |
| 468 // static |
444 int AudioManagerMac::HardwareSampleRateForDevice(AudioDeviceID device_id) { | 469 int AudioManagerMac::HardwareSampleRateForDevice(AudioDeviceID device_id) { |
445 DCHECK(AudioManager::Get()->GetTaskRunner()->BelongsToCurrentThread()); | 470 DCHECK(AudioManager::Get()->GetTaskRunner()->BelongsToCurrentThread()); |
446 Float64 nominal_sample_rate; | 471 Float64 nominal_sample_rate; |
447 UInt32 info_size = sizeof(nominal_sample_rate); | 472 UInt32 info_size = sizeof(nominal_sample_rate); |
448 | 473 |
449 static const AudioObjectPropertyAddress kNominalSampleRateAddress = { | 474 static const AudioObjectPropertyAddress kNominalSampleRateAddress = { |
450 kAudioDevicePropertyNominalSampleRate, | 475 kAudioDevicePropertyNominalSampleRate, |
451 kAudioObjectPropertyScopeGlobal, | 476 kAudioObjectPropertyScopeGlobal, |
452 kAudioObjectPropertyElementMaster | 477 kAudioObjectPropertyElementMaster |
453 }; | 478 }; |
454 OSStatus result = AudioObjectGetPropertyData(device_id, | 479 OSStatus result = AudioObjectGetPropertyData(device_id, |
455 &kNominalSampleRateAddress, | 480 &kNominalSampleRateAddress, |
456 0, | 481 0, |
457 0, | 482 0, |
458 &info_size, | 483 &info_size, |
459 &nominal_sample_rate); | 484 &nominal_sample_rate); |
460 if (result != noErr) { | 485 if (result != noErr) { |
461 OSSTATUS_DLOG(WARNING, result) | 486 OSSTATUS_DLOG(WARNING, result) |
462 << "Could not get default sample rate for device: " << device_id; | 487 << "Could not get default sample rate for device: " << device_id; |
463 return 0; | 488 return 0; |
464 } | 489 } |
465 | 490 |
466 return static_cast<int>(nominal_sample_rate); | 491 return static_cast<int>(nominal_sample_rate); |
467 } | 492 } |
468 | 493 |
| 494 // static |
469 int AudioManagerMac::HardwareSampleRate() { | 495 int AudioManagerMac::HardwareSampleRate() { |
470 // Determine the default output device's sample-rate. | 496 // Determine the default output device's sample-rate. |
471 AudioDeviceID device_id = kAudioObjectUnknown; | 497 AudioDeviceID device_id = kAudioObjectUnknown; |
472 if (!GetDefaultOutputDevice(&device_id)) | 498 if (!GetDefaultOutputDevice(&device_id)) |
473 return kFallbackSampleRate; | 499 return kFallbackSampleRate; |
474 | 500 |
475 return HardwareSampleRateForDevice(device_id); | 501 return HardwareSampleRateForDevice(device_id); |
476 } | 502 } |
477 | 503 |
478 void AudioManagerMac::GetAudioInputDeviceNames( | 504 void AudioManagerMac::GetAudioInputDeviceNames( |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
517 const int buffer_size = ChooseBufferSize(true, sample_rate); | 543 const int buffer_size = ChooseBufferSize(true, sample_rate); |
518 | 544 |
519 // TODO(xians): query the native channel layout for the specific device. | 545 // TODO(xians): query the native channel layout for the specific device. |
520 return AudioParameters( | 546 return AudioParameters( |
521 AudioParameters::AUDIO_PCM_LOW_LATENCY, channel_layout, | 547 AudioParameters::AUDIO_PCM_LOW_LATENCY, channel_layout, |
522 sample_rate, 16, buffer_size); | 548 sample_rate, 16, buffer_size); |
523 } | 549 } |
524 | 550 |
525 std::string AudioManagerMac::GetAssociatedOutputDeviceID( | 551 std::string AudioManagerMac::GetAssociatedOutputDeviceID( |
526 const std::string& input_device_id) { | 552 const std::string& input_device_id) { |
527 DCHECK(GetTaskRunner()->BelongsToCurrentThread()); | 553 DCHECK(task_runner_->BelongsToCurrentThread()); |
528 AudioDeviceID device = GetAudioDeviceIdByUId(true, input_device_id); | 554 AudioDeviceID device = GetAudioDeviceIdByUId(true, input_device_id); |
529 if (device == kAudioObjectUnknown) | 555 if (device == kAudioObjectUnknown) |
530 return std::string(); | 556 return std::string(); |
531 | 557 |
532 UInt32 size = 0; | 558 UInt32 size = 0; |
533 AudioObjectPropertyAddress pa = { | 559 AudioObjectPropertyAddress pa = { |
534 kAudioDevicePropertyRelatedDevices, | 560 kAudioDevicePropertyRelatedDevices, |
535 kAudioDevicePropertyScopeOutput, | 561 kAudioDevicePropertyScopeOutput, |
536 kAudioObjectPropertyElementMaster | 562 kAudioObjectPropertyElementMaster |
537 }; | 563 }; |
(...skipping 104 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
642 // rates on OSX. | 668 // rates on OSX. |
643 current_sample_rate_ = params.sample_rate(); | 669 current_sample_rate_ = params.sample_rate(); |
644 } | 670 } |
645 | 671 |
646 AUHALStream* stream = new AUHALStream(this, params, device); | 672 AUHALStream* stream = new AUHALStream(this, params, device); |
647 output_streams_.push_back(stream); | 673 output_streams_.push_back(stream); |
648 return stream; | 674 return stream; |
649 } | 675 } |
650 | 676 |
651 std::string AudioManagerMac::GetDefaultOutputDeviceID() { | 677 std::string AudioManagerMac::GetDefaultOutputDeviceID() { |
652 DCHECK(GetTaskRunner()->BelongsToCurrentThread()); | 678 DCHECK(task_runner_->BelongsToCurrentThread()); |
653 AudioDeviceID device_id = kAudioObjectUnknown; | 679 AudioDeviceID device_id = kAudioObjectUnknown; |
654 if (!GetDefaultOutputDevice(&device_id)) | 680 if (!GetDefaultOutputDevice(&device_id)) |
655 return std::string(); | 681 return std::string(); |
656 | 682 |
657 const AudioObjectPropertyAddress property_address = { | 683 const AudioObjectPropertyAddress property_address = { |
658 kAudioDevicePropertyDeviceUID, | 684 kAudioDevicePropertyDeviceUID, |
659 kAudioObjectPropertyScopeGlobal, | 685 kAudioObjectPropertyScopeGlobal, |
660 kAudioObjectPropertyElementMaster | 686 kAudioObjectPropertyElementMaster |
661 }; | 687 }; |
662 CFStringRef device_uid = NULL; | 688 CFStringRef device_uid = NULL; |
(...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
741 channel_layout = CHANNEL_LAYOUT_DISCRETE; | 767 channel_layout = CHANNEL_LAYOUT_DISCRETE; |
742 } | 768 } |
743 | 769 |
744 AudioParameters params(AudioParameters::AUDIO_PCM_LOW_LATENCY, channel_layout, | 770 AudioParameters params(AudioParameters::AUDIO_PCM_LOW_LATENCY, channel_layout, |
745 hardware_sample_rate, 16, buffer_size); | 771 hardware_sample_rate, 16, buffer_size); |
746 params.set_channels_for_discrete(output_channels); | 772 params.set_channels_for_discrete(output_channels); |
747 return params; | 773 return params; |
748 } | 774 } |
749 | 775 |
750 void AudioManagerMac::InitializeOnAudioThread() { | 776 void AudioManagerMac::InitializeOnAudioThread() { |
751 DCHECK(GetTaskRunner()->BelongsToCurrentThread()); | 777 DCHECK(task_runner_->BelongsToCurrentThread()); |
752 power_observer_.reset(new AudioPowerObserver()); | 778 power_observer_.reset(new AudioPowerObserver()); |
753 } | 779 } |
754 | 780 |
755 void AudioManagerMac::ShutdownOnAudioThread() { | 781 void AudioManagerMac::ShutdownOnAudioThread() { |
756 DCHECK(GetTaskRunner()->BelongsToCurrentThread()); | 782 DCHECK(task_runner_->BelongsToCurrentThread()); |
757 output_device_listener_.reset(); | 783 output_device_listener_.reset(); |
758 power_observer_.reset(); | 784 power_observer_.reset(); |
759 | 785 |
760 // Since CoreAudio calls have to run on the UI thread and browser shutdown | 786 // Since CoreAudio calls have to run on the UI thread and browser shutdown |
761 // doesn't wait for outstanding tasks to complete, we may have input/output | 787 // doesn't wait for outstanding tasks to complete, we may have input/output |
762 // streams still running at shutdown. | 788 // streams still running at shutdown. |
763 // | 789 // |
764 // To avoid calls into destructed classes, we need to stop the OS callbacks | 790 // To avoid calls into destructed classes, we need to stop the OS callbacks |
765 // by stopping the streams. Note: The streams are leaked since process | 791 // by stopping the streams. Note: The streams are leaked since process |
766 // destruction is imminent. | 792 // destruction is imminent. |
767 // | 793 // |
768 // See http://crbug.com/354139 for crash details. | 794 // See http://crbug.com/354139 for crash details. |
769 StopStreams(&basic_input_streams_); | 795 StopStreams(&basic_input_streams_); |
770 StopStreams(&low_latency_input_streams_); | 796 StopStreams(&low_latency_input_streams_); |
771 StopStreams(&output_streams_); | 797 StopStreams(&output_streams_); |
772 } | 798 } |
773 | 799 |
774 void AudioManagerMac::HandleDeviceChanges() { | 800 void AudioManagerMac::HandleDeviceChanges() { |
775 DCHECK(GetTaskRunner()->BelongsToCurrentThread()); | 801 DCHECK(task_runner_->BelongsToCurrentThread()); |
776 const int new_sample_rate = HardwareSampleRate(); | 802 const int new_sample_rate = HardwareSampleRate(); |
777 AudioDeviceID new_output_device; | 803 AudioDeviceID new_output_device; |
778 GetDefaultOutputDevice(&new_output_device); | 804 GetDefaultOutputDevice(&new_output_device); |
779 | 805 |
780 if (current_sample_rate_ == new_sample_rate && | 806 if (current_sample_rate_ == new_sample_rate && |
781 current_output_device_ == new_output_device) { | 807 current_output_device_ == new_output_device) { |
782 return; | 808 return; |
783 } | 809 } |
784 | 810 |
785 current_sample_rate_ = new_sample_rate; | 811 current_sample_rate_ = new_sample_rate; |
(...skipping 21 matching lines...) Expand all Loading... |
807 if (sample_rate <= 96000) | 833 if (sample_rate <= 96000) |
808 buffer_size = 2 * kMinimumInputOutputBufferSize; | 834 buffer_size = 2 * kMinimumInputOutputBufferSize; |
809 else if (sample_rate <= 192000) | 835 else if (sample_rate <= 192000) |
810 buffer_size = 4 * kMinimumInputOutputBufferSize; | 836 buffer_size = 4 * kMinimumInputOutputBufferSize; |
811 } | 837 } |
812 | 838 |
813 return buffer_size; | 839 return buffer_size; |
814 } | 840 } |
815 | 841 |
816 bool AudioManagerMac::ShouldDeferStreamStart() const { | 842 bool AudioManagerMac::ShouldDeferStreamStart() const { |
817 DCHECK(GetTaskRunner()->BelongsToCurrentThread()); | 843 DCHECK(task_runner_->BelongsToCurrentThread()); |
818 return power_observer_->ShouldDeferStreamStart(); | 844 return power_observer_->ShouldDeferStreamStart(); |
819 } | 845 } |
820 | 846 |
821 bool AudioManagerMac::IsOnBatteryPower() const { | 847 bool AudioManagerMac::IsOnBatteryPower() const { |
822 DCHECK(GetTaskRunner()->BelongsToCurrentThread()); | 848 DCHECK(task_runner_->BelongsToCurrentThread()); |
823 return power_observer_->IsOnBatteryPower(); | 849 return power_observer_->IsOnBatteryPower(); |
824 } | 850 } |
825 | 851 |
826 size_t AudioManagerMac::GetNumberOfResumeNotifications() const { | 852 size_t AudioManagerMac::GetNumberOfResumeNotifications() const { |
827 DCHECK(GetTaskRunner()->BelongsToCurrentThread()); | 853 DCHECK(task_runner_->BelongsToCurrentThread()); |
828 return power_observer_->num_resume_notifications(); | 854 return power_observer_->num_resume_notifications(); |
829 } | 855 } |
830 | 856 |
831 bool AudioManagerMac::MaybeChangeBufferSize(AudioDeviceID device_id, | 857 bool AudioManagerMac::MaybeChangeBufferSize(AudioDeviceID device_id, |
832 AudioUnit audio_unit, | 858 AudioUnit audio_unit, |
833 AudioUnitElement element, | 859 AudioUnitElement element, |
834 size_t desired_buffer_size, | 860 size_t desired_buffer_size, |
835 bool* size_was_changed, | 861 bool* size_was_changed, |
836 size_t* io_buffer_frame_size) { | 862 size_t* io_buffer_frame_size) { |
837 DCHECK(GetTaskRunner()->BelongsToCurrentThread()); | 863 DCHECK(task_runner_->BelongsToCurrentThread()); |
838 const bool is_input = (element == 1); | 864 const bool is_input = (element == 1); |
839 DVLOG(1) << "MaybeChangeBufferSize(id=0x" << std::hex << device_id | 865 DVLOG(1) << "MaybeChangeBufferSize(id=0x" << std::hex << device_id |
840 << ", is_input=" << is_input << ", desired_buffer_size=" << std::dec | 866 << ", is_input=" << is_input << ", desired_buffer_size=" << std::dec |
841 << desired_buffer_size << ")"; | 867 << desired_buffer_size << ")"; |
842 | 868 |
843 *size_was_changed = false; | 869 *size_was_changed = false; |
844 *io_buffer_frame_size = 0; | 870 *io_buffer_frame_size = 0; |
845 | 871 |
846 // Log the device name (and id) for debugging purposes. | 872 // Log the device name (and id) for debugging purposes. |
847 std::string device_name = GetAudioDeviceNameFromDeviceId(device_id, is_input); | 873 std::string device_name = GetAudioDeviceNameFromDeviceId(device_id, is_input); |
(...skipping 104 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
952 basic_input_streams_.erase(stream_it); | 978 basic_input_streams_.erase(stream_it); |
953 | 979 |
954 AudioManagerBase::ReleaseInputStream(stream); | 980 AudioManagerBase::ReleaseInputStream(stream); |
955 } | 981 } |
956 | 982 |
957 AudioManager* CreateAudioManager(AudioLogFactory* audio_log_factory) { | 983 AudioManager* CreateAudioManager(AudioLogFactory* audio_log_factory) { |
958 return new AudioManagerMac(audio_log_factory); | 984 return new AudioManagerMac(audio_log_factory); |
959 } | 985 } |
960 | 986 |
961 } // namespace media | 987 } // namespace media |
OLD | NEW |