Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / media / audio / mac / audio_manager_mac.cc
blob42651ffbea8afe2190c75fa36b3b036a5b17c8ce
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"
8 #include "base/bind.h"
9 #include "base/command_line.h"
10 #include "base/mac/mac_logging.h"
11 #include "base/mac/scoped_cftyperef.h"
12 #include "base/power_monitor/power_monitor.h"
13 #include "base/power_monitor/power_observer.h"
14 #include "base/strings/sys_string_conversions.h"
15 #include "base/threading/thread_checker.h"
16 #include "media/audio/audio_parameters.h"
17 #include "media/audio/mac/audio_auhal_mac.h"
18 #include "media/audio/mac/audio_input_mac.h"
19 #include "media/audio/mac/audio_low_latency_input_mac.h"
20 #include "media/base/bind_to_current_loop.h"
21 #include "media/base/channel_layout.h"
22 #include "media/base/limits.h"
23 #include "media/base/media_switches.h"
25 namespace media {
27 // Maximum number of output streams that can be open simultaneously.
28 static const int kMaxOutputStreams = 50;
30 // Define bounds for for low-latency input and output streams.
31 static const int kMinimumInputOutputBufferSize = 128;
32 static const int kMaximumInputOutputBufferSize = 4096;
34 // Default sample-rate on most Apple hardware.
35 static const int kFallbackSampleRate = 44100;
37 static bool HasAudioHardware(AudioObjectPropertySelector selector) {
38 AudioDeviceID output_device_id = kAudioObjectUnknown;
39 const AudioObjectPropertyAddress property_address = {
40 selector,
41 kAudioObjectPropertyScopeGlobal, // mScope
42 kAudioObjectPropertyElementMaster // mElement
44 UInt32 output_device_id_size = static_cast<UInt32>(sizeof(output_device_id));
45 OSStatus err = AudioObjectGetPropertyData(kAudioObjectSystemObject,
46 &property_address,
47 0, // inQualifierDataSize
48 NULL, // inQualifierData
49 &output_device_id_size,
50 &output_device_id);
51 return err == kAudioHardwareNoError &&
52 output_device_id != kAudioObjectUnknown;
55 // Retrieves information on audio devices, and prepends the default
56 // device to the list if the list is non-empty.
57 static void GetAudioDeviceInfo(bool is_input,
58 media::AudioDeviceNames* device_names) {
59 // Query the number of total devices.
60 AudioObjectPropertyAddress property_address = {
61 kAudioHardwarePropertyDevices,
62 kAudioObjectPropertyScopeGlobal,
63 kAudioObjectPropertyElementMaster
65 UInt32 size = 0;
66 OSStatus result = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject,
67 &property_address,
69 NULL,
70 &size);
71 if (result || !size)
72 return;
74 int device_count = size / sizeof(AudioDeviceID);
76 // Get the array of device ids for all the devices, which includes both
77 // input devices and output devices.
78 scoped_ptr<AudioDeviceID, base::FreeDeleter>
79 devices(static_cast<AudioDeviceID*>(malloc(size)));
80 AudioDeviceID* device_ids = devices.get();
81 result = AudioObjectGetPropertyData(kAudioObjectSystemObject,
82 &property_address,
84 NULL,
85 &size,
86 device_ids);
87 if (result)
88 return;
90 // Iterate over all available devices to gather information.
91 for (int i = 0; i < device_count; ++i) {
92 // Get the number of input or output channels of the device.
93 property_address.mScope = is_input ?
94 kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput;
95 property_address.mSelector = kAudioDevicePropertyStreams;
96 size = 0;
97 result = AudioObjectGetPropertyDataSize(device_ids[i],
98 &property_address,
100 NULL,
101 &size);
102 if (result || !size)
103 continue;
105 // Get device UID.
106 CFStringRef uid = NULL;
107 size = sizeof(uid);
108 property_address.mSelector = kAudioDevicePropertyDeviceUID;
109 property_address.mScope = kAudioObjectPropertyScopeGlobal;
110 result = AudioObjectGetPropertyData(device_ids[i],
111 &property_address,
113 NULL,
114 &size,
115 &uid);
116 if (result)
117 continue;
119 // Get device name.
120 CFStringRef name = NULL;
121 property_address.mSelector = kAudioObjectPropertyName;
122 property_address.mScope = kAudioObjectPropertyScopeGlobal;
123 result = AudioObjectGetPropertyData(device_ids[i],
124 &property_address,
126 NULL,
127 &size,
128 &name);
129 if (result) {
130 if (uid)
131 CFRelease(uid);
132 continue;
135 // Store the device name and UID.
136 media::AudioDeviceName device_name;
137 device_name.device_name = base::SysCFStringRefToUTF8(name);
138 device_name.unique_id = base::SysCFStringRefToUTF8(uid);
139 device_names->push_back(device_name);
141 // We are responsible for releasing the returned CFObject. See the
142 // comment in the AudioHardware.h for constant
143 // kAudioDevicePropertyDeviceUID.
144 if (uid)
145 CFRelease(uid);
146 if (name)
147 CFRelease(name);
150 if (!device_names->empty()) {
151 // Prepend the default device to the list since we always want it to be
152 // on the top of the list for all platforms. There is no duplicate
153 // counting here since the default device has been abstracted out before.
154 media::AudioDeviceName name;
155 name.device_name = AudioManagerBase::kDefaultDeviceName;
156 name.unique_id = AudioManagerBase::kDefaultDeviceId;
157 device_names->push_front(name);
161 static AudioDeviceID GetAudioDeviceIdByUId(bool is_input,
162 const std::string& device_id) {
163 AudioObjectPropertyAddress property_address = {
164 kAudioHardwarePropertyDevices,
165 kAudioObjectPropertyScopeGlobal,
166 kAudioObjectPropertyElementMaster
168 AudioDeviceID audio_device_id = kAudioObjectUnknown;
169 UInt32 device_size = sizeof(audio_device_id);
170 OSStatus result = -1;
172 if (device_id == AudioManagerBase::kDefaultDeviceId || device_id.empty()) {
173 // Default Device.
174 property_address.mSelector = is_input ?
175 kAudioHardwarePropertyDefaultInputDevice :
176 kAudioHardwarePropertyDefaultOutputDevice;
178 result = AudioObjectGetPropertyData(kAudioObjectSystemObject,
179 &property_address,
182 &device_size,
183 &audio_device_id);
184 } else {
185 // Non-default device.
186 base::ScopedCFTypeRef<CFStringRef> uid(
187 base::SysUTF8ToCFStringRef(device_id));
188 AudioValueTranslation value;
189 value.mInputData = &uid;
190 value.mInputDataSize = sizeof(CFStringRef);
191 value.mOutputData = &audio_device_id;
192 value.mOutputDataSize = device_size;
193 UInt32 translation_size = sizeof(AudioValueTranslation);
195 property_address.mSelector = kAudioHardwarePropertyDeviceForUID;
196 result = AudioObjectGetPropertyData(kAudioObjectSystemObject,
197 &property_address,
200 &translation_size,
201 &value);
204 if (result) {
205 OSSTATUS_DLOG(WARNING, result) << "Unable to query device " << device_id
206 << " for AudioDeviceID";
209 return audio_device_id;
212 template <class T>
213 void StopStreams(std::list<T*>* streams) {
214 for (typename std::list<T*>::iterator it = streams->begin();
215 it != streams->end();
216 ++it) {
217 // Stop() is safe to call multiple times, so it doesn't matter if a stream
218 // has already been stopped.
219 (*it)->Stop();
221 streams->clear();
224 class AudioManagerMac::AudioPowerObserver : public base::PowerObserver {
225 public:
226 AudioPowerObserver()
227 : is_suspending_(false),
228 is_monitoring_(base::PowerMonitor::Get()) {
229 // The PowerMonitor requires signifcant setup (a CFRunLoop and preallocated
230 // IO ports) so it's not available under unit tests. See the OSX impl of
231 // base::PowerMonitorDeviceSource for more details.
232 if (!is_monitoring_)
233 return;
234 base::PowerMonitor::Get()->AddObserver(this);
237 ~AudioPowerObserver() override {
238 DCHECK(thread_checker_.CalledOnValidThread());
239 if (!is_monitoring_)
240 return;
241 base::PowerMonitor::Get()->RemoveObserver(this);
244 bool ShouldDeferStreamStart() {
245 DCHECK(thread_checker_.CalledOnValidThread());
246 // Start() should be deferred if the system is in the middle of a suspend or
247 // has recently started the process of resuming.
248 return is_suspending_ || base::TimeTicks::Now() < earliest_start_time_;
251 private:
252 void OnSuspend() override {
253 DCHECK(thread_checker_.CalledOnValidThread());
254 is_suspending_ = true;
257 void OnResume() override {
258 DCHECK(thread_checker_.CalledOnValidThread());
259 is_suspending_ = false;
260 earliest_start_time_ = base::TimeTicks::Now() +
261 base::TimeDelta::FromSeconds(kStartDelayInSecsForPowerEvents);
264 bool is_suspending_;
265 const bool is_monitoring_;
266 base::TimeTicks earliest_start_time_;
267 base::ThreadChecker thread_checker_;
269 DISALLOW_COPY_AND_ASSIGN(AudioPowerObserver);
272 AudioManagerMac::AudioManagerMac(AudioLogFactory* audio_log_factory)
273 : AudioManagerBase(audio_log_factory),
274 current_sample_rate_(0),
275 current_output_device_(kAudioDeviceUnknown) {
276 SetMaxOutputStreamsAllowed(kMaxOutputStreams);
278 // Task must be posted last to avoid races from handing out "this" to the
279 // audio thread. Always PostTask even if we're on the right thread since
280 // AudioManager creation is on the startup path and this may be slow.
281 GetTaskRunner()->PostTask(FROM_HERE, base::Bind(
282 &AudioManagerMac::InitializeOnAudioThread, base::Unretained(this)));
285 AudioManagerMac::~AudioManagerMac() {
286 if (GetTaskRunner()->BelongsToCurrentThread()) {
287 ShutdownOnAudioThread();
288 } else {
289 // It's safe to post a task here since Shutdown() will wait for all tasks to
290 // complete before returning.
291 GetTaskRunner()->PostTask(FROM_HERE, base::Bind(
292 &AudioManagerMac::ShutdownOnAudioThread, base::Unretained(this)));
295 Shutdown();
298 bool AudioManagerMac::HasAudioOutputDevices() {
299 return HasAudioHardware(kAudioHardwarePropertyDefaultOutputDevice);
302 bool AudioManagerMac::HasAudioInputDevices() {
303 return HasAudioHardware(kAudioHardwarePropertyDefaultInputDevice);
306 // TODO(xians): There are several places on the OSX specific code which
307 // could benefit from these helper functions.
308 bool AudioManagerMac::GetDefaultInputDevice(AudioDeviceID* device) {
309 return GetDefaultDevice(device, true);
312 bool AudioManagerMac::GetDefaultOutputDevice(AudioDeviceID* device) {
313 return GetDefaultDevice(device, false);
316 bool AudioManagerMac::GetDefaultDevice(AudioDeviceID* device, bool input) {
317 CHECK(device);
319 // Obtain the current output device selected by the user.
320 AudioObjectPropertyAddress pa;
321 pa.mSelector = input ? kAudioHardwarePropertyDefaultInputDevice :
322 kAudioHardwarePropertyDefaultOutputDevice;
323 pa.mScope = kAudioObjectPropertyScopeGlobal;
324 pa.mElement = kAudioObjectPropertyElementMaster;
326 UInt32 size = sizeof(*device);
327 OSStatus result = AudioObjectGetPropertyData(kAudioObjectSystemObject,
328 &pa,
331 &size,
332 device);
334 if ((result != kAudioHardwareNoError) || (*device == kAudioDeviceUnknown)) {
335 DLOG(ERROR) << "Error getting default AudioDevice.";
336 return false;
339 return true;
342 bool AudioManagerMac::GetDefaultOutputChannels(int* channels) {
343 AudioDeviceID device;
344 if (!GetDefaultOutputDevice(&device))
345 return false;
346 return GetDeviceChannels(device, kAudioDevicePropertyScopeOutput, channels);
349 bool AudioManagerMac::GetDeviceChannels(AudioDeviceID device,
350 AudioObjectPropertyScope scope,
351 int* channels) {
352 CHECK(channels);
354 // Get stream configuration.
355 AudioObjectPropertyAddress pa;
356 pa.mSelector = kAudioDevicePropertyStreamConfiguration;
357 pa.mScope = scope;
358 pa.mElement = kAudioObjectPropertyElementMaster;
360 UInt32 size;
361 OSStatus result = AudioObjectGetPropertyDataSize(device, &pa, 0, 0, &size);
362 if (result != noErr || !size)
363 return false;
365 // Allocate storage.
366 scoped_ptr<uint8[]> list_storage(new uint8[size]);
367 AudioBufferList& buffer_list =
368 *reinterpret_cast<AudioBufferList*>(list_storage.get());
370 result = AudioObjectGetPropertyData(device, &pa, 0, 0, &size, &buffer_list);
371 if (result != noErr)
372 return false;
374 // Determine number of input channels.
375 int channels_per_frame = buffer_list.mNumberBuffers > 0 ?
376 buffer_list.mBuffers[0].mNumberChannels : 0;
377 if (channels_per_frame == 1 && buffer_list.mNumberBuffers > 1) {
378 // Non-interleaved.
379 *channels = buffer_list.mNumberBuffers;
380 } else {
381 // Interleaved.
382 *channels = channels_per_frame;
385 return true;
388 int AudioManagerMac::HardwareSampleRateForDevice(AudioDeviceID device_id) {
389 Float64 nominal_sample_rate;
390 UInt32 info_size = sizeof(nominal_sample_rate);
392 static const AudioObjectPropertyAddress kNominalSampleRateAddress = {
393 kAudioDevicePropertyNominalSampleRate,
394 kAudioObjectPropertyScopeGlobal,
395 kAudioObjectPropertyElementMaster
397 OSStatus result = AudioObjectGetPropertyData(device_id,
398 &kNominalSampleRateAddress,
401 &info_size,
402 &nominal_sample_rate);
403 if (result != noErr) {
404 OSSTATUS_DLOG(WARNING, result)
405 << "Could not get default sample rate for device: " << device_id;
406 return 0;
409 return static_cast<int>(nominal_sample_rate);
412 int AudioManagerMac::HardwareSampleRate() {
413 // Determine the default output device's sample-rate.
414 AudioDeviceID device_id = kAudioObjectUnknown;
415 if (!GetDefaultOutputDevice(&device_id))
416 return kFallbackSampleRate;
418 return HardwareSampleRateForDevice(device_id);
421 void AudioManagerMac::GetAudioInputDeviceNames(
422 media::AudioDeviceNames* device_names) {
423 DCHECK(device_names->empty());
424 GetAudioDeviceInfo(true, device_names);
427 void AudioManagerMac::GetAudioOutputDeviceNames(
428 media::AudioDeviceNames* device_names) {
429 DCHECK(device_names->empty());
430 GetAudioDeviceInfo(false, device_names);
433 AudioParameters AudioManagerMac::GetInputStreamParameters(
434 const std::string& device_id) {
435 AudioDeviceID device = GetAudioDeviceIdByUId(true, device_id);
436 if (device == kAudioObjectUnknown) {
437 DLOG(ERROR) << "Invalid device " << device_id;
438 return AudioParameters(
439 AudioParameters::AUDIO_PCM_LOW_LATENCY, CHANNEL_LAYOUT_STEREO,
440 kFallbackSampleRate, 16, ChooseBufferSize(true, kFallbackSampleRate));
443 int channels = 0;
444 ChannelLayout channel_layout = CHANNEL_LAYOUT_STEREO;
445 if (GetDeviceChannels(device, kAudioDevicePropertyScopeInput, &channels) &&
446 channels <= 2) {
447 channel_layout = GuessChannelLayout(channels);
448 } else {
449 DLOG(ERROR) << "Failed to get the device channels, use stereo as default "
450 << "for device " << device_id;
453 int sample_rate = HardwareSampleRateForDevice(device);
454 if (!sample_rate)
455 sample_rate = kFallbackSampleRate;
457 // Due to the sharing of the input and output buffer sizes, we need to choose
458 // the input buffer size based on the output sample rate. See
459 // http://crbug.com/154352.
460 const int buffer_size = ChooseBufferSize(true, sample_rate);
462 // TODO(xians): query the native channel layout for the specific device.
463 return AudioParameters(
464 AudioParameters::AUDIO_PCM_LOW_LATENCY, channel_layout,
465 sample_rate, 16, buffer_size);
468 std::string AudioManagerMac::GetAssociatedOutputDeviceID(
469 const std::string& input_device_id) {
470 AudioDeviceID device = GetAudioDeviceIdByUId(true, input_device_id);
471 if (device == kAudioObjectUnknown)
472 return std::string();
474 UInt32 size = 0;
475 AudioObjectPropertyAddress pa = {
476 kAudioDevicePropertyRelatedDevices,
477 kAudioDevicePropertyScopeOutput,
478 kAudioObjectPropertyElementMaster
480 OSStatus result = AudioObjectGetPropertyDataSize(device, &pa, 0, 0, &size);
481 if (result || !size)
482 return std::string();
484 int device_count = size / sizeof(AudioDeviceID);
485 scoped_ptr<AudioDeviceID, base::FreeDeleter>
486 devices(static_cast<AudioDeviceID*>(malloc(size)));
487 result = AudioObjectGetPropertyData(
488 device, &pa, 0, NULL, &size, devices.get());
489 if (result)
490 return std::string();
492 std::vector<std::string> associated_devices;
493 for (int i = 0; i < device_count; ++i) {
494 // Get the number of output channels of the device.
495 pa.mSelector = kAudioDevicePropertyStreams;
496 size = 0;
497 result = AudioObjectGetPropertyDataSize(devices.get()[i],
498 &pa,
500 NULL,
501 &size);
502 if (result || !size)
503 continue; // Skip if there aren't any output channels.
505 // Get device UID.
506 CFStringRef uid = NULL;
507 size = sizeof(uid);
508 pa.mSelector = kAudioDevicePropertyDeviceUID;
509 result = AudioObjectGetPropertyData(devices.get()[i],
510 &pa,
512 NULL,
513 &size,
514 &uid);
515 if (result || !uid)
516 continue;
518 std::string ret(base::SysCFStringRefToUTF8(uid));
519 CFRelease(uid);
520 associated_devices.push_back(ret);
523 // No matching device found.
524 if (associated_devices.empty())
525 return std::string();
527 // Return the device if there is only one associated device.
528 if (associated_devices.size() == 1)
529 return associated_devices[0];
531 // When there are multiple associated devices, we currently do not have a way
532 // to detect if a device (e.g. a digital output device) is actually connected
533 // to an endpoint, so we cannot randomly pick a device.
534 // We pick the device iff the associated device is the default output device.
535 const std::string default_device = GetDefaultOutputDeviceID();
536 for (std::vector<std::string>::const_iterator iter =
537 associated_devices.begin();
538 iter != associated_devices.end(); ++iter) {
539 if (default_device == *iter)
540 return *iter;
543 // Failed to figure out which is the matching device, return an emtpy string.
544 return std::string();
547 AudioOutputStream* AudioManagerMac::MakeLinearOutputStream(
548 const AudioParameters& params) {
549 return MakeLowLatencyOutputStream(params, std::string());
552 AudioOutputStream* AudioManagerMac::MakeLowLatencyOutputStream(
553 const AudioParameters& params,
554 const std::string& device_id) {
555 AudioDeviceID device = GetAudioDeviceIdByUId(false, device_id);
556 if (device == kAudioObjectUnknown) {
557 DLOG(ERROR) << "Failed to open output device: " << device_id;
558 return NULL;
561 // Lazily create the audio device listener on the first stream creation.
562 if (!output_device_listener_) {
563 // NOTE: Use BindToCurrentLoop() to ensure the callback is always PostTask'd
564 // even if OSX calls us on the right thread. Some CoreAudio drivers will
565 // fire the callbacks during stream creation, leading to re-entrancy issues
566 // otherwise. See http://crbug.com/349604
567 output_device_listener_.reset(
568 new AudioDeviceListenerMac(BindToCurrentLoop(base::Bind(
569 &AudioManagerMac::HandleDeviceChanges, base::Unretained(this)))));
570 // Only set the current output device for the default device.
571 if (device_id == AudioManagerBase::kDefaultDeviceId || device_id.empty())
572 current_output_device_ = device;
573 // Just use the current sample rate since we don't allow non-native sample
574 // rates on OSX.
575 current_sample_rate_ = params.sample_rate();
578 AUHALStream* stream = new AUHALStream(this, params, device);
579 output_streams_.push_back(stream);
580 return stream;
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,
596 &property_address,
598 NULL,
599 &size,
600 &device_uid);
601 if (status != kAudioHardwareNoError || !device_uid)
602 return std::string();
604 std::string ret(base::SysCFStringRefToUTF8(device_uid));
605 CFRelease(device_uid);
607 return ret;
610 AudioInputStream* AudioManagerMac::MakeLinearInputStream(
611 const AudioParameters& params, const std::string& device_id) {
612 DCHECK_EQ(AudioParameters::AUDIO_PCM_LINEAR, params.format());
613 AudioInputStream* stream = new PCMQueueInAudioInputStream(this, params);
614 basic_input_streams_.push_back(stream);
615 return stream;
618 AudioInputStream* AudioManagerMac::MakeLowLatencyInputStream(
619 const AudioParameters& params, const std::string& device_id) {
620 DCHECK_EQ(AudioParameters::AUDIO_PCM_LOW_LATENCY, params.format());
621 // Gets the AudioDeviceID that refers to the AudioInputDevice with the device
622 // unique id. This AudioDeviceID is used to set the device for Audio Unit.
623 AudioDeviceID audio_device_id = GetAudioDeviceIdByUId(true, device_id);
624 AUAudioInputStream* stream = NULL;
625 if (audio_device_id != kAudioObjectUnknown) {
626 stream = new AUAudioInputStream(this, params, audio_device_id);
627 low_latency_input_streams_.push_back(stream);
630 return stream;
633 AudioParameters AudioManagerMac::GetPreferredOutputStreamParameters(
634 const std::string& output_device_id,
635 const AudioParameters& input_params) {
636 const AudioDeviceID device = GetAudioDeviceIdByUId(false, output_device_id);
637 if (device == kAudioObjectUnknown) {
638 DLOG(ERROR) << "Invalid output device " << output_device_id;
639 return input_params.IsValid() ? input_params : AudioParameters(
640 AudioParameters::AUDIO_PCM_LOW_LATENCY, CHANNEL_LAYOUT_STEREO,
641 kFallbackSampleRate, 16, ChooseBufferSize(false, kFallbackSampleRate));
644 const bool has_valid_input_params = input_params.IsValid();
645 const int hardware_sample_rate = HardwareSampleRateForDevice(device);
647 // Allow pass through buffer sizes. If concurrent input and output streams
648 // exist, they will use the smallest buffer size amongst them. As such, each
649 // stream must be able to FIFO requests appropriately when this happens.
650 int buffer_size = ChooseBufferSize(false, hardware_sample_rate);
651 if (has_valid_input_params) {
652 buffer_size =
653 std::min(kMaximumInputOutputBufferSize,
654 std::max(input_params.frames_per_buffer(), buffer_size));
657 int hardware_channels;
658 if (!GetDeviceChannels(device, kAudioDevicePropertyScopeOutput,
659 &hardware_channels)) {
660 hardware_channels = 2;
663 // Use the input channel count and channel layout if possible. Let OSX take
664 // care of remapping the channels; this lets user specified channel layouts
665 // work correctly.
666 int output_channels = input_params.channels();
667 ChannelLayout channel_layout = input_params.channel_layout();
668 if (!has_valid_input_params || output_channels > hardware_channels) {
669 output_channels = hardware_channels;
670 channel_layout = GuessChannelLayout(output_channels);
671 if (channel_layout == CHANNEL_LAYOUT_UNSUPPORTED)
672 channel_layout = CHANNEL_LAYOUT_DISCRETE;
675 return AudioParameters(
676 AudioParameters::AUDIO_PCM_LOW_LATENCY, channel_layout, output_channels,
677 hardware_sample_rate, 16, buffer_size, AudioParameters::NO_EFFECTS);
680 void AudioManagerMac::InitializeOnAudioThread() {
681 DCHECK(GetTaskRunner()->BelongsToCurrentThread());
682 power_observer_.reset(new AudioPowerObserver());
685 void AudioManagerMac::ShutdownOnAudioThread() {
686 DCHECK(GetTaskRunner()->BelongsToCurrentThread());
687 output_device_listener_.reset();
688 power_observer_.reset();
690 // Since CoreAudio calls have to run on the UI thread and browser shutdown
691 // doesn't wait for outstanding tasks to complete, we may have input/output
692 // streams still running at shutdown.
694 // To avoid calls into destructed classes, we need to stop the OS callbacks
695 // by stopping the streams. Note: The streams are leaked since process
696 // destruction is imminent.
698 // See http://crbug.com/354139 for crash details.
699 StopStreams(&basic_input_streams_);
700 StopStreams(&low_latency_input_streams_);
701 StopStreams(&output_streams_);
704 void AudioManagerMac::HandleDeviceChanges() {
705 DCHECK(GetTaskRunner()->BelongsToCurrentThread());
706 const int new_sample_rate = HardwareSampleRate();
707 AudioDeviceID new_output_device;
708 GetDefaultOutputDevice(&new_output_device);
710 if (current_sample_rate_ == new_sample_rate &&
711 current_output_device_ == new_output_device)
712 return;
714 current_sample_rate_ = new_sample_rate;
715 current_output_device_ = new_output_device;
716 NotifyAllOutputDeviceChangeListeners();
719 int AudioManagerMac::ChooseBufferSize(bool is_input, int sample_rate) {
720 // kMinimumInputOutputBufferSize is too small for the output side because
721 // CoreAudio can get into under-run if the renderer fails delivering data
722 // to the browser within the allowed time by the OS. The workaround is to
723 // use 256 samples as the default output buffer size for sample rates
724 // smaller than 96KHz.
725 // TODO(xians): Remove this workaround after WebAudio supports user defined
726 // buffer size. See https://github.com/WebAudio/web-audio-api/issues/348
727 // for details.
728 int buffer_size = is_input ?
729 kMinimumInputOutputBufferSize : 2 * kMinimumInputOutputBufferSize;
730 const int user_buffer_size = GetUserBufferSize();
731 if (user_buffer_size) {
732 buffer_size = user_buffer_size;
733 } else if (sample_rate > 48000) {
734 // The default buffer size is too small for higher sample rates and may lead
735 // to glitching. Adjust upwards by multiples of the default size.
736 if (sample_rate <= 96000)
737 buffer_size = 2 * kMinimumInputOutputBufferSize;
738 else if (sample_rate <= 192000)
739 buffer_size = 4 * kMinimumInputOutputBufferSize;
742 return buffer_size;
745 bool AudioManagerMac::ShouldDeferStreamStart() {
746 DCHECK(GetTaskRunner()->BelongsToCurrentThread());
747 return power_observer_->ShouldDeferStreamStart();
750 bool AudioManagerMac::MaybeChangeBufferSize(AudioDeviceID device_id,
751 AudioUnit audio_unit,
752 AudioUnitElement element,
753 size_t desired_buffer_size) {
754 UInt32 buffer_size = 0;
755 UInt32 property_size = sizeof(buffer_size);
756 OSStatus result = AudioUnitGetProperty(audio_unit,
757 kAudioDevicePropertyBufferFrameSize,
758 kAudioUnitScope_Output,
759 element,
760 &buffer_size,
761 &property_size);
762 if (result != noErr) {
763 OSSTATUS_DLOG(ERROR, result)
764 << "AudioUnitGetProperty(kAudioDevicePropertyBufferFrameSize) failed.";
765 return false;
768 // The lowest buffer size always wins. For larger buffer sizes, we have
769 // to perform some checks to see if the size can actually be changed.
770 // If there is any other active streams on the same device, either input or
771 // output, a larger size than their requested buffer size can't be set.
772 // The reason is that an existing stream can't handle buffer size larger
773 // than its requested buffer size.
775 // See http://crbug.com/428706 for a reason why.
776 if (desired_buffer_size > buffer_size) {
777 // Do NOT set the buffer size if there is another output stream using
778 // the same device with a smaller requested buffer size.
779 // Note, for the caller stream, its requested_buffer_size() will be the same
780 // as |desired_buffer_size|, so it won't return true due to comparing with
781 // itself.
782 for (auto* stream : output_streams_) {
783 if (stream->device_id() == device_id &&
784 stream->requested_buffer_size() < desired_buffer_size) {
785 return true;
789 // Do NOT set the buffer size if there is another input stream using
790 // the same device with a smaller buffer size.
791 for (auto* stream : low_latency_input_streams_) {
792 if (stream->device_id() == device_id &&
793 stream->requested_buffer_size() < desired_buffer_size) {
794 return true;
799 buffer_size = desired_buffer_size;
800 result = AudioUnitSetProperty(audio_unit,
801 kAudioDevicePropertyBufferFrameSize,
802 kAudioUnitScope_Output,
803 element,
804 &buffer_size,
805 sizeof(buffer_size));
806 OSSTATUS_DLOG_IF(ERROR, result != noErr, result)
807 << "AudioUnitSetProperty(kAudioDevicePropertyBufferFrameSize) failed. "
808 << "Size:: " << buffer_size;
810 return (result == noErr);
813 void AudioManagerMac::ReleaseOutputStream(AudioOutputStream* stream) {
814 output_streams_.remove(static_cast<AUHALStream*>(stream));
815 AudioManagerBase::ReleaseOutputStream(stream);
818 void AudioManagerMac::ReleaseInputStream(AudioInputStream* stream) {
819 auto stream_it = std::find(basic_input_streams_.begin(),
820 basic_input_streams_.end(),
821 stream);
822 if (stream_it == basic_input_streams_.end())
823 low_latency_input_streams_.remove(static_cast<AUAudioInputStream*>(stream));
824 else
825 basic_input_streams_.erase(stream_it);
827 AudioManagerBase::ReleaseInputStream(stream);
830 AudioManager* CreateAudioManager(AudioLogFactory* audio_log_factory) {
831 return new AudioManagerMac(audio_log_factory);
834 } // namespace media