Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(8)

Side by Side Diff: media/audio/mac/audio_manager_mac.cc

Issue 2771093002: Use AudioUnit instead of AudioDevice properties to get channel count. (Closed)
Patch Set: Fix enum type. Created 3 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "media/audio/mac/audio_manager_mac.h" 5 #include "media/audio/mac/audio_manager_mac.h"
6 6
7 #include <algorithm> 7 #include <algorithm>
8 #include <limits> 8 #include <limits>
9 #include <vector> 9 #include <vector>
10 10
11 #include "base/bind.h" 11 #include "base/bind.h"
12 #include "base/command_line.h" 12 #include "base/command_line.h"
13 #include "base/mac/mac_logging.h" 13 #include "base/mac/mac_logging.h"
14 #include "base/mac/scoped_cftyperef.h" 14 #include "base/mac/scoped_cftyperef.h"
15 #include "base/macros.h" 15 #include "base/macros.h"
16 #include "base/memory/free_deleter.h" 16 #include "base/memory/free_deleter.h"
17 #include "base/power_monitor/power_monitor.h" 17 #include "base/power_monitor/power_monitor.h"
18 #include "base/power_monitor/power_observer.h" 18 #include "base/power_monitor/power_observer.h"
19 #include "base/strings/sys_string_conversions.h" 19 #include "base/strings/sys_string_conversions.h"
20 #include "base/threading/thread_checker.h" 20 #include "base/threading/thread_checker.h"
21 #include "media/audio/audio_device_description.h" 21 #include "media/audio/audio_device_description.h"
22 #include "media/audio/mac/audio_auhal_mac.h" 22 #include "media/audio/mac/audio_auhal_mac.h"
23 #include "media/audio/mac/audio_input_mac.h" 23 #include "media/audio/mac/audio_input_mac.h"
24 #include "media/audio/mac/audio_low_latency_input_mac.h" 24 #include "media/audio/mac/audio_low_latency_input_mac.h"
25 #include "media/audio/mac/scoped_audio_unit.h"
25 #include "media/base/audio_parameters.h" 26 #include "media/base/audio_parameters.h"
26 #include "media/base/bind_to_current_loop.h" 27 #include "media/base/bind_to_current_loop.h"
27 #include "media/base/channel_layout.h" 28 #include "media/base/channel_layout.h"
28 #include "media/base/limits.h" 29 #include "media/base/limits.h"
29 #include "media/base/media_switches.h" 30 #include "media/base/media_switches.h"
30 31
31 namespace media { 32 namespace media {
32 33
33 // Maximum number of output streams that can be open simultaneously. 34 // Maximum number of output streams that can be open simultaneously.
34 static const int kMaxOutputStreams = 50; 35 static const int kMaxOutputStreams = 50;
(...skipping 251 matching lines...) Expand 10 before | Expand all | Expand 10 after
286 DLOG(ERROR) << "Error getting default AudioDevice."; 287 DLOG(ERROR) << "Error getting default AudioDevice.";
287 return false; 288 return false;
288 } 289 }
289 return true; 290 return true;
290 } 291 }
291 292
292 static bool GetDefaultOutputDevice(AudioDeviceID* device) { 293 static bool GetDefaultOutputDevice(AudioDeviceID* device) {
293 return GetDefaultDevice(device, false); 294 return GetDefaultDevice(device, false);
294 } 295 }
295 296
296 // Returns the total number of channels on a device; regardless of what the 297 static bool GetDeviceChannels(AudioDeviceID device,
297 // device's preferred rendering layout looks like. Should only be used for the 298 AUElement element,
298 // channel count when a device has more than kMaxConcurrentChannels. 299 int* channels) {
299 static bool GetDeviceTotalChannelCount(AudioDeviceID device,
300 AudioObjectPropertyScope scope,
301 int* channels) {
302 DCHECK(AudioManager::Get()->GetTaskRunner()->BelongsToCurrentThread()); 300 DCHECK(AudioManager::Get()->GetTaskRunner()->BelongsToCurrentThread());
303 CHECK(channels); 301 CHECK(channels);
304 302
305 // Get the stream configuration of the device in an AudioBufferList (with the 303 ScopedAudioUnit au(device, element);
306 // buffer pointers set to nullptr) which describes the list of streams and the 304 if (!au.is_valid())
307 // number of channels in each stream.
308 AudioObjectPropertyAddress pa = {kAudioDevicePropertyStreamConfiguration,
309 scope, kAudioObjectPropertyElementMaster};
310
311 UInt32 size;
312 OSStatus result = AudioObjectGetPropertyDataSize(device, &pa, 0, 0, &size);
313 if (result != noErr || !size)
314 return false; 305 return false;
315 306
316 std::unique_ptr<uint8_t[]> list_storage(new uint8_t[size]); 307 // Attempt to retrieve the channel layout from the AudioUnit.
317 AudioBufferList* buffer_list = 308 //
318 reinterpret_cast<AudioBufferList*>(list_storage.get()); 309 // Note: We don't use kAudioDevicePropertyPreferredChannelLayout on the device
310 // because it is not available on all devices.
311 UInt32 size;
312 Boolean writable;
313 OSStatus result = AudioUnitGetPropertyInfo(
314 au.audio_unit(), kAudioUnitProperty_AudioChannelLayout,
315 kAudioUnitScope_Output, element, &size, &writable);
316 if (result != noErr) {
317 OSSTATUS_DLOG(ERROR, result)
318 << "Failed to get property info for AudioUnit channel layout.";
319 return false;
320 }
319 321
320 result = AudioObjectGetPropertyData(device, &pa, 0, 0, &size, buffer_list); 322 std::unique_ptr<uint8_t[]> layout_storage(new uint8_t[size]);
321 if (result != noErr) 323 AudioChannelLayout* layout =
324 reinterpret_cast<AudioChannelLayout*>(layout_storage.get());
325
326 result = AudioUnitGetProperty(au.audio_unit(),
327 kAudioUnitProperty_AudioChannelLayout,
328 kAudioUnitScope_Output, element, layout, &size);
329 if (result != noErr) {
330 OSSTATUS_LOG(ERROR, result) << "Failed to get AudioUnit channel layout.";
322 return false; 331 return false;
332 }
323 333
324 // Determine number of channels based on the AudioBufferList. 334 // We don't want to have to know about all channel layout tags, so force OSX
325 // |mNumberBuffers] is the number of interleaved channels in the buffer. 335 // to give us the channel descriptions from the bitmap or tag if necessary.
326 // If the number is 1, the buffer is noninterleaved. 336 const AudioChannelLayoutTag tag = layout->mChannelLayoutTag;
327 *channels = 0; 337 if (tag != kAudioChannelLayoutTag_UseChannelDescriptions) {
328 for (UInt32 i = 0; i < buffer_list->mNumberBuffers; ++i) 338 const bool is_bitmap = tag == kAudioChannelLayoutTag_UseChannelBitmap;
329 *channels += buffer_list->mBuffers[i].mNumberChannels; 339 const AudioFormatPropertyID fa =
340 is_bitmap ? kAudioFormatProperty_ChannelLayoutForBitmap
341 : kAudioFormatProperty_ChannelLayoutForTag;
330 342
331 DVLOG(1) << (scope == kAudioDevicePropertyScopeInput ? "Input" : "Output") 343 if (is_bitmap) {
332 << " total channels: " << *channels; 344 result = AudioFormatGetPropertyInfo(fa, sizeof(UInt32),
345 &layout->mChannelBitmap, &size);
346 } else {
347 result = AudioFormatGetPropertyInfo(fa, sizeof(AudioChannelLayoutTag),
348 &tag, &size);
349 }
350 if (result != noErr || !size) {
351 OSSTATUS_DLOG(ERROR, result)
352 << "Failed to get AudioFormat property info, size=" << size;
353 return false;
354 }
355
356 layout_storage.reset(new uint8_t[size]);
357 layout = reinterpret_cast<AudioChannelLayout*>(layout_storage.get());
358 if (is_bitmap) {
359 result = AudioFormatGetProperty(fa, sizeof(UInt32),
360 &layout->mChannelBitmap, &size, layout);
361 } else {
362 result = AudioFormatGetProperty(fa, sizeof(AudioChannelLayoutTag), &tag,
363 &size, layout);
364 }
365 if (result != noErr) {
366 OSSTATUS_DLOG(ERROR, result) << "Failed to get AudioFormat property.";
367 return false;
368 }
369 }
370
371 // There is no channel info for stereo, assume so for mono as well.
372 if (layout->mNumberChannelDescriptions <= 2) {
373 *channels = layout->mNumberChannelDescriptions;
374 } else {
375 *channels = 0;
376 for (UInt32 i = 0; i < layout->mNumberChannelDescriptions; ++i) {
377 if (layout->mChannelDescriptions[i].mChannelLabel !=
378 kAudioChannelLabel_Unknown)
379 (*channels)++;
380 }
381 }
382
383 DVLOG(1) << (element == AUElement::OUTPUT ? "Output" : "Input")
384 << " channels: " << *channels;
333 return true; 385 return true;
334 } 386 }
335 387
336 class AudioManagerMac::AudioPowerObserver : public base::PowerObserver { 388 class AudioManagerMac::AudioPowerObserver : public base::PowerObserver {
337 public: 389 public:
338 AudioPowerObserver() 390 AudioPowerObserver()
339 : is_suspending_(false), 391 : is_suspending_(false),
340 is_monitoring_(base::PowerMonitor::Get()), 392 is_monitoring_(base::PowerMonitor::Get()),
341 num_resume_notifications_(0) { 393 num_resume_notifications_(0) {
342 // The PowerMonitor requires significant setup (a CFRunLoop and preallocated 394 // The PowerMonitor requires significant setup (a CFRunLoop and preallocated
(...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after
429 481
430 bool AudioManagerMac::HasAudioOutputDevices() { 482 bool AudioManagerMac::HasAudioOutputDevices() {
431 return HasAudioHardware(kAudioHardwarePropertyDefaultOutputDevice); 483 return HasAudioHardware(kAudioHardwarePropertyDefaultOutputDevice);
432 } 484 }
433 485
434 bool AudioManagerMac::HasAudioInputDevices() { 486 bool AudioManagerMac::HasAudioInputDevices() {
435 return HasAudioHardware(kAudioHardwarePropertyDefaultInputDevice); 487 return HasAudioHardware(kAudioHardwarePropertyDefaultInputDevice);
436 } 488 }
437 489
438 // static 490 // static
439 bool AudioManagerMac::GetDeviceChannels(AudioDeviceID device,
440 AudioObjectPropertyScope scope,
441 int* channels) {
442 DCHECK(AudioManager::Get()->GetTaskRunner()->BelongsToCurrentThread());
443 CHECK(channels);
444
445 // If the device has more channels than possible for layouts to express, use
446 // the total count of channels on the device; as of this writing, macOS will
447 // only return up to 8 channels in any layout. To allow WebAudio to work with
448 // > 8 channel devices, we must use the total channel count instead of the
449 // channel count of the preferred layout.
450 int total_channel_count = 0;
451 if (GetDeviceTotalChannelCount(device, scope, &total_channel_count) &&
452 total_channel_count > kMaxConcurrentChannels) {
453 *channels = total_channel_count;
454 return true;
455 }
456
457 AudioObjectPropertyAddress pa = {kAudioDevicePropertyPreferredChannelLayout,
458 scope, kAudioObjectPropertyElementMaster};
459 UInt32 size;
460 OSStatus result = AudioObjectGetPropertyDataSize(device, &pa, 0, 0, &size);
461 if (result != noErr || !size)
462 return false;
463
464 std::unique_ptr<uint8_t[]> layout_storage(new uint8_t[size]);
465 AudioChannelLayout* layout =
466 reinterpret_cast<AudioChannelLayout*>(layout_storage.get());
467 result = AudioObjectGetPropertyData(device, &pa, 0, 0, &size, layout);
468 if (result != noErr)
469 return false;
470
471 // We don't want to have to know about all channel layout tags, so force OSX
472 // to give us the channel descriptions from the bitmap or tag if necessary.
473 const AudioChannelLayoutTag tag = layout->mChannelLayoutTag;
474 if (tag != kAudioChannelLayoutTag_UseChannelDescriptions) {
475 const bool is_bitmap = tag == kAudioChannelLayoutTag_UseChannelBitmap;
476 const AudioFormatPropertyID fa =
477 is_bitmap ? kAudioFormatProperty_ChannelLayoutForBitmap
478 : kAudioFormatProperty_ChannelLayoutForTag;
479
480 if (is_bitmap) {
481 result = AudioFormatGetPropertyInfo(fa, sizeof(UInt32),
482 &layout->mChannelBitmap, &size);
483 } else {
484 result = AudioFormatGetPropertyInfo(fa, sizeof(AudioChannelLayoutTag),
485 &tag, &size);
486 }
487 if (result != noErr || !size)
488 return false;
489
490 layout_storage.reset(new uint8_t[size]);
491 layout = reinterpret_cast<AudioChannelLayout*>(layout_storage.get());
492 if (is_bitmap) {
493 result = AudioFormatGetProperty(fa, sizeof(UInt32),
494 &layout->mChannelBitmap, &size, layout);
495 } else {
496 result = AudioFormatGetProperty(fa, sizeof(AudioChannelLayoutTag), &tag,
497 &size, layout);
498 }
499 if (result != noErr)
500 return false;
501 }
502
503 // There is no channel info for stereo, assume so for mono as well.
504 if (layout->mNumberChannelDescriptions <= 2) {
505 *channels = layout->mNumberChannelDescriptions;
506 } else {
507 *channels = 0;
508 for (UInt32 i = 0; i < layout->mNumberChannelDescriptions; ++i) {
509 if (layout->mChannelDescriptions[i].mChannelLabel !=
510 kAudioChannelLabel_Unknown)
511 (*channels)++;
512 }
513 }
514
515 // If we still don't have a channel count, fall back to total channel count.
516 if (*channels == 0) {
517 DLOG(WARNING) << "Unable to use channel layout for channel count.";
518 *channels = total_channel_count;
519 }
520
521 DVLOG(1) << (scope == kAudioDevicePropertyScopeInput ? "Input" : "Output")
522 << " channels: " << *channels;
523 return true;
524 }
525
526 // static
527 int AudioManagerMac::HardwareSampleRateForDevice(AudioDeviceID device_id) { 491 int AudioManagerMac::HardwareSampleRateForDevice(AudioDeviceID device_id) {
528 DCHECK(AudioManager::Get()->GetTaskRunner()->BelongsToCurrentThread()); 492 DCHECK(AudioManager::Get()->GetTaskRunner()->BelongsToCurrentThread());
529 Float64 nominal_sample_rate; 493 Float64 nominal_sample_rate;
530 UInt32 info_size = sizeof(nominal_sample_rate); 494 UInt32 info_size = sizeof(nominal_sample_rate);
531 495
532 static const AudioObjectPropertyAddress kNominalSampleRateAddress = { 496 static const AudioObjectPropertyAddress kNominalSampleRateAddress = {
533 kAudioDevicePropertyNominalSampleRate, 497 kAudioDevicePropertyNominalSampleRate,
534 kAudioObjectPropertyScopeGlobal, 498 kAudioObjectPropertyScopeGlobal,
535 kAudioObjectPropertyElementMaster 499 kAudioObjectPropertyElementMaster
536 }; 500 };
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
570 DCHECK(device_names->empty()); 534 DCHECK(device_names->empty());
571 GetAudioDeviceInfo(false, device_names); 535 GetAudioDeviceInfo(false, device_names);
572 } 536 }
573 537
574 AudioParameters AudioManagerMac::GetInputStreamParameters( 538 AudioParameters AudioManagerMac::GetInputStreamParameters(
575 const std::string& device_id) { 539 const std::string& device_id) {
576 DCHECK(GetTaskRunner()->BelongsToCurrentThread()); 540 DCHECK(GetTaskRunner()->BelongsToCurrentThread());
577 AudioDeviceID device = GetAudioDeviceIdByUId(true, device_id); 541 AudioDeviceID device = GetAudioDeviceIdByUId(true, device_id);
578 if (device == kAudioObjectUnknown) { 542 if (device == kAudioObjectUnknown) {
579 DLOG(ERROR) << "Invalid device " << device_id; 543 DLOG(ERROR) << "Invalid device " << device_id;
580 return AudioParameters( 544 return AudioParameters(AudioParameters::AUDIO_PCM_LOW_LATENCY,
581 AudioParameters::AUDIO_PCM_LOW_LATENCY, CHANNEL_LAYOUT_STEREO, 545 CHANNEL_LAYOUT_STEREO, kFallbackSampleRate, 16,
582 kFallbackSampleRate, 16, ChooseBufferSize(true, kFallbackSampleRate)); 546 ChooseBufferSize(true, kFallbackSampleRate));
583 } 547 }
584 548
585 int channels = 0; 549 int channels = 0;
586 ChannelLayout channel_layout = CHANNEL_LAYOUT_STEREO; 550 ChannelLayout channel_layout = CHANNEL_LAYOUT_STEREO;
587 if (GetDeviceChannels(device, kAudioDevicePropertyScopeInput, &channels) && 551 if (GetDeviceChannels(device, AUElement::INPUT, &channels) && channels <= 2) {
588 channels <= 2) {
589 channel_layout = GuessChannelLayout(channels); 552 channel_layout = GuessChannelLayout(channels);
590 } else { 553 } else {
591 DLOG(ERROR) << "Failed to get the device channels, use stereo as default " 554 DLOG(ERROR) << "Failed to get the device channels, use stereo as default "
592 << "for device " << device_id; 555 << "for device " << device_id;
593 } 556 }
594 557
595 int sample_rate = HardwareSampleRateForDevice(device); 558 int sample_rate = HardwareSampleRateForDevice(device);
596 if (!sample_rate) 559 if (!sample_rate)
597 sample_rate = kFallbackSampleRate; 560 sample_rate = kFallbackSampleRate;
598 561
(...skipping 219 matching lines...) Expand 10 before | Expand all | Expand 10 after
818 // exist, they will use the smallest buffer size amongst them. As such, each 781 // exist, they will use the smallest buffer size amongst them. As such, each
819 // stream must be able to FIFO requests appropriately when this happens. 782 // stream must be able to FIFO requests appropriately when this happens.
820 int buffer_size = ChooseBufferSize(false, hardware_sample_rate); 783 int buffer_size = ChooseBufferSize(false, hardware_sample_rate);
821 if (has_valid_input_params) { 784 if (has_valid_input_params) {
822 buffer_size = 785 buffer_size =
823 std::min(kMaximumInputOutputBufferSize, 786 std::min(kMaximumInputOutputBufferSize,
824 std::max(input_params.frames_per_buffer(), buffer_size)); 787 std::max(input_params.frames_per_buffer(), buffer_size));
825 } 788 }
826 789
827 int hardware_channels; 790 int hardware_channels;
828 if (!GetDeviceChannels(device, kAudioDevicePropertyScopeOutput, 791 if (!GetDeviceChannels(device, AUElement::OUTPUT, &hardware_channels))
829 &hardware_channels)) {
830 hardware_channels = 2; 792 hardware_channels = 2;
831 }
832 793
833 // Use the input channel count and channel layout if possible. Let OSX take 794 // Use the input channel count and channel layout if possible. Let OSX take
834 // care of remapping the channels; this lets user specified channel layouts 795 // care of remapping the channels; this lets user specified channel layouts
835 // work correctly. 796 // work correctly.
836 int output_channels = input_params.channels(); 797 int output_channels = input_params.channels();
837 ChannelLayout channel_layout = input_params.channel_layout(); 798 ChannelLayout channel_layout = input_params.channel_layout();
838 if (!has_valid_input_params || output_channels > hardware_channels) { 799 if (!has_valid_input_params || output_channels > hardware_channels) {
839 output_channels = hardware_channels; 800 output_channels = hardware_channels;
840 channel_layout = GuessChannelLayout(output_channels); 801 channel_layout = GuessChannelLayout(output_channels);
841 if (channel_layout == CHANNEL_LAYOUT_UNSUPPORTED) 802 if (channel_layout == CHANNEL_LAYOUT_UNSUPPORTED)
(...skipping 326 matching lines...) Expand 10 before | Expand all | Expand 10 after
1168 ScopedAudioManagerPtr CreateAudioManager( 1129 ScopedAudioManagerPtr CreateAudioManager(
1169 scoped_refptr<base::SingleThreadTaskRunner> task_runner, 1130 scoped_refptr<base::SingleThreadTaskRunner> task_runner,
1170 scoped_refptr<base::SingleThreadTaskRunner> worker_task_runner, 1131 scoped_refptr<base::SingleThreadTaskRunner> worker_task_runner,
1171 AudioLogFactory* audio_log_factory) { 1132 AudioLogFactory* audio_log_factory) {
1172 return ScopedAudioManagerPtr( 1133 return ScopedAudioManagerPtr(
1173 new AudioManagerMac(std::move(task_runner), std::move(worker_task_runner), 1134 new AudioManagerMac(std::move(task_runner), std::move(worker_task_runner),
1174 audio_log_factory)); 1135 audio_log_factory));
1175 } 1136 }
1176 1137
1177 } // namespace media 1138 } // namespace media
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698