summaryrefslogtreecommitdiff
path: root/chromium/media/audio
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@qt.io>2017-07-12 14:07:37 +0200
committerAllan Sandfeld Jensen <allan.jensen@qt.io>2017-07-17 10:29:26 +0000
commitec02ee4181c49b61fce1c8fb99292dbb8139cc90 (patch)
tree25cde714b2b71eb639d1cd53f5a22e9ba76e14ef /chromium/media/audio
parentbb09965444b5bb20b096a291445170876225268d (diff)
downloadqtwebengine-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')
-rw-r--r--chromium/media/audio/BUILD.gn4
-rw-r--r--chromium/media/audio/PRESUBMIT.py1
-rw-r--r--chromium/media/audio/android/opensles_output.cc49
-rw-r--r--chromium/media/audio/android/opensles_output.h28
-rw-r--r--chromium/media/audio/audio_device_thread.cc21
-rw-r--r--chromium/media/audio/audio_features.cc2
-rw-r--r--chromium/media/audio/audio_manager.cc26
-rw-r--r--chromium/media/audio/audio_manager_base.cc4
-rw-r--r--chromium/media/audio/audio_manager_unittest.cc6
-rw-r--r--chromium/media/audio/audio_output_delegate.cc11
-rw-r--r--chromium/media/audio/audio_output_delegate.h65
-rw-r--r--chromium/media/audio/audio_system.h56
-rw-r--r--chromium/media/audio/audio_system_impl.cc130
-rw-r--r--chromium/media/audio/audio_system_impl.h22
-rw-r--r--chromium/media/audio/audio_system_impl_unittest.cc249
-rw-r--r--chromium/media/audio/cras/audio_manager_cras.cc56
-rw-r--r--chromium/media/audio/cras/audio_manager_cras.h3
-rw-r--r--chromium/media/audio/mac/audio_auhal_mac.cc411
-rw-r--r--chromium/media/audio/mac/audio_auhal_mac.h24
-rw-r--r--chromium/media/audio/mac/audio_low_latency_input_mac.cc60
-rw-r--r--chromium/media/audio/mac/audio_low_latency_input_mac.h3
-rw-r--r--chromium/media/audio/mac/audio_manager_mac.cc212
-rw-r--r--chromium/media/audio/mac/audio_manager_mac.h4
-rw-r--r--chromium/media/audio/mac/scoped_audio_unit.cc53
-rw-r--r--chromium/media/audio/mac/scoped_audio_unit.h40
-rw-r--r--chromium/media/audio/mock_audio_manager.cc50
-rw-r--r--chromium/media/audio/mock_audio_manager.h23
-rw-r--r--chromium/media/audio/win/core_audio_util_win.cc4
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;
}