- add sources.
[platform/framework/web/crosswalk.git] / src / media / audio / mac / audio_manager_mac.cc
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
27 namespace media {
28
29 // Maximum number of output streams that can be open simultaneously.
30 static const int kMaxOutputStreams = 50;
31
32 // Default buffer size in samples for low-latency input and output streams.
33 static const int kDefaultLowLatencyBufferSize = 128;
34
35 // Default sample-rate on most Apple hardware.
36 static const int kFallbackSampleRate = 44100;
37
38 static 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.
58 bool 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.
68 static 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
172 static 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
223 AudioManagerMac::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
236 AudioManagerMac::~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
249 bool AudioManagerMac::HasAudioOutputDevices() {
250   return HasAudioHardware(kAudioHardwarePropertyDefaultOutputDevice);
251 }
252
253 bool 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.
259 bool AudioManagerMac::GetDefaultInputDevice(
260     AudioDeviceID* device) {
261   return GetDefaultDevice(device, true);
262 }
263
264 bool AudioManagerMac::GetDefaultOutputDevice(
265     AudioDeviceID* device) {
266   return GetDefaultDevice(device, false);
267 }
268
269 bool 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
298 bool 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
309 bool 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
355 int 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
380 int 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
389 void AudioManagerMac::GetAudioInputDeviceNames(
390     media::AudioDeviceNames* device_names) {
391   DCHECK(device_names->empty());
392   GetAudioDeviceInfo(true, device_names);
393 }
394
395 void AudioManagerMac::GetAudioOutputDeviceNames(
396     media::AudioDeviceNames* device_names) {
397   DCHECK(device_names->empty());
398   GetAudioDeviceInfo(false, device_names);
399 }
400
401 AudioParameters 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
435 std::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
493 AudioOutputStream* AudioManagerMac::MakeLinearOutputStream(
494     const AudioParameters& params) {
495   return MakeLowLatencyOutputStream(params, std::string(), std::string());
496 }
497
498 AudioOutputStream* 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
563 std::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
590 AudioInputStream* 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
596 AudioInputStream* 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
621 AudioParameters 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
669 void 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
682 void AudioManagerMac::DestroyDeviceListener() {
683   DCHECK(GetMessageLoop()->BelongsToCurrentThread());
684   output_device_listener_.reset();
685 }
686
687 void 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
707 int 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
724 AudioManager* CreateAudioManager() {
725   return new AudioManagerMac();
726 }
727
728 }  // namespace media