cc: Added inline to Tile::IsReadyToDraw
[chromium-blink-merge.git] / media / audio / mac / audio_manager_mac.cc
blobc0c18ee2ccee09b821d6b3d86e84ee50ff47623b
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>
8 #include <string>
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/strings/sys_string_conversions.h"
15 #include "media/audio/audio_parameters.h"
16 #include "media/audio/audio_util.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/audio/mac/audio_low_latency_output_mac.h"
21 #include "media/audio/mac/audio_synchronized_mac.h"
22 #include "media/audio/mac/audio_unified_mac.h"
23 #include "media/base/bind_to_loop.h"
24 #include "media/base/channel_layout.h"
25 #include "media/base/limits.h"
26 #include "media/base/media_switches.h"
28 namespace media {
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 int ChooseBufferSize(int output_sample_rate) {
40 int buffer_size = kDefaultLowLatencyBufferSize;
41 const int user_buffer_size = GetUserBufferSize();
42 if (user_buffer_size) {
43 buffer_size = user_buffer_size;
44 } else if (output_sample_rate > 48000) {
45 // The default buffer size is too small for higher sample rates and may lead
46 // to glitching. Adjust upwards by multiples of the default size.
47 if (output_sample_rate <= 96000)
48 buffer_size = 2 * kDefaultLowLatencyBufferSize;
49 else if (output_sample_rate <= 192000)
50 buffer_size = 4 * kDefaultLowLatencyBufferSize;
53 return buffer_size;
56 static bool HasAudioHardware(AudioObjectPropertySelector selector) {
57 AudioDeviceID output_device_id = kAudioObjectUnknown;
58 const AudioObjectPropertyAddress property_address = {
59 selector,
60 kAudioObjectPropertyScopeGlobal, // mScope
61 kAudioObjectPropertyElementMaster // mElement
63 UInt32 output_device_id_size = static_cast<UInt32>(sizeof(output_device_id));
64 OSStatus err = AudioObjectGetPropertyData(kAudioObjectSystemObject,
65 &property_address,
66 0, // inQualifierDataSize
67 NULL, // inQualifierData
68 &output_device_id_size,
69 &output_device_id);
70 return err == kAudioHardwareNoError &&
71 output_device_id != kAudioObjectUnknown;
74 // Returns true if the default input device is the same as
75 // the default output device.
76 bool AudioManagerMac::HasUnifiedDefaultIO() {
77 AudioDeviceID input_id, output_id;
78 if (!GetDefaultInputDevice(&input_id) || !GetDefaultOutputDevice(&output_id))
79 return false;
81 return input_id == output_id;
84 static void GetAudioDeviceInfo(bool is_input,
85 media::AudioDeviceNames* device_names) {
86 DCHECK(device_names);
87 device_names->clear();
89 // Query the number of total devices.
90 AudioObjectPropertyAddress property_address = {
91 kAudioHardwarePropertyDevices,
92 kAudioObjectPropertyScopeGlobal,
93 kAudioObjectPropertyElementMaster
95 UInt32 size = 0;
96 OSStatus result = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject,
97 &property_address,
99 NULL,
100 &size);
101 if (result || !size)
102 return;
104 int device_count = size / sizeof(AudioDeviceID);
106 // Get the array of device ids for all the devices, which includes both
107 // input devices and output devices.
108 scoped_ptr_malloc<AudioDeviceID>
109 devices(reinterpret_cast<AudioDeviceID*>(malloc(size)));
110 AudioDeviceID* device_ids = devices.get();
111 result = AudioObjectGetPropertyData(kAudioObjectSystemObject,
112 &property_address,
114 NULL,
115 &size,
116 device_ids);
117 if (result)
118 return;
120 // Iterate over all available devices to gather information.
121 for (int i = 0; i < device_count; ++i) {
122 // Get the number of input or output channels of the device.
123 property_address.mScope = is_input ?
124 kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput;
125 property_address.mSelector = kAudioDevicePropertyStreams;
126 size = 0;
127 result = AudioObjectGetPropertyDataSize(device_ids[i],
128 &property_address,
130 NULL,
131 &size);
132 if (result || !size)
133 continue;
135 // Get device UID.
136 CFStringRef uid = NULL;
137 size = sizeof(uid);
138 property_address.mSelector = kAudioDevicePropertyDeviceUID;
139 property_address.mScope = kAudioObjectPropertyScopeGlobal;
140 result = AudioObjectGetPropertyData(device_ids[i],
141 &property_address,
143 NULL,
144 &size,
145 &uid);
146 if (result)
147 continue;
149 // Get device name.
150 CFStringRef name = NULL;
151 property_address.mSelector = kAudioObjectPropertyName;
152 property_address.mScope = kAudioObjectPropertyScopeGlobal;
153 result = AudioObjectGetPropertyData(device_ids[i],
154 &property_address,
156 NULL,
157 &size,
158 &name);
159 if (result) {
160 if (uid)
161 CFRelease(uid);
162 continue;
165 // Store the device name and UID.
166 media::AudioDeviceName device_name;
167 device_name.device_name = base::SysCFStringRefToUTF8(name);
168 device_name.unique_id = base::SysCFStringRefToUTF8(uid);
169 device_names->push_back(device_name);
171 // We are responsible for releasing the returned CFObject. See the
172 // comment in the AudioHardware.h for constant
173 // kAudioDevicePropertyDeviceUID.
174 if (uid)
175 CFRelease(uid);
176 if (name)
177 CFRelease(name);
181 static AudioDeviceID GetAudioDeviceIdByUId(bool is_input,
182 const std::string& device_id) {
183 AudioObjectPropertyAddress property_address = {
184 kAudioHardwarePropertyDevices,
185 kAudioObjectPropertyScopeGlobal,
186 kAudioObjectPropertyElementMaster
188 AudioDeviceID audio_device_id = kAudioObjectUnknown;
189 UInt32 device_size = sizeof(audio_device_id);
190 OSStatus result = -1;
192 if (device_id == AudioManagerBase::kDefaultDeviceId) {
193 // Default Device.
194 property_address.mSelector = is_input ?
195 kAudioHardwarePropertyDefaultInputDevice :
196 kAudioHardwarePropertyDefaultOutputDevice;
198 result = AudioObjectGetPropertyData(kAudioObjectSystemObject,
199 &property_address,
202 &device_size,
203 &audio_device_id);
204 } else {
205 // Non-default device.
206 base::ScopedCFTypeRef<CFStringRef> uid(
207 base::SysUTF8ToCFStringRef(device_id));
208 AudioValueTranslation value;
209 value.mInputData = &uid;
210 value.mInputDataSize = sizeof(CFStringRef);
211 value.mOutputData = &audio_device_id;
212 value.mOutputDataSize = device_size;
213 UInt32 translation_size = sizeof(AudioValueTranslation);
215 property_address.mSelector = kAudioHardwarePropertyDeviceForUID;
216 result = AudioObjectGetPropertyData(kAudioObjectSystemObject,
217 &property_address,
220 &translation_size,
221 &value);
224 if (result) {
225 OSSTATUS_DLOG(WARNING, result) << "Unable to query device " << device_id
226 << " for AudioDeviceID";
229 return audio_device_id;
232 AudioManagerMac::AudioManagerMac()
233 : current_sample_rate_(0) {
234 current_output_device_ = kAudioDeviceUnknown;
236 SetMaxOutputStreamsAllowed(kMaxOutputStreams);
238 // Task must be posted last to avoid races from handing out "this" to the
239 // audio thread. Always PostTask even if we're on the right thread since
240 // AudioManager creation is on the startup path and this may be slow.
241 GetMessageLoop()->PostTask(FROM_HERE, base::Bind(
242 &AudioManagerMac::CreateDeviceListener, base::Unretained(this)));
245 AudioManagerMac::~AudioManagerMac() {
246 if (GetMessageLoop()->BelongsToCurrentThread()) {
247 DestroyDeviceListener();
248 } else {
249 // It's safe to post a task here since Shutdown() will wait for all tasks to
250 // complete before returning.
251 GetMessageLoop()->PostTask(FROM_HERE, base::Bind(
252 &AudioManagerMac::DestroyDeviceListener, base::Unretained(this)));
255 Shutdown();
258 bool AudioManagerMac::HasAudioOutputDevices() {
259 return HasAudioHardware(kAudioHardwarePropertyDefaultOutputDevice);
262 bool AudioManagerMac::HasAudioInputDevices() {
263 return HasAudioHardware(kAudioHardwarePropertyDefaultInputDevice);
266 // TODO(crogers): There are several places on the OSX specific code which
267 // could benefit from these helper functions.
268 bool AudioManagerMac::GetDefaultInputDevice(
269 AudioDeviceID* device) {
270 return GetDefaultDevice(device, true);
273 bool AudioManagerMac::GetDefaultOutputDevice(
274 AudioDeviceID* device) {
275 return GetDefaultDevice(device, false);
278 bool AudioManagerMac::GetDefaultDevice(
279 AudioDeviceID* device, bool input) {
280 CHECK(device);
282 // Obtain the current output device selected by the user.
283 AudioObjectPropertyAddress pa;
284 pa.mSelector = input ? kAudioHardwarePropertyDefaultInputDevice :
285 kAudioHardwarePropertyDefaultOutputDevice;
286 pa.mScope = kAudioObjectPropertyScopeGlobal;
287 pa.mElement = kAudioObjectPropertyElementMaster;
289 UInt32 size = sizeof(*device);
291 OSStatus result = AudioObjectGetPropertyData(
292 kAudioObjectSystemObject,
293 &pa,
296 &size,
297 device);
299 if ((result != kAudioHardwareNoError) || (*device == kAudioDeviceUnknown)) {
300 DLOG(ERROR) << "Error getting default AudioDevice.";
301 return false;
304 return true;
307 bool AudioManagerMac::GetDefaultOutputChannels(
308 int* channels) {
309 AudioDeviceID device;
310 if (!GetDefaultOutputDevice(&device))
311 return false;
313 return GetDeviceChannels(device,
314 kAudioDevicePropertyScopeOutput,
315 channels);
318 bool AudioManagerMac::GetDeviceChannels(
319 AudioDeviceID device,
320 AudioObjectPropertyScope scope,
321 int* channels) {
322 CHECK(channels);
324 // Get stream configuration.
325 AudioObjectPropertyAddress pa;
326 pa.mSelector = kAudioDevicePropertyStreamConfiguration;
327 pa.mScope = scope;
328 pa.mElement = kAudioObjectPropertyElementMaster;
330 UInt32 size;
331 OSStatus result = AudioObjectGetPropertyDataSize(device, &pa, 0, 0, &size);
332 if (result != noErr || !size)
333 return false;
335 // Allocate storage.
336 scoped_ptr<uint8[]> list_storage(new uint8[size]);
337 AudioBufferList& buffer_list =
338 *reinterpret_cast<AudioBufferList*>(list_storage.get());
340 result = AudioObjectGetPropertyData(
341 device,
342 &pa,
345 &size,
346 &buffer_list);
347 if (result != noErr)
348 return false;
350 // Determine number of input channels.
351 int channels_per_frame = buffer_list.mNumberBuffers > 0 ?
352 buffer_list.mBuffers[0].mNumberChannels : 0;
353 if (channels_per_frame == 1 && buffer_list.mNumberBuffers > 1) {
354 // Non-interleaved.
355 *channels = buffer_list.mNumberBuffers;
356 } else {
357 // Interleaved.
358 *channels = channels_per_frame;
361 return true;
364 int AudioManagerMac::HardwareSampleRateForDevice(AudioDeviceID device_id) {
365 Float64 nominal_sample_rate;
366 UInt32 info_size = sizeof(nominal_sample_rate);
368 static const AudioObjectPropertyAddress kNominalSampleRateAddress = {
369 kAudioDevicePropertyNominalSampleRate,
370 kAudioObjectPropertyScopeGlobal,
371 kAudioObjectPropertyElementMaster
373 OSStatus result = AudioObjectGetPropertyData(
374 device_id,
375 &kNominalSampleRateAddress,
378 &info_size,
379 &nominal_sample_rate);
380 if (result != noErr) {
381 OSSTATUS_DLOG(WARNING, result)
382 << "Could not get default sample rate for device: " << device_id;
383 return 0;
386 return static_cast<int>(nominal_sample_rate);
389 int AudioManagerMac::HardwareSampleRate() {
390 // Determine the default output device's sample-rate.
391 AudioDeviceID device_id = kAudioObjectUnknown;
392 if (!GetDefaultOutputDevice(&device_id))
393 return kFallbackSampleRate;
395 return HardwareSampleRateForDevice(device_id);
398 void AudioManagerMac::GetAudioInputDeviceNames(
399 media::AudioDeviceNames* device_names) {
400 GetAudioDeviceInfo(true, device_names);
401 if (!device_names->empty()) {
402 // Prepend the default device to the list since we always want it to be
403 // on the top of the list for all platforms. There is no duplicate
404 // counting here since the default device has been abstracted out before.
405 media::AudioDeviceName name;
406 name.device_name = AudioManagerBase::kDefaultDeviceName;
407 name.unique_id = AudioManagerBase::kDefaultDeviceId;
408 device_names->push_front(name);
412 AudioParameters AudioManagerMac::GetInputStreamParameters(
413 const std::string& device_id) {
414 // Due to the sharing of the input and output buffer sizes, we need to choose
415 // the input buffer size based on the output sample rate. See
416 // http://crbug.com/154352.
417 const int buffer_size = ChooseBufferSize(
418 AUAudioOutputStream::HardwareSampleRate());
420 AudioDeviceID device = GetAudioDeviceIdByUId(true, device_id);
421 if (device == kAudioObjectUnknown) {
422 DLOG(ERROR) << "Invalid device " << device_id;
423 return AudioParameters();
426 int channels = 0;
427 ChannelLayout channel_layout = CHANNEL_LAYOUT_STEREO;
428 if (GetDeviceChannels(device, kAudioDevicePropertyScopeInput, &channels) &&
429 channels <= 2) {
430 channel_layout = GuessChannelLayout(channels);
431 } else {
432 DLOG(ERROR) << "Failed to get the device channels, use stereo as default "
433 << "for device " << device_id;
436 int sample_rate = HardwareSampleRateForDevice(device);
437 if (!sample_rate)
438 sample_rate = kFallbackSampleRate;
440 // TODO(xians): query the native channel layout for the specific device.
441 return AudioParameters(
442 AudioParameters::AUDIO_PCM_LOW_LATENCY, channel_layout,
443 sample_rate, 16, buffer_size);
446 AudioOutputStream* AudioManagerMac::MakeLinearOutputStream(
447 const AudioParameters& params) {
448 return MakeLowLatencyOutputStream(params, std::string());
451 AudioOutputStream* AudioManagerMac::MakeLowLatencyOutputStream(
452 const AudioParameters& params, const std::string& input_device_id) {
453 // Handle basic output with no input channels.
454 if (params.input_channels() == 0) {
455 AudioDeviceID device = kAudioObjectUnknown;
456 GetDefaultOutputDevice(&device);
457 return new AUHALStream(this, params, device);
460 // TODO(crogers): support more than stereo input.
461 if (params.input_channels() != 2) {
462 // WebAudio is currently hard-coded to 2 channels so we should not
463 // see this case.
464 NOTREACHED() << "Only stereo input is currently supported!";
465 return NULL;
468 AudioDeviceID device = kAudioObjectUnknown;
469 if (HasUnifiedDefaultIO()) {
470 // For I/O, the simplest case is when the default input and output
471 // devices are the same.
472 GetDefaultOutputDevice(&device);
473 LOG(INFO) << "UNIFIED: default input and output devices are identical";
474 } else {
475 // Some audio hardware is presented as separate input and output devices
476 // even though they are really the same physical hardware and
477 // share the same "clock domain" at the lowest levels of the driver.
478 // A common of example of this is the "built-in" audio hardware:
479 // "Built-in Line Input"
480 // "Built-in Output"
481 // We would like to use an "aggregate" device for these situations, since
482 // CoreAudio will make the most efficient use of the shared "clock domain"
483 // so we get the lowest latency and use fewer threads.
484 device = aggregate_device_manager_.GetDefaultAggregateDevice();
485 if (device != kAudioObjectUnknown)
486 LOG(INFO) << "Using AGGREGATE audio device";
489 if (device != kAudioObjectUnknown &&
490 input_device_id == AudioManagerBase::kDefaultDeviceId)
491 return new AUHALStream(this, params, device);
493 // Fallback to AudioSynchronizedStream which will handle completely
494 // different and arbitrary combinations of input and output devices
495 // even running at different sample-rates.
496 // kAudioDeviceUnknown translates to "use default" here.
497 // TODO(crogers): consider tracking UMA stats on AUHALStream
498 // versus AudioSynchronizedStream.
499 AudioDeviceID audio_device_id = GetAudioDeviceIdByUId(true, input_device_id);
500 if (audio_device_id == kAudioObjectUnknown)
501 return NULL;
503 return new AudioSynchronizedStream(this,
504 params,
505 audio_device_id,
506 kAudioDeviceUnknown);
509 AudioInputStream* AudioManagerMac::MakeLinearInputStream(
510 const AudioParameters& params, const std::string& device_id) {
511 DCHECK_EQ(AudioParameters::AUDIO_PCM_LINEAR, params.format());
512 return new PCMQueueInAudioInputStream(this, params);
515 AudioInputStream* AudioManagerMac::MakeLowLatencyInputStream(
516 const AudioParameters& params, const std::string& device_id) {
517 DCHECK_EQ(AudioParameters::AUDIO_PCM_LOW_LATENCY, params.format());
518 // Gets the AudioDeviceID that refers to the AudioOutputDevice with the device
519 // unique id. This AudioDeviceID is used to set the device for Audio Unit.
520 AudioDeviceID audio_device_id = GetAudioDeviceIdByUId(true, device_id);
521 AudioInputStream* stream = NULL;
522 if (audio_device_id != kAudioObjectUnknown)
523 stream = new AUAudioInputStream(this, params, audio_device_id);
525 return stream;
528 AudioParameters AudioManagerMac::GetPreferredOutputStreamParameters(
529 const AudioParameters& input_params) {
530 int hardware_channels = 2;
531 if (!GetDefaultOutputChannels(&hardware_channels)) {
532 // Fallback to stereo.
533 hardware_channels = 2;
536 ChannelLayout channel_layout = GuessChannelLayout(hardware_channels);
538 const int hardware_sample_rate = AUAudioOutputStream::HardwareSampleRate();
539 const int buffer_size = ChooseBufferSize(hardware_sample_rate);
541 int input_channels = 0;
542 if (input_params.IsValid()) {
543 input_channels = input_params.input_channels();
545 if (input_channels > 0) {
546 // TODO(crogers): given the limitations of the AudioOutputStream
547 // back-ends used with synchronized I/O, we hard-code to stereo.
548 // Specifically, this is a limitation of AudioSynchronizedStream which
549 // can be removed as part of the work to consolidate these back-ends.
550 channel_layout = CHANNEL_LAYOUT_STEREO;
554 AudioParameters params(
555 AudioParameters::AUDIO_PCM_LOW_LATENCY,
556 channel_layout,
557 input_channels,
558 hardware_sample_rate,
560 buffer_size);
562 if (channel_layout == CHANNEL_LAYOUT_UNSUPPORTED)
563 params.SetDiscreteChannels(hardware_channels);
565 return params;
568 void AudioManagerMac::CreateDeviceListener() {
569 DCHECK(GetMessageLoop()->BelongsToCurrentThread());
571 // Get a baseline for the sample-rate and current device,
572 // so we can intelligently handle device notifications only when necessary.
573 current_sample_rate_ = HardwareSampleRate();
574 if (!GetDefaultOutputDevice(&current_output_device_))
575 current_output_device_ = kAudioDeviceUnknown;
577 output_device_listener_.reset(new AudioDeviceListenerMac(base::Bind(
578 &AudioManagerMac::HandleDeviceChanges, base::Unretained(this))));
581 void AudioManagerMac::DestroyDeviceListener() {
582 DCHECK(GetMessageLoop()->BelongsToCurrentThread());
583 output_device_listener_.reset();
586 void AudioManagerMac::HandleDeviceChanges() {
587 if (!GetMessageLoop()->BelongsToCurrentThread()) {
588 GetMessageLoop()->PostTask(FROM_HERE, base::Bind(
589 &AudioManagerMac::HandleDeviceChanges, base::Unretained(this)));
590 return;
593 int new_sample_rate = HardwareSampleRate();
594 AudioDeviceID new_output_device;
595 GetDefaultOutputDevice(&new_output_device);
597 if (current_sample_rate_ == new_sample_rate &&
598 current_output_device_ == new_output_device)
599 return;
601 current_sample_rate_ = new_sample_rate;
602 current_output_device_ = new_output_device;
603 NotifyAllOutputDeviceChangeListeners();
606 AudioManager* CreateAudioManager() {
607 return new AudioManagerMac();
610 } // namespace media