audio_manager_mac.cc revision 58537e28ecd584eab876aee8be7156509866d23a
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
84// Retrieves information on audio devices, and prepends the default
85// device to the list if the list is non-empty.
86static void GetAudioDeviceInfo(bool is_input,
87                               media::AudioDeviceNames* device_names) {
88  // Query the number of total devices.
89  AudioObjectPropertyAddress property_address = {
90    kAudioHardwarePropertyDevices,
91    kAudioObjectPropertyScopeGlobal,
92    kAudioObjectPropertyElementMaster
93  };
94  UInt32 size = 0;
95  OSStatus result = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject,
96                                                   &property_address,
97                                                   0,
98                                                   NULL,
99                                                   &size);
100  if (result || !size)
101    return;
102
103  int device_count = size / sizeof(AudioDeviceID);
104
105  // Get the array of device ids for all the devices, which includes both
106  // input devices and output devices.
107  scoped_ptr_malloc<AudioDeviceID>
108      devices(reinterpret_cast<AudioDeviceID*>(malloc(size)));
109  AudioDeviceID* device_ids = devices.get();
110  result = AudioObjectGetPropertyData(kAudioObjectSystemObject,
111                                      &property_address,
112                                      0,
113                                      NULL,
114                                      &size,
115                                      device_ids);
116  if (result)
117    return;
118
119  // Iterate over all available devices to gather information.
120  for (int i = 0; i < device_count; ++i) {
121    // Get the number of input or output channels of the device.
122    property_address.mScope = is_input ?
123        kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput;
124    property_address.mSelector = kAudioDevicePropertyStreams;
125    size = 0;
126    result = AudioObjectGetPropertyDataSize(device_ids[i],
127                                            &property_address,
128                                            0,
129                                            NULL,
130                                            &size);
131    if (result || !size)
132      continue;
133
134    // Get device UID.
135    CFStringRef uid = NULL;
136    size = sizeof(uid);
137    property_address.mSelector = kAudioDevicePropertyDeviceUID;
138    property_address.mScope = kAudioObjectPropertyScopeGlobal;
139    result = AudioObjectGetPropertyData(device_ids[i],
140                                        &property_address,
141                                        0,
142                                        NULL,
143                                        &size,
144                                        &uid);
145    if (result)
146      continue;
147
148    // Get device name.
149    CFStringRef name = NULL;
150    property_address.mSelector = kAudioObjectPropertyName;
151    property_address.mScope = kAudioObjectPropertyScopeGlobal;
152    result = AudioObjectGetPropertyData(device_ids[i],
153                                        &property_address,
154                                        0,
155                                        NULL,
156                                        &size,
157                                        &name);
158    if (result) {
159      if (uid)
160        CFRelease(uid);
161      continue;
162    }
163
164    // Store the device name and UID.
165    media::AudioDeviceName device_name;
166    device_name.device_name = base::SysCFStringRefToUTF8(name);
167    device_name.unique_id = base::SysCFStringRefToUTF8(uid);
168    device_names->push_back(device_name);
169
170    // We are responsible for releasing the returned CFObject.  See the
171    // comment in the AudioHardware.h for constant
172    // kAudioDevicePropertyDeviceUID.
173    if (uid)
174      CFRelease(uid);
175    if (name)
176      CFRelease(name);
177  }
178
179  if (!device_names->empty()) {
180    // Prepend the default device to the list since we always want it to be
181    // on the top of the list for all platforms. There is no duplicate
182    // counting here since the default device has been abstracted out before.
183    media::AudioDeviceName name;
184    name.device_name = AudioManagerBase::kDefaultDeviceName;
185    name.unique_id = AudioManagerBase::kDefaultDeviceId;
186    device_names->push_front(name);
187  }
188}
189
190static AudioDeviceID GetAudioDeviceIdByUId(bool is_input,
191                                           const std::string& device_id) {
192  AudioObjectPropertyAddress property_address = {
193    kAudioHardwarePropertyDevices,
194    kAudioObjectPropertyScopeGlobal,
195    kAudioObjectPropertyElementMaster
196  };
197  AudioDeviceID audio_device_id = kAudioObjectUnknown;
198  UInt32 device_size = sizeof(audio_device_id);
199  OSStatus result = -1;
200
201  if (device_id == AudioManagerBase::kDefaultDeviceId || device_id.empty()) {
202    // Default Device.
203    property_address.mSelector = is_input ?
204        kAudioHardwarePropertyDefaultInputDevice :
205        kAudioHardwarePropertyDefaultOutputDevice;
206
207    result = AudioObjectGetPropertyData(kAudioObjectSystemObject,
208                                        &property_address,
209                                        0,
210                                        0,
211                                        &device_size,
212                                        &audio_device_id);
213  } else {
214    // Non-default device.
215    base::ScopedCFTypeRef<CFStringRef> uid(
216        base::SysUTF8ToCFStringRef(device_id));
217    AudioValueTranslation value;
218    value.mInputData = &uid;
219    value.mInputDataSize = sizeof(CFStringRef);
220    value.mOutputData = &audio_device_id;
221    value.mOutputDataSize = device_size;
222    UInt32 translation_size = sizeof(AudioValueTranslation);
223
224    property_address.mSelector = kAudioHardwarePropertyDeviceForUID;
225    result = AudioObjectGetPropertyData(kAudioObjectSystemObject,
226                                        &property_address,
227                                        0,
228                                        0,
229                                        &translation_size,
230                                        &value);
231  }
232
233  if (result) {
234    OSSTATUS_DLOG(WARNING, result) << "Unable to query device " << device_id
235                                   << " for AudioDeviceID";
236  }
237
238  return audio_device_id;
239}
240
241AudioManagerMac::AudioManagerMac()
242    : current_sample_rate_(0) {
243  current_output_device_ = kAudioDeviceUnknown;
244
245  SetMaxOutputStreamsAllowed(kMaxOutputStreams);
246
247  // Task must be posted last to avoid races from handing out "this" to the
248  // audio thread.  Always PostTask even if we're on the right thread since
249  // AudioManager creation is on the startup path and this may be slow.
250  GetMessageLoop()->PostTask(FROM_HERE, base::Bind(
251      &AudioManagerMac::CreateDeviceListener, base::Unretained(this)));
252}
253
254AudioManagerMac::~AudioManagerMac() {
255  if (GetMessageLoop()->BelongsToCurrentThread()) {
256    DestroyDeviceListener();
257  } else {
258    // It's safe to post a task here since Shutdown() will wait for all tasks to
259    // complete before returning.
260    GetMessageLoop()->PostTask(FROM_HERE, base::Bind(
261        &AudioManagerMac::DestroyDeviceListener, base::Unretained(this)));
262  }
263
264  Shutdown();
265}
266
267bool AudioManagerMac::HasAudioOutputDevices() {
268  return HasAudioHardware(kAudioHardwarePropertyDefaultOutputDevice);
269}
270
271bool AudioManagerMac::HasAudioInputDevices() {
272  return HasAudioHardware(kAudioHardwarePropertyDefaultInputDevice);
273}
274
275// TODO(xians): There are several places on the OSX specific code which
276// could benefit from these helper functions.
277bool AudioManagerMac::GetDefaultInputDevice(
278    AudioDeviceID* device) {
279  return GetDefaultDevice(device, true);
280}
281
282bool AudioManagerMac::GetDefaultOutputDevice(
283    AudioDeviceID* device) {
284  return GetDefaultDevice(device, false);
285}
286
287bool AudioManagerMac::GetDefaultDevice(
288    AudioDeviceID* device, bool input) {
289  CHECK(device);
290
291  // Obtain the current output device selected by the user.
292  AudioObjectPropertyAddress pa;
293  pa.mSelector = input ? kAudioHardwarePropertyDefaultInputDevice :
294      kAudioHardwarePropertyDefaultOutputDevice;
295  pa.mScope = kAudioObjectPropertyScopeGlobal;
296  pa.mElement = kAudioObjectPropertyElementMaster;
297
298  UInt32 size = sizeof(*device);
299
300  OSStatus result = AudioObjectGetPropertyData(
301      kAudioObjectSystemObject,
302      &pa,
303      0,
304      0,
305      &size,
306      device);
307
308  if ((result != kAudioHardwareNoError) || (*device == kAudioDeviceUnknown)) {
309    DLOG(ERROR) << "Error getting default AudioDevice.";
310    return false;
311  }
312
313  return true;
314}
315
316bool AudioManagerMac::GetDefaultOutputChannels(
317    int* channels) {
318  AudioDeviceID device;
319  if (!GetDefaultOutputDevice(&device))
320    return false;
321
322  return GetDeviceChannels(device,
323                           kAudioDevicePropertyScopeOutput,
324                           channels);
325}
326
327bool AudioManagerMac::GetDeviceChannels(
328    AudioDeviceID device,
329    AudioObjectPropertyScope scope,
330    int* channels) {
331  CHECK(channels);
332
333  // Get stream configuration.
334  AudioObjectPropertyAddress pa;
335  pa.mSelector = kAudioDevicePropertyStreamConfiguration;
336  pa.mScope = scope;
337  pa.mElement = kAudioObjectPropertyElementMaster;
338
339  UInt32 size;
340  OSStatus result = AudioObjectGetPropertyDataSize(device, &pa, 0, 0, &size);
341  if (result != noErr || !size)
342    return false;
343
344  // Allocate storage.
345  scoped_ptr<uint8[]> list_storage(new uint8[size]);
346  AudioBufferList& buffer_list =
347      *reinterpret_cast<AudioBufferList*>(list_storage.get());
348
349  result = AudioObjectGetPropertyData(
350      device,
351      &pa,
352      0,
353      0,
354      &size,
355      &buffer_list);
356  if (result != noErr)
357    return false;
358
359  // Determine number of input channels.
360  int channels_per_frame = buffer_list.mNumberBuffers > 0 ?
361      buffer_list.mBuffers[0].mNumberChannels : 0;
362  if (channels_per_frame == 1 && buffer_list.mNumberBuffers > 1) {
363    // Non-interleaved.
364    *channels = buffer_list.mNumberBuffers;
365  } else {
366    // Interleaved.
367    *channels = channels_per_frame;
368  }
369
370  return true;
371}
372
373int AudioManagerMac::HardwareSampleRateForDevice(AudioDeviceID device_id) {
374  Float64 nominal_sample_rate;
375  UInt32 info_size = sizeof(nominal_sample_rate);
376
377  static const AudioObjectPropertyAddress kNominalSampleRateAddress = {
378      kAudioDevicePropertyNominalSampleRate,
379      kAudioObjectPropertyScopeGlobal,
380      kAudioObjectPropertyElementMaster
381  };
382  OSStatus result = AudioObjectGetPropertyData(
383      device_id,
384      &kNominalSampleRateAddress,
385      0,
386      0,
387      &info_size,
388      &nominal_sample_rate);
389  if (result != noErr) {
390    OSSTATUS_DLOG(WARNING, result)
391        << "Could not get default sample rate for device: " << device_id;
392    return 0;
393  }
394
395  return static_cast<int>(nominal_sample_rate);
396}
397
398int AudioManagerMac::HardwareSampleRate() {
399  // Determine the default output device's sample-rate.
400  AudioDeviceID device_id = kAudioObjectUnknown;
401  if (!GetDefaultOutputDevice(&device_id))
402    return kFallbackSampleRate;
403
404  return HardwareSampleRateForDevice(device_id);
405}
406
407void AudioManagerMac::GetAudioInputDeviceNames(
408    media::AudioDeviceNames* device_names) {
409  DCHECK(device_names->empty());
410  GetAudioDeviceInfo(true, device_names);
411}
412
413void AudioManagerMac::GetAudioOutputDeviceNames(
414    media::AudioDeviceNames* device_names) {
415  DCHECK(device_names->empty());
416  GetAudioDeviceInfo(false, device_names);
417}
418
419AudioParameters AudioManagerMac::GetInputStreamParameters(
420    const std::string& device_id) {
421  // Due to the sharing of the input and output buffer sizes, we need to choose
422  // the input buffer size based on the output sample rate.  See
423  // http://crbug.com/154352.
424  const int buffer_size = ChooseBufferSize(
425      AUAudioOutputStream::HardwareSampleRate());
426
427  AudioDeviceID device = GetAudioDeviceIdByUId(true, device_id);
428  if (device == kAudioObjectUnknown) {
429    DLOG(ERROR) << "Invalid device " << device_id;
430    return AudioParameters();
431  }
432
433  int channels = 0;
434  ChannelLayout channel_layout = CHANNEL_LAYOUT_STEREO;
435  if (GetDeviceChannels(device, kAudioDevicePropertyScopeInput, &channels) &&
436      channels <= 2) {
437    channel_layout = GuessChannelLayout(channels);
438  } else {
439    DLOG(ERROR) << "Failed to get the device channels, use stereo as default "
440                << "for device " << device_id;
441  }
442
443  int sample_rate = HardwareSampleRateForDevice(device);
444  if (!sample_rate)
445    sample_rate = kFallbackSampleRate;
446
447  // TODO(xians): query the native channel layout for the specific device.
448  return AudioParameters(
449      AudioParameters::AUDIO_PCM_LOW_LATENCY, channel_layout,
450      sample_rate, 16, buffer_size);
451}
452
453std::string AudioManagerMac::GetAssociatedOutputDeviceID(
454    const std::string& input_device_id) {
455  AudioDeviceID device = GetAudioDeviceIdByUId(true, input_device_id);
456  if (device == kAudioObjectUnknown)
457    return std::string();
458
459  UInt32 size = 0;
460  AudioObjectPropertyAddress pa = {
461    kAudioDevicePropertyRelatedDevices,
462    kAudioDevicePropertyScopeOutput,
463    kAudioObjectPropertyElementMaster
464  };
465  OSStatus result = AudioObjectGetPropertyDataSize(device, &pa, 0, 0, &size);
466  if (result || !size)
467    return std::string();
468
469  int device_count = size / sizeof(AudioDeviceID);
470  scoped_ptr_malloc<AudioDeviceID>
471      devices(reinterpret_cast<AudioDeviceID*>(malloc(size)));
472  result = AudioObjectGetPropertyData(
473      device, &pa, 0, NULL, &size, devices.get());
474  if (result)
475    return std::string();
476
477  for (int i = 0; i < device_count; ++i) {
478    // Get the number of  output channels of the device.
479    pa.mSelector = kAudioDevicePropertyStreams;
480    size = 0;
481    result = AudioObjectGetPropertyDataSize(devices.get()[i],
482                                            &pa,
483                                            0,
484                                            NULL,
485                                            &size);
486    if (result || !size)
487      continue;  // Skip if there aren't any output channels.
488
489    // Get device UID.
490    CFStringRef uid = NULL;
491    size = sizeof(uid);
492    pa.mSelector = kAudioDevicePropertyDeviceUID;
493    result = AudioObjectGetPropertyData(devices.get()[i],
494                                        &pa,
495                                        0,
496                                        NULL,
497                                        &size,
498                                        &uid);
499    if (result || !uid)
500      continue;
501
502    std::string ret(base::SysCFStringRefToUTF8(uid));
503    CFRelease(uid);
504    return ret;
505  }
506
507  // No matching device found.
508  return std::string();
509}
510
511AudioOutputStream* AudioManagerMac::MakeLinearOutputStream(
512    const AudioParameters& params) {
513  return MakeLowLatencyOutputStream(params, std::string(), std::string());
514}
515
516AudioOutputStream* AudioManagerMac::MakeLowLatencyOutputStream(
517    const AudioParameters& params,
518    const std::string& device_id,
519    const std::string& input_device_id) {
520  // Handle basic output with no input channels.
521  if (params.input_channels() == 0) {
522    AudioDeviceID device = GetAudioDeviceIdByUId(false, device_id);
523    if (device == kAudioObjectUnknown) {
524      DLOG(ERROR) << "Failed to open output device: " << device_id;
525      return NULL;
526    }
527    return new AUHALStream(this, params, device);
528  }
529
530  DLOG_IF(ERROR, !device_id.empty()) << "Not implemented!";
531
532  // TODO(xians): support more than stereo input.
533  if (params.input_channels() != 2) {
534    // WebAudio is currently hard-coded to 2 channels so we should not
535    // see this case.
536    NOTREACHED() << "Only stereo input is currently supported!";
537    return NULL;
538  }
539
540  AudioDeviceID device = kAudioObjectUnknown;
541  if (HasUnifiedDefaultIO()) {
542    // For I/O, the simplest case is when the default input and output
543    // devices are the same.
544    GetDefaultOutputDevice(&device);
545    LOG(INFO) << "UNIFIED: default input and output devices are identical";
546  } else {
547    // Some audio hardware is presented as separate input and output devices
548    // even though they are really the same physical hardware and
549    // share the same "clock domain" at the lowest levels of the driver.
550    // A common of example of this is the "built-in" audio hardware:
551    //     "Built-in Line Input"
552    //     "Built-in Output"
553    // We would like to use an "aggregate" device for these situations, since
554    // CoreAudio will make the most efficient use of the shared "clock domain"
555    // so we get the lowest latency and use fewer threads.
556    device = aggregate_device_manager_.GetDefaultAggregateDevice();
557    if (device != kAudioObjectUnknown)
558      LOG(INFO) << "Using AGGREGATE audio device";
559  }
560
561  if (device != kAudioObjectUnknown &&
562      input_device_id == AudioManagerBase::kDefaultDeviceId)
563    return new AUHALStream(this, params, device);
564
565  // Fallback to AudioSynchronizedStream which will handle completely
566  // different and arbitrary combinations of input and output devices
567  // even running at different sample-rates.
568  // kAudioDeviceUnknown translates to "use default" here.
569  // TODO(xians): consider tracking UMA stats on AUHALStream
570  // versus AudioSynchronizedStream.
571  AudioDeviceID audio_device_id = GetAudioDeviceIdByUId(true, input_device_id);
572  if (audio_device_id == kAudioObjectUnknown)
573    return NULL;
574
575  return new AudioSynchronizedStream(this,
576                                     params,
577                                     audio_device_id,
578                                     kAudioDeviceUnknown);
579}
580
581std::string AudioManagerMac::GetDefaultOutputDeviceID() {
582  AudioDeviceID device_id = kAudioObjectUnknown;
583  if (!GetDefaultOutputDevice(&device_id))
584    return std::string();
585
586  const AudioObjectPropertyAddress property_address = {
587    kAudioDevicePropertyDeviceUID,
588    kAudioObjectPropertyScopeGlobal,
589    kAudioObjectPropertyElementMaster
590  };
591  CFStringRef device_uid = NULL;
592  UInt32 size = sizeof(device_uid);
593  OSStatus status = AudioObjectGetPropertyData(device_id,
594                                               &property_address,
595                                               0,
596                                               NULL,
597                                               &size,
598                                               &device_uid);
599  if (status != kAudioHardwareNoError || !device_uid)
600    return std::string();
601
602  std::string ret(base::SysCFStringRefToUTF8(device_uid));
603  CFRelease(device_uid);
604
605  return ret;
606}
607
608AudioInputStream* AudioManagerMac::MakeLinearInputStream(
609    const AudioParameters& params, const std::string& device_id) {
610  DCHECK_EQ(AudioParameters::AUDIO_PCM_LINEAR, params.format());
611  return new PCMQueueInAudioInputStream(this, params);
612}
613
614AudioInputStream* AudioManagerMac::MakeLowLatencyInputStream(
615    const AudioParameters& params, const std::string& device_id) {
616  DCHECK_EQ(AudioParameters::AUDIO_PCM_LOW_LATENCY, params.format());
617  // Gets the AudioDeviceID that refers to the AudioInputDevice with the device
618  // unique id. This AudioDeviceID is used to set the device for Audio Unit.
619  AudioDeviceID audio_device_id = GetAudioDeviceIdByUId(true, device_id);
620  AudioInputStream* stream = NULL;
621  if (audio_device_id != kAudioObjectUnknown) {
622    // AUAudioInputStream needs to be fed the preferred audio output parameters
623    // of the matching device so that the buffer size of both input and output
624    // can be matched.  See constructor of AUAudioInputStream for more.
625    const std::string associated_output_device(
626        GetAssociatedOutputDeviceID(device_id));
627    const AudioParameters output_params =
628        GetPreferredOutputStreamParameters(
629            associated_output_device.empty() ?
630                AudioManagerBase::kDefaultDeviceId : associated_output_device,
631            params);
632    stream = new AUAudioInputStream(this, params, output_params,
633        audio_device_id);
634  }
635
636  return stream;
637}
638
639AudioParameters AudioManagerMac::GetPreferredOutputStreamParameters(
640    const std::string& output_device_id,
641    const AudioParameters& input_params) {
642  AudioDeviceID device = GetAudioDeviceIdByUId(false, output_device_id);
643  if (device == kAudioObjectUnknown) {
644    DLOG(ERROR) << "Invalid output device " << output_device_id;
645    return AudioParameters();
646  }
647
648  int hardware_channels = 2;
649  if (!GetDeviceChannels(device, kAudioDevicePropertyScopeOutput,
650                         &hardware_channels)) {
651    // Fallback to stereo.
652    hardware_channels = 2;
653  }
654
655  ChannelLayout channel_layout = GuessChannelLayout(hardware_channels);
656
657  const int hardware_sample_rate = HardwareSampleRateForDevice(device);
658  const int buffer_size = ChooseBufferSize(hardware_sample_rate);
659
660  int input_channels = 0;
661  if (input_params.IsValid()) {
662    input_channels = input_params.input_channels();
663
664    if (input_channels > 0) {
665      // TODO(xians): given the limitations of the AudioOutputStream
666      // back-ends used with synchronized I/O, we hard-code to stereo.
667      // Specifically, this is a limitation of AudioSynchronizedStream which
668      // can be removed as part of the work to consolidate these back-ends.
669      channel_layout = CHANNEL_LAYOUT_STEREO;
670    }
671  }
672
673  AudioParameters params(
674      AudioParameters::AUDIO_PCM_LOW_LATENCY,
675      channel_layout,
676      input_channels,
677      hardware_sample_rate,
678      16,
679      buffer_size);
680
681  if (channel_layout == CHANNEL_LAYOUT_UNSUPPORTED)
682    params.SetDiscreteChannels(hardware_channels);
683
684  return params;
685}
686
687void AudioManagerMac::CreateDeviceListener() {
688  DCHECK(GetMessageLoop()->BelongsToCurrentThread());
689
690  // Get a baseline for the sample-rate and current device,
691  // so we can intelligently handle device notifications only when necessary.
692  current_sample_rate_ = HardwareSampleRate();
693  if (!GetDefaultOutputDevice(&current_output_device_))
694    current_output_device_ = kAudioDeviceUnknown;
695
696  output_device_listener_.reset(new AudioDeviceListenerMac(base::Bind(
697      &AudioManagerMac::HandleDeviceChanges, base::Unretained(this))));
698}
699
700void AudioManagerMac::DestroyDeviceListener() {
701  DCHECK(GetMessageLoop()->BelongsToCurrentThread());
702  output_device_listener_.reset();
703}
704
705void AudioManagerMac::HandleDeviceChanges() {
706  if (!GetMessageLoop()->BelongsToCurrentThread()) {
707    GetMessageLoop()->PostTask(FROM_HERE, base::Bind(
708        &AudioManagerMac::HandleDeviceChanges, base::Unretained(this)));
709    return;
710  }
711
712  int new_sample_rate = HardwareSampleRate();
713  AudioDeviceID new_output_device;
714  GetDefaultOutputDevice(&new_output_device);
715
716  if (current_sample_rate_ == new_sample_rate &&
717      current_output_device_ == new_output_device)
718    return;
719
720  current_sample_rate_ = new_sample_rate;
721  current_output_device_ = new_output_device;
722  NotifyAllOutputDeviceChangeListeners();
723}
724
725AudioManager* CreateAudioManager() {
726  return new AudioManagerMac();
727}
728
729}  // namespace media
730