audio_manager_mac.cc revision 7d4cd473f85ac64c3747c96c277f9e506a0d2246
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.
4
5#include "media/audio/mac/audio_manager_mac.h"
6
7#include <CoreAudio/AudioHardware.h>
8#include <string>
9
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"
27
28namespace media {
29
30// Maximum number of output streams that can be open simultaneously.
31static const int kMaxOutputStreams = 50;
32
33// Default buffer size in samples for low-latency input and output streams.
34static const int kDefaultLowLatencyBufferSize = 128;
35
36// Default sample-rate on most Apple hardware.
37static const int kFallbackSampleRate = 44100;
38
39static 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;
51  }
52
53  return buffer_size;
54}
55
56static bool HasAudioHardware(AudioObjectPropertySelector selector) {
57  AudioDeviceID output_device_id = kAudioObjectUnknown;
58  const AudioObjectPropertyAddress property_address = {
59    selector,
60    kAudioObjectPropertyScopeGlobal,            // mScope
61    kAudioObjectPropertyElementMaster           // mElement
62  };
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;
72}
73
74// Returns true if the default input device is the same as
75// the default output device.
76bool AudioManagerMac::HasUnifiedDefaultIO() {
77  AudioDeviceID input_id, output_id;
78  if (!GetDefaultInputDevice(&input_id) || !GetDefaultOutputDevice(&output_id))
79    return false;
80
81  return input_id == output_id;
82}
83
84static void GetAudioDeviceInfo(bool is_input,
85                               media::AudioDeviceNames* device_names) {
86  DCHECK(device_names);
87  device_names->clear();
88
89  // Query the number of total devices.
90  AudioObjectPropertyAddress property_address = {
91    kAudioHardwarePropertyDevices,
92    kAudioObjectPropertyScopeGlobal,
93    kAudioObjectPropertyElementMaster
94  };
95  UInt32 size = 0;
96  OSStatus result = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject,
97                                                   &property_address,
98                                                   0,
99                                                   NULL,
100                                                   &size);
101  if (result || !size)
102    return;
103
104  int device_count = size / sizeof(AudioDeviceID);
105
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,
113                                      0,
114                                      NULL,
115                                      &size,
116                                      device_ids);
117  if (result)
118    return;
119
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,
129                                            0,
130                                            NULL,
131                                            &size);
132    if (result || !size)
133      continue;
134
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,
142                                        0,
143                                        NULL,
144                                        &size,
145                                        &uid);
146    if (result)
147      continue;
148
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,
155                                        0,
156                                        NULL,
157                                        &size,
158                                        &name);
159    if (result) {
160      if (uid)
161        CFRelease(uid);
162      continue;
163    }
164
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);
170
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);
178  }
179}
180
181static AudioDeviceID GetAudioDeviceIdByUId(bool is_input,
182                                           const std::string& device_id) {
183  AudioObjectPropertyAddress property_address = {
184    kAudioHardwarePropertyDevices,
185    kAudioObjectPropertyScopeGlobal,
186    kAudioObjectPropertyElementMaster
187  };
188  AudioDeviceID audio_device_id = kAudioObjectUnknown;
189  UInt32 device_size = sizeof(audio_device_id);
190  OSStatus result = -1;
191
192  if (device_id == AudioManagerBase::kDefaultDeviceId) {
193    // Default Device.
194    property_address.mSelector = is_input ?
195        kAudioHardwarePropertyDefaultInputDevice :
196        kAudioHardwarePropertyDefaultOutputDevice;
197
198    result = AudioObjectGetPropertyData(kAudioObjectSystemObject,
199                                        &property_address,
200                                        0,
201                                        0,
202                                        &device_size,
203                                        &audio_device_id);
204  } else {
205    // Non-default device.
206    base::mac::ScopedCFTypeRef<CFStringRef>
207        uid(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);
214
215    property_address.mSelector = kAudioHardwarePropertyDeviceForUID;
216    result = AudioObjectGetPropertyData(kAudioObjectSystemObject,
217                                        &property_address,
218                                        0,
219                                        0,
220                                        &translation_size,
221                                        &value);
222  }
223
224  if (result) {
225    OSSTATUS_DLOG(WARNING, result) << "Unable to query device " << device_id
226                                   << " for AudioDeviceID";
227  }
228
229  return audio_device_id;
230}
231
232AudioManagerMac::AudioManagerMac()
233    : current_sample_rate_(0) {
234  current_output_device_ = kAudioDeviceUnknown;
235
236  SetMaxOutputStreamsAllowed(kMaxOutputStreams);
237
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)));
243}
244
245AudioManagerMac::~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)));
253  }
254
255  Shutdown();
256}
257
258bool AudioManagerMac::HasAudioOutputDevices() {
259  return HasAudioHardware(kAudioHardwarePropertyDefaultOutputDevice);
260}
261
262bool AudioManagerMac::HasAudioInputDevices() {
263  return HasAudioHardware(kAudioHardwarePropertyDefaultInputDevice);
264}
265
266// TODO(crogers): There are several places on the OSX specific code which
267// could benefit from these helper functions.
268bool AudioManagerMac::GetDefaultInputDevice(
269    AudioDeviceID* device) {
270  return GetDefaultDevice(device, true);
271}
272
273bool AudioManagerMac::GetDefaultOutputDevice(
274    AudioDeviceID* device) {
275  return GetDefaultDevice(device, false);
276}
277
278bool AudioManagerMac::GetDefaultDevice(
279    AudioDeviceID* device, bool input) {
280  CHECK(device);
281
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;
288
289  UInt32 size = sizeof(*device);
290
291  OSStatus result = AudioObjectGetPropertyData(
292      kAudioObjectSystemObject,
293      &pa,
294      0,
295      0,
296      &size,
297      device);
298
299  if ((result != kAudioHardwareNoError) || (*device == kAudioDeviceUnknown)) {
300    DLOG(ERROR) << "Error getting default AudioDevice.";
301    return false;
302  }
303
304  return true;
305}
306
307bool AudioManagerMac::GetDefaultOutputChannels(
308    int* channels) {
309  AudioDeviceID device;
310  if (!GetDefaultOutputDevice(&device))
311    return false;
312
313  return GetDeviceChannels(device,
314                           kAudioDevicePropertyScopeOutput,
315                           channels);
316}
317
318bool AudioManagerMac::GetDeviceChannels(
319    AudioDeviceID device,
320    AudioObjectPropertyScope scope,
321    int* channels) {
322  CHECK(channels);
323
324  // Get stream configuration.
325  AudioObjectPropertyAddress pa;
326  pa.mSelector = kAudioDevicePropertyStreamConfiguration;
327  pa.mScope = scope;
328  pa.mElement = kAudioObjectPropertyElementMaster;
329
330  UInt32 size;
331  OSStatus result = AudioObjectGetPropertyDataSize(device, &pa, 0, 0, &size);
332  if (result != noErr || !size)
333    return false;
334
335  // Allocate storage.
336  scoped_ptr<uint8[]> list_storage(new uint8[size]);
337  AudioBufferList& buffer_list =
338      *reinterpret_cast<AudioBufferList*>(list_storage.get());
339
340  result = AudioObjectGetPropertyData(
341      device,
342      &pa,
343      0,
344      0,
345      &size,
346      &buffer_list);
347  if (result != noErr)
348    return false;
349
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;
359  }
360
361  return true;
362}
363
364int AudioManagerMac::HardwareSampleRateForDevice(AudioDeviceID device_id) {
365  Float64 nominal_sample_rate;
366  UInt32 info_size = sizeof(nominal_sample_rate);
367
368  static const AudioObjectPropertyAddress kNominalSampleRateAddress = {
369      kAudioDevicePropertyNominalSampleRate,
370      kAudioObjectPropertyScopeGlobal,
371      kAudioObjectPropertyElementMaster
372  };
373  OSStatus result = AudioObjectGetPropertyData(
374      device_id,
375      &kNominalSampleRateAddress,
376      0,
377      0,
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;
384  }
385
386  return static_cast<int>(nominal_sample_rate);
387}
388
389int AudioManagerMac::HardwareSampleRate() {
390  // Determine the default output device's sample-rate.
391  AudioDeviceID device_id = kAudioObjectUnknown;
392  if (!GetDefaultOutputDevice(&device_id))
393    return kFallbackSampleRate;
394
395  return HardwareSampleRateForDevice(device_id);
396}
397
398void 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);
409  }
410}
411
412AudioParameters 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());
419
420  AudioDeviceID device = GetAudioDeviceIdByUId(true, device_id);
421  if (device == kAudioObjectUnknown) {
422    DLOG(ERROR) << "Invalid device " << device_id;
423    return AudioParameters();
424  }
425
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;
434  }
435
436  int sample_rate = HardwareSampleRateForDevice(device);
437  if (!sample_rate)
438    sample_rate = kFallbackSampleRate;
439
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);
444}
445
446AudioOutputStream* AudioManagerMac::MakeLinearOutputStream(
447    const AudioParameters& params) {
448  return MakeLowLatencyOutputStream(params, std::string());
449}
450
451AudioOutputStream* 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);
458  }
459
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;
466  }
467
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";
487  }
488
489  if (device != kAudioObjectUnknown &&
490      input_device_id == AudioManagerBase::kDefaultDeviceId)
491    return new AUHALStream(this, params, device);
492
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;
502
503  return new AudioSynchronizedStream(this,
504                                     params,
505                                     audio_device_id,
506                                     kAudioDeviceUnknown);
507}
508
509AudioInputStream* 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);
513}
514
515AudioInputStream* 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);
524
525  return stream;
526}
527
528AudioParameters 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;
534  }
535
536  ChannelLayout channel_layout = GuessChannelLayout(hardware_channels);
537
538  const int hardware_sample_rate = AUAudioOutputStream::HardwareSampleRate();
539  const int buffer_size = ChooseBufferSize(hardware_sample_rate);
540
541  int input_channels = 0;
542  if (input_params.IsValid()) {
543    input_channels = input_params.input_channels();
544
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;
551    }
552  }
553
554  AudioParameters params(
555      AudioParameters::AUDIO_PCM_LOW_LATENCY,
556      channel_layout,
557      input_channels,
558      hardware_sample_rate,
559      16,
560      buffer_size);
561
562  if (channel_layout == CHANNEL_LAYOUT_UNSUPPORTED)
563    params.SetDiscreteChannels(hardware_channels);
564
565  return params;
566}
567
568void AudioManagerMac::CreateDeviceListener() {
569  DCHECK(GetMessageLoop()->BelongsToCurrentThread());
570
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;
576
577  output_device_listener_.reset(new AudioDeviceListenerMac(base::Bind(
578      &AudioManagerMac::HandleDeviceChanges, base::Unretained(this))));
579}
580
581void AudioManagerMac::DestroyDeviceListener() {
582  DCHECK(GetMessageLoop()->BelongsToCurrentThread());
583  output_device_listener_.reset();
584}
585
586void AudioManagerMac::HandleDeviceChanges() {
587  if (!GetMessageLoop()->BelongsToCurrentThread()) {
588    GetMessageLoop()->PostTask(FROM_HERE, base::Bind(
589        &AudioManagerMac::HandleDeviceChanges, base::Unretained(this)));
590    return;
591  }
592
593  int new_sample_rate = HardwareSampleRate();
594  AudioDeviceID new_output_device;
595  GetDefaultOutputDevice(&new_output_device);
596
597  if (current_sample_rate_ == new_sample_rate &&
598      current_output_device_ == new_output_device)
599    return;
600
601  current_sample_rate_ = new_sample_rate;
602  current_output_device_ = new_output_device;
603  NotifyAllOutputDeviceChangeListeners();
604}
605
606AudioManager* CreateAudioManager() {
607  return new AudioManagerMac();
608}
609
610}  // namespace media
611