audio_manager_mac.cc revision 116680a4aac90f2aa7413d9095a592090648e557
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// Define bounds for for low-latency input and output streams.
33static const int kMinimumInputOutputBufferSize = 128;
34static const int kMaximumInputOutputBufferSize = 4096;
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<AudioDeviceID, base::FreeDeleter>
81      devices(static_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
214template <class T>
215void StopStreams(std::list<T*>* streams) {
216  for (typename std::list<T*>::iterator it = streams->begin();
217       it != streams->end();
218       ++it) {
219    // Stop() is safe to call multiple times, so it doesn't matter if a stream
220    // has already been stopped.
221    (*it)->Stop();
222  }
223  streams->clear();
224}
225
226class AudioManagerMac::AudioPowerObserver : public base::PowerObserver {
227 public:
228  AudioPowerObserver()
229      : is_suspending_(false),
230        is_monitoring_(base::PowerMonitor::Get()) {
231    // The PowerMonitor requires signifcant setup (a CFRunLoop and preallocated
232    // IO ports) so it's not available under unit tests.  See the OSX impl of
233    // base::PowerMonitorDeviceSource for more details.
234    if (!is_monitoring_)
235      return;
236    base::PowerMonitor::Get()->AddObserver(this);
237  }
238
239  virtual ~AudioPowerObserver() {
240    DCHECK(thread_checker_.CalledOnValidThread());
241    if (!is_monitoring_)
242      return;
243    base::PowerMonitor::Get()->RemoveObserver(this);
244  }
245
246  bool ShouldDeferStreamStart() {
247    DCHECK(thread_checker_.CalledOnValidThread());
248    // Start() should be deferred if the system is in the middle of a suspend or
249    // has recently started the process of resuming.
250    return is_suspending_ || base::TimeTicks::Now() < earliest_start_time_;
251  }
252
253 private:
254  virtual void OnSuspend() OVERRIDE {
255    DCHECK(thread_checker_.CalledOnValidThread());
256    is_suspending_ = true;
257  }
258
259  virtual void OnResume() OVERRIDE {
260    DCHECK(thread_checker_.CalledOnValidThread());
261    is_suspending_ = false;
262    earliest_start_time_ = base::TimeTicks::Now() +
263        base::TimeDelta::FromSeconds(kStartDelayInSecsForPowerEvents);
264  }
265
266  bool is_suspending_;
267  const bool is_monitoring_;
268  base::TimeTicks earliest_start_time_;
269  base::ThreadChecker thread_checker_;
270
271  DISALLOW_COPY_AND_ASSIGN(AudioPowerObserver);
272};
273
274AudioManagerMac::AudioManagerMac(AudioLogFactory* audio_log_factory)
275    : AudioManagerBase(audio_log_factory),
276      current_sample_rate_(0),
277      current_output_device_(kAudioDeviceUnknown) {
278  SetMaxOutputStreamsAllowed(kMaxOutputStreams);
279
280  // Task must be posted last to avoid races from handing out "this" to the
281  // audio thread.  Always PostTask even if we're on the right thread since
282  // AudioManager creation is on the startup path and this may be slow.
283  GetTaskRunner()->PostTask(FROM_HERE, base::Bind(
284      &AudioManagerMac::InitializeOnAudioThread, base::Unretained(this)));
285}
286
287AudioManagerMac::~AudioManagerMac() {
288  if (GetTaskRunner()->BelongsToCurrentThread()) {
289    ShutdownOnAudioThread();
290  } else {
291    // It's safe to post a task here since Shutdown() will wait for all tasks to
292    // complete before returning.
293    GetTaskRunner()->PostTask(FROM_HERE, base::Bind(
294        &AudioManagerMac::ShutdownOnAudioThread, base::Unretained(this)));
295  }
296
297  Shutdown();
298}
299
300bool AudioManagerMac::HasAudioOutputDevices() {
301  return HasAudioHardware(kAudioHardwarePropertyDefaultOutputDevice);
302}
303
304bool AudioManagerMac::HasAudioInputDevices() {
305  return HasAudioHardware(kAudioHardwarePropertyDefaultInputDevice);
306}
307
308// TODO(xians): There are several places on the OSX specific code which
309// could benefit from these helper functions.
310bool AudioManagerMac::GetDefaultInputDevice(AudioDeviceID* device) {
311  return GetDefaultDevice(device, true);
312}
313
314bool AudioManagerMac::GetDefaultOutputDevice(AudioDeviceID* device) {
315  return GetDefaultDevice(device, false);
316}
317
318bool AudioManagerMac::GetDefaultDevice(AudioDeviceID* device, bool input) {
319  CHECK(device);
320
321  // Obtain the current output device selected by the user.
322  AudioObjectPropertyAddress pa;
323  pa.mSelector = input ? kAudioHardwarePropertyDefaultInputDevice :
324      kAudioHardwarePropertyDefaultOutputDevice;
325  pa.mScope = kAudioObjectPropertyScopeGlobal;
326  pa.mElement = kAudioObjectPropertyElementMaster;
327
328  UInt32 size = sizeof(*device);
329  OSStatus result = AudioObjectGetPropertyData(kAudioObjectSystemObject,
330                                               &pa,
331                                               0,
332                                               0,
333                                               &size,
334                                               device);
335
336  if ((result != kAudioHardwareNoError) || (*device == kAudioDeviceUnknown)) {
337    DLOG(ERROR) << "Error getting default AudioDevice.";
338    return false;
339  }
340
341  return true;
342}
343
344bool AudioManagerMac::GetDefaultOutputChannels(int* channels) {
345  AudioDeviceID device;
346  if (!GetDefaultOutputDevice(&device))
347    return false;
348  return GetDeviceChannels(device, kAudioDevicePropertyScopeOutput, channels);
349}
350
351bool AudioManagerMac::GetDeviceChannels(AudioDeviceID device,
352                                        AudioObjectPropertyScope scope,
353                                        int* channels) {
354  CHECK(channels);
355
356  // Get stream configuration.
357  AudioObjectPropertyAddress pa;
358  pa.mSelector = kAudioDevicePropertyStreamConfiguration;
359  pa.mScope = scope;
360  pa.mElement = kAudioObjectPropertyElementMaster;
361
362  UInt32 size;
363  OSStatus result = AudioObjectGetPropertyDataSize(device, &pa, 0, 0, &size);
364  if (result != noErr || !size)
365    return false;
366
367  // Allocate storage.
368  scoped_ptr<uint8[]> list_storage(new uint8[size]);
369  AudioBufferList& buffer_list =
370      *reinterpret_cast<AudioBufferList*>(list_storage.get());
371
372  result = AudioObjectGetPropertyData(device, &pa, 0, 0, &size, &buffer_list);
373  if (result != noErr)
374    return false;
375
376  // Determine number of input channels.
377  int channels_per_frame = buffer_list.mNumberBuffers > 0 ?
378      buffer_list.mBuffers[0].mNumberChannels : 0;
379  if (channels_per_frame == 1 && buffer_list.mNumberBuffers > 1) {
380    // Non-interleaved.
381    *channels = buffer_list.mNumberBuffers;
382  } else {
383    // Interleaved.
384    *channels = channels_per_frame;
385  }
386
387  return true;
388}
389
390int AudioManagerMac::HardwareSampleRateForDevice(AudioDeviceID device_id) {
391  Float64 nominal_sample_rate;
392  UInt32 info_size = sizeof(nominal_sample_rate);
393
394  static const AudioObjectPropertyAddress kNominalSampleRateAddress = {
395      kAudioDevicePropertyNominalSampleRate,
396      kAudioObjectPropertyScopeGlobal,
397      kAudioObjectPropertyElementMaster
398  };
399  OSStatus result = AudioObjectGetPropertyData(device_id,
400                                               &kNominalSampleRateAddress,
401                                               0,
402                                               0,
403                                               &info_size,
404                                               &nominal_sample_rate);
405  if (result != noErr) {
406    OSSTATUS_DLOG(WARNING, result)
407        << "Could not get default sample rate for device: " << device_id;
408    return 0;
409  }
410
411  return static_cast<int>(nominal_sample_rate);
412}
413
414int AudioManagerMac::HardwareSampleRate() {
415  // Determine the default output device's sample-rate.
416  AudioDeviceID device_id = kAudioObjectUnknown;
417  if (!GetDefaultOutputDevice(&device_id))
418    return kFallbackSampleRate;
419
420  return HardwareSampleRateForDevice(device_id);
421}
422
423void AudioManagerMac::GetAudioInputDeviceNames(
424    media::AudioDeviceNames* device_names) {
425  DCHECK(device_names->empty());
426  GetAudioDeviceInfo(true, device_names);
427}
428
429void AudioManagerMac::GetAudioOutputDeviceNames(
430    media::AudioDeviceNames* device_names) {
431  DCHECK(device_names->empty());
432  GetAudioDeviceInfo(false, device_names);
433}
434
435AudioParameters AudioManagerMac::GetInputStreamParameters(
436    const std::string& device_id) {
437  AudioDeviceID device = GetAudioDeviceIdByUId(true, device_id);
438  if (device == kAudioObjectUnknown) {
439    DLOG(ERROR) << "Invalid device " << device_id;
440    return AudioParameters(
441        AudioParameters::AUDIO_PCM_LOW_LATENCY, CHANNEL_LAYOUT_STEREO,
442        kFallbackSampleRate, 16, ChooseBufferSize(kFallbackSampleRate));
443  }
444
445  int channels = 0;
446  ChannelLayout channel_layout = CHANNEL_LAYOUT_STEREO;
447  if (GetDeviceChannels(device, kAudioDevicePropertyScopeInput, &channels) &&
448      channels <= 2) {
449    channel_layout = GuessChannelLayout(channels);
450  } else {
451    DLOG(ERROR) << "Failed to get the device channels, use stereo as default "
452                << "for device " << device_id;
453  }
454
455  int sample_rate = HardwareSampleRateForDevice(device);
456  if (!sample_rate)
457    sample_rate = kFallbackSampleRate;
458
459  // Due to the sharing of the input and output buffer sizes, we need to choose
460  // the input buffer size based on the output sample rate.  See
461  // http://crbug.com/154352.
462  const int buffer_size = ChooseBufferSize(sample_rate);
463
464  // TODO(xians): query the native channel layout for the specific device.
465  return AudioParameters(
466      AudioParameters::AUDIO_PCM_LOW_LATENCY, channel_layout,
467      sample_rate, 16, buffer_size);
468}
469
470std::string AudioManagerMac::GetAssociatedOutputDeviceID(
471    const std::string& input_device_id) {
472  AudioDeviceID device = GetAudioDeviceIdByUId(true, input_device_id);
473  if (device == kAudioObjectUnknown)
474    return std::string();
475
476  UInt32 size = 0;
477  AudioObjectPropertyAddress pa = {
478    kAudioDevicePropertyRelatedDevices,
479    kAudioDevicePropertyScopeOutput,
480    kAudioObjectPropertyElementMaster
481  };
482  OSStatus result = AudioObjectGetPropertyDataSize(device, &pa, 0, 0, &size);
483  if (result || !size)
484    return std::string();
485
486  int device_count = size / sizeof(AudioDeviceID);
487  scoped_ptr<AudioDeviceID, base::FreeDeleter>
488      devices(static_cast<AudioDeviceID*>(malloc(size)));
489  result = AudioObjectGetPropertyData(
490      device, &pa, 0, NULL, &size, devices.get());
491  if (result)
492    return std::string();
493
494  std::vector<std::string> associated_devices;
495  for (int i = 0; i < device_count; ++i) {
496    // Get the number of  output channels of the device.
497    pa.mSelector = kAudioDevicePropertyStreams;
498    size = 0;
499    result = AudioObjectGetPropertyDataSize(devices.get()[i],
500                                            &pa,
501                                            0,
502                                            NULL,
503                                            &size);
504    if (result || !size)
505      continue;  // Skip if there aren't any output channels.
506
507    // Get device UID.
508    CFStringRef uid = NULL;
509    size = sizeof(uid);
510    pa.mSelector = kAudioDevicePropertyDeviceUID;
511    result = AudioObjectGetPropertyData(devices.get()[i],
512                                        &pa,
513                                        0,
514                                        NULL,
515                                        &size,
516                                        &uid);
517    if (result || !uid)
518      continue;
519
520    std::string ret(base::SysCFStringRefToUTF8(uid));
521    CFRelease(uid);
522    associated_devices.push_back(ret);
523  }
524
525  // No matching device found.
526  if (associated_devices.empty())
527    return std::string();
528
529  // Return the device if there is only one associated device.
530  if (associated_devices.size() == 1)
531    return associated_devices[0];
532
533  // When there are multiple associated devices, we currently do not have a way
534  // to detect if a device (e.g. a digital output device) is actually connected
535  // to an endpoint, so we cannot randomly pick a device.
536  // We pick the device iff the associated device is the default output device.
537  const std::string default_device = GetDefaultOutputDeviceID();
538  for (std::vector<std::string>::const_iterator iter =
539           associated_devices.begin();
540       iter != associated_devices.end(); ++iter) {
541    if (default_device == *iter)
542      return *iter;
543  }
544
545  // Failed to figure out which is the matching device, return an emtpy string.
546  return std::string();
547}
548
549AudioOutputStream* AudioManagerMac::MakeLinearOutputStream(
550    const AudioParameters& params) {
551  return MakeLowLatencyOutputStream(params, std::string());
552}
553
554AudioOutputStream* AudioManagerMac::MakeLowLatencyOutputStream(
555    const AudioParameters& params,
556    const std::string& device_id) {
557  AudioDeviceID device = GetAudioDeviceIdByUId(false, device_id);
558  if (device == kAudioObjectUnknown) {
559    DLOG(ERROR) << "Failed to open output device: " << device_id;
560    return NULL;
561  }
562
563  // Lazily create the audio device listener on the first stream creation.
564  if (!output_device_listener_) {
565    // NOTE: Use BindToCurrentLoop() to ensure the callback is always PostTask'd
566    // even if OSX calls us on the right thread.  Some CoreAudio drivers will
567    // fire the callbacks during stream creation, leading to re-entrancy issues
568    // otherwise.  See http://crbug.com/349604
569    output_device_listener_.reset(
570        new AudioDeviceListenerMac(BindToCurrentLoop(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  AudioOutputStream* stream = new AUHALStream(this, params, device);
581  output_streams_.push_back(stream);
582  return stream;
583}
584
585std::string AudioManagerMac::GetDefaultOutputDeviceID() {
586  AudioDeviceID device_id = kAudioObjectUnknown;
587  if (!GetDefaultOutputDevice(&device_id))
588    return std::string();
589
590  const AudioObjectPropertyAddress property_address = {
591    kAudioDevicePropertyDeviceUID,
592    kAudioObjectPropertyScopeGlobal,
593    kAudioObjectPropertyElementMaster
594  };
595  CFStringRef device_uid = NULL;
596  UInt32 size = sizeof(device_uid);
597  OSStatus status = AudioObjectGetPropertyData(device_id,
598                                               &property_address,
599                                               0,
600                                               NULL,
601                                               &size,
602                                               &device_uid);
603  if (status != kAudioHardwareNoError || !device_uid)
604    return std::string();
605
606  std::string ret(base::SysCFStringRefToUTF8(device_uid));
607  CFRelease(device_uid);
608
609  return ret;
610}
611
612AudioInputStream* AudioManagerMac::MakeLinearInputStream(
613    const AudioParameters& params, const std::string& device_id) {
614  DCHECK_EQ(AudioParameters::AUDIO_PCM_LINEAR, params.format());
615  AudioInputStream* stream = new PCMQueueInAudioInputStream(this, params);
616  input_streams_.push_back(stream);
617  return stream;
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    stream = new AUAudioInputStream(this, params, audio_device_id);
629    input_streams_.push_back(stream);
630  }
631
632  return stream;
633}
634
635AudioParameters AudioManagerMac::GetPreferredOutputStreamParameters(
636    const std::string& output_device_id,
637    const AudioParameters& input_params) {
638  const AudioDeviceID device = GetAudioDeviceIdByUId(false, output_device_id);
639  if (device == kAudioObjectUnknown) {
640    DLOG(ERROR) << "Invalid output device " << output_device_id;
641    return input_params.IsValid() ? input_params : AudioParameters(
642        AudioParameters::AUDIO_PCM_LOW_LATENCY, CHANNEL_LAYOUT_STEREO,
643        kFallbackSampleRate, 16, ChooseBufferSize(kFallbackSampleRate));
644  }
645
646  const bool has_valid_input_params = input_params.IsValid();
647  const int hardware_sample_rate = HardwareSampleRateForDevice(device);
648
649  // Allow pass through buffer sizes.  If concurrent input and output streams
650  // exist, they will use the smallest buffer size amongst them.  As such, each
651  // stream must be able to FIFO requests appropriately when this happens.
652  int buffer_size = ChooseBufferSize(hardware_sample_rate);
653  if (has_valid_input_params) {
654    buffer_size =
655        std::min(kMaximumInputOutputBufferSize,
656                 std::max(input_params.frames_per_buffer(), buffer_size));
657  }
658
659  int hardware_channels;
660  if (!GetDeviceChannels(device, kAudioDevicePropertyScopeOutput,
661                         &hardware_channels)) {
662    hardware_channels = 2;
663  }
664
665  // Use the input channel count and channel layout if possible.  Let OSX take
666  // care of remapping the channels; this lets user specified channel layouts
667  // work correctly.
668  int output_channels = input_params.channels();
669  ChannelLayout channel_layout = input_params.channel_layout();
670  if (!has_valid_input_params || output_channels > hardware_channels) {
671    output_channels = hardware_channels;
672    channel_layout = GuessChannelLayout(output_channels);
673    if (channel_layout == CHANNEL_LAYOUT_UNSUPPORTED)
674      channel_layout = CHANNEL_LAYOUT_DISCRETE;
675  }
676
677  const int input_channels =
678      has_valid_input_params ? input_params.input_channels() : 0;
679  if (input_channels > 0) {
680    // TODO(xians): given the limitations of the AudioOutputStream
681    // back-ends used with synchronized I/O, we hard-code to stereo.
682    // Specifically, this is a limitation of AudioSynchronizedStream which
683    // can be removed as part of the work to consolidate these back-ends.
684    channel_layout = CHANNEL_LAYOUT_STEREO;
685  }
686
687  return AudioParameters(
688      AudioParameters::AUDIO_PCM_LOW_LATENCY, channel_layout, output_channels,
689      input_channels, hardware_sample_rate, 16, buffer_size,
690      AudioParameters::NO_EFFECTS);
691}
692
693void AudioManagerMac::InitializeOnAudioThread() {
694  DCHECK(GetTaskRunner()->BelongsToCurrentThread());
695  power_observer_.reset(new AudioPowerObserver());
696}
697
698void AudioManagerMac::ShutdownOnAudioThread() {
699  DCHECK(GetTaskRunner()->BelongsToCurrentThread());
700  output_device_listener_.reset();
701  power_observer_.reset();
702
703  // Since CoreAudio calls have to run on the UI thread and browser shutdown
704  // doesn't wait for outstanding tasks to complete, we may have input/output
705  // streams still running at shutdown.
706  //
707  // To avoid calls into destructed classes, we need to stop the OS callbacks
708  // by stopping the streams.  Note: The streams are leaked since process
709  // destruction is imminent.
710  //
711  // See http://crbug.com/354139 for crash details.
712  StopStreams(&input_streams_);
713  StopStreams(&output_streams_);
714}
715
716void AudioManagerMac::HandleDeviceChanges() {
717  DCHECK(GetTaskRunner()->BelongsToCurrentThread());
718  const int new_sample_rate = HardwareSampleRate();
719  AudioDeviceID new_output_device;
720  GetDefaultOutputDevice(&new_output_device);
721
722  if (current_sample_rate_ == new_sample_rate &&
723      current_output_device_ == new_output_device)
724    return;
725
726  current_sample_rate_ = new_sample_rate;
727  current_output_device_ = new_output_device;
728  NotifyAllOutputDeviceChangeListeners();
729}
730
731int AudioManagerMac::ChooseBufferSize(int output_sample_rate) {
732  int buffer_size = kMinimumInputOutputBufferSize;
733  const int user_buffer_size = GetUserBufferSize();
734  if (user_buffer_size) {
735    buffer_size = user_buffer_size;
736  } else if (output_sample_rate > 48000) {
737    // The default buffer size is too small for higher sample rates and may lead
738    // to glitching.  Adjust upwards by multiples of the default size.
739    if (output_sample_rate <= 96000)
740      buffer_size = 2 * kMinimumInputOutputBufferSize;
741    else if (output_sample_rate <= 192000)
742      buffer_size = 4 * kMinimumInputOutputBufferSize;
743  }
744
745  return buffer_size;
746}
747
748bool AudioManagerMac::ShouldDeferStreamStart() {
749  DCHECK(GetTaskRunner()->BelongsToCurrentThread());
750  return power_observer_->ShouldDeferStreamStart();
751}
752
753void AudioManagerMac::ReleaseOutputStream(AudioOutputStream* stream) {
754  output_streams_.remove(stream);
755  AudioManagerBase::ReleaseOutputStream(stream);
756}
757
758void AudioManagerMac::ReleaseInputStream(AudioInputStream* stream) {
759  input_streams_.remove(stream);
760  AudioManagerBase::ReleaseInputStream(stream);
761}
762
763AudioManager* CreateAudioManager(AudioLogFactory* audio_log_factory) {
764  return new AudioManagerMac(audio_log_factory);
765}
766
767}  // namespace media
768