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.
5 #include "media/audio/mac/audio_manager_mac.h"
7 #include <CoreAudio/AudioHardware.h>
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/power_monitor/power_monitor.h"
15 #include "base/power_monitor/power_observer.h"
16 #include "base/strings/sys_string_conversions.h"
17 #include "base/threading/thread_checker.h"
18 #include "media/audio/audio_parameters.h"
19 #include "media/audio/mac/audio_auhal_mac.h"
20 #include "media/audio/mac/audio_input_mac.h"
21 #include "media/audio/mac/audio_low_latency_input_mac.h"
22 #include "media/audio/mac/audio_low_latency_output_mac.h"
23 #include "media/base/bind_to_current_loop.h"
24 #include "media/base/channel_layout.h"
25 #include "media/base/limits.h"
26 #include "media/base/media_switches.h"
30 // Maximum number of output streams that can be open simultaneously.
31 static const int kMaxOutputStreams = 50;
33 // Default buffer size in samples for low-latency input and output streams.
34 static const int kDefaultLowLatencyBufferSize = 128;
36 // Default sample-rate on most Apple hardware.
37 static const int kFallbackSampleRate = 44100;
39 static bool HasAudioHardware(AudioObjectPropertySelector selector) {
40 AudioDeviceID output_device_id = kAudioObjectUnknown;
41 const AudioObjectPropertyAddress property_address = {
43 kAudioObjectPropertyScopeGlobal, // mScope
44 kAudioObjectPropertyElementMaster // mElement
46 UInt32 output_device_id_size = static_cast<UInt32>(sizeof(output_device_id));
47 OSStatus err = AudioObjectGetPropertyData(kAudioObjectSystemObject,
49 0, // inQualifierDataSize
50 NULL, // inQualifierData
51 &output_device_id_size,
53 return err == kAudioHardwareNoError &&
54 output_device_id != kAudioObjectUnknown;
57 // Retrieves information on audio devices, and prepends the default
58 // device to the list if the list is non-empty.
59 static void GetAudioDeviceInfo(bool is_input,
60 media::AudioDeviceNames* device_names) {
61 // Query the number of total devices.
62 AudioObjectPropertyAddress property_address = {
63 kAudioHardwarePropertyDevices,
64 kAudioObjectPropertyScopeGlobal,
65 kAudioObjectPropertyElementMaster
68 OSStatus result = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject,
76 int device_count = size / sizeof(AudioDeviceID);
78 // Get the array of device ids for all the devices, which includes both
79 // input devices and output devices.
80 scoped_ptr_malloc<AudioDeviceID>
81 devices(reinterpret_cast<AudioDeviceID*>(malloc(size)));
82 AudioDeviceID* device_ids = devices.get();
83 result = AudioObjectGetPropertyData(kAudioObjectSystemObject,
92 // Iterate over all available devices to gather information.
93 for (int i = 0; i < device_count; ++i) {
94 // Get the number of input or output channels of the device.
95 property_address.mScope = is_input ?
96 kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput;
97 property_address.mSelector = kAudioDevicePropertyStreams;
99 result = AudioObjectGetPropertyDataSize(device_ids[i],
108 CFStringRef uid = NULL;
110 property_address.mSelector = kAudioDevicePropertyDeviceUID;
111 property_address.mScope = kAudioObjectPropertyScopeGlobal;
112 result = AudioObjectGetPropertyData(device_ids[i],
122 CFStringRef name = NULL;
123 property_address.mSelector = kAudioObjectPropertyName;
124 property_address.mScope = kAudioObjectPropertyScopeGlobal;
125 result = AudioObjectGetPropertyData(device_ids[i],
137 // Store the device name and UID.
138 media::AudioDeviceName device_name;
139 device_name.device_name = base::SysCFStringRefToUTF8(name);
140 device_name.unique_id = base::SysCFStringRefToUTF8(uid);
141 device_names->push_back(device_name);
143 // We are responsible for releasing the returned CFObject. See the
144 // comment in the AudioHardware.h for constant
145 // kAudioDevicePropertyDeviceUID.
152 if (!device_names->empty()) {
153 // Prepend the default device to the list since we always want it to be
154 // on the top of the list for all platforms. There is no duplicate
155 // counting here since the default device has been abstracted out before.
156 media::AudioDeviceName name;
157 name.device_name = AudioManagerBase::kDefaultDeviceName;
158 name.unique_id = AudioManagerBase::kDefaultDeviceId;
159 device_names->push_front(name);
163 static AudioDeviceID GetAudioDeviceIdByUId(bool is_input,
164 const std::string& device_id) {
165 AudioObjectPropertyAddress property_address = {
166 kAudioHardwarePropertyDevices,
167 kAudioObjectPropertyScopeGlobal,
168 kAudioObjectPropertyElementMaster
170 AudioDeviceID audio_device_id = kAudioObjectUnknown;
171 UInt32 device_size = sizeof(audio_device_id);
172 OSStatus result = -1;
174 if (device_id == AudioManagerBase::kDefaultDeviceId || device_id.empty()) {
176 property_address.mSelector = is_input ?
177 kAudioHardwarePropertyDefaultInputDevice :
178 kAudioHardwarePropertyDefaultOutputDevice;
180 result = AudioObjectGetPropertyData(kAudioObjectSystemObject,
187 // Non-default device.
188 base::ScopedCFTypeRef<CFStringRef> uid(
189 base::SysUTF8ToCFStringRef(device_id));
190 AudioValueTranslation value;
191 value.mInputData = &uid;
192 value.mInputDataSize = sizeof(CFStringRef);
193 value.mOutputData = &audio_device_id;
194 value.mOutputDataSize = device_size;
195 UInt32 translation_size = sizeof(AudioValueTranslation);
197 property_address.mSelector = kAudioHardwarePropertyDeviceForUID;
198 result = AudioObjectGetPropertyData(kAudioObjectSystemObject,
207 OSSTATUS_DLOG(WARNING, result) << "Unable to query device " << device_id
208 << " for AudioDeviceID";
211 return audio_device_id;
214 class AudioManagerMac::AudioPowerObserver : public base::PowerObserver {
217 : is_suspending_(false),
218 is_monitoring_(base::PowerMonitor::Get()) {
219 // The PowerMonitor requires signifcant setup (a CFRunLoop and preallocated
220 // IO ports) so it's not available under unit tests. See the OSX impl of
221 // base::PowerMonitorDeviceSource for more details.
224 base::PowerMonitor::Get()->AddObserver(this);
227 virtual ~AudioPowerObserver() {
228 DCHECK(thread_checker_.CalledOnValidThread());
231 base::PowerMonitor::Get()->RemoveObserver(this);
234 bool ShouldDeferOutputStreamStart() {
235 DCHECK(thread_checker_.CalledOnValidThread());
236 // Start() should be deferred if the system is in the middle of a suspend or
237 // has recently started the process of resuming.
238 return is_suspending_ || base::TimeTicks::Now() < earliest_start_time_;
242 virtual void OnSuspend() OVERRIDE {
243 DCHECK(thread_checker_.CalledOnValidThread());
244 is_suspending_ = true;
247 virtual void OnResume() OVERRIDE {
248 DCHECK(thread_checker_.CalledOnValidThread());
249 is_suspending_ = false;
250 earliest_start_time_ = base::TimeTicks::Now() +
251 base::TimeDelta::FromSeconds(kStartDelayInSecsForPowerEvents);
255 const bool is_monitoring_;
256 base::TimeTicks earliest_start_time_;
257 base::ThreadChecker thread_checker_;
259 DISALLOW_COPY_AND_ASSIGN(AudioPowerObserver);
262 AudioManagerMac::AudioManagerMac(AudioLogFactory* audio_log_factory)
263 : AudioManagerBase(audio_log_factory),
264 current_sample_rate_(0),
265 current_output_device_(kAudioDeviceUnknown) {
266 SetMaxOutputStreamsAllowed(kMaxOutputStreams);
268 // Task must be posted last to avoid races from handing out "this" to the
269 // audio thread. Always PostTask even if we're on the right thread since
270 // AudioManager creation is on the startup path and this may be slow.
271 GetTaskRunner()->PostTask(FROM_HERE, base::Bind(
272 &AudioManagerMac::InitializeOnAudioThread, base::Unretained(this)));
275 AudioManagerMac::~AudioManagerMac() {
276 if (GetTaskRunner()->BelongsToCurrentThread()) {
277 ShutdownOnAudioThread();
279 // It's safe to post a task here since Shutdown() will wait for all tasks to
280 // complete before returning.
281 GetTaskRunner()->PostTask(FROM_HERE, base::Bind(
282 &AudioManagerMac::ShutdownOnAudioThread, base::Unretained(this)));
288 bool AudioManagerMac::HasAudioOutputDevices() {
289 return HasAudioHardware(kAudioHardwarePropertyDefaultOutputDevice);
292 bool AudioManagerMac::HasAudioInputDevices() {
293 return HasAudioHardware(kAudioHardwarePropertyDefaultInputDevice);
296 // TODO(xians): There are several places on the OSX specific code which
297 // could benefit from these helper functions.
298 bool AudioManagerMac::GetDefaultInputDevice(
299 AudioDeviceID* device) {
300 return GetDefaultDevice(device, true);
303 bool AudioManagerMac::GetDefaultOutputDevice(
304 AudioDeviceID* device) {
305 return GetDefaultDevice(device, false);
308 bool AudioManagerMac::GetDefaultDevice(
309 AudioDeviceID* device, bool input) {
312 // Obtain the current output device selected by the user.
313 AudioObjectPropertyAddress pa;
314 pa.mSelector = input ? kAudioHardwarePropertyDefaultInputDevice :
315 kAudioHardwarePropertyDefaultOutputDevice;
316 pa.mScope = kAudioObjectPropertyScopeGlobal;
317 pa.mElement = kAudioObjectPropertyElementMaster;
319 UInt32 size = sizeof(*device);
321 OSStatus result = AudioObjectGetPropertyData(
322 kAudioObjectSystemObject,
329 if ((result != kAudioHardwareNoError) || (*device == kAudioDeviceUnknown)) {
330 DLOG(ERROR) << "Error getting default AudioDevice.";
337 bool AudioManagerMac::GetDefaultOutputChannels(
339 AudioDeviceID device;
340 if (!GetDefaultOutputDevice(&device))
343 return GetDeviceChannels(device,
344 kAudioDevicePropertyScopeOutput,
348 bool AudioManagerMac::GetDeviceChannels(
349 AudioDeviceID device,
350 AudioObjectPropertyScope scope,
354 // Get stream configuration.
355 AudioObjectPropertyAddress pa;
356 pa.mSelector = kAudioDevicePropertyStreamConfiguration;
358 pa.mElement = kAudioObjectPropertyElementMaster;
361 OSStatus result = AudioObjectGetPropertyDataSize(device, &pa, 0, 0, &size);
362 if (result != noErr || !size)
366 scoped_ptr<uint8[]> list_storage(new uint8[size]);
367 AudioBufferList& buffer_list =
368 *reinterpret_cast<AudioBufferList*>(list_storage.get());
370 result = AudioObjectGetPropertyData(
380 // Determine number of input channels.
381 int channels_per_frame = buffer_list.mNumberBuffers > 0 ?
382 buffer_list.mBuffers[0].mNumberChannels : 0;
383 if (channels_per_frame == 1 && buffer_list.mNumberBuffers > 1) {
385 *channels = buffer_list.mNumberBuffers;
388 *channels = channels_per_frame;
394 int AudioManagerMac::HardwareSampleRateForDevice(AudioDeviceID device_id) {
395 Float64 nominal_sample_rate;
396 UInt32 info_size = sizeof(nominal_sample_rate);
398 static const AudioObjectPropertyAddress kNominalSampleRateAddress = {
399 kAudioDevicePropertyNominalSampleRate,
400 kAudioObjectPropertyScopeGlobal,
401 kAudioObjectPropertyElementMaster
403 OSStatus result = AudioObjectGetPropertyData(
405 &kNominalSampleRateAddress,
409 &nominal_sample_rate);
410 if (result != noErr) {
411 OSSTATUS_DLOG(WARNING, result)
412 << "Could not get default sample rate for device: " << device_id;
416 return static_cast<int>(nominal_sample_rate);
419 int AudioManagerMac::HardwareSampleRate() {
420 // Determine the default output device's sample-rate.
421 AudioDeviceID device_id = kAudioObjectUnknown;
422 if (!GetDefaultOutputDevice(&device_id))
423 return kFallbackSampleRate;
425 return HardwareSampleRateForDevice(device_id);
428 void AudioManagerMac::GetAudioInputDeviceNames(
429 media::AudioDeviceNames* device_names) {
430 DCHECK(device_names->empty());
431 GetAudioDeviceInfo(true, device_names);
434 void AudioManagerMac::GetAudioOutputDeviceNames(
435 media::AudioDeviceNames* device_names) {
436 DCHECK(device_names->empty());
437 GetAudioDeviceInfo(false, device_names);
440 AudioParameters AudioManagerMac::GetInputStreamParameters(
441 const std::string& device_id) {
442 AudioDeviceID device = GetAudioDeviceIdByUId(true, device_id);
443 if (device == kAudioObjectUnknown) {
444 DLOG(ERROR) << "Invalid device " << device_id;
445 return AudioParameters(
446 AudioParameters::AUDIO_PCM_LOW_LATENCY, CHANNEL_LAYOUT_STEREO,
447 kFallbackSampleRate, 16, ChooseBufferSize(kFallbackSampleRate));
451 ChannelLayout channel_layout = CHANNEL_LAYOUT_STEREO;
452 if (GetDeviceChannels(device, kAudioDevicePropertyScopeInput, &channels) &&
454 channel_layout = GuessChannelLayout(channels);
456 DLOG(ERROR) << "Failed to get the device channels, use stereo as default "
457 << "for device " << device_id;
460 int sample_rate = HardwareSampleRateForDevice(device);
462 sample_rate = kFallbackSampleRate;
464 // Due to the sharing of the input and output buffer sizes, we need to choose
465 // the input buffer size based on the output sample rate. See
466 // http://crbug.com/154352.
467 const int buffer_size = ChooseBufferSize(sample_rate);
469 // TODO(xians): query the native channel layout for the specific device.
470 return AudioParameters(
471 AudioParameters::AUDIO_PCM_LOW_LATENCY, channel_layout,
472 sample_rate, 16, buffer_size);
475 std::string AudioManagerMac::GetAssociatedOutputDeviceID(
476 const std::string& input_device_id) {
477 AudioDeviceID device = GetAudioDeviceIdByUId(true, input_device_id);
478 if (device == kAudioObjectUnknown)
479 return std::string();
482 AudioObjectPropertyAddress pa = {
483 kAudioDevicePropertyRelatedDevices,
484 kAudioDevicePropertyScopeOutput,
485 kAudioObjectPropertyElementMaster
487 OSStatus result = AudioObjectGetPropertyDataSize(device, &pa, 0, 0, &size);
489 return std::string();
491 int device_count = size / sizeof(AudioDeviceID);
492 scoped_ptr_malloc<AudioDeviceID>
493 devices(reinterpret_cast<AudioDeviceID*>(malloc(size)));
494 result = AudioObjectGetPropertyData(
495 device, &pa, 0, NULL, &size, devices.get());
497 return std::string();
499 std::vector<std::string> associated_devices;
500 for (int i = 0; i < device_count; ++i) {
501 // Get the number of output channels of the device.
502 pa.mSelector = kAudioDevicePropertyStreams;
504 result = AudioObjectGetPropertyDataSize(devices.get()[i],
510 continue; // Skip if there aren't any output channels.
513 CFStringRef uid = NULL;
515 pa.mSelector = kAudioDevicePropertyDeviceUID;
516 result = AudioObjectGetPropertyData(devices.get()[i],
525 std::string ret(base::SysCFStringRefToUTF8(uid));
527 associated_devices.push_back(ret);
530 // No matching device found.
531 if (associated_devices.empty())
532 return std::string();
534 // Return the device if there is only one associated device.
535 if (associated_devices.size() == 1)
536 return associated_devices[0];
538 // When there are multiple associated devices, we currently do not have a way
539 // to detect if a device (e.g. a digital output device) is actually connected
540 // to an endpoint, so we cannot randomly pick a device.
541 // We pick the device iff the associated device is the default output device.
542 const std::string default_device = GetDefaultOutputDeviceID();
543 for (std::vector<std::string>::const_iterator iter =
544 associated_devices.begin();
545 iter != associated_devices.end(); ++iter) {
546 if (default_device == *iter)
550 // Failed to figure out which is the matching device, return an emtpy string.
551 return std::string();
554 AudioOutputStream* AudioManagerMac::MakeLinearOutputStream(
555 const AudioParameters& params) {
556 return MakeLowLatencyOutputStream(params, std::string());
559 AudioOutputStream* AudioManagerMac::MakeLowLatencyOutputStream(
560 const AudioParameters& params,
561 const std::string& device_id) {
562 AudioDeviceID device = GetAudioDeviceIdByUId(false, device_id);
563 if (device == kAudioObjectUnknown) {
564 DLOG(ERROR) << "Failed to open output device: " << device_id;
568 // Lazily create the audio device listener on the first stream creation.
569 if (!output_device_listener_) {
570 output_device_listener_.reset(new AudioDeviceListenerMac(base::Bind(
571 &AudioManagerMac::HandleDeviceChanges, base::Unretained(this))));
572 // Only set the current output device for the default device.
573 if (device_id == AudioManagerBase::kDefaultDeviceId || device_id.empty())
574 current_output_device_ = device;
575 // Just use the current sample rate since we don't allow non-native sample
577 current_sample_rate_ = params.sample_rate();
580 return new AUHALStream(this, params, device);
583 std::string AudioManagerMac::GetDefaultOutputDeviceID() {
584 AudioDeviceID device_id = kAudioObjectUnknown;
585 if (!GetDefaultOutputDevice(&device_id))
586 return std::string();
588 const AudioObjectPropertyAddress property_address = {
589 kAudioDevicePropertyDeviceUID,
590 kAudioObjectPropertyScopeGlobal,
591 kAudioObjectPropertyElementMaster
593 CFStringRef device_uid = NULL;
594 UInt32 size = sizeof(device_uid);
595 OSStatus status = AudioObjectGetPropertyData(device_id,
601 if (status != kAudioHardwareNoError || !device_uid)
602 return std::string();
604 std::string ret(base::SysCFStringRefToUTF8(device_uid));
605 CFRelease(device_uid);
610 AudioInputStream* AudioManagerMac::MakeLinearInputStream(
611 const AudioParameters& params, const std::string& device_id) {
612 DCHECK_EQ(AudioParameters::AUDIO_PCM_LINEAR, params.format());
613 return new PCMQueueInAudioInputStream(this, params);
616 AudioInputStream* AudioManagerMac::MakeLowLatencyInputStream(
617 const AudioParameters& params, const std::string& device_id) {
618 DCHECK_EQ(AudioParameters::AUDIO_PCM_LOW_LATENCY, params.format());
619 // Gets the AudioDeviceID that refers to the AudioInputDevice with the device
620 // unique id. This AudioDeviceID is used to set the device for Audio Unit.
621 AudioDeviceID audio_device_id = GetAudioDeviceIdByUId(true, device_id);
622 AudioInputStream* stream = NULL;
623 if (audio_device_id != kAudioObjectUnknown) {
624 // AUAudioInputStream needs to be fed the preferred audio output parameters
625 // of the matching device so that the buffer size of both input and output
626 // can be matched. See constructor of AUAudioInputStream for more.
627 const std::string associated_output_device(
628 GetAssociatedOutputDeviceID(device_id));
629 const AudioParameters output_params =
630 GetPreferredOutputStreamParameters(
631 associated_output_device.empty() ?
632 AudioManagerBase::kDefaultDeviceId : associated_output_device,
634 stream = new AUAudioInputStream(this, params, output_params,
641 AudioParameters AudioManagerMac::GetPreferredOutputStreamParameters(
642 const std::string& output_device_id,
643 const AudioParameters& input_params) {
644 const AudioDeviceID device = GetAudioDeviceIdByUId(false, output_device_id);
645 if (device == kAudioObjectUnknown) {
646 DLOG(ERROR) << "Invalid output device " << output_device_id;
647 return input_params.IsValid() ? input_params : AudioParameters(
648 AudioParameters::AUDIO_PCM_LOW_LATENCY, CHANNEL_LAYOUT_STEREO,
649 kFallbackSampleRate, 16, ChooseBufferSize(kFallbackSampleRate));
652 const bool has_valid_input_params = input_params.IsValid();
653 const int hardware_sample_rate = HardwareSampleRateForDevice(device);
654 const int buffer_size = ChooseBufferSize(hardware_sample_rate);
656 int hardware_channels;
657 if (!GetDeviceChannels(device, kAudioDevicePropertyScopeOutput,
658 &hardware_channels)) {
659 hardware_channels = 2;
662 // Use the input channel count and channel layout if possible. Let OSX take
663 // care of remapping the channels; this lets user specified channel layouts
665 int output_channels = input_params.channels();
666 ChannelLayout channel_layout = input_params.channel_layout();
667 if (!has_valid_input_params || output_channels > hardware_channels) {
668 output_channels = hardware_channels;
669 channel_layout = GuessChannelLayout(output_channels);
670 if (channel_layout == CHANNEL_LAYOUT_UNSUPPORTED)
671 channel_layout = CHANNEL_LAYOUT_DISCRETE;
674 const int input_channels =
675 has_valid_input_params ? input_params.input_channels() : 0;
676 if (input_channels > 0) {
677 // TODO(xians): given the limitations of the AudioOutputStream
678 // back-ends used with synchronized I/O, we hard-code to stereo.
679 // Specifically, this is a limitation of AudioSynchronizedStream which
680 // can be removed as part of the work to consolidate these back-ends.
681 channel_layout = CHANNEL_LAYOUT_STEREO;
684 return AudioParameters(
685 AudioParameters::AUDIO_PCM_LOW_LATENCY, channel_layout, output_channels,
686 input_channels, hardware_sample_rate, 16, buffer_size,
687 AudioParameters::NO_EFFECTS);
690 void AudioManagerMac::InitializeOnAudioThread() {
691 DCHECK(GetTaskRunner()->BelongsToCurrentThread());
692 power_observer_.reset(new AudioPowerObserver());
695 void AudioManagerMac::ShutdownOnAudioThread() {
696 DCHECK(GetTaskRunner()->BelongsToCurrentThread());
697 output_device_listener_.reset();
698 power_observer_.reset();
701 void AudioManagerMac::HandleDeviceChanges() {
702 if (!GetTaskRunner()->BelongsToCurrentThread()) {
703 GetTaskRunner()->PostTask(FROM_HERE, base::Bind(
704 &AudioManagerMac::HandleDeviceChanges, base::Unretained(this)));
708 int new_sample_rate = HardwareSampleRate();
709 AudioDeviceID new_output_device;
710 GetDefaultOutputDevice(&new_output_device);
712 if (current_sample_rate_ == new_sample_rate &&
713 current_output_device_ == new_output_device)
716 current_sample_rate_ = new_sample_rate;
717 current_output_device_ = new_output_device;
718 NotifyAllOutputDeviceChangeListeners();
721 int AudioManagerMac::ChooseBufferSize(int output_sample_rate) {
722 int buffer_size = kDefaultLowLatencyBufferSize;
723 const int user_buffer_size = GetUserBufferSize();
724 if (user_buffer_size) {
725 buffer_size = user_buffer_size;
726 } else if (output_sample_rate > 48000) {
727 // The default buffer size is too small for higher sample rates and may lead
728 // to glitching. Adjust upwards by multiples of the default size.
729 if (output_sample_rate <= 96000)
730 buffer_size = 2 * kDefaultLowLatencyBufferSize;
731 else if (output_sample_rate <= 192000)
732 buffer_size = 4 * kDefaultLowLatencyBufferSize;
738 bool AudioManagerMac::ShouldDeferOutputStreamStart() {
739 DCHECK(GetTaskRunner()->BelongsToCurrentThread());
740 return power_observer_->ShouldDeferOutputStreamStart();
743 AudioManager* CreateAudioManager(AudioLogFactory* audio_log_factory) {
744 return new AudioManagerMac(audio_log_factory);