• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
HasAudioHardware(AudioObjectPropertySelector selector)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.
HasUnifiedDefaultIO()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.
GetAudioDeviceInfo(bool is_input,media::AudioDeviceNames * device_names)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 
GetAudioDeviceIdByUId(bool is_input,const std::string & device_id)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 
AudioManagerMac(AudioLogFactory * audio_log_factory)223 AudioManagerMac::AudioManagerMac(AudioLogFactory* audio_log_factory)
224     : AudioManagerBase(audio_log_factory),
225       current_sample_rate_(0) {
226   current_output_device_ = kAudioDeviceUnknown;
227 
228   SetMaxOutputStreamsAllowed(kMaxOutputStreams);
229 
230   // Task must be posted last to avoid races from handing out "this" to the
231   // audio thread.  Always PostTask even if we're on the right thread since
232   // AudioManager creation is on the startup path and this may be slow.
233   GetMessageLoop()->PostTask(FROM_HERE, base::Bind(
234       &AudioManagerMac::CreateDeviceListener, base::Unretained(this)));
235 }
236 
~AudioManagerMac()237 AudioManagerMac::~AudioManagerMac() {
238   if (GetMessageLoop()->BelongsToCurrentThread()) {
239     DestroyDeviceListener();
240   } else {
241     // It's safe to post a task here since Shutdown() will wait for all tasks to
242     // complete before returning.
243     GetMessageLoop()->PostTask(FROM_HERE, base::Bind(
244         &AudioManagerMac::DestroyDeviceListener, base::Unretained(this)));
245   }
246 
247   Shutdown();
248 }
249 
HasAudioOutputDevices()250 bool AudioManagerMac::HasAudioOutputDevices() {
251   return HasAudioHardware(kAudioHardwarePropertyDefaultOutputDevice);
252 }
253 
HasAudioInputDevices()254 bool AudioManagerMac::HasAudioInputDevices() {
255   return HasAudioHardware(kAudioHardwarePropertyDefaultInputDevice);
256 }
257 
258 // TODO(xians): There are several places on the OSX specific code which
259 // could benefit from these helper functions.
GetDefaultInputDevice(AudioDeviceID * device)260 bool AudioManagerMac::GetDefaultInputDevice(
261     AudioDeviceID* device) {
262   return GetDefaultDevice(device, true);
263 }
264 
GetDefaultOutputDevice(AudioDeviceID * device)265 bool AudioManagerMac::GetDefaultOutputDevice(
266     AudioDeviceID* device) {
267   return GetDefaultDevice(device, false);
268 }
269 
GetDefaultDevice(AudioDeviceID * device,bool input)270 bool AudioManagerMac::GetDefaultDevice(
271     AudioDeviceID* device, bool input) {
272   CHECK(device);
273 
274   // Obtain the current output device selected by the user.
275   AudioObjectPropertyAddress pa;
276   pa.mSelector = input ? kAudioHardwarePropertyDefaultInputDevice :
277       kAudioHardwarePropertyDefaultOutputDevice;
278   pa.mScope = kAudioObjectPropertyScopeGlobal;
279   pa.mElement = kAudioObjectPropertyElementMaster;
280 
281   UInt32 size = sizeof(*device);
282 
283   OSStatus result = AudioObjectGetPropertyData(
284       kAudioObjectSystemObject,
285       &pa,
286       0,
287       0,
288       &size,
289       device);
290 
291   if ((result != kAudioHardwareNoError) || (*device == kAudioDeviceUnknown)) {
292     DLOG(ERROR) << "Error getting default AudioDevice.";
293     return false;
294   }
295 
296   return true;
297 }
298 
GetDefaultOutputChannels(int * channels)299 bool AudioManagerMac::GetDefaultOutputChannels(
300     int* channels) {
301   AudioDeviceID device;
302   if (!GetDefaultOutputDevice(&device))
303     return false;
304 
305   return GetDeviceChannels(device,
306                            kAudioDevicePropertyScopeOutput,
307                            channels);
308 }
309 
GetDeviceChannels(AudioDeviceID device,AudioObjectPropertyScope scope,int * channels)310 bool AudioManagerMac::GetDeviceChannels(
311     AudioDeviceID device,
312     AudioObjectPropertyScope scope,
313     int* channels) {
314   CHECK(channels);
315 
316   // Get stream configuration.
317   AudioObjectPropertyAddress pa;
318   pa.mSelector = kAudioDevicePropertyStreamConfiguration;
319   pa.mScope = scope;
320   pa.mElement = kAudioObjectPropertyElementMaster;
321 
322   UInt32 size;
323   OSStatus result = AudioObjectGetPropertyDataSize(device, &pa, 0, 0, &size);
324   if (result != noErr || !size)
325     return false;
326 
327   // Allocate storage.
328   scoped_ptr<uint8[]> list_storage(new uint8[size]);
329   AudioBufferList& buffer_list =
330       *reinterpret_cast<AudioBufferList*>(list_storage.get());
331 
332   result = AudioObjectGetPropertyData(
333       device,
334       &pa,
335       0,
336       0,
337       &size,
338       &buffer_list);
339   if (result != noErr)
340     return false;
341 
342   // Determine number of input channels.
343   int channels_per_frame = buffer_list.mNumberBuffers > 0 ?
344       buffer_list.mBuffers[0].mNumberChannels : 0;
345   if (channels_per_frame == 1 && buffer_list.mNumberBuffers > 1) {
346     // Non-interleaved.
347     *channels = buffer_list.mNumberBuffers;
348   } else {
349     // Interleaved.
350     *channels = channels_per_frame;
351   }
352 
353   return true;
354 }
355 
HardwareSampleRateForDevice(AudioDeviceID device_id)356 int AudioManagerMac::HardwareSampleRateForDevice(AudioDeviceID device_id) {
357   Float64 nominal_sample_rate;
358   UInt32 info_size = sizeof(nominal_sample_rate);
359 
360   static const AudioObjectPropertyAddress kNominalSampleRateAddress = {
361       kAudioDevicePropertyNominalSampleRate,
362       kAudioObjectPropertyScopeGlobal,
363       kAudioObjectPropertyElementMaster
364   };
365   OSStatus result = AudioObjectGetPropertyData(
366       device_id,
367       &kNominalSampleRateAddress,
368       0,
369       0,
370       &info_size,
371       &nominal_sample_rate);
372   if (result != noErr) {
373     OSSTATUS_DLOG(WARNING, result)
374         << "Could not get default sample rate for device: " << device_id;
375     return 0;
376   }
377 
378   return static_cast<int>(nominal_sample_rate);
379 }
380 
HardwareSampleRate()381 int AudioManagerMac::HardwareSampleRate() {
382   // Determine the default output device's sample-rate.
383   AudioDeviceID device_id = kAudioObjectUnknown;
384   if (!GetDefaultOutputDevice(&device_id))
385     return kFallbackSampleRate;
386 
387   return HardwareSampleRateForDevice(device_id);
388 }
389 
GetAudioInputDeviceNames(media::AudioDeviceNames * device_names)390 void AudioManagerMac::GetAudioInputDeviceNames(
391     media::AudioDeviceNames* device_names) {
392   DCHECK(device_names->empty());
393   GetAudioDeviceInfo(true, device_names);
394 }
395 
GetAudioOutputDeviceNames(media::AudioDeviceNames * device_names)396 void AudioManagerMac::GetAudioOutputDeviceNames(
397     media::AudioDeviceNames* device_names) {
398   DCHECK(device_names->empty());
399   GetAudioDeviceInfo(false, device_names);
400 }
401 
GetInputStreamParameters(const std::string & device_id)402 AudioParameters AudioManagerMac::GetInputStreamParameters(
403     const std::string& device_id) {
404   // Due to the sharing of the input and output buffer sizes, we need to choose
405   // the input buffer size based on the output sample rate.  See
406   // http://crbug.com/154352.
407   const int buffer_size = ChooseBufferSize(
408       AUAudioOutputStream::HardwareSampleRate());
409 
410   AudioDeviceID device = GetAudioDeviceIdByUId(true, device_id);
411   if (device == kAudioObjectUnknown) {
412     DLOG(ERROR) << "Invalid device " << device_id;
413     return AudioParameters();
414   }
415 
416   int channels = 0;
417   ChannelLayout channel_layout = CHANNEL_LAYOUT_STEREO;
418   if (GetDeviceChannels(device, kAudioDevicePropertyScopeInput, &channels) &&
419       channels <= 2) {
420     channel_layout = GuessChannelLayout(channels);
421   } else {
422     DLOG(ERROR) << "Failed to get the device channels, use stereo as default "
423                 << "for device " << device_id;
424   }
425 
426   int sample_rate = HardwareSampleRateForDevice(device);
427   if (!sample_rate)
428     sample_rate = kFallbackSampleRate;
429 
430   // TODO(xians): query the native channel layout for the specific device.
431   return AudioParameters(
432       AudioParameters::AUDIO_PCM_LOW_LATENCY, channel_layout,
433       sample_rate, 16, buffer_size);
434 }
435 
GetAssociatedOutputDeviceID(const std::string & input_device_id)436 std::string AudioManagerMac::GetAssociatedOutputDeviceID(
437     const std::string& input_device_id) {
438   AudioDeviceID device = GetAudioDeviceIdByUId(true, input_device_id);
439   if (device == kAudioObjectUnknown)
440     return std::string();
441 
442   UInt32 size = 0;
443   AudioObjectPropertyAddress pa = {
444     kAudioDevicePropertyRelatedDevices,
445     kAudioDevicePropertyScopeOutput,
446     kAudioObjectPropertyElementMaster
447   };
448   OSStatus result = AudioObjectGetPropertyDataSize(device, &pa, 0, 0, &size);
449   if (result || !size)
450     return std::string();
451 
452   int device_count = size / sizeof(AudioDeviceID);
453   scoped_ptr_malloc<AudioDeviceID>
454       devices(reinterpret_cast<AudioDeviceID*>(malloc(size)));
455   result = AudioObjectGetPropertyData(
456       device, &pa, 0, NULL, &size, devices.get());
457   if (result)
458     return std::string();
459 
460   std::vector<std::string> associated_devices;
461   for (int i = 0; i < device_count; ++i) {
462     // Get the number of  output channels of the device.
463     pa.mSelector = kAudioDevicePropertyStreams;
464     size = 0;
465     result = AudioObjectGetPropertyDataSize(devices.get()[i],
466                                             &pa,
467                                             0,
468                                             NULL,
469                                             &size);
470     if (result || !size)
471       continue;  // Skip if there aren't any output channels.
472 
473     // Get device UID.
474     CFStringRef uid = NULL;
475     size = sizeof(uid);
476     pa.mSelector = kAudioDevicePropertyDeviceUID;
477     result = AudioObjectGetPropertyData(devices.get()[i],
478                                         &pa,
479                                         0,
480                                         NULL,
481                                         &size,
482                                         &uid);
483     if (result || !uid)
484       continue;
485 
486     std::string ret(base::SysCFStringRefToUTF8(uid));
487     CFRelease(uid);
488     associated_devices.push_back(ret);
489   }
490 
491   // No matching device found.
492   if (associated_devices.empty())
493     return std::string();
494 
495   // Return the device if there is only one associated device.
496   if (associated_devices.size() == 1)
497     return associated_devices[0];
498 
499   // When there are multiple associated devices, we currently do not have a way
500   // to detect if a device (e.g. a digital output device) is actually connected
501   // to an endpoint, so we cannot randomly pick a device.
502   // We pick the device iff the associated device is the default output device.
503   const std::string default_device = GetDefaultOutputDeviceID();
504   for (std::vector<std::string>::const_iterator iter =
505            associated_devices.begin();
506        iter != associated_devices.end(); ++iter) {
507     if (default_device == *iter)
508       return *iter;
509   }
510 
511   // Failed to figure out which is the matching device, return an emtpy string.
512   return std::string();
513 }
514 
MakeLinearOutputStream(const AudioParameters & params)515 AudioOutputStream* AudioManagerMac::MakeLinearOutputStream(
516     const AudioParameters& params) {
517   return MakeLowLatencyOutputStream(params, std::string(), std::string());
518 }
519 
MakeLowLatencyOutputStream(const AudioParameters & params,const std::string & device_id,const std::string & input_device_id)520 AudioOutputStream* AudioManagerMac::MakeLowLatencyOutputStream(
521     const AudioParameters& params,
522     const std::string& device_id,
523     const std::string& input_device_id) {
524   // Handle basic output with no input channels.
525   if (params.input_channels() == 0) {
526     AudioDeviceID device = GetAudioDeviceIdByUId(false, device_id);
527     if (device == kAudioObjectUnknown) {
528       DLOG(ERROR) << "Failed to open output device: " << device_id;
529       return NULL;
530     }
531     return new AUHALStream(this, params, device);
532   }
533 
534   DLOG_IF(ERROR, !device_id.empty()) << "Not implemented!";
535 
536   // TODO(xians): support more than stereo input.
537   if (params.input_channels() != 2) {
538     // WebAudio is currently hard-coded to 2 channels so we should not
539     // see this case.
540     NOTREACHED() << "Only stereo input is currently supported!";
541     return NULL;
542   }
543 
544   AudioDeviceID device = kAudioObjectUnknown;
545   if (HasUnifiedDefaultIO()) {
546     // For I/O, the simplest case is when the default input and output
547     // devices are the same.
548     GetDefaultOutputDevice(&device);
549     VLOG(0) << "UNIFIED: default input and output devices are identical";
550   } else {
551     // Some audio hardware is presented as separate input and output devices
552     // even though they are really the same physical hardware and
553     // share the same "clock domain" at the lowest levels of the driver.
554     // A common of example of this is the "built-in" audio hardware:
555     //     "Built-in Line Input"
556     //     "Built-in Output"
557     // We would like to use an "aggregate" device for these situations, since
558     // CoreAudio will make the most efficient use of the shared "clock domain"
559     // so we get the lowest latency and use fewer threads.
560     device = aggregate_device_manager_.GetDefaultAggregateDevice();
561     if (device != kAudioObjectUnknown)
562       VLOG(0) << "Using AGGREGATE audio device";
563   }
564 
565   if (device != kAudioObjectUnknown &&
566       input_device_id == AudioManagerBase::kDefaultDeviceId)
567     return new AUHALStream(this, params, device);
568 
569   // Fallback to AudioSynchronizedStream which will handle completely
570   // different and arbitrary combinations of input and output devices
571   // even running at different sample-rates.
572   // kAudioDeviceUnknown translates to "use default" here.
573   // TODO(xians): consider tracking UMA stats on AUHALStream
574   // versus AudioSynchronizedStream.
575   AudioDeviceID audio_device_id = GetAudioDeviceIdByUId(true, input_device_id);
576   if (audio_device_id == kAudioObjectUnknown)
577     return NULL;
578 
579   return new AudioSynchronizedStream(this,
580                                      params,
581                                      audio_device_id,
582                                      kAudioDeviceUnknown);
583 }
584 
GetDefaultOutputDeviceID()585 std::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 
MakeLinearInputStream(const AudioParameters & params,const std::string & device_id)612 AudioInputStream* AudioManagerMac::MakeLinearInputStream(
613     const AudioParameters& params, const std::string& device_id) {
614   DCHECK_EQ(AudioParameters::AUDIO_PCM_LINEAR, params.format());
615   return new PCMQueueInAudioInputStream(this, params);
616 }
617 
MakeLowLatencyInputStream(const AudioParameters & params,const std::string & device_id)618 AudioInputStream* AudioManagerMac::MakeLowLatencyInputStream(
619     const AudioParameters& params, const std::string& device_id) {
620   DCHECK_EQ(AudioParameters::AUDIO_PCM_LOW_LATENCY, params.format());
621   // Gets the AudioDeviceID that refers to the AudioInputDevice with the device
622   // unique id. This AudioDeviceID is used to set the device for Audio Unit.
623   AudioDeviceID audio_device_id = GetAudioDeviceIdByUId(true, device_id);
624   AudioInputStream* stream = NULL;
625   if (audio_device_id != kAudioObjectUnknown) {
626     // AUAudioInputStream needs to be fed the preferred audio output parameters
627     // of the matching device so that the buffer size of both input and output
628     // can be matched.  See constructor of AUAudioInputStream for more.
629     const std::string associated_output_device(
630         GetAssociatedOutputDeviceID(device_id));
631     const AudioParameters output_params =
632         GetPreferredOutputStreamParameters(
633             associated_output_device.empty() ?
634                 AudioManagerBase::kDefaultDeviceId : associated_output_device,
635             params);
636     stream = new AUAudioInputStream(this, params, output_params,
637         audio_device_id);
638   }
639 
640   return stream;
641 }
642 
GetPreferredOutputStreamParameters(const std::string & output_device_id,const AudioParameters & input_params)643 AudioParameters AudioManagerMac::GetPreferredOutputStreamParameters(
644     const std::string& output_device_id,
645     const AudioParameters& input_params) {
646   AudioDeviceID device = GetAudioDeviceIdByUId(false, output_device_id);
647   if (device == kAudioObjectUnknown) {
648     DLOG(ERROR) << "Invalid output device " << output_device_id;
649     return AudioParameters();
650   }
651 
652   int hardware_channels = 2;
653   if (!GetDeviceChannels(device, kAudioDevicePropertyScopeOutput,
654                          &hardware_channels)) {
655     // Fallback to stereo.
656     hardware_channels = 2;
657   }
658 
659   ChannelLayout channel_layout = GuessChannelLayout(hardware_channels);
660 
661   const int hardware_sample_rate = HardwareSampleRateForDevice(device);
662   const int buffer_size = ChooseBufferSize(hardware_sample_rate);
663 
664   int input_channels = 0;
665   if (input_params.IsValid()) {
666     input_channels = input_params.input_channels();
667 
668     if (input_channels > 0) {
669       // TODO(xians): given the limitations of the AudioOutputStream
670       // back-ends used with synchronized I/O, we hard-code to stereo.
671       // Specifically, this is a limitation of AudioSynchronizedStream which
672       // can be removed as part of the work to consolidate these back-ends.
673       channel_layout = CHANNEL_LAYOUT_STEREO;
674     }
675   }
676 
677   if (channel_layout == CHANNEL_LAYOUT_UNSUPPORTED)
678     channel_layout = CHANNEL_LAYOUT_DISCRETE;
679   else
680     hardware_channels = ChannelLayoutToChannelCount(channel_layout);
681 
682   AudioParameters params(
683       AudioParameters::AUDIO_PCM_LOW_LATENCY,
684       channel_layout,
685       hardware_channels,
686       input_channels,
687       hardware_sample_rate,
688       16,
689       buffer_size,
690       AudioParameters::NO_EFFECTS);
691 
692   return params;
693 }
694 
CreateDeviceListener()695 void AudioManagerMac::CreateDeviceListener() {
696   DCHECK(GetMessageLoop()->BelongsToCurrentThread());
697 
698   // Get a baseline for the sample-rate and current device,
699   // so we can intelligently handle device notifications only when necessary.
700   current_sample_rate_ = HardwareSampleRate();
701   if (!GetDefaultOutputDevice(&current_output_device_))
702     current_output_device_ = kAudioDeviceUnknown;
703 
704   output_device_listener_.reset(new AudioDeviceListenerMac(base::Bind(
705       &AudioManagerMac::HandleDeviceChanges, base::Unretained(this))));
706 }
707 
DestroyDeviceListener()708 void AudioManagerMac::DestroyDeviceListener() {
709   DCHECK(GetMessageLoop()->BelongsToCurrentThread());
710   output_device_listener_.reset();
711 }
712 
HandleDeviceChanges()713 void AudioManagerMac::HandleDeviceChanges() {
714   if (!GetMessageLoop()->BelongsToCurrentThread()) {
715     GetMessageLoop()->PostTask(FROM_HERE, base::Bind(
716         &AudioManagerMac::HandleDeviceChanges, base::Unretained(this)));
717     return;
718   }
719 
720   int new_sample_rate = HardwareSampleRate();
721   AudioDeviceID new_output_device;
722   GetDefaultOutputDevice(&new_output_device);
723 
724   if (current_sample_rate_ == new_sample_rate &&
725       current_output_device_ == new_output_device)
726     return;
727 
728   current_sample_rate_ = new_sample_rate;
729   current_output_device_ = new_output_device;
730   NotifyAllOutputDeviceChangeListeners();
731 }
732 
ChooseBufferSize(int output_sample_rate)733 int AudioManagerMac::ChooseBufferSize(int output_sample_rate) {
734   int buffer_size = kDefaultLowLatencyBufferSize;
735   const int user_buffer_size = GetUserBufferSize();
736   if (user_buffer_size) {
737     buffer_size = user_buffer_size;
738   } else if (output_sample_rate > 48000) {
739     // The default buffer size is too small for higher sample rates and may lead
740     // to glitching.  Adjust upwards by multiples of the default size.
741     if (output_sample_rate <= 96000)
742       buffer_size = 2 * kDefaultLowLatencyBufferSize;
743     else if (output_sample_rate <= 192000)
744       buffer_size = 4 * kDefaultLowLatencyBufferSize;
745   }
746 
747   return buffer_size;
748 }
749 
CreateAudioManager(AudioLogFactory * audio_log_factory)750 AudioManager* CreateAudioManager(AudioLogFactory* audio_log_factory) {
751   return new AudioManagerMac(audio_log_factory);
752 }
753 
754 }  // namespace media
755