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(¤t_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