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