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 | |
308 class AudioManagerMac::AudioPowerObserver : public base::PowerObserver { | 296 class AudioManagerMac::AudioPowerObserver : public base::PowerObserver { |
309 public: | 297 public: |
310 AudioPowerObserver() | 298 AudioPowerObserver() |
311 : is_suspending_(false), | 299 : is_suspending_(false), |
312 is_monitoring_(base::PowerMonitor::Get()), | 300 is_monitoring_(base::PowerMonitor::Get()), |
313 num_resume_notifications_(0) { | 301 num_resume_notifications_(0) { |
314 // The PowerMonitor requires significant setup (a CFRunLoop and preallocated | 302 // The PowerMonitor requires significant setup (a CFRunLoop and preallocated |
315 // IO ports) so it's not available under unit tests. See the OSX impl of | 303 // IO ports) so it's not available under unit tests. See the OSX impl of |
316 // base::PowerMonitorDeviceSource for more details. | 304 // base::PowerMonitorDeviceSource for more details. |
317 if (!is_monitoring_) | 305 if (!is_monitoring_) |
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
363 | 351 |
364 bool is_suspending_; | 352 bool is_suspending_; |
365 const bool is_monitoring_; | 353 const bool is_monitoring_; |
366 base::TimeTicks earliest_start_time_; | 354 base::TimeTicks earliest_start_time_; |
367 base::ThreadChecker thread_checker_; | 355 base::ThreadChecker thread_checker_; |
368 size_t num_resume_notifications_; | 356 size_t num_resume_notifications_; |
369 | 357 |
370 DISALLOW_COPY_AND_ASSIGN(AudioPowerObserver); | 358 DISALLOW_COPY_AND_ASSIGN(AudioPowerObserver); |
371 }; | 359 }; |
372 | 360 |
373 AudioManagerMac::AudioManagerMac(AudioLogFactory* audio_log_factory) | 361 AudioManagerMac::AudioManagerMac( |
374 : AudioManagerBase(audio_log_factory), | 362 scoped_refptr<base::SingleThreadTaskRunner> task_runner, |
| 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), |
375 current_sample_rate_(0), | 368 current_sample_rate_(0), |
376 current_output_device_(kAudioDeviceUnknown) { | 369 current_output_device_(kAudioDeviceUnknown) { |
377 SetMaxOutputStreamsAllowed(kMaxOutputStreams); | 370 SetMaxOutputStreamsAllowed(kMaxOutputStreams); |
378 | 371 |
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 | |
387 // Task must be posted last to avoid races from handing out "this" to the | 372 // Task must be posted last to avoid races from handing out "this" to the |
388 // audio thread. Always PostTask even if we're on the right thread since | 373 // audio thread. Always PostTask even if we're on the right thread since |
389 // AudioManager creation is on the startup path and this may be slow. | 374 // AudioManager creation is on the startup path and this may be slow. |
390 task_runner_->PostTask(FROM_HERE, | 375 GetTaskRunner()->PostTask( |
391 base::Bind(&AudioManagerMac::InitializeOnAudioThread, | 376 FROM_HERE, base::Bind(&AudioManagerMac::InitializeOnAudioThread, |
392 base::Unretained(this))); | 377 base::Unretained(this))); |
393 } | 378 } |
394 | 379 |
395 AudioManagerMac::~AudioManagerMac() { | 380 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 | |
406 Shutdown(); | 381 Shutdown(); |
407 } | 382 } |
408 | 383 |
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 | |
422 bool AudioManagerMac::HasAudioOutputDevices() { | 384 bool AudioManagerMac::HasAudioOutputDevices() { |
423 return HasAudioHardware(kAudioHardwarePropertyDefaultOutputDevice); | 385 return HasAudioHardware(kAudioHardwarePropertyDefaultOutputDevice); |
424 } | 386 } |
425 | 387 |
426 bool AudioManagerMac::HasAudioInputDevices() { | 388 bool AudioManagerMac::HasAudioInputDevices() { |
427 return HasAudioHardware(kAudioHardwarePropertyDefaultInputDevice); | 389 return HasAudioHardware(kAudioHardwarePropertyDefaultInputDevice); |
428 } | 390 } |
429 | 391 |
430 // static | 392 // static |
431 bool AudioManagerMac::GetDeviceChannels(AudioDeviceID device, | 393 bool AudioManagerMac::GetDeviceChannels(AudioDeviceID device, |
(...skipping 117 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
549 const int buffer_size = ChooseBufferSize(true, sample_rate); | 511 const int buffer_size = ChooseBufferSize(true, sample_rate); |
550 | 512 |
551 // TODO(xians): query the native channel layout for the specific device. | 513 // TODO(xians): query the native channel layout for the specific device. |
552 return AudioParameters( | 514 return AudioParameters( |
553 AudioParameters::AUDIO_PCM_LOW_LATENCY, channel_layout, | 515 AudioParameters::AUDIO_PCM_LOW_LATENCY, channel_layout, |
554 sample_rate, 16, buffer_size); | 516 sample_rate, 16, buffer_size); |
555 } | 517 } |
556 | 518 |
557 std::string AudioManagerMac::GetAssociatedOutputDeviceID( | 519 std::string AudioManagerMac::GetAssociatedOutputDeviceID( |
558 const std::string& input_device_id) { | 520 const std::string& input_device_id) { |
559 DCHECK(task_runner_->BelongsToCurrentThread()); | 521 DCHECK(GetTaskRunner()->BelongsToCurrentThread()); |
560 AudioDeviceID device = GetAudioDeviceIdByUId(true, input_device_id); | 522 AudioDeviceID device = GetAudioDeviceIdByUId(true, input_device_id); |
561 if (device == kAudioObjectUnknown) | 523 if (device == kAudioObjectUnknown) |
562 return std::string(); | 524 return std::string(); |
563 | 525 |
564 UInt32 size = 0; | 526 UInt32 size = 0; |
565 AudioObjectPropertyAddress pa = { | 527 AudioObjectPropertyAddress pa = { |
566 kAudioDevicePropertyRelatedDevices, | 528 kAudioDevicePropertyRelatedDevices, |
567 kAudioDevicePropertyScopeOutput, | 529 kAudioDevicePropertyScopeOutput, |
568 kAudioObjectPropertyElementMaster | 530 kAudioObjectPropertyElementMaster |
569 }; | 531 }; |
(...skipping 104 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
674 // rates on OSX. | 636 // rates on OSX. |
675 current_sample_rate_ = params.sample_rate(); | 637 current_sample_rate_ = params.sample_rate(); |
676 } | 638 } |
677 | 639 |
678 AUHALStream* stream = new AUHALStream(this, params, device); | 640 AUHALStream* stream = new AUHALStream(this, params, device); |
679 output_streams_.push_back(stream); | 641 output_streams_.push_back(stream); |
680 return stream; | 642 return stream; |
681 } | 643 } |
682 | 644 |
683 std::string AudioManagerMac::GetDefaultOutputDeviceID() { | 645 std::string AudioManagerMac::GetDefaultOutputDeviceID() { |
684 DCHECK(task_runner_->BelongsToCurrentThread()); | 646 DCHECK(GetTaskRunner()->BelongsToCurrentThread()); |
685 AudioDeviceID device_id = kAudioObjectUnknown; | 647 AudioDeviceID device_id = kAudioObjectUnknown; |
686 if (!GetDefaultOutputDevice(&device_id)) | 648 if (!GetDefaultOutputDevice(&device_id)) |
687 return std::string(); | 649 return std::string(); |
688 | 650 |
689 const AudioObjectPropertyAddress property_address = { | 651 const AudioObjectPropertyAddress property_address = { |
690 kAudioDevicePropertyDeviceUID, | 652 kAudioDevicePropertyDeviceUID, |
691 kAudioObjectPropertyScopeGlobal, | 653 kAudioObjectPropertyScopeGlobal, |
692 kAudioObjectPropertyElementMaster | 654 kAudioObjectPropertyElementMaster |
693 }; | 655 }; |
694 CFStringRef device_uid = NULL; | 656 CFStringRef device_uid = NULL; |
(...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
773 channel_layout = CHANNEL_LAYOUT_DISCRETE; | 735 channel_layout = CHANNEL_LAYOUT_DISCRETE; |
774 } | 736 } |
775 | 737 |
776 AudioParameters params(AudioParameters::AUDIO_PCM_LOW_LATENCY, channel_layout, | 738 AudioParameters params(AudioParameters::AUDIO_PCM_LOW_LATENCY, channel_layout, |
777 hardware_sample_rate, 16, buffer_size); | 739 hardware_sample_rate, 16, buffer_size); |
778 params.set_channels_for_discrete(output_channels); | 740 params.set_channels_for_discrete(output_channels); |
779 return params; | 741 return params; |
780 } | 742 } |
781 | 743 |
782 void AudioManagerMac::InitializeOnAudioThread() { | 744 void AudioManagerMac::InitializeOnAudioThread() { |
783 DCHECK(task_runner_->BelongsToCurrentThread()); | 745 DCHECK(GetTaskRunner()->BelongsToCurrentThread()); |
784 power_observer_.reset(new AudioPowerObserver()); | 746 power_observer_.reset(new AudioPowerObserver()); |
785 } | 747 } |
786 | 748 |
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 | |
806 void AudioManagerMac::HandleDeviceChanges() { | 749 void AudioManagerMac::HandleDeviceChanges() { |
807 DCHECK(task_runner_->BelongsToCurrentThread()); | 750 DCHECK(GetTaskRunner()->BelongsToCurrentThread()); |
808 const int new_sample_rate = HardwareSampleRate(); | 751 const int new_sample_rate = HardwareSampleRate(); |
809 AudioDeviceID new_output_device; | 752 AudioDeviceID new_output_device; |
810 GetDefaultOutputDevice(&new_output_device); | 753 GetDefaultOutputDevice(&new_output_device); |
811 | 754 |
812 if (current_sample_rate_ == new_sample_rate && | 755 if (current_sample_rate_ == new_sample_rate && |
813 current_output_device_ == new_output_device) { | 756 current_output_device_ == new_output_device) { |
814 return; | 757 return; |
815 } | 758 } |
816 | 759 |
817 current_sample_rate_ = new_sample_rate; | 760 current_sample_rate_ = new_sample_rate; |
(...skipping 21 matching lines...) Expand all Loading... |
839 if (sample_rate <= 96000) | 782 if (sample_rate <= 96000) |
840 buffer_size = 2 * kMinimumInputOutputBufferSize; | 783 buffer_size = 2 * kMinimumInputOutputBufferSize; |
841 else if (sample_rate <= 192000) | 784 else if (sample_rate <= 192000) |
842 buffer_size = 4 * kMinimumInputOutputBufferSize; | 785 buffer_size = 4 * kMinimumInputOutputBufferSize; |
843 } | 786 } |
844 | 787 |
845 return buffer_size; | 788 return buffer_size; |
846 } | 789 } |
847 | 790 |
848 bool AudioManagerMac::IsSuspending() const { | 791 bool AudioManagerMac::IsSuspending() const { |
849 DCHECK(task_runner_->BelongsToCurrentThread()); | 792 DCHECK(GetTaskRunner()->BelongsToCurrentThread()); |
850 return power_observer_->IsSuspending(); | 793 return power_observer_->IsSuspending(); |
851 } | 794 } |
852 | 795 |
853 bool AudioManagerMac::ShouldDeferStreamStart() const { | 796 bool AudioManagerMac::ShouldDeferStreamStart() const { |
854 DCHECK(task_runner_->BelongsToCurrentThread()); | 797 DCHECK(GetTaskRunner()->BelongsToCurrentThread()); |
855 return power_observer_->ShouldDeferStreamStart(); | 798 return power_observer_->ShouldDeferStreamStart(); |
856 } | 799 } |
857 | 800 |
858 bool AudioManagerMac::IsOnBatteryPower() const { | 801 bool AudioManagerMac::IsOnBatteryPower() const { |
859 DCHECK(task_runner_->BelongsToCurrentThread()); | 802 DCHECK(GetTaskRunner()->BelongsToCurrentThread()); |
860 return power_observer_->IsOnBatteryPower(); | 803 return power_observer_->IsOnBatteryPower(); |
861 } | 804 } |
862 | 805 |
863 size_t AudioManagerMac::GetNumberOfResumeNotifications() const { | 806 size_t AudioManagerMac::GetNumberOfResumeNotifications() const { |
864 DCHECK(task_runner_->BelongsToCurrentThread()); | 807 DCHECK(GetTaskRunner()->BelongsToCurrentThread()); |
865 return power_observer_->num_resume_notifications(); | 808 return power_observer_->num_resume_notifications(); |
866 } | 809 } |
867 | 810 |
868 bool AudioManagerMac::MaybeChangeBufferSize(AudioDeviceID device_id, | 811 bool AudioManagerMac::MaybeChangeBufferSize(AudioDeviceID device_id, |
869 AudioUnit audio_unit, | 812 AudioUnit audio_unit, |
870 AudioUnitElement element, | 813 AudioUnitElement element, |
871 size_t desired_buffer_size, | 814 size_t desired_buffer_size, |
872 bool* size_was_changed, | 815 bool* size_was_changed, |
873 size_t* io_buffer_frame_size) { | 816 size_t* io_buffer_frame_size) { |
874 DCHECK(task_runner_->BelongsToCurrentThread()); | 817 DCHECK(GetTaskRunner()->BelongsToCurrentThread()); |
875 const bool is_input = (element == 1); | 818 const bool is_input = (element == 1); |
876 DVLOG(1) << "MaybeChangeBufferSize(id=0x" << std::hex << device_id | 819 DVLOG(1) << "MaybeChangeBufferSize(id=0x" << std::hex << device_id |
877 << ", is_input=" << is_input << ", desired_buffer_size=" << std::dec | 820 << ", is_input=" << is_input << ", desired_buffer_size=" << std::dec |
878 << desired_buffer_size << ")"; | 821 << desired_buffer_size << ")"; |
879 | 822 |
880 *size_was_changed = false; | 823 *size_was_changed = false; |
881 *io_buffer_frame_size = 0; | 824 *io_buffer_frame_size = 0; |
882 | 825 |
883 // Log the device name (and id) for debugging purposes. | 826 // Log the device name (and id) for debugging purposes. |
884 std::string device_name = GetAudioDeviceNameFromDeviceId(device_id, is_input); | 827 std::string device_name = GetAudioDeviceNameFromDeviceId(device_id, is_input); |
(...skipping 99 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
984 basic_input_streams_.end(), | 927 basic_input_streams_.end(), |
985 stream); | 928 stream); |
986 if (stream_it == basic_input_streams_.end()) | 929 if (stream_it == basic_input_streams_.end()) |
987 low_latency_input_streams_.remove(static_cast<AUAudioInputStream*>(stream)); | 930 low_latency_input_streams_.remove(static_cast<AUAudioInputStream*>(stream)); |
988 else | 931 else |
989 basic_input_streams_.erase(stream_it); | 932 basic_input_streams_.erase(stream_it); |
990 | 933 |
991 AudioManagerBase::ReleaseInputStream(stream); | 934 AudioManagerBase::ReleaseInputStream(stream); |
992 } | 935 } |
993 | 936 |
994 AudioManager* CreateAudioManager(AudioLogFactory* audio_log_factory) { | 937 ScopedAudioManagerPtr CreateAudioManager( |
995 return new AudioManagerMac(audio_log_factory); | 938 scoped_refptr<base::SingleThreadTaskRunner> task_runner, |
| 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)); |
996 } | 944 } |
997 | 945 |
998 } // namespace media | 946 } // namespace media |
OLD | NEW |