Home | History | Annotate | Download | only in mac
      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 
     28 namespace media {
     29 
     30 // Maximum number of output streams that can be open simultaneously.
     31 static const int kMaxOutputStreams = 50;
     32 
     33 // Default buffer size in samples for low-latency input and output streams.
     34 static const int kDefaultLowLatencyBufferSize = 128;
     35 
     36 // Default sample-rate on most Apple hardware.
     37 static const int kFallbackSampleRate = 44100;
     38 
     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;
     51   }
     52 
     53   return buffer_size;
     54 }
     55 
     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
     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.
     76 bool 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 
     84 static 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 
    181 static 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::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);
    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 
    232 AudioManagerMac::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 
    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)));
    253   }
    254 
    255   Shutdown();
    256 }
    257 
    258 bool AudioManagerMac::HasAudioOutputDevices() {
    259   return HasAudioHardware(kAudioHardwarePropertyDefaultOutputDevice);
    260 }
    261 
    262 bool 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.
    268 bool AudioManagerMac::GetDefaultInputDevice(
    269     AudioDeviceID* device) {
    270   return GetDefaultDevice(device, true);
    271 }
    272 
    273 bool AudioManagerMac::GetDefaultOutputDevice(
    274     AudioDeviceID* device) {
    275   return GetDefaultDevice(device, false);
    276 }
    277 
    278 bool 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 
    307 bool 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 
    318 bool 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 
    364 int 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 
    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;
    394 
    395   return HardwareSampleRateForDevice(device_id);
    396 }
    397 
    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);
    409   }
    410 }
    411 
    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());
    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 
    446 AudioOutputStream* AudioManagerMac::MakeLinearOutputStream(
    447     const AudioParameters& params) {
    448   return MakeLowLatencyOutputStream(params, std::string());
    449 }
    450 
    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);
    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 
    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);
    513 }
    514 
    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);
    524 
    525   return stream;
    526 }
    527 
    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;
    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 
    568 void 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 
    581 void AudioManagerMac::DestroyDeviceListener() {
    582   DCHECK(GetMessageLoop()->BelongsToCurrentThread());
    583   output_device_listener_.reset();
    584 }
    585 
    586 void 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 
    606 AudioManager* CreateAudioManager() {
    607   return new AudioManagerMac();
    608 }
    609 
    610 }  // namespace media
    611