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

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: Revert deletion of GetDeviceTotalChannelCount. 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
« no previous file with comments | « media/audio/mac/audio_manager_mac.h ('k') | media/audio/mac/scoped_audio_unit.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 291 matching lines...) Expand 10 before | Expand all | Expand 10 after
326 // If the number is 1, the buffer is noninterleaved. 327 // If the number is 1, the buffer is noninterleaved.
327 *channels = 0; 328 *channels = 0;
328 for (UInt32 i = 0; i < buffer_list->mNumberBuffers; ++i) 329 for (UInt32 i = 0; i < buffer_list->mNumberBuffers; ++i)
329 *channels += buffer_list->mBuffers[i].mNumberChannels; 330 *channels += buffer_list->mBuffers[i].mNumberChannels;
330 331
331 DVLOG(1) << (scope == kAudioDevicePropertyScopeInput ? "Input" : "Output") 332 DVLOG(1) << (scope == kAudioDevicePropertyScopeInput ? "Input" : "Output")
332 << " total channels: " << *channels; 333 << " total channels: " << *channels;
333 return true; 334 return true;
334 } 335 }
335 336
337 // Returns the channel layout for |device| as provided by the AudioUnit attached
338 // to that device matching |element|. Returns true if the count could be pulled
339 // from the AudioUnit successfully, false otherwise.
340 static bool GetDeviceChannels(AudioDeviceID device,
341 AUElement element,
342 int* channels) {
343 DCHECK(AudioManager::Get()->GetTaskRunner()->BelongsToCurrentThread());
344 CHECK(channels);
345
346 // If the device has more channels than possible for layouts to express, use
347 // the total count of channels on the device; as of this writing, macOS will
348 // only return up to 8 channels in any layout. To allow WebAudio to work with
349 // > 8 channel devices, we must use the total channel count instead of the
350 // channel count of the preferred layout.
351 int total_channel_count = 0;
352 if (GetDeviceTotalChannelCount(device,
353 element == AUElement::OUTPUT
354 ? kAudioDevicePropertyScopeOutput
355 : kAudioDevicePropertyScopeInput,
356 &total_channel_count) &&
357 total_channel_count > kMaxConcurrentChannels) {
358 *channels = total_channel_count;
359 return true;
360 }
361
362 ScopedAudioUnit au(device, element);
363 if (!au.is_valid())
364 return false;
365
366 // Attempt to retrieve the channel layout from the AudioUnit.
367 //
368 // Note: We don't use kAudioDevicePropertyPreferredChannelLayout on the device
369 // because it is not available on all devices.
370 UInt32 size;
371 Boolean writable;
372 OSStatus result = AudioUnitGetPropertyInfo(
373 au.audio_unit(), kAudioUnitProperty_AudioChannelLayout,
374 kAudioUnitScope_Output, element, &size, &writable);
375 if (result != noErr) {
376 OSSTATUS_DLOG(ERROR, result)
377 << "Failed to get property info for AudioUnit channel layout.";
378 return false;
379 }
380
381 std::unique_ptr<uint8_t[]> layout_storage(new uint8_t[size]);
382 AudioChannelLayout* layout =
383 reinterpret_cast<AudioChannelLayout*>(layout_storage.get());
384
385 result = AudioUnitGetProperty(au.audio_unit(),
386 kAudioUnitProperty_AudioChannelLayout,
387 kAudioUnitScope_Output, element, layout, &size);
388 if (result != noErr) {
389 OSSTATUS_LOG(ERROR, result) << "Failed to get AudioUnit channel layout.";
390 return false;
391 }
392
393 // We don't want to have to know about all channel layout tags, so force OSX
394 // to give us the channel descriptions from the bitmap or tag if necessary.
395 const AudioChannelLayoutTag tag = layout->mChannelLayoutTag;
396 if (tag != kAudioChannelLayoutTag_UseChannelDescriptions) {
397 const bool is_bitmap = tag == kAudioChannelLayoutTag_UseChannelBitmap;
398 const AudioFormatPropertyID fa =
399 is_bitmap ? kAudioFormatProperty_ChannelLayoutForBitmap
400 : kAudioFormatProperty_ChannelLayoutForTag;
401
402 if (is_bitmap) {
403 result = AudioFormatGetPropertyInfo(fa, sizeof(UInt32),
404 &layout->mChannelBitmap, &size);
405 } else {
406 result = AudioFormatGetPropertyInfo(fa, sizeof(AudioChannelLayoutTag),
407 &tag, &size);
408 }
409 if (result != noErr || !size) {
410 OSSTATUS_DLOG(ERROR, result)
411 << "Failed to get AudioFormat property info, size=" << size;
412 return false;
413 }
414
415 layout_storage.reset(new uint8_t[size]);
416 layout = reinterpret_cast<AudioChannelLayout*>(layout_storage.get());
417 if (is_bitmap) {
418 result = AudioFormatGetProperty(fa, sizeof(UInt32),
419 &layout->mChannelBitmap, &size, layout);
420 } else {
421 result = AudioFormatGetProperty(fa, sizeof(AudioChannelLayoutTag), &tag,
422 &size, layout);
423 }
424 if (result != noErr) {
425 OSSTATUS_DLOG(ERROR, result) << "Failed to get AudioFormat property.";
426 return false;
427 }
428 }
429
430 // There is no channel info for stereo, assume so for mono as well.
431 if (layout->mNumberChannelDescriptions <= 2) {
432 *channels = layout->mNumberChannelDescriptions;
433 } else {
434 *channels = 0;
435 for (UInt32 i = 0; i < layout->mNumberChannelDescriptions; ++i) {
436 if (layout->mChannelDescriptions[i].mChannelLabel !=
437 kAudioChannelLabel_Unknown)
438 (*channels)++;
439 }
440 }
441
442 DVLOG(1) << (element == AUElement::OUTPUT ? "Output" : "Input")
443 << " channels: " << *channels;
444 return true;
445 }
446
336 class AudioManagerMac::AudioPowerObserver : public base::PowerObserver { 447 class AudioManagerMac::AudioPowerObserver : public base::PowerObserver {
337 public: 448 public:
338 AudioPowerObserver() 449 AudioPowerObserver()
339 : is_suspending_(false), 450 : is_suspending_(false),
340 is_monitoring_(base::PowerMonitor::Get()), 451 is_monitoring_(base::PowerMonitor::Get()),
341 num_resume_notifications_(0) { 452 num_resume_notifications_(0) {
342 // The PowerMonitor requires significant setup (a CFRunLoop and preallocated 453 // The PowerMonitor requires significant setup (a CFRunLoop and preallocated
343 // IO ports) so it's not available under unit tests. See the OSX impl of 454 // IO ports) so it's not available under unit tests. See the OSX impl of
344 // base::PowerMonitorDeviceSource for more details. 455 // base::PowerMonitorDeviceSource for more details.
345 if (!is_monitoring_) 456 if (!is_monitoring_)
(...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after
429 540
430 bool AudioManagerMac::HasAudioOutputDevices() { 541 bool AudioManagerMac::HasAudioOutputDevices() {
431 return HasAudioHardware(kAudioHardwarePropertyDefaultOutputDevice); 542 return HasAudioHardware(kAudioHardwarePropertyDefaultOutputDevice);
432 } 543 }
433 544
434 bool AudioManagerMac::HasAudioInputDevices() { 545 bool AudioManagerMac::HasAudioInputDevices() {
435 return HasAudioHardware(kAudioHardwarePropertyDefaultInputDevice); 546 return HasAudioHardware(kAudioHardwarePropertyDefaultInputDevice);
436 } 547 }
437 548
438 // static 549 // 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) { 550 int AudioManagerMac::HardwareSampleRateForDevice(AudioDeviceID device_id) {
528 DCHECK(AudioManager::Get()->GetTaskRunner()->BelongsToCurrentThread()); 551 DCHECK(AudioManager::Get()->GetTaskRunner()->BelongsToCurrentThread());
529 Float64 nominal_sample_rate; 552 Float64 nominal_sample_rate;
530 UInt32 info_size = sizeof(nominal_sample_rate); 553 UInt32 info_size = sizeof(nominal_sample_rate);
531 554
532 static const AudioObjectPropertyAddress kNominalSampleRateAddress = { 555 static const AudioObjectPropertyAddress kNominalSampleRateAddress = {
533 kAudioDevicePropertyNominalSampleRate, 556 kAudioDevicePropertyNominalSampleRate,
534 kAudioObjectPropertyScopeGlobal, 557 kAudioObjectPropertyScopeGlobal,
535 kAudioObjectPropertyElementMaster 558 kAudioObjectPropertyElementMaster
536 }; 559 };
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
570 DCHECK(device_names->empty()); 593 DCHECK(device_names->empty());
571 GetAudioDeviceInfo(false, device_names); 594 GetAudioDeviceInfo(false, device_names);
572 } 595 }
573 596
574 AudioParameters AudioManagerMac::GetInputStreamParameters( 597 AudioParameters AudioManagerMac::GetInputStreamParameters(
575 const std::string& device_id) { 598 const std::string& device_id) {
576 DCHECK(GetTaskRunner()->BelongsToCurrentThread()); 599 DCHECK(GetTaskRunner()->BelongsToCurrentThread());
577 AudioDeviceID device = GetAudioDeviceIdByUId(true, device_id); 600 AudioDeviceID device = GetAudioDeviceIdByUId(true, device_id);
578 if (device == kAudioObjectUnknown) { 601 if (device == kAudioObjectUnknown) {
579 DLOG(ERROR) << "Invalid device " << device_id; 602 DLOG(ERROR) << "Invalid device " << device_id;
580 return AudioParameters( 603 return AudioParameters(AudioParameters::AUDIO_PCM_LOW_LATENCY,
581 AudioParameters::AUDIO_PCM_LOW_LATENCY, CHANNEL_LAYOUT_STEREO, 604 CHANNEL_LAYOUT_STEREO, kFallbackSampleRate, 16,
582 kFallbackSampleRate, 16, ChooseBufferSize(true, kFallbackSampleRate)); 605 ChooseBufferSize(true, kFallbackSampleRate));
583 } 606 }
584 607
585 int channels = 0; 608 int channels = 0;
586 ChannelLayout channel_layout = CHANNEL_LAYOUT_STEREO; 609 ChannelLayout channel_layout = CHANNEL_LAYOUT_STEREO;
587 if (GetDeviceChannels(device, kAudioDevicePropertyScopeInput, &channels) && 610 if (GetDeviceChannels(device, AUElement::INPUT, &channels) && channels <= 2) {
588 channels <= 2) {
589 channel_layout = GuessChannelLayout(channels); 611 channel_layout = GuessChannelLayout(channels);
590 } else { 612 } else {
591 DLOG(ERROR) << "Failed to get the device channels, use stereo as default " 613 DLOG(ERROR) << "Failed to get the device channels, use stereo as default "
592 << "for device " << device_id; 614 << "for device " << device_id;
593 } 615 }
594 616
595 int sample_rate = HardwareSampleRateForDevice(device); 617 int sample_rate = HardwareSampleRateForDevice(device);
596 if (!sample_rate) 618 if (!sample_rate)
597 sample_rate = kFallbackSampleRate; 619 sample_rate = kFallbackSampleRate;
598 620
(...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 840 // 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. 841 // stream must be able to FIFO requests appropriately when this happens.
820 int buffer_size = ChooseBufferSize(false, hardware_sample_rate); 842 int buffer_size = ChooseBufferSize(false, hardware_sample_rate);
821 if (has_valid_input_params) { 843 if (has_valid_input_params) {
822 buffer_size = 844 buffer_size =
823 std::min(kMaximumInputOutputBufferSize, 845 std::min(kMaximumInputOutputBufferSize,
824 std::max(input_params.frames_per_buffer(), buffer_size)); 846 std::max(input_params.frames_per_buffer(), buffer_size));
825 } 847 }
826 848
827 int hardware_channels; 849 int hardware_channels;
828 if (!GetDeviceChannels(device, kAudioDevicePropertyScopeOutput, 850 if (!GetDeviceChannels(device, AUElement::OUTPUT, &hardware_channels))
829 &hardware_channels)) {
830 hardware_channels = 2; 851 hardware_channels = 2;
831 }
832 852
833 // Use the input channel count and channel layout if possible. Let OSX take 853 // 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 854 // care of remapping the channels; this lets user specified channel layouts
835 // work correctly. 855 // work correctly.
836 int output_channels = input_params.channels(); 856 int output_channels = input_params.channels();
837 ChannelLayout channel_layout = input_params.channel_layout(); 857 ChannelLayout channel_layout = input_params.channel_layout();
838 if (!has_valid_input_params || output_channels > hardware_channels) { 858 if (!has_valid_input_params || output_channels > hardware_channels) {
839 output_channels = hardware_channels; 859 output_channels = hardware_channels;
840 channel_layout = GuessChannelLayout(output_channels); 860 channel_layout = GuessChannelLayout(output_channels);
841 if (channel_layout == CHANNEL_LAYOUT_UNSUPPORTED) 861 if (channel_layout == CHANNEL_LAYOUT_UNSUPPORTED)
(...skipping 326 matching lines...) Expand 10 before | Expand all | Expand 10 after
1168 ScopedAudioManagerPtr CreateAudioManager( 1188 ScopedAudioManagerPtr CreateAudioManager(
1169 scoped_refptr<base::SingleThreadTaskRunner> task_runner, 1189 scoped_refptr<base::SingleThreadTaskRunner> task_runner,
1170 scoped_refptr<base::SingleThreadTaskRunner> worker_task_runner, 1190 scoped_refptr<base::SingleThreadTaskRunner> worker_task_runner,
1171 AudioLogFactory* audio_log_factory) { 1191 AudioLogFactory* audio_log_factory) {
1172 return ScopedAudioManagerPtr( 1192 return ScopedAudioManagerPtr(
1173 new AudioManagerMac(std::move(task_runner), std::move(worker_task_runner), 1193 new AudioManagerMac(std::move(task_runner), std::move(worker_task_runner),
1174 audio_log_factory)); 1194 audio_log_factory));
1175 } 1195 }
1176 1196
1177 } // namespace media 1197 } // namespace media
OLDNEW
« no previous file with comments | « media/audio/mac/audio_manager_mac.h ('k') | media/audio/mac/scoped_audio_unit.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698