audio_manager_mac.cc revision 0529e5d033099cbfc42635f6f6183833b09dff6e
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(
311    AudioDeviceID* device) {
312  return GetDefaultDevice(device, true);
313}
314
315bool AudioManagerMac::GetDefaultOutputDevice(
316    AudioDeviceID* device) {
317  return GetDefaultDevice(device, false);
318}
319
320bool AudioManagerMac::GetDefaultDevice(
321    AudioDeviceID* device, bool input) {
322  CHECK(device);
323
324  // Obtain the current output device selected by the user.
325  AudioObjectPropertyAddress pa;
326  pa.mSelector = input ? kAudioHardwarePropertyDefaultInputDevice :
327      kAudioHardwarePropertyDefaultOutputDevice;
328  pa.mScope = kAudioObjectPropertyScopeGlobal;
329  pa.mElement = kAudioObjectPropertyElementMaster;
330
331  UInt32 size = sizeof(*device);
332
333  OSStatus result = AudioObjectGetPropertyData(
334      kAudioObjectSystemObject,
335      &pa,
336      0,
337      0,
338      &size,
339      device);
340
341  if ((result != kAudioHardwareNoError) || (*device == kAudioDeviceUnknown)) {
342    DLOG(ERROR) << "Error getting default AudioDevice.";
343    return false;
344  }
345
346  return true;
347}
348
349bool AudioManagerMac::GetDefaultOutputChannels(
350    int* channels) {
351  AudioDeviceID device;
352  if (!GetDefaultOutputDevice(&device))
353    return false;
354
355  return GetDeviceChannels(device,
356                           kAudioDevicePropertyScopeOutput,
357                           channels);
358}
359
360bool AudioManagerMac::GetDeviceChannels(
361    AudioDeviceID device,
362    AudioObjectPropertyScope scope,
363    int* channels) {
364  CHECK(channels);
365
366  // Get stream configuration.
367  AudioObjectPropertyAddress pa;
368  pa.mSelector = kAudioDevicePropertyStreamConfiguration;
369  pa.mScope = scope;
370  pa.mElement = kAudioObjectPropertyElementMaster;
371
372  UInt32 size;
373  OSStatus result = AudioObjectGetPropertyDataSize(device, &pa, 0, 0, &size);
374  if (result != noErr || !size)
375    return false;
376
377  // Allocate storage.
378  scoped_ptr<uint8[]> list_storage(new uint8[size]);
379  AudioBufferList& buffer_list =
380      *reinterpret_cast<AudioBufferList*>(list_storage.get());
381
382  result = AudioObjectGetPropertyData(
383      device,
384      &pa,
385      0,
386      0,
387      &size,
388      &buffer_list);
389  if (result != noErr)
390    return false;
391
392  // Determine number of input channels.
393  int channels_per_frame = buffer_list.mNumberBuffers > 0 ?
394      buffer_list.mBuffers[0].mNumberChannels : 0;
395  if (channels_per_frame == 1 && buffer_list.mNumberBuffers > 1) {
396    // Non-interleaved.
397    *channels = buffer_list.mNumberBuffers;
398  } else {
399    // Interleaved.
400    *channels = channels_per_frame;
401  }
402
403  return true;
404}
405
406int AudioManagerMac::HardwareSampleRateForDevice(AudioDeviceID device_id) {
407  Float64 nominal_sample_rate;
408  UInt32 info_size = sizeof(nominal_sample_rate);
409
410  static const AudioObjectPropertyAddress kNominalSampleRateAddress = {
411      kAudioDevicePropertyNominalSampleRate,
412      kAudioObjectPropertyScopeGlobal,
413      kAudioObjectPropertyElementMaster
414  };
415  OSStatus result = AudioObjectGetPropertyData(
416      device_id,
417      &kNominalSampleRateAddress,
418      0,
419      0,
420      &info_size,
421      &nominal_sample_rate);
422  if (result != noErr) {
423    OSSTATUS_DLOG(WARNING, result)
424        << "Could not get default sample rate for device: " << device_id;
425    return 0;
426  }
427
428  return static_cast<int>(nominal_sample_rate);
429}
430
431int AudioManagerMac::HardwareSampleRate() {
432  // Determine the default output device's sample-rate.
433  AudioDeviceID device_id = kAudioObjectUnknown;
434  if (!GetDefaultOutputDevice(&device_id))
435    return kFallbackSampleRate;
436
437  return HardwareSampleRateForDevice(device_id);
438}
439
440void AudioManagerMac::GetAudioInputDeviceNames(
441    media::AudioDeviceNames* device_names) {
442  DCHECK(device_names->empty());
443  GetAudioDeviceInfo(true, device_names);
444}
445
446void AudioManagerMac::GetAudioOutputDeviceNames(
447    media::AudioDeviceNames* device_names) {
448  DCHECK(device_names->empty());
449  GetAudioDeviceInfo(false, device_names);
450}
451
452AudioParameters AudioManagerMac::GetInputStreamParameters(
453    const std::string& device_id) {
454  AudioDeviceID device = GetAudioDeviceIdByUId(true, device_id);
455  if (device == kAudioObjectUnknown) {
456    DLOG(ERROR) << "Invalid device " << device_id;
457    return AudioParameters(
458        AudioParameters::AUDIO_PCM_LOW_LATENCY, CHANNEL_LAYOUT_STEREO,
459        kFallbackSampleRate, 16, ChooseBufferSize(kFallbackSampleRate));
460  }
461
462  int channels = 0;
463  ChannelLayout channel_layout = CHANNEL_LAYOUT_STEREO;
464  if (GetDeviceChannels(device, kAudioDevicePropertyScopeInput, &channels) &&
465      channels <= 2) {
466    channel_layout = GuessChannelLayout(channels);
467  } else {
468    DLOG(ERROR) << "Failed to get the device channels, use stereo as default "
469                << "for device " << device_id;
470  }
471
472  int sample_rate = HardwareSampleRateForDevice(device);
473  if (!sample_rate)
474    sample_rate = kFallbackSampleRate;
475
476  // Due to the sharing of the input and output buffer sizes, we need to choose
477  // the input buffer size based on the output sample rate.  See
478  // http://crbug.com/154352.
479  const int buffer_size = ChooseBufferSize(sample_rate);
480
481  // TODO(xians): query the native channel layout for the specific device.
482  return AudioParameters(
483      AudioParameters::AUDIO_PCM_LOW_LATENCY, channel_layout,
484      sample_rate, 16, buffer_size);
485}
486
487std::string AudioManagerMac::GetAssociatedOutputDeviceID(
488    const std::string& input_device_id) {
489  AudioDeviceID device = GetAudioDeviceIdByUId(true, input_device_id);
490  if (device == kAudioObjectUnknown)
491    return std::string();
492
493  UInt32 size = 0;
494  AudioObjectPropertyAddress pa = {
495    kAudioDevicePropertyRelatedDevices,
496    kAudioDevicePropertyScopeOutput,
497    kAudioObjectPropertyElementMaster
498  };
499  OSStatus result = AudioObjectGetPropertyDataSize(device, &pa, 0, 0, &size);
500  if (result || !size)
501    return std::string();
502
503  int device_count = size / sizeof(AudioDeviceID);
504  scoped_ptr<AudioDeviceID, base::FreeDeleter>
505      devices(static_cast<AudioDeviceID*>(malloc(size)));
506  result = AudioObjectGetPropertyData(
507      device, &pa, 0, NULL, &size, devices.get());
508  if (result)
509    return std::string();
510
511  std::vector<std::string> associated_devices;
512  for (int i = 0; i < device_count; ++i) {
513    // Get the number of  output channels of the device.
514    pa.mSelector = kAudioDevicePropertyStreams;
515    size = 0;
516    result = AudioObjectGetPropertyDataSize(devices.get()[i],
517                                            &pa,
518                                            0,
519                                            NULL,
520                                            &size);
521    if (result || !size)
522      continue;  // Skip if there aren't any output channels.
523
524    // Get device UID.
525    CFStringRef uid = NULL;
526    size = sizeof(uid);
527    pa.mSelector = kAudioDevicePropertyDeviceUID;
528    result = AudioObjectGetPropertyData(devices.get()[i],
529                                        &pa,
530                                        0,
531                                        NULL,
532                                        &size,
533                                        &uid);
534    if (result || !uid)
535      continue;
536
537    std::string ret(base::SysCFStringRefToUTF8(uid));
538    CFRelease(uid);
539    associated_devices.push_back(ret);
540  }
541
542  // No matching device found.
543  if (associated_devices.empty())
544    return std::string();
545
546  // Return the device if there is only one associated device.
547  if (associated_devices.size() == 1)
548    return associated_devices[0];
549
550  // When there are multiple associated devices, we currently do not have a way
551  // to detect if a device (e.g. a digital output device) is actually connected
552  // to an endpoint, so we cannot randomly pick a device.
553  // We pick the device iff the associated device is the default output device.
554  const std::string default_device = GetDefaultOutputDeviceID();
555  for (std::vector<std::string>::const_iterator iter =
556           associated_devices.begin();
557       iter != associated_devices.end(); ++iter) {
558    if (default_device == *iter)
559      return *iter;
560  }
561
562  // Failed to figure out which is the matching device, return an emtpy string.
563  return std::string();
564}
565
566AudioOutputStream* AudioManagerMac::MakeLinearOutputStream(
567    const AudioParameters& params) {
568  return MakeLowLatencyOutputStream(params, std::string());
569}
570
571AudioOutputStream* AudioManagerMac::MakeLowLatencyOutputStream(
572    const AudioParameters& params,
573    const std::string& device_id) {
574  AudioDeviceID device = GetAudioDeviceIdByUId(false, device_id);
575  if (device == kAudioObjectUnknown) {
576    DLOG(ERROR) << "Failed to open output device: " << device_id;
577    return NULL;
578  }
579
580  // Lazily create the audio device listener on the first stream creation.
581  if (!output_device_listener_) {
582    // NOTE: Use BindToCurrentLoop() to ensure the callback is always PostTask'd
583    // even if OSX calls us on the right thread.  Some CoreAudio drivers will
584    // fire the callbacks during stream creation, leading to re-entrancy issues
585    // otherwise.  See http://crbug.com/349604
586    output_device_listener_.reset(
587        new AudioDeviceListenerMac(BindToCurrentLoop(base::Bind(
588            &AudioManagerMac::HandleDeviceChanges, base::Unretained(this)))));
589    // Only set the current output device for the default device.
590    if (device_id == AudioManagerBase::kDefaultDeviceId || device_id.empty())
591      current_output_device_ = device;
592    // Just use the current sample rate since we don't allow non-native sample
593    // rates on OSX.
594    current_sample_rate_ = params.sample_rate();
595  }
596
597  AudioOutputStream* stream = new AUHALStream(this, params, device);
598  output_streams_.push_back(stream);
599  return stream;
600}
601
602std::string AudioManagerMac::GetDefaultOutputDeviceID() {
603  AudioDeviceID device_id = kAudioObjectUnknown;
604  if (!GetDefaultOutputDevice(&device_id))
605    return std::string();
606
607  const AudioObjectPropertyAddress property_address = {
608    kAudioDevicePropertyDeviceUID,
609    kAudioObjectPropertyScopeGlobal,
610    kAudioObjectPropertyElementMaster
611  };
612  CFStringRef device_uid = NULL;
613  UInt32 size = sizeof(device_uid);
614  OSStatus status = AudioObjectGetPropertyData(device_id,
615                                               &property_address,
616                                               0,
617                                               NULL,
618                                               &size,
619                                               &device_uid);
620  if (status != kAudioHardwareNoError || !device_uid)
621    return std::string();
622
623  std::string ret(base::SysCFStringRefToUTF8(device_uid));
624  CFRelease(device_uid);
625
626  return ret;
627}
628
629AudioInputStream* AudioManagerMac::MakeLinearInputStream(
630    const AudioParameters& params, const std::string& device_id) {
631  DCHECK_EQ(AudioParameters::AUDIO_PCM_LINEAR, params.format());
632  AudioInputStream* stream = new PCMQueueInAudioInputStream(this, params);
633  input_streams_.push_back(stream);
634  return stream;
635}
636
637AudioInputStream* AudioManagerMac::MakeLowLatencyInputStream(
638    const AudioParameters& params, const std::string& device_id) {
639  DCHECK_EQ(AudioParameters::AUDIO_PCM_LOW_LATENCY, params.format());
640  // Gets the AudioDeviceID that refers to the AudioInputDevice with the device
641  // unique id. This AudioDeviceID is used to set the device for Audio Unit.
642  AudioDeviceID audio_device_id = GetAudioDeviceIdByUId(true, device_id);
643  AudioInputStream* stream = NULL;
644  if (audio_device_id != kAudioObjectUnknown) {
645    // AUAudioInputStream needs to be fed the preferred audio output parameters
646    // of the matching device so that the buffer size of both input and output
647    // can be matched.  See constructor of AUAudioInputStream for more.
648    const std::string associated_output_device(
649        GetAssociatedOutputDeviceID(device_id));
650    const AudioParameters output_params =
651        GetPreferredOutputStreamParameters(
652            associated_output_device.empty() ?
653                AudioManagerBase::kDefaultDeviceId : associated_output_device,
654            params);
655    stream = new AUAudioInputStream(this, params, output_params,
656        audio_device_id);
657    input_streams_.push_back(stream);
658  }
659
660  return stream;
661}
662
663AudioParameters AudioManagerMac::GetPreferredOutputStreamParameters(
664    const std::string& output_device_id,
665    const AudioParameters& input_params) {
666  const AudioDeviceID device = GetAudioDeviceIdByUId(false, output_device_id);
667  if (device == kAudioObjectUnknown) {
668    DLOG(ERROR) << "Invalid output device " << output_device_id;
669    return input_params.IsValid() ? input_params : AudioParameters(
670        AudioParameters::AUDIO_PCM_LOW_LATENCY, CHANNEL_LAYOUT_STEREO,
671        kFallbackSampleRate, 16, ChooseBufferSize(kFallbackSampleRate));
672  }
673
674  const bool has_valid_input_params = input_params.IsValid();
675  const int hardware_sample_rate = HardwareSampleRateForDevice(device);
676
677  // Allow pass through buffer sizes.  If concurrent input and output streams
678  // exist, they will use the smallest buffer size amongst them.  As such, each
679  // stream must be able to FIFO requests appropriately when this happens.
680  int buffer_size = ChooseBufferSize(hardware_sample_rate);
681  if (has_valid_input_params) {
682    buffer_size =
683        std::min(kMaximumInputOutputBufferSize,
684                 std::max(input_params.frames_per_buffer(), buffer_size));
685  }
686
687  int hardware_channels;
688  if (!GetDeviceChannels(device, kAudioDevicePropertyScopeOutput,
689                         &hardware_channels)) {
690    hardware_channels = 2;
691  }
692
693  // Use the input channel count and channel layout if possible.  Let OSX take
694  // care of remapping the channels; this lets user specified channel layouts
695  // work correctly.
696  int output_channels = input_params.channels();
697  ChannelLayout channel_layout = input_params.channel_layout();
698  if (!has_valid_input_params || output_channels > hardware_channels) {
699    output_channels = hardware_channels;
700    channel_layout = GuessChannelLayout(output_channels);
701    if (channel_layout == CHANNEL_LAYOUT_UNSUPPORTED)
702      channel_layout = CHANNEL_LAYOUT_DISCRETE;
703  }
704
705  const int input_channels =
706      has_valid_input_params ? input_params.input_channels() : 0;
707  if (input_channels > 0) {
708    // TODO(xians): given the limitations of the AudioOutputStream
709    // back-ends used with synchronized I/O, we hard-code to stereo.
710    // Specifically, this is a limitation of AudioSynchronizedStream which
711    // can be removed as part of the work to consolidate these back-ends.
712    channel_layout = CHANNEL_LAYOUT_STEREO;
713  }
714
715  return AudioParameters(
716      AudioParameters::AUDIO_PCM_LOW_LATENCY, channel_layout, output_channels,
717      input_channels, hardware_sample_rate, 16, buffer_size,
718      AudioParameters::NO_EFFECTS);
719}
720
721void AudioManagerMac::InitializeOnAudioThread() {
722  DCHECK(GetTaskRunner()->BelongsToCurrentThread());
723  power_observer_.reset(new AudioPowerObserver());
724}
725
726void AudioManagerMac::ShutdownOnAudioThread() {
727  DCHECK(GetTaskRunner()->BelongsToCurrentThread());
728  output_device_listener_.reset();
729  power_observer_.reset();
730
731  // Since CoreAudio calls have to run on the UI thread and browser shutdown
732  // doesn't wait for outstanding tasks to complete, we may have input/output
733  // streams still running at shutdown.
734  //
735  // To avoid calls into destructed classes, we need to stop the OS callbacks
736  // by stopping the streams.  Note: The streams are leaked since process
737  // destruction is imminent.
738  //
739  // See http://crbug.com/354139 for crash details.
740  StopStreams(&input_streams_);
741  StopStreams(&output_streams_);
742}
743
744void AudioManagerMac::HandleDeviceChanges() {
745  DCHECK(GetTaskRunner()->BelongsToCurrentThread());
746  const int new_sample_rate = HardwareSampleRate();
747  AudioDeviceID new_output_device;
748  GetDefaultOutputDevice(&new_output_device);
749
750  if (current_sample_rate_ == new_sample_rate &&
751      current_output_device_ == new_output_device)
752    return;
753
754  current_sample_rate_ = new_sample_rate;
755  current_output_device_ = new_output_device;
756  NotifyAllOutputDeviceChangeListeners();
757}
758
759int AudioManagerMac::ChooseBufferSize(int output_sample_rate) {
760  int buffer_size = kMinimumInputOutputBufferSize;
761  const int user_buffer_size = GetUserBufferSize();
762  if (user_buffer_size) {
763    buffer_size = user_buffer_size;
764  } else if (output_sample_rate > 48000) {
765    // The default buffer size is too small for higher sample rates and may lead
766    // to glitching.  Adjust upwards by multiples of the default size.
767    if (output_sample_rate <= 96000)
768      buffer_size = 2 * kMinimumInputOutputBufferSize;
769    else if (output_sample_rate <= 192000)
770      buffer_size = 4 * kMinimumInputOutputBufferSize;
771  }
772
773  return buffer_size;
774}
775
776bool AudioManagerMac::ShouldDeferStreamStart() {
777  DCHECK(GetTaskRunner()->BelongsToCurrentThread());
778  return power_observer_->ShouldDeferStreamStart();
779}
780
781void AudioManagerMac::ReleaseOutputStream(AudioOutputStream* stream) {
782  output_streams_.remove(stream);
783  AudioManagerBase::ReleaseOutputStream(stream);
784}
785
786void AudioManagerMac::ReleaseInputStream(AudioInputStream* stream) {
787  input_streams_.remove(stream);
788  AudioManagerBase::ReleaseInputStream(stream);
789}
790
791AudioManager* CreateAudioManager(AudioLogFactory* audio_log_factory) {
792  return new AudioManagerMac(audio_log_factory);
793}
794
795}  // namespace media
796