diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2017-07-12 14:07:37 +0200 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2017-07-17 10:29:26 +0000 |
commit | ec02ee4181c49b61fce1c8fb99292dbb8139cc90 (patch) | |
tree | 25cde714b2b71eb639d1cd53f5a22e9ba76e14ef /chromium/media/audio | |
parent | bb09965444b5bb20b096a291445170876225268d (diff) | |
download | qtwebengine-chromium-ec02ee4181c49b61fce1c8fb99292dbb8139cc90.tar.gz |
BASELINE: Update Chromium to 59.0.3071.134
Change-Id: Id02ef6fb2204c5fd21668a1c3e6911c83b17585a
Reviewed-by: Alexandru Croitor <alexandru.croitor@qt.io>
Diffstat (limited to 'chromium/media/audio')
28 files changed, 1133 insertions, 484 deletions
diff --git a/chromium/media/audio/BUILD.gn b/chromium/media/audio/BUILD.gn index 11e618a4ee8..f7d9a5102ae 100644 --- a/chromium/media/audio/BUILD.gn +++ b/chromium/media/audio/BUILD.gn @@ -81,6 +81,8 @@ source_set("audio") { "audio_manager_base.h", "audio_output_controller.cc", "audio_output_controller.h", + "audio_output_delegate.cc", + "audio_output_delegate.h", "audio_output_device.cc", "audio_output_device.h", "audio_output_dispatcher.cc", @@ -160,6 +162,8 @@ source_set("audio") { "mac/audio_low_latency_input_mac.h", "mac/audio_manager_mac.cc", "mac/audio_manager_mac.h", + "mac/scoped_audio_unit.cc", + "mac/scoped_audio_unit.h", ] libs += [ "AudioToolbox.framework", diff --git a/chromium/media/audio/PRESUBMIT.py b/chromium/media/audio/PRESUBMIT.py index 327105aed48..1be756fe430 100644 --- a/chromium/media/audio/PRESUBMIT.py +++ b/chromium/media/audio/PRESUBMIT.py @@ -23,5 +23,6 @@ def PostUploadHook(cl, change, output_api): 'master.tryserver.chromium.linux:linux_optional_gpu_tests_rel', 'master.tryserver.chromium.mac:mac_optional_gpu_tests_rel', 'master.tryserver.chromium.win:win_optional_gpu_tests_rel', + 'master.tryserver.chromium.android:android_optional_gpu_tests_rel', ], 'Automatically added optional GPU tests to run on CQ.') diff --git a/chromium/media/audio/android/opensles_output.cc b/chromium/media/audio/android/opensles_output.cc index e6062362fad..d8eb72ff3b3 100644 --- a/chromium/media/audio/android/opensles_output.cc +++ b/chromium/media/audio/android/opensles_output.cc @@ -4,11 +4,13 @@ #include "media/audio/android/opensles_output.h" +#include "base/android/build_info.h" #include "base/logging.h" #include "base/macros.h" #include "base/time/time.h" #include "base/trace_event/trace_event.h" #include "media/audio/android/audio_manager_android.h" +#include "media/base/audio_sample_types.h" #include "media/base/audio_timestamp_helper.h" #define LOG_ON_FAILURE_AND_RETURN(op, ...) \ @@ -32,15 +34,38 @@ OpenSLESOutputStream::OpenSLESOutputStream(AudioManagerAndroid* manager, simple_buffer_queue_(NULL), audio_data_(), active_buffer_index_(0), - bytes_per_frame_(params.GetBytesPerFrame()), - buffer_size_bytes_(params.GetBytesPerBuffer()), started_(false), muted_(false), volume_(1.0), samples_per_second_(params.sample_rate()), + have_float_output_(base::android::BuildInfo::GetInstance()->sdk_int() >= + base::android::SDK_VERSION_LOLLIPOP), + bytes_per_frame_(have_float_output_ ? params.channels() * sizeof(float) + : params.GetBytesPerFrame()), + buffer_size_bytes_(have_float_output_ + ? bytes_per_frame_ * params.frames_per_buffer() + : params.GetBytesPerBuffer()), delay_calculator_(samples_per_second_) { DVLOG(2) << "OpenSLESOutputStream::OpenSLESOutputStream(" << "stream_type=" << stream_type << ")"; + + audio_bus_ = AudioBus::Create(params); + + if (have_float_output_) { + float_format_.formatType = SL_ANDROID_DATAFORMAT_PCM_EX; + float_format_.numChannels = static_cast<SLuint32>(params.channels()); + // Despite the name, this field is actually the sampling rate in millihertz. + float_format_.sampleRate = + static_cast<SLuint32>(samples_per_second_ * 1000); + float_format_.bitsPerSample = 32; + float_format_.containerSize = 32; + float_format_.endianness = SL_BYTEORDER_LITTLEENDIAN; + float_format_.channelMask = + ChannelCountToSLESChannelMask(params.channels()); + float_format_.representation = SL_ANDROID_PCM_REPRESENTATION_FLOAT; + return; + } + format_.formatType = SL_DATAFORMAT_PCM; format_.numChannels = static_cast<SLuint32>(params.channels()); // Despite the name, this field is actually the sampling rate in millihertz :| @@ -49,7 +74,6 @@ OpenSLESOutputStream::OpenSLESOutputStream(AudioManagerAndroid* manager, format_.containerSize = params.bits_per_sample(); format_.endianness = SL_BYTEORDER_LITTLEENDIAN; format_.channelMask = ChannelCountToSLESChannelMask(params.channels()); - audio_bus_ = AudioBus::Create(params); } OpenSLESOutputStream::~OpenSLESOutputStream() { @@ -241,7 +265,11 @@ bool OpenSLESOutputStream::CreatePlayer() { SLDataLocator_AndroidSimpleBufferQueue simple_buffer_queue = { SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, static_cast<SLuint32>(kMaxNumOfBuffersInQueue)}; - SLDataSource audio_source = {&simple_buffer_queue, &format_}; + SLDataSource audio_source; + if (have_float_output_) + audio_source = {&simple_buffer_queue, &float_format_}; + else + audio_source = {&simple_buffer_queue, &format_}; // Audio sink configuration. SLDataLocator_OutputMix locator_output_mix = {SL_DATALOCATOR_OUTPUTMIX, @@ -373,8 +401,14 @@ void OpenSLESOutputStream::FillBufferQueueNoLock() { // raw float, the data must be clipped and sanitized since it may come // from an untrusted source such as NaCl. audio_bus_->Scale(muted_ ? 0.0f : volume_); - audio_bus_->ToInterleaved(frames_filled, format_.bitsPerSample / 8, - audio_data_[active_buffer_index_]); + if (!have_float_output_) { + audio_bus_->ToInterleaved(frames_filled, format_.bitsPerSample / 8, + audio_data_[active_buffer_index_]); + } else { + audio_bus_->ToInterleaved<Float32SampleTypeTraits>( + frames_filled, + reinterpret_cast<float*>(audio_data_[active_buffer_index_])); + } delay_calculator_.AddFrames(frames_filled); const int num_filled_bytes = frames_filled * bytes_per_frame_; @@ -393,9 +427,8 @@ void OpenSLESOutputStream::FillBufferQueueNoLock() { void OpenSLESOutputStream::SetupAudioBuffer() { DCHECK(thread_checker_.CalledOnValidThread()); DCHECK(!audio_data_[0]); - for (int i = 0; i < kMaxNumOfBuffersInQueue; ++i) { + for (int i = 0; i < kMaxNumOfBuffersInQueue; ++i) audio_data_[i] = new uint8_t[buffer_size_bytes_]; - } } void OpenSLESOutputStream::ReleaseAudioBuffer() { diff --git a/chromium/media/audio/android/opensles_output.h b/chromium/media/audio/android/opensles_output.h index 671dbdec4a3..80d8929769a 100644 --- a/chromium/media/audio/android/opensles_output.h +++ b/chromium/media/audio/android/opensles_output.h @@ -21,6 +21,25 @@ #include "media/base/audio_parameters.h" #include "media/base/audio_timestamp_helper.h" +// On L+, we want to use floating point output for better fidelity. +#if __ANDROID_API__ < 21 +#define SL_ANDROID_PCM_REPRESENTATION_SIGNED_INT ((SLuint32)0x00000001) +#define SL_ANDROID_PCM_REPRESENTATION_UNSIGNED_INT ((SLuint32)0x00000002) +#define SL_ANDROID_PCM_REPRESENTATION_FLOAT ((SLuint32)0x00000003) +#define SL_ANDROID_DATAFORMAT_PCM_EX ((SLuint32)0x00000004) + +typedef struct SLAndroidDataFormat_PCM_EX_ { + SLuint32 formatType; + SLuint32 numChannels; + SLuint32 sampleRate; + SLuint32 bitsPerSample; + SLuint32 containerSize; + SLuint32 channelMask; + SLuint32 endianness; + SLuint32 representation; +} SLAndroidDataFormat_PCM_EX; +#endif + namespace media { class AudioManagerAndroid; @@ -101,14 +120,13 @@ class OpenSLESOutputStream : public AudioOutputStream { SLAndroidSimpleBufferQueueItf simple_buffer_queue_; SLDataFormat_PCM format_; + SLAndroidDataFormat_PCM_EX float_format_; // Audio buffers that are allocated during Open() based on parameters given // during construction. uint8_t* audio_data_[kMaxNumOfBuffersInQueue]; int active_buffer_index_; - int bytes_per_frame_; - size_t buffer_size_bytes_; bool started_; @@ -123,6 +141,12 @@ class OpenSLESOutputStream : public AudioOutputStream { int samples_per_second_; + // On Android 5.0+ we can output directly to float instead of in integer. + bool have_float_output_; + + int bytes_per_frame_; + size_t buffer_size_bytes_; + // Used to calculate the delay value for each OnMoreData() call. AudioTimestampHelper delay_calculator_; diff --git a/chromium/media/audio/audio_device_thread.cc b/chromium/media/audio/audio_device_thread.cc index 0a0d274a00d..f0aa11c64a2 100644 --- a/chromium/media/audio/audio_device_thread.cc +++ b/chromium/media/audio/audio_device_thread.cc @@ -7,6 +7,23 @@ #include <limits> #include "base/logging.h" +#include "base/sys_info.h" + +namespace { + +base::ThreadPriority GetAudioThreadPriority() { +#if defined(OS_CHROMEOS) + // On Chrome OS, there are priority inversion issues with having realtime + // threads on systems with only two cores, see crbug.com/710245. + return base::SysInfo::NumberOfProcessors() > 2 + ? base::ThreadPriority::REALTIME_AUDIO + : base::ThreadPriority::NORMAL; +#else + return base::ThreadPriority::REALTIME_AUDIO; +#endif +} + +} // namespace namespace media { @@ -47,8 +64,8 @@ AudioDeviceThread::AudioDeviceThread(Callback* callback, base::SyncSocket::Handle socket, const char* thread_name) : callback_(callback), thread_name_(thread_name), socket_(socket) { - CHECK(base::PlatformThread::CreateWithPriority( - 0, this, &thread_handle_, base::ThreadPriority::REALTIME_AUDIO)); + CHECK(base::PlatformThread::CreateWithPriority(0, this, &thread_handle_, + GetAudioThreadPriority())); DCHECK(!thread_handle_.is_null()); } diff --git a/chromium/media/audio/audio_features.cc b/chromium/media/audio/audio_features.cc index 5a5ead4dae9..a468e2f84b7 100644 --- a/chromium/media/audio/audio_features.cc +++ b/chromium/media/audio/audio_features.cc @@ -10,7 +10,7 @@ namespace features { // Allows experimentally enables mediaDevices.enumerateDevices() on ChromeOS. // Default disabled (crbug.com/554168). const base::Feature kEnumerateAudioDevices{"EnumerateAudioDevices", - base::FEATURE_DISABLED_BY_DEFAULT}; + base::FEATURE_ENABLED_BY_DEFAULT}; #endif } // namespace features diff --git a/chromium/media/audio/audio_manager.cc b/chromium/media/audio/audio_manager.cc index 4b98e36910f..9fb92703ee4 100644 --- a/chromium/media/audio/audio_manager.cc +++ b/chromium/media/audio/audio_manager.cc @@ -254,21 +254,19 @@ void AudioManagerDeleter::operator()(const AudioManager* instance) const { LOG(WARNING) << "Multiple instances of AudioManager detected"; } -#if defined(OS_MACOSX) - // If we are on Mac, tasks after this point are not executed, hence this is - // the only chance to delete the audio manager (which on Mac lives on the - // main browser thread instead of a dedicated audio thread). If we don't - // delete here, the CoreAudio thread can keep providing callbacks, which - // uses a state that is destroyed in ~BrowserMainLoop(). - // See http://crbug.com/623703 for more details. - DCHECK(instance->GetTaskRunner()->BelongsToCurrentThread()); - delete instance; -#else - // AudioManager must be destroyed on the audio thread. - if (!instance->GetTaskRunner()->DeleteSoon(FROM_HERE, instance)) { - LOG(WARNING) << "Failed to delete AudioManager instance."; + // The deleter runs on the main thread, and AudioManager must be destroyed on + // the audio thread. If the audio thread is the same as the main one, tasks + // after this point are not executed, hence this is the only chance to delete + // AudioManager. See http://crbug.com/623703 for more details. + if (instance->GetTaskRunner()->BelongsToCurrentThread()) { + delete instance; + return; } -#endif + + // AudioManager must be destroyed on the audio thread. See + // http://crbug.com/705455 for an existing AudioManager lifetime issue. + if (!instance->GetTaskRunner()->DeleteSoon(FROM_HERE, instance)) + LOG(WARNING) << "Failed to delete AudioManager instance."; } // Forward declaration of the platform specific AudioManager factory function. diff --git a/chromium/media/audio/audio_manager_base.cc b/chromium/media/audio/audio_manager_base.cc index 456adf7cc89..498aa1feb8a 100644 --- a/chromium/media/audio/audio_manager_base.cc +++ b/chromium/media/audio/audio_manager_base.cc @@ -29,11 +29,11 @@ const int kStreamCloseDelaySeconds = 5; // Default maximum number of output streams that can be open simultaneously // for all platforms. -const int kDefaultMaxOutputStreams = 32; +const int kDefaultMaxOutputStreams = 16; // Default maximum number of input streams that can be open simultaneously // for all platforms. -const int kDefaultMaxInputStreams = 32; +const int kDefaultMaxInputStreams = 16; const int kMaxInputChannels = 3; diff --git a/chromium/media/audio/audio_manager_unittest.cc b/chromium/media/audio/audio_manager_unittest.cc index 1af0db79965..1f67754b25c 100644 --- a/chromium/media/audio/audio_manager_unittest.cc +++ b/chromium/media/audio/audio_manager_unittest.cc @@ -354,8 +354,7 @@ class AudioManagerTest : public ::testing::Test { }; #if defined(USE_CRAS) -// TODO(warx): enable the test once crbug.com/554168 is fixed. -TEST_F(AudioManagerTest, DISABLED_EnumerateInputDevicesCras) { +TEST_F(AudioManagerTest, EnumerateInputDevicesCras) { // Setup the devices without internal mic, so that it doesn't exist // beamforming capable mic. AudioNodeList audio_nodes; @@ -384,8 +383,7 @@ TEST_F(AudioManagerTest, DISABLED_EnumerateInputDevicesCras) { CheckDeviceDescriptionsCras(device_descriptions, expectation); } -// TODO(warx): enable the test once crbug.com/554168 is fixed. -TEST_F(AudioManagerTest, DISABLED_EnumerateOutputDevicesCras) { +TEST_F(AudioManagerTest, EnumerateOutputDevicesCras) { // Setup the devices without internal mic, so that it doesn't exist // beamforming capable mic. AudioNodeList audio_nodes; diff --git a/chromium/media/audio/audio_output_delegate.cc b/chromium/media/audio/audio_output_delegate.cc new file mode 100644 index 00000000000..6ac2b2e4262 --- /dev/null +++ b/chromium/media/audio/audio_output_delegate.cc @@ -0,0 +1,11 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "audio_output_delegate.h" + +media::AudioOutputDelegate::EventHandler::EventHandler() {} +media::AudioOutputDelegate::EventHandler::~EventHandler() {} + +media::AudioOutputDelegate::AudioOutputDelegate() {} +media::AudioOutputDelegate::~AudioOutputDelegate() {} diff --git a/chromium/media/audio/audio_output_delegate.h b/chromium/media/audio/audio_output_delegate.h new file mode 100644 index 00000000000..3c121778c4f --- /dev/null +++ b/chromium/media/audio/audio_output_delegate.h @@ -0,0 +1,65 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MEDIA_AUDIO_AUDIO_OUTPUT_DELEGATE_H_ +#define MEDIA_AUDIO_AUDIO_OUTPUT_DELEGATE_H_ + +#include <memory> +#include <string> + +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "media/base/media_export.h" + +namespace base { +class SharedMemory; +class CancelableSyncSocket; +} + +namespace media { + +class AudioOutputController; + +class MEDIA_EXPORT AudioOutputDelegate { + public: + // An AudioOutputDelegate must not call back to its EventHandler in its + // constructor. + class MEDIA_EXPORT EventHandler { + public: + EventHandler(); + virtual ~EventHandler(); + + // Called when construction is finished and the stream is ready for + // playout. + virtual void OnStreamCreated( + int stream_id, + base::SharedMemory* shared_memory, + std::unique_ptr<base::CancelableSyncSocket> socket) = 0; + + // Called if stream encounters an error and has become unusable. + virtual void OnStreamError(int stream_id) = 0; + }; + + AudioOutputDelegate(); + virtual ~AudioOutputDelegate(); + + // TODO(maxmorin): Remove GetController() when crbug.com/647185 is closed. + // This function is used to provide control of the audio stream to + // WebrtcAudioPrivateGetActiveSinkFunction and others in the webrtc extension + // API. Since the controller is shared, this means that it might outlive the + // AudioOutputDelegate. In this case, it is still safe to call functions on + // the controller, but it will not do anything. The controller is also shared + // with AudioStreamMonitor. + virtual scoped_refptr<AudioOutputController> GetController() const = 0; + virtual int GetStreamId() const = 0; + + // Stream control: + virtual void OnPlayStream() = 0; + virtual void OnPauseStream() = 0; + virtual void OnSetVolume(double volume) = 0; +}; + +} // namespace media + +#endif // MEDIA_AUDIO_AUDIO_OUTPUT_DELEGATE_H_ diff --git a/chromium/media/audio/audio_system.h b/chromium/media/audio/audio_system.h index a11b030c742..932151ed3b9 100644 --- a/chromium/media/audio/audio_system.h +++ b/chromium/media/audio/audio_system.h @@ -6,9 +6,14 @@ #define MEDIA_AUDIO_AUDIO_SYSTEM_H_ #include "base/callback.h" +#include "media/audio/audio_device_description.h" #include "media/base/audio_parameters.h" #include "media/base/media_export.h" +namespace base { +class SingleThreadTaskRunner; +} + namespace media { class AudioManager; @@ -17,23 +22,66 @@ class AudioManager; // to Mojo audio service. class MEDIA_EXPORT AudioSystem { public: - // Replies are asynchronously sent to the thread the call is issued on. + // Replies are asynchronously sent from audio system thread to the thread the + // call is issued on. Attention! Audio system thread may outlive the client + // objects; bind callbacks with care. using OnAudioParamsCallback = base::Callback<void(const AudioParameters&)>; using OnBoolCallback = base::Callback<void(bool)>; + using OnDeviceDescriptionsCallback = + base::Callback<void(AudioDeviceDescriptions)>; + using OnDeviceIdCallback = base::Callback<void(const std::string&)>; + using OnInputDeviceInfoCallback = base::Callback< + void(const AudioParameters&, const AudioParameters&, const std::string&)>; + // Must not be called on audio system thread if it differs from the one + // AudioSystem is destroyed on. See http://crbug.com/705455. static AudioSystem* Get(); virtual ~AudioSystem(); - // Callback will receive invalid parameters if the device is not found. + // Callback may receive invalid parameters, it means the specified device is + // not found. This is best-effort: valid parameters do not guarantee existance + // of the device. + // TODO(olka,tommi): fix all AudioManager implementations to return invalid + // parameters if the device is not found. virtual void GetInputStreamParameters( const std::string& device_id, OnAudioParamsCallback on_params_cb) const = 0; + // If media::AudioDeviceDescription::IsDefaultDevice(device_id) is true, + // callback will receive the parameters of the default output device. + // Callback may receive invalid parameters, it means the specified device is + // not found. This is best-effort: valid parameters do not guarantee existance + // of the device. + // TODO(olka,tommi): fix all AudioManager implementations to return invalid + // parameters if the device is not found. + virtual void GetOutputStreamParameters( + const std::string& device_id, + OnAudioParamsCallback on_params_cb) const = 0; + virtual void HasInputDevices(OnBoolCallback on_has_devices_cb) const = 0; - // Must not be used for anything but stream creation. - virtual AudioManager* GetAudioManager() const = 0; + virtual void HasOutputDevices(OnBoolCallback on_has_devices_cb) const = 0; + + // Replies with device descriptions of input audio devices if |for_input| is + // true, and of output audio devices otherwise. + virtual void GetDeviceDescriptions( + OnDeviceDescriptionsCallback on_descriptions_cb, + bool for_input) = 0; + + // Replies with an empty string if there is no associated output device found. + virtual void GetAssociatedOutputDeviceID( + const std::string& input_device_id, + OnDeviceIdCallback on_device_id_cb) = 0; + + // Replies with audio parameters for the specified input device and audio + // parameters and device ID of the associated output device, if any (otherwise + // it's AudioParameters() and an empty string). + virtual void GetInputDeviceInfo( + const std::string& input_device_id, + OnInputDeviceInfoCallback on_input_device_info_cb) = 0; + + virtual base::SingleThreadTaskRunner* GetTaskRunner() const = 0; protected: // Sets the global AudioSystem pointer to the specified non-null value. diff --git a/chromium/media/audio/audio_system_impl.cc b/chromium/media/audio/audio_system_impl.cc index fdffa70fda0..9d009aa5079 100644 --- a/chromium/media/audio/audio_system_impl.cc +++ b/chromium/media/audio/audio_system_impl.cc @@ -7,7 +7,9 @@ #include "base/memory/ptr_util.h" #include "base/single_thread_task_runner.h" #include "base/task_runner_util.h" +#include "media/audio/audio_device_description.h" #include "media/audio/audio_manager.h" +#include "media/base/bind_to_current_loop.h" // Using base::Unretained for |audio_manager_| is safe since it is deleted after // its task runner, and AudioSystemImpl is deleted on the UI thread after the IO @@ -21,13 +23,58 @@ AudioParameters GetInputParametersOnDeviceThread(AudioManager* audio_manager, DCHECK(audio_manager->GetTaskRunner()->BelongsToCurrentThread()); // TODO(olka): remove this when AudioManager::GetInputStreamParameters() - // works this way on all the platforms. + // returns invalid parameters if the device is not found. if (!audio_manager->HasAudioInputDevices()) return AudioParameters(); return audio_manager->GetInputStreamParameters(device_id); } +AudioParameters GetOutputParametersOnDeviceThread( + AudioManager* audio_manager, + const std::string& device_id) { + DCHECK(audio_manager->GetTaskRunner()->BelongsToCurrentThread()); + + // TODO(olka): remove this when + // AudioManager::Get[Default]OutputStreamParameters() returns invalid + // parameters if the device is not found. + if (!audio_manager->HasAudioOutputDevices()) + return AudioParameters(); + + return media::AudioDeviceDescription::IsDefaultDevice(device_id) + ? audio_manager->GetDefaultOutputStreamParameters() + : audio_manager->GetOutputStreamParameters(device_id); +} + +AudioDeviceDescriptions GetDeviceDescriptionsOnDeviceThread( + AudioManager* audio_manager, + bool for_input) { + DCHECK(audio_manager->GetTaskRunner()->BelongsToCurrentThread()); + AudioDeviceDescriptions descriptions; + if (for_input) + audio_manager->GetAudioInputDeviceDescriptions(&descriptions); + else + audio_manager->GetAudioOutputDeviceDescriptions(&descriptions); + return descriptions; +} + +void GetInputDeviceInfoOnDeviceThread( + AudioManager* audio_manager, + const std::string& input_device_id, + AudioSystem::OnInputDeviceInfoCallback on_input_device_info_cb) { + DCHECK(audio_manager->GetTaskRunner()->BelongsToCurrentThread()); + const std::string associated_output_device_id = + audio_manager->GetAssociatedOutputDeviceID(input_device_id); + + on_input_device_info_cb.Run( + GetInputParametersOnDeviceThread(audio_manager, input_device_id), + associated_output_device_id.empty() + ? AudioParameters() + : GetOutputParametersOnDeviceThread(audio_manager, + associated_output_device_id), + associated_output_device_id); +} + } // namespace AudioSystemImpl::AudioSystemImpl(AudioManager* audio_manager) @@ -62,6 +109,22 @@ void AudioSystemImpl::GetInputStreamParameters( std::move(on_params_cb)); } +void AudioSystemImpl::GetOutputStreamParameters( + const std::string& device_id, + OnAudioParamsCallback on_params_cb) const { + if (GetTaskRunner()->BelongsToCurrentThread()) { + GetTaskRunner()->PostTask( + FROM_HERE, base::Bind(on_params_cb, GetOutputParametersOnDeviceThread( + audio_manager_, device_id))); + return; + } + base::PostTaskAndReplyWithResult( + GetTaskRunner(), FROM_HERE, + base::Bind(&GetOutputParametersOnDeviceThread, + base::Unretained(audio_manager_), device_id), + std::move(on_params_cb)); +} + void AudioSystemImpl::HasInputDevices(OnBoolCallback on_has_devices_cb) const { if (GetTaskRunner()->BelongsToCurrentThread()) { GetTaskRunner()->PostTask( @@ -76,8 +139,69 @@ void AudioSystemImpl::HasInputDevices(OnBoolCallback on_has_devices_cb) const { std::move(on_has_devices_cb)); } -AudioManager* AudioSystemImpl::GetAudioManager() const { - return audio_manager_; +void AudioSystemImpl::HasOutputDevices(OnBoolCallback on_has_devices_cb) const { + if (GetTaskRunner()->BelongsToCurrentThread()) { + GetTaskRunner()->PostTask( + FROM_HERE, + base::Bind(on_has_devices_cb, audio_manager_->HasAudioOutputDevices())); + return; + } + base::PostTaskAndReplyWithResult( + GetTaskRunner(), FROM_HERE, + base::Bind(&AudioManager::HasAudioOutputDevices, + base::Unretained(audio_manager_)), + std::move(on_has_devices_cb)); +} + +void AudioSystemImpl::GetDeviceDescriptions( + OnDeviceDescriptionsCallback on_descriptions_cb, + bool for_input) { + if (GetTaskRunner()->BelongsToCurrentThread()) { + GetTaskRunner()->PostTask( + FROM_HERE, base::Bind(on_descriptions_cb, + base::Passed(GetDeviceDescriptionsOnDeviceThread( + audio_manager_, for_input)))); + return; + } + + base::PostTaskAndReplyWithResult( + GetTaskRunner(), FROM_HERE, + base::Bind(&GetDeviceDescriptionsOnDeviceThread, + base::Unretained(audio_manager_), for_input), + std::move(on_descriptions_cb)); +} + +void AudioSystemImpl::GetAssociatedOutputDeviceID( + const std::string& input_device_id, + OnDeviceIdCallback on_device_id_cb) { + if (GetTaskRunner()->BelongsToCurrentThread()) { + GetTaskRunner()->PostTask( + FROM_HERE, + base::Bind(on_device_id_cb, audio_manager_->GetAssociatedOutputDeviceID( + input_device_id))); + return; + } + base::PostTaskAndReplyWithResult( + GetTaskRunner(), FROM_HERE, + base::Bind(&AudioManager::GetAssociatedOutputDeviceID, + base::Unretained(audio_manager_), input_device_id), + std::move(on_device_id_cb)); +} + +void AudioSystemImpl::GetInputDeviceInfo( + const std::string& input_device_id, + OnInputDeviceInfoCallback on_input_device_info_cb) { + // No need to bind |on_input_device_info_cb| to the current loop if we are on + // the audio thread. However, the client still expect to receive the reply + // asynchronously, so we always post GetInputDeviceInfoOnDeviceThread(), which + // will syncronously call the (bound to current loop or not) callback. + GetTaskRunner()->PostTask( + FROM_HERE, base::Bind(&GetInputDeviceInfoOnDeviceThread, + base::Unretained(audio_manager_), input_device_id, + GetTaskRunner()->BelongsToCurrentThread() + ? std::move(on_input_device_info_cb) + : media::BindToCurrentLoop( + std::move(on_input_device_info_cb)))); } base::SingleThreadTaskRunner* AudioSystemImpl::GetTaskRunner() const { diff --git a/chromium/media/audio/audio_system_impl.h b/chromium/media/audio/audio_system_impl.h index e7efd10c02e..10884d08afb 100644 --- a/chromium/media/audio/audio_system_impl.h +++ b/chromium/media/audio/audio_system_impl.h @@ -24,15 +24,31 @@ class MEDIA_EXPORT AudioSystemImpl : public AudioSystem { void GetInputStreamParameters( const std::string& device_id, OnAudioParamsCallback on_params_cb) const override; + + void GetOutputStreamParameters( + const std::string& device_id, + OnAudioParamsCallback on_params_cb) const override; + void HasInputDevices(OnBoolCallback on_has_devices_cb) const override; - AudioManager* GetAudioManager() const override; + + void HasOutputDevices(OnBoolCallback on_has_devices_cb) const override; + + void GetDeviceDescriptions(OnDeviceDescriptionsCallback on_descriptions_cp, + bool for_input) override; + + void GetAssociatedOutputDeviceID(const std::string& input_device_id, + OnDeviceIdCallback on_device_id_cb) override; + + void GetInputDeviceInfo( + const std::string& input_device_id, + OnInputDeviceInfoCallback on_input_device_info_cb) override; + + base::SingleThreadTaskRunner* GetTaskRunner() const override; protected: AudioSystemImpl(AudioManager* audio_manager); private: - base::SingleThreadTaskRunner* GetTaskRunner() const; - AudioManager* const audio_manager_; DISALLOW_COPY_AND_ASSIGN(AudioSystemImpl); diff --git a/chromium/media/audio/audio_system_impl_unittest.cc b/chromium/media/audio/audio_system_impl_unittest.cc index 04f6f645a67..618bf9298e7 100644 --- a/chromium/media/audio/audio_system_impl_unittest.cc +++ b/chromium/media/audio/audio_system_impl_unittest.cc @@ -16,12 +16,38 @@ #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" +namespace { +const char* kNonDefaultDeviceId = "non-default-device-id"; +} + namespace media { +bool operator==(const media::AudioDeviceDescription& lhs, + const media::AudioDeviceDescription& rhs) { + return lhs.device_name == rhs.device_name && lhs.unique_id == rhs.unique_id && + lhs.group_id == rhs.group_id; +} + class AudioSystemImplTest : public testing::TestWithParam<bool> { public: AudioSystemImplTest() - : use_audio_thread_(GetParam()), audio_thread_("AudioSystemThread") { + : use_audio_thread_(GetParam()), + audio_thread_("AudioSystemThread"), + input_params_(AudioParameters::AUDIO_PCM_LINEAR, + CHANNEL_LAYOUT_MONO, + AudioParameters::kTelephoneSampleRate, + 16, + AudioParameters::kTelephoneSampleRate / 10), + output_params_(AudioParameters::AUDIO_PCM_LINEAR, + CHANNEL_LAYOUT_MONO, + AudioParameters::kTelephoneSampleRate, + 16, + AudioParameters::kTelephoneSampleRate / 20), + default_output_params_(AudioParameters::AUDIO_PCM_LINEAR, + CHANNEL_LAYOUT_MONO, + AudioParameters::kTelephoneSampleRate, + 16, + AudioParameters::kTelephoneSampleRate / 30) { if (use_audio_thread_) { audio_thread_.StartAndWaitForTesting(); audio_manager_.reset( @@ -30,8 +56,23 @@ class AudioSystemImplTest : public testing::TestWithParam<bool> { audio_manager_.reset(new media::MockAudioManager( base::ThreadTaskRunnerHandle::Get().get())); } - audio_manager_->SetInputStreamParameters( - media::AudioParameters::UnavailableDeviceParams()); + + audio_manager_->SetInputStreamParameters(input_params_); + audio_manager_->SetOutputStreamParameters(output_params_); + audio_manager_->SetDefaultOutputStreamParameters(default_output_params_); + + auto get_device_descriptions = [](const AudioDeviceDescriptions* source, + AudioDeviceDescriptions* destination) { + destination->insert(destination->end(), source->begin(), source->end()); + }; + + audio_manager_->SetInputDeviceDescriptionsCallback( + base::Bind(get_device_descriptions, + base::Unretained(&input_device_descriptions_))); + audio_manager_->SetOutputDeviceDescriptionsCallback( + base::Bind(get_device_descriptions, + base::Unretained(&output_device_descriptions_))); + audio_system_ = media::AudioSystemImpl::Create(audio_manager_.get()); EXPECT_EQ(AudioSystem::Get(), audio_system_.get()); } @@ -52,12 +93,45 @@ class AudioSystemImplTest : public testing::TestWithParam<bool> { AudioParametersReceived(); } + void OnHasInputDevices(bool result) { + EXPECT_TRUE(thread_checker_.CalledOnValidThread()); + HasInputDevicesCallback(result); + } + + void OnHasOutputDevices(bool result) { + EXPECT_TRUE(thread_checker_.CalledOnValidThread()); + HasOutputDevicesCallback(result); + } + + void OnGetDeviceDescriptions( + const AudioDeviceDescriptions& expected_descriptions, + AudioDeviceDescriptions descriptions) { + EXPECT_TRUE(thread_checker_.CalledOnValidThread()); + EXPECT_EQ(expected_descriptions, descriptions); + DeviceDescriptionsReceived(); + } + + void OnInputDeviceInfo(const AudioParameters& expected_input, + const AudioParameters& expected_associated_output, + const std::string& expected_associated_device_id, + const AudioParameters& input, + const AudioParameters& associated_output, + const std::string& associated_device_id) { + EXPECT_TRUE(thread_checker_.CalledOnValidThread()); + EXPECT_EQ(expected_input.AsHumanReadableString(), + input.AsHumanReadableString()); + EXPECT_EQ(expected_associated_output.AsHumanReadableString(), + associated_output.AsHumanReadableString()); + EXPECT_EQ(expected_associated_device_id, associated_device_id); + InputDeviceInfoReceived(); + } + void WaitForCallback() { if (!use_audio_thread_) { base::RunLoop().RunUntilIdle(); return; } - media::WaitableMessageLoopEvent event; + WaitableMessageLoopEvent event; audio_thread_.task_runner()->PostTaskAndReply( FROM_HERE, base::Bind(&base::DoNothing), event.GetClosure()); // Runs the loop and waits for the |audio_thread_| to call event's closure, @@ -67,8 +141,13 @@ class AudioSystemImplTest : public testing::TestWithParam<bool> { base::RunLoop().RunUntilIdle(); } + // Mocks to verify that AudioSystem replied with an expected callback. MOCK_METHOD0(AudioParametersReceived, void(void)); MOCK_METHOD1(HasInputDevicesCallback, void(bool)); + MOCK_METHOD1(HasOutputDevicesCallback, void(bool)); + MOCK_METHOD0(DeviceDescriptionsReceived, void(void)); + MOCK_METHOD1(AssociatedOutputDeviceIDReceived, void(const std::string&)); + MOCK_METHOD0(InputDeviceInfoReceived, void(void)); protected: base::MessageLoop message_loop_; @@ -77,6 +156,11 @@ class AudioSystemImplTest : public testing::TestWithParam<bool> { base::Thread audio_thread_; MockAudioManager::UniquePtr audio_manager_; std::unique_ptr<media::AudioSystem> audio_system_; + AudioParameters input_params_; + AudioParameters output_params_; + AudioParameters default_output_params_; + AudioDeviceDescriptions input_device_descriptions_; + AudioDeviceDescriptions output_device_descriptions_; }; TEST_P(AudioSystemImplTest, GetInputStreamParameters) { @@ -84,7 +168,7 @@ TEST_P(AudioSystemImplTest, GetInputStreamParameters) { audio_system_->GetInputStreamParameters( media::AudioDeviceDescription::kDefaultDeviceId, base::Bind(&AudioSystemImplTest::OnAudioParams, base::Unretained(this), - media::AudioParameters::UnavailableDeviceParams())); + input_params_)); WaitForCallback(); } @@ -98,10 +182,44 @@ TEST_P(AudioSystemImplTest, GetInputStreamParametersNoDevice) { WaitForCallback(); } +TEST_P(AudioSystemImplTest, GetOutputStreamParameters) { + EXPECT_CALL(*this, AudioParametersReceived()); + audio_system_->GetOutputStreamParameters( + kNonDefaultDeviceId, base::Bind(&AudioSystemImplTest::OnAudioParams, + base::Unretained(this), output_params_)); + WaitForCallback(); +} + +TEST_P(AudioSystemImplTest, GetDefaultOutputStreamParameters) { + EXPECT_CALL(*this, AudioParametersReceived()); + audio_system_->GetOutputStreamParameters( + media::AudioDeviceDescription::kDefaultDeviceId, + base::Bind(&AudioSystemImplTest::OnAudioParams, base::Unretained(this), + default_output_params_)); + WaitForCallback(); +} + +TEST_P(AudioSystemImplTest, GetOutputStreamParametersNoDevice) { + audio_manager_->SetHasOutputDevices(false); + EXPECT_CALL(*this, AudioParametersReceived()).Times(2); + + audio_system_->GetOutputStreamParameters( + media::AudioDeviceDescription::kDefaultDeviceId, + base::Bind(&AudioSystemImplTest::OnAudioParams, base::Unretained(this), + media::AudioParameters())); + WaitForCallback(); + + audio_system_->GetOutputStreamParameters( + kNonDefaultDeviceId, + base::Bind(&AudioSystemImplTest::OnAudioParams, base::Unretained(this), + media::AudioParameters())); + WaitForCallback(); +} + TEST_P(AudioSystemImplTest, HasInputDevices) { EXPECT_CALL(*this, HasInputDevicesCallback(true)); audio_system_->HasInputDevices(base::Bind( - &AudioSystemImplTest::HasInputDevicesCallback, base::Unretained(this))); + &AudioSystemImplTest::OnHasInputDevices, base::Unretained(this))); WaitForCallback(); } @@ -109,7 +227,124 @@ TEST_P(AudioSystemImplTest, HasNoInputDevices) { audio_manager_->SetHasInputDevices(false); EXPECT_CALL(*this, HasInputDevicesCallback(false)); audio_system_->HasInputDevices(base::Bind( - &AudioSystemImplTest::HasInputDevicesCallback, base::Unretained(this))); + &AudioSystemImplTest::OnHasInputDevices, base::Unretained(this))); + WaitForCallback(); +} + +TEST_P(AudioSystemImplTest, HasOutputDevices) { + EXPECT_CALL(*this, HasOutputDevicesCallback(true)); + audio_system_->HasOutputDevices(base::Bind( + &AudioSystemImplTest::OnHasOutputDevices, base::Unretained(this))); + WaitForCallback(); +} + +TEST_P(AudioSystemImplTest, HasNoOutputDevices) { + audio_manager_->SetHasOutputDevices(false); + EXPECT_CALL(*this, HasOutputDevicesCallback(false)); + audio_system_->HasOutputDevices(base::Bind( + &AudioSystemImplTest::OnHasOutputDevices, base::Unretained(this))); + WaitForCallback(); +} + +TEST_P(AudioSystemImplTest, GetInputDeviceDescriptionsNoInputDevices) { + output_device_descriptions_.emplace_back("output_device_name", + "output_device_id", "group_id"); + EXPECT_EQ(0, static_cast<int>(input_device_descriptions_.size())); + EXPECT_EQ(1, static_cast<int>(output_device_descriptions_.size())); + EXPECT_CALL(*this, DeviceDescriptionsReceived()); + audio_system_->GetDeviceDescriptions( + base::Bind(&AudioSystemImplTest::OnGetDeviceDescriptions, + base::Unretained(this), input_device_descriptions_), + true); + WaitForCallback(); +} + +TEST_P(AudioSystemImplTest, GetInputDeviceDescriptions) { + output_device_descriptions_.emplace_back("output_device_name", + "output_device_id", "group_id"); + input_device_descriptions_.emplace_back("input_device_name1", + "input_device_id1", "group_id1"); + input_device_descriptions_.emplace_back("input_device_name2", + "input_device_id2", "group_id2"); + EXPECT_EQ(2, static_cast<int>(input_device_descriptions_.size())); + EXPECT_EQ(1, static_cast<int>(output_device_descriptions_.size())); + EXPECT_CALL(*this, DeviceDescriptionsReceived()); + audio_system_->GetDeviceDescriptions( + base::Bind(&AudioSystemImplTest::OnGetDeviceDescriptions, + base::Unretained(this), input_device_descriptions_), + true); + WaitForCallback(); +} + +TEST_P(AudioSystemImplTest, GetOutputDeviceDescriptionsNoInputDevices) { + input_device_descriptions_.emplace_back("input_device_name", + "input_device_id", "group_id"); + EXPECT_EQ(0, static_cast<int>(output_device_descriptions_.size())); + EXPECT_EQ(1, static_cast<int>(input_device_descriptions_.size())); + EXPECT_CALL(*this, DeviceDescriptionsReceived()); + audio_system_->GetDeviceDescriptions( + base::Bind(&AudioSystemImplTest::OnGetDeviceDescriptions, + base::Unretained(this), output_device_descriptions_), + false); + WaitForCallback(); +} + +TEST_P(AudioSystemImplTest, GetOutputDeviceDescriptions) { + input_device_descriptions_.emplace_back("input_device_name", + "input_device_id", "group_id"); + output_device_descriptions_.emplace_back("output_device_name1", + "output_device_id1", "group_id1"); + output_device_descriptions_.emplace_back("output_device_name2", + "output_device_id2", "group_id2"); + EXPECT_EQ(2, static_cast<int>(output_device_descriptions_.size())); + EXPECT_EQ(1, static_cast<int>(input_device_descriptions_.size())); + EXPECT_CALL(*this, DeviceDescriptionsReceived()); + audio_system_->GetDeviceDescriptions( + base::Bind(&AudioSystemImplTest::OnGetDeviceDescriptions, + base::Unretained(this), output_device_descriptions_), + false); + WaitForCallback(); +} + +TEST_P(AudioSystemImplTest, GetAssociatedOutputDeviceID) { + const std::string associated_id("associated_id"); + audio_manager_->SetAssociatedOutputDeviceIDCallback( + base::Bind([](const std::string& result, + const std::string&) -> std::string { return result; }, + associated_id)); + + EXPECT_CALL(*this, AssociatedOutputDeviceIDReceived(associated_id)); + + audio_system_->GetAssociatedOutputDeviceID( + std::string(), + base::Bind(&AudioSystemImplTest::AssociatedOutputDeviceIDReceived, + base::Unretained(this))); + WaitForCallback(); +} + +TEST_P(AudioSystemImplTest, GetInputDeviceInfoNoAssociation) { + EXPECT_CALL(*this, InputDeviceInfoReceived()); + + audio_system_->GetInputDeviceInfo( + kNonDefaultDeviceId, base::Bind(&AudioSystemImplTest::OnInputDeviceInfo, + base::Unretained(this), input_params_, + AudioParameters(), std::string())); + WaitForCallback(); +} + +TEST_P(AudioSystemImplTest, GetInputDeviceInfoWithAssociation) { + EXPECT_CALL(*this, InputDeviceInfoReceived()); + + const std::string associated_id("associated_id"); + audio_manager_->SetAssociatedOutputDeviceIDCallback( + base::Bind([](const std::string& result, + const std::string&) -> std::string { return result; }, + associated_id)); + + audio_system_->GetInputDeviceInfo( + kNonDefaultDeviceId, base::Bind(&AudioSystemImplTest::OnInputDeviceInfo, + base::Unretained(this), input_params_, + output_params_, associated_id)); WaitForCallback(); } diff --git a/chromium/media/audio/cras/audio_manager_cras.cc b/chromium/media/audio/cras/audio_manager_cras.cc index 65a2f66d43d..118d384200d 100644 --- a/chromium/media/audio/cras/audio_manager_cras.cc +++ b/chromium/media/audio/cras/audio_manager_cras.cc @@ -16,6 +16,7 @@ #include "base/nix/xdg_util.h" #include "base/stl_util.h" #include "base/strings/string_number_conversions.h" +#include "base/sys_info.h" #include "chromeos/audio/audio_device.h" #include "chromeos/audio/cras_audio_handler.h" #include "media/audio/audio_device_description.h" @@ -49,6 +50,10 @@ const int kDefaultInputBufferSize = 1024; const char kBeamformingOnDeviceId[] = "default-beamforming-on"; const char kBeamformingOffDeviceId[] = "default-beamforming-off"; +const char kInternalInputVirtualDevice[] = "Built-in mic"; +const char kInternalOutputVirtualDevice[] = "Built-in speaker"; +const char kHeadphoneLineOutVirtualDevice[] = "Headphone/Line Out"; + enum CrosBeamformingDeviceState { BEAMFORMING_DEFAULT_ENABLED = 0, BEAMFORMING_USER_ENABLED, @@ -83,6 +88,27 @@ std::string MicPositions() { return ""; } +// Process |device_list| that two shares the same dev_index by creating a +// virtual device name for them. +void ProcessVirtualDeviceName(AudioDeviceNames* device_names, + const chromeos::AudioDeviceList& device_list) { + DCHECK_EQ(2, device_list.size()); + if (device_list[0].type == chromeos::AUDIO_TYPE_LINEOUT || + device_list[1].type == chromeos::AUDIO_TYPE_LINEOUT) { + device_names->emplace_back(kHeadphoneLineOutVirtualDevice, + base::Uint64ToString(device_list[0].id)); + } else if (device_list[0].type == chromeos::AUDIO_TYPE_INTERNAL_SPEAKER || + device_list[1].type == chromeos::AUDIO_TYPE_INTERNAL_SPEAKER) { + device_names->emplace_back(kInternalOutputVirtualDevice, + base::Uint64ToString(device_list[0].id)); + } else { + DCHECK(device_list[0].type == chromeos::AUDIO_TYPE_INTERNAL_MIC || + device_list[1].type == chromeos::AUDIO_TYPE_INTERNAL_MIC); + device_names->emplace_back(kInternalInputVirtualDevice, + base::Uint64ToString(device_list[0].id)); + } +} + } // namespace // Adds the beamforming on and off devices to |device_names|. @@ -164,10 +190,25 @@ void AudioManagerCras::GetAudioDeviceNamesImpl(bool is_input, if (base::FeatureList::IsEnabled(features::kEnumerateAudioDevices)) { chromeos::AudioDeviceList devices; chromeos::CrasAudioHandler::Get()->GetAudioDevices(&devices); + + // |dev_idx_map| is a map of dev_index and their audio devices. + std::map<int, chromeos::AudioDeviceList> dev_idx_map; for (const auto& device : devices) { - if (device.is_input == is_input && device.is_for_simple_usage()) { + if (device.is_input != is_input || !device.is_for_simple_usage()) + continue; + + dev_idx_map[dev_index_of(device.id)].push_back(device); + } + + for (const auto& item : dev_idx_map) { + if (1 == item.second.size()) { + const chromeos::AudioDevice& device = item.second.front(); device_names->emplace_back(device.display_name, base::Uint64ToString(device.id)); + } else { + // Create virtual device name for audio nodes that share the same device + // index. + ProcessVirtualDeviceName(device_names, item.second); } } } @@ -265,12 +306,23 @@ AudioInputStream* AudioManagerCras::MakeLowLatencyInputStream( return MakeInputStream(params, device_id); } +int AudioManagerCras::GetMinimumOutputBufferSizePerBoard() { + // On faster boards we can use smaller buffer size for lower latency. + // On slower boards we should use larger buffer size to prevent underrun. + std::string board = base::SysInfo::GetLsbReleaseBoard(); + if (board == "kevin") + return 768; + else if (board == "samus") + return 256; + return kMinimumOutputBufferSize; +} + AudioParameters AudioManagerCras::GetPreferredOutputStreamParameters( const std::string& output_device_id, const AudioParameters& input_params) { ChannelLayout channel_layout = CHANNEL_LAYOUT_STEREO; int sample_rate = kDefaultSampleRate; - int buffer_size = kMinimumOutputBufferSize; + int buffer_size = GetMinimumOutputBufferSizePerBoard(); int bits_per_sample = 16; if (input_params.IsValid()) { sample_rate = input_params.sample_rate(); diff --git a/chromium/media/audio/cras/audio_manager_cras.h b/chromium/media/audio/cras/audio_manager_cras.h index e5a4b5b9e04..e50ad242279 100644 --- a/chromium/media/audio/cras/audio_manager_cras.h +++ b/chromium/media/audio/cras/audio_manager_cras.h @@ -72,6 +72,9 @@ class MEDIA_EXPORT AudioManagerCras : public AudioManagerBase { AudioInputStream* MakeInputStream(const AudioParameters& params, const std::string& device_id); + // Get minimum output buffer size for this board. + int GetMinimumOutputBufferSizePerBoard(); + void GetAudioDeviceNamesImpl(bool is_input, AudioDeviceNames* device_names); void AddBeamformingDevices(AudioDeviceNames* device_names); diff --git a/chromium/media/audio/mac/audio_auhal_mac.cc b/chromium/media/audio/mac/audio_auhal_mac.cc index e11885fa3d0..0729e48febb 100644 --- a/chromium/media/audio/mac/audio_auhal_mac.cc +++ b/chromium/media/audio/mac/audio_auhal_mac.cc @@ -52,34 +52,116 @@ static_assert(0 == LEFT && 1 == RIGHT && 2 == CENTER && 3 == LFE && static void WrapBufferList(AudioBufferList* buffer_list, AudioBus* bus, int frames) { - DCHECK(buffer_list); - DCHECK(bus); const int channels = bus->channels(); const int buffer_list_channels = buffer_list->mNumberBuffers; CHECK_EQ(channels, buffer_list_channels); // Copy pointers from AudioBufferList. - for (int i = 0; i < channels; ++i) { - bus->SetChannelData( - i, static_cast<float*>(buffer_list->mBuffers[i].mData)); - } + for (int i = 0; i < channels; ++i) + bus->SetChannelData(i, static_cast<float*>(buffer_list->mBuffers[i].mData)); // Finally set the actual length. bus->set_frames(frames); } +// Sets the stream format on the AUHAL to PCM Float32 non-interleaved for the +// given number of channels on the given scope and element. The created stream +// description will be stored in |desc|. +static bool SetStreamFormat(int channels, + int sample_rate, + AudioUnit audio_unit, + AudioStreamBasicDescription* format) { + format->mSampleRate = sample_rate; + format->mFormatID = kAudioFormatLinearPCM; + format->mFormatFlags = + kAudioFormatFlagsNativeFloatPacked | kLinearPCMFormatFlagIsNonInterleaved; + format->mBytesPerPacket = sizeof(Float32); + format->mFramesPerPacket = 1; + format->mBytesPerFrame = sizeof(Float32); + format->mChannelsPerFrame = channels; + format->mBitsPerChannel = 32; + format->mReserved = 0; + + // Set stream formats. See Apple's tech note for details on the peculiar way + // that inputs and outputs are handled in the AUHAL concerning scope and bus + // (element) numbers: + // http://developer.apple.com/library/mac/#technotes/tn2091/_index.html + return AudioUnitSetProperty(audio_unit, kAudioUnitProperty_StreamFormat, + kAudioUnitScope_Input, AUElement::OUTPUT, format, + sizeof(*format)) == noErr; +} + +// Converts |channel_layout| into CoreAudio format and sets up the AUHAL with +// our layout information so it knows how to remap the channels. +static void SetAudioChannelLayout(int channels, + ChannelLayout channel_layout, + AudioUnit audio_unit) { + DCHECK(audio_unit); + DCHECK_GT(channels, 0); + DCHECK_GT(channel_layout, CHANNEL_LAYOUT_UNSUPPORTED); + + // AudioChannelLayout is structure ending in a variable length array, so we + // can't directly allocate one. Instead compute the size and and allocate one + // inside of a byte array. + // + // Code modeled after example from Apple documentation here: + // https://developer.apple.com/library/content/qa/qa1627/_index.html + const size_t layout_size = + offsetof(AudioChannelLayout, mChannelDescriptions[channels]); + std::unique_ptr<uint8_t[]> layout_storage(new uint8_t[layout_size]); + memset(layout_storage.get(), 0, layout_size); + AudioChannelLayout* coreaudio_layout = + reinterpret_cast<AudioChannelLayout*>(layout_storage.get()); + + coreaudio_layout->mNumberChannelDescriptions = channels; + coreaudio_layout->mChannelLayoutTag = + kAudioChannelLayoutTag_UseChannelDescriptions; + AudioChannelDescription* descriptions = + coreaudio_layout->mChannelDescriptions; + + if (channel_layout == CHANNEL_LAYOUT_DISCRETE) { + // For the discrete case just assume common input mappings; once we run out + // of known channels mark them as unknown. + for (int ch = 0; ch < channels; ++ch) { + descriptions[ch].mChannelLabel = ch > CHANNELS_MAX + ? kAudioChannelLabel_Unknown + : kCoreAudioChannelMapping[ch]; + descriptions[ch].mChannelFlags = kAudioChannelFlags_AllOff; + } + } else if (channel_layout == CHANNEL_LAYOUT_MONO) { + // CoreAudio has a special label for mono. + DCHECK_EQ(channels, 1); + descriptions[0].mChannelLabel = kAudioChannelLabel_Mono; + descriptions[0].mChannelFlags = kAudioChannelFlags_AllOff; + } else { + for (int ch = 0; ch <= CHANNELS_MAX; ++ch) { + const int order = ChannelOrder(channel_layout, static_cast<Channels>(ch)); + if (order == -1) + continue; + descriptions[order].mChannelLabel = kCoreAudioChannelMapping[ch]; + descriptions[order].mChannelFlags = kAudioChannelFlags_AllOff; + } + } + + OSStatus result = AudioUnitSetProperty( + audio_unit, kAudioUnitProperty_AudioChannelLayout, kAudioUnitScope_Input, + 0, coreaudio_layout, layout_size); + if (result != noErr) { + OSSTATUS_DLOG(ERROR, result) + << "Failed to set audio channel layout. Using default layout."; + } +} + AUHALStream::AUHALStream(AudioManagerMac* manager, const AudioParameters& params, AudioDeviceID device, const AudioManager::LogCallback& log_callback) : manager_(manager), params_(params), - output_channels_(params_.channels()), number_of_frames_(params_.frames_per_buffer()), number_of_frames_requested_(0), source_(NULL), device_(device), - audio_unit_(0), volume_(1), stopped_(true), current_lost_frames_(0), @@ -91,18 +173,13 @@ AUHALStream::AUHALStream(AudioManagerMac* manager, log_callback_(log_callback) { // We must have a manager. DCHECK(manager_); + DCHECK(params_.IsValid()); + DCHECK_NE(device, kAudioObjectUnknown); CHECK(!log_callback_.Equals(AudioManager::LogCallback())); - - DVLOG(1) << "ctor"; - DVLOG(1) << "device ID: 0x" << std::hex << device; - DVLOG(1) << "buffer size: " << number_of_frames_; - DVLOG(1) << "output channels: " << output_channels_; - DVLOG(1) << "sample rate: " << params_.sample_rate(); } AUHALStream::~AUHALStream() { DCHECK(thread_checker_.CalledOnValidThread()); - DVLOG(1) << "~dtor"; CHECK(!audio_unit_); ReportAndResetStats(); @@ -110,50 +187,26 @@ AUHALStream::~AUHALStream() { bool AUHALStream::Open() { DCHECK(thread_checker_.CalledOnValidThread()); - DCHECK(!output_bus_.get()); + DCHECK(!output_bus_); DCHECK(!audio_unit_); - DVLOG(1) << "Open"; - - // Get the total number of output channels that the - // hardware supports. - int device_output_channels; - bool got_output_channels = AudioManagerMac::GetDeviceChannels( - device_, - kAudioDevicePropertyScopeOutput, - &device_output_channels); - - // Sanity check the requested output channels. - if (!got_output_channels || - output_channels_ <= 0 || output_channels_ > device_output_channels) { - LOG(ERROR) << "AudioDevice does not support requested output channels."; - return false; - } - - // The requested sample-rate must match the hardware sample-rate. - int sample_rate = AudioManagerMac::HardwareSampleRateForDevice(device_); - - if (sample_rate != params_.sample_rate()) { - LOG(ERROR) << "Requested sample-rate: " << params_.sample_rate() - << " must match the hardware sample-rate: " << sample_rate; - return false; - } // The output bus will wrap the AudioBufferList given to us in // the Render() callback. - DCHECK_GT(output_channels_, 0); - output_bus_ = AudioBus::CreateWrapper(output_channels_); + output_bus_ = AudioBus::CreateWrapper(params_.channels()); bool configured = ConfigureAUHAL(); - if (configured) + if (configured) { + DCHECK(audio_unit_); + DCHECK(audio_unit_->is_valid()); hardware_latency_ = GetHardwareLatency(); + } return configured; } void AUHALStream::Close() { DCHECK(thread_checker_.CalledOnValidThread()); - DVLOG(1) << "Close"; - CloseAudioUnit(); + audio_unit_.reset(); // Inform the audio manager that we have been closed. This will cause our // destruction. Also include the device ID as a signal to the audio manager // that it should try to increase the native I/O buffer size after the stream @@ -163,7 +216,6 @@ void AUHALStream::Close() { void AUHALStream::Start(AudioSourceCallback* callback) { DCHECK(thread_checker_.CalledOnValidThread()); - DVLOG(1) << "Start"; DCHECK(callback); if (!audio_unit_) { DLOG(ERROR) << "Open() has not been called successfully"; @@ -182,7 +234,8 @@ void AUHALStream::Start(AudioSourceCallback* callback) { deferred_start_cb_.Reset( base::Bind(&AUHALStream::Start, base::Unretained(this), callback)); manager_->GetTaskRunner()->PostDelayedTask( - FROM_HERE, deferred_start_cb_.callback(), base::TimeDelta::FromSeconds( + FROM_HERE, deferred_start_cb_.callback(), + base::TimeDelta::FromSeconds( AudioManagerMac::kStartDelayInSecsForPowerEvents)); return; } @@ -194,7 +247,7 @@ void AUHALStream::Start(AudioSourceCallback* callback) { source_ = callback; } - OSStatus result = AudioOutputUnitStart(audio_unit_); + OSStatus result = AudioOutputUnitStart(audio_unit_->audio_unit()); if (result == noErr) return; @@ -208,9 +261,8 @@ void AUHALStream::Stop() { deferred_start_cb_.Cancel(); if (stopped_) return; - DVLOG(1) << "Stop"; - DVLOG(2) << "number_of_frames: " << number_of_frames_; - OSStatus result = AudioOutputUnitStop(audio_unit_); + + OSStatus result = AudioOutputUnitStop(audio_unit_->audio_unit()); OSSTATUS_DLOG_IF(ERROR, result != noErr, result) << "AudioOutputUnitStop() failed."; if (result != noErr) @@ -233,12 +285,11 @@ void AUHALStream::GetVolume(double* volume) { // Note to future hackers of this function: Do not add locks which can // be contended in the middle of stream processing here (starting and stopping // the stream are ok) because this is running on a real-time thread. -OSStatus AUHALStream::Render( - AudioUnitRenderActionFlags* flags, - const AudioTimeStamp* output_time_stamp, - UInt32 bus_number, - UInt32 number_of_frames, - AudioBufferList* data) { +OSStatus AUHALStream::Render(AudioUnitRenderActionFlags* flags, + const AudioTimeStamp* output_time_stamp, + UInt32 bus_number, + UInt32 number_of_frames, + AudioBufferList* data) { TRACE_EVENT2("audio", "AUHALStream::Render", "input buffer size", number_of_frames_, "output buffer size", number_of_frames); @@ -256,8 +307,7 @@ OSStatus AUHALStream::Render( DVLOG(1) << "Audio frame size changed from " << number_of_frames_ << " to " << number_of_frames << " adding FIFO to compensate."; audio_fifo_.reset(new AudioPullFifo( - output_channels_, - number_of_frames_, + params_.channels(), number_of_frames_, base::Bind(&AUHALStream::ProvideInput, base::Unretained(this)))); } } @@ -297,43 +347,30 @@ void AUHALStream::ProvideInput(int frame_delay, AudioBus* dest) { } // AUHAL callback. -OSStatus AUHALStream::InputProc( - void* user_data, - AudioUnitRenderActionFlags* flags, - const AudioTimeStamp* output_time_stamp, - UInt32 bus_number, - UInt32 number_of_frames, - AudioBufferList* io_data) { +OSStatus AUHALStream::InputProc(void* user_data, + AudioUnitRenderActionFlags* flags, + const AudioTimeStamp* output_time_stamp, + UInt32 bus_number, + UInt32 number_of_frames, + AudioBufferList* io_data) { // Dispatch to our class method. - AUHALStream* audio_output = - static_cast<AUHALStream*>(user_data); + AUHALStream* audio_output = static_cast<AUHALStream*>(user_data); if (!audio_output) return -1; - return audio_output->Render( - flags, - output_time_stamp, - bus_number, - number_of_frames, - io_data); + return audio_output->Render(flags, output_time_stamp, bus_number, + number_of_frames, io_data); } base::TimeDelta AUHALStream::GetHardwareLatency() { - if (!audio_unit_ || device_ == kAudioObjectUnknown) { - DLOG(WARNING) << "AudioUnit is NULL or device ID is unknown"; - return base::TimeDelta(); - } + DCHECK(audio_unit_); // Get audio unit latency. - Float64 audio_unit_latency_sec = 0.0; + Float64 audio_unit_latency_sec; UInt32 size = sizeof(audio_unit_latency_sec); OSStatus result = AudioUnitGetProperty( - audio_unit_, - kAudioUnitProperty_Latency, - kAudioUnitScope_Global, - 0, - &audio_unit_latency_sec, - &size); + audio_unit_->audio_unit(), kAudioUnitProperty_Latency, + kAudioUnitScope_Global, 0, &audio_unit_latency_sec, &size); if (result != noErr) { OSSTATUS_DLOG(WARNING, result) << "Could not get AudioUnit latency"; return base::TimeDelta(); @@ -341,20 +378,13 @@ base::TimeDelta AUHALStream::GetHardwareLatency() { // Get output audio device latency. static const AudioObjectPropertyAddress property_address = { - kAudioDevicePropertyLatency, - kAudioDevicePropertyScopeOutput, - kAudioObjectPropertyElementMaster - }; + kAudioDevicePropertyLatency, kAudioDevicePropertyScopeOutput, + kAudioObjectPropertyElementMaster}; UInt32 device_latency_frames = 0; size = sizeof(device_latency_frames); - result = AudioObjectGetPropertyData( - device_, - &property_address, - 0, - NULL, - &size, - &device_latency_frames); + result = AudioObjectGetPropertyData(device_, &property_address, 0, NULL, + &size, &device_latency_frames); if (result != noErr) { OSSTATUS_DLOG(WARNING, result) << "Could not get audio device latency"; return base::TimeDelta(); @@ -442,107 +472,32 @@ void AUHALStream::ReportAndResetStats() { largest_glitch_frames_ = 0; } -bool AUHALStream::SetStreamFormat( - AudioStreamBasicDescription* desc, - int channels, - UInt32 scope, - UInt32 element) { - DCHECK(desc); - AudioStreamBasicDescription& format = *desc; - - format.mSampleRate = params_.sample_rate(); - format.mFormatID = kAudioFormatLinearPCM; - format.mFormatFlags = kAudioFormatFlagsNativeFloatPacked | - kLinearPCMFormatFlagIsNonInterleaved; - format.mBytesPerPacket = sizeof(Float32); - format.mFramesPerPacket = 1; - format.mBytesPerFrame = sizeof(Float32); - format.mChannelsPerFrame = channels; - format.mBitsPerChannel = 32; - format.mReserved = 0; - - OSStatus result = AudioUnitSetProperty( - audio_unit_, - kAudioUnitProperty_StreamFormat, - scope, - element, - &format, - sizeof(format)); - return (result == noErr); -} - bool AUHALStream::ConfigureAUHAL() { DCHECK(thread_checker_.CalledOnValidThread()); - if (device_ == kAudioObjectUnknown || output_channels_ == 0) - return false; - AudioComponentDescription desc = { - kAudioUnitType_Output, - kAudioUnitSubType_HALOutput, - kAudioUnitManufacturer_Apple, - 0, - 0 - }; - AudioComponent comp = AudioComponentFindNext(0, &desc); - if (!comp) + std::unique_ptr<ScopedAudioUnit> local_audio_unit( + new ScopedAudioUnit(device_, AUElement::OUTPUT)); + if (!local_audio_unit->is_valid()) return false; - OSStatus result = AudioComponentInstanceNew(comp, &audio_unit_); - if (result != noErr) { - OSSTATUS_DLOG(ERROR, result) << "AudioComponentInstanceNew() failed."; - return false; - } - // Enable output as appropriate. - // See Apple technote for details about the EnableIO property. - // Note that we use bus 1 for input and bus 0 for output: - // http://developer.apple.com/library/mac/#technotes/tn2091/_index.html - UInt32 enable_IO = 1; - result = AudioUnitSetProperty( - audio_unit_, - kAudioOutputUnitProperty_EnableIO, - kAudioUnitScope_Output, - 0, - &enable_IO, - sizeof(enable_IO)); - if (result != noErr) { - CloseAudioUnit(); - return false; - } - - // Set the device to be used with the AUHAL AudioUnit. - result = AudioUnitSetProperty( - audio_unit_, - kAudioOutputUnitProperty_CurrentDevice, - kAudioUnitScope_Global, - 0, - &device_, - sizeof(AudioDeviceID)); - if (result != noErr) { - CloseAudioUnit(); + UInt32 enable_io = 1; + OSStatus result = AudioUnitSetProperty( + local_audio_unit->audio_unit(), kAudioOutputUnitProperty_EnableIO, + kAudioUnitScope_Output, AUElement::OUTPUT, &enable_io, sizeof(enable_io)); + if (result != noErr) return false; - } - - // Set stream formats. - // See Apple's tech note for details on the peculiar way that - // inputs and outputs are handled in the AUHAL concerning scope and bus - // (element) numbers: - // http://developer.apple.com/library/mac/#technotes/tn2091/_index.html - if (!SetStreamFormat(&output_format_, - output_channels_, - kAudioUnitScope_Input, - 0)) { - CloseAudioUnit(); + if (!SetStreamFormat(params_.channels(), params_.sample_rate(), + local_audio_unit->audio_unit(), &output_format_)) { return false; } bool size_was_changed = false; size_t io_buffer_frame_size = 0; - if (!manager_->MaybeChangeBufferSize(device_, audio_unit_, 0, - number_of_frames_, &size_was_changed, + if (!manager_->MaybeChangeBufferSize(device_, local_audio_unit->audio_unit(), + 0, number_of_frames_, &size_was_changed, &io_buffer_frame_size)) { - CloseAudioUnit(); return false; } @@ -551,96 +506,22 @@ bool AUHALStream::ConfigureAUHAL() { callback.inputProc = InputProc; callback.inputProcRefCon = this; result = AudioUnitSetProperty( - audio_unit_, - kAudioUnitProperty_SetRenderCallback, - kAudioUnitScope_Input, - 0, - &callback, - sizeof(callback)); - if (result != noErr) { - CloseAudioUnit(); + local_audio_unit->audio_unit(), kAudioUnitProperty_SetRenderCallback, + kAudioUnitScope_Input, AUElement::OUTPUT, &callback, sizeof(callback)); + if (result != noErr) return false; - } - SetAudioChannelLayout(); + SetAudioChannelLayout(params_.channels(), params_.channel_layout(), + local_audio_unit->audio_unit()); - result = AudioUnitInitialize(audio_unit_); + result = AudioUnitInitialize(local_audio_unit->audio_unit()); if (result != noErr) { OSSTATUS_DLOG(ERROR, result) << "AudioUnitInitialize() failed."; - CloseAudioUnit(); return false; } + audio_unit_ = std::move(local_audio_unit); return true; } -void AUHALStream::CloseAudioUnit() { - DCHECK(thread_checker_.CalledOnValidThread()); - if (!audio_unit_) - return; - - OSStatus result = AudioUnitUninitialize(audio_unit_); - OSSTATUS_DLOG_IF(ERROR, result != noErr, result) - << "AudioUnitUninitialize() failed."; - result = AudioComponentInstanceDispose(audio_unit_); - OSSTATUS_DLOG_IF(ERROR, result != noErr, result) - << "AudioComponentInstanceDispose() failed."; - audio_unit_ = 0; -} - -void AUHALStream::SetAudioChannelLayout() { - DCHECK(audio_unit_); - - // AudioChannelLayout is structure ending in a variable length array, so we - // can't directly allocate one. Instead compute the size and and allocate one - // inside of a byte array. - // - // Code modeled after example from Apple documentation here: - // https://developer.apple.com/library/content/qa/qa1627/_index.html - const size_t layout_size = - offsetof(AudioChannelLayout, mChannelDescriptions[params_.channels()]); - std::unique_ptr<uint8_t[]> layout_storage(new uint8_t[layout_size]); - memset(layout_storage.get(), 0, layout_size); - AudioChannelLayout* channel_layout = - reinterpret_cast<AudioChannelLayout*>(layout_storage.get()); - - channel_layout->mNumberChannelDescriptions = params_.channels(); - channel_layout->mChannelLayoutTag = - kAudioChannelLayoutTag_UseChannelDescriptions; - AudioChannelDescription* descriptions = channel_layout->mChannelDescriptions; - - if (params_.channel_layout() == CHANNEL_LAYOUT_DISCRETE) { - // For the discrete case just assume common input mappings; once we run out - // of known channels mark them as unknown. - for (int ch = 0; ch < params_.channels(); ++ch) { - descriptions[ch].mChannelLabel = ch > CHANNELS_MAX - ? kAudioChannelLabel_Unknown - : kCoreAudioChannelMapping[ch]; - descriptions[ch].mChannelFlags = kAudioChannelFlags_AllOff; - } - } else if (params_.channel_layout() == CHANNEL_LAYOUT_MONO) { - // CoreAudio has a special label for mono. - DCHECK_EQ(params_.channels(), 1); - descriptions[0].mChannelLabel = kAudioChannelLabel_Mono; - descriptions[0].mChannelFlags = kAudioChannelFlags_AllOff; - } else { - for (int ch = 0; ch <= CHANNELS_MAX; ++ch) { - const int order = - ChannelOrder(params_.channel_layout(), static_cast<Channels>(ch)); - if (order == -1) - continue; - descriptions[order].mChannelLabel = kCoreAudioChannelMapping[ch]; - descriptions[order].mChannelFlags = kAudioChannelFlags_AllOff; - } - } - - OSStatus result = AudioUnitSetProperty( - audio_unit_, kAudioUnitProperty_AudioChannelLayout, kAudioUnitScope_Input, - 0, channel_layout, layout_size); - if (result != noErr) { - OSSTATUS_DLOG(ERROR, result) - << "Failed to set audio channel layout. Using default layout."; - } -} - } // namespace media diff --git a/chromium/media/audio/mac/audio_auhal_mac.h b/chromium/media/audio/mac/audio_auhal_mac.h index 14d78c41b2f..0c21a7f5906 100644 --- a/chromium/media/audio/mac/audio_auhal_mac.h +++ b/chromium/media/audio/mac/audio_auhal_mac.h @@ -32,6 +32,7 @@ #include "base/time/time.h" #include "media/audio/audio_io.h" #include "media/audio/audio_manager.h" +#include "media/audio/mac/scoped_audio_unit.h" #include "media/base/audio_parameters.h" namespace media { @@ -91,7 +92,9 @@ class AUHALStream : public AudioOutputStream { AudioDeviceID device_id() const { return device_; } size_t requested_buffer_size() const { return number_of_frames_; } - AudioUnit audio_unit() const { return audio_unit_; } + AudioUnit audio_unit() const { + return audio_unit_ ? audio_unit_->audio_unit() : nullptr; + } private: // AUHAL callback. @@ -111,20 +114,9 @@ class AUHALStream : public AudioOutputStream { // Called by either |audio_fifo_| or Render() to provide audio data. void ProvideInput(int frame_delay, AudioBus* dest); - // Sets the stream format on the AUHAL to PCM Float32 non-interleaved - // for the given number of channels on the given scope and element. - // The created stream description will be stored in |desc|. - bool SetStreamFormat(AudioStreamBasicDescription* desc, - int channels, - UInt32 scope, - UInt32 element); - // Creates the AUHAL, sets its stream format, buffer-size, etc. bool ConfigureAUHAL(); - // Uninitializes audio_unit_ if needed. - void CloseAudioUnit(); - // Creates the input and output busses. void CreateIOBusses(); @@ -141,16 +133,10 @@ class AUHALStream : public AudioOutputStream { // Called from the dtor and when the stream is reset. void ReportAndResetStats(); - // Converts |params_.channel_layout()| into CoreAudio format and sets up the - // AUHAL with our layout information so it knows how to remap the channels. - void SetAudioChannelLayout(); - // Our creator, the audio manager needs to be notified when we close. AudioManagerMac* const manager_; const AudioParameters params_; - // For convenience - same as in params_. - const int output_channels_; // Size of audio buffer requested at construction. The actual buffer size // is given by |actual_io_buffer_frame_size_| and it can differ from the @@ -177,7 +163,7 @@ class AUHALStream : public AudioOutputStream { const AudioDeviceID device_; // The AUHAL Audio Unit which talks to |device_|. - AudioUnit audio_unit_; + std::unique_ptr<ScopedAudioUnit> audio_unit_; // Volume level from 0 to 1. float volume_; diff --git a/chromium/media/audio/mac/audio_low_latency_input_mac.cc b/chromium/media/audio/mac/audio_low_latency_input_mac.cc index cddf4300d3d..c347e8845ce 100644 --- a/chromium/media/audio/mac/audio_low_latency_input_mac.cc +++ b/chromium/media/audio/mac/audio_low_latency_input_mac.cc @@ -1,8 +1,8 @@ // Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. - #include "media/audio/mac/audio_low_latency_input_mac.h" + #include <CoreServices/CoreServices.h> #include <mach/mach.h> #include <string> @@ -276,14 +276,14 @@ AUAudioInputStream::AUAudioInputStream( // Set up the desired (output) format specified by the client. format_.mSampleRate = input_params.sample_rate(); format_.mFormatID = kAudioFormatLinearPCM; - format_.mFormatFlags = kLinearPCMFormatFlagIsPacked | - kLinearPCMFormatFlagIsSignedInteger; + format_.mFormatFlags = + kLinearPCMFormatFlagIsPacked | kLinearPCMFormatFlagIsSignedInteger; DCHECK(FormatIsInterleaved(format_.mFormatFlags)); format_.mBitsPerChannel = input_params.bits_per_sample(); format_.mChannelsPerFrame = input_params.channels(); format_.mFramesPerPacket = 1; // uncompressed audio - format_.mBytesPerPacket = (format_.mBitsPerChannel * - input_params.channels()) / 8; + format_.mBytesPerPacket = + (format_.mBitsPerChannel * input_params.channels()) / 8; format_.mBytesPerFrame = format_.mBytesPerPacket; format_.mReserved = 0; @@ -345,13 +345,9 @@ bool AUAudioInputStream::Open() { // The user specifies which audio device to track. The audio unit can do // input from the device as well as output to the device. Bus 0 is used for // the output side, bus 1 is used to get audio input from the device. - AudioComponentDescription desc = { - kAudioUnitType_Output, - kAudioUnitSubType_HALOutput, - kAudioUnitManufacturer_Apple, - 0, - 0 - }; + AudioComponentDescription desc = {kAudioUnitType_Output, + kAudioUnitSubType_HALOutput, + kAudioUnitManufacturer_Apple, 0, 0}; // Find a component that meets the description in |desc|. AudioComponent comp = AudioComponentFindNext(nullptr, &desc); @@ -394,8 +390,7 @@ bool AUAudioInputStream::Open() { UInt32 enableIO = 1; // Enable input on the AUHAL. - result = AudioUnitSetProperty(audio_unit_, - kAudioOutputUnitProperty_EnableIO, + result = AudioUnitSetProperty(audio_unit_, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, 1, // input element 1 &enableIO, // enable @@ -407,8 +402,7 @@ bool AUAudioInputStream::Open() { // Disable output on the AUHAL. enableIO = 0; - result = AudioUnitSetProperty(audio_unit_, - kAudioOutputUnitProperty_EnableIO, + result = AudioUnitSetProperty(audio_unit_, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, 0, // output element 0 &enableIO, // disable @@ -420,12 +414,9 @@ bool AUAudioInputStream::Open() { // Next, set the audio device to be the Audio Unit's current device. // Note that, devices can only be set to the AUHAL after enabling IO. - result = AudioUnitSetProperty(audio_unit_, - kAudioOutputUnitProperty_CurrentDevice, - kAudioUnitScope_Global, - 0, - &input_device_id_, - sizeof(input_device_id_)); + result = AudioUnitSetProperty( + audio_unit_, kAudioOutputUnitProperty_CurrentDevice, + kAudioUnitScope_Global, 0, &input_device_id_, sizeof(input_device_id_)); if (result != noErr) { HandleError(result); return false; @@ -541,8 +532,8 @@ void AUAudioInputStream::Start(AudioInputCallback* callback) { start_was_deferred_ = true; // Use a cancellable closure so that if Stop() is called before Start() // actually runs, we can cancel the pending start. - deferred_start_cb_.Reset(base::Bind( - &AUAudioInputStream::Start, base::Unretained(this), callback)); + deferred_start_cb_.Reset(base::Bind(&AUAudioInputStream::Start, + base::Unretained(this), callback)); manager_->GetTaskRunner()->PostDelayedTask( FROM_HERE, deferred_start_cb_.callback(), base::TimeDelta::FromSeconds( @@ -691,10 +682,8 @@ void AUAudioInputStream::SetVolume(double volume) { Float32 volume_float32 = static_cast<Float32>(volume); AudioObjectPropertyAddress property_address = { - kAudioDevicePropertyVolumeScalar, - kAudioDevicePropertyScopeInput, - kAudioObjectPropertyElementMaster - }; + kAudioDevicePropertyVolumeScalar, kAudioDevicePropertyScopeInput, + kAudioObjectPropertyElementMaster}; // Try to set the volume for master volume channel. if (IsVolumeSettableOnChannel(kAudioObjectPropertyElementMaster)) { @@ -739,10 +728,8 @@ double AUAudioInputStream::GetVolume() { } AudioObjectPropertyAddress property_address = { - kAudioDevicePropertyVolumeScalar, - kAudioDevicePropertyScopeInput, - kAudioObjectPropertyElementMaster - }; + kAudioDevicePropertyVolumeScalar, kAudioDevicePropertyScopeInput, + kAudioObjectPropertyElementMaster}; if (AudioObjectHasProperty(input_device_id_, &property_address)) { // The device supports master volume control, get the volume from the @@ -788,10 +775,8 @@ bool AUAudioInputStream::IsMuted() { DCHECK_NE(input_device_id_, kAudioObjectUnknown) << "Device ID is unknown"; AudioObjectPropertyAddress property_address = { - kAudioDevicePropertyMute, - kAudioDevicePropertyScopeInput, - kAudioObjectPropertyElementMaster - }; + kAudioDevicePropertyMute, kAudioDevicePropertyScopeInput, + kAudioObjectPropertyElementMaster}; if (!AudioObjectHasProperty(input_device_id_, &property_address)) { DLOG(ERROR) << "Device does not support checking master mute state"; @@ -986,7 +971,8 @@ OSStatus AUAudioInputStream::Provide(UInt32 number_of_frames, // typically one block. const int blocks = static_cast<int>((number_of_frames - fifo_.GetUnfilledFrames()) / - number_of_frames_) + 1; + number_of_frames_) + + 1; DLOG(WARNING) << "Increasing FIFO capacity by " << blocks << " blocks"; TRACE_EVENT_INSTANT1("audio", "Increasing FIFO capacity", TRACE_EVENT_SCOPE_THREAD, "increased by", blocks); diff --git a/chromium/media/audio/mac/audio_low_latency_input_mac.h b/chromium/media/audio/mac/audio_low_latency_input_mac.h index 623fe8973b0..b30b604193b 100644 --- a/chromium/media/audio/mac/audio_low_latency_input_mac.h +++ b/chromium/media/audio/mac/audio_low_latency_input_mac.h @@ -111,7 +111,8 @@ class MEDIA_EXPORT AUAudioInputStream UInt32 number_of_frames); // Pushes recorded data to consumer of the input audio stream. - OSStatus Provide(UInt32 number_of_frames, AudioBufferList* io_data, + OSStatus Provide(UInt32 number_of_frames, + AudioBufferList* io_data, const AudioTimeStamp* time_stamp); // Callback functions called on different system threads from the Core Audio diff --git a/chromium/media/audio/mac/audio_manager_mac.cc b/chromium/media/audio/mac/audio_manager_mac.cc index cb2ac1f1f2f..642b33b8634 100644 --- a/chromium/media/audio/mac/audio_manager_mac.cc +++ b/chromium/media/audio/mac/audio_manager_mac.cc @@ -22,6 +22,7 @@ #include "media/audio/mac/audio_auhal_mac.h" #include "media/audio/mac/audio_input_mac.h" #include "media/audio/mac/audio_low_latency_input_mac.h" +#include "media/audio/mac/scoped_audio_unit.h" #include "media/base/audio_parameters.h" #include "media/base/bind_to_current_loop.h" #include "media/base/channel_layout.h" @@ -333,6 +334,116 @@ static bool GetDeviceTotalChannelCount(AudioDeviceID device, return true; } +// Returns the channel layout for |device| as provided by the AudioUnit attached +// to that device matching |element|. Returns true if the count could be pulled +// from the AudioUnit successfully, false otherwise. +static bool GetDeviceChannels(AudioDeviceID device, + AUElement element, + int* channels) { + DCHECK(AudioManager::Get()->GetTaskRunner()->BelongsToCurrentThread()); + CHECK(channels); + + // If the device has more channels than possible for layouts to express, use + // the total count of channels on the device; as of this writing, macOS will + // only return up to 8 channels in any layout. To allow WebAudio to work with + // > 8 channel devices, we must use the total channel count instead of the + // channel count of the preferred layout. + int total_channel_count = 0; + if (GetDeviceTotalChannelCount(device, + element == AUElement::OUTPUT + ? kAudioDevicePropertyScopeOutput + : kAudioDevicePropertyScopeInput, + &total_channel_count) && + total_channel_count > kMaxConcurrentChannels) { + *channels = total_channel_count; + return true; + } + + ScopedAudioUnit au(device, element); + if (!au.is_valid()) + return false; + + // Attempt to retrieve the channel layout from the AudioUnit. + // + // Note: We don't use kAudioDevicePropertyPreferredChannelLayout on the device + // because it is not available on all devices. + UInt32 size; + Boolean writable; + OSStatus result = AudioUnitGetPropertyInfo( + au.audio_unit(), kAudioUnitProperty_AudioChannelLayout, + kAudioUnitScope_Output, element, &size, &writable); + if (result != noErr) { + OSSTATUS_DLOG(ERROR, result) + << "Failed to get property info for AudioUnit channel layout."; + return false; + } + + std::unique_ptr<uint8_t[]> layout_storage(new uint8_t[size]); + AudioChannelLayout* layout = + reinterpret_cast<AudioChannelLayout*>(layout_storage.get()); + + result = AudioUnitGetProperty(au.audio_unit(), + kAudioUnitProperty_AudioChannelLayout, + kAudioUnitScope_Output, element, layout, &size); + if (result != noErr) { + OSSTATUS_LOG(ERROR, result) << "Failed to get AudioUnit channel layout."; + return false; + } + + // We don't want to have to know about all channel layout tags, so force OSX + // to give us the channel descriptions from the bitmap or tag if necessary. + const AudioChannelLayoutTag tag = layout->mChannelLayoutTag; + if (tag != kAudioChannelLayoutTag_UseChannelDescriptions) { + const bool is_bitmap = tag == kAudioChannelLayoutTag_UseChannelBitmap; + const AudioFormatPropertyID fa = + is_bitmap ? kAudioFormatProperty_ChannelLayoutForBitmap + : kAudioFormatProperty_ChannelLayoutForTag; + + if (is_bitmap) { + result = AudioFormatGetPropertyInfo(fa, sizeof(UInt32), + &layout->mChannelBitmap, &size); + } else { + result = AudioFormatGetPropertyInfo(fa, sizeof(AudioChannelLayoutTag), + &tag, &size); + } + if (result != noErr || !size) { + OSSTATUS_DLOG(ERROR, result) + << "Failed to get AudioFormat property info, size=" << size; + return false; + } + + layout_storage.reset(new uint8_t[size]); + layout = reinterpret_cast<AudioChannelLayout*>(layout_storage.get()); + if (is_bitmap) { + result = AudioFormatGetProperty(fa, sizeof(UInt32), + &layout->mChannelBitmap, &size, layout); + } else { + result = AudioFormatGetProperty(fa, sizeof(AudioChannelLayoutTag), &tag, + &size, layout); + } + if (result != noErr) { + OSSTATUS_DLOG(ERROR, result) << "Failed to get AudioFormat property."; + return false; + } + } + + // There is no channel info for stereo, assume so for mono as well. + if (layout->mNumberChannelDescriptions <= 2) { + *channels = layout->mNumberChannelDescriptions; + } else { + *channels = 0; + for (UInt32 i = 0; i < layout->mNumberChannelDescriptions; ++i) { + if (layout->mChannelDescriptions[i].mChannelLabel != + kAudioChannelLabel_Unknown) + (*channels)++; + } + } + + DVLOG(1) << (element == AUElement::OUTPUT ? "Output" : "Input") + << " channels: " << *channels; + return true; +} + class AudioManagerMac::AudioPowerObserver : public base::PowerObserver { public: AudioPowerObserver() @@ -436,94 +547,6 @@ bool AudioManagerMac::HasAudioInputDevices() { } // static -bool AudioManagerMac::GetDeviceChannels(AudioDeviceID device, - AudioObjectPropertyScope scope, - int* channels) { - DCHECK(AudioManager::Get()->GetTaskRunner()->BelongsToCurrentThread()); - CHECK(channels); - - // If the device has more channels than possible for layouts to express, use - // the total count of channels on the device; as of this writing, macOS will - // only return up to 8 channels in any layout. To allow WebAudio to work with - // > 8 channel devices, we must use the total channel count instead of the - // channel count of the preferred layout. - int total_channel_count = 0; - if (GetDeviceTotalChannelCount(device, scope, &total_channel_count) && - total_channel_count > kMaxConcurrentChannels) { - *channels = total_channel_count; - return true; - } - - AudioObjectPropertyAddress pa = {kAudioDevicePropertyPreferredChannelLayout, - scope, kAudioObjectPropertyElementMaster}; - UInt32 size; - OSStatus result = AudioObjectGetPropertyDataSize(device, &pa, 0, 0, &size); - if (result != noErr || !size) - return false; - - std::unique_ptr<uint8_t[]> layout_storage(new uint8_t[size]); - AudioChannelLayout* layout = - reinterpret_cast<AudioChannelLayout*>(layout_storage.get()); - result = AudioObjectGetPropertyData(device, &pa, 0, 0, &size, layout); - if (result != noErr) - return false; - - // We don't want to have to know about all channel layout tags, so force OSX - // to give us the channel descriptions from the bitmap or tag if necessary. - const AudioChannelLayoutTag tag = layout->mChannelLayoutTag; - if (tag != kAudioChannelLayoutTag_UseChannelDescriptions) { - const bool is_bitmap = tag == kAudioChannelLayoutTag_UseChannelBitmap; - const AudioFormatPropertyID fa = - is_bitmap ? kAudioFormatProperty_ChannelLayoutForBitmap - : kAudioFormatProperty_ChannelLayoutForTag; - - if (is_bitmap) { - result = AudioFormatGetPropertyInfo(fa, sizeof(UInt32), - &layout->mChannelBitmap, &size); - } else { - result = AudioFormatGetPropertyInfo(fa, sizeof(AudioChannelLayoutTag), - &tag, &size); - } - if (result != noErr || !size) - return false; - - layout_storage.reset(new uint8_t[size]); - layout = reinterpret_cast<AudioChannelLayout*>(layout_storage.get()); - if (is_bitmap) { - result = AudioFormatGetProperty(fa, sizeof(UInt32), - &layout->mChannelBitmap, &size, layout); - } else { - result = AudioFormatGetProperty(fa, sizeof(AudioChannelLayoutTag), &tag, - &size, layout); - } - if (result != noErr) - return false; - } - - // There is no channel info for stereo, assume so for mono as well. - if (layout->mNumberChannelDescriptions <= 2) { - *channels = layout->mNumberChannelDescriptions; - } else { - *channels = 0; - for (UInt32 i = 0; i < layout->mNumberChannelDescriptions; ++i) { - if (layout->mChannelDescriptions[i].mChannelLabel != - kAudioChannelLabel_Unknown) - (*channels)++; - } - } - - // If we still don't have a channel count, fall back to total channel count. - if (*channels == 0) { - DLOG(WARNING) << "Unable to use channel layout for channel count."; - *channels = total_channel_count; - } - - DVLOG(1) << (scope == kAudioDevicePropertyScopeInput ? "Input" : "Output") - << " channels: " << *channels; - return true; -} - -// static int AudioManagerMac::HardwareSampleRateForDevice(AudioDeviceID device_id) { DCHECK(AudioManager::Get()->GetTaskRunner()->BelongsToCurrentThread()); Float64 nominal_sample_rate; @@ -577,15 +600,14 @@ AudioParameters AudioManagerMac::GetInputStreamParameters( AudioDeviceID device = GetAudioDeviceIdByUId(true, device_id); if (device == kAudioObjectUnknown) { DLOG(ERROR) << "Invalid device " << device_id; - return AudioParameters( - AudioParameters::AUDIO_PCM_LOW_LATENCY, CHANNEL_LAYOUT_STEREO, - kFallbackSampleRate, 16, ChooseBufferSize(true, kFallbackSampleRate)); + return AudioParameters(AudioParameters::AUDIO_PCM_LOW_LATENCY, + CHANNEL_LAYOUT_STEREO, kFallbackSampleRate, 16, + ChooseBufferSize(true, kFallbackSampleRate)); } int channels = 0; ChannelLayout channel_layout = CHANNEL_LAYOUT_STEREO; - if (GetDeviceChannels(device, kAudioDevicePropertyScopeInput, &channels) && - channels <= 2) { + if (GetDeviceChannels(device, AUElement::INPUT, &channels) && channels <= 2) { channel_layout = GuessChannelLayout(channels); } else { DLOG(ERROR) << "Failed to get the device channels, use stereo as default " @@ -825,10 +847,8 @@ AudioParameters AudioManagerMac::GetPreferredOutputStreamParameters( } int hardware_channels; - if (!GetDeviceChannels(device, kAudioDevicePropertyScopeOutput, - &hardware_channels)) { + if (!GetDeviceChannels(device, AUElement::OUTPUT, &hardware_channels)) hardware_channels = 2; - } // Use the input channel count and channel layout if possible. Let OSX take // care of remapping the channels; this lets user specified channel layouts diff --git a/chromium/media/audio/mac/audio_manager_mac.h b/chromium/media/audio/mac/audio_manager_mac.h index ae6238b958b..dd9acec1c64 100644 --- a/chromium/media/audio/mac/audio_manager_mac.h +++ b/chromium/media/audio/mac/audio_manager_mac.h @@ -74,10 +74,6 @@ class MEDIA_EXPORT AudioManagerMac : public AudioManagerBase { void ReleaseOutputStreamUsingRealDevice(AudioOutputStream* stream, AudioDeviceID device_id); - static bool GetDeviceChannels(AudioDeviceID device, - AudioObjectPropertyScope scope, - int* channels); - static int HardwareSampleRateForDevice(AudioDeviceID device_id); static int HardwareSampleRate(); diff --git a/chromium/media/audio/mac/scoped_audio_unit.cc b/chromium/media/audio/mac/scoped_audio_unit.cc new file mode 100644 index 00000000000..3f0cdb3c0ce --- /dev/null +++ b/chromium/media/audio/mac/scoped_audio_unit.cc @@ -0,0 +1,53 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "media/audio/mac/scoped_audio_unit.h" + +#include "base/mac/mac_logging.h" + +namespace media { + +constexpr AudioComponentDescription desc = {kAudioUnitType_Output, + kAudioUnitSubType_HALOutput, + kAudioUnitManufacturer_Apple, 0, 0}; + +static void DestroyAudioUnit(AudioUnit audio_unit) { + OSStatus result = AudioUnitUninitialize(audio_unit); + OSSTATUS_DLOG_IF(ERROR, result != noErr, result) + << "AudioUnitUninitialize() failed : " << audio_unit; + result = AudioComponentInstanceDispose(audio_unit); + OSSTATUS_DLOG_IF(ERROR, result != noErr, result) + << "AudioComponentInstanceDispose() failed : " << audio_unit; +} + +ScopedAudioUnit::ScopedAudioUnit(AudioDeviceID device, AUElement element) { + AudioComponent comp = AudioComponentFindNext(0, &desc); + if (!comp) + return; + + AudioUnit audio_unit; + OSStatus result = AudioComponentInstanceNew(comp, &audio_unit); + if (result != noErr) { + OSSTATUS_DLOG(ERROR, result) << "AudioComponentInstanceNew() failed."; + return; + } + + result = AudioUnitSetProperty( + audio_unit, kAudioOutputUnitProperty_CurrentDevice, + kAudioUnitScope_Global, element, &device, sizeof(AudioDeviceID)); + if (result == noErr) { + audio_unit_ = audio_unit; + return; + } + OSSTATUS_DLOG(ERROR, result) + << "Failed to set current device for audio unit."; + DestroyAudioUnit(audio_unit); +} + +ScopedAudioUnit::~ScopedAudioUnit() { + if (audio_unit_) + DestroyAudioUnit(audio_unit_); +} + +} // namespace media diff --git a/chromium/media/audio/mac/scoped_audio_unit.h b/chromium/media/audio/mac/scoped_audio_unit.h new file mode 100644 index 00000000000..cb6b85121d8 --- /dev/null +++ b/chromium/media/audio/mac/scoped_audio_unit.h @@ -0,0 +1,40 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MEDIA_AUDIO_MAC_SCOPED_AUDIO_UNIT_H_ +#define MEDIA_AUDIO_MAC_SCOPED_AUDIO_UNIT_H_ + +#include <AudioUnit/AudioUnit.h> +#include <CoreAudio/CoreAudio.h> + +#include "base/macros.h" + +namespace media { + +// For whatever reason Apple doesn't have constants defined for these; per the +// documentation, we use bus 0 for output and bus 1 for input: +// http://developer.apple.com/library/mac/#technotes/tn2091/_index.html +enum AUElement : AudioUnitElement { OUTPUT = 0, INPUT = 1 }; + +// A helper class that ensures AudioUnits are properly disposed of. +class ScopedAudioUnit { + public: + // Creates a new AudioUnit and sets its device for |element| to |device|. If + // the operation fails, is_valid() will return false and audio_unit() will + // return nullptr. + ScopedAudioUnit(AudioDeviceID device, AUElement element); + ~ScopedAudioUnit(); + + bool is_valid() const { return audio_unit_ != nullptr; } + AudioUnit audio_unit() const { return audio_unit_; } + + private: + AudioUnit audio_unit_ = nullptr; + + DISALLOW_COPY_AND_ASSIGN(ScopedAudioUnit); +}; + +} // namespace media + +#endif // MEDIA_AUDIO_MAC_SCOPED_AUDIO_UNIT_H_ diff --git a/chromium/media/audio/mock_audio_manager.cc b/chromium/media/audio/mock_audio_manager.cc index 04b59aaa46d..b6c09e0d707 100644 --- a/chromium/media/audio/mock_audio_manager.cc +++ b/chromium/media/audio/mock_audio_manager.cc @@ -33,7 +33,7 @@ MockAudioManager::~MockAudioManager() { bool MockAudioManager::HasAudioOutputDevices() { DCHECK(GetTaskRunner()->BelongsToCurrentThread()); - return true; + return has_output_devices_; } bool MockAudioManager::HasAudioInputDevices() { @@ -52,11 +52,17 @@ void MockAudioManager::ShowAudioInputSettings() { void MockAudioManager::GetAudioInputDeviceDescriptions( AudioDeviceDescriptions* device_descriptions) { DCHECK(GetTaskRunner()->BelongsToCurrentThread()); + if (get_input_device_descriptions_cb_.is_null()) + return; + get_input_device_descriptions_cb_.Run(device_descriptions); } void MockAudioManager::GetAudioOutputDeviceDescriptions( AudioDeviceDescriptions* device_descriptions) { DCHECK(GetTaskRunner()->BelongsToCurrentThread()); + if (get_output_device_descriptions_cb_.is_null()) + return; + get_output_device_descriptions_cb_.Run(device_descriptions); } media::AudioOutputStream* MockAudioManager::MakeAudioOutputStream( @@ -91,13 +97,13 @@ void MockAudioManager::RemoveOutputDeviceChangeListener( } AudioParameters MockAudioManager::GetDefaultOutputStreamParameters() { - return AudioParameters(); + return default_output_params_; } AudioParameters MockAudioManager::GetOutputStreamParameters( const std::string& device_id) { DCHECK(GetTaskRunner()->BelongsToCurrentThread()); - return AudioParameters(); + return output_params_; } AudioParameters MockAudioManager::GetInputStreamParameters( @@ -109,7 +115,9 @@ AudioParameters MockAudioManager::GetInputStreamParameters( std::string MockAudioManager::GetAssociatedOutputDeviceID( const std::string& input_device_id) { DCHECK(GetTaskRunner()->BelongsToCurrentThread()); - return std::string(); + return get_associated_output_device_id_cb_.is_null() + ? std::string() + : get_associated_output_device_id_cb_.Run(input_device_id); } std::unique_ptr<AudioLog> MockAudioManager::CreateAudioLog( @@ -129,13 +137,41 @@ const char* MockAudioManager::GetName() { return nullptr; } -void MockAudioManager::SetInputStreamParameters( - const AudioParameters& input_params) { - input_params_ = input_params; +void MockAudioManager::SetInputStreamParameters(const AudioParameters& params) { + input_params_ = params; +} + +void MockAudioManager::SetOutputStreamParameters( + const AudioParameters& params) { + output_params_ = params; +} + +void MockAudioManager::SetDefaultOutputStreamParameters( + const AudioParameters& params) { + default_output_params_ = params; } void MockAudioManager::SetHasInputDevices(bool has_input_devices) { has_input_devices_ = has_input_devices; } +void MockAudioManager::SetHasOutputDevices(bool has_output_devices) { + has_output_devices_ = has_output_devices; +} + +void MockAudioManager::SetInputDeviceDescriptionsCallback( + GetDeviceDescriptionsCallback callback) { + get_input_device_descriptions_cb_ = std::move(callback); +} + +void MockAudioManager::SetOutputDeviceDescriptionsCallback( + GetDeviceDescriptionsCallback callback) { + get_output_device_descriptions_cb_ = std::move(callback); +} + +void MockAudioManager::SetAssociatedOutputDeviceIDCallback( + GetAssociatedOutputDeviceIDCallback callback) { + get_associated_output_device_id_cb_ = std::move(callback); +} + } // namespace media. diff --git a/chromium/media/audio/mock_audio_manager.h b/chromium/media/audio/mock_audio_manager.h index dedfdec5a71..781729a56ca 100644 --- a/chromium/media/audio/mock_audio_manager.h +++ b/chromium/media/audio/mock_audio_manager.h @@ -5,6 +5,7 @@ #ifndef MEDIA_AUDIO_MOCK_AUDIO_MANAGER_H_ #define MEDIA_AUDIO_MOCK_AUDIO_MANAGER_H_ +#include "base/callback.h" #include "base/macros.h" #include "base/sequenced_task_runner_helpers.h" #include "media/audio/audio_manager.h" @@ -23,6 +24,10 @@ class MockAudioManager : public AudioManager { }; using UniquePtr = std::unique_ptr<MockAudioManager, Deleter>; + using GetDeviceDescriptionsCallback = + base::RepeatingCallback<void(AudioDeviceDescriptions*)>; + using GetAssociatedOutputDeviceIDCallback = + base::RepeatingCallback<std::string(const std::string&)>; explicit MockAudioManager( scoped_refptr<base::SingleThreadTaskRunner> task_runner); @@ -78,16 +83,32 @@ class MockAudioManager : public AudioManager { const char* GetName() override; // Setters to emulate desired in-test behavior. - void SetInputStreamParameters(const AudioParameters& input_params); + void SetInputStreamParameters(const AudioParameters& params); + void SetOutputStreamParameters(const AudioParameters& params); + void SetDefaultOutputStreamParameters(const AudioParameters& params); void SetHasInputDevices(bool has_input_devices); + void SetHasOutputDevices(bool has_output_devices); + void SetInputDeviceDescriptionsCallback( + GetDeviceDescriptionsCallback callback); + void SetOutputDeviceDescriptionsCallback( + GetDeviceDescriptionsCallback callback); + void SetAssociatedOutputDeviceIDCallback( + GetAssociatedOutputDeviceIDCallback callback); protected: ~MockAudioManager() override; private: friend class base::DeleteHelper<MockAudioManager>; + AudioParameters input_params_; + AudioParameters output_params_; + AudioParameters default_output_params_; bool has_input_devices_ = true; + bool has_output_devices_ = true; + GetDeviceDescriptionsCallback get_input_device_descriptions_cb_; + GetDeviceDescriptionsCallback get_output_device_descriptions_cb_; + GetAssociatedOutputDeviceIDCallback get_associated_output_device_id_cb_; DISALLOW_COPY_AND_ASSIGN(MockAudioManager); }; diff --git a/chromium/media/audio/win/core_audio_util_win.cc b/chromium/media/audio/win/core_audio_util_win.cc index 846bd2753f6..a26841bcf9a 100644 --- a/chromium/media/audio/win/core_audio_util_win.cc +++ b/chromium/media/audio/win/core_audio_util_win.cc @@ -305,7 +305,7 @@ ScopedComPtr<IMMDevice> CoreAudioUtil::CreateDefaultDevice(EDataFlow data_flow, // adapter that connects to the endpoint device is present and enabled. if (!IsDeviceActive(endpoint_device.get())) { DVLOG(1) << "Selected endpoint device is not active"; - endpoint_device.Release(); + endpoint_device.Reset(); } return endpoint_device; } @@ -341,7 +341,7 @@ ScopedComPtr<IMMDevice> CoreAudioUtil::CreateDevice( // adapter that connects to the endpoint device is present and enabled. if (!IsDeviceActive(endpoint_device.get())) { DVLOG(1) << "Selected endpoint device is not active"; - endpoint_device.Release(); + endpoint_device.Reset(); } return endpoint_device; } |