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 275 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
286 DLOG(ERROR) << "Error getting default AudioDevice."; | 286 DLOG(ERROR) << "Error getting default AudioDevice."; |
287 return false; | 287 return false; |
288 } | 288 } |
289 return true; | 289 return true; |
290 } | 290 } |
291 | 291 |
292 static bool GetDefaultOutputDevice(AudioDeviceID* device) { | 292 static bool GetDefaultOutputDevice(AudioDeviceID* device) { |
293 return GetDefaultDevice(device, false); | 293 return GetDefaultDevice(device, false); |
294 } | 294 } |
295 | 295 |
| 296 template <class T> |
| 297 void StopStreams(std::list<T*>* streams) { |
| 298 for (typename std::list<T*>::iterator it = streams->begin(); |
| 299 it != streams->end(); |
| 300 ++it) { |
| 301 // Stop() is safe to call multiple times, so it doesn't matter if a stream |
| 302 // has already been stopped. |
| 303 (*it)->Stop(); |
| 304 } |
| 305 streams->clear(); |
| 306 } |
| 307 |
296 class AudioManagerMac::AudioPowerObserver : public base::PowerObserver { | 308 class AudioManagerMac::AudioPowerObserver : public base::PowerObserver { |
297 public: | 309 public: |
298 AudioPowerObserver() | 310 AudioPowerObserver() |
299 : is_suspending_(false), | 311 : is_suspending_(false), |
300 is_monitoring_(base::PowerMonitor::Get()), | 312 is_monitoring_(base::PowerMonitor::Get()), |
301 num_resume_notifications_(0) { | 313 num_resume_notifications_(0) { |
302 // The PowerMonitor requires significant setup (a CFRunLoop and preallocated | 314 // The PowerMonitor requires significant setup (a CFRunLoop and preallocated |
303 // IO ports) so it's not available under unit tests. See the OSX impl of | 315 // IO ports) so it's not available under unit tests. See the OSX impl of |
304 // base::PowerMonitorDeviceSource for more details. | 316 // base::PowerMonitorDeviceSource for more details. |
305 if (!is_monitoring_) | 317 if (!is_monitoring_) |
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
351 | 363 |
352 bool is_suspending_; | 364 bool is_suspending_; |
353 const bool is_monitoring_; | 365 const bool is_monitoring_; |
354 base::TimeTicks earliest_start_time_; | 366 base::TimeTicks earliest_start_time_; |
355 base::ThreadChecker thread_checker_; | 367 base::ThreadChecker thread_checker_; |
356 size_t num_resume_notifications_; | 368 size_t num_resume_notifications_; |
357 | 369 |
358 DISALLOW_COPY_AND_ASSIGN(AudioPowerObserver); | 370 DISALLOW_COPY_AND_ASSIGN(AudioPowerObserver); |
359 }; | 371 }; |
360 | 372 |
361 AudioManagerMac::AudioManagerMac( | 373 AudioManagerMac::AudioManagerMac(AudioLogFactory* audio_log_factory) |
362 scoped_refptr<base::SingleThreadTaskRunner> task_runner, | 374 : AudioManagerBase(audio_log_factory), |
363 scoped_refptr<base::SingleThreadTaskRunner> worker_task_runner, | |
364 AudioLogFactory* audio_log_factory) | |
365 : AudioManagerBase(std::move(task_runner), | |
366 std::move(worker_task_runner), | |
367 audio_log_factory), | |
368 current_sample_rate_(0), | 375 current_sample_rate_(0), |
369 current_output_device_(kAudioDeviceUnknown) { | 376 current_output_device_(kAudioDeviceUnknown) { |
370 SetMaxOutputStreamsAllowed(kMaxOutputStreams); | 377 SetMaxOutputStreamsAllowed(kMaxOutputStreams); |
371 | 378 |
| 379 // CoreAudio calls must occur on the main thread of the process, which in our |
| 380 // case is sadly the browser UI thread. Failure to execute calls on the right |
| 381 // thread leads to crashes and odd behavior. See http://crbug.com/158170. |
| 382 // TODO(dalecurtis): We should require the message loop to be passed in. |
| 383 task_runner_ = base::MessageLoopForUI::IsCurrent() |
| 384 ? base::ThreadTaskRunnerHandle::Get() |
| 385 : AudioManagerBase::GetTaskRunner(); |
| 386 |
372 // Task must be posted last to avoid races from handing out "this" to the | 387 // Task must be posted last to avoid races from handing out "this" to the |
373 // audio thread. Always PostTask even if we're on the right thread since | 388 // audio thread. Always PostTask even if we're on the right thread since |
374 // AudioManager creation is on the startup path and this may be slow. | 389 // AudioManager creation is on the startup path and this may be slow. |
375 GetTaskRunner()->PostTask( | 390 task_runner_->PostTask(FROM_HERE, |
376 FROM_HERE, base::Bind(&AudioManagerMac::InitializeOnAudioThread, | 391 base::Bind(&AudioManagerMac::InitializeOnAudioThread, |
377 base::Unretained(this))); | 392 base::Unretained(this))); |
378 } | 393 } |
379 | 394 |
380 AudioManagerMac::~AudioManagerMac() { | 395 AudioManagerMac::~AudioManagerMac() { |
| 396 if (task_runner_->BelongsToCurrentThread()) { |
| 397 ShutdownOnAudioThread(); |
| 398 } else { |
| 399 // It's safe to post a task here since Shutdown() will wait for all tasks to |
| 400 // complete before returning. |
| 401 task_runner_->PostTask(FROM_HERE, |
| 402 base::Bind(&AudioManagerMac::ShutdownOnAudioThread, |
| 403 base::Unretained(this))); |
| 404 } |
| 405 |
381 Shutdown(); | 406 Shutdown(); |
382 } | 407 } |
383 | 408 |
| 409 scoped_refptr<base::SingleThreadTaskRunner> AudioManagerMac::GetTaskRunner() { |
| 410 return task_runner_; |
| 411 } |
| 412 |
| 413 scoped_refptr<base::SingleThreadTaskRunner> |
| 414 AudioManagerMac::GetWorkerTaskRunner() { |
| 415 if (!worker_thread_) { |
| 416 worker_thread_.reset(new base::Thread("AudioWorkerThread")); |
| 417 CHECK(worker_thread_->Start()); |
| 418 } |
| 419 return worker_thread_->task_runner(); |
| 420 } |
| 421 |
384 bool AudioManagerMac::HasAudioOutputDevices() { | 422 bool AudioManagerMac::HasAudioOutputDevices() { |
385 return HasAudioHardware(kAudioHardwarePropertyDefaultOutputDevice); | 423 return HasAudioHardware(kAudioHardwarePropertyDefaultOutputDevice); |
386 } | 424 } |
387 | 425 |
388 bool AudioManagerMac::HasAudioInputDevices() { | 426 bool AudioManagerMac::HasAudioInputDevices() { |
389 return HasAudioHardware(kAudioHardwarePropertyDefaultInputDevice); | 427 return HasAudioHardware(kAudioHardwarePropertyDefaultInputDevice); |
390 } | 428 } |
391 | 429 |
392 // static | 430 // static |
393 bool AudioManagerMac::GetDeviceChannels(AudioDeviceID device, | 431 bool AudioManagerMac::GetDeviceChannels(AudioDeviceID device, |
(...skipping 117 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
511 const int buffer_size = ChooseBufferSize(true, sample_rate); | 549 const int buffer_size = ChooseBufferSize(true, sample_rate); |
512 | 550 |
513 // TODO(xians): query the native channel layout for the specific device. | 551 // TODO(xians): query the native channel layout for the specific device. |
514 return AudioParameters( | 552 return AudioParameters( |
515 AudioParameters::AUDIO_PCM_LOW_LATENCY, channel_layout, | 553 AudioParameters::AUDIO_PCM_LOW_LATENCY, channel_layout, |
516 sample_rate, 16, buffer_size); | 554 sample_rate, 16, buffer_size); |
517 } | 555 } |
518 | 556 |
519 std::string AudioManagerMac::GetAssociatedOutputDeviceID( | 557 std::string AudioManagerMac::GetAssociatedOutputDeviceID( |
520 const std::string& input_device_id) { | 558 const std::string& input_device_id) { |
521 DCHECK(GetTaskRunner()->BelongsToCurrentThread()); | 559 DCHECK(task_runner_->BelongsToCurrentThread()); |
522 AudioDeviceID device = GetAudioDeviceIdByUId(true, input_device_id); | 560 AudioDeviceID device = GetAudioDeviceIdByUId(true, input_device_id); |
523 if (device == kAudioObjectUnknown) | 561 if (device == kAudioObjectUnknown) |
524 return std::string(); | 562 return std::string(); |
525 | 563 |
526 UInt32 size = 0; | 564 UInt32 size = 0; |
527 AudioObjectPropertyAddress pa = { | 565 AudioObjectPropertyAddress pa = { |
528 kAudioDevicePropertyRelatedDevices, | 566 kAudioDevicePropertyRelatedDevices, |
529 kAudioDevicePropertyScopeOutput, | 567 kAudioDevicePropertyScopeOutput, |
530 kAudioObjectPropertyElementMaster | 568 kAudioObjectPropertyElementMaster |
531 }; | 569 }; |
(...skipping 104 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
636 // rates on OSX. | 674 // rates on OSX. |
637 current_sample_rate_ = params.sample_rate(); | 675 current_sample_rate_ = params.sample_rate(); |
638 } | 676 } |
639 | 677 |
640 AUHALStream* stream = new AUHALStream(this, params, device); | 678 AUHALStream* stream = new AUHALStream(this, params, device); |
641 output_streams_.push_back(stream); | 679 output_streams_.push_back(stream); |
642 return stream; | 680 return stream; |
643 } | 681 } |
644 | 682 |
645 std::string AudioManagerMac::GetDefaultOutputDeviceID() { | 683 std::string AudioManagerMac::GetDefaultOutputDeviceID() { |
646 DCHECK(GetTaskRunner()->BelongsToCurrentThread()); | 684 DCHECK(task_runner_->BelongsToCurrentThread()); |
647 AudioDeviceID device_id = kAudioObjectUnknown; | 685 AudioDeviceID device_id = kAudioObjectUnknown; |
648 if (!GetDefaultOutputDevice(&device_id)) | 686 if (!GetDefaultOutputDevice(&device_id)) |
649 return std::string(); | 687 return std::string(); |
650 | 688 |
651 const AudioObjectPropertyAddress property_address = { | 689 const AudioObjectPropertyAddress property_address = { |
652 kAudioDevicePropertyDeviceUID, | 690 kAudioDevicePropertyDeviceUID, |
653 kAudioObjectPropertyScopeGlobal, | 691 kAudioObjectPropertyScopeGlobal, |
654 kAudioObjectPropertyElementMaster | 692 kAudioObjectPropertyElementMaster |
655 }; | 693 }; |
656 CFStringRef device_uid = NULL; | 694 CFStringRef device_uid = NULL; |
(...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
735 channel_layout = CHANNEL_LAYOUT_DISCRETE; | 773 channel_layout = CHANNEL_LAYOUT_DISCRETE; |
736 } | 774 } |
737 | 775 |
738 AudioParameters params(AudioParameters::AUDIO_PCM_LOW_LATENCY, channel_layout, | 776 AudioParameters params(AudioParameters::AUDIO_PCM_LOW_LATENCY, channel_layout, |
739 hardware_sample_rate, 16, buffer_size); | 777 hardware_sample_rate, 16, buffer_size); |
740 params.set_channels_for_discrete(output_channels); | 778 params.set_channels_for_discrete(output_channels); |
741 return params; | 779 return params; |
742 } | 780 } |
743 | 781 |
744 void AudioManagerMac::InitializeOnAudioThread() { | 782 void AudioManagerMac::InitializeOnAudioThread() { |
745 DCHECK(GetTaskRunner()->BelongsToCurrentThread()); | 783 DCHECK(task_runner_->BelongsToCurrentThread()); |
746 power_observer_.reset(new AudioPowerObserver()); | 784 power_observer_.reset(new AudioPowerObserver()); |
747 } | 785 } |
748 | 786 |
| 787 void AudioManagerMac::ShutdownOnAudioThread() { |
| 788 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 789 output_device_listener_.reset(); |
| 790 power_observer_.reset(); |
| 791 |
| 792 // Since CoreAudio calls have to run on the UI thread and browser shutdown |
| 793 // doesn't wait for outstanding tasks to complete, we may have input/output |
| 794 // streams still running at shutdown. |
| 795 // |
| 796 // To avoid calls into destructed classes, we need to stop the OS callbacks |
| 797 // by stopping the streams. Note: The streams are leaked since process |
| 798 // destruction is imminent. |
| 799 // |
| 800 // See http://crbug.com/354139 for crash details. |
| 801 StopStreams(&basic_input_streams_); |
| 802 StopStreams(&low_latency_input_streams_); |
| 803 StopStreams(&output_streams_); |
| 804 } |
| 805 |
749 void AudioManagerMac::HandleDeviceChanges() { | 806 void AudioManagerMac::HandleDeviceChanges() { |
750 DCHECK(GetTaskRunner()->BelongsToCurrentThread()); | 807 DCHECK(task_runner_->BelongsToCurrentThread()); |
751 const int new_sample_rate = HardwareSampleRate(); | 808 const int new_sample_rate = HardwareSampleRate(); |
752 AudioDeviceID new_output_device; | 809 AudioDeviceID new_output_device; |
753 GetDefaultOutputDevice(&new_output_device); | 810 GetDefaultOutputDevice(&new_output_device); |
754 | 811 |
755 if (current_sample_rate_ == new_sample_rate && | 812 if (current_sample_rate_ == new_sample_rate && |
756 current_output_device_ == new_output_device) { | 813 current_output_device_ == new_output_device) { |
757 return; | 814 return; |
758 } | 815 } |
759 | 816 |
760 current_sample_rate_ = new_sample_rate; | 817 current_sample_rate_ = new_sample_rate; |
(...skipping 21 matching lines...) Expand all Loading... |
782 if (sample_rate <= 96000) | 839 if (sample_rate <= 96000) |
783 buffer_size = 2 * kMinimumInputOutputBufferSize; | 840 buffer_size = 2 * kMinimumInputOutputBufferSize; |
784 else if (sample_rate <= 192000) | 841 else if (sample_rate <= 192000) |
785 buffer_size = 4 * kMinimumInputOutputBufferSize; | 842 buffer_size = 4 * kMinimumInputOutputBufferSize; |
786 } | 843 } |
787 | 844 |
788 return buffer_size; | 845 return buffer_size; |
789 } | 846 } |
790 | 847 |
791 bool AudioManagerMac::IsSuspending() const { | 848 bool AudioManagerMac::IsSuspending() const { |
792 DCHECK(GetTaskRunner()->BelongsToCurrentThread()); | 849 DCHECK(task_runner_->BelongsToCurrentThread()); |
793 return power_observer_->IsSuspending(); | 850 return power_observer_->IsSuspending(); |
794 } | 851 } |
795 | 852 |
796 bool AudioManagerMac::ShouldDeferStreamStart() const { | 853 bool AudioManagerMac::ShouldDeferStreamStart() const { |
797 DCHECK(GetTaskRunner()->BelongsToCurrentThread()); | 854 DCHECK(task_runner_->BelongsToCurrentThread()); |
798 return power_observer_->ShouldDeferStreamStart(); | 855 return power_observer_->ShouldDeferStreamStart(); |
799 } | 856 } |
800 | 857 |
801 bool AudioManagerMac::IsOnBatteryPower() const { | 858 bool AudioManagerMac::IsOnBatteryPower() const { |
802 DCHECK(GetTaskRunner()->BelongsToCurrentThread()); | 859 DCHECK(task_runner_->BelongsToCurrentThread()); |
803 return power_observer_->IsOnBatteryPower(); | 860 return power_observer_->IsOnBatteryPower(); |
804 } | 861 } |
805 | 862 |
806 size_t AudioManagerMac::GetNumberOfResumeNotifications() const { | 863 size_t AudioManagerMac::GetNumberOfResumeNotifications() const { |
807 DCHECK(GetTaskRunner()->BelongsToCurrentThread()); | 864 DCHECK(task_runner_->BelongsToCurrentThread()); |
808 return power_observer_->num_resume_notifications(); | 865 return power_observer_->num_resume_notifications(); |
809 } | 866 } |
810 | 867 |
811 bool AudioManagerMac::MaybeChangeBufferSize(AudioDeviceID device_id, | 868 bool AudioManagerMac::MaybeChangeBufferSize(AudioDeviceID device_id, |
812 AudioUnit audio_unit, | 869 AudioUnit audio_unit, |
813 AudioUnitElement element, | 870 AudioUnitElement element, |
814 size_t desired_buffer_size, | 871 size_t desired_buffer_size, |
815 bool* size_was_changed, | 872 bool* size_was_changed, |
816 size_t* io_buffer_frame_size) { | 873 size_t* io_buffer_frame_size) { |
817 DCHECK(GetTaskRunner()->BelongsToCurrentThread()); | 874 DCHECK(task_runner_->BelongsToCurrentThread()); |
818 const bool is_input = (element == 1); | 875 const bool is_input = (element == 1); |
819 DVLOG(1) << "MaybeChangeBufferSize(id=0x" << std::hex << device_id | 876 DVLOG(1) << "MaybeChangeBufferSize(id=0x" << std::hex << device_id |
820 << ", is_input=" << is_input << ", desired_buffer_size=" << std::dec | 877 << ", is_input=" << is_input << ", desired_buffer_size=" << std::dec |
821 << desired_buffer_size << ")"; | 878 << desired_buffer_size << ")"; |
822 | 879 |
823 *size_was_changed = false; | 880 *size_was_changed = false; |
824 *io_buffer_frame_size = 0; | 881 *io_buffer_frame_size = 0; |
825 | 882 |
826 // Log the device name (and id) for debugging purposes. | 883 // Log the device name (and id) for debugging purposes. |
827 std::string device_name = GetAudioDeviceNameFromDeviceId(device_id, is_input); | 884 std::string device_name = GetAudioDeviceNameFromDeviceId(device_id, is_input); |
(...skipping 99 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
927 basic_input_streams_.end(), | 984 basic_input_streams_.end(), |
928 stream); | 985 stream); |
929 if (stream_it == basic_input_streams_.end()) | 986 if (stream_it == basic_input_streams_.end()) |
930 low_latency_input_streams_.remove(static_cast<AUAudioInputStream*>(stream)); | 987 low_latency_input_streams_.remove(static_cast<AUAudioInputStream*>(stream)); |
931 else | 988 else |
932 basic_input_streams_.erase(stream_it); | 989 basic_input_streams_.erase(stream_it); |
933 | 990 |
934 AudioManagerBase::ReleaseInputStream(stream); | 991 AudioManagerBase::ReleaseInputStream(stream); |
935 } | 992 } |
936 | 993 |
937 ScopedAudioManagerPtr CreateAudioManager( | 994 AudioManager* CreateAudioManager(AudioLogFactory* audio_log_factory) { |
938 scoped_refptr<base::SingleThreadTaskRunner> task_runner, | 995 return new AudioManagerMac(audio_log_factory); |
939 scoped_refptr<base::SingleThreadTaskRunner> worker_task_runner, | |
940 AudioLogFactory* audio_log_factory) { | |
941 return ScopedAudioManagerPtr( | |
942 new AudioManagerMac(std::move(task_runner), std::move(worker_task_runner), | |
943 audio_log_factory)); | |
944 } | 996 } |
945 | 997 |
946 } // namespace media | 998 } // namespace media |
OLD | NEW |