audio_manager_mac.cc revision 5d1f7b1de12d16ceb2c938c56701a3e8bfa558f7
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/power_monitor/power_monitor.h"
15#include "base/power_monitor/power_observer.h"
16#include "base/strings/sys_string_conversions.h"
17#include "base/threading/thread_checker.h"
18#include "media/audio/audio_parameters.h"
19#include "media/audio/mac/audio_auhal_mac.h"
20#include "media/audio/mac/audio_input_mac.h"
21#include "media/audio/mac/audio_low_latency_input_mac.h"
22#include "media/audio/mac/audio_low_latency_output_mac.h"
23#include "media/base/bind_to_current_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 bool HasAudioHardware(AudioObjectPropertySelector selector) {
40  AudioDeviceID output_device_id = kAudioObjectUnknown;
41  const AudioObjectPropertyAddress property_address = {
42    selector,
43    kAudioObjectPropertyScopeGlobal,            // mScope
44    kAudioObjectPropertyElementMaster           // mElement
45  };
46  UInt32 output_device_id_size = static_cast<UInt32>(sizeof(output_device_id));
47  OSStatus err = AudioObjectGetPropertyData(kAudioObjectSystemObject,
48                                            &property_address,
49                                            0,     // inQualifierDataSize
50                                            NULL,  // inQualifierData
51                                            &output_device_id_size,
52                                            &output_device_id);
53  return err == kAudioHardwareNoError &&
54      output_device_id != kAudioObjectUnknown;
55}
56
57// Retrieves information on audio devices, and prepends the default
58// device to the list if the list is non-empty.
59static void GetAudioDeviceInfo(bool is_input,
60                               media::AudioDeviceNames* device_names) {
61  // Query the number of total devices.
62  AudioObjectPropertyAddress property_address = {
63    kAudioHardwarePropertyDevices,
64    kAudioObjectPropertyScopeGlobal,
65    kAudioObjectPropertyElementMaster
66  };
67  UInt32 size = 0;
68  OSStatus result = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject,
69                                                   &property_address,
70                                                   0,
71                                                   NULL,
72                                                   &size);
73  if (result || !size)
74    return;
75
76  int device_count = size / sizeof(AudioDeviceID);
77
78  // Get the array of device ids for all the devices, which includes both
79  // input devices and output devices.
80  scoped_ptr_malloc<AudioDeviceID>
81      devices(reinterpret_cast<AudioDeviceID*>(malloc(size)));
82  AudioDeviceID* device_ids = devices.get();
83  result = AudioObjectGetPropertyData(kAudioObjectSystemObject,
84                                      &property_address,
85                                      0,
86                                      NULL,
87                                      &size,
88                                      device_ids);
89  if (result)
90    return;
91
92  // Iterate over all available devices to gather information.
93  for (int i = 0; i < device_count; ++i) {
94    // Get the number of input or output channels of the device.
95    property_address.mScope = is_input ?
96        kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput;
97    property_address.mSelector = kAudioDevicePropertyStreams;
98    size = 0;
99    result = AudioObjectGetPropertyDataSize(device_ids[i],
100                                            &property_address,
101                                            0,
102                                            NULL,
103                                            &size);
104    if (result || !size)
105      continue;
106
107    // Get device UID.
108    CFStringRef uid = NULL;
109    size = sizeof(uid);
110    property_address.mSelector = kAudioDevicePropertyDeviceUID;
111    property_address.mScope = kAudioObjectPropertyScopeGlobal;
112    result = AudioObjectGetPropertyData(device_ids[i],
113                                        &property_address,
114                                        0,
115                                        NULL,
116                                        &size,
117                                        &uid);
118    if (result)
119      continue;
120
121    // Get device name.
122    CFStringRef name = NULL;
123    property_address.mSelector = kAudioObjectPropertyName;
124    property_address.mScope = kAudioObjectPropertyScopeGlobal;
125    result = AudioObjectGetPropertyData(device_ids[i],
126                                        &property_address,
127                                        0,
128                                        NULL,
129                                        &size,
130                                        &name);
131    if (result) {
132      if (uid)
133        CFRelease(uid);
134      continue;
135    }
136
137    // Store the device name and UID.
138    media::AudioDeviceName device_name;
139    device_name.device_name = base::SysCFStringRefToUTF8(name);
140    device_name.unique_id = base::SysCFStringRefToUTF8(uid);
141    device_names->push_back(device_name);
142
143    // We are responsible for releasing the returned CFObject.  See the
144    // comment in the AudioHardware.h for constant
145    // kAudioDevicePropertyDeviceUID.
146    if (uid)
147      CFRelease(uid);
148    if (name)
149      CFRelease(name);
150  }
151
152  if (!device_names->empty()) {
153    // Prepend the default device to the list since we always want it to be
154    // on the top of the list for all platforms. There is no duplicate
155    // counting here since the default device has been abstracted out before.
156    media::AudioDeviceName name;
157    name.device_name = AudioManagerBase::kDefaultDeviceName;
158    name.unique_id = AudioManagerBase::kDefaultDeviceId;
159    device_names->push_front(name);
160  }
161}
162
163static AudioDeviceID GetAudioDeviceIdByUId(bool is_input,
164                                           const std::string& device_id) {
165  AudioObjectPropertyAddress property_address = {
166    kAudioHardwarePropertyDevices,
167    kAudioObjectPropertyScopeGlobal,
168    kAudioObjectPropertyElementMaster
169  };
170  AudioDeviceID audio_device_id = kAudioObjectUnknown;
171  UInt32 device_size = sizeof(audio_device_id);
172  OSStatus result = -1;
173
174  if (device_id == AudioManagerBase::kDefaultDeviceId || device_id.empty()) {
175    // Default Device.
176    property_address.mSelector = is_input ?
177        kAudioHardwarePropertyDefaultInputDevice :
178        kAudioHardwarePropertyDefaultOutputDevice;
179
180    result = AudioObjectGetPropertyData(kAudioObjectSystemObject,
181                                        &property_address,
182                                        0,
183                                        0,
184                                        &device_size,
185                                        &audio_device_id);
186  } else {
187    // Non-default device.
188    base::ScopedCFTypeRef<CFStringRef> uid(
189        base::SysUTF8ToCFStringRef(device_id));
190    AudioValueTranslation value;
191    value.mInputData = &uid;
192    value.mInputDataSize = sizeof(CFStringRef);
193    value.mOutputData = &audio_device_id;
194    value.mOutputDataSize = device_size;
195    UInt32 translation_size = sizeof(AudioValueTranslation);
196
197    property_address.mSelector = kAudioHardwarePropertyDeviceForUID;
198    result = AudioObjectGetPropertyData(kAudioObjectSystemObject,
199                                        &property_address,
200                                        0,
201                                        0,
202                                        &translation_size,
203                                        &value);
204  }
205
206  if (result) {
207    OSSTATUS_DLOG(WARNING, result) << "Unable to query device " << device_id
208                                   << " for AudioDeviceID";
209  }
210
211  return audio_device_id;
212}
213
214class AudioManagerMac::AudioPowerObserver : public base::PowerObserver {
215 public:
216  AudioPowerObserver()
217      : is_suspending_(false),
218        is_monitoring_(base::PowerMonitor::Get()) {
219    // The PowerMonitor requires signifcant setup (a CFRunLoop and preallocated
220    // IO ports) so it's not available under unit tests.  See the OSX impl of
221    // base::PowerMonitorDeviceSource for more details.
222    if (!is_monitoring_)
223      return;
224    base::PowerMonitor::Get()->AddObserver(this);
225  }
226
227  virtual ~AudioPowerObserver() {
228    DCHECK(thread_checker_.CalledOnValidThread());
229    if (!is_monitoring_)
230      return;
231    base::PowerMonitor::Get()->RemoveObserver(this);
232  }
233
234  bool ShouldDeferOutputStreamStart() {
235    DCHECK(thread_checker_.CalledOnValidThread());
236    // Start() should be deferred if the system is in the middle of a suspend or
237    // has recently started the process of resuming.
238    return is_suspending_ || base::TimeTicks::Now() < earliest_start_time_;
239  }
240
241 private:
242  virtual void OnSuspend() OVERRIDE {
243    DCHECK(thread_checker_.CalledOnValidThread());
244    is_suspending_ = true;
245  }
246
247  virtual void OnResume() OVERRIDE {
248    DCHECK(thread_checker_.CalledOnValidThread());
249    is_suspending_ = false;
250    earliest_start_time_ = base::TimeTicks::Now() +
251        base::TimeDelta::FromSeconds(kStartDelayInSecsForPowerEvents);
252  }
253
254  bool is_suspending_;
255  const bool is_monitoring_;
256  base::TimeTicks earliest_start_time_;
257  base::ThreadChecker thread_checker_;
258
259  DISALLOW_COPY_AND_ASSIGN(AudioPowerObserver);
260};
261
262AudioManagerMac::AudioManagerMac(AudioLogFactory* audio_log_factory)
263    : AudioManagerBase(audio_log_factory),
264      current_sample_rate_(0),
265      current_output_device_(kAudioDeviceUnknown) {
266  SetMaxOutputStreamsAllowed(kMaxOutputStreams);
267
268  // Task must be posted last to avoid races from handing out "this" to the
269  // audio thread.  Always PostTask even if we're on the right thread since
270  // AudioManager creation is on the startup path and this may be slow.
271  GetTaskRunner()->PostTask(FROM_HERE, base::Bind(
272      &AudioManagerMac::InitializeOnAudioThread, base::Unretained(this)));
273}
274
275AudioManagerMac::~AudioManagerMac() {
276  if (GetTaskRunner()->BelongsToCurrentThread()) {
277    ShutdownOnAudioThread();
278  } else {
279    // It's safe to post a task here since Shutdown() will wait for all tasks to
280    // complete before returning.
281    GetTaskRunner()->PostTask(FROM_HERE, base::Bind(
282        &AudioManagerMac::ShutdownOnAudioThread, base::Unretained(this)));
283  }
284
285  Shutdown();
286}
287
288bool AudioManagerMac::HasAudioOutputDevices() {
289  return HasAudioHardware(kAudioHardwarePropertyDefaultOutputDevice);
290}
291
292bool AudioManagerMac::HasAudioInputDevices() {
293  return HasAudioHardware(kAudioHardwarePropertyDefaultInputDevice);
294}
295
296// TODO(xians): There are several places on the OSX specific code which
297// could benefit from these helper functions.
298bool AudioManagerMac::GetDefaultInputDevice(
299    AudioDeviceID* device) {
300  return GetDefaultDevice(device, true);
301}
302
303bool AudioManagerMac::GetDefaultOutputDevice(
304    AudioDeviceID* device) {
305  return GetDefaultDevice(device, false);
306}
307
308bool AudioManagerMac::GetDefaultDevice(
309    AudioDeviceID* device, bool input) {
310  CHECK(device);
311
312  // Obtain the current output device selected by the user.
313  AudioObjectPropertyAddress pa;
314  pa.mSelector = input ? kAudioHardwarePropertyDefaultInputDevice :
315      kAudioHardwarePropertyDefaultOutputDevice;
316  pa.mScope = kAudioObjectPropertyScopeGlobal;
317  pa.mElement = kAudioObjectPropertyElementMaster;
318
319  UInt32 size = sizeof(*device);
320
321  OSStatus result = AudioObjectGetPropertyData(
322      kAudioObjectSystemObject,
323      &pa,
324      0,
325      0,
326      &size,
327      device);
328
329  if ((result != kAudioHardwareNoError) || (*device == kAudioDeviceUnknown)) {
330    DLOG(ERROR) << "Error getting default AudioDevice.";
331    return false;
332  }
333
334  return true;
335}
336
337bool AudioManagerMac::GetDefaultOutputChannels(
338    int* channels) {
339  AudioDeviceID device;
340  if (!GetDefaultOutputDevice(&device))
341    return false;
342
343  return GetDeviceChannels(device,
344                           kAudioDevicePropertyScopeOutput,
345                           channels);
346}
347
348bool AudioManagerMac::GetDeviceChannels(
349    AudioDeviceID device,
350    AudioObjectPropertyScope scope,
351    int* channels) {
352  CHECK(channels);
353
354  // Get stream configuration.
355  AudioObjectPropertyAddress pa;
356  pa.mSelector = kAudioDevicePropertyStreamConfiguration;
357  pa.mScope = scope;
358  pa.mElement = kAudioObjectPropertyElementMaster;
359
360  UInt32 size;
361  OSStatus result = AudioObjectGetPropertyDataSize(device, &pa, 0, 0, &size);
362  if (result != noErr || !size)
363    return false;
364
365  // Allocate storage.
366  scoped_ptr<uint8[]> list_storage(new uint8[size]);
367  AudioBufferList& buffer_list =
368      *reinterpret_cast<AudioBufferList*>(list_storage.get());
369
370  result = AudioObjectGetPropertyData(
371      device,
372      &pa,
373      0,
374      0,
375      &size,
376      &buffer_list);
377  if (result != noErr)
378    return false;
379
380  // Determine number of input channels.
381  int channels_per_frame = buffer_list.mNumberBuffers > 0 ?
382      buffer_list.mBuffers[0].mNumberChannels : 0;
383  if (channels_per_frame == 1 && buffer_list.mNumberBuffers > 1) {
384    // Non-interleaved.
385    *channels = buffer_list.mNumberBuffers;
386  } else {
387    // Interleaved.
388    *channels = channels_per_frame;
389  }
390
391  return true;
392}
393
394int AudioManagerMac::HardwareSampleRateForDevice(AudioDeviceID device_id) {
395  Float64 nominal_sample_rate;
396  UInt32 info_size = sizeof(nominal_sample_rate);
397
398  static const AudioObjectPropertyAddress kNominalSampleRateAddress = {
399      kAudioDevicePropertyNominalSampleRate,
400      kAudioObjectPropertyScopeGlobal,
401      kAudioObjectPropertyElementMaster
402  };
403  OSStatus result = AudioObjectGetPropertyData(
404      device_id,
405      &kNominalSampleRateAddress,
406      0,
407      0,
408      &info_size,
409      &nominal_sample_rate);
410  if (result != noErr) {
411    OSSTATUS_DLOG(WARNING, result)
412        << "Could not get default sample rate for device: " << device_id;
413    return 0;
414  }
415
416  return static_cast<int>(nominal_sample_rate);
417}
418
419int AudioManagerMac::HardwareSampleRate() {
420  // Determine the default output device's sample-rate.
421  AudioDeviceID device_id = kAudioObjectUnknown;
422  if (!GetDefaultOutputDevice(&device_id))
423    return kFallbackSampleRate;
424
425  return HardwareSampleRateForDevice(device_id);
426}
427
428void AudioManagerMac::GetAudioInputDeviceNames(
429    media::AudioDeviceNames* device_names) {
430  DCHECK(device_names->empty());
431  GetAudioDeviceInfo(true, device_names);
432}
433
434void AudioManagerMac::GetAudioOutputDeviceNames(
435    media::AudioDeviceNames* device_names) {
436  DCHECK(device_names->empty());
437  GetAudioDeviceInfo(false, device_names);
438}
439
440AudioParameters AudioManagerMac::GetInputStreamParameters(
441    const std::string& device_id) {
442  AudioDeviceID device = GetAudioDeviceIdByUId(true, device_id);
443  if (device == kAudioObjectUnknown) {
444    DLOG(ERROR) << "Invalid device " << device_id;
445    return AudioParameters(
446        AudioParameters::AUDIO_PCM_LOW_LATENCY, CHANNEL_LAYOUT_STEREO,
447        kFallbackSampleRate, 16, ChooseBufferSize(kFallbackSampleRate));
448  }
449
450  int channels = 0;
451  ChannelLayout channel_layout = CHANNEL_LAYOUT_STEREO;
452  if (GetDeviceChannels(device, kAudioDevicePropertyScopeInput, &channels) &&
453      channels <= 2) {
454    channel_layout = GuessChannelLayout(channels);
455  } else {
456    DLOG(ERROR) << "Failed to get the device channels, use stereo as default "
457                << "for device " << device_id;
458  }
459
460  int sample_rate = HardwareSampleRateForDevice(device);
461  if (!sample_rate)
462    sample_rate = kFallbackSampleRate;
463
464  // Due to the sharing of the input and output buffer sizes, we need to choose
465  // the input buffer size based on the output sample rate.  See
466  // http://crbug.com/154352.
467  const int buffer_size = ChooseBufferSize(sample_rate);
468
469  // TODO(xians): query the native channel layout for the specific device.
470  return AudioParameters(
471      AudioParameters::AUDIO_PCM_LOW_LATENCY, channel_layout,
472      sample_rate, 16, buffer_size);
473}
474
475std::string AudioManagerMac::GetAssociatedOutputDeviceID(
476    const std::string& input_device_id) {
477  AudioDeviceID device = GetAudioDeviceIdByUId(true, input_device_id);
478  if (device == kAudioObjectUnknown)
479    return std::string();
480
481  UInt32 size = 0;
482  AudioObjectPropertyAddress pa = {
483    kAudioDevicePropertyRelatedDevices,
484    kAudioDevicePropertyScopeOutput,
485    kAudioObjectPropertyElementMaster
486  };
487  OSStatus result = AudioObjectGetPropertyDataSize(device, &pa, 0, 0, &size);
488  if (result || !size)
489    return std::string();
490
491  int device_count = size / sizeof(AudioDeviceID);
492  scoped_ptr_malloc<AudioDeviceID>
493      devices(reinterpret_cast<AudioDeviceID*>(malloc(size)));
494  result = AudioObjectGetPropertyData(
495      device, &pa, 0, NULL, &size, devices.get());
496  if (result)
497    return std::string();
498
499  std::vector<std::string> associated_devices;
500  for (int i = 0; i < device_count; ++i) {
501    // Get the number of  output channels of the device.
502    pa.mSelector = kAudioDevicePropertyStreams;
503    size = 0;
504    result = AudioObjectGetPropertyDataSize(devices.get()[i],
505                                            &pa,
506                                            0,
507                                            NULL,
508                                            &size);
509    if (result || !size)
510      continue;  // Skip if there aren't any output channels.
511
512    // Get device UID.
513    CFStringRef uid = NULL;
514    size = sizeof(uid);
515    pa.mSelector = kAudioDevicePropertyDeviceUID;
516    result = AudioObjectGetPropertyData(devices.get()[i],
517                                        &pa,
518                                        0,
519                                        NULL,
520                                        &size,
521                                        &uid);
522    if (result || !uid)
523      continue;
524
525    std::string ret(base::SysCFStringRefToUTF8(uid));
526    CFRelease(uid);
527    associated_devices.push_back(ret);
528  }
529
530  // No matching device found.
531  if (associated_devices.empty())
532    return std::string();
533
534  // Return the device if there is only one associated device.
535  if (associated_devices.size() == 1)
536    return associated_devices[0];
537
538  // When there are multiple associated devices, we currently do not have a way
539  // to detect if a device (e.g. a digital output device) is actually connected
540  // to an endpoint, so we cannot randomly pick a device.
541  // We pick the device iff the associated device is the default output device.
542  const std::string default_device = GetDefaultOutputDeviceID();
543  for (std::vector<std::string>::const_iterator iter =
544           associated_devices.begin();
545       iter != associated_devices.end(); ++iter) {
546    if (default_device == *iter)
547      return *iter;
548  }
549
550  // Failed to figure out which is the matching device, return an emtpy string.
551  return std::string();
552}
553
554AudioOutputStream* AudioManagerMac::MakeLinearOutputStream(
555    const AudioParameters& params) {
556  return MakeLowLatencyOutputStream(params, std::string());
557}
558
559AudioOutputStream* AudioManagerMac::MakeLowLatencyOutputStream(
560    const AudioParameters& params,
561    const std::string& device_id) {
562  AudioDeviceID device = GetAudioDeviceIdByUId(false, device_id);
563  if (device == kAudioObjectUnknown) {
564    DLOG(ERROR) << "Failed to open output device: " << device_id;
565    return NULL;
566  }
567
568  // Lazily create the audio device listener on the first stream creation.
569  if (!output_device_listener_) {
570    output_device_listener_.reset(new AudioDeviceListenerMac(base::Bind(
571        &AudioManagerMac::HandleDeviceChanges, base::Unretained(this))));
572    // Only set the current output device for the default device.
573    if (device_id == AudioManagerBase::kDefaultDeviceId || device_id.empty())
574      current_output_device_ = device;
575    // Just use the current sample rate since we don't allow non-native sample
576    // rates on OSX.
577    current_sample_rate_ = params.sample_rate();
578  }
579
580  return new AUHALStream(this, params, device);
581}
582
583std::string AudioManagerMac::GetDefaultOutputDeviceID() {
584  AudioDeviceID device_id = kAudioObjectUnknown;
585  if (!GetDefaultOutputDevice(&device_id))
586    return std::string();
587
588  const AudioObjectPropertyAddress property_address = {
589    kAudioDevicePropertyDeviceUID,
590    kAudioObjectPropertyScopeGlobal,
591    kAudioObjectPropertyElementMaster
592  };
593  CFStringRef device_uid = NULL;
594  UInt32 size = sizeof(device_uid);
595  OSStatus status = AudioObjectGetPropertyData(device_id,
596                                               &property_address,
597                                               0,
598                                               NULL,
599                                               &size,
600                                               &device_uid);
601  if (status != kAudioHardwareNoError || !device_uid)
602    return std::string();
603
604  std::string ret(base::SysCFStringRefToUTF8(device_uid));
605  CFRelease(device_uid);
606
607  return ret;
608}
609
610AudioInputStream* AudioManagerMac::MakeLinearInputStream(
611    const AudioParameters& params, const std::string& device_id) {
612  DCHECK_EQ(AudioParameters::AUDIO_PCM_LINEAR, params.format());
613  return new PCMQueueInAudioInputStream(this, params);
614}
615
616AudioInputStream* AudioManagerMac::MakeLowLatencyInputStream(
617    const AudioParameters& params, const std::string& device_id) {
618  DCHECK_EQ(AudioParameters::AUDIO_PCM_LOW_LATENCY, params.format());
619  // Gets the AudioDeviceID that refers to the AudioInputDevice with the device
620  // unique id. This AudioDeviceID is used to set the device for Audio Unit.
621  AudioDeviceID audio_device_id = GetAudioDeviceIdByUId(true, device_id);
622  AudioInputStream* stream = NULL;
623  if (audio_device_id != kAudioObjectUnknown) {
624    // AUAudioInputStream needs to be fed the preferred audio output parameters
625    // of the matching device so that the buffer size of both input and output
626    // can be matched.  See constructor of AUAudioInputStream for more.
627    const std::string associated_output_device(
628        GetAssociatedOutputDeviceID(device_id));
629    const AudioParameters output_params =
630        GetPreferredOutputStreamParameters(
631            associated_output_device.empty() ?
632                AudioManagerBase::kDefaultDeviceId : associated_output_device,
633            params);
634    stream = new AUAudioInputStream(this, params, output_params,
635        audio_device_id);
636  }
637
638  return stream;
639}
640
641AudioParameters AudioManagerMac::GetPreferredOutputStreamParameters(
642    const std::string& output_device_id,
643    const AudioParameters& input_params) {
644  const AudioDeviceID device = GetAudioDeviceIdByUId(false, output_device_id);
645  if (device == kAudioObjectUnknown) {
646    DLOG(ERROR) << "Invalid output device " << output_device_id;
647    return input_params.IsValid() ? input_params : AudioParameters(
648        AudioParameters::AUDIO_PCM_LOW_LATENCY, CHANNEL_LAYOUT_STEREO,
649        kFallbackSampleRate, 16, ChooseBufferSize(kFallbackSampleRate));
650  }
651
652  const bool has_valid_input_params = input_params.IsValid();
653  const int hardware_sample_rate = HardwareSampleRateForDevice(device);
654  const int buffer_size = ChooseBufferSize(hardware_sample_rate);
655
656  int hardware_channels;
657  if (!GetDeviceChannels(device, kAudioDevicePropertyScopeOutput,
658                         &hardware_channels)) {
659    hardware_channels = 2;
660  }
661
662  // Use the input channel count and channel layout if possible.  Let OSX take
663  // care of remapping the channels; this lets user specified channel layouts
664  // work correctly.
665  int output_channels = input_params.channels();
666  ChannelLayout channel_layout = input_params.channel_layout();
667  if (!has_valid_input_params || output_channels > hardware_channels) {
668    output_channels = hardware_channels;
669    channel_layout = GuessChannelLayout(output_channels);
670    if (channel_layout == CHANNEL_LAYOUT_UNSUPPORTED)
671      channel_layout = CHANNEL_LAYOUT_DISCRETE;
672  }
673
674  const int input_channels =
675      has_valid_input_params ? input_params.input_channels() : 0;
676  if (input_channels > 0) {
677    // TODO(xians): given the limitations of the AudioOutputStream
678    // back-ends used with synchronized I/O, we hard-code to stereo.
679    // Specifically, this is a limitation of AudioSynchronizedStream which
680    // can be removed as part of the work to consolidate these back-ends.
681    channel_layout = CHANNEL_LAYOUT_STEREO;
682  }
683
684  return AudioParameters(
685      AudioParameters::AUDIO_PCM_LOW_LATENCY, channel_layout, output_channels,
686      input_channels, hardware_sample_rate, 16, buffer_size,
687      AudioParameters::NO_EFFECTS);
688}
689
690void AudioManagerMac::InitializeOnAudioThread() {
691  DCHECK(GetTaskRunner()->BelongsToCurrentThread());
692  power_observer_.reset(new AudioPowerObserver());
693}
694
695void AudioManagerMac::ShutdownOnAudioThread() {
696  DCHECK(GetTaskRunner()->BelongsToCurrentThread());
697  output_device_listener_.reset();
698  power_observer_.reset();
699}
700
701void AudioManagerMac::HandleDeviceChanges() {
702  if (!GetTaskRunner()->BelongsToCurrentThread()) {
703    GetTaskRunner()->PostTask(FROM_HERE, base::Bind(
704        &AudioManagerMac::HandleDeviceChanges, base::Unretained(this)));
705    return;
706  }
707
708  int new_sample_rate = HardwareSampleRate();
709  AudioDeviceID new_output_device;
710  GetDefaultOutputDevice(&new_output_device);
711
712  if (current_sample_rate_ == new_sample_rate &&
713      current_output_device_ == new_output_device)
714    return;
715
716  current_sample_rate_ = new_sample_rate;
717  current_output_device_ = new_output_device;
718  NotifyAllOutputDeviceChangeListeners();
719}
720
721int AudioManagerMac::ChooseBufferSize(int output_sample_rate) {
722  int buffer_size = kDefaultLowLatencyBufferSize;
723  const int user_buffer_size = GetUserBufferSize();
724  if (user_buffer_size) {
725    buffer_size = user_buffer_size;
726  } else if (output_sample_rate > 48000) {
727    // The default buffer size is too small for higher sample rates and may lead
728    // to glitching.  Adjust upwards by multiples of the default size.
729    if (output_sample_rate <= 96000)
730      buffer_size = 2 * kDefaultLowLatencyBufferSize;
731    else if (output_sample_rate <= 192000)
732      buffer_size = 4 * kDefaultLowLatencyBufferSize;
733  }
734
735  return buffer_size;
736}
737
738bool AudioManagerMac::ShouldDeferOutputStreamStart() {
739  DCHECK(GetTaskRunner()->BelongsToCurrentThread());
740  return power_observer_->ShouldDeferOutputStreamStart();
741}
742
743AudioManager* CreateAudioManager(AudioLogFactory* audio_log_factory) {
744  return new AudioManagerMac(audio_log_factory);
745}
746
747}  // namespace media
748