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