1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "media/audio/mac/audio_manager_mac.h"
6
7 #include <CoreAudio/AudioHardware.h>
8 #include <string>
9
10 #include "base/bind.h"
11 #include "base/command_line.h"
12 #include "base/mac/mac_logging.h"
13 #include "base/mac/scoped_cftyperef.h"
14 #include "base/power_monitor/power_monitor.h"
15 #include "base/power_monitor/power_observer.h"
16 #include "base/strings/sys_string_conversions.h"
17 #include "base/threading/thread_checker.h"
18 #include "media/audio/audio_parameters.h"
19 #include "media/audio/mac/audio_auhal_mac.h"
20 #include "media/audio/mac/audio_input_mac.h"
21 #include "media/audio/mac/audio_low_latency_input_mac.h"
22 #include "media/base/bind_to_current_loop.h"
23 #include "media/base/channel_layout.h"
24 #include "media/base/limits.h"
25 #include "media/base/media_switches.h"
26
27 namespace media {
28
29 // Maximum number of output streams that can be open simultaneously.
30 static const int kMaxOutputStreams = 50;
31
32 // Define bounds for for low-latency input and output streams.
33 static const int kMinimumInputOutputBufferSize = 128;
34 static const int kMaximumInputOutputBufferSize = 4096;
35
36 // Default sample-rate on most Apple hardware.
37 static const int kFallbackSampleRate = 44100;
38
HasAudioHardware(AudioObjectPropertySelector selector)39 static bool HasAudioHardware(AudioObjectPropertySelector selector) {
40 AudioDeviceID output_device_id = kAudioObjectUnknown;
41 const AudioObjectPropertyAddress property_address = {
42 selector,
43 kAudioObjectPropertyScopeGlobal, // mScope
44 kAudioObjectPropertyElementMaster // mElement
45 };
46 UInt32 output_device_id_size = static_cast<UInt32>(sizeof(output_device_id));
47 OSStatus err = AudioObjectGetPropertyData(kAudioObjectSystemObject,
48 &property_address,
49 0, // inQualifierDataSize
50 NULL, // inQualifierData
51 &output_device_id_size,
52 &output_device_id);
53 return err == kAudioHardwareNoError &&
54 output_device_id != kAudioObjectUnknown;
55 }
56
57 // Retrieves information on audio devices, and prepends the default
58 // device to the list if the list is non-empty.
GetAudioDeviceInfo(bool is_input,media::AudioDeviceNames * device_names)59 static void GetAudioDeviceInfo(bool is_input,
60 media::AudioDeviceNames* device_names) {
61 // Query the number of total devices.
62 AudioObjectPropertyAddress property_address = {
63 kAudioHardwarePropertyDevices,
64 kAudioObjectPropertyScopeGlobal,
65 kAudioObjectPropertyElementMaster
66 };
67 UInt32 size = 0;
68 OSStatus result = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject,
69 &property_address,
70 0,
71 NULL,
72 &size);
73 if (result || !size)
74 return;
75
76 int device_count = size / sizeof(AudioDeviceID);
77
78 // Get the array of device ids for all the devices, which includes both
79 // input devices and output devices.
80 scoped_ptr<AudioDeviceID, base::FreeDeleter>
81 devices(static_cast<AudioDeviceID*>(malloc(size)));
82 AudioDeviceID* device_ids = devices.get();
83 result = AudioObjectGetPropertyData(kAudioObjectSystemObject,
84 &property_address,
85 0,
86 NULL,
87 &size,
88 device_ids);
89 if (result)
90 return;
91
92 // Iterate over all available devices to gather information.
93 for (int i = 0; i < device_count; ++i) {
94 // Get the number of input or output channels of the device.
95 property_address.mScope = is_input ?
96 kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput;
97 property_address.mSelector = kAudioDevicePropertyStreams;
98 size = 0;
99 result = AudioObjectGetPropertyDataSize(device_ids[i],
100 &property_address,
101 0,
102 NULL,
103 &size);
104 if (result || !size)
105 continue;
106
107 // Get device UID.
108 CFStringRef uid = NULL;
109 size = sizeof(uid);
110 property_address.mSelector = kAudioDevicePropertyDeviceUID;
111 property_address.mScope = kAudioObjectPropertyScopeGlobal;
112 result = AudioObjectGetPropertyData(device_ids[i],
113 &property_address,
114 0,
115 NULL,
116 &size,
117 &uid);
118 if (result)
119 continue;
120
121 // Get device name.
122 CFStringRef name = NULL;
123 property_address.mSelector = kAudioObjectPropertyName;
124 property_address.mScope = kAudioObjectPropertyScopeGlobal;
125 result = AudioObjectGetPropertyData(device_ids[i],
126 &property_address,
127 0,
128 NULL,
129 &size,
130 &name);
131 if (result) {
132 if (uid)
133 CFRelease(uid);
134 continue;
135 }
136
137 // Store the device name and UID.
138 media::AudioDeviceName device_name;
139 device_name.device_name = base::SysCFStringRefToUTF8(name);
140 device_name.unique_id = base::SysCFStringRefToUTF8(uid);
141 device_names->push_back(device_name);
142
143 // We are responsible for releasing the returned CFObject. See the
144 // comment in the AudioHardware.h for constant
145 // kAudioDevicePropertyDeviceUID.
146 if (uid)
147 CFRelease(uid);
148 if (name)
149 CFRelease(name);
150 }
151
152 if (!device_names->empty()) {
153 // Prepend the default device to the list since we always want it to be
154 // on the top of the list for all platforms. There is no duplicate
155 // counting here since the default device has been abstracted out before.
156 media::AudioDeviceName name;
157 name.device_name = AudioManagerBase::kDefaultDeviceName;
158 name.unique_id = AudioManagerBase::kDefaultDeviceId;
159 device_names->push_front(name);
160 }
161 }
162
GetAudioDeviceIdByUId(bool is_input,const std::string & device_id)163 static AudioDeviceID GetAudioDeviceIdByUId(bool is_input,
164 const std::string& device_id) {
165 AudioObjectPropertyAddress property_address = {
166 kAudioHardwarePropertyDevices,
167 kAudioObjectPropertyScopeGlobal,
168 kAudioObjectPropertyElementMaster
169 };
170 AudioDeviceID audio_device_id = kAudioObjectUnknown;
171 UInt32 device_size = sizeof(audio_device_id);
172 OSStatus result = -1;
173
174 if (device_id == AudioManagerBase::kDefaultDeviceId || device_id.empty()) {
175 // Default Device.
176 property_address.mSelector = is_input ?
177 kAudioHardwarePropertyDefaultInputDevice :
178 kAudioHardwarePropertyDefaultOutputDevice;
179
180 result = AudioObjectGetPropertyData(kAudioObjectSystemObject,
181 &property_address,
182 0,
183 0,
184 &device_size,
185 &audio_device_id);
186 } else {
187 // Non-default device.
188 base::ScopedCFTypeRef<CFStringRef> uid(
189 base::SysUTF8ToCFStringRef(device_id));
190 AudioValueTranslation value;
191 value.mInputData = &uid;
192 value.mInputDataSize = sizeof(CFStringRef);
193 value.mOutputData = &audio_device_id;
194 value.mOutputDataSize = device_size;
195 UInt32 translation_size = sizeof(AudioValueTranslation);
196
197 property_address.mSelector = kAudioHardwarePropertyDeviceForUID;
198 result = AudioObjectGetPropertyData(kAudioObjectSystemObject,
199 &property_address,
200 0,
201 0,
202 &translation_size,
203 &value);
204 }
205
206 if (result) {
207 OSSTATUS_DLOG(WARNING, result) << "Unable to query device " << device_id
208 << " for AudioDeviceID";
209 }
210
211 return audio_device_id;
212 }
213
214 template <class T>
StopStreams(std::list<T * > * streams)215 void StopStreams(std::list<T*>* streams) {
216 for (typename std::list<T*>::iterator it = streams->begin();
217 it != streams->end();
218 ++it) {
219 // Stop() is safe to call multiple times, so it doesn't matter if a stream
220 // has already been stopped.
221 (*it)->Stop();
222 }
223 streams->clear();
224 }
225
226 class AudioManagerMac::AudioPowerObserver : public base::PowerObserver {
227 public:
AudioPowerObserver()228 AudioPowerObserver()
229 : is_suspending_(false),
230 is_monitoring_(base::PowerMonitor::Get()) {
231 // The PowerMonitor requires signifcant setup (a CFRunLoop and preallocated
232 // IO ports) so it's not available under unit tests. See the OSX impl of
233 // base::PowerMonitorDeviceSource for more details.
234 if (!is_monitoring_)
235 return;
236 base::PowerMonitor::Get()->AddObserver(this);
237 }
238
~AudioPowerObserver()239 virtual ~AudioPowerObserver() {
240 DCHECK(thread_checker_.CalledOnValidThread());
241 if (!is_monitoring_)
242 return;
243 base::PowerMonitor::Get()->RemoveObserver(this);
244 }
245
ShouldDeferStreamStart()246 bool ShouldDeferStreamStart() {
247 DCHECK(thread_checker_.CalledOnValidThread());
248 // Start() should be deferred if the system is in the middle of a suspend or
249 // has recently started the process of resuming.
250 return is_suspending_ || base::TimeTicks::Now() < earliest_start_time_;
251 }
252
253 private:
OnSuspend()254 virtual void OnSuspend() OVERRIDE {
255 DCHECK(thread_checker_.CalledOnValidThread());
256 is_suspending_ = true;
257 }
258
OnResume()259 virtual void OnResume() OVERRIDE {
260 DCHECK(thread_checker_.CalledOnValidThread());
261 is_suspending_ = false;
262 earliest_start_time_ = base::TimeTicks::Now() +
263 base::TimeDelta::FromSeconds(kStartDelayInSecsForPowerEvents);
264 }
265
266 bool is_suspending_;
267 const bool is_monitoring_;
268 base::TimeTicks earliest_start_time_;
269 base::ThreadChecker thread_checker_;
270
271 DISALLOW_COPY_AND_ASSIGN(AudioPowerObserver);
272 };
273
AudioManagerMac(AudioLogFactory * audio_log_factory)274 AudioManagerMac::AudioManagerMac(AudioLogFactory* audio_log_factory)
275 : AudioManagerBase(audio_log_factory),
276 current_sample_rate_(0),
277 current_output_device_(kAudioDeviceUnknown) {
278 SetMaxOutputStreamsAllowed(kMaxOutputStreams);
279
280 // Task must be posted last to avoid races from handing out "this" to the
281 // audio thread. Always PostTask even if we're on the right thread since
282 // AudioManager creation is on the startup path and this may be slow.
283 GetTaskRunner()->PostTask(FROM_HERE, base::Bind(
284 &AudioManagerMac::InitializeOnAudioThread, base::Unretained(this)));
285 }
286
~AudioManagerMac()287 AudioManagerMac::~AudioManagerMac() {
288 if (GetTaskRunner()->BelongsToCurrentThread()) {
289 ShutdownOnAudioThread();
290 } else {
291 // It's safe to post a task here since Shutdown() will wait for all tasks to
292 // complete before returning.
293 GetTaskRunner()->PostTask(FROM_HERE, base::Bind(
294 &AudioManagerMac::ShutdownOnAudioThread, base::Unretained(this)));
295 }
296
297 Shutdown();
298 }
299
HasAudioOutputDevices()300 bool AudioManagerMac::HasAudioOutputDevices() {
301 return HasAudioHardware(kAudioHardwarePropertyDefaultOutputDevice);
302 }
303
HasAudioInputDevices()304 bool AudioManagerMac::HasAudioInputDevices() {
305 return HasAudioHardware(kAudioHardwarePropertyDefaultInputDevice);
306 }
307
308 // TODO(xians): There are several places on the OSX specific code which
309 // could benefit from these helper functions.
GetDefaultInputDevice(AudioDeviceID * device)310 bool AudioManagerMac::GetDefaultInputDevice(AudioDeviceID* device) {
311 return GetDefaultDevice(device, true);
312 }
313
GetDefaultOutputDevice(AudioDeviceID * device)314 bool AudioManagerMac::GetDefaultOutputDevice(AudioDeviceID* device) {
315 return GetDefaultDevice(device, false);
316 }
317
GetDefaultDevice(AudioDeviceID * device,bool input)318 bool AudioManagerMac::GetDefaultDevice(AudioDeviceID* device, bool input) {
319 CHECK(device);
320
321 // Obtain the current output device selected by the user.
322 AudioObjectPropertyAddress pa;
323 pa.mSelector = input ? kAudioHardwarePropertyDefaultInputDevice :
324 kAudioHardwarePropertyDefaultOutputDevice;
325 pa.mScope = kAudioObjectPropertyScopeGlobal;
326 pa.mElement = kAudioObjectPropertyElementMaster;
327
328 UInt32 size = sizeof(*device);
329 OSStatus result = AudioObjectGetPropertyData(kAudioObjectSystemObject,
330 &pa,
331 0,
332 0,
333 &size,
334 device);
335
336 if ((result != kAudioHardwareNoError) || (*device == kAudioDeviceUnknown)) {
337 DLOG(ERROR) << "Error getting default AudioDevice.";
338 return false;
339 }
340
341 return true;
342 }
343
GetDefaultOutputChannels(int * channels)344 bool AudioManagerMac::GetDefaultOutputChannels(int* channels) {
345 AudioDeviceID device;
346 if (!GetDefaultOutputDevice(&device))
347 return false;
348 return GetDeviceChannels(device, kAudioDevicePropertyScopeOutput, channels);
349 }
350
GetDeviceChannels(AudioDeviceID device,AudioObjectPropertyScope scope,int * channels)351 bool AudioManagerMac::GetDeviceChannels(AudioDeviceID device,
352 AudioObjectPropertyScope scope,
353 int* channels) {
354 CHECK(channels);
355
356 // Get stream configuration.
357 AudioObjectPropertyAddress pa;
358 pa.mSelector = kAudioDevicePropertyStreamConfiguration;
359 pa.mScope = scope;
360 pa.mElement = kAudioObjectPropertyElementMaster;
361
362 UInt32 size;
363 OSStatus result = AudioObjectGetPropertyDataSize(device, &pa, 0, 0, &size);
364 if (result != noErr || !size)
365 return false;
366
367 // Allocate storage.
368 scoped_ptr<uint8[]> list_storage(new uint8[size]);
369 AudioBufferList& buffer_list =
370 *reinterpret_cast<AudioBufferList*>(list_storage.get());
371
372 result = AudioObjectGetPropertyData(device, &pa, 0, 0, &size, &buffer_list);
373 if (result != noErr)
374 return false;
375
376 // Determine number of input channels.
377 int channels_per_frame = buffer_list.mNumberBuffers > 0 ?
378 buffer_list.mBuffers[0].mNumberChannels : 0;
379 if (channels_per_frame == 1 && buffer_list.mNumberBuffers > 1) {
380 // Non-interleaved.
381 *channels = buffer_list.mNumberBuffers;
382 } else {
383 // Interleaved.
384 *channels = channels_per_frame;
385 }
386
387 return true;
388 }
389
HardwareSampleRateForDevice(AudioDeviceID device_id)390 int AudioManagerMac::HardwareSampleRateForDevice(AudioDeviceID device_id) {
391 Float64 nominal_sample_rate;
392 UInt32 info_size = sizeof(nominal_sample_rate);
393
394 static const AudioObjectPropertyAddress kNominalSampleRateAddress = {
395 kAudioDevicePropertyNominalSampleRate,
396 kAudioObjectPropertyScopeGlobal,
397 kAudioObjectPropertyElementMaster
398 };
399 OSStatus result = AudioObjectGetPropertyData(device_id,
400 &kNominalSampleRateAddress,
401 0,
402 0,
403 &info_size,
404 &nominal_sample_rate);
405 if (result != noErr) {
406 OSSTATUS_DLOG(WARNING, result)
407 << "Could not get default sample rate for device: " << device_id;
408 return 0;
409 }
410
411 return static_cast<int>(nominal_sample_rate);
412 }
413
HardwareSampleRate()414 int AudioManagerMac::HardwareSampleRate() {
415 // Determine the default output device's sample-rate.
416 AudioDeviceID device_id = kAudioObjectUnknown;
417 if (!GetDefaultOutputDevice(&device_id))
418 return kFallbackSampleRate;
419
420 return HardwareSampleRateForDevice(device_id);
421 }
422
GetAudioInputDeviceNames(media::AudioDeviceNames * device_names)423 void AudioManagerMac::GetAudioInputDeviceNames(
424 media::AudioDeviceNames* device_names) {
425 DCHECK(device_names->empty());
426 GetAudioDeviceInfo(true, device_names);
427 }
428
GetAudioOutputDeviceNames(media::AudioDeviceNames * device_names)429 void AudioManagerMac::GetAudioOutputDeviceNames(
430 media::AudioDeviceNames* device_names) {
431 DCHECK(device_names->empty());
432 GetAudioDeviceInfo(false, device_names);
433 }
434
GetInputStreamParameters(const std::string & device_id)435 AudioParameters AudioManagerMac::GetInputStreamParameters(
436 const std::string& device_id) {
437 AudioDeviceID device = GetAudioDeviceIdByUId(true, device_id);
438 if (device == kAudioObjectUnknown) {
439 DLOG(ERROR) << "Invalid device " << device_id;
440 return AudioParameters(
441 AudioParameters::AUDIO_PCM_LOW_LATENCY, CHANNEL_LAYOUT_STEREO,
442 kFallbackSampleRate, 16, ChooseBufferSize(kFallbackSampleRate));
443 }
444
445 int channels = 0;
446 ChannelLayout channel_layout = CHANNEL_LAYOUT_STEREO;
447 if (GetDeviceChannels(device, kAudioDevicePropertyScopeInput, &channels) &&
448 channels <= 2) {
449 channel_layout = GuessChannelLayout(channels);
450 } else {
451 DLOG(ERROR) << "Failed to get the device channels, use stereo as default "
452 << "for device " << device_id;
453 }
454
455 int sample_rate = HardwareSampleRateForDevice(device);
456 if (!sample_rate)
457 sample_rate = kFallbackSampleRate;
458
459 // Due to the sharing of the input and output buffer sizes, we need to choose
460 // the input buffer size based on the output sample rate. See
461 // http://crbug.com/154352.
462 const int buffer_size = ChooseBufferSize(sample_rate);
463
464 // TODO(xians): query the native channel layout for the specific device.
465 return AudioParameters(
466 AudioParameters::AUDIO_PCM_LOW_LATENCY, channel_layout,
467 sample_rate, 16, buffer_size);
468 }
469
GetAssociatedOutputDeviceID(const std::string & input_device_id)470 std::string AudioManagerMac::GetAssociatedOutputDeviceID(
471 const std::string& input_device_id) {
472 AudioDeviceID device = GetAudioDeviceIdByUId(true, input_device_id);
473 if (device == kAudioObjectUnknown)
474 return std::string();
475
476 UInt32 size = 0;
477 AudioObjectPropertyAddress pa = {
478 kAudioDevicePropertyRelatedDevices,
479 kAudioDevicePropertyScopeOutput,
480 kAudioObjectPropertyElementMaster
481 };
482 OSStatus result = AudioObjectGetPropertyDataSize(device, &pa, 0, 0, &size);
483 if (result || !size)
484 return std::string();
485
486 int device_count = size / sizeof(AudioDeviceID);
487 scoped_ptr<AudioDeviceID, base::FreeDeleter>
488 devices(static_cast<AudioDeviceID*>(malloc(size)));
489 result = AudioObjectGetPropertyData(
490 device, &pa, 0, NULL, &size, devices.get());
491 if (result)
492 return std::string();
493
494 std::vector<std::string> associated_devices;
495 for (int i = 0; i < device_count; ++i) {
496 // Get the number of output channels of the device.
497 pa.mSelector = kAudioDevicePropertyStreams;
498 size = 0;
499 result = AudioObjectGetPropertyDataSize(devices.get()[i],
500 &pa,
501 0,
502 NULL,
503 &size);
504 if (result || !size)
505 continue; // Skip if there aren't any output channels.
506
507 // Get device UID.
508 CFStringRef uid = NULL;
509 size = sizeof(uid);
510 pa.mSelector = kAudioDevicePropertyDeviceUID;
511 result = AudioObjectGetPropertyData(devices.get()[i],
512 &pa,
513 0,
514 NULL,
515 &size,
516 &uid);
517 if (result || !uid)
518 continue;
519
520 std::string ret(base::SysCFStringRefToUTF8(uid));
521 CFRelease(uid);
522 associated_devices.push_back(ret);
523 }
524
525 // No matching device found.
526 if (associated_devices.empty())
527 return std::string();
528
529 // Return the device if there is only one associated device.
530 if (associated_devices.size() == 1)
531 return associated_devices[0];
532
533 // When there are multiple associated devices, we currently do not have a way
534 // to detect if a device (e.g. a digital output device) is actually connected
535 // to an endpoint, so we cannot randomly pick a device.
536 // We pick the device iff the associated device is the default output device.
537 const std::string default_device = GetDefaultOutputDeviceID();
538 for (std::vector<std::string>::const_iterator iter =
539 associated_devices.begin();
540 iter != associated_devices.end(); ++iter) {
541 if (default_device == *iter)
542 return *iter;
543 }
544
545 // Failed to figure out which is the matching device, return an emtpy string.
546 return std::string();
547 }
548
MakeLinearOutputStream(const AudioParameters & params)549 AudioOutputStream* AudioManagerMac::MakeLinearOutputStream(
550 const AudioParameters& params) {
551 return MakeLowLatencyOutputStream(params, std::string());
552 }
553
MakeLowLatencyOutputStream(const AudioParameters & params,const std::string & device_id)554 AudioOutputStream* AudioManagerMac::MakeLowLatencyOutputStream(
555 const AudioParameters& params,
556 const std::string& device_id) {
557 AudioDeviceID device = GetAudioDeviceIdByUId(false, device_id);
558 if (device == kAudioObjectUnknown) {
559 DLOG(ERROR) << "Failed to open output device: " << device_id;
560 return NULL;
561 }
562
563 // Lazily create the audio device listener on the first stream creation.
564 if (!output_device_listener_) {
565 // NOTE: Use BindToCurrentLoop() to ensure the callback is always PostTask'd
566 // even if OSX calls us on the right thread. Some CoreAudio drivers will
567 // fire the callbacks during stream creation, leading to re-entrancy issues
568 // otherwise. See http://crbug.com/349604
569 output_device_listener_.reset(
570 new AudioDeviceListenerMac(BindToCurrentLoop(base::Bind(
571 &AudioManagerMac::HandleDeviceChanges, base::Unretained(this)))));
572 // Only set the current output device for the default device.
573 if (device_id == AudioManagerBase::kDefaultDeviceId || device_id.empty())
574 current_output_device_ = device;
575 // Just use the current sample rate since we don't allow non-native sample
576 // rates on OSX.
577 current_sample_rate_ = params.sample_rate();
578 }
579
580 AudioOutputStream* stream = new AUHALStream(this, params, device);
581 output_streams_.push_back(stream);
582 return stream;
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 AudioInputStream* stream = new PCMQueueInAudioInputStream(this, params);
616 input_streams_.push_back(stream);
617 return stream;
618 }
619
MakeLowLatencyInputStream(const AudioParameters & params,const std::string & device_id)620 AudioInputStream* AudioManagerMac::MakeLowLatencyInputStream(
621 const AudioParameters& params, const std::string& device_id) {
622 DCHECK_EQ(AudioParameters::AUDIO_PCM_LOW_LATENCY, params.format());
623 // Gets the AudioDeviceID that refers to the AudioInputDevice with the device
624 // unique id. This AudioDeviceID is used to set the device for Audio Unit.
625 AudioDeviceID audio_device_id = GetAudioDeviceIdByUId(true, device_id);
626 AudioInputStream* stream = NULL;
627 if (audio_device_id != kAudioObjectUnknown) {
628 // AUAudioInputStream needs to be fed the preferred audio output parameters
629 // of the matching device so that the buffer size of both input and output
630 // can be matched. See constructor of AUAudioInputStream for more.
631 const std::string associated_output_device(
632 GetAssociatedOutputDeviceID(device_id));
633 const AudioParameters output_params =
634 GetPreferredOutputStreamParameters(
635 associated_output_device.empty() ?
636 AudioManagerBase::kDefaultDeviceId : associated_output_device,
637 params);
638 stream = new AUAudioInputStream(this, params, output_params,
639 audio_device_id);
640 input_streams_.push_back(stream);
641 }
642
643 return stream;
644 }
645
GetPreferredOutputStreamParameters(const std::string & output_device_id,const AudioParameters & input_params)646 AudioParameters AudioManagerMac::GetPreferredOutputStreamParameters(
647 const std::string& output_device_id,
648 const AudioParameters& input_params) {
649 const AudioDeviceID device = GetAudioDeviceIdByUId(false, output_device_id);
650 if (device == kAudioObjectUnknown) {
651 DLOG(ERROR) << "Invalid output device " << output_device_id;
652 return input_params.IsValid() ? input_params : AudioParameters(
653 AudioParameters::AUDIO_PCM_LOW_LATENCY, CHANNEL_LAYOUT_STEREO,
654 kFallbackSampleRate, 16, ChooseBufferSize(kFallbackSampleRate));
655 }
656
657 const bool has_valid_input_params = input_params.IsValid();
658 const int hardware_sample_rate = HardwareSampleRateForDevice(device);
659
660 // Allow pass through buffer sizes. If concurrent input and output streams
661 // exist, they will use the smallest buffer size amongst them. As such, each
662 // stream must be able to FIFO requests appropriately when this happens.
663 int buffer_size = ChooseBufferSize(hardware_sample_rate);
664 if (has_valid_input_params) {
665 buffer_size =
666 std::min(kMaximumInputOutputBufferSize,
667 std::max(input_params.frames_per_buffer(), buffer_size));
668 }
669
670 int hardware_channels;
671 if (!GetDeviceChannels(device, kAudioDevicePropertyScopeOutput,
672 &hardware_channels)) {
673 hardware_channels = 2;
674 }
675
676 // Use the input channel count and channel layout if possible. Let OSX take
677 // care of remapping the channels; this lets user specified channel layouts
678 // work correctly.
679 int output_channels = input_params.channels();
680 ChannelLayout channel_layout = input_params.channel_layout();
681 if (!has_valid_input_params || output_channels > hardware_channels) {
682 output_channels = hardware_channels;
683 channel_layout = GuessChannelLayout(output_channels);
684 if (channel_layout == CHANNEL_LAYOUT_UNSUPPORTED)
685 channel_layout = CHANNEL_LAYOUT_DISCRETE;
686 }
687
688 const int input_channels =
689 has_valid_input_params ? input_params.input_channels() : 0;
690 if (input_channels > 0) {
691 // TODO(xians): given the limitations of the AudioOutputStream
692 // back-ends used with synchronized I/O, we hard-code to stereo.
693 // Specifically, this is a limitation of AudioSynchronizedStream which
694 // can be removed as part of the work to consolidate these back-ends.
695 channel_layout = CHANNEL_LAYOUT_STEREO;
696 }
697
698 return AudioParameters(
699 AudioParameters::AUDIO_PCM_LOW_LATENCY, channel_layout, output_channels,
700 input_channels, hardware_sample_rate, 16, buffer_size,
701 AudioParameters::NO_EFFECTS);
702 }
703
InitializeOnAudioThread()704 void AudioManagerMac::InitializeOnAudioThread() {
705 DCHECK(GetTaskRunner()->BelongsToCurrentThread());
706 power_observer_.reset(new AudioPowerObserver());
707 }
708
ShutdownOnAudioThread()709 void AudioManagerMac::ShutdownOnAudioThread() {
710 DCHECK(GetTaskRunner()->BelongsToCurrentThread());
711 output_device_listener_.reset();
712 power_observer_.reset();
713
714 // Since CoreAudio calls have to run on the UI thread and browser shutdown
715 // doesn't wait for outstanding tasks to complete, we may have input/output
716 // streams still running at shutdown.
717 //
718 // To avoid calls into destructed classes, we need to stop the OS callbacks
719 // by stopping the streams. Note: The streams are leaked since process
720 // destruction is imminent.
721 //
722 // See http://crbug.com/354139 for crash details.
723 StopStreams(&input_streams_);
724 StopStreams(&output_streams_);
725 }
726
HandleDeviceChanges()727 void AudioManagerMac::HandleDeviceChanges() {
728 DCHECK(GetTaskRunner()->BelongsToCurrentThread());
729 const int new_sample_rate = HardwareSampleRate();
730 AudioDeviceID new_output_device;
731 GetDefaultOutputDevice(&new_output_device);
732
733 if (current_sample_rate_ == new_sample_rate &&
734 current_output_device_ == new_output_device)
735 return;
736
737 current_sample_rate_ = new_sample_rate;
738 current_output_device_ = new_output_device;
739 NotifyAllOutputDeviceChangeListeners();
740 }
741
ChooseBufferSize(int output_sample_rate)742 int AudioManagerMac::ChooseBufferSize(int output_sample_rate) {
743 int buffer_size = kMinimumInputOutputBufferSize;
744 const int user_buffer_size = GetUserBufferSize();
745 if (user_buffer_size) {
746 buffer_size = user_buffer_size;
747 } else if (output_sample_rate > 48000) {
748 // The default buffer size is too small for higher sample rates and may lead
749 // to glitching. Adjust upwards by multiples of the default size.
750 if (output_sample_rate <= 96000)
751 buffer_size = 2 * kMinimumInputOutputBufferSize;
752 else if (output_sample_rate <= 192000)
753 buffer_size = 4 * kMinimumInputOutputBufferSize;
754 }
755
756 return buffer_size;
757 }
758
ShouldDeferStreamStart()759 bool AudioManagerMac::ShouldDeferStreamStart() {
760 DCHECK(GetTaskRunner()->BelongsToCurrentThread());
761 return power_observer_->ShouldDeferStreamStart();
762 }
763
ReleaseOutputStream(AudioOutputStream * stream)764 void AudioManagerMac::ReleaseOutputStream(AudioOutputStream* stream) {
765 output_streams_.remove(stream);
766 AudioManagerBase::ReleaseOutputStream(stream);
767 }
768
ReleaseInputStream(AudioInputStream * stream)769 void AudioManagerMac::ReleaseInputStream(AudioInputStream* stream) {
770 input_streams_.remove(stream);
771 AudioManagerBase::ReleaseInputStream(stream);
772 }
773
CreateAudioManager(AudioLogFactory * audio_log_factory)774 AudioManager* CreateAudioManager(AudioLogFactory* audio_log_factory) {
775 return new AudioManagerMac(audio_log_factory);
776 }
777
778 } // namespace media
779