summaryrefslogtreecommitdiff
path: root/chromium/media/audio
diff options
context:
space:
mode:
authorAndras Becsi <andras.becsi@digia.com>2014-03-18 13:16:26 +0100
committerFrederik Gladhorn <frederik.gladhorn@digia.com>2014-03-20 15:55:39 +0100
commit3f0f86b0caed75241fa71c95a5d73bc0164348c5 (patch)
tree92b9fb00f2e9e90b0be2262093876d4f43b6cd13 /chromium/media/audio
parente90d7c4b152c56919d963987e2503f9909a666d2 (diff)
downloadqtwebengine-chromium-3f0f86b0caed75241fa71c95a5d73bc0164348c5.tar.gz
Update to new stable branch 1750
This also includes an updated ninja and chromium dependencies needed on Windows. Change-Id: Icd597d80ed3fa4425933c9f1334c3c2e31291c42 Reviewed-by: Zoltan Arvai <zarvai@inf.u-szeged.hu> Reviewed-by: Zeno Albisser <zeno.albisser@digia.com>
Diffstat (limited to 'chromium/media/audio')
-rw-r--r--chromium/media/audio/alsa/alsa_input.cc (renamed from chromium/media/audio/linux/alsa_input.cc)14
-rw-r--r--chromium/media/audio/alsa/alsa_input.h (renamed from chromium/media/audio/linux/alsa_input.h)14
-rw-r--r--chromium/media/audio/alsa/alsa_output.cc (renamed from chromium/media/audio/linux/alsa_output.cc)12
-rw-r--r--chromium/media/audio/alsa/alsa_output.h (renamed from chromium/media/audio/linux/alsa_output.h)14
-rw-r--r--chromium/media/audio/alsa/alsa_output_unittest.cc (renamed from chromium/media/audio/linux/alsa_output_unittest.cc)19
-rw-r--r--chromium/media/audio/alsa/alsa_util.cc (renamed from chromium/media/audio/linux/alsa_util.cc)6
-rw-r--r--chromium/media/audio/alsa/alsa_util.h (renamed from chromium/media/audio/linux/alsa_util.h)8
-rw-r--r--chromium/media/audio/alsa/alsa_wrapper.cc (renamed from chromium/media/audio/linux/alsa_wrapper.cc)4
-rw-r--r--chromium/media/audio/alsa/alsa_wrapper.h (renamed from chromium/media/audio/linux/alsa_wrapper.h)7
-rw-r--r--chromium/media/audio/alsa/audio_manager_alsa.cc362
-rw-r--r--chromium/media/audio/alsa/audio_manager_alsa.h (renamed from chromium/media/audio/linux/audio_manager_linux.h)16
-rw-r--r--chromium/media/audio/android/audio_android_unittest.cc202
-rw-r--r--chromium/media/audio/android/audio_manager_android.cc141
-rw-r--r--chromium/media/audio/android/audio_manager_android.h25
-rw-r--r--chromium/media/audio/android/audio_record_input.cc131
-rw-r--r--chromium/media/audio/android/audio_record_input.h72
-rw-r--r--chromium/media/audio/android/opensles_input.cc16
-rw-r--r--chromium/media/audio/android/opensles_input.h4
-rw-r--r--chromium/media/audio/android/opensles_output.cc10
-rw-r--r--chromium/media/audio/android/opensles_output.h10
-rw-r--r--chromium/media/audio/audio_device_thread.cc40
-rw-r--r--chromium/media/audio/audio_device_thread.h8
-rw-r--r--chromium/media/audio/audio_input_controller_unittest.cc10
-rw-r--r--chromium/media/audio/audio_input_device.cc8
-rw-r--r--chromium/media/audio/audio_input_unittest.cc10
-rw-r--r--chromium/media/audio/audio_input_volume_unittest.cc3
-rw-r--r--chromium/media/audio/audio_logging.h84
-rw-r--r--chromium/media/audio/audio_low_latency_input_output_unittest.cc30
-rw-r--r--chromium/media/audio/audio_manager.cc24
-rw-r--r--chromium/media/audio/audio_manager.h38
-rw-r--r--chromium/media/audio/audio_manager_base.cc109
-rw-r--r--chromium/media/audio/audio_manager_base.h26
-rw-r--r--chromium/media/audio/audio_manager_unittest.cc32
-rw-r--r--chromium/media/audio/audio_output_controller.cc123
-rw-r--r--chromium/media/audio/audio_output_controller.h40
-rw-r--r--chromium/media/audio/audio_output_controller_unittest.cc55
-rw-r--r--chromium/media/audio/audio_output_device.cc52
-rw-r--r--chromium/media/audio/audio_output_device_unittest.cc13
-rw-r--r--chromium/media/audio/audio_output_dispatcher.cc6
-rw-r--r--chromium/media/audio/audio_output_dispatcher.h17
-rw-r--r--chromium/media/audio/audio_output_dispatcher_impl.cc172
-rw-r--r--chromium/media/audio/audio_output_dispatcher_impl.h53
-rw-r--r--chromium/media/audio/audio_output_proxy_unittest.cc336
-rw-r--r--chromium/media/audio/audio_output_resampler.cc124
-rw-r--r--chromium/media/audio/audio_output_resampler.h6
-rw-r--r--chromium/media/audio/audio_parameters.cc34
-rw-r--r--chromium/media/audio/audio_parameters.h29
-rw-r--r--chromium/media/audio/audio_power_monitor.cc30
-rw-r--r--chromium/media/audio/audio_util.cc99
-rw-r--r--chromium/media/audio/audio_util.h31
-rw-r--r--chromium/media/audio/cras/audio_manager_cras.cc6
-rw-r--r--chromium/media/audio/cras/audio_manager_cras.h2
-rw-r--r--chromium/media/audio/cras/cras_input.cc2
-rw-r--r--chromium/media/audio/cras/cras_unified.cc36
-rw-r--r--chromium/media/audio/fake_audio_log_factory.cc32
-rw-r--r--chromium/media/audio/fake_audio_log_factory.h29
-rw-r--r--chromium/media/audio/fake_audio_manager.cc83
-rw-r--r--chromium/media/audio/fake_audio_manager.h53
-rw-r--r--chromium/media/audio/fake_audio_output_stream.cc4
-rw-r--r--chromium/media/audio/linux/audio_manager_linux.cc362
-rw-r--r--chromium/media/audio/mac/audio_auhal_mac.cc155
-rw-r--r--chromium/media/audio/mac/audio_auhal_mac.h27
-rw-r--r--chromium/media/audio/mac/audio_auhal_mac_unittest.cc2
-rw-r--r--chromium/media/audio/mac/audio_input_mac.cc1
-rw-r--r--chromium/media/audio/mac/audio_low_latency_input_mac.cc5
-rw-r--r--chromium/media/audio/mac/audio_low_latency_input_mac_unittest.cc2
-rw-r--r--chromium/media/audio/mac/audio_manager_mac.cc83
-rw-r--r--chromium/media/audio/mac/audio_manager_mac.h12
-rw-r--r--chromium/media/audio/mac/audio_synchronized_mac.cc1
-rw-r--r--chromium/media/audio/mac/audio_unified_mac.cc1
-rw-r--r--chromium/media/audio/mock_audio_manager.cc16
-rw-r--r--chromium/media/audio/mock_audio_manager.h10
-rw-r--r--chromium/media/audio/openbsd/audio_manager_openbsd.cc12
-rw-r--r--chromium/media/audio/openbsd/audio_manager_openbsd.h2
-rw-r--r--chromium/media/audio/pulse/audio_manager_pulse.cc16
-rw-r--r--chromium/media/audio/pulse/audio_manager_pulse.h4
-rw-r--r--chromium/media/audio/shared_memory_util.cc72
-rw-r--r--chromium/media/audio/shared_memory_util.h39
-rw-r--r--chromium/media/audio/simple_sources.cc1
-rw-r--r--chromium/media/audio/sounds/audio_stream_handler.cc188
-rw-r--r--chromium/media/audio/sounds/audio_stream_handler.h76
-rw-r--r--chromium/media/audio/sounds/audio_stream_handler_unittest.cc108
-rw-r--r--chromium/media/audio/sounds/sounds_manager.cc150
-rw-r--r--chromium/media/audio/sounds/sounds_manager.h56
-rw-r--r--chromium/media/audio/sounds/sounds_manager_unittest.cc69
-rw-r--r--chromium/media/audio/sounds/test_data.cc34
-rw-r--r--chromium/media/audio/sounds/test_data.h51
-rw-r--r--chromium/media/audio/sounds/wav_audio_handler.cc141
-rw-r--r--chromium/media/audio/sounds/wav_audio_handler.h59
-rw-r--r--chromium/media/audio/sounds/wav_audio_handler_unittest.cc33
-rw-r--r--chromium/media/audio/test_audio_input_controller_factory.cc5
-rw-r--r--chromium/media/audio/test_audio_input_controller_factory.h8
-rw-r--r--chromium/media/audio/win/audio_device_listener_win.cc1
-rw-r--r--chromium/media/audio/win/audio_low_latency_input_win.cc1
-rw-r--r--chromium/media/audio/win/audio_low_latency_input_win_unittest.cc31
-rw-r--r--chromium/media/audio/win/audio_low_latency_output_win.cc184
-rw-r--r--chromium/media/audio/win/audio_low_latency_output_win.h29
-rw-r--r--chromium/media/audio/win/audio_low_latency_output_win_unittest.cc49
-rw-r--r--chromium/media/audio/win/audio_manager_win.cc87
-rw-r--r--chromium/media/audio/win/audio_manager_win.h4
-rw-r--r--chromium/media/audio/win/audio_output_win_unittest.cc65
-rw-r--r--chromium/media/audio/win/audio_unified_win.cc30
-rw-r--r--chromium/media/audio/win/audio_unified_win_unittest.cc17
-rw-r--r--chromium/media/audio/win/core_audio_util_win.cc58
-rw-r--r--chromium/media/audio/win/core_audio_util_win.h38
-rw-r--r--chromium/media/audio/win/core_audio_util_win_unittest.cc4
-rw-r--r--chromium/media/audio/win/wavein_input_win.cc1
-rw-r--r--chromium/media/audio/win/waveout_output_win.cc24
108 files changed, 3548 insertions, 1852 deletions
diff --git a/chromium/media/audio/linux/alsa_input.cc b/chromium/media/audio/alsa/alsa_input.cc
index 929cbe79063..9dcbf2b8662 100644
--- a/chromium/media/audio/linux/alsa_input.cc
+++ b/chromium/media/audio/alsa/alsa_input.cc
@@ -1,19 +1,19 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2013 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/linux/alsa_input.h"
+#include "media/audio/alsa/alsa_input.h"
#include "base/basictypes.h"
#include "base/bind.h"
#include "base/logging.h"
#include "base/message_loop/message_loop.h"
#include "base/time/time.h"
+#include "media/audio/alsa/alsa_output.h"
+#include "media/audio/alsa/alsa_util.h"
+#include "media/audio/alsa/alsa_wrapper.h"
+#include "media/audio/alsa/audio_manager_alsa.h"
#include "media/audio/audio_manager.h"
-#include "media/audio/linux/alsa_output.h"
-#include "media/audio/linux/alsa_util.h"
-#include "media/audio/linux/alsa_wrapper.h"
-#include "media/audio/linux/audio_manager_linux.h"
namespace media {
@@ -24,7 +24,7 @@ static const char kDefaultDevice2[] = "plug:default";
const char AlsaPcmInputStream::kAutoSelectDevice[] = "";
-AlsaPcmInputStream::AlsaPcmInputStream(AudioManagerLinux* audio_manager,
+AlsaPcmInputStream::AlsaPcmInputStream(AudioManagerBase* audio_manager,
const std::string& device_name,
const AudioParameters& params,
AlsaWrapper* wrapper)
diff --git a/chromium/media/audio/linux/alsa_input.h b/chromium/media/audio/alsa/alsa_input.h
index 888e4780ac0..6e9aad9056e 100644
--- a/chromium/media/audio/linux/alsa_input.h
+++ b/chromium/media/audio/alsa/alsa_input.h
@@ -1,9 +1,9 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2013 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_LINUX_ALSA_INPUT_H_
-#define MEDIA_AUDIO_LINUX_ALSA_INPUT_H_
+#ifndef MEDIA_AUDIO_ALSA_ALSA_INPUT_H_
+#define MEDIA_AUDIO_ALSA_ALSA_INPUT_H_
#include <alsa/asoundlib.h>
@@ -20,7 +20,7 @@
namespace media {
class AlsaWrapper;
-class AudioManagerLinux;
+class AudioManagerBase;
// Provides an input stream for audio capture based on the ALSA PCM interface.
// This object is not thread safe and all methods should be invoked in the
@@ -34,7 +34,7 @@ class AlsaPcmInputStream : public AgcAudioStream<AudioInputStream> {
// Create a PCM Output stream for the ALSA device identified by
// |device_name|. If unsure of what to use for |device_name|, use
// |kAutoSelectDevice|.
- AlsaPcmInputStream(AudioManagerLinux* audio_manager,
+ AlsaPcmInputStream(AudioManagerBase* audio_manager,
const std::string& device_name,
const AudioParameters& params,
AlsaWrapper* wrapper);
@@ -69,7 +69,7 @@ class AlsaPcmInputStream : public AgcAudioStream<AudioInputStream> {
// want circular references. Additionally, stream objects live on the audio
// thread, which is owned by the audio manager and we don't want to addref
// the manager from that thread.
- AudioManagerLinux* audio_manager_;
+ AudioManagerBase* audio_manager_;
std::string device_name_;
AudioParameters params_;
int bytes_per_buffer_;
@@ -89,4 +89,4 @@ class AlsaPcmInputStream : public AgcAudioStream<AudioInputStream> {
} // namespace media
-#endif // MEDIA_AUDIO_LINUX_ALSA_INPUT_H_
+#endif // MEDIA_AUDIO_ALSA_ALSA_INPUT_H_
diff --git a/chromium/media/audio/linux/alsa_output.cc b/chromium/media/audio/alsa/alsa_output.cc
index fa838354b5a..eccf8ee28a8 100644
--- a/chromium/media/audio/linux/alsa_output.cc
+++ b/chromium/media/audio/alsa/alsa_output.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2013 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.
//
@@ -32,7 +32,7 @@
// view, it will seem that the device has just clogged and stopped requesting
// data.
-#include "media/audio/linux/alsa_output.h"
+#include "media/audio/alsa/alsa_output.h"
#include <algorithm>
@@ -42,9 +42,9 @@
#include "base/message_loop/message_loop.h"
#include "base/stl_util.h"
#include "base/time/time.h"
-#include "media/audio/linux/alsa_util.h"
-#include "media/audio/linux/alsa_wrapper.h"
-#include "media/audio/linux/audio_manager_linux.h"
+#include "media/audio/alsa/alsa_util.h"
+#include "media/audio/alsa/alsa_wrapper.h"
+#include "media/audio/alsa/audio_manager_alsa.h"
#include "media/base/channel_mixer.h"
#include "media/base/data_buffer.h"
#include "media/base/seekable_buffer.h"
@@ -134,7 +134,7 @@ const uint32 AlsaPcmOutputStream::kMinLatencyMicros = 40 * 1000;
AlsaPcmOutputStream::AlsaPcmOutputStream(const std::string& device_name,
const AudioParameters& params,
AlsaWrapper* wrapper,
- AudioManagerLinux* manager)
+ AudioManagerBase* manager)
: requested_device_name_(device_name),
pcm_format_(alsa_util::BitsToFormat(params.bits_per_sample())),
channels_(params.channels()),
diff --git a/chromium/media/audio/linux/alsa_output.h b/chromium/media/audio/alsa/alsa_output.h
index 841615d9d3f..65a23f75124 100644
--- a/chromium/media/audio/linux/alsa_output.h
+++ b/chromium/media/audio/alsa/alsa_output.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2013 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.
//
@@ -18,8 +18,8 @@
// the audio thread. When modifying the code in this class, please read the
// threading assumptions at the top of the implementation.
-#ifndef MEDIA_AUDIO_LINUX_ALSA_OUTPUT_H_
-#define MEDIA_AUDIO_LINUX_ALSA_OUTPUT_H_
+#ifndef MEDIA_AUDIO_ALSA_ALSA_OUTPUT_H_
+#define MEDIA_AUDIO_ALSA_ALSA_OUTPUT_H_
#include <alsa/asoundlib.h>
@@ -40,7 +40,7 @@ class MessageLoop;
namespace media {
class AlsaWrapper;
-class AudioManagerLinux;
+class AudioManagerBase;
class ChannelMixer;
class SeekableBuffer;
@@ -70,7 +70,7 @@ class MEDIA_EXPORT AlsaPcmOutputStream : public AudioOutputStream {
AlsaPcmOutputStream(const std::string& device_name,
const AudioParameters& params,
AlsaWrapper* wrapper,
- AudioManagerLinux* manager);
+ AudioManagerBase* manager);
virtual ~AlsaPcmOutputStream();
@@ -187,7 +187,7 @@ class MEDIA_EXPORT AlsaPcmOutputStream : public AudioOutputStream {
AlsaWrapper* wrapper_;
// Audio manager that created us. Used to report that we've been closed.
- AudioManagerLinux* manager_;
+ AudioManagerBase* manager_;
// Message loop to use for polling. The object is owned by the AudioManager.
// We hold a reference to the audio thread message loop since
@@ -225,4 +225,4 @@ MEDIA_EXPORT std::ostream& operator<<(std::ostream& os,
}; // namespace media
-#endif // MEDIA_AUDIO_LINUX_ALSA_OUTPUT_H_
+#endif // MEDIA_AUDIO_ALSA_ALSA_OUTPUT_H_
diff --git a/chromium/media/audio/linux/alsa_output_unittest.cc b/chromium/media/audio/alsa/alsa_output_unittest.cc
index 82fbab94c19..99ae8b02e0a 100644
--- a/chromium/media/audio/linux/alsa_output_unittest.cc
+++ b/chromium/media/audio/alsa/alsa_output_unittest.cc
@@ -1,12 +1,13 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2013 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 "base/message_loop/message_loop.h"
#include "base/strings/stringprintf.h"
-#include "media/audio/linux/alsa_output.h"
-#include "media/audio/linux/alsa_wrapper.h"
-#include "media/audio/linux/audio_manager_linux.h"
+#include "media/audio/alsa/alsa_output.h"
+#include "media/audio/alsa/alsa_wrapper.h"
+#include "media/audio/alsa/audio_manager_alsa.h"
+#include "media/audio/fake_audio_log_factory.h"
#include "media/base/data_buffer.h"
#include "media/base/seekable_buffer.h"
#include "testing/gmock/include/gmock/gmock.h"
@@ -76,8 +77,9 @@ class MockAudioSourceCallback : public AudioOutputStream::AudioSourceCallback {
MOCK_METHOD1(OnError, void(AudioOutputStream* stream));
};
-class MockAudioManagerLinux : public AudioManagerLinux {
+class MockAudioManagerAlsa : public AudioManagerAlsa {
public:
+ MockAudioManagerAlsa() : AudioManagerAlsa(&fake_audio_log_factory_) {}
MOCK_METHOD0(Init, void());
MOCK_METHOD0(HasAudioOutputDevices, bool());
MOCK_METHOD0(HasAudioInputDevices, bool());
@@ -104,12 +106,15 @@ class MockAudioManagerLinux : public AudioManagerLinux {
virtual scoped_refptr<base::MessageLoopProxy> GetMessageLoop() OVERRIDE {
return base::MessageLoop::current()->message_loop_proxy();
}
+
+ private:
+ FakeAudioLogFactory fake_audio_log_factory_;
};
class AlsaPcmOutputStreamTest : public testing::Test {
protected:
AlsaPcmOutputStreamTest() {
- mock_manager_.reset(new StrictMock<MockAudioManagerLinux>());
+ mock_manager_.reset(new StrictMock<MockAudioManagerAlsa>());
}
virtual ~AlsaPcmOutputStreamTest() {
@@ -171,7 +176,7 @@ class AlsaPcmOutputStreamTest : public testing::Test {
static void* kFakeHints[];
StrictMock<MockAlsaWrapper> mock_alsa_wrapper_;
- scoped_ptr<StrictMock<MockAudioManagerLinux> > mock_manager_;
+ scoped_ptr<StrictMock<MockAudioManagerAlsa> > mock_manager_;
base::MessageLoop message_loop_;
scoped_refptr<media::DataBuffer> packet_;
diff --git a/chromium/media/audio/linux/alsa_util.cc b/chromium/media/audio/alsa/alsa_util.cc
index 176ef697741..f26cbd30f2f 100644
--- a/chromium/media/audio/linux/alsa_util.cc
+++ b/chromium/media/audio/alsa/alsa_util.cc
@@ -1,13 +1,13 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2013 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/linux/alsa_util.h"
+#include "media/audio/alsa/alsa_util.h"
#include <string>
#include "base/logging.h"
-#include "media/audio/linux/alsa_wrapper.h"
+#include "media/audio/alsa/alsa_wrapper.h"
namespace alsa_util {
diff --git a/chromium/media/audio/linux/alsa_util.h b/chromium/media/audio/alsa/alsa_util.h
index 53cf80af84c..a23ab317dd7 100644
--- a/chromium/media/audio/linux/alsa_util.h
+++ b/chromium/media/audio/alsa/alsa_util.h
@@ -1,9 +1,9 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2013 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_LINUX_ALSA_UTIL_H_
-#define MEDIA_AUDIO_LINUX_ALSA_UTIL_H_
+#ifndef MEDIA_AUDIO_ALSA_ALSA_UTIL_H_
+#define MEDIA_AUDIO_ALSA_ALSA_UTIL_H_
#include <alsa/asoundlib.h>
#include <string>
@@ -44,4 +44,4 @@ snd_mixer_elem_t* LoadCaptureMixerElement(media::AlsaWrapper* wrapper,
} // namespace alsa_util
-#endif // MEDIA_AUDIO_LINUX_ALSA_UTIL_H_
+#endif // MEDIA_AUDIO_ALSA_ALSA_UTIL_H_
diff --git a/chromium/media/audio/linux/alsa_wrapper.cc b/chromium/media/audio/alsa/alsa_wrapper.cc
index c1ce359b303..969f3c499f4 100644
--- a/chromium/media/audio/linux/alsa_wrapper.cc
+++ b/chromium/media/audio/alsa/alsa_wrapper.cc
@@ -1,8 +1,8 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2013 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/linux/alsa_wrapper.h"
+#include "media/audio/alsa/alsa_wrapper.h"
#include <alsa/asoundlib.h>
diff --git a/chromium/media/audio/linux/alsa_wrapper.h b/chromium/media/audio/alsa/alsa_wrapper.h
index 30d94635dd5..4b3c295e582 100644
--- a/chromium/media/audio/linux/alsa_wrapper.h
+++ b/chromium/media/audio/alsa/alsa_wrapper.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2013 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.
//
@@ -6,6 +6,9 @@
// we want to use. It's purpose is to allow injection of a mock so that the
// higher level code is testable.
+#ifndef MEDIA_AUDIO_ALSA_ALSA_WRAPPER_H_
+#define MEDIA_AUDIO_ALSA_ALSA_WRAPPER_H_
+
#include <alsa/asoundlib.h>
#include "base/basictypes.h"
@@ -79,3 +82,5 @@ class MEDIA_EXPORT AlsaWrapper {
};
} // namespace media
+
+#endif // MEDIA_AUDIO_ALSA_ALSA_WRAPPER_H_
diff --git a/chromium/media/audio/alsa/audio_manager_alsa.cc b/chromium/media/audio/alsa/audio_manager_alsa.cc
new file mode 100644
index 00000000000..ac61a5fa974
--- /dev/null
+++ b/chromium/media/audio/alsa/audio_manager_alsa.cc
@@ -0,0 +1,362 @@
+// Copyright 2013 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/alsa/audio_manager_alsa.h"
+
+#include "base/command_line.h"
+#include "base/environment.h"
+#include "base/files/file_path.h"
+#include "base/logging.h"
+#include "base/metrics/histogram.h"
+#include "base/nix/xdg_util.h"
+#include "base/process/launch.h"
+#include "base/stl_util.h"
+#include "media/audio/audio_output_dispatcher.h"
+#include "media/audio/audio_parameters.h"
+#if defined(USE_CRAS)
+#include "media/audio/cras/audio_manager_cras.h"
+#endif
+#include "media/audio/alsa/alsa_input.h"
+#include "media/audio/alsa/alsa_output.h"
+#include "media/audio/alsa/alsa_wrapper.h"
+#if defined(USE_PULSEAUDIO)
+#include "media/audio/pulse/audio_manager_pulse.h"
+#endif
+#include "media/base/channel_layout.h"
+#include "media/base/limits.h"
+#include "media/base/media_switches.h"
+
+namespace media {
+
+// Maximum number of output streams that can be open simultaneously.
+static const int kMaxOutputStreams = 50;
+
+// Default sample rate for input and output streams.
+static const int kDefaultSampleRate = 48000;
+
+// Since "default", "pulse" and "dmix" devices are virtual devices mapped to
+// real devices, we remove them from the list to avoiding duplicate counting.
+// In addition, note that we support no more than 2 channels for recording,
+// hence surround devices are not stored in the list.
+static const char* kInvalidAudioInputDevices[] = {
+ "default",
+ "dmix",
+ "null",
+ "pulse",
+ "surround",
+};
+
+// static
+void AudioManagerAlsa::ShowLinuxAudioInputSettings() {
+ scoped_ptr<base::Environment> env(base::Environment::Create());
+ CommandLine command_line(CommandLine::NO_PROGRAM);
+ switch (base::nix::GetDesktopEnvironment(env.get())) {
+ case base::nix::DESKTOP_ENVIRONMENT_GNOME:
+ command_line.SetProgram(base::FilePath("gnome-volume-control"));
+ break;
+ case base::nix::DESKTOP_ENVIRONMENT_KDE3:
+ case base::nix::DESKTOP_ENVIRONMENT_KDE4:
+ command_line.SetProgram(base::FilePath("kmix"));
+ break;
+ case base::nix::DESKTOP_ENVIRONMENT_UNITY:
+ command_line.SetProgram(base::FilePath("gnome-control-center"));
+ command_line.AppendArg("sound");
+ command_line.AppendArg("input");
+ break;
+ default:
+ LOG(ERROR) << "Failed to show audio input settings: we don't know "
+ << "what command to use for your desktop environment.";
+ return;
+ }
+ base::LaunchProcess(command_line, base::LaunchOptions(), NULL);
+}
+
+// Implementation of AudioManager.
+bool AudioManagerAlsa::HasAudioOutputDevices() {
+ return HasAnyAlsaAudioDevice(kStreamPlayback);
+}
+
+bool AudioManagerAlsa::HasAudioInputDevices() {
+ return HasAnyAlsaAudioDevice(kStreamCapture);
+}
+
+AudioManagerAlsa::AudioManagerAlsa(AudioLogFactory* audio_log_factory)
+ : AudioManagerBase(audio_log_factory),
+ wrapper_(new AlsaWrapper()) {
+ SetMaxOutputStreamsAllowed(kMaxOutputStreams);
+}
+
+AudioManagerAlsa::~AudioManagerAlsa() {
+ Shutdown();
+}
+
+void AudioManagerAlsa::ShowAudioInputSettings() {
+ ShowLinuxAudioInputSettings();
+}
+
+void AudioManagerAlsa::GetAudioInputDeviceNames(
+ AudioDeviceNames* device_names) {
+ DCHECK(device_names->empty());
+ GetAlsaAudioDevices(kStreamCapture, device_names);
+}
+
+void AudioManagerAlsa::GetAudioOutputDeviceNames(
+ AudioDeviceNames* device_names) {
+ DCHECK(device_names->empty());
+ GetAlsaAudioDevices(kStreamPlayback, device_names);
+}
+
+AudioParameters AudioManagerAlsa::GetInputStreamParameters(
+ const std::string& device_id) {
+ static const int kDefaultInputBufferSize = 1024;
+
+ return AudioParameters(
+ AudioParameters::AUDIO_PCM_LOW_LATENCY, CHANNEL_LAYOUT_STEREO,
+ kDefaultSampleRate, 16, kDefaultInputBufferSize);
+}
+
+void AudioManagerAlsa::GetAlsaAudioDevices(
+ StreamType type,
+ media::AudioDeviceNames* device_names) {
+ // Constants specified by the ALSA API for device hints.
+ static const char kPcmInterfaceName[] = "pcm";
+ int card = -1;
+
+ // Loop through the sound cards to get ALSA device hints.
+ while (!wrapper_->CardNext(&card) && card >= 0) {
+ void** hints = NULL;
+ int error = wrapper_->DeviceNameHint(card, kPcmInterfaceName, &hints);
+ if (!error) {
+ GetAlsaDevicesInfo(type, hints, device_names);
+
+ // Destroy the hints now that we're done with it.
+ wrapper_->DeviceNameFreeHint(hints);
+ } else {
+ DLOG(WARNING) << "GetAlsaAudioDevices: unable to get device hints: "
+ << wrapper_->StrError(error);
+ }
+ }
+}
+
+void AudioManagerAlsa::GetAlsaDevicesInfo(
+ AudioManagerAlsa::StreamType type,
+ void** hints,
+ media::AudioDeviceNames* device_names) {
+ static const char kIoHintName[] = "IOID";
+ static const char kNameHintName[] = "NAME";
+ static const char kDescriptionHintName[] = "DESC";
+
+ const char* unwanted_device_type = UnwantedDeviceTypeWhenEnumerating(type);
+
+ for (void** hint_iter = hints; *hint_iter != NULL; hint_iter++) {
+ // Only examine devices of the right type. Valid values are
+ // "Input", "Output", and NULL which means both input and output.
+ scoped_ptr_malloc<char> io(wrapper_->DeviceNameGetHint(*hint_iter,
+ kIoHintName));
+ if (io != NULL && strcmp(unwanted_device_type, io.get()) == 0)
+ continue;
+
+ // Found a device, prepend the default device since we always want
+ // it to be on the top of the list for all platforms. And there is
+ // no duplicate counting here since it is only done if the list is
+ // still empty. Note, pulse has exclusively opened the default
+ // device, so we must open the device via the "default" moniker.
+ if (device_names->empty()) {
+ device_names->push_front(media::AudioDeviceName(
+ AudioManagerBase::kDefaultDeviceName,
+ AudioManagerBase::kDefaultDeviceId));
+ }
+
+ // Get the unique device name for the device.
+ scoped_ptr_malloc<char> unique_device_name(
+ wrapper_->DeviceNameGetHint(*hint_iter, kNameHintName));
+
+ // Find out if the device is available.
+ if (IsAlsaDeviceAvailable(type, unique_device_name.get())) {
+ // Get the description for the device.
+ scoped_ptr_malloc<char> desc(wrapper_->DeviceNameGetHint(
+ *hint_iter, kDescriptionHintName));
+
+ media::AudioDeviceName name;
+ name.unique_id = unique_device_name.get();
+ if (desc) {
+ // Use the more user friendly description as name.
+ // Replace '\n' with '-'.
+ char* pret = strchr(desc.get(), '\n');
+ if (pret)
+ *pret = '-';
+ name.device_name = desc.get();
+ } else {
+ // Virtual devices don't necessarily have descriptions.
+ // Use their names instead.
+ name.device_name = unique_device_name.get();
+ }
+
+ // Store the device information.
+ device_names->push_back(name);
+ }
+ }
+}
+
+// static
+bool AudioManagerAlsa::IsAlsaDeviceAvailable(
+ AudioManagerAlsa::StreamType type,
+ const char* device_name) {
+ if (!device_name)
+ return false;
+
+ // We do prefix matches on the device name to see whether to include
+ // it or not.
+ if (type == kStreamCapture) {
+ // Check if the device is in the list of invalid devices.
+ for (size_t i = 0; i < arraysize(kInvalidAudioInputDevices); ++i) {
+ if (strncmp(kInvalidAudioInputDevices[i], device_name,
+ strlen(kInvalidAudioInputDevices[i])) == 0)
+ return false;
+ }
+ return true;
+ } else {
+ DCHECK_EQ(kStreamPlayback, type);
+ // We prefer the device type that maps straight to hardware but
+ // goes through software conversion if needed (e.g. incompatible
+ // sample rate).
+ // TODO(joi): Should we prefer "hw" instead?
+ static const char kDeviceTypeDesired[] = "plughw";
+ return strncmp(kDeviceTypeDesired,
+ device_name,
+ arraysize(kDeviceTypeDesired) - 1) == 0;
+ }
+}
+
+// static
+const char* AudioManagerAlsa::UnwantedDeviceTypeWhenEnumerating(
+ AudioManagerAlsa::StreamType wanted_type) {
+ return wanted_type == kStreamPlayback ? "Input" : "Output";
+}
+
+bool AudioManagerAlsa::HasAnyAlsaAudioDevice(
+ AudioManagerAlsa::StreamType stream) {
+ static const char kPcmInterfaceName[] = "pcm";
+ static const char kIoHintName[] = "IOID";
+ void** hints = NULL;
+ bool has_device = false;
+ int card = -1;
+
+ // Loop through the sound cards.
+ // Don't use snd_device_name_hint(-1,..) since there is a access violation
+ // inside this ALSA API with libasound.so.2.0.0.
+ while (!wrapper_->CardNext(&card) && (card >= 0) && !has_device) {
+ int error = wrapper_->DeviceNameHint(card, kPcmInterfaceName, &hints);
+ if (!error) {
+ for (void** hint_iter = hints; *hint_iter != NULL; hint_iter++) {
+ // Only examine devices that are |stream| capable. Valid values are
+ // "Input", "Output", and NULL which means both input and output.
+ scoped_ptr_malloc<char> io(wrapper_->DeviceNameGetHint(*hint_iter,
+ kIoHintName));
+ const char* unwanted_type = UnwantedDeviceTypeWhenEnumerating(stream);
+ if (io != NULL && strcmp(unwanted_type, io.get()) == 0)
+ continue; // Wrong type, skip the device.
+
+ // Found an input device.
+ has_device = true;
+ break;
+ }
+
+ // Destroy the hints now that we're done with it.
+ wrapper_->DeviceNameFreeHint(hints);
+ hints = NULL;
+ } else {
+ DLOG(WARNING) << "HasAnyAudioDevice: unable to get device hints: "
+ << wrapper_->StrError(error);
+ }
+ }
+
+ return has_device;
+}
+
+AudioOutputStream* AudioManagerAlsa::MakeLinearOutputStream(
+ const AudioParameters& params) {
+ DCHECK_EQ(AudioParameters::AUDIO_PCM_LINEAR, params.format());
+ return MakeOutputStream(params);
+}
+
+AudioOutputStream* AudioManagerAlsa::MakeLowLatencyOutputStream(
+ const AudioParameters& params,
+ const std::string& device_id,
+ const std::string& input_device_id) {
+ DLOG_IF(ERROR, !device_id.empty()) << "Not implemented!";
+ DCHECK_EQ(AudioParameters::AUDIO_PCM_LOW_LATENCY, params.format());
+ // TODO(xians): Use input_device_id for unified IO.
+ return MakeOutputStream(params);
+}
+
+AudioInputStream* AudioManagerAlsa::MakeLinearInputStream(
+ const AudioParameters& params, const std::string& device_id) {
+ DCHECK_EQ(AudioParameters::AUDIO_PCM_LINEAR, params.format());
+ return MakeInputStream(params, device_id);
+}
+
+AudioInputStream* AudioManagerAlsa::MakeLowLatencyInputStream(
+ const AudioParameters& params, const std::string& device_id) {
+ DCHECK_EQ(AudioParameters::AUDIO_PCM_LOW_LATENCY, params.format());
+ return MakeInputStream(params, device_id);
+}
+
+AudioParameters AudioManagerAlsa::GetPreferredOutputStreamParameters(
+ const std::string& output_device_id,
+ const AudioParameters& input_params) {
+ // TODO(tommi): Support |output_device_id|.
+ DLOG_IF(ERROR, !output_device_id.empty()) << "Not implemented!";
+ static const int kDefaultOutputBufferSize = 2048;
+ ChannelLayout channel_layout = CHANNEL_LAYOUT_STEREO;
+ int sample_rate = kDefaultSampleRate;
+ int buffer_size = kDefaultOutputBufferSize;
+ int bits_per_sample = 16;
+ int input_channels = 0;
+ if (input_params.IsValid()) {
+ // Some clients, such as WebRTC, have a more limited use case and work
+ // acceptably with a smaller buffer size. The check below allows clients
+ // which want to try a smaller buffer size on Linux to do so.
+ // TODO(dalecurtis): This should include bits per channel and channel layout
+ // eventually.
+ sample_rate = input_params.sample_rate();
+ bits_per_sample = input_params.bits_per_sample();
+ channel_layout = input_params.channel_layout();
+ input_channels = input_params.input_channels();
+ buffer_size = std::min(input_params.frames_per_buffer(), buffer_size);
+ }
+
+ int user_buffer_size = GetUserBufferSize();
+ if (user_buffer_size)
+ buffer_size = user_buffer_size;
+
+ return AudioParameters(
+ AudioParameters::AUDIO_PCM_LOW_LATENCY, channel_layout, input_channels,
+ sample_rate, bits_per_sample, buffer_size, AudioParameters::NO_EFFECTS);
+}
+
+AudioOutputStream* AudioManagerAlsa::MakeOutputStream(
+ const AudioParameters& params) {
+ std::string device_name = AlsaPcmOutputStream::kAutoSelectDevice;
+ if (CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kAlsaOutputDevice)) {
+ device_name = CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
+ switches::kAlsaOutputDevice);
+ }
+ return new AlsaPcmOutputStream(device_name, params, wrapper_.get(), this);
+}
+
+AudioInputStream* AudioManagerAlsa::MakeInputStream(
+ const AudioParameters& params, const std::string& device_id) {
+ std::string device_name = (device_id == AudioManagerBase::kDefaultDeviceId) ?
+ AlsaPcmInputStream::kAutoSelectDevice : device_id;
+ if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAlsaInputDevice)) {
+ device_name = CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
+ switches::kAlsaInputDevice);
+ }
+
+ return new AlsaPcmInputStream(this, device_name, params, wrapper_.get());
+}
+
+} // namespace media
diff --git a/chromium/media/audio/linux/audio_manager_linux.h b/chromium/media/audio/alsa/audio_manager_alsa.h
index ab284dfdce9..155089f06bc 100644
--- a/chromium/media/audio/linux/audio_manager_linux.h
+++ b/chromium/media/audio/alsa/audio_manager_alsa.h
@@ -1,9 +1,9 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2013 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_LINUX_AUDIO_MANAGER_LINUX_H_
-#define MEDIA_AUDIO_LINUX_AUDIO_MANAGER_LINUX_H_
+#ifndef MEDIA_AUDIO_ALSA_AUDIO_MANAGER_ALSA_H_
+#define MEDIA_AUDIO_ALSA_AUDIO_MANAGER_ALSA_H_
#include <string>
#include "base/compiler_specific.h"
@@ -15,9 +15,9 @@ namespace media {
class AlsaWrapper;
-class MEDIA_EXPORT AudioManagerLinux : public AudioManagerBase {
+class MEDIA_EXPORT AudioManagerAlsa : public AudioManagerBase {
public:
- AudioManagerLinux();
+ AudioManagerAlsa(AudioLogFactory* audio_log_factory);
static void ShowLinuxAudioInputSettings();
@@ -45,7 +45,7 @@ class MEDIA_EXPORT AudioManagerLinux : public AudioManagerBase {
const AudioParameters& params, const std::string& device_id) OVERRIDE;
protected:
- virtual ~AudioManagerLinux();
+ virtual ~AudioManagerAlsa();
virtual AudioParameters GetPreferredOutputStreamParameters(
const std::string& output_device_id,
@@ -86,9 +86,9 @@ class MEDIA_EXPORT AudioManagerLinux : public AudioManagerBase {
scoped_ptr<AlsaWrapper> wrapper_;
- DISALLOW_COPY_AND_ASSIGN(AudioManagerLinux);
+ DISALLOW_COPY_AND_ASSIGN(AudioManagerAlsa);
};
} // namespace media
-#endif // MEDIA_AUDIO_LINUX_AUDIO_MANAGER_LINUX_H_
+#endif // MEDIA_AUDIO_ALSA_AUDIO_MANAGER_ALSA_H_
diff --git a/chromium/media/audio/android/audio_android_unittest.cc b/chromium/media/audio/android/audio_android_unittest.cc
index a8e448f821f..e7913265269 100644
--- a/chromium/media/audio/android/audio_android_unittest.cc
+++ b/chromium/media/audio/android/audio_android_unittest.cc
@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include "base/android/build_info.h"
#include "base/basictypes.h"
#include "base/file_util.h"
#include "base/memory/scoped_ptr.h"
@@ -95,7 +96,9 @@ std::ostream& operator<<(std::ostream& os, const AudioParameters& params) {
<< "bytes per buffer: " << params.GetBytesPerBuffer() << endl
<< "bytes per second: " << params.GetBytesPerSecond() << endl
<< "bytes per frame: " << params.GetBytesPerFrame() << endl
- << "frame size in ms: " << ExpectedTimeBetweenCallbacks(params);
+ << "chunk size in ms: " << ExpectedTimeBetweenCallbacks(params) << endl
+ << "echo_canceller: "
+ << (params.effects() & AudioParameters::ECHO_CANCELLER);
return os;
}
@@ -142,7 +145,7 @@ class FileAudioSource : public AudioOutputStream::AudioSourceCallback {
// Log the name of the file which is used as input for this test.
base::FilePath file_path = GetTestDataFilePath(name);
- LOG(INFO) << "Reading from file: " << file_path.value().c_str();
+ VLOG(0) << "Reading from file: " << file_path.value().c_str();
}
virtual ~FileAudioSource() {}
@@ -218,9 +221,9 @@ class FileAudioSink : public AudioInputStream::AudioInputCallback {
base::FilePath file_path;
EXPECT_TRUE(PathService::Get(base::DIR_SOURCE_ROOT, &file_path));
file_path = file_path.AppendASCII(file_name.c_str());
- binary_file_ = file_util::OpenFile(file_path, "wb");
+ binary_file_ = base::OpenFile(file_path, "wb");
DLOG_IF(ERROR, !binary_file_) << "Failed to open binary PCM data file.";
- LOG(INFO) << "Writing to file: " << file_path.value().c_str();
+ VLOG(0) << "Writing to file: " << file_path.value().c_str();
}
virtual ~FileAudioSink() {
@@ -239,7 +242,7 @@ class FileAudioSink : public AudioInputStream::AudioInputCallback {
buffer_->Seek(chunk_size);
bytes_written += chunk_size;
}
- file_util::CloseFile(binary_file_);
+ base::CloseFile(binary_file_);
}
// AudioInputStream::AudioInputCallback implementation.
@@ -383,14 +386,14 @@ class FullDuplexAudioSinkSource
DISALLOW_COPY_AND_ASSIGN(FullDuplexAudioSinkSource);
};
-// Test fixture class.
-class AudioAndroidTest : public testing::Test {
+// Test fixture class for tests which only exercise the output path.
+class AudioAndroidOutputTest : public testing::Test {
public:
- AudioAndroidTest() {}
+ AudioAndroidOutputTest() {}
protected:
virtual void SetUp() {
- audio_manager_.reset(AudioManager::Create());
+ audio_manager_.reset(AudioManager::CreateForTesting());
loop_.reset(new base::MessageLoopForUI());
}
@@ -399,11 +402,6 @@ class AudioAndroidTest : public testing::Test {
AudioManager* audio_manager() { return audio_manager_.get(); }
base::MessageLoopForUI* loop() { return loop_.get(); }
- AudioParameters GetDefaultInputStreamParameters() {
- return audio_manager()->GetInputStreamParameters(
- AudioManagerBase::kDefaultDeviceId);
- }
-
AudioParameters GetDefaultOutputStreamParameters() {
return audio_manager()->GetDefaultOutputStreamParameters();
}
@@ -413,28 +411,28 @@ class AudioAndroidTest : public testing::Test {
.InMillisecondsF();
}
- void StartInputStreamCallbacks(const AudioParameters& params) {
+ void StartOutputStreamCallbacks(const AudioParameters& params) {
double expected_time_between_callbacks_ms =
ExpectedTimeBetweenCallbacks(params);
const int num_callbacks =
(kCallbackTestTimeMs / expected_time_between_callbacks_ms);
- AudioInputStream* stream = audio_manager()->MakeAudioInputStream(
- params, AudioManagerBase::kDefaultDeviceId);
+ AudioOutputStream* stream = audio_manager()->MakeAudioOutputStream(
+ params, std::string(), std::string());
EXPECT_TRUE(stream);
int count = 0;
- MockAudioInputCallback sink;
+ MockAudioOutputCallback source;
- EXPECT_CALL(sink,
- OnData(stream, NotNull(), params.GetBytesPerBuffer(), _, _))
+ EXPECT_CALL(source, OnMoreData(NotNull(), _))
.Times(AtLeast(num_callbacks))
.WillRepeatedly(
- CheckCountAndPostQuitTask(&count, num_callbacks, loop()));
- EXPECT_CALL(sink, OnError(stream)).Times(0);
- EXPECT_CALL(sink, OnClose(stream)).Times(1);
+ DoAll(CheckCountAndPostQuitTask(&count, num_callbacks, loop()),
+ Invoke(&source, &MockAudioOutputCallback::RealOnMoreData)));
+ EXPECT_CALL(source, OnError(stream)).Times(0);
+ EXPECT_CALL(source, OnMoreIOData(_, _, _)).Times(0);
EXPECT_TRUE(stream->Open());
- stream->Start(&sink);
+ stream->Start(&source);
start_time_ = base::TimeTicks::Now();
loop()->Run();
end_time_ = base::TimeTicks::Now();
@@ -443,38 +441,83 @@ class AudioAndroidTest : public testing::Test {
double average_time_between_callbacks_ms =
AverageTimeBetweenCallbacks(num_callbacks);
- LOG(INFO) << "expected time between callbacks: "
- << expected_time_between_callbacks_ms << " ms";
- LOG(INFO) << "average time between callbacks: "
- << average_time_between_callbacks_ms << " ms";
+ VLOG(0) << "expected time between callbacks: "
+ << expected_time_between_callbacks_ms << " ms";
+ VLOG(0) << "average time between callbacks: "
+ << average_time_between_callbacks_ms << " ms";
EXPECT_GE(average_time_between_callbacks_ms,
0.70 * expected_time_between_callbacks_ms);
EXPECT_LE(average_time_between_callbacks_ms,
1.30 * expected_time_between_callbacks_ms);
}
- void StartOutputStreamCallbacks(const AudioParameters& params) {
+ scoped_ptr<base::MessageLoopForUI> loop_;
+ scoped_ptr<AudioManager> audio_manager_;
+ base::TimeTicks start_time_;
+ base::TimeTicks end_time_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(AudioAndroidOutputTest);
+};
+
+// AudioRecordInputStream should only be created on Jelly Bean and higher. This
+// ensures we only test against the AudioRecord path when that is satisfied.
+std::vector<bool> RunAudioRecordInputPathTests() {
+ std::vector<bool> tests;
+ tests.push_back(false);
+ if (base::android::BuildInfo::GetInstance()->sdk_int() >= 16)
+ tests.push_back(true);
+ return tests;
+}
+
+// Test fixture class for tests which exercise the input path, or both input and
+// output paths. It is value-parameterized to test against both the Java
+// AudioRecord (when true) and native OpenSLES (when false) input paths.
+class AudioAndroidInputTest : public AudioAndroidOutputTest,
+ public testing::WithParamInterface<bool> {
+ public:
+ AudioAndroidInputTest() {}
+
+ protected:
+ AudioParameters GetInputStreamParameters() {
+ AudioParameters input_params = audio_manager()->GetInputStreamParameters(
+ AudioManagerBase::kDefaultDeviceId);
+ // Override the platform effects setting to use the AudioRecord or OpenSLES
+ // path as requested.
+ int effects = GetParam() ? AudioParameters::ECHO_CANCELLER :
+ AudioParameters::NO_EFFECTS;
+ AudioParameters params(input_params.format(),
+ input_params.channel_layout(),
+ input_params.input_channels(),
+ input_params.sample_rate(),
+ input_params.bits_per_sample(),
+ input_params.frames_per_buffer(),
+ effects);
+ return params;
+ }
+
+ void StartInputStreamCallbacks(const AudioParameters& params) {
double expected_time_between_callbacks_ms =
ExpectedTimeBetweenCallbacks(params);
const int num_callbacks =
(kCallbackTestTimeMs / expected_time_between_callbacks_ms);
- AudioOutputStream* stream = audio_manager()->MakeAudioOutputStream(
- params, std::string(), std::string());
+ AudioInputStream* stream = audio_manager()->MakeAudioInputStream(
+ params, AudioManagerBase::kDefaultDeviceId);
EXPECT_TRUE(stream);
int count = 0;
- MockAudioOutputCallback source;
+ MockAudioInputCallback sink;
- EXPECT_CALL(source, OnMoreData(NotNull(), _))
+ EXPECT_CALL(sink,
+ OnData(stream, NotNull(), params.GetBytesPerBuffer(), _, _))
.Times(AtLeast(num_callbacks))
.WillRepeatedly(
- DoAll(CheckCountAndPostQuitTask(&count, num_callbacks, loop()),
- Invoke(&source, &MockAudioOutputCallback::RealOnMoreData)));
- EXPECT_CALL(source, OnError(stream)).Times(0);
- EXPECT_CALL(source, OnMoreIOData(_, _, _)).Times(0);
+ CheckCountAndPostQuitTask(&count, num_callbacks, loop()));
+ EXPECT_CALL(sink, OnError(stream)).Times(0);
+ EXPECT_CALL(sink, OnClose(stream)).Times(1);
EXPECT_TRUE(stream->Open());
- stream->Start(&source);
+ stream->Start(&sink);
start_time_ = base::TimeTicks::Now();
loop()->Run();
end_time_ = base::TimeTicks::Now();
@@ -483,50 +526,50 @@ class AudioAndroidTest : public testing::Test {
double average_time_between_callbacks_ms =
AverageTimeBetweenCallbacks(num_callbacks);
- LOG(INFO) << "expected time between callbacks: "
- << expected_time_between_callbacks_ms << " ms";
- LOG(INFO) << "average time between callbacks: "
- << average_time_between_callbacks_ms << " ms";
+ VLOG(0) << "expected time between callbacks: "
+ << expected_time_between_callbacks_ms << " ms";
+ VLOG(0) << "average time between callbacks: "
+ << average_time_between_callbacks_ms << " ms";
EXPECT_GE(average_time_between_callbacks_ms,
0.70 * expected_time_between_callbacks_ms);
EXPECT_LE(average_time_between_callbacks_ms,
1.30 * expected_time_between_callbacks_ms);
}
- scoped_ptr<base::MessageLoopForUI> loop_;
- scoped_ptr<AudioManager> audio_manager_;
- base::TimeTicks start_time_;
- base::TimeTicks end_time_;
- DISALLOW_COPY_AND_ASSIGN(AudioAndroidTest);
+ private:
+ DISALLOW_COPY_AND_ASSIGN(AudioAndroidInputTest);
};
// Get the default audio input parameters and log the result.
-TEST_F(AudioAndroidTest, GetInputStreamParameters) {
- AudioParameters params = GetDefaultInputStreamParameters();
+TEST_P(AudioAndroidInputTest, GetDefaultInputStreamParameters) {
+ // We don't go through AudioAndroidInputTest::GetInputStreamParameters() here
+ // so that we can log the real (non-overridden) values of the effects.
+ AudioParameters params = audio_manager()->GetInputStreamParameters(
+ AudioManagerBase::kDefaultDeviceId);
EXPECT_TRUE(params.IsValid());
VLOG(1) << params;
}
// Get the default audio output parameters and log the result.
-TEST_F(AudioAndroidTest, GetDefaultOutputStreamParameters) {
+TEST_F(AudioAndroidOutputTest, GetDefaultOutputStreamParameters) {
AudioParameters params = GetDefaultOutputStreamParameters();
EXPECT_TRUE(params.IsValid());
VLOG(1) << params;
}
// Check if low-latency output is supported and log the result as output.
-TEST_F(AudioAndroidTest, IsAudioLowLatencySupported) {
+TEST_F(AudioAndroidOutputTest, IsAudioLowLatencySupported) {
AudioManagerAndroid* manager =
static_cast<AudioManagerAndroid*>(audio_manager());
bool low_latency = manager->IsAudioLowLatencySupported();
- low_latency ? LOG(INFO) << "Low latency output is supported"
- : LOG(INFO) << "Low latency output is *not* supported";
+ low_latency ? VLOG(0) << "Low latency output is supported"
+ : VLOG(0) << "Low latency output is *not* supported";
}
// Ensure that a default input stream can be created and closed.
-TEST_F(AudioAndroidTest, CreateAndCloseInputStream) {
- AudioParameters params = GetDefaultInputStreamParameters();
+TEST_P(AudioAndroidInputTest, CreateAndCloseInputStream) {
+ AudioParameters params = GetInputStreamParameters();
AudioInputStream* ais = audio_manager()->MakeAudioInputStream(
params, AudioManagerBase::kDefaultDeviceId);
EXPECT_TRUE(ais);
@@ -537,7 +580,7 @@ TEST_F(AudioAndroidTest, CreateAndCloseInputStream) {
// TODO(henrika): should we also verify that this API changes the audio mode
// to communication mode, and calls RegisterHeadsetReceiver, the first time
// it is called?
-TEST_F(AudioAndroidTest, CreateAndCloseOutputStream) {
+TEST_F(AudioAndroidOutputTest, CreateAndCloseOutputStream) {
AudioParameters params = GetDefaultOutputStreamParameters();
AudioOutputStream* aos = audio_manager()->MakeAudioOutputStream(
params, std::string(), std::string());
@@ -546,8 +589,8 @@ TEST_F(AudioAndroidTest, CreateAndCloseOutputStream) {
}
// Ensure that a default input stream can be opened and closed.
-TEST_F(AudioAndroidTest, OpenAndCloseInputStream) {
- AudioParameters params = GetDefaultInputStreamParameters();
+TEST_P(AudioAndroidInputTest, OpenAndCloseInputStream) {
+ AudioParameters params = GetInputStreamParameters();
AudioInputStream* ais = audio_manager()->MakeAudioInputStream(
params, AudioManagerBase::kDefaultDeviceId);
EXPECT_TRUE(ais);
@@ -556,7 +599,7 @@ TEST_F(AudioAndroidTest, OpenAndCloseInputStream) {
}
// Ensure that a default output stream can be opened and closed.
-TEST_F(AudioAndroidTest, OpenAndCloseOutputStream) {
+TEST_F(AudioAndroidOutputTest, OpenAndCloseOutputStream) {
AudioParameters params = GetDefaultOutputStreamParameters();
AudioOutputStream* aos = audio_manager()->MakeAudioOutputStream(
params, std::string(), std::string());
@@ -567,8 +610,8 @@ TEST_F(AudioAndroidTest, OpenAndCloseOutputStream) {
// Start input streaming using default input parameters and ensure that the
// callback sequence is sane.
-TEST_F(AudioAndroidTest, StartInputStreamCallbacks) {
- AudioParameters params = GetDefaultInputStreamParameters();
+TEST_P(AudioAndroidInputTest, StartInputStreamCallbacks) {
+ AudioParameters params = GetInputStreamParameters();
StartInputStreamCallbacks(params);
}
@@ -576,19 +619,21 @@ TEST_F(AudioAndroidTest, StartInputStreamCallbacks) {
// callback sequence is sane. The only change we make in this test is to select
// a 10ms buffer size instead of the default size.
// TODO(henrika): possibly add support for more variations.
-TEST_F(AudioAndroidTest, StartInputStreamCallbacksNonDefaultParameters) {
- AudioParameters native_params = GetDefaultInputStreamParameters();
+TEST_P(AudioAndroidInputTest, StartInputStreamCallbacksNonDefaultParameters) {
+ AudioParameters native_params = GetInputStreamParameters();
AudioParameters params(native_params.format(),
native_params.channel_layout(),
+ native_params.input_channels(),
native_params.sample_rate(),
native_params.bits_per_sample(),
- native_params.sample_rate() / 100);
+ native_params.sample_rate() / 100,
+ native_params.effects());
StartInputStreamCallbacks(params);
}
// Start output streaming using default output parameters and ensure that the
// callback sequence is sane.
-TEST_F(AudioAndroidTest, StartOutputStreamCallbacks) {
+TEST_F(AudioAndroidOutputTest, StartOutputStreamCallbacks) {
AudioParameters params = GetDefaultOutputStreamParameters();
StartOutputStreamCallbacks(params);
}
@@ -598,7 +643,7 @@ TEST_F(AudioAndroidTest, StartOutputStreamCallbacks) {
// select a 10ms buffer size instead of the default size and to open up the
// device in mono.
// TODO(henrika): possibly add support for more variations.
-TEST_F(AudioAndroidTest, StartOutputStreamCallbacksNonDefaultParameters) {
+TEST_F(AudioAndroidOutputTest, StartOutputStreamCallbacksNonDefaultParameters) {
AudioParameters native_params = GetDefaultOutputStreamParameters();
AudioParameters params(native_params.format(),
CHANNEL_LAYOUT_MONO,
@@ -612,7 +657,7 @@ TEST_F(AudioAndroidTest, StartOutputStreamCallbacksNonDefaultParameters) {
// the rendered audio sounds OK.
// NOTE: this test requires user interaction and is not designed to run as an
// automatized test on bots.
-TEST_F(AudioAndroidTest, DISABLED_RunOutputStreamWithFileAsSource) {
+TEST_F(AudioAndroidOutputTest, DISABLED_RunOutputStreamWithFileAsSource) {
AudioParameters params = GetDefaultOutputStreamParameters();
VLOG(1) << params;
AudioOutputStream* aos = audio_manager()->MakeAudioOutputStream(
@@ -639,7 +684,7 @@ TEST_F(AudioAndroidTest, DISABLED_RunOutputStreamWithFileAsSource) {
EXPECT_TRUE(aos->Open());
aos->SetVolume(1.0);
aos->Start(&source);
- LOG(INFO) << ">> Verify that the file is played out correctly...";
+ VLOG(0) << ">> Verify that the file is played out correctly...";
EXPECT_TRUE(event.TimedWait(TestTimeouts::action_max_timeout()));
aos->Stop();
aos->Close();
@@ -649,8 +694,8 @@ TEST_F(AudioAndroidTest, DISABLED_RunOutputStreamWithFileAsSource) {
// local audio file.
// NOTE: this test requires user interaction and is not designed to run as an
// automatized test on bots.
-TEST_F(AudioAndroidTest, DISABLED_RunSimplexInputStreamWithFileAsSink) {
- AudioParameters params = GetDefaultInputStreamParameters();
+TEST_P(AudioAndroidInputTest, DISABLED_RunSimplexInputStreamWithFileAsSink) {
+ AudioParameters params = GetInputStreamParameters();
VLOG(1) << params;
AudioInputStream* ais = audio_manager()->MakeAudioInputStream(
params, AudioManagerBase::kDefaultDeviceId);
@@ -666,7 +711,7 @@ TEST_F(AudioAndroidTest, DISABLED_RunSimplexInputStreamWithFileAsSink) {
EXPECT_TRUE(ais->Open());
ais->Start(&sink);
- LOG(INFO) << ">> Speak into the microphone to record audio...";
+ VLOG(0) << ">> Speak into the microphone to record audio...";
EXPECT_TRUE(event.TimedWait(TestTimeouts::action_max_timeout()));
ais->Stop();
ais->Close();
@@ -676,8 +721,8 @@ TEST_F(AudioAndroidTest, DISABLED_RunSimplexInputStreamWithFileAsSink) {
// streaming is active as well (reads zeros only).
// NOTE: this test requires user interaction and is not designed to run as an
// automatized test on bots.
-TEST_F(AudioAndroidTest, DISABLED_RunDuplexInputStreamWithFileAsSink) {
- AudioParameters in_params = GetDefaultInputStreamParameters();
+TEST_P(AudioAndroidInputTest, DISABLED_RunDuplexInputStreamWithFileAsSink) {
+ AudioParameters in_params = GetInputStreamParameters();
AudioInputStream* ais = audio_manager()->MakeAudioInputStream(
in_params, AudioManagerBase::kDefaultDeviceId);
EXPECT_TRUE(ais);
@@ -706,7 +751,7 @@ TEST_F(AudioAndroidTest, DISABLED_RunDuplexInputStreamWithFileAsSink) {
EXPECT_TRUE(aos->Open());
ais->Start(&sink);
aos->Start(&source);
- LOG(INFO) << ">> Speak into the microphone to record audio";
+ VLOG(0) << ">> Speak into the microphone to record audio";
EXPECT_TRUE(event.TimedWait(TestTimeouts::action_max_timeout()));
aos->Stop();
ais->Stop();
@@ -720,10 +765,10 @@ TEST_F(AudioAndroidTest, DISABLED_RunDuplexInputStreamWithFileAsSink) {
// printed out during the test.
// NOTE: this test requires user interaction and is not designed to run as an
// automatized test on bots.
-TEST_F(AudioAndroidTest,
+TEST_P(AudioAndroidInputTest,
DISABLED_RunSymmetricInputAndOutputStreamsInFullDuplex) {
// Get native audio parameters for the input side.
- AudioParameters default_input_params = GetDefaultInputStreamParameters();
+ AudioParameters default_input_params = GetInputStreamParameters();
// Modify the parameters so that both input and output can use the same
// parameters by selecting 10ms as buffer size. This will also ensure that
@@ -756,7 +801,7 @@ TEST_F(AudioAndroidTest,
aos->Start(&full_duplex);
VLOG(1) << "HINT: an estimate of the extra FIFO delay will be updated "
<< "once per second during this test.";
- LOG(INFO) << ">> Speak into the mic and listen to the audio in loopback...";
+ VLOG(0) << ">> Speak into the mic and listen to the audio in loopback...";
fflush(stdout);
base::PlatformThread::Sleep(base::TimeDelta::FromSeconds(20));
printf("\n");
@@ -766,4 +811,7 @@ TEST_F(AudioAndroidTest,
ais->Close();
}
+INSTANTIATE_TEST_CASE_P(AudioAndroidInputTest, AudioAndroidInputTest,
+ testing::ValuesIn(RunAudioRecordInputPathTests()));
+
} // namespace media
diff --git a/chromium/media/audio/android/audio_manager_android.cc b/chromium/media/audio/android/audio_manager_android.cc
index 04b226fa64f..3464d89a30f 100644
--- a/chromium/media/audio/android/audio_manager_android.cc
+++ b/chromium/media/audio/android/audio_manager_android.cc
@@ -4,16 +4,27 @@
#include "media/audio/android/audio_manager_android.h"
+#include "base/android/build_info.h"
+#include "base/android/jni_array.h"
+#include "base/android/jni_string.h"
+#include "base/android/scoped_java_ref.h"
#include "base/logging.h"
+#include "base/strings/string_number_conversions.h"
#include "jni/AudioManagerAndroid_jni.h"
+#include "media/audio/android/audio_record_input.h"
#include "media/audio/android/opensles_input.h"
#include "media/audio/android/opensles_output.h"
#include "media/audio/audio_manager.h"
#include "media/audio/audio_parameters.h"
-#include "media/audio/audio_util.h"
#include "media/audio/fake_audio_input_stream.h"
#include "media/base/channel_layout.h"
+using base::android::AppendJavaStringArrayToStringVector;
+using base::android::AttachCurrentThread;
+using base::android::ConvertJavaStringToUTF8;
+using base::android::ConvertUTF8ToJavaString;
+using base::android::ScopedJavaLocalRef;
+
namespace media {
static void AddDefaultDevice(AudioDeviceNames* device_names) {
@@ -32,20 +43,24 @@ static const int kAudioModeInCommunication = 0x00000003;
static const int kDefaultInputBufferSize = 1024;
static const int kDefaultOutputBufferSize = 2048;
-AudioManager* CreateAudioManager() {
- return new AudioManagerAndroid();
+AudioManager* CreateAudioManager(AudioLogFactory* audio_log_factory) {
+ return new AudioManagerAndroid(audio_log_factory);
}
-AudioManagerAndroid::AudioManagerAndroid() {
+AudioManagerAndroid::AudioManagerAndroid(AudioLogFactory* audio_log_factory)
+ : AudioManagerBase(audio_log_factory) {
SetMaxOutputStreamsAllowed(kMaxOutputStreams);
j_audio_manager_.Reset(
Java_AudioManagerAndroid_createAudioManagerAndroid(
base::android::AttachCurrentThread(),
- base::android::GetApplicationContext()));
+ base::android::GetApplicationContext(),
+ reinterpret_cast<intptr_t>(this)));
+ Init();
}
AudioManagerAndroid::~AudioManagerAndroid() {
+ Close();
Shutdown();
}
@@ -59,28 +74,52 @@ bool AudioManagerAndroid::HasAudioInputDevices() {
void AudioManagerAndroid::GetAudioInputDeviceNames(
AudioDeviceNames* device_names) {
+ // Always add default device parameters as first element.
AddDefaultDevice(device_names);
+
+ JNIEnv* env = AttachCurrentThread();
+ ScopedJavaLocalRef<jobjectArray> j_device_array =
+ Java_AudioManagerAndroid_getAudioInputDeviceNames(
+ env, j_audio_manager_.obj());
+ jsize len = env->GetArrayLength(j_device_array.obj());
+ AudioDeviceName device;
+ for (jsize i = 0; i < len; ++i) {
+ ScopedJavaLocalRef<jobject> j_device(
+ env, env->GetObjectArrayElement(j_device_array.obj(), i));
+ ScopedJavaLocalRef<jstring> j_device_name =
+ Java_AudioDeviceName_name(env, j_device.obj());
+ ConvertJavaStringToUTF8(env, j_device_name.obj(), &device.device_name);
+ ScopedJavaLocalRef<jstring> j_device_id =
+ Java_AudioDeviceName_id(env, j_device.obj());
+ ConvertJavaStringToUTF8(env, j_device_id.obj(), &device.unique_id);
+ device_names->push_back(device);
+ }
}
void AudioManagerAndroid::GetAudioOutputDeviceNames(
AudioDeviceNames* device_names) {
+ // TODO(henrika): enumerate using GetAudioInputDeviceNames().
AddDefaultDevice(device_names);
}
AudioParameters AudioManagerAndroid::GetInputStreamParameters(
const std::string& device_id) {
+ JNIEnv* env = AttachCurrentThread();
// Use mono as preferred number of input channels on Android to save
// resources. Using mono also avoids a driver issue seen on Samsung
// Galaxy S3 and S4 devices. See http://crbug.com/256851 for details.
ChannelLayout channel_layout = CHANNEL_LAYOUT_MONO;
int buffer_size = Java_AudioManagerAndroid_getMinInputFrameSize(
- base::android::AttachCurrentThread(), GetNativeOutputSampleRate(),
+ env, GetNativeOutputSampleRate(),
ChannelLayoutToChannelCount(channel_layout));
-
- return AudioParameters(
- AudioParameters::AUDIO_PCM_LOW_LATENCY, channel_layout,
+ int effects = AudioParameters::NO_EFFECTS;
+ effects |= Java_AudioManagerAndroid_shouldUseAcousticEchoCanceler(env) ?
+ AudioParameters::ECHO_CANCELLER : AudioParameters::NO_EFFECTS;
+ AudioParameters params(
+ AudioParameters::AUDIO_PCM_LOW_LATENCY, channel_layout, 0,
GetNativeOutputSampleRate(), 16,
- buffer_size <= 0 ? kDefaultInputBufferSize : buffer_size);
+ buffer_size <= 0 ? kDefaultInputBufferSize : buffer_size, effects);
+ return params;
}
AudioOutputStream* AudioManagerAndroid::MakeAudioOutputStream(
@@ -92,8 +131,13 @@ AudioOutputStream* AudioManagerAndroid::MakeAudioOutputStream(
std::string());
if (stream && output_stream_count() == 1) {
SetAudioMode(kAudioModeInCommunication);
- RegisterHeadsetReceiver();
}
+
+ {
+ base::AutoLock lock(streams_lock_);
+ streams_.insert(static_cast<OpenSLESOutputStream*>(stream));
+ }
+
return stream;
}
@@ -107,9 +151,10 @@ AudioInputStream* AudioManagerAndroid::MakeAudioInputStream(
void AudioManagerAndroid::ReleaseOutputStream(AudioOutputStream* stream) {
AudioManagerBase::ReleaseOutputStream(stream);
if (!output_stream_count()) {
- UnregisterHeadsetReceiver();
SetAudioMode(kAudioModeNormal);
}
+ base::AutoLock lock(streams_lock_);
+ streams_.erase(static_cast<OpenSLESOutputStream*>(stream));
}
void AudioManagerAndroid::ReleaseInputStream(AudioInputStream* stream) {
@@ -133,6 +178,9 @@ AudioOutputStream* AudioManagerAndroid::MakeLowLatencyOutputStream(
AudioInputStream* AudioManagerAndroid::MakeLinearInputStream(
const AudioParameters& params, const std::string& device_id) {
+ // TODO(henrika): add support for device selection if/when any client
+ // needs it.
+ DLOG_IF(ERROR, !device_id.empty()) << "Not implemented!";
DCHECK_EQ(AudioParameters::AUDIO_PCM_LINEAR, params.format());
return new OpenSLESInputStream(this, params);
}
@@ -140,6 +188,26 @@ AudioInputStream* AudioManagerAndroid::MakeLinearInputStream(
AudioInputStream* AudioManagerAndroid::MakeLowLatencyInputStream(
const AudioParameters& params, const std::string& device_id) {
DCHECK_EQ(AudioParameters::AUDIO_PCM_LOW_LATENCY, params.format());
+ DLOG_IF(ERROR, device_id.empty()) << "Invalid device ID!";
+ // Utilize the device ID to select the correct input device.
+ // Note that the input device is always associated with a certain output
+ // device, i.e., this selection does also switch the output device.
+ // All input and output streams will be affected by the device selection.
+ SetAudioDevice(device_id);
+
+ if (params.effects() != AudioParameters::NO_EFFECTS) {
+ // Platform effects can only be enabled through the AudioRecord path.
+ // An effect should only have been requested here if recommended by
+ // AudioManagerAndroid.shouldUse<Effect>.
+ //
+ // Creating this class requires Jelly Bean, which is already guaranteed by
+ // shouldUse<Effect>. Only DCHECK on that condition to allow tests to use
+ // the effect settings as a way to select the input path.
+ DCHECK_GE(base::android::BuildInfo::GetInstance()->sdk_int(), 16);
+ DVLOG(1) << "Creating AudioRecordInputStream";
+ return new AudioRecordInputStream(this, params);
+ }
+ DVLOG(1) << "Creating OpenSLESInputStream";
return new OpenSLESInputStream(this, params);
}
@@ -181,7 +249,7 @@ AudioParameters AudioManagerAndroid::GetPreferredOutputStreamParameters(
return AudioParameters(
AudioParameters::AUDIO_PCM_LOW_LATENCY, channel_layout, input_channels,
- sample_rate, bits_per_sample, buffer_size);
+ sample_rate, bits_per_sample, buffer_size, AudioParameters::NO_EFFECTS);
}
// static
@@ -189,22 +257,53 @@ bool AudioManagerAndroid::RegisterAudioManager(JNIEnv* env) {
return RegisterNativesImpl(env);
}
-void AudioManagerAndroid::SetAudioMode(int mode) {
- Java_AudioManagerAndroid_setMode(
+void AudioManagerAndroid::Init() {
+ Java_AudioManagerAndroid_init(
base::android::AttachCurrentThread(),
- j_audio_manager_.obj(), mode);
+ j_audio_manager_.obj());
}
-void AudioManagerAndroid::RegisterHeadsetReceiver() {
- Java_AudioManagerAndroid_registerHeadsetReceiver(
+void AudioManagerAndroid::Close() {
+ Java_AudioManagerAndroid_close(
base::android::AttachCurrentThread(),
j_audio_manager_.obj());
}
-void AudioManagerAndroid::UnregisterHeadsetReceiver() {
- Java_AudioManagerAndroid_unregisterHeadsetReceiver(
+void AudioManagerAndroid::SetMute(JNIEnv* env, jobject obj, jboolean muted) {
+ GetMessageLoop()->PostTask(
+ FROM_HERE,
+ base::Bind(
+ &AudioManagerAndroid::DoSetMuteOnAudioThread,
+ base::Unretained(this),
+ muted));
+}
+
+void AudioManagerAndroid::DoSetMuteOnAudioThread(bool muted) {
+ base::AutoLock lock(streams_lock_);
+ for (OutputStreams::iterator it = streams_.begin();
+ it != streams_.end(); ++it) {
+ (*it)->SetMute(muted);
+ }
+}
+
+void AudioManagerAndroid::SetAudioMode(int mode) {
+ Java_AudioManagerAndroid_setMode(
base::android::AttachCurrentThread(),
- j_audio_manager_.obj());
+ j_audio_manager_.obj(), mode);
+}
+
+void AudioManagerAndroid::SetAudioDevice(const std::string& device_id) {
+ JNIEnv* env = AttachCurrentThread();
+
+ // Send the unique device ID to the Java audio manager and make the
+ // device switch. Provide an empty string to the Java audio manager
+ // if the default device is selected.
+ ScopedJavaLocalRef<jstring> j_device_id = ConvertUTF8ToJavaString(
+ env,
+ device_id == AudioManagerBase::kDefaultDeviceId ?
+ std::string() : device_id);
+ Java_AudioManagerAndroid_setDevice(
+ env, j_audio_manager_.obj(), j_device_id.obj());
}
int AudioManagerAndroid::GetNativeOutputSampleRate() {
diff --git a/chromium/media/audio/android/audio_manager_android.h b/chromium/media/audio/android/audio_manager_android.h
index ed2b2c3ce91..2900c0f8e29 100644
--- a/chromium/media/audio/android/audio_manager_android.h
+++ b/chromium/media/audio/android/audio_manager_android.h
@@ -5,16 +5,21 @@
#ifndef MEDIA_AUDIO_ANDROID_AUDIO_MANAGER_ANDROID_H_
#define MEDIA_AUDIO_ANDROID_AUDIO_MANAGER_ANDROID_H_
+#include <set>
+
#include "base/android/jni_android.h"
#include "base/gtest_prod_util.h"
+#include "base/synchronization/lock.h"
#include "media/audio/audio_manager_base.h"
namespace media {
+class OpenSLESOutputStream;
+
// Android implemention of AudioManager.
class MEDIA_EXPORT AudioManagerAndroid : public AudioManagerBase {
public:
- AudioManagerAndroid();
+ AudioManagerAndroid(AudioLogFactory* audio_log_factory);
// Implementation of AudioManager.
virtual bool HasAudioOutputDevices() OVERRIDE;
@@ -52,6 +57,8 @@ class MEDIA_EXPORT AudioManagerAndroid : public AudioManagerBase {
static bool RegisterAudioManager(JNIEnv* env);
+ void SetMute(JNIEnv* env, jobject obj, jboolean muted);
+
protected:
virtual ~AudioManagerAndroid();
@@ -60,20 +67,30 @@ class MEDIA_EXPORT AudioManagerAndroid : public AudioManagerBase {
const AudioParameters& input_params) OVERRIDE;
private:
+ void Init();
+ void Close();
void SetAudioMode(int mode);
- void RegisterHeadsetReceiver();
- void UnregisterHeadsetReceiver();
+ void SetAudioDevice(const std::string& device_id);
int GetNativeOutputSampleRate();
bool IsAudioLowLatencySupported();
int GetAudioLowLatencyOutputFrameSize();
int GetOptimalOutputFrameSize(int sample_rate, int channels);
+ void DoSetMuteOnAudioThread(bool muted);
+
// Allow the AudioAndroidTest to access private methods.
- FRIEND_TEST_ALL_PREFIXES(AudioAndroidTest, IsAudioLowLatencySupported);
+ FRIEND_TEST_ALL_PREFIXES(AudioAndroidOutputTest, IsAudioLowLatencySupported);
// Java AudioManager instance.
base::android::ScopedJavaGlobalRef<jobject> j_audio_manager_;
+ typedef std::set<OpenSLESOutputStream*> OutputStreams;
+ OutputStreams streams_;
+ // TODO(wjia): remove this lock once unit test modules are fixed to call
+ // AudioManager::MakeAudioOutputStream on the audio thread. For now, this
+ // lock is used to guard access to |streams_|.
+ base::Lock streams_lock_;
+
DISALLOW_COPY_AND_ASSIGN(AudioManagerAndroid);
};
diff --git a/chromium/media/audio/android/audio_record_input.cc b/chromium/media/audio/android/audio_record_input.cc
new file mode 100644
index 00000000000..15a0c3d3b7b
--- /dev/null
+++ b/chromium/media/audio/android/audio_record_input.cc
@@ -0,0 +1,131 @@
+// Copyright 2013 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/android/audio_record_input.h"
+
+#include "base/logging.h"
+#include "jni/AudioRecordInput_jni.h"
+#include "media/audio/android/audio_manager_android.h"
+
+namespace media {
+
+AudioRecordInputStream::AudioRecordInputStream(
+ AudioManagerAndroid* audio_manager, const AudioParameters& params)
+ : audio_manager_(audio_manager),
+ callback_(NULL),
+ direct_buffer_address_(NULL) {
+ DVLOG(2) << __PRETTY_FUNCTION__;
+ DCHECK(params.IsValid());
+ j_audio_record_.Reset(
+ Java_AudioRecordInput_createAudioRecordInput(
+ base::android::AttachCurrentThread(),
+ reinterpret_cast<intptr_t>(this),
+ params.sample_rate(),
+ params.channels(),
+ params.bits_per_sample(),
+ params.GetBytesPerBuffer(),
+ params.effects() & AudioParameters::ECHO_CANCELLER));
+}
+
+AudioRecordInputStream::~AudioRecordInputStream() {
+ DVLOG(2) << __PRETTY_FUNCTION__;
+ DCHECK(thread_checker_.CalledOnValidThread());
+}
+
+void AudioRecordInputStream::CacheDirectBufferAddress(JNIEnv* env, jobject obj,
+ jobject byte_buffer) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ direct_buffer_address_ = static_cast<uint8*>(
+ env->GetDirectBufferAddress(byte_buffer));
+}
+
+// static
+bool AudioRecordInputStream::RegisterAudioRecordInput(JNIEnv* env) {
+ return RegisterNativesImpl(env);
+}
+
+void AudioRecordInputStream::OnData(JNIEnv* env, jobject obj, jint size,
+ jint hardware_delay_bytes) {
+ DCHECK(direct_buffer_address_);
+ // Passing zero as the volume parameter indicates there is no access to a
+ // hardware volume slider.
+ callback_->OnData(this, direct_buffer_address_, size, hardware_delay_bytes,
+ 0.0);
+}
+
+bool AudioRecordInputStream::Open() {
+ DVLOG(2) << __PRETTY_FUNCTION__;
+ DCHECK(thread_checker_.CalledOnValidThread());
+ return Java_AudioRecordInput_open(
+ base::android::AttachCurrentThread(), j_audio_record_.obj());
+}
+
+void AudioRecordInputStream::Start(AudioInputCallback* callback) {
+ DVLOG(2) << __PRETTY_FUNCTION__;
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(callback);
+
+ if (callback_) {
+ // Start() was already called.
+ DCHECK_EQ(callback_, callback);
+ return;
+ }
+ // The Java thread has not yet started, so we are free to set |callback_|.
+ callback_ = callback;
+
+ Java_AudioRecordInput_start(
+ base::android::AttachCurrentThread(), j_audio_record_.obj());
+}
+
+void AudioRecordInputStream::Stop() {
+ DVLOG(2) << __PRETTY_FUNCTION__;
+ DCHECK(thread_checker_.CalledOnValidThread());
+ if (!callback_) {
+ // Start() was never called, or Stop() was already called.
+ return;
+ }
+
+ Java_AudioRecordInput_stop(
+ base::android::AttachCurrentThread(), j_audio_record_.obj());
+
+ // The Java thread must have been stopped at this point, so we are free to
+ // set |callback_|.
+ callback_->OnClose(this);
+ callback_ = NULL;
+}
+
+void AudioRecordInputStream::Close() {
+ DVLOG(2) << __PRETTY_FUNCTION__;
+ DCHECK(thread_checker_.CalledOnValidThread());
+ Stop();
+ DCHECK(!callback_);
+ Java_AudioRecordInput_close(
+ base::android::AttachCurrentThread(), j_audio_record_.obj());
+ audio_manager_->ReleaseInputStream(this);
+}
+
+double AudioRecordInputStream::GetMaxVolume() {
+ NOTIMPLEMENTED();
+ return 0.0;
+}
+
+void AudioRecordInputStream::SetVolume(double volume) {
+ NOTIMPLEMENTED();
+}
+
+double AudioRecordInputStream::GetVolume() {
+ NOTIMPLEMENTED();
+ return 0.0;
+}
+
+void AudioRecordInputStream::SetAutomaticGainControl(bool enabled) {
+ NOTIMPLEMENTED();
+}
+
+bool AudioRecordInputStream::GetAutomaticGainControl() {
+ NOTIMPLEMENTED();
+ return false;
+}
+
+} // namespace media
diff --git a/chromium/media/audio/android/audio_record_input.h b/chromium/media/audio/android/audio_record_input.h
new file mode 100644
index 00000000000..0a2578b1079
--- /dev/null
+++ b/chromium/media/audio/android/audio_record_input.h
@@ -0,0 +1,72 @@
+// Copyright 2013 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_ANDROID_AUDIO_RECORD_INPUT_H_
+#define MEDIA_AUDIO_ANDROID_AUDIO_RECORD_INPUT_H_
+
+#include "base/android/jni_android.h"
+#include "base/threading/thread_checker.h"
+#include "media/audio/audio_io.h"
+#include "media/audio/audio_parameters.h"
+
+namespace media {
+
+class AudioManagerAndroid;
+
+// Implements PCM audio input support for Android using the Java AudioRecord
+// interface. Most of the work is done by its Java counterpart in
+// AudioRecordInput.java. This class is created and lives on the Audio Manager
+// thread but recorded audio buffers are delivered on a thread managed by
+// the Java class.
+//
+// The Java class makes use of AudioEffect features which are first available
+// in Jelly Bean. It should not be instantiated running against earlier SDKs.
+class MEDIA_EXPORT AudioRecordInputStream : public AudioInputStream {
+ public:
+ AudioRecordInputStream(AudioManagerAndroid* manager,
+ const AudioParameters& params);
+
+ virtual ~AudioRecordInputStream();
+
+ // Implementation of AudioInputStream.
+ virtual bool Open() OVERRIDE;
+ virtual void Start(AudioInputCallback* callback) OVERRIDE;
+ virtual void Stop() OVERRIDE;
+ virtual void Close() OVERRIDE;
+ virtual double GetMaxVolume() OVERRIDE;
+ virtual void SetVolume(double volume) OVERRIDE;
+ virtual double GetVolume() OVERRIDE;
+ virtual void SetAutomaticGainControl(bool enabled) OVERRIDE;
+ virtual bool GetAutomaticGainControl() OVERRIDE;
+
+ static bool RegisterAudioRecordInput(JNIEnv* env);
+
+ // Called from Java when data is available.
+ void OnData(JNIEnv* env, jobject obj, jint size, jint hardware_delay_bytes);
+
+ // Called from Java so that we can cache the address of the Java-managed
+ // |byte_buffer| in |direct_buffer_address_|.
+ void CacheDirectBufferAddress(JNIEnv* env, jobject obj, jobject byte_buffer);
+
+ private:
+ base::ThreadChecker thread_checker_;
+ AudioManagerAndroid* audio_manager_;
+
+ // Java AudioRecordInput instance.
+ base::android::ScopedJavaGlobalRef<jobject> j_audio_record_;
+
+ // This is the only member accessed by both the Audio Manager and Java
+ // threads. Explanations for why we do not require explicit synchronization
+ // are given in the implementation.
+ AudioInputCallback* callback_;
+
+ // Owned by j_audio_record_.
+ uint8* direct_buffer_address_;
+
+ DISALLOW_COPY_AND_ASSIGN(AudioRecordInputStream);
+};
+
+} // namespace media
+
+#endif // MEDIA_AUDIO_ANDROID_AUDIO_RECORD_INPUT_H_
diff --git a/chromium/media/audio/android/opensles_input.cc b/chromium/media/audio/android/opensles_input.cc
index a0e4ce3b987..e51ba4f3a97 100644
--- a/chromium/media/audio/android/opensles_input.cc
+++ b/chromium/media/audio/android/opensles_input.cc
@@ -28,7 +28,7 @@ OpenSLESInputStream::OpenSLESInputStream(AudioManagerAndroid* audio_manager,
active_buffer_index_(0),
buffer_size_bytes_(0),
started_(false) {
- DVLOG(2) << "OpenSLESInputStream::OpenSLESInputStream()";
+ DVLOG(2) << __PRETTY_FUNCTION__;
format_.formatType = SL_DATAFORMAT_PCM;
format_.numChannels = static_cast<SLuint32>(params.channels());
// Provides sampling rate in milliHertz to OpenSLES.
@@ -49,7 +49,7 @@ OpenSLESInputStream::OpenSLESInputStream(AudioManagerAndroid* audio_manager,
}
OpenSLESInputStream::~OpenSLESInputStream() {
- DVLOG(2) << "OpenSLESInputStream::~OpenSLESInputStream()";
+ DVLOG(2) << __PRETTY_FUNCTION__;
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(!recorder_object_.Get());
DCHECK(!engine_object_.Get());
@@ -59,7 +59,7 @@ OpenSLESInputStream::~OpenSLESInputStream() {
}
bool OpenSLESInputStream::Open() {
- DVLOG(2) << "OpenSLESInputStream::Open()";
+ DVLOG(2) << __PRETTY_FUNCTION__;
DCHECK(thread_checker_.CalledOnValidThread());
if (engine_object_.Get())
return false;
@@ -73,7 +73,7 @@ bool OpenSLESInputStream::Open() {
}
void OpenSLESInputStream::Start(AudioInputCallback* callback) {
- DVLOG(2) << "OpenSLESInputStream::Start()";
+ DVLOG(2) << __PRETTY_FUNCTION__;
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(callback);
DCHECK(recorder_);
@@ -116,7 +116,7 @@ void OpenSLESInputStream::Start(AudioInputCallback* callback) {
}
void OpenSLESInputStream::Stop() {
- DVLOG(2) << "OpenSLESInputStream::Stop()";
+ DVLOG(2) << __PRETTY_FUNCTION__;
DCHECK(thread_checker_.CalledOnValidThread());
if (!started_)
return;
@@ -135,7 +135,7 @@ void OpenSLESInputStream::Stop() {
}
void OpenSLESInputStream::Close() {
- DVLOG(2) << "OpenSLESInputStream::Close()";
+ DVLOG(2) << __PRETTY_FUNCTION__;
DCHECK(thread_checker_.CalledOnValidThread());
// Stop the stream if it is still recording.
@@ -170,7 +170,9 @@ double OpenSLESInputStream::GetMaxVolume() {
return 0.0;
}
-void OpenSLESInputStream::SetVolume(double volume) { NOTIMPLEMENTED(); }
+void OpenSLESInputStream::SetVolume(double volume) {
+ NOTIMPLEMENTED();
+}
double OpenSLESInputStream::GetVolume() {
NOTIMPLEMENTED();
diff --git a/chromium/media/audio/android/opensles_input.h b/chromium/media/audio/android/opensles_input.h
index e05831c6712..cb07d51f78b 100644
--- a/chromium/media/audio/android/opensles_input.h
+++ b/chromium/media/audio/android/opensles_input.h
@@ -21,8 +21,8 @@ class AudioManagerAndroid;
// Implements PCM audio input support for Android using the OpenSLES API.
// This class is created and lives on the Audio Manager thread but recorded
-// audio buffers are given to us from an internal OpenSLES audio thread.
-// All public methods should be called on the Audio Manager thread.
+// audio buffers are delivered on an internal OpenSLES audio thread. All public
+// methods should be called on the Audio Manager thread.
class OpenSLESInputStream : public AudioInputStream {
public:
static const int kMaxNumOfBuffersInQueue = 2;
diff --git a/chromium/media/audio/android/opensles_output.cc b/chromium/media/audio/android/opensles_output.cc
index 5643f833c3d..b71680f0a7e 100644
--- a/chromium/media/audio/android/opensles_output.cc
+++ b/chromium/media/audio/android/opensles_output.cc
@@ -28,6 +28,7 @@ OpenSLESOutputStream::OpenSLESOutputStream(AudioManagerAndroid* manager,
active_buffer_index_(0),
buffer_size_bytes_(0),
started_(false),
+ muted_(false),
volume_(1.0) {
DVLOG(2) << "OpenSLESOutputStream::OpenSLESOutputStream()";
format_.formatType = SL_DATAFORMAT_PCM;
@@ -128,6 +129,7 @@ void OpenSLESOutputStream::Stop() {
DCHECK_EQ(0u, buffer_queue_state.index);
#endif
+ callback_ = NULL;
started_ = false;
}
@@ -172,6 +174,12 @@ void OpenSLESOutputStream::GetVolume(double* volume) {
*volume = static_cast<double>(volume_);
}
+void OpenSLESOutputStream::SetMute(bool muted) {
+ DVLOG(2) << "OpenSLESOutputStream::SetMute(" << muted << ")";
+ DCHECK(thread_checker_.CalledOnValidThread());
+ muted_ = muted;
+}
+
bool OpenSLESOutputStream::CreatePlayer() {
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(!engine_object_.Get());
@@ -324,7 +332,7 @@ void OpenSLESOutputStream::FillBufferQueueNoLock() {
// Note: If the internal representation ever changes from 16-bit PCM to
// raw float, the data must be clipped and sanitized since it may come
// from an untrusted source such as NaCl.
- audio_bus_->Scale(volume_);
+ audio_bus_->Scale(muted_ ? 0.0f : volume_);
audio_bus_->ToInterleaved(frames_filled,
format_.bitsPerSample / 8,
audio_data_[active_buffer_index_]);
diff --git a/chromium/media/audio/android/opensles_output.h b/chromium/media/audio/android/opensles_output.h
index 7232d5da5f7..623b0193894 100644
--- a/chromium/media/audio/android/opensles_output.h
+++ b/chromium/media/audio/android/opensles_output.h
@@ -40,6 +40,10 @@ class OpenSLESOutputStream : public AudioOutputStream {
virtual void SetVolume(double volume) OVERRIDE;
virtual void GetVolume(double* volume) OVERRIDE;
+ // Set the value of |muted_|. It does not affect |volume_| which can be
+ // got by calling GetVolume(). See comments for |muted_| below.
+ void SetMute(bool muted);
+
private:
bool CreatePlayer();
@@ -96,6 +100,12 @@ class OpenSLESOutputStream : public AudioOutputStream {
bool started_;
+ // Volume control coming from hardware. It overrides |volume_| when it's
+ // true. Otherwise, use |volume_| for scaling.
+ // This is needed because platform voice volume never goes to zero in
+ // COMMUNICATION mode on Android.
+ bool muted_;
+
// Volume level from 0 to 1.
float volume_;
diff --git a/chromium/media/audio/audio_device_thread.cc b/chromium/media/audio/audio_device_thread.cc
index d5c1bbcebdc..daf908556d8 100644
--- a/chromium/media/audio/audio_device_thread.cc
+++ b/chromium/media/audio/audio_device_thread.cc
@@ -12,7 +12,6 @@
#include "base/message_loop/message_loop.h"
#include "base/threading/platform_thread.h"
#include "base/threading/thread_restrictions.h"
-#include "media/audio/audio_util.h"
#include "media/base/audio_bus.h"
using base::PlatformThread;
@@ -29,7 +28,8 @@ class AudioDeviceThread::Thread
public:
Thread(AudioDeviceThread::Callback* callback,
base::SyncSocket::Handle socket,
- const char* thread_name);
+ const char* thread_name,
+ bool synchronized_buffers);
void Start();
@@ -55,6 +55,7 @@ class AudioDeviceThread::Thread
base::CancelableSyncSocket socket_;
base::Lock callback_lock_;
const char* thread_name_;
+ const bool synchronized_buffers_;
DISALLOW_COPY_AND_ASSIGN(Thread);
};
@@ -68,10 +69,12 @@ AudioDeviceThread::~AudioDeviceThread() { DCHECK(!thread_.get()); }
void AudioDeviceThread::Start(AudioDeviceThread::Callback* callback,
base::SyncSocket::Handle socket,
- const char* thread_name) {
+ const char* thread_name,
+ bool synchronized_buffers) {
base::AutoLock auto_lock(thread_lock_);
- CHECK(thread_.get() == NULL);
- thread_ = new AudioDeviceThread::Thread(callback, socket, thread_name);
+ CHECK(!thread_);
+ thread_ = new AudioDeviceThread::Thread(
+ callback, socket, thread_name, synchronized_buffers);
thread_->Start();
}
@@ -85,17 +88,19 @@ void AudioDeviceThread::Stop(base::MessageLoop* loop_for_join) {
bool AudioDeviceThread::IsStopped() {
base::AutoLock auto_lock(thread_lock_);
- return thread_.get() == NULL;
+ return !thread_;
}
// AudioDeviceThread::Thread implementation
AudioDeviceThread::Thread::Thread(AudioDeviceThread::Callback* callback,
base::SyncSocket::Handle socket,
- const char* thread_name)
+ const char* thread_name,
+ bool synchronized_buffers)
: thread_(),
callback_(callback),
socket_(socket),
- thread_name_(thread_name) {
+ thread_name_(thread_name),
+ synchronized_buffers_(synchronized_buffers) {
}
AudioDeviceThread::Thread::~Thread() {
@@ -157,6 +162,7 @@ void AudioDeviceThread::Thread::ThreadMain() {
}
void AudioDeviceThread::Thread::Run() {
+ uint32 buffer_index = 0;
while (true) {
int pending_data = 0;
size_t bytes_read = socket_.Receive(&pending_data, sizeof(pending_data));
@@ -165,9 +171,21 @@ void AudioDeviceThread::Thread::Run() {
break;
}
- base::AutoLock auto_lock(callback_lock_);
- if (callback_)
- callback_->Process(pending_data);
+ {
+ base::AutoLock auto_lock(callback_lock_);
+ if (callback_)
+ callback_->Process(pending_data);
+ }
+
+ // Let the other end know which buffer we just filled. The buffer index is
+ // used to ensure the other end is getting the buffer it expects. For more
+ // details on how this works see AudioSyncReader::WaitUntilDataIsReady().
+ if (synchronized_buffers_) {
+ ++buffer_index;
+ size_t bytes_sent = socket_.Send(&buffer_index, sizeof(buffer_index));
+ if (bytes_sent != sizeof(buffer_index))
+ break;
+ }
}
}
diff --git a/chromium/media/audio/audio_device_thread.h b/chromium/media/audio/audio_device_thread.h
index 976f88359ba..7a1a6ed8c4c 100644
--- a/chromium/media/audio/audio_device_thread.h
+++ b/chromium/media/audio/audio_device_thread.h
@@ -12,7 +12,6 @@
#include "base/sync_socket.h"
#include "base/synchronization/lock.h"
#include "media/audio/audio_parameters.h"
-#include "media/audio/shared_memory_util.h"
#include "media/base/media_export.h"
namespace base {
@@ -74,10 +73,13 @@ class MEDIA_EXPORT AudioDeviceThread {
AudioDeviceThread();
~AudioDeviceThread();
- // Starts the audio thread. The thread must not already be running.
+ // Starts the audio thread. The thread must not already be running. If
+ // |sychronized_buffers| is set, the browser expects to be notified via the
+ // |socket| every time AudioDeviceThread::Process() completes.
void Start(AudioDeviceThread::Callback* callback,
base::SyncSocket::Handle socket,
- const char* thread_name);
+ const char* thread_name,
+ bool synchronized_buffers);
// This tells the audio thread to stop and clean up the data.
// The method can stop the thread synchronously or asynchronously.
diff --git a/chromium/media/audio/audio_input_controller_unittest.cc b/chromium/media/audio/audio_input_controller_unittest.cc
index 6388cbf975b..a7bb600aaf4 100644
--- a/chromium/media/audio/audio_input_controller_unittest.cc
+++ b/chromium/media/audio/audio_input_controller_unittest.cc
@@ -80,7 +80,7 @@ TEST_F(AudioInputControllerTest, CreateAndClose) {
EXPECT_CALL(event_handler, OnCreated(NotNull()))
.WillOnce(QuitMessageLoop(&message_loop_));
- scoped_ptr<AudioManager> audio_manager(AudioManager::Create());
+ scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting());
AudioParameters params(AudioParameters::AUDIO_FAKE, kChannelLayout,
kSampleRate, kBitsPerSample, kSamplesPerPacket);
@@ -118,7 +118,7 @@ TEST_F(AudioInputControllerTest, RecordAndClose) {
.WillRepeatedly(CheckCountAndPostQuitTask(&count, 10,
message_loop_.message_loop_proxy()));
- scoped_ptr<AudioManager> audio_manager(AudioManager::Create());
+ scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting());
AudioParameters params(AudioParameters::AUDIO_FAKE, kChannelLayout,
kSampleRate, kBitsPerSample, kSamplesPerPacket);
@@ -168,7 +168,7 @@ TEST_F(AudioInputControllerTest, RecordAndError) {
.Times(Exactly(1))
.WillOnce(QuitMessageLoop(&message_loop_));
- scoped_ptr<AudioManager> audio_manager(AudioManager::Create());
+ scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting());
AudioParameters params(AudioParameters::AUDIO_FAKE, kChannelLayout,
kSampleRate, kBitsPerSample, kSamplesPerPacket);
@@ -205,7 +205,7 @@ TEST_F(AudioInputControllerTest, SamplesPerPacketTooLarge) {
EXPECT_CALL(event_handler, OnCreated(NotNull()))
.Times(Exactly(0));
- scoped_ptr<AudioManager> audio_manager(AudioManager::Create());
+ scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting());
AudioParameters params(AudioParameters::AUDIO_FAKE,
kChannelLayout,
kSampleRate,
@@ -231,7 +231,7 @@ TEST_F(AudioInputControllerTest, CloseTwice) {
EXPECT_CALL(event_handler, OnRecording(NotNull()))
.Times(Exactly(1));
- scoped_ptr<AudioManager> audio_manager(AudioManager::Create());
+ scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting());
AudioParameters params(AudioParameters::AUDIO_FAKE,
kChannelLayout,
kSampleRate,
diff --git a/chromium/media/audio/audio_input_device.cc b/chromium/media/audio/audio_input_device.cc
index d7685840ecf..d1a6ab89f9f 100644
--- a/chromium/media/audio/audio_input_device.cc
+++ b/chromium/media/audio/audio_input_device.cc
@@ -138,10 +138,10 @@ void AudioInputDevice::OnStreamCreated(
return;
DCHECK(audio_thread_.IsStopped());
- audio_callback_.reset(
- new AudioInputDevice::AudioThreadCallback(
- audio_parameters_, handle, length, total_segments, callback_));
- audio_thread_.Start(audio_callback_.get(), socket_handle, "AudioInputDevice");
+ audio_callback_.reset(new AudioInputDevice::AudioThreadCallback(
+ audio_parameters_, handle, length, total_segments, callback_));
+ audio_thread_.Start(
+ audio_callback_.get(), socket_handle, "AudioInputDevice", false);
state_ = RECORDING;
ipc_->RecordStream();
diff --git a/chromium/media/audio/audio_input_unittest.cc b/chromium/media/audio/audio_input_unittest.cc
index 8adb746ee86..838cab3867a 100644
--- a/chromium/media/audio/audio_input_unittest.cc
+++ b/chromium/media/audio/audio_input_unittest.cc
@@ -77,7 +77,7 @@ static AudioInputStream* CreateTestAudioInputStream(AudioManager* audio_man) {
// Test that AudioInputStream rejects out of range parameters.
TEST(AudioInputTest, SanityOnMakeParams) {
- scoped_ptr<AudioManager> audio_man(AudioManager::Create());
+ scoped_ptr<AudioManager> audio_man(AudioManager::CreateForTesting());
if (!CanRunAudioTests(audio_man.get()))
return;
@@ -111,7 +111,7 @@ TEST(AudioInputTest, SanityOnMakeParams) {
// Test create and close of an AudioInputStream without recording audio.
TEST(AudioInputTest, CreateAndClose) {
- scoped_ptr<AudioManager> audio_man(AudioManager::Create());
+ scoped_ptr<AudioManager> audio_man(AudioManager::CreateForTesting());
if (!CanRunAudioTests(audio_man.get()))
return;
AudioInputStream* ais = CreateTestAudioInputStream(audio_man.get());
@@ -126,7 +126,7 @@ TEST(AudioInputTest, CreateAndClose) {
#endif
// Test create, open and close of an AudioInputStream without recording audio.
TEST(AudioInputTest, MAYBE_OpenAndClose) {
- scoped_ptr<AudioManager> audio_man(AudioManager::Create());
+ scoped_ptr<AudioManager> audio_man(AudioManager::CreateForTesting());
if (!CanRunAudioTests(audio_man.get()))
return;
AudioInputStream* ais = CreateTestAudioInputStream(audio_man.get());
@@ -142,7 +142,7 @@ TEST(AudioInputTest, MAYBE_OpenAndClose) {
#endif
// Test create, open, stop and close of an AudioInputStream without recording.
TEST(AudioInputTest, MAYBE_OpenStopAndClose) {
- scoped_ptr<AudioManager> audio_man(AudioManager::Create());
+ scoped_ptr<AudioManager> audio_man(AudioManager::CreateForTesting());
if (!CanRunAudioTests(audio_man.get()))
return;
AudioInputStream* ais = CreateTestAudioInputStream(audio_man.get());
@@ -159,7 +159,7 @@ TEST(AudioInputTest, MAYBE_OpenStopAndClose) {
#endif
// Test a normal recording sequence using an AudioInputStream.
TEST(AudioInputTest, MAYBE_Record) {
- scoped_ptr<AudioManager> audio_man(AudioManager::Create());
+ scoped_ptr<AudioManager> audio_man(AudioManager::CreateForTesting());
if (!CanRunAudioTests(audio_man.get()))
return;
base::MessageLoop message_loop(base::MessageLoop::TYPE_DEFAULT);
diff --git a/chromium/media/audio/audio_input_volume_unittest.cc b/chromium/media/audio/audio_input_volume_unittest.cc
index 570c045570e..e89d106f7ed 100644
--- a/chromium/media/audio/audio_input_volume_unittest.cc
+++ b/chromium/media/audio/audio_input_volume_unittest.cc
@@ -8,7 +8,6 @@
#include "base/memory/scoped_ptr.h"
#include "media/audio/audio_io.h"
#include "media/audio/audio_manager_base.h"
-#include "media/audio/audio_util.h"
#include "testing/gtest/include/gtest/gtest.h"
#if defined(OS_WIN)
@@ -39,7 +38,7 @@ double GetVolumeAfterSetVolumeOnLinux(AudioInputStream* ais,
class AudioInputVolumeTest : public ::testing::Test {
protected:
AudioInputVolumeTest()
- : audio_manager_(AudioManager::Create())
+ : audio_manager_(AudioManager::CreateForTesting())
#if defined(OS_WIN)
, com_init_(base::win::ScopedCOMInitializer::kMTA)
#endif
diff --git a/chromium/media/audio/audio_logging.h b/chromium/media/audio/audio_logging.h
new file mode 100644
index 00000000000..1d8366bad75
--- /dev/null
+++ b/chromium/media/audio/audio_logging.h
@@ -0,0 +1,84 @@
+// Copyright 2013 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_LOGGING_H_
+#define MEDIA_AUDIO_AUDIO_LOGGING_H_
+
+#include <string>
+
+#include "base/memory/scoped_ptr.h"
+
+namespace media {
+class AudioParameters;
+
+// AudioLog logs state information about an active audio component. Each method
+// takes a |component_id| along with method specific information. Its methods
+// are safe to call from any thread.
+class AudioLog {
+ public:
+ virtual ~AudioLog() {}
+
+ // Called when an audio component is created. |params| are the parameters of
+ // the created stream. |input_device_id| and |output_device_id| are the
+ // respective device ids for input and output. Either one or both may be
+ // specified.
+ virtual void OnCreated(int component_id,
+ const media::AudioParameters& params,
+ const std::string& input_device_id,
+ const std::string& output_device_id) = 0;
+
+ // Called when an audio component is started, generally this is synonymous
+ // with "playing."
+ virtual void OnStarted(int component_id) = 0;
+
+ // Called when an audio component is stopped, generally this is synonymous
+ // with "paused."
+ virtual void OnStopped(int component_id) = 0;
+
+ // Called when an audio component is closed, generally this is synonymous
+ // with "deleted."
+ virtual void OnClosed(int component_id) = 0;
+
+ // Called when an audio component encounters an error.
+ virtual void OnError(int component_id) = 0;
+
+ // Called when an audio component changes volume. |volume| is the new volume.
+ virtual void OnSetVolume(int component_id, double volume) = 0;
+};
+
+// AudioLogFactory dispenses AudioLog instances to owning classes for tracking
+// AudioComponent behavior. All AudioComponents have the concept of an owning
+// class:
+//
+// - AudioInputRendererHost for AudioInputController
+// - AudioRendererHost for AudioOutputController
+// - AudioOutputDispatcherImpl for AudioOutputStream
+//
+// Each of these owning classes may own multiple instances of each component, as
+// such each AudioLog supports logging for multiple instances.
+class AudioLogFactory {
+ public:
+ enum AudioComponent {
+ // Input controllers have a 1:1 mapping with streams, so there's no need to
+ // track both controllers and streams.
+ AUDIO_INPUT_CONTROLLER,
+ // Output controllers may or may not be backed by an active stream, so we
+ // need to track both controllers and streams.
+ AUDIO_OUTPUT_CONTROLLER,
+ AUDIO_OUTPUT_STREAM,
+ AUDIO_COMPONENT_MAX
+ };
+
+ // Create a new AudioLog object for tracking the behavior for one or more
+ // instances of the given component. Each instance of an "owning" class must
+ // create its own AudioLog.
+ virtual scoped_ptr<AudioLog> CreateAudioLog(AudioComponent component) = 0;
+
+ protected:
+ virtual ~AudioLogFactory() {}
+};
+
+} // namespace media
+
+#endif // MEDIA_AUDIO_AUDIO_LOGGING_H_
diff --git a/chromium/media/audio/audio_low_latency_input_output_unittest.cc b/chromium/media/audio/audio_low_latency_input_output_unittest.cc
index a616761294d..c0cfa6937cf 100644
--- a/chromium/media/audio/audio_low_latency_input_output_unittest.cc
+++ b/chromium/media/audio/audio_low_latency_input_output_unittest.cc
@@ -14,12 +14,13 @@
#include "build/build_config.h"
#include "media/audio/audio_io.h"
#include "media/audio/audio_manager_base.h"
+#include "media/audio/fake_audio_log_factory.h"
#include "media/base/seekable_buffer.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
-#if defined(OS_LINUX) || defined(OS_OPENBSD)
-#include "media/audio/linux/audio_manager_linux.h"
+#if defined(USE_ALSA)
+#include "media/audio/alsa/audio_manager_alsa.h"
#elif defined(OS_MACOSX)
#include "media/audio/mac/audio_manager_mac.h"
#elif defined(OS_WIN)
@@ -27,18 +28,22 @@
#include "media/audio/win/core_audio_util_win.h"
#elif defined(OS_ANDROID)
#include "media/audio/android/audio_manager_android.h"
+#else
+#include "media/audio/fake_audio_manager.h"
#endif
namespace media {
-#if defined(OS_LINUX) || defined(OS_OPENBSD)
-typedef AudioManagerLinux AudioManagerAnyPlatform;
+#if defined(USE_ALSA)
+typedef AudioManagerAlsa AudioManagerAnyPlatform;
#elif defined(OS_MACOSX)
typedef AudioManagerMac AudioManagerAnyPlatform;
#elif defined(OS_WIN)
typedef AudioManagerWin AudioManagerAnyPlatform;
#elif defined(OS_ANDROID)
typedef AudioManagerAndroid AudioManagerAnyPlatform;
+#else
+typedef FakeAudioManager AudioManagerAnyPlatform;
#endif
// Limits the number of delay measurements we can store in an array and
@@ -80,7 +85,7 @@ struct AudioDelayState {
// the main thread instead of the audio thread.
class MockAudioManager : public AudioManagerAnyPlatform {
public:
- MockAudioManager() {}
+ MockAudioManager() : AudioManagerAnyPlatform(&fake_audio_log_factory_) {}
virtual ~MockAudioManager() {}
virtual scoped_refptr<base::MessageLoopProxy> GetMessageLoop() OVERRIDE {
@@ -88,6 +93,7 @@ class MockAudioManager : public AudioManagerAnyPlatform {
}
private:
+ FakeAudioLogFactory fake_audio_log_factory_;
DISALLOW_COPY_AND_ASSIGN(MockAudioManager);
};
@@ -156,9 +162,9 @@ class FullDuplexAudioSinkSource
EXPECT_TRUE(PathService::Get(base::DIR_EXE, &file_name));
file_name = file_name.AppendASCII(kDelayValuesFileName);
- FILE* text_file = file_util::OpenFile(file_name, "wt");
+ FILE* text_file = base::OpenFile(file_name, "wt");
DLOG_IF(ERROR, !text_file) << "Failed to open log file.";
- LOG(INFO) << ">> Output file " << file_name.value() << " has been created.";
+ VLOG(0) << ">> Output file " << file_name.value() << " has been created.";
// Write the array which contains time-stamps, buffer size and
// audio delays values to a text file.
@@ -174,7 +180,7 @@ class FullDuplexAudioSinkSource
++elements_written;
}
- file_util::CloseFile(text_file);
+ base::CloseFile(text_file);
}
// AudioInputStream::AudioInputCallback.
@@ -421,10 +427,10 @@ TEST_F(AudioLowLatencyInputOutputTest, DISABLED_FullDuplexDelayMeasurement) {
FullDuplexAudioSinkSource full_duplex(
aisw.sample_rate(), aisw.samples_per_packet(), aisw.channels());
- LOG(INFO) << ">> You should now be able to hear yourself in loopback...";
- DLOG(INFO) << " sample_rate : " << aisw.sample_rate();
- DLOG(INFO) << " samples_per_packet: " << aisw.samples_per_packet();
- DLOG(INFO) << " channels : " << aisw.channels();
+ VLOG(0) << ">> You should now be able to hear yourself in loopback...";
+ DVLOG(0) << " sample_rate : " << aisw.sample_rate();
+ DVLOG(0) << " samples_per_packet: " << aisw.samples_per_packet();
+ DVLOG(0) << " channels : " << aisw.channels();
ais->Start(&full_duplex);
aos->Start(&full_duplex);
diff --git a/chromium/media/audio/audio_manager.cc b/chromium/media/audio/audio_manager.cc
index 3f49a45ad87..03eeb171d70 100644
--- a/chromium/media/audio/audio_manager.cc
+++ b/chromium/media/audio/audio_manager.cc
@@ -4,12 +4,12 @@
#include "media/audio/audio_manager.h"
-#include "base/at_exit.h"
-#include "base/atomicops.h"
#include "base/bind.h"
#include "base/bind_helpers.h"
+#include "base/lazy_instance.h"
#include "base/logging.h"
#include "base/message_loop/message_loop.h"
+#include "media/audio/fake_audio_log_factory.h"
namespace media {
namespace {
@@ -17,24 +17,30 @@ AudioManager* g_last_created = NULL;
}
// Forward declaration of the platform specific AudioManager factory function.
-AudioManager* CreateAudioManager();
+AudioManager* CreateAudioManager(AudioLogFactory* audio_log_factory);
-AudioManager::AudioManager() {
-}
+AudioManager::AudioManager() {}
AudioManager::~AudioManager() {
- CHECK(g_last_created == NULL || g_last_created == this);
+ CHECK(!g_last_created || g_last_created == this);
g_last_created = NULL;
}
// static
-AudioManager* AudioManager::Create() {
- CHECK(g_last_created == NULL);
- g_last_created = CreateAudioManager();
+AudioManager* AudioManager::Create(AudioLogFactory* audio_log_factory) {
+ CHECK(!g_last_created);
+ g_last_created = CreateAudioManager(audio_log_factory);
return g_last_created;
}
// static
+AudioManager* AudioManager::CreateForTesting() {
+ static base::LazyInstance<FakeAudioLogFactory>::Leaky fake_log_factory =
+ LAZY_INSTANCE_INITIALIZER;
+ return Create(fake_log_factory.Pointer());
+}
+
+// static
AudioManager* AudioManager::Get() {
return g_last_created;
}
diff --git a/chromium/media/audio/audio_manager.h b/chromium/media/audio/audio_manager.h
index 891d2a26589..0ca468ed4dd 100644
--- a/chromium/media/audio/audio_manager.h
+++ b/chromium/media/audio/audio_manager.h
@@ -11,6 +11,7 @@
#include "base/memory/ref_counted.h"
#include "base/strings/string16.h"
#include "media/audio/audio_device_name.h"
+#include "media/audio/audio_logging.h"
#include "media/audio/audio_parameters.h"
namespace base {
@@ -23,16 +24,19 @@ namespace media {
class AudioInputStream;
class AudioOutputStream;
-// Manages all audio resources. In particular it owns the AudioOutputStream
-// objects. Provides some convenience functions that avoid the need to provide
-// iterators over the existing streams.
+// Manages all audio resources. Provides some convenience functions that avoid
+// the need to provide iterators over the existing streams.
class MEDIA_EXPORT AudioManager {
- public:
- virtual ~AudioManager();
+ public:
+ virtual ~AudioManager();
- // Use to construct the audio manager.
- // NOTE: There should only be one instance.
- static AudioManager* Create();
+ // Construct the audio manager; only one instance is allowed. The manager
+ // will forward CreateAudioLog() calls to the provided AudioLogFactory; as
+ // such |audio_log_factory| must outlive the AudioManager.
+ static AudioManager* Create(AudioLogFactory* audio_log_factory);
+
+ // Similar to Create() except uses a FakeAudioLogFactory for testing.
+ static AudioManager* CreateForTesting();
// Returns the pointer to the last created instance, or NULL if not yet
// created. This is a utility method for the code outside of media directory,
@@ -50,7 +54,7 @@ class MEDIA_EXPORT AudioManager {
// Returns a human readable string for the model/make of the active audio
// input device for this computer.
- virtual string16 GetAudioInputDeviceModel() = 0;
+ virtual base::string16 GetAudioInputDeviceModel() = 0;
// Opens the platform default audio input settings UI.
// Note: This could invoke an external application/preferences pane, so
@@ -62,10 +66,16 @@ class MEDIA_EXPORT AudioManager {
// which must initially be empty. It is not guaranteed that all the
// devices in the list support all formats and sample rates for
// recording.
+ //
+ // Not threadsafe; in production this should only be called from the
+ // Audio IO thread (see GetMessageLoop).
virtual void GetAudioInputDeviceNames(AudioDeviceNames* device_names) = 0;
// Appends a list of available output devices to |device_names|,
// which must initially be empty.
+ //
+ // Not threadsafe; in production this should only be called from the
+ // Audio IO thread (see GetMessageLoop).
virtual void GetAudioOutputDeviceNames(AudioDeviceNames* device_names) = 0;
// Factory for all the supported stream formats. |params| defines parameters
@@ -169,6 +179,16 @@ class MEDIA_EXPORT AudioManager {
virtual std::string GetAssociatedOutputDeviceID(
const std::string& input_device_id) = 0;
+ // Create a new AudioLog object for tracking the behavior for one or more
+ // instances of the given component. See AudioLogFactory for more details.
+ virtual scoped_ptr<AudioLog> CreateAudioLog(
+ AudioLogFactory::AudioComponent component) = 0;
+
+ // Called when a component has detected a OS level audio wedge. Shuts down
+ // all active audio streams and then restarts them transparently. See
+ // http://crbug.com/160920
+ virtual void FixWedgedAudio() = 0;
+
protected:
AudioManager();
diff --git a/chromium/media/audio/audio_manager_base.cc b/chromium/media/audio/audio_manager_base.cc
index 5b1f4b3690a..f7b590ae724 100644
--- a/chromium/media/audio/audio_manager_base.cc
+++ b/chromium/media/audio/audio_manager_base.cc
@@ -8,12 +8,11 @@
#include "base/bind_helpers.h"
#include "base/command_line.h"
#include "base/message_loop/message_loop_proxy.h"
-#include "base/threading/thread.h"
+#include "base/strings/string_number_conversions.h"
#include "build/build_config.h"
#include "media/audio/audio_output_dispatcher_impl.h"
#include "media/audio/audio_output_proxy.h"
#include "media/audio/audio_output_resampler.h"
-#include "media/audio/audio_util.h"
#include "media/audio/fake_audio_input_stream.h"
#include "media/audio/fake_audio_output_stream.h"
#include "media/base/media_switches.h"
@@ -78,7 +77,7 @@ class AudioManagerBase::CompareByParams {
const DispatcherParams* dispatcher_;
};
-AudioManagerBase::AudioManagerBase()
+AudioManagerBase::AudioManagerBase(AudioLogFactory* audio_log_factory)
: max_num_output_streams_(kDefaultMaxOutputStreams),
max_num_input_streams_(kDefaultMaxInputStreams),
num_output_streams_(0),
@@ -87,9 +86,10 @@ AudioManagerBase::AudioManagerBase()
// block the UI thread when swapping devices.
output_listeners_(
ObserverList<AudioDeviceListener>::NOTIFY_EXISTING_ONLY),
- audio_thread_(new base::Thread("AudioThread")) {
+ audio_thread_("AudioThread"),
+ audio_log_factory_(audio_log_factory) {
#if defined(OS_WIN)
- audio_thread_->init_com_with_mta(true);
+ audio_thread_.init_com_with_mta(true);
#elif defined(OS_MACOSX)
// CoreAudio calls must occur on the main thread of the process, which in our
// case is sadly the browser UI thread. Failure to execute calls on the right
@@ -104,8 +104,8 @@ AudioManagerBase::AudioManagerBase()
}
#endif
- CHECK(audio_thread_->Start());
- message_loop_ = audio_thread_->message_loop_proxy();
+ CHECK(audio_thread_.Start());
+ message_loop_ = audio_thread_.message_loop_proxy();
}
AudioManagerBase::~AudioManagerBase() {
@@ -114,15 +114,15 @@ AudioManagerBase::~AudioManagerBase() {
// stopping the thread, resulting an unexpected behavior.
// This way we make sure activities of the audio streams are all stopped
// before we destroy them.
- CHECK(!audio_thread_.get());
+ CHECK(!audio_thread_.IsRunning());
// All the output streams should have been deleted.
DCHECK_EQ(0, num_output_streams_);
// All the input streams should have been deleted.
DCHECK_EQ(0, num_input_streams_);
}
-string16 AudioManagerBase::GetAudioInputDeviceModel() {
- return string16();
+base::string16 AudioManagerBase::GetAudioInputDeviceModel() {
+ return base::string16();
}
scoped_refptr<base::MessageLoopProxy> AudioManagerBase::GetMessageLoop() {
@@ -131,10 +131,10 @@ scoped_refptr<base::MessageLoopProxy> AudioManagerBase::GetMessageLoop() {
scoped_refptr<base::MessageLoopProxy> AudioManagerBase::GetWorkerLoop() {
// Lazily start the worker thread.
- if (!audio_thread_->IsRunning())
- CHECK(audio_thread_->Start());
+ if (!audio_thread_.IsRunning())
+ CHECK(audio_thread_.Start());
- return audio_thread_->message_loop_proxy();
+ return audio_thread_.message_loop_proxy();
}
AudioOutputStream* AudioManagerBase::MakeAudioOutputStream(
@@ -330,17 +330,6 @@ void AudioManagerBase::ReleaseInputStream(AudioInputStream* stream) {
}
void AudioManagerBase::Shutdown() {
- // To avoid running into deadlocks while we stop the thread, shut it down
- // via a local variable while not holding the audio thread lock.
- scoped_ptr<base::Thread> audio_thread;
- {
- base::AutoLock lock(audio_thread_lock_);
- audio_thread_.swap(audio_thread);
- }
-
- if (!audio_thread)
- return;
-
// Only true when we're sharing the UI message loop with the browser. The UI
// loop is no longer running at this time and browser destruction is imminent.
if (message_loop_->BelongsToCurrentThread()) {
@@ -351,27 +340,24 @@ void AudioManagerBase::Shutdown() {
}
// Stop() will wait for any posted messages to be processed first.
- audio_thread->Stop();
+ audio_thread_.Stop();
}
void AudioManagerBase::ShutdownOnAudioThread() {
- // This should always be running on the audio thread, but since we've cleared
- // the audio_thread_ member pointer when we get here, we can't verify exactly
- // what thread we're running on. The method is not public though and only
- // called from one place, so we'll leave it at that.
+ DCHECK(message_loop_->BelongsToCurrentThread());
+
AudioOutputDispatchers::iterator it = output_dispatchers_.begin();
for (; it != output_dispatchers_.end(); ++it) {
scoped_refptr<AudioOutputDispatcher>& dispatcher = (*it)->dispatcher;
- if (dispatcher.get()) {
- dispatcher->Shutdown();
- // All AudioOutputProxies must have been freed before Shutdown is called.
- // If they still exist, things will go bad. They have direct pointers to
- // both physical audio stream objects that belong to the dispatcher as
- // well as the message loop of the audio thread that will soon go away.
- // So, better crash now than later.
- DCHECK(dispatcher->HasOneRef()) << "AudioOutputProxies are still alive";
- dispatcher = NULL;
- }
+ dispatcher->Shutdown();
+
+ // All AudioOutputProxies must have been freed before Shutdown is called.
+ // If they still exist, things will go bad. They have direct pointers to
+ // both physical audio stream objects that belong to the dispatcher as
+ // well as the message loop of the audio thread that will soon go away.
+ // So, better crash now than later.
+ DCHECK(dispatcher->HasOneRef()) << "AudioOutputProxies are still alive";
+ dispatcher = NULL;
}
output_dispatchers_.clear();
@@ -419,8 +405,51 @@ std::string AudioManagerBase::GetAssociatedOutputDeviceID(
}
std::string AudioManagerBase::GetDefaultOutputDeviceID() {
- NOTIMPLEMENTED();
return "";
}
+int AudioManagerBase::GetUserBufferSize() {
+ const CommandLine* cmd_line = CommandLine::ForCurrentProcess();
+ int buffer_size = 0;
+ std::string buffer_size_str(cmd_line->GetSwitchValueASCII(
+ switches::kAudioBufferSize));
+ if (base::StringToInt(buffer_size_str, &buffer_size) && buffer_size > 0)
+ return buffer_size;
+
+ return 0;
+}
+
+scoped_ptr<AudioLog> AudioManagerBase::CreateAudioLog(
+ AudioLogFactory::AudioComponent component) {
+ return audio_log_factory_->CreateAudioLog(component);
+}
+
+void AudioManagerBase::FixWedgedAudio() {
+ DCHECK(message_loop_->BelongsToCurrentThread());
+#if defined(OS_MACOSX)
+ // Through trial and error, we've found that one way to restore audio after a
+ // hang is to close all outstanding audio streams. Once all streams have been
+ // closed, new streams appear to work correctly.
+ //
+ // In Chrome terms, this means we need to ask all AudioOutputDispatchers to
+ // close all Open()'d streams. Once all streams across all dispatchers have
+ // been closed, we ask for all previously Start()'d streams to be recreated
+ // using the same AudioSourceCallback they had before.
+ //
+ // Since this operation takes place on the audio thread we can be sure that no
+ // other state-changing stream operations will take place while the fix is in
+ // progress.
+ //
+ // See http://crbug.com/160920 for additional details.
+ for (AudioOutputDispatchers::iterator it = output_dispatchers_.begin();
+ it != output_dispatchers_.end(); ++it) {
+ (*it)->dispatcher->CloseStreamsForWedgeFix();
+ }
+ for (AudioOutputDispatchers::iterator it = output_dispatchers_.begin();
+ it != output_dispatchers_.end(); ++it) {
+ (*it)->dispatcher->RestartStreamsForWedgeFix();
+ }
+#endif
+}
+
} // namespace media
diff --git a/chromium/media/audio/audio_manager_base.h b/chromium/media/audio/audio_manager_base.h
index cdf7d3a76ae..09b021a0d2b 100644
--- a/chromium/media/audio/audio_manager_base.h
+++ b/chromium/media/audio/audio_manager_base.h
@@ -12,7 +12,7 @@
#include "base/memory/scoped_ptr.h"
#include "base/memory/scoped_vector.h"
#include "base/observer_list.h"
-#include "base/synchronization/lock.h"
+#include "base/threading/thread.h"
#include "media/audio/audio_manager.h"
#include "media/audio/audio_output_dispatcher.h"
@@ -21,10 +21,6 @@
#include "base/win/scoped_com_initializer.h"
#endif
-namespace base {
-class Thread;
-}
-
namespace media {
class AudioOutputDispatcher;
@@ -55,7 +51,7 @@ class MEDIA_EXPORT AudioManagerBase : public AudioManager {
virtual scoped_refptr<base::MessageLoopProxy> GetMessageLoop() OVERRIDE;
virtual scoped_refptr<base::MessageLoopProxy> GetWorkerLoop() OVERRIDE;
- virtual string16 GetAudioInputDeviceModel() OVERRIDE;
+ virtual base::string16 GetAudioInputDeviceModel() OVERRIDE;
virtual void ShowAudioInputSettings() OVERRIDE;
@@ -119,9 +115,13 @@ class MEDIA_EXPORT AudioManagerBase : public AudioManager {
virtual std::string GetAssociatedOutputDeviceID(
const std::string& input_device_id) OVERRIDE;
- protected:
- AudioManagerBase();
+ virtual scoped_ptr<AudioLog> CreateAudioLog(
+ AudioLogFactory::AudioComponent component) OVERRIDE;
+
+ virtual void FixWedgedAudio() OVERRIDE;
+ protected:
+ AudioManagerBase(AudioLogFactory* audio_log_factory);
// Shuts down the audio thread and releases all the audio output dispatchers
// on the audio thread. All audio streams should be freed before Shutdown()
@@ -136,6 +136,10 @@ class MEDIA_EXPORT AudioManagerBase : public AudioManager {
// thread.
void NotifyAllOutputDeviceChangeListeners();
+ // Returns user buffer size as specified on the command line or 0 if no buffer
+ // size has been specified.
+ int GetUserBufferSize();
+
// Returns the preferred hardware audio output parameters for opening output
// streams. If the users inject a valid |input_params|, each AudioManager
// will decide if they should return the values from |input_params| or the
@@ -181,8 +185,7 @@ class MEDIA_EXPORT AudioManagerBase : public AudioManager {
ObserverList<AudioDeviceListener> output_listeners_;
// Thread used to interact with audio streams created by this audio manager.
- scoped_ptr<base::Thread> audio_thread_;
- mutable base::Lock audio_thread_lock_;
+ base::Thread audio_thread_;
// The message loop of the audio thread this object runs on. Used for internal
// tasks which run on the audio thread even after Shutdown() has been started
@@ -193,6 +196,9 @@ class MEDIA_EXPORT AudioManagerBase : public AudioManager {
// from the audio thread (no locking).
AudioOutputDispatchers output_dispatchers_;
+ // Proxy for creating AudioLog objects.
+ AudioLogFactory* const audio_log_factory_;
+
DISALLOW_COPY_AND_ASSIGN(AudioManagerBase);
};
diff --git a/chromium/media/audio/audio_manager_unittest.cc b/chromium/media/audio/audio_manager_unittest.cc
index 4747c2e2996..8c6cc10b423 100644
--- a/chromium/media/audio/audio_manager_unittest.cc
+++ b/chromium/media/audio/audio_manager_unittest.cc
@@ -7,11 +7,12 @@
#include "base/memory/scoped_ptr.h"
#include "media/audio/audio_manager.h"
#include "media/audio/audio_manager_base.h"
+#include "media/audio/fake_audio_log_factory.h"
#include "testing/gtest/include/gtest/gtest.h"
-#if defined(OS_LINUX)
-#include "media/audio/linux/audio_manager_linux.h"
-#endif // defined(OS_LINUX)
+#if defined(USE_ALSA)
+#include "media/audio/alsa/audio_manager_alsa.h"
+#endif // defined(USE_ALSA)
#if defined(OS_WIN)
#include "base/win/scoped_com_initializer.h"
@@ -31,7 +32,7 @@ class AudioManagerTest
: public ::testing::Test {
protected:
AudioManagerTest()
- : audio_manager_(AudioManager::Create())
+ : audio_manager_(AudioManager::CreateForTesting())
#if defined(OS_WIN)
, com_init_(base::win::ScopedCOMInitializer::kMTA)
#endif
@@ -111,6 +112,17 @@ class AudioManagerTest
return audio_manager_->HasAudioOutputDevices();
}
+#if defined(USE_ALSA) || defined(USE_PULSEAUDIO)
+ template <class T>
+ void CreateAudioManagerForTesting() {
+ // Only one AudioManager may exist at a time, so destroy the one we're
+ // currently holding before creating a new one.
+ audio_manager_.reset();
+ audio_manager_.reset(T::Create(&fake_audio_log_factory_));
+ }
+#endif
+
+ FakeAudioLogFactory fake_audio_log_factory_;
scoped_ptr<AudioManager> audio_manager_;
#if defined(OS_WIN)
@@ -254,7 +266,7 @@ TEST_F(AudioManagerTest, EnumerateInputDevicesPulseaudio) {
if (!CanRunInputTest())
return;
- audio_manager_.reset(AudioManagerPulse::Create());
+ CreateAudioManagerForTesting<AudioManagerPulse>();
if (audio_manager_.get()) {
AudioDeviceNames device_names;
audio_manager_->GetAudioInputDeviceNames(&device_names);
@@ -268,7 +280,7 @@ TEST_F(AudioManagerTest, EnumerateOutputDevicesPulseaudio) {
if (!CanRunOutputTest())
return;
- audio_manager_.reset(AudioManagerPulse::Create());
+ CreateAudioManagerForTesting<AudioManagerPulse>();
if (audio_manager_.get()) {
AudioDeviceNames device_names;
audio_manager_->GetAudioOutputDeviceNames(&device_names);
@@ -288,8 +300,8 @@ TEST_F(AudioManagerTest, EnumerateInputDevicesAlsa) {
if (!CanRunInputTest())
return;
- VLOG(2) << "Testing AudioManagerLinux.";
- audio_manager_.reset(new AudioManagerLinux());
+ VLOG(2) << "Testing AudioManagerAlsa.";
+ CreateAudioManagerForTesting<AudioManagerAlsa>();
AudioDeviceNames device_names;
audio_manager_->GetAudioInputDeviceNames(&device_names);
CheckDeviceNames(device_names);
@@ -299,8 +311,8 @@ TEST_F(AudioManagerTest, EnumerateOutputDevicesAlsa) {
if (!CanRunOutputTest())
return;
- VLOG(2) << "Testing AudioManagerLinux.";
- audio_manager_.reset(new AudioManagerLinux());
+ VLOG(2) << "Testing AudioManagerAlsa.";
+ CreateAudioManagerForTesting<AudioManagerAlsa>();
AudioDeviceNames device_names;
audio_manager_->GetAudioOutputDeviceNames(&device_names);
CheckDeviceNames(device_names);
diff --git a/chromium/media/audio/audio_output_controller.cc b/chromium/media/audio/audio_output_controller.cc
index 649612cd4f6..92f9f25de53 100644
--- a/chromium/media/audio/audio_output_controller.cc
+++ b/chromium/media/audio/audio_output_controller.cc
@@ -8,11 +8,10 @@
#include "base/debug/trace_event.h"
#include "base/message_loop/message_loop.h"
#include "base/metrics/histogram.h"
+#include "base/task_runner_util.h"
#include "base/threading/platform_thread.h"
#include "base/time/time.h"
#include "build/build_config.h"
-#include "media/audio/audio_util.h"
-#include "media/audio/shared_memory_util.h"
#include "media/base/scoped_histogram_timer.h"
using base::Time;
@@ -58,7 +57,7 @@ AudioOutputController::AudioOutputController(
params.sample_rate(),
TimeDelta::FromMilliseconds(kPowerMeasurementTimeConstantMillis)),
#endif
- number_polling_attempts_left_(0) {
+ on_more_io_data_called_(0) {
DCHECK(audio_manager);
DCHECK(handler_);
DCHECK(sync_reader_);
@@ -112,9 +111,28 @@ void AudioOutputController::SetVolume(double volume) {
&AudioOutputController::DoSetVolume, this, volume));
}
+void AudioOutputController::GetOutputDeviceId(
+ base::Callback<void(const std::string&)> callback) const {
+ base::PostTaskAndReplyWithResult(
+ message_loop_.get(),
+ FROM_HERE,
+ base::Bind(&AudioOutputController::DoGetOutputDeviceId, this),
+ callback);
+}
+
+void AudioOutputController::SwitchOutputDevice(
+ const std::string& output_device_id, const base::Closure& callback) {
+ message_loop_->PostTaskAndReply(
+ FROM_HERE,
+ base::Bind(&AudioOutputController::DoSwitchOutputDevice, this,
+ output_device_id),
+ callback);
+}
+
void AudioOutputController::DoCreate(bool is_for_device_change) {
DCHECK(message_loop_->BelongsToCurrentThread());
SCOPED_UMA_HISTOGRAM_TIMER("Media.AudioOutputController.CreateTime");
+ TRACE_EVENT0("audio", "AudioOutputController::DoCreate");
// Close() can be called before DoCreate() is executed.
if (state_ == kClosed)
@@ -159,6 +177,7 @@ void AudioOutputController::DoCreate(bool is_for_device_change) {
void AudioOutputController::DoPlay() {
DCHECK(message_loop_->BelongsToCurrentThread());
SCOPED_UMA_HISTOGRAM_TIMER("Media.AudioOutputController.PlayTime");
+ TRACE_EVENT0("audio", "AudioOutputController::DoPlay");
// We can start from created or paused state.
if (state_ != kCreated && state_ != kPaused)
@@ -179,10 +198,26 @@ void AudioOutputController::DoPlay() {
power_poll_callback_.callback().Run();
#endif
- // We start the AudioOutputStream lazily.
+ on_more_io_data_called_ = 0;
AllowEntryToOnMoreIOData();
stream_->Start(this);
+ // For UMA tracking purposes, start the wedge detection timer. This allows us
+ // to record statistics about the number of wedged playbacks in the field.
+ //
+ // WedgeCheck() will look to see if |on_more_io_data_called_| is true after
+ // the timeout expires. Care must be taken to ensure the wedge check delay is
+ // large enough that the value isn't queried while OnMoreDataIO() is setting
+ // it.
+ //
+ // Timer self-manages its lifetime and WedgeCheck() will only record the UMA
+ // statistic if state is still kPlaying. Additional Start() calls will
+ // invalidate the previous timer.
+ wedge_timer_.reset(new base::OneShotTimer<AudioOutputController>());
+ wedge_timer_->Start(
+ FROM_HERE, TimeDelta::FromSeconds(5), this,
+ &AudioOutputController::WedgeCheck);
+
handler_->OnPlaying();
}
@@ -202,6 +237,7 @@ void AudioOutputController::StopStream() {
DCHECK(message_loop_->BelongsToCurrentThread());
if (state_ == kPlaying) {
+ wedge_timer_.reset();
stream_->Stop();
DisallowEntryToOnMoreIOData();
@@ -216,14 +252,17 @@ void AudioOutputController::StopStream() {
void AudioOutputController::DoPause() {
DCHECK(message_loop_->BelongsToCurrentThread());
SCOPED_UMA_HISTOGRAM_TIMER("Media.AudioOutputController.PauseTime");
+ TRACE_EVENT0("audio", "AudioOutputController::DoPause");
StopStream();
if (state_ != kPaused)
return;
- // Send a special pause mark to the low-latency audio thread.
- sync_reader_->UpdatePendingBytes(kPauseMark);
+ // Let the renderer know we've stopped. Necessary to let PPAPI clients know
+ // audio has been shutdown. TODO(dalecurtis): This stinks. PPAPI should have
+ // a better way to know when it should exit PPB_Audio_Shared::Run().
+ sync_reader_->UpdatePendingBytes(-1);
#if defined(AUDIO_POWER_MONITORING)
// Paused means silence follows.
@@ -236,6 +275,7 @@ void AudioOutputController::DoPause() {
void AudioOutputController::DoClose() {
DCHECK(message_loop_->BelongsToCurrentThread());
SCOPED_UMA_HISTOGRAM_TIMER("Media.AudioOutputController.CloseTime");
+ TRACE_EVENT0("audio", "AudioOutputController::DoClose");
if (state_ != kClosed) {
DoStopCloseAndClearStream();
@@ -262,6 +302,31 @@ void AudioOutputController::DoSetVolume(double volume) {
}
}
+std::string AudioOutputController::DoGetOutputDeviceId() const {
+ DCHECK(message_loop_->BelongsToCurrentThread());
+ return output_device_id_;
+}
+
+void AudioOutputController::DoSwitchOutputDevice(
+ const std::string& output_device_id) {
+ DCHECK(message_loop_->BelongsToCurrentThread());
+
+ if (state_ == kClosed)
+ return;
+
+ if (output_device_id == output_device_id_)
+ return;
+
+ output_device_id_ = output_device_id;
+
+ // If output is currently diverted, we must not call OnDeviceChange
+ // since it would break the diverted setup. Once diversion is
+ // finished using StopDiverting() the output will switch to the new
+ // device ID.
+ if (stream_ != diverting_to_stream_)
+ OnDeviceChange();
+}
+
void AudioOutputController::DoReportError() {
DCHECK(message_loop_->BelongsToCurrentThread());
if (state_ != kClosed)
@@ -279,26 +344,16 @@ int AudioOutputController::OnMoreIOData(AudioBus* source,
DisallowEntryToOnMoreIOData();
TRACE_EVENT0("audio", "AudioOutputController::OnMoreIOData");
- // The OS level audio APIs on Linux and Windows all have problems requesting
- // data on a fixed interval. Sometimes they will issue calls back to back
- // which can cause glitching, so wait until the renderer is ready.
- //
- // We also need to wait when diverting since the virtual stream will call this
- // multiple times without waiting.
- //
- // NEVER wait on OSX unless a virtual stream is connected, otherwise we can
- // end up hanging the entire OS.
- //
- // See many bugs for context behind this decision: http://crbug.com/170498,
- // http://crbug.com/171651, http://crbug.com/174985, and more.
-#if defined(OS_WIN) || defined(OS_LINUX)
- const bool kShouldBlock = true;
-#else
- const bool kShouldBlock = diverting_to_stream_ != NULL;
-#endif
+ // Indicate that we haven't wedged (at least not indefinitely, WedgeCheck()
+ // may have already fired if OnMoreIOData() took an abnormal amount of time).
+ // Since this thread is the only writer of |on_more_io_data_called_| once the
+ // thread starts, its safe to compare and then increment.
+ if (base::AtomicRefCountIsZero(&on_more_io_data_called_))
+ base::AtomicRefCountInc(&on_more_io_data_called_);
- const int frames = sync_reader_->Read(kShouldBlock, source, dest);
- DCHECK_LE(0, frames);
+ sync_reader_->Read(source, dest);
+
+ const int frames = dest->frames();
sync_reader_->UpdatePendingBytes(
buffers_state.total_bytes() + frames * params_.GetBytesPerFrame());
@@ -339,6 +394,7 @@ void AudioOutputController::DoStopCloseAndClearStream() {
void AudioOutputController::OnDeviceChange() {
DCHECK(message_loop_->BelongsToCurrentThread());
SCOPED_UMA_HISTOGRAM_TIMER("Media.AudioOutputController.DeviceChangeTime");
+ TRACE_EVENT0("audio", "AudioOutputController::OnDeviceChange");
// TODO(dalecurtis): Notify the renderer side that a device change has
// occurred. Currently querying the hardware information here will lead to
@@ -417,4 +473,21 @@ void AudioOutputController::DisallowEntryToOnMoreIOData() {
DCHECK(is_zero);
}
+void AudioOutputController::WedgeCheck() {
+ DCHECK(message_loop_->BelongsToCurrentThread());
+
+ // If we should be playing and we haven't, that's a wedge.
+ if (state_ == kPlaying) {
+ const bool playback_success =
+ base::AtomicRefCountIsOne(&on_more_io_data_called_);
+
+ UMA_HISTOGRAM_BOOLEAN(
+ "Media.AudioOutputControllerPlaybackStartupSuccess", playback_success);
+
+ // Let the AudioManager try and fix it.
+ if (!playback_success)
+ audio_manager_->FixWedgedAudio();
+ }
+}
+
} // namespace media
diff --git a/chromium/media/audio/audio_output_controller.h b/chromium/media/audio/audio_output_controller.h
index 615c6a5e6c6..d16ce9e79b6 100644
--- a/chromium/media/audio/audio_output_controller.h
+++ b/chromium/media/audio/audio_output_controller.h
@@ -9,6 +9,7 @@
#include "base/callback.h"
#include "base/cancelable_callback.h"
#include "base/memory/ref_counted.h"
+#include "base/timer/timer.h"
#include "media/audio/audio_io.h"
#include "media/audio/audio_manager.h"
#include "media/audio/audio_power_monitor.h"
@@ -91,11 +92,10 @@ class MEDIA_EXPORT AudioOutputController
// prepare more data and perform synchronization.
virtual void UpdatePendingBytes(uint32 bytes) = 0;
- // Attempt to completely fill |dest|, return the actual number of frames
- // that could be read. |source| may optionally be provided for input data.
- // If |block| is specified, the Read() will block until data is available
- // or a timeout is reached.
- virtual int Read(bool block, const AudioBus* source, AudioBus* dest) = 0;
+ // Attempts to completely fill |dest|, zeroing |dest| if the request can not
+ // be fulfilled (due to timeout). |source| may optionally be provided for
+ // input data.
+ virtual void Read(const AudioBus* source, AudioBus* dest) = 0;
// Close this synchronous reader.
virtual void Close() = 0;
@@ -135,6 +135,23 @@ class MEDIA_EXPORT AudioOutputController
// Sets the volume of the audio output stream.
void SetVolume(double volume);
+ // Calls |callback| (on the caller's thread) with the current output
+ // device ID.
+ void GetOutputDeviceId(
+ base::Callback<void(const std::string&)> callback) const;
+
+ // Changes which output device to use. If desired, you can provide a
+ // callback that will be notified (on the thread you called from)
+ // when the function has completed execution.
+ //
+ // Changing the output device causes the controller to go through
+ // the same state transition back to the current state as a call to
+ // OnDeviceChange (unless it is currently diverting, see
+ // Start/StopDiverting below, in which case the state transition
+ // will happen when StopDiverting is called).
+ void SwitchOutputDevice(const std::string& output_device_id,
+ const base::Closure& callback);
+
// AudioSourceCallback implementation.
virtual int OnMoreData(AudioBus* dest,
AudioBuffersState buffers_state) OVERRIDE;
@@ -185,6 +202,8 @@ class MEDIA_EXPORT AudioOutputController
void DoPause();
void DoClose();
void DoSetVolume(double volume);
+ std::string DoGetOutputDeviceId() const;
+ void DoSwitchOutputDevice(const std::string& output_device_id);
void DoReportError();
void DoStartDiverting(AudioOutputStream* to_stream);
void DoStopDiverting();
@@ -204,13 +223,16 @@ class MEDIA_EXPORT AudioOutputController
void AllowEntryToOnMoreIOData();
void DisallowEntryToOnMoreIOData();
+ // Checks if a stream was started successfully but never calls OnMoreIOData().
+ void WedgeCheck();
+
AudioManager* const audio_manager_;
const AudioParameters params_;
EventHandler* const handler_;
// Specifies the device id of the output device to open or empty for the
// default output device.
- const std::string output_device_id_;
+ std::string output_device_id_;
// Used by the unified IO to open the correct input device.
const std::string input_device_id_;
@@ -249,9 +271,9 @@ class MEDIA_EXPORT AudioOutputController
base::CancelableClosure power_poll_callback_;
#endif
- // When starting stream we wait for data to become available.
- // Number of times left.
- int number_polling_attempts_left_;
+ // Flags when we've asked for a stream to start but it never did.
+ base::AtomicRefCount on_more_io_data_called_;
+ scoped_ptr<base::OneShotTimer<AudioOutputController> > wedge_timer_;
DISALLOW_COPY_AND_ASSIGN(AudioOutputController);
};
diff --git a/chromium/media/audio/audio_output_controller_unittest.cc b/chromium/media/audio/audio_output_controller_unittest.cc
index a7118e17a30..457265ec970 100644
--- a/chromium/media/audio/audio_output_controller_unittest.cc
+++ b/chromium/media/audio/audio_output_controller_unittest.cc
@@ -10,6 +10,7 @@
#include "base/memory/scoped_ptr.h"
#include "base/message_loop/message_loop.h"
#include "base/synchronization/waitable_event.h"
+#include "media/audio/audio_manager_base.h"
#include "media/audio/audio_output_controller.h"
#include "media/audio/audio_parameters.h"
#include "media/base/audio_bus.h"
@@ -53,7 +54,7 @@ class MockAudioOutputControllerSyncReader
MockAudioOutputControllerSyncReader() {}
MOCK_METHOD1(UpdatePendingBytes, void(uint32 bytes));
- MOCK_METHOD3(Read, int(bool block, const AudioBus* source, AudioBus* dest));
+ MOCK_METHOD2(Read, void(const AudioBus* source, AudioBus* dest));
MOCK_METHOD0(Close, void());
private:
@@ -83,16 +84,16 @@ ACTION_P(SignalEvent, event) {
static const float kBufferNonZeroData = 1.0f;
ACTION(PopulateBuffer) {
- arg2->Zero();
+ arg1->Zero();
// Note: To confirm the buffer will be populated in these tests, it's
// sufficient that only the first float in channel 0 is set to the value.
- arg2->channel(0)[0] = kBufferNonZeroData;
+ arg1->channel(0)[0] = kBufferNonZeroData;
}
class AudioOutputControllerTest : public testing::Test {
public:
AudioOutputControllerTest()
- : audio_manager_(AudioManager::Create()),
+ : audio_manager_(AudioManager::CreateForTesting()),
create_event_(false, false),
play_event_(false, false),
read_event_(false, false),
@@ -141,10 +142,9 @@ class AudioOutputControllerTest : public testing::Test {
// sent from the render process.
EXPECT_CALL(mock_sync_reader_, UpdatePendingBytes(_))
.Times(AtLeast(1));
- EXPECT_CALL(mock_sync_reader_, Read(_, _, _))
+ EXPECT_CALL(mock_sync_reader_, Read(_, _))
.WillRepeatedly(DoAll(PopulateBuffer(),
- SignalEvent(&read_event_),
- Return(params_.frames_per_buffer())));
+ SignalEvent(&read_event_)));
controller_->Play();
}
@@ -216,6 +216,19 @@ class AudioOutputControllerTest : public testing::Test {
controller_->StopDiverting();
}
+ void SwitchDevice(bool diverting) {
+ if (!diverting) {
+ // Expect the current stream to close and a new stream to start
+ // playing if not diverting. When diverting, nothing happens
+ // until diverting is stopped.
+ EXPECT_CALL(mock_event_handler_, OnPlaying())
+ .WillOnce(SignalEvent(&play_event_));
+ }
+
+ controller_->SwitchOutputDevice(AudioManagerBase::kDefaultDeviceName,
+ base::Bind(&base::DoNothing));
+ }
+
void Close() {
EXPECT_CALL(mock_sync_reader_, Close());
@@ -314,6 +327,18 @@ TEST_F(AudioOutputControllerTest, PlayDeviceChangeClose) {
Close();
}
+TEST_F(AudioOutputControllerTest, PlaySwitchDeviceClose) {
+ Create(kSamplesPerPacket);
+ WaitForCreate();
+ Play();
+ WaitForPlay();
+ WaitForReads();
+ SwitchDevice(false);
+ WaitForPlay();
+ WaitForReads();
+ Close();
+}
+
TEST_F(AudioOutputControllerTest, PlayDivertRevertClose) {
Create(kSamplesPerPacket);
WaitForCreate();
@@ -329,6 +354,22 @@ TEST_F(AudioOutputControllerTest, PlayDivertRevertClose) {
Close();
}
+TEST_F(AudioOutputControllerTest, PlayDivertSwitchDeviceRevertClose) {
+ Create(kSamplesPerPacket);
+ WaitForCreate();
+ Play();
+ WaitForPlay();
+ WaitForReads();
+ DivertWhilePlaying();
+ WaitForPlay();
+ SwitchDevice(true);
+ ReadDivertedAudioData();
+ RevertWhilePlaying();
+ WaitForPlay();
+ WaitForReads();
+ Close();
+}
+
TEST_F(AudioOutputControllerTest, PlayDivertRevertDivertRevertClose) {
Create(kSamplesPerPacket);
WaitForCreate();
diff --git a/chromium/media/audio/audio_output_device.cc b/chromium/media/audio/audio_output_device.cc
index 0c406cab0d6..1f9efc185bd 100644
--- a/chromium/media/audio/audio_output_device.cc
+++ b/chromium/media/audio/audio_output_device.cc
@@ -10,8 +10,6 @@
#include "base/threading/thread_restrictions.h"
#include "base/time/time.h"
#include "media/audio/audio_output_controller.h"
-#include "media/audio/audio_util.h"
-#include "media/audio/shared_memory_util.h"
#include "media/base/limits.h"
namespace media {
@@ -245,8 +243,8 @@ void AudioOutputDevice::OnStreamCreated(
DCHECK(audio_thread_.IsStopped());
audio_callback_.reset(new AudioOutputDevice::AudioThreadCallback(
audio_parameters_, handle, length, callback_));
- audio_thread_.Start(audio_callback_.get(), socket_handle,
- "AudioOutputDevice");
+ audio_thread_.Start(
+ audio_callback_.get(), socket_handle, "AudioOutputDevice", true);
state_ = PAUSED;
// We handle the case where Play() and/or Pause() may have been called
@@ -273,26 +271,21 @@ AudioOutputDevice::AudioThreadCallback::AudioThreadCallback(
base::SharedMemoryHandle memory,
int memory_length,
AudioRendererSink::RenderCallback* render_callback)
- : AudioDeviceThread::Callback(audio_parameters,
- memory,
- memory_length,
- 1),
- render_callback_(render_callback) {
-}
+ : AudioDeviceThread::Callback(audio_parameters, memory, memory_length, 1),
+ render_callback_(render_callback) {}
AudioOutputDevice::AudioThreadCallback::~AudioThreadCallback() {
}
void AudioOutputDevice::AudioThreadCallback::MapSharedMemory() {
CHECK_EQ(total_segments_, 1);
- CHECK(shared_memory_.Map(TotalSharedMemorySizeInBytes(memory_length_)));
+ CHECK(shared_memory_.Map(memory_length_));
// Calculate output and input memory size.
int output_memory_size = AudioBus::CalculateMemorySize(audio_parameters_);
int input_channels = audio_parameters_.input_channels();
int frames = audio_parameters_.frames_per_buffer();
- int input_memory_size =
- AudioBus::CalculateMemorySize(input_channels, frames);
+ int input_memory_size = AudioBus::CalculateMemorySize(input_channels, frames);
int io_size = output_memory_size + input_memory_size;
@@ -305,21 +298,17 @@ void AudioOutputDevice::AudioThreadCallback::MapSharedMemory() {
// The input data is after the output data.
char* input_data =
static_cast<char*>(shared_memory_.memory()) + output_memory_size;
- input_bus_ =
- AudioBus::WrapMemory(input_channels, frames, input_data);
+ input_bus_ = AudioBus::WrapMemory(input_channels, frames, input_data);
}
}
// Called whenever we receive notifications about pending data.
void AudioOutputDevice::AudioThreadCallback::Process(int pending_data) {
- if (pending_data == kPauseMark) {
- memset(shared_memory_.memory(), 0, memory_length_);
- SetActualDataSizeInBytes(&shared_memory_, memory_length_, 0);
+ // Negative |pending_data| indicates the browser side stream has stopped.
+ if (pending_data < 0)
return;
- }
- // Convert the number of pending bytes in the render buffer
- // into milliseconds.
+ // Convert the number of pending bytes in the render buffer into milliseconds.
int audio_delay_milliseconds = pending_data / bytes_per_ms_;
TRACE_EVENT0("audio", "AudioOutputDevice::FireRenderCallback");
@@ -328,25 +317,12 @@ void AudioOutputDevice::AudioThreadCallback::Process(int pending_data) {
// |output_bus_| is wrapping the shared memory the Render() call is writing
// directly into the shared memory.
int input_channels = audio_parameters_.input_channels();
- size_t num_frames = audio_parameters_.frames_per_buffer();
-
- if (input_bus_.get() && input_channels > 0) {
- render_callback_->RenderIO(input_bus_.get(),
- output_bus_.get(),
- audio_delay_milliseconds);
+ if (input_bus_ && input_channels > 0) {
+ render_callback_->RenderIO(
+ input_bus_.get(), output_bus_.get(), audio_delay_milliseconds);
} else {
- num_frames = render_callback_->Render(output_bus_.get(),
- audio_delay_milliseconds);
+ render_callback_->Render(output_bus_.get(), audio_delay_milliseconds);
}
-
- // Let the host know we are done.
- // TODO(dalecurtis): Technically this is not always correct. Due to channel
- // padding for alignment, there may be more data available than this. We're
- // relying on AudioSyncReader::Read() to parse this with that in mind. Rename
- // these methods to Set/GetActualFrameCount().
- SetActualDataSizeInBytes(
- &shared_memory_, memory_length_,
- num_frames * sizeof(*output_bus_->channel(0)) * output_bus_->channels());
}
} // namespace media.
diff --git a/chromium/media/audio/audio_output_device_unittest.cc b/chromium/media/audio/audio_output_device_unittest.cc
index 96da77d7404..7aca2627745 100644
--- a/chromium/media/audio/audio_output_device_unittest.cc
+++ b/chromium/media/audio/audio_output_device_unittest.cc
@@ -12,7 +12,6 @@
#include "base/test/test_timeouts.h"
#include "media/audio/audio_output_device.h"
#include "media/audio/sample_rates.h"
-#include "media/audio/shared_memory_util.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gmock_mutant.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -124,8 +123,6 @@ class AudioOutputDeviceTest
DISALLOW_COPY_AND_ASSIGN(AudioOutputDeviceTest);
};
-static const int kStreamId = 123;
-
int AudioOutputDeviceTest::CalculateMemorySize() {
// Calculate output and input memory size.
int output_memory_size =
@@ -135,13 +132,7 @@ int AudioOutputDeviceTest::CalculateMemorySize() {
int input_memory_size =
AudioBus::CalculateMemorySize(input_channels_, frames);
- int io_buffer_size = output_memory_size + input_memory_size;
-
- // This is where it gets a bit hacky. The shared memory contract between
- // AudioOutputDevice and its browser side counter part includes a bit more
- // than just the audio data, so we must call TotalSharedMemorySizeInBytes()
- // to get the actual size needed to fit the audio data plus the extra data.
- return TotalSharedMemorySizeInBytes(io_buffer_size);
+ return output_memory_size + input_memory_size;
}
AudioOutputDeviceTest::AudioOutputDeviceTest()
@@ -195,7 +186,7 @@ void AudioOutputDeviceTest::CreateStream() {
&duplicated_memory_handle));
audio_device_->OnStreamCreated(duplicated_memory_handle, audio_device_socket,
- PacketSizeInBytes(kMemorySize));
+ kMemorySize);
io_loop_.RunUntilIdle();
}
diff --git a/chromium/media/audio/audio_output_dispatcher.cc b/chromium/media/audio/audio_output_dispatcher.cc
index a151c449f02..89912c07dce 100644
--- a/chromium/media/audio/audio_output_dispatcher.cc
+++ b/chromium/media/audio/audio_output_dispatcher.cc
@@ -4,7 +4,7 @@
#include "media/audio/audio_output_dispatcher.h"
-#include "base/message_loop/message_loop.h"
+#include "base/message_loop/message_loop_proxy.h"
namespace media {
@@ -14,7 +14,7 @@ AudioOutputDispatcher::AudioOutputDispatcher(
const std::string& output_device_id,
const std::string& input_device_id)
: audio_manager_(audio_manager),
- message_loop_(base::MessageLoop::current()),
+ message_loop_(audio_manager->GetMessageLoop()),
params_(params),
output_device_id_(output_device_id),
input_device_id_(input_device_id) {
@@ -24,7 +24,7 @@ AudioOutputDispatcher::AudioOutputDispatcher(
}
AudioOutputDispatcher::~AudioOutputDispatcher() {
- DCHECK_EQ(base::MessageLoop::current(), message_loop_);
+ DCHECK(message_loop_->BelongsToCurrentThread());
}
} // namespace media
diff --git a/chromium/media/audio/audio_output_dispatcher.h b/chromium/media/audio/audio_output_dispatcher.h
index 30266ed6a9a..d707aff14b6 100644
--- a/chromium/media/audio/audio_output_dispatcher.h
+++ b/chromium/media/audio/audio_output_dispatcher.h
@@ -26,7 +26,7 @@
#include "media/audio/audio_parameters.h"
namespace base {
-class MessageLoop;
+class MessageLoopProxy;
}
namespace media {
@@ -66,21 +66,26 @@ class MEDIA_EXPORT AudioOutputDispatcher
// Called on the audio thread when the AudioManager is shutting down.
virtual void Shutdown() = 0;
+ // Called by the AudioManager to restart streams when a wedge is detected. A
+ // wedge means the OS failed to request any audio after StartStream(). When a
+ // wedge is detected all streams across all dispatchers must be closed. After
+ // all streams are closed, streams are restarted. See http://crbug.com/160920
+ virtual void CloseStreamsForWedgeFix() = 0;
+ virtual void RestartStreamsForWedgeFix() = 0;
+
// Accessor to the input device id used by unified IO.
const std::string& input_device_id() const { return input_device_id_; }
protected:
friend class base::RefCountedThreadSafe<AudioOutputDispatcher>;
- friend class AudioOutputProxyTest;
-
virtual ~AudioOutputDispatcher();
// A no-reference-held pointer (we don't want circular references) back to the
// AudioManager that owns this object.
AudioManager* audio_manager_;
- base::MessageLoop* message_loop_;
- AudioParameters params_;
- const std::string output_device_id_;
+ const scoped_refptr<base::MessageLoopProxy> message_loop_;
+ const AudioParameters params_;
+ std::string output_device_id_;
const std::string input_device_id_;
private:
diff --git a/chromium/media/audio/audio_output_dispatcher_impl.cc b/chromium/media/audio/audio_output_dispatcher_impl.cc
index bcdcd65146e..5118bef71e9 100644
--- a/chromium/media/audio/audio_output_dispatcher_impl.cc
+++ b/chromium/media/audio/audio_output_dispatcher_impl.cc
@@ -12,7 +12,6 @@
#include "base/time/time.h"
#include "media/audio/audio_io.h"
#include "media/audio/audio_output_proxy.h"
-#include "media/audio/audio_util.h"
namespace media {
@@ -22,36 +21,33 @@ AudioOutputDispatcherImpl::AudioOutputDispatcherImpl(
const std::string& output_device_id,
const std::string& input_device_id,
const base::TimeDelta& close_delay)
- : AudioOutputDispatcher(audio_manager, params, output_device_id,
- input_device_id),
- pause_delay_(base::TimeDelta::FromMicroseconds(
- 2 * params.frames_per_buffer() * base::Time::kMicrosecondsPerSecond /
- static_cast<float>(params.sample_rate()))),
- paused_proxies_(0),
- weak_this_(this),
+ : AudioOutputDispatcher(audio_manager,
+ params,
+ output_device_id,
+ input_device_id),
+ idle_proxies_(0),
close_timer_(FROM_HERE,
close_delay,
this,
- &AudioOutputDispatcherImpl::ClosePendingStreams) {
-}
+ &AudioOutputDispatcherImpl::CloseAllIdleStreams),
+ audio_log_(
+ audio_manager->CreateAudioLog(AudioLogFactory::AUDIO_OUTPUT_STREAM)),
+ audio_stream_id_(0) {}
AudioOutputDispatcherImpl::~AudioOutputDispatcherImpl() {
+ DCHECK_EQ(idle_proxies_, 0u);
DCHECK(proxy_to_physical_map_.empty());
DCHECK(idle_streams_.empty());
- DCHECK(pausing_streams_.empty());
}
bool AudioOutputDispatcherImpl::OpenStream() {
- DCHECK_EQ(base::MessageLoop::current(), message_loop_);
-
- paused_proxies_++;
+ DCHECK(message_loop_->BelongsToCurrentThread());
// Ensure that there is at least one open stream.
- if (idle_streams_.empty() && !CreateAndOpenStream()) {
- paused_proxies_--;
+ if (idle_streams_.empty() && !CreateAndOpenStream())
return false;
- }
+ ++idle_proxies_;
close_timer_.Reset();
return true;
}
@@ -59,34 +55,34 @@ bool AudioOutputDispatcherImpl::OpenStream() {
bool AudioOutputDispatcherImpl::StartStream(
AudioOutputStream::AudioSourceCallback* callback,
AudioOutputProxy* stream_proxy) {
- DCHECK_EQ(base::MessageLoop::current(), message_loop_);
+ DCHECK(message_loop_->BelongsToCurrentThread());
+ DCHECK(proxy_to_physical_map_.find(stream_proxy) ==
+ proxy_to_physical_map_.end());
if (idle_streams_.empty() && !CreateAndOpenStream())
return false;
AudioOutputStream* physical_stream = idle_streams_.back();
- DCHECK(physical_stream);
idle_streams_.pop_back();
- DCHECK_GT(paused_proxies_, 0u);
- --paused_proxies_;
-
- close_timer_.Reset();
-
- // Schedule task to allocate streams for other proxies if we need to.
- message_loop_->PostTask(FROM_HERE, base::Bind(
- &AudioOutputDispatcherImpl::OpenTask, weak_this_.GetWeakPtr()));
+ DCHECK_GT(idle_proxies_, 0u);
+ --idle_proxies_;
double volume = 0;
stream_proxy->GetVolume(&volume);
physical_stream->SetVolume(volume);
+ const int stream_id = audio_stream_ids_[physical_stream];
+ audio_log_->OnSetVolume(stream_id, volume);
physical_stream->Start(callback);
+ audio_log_->OnStarted(stream_id);
proxy_to_physical_map_[stream_proxy] = physical_stream;
+
+ close_timer_.Reset();
return true;
}
void AudioOutputDispatcherImpl::StopStream(AudioOutputProxy* stream_proxy) {
- DCHECK_EQ(base::MessageLoop::current(), message_loop_);
+ DCHECK(message_loop_->BelongsToCurrentThread());
AudioStreamMap::iterator it = proxy_to_physical_map_.find(stream_proxy);
DCHECK(it != proxy_to_physical_map_.end());
@@ -94,81 +90,46 @@ void AudioOutputDispatcherImpl::StopStream(AudioOutputProxy* stream_proxy) {
proxy_to_physical_map_.erase(it);
physical_stream->Stop();
+ audio_log_->OnStopped(audio_stream_ids_[physical_stream]);
+ ++idle_proxies_;
+ idle_streams_.push_back(physical_stream);
- ++paused_proxies_;
-
- pausing_streams_.push_front(physical_stream);
-
- // Don't recycle stream until two buffers worth of time has elapsed.
- message_loop_->PostDelayedTask(
- FROM_HERE,
- base::Bind(&AudioOutputDispatcherImpl::StopStreamTask,
- weak_this_.GetWeakPtr()),
- pause_delay_);
+ close_timer_.Reset();
}
void AudioOutputDispatcherImpl::StreamVolumeSet(AudioOutputProxy* stream_proxy,
double volume) {
- DCHECK_EQ(base::MessageLoop::current(), message_loop_);
+ DCHECK(message_loop_->BelongsToCurrentThread());
AudioStreamMap::iterator it = proxy_to_physical_map_.find(stream_proxy);
if (it != proxy_to_physical_map_.end()) {
AudioOutputStream* physical_stream = it->second;
physical_stream->SetVolume(volume);
+ audio_log_->OnSetVolume(audio_stream_ids_[physical_stream], volume);
}
}
-void AudioOutputDispatcherImpl::StopStreamTask() {
- DCHECK_EQ(base::MessageLoop::current(), message_loop_);
-
- if (pausing_streams_.empty())
- return;
-
- AudioOutputStream* stream = pausing_streams_.back();
- pausing_streams_.pop_back();
- idle_streams_.push_back(stream);
- close_timer_.Reset();
-}
-
void AudioOutputDispatcherImpl::CloseStream(AudioOutputProxy* stream_proxy) {
- DCHECK_EQ(base::MessageLoop::current(), message_loop_);
+ DCHECK(message_loop_->BelongsToCurrentThread());
- while (!pausing_streams_.empty()) {
- idle_streams_.push_back(pausing_streams_.back());
- pausing_streams_.pop_back();
- }
-
- DCHECK_GT(paused_proxies_, 0u);
- paused_proxies_--;
+ DCHECK_GT(idle_proxies_, 0u);
+ --idle_proxies_;
- while (idle_streams_.size() > paused_proxies_) {
- idle_streams_.back()->Close();
- idle_streams_.pop_back();
- }
+ // Leave at least a single stream running until the close timer fires to help
+ // cycle time when streams are opened and closed repeatedly.
+ CloseIdleStreams(std::max(idle_proxies_, static_cast<size_t>(1)));
+ close_timer_.Reset();
}
void AudioOutputDispatcherImpl::Shutdown() {
- DCHECK_EQ(base::MessageLoop::current(), message_loop_);
-
- // Cancel any pending tasks to close paused streams or create new ones.
- weak_this_.InvalidateWeakPtrs();
+ DCHECK(message_loop_->BelongsToCurrentThread());
- // No AudioOutputProxy objects should hold a reference to us when we get
- // to this stage.
- DCHECK(HasOneRef()) << "Only the AudioManager should hold a reference";
-
- AudioOutputStreamList::iterator it = idle_streams_.begin();
- for (; it != idle_streams_.end(); ++it)
- (*it)->Close();
- idle_streams_.clear();
-
- it = pausing_streams_.begin();
- for (; it != pausing_streams_.end(); ++it)
- (*it)->Close();
- pausing_streams_.clear();
+ // Close all idle streams immediately. The |close_timer_| will handle
+ // invalidating any outstanding tasks upon its destruction.
+ CloseAllIdleStreams();
}
bool AudioOutputDispatcherImpl::CreateAndOpenStream() {
- DCHECK_EQ(base::MessageLoop::current(), message_loop_);
+ DCHECK(message_loop_->BelongsToCurrentThread());
AudioOutputStream* stream = audio_manager_->MakeAudioOutputStream(
params_, output_device_id_, input_device_id_);
if (!stream)
@@ -178,29 +139,48 @@ bool AudioOutputDispatcherImpl::CreateAndOpenStream() {
stream->Close();
return false;
}
+
+ const int stream_id = audio_stream_id_++;
+ audio_stream_ids_[stream] = stream_id;
+ audio_log_->OnCreated(
+ stream_id, params_, input_device_id_, output_device_id_);
+
idle_streams_.push_back(stream);
return true;
}
-void AudioOutputDispatcherImpl::OpenTask() {
- DCHECK_EQ(base::MessageLoop::current(), message_loop_);
- // Make sure that we have at least one stream allocated if there
- // are paused streams.
- if (paused_proxies_ > 0 && idle_streams_.empty() &&
- pausing_streams_.empty()) {
- CreateAndOpenStream();
+void AudioOutputDispatcherImpl::CloseAllIdleStreams() {
+ DCHECK(message_loop_->BelongsToCurrentThread());
+ CloseIdleStreams(0);
+}
+
+void AudioOutputDispatcherImpl::CloseIdleStreams(size_t keep_alive) {
+ DCHECK(message_loop_->BelongsToCurrentThread());
+ if (idle_streams_.size() <= keep_alive)
+ return;
+ for (size_t i = keep_alive; i < idle_streams_.size(); ++i) {
+ AudioOutputStream* stream = idle_streams_[i];
+ stream->Close();
+
+ AudioStreamIDMap::iterator it = audio_stream_ids_.find(stream);
+ DCHECK(it != audio_stream_ids_.end());
+ audio_log_->OnClosed(it->second);
+ audio_stream_ids_.erase(it);
}
+ idle_streams_.erase(idle_streams_.begin() + keep_alive, idle_streams_.end());
+}
- close_timer_.Reset();
+void AudioOutputDispatcherImpl::CloseStreamsForWedgeFix() {
+ DCHECK(message_loop_->BelongsToCurrentThread());
+ CloseAllIdleStreams();
}
-// This method is called by |close_timer_|.
-void AudioOutputDispatcherImpl::ClosePendingStreams() {
- DCHECK_EQ(base::MessageLoop::current(), message_loop_);
- while (!idle_streams_.empty()) {
- idle_streams_.back()->Close();
- idle_streams_.pop_back();
- }
+void AudioOutputDispatcherImpl::RestartStreamsForWedgeFix() {
+ DCHECK(message_loop_->BelongsToCurrentThread());
+
+ // Should only be called when the dispatcher is used with fake streams which
+ // don't need to be shutdown or restarted.
+ CHECK_EQ(params_.format(), AudioParameters::AUDIO_FAKE);
}
} // namespace media
diff --git a/chromium/media/audio/audio_output_dispatcher_impl.h b/chromium/media/audio/audio_output_dispatcher_impl.h
index b59f835f9b0..037e11466f1 100644
--- a/chromium/media/audio/audio_output_dispatcher_impl.h
+++ b/chromium/media/audio/audio_output_dispatcher_impl.h
@@ -13,14 +13,14 @@
#ifndef MEDIA_AUDIO_AUDIO_OUTPUT_DISPATCHER_IMPL_H_
#define MEDIA_AUDIO_AUDIO_OUTPUT_DISPATCHER_IMPL_H_
-#include <list>
#include <map>
+#include <vector>
#include "base/basictypes.h"
#include "base/memory/ref_counted.h"
-#include "base/memory/weak_ptr.h"
#include "base/timer/timer.h"
#include "media/audio/audio_io.h"
+#include "media/audio/audio_logging.h"
#include "media/audio/audio_manager.h"
#include "media/audio/audio_output_dispatcher.h"
#include "media/audio/audio_parameters.h"
@@ -31,8 +31,8 @@ class AudioOutputProxy;
class MEDIA_EXPORT AudioOutputDispatcherImpl : public AudioOutputDispatcher {
public:
- // |close_delay_ms| specifies delay after the stream is paused until
- // the audio device is closed.
+ // |close_delay| specifies delay after the stream is idle until the audio
+ // device is closed.
AudioOutputDispatcherImpl(AudioManager* audio_manager,
const AudioParameters& params,
const std::string& output_device_id,
@@ -48,52 +48,53 @@ class MEDIA_EXPORT AudioOutputDispatcherImpl : public AudioOutputDispatcher {
virtual bool StartStream(AudioOutputStream::AudioSourceCallback* callback,
AudioOutputProxy* stream_proxy) OVERRIDE;
- // Holds the physical stream temporarily in |pausing_streams_| and then
- // |stream| is added to the pool of pending streams (i.e. |idle_streams_|).
+ // Stops the stream assigned to the specified proxy and moves it into
+ // |idle_streams_| for reuse by other proxies.
virtual void StopStream(AudioOutputProxy* stream_proxy) OVERRIDE;
virtual void StreamVolumeSet(AudioOutputProxy* stream_proxy,
double volume) OVERRIDE;
+ // Closes |idle_streams_| until the number of |idle_streams_| is equal to the
+ // |idle_proxies_| count. If there are no |idle_proxies_| a single stream is
+ // kept alive until |close_timer_| fires.
virtual void CloseStream(AudioOutputProxy* stream_proxy) OVERRIDE;
virtual void Shutdown() OVERRIDE;
+ virtual void CloseStreamsForWedgeFix() OVERRIDE;
+ virtual void RestartStreamsForWedgeFix() OVERRIDE;
+
private:
- typedef std::map<AudioOutputProxy*, AudioOutputStream*> AudioStreamMap;
friend class base::RefCountedThreadSafe<AudioOutputDispatcherImpl>;
virtual ~AudioOutputDispatcherImpl();
- friend class AudioOutputProxyTest;
-
// Creates a new physical output stream, opens it and pushes to
// |idle_streams_|. Returns false if the stream couldn't be created or
// opened.
bool CreateAndOpenStream();
- // A task scheduled by StartStream(). Opens a new stream and puts
- // it in |idle_streams_|.
- void OpenTask();
-
- // Before a stream is reused, it should sit idle for a bit. This task is
- // called once that time has elapsed.
- void StopStreamTask();
+ // Closes all |idle_streams_|.
+ void CloseAllIdleStreams();
+ // Similar to CloseAllIdleStreams(), but keeps |keep_alive| streams alive.
+ void CloseIdleStreams(size_t keep_alive);
- // Called by |close_timer_|. Closes all pending streams.
- void ClosePendingStreams();
+ size_t idle_proxies_;
+ std::vector<AudioOutputStream*> idle_streams_;
- base::TimeDelta pause_delay_;
- size_t paused_proxies_;
- typedef std::list<AudioOutputStream*> AudioOutputStreamList;
- AudioOutputStreamList idle_streams_;
- AudioOutputStreamList pausing_streams_;
-
- // Used to post delayed tasks to ourselves that we cancel inside Shutdown().
- base::WeakPtrFactory<AudioOutputDispatcherImpl> weak_this_;
+ // When streams are stopped they're added to |idle_streams_|, if no stream is
+ // reused before |close_delay_| elapses |close_timer_| will run
+ // CloseIdleStreams().
base::DelayTimer<AudioOutputDispatcherImpl> close_timer_;
+ typedef std::map<AudioOutputProxy*, AudioOutputStream*> AudioStreamMap;
AudioStreamMap proxy_to_physical_map_;
+ scoped_ptr<AudioLog> audio_log_;
+ typedef std::map<AudioOutputStream*, int> AudioStreamIDMap;
+ AudioStreamIDMap audio_stream_ids_;
+ int audio_stream_id_;
+
DISALLOW_COPY_AND_ASSIGN(AudioOutputDispatcherImpl);
};
diff --git a/chromium/media/audio/audio_output_proxy_unittest.cc b/chromium/media/audio/audio_output_proxy_unittest.cc
index 1806ce66131..cea098820aa 100644
--- a/chromium/media/audio/audio_output_proxy_unittest.cc
+++ b/chromium/media/audio/audio_output_proxy_unittest.cc
@@ -6,11 +6,13 @@
#include "base/message_loop/message_loop.h"
#include "base/message_loop/message_loop_proxy.h"
+#include "base/run_loop.h"
#include "media/audio/audio_manager.h"
#include "media/audio/audio_manager_base.h"
#include "media/audio/audio_output_dispatcher_impl.h"
#include "media/audio/audio_output_proxy.h"
#include "media/audio/audio_output_resampler.h"
+#include "media/audio/fake_audio_log_factory.h"
#include "media/audio/fake_audio_output_stream.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -36,10 +38,7 @@ using media::FakeAudioOutputStream;
namespace {
-static const int kTestCloseDelayMs = 100;
-
-// Used in the test where we don't want a stream to be closed unexpectedly.
-static const int kTestBigCloseDelaySeconds = 1000;
+static const int kTestCloseDelayMs = 10;
// Delay between callbacks to AudioSourceCallback::OnMoreData.
static const int kOnMoreDataCallbackDelayMs = 10;
@@ -87,14 +86,14 @@ class MockAudioOutputStream : public AudioOutputStream {
class MockAudioManager : public AudioManagerBase {
public:
- MockAudioManager() {}
+ MockAudioManager() : AudioManagerBase(&fake_audio_log_factory_) {}
virtual ~MockAudioManager() {
Shutdown();
}
MOCK_METHOD0(HasAudioOutputDevices, bool());
MOCK_METHOD0(HasAudioInputDevices, bool());
- MOCK_METHOD0(GetAudioInputDeviceModel, string16());
+ MOCK_METHOD0(GetAudioInputDeviceModel, base::string16());
MOCK_METHOD3(MakeAudioOutputStream, AudioOutputStream*(
const AudioParameters& params,
const std::string& device_id,
@@ -107,6 +106,7 @@ class MockAudioManager : public AudioManagerBase {
const AudioParameters& params, const std::string& device_id));
MOCK_METHOD0(ShowAudioInputSettings, void());
MOCK_METHOD0(GetMessageLoop, scoped_refptr<base::MessageLoopProxy>());
+ MOCK_METHOD0(GetWorkerLoop, scoped_refptr<base::MessageLoopProxy>());
MOCK_METHOD1(GetAudioInputDeviceNames, void(
media::AudioDeviceNames* device_name));
@@ -121,6 +121,9 @@ class MockAudioManager : public AudioManagerBase {
const AudioParameters& params, const std::string& device_id));
MOCK_METHOD2(GetPreferredOutputStreamParameters, AudioParameters(
const std::string& device_id, const AudioParameters& params));
+
+ private:
+ media::FakeAudioLogFactory fake_audio_log_factory_;
};
class MockAudioSourceCallback : public AudioOutputStream::AudioSourceCallback {
@@ -145,33 +148,28 @@ class AudioOutputProxyTest : public testing::Test {
virtual void SetUp() {
EXPECT_CALL(manager_, GetMessageLoop())
.WillRepeatedly(Return(message_loop_.message_loop_proxy()));
+ EXPECT_CALL(manager_, GetWorkerLoop())
+ .WillRepeatedly(Return(message_loop_.message_loop_proxy()));
+ // Use a low sample rate and large buffer size when testing otherwise the
+ // FakeAudioOutputStream will keep the message loop busy indefinitely; i.e.,
+ // RunUntilIdle() will never terminate.
+ params_ = AudioParameters(AudioParameters::AUDIO_PCM_LINEAR,
+ CHANNEL_LAYOUT_STEREO, 8000, 16, 2048);
InitDispatcher(base::TimeDelta::FromMilliseconds(kTestCloseDelayMs));
}
virtual void TearDown() {
- // All paused proxies should have been closed at this point.
- EXPECT_EQ(0u, dispatcher_impl_->paused_proxies_);
-
// This is necessary to free all proxy objects that have been
// closed by the test.
message_loop_.RunUntilIdle();
}
virtual void InitDispatcher(base::TimeDelta close_delay) {
- // Use a low sample rate and large buffer size when testing otherwise the
- // FakeAudioOutputStream will keep the message loop busy indefinitely; i.e.,
- // RunUntilIdle() will never terminate.
- params_ = AudioParameters(AudioParameters::AUDIO_PCM_LINEAR,
- CHANNEL_LAYOUT_STEREO, 8000, 16, 2048);
dispatcher_impl_ = new AudioOutputDispatcherImpl(&manager(),
params_,
std::string(),
std::string(),
close_delay);
-
- // Necessary to know how long the dispatcher will wait before posting
- // StopStreamTask.
- pause_delay_ = dispatcher_impl_->pause_delay_;
}
virtual void OnStart() {}
@@ -180,15 +178,24 @@ class AudioOutputProxyTest : public testing::Test {
return manager_;
}
- // Wait for the close timer to fire.
- void WaitForCloseTimer(const int timer_delay_ms) {
- message_loop_.RunUntilIdle(); // OpenTask() may reset the timer.
- base::PlatformThread::Sleep(
- base::TimeDelta::FromMilliseconds(timer_delay_ms) * 2);
- message_loop_.RunUntilIdle();
+ void WaitForCloseTimer(MockAudioOutputStream* stream) {
+ base::RunLoop run_loop;
+ EXPECT_CALL(*stream, Close())
+ .WillOnce(testing::InvokeWithoutArgs(&run_loop, &base::RunLoop::Quit));
+ run_loop.Run();
}
- // Methods that do actual tests.
+ void CloseAndWaitForCloseTimer(AudioOutputProxy* proxy,
+ MockAudioOutputStream* stream) {
+ // Close the stream and verify it doesn't happen immediately.
+ proxy->Close();
+ Mock::VerifyAndClear(stream);
+
+ // Wait for the actual close event to come from the close timer.
+ WaitForCloseTimer(stream);
+ }
+
+ // Basic Open() and Close() test.
void OpenAndClose(AudioOutputDispatcher* dispatcher) {
MockAudioOutputStream stream(&manager_, params_);
@@ -196,16 +203,13 @@ class AudioOutputProxyTest : public testing::Test {
.WillOnce(Return(&stream));
EXPECT_CALL(stream, Open())
.WillOnce(Return(true));
- EXPECT_CALL(stream, Close())
- .Times(1);
AudioOutputProxy* proxy = new AudioOutputProxy(dispatcher);
EXPECT_TRUE(proxy->Open());
- proxy->Close();
- WaitForCloseTimer(kTestCloseDelayMs);
+ CloseAndWaitForCloseTimer(proxy, &stream);
}
- // Create a stream, and then calls Start() and Stop().
+ // Creates a stream, and then calls Start() and Stop().
void StartAndStop(AudioOutputDispatcher* dispatcher) {
MockAudioOutputStream stream(&manager_, params_);
@@ -215,8 +219,6 @@ class AudioOutputProxyTest : public testing::Test {
.WillOnce(Return(true));
EXPECT_CALL(stream, SetVolume(_))
.Times(1);
- EXPECT_CALL(stream, Close())
- .Times(1);
AudioOutputProxy* proxy = new AudioOutputProxy(dispatcher);
EXPECT_TRUE(proxy->Open());
@@ -225,13 +227,12 @@ class AudioOutputProxyTest : public testing::Test {
OnStart();
proxy->Stop();
- proxy->Close();
- WaitForCloseTimer(kTestCloseDelayMs);
+ CloseAndWaitForCloseTimer(proxy, &stream);
EXPECT_TRUE(stream.stop_called());
EXPECT_TRUE(stream.start_called());
}
- // Verify that the stream is closed after Stop is called.
+ // Verify that the stream is closed after Stop() is called.
void CloseAfterStop(AudioOutputDispatcher* dispatcher) {
MockAudioOutputStream stream(&manager_, params_);
@@ -241,8 +242,6 @@ class AudioOutputProxyTest : public testing::Test {
.WillOnce(Return(true));
EXPECT_CALL(stream, SetVolume(_))
.Times(1);
- EXPECT_CALL(stream, Close())
- .Times(1);
AudioOutputProxy* proxy = new AudioOutputProxy(dispatcher);
EXPECT_TRUE(proxy->Open());
@@ -251,19 +250,14 @@ class AudioOutputProxyTest : public testing::Test {
OnStart();
proxy->Stop();
- // Wait for StopStream() to post StopStreamTask().
- base::PlatformThread::Sleep(pause_delay_ * 2);
- WaitForCloseTimer(kTestCloseDelayMs);
-
- // Verify expectation before calling Close().
- Mock::VerifyAndClear(&stream);
-
+ // Wait for the close timer to fire after StopStream().
+ WaitForCloseTimer(&stream);
proxy->Close();
EXPECT_TRUE(stream.stop_called());
EXPECT_TRUE(stream.start_called());
}
- // Create two streams, but don't start them. Only one device must be open.
+ // Create two streams, but don't start them. Only one device must be opened.
void TwoStreams(AudioOutputDispatcher* dispatcher) {
MockAudioOutputStream stream(&manager_, params_);
@@ -271,16 +265,13 @@ class AudioOutputProxyTest : public testing::Test {
.WillOnce(Return(&stream));
EXPECT_CALL(stream, Open())
.WillOnce(Return(true));
- EXPECT_CALL(stream, Close())
- .Times(1);
AudioOutputProxy* proxy1 = new AudioOutputProxy(dispatcher);
AudioOutputProxy* proxy2 = new AudioOutputProxy(dispatcher);
EXPECT_TRUE(proxy1->Open());
EXPECT_TRUE(proxy2->Open());
proxy1->Close();
- proxy2->Close();
- WaitForCloseTimer(kTestCloseDelayMs);
+ CloseAndWaitForCloseTimer(proxy2, &stream);
EXPECT_FALSE(stream.stop_called());
EXPECT_FALSE(stream.start_called());
}
@@ -299,7 +290,6 @@ class AudioOutputProxyTest : public testing::Test {
AudioOutputProxy* proxy = new AudioOutputProxy(dispatcher);
EXPECT_FALSE(proxy->Open());
proxy->Close();
- WaitForCloseTimer(kTestCloseDelayMs);
EXPECT_FALSE(stream.stop_called());
EXPECT_FALSE(stream.start_called());
}
@@ -311,61 +301,45 @@ class AudioOutputProxyTest : public testing::Test {
.WillOnce(Return(&stream));
EXPECT_CALL(stream, Open())
.WillOnce(Return(true));
- EXPECT_CALL(stream, Close())
- .Times(1);
AudioOutputProxy* proxy = new AudioOutputProxy(dispatcher);
EXPECT_TRUE(proxy->Open());
- // Simulate a delay.
- base::PlatformThread::Sleep(
- base::TimeDelta::FromMilliseconds(kTestCloseDelayMs) * 2);
- message_loop_.RunUntilIdle();
-
- // Verify expectation before calling Close().
- Mock::VerifyAndClear(&stream);
-
+ WaitForCloseTimer(&stream);
proxy->Close();
EXPECT_FALSE(stream.stop_called());
EXPECT_FALSE(stream.start_called());
}
- void TwoStreams_OnePlaying(AudioOutputDispatcher* dispatcher) {
- MockAudioOutputStream stream1(&manager_, params_);
- MockAudioOutputStream stream2(&manager_, params_);
+ void OneStream_TwoPlays(AudioOutputDispatcher* dispatcher) {
+ MockAudioOutputStream stream(&manager_, params_);
EXPECT_CALL(manager(), MakeAudioOutputStream(_, _, _))
- .WillOnce(Return(&stream1))
- .WillOnce(Return(&stream2));
-
- EXPECT_CALL(stream1, Open())
- .WillOnce(Return(true));
- EXPECT_CALL(stream1, SetVolume(_))
- .Times(1);
- EXPECT_CALL(stream1, Close())
- .Times(1);
+ .WillOnce(Return(&stream));
- EXPECT_CALL(stream2, Open())
+ EXPECT_CALL(stream, Open())
.WillOnce(Return(true));
- EXPECT_CALL(stream2, Close())
- .Times(1);
+ EXPECT_CALL(stream, SetVolume(_))
+ .Times(2);
AudioOutputProxy* proxy1 = new AudioOutputProxy(dispatcher);
- AudioOutputProxy* proxy2 = new AudioOutputProxy(dispatcher);
EXPECT_TRUE(proxy1->Open());
- EXPECT_TRUE(proxy2->Open());
proxy1->Start(&callback_);
- message_loop_.RunUntilIdle();
OnStart();
proxy1->Stop();
+ // The stream should now be idle and get reused by |proxy2|.
+ AudioOutputProxy* proxy2 = new AudioOutputProxy(dispatcher);
+ EXPECT_TRUE(proxy2->Open());
+ proxy2->Start(&callback_);
+ OnStart();
+ proxy2->Stop();
+
proxy1->Close();
- proxy2->Close();
- EXPECT_TRUE(stream1.stop_called());
- EXPECT_TRUE(stream1.start_called());
- EXPECT_FALSE(stream2.stop_called());
- EXPECT_FALSE(stream2.start_called());
+ CloseAndWaitForCloseTimer(proxy2, &stream);
+ EXPECT_TRUE(stream.stop_called());
+ EXPECT_TRUE(stream.start_called());
}
void TwoStreams_BothPlaying(AudioOutputDispatcher* dispatcher) {
@@ -380,15 +354,11 @@ class AudioOutputProxyTest : public testing::Test {
.WillOnce(Return(true));
EXPECT_CALL(stream1, SetVolume(_))
.Times(1);
- EXPECT_CALL(stream1, Close())
- .Times(1);
EXPECT_CALL(stream2, Open())
.WillOnce(Return(true));
EXPECT_CALL(stream2, SetVolume(_))
.Times(1);
- EXPECT_CALL(stream2, Close())
- .Times(1);
AudioOutputProxy* proxy1 = new AudioOutputProxy(dispatcher);
AudioOutputProxy* proxy2 = new AudioOutputProxy(dispatcher);
@@ -399,10 +369,11 @@ class AudioOutputProxyTest : public testing::Test {
proxy2->Start(&callback_);
OnStart();
proxy1->Stop();
+ CloseAndWaitForCloseTimer(proxy1, &stream1);
+
proxy2->Stop();
+ CloseAndWaitForCloseTimer(proxy2, &stream2);
- proxy1->Close();
- proxy2->Close();
EXPECT_TRUE(stream1.stop_called());
EXPECT_TRUE(stream1.start_called());
EXPECT_TRUE(stream2.stop_called());
@@ -416,19 +387,11 @@ class AudioOutputProxyTest : public testing::Test {
.WillOnce(Return(&stream));
EXPECT_CALL(stream, Open())
.WillOnce(Return(true));
- EXPECT_CALL(stream, Close())
- .Times(1);
AudioOutputProxy* proxy = new AudioOutputProxy(dispatcher);
EXPECT_TRUE(proxy->Open());
- // Simulate a delay.
- base::PlatformThread::Sleep(
- base::TimeDelta::FromMilliseconds(kTestCloseDelayMs) * 2);
- message_loop_.RunUntilIdle();
-
- // Verify expectation before calling Close().
- Mock::VerifyAndClear(&stream);
+ WaitForCloseTimer(&stream);
// |stream| is closed at this point. Start() should reopen it again.
EXPECT_CALL(manager(), MakeAudioOutputStream(_, _, _))
@@ -452,7 +415,6 @@ class AudioOutputProxyTest : public testing::Test {
base::MessageLoop message_loop_;
scoped_refptr<AudioOutputDispatcherImpl> dispatcher_impl_;
- base::TimeDelta pause_delay_;
MockAudioManager manager_;
MockAudioSourceCallback callback_;
AudioParameters params_;
@@ -465,7 +427,6 @@ class AudioOutputResamplerTest : public AudioOutputProxyTest {
}
virtual void InitDispatcher(base::TimeDelta close_delay) OVERRIDE {
- AudioOutputProxyTest::InitDispatcher(close_delay);
// Use a low sample rate and large buffer size when testing otherwise the
// FakeAudioOutputStream will keep the message loop busy indefinitely; i.e.,
// RunUntilIdle() will never terminate.
@@ -478,10 +439,13 @@ class AudioOutputResamplerTest : public AudioOutputProxyTest {
}
virtual void OnStart() OVERRIDE {
- // Let start run for a bit.
- message_loop_.RunUntilIdle();
- base::PlatformThread::Sleep(
+ // Let Start() run for a bit.
+ base::RunLoop run_loop;
+ message_loop_.PostDelayedTask(
+ FROM_HERE,
+ run_loop.QuitClosure(),
base::TimeDelta::FromMilliseconds(kStartRunTimeMs));
+ run_loop.Run();
}
protected:
@@ -490,86 +454,82 @@ class AudioOutputResamplerTest : public AudioOutputProxyTest {
};
TEST_F(AudioOutputProxyTest, CreateAndClose) {
- AudioOutputProxy* proxy = new AudioOutputProxy(dispatcher_impl_.get());
+ AudioOutputProxy* proxy = new AudioOutputProxy(dispatcher_impl_);
proxy->Close();
}
TEST_F(AudioOutputResamplerTest, CreateAndClose) {
- AudioOutputProxy* proxy = new AudioOutputProxy(resampler_.get());
+ AudioOutputProxy* proxy = new AudioOutputProxy(resampler_);
proxy->Close();
}
TEST_F(AudioOutputProxyTest, OpenAndClose) {
- OpenAndClose(dispatcher_impl_.get());
+ OpenAndClose(dispatcher_impl_);
}
TEST_F(AudioOutputResamplerTest, OpenAndClose) {
- OpenAndClose(resampler_.get());
+ OpenAndClose(resampler_);
}
// Create a stream, and verify that it is closed after kTestCloseDelayMs.
// if it doesn't start playing.
TEST_F(AudioOutputProxyTest, CreateAndWait) {
- CreateAndWait(dispatcher_impl_.get());
+ CreateAndWait(dispatcher_impl_);
}
// Create a stream, and verify that it is closed after kTestCloseDelayMs.
// if it doesn't start playing.
TEST_F(AudioOutputResamplerTest, CreateAndWait) {
- CreateAndWait(resampler_.get());
+ CreateAndWait(resampler_);
}
TEST_F(AudioOutputProxyTest, StartAndStop) {
- StartAndStop(dispatcher_impl_.get());
+ StartAndStop(dispatcher_impl_);
}
TEST_F(AudioOutputResamplerTest, StartAndStop) {
- StartAndStop(resampler_.get());
+ StartAndStop(resampler_);
}
TEST_F(AudioOutputProxyTest, CloseAfterStop) {
- CloseAfterStop(dispatcher_impl_.get());
+ CloseAfterStop(dispatcher_impl_);
}
TEST_F(AudioOutputResamplerTest, CloseAfterStop) {
- CloseAfterStop(resampler_.get());
+ CloseAfterStop(resampler_);
}
-TEST_F(AudioOutputProxyTest, TwoStreams) { TwoStreams(dispatcher_impl_.get()); }
+TEST_F(AudioOutputProxyTest, TwoStreams) { TwoStreams(dispatcher_impl_); }
-TEST_F(AudioOutputResamplerTest, TwoStreams) { TwoStreams(resampler_.get()); }
+TEST_F(AudioOutputResamplerTest, TwoStreams) { TwoStreams(resampler_); }
// Two streams: verify that second stream is allocated when the first
// starts playing.
-TEST_F(AudioOutputProxyTest, TwoStreams_OnePlaying) {
- InitDispatcher(base::TimeDelta::FromSeconds(kTestBigCloseDelaySeconds));
- TwoStreams_OnePlaying(dispatcher_impl_.get());
+TEST_F(AudioOutputProxyTest, OneStream_TwoPlays) {
+ OneStream_TwoPlays(dispatcher_impl_);
}
-TEST_F(AudioOutputResamplerTest, TwoStreams_OnePlaying) {
- InitDispatcher(base::TimeDelta::FromSeconds(kTestBigCloseDelaySeconds));
- TwoStreams_OnePlaying(resampler_.get());
+TEST_F(AudioOutputResamplerTest, OneStream_TwoPlays) {
+ OneStream_TwoPlays(resampler_);
}
// Two streams, both are playing. Dispatcher should not open a third stream.
TEST_F(AudioOutputProxyTest, TwoStreams_BothPlaying) {
- InitDispatcher(base::TimeDelta::FromSeconds(kTestBigCloseDelaySeconds));
- TwoStreams_BothPlaying(dispatcher_impl_.get());
+ TwoStreams_BothPlaying(dispatcher_impl_);
}
TEST_F(AudioOutputResamplerTest, TwoStreams_BothPlaying) {
- InitDispatcher(base::TimeDelta::FromSeconds(kTestBigCloseDelaySeconds));
- TwoStreams_BothPlaying(resampler_.get());
+ TwoStreams_BothPlaying(resampler_);
}
-TEST_F(AudioOutputProxyTest, OpenFailed) { OpenFailed(dispatcher_impl_.get()); }
+TEST_F(AudioOutputProxyTest, OpenFailed) { OpenFailed(dispatcher_impl_); }
// Start() method failed.
TEST_F(AudioOutputProxyTest, StartFailed) {
- StartFailed(dispatcher_impl_.get());
+ StartFailed(dispatcher_impl_);
}
-TEST_F(AudioOutputResamplerTest, StartFailed) { StartFailed(resampler_.get()); }
+TEST_F(AudioOutputResamplerTest, StartFailed) { StartFailed(resampler_); }
// Simulate AudioOutputStream::Create() failure with a low latency stream and
// ensure AudioOutputResampler falls back to the high latency path.
@@ -581,13 +541,10 @@ TEST_F(AudioOutputResamplerTest, LowLatencyCreateFailedFallback) {
.WillRepeatedly(Return(&stream));
EXPECT_CALL(stream, Open())
.WillOnce(Return(true));
- EXPECT_CALL(stream, Close())
- .Times(1);
- AudioOutputProxy* proxy = new AudioOutputProxy(resampler_.get());
+ AudioOutputProxy* proxy = new AudioOutputProxy(resampler_);
EXPECT_TRUE(proxy->Open());
- proxy->Close();
- WaitForCloseTimer(kTestCloseDelayMs);
+ CloseAndWaitForCloseTimer(proxy, &stream);
}
// Simulate AudioOutputStream::Open() failure with a low latency stream and
@@ -605,13 +562,10 @@ TEST_F(AudioOutputResamplerTest, LowLatencyOpenFailedFallback) {
.Times(1);
EXPECT_CALL(okay_stream, Open())
.WillOnce(Return(true));
- EXPECT_CALL(okay_stream, Close())
- .Times(1);
- AudioOutputProxy* proxy = new AudioOutputProxy(resampler_.get());
+ AudioOutputProxy* proxy = new AudioOutputProxy(resampler_);
EXPECT_TRUE(proxy->Open());
- proxy->Close();
- WaitForCloseTimer(kTestCloseDelayMs);
+ CloseAndWaitForCloseTimer(proxy, &okay_stream);
}
// Simulate failures to open both the low latency and the fallback high latency
@@ -642,13 +596,10 @@ TEST_F(AudioOutputResamplerTest, HighLatencyFallbackFailed) {
.WillOnce(Return(&okay_stream));
EXPECT_CALL(okay_stream, Open())
.WillOnce(Return(true));
- EXPECT_CALL(okay_stream, Close())
- .Times(1);
- AudioOutputProxy* proxy = new AudioOutputProxy(resampler_.get());
+ AudioOutputProxy* proxy = new AudioOutputProxy(resampler_);
EXPECT_TRUE(proxy->Open());
- proxy->Close();
- WaitForCloseTimer(kTestCloseDelayMs);
+ CloseAndWaitForCloseTimer(proxy, &okay_stream);
}
// Simulate failures to open both the low latency, the fallback high latency
@@ -666,10 +617,9 @@ TEST_F(AudioOutputResamplerTest, AllFallbackFailed) {
.Times(kFallbackCount)
.WillRepeatedly(Return(static_cast<AudioOutputStream*>(NULL)));
- AudioOutputProxy* proxy = new AudioOutputProxy(resampler_.get());
+ AudioOutputProxy* proxy = new AudioOutputProxy(resampler_);
EXPECT_FALSE(proxy->Open());
proxy->Close();
- WaitForCloseTimer(kTestCloseDelayMs);
}
// Simulate an eventual OpenStream() failure; i.e. successful OpenStream() calls
@@ -677,72 +627,126 @@ TEST_F(AudioOutputResamplerTest, AllFallbackFailed) {
TEST_F(AudioOutputResamplerTest, LowLatencyOpenEventuallyFails) {
MockAudioOutputStream stream1(&manager_, params_);
MockAudioOutputStream stream2(&manager_, params_);
- MockAudioOutputStream stream3(&manager_, params_);
// Setup the mock such that all three streams are successfully created.
EXPECT_CALL(manager(), MakeAudioOutputStream(_, _, _))
.WillOnce(Return(&stream1))
.WillOnce(Return(&stream2))
- .WillOnce(Return(&stream3))
.WillRepeatedly(Return(static_cast<AudioOutputStream*>(NULL)));
// Stream1 should be able to successfully open and start.
EXPECT_CALL(stream1, Open())
.WillOnce(Return(true));
- EXPECT_CALL(stream1, Close())
- .Times(1);
EXPECT_CALL(stream1, SetVolume(_))
.Times(1);
// Stream2 should also be able to successfully open and start.
EXPECT_CALL(stream2, Open())
.WillOnce(Return(true));
- EXPECT_CALL(stream2, Close())
- .Times(1);
EXPECT_CALL(stream2, SetVolume(_))
.Times(1);
- // Stream3 should fail on Open() (yet still be closed since
- // MakeAudioOutputStream returned a valid AudioOutputStream object).
- EXPECT_CALL(stream3, Open())
- .WillOnce(Return(false));
- EXPECT_CALL(stream3, Close())
- .Times(1);
-
// Open and start the first proxy and stream.
- AudioOutputProxy* proxy1 = new AudioOutputProxy(resampler_.get());
+ AudioOutputProxy* proxy1 = new AudioOutputProxy(resampler_);
EXPECT_TRUE(proxy1->Open());
proxy1->Start(&callback_);
OnStart();
// Open and start the second proxy and stream.
- AudioOutputProxy* proxy2 = new AudioOutputProxy(resampler_.get());
+ AudioOutputProxy* proxy2 = new AudioOutputProxy(resampler_);
EXPECT_TRUE(proxy2->Open());
proxy2->Start(&callback_);
OnStart();
// Attempt to open the third stream which should fail.
- AudioOutputProxy* proxy3 = new AudioOutputProxy(resampler_.get());
+ AudioOutputProxy* proxy3 = new AudioOutputProxy(resampler_);
EXPECT_FALSE(proxy3->Open());
+ proxy3->Close();
// Perform the required Stop()/Close() shutdown dance for each proxy. Under
// the hood each proxy should correctly call CloseStream() if OpenStream()
// succeeded or not.
+ proxy2->Stop();
+ CloseAndWaitForCloseTimer(proxy2, &stream2);
+
+ proxy1->Stop();
+ CloseAndWaitForCloseTimer(proxy1, &stream1);
+
+ EXPECT_TRUE(stream1.stop_called());
+ EXPECT_TRUE(stream1.start_called());
+ EXPECT_TRUE(stream2.stop_called());
+ EXPECT_TRUE(stream2.start_called());
+}
+
+// Ensures the methods used to fix audio output wedges are working correctly.
+TEST_F(AudioOutputResamplerTest, WedgeFix) {
+ MockAudioOutputStream stream1(&manager_, params_);
+ MockAudioOutputStream stream2(&manager_, params_);
+ MockAudioOutputStream stream3(&manager_, params_);
+
+ // Setup the mock such that all three streams are successfully created.
+ EXPECT_CALL(manager(), MakeAudioOutputStream(_, _, _))
+ .WillOnce(Return(&stream1))
+ .WillOnce(Return(&stream2))
+ .WillOnce(Return(&stream3));
+
+ // Stream1 should be able to successfully open and start.
+ EXPECT_CALL(stream1, Open())
+ .WillOnce(Return(true));
+ EXPECT_CALL(stream1, SetVolume(_));
+ EXPECT_CALL(stream2, Open())
+ .WillOnce(Return(true));
+ EXPECT_CALL(stream2, SetVolume(_));
+
+ // Open and start the first proxy and stream.
+ AudioOutputProxy* proxy1 = new AudioOutputProxy(resampler_.get());
+ EXPECT_TRUE(proxy1->Open());
+ proxy1->Start(&callback_);
+ OnStart();
+
+ // Open, but do not start the second proxy.
+ AudioOutputProxy* proxy2 = new AudioOutputProxy(resampler_.get());
+ EXPECT_TRUE(proxy2->Open());
+
+ // Open, start and then stop the third proxy.
+ AudioOutputProxy* proxy3 = new AudioOutputProxy(resampler_.get());
+ EXPECT_TRUE(proxy3->Open());
+ proxy3->Start(&callback_);
+ OnStart();
proxy3->Stop();
+
+ // Wait for stream to timeout and shutdown.
+ WaitForCloseTimer(&stream2);
+
+ EXPECT_CALL(stream1, Close());
+ resampler_->CloseStreamsForWedgeFix();
+
+ // Don't pump the MessageLoop between CloseStreamsForWedgeFix() and
+ // RestartStreamsForWedgeFix() to simulate intended usage. The OnStart() call
+ // will take care of necessary work.
+
+ // Stream3 should take Stream1's place after RestartStreamsForWedgeFix(). No
+ // additional streams should be opened for proxy2 and proxy3.
+ EXPECT_CALL(stream3, Open())
+ .WillOnce(Return(true));
+ EXPECT_CALL(stream3, SetVolume(_));
+
+ resampler_->RestartStreamsForWedgeFix();
+ OnStart();
+
+ // Perform the required Stop()/Close() shutdown dance for each proxy.
proxy3->Close();
- proxy2->Stop();
proxy2->Close();
proxy1->Stop();
- proxy1->Close();
+ CloseAndWaitForCloseTimer(proxy1, &stream3);
// Wait for all of the messages to fly and then verify stream behavior.
- WaitForCloseTimer(kTestCloseDelayMs);
EXPECT_TRUE(stream1.stop_called());
EXPECT_TRUE(stream1.start_called());
EXPECT_TRUE(stream2.stop_called());
EXPECT_TRUE(stream2.start_called());
- EXPECT_FALSE(stream3.stop_called());
- EXPECT_FALSE(stream3.start_called());
+ EXPECT_TRUE(stream3.stop_called());
+ EXPECT_TRUE(stream3.start_called());
}
} // namespace media
diff --git a/chromium/media/audio/audio_output_resampler.cc b/chromium/media/audio/audio_output_resampler.cc
index da424ec1246..c53f3e089ce 100644
--- a/chromium/media/audio/audio_output_resampler.cc
+++ b/chromium/media/audio/audio_output_resampler.cc
@@ -14,7 +14,6 @@
#include "media/audio/audio_io.h"
#include "media/audio/audio_output_dispatcher_impl.h"
#include "media/audio/audio_output_proxy.h"
-#include "media/audio/audio_util.h"
#include "media/audio/sample_rates.h"
#include "media/base/audio_converter.h"
#include "media/base/limits.h"
@@ -44,6 +43,8 @@ class OnMoreDataConverter
// Clears |source_callback_| and flushes the resampler.
void Stop();
+ bool started() { return source_callback_ != NULL; }
+
private:
// AudioConverter::InputCallback implementation.
virtual double ProvideInput(AudioBus* audio_bus,
@@ -51,15 +52,11 @@ class OnMoreDataConverter
// Ratio of input bytes to output bytes used to correct playback delay with
// regard to buffering and resampling.
- double io_ratio_;
+ const double io_ratio_;
- // Source callback and associated lock.
- base::Lock source_lock_;
+ // Source callback.
AudioOutputStream::AudioSourceCallback* source_callback_;
- // |source| passed to OnMoreIOData() which should be passed downstream.
- AudioBus* source_bus_;
-
// Last AudioBuffersState object received via OnMoreData(), used to correct
// playback delay by ProvideInput() and passed on to |source_callback_|.
AudioBuffersState current_buffers_state_;
@@ -121,28 +118,27 @@ static void RecordFallbackStats(const AudioParameters& output_params) {
}
}
+// Converts low latency based |output_params| into high latency appropriate
+// output parameters in error situations.
+void AudioOutputResampler::SetupFallbackParams() {
// Only Windows has a high latency output driver that is not the same as the low
// latency path.
#if defined(OS_WIN)
-// Converts low latency based |output_params| into high latency appropriate
-// output parameters in error situations.
-static AudioParameters SetupFallbackParams(
- const AudioParameters& input_params, const AudioParameters& output_params) {
// Choose AudioParameters appropriate for opening the device in high latency
// mode. |kMinLowLatencyFrameSize| is arbitrarily based on Pepper Flash's
// MAXIMUM frame size for low latency.
static const int kMinLowLatencyFrameSize = 2048;
- int frames_per_buffer = std::min(
- std::max(input_params.frames_per_buffer(), kMinLowLatencyFrameSize),
- static_cast<int>(
- GetHighLatencyOutputBufferSize(input_params.sample_rate())));
-
- return AudioParameters(
- AudioParameters::AUDIO_PCM_LINEAR, input_params.channel_layout(),
- input_params.sample_rate(), input_params.bits_per_sample(),
+ const int frames_per_buffer =
+ std::max(params_.frames_per_buffer(), kMinLowLatencyFrameSize);
+
+ output_params_ = AudioParameters(
+ AudioParameters::AUDIO_PCM_LINEAR, params_.channel_layout(),
+ params_.sample_rate(), params_.bits_per_sample(),
frames_per_buffer);
-}
+ output_device_id_ = "";
+ Initialize();
#endif
+}
AudioOutputResampler::AudioOutputResampler(AudioManager* audio_manager,
const AudioParameters& input_params,
@@ -178,7 +174,7 @@ void AudioOutputResampler::Initialize() {
}
bool AudioOutputResampler::OpenStream() {
- DCHECK_EQ(base::MessageLoop::current(), message_loop_);
+ DCHECK(message_loop_->BelongsToCurrentThread());
if (dispatcher_->OpenStream()) {
// Only record the UMA statistic if we didn't fallback during construction
@@ -210,8 +206,7 @@ bool AudioOutputResampler::OpenStream() {
DLOG(ERROR) << "Unable to open audio device in low latency mode. Falling "
<< "back to high latency audio output.";
- output_params_ = SetupFallbackParams(params_, output_params_);
- Initialize();
+ SetupFallbackParams();
if (dispatcher_->OpenStream()) {
streams_opened_ = true;
return true;
@@ -238,7 +233,7 @@ bool AudioOutputResampler::OpenStream() {
bool AudioOutputResampler::StartStream(
AudioOutputStream::AudioSourceCallback* callback,
AudioOutputProxy* stream_proxy) {
- DCHECK_EQ(base::MessageLoop::current(), message_loop_);
+ DCHECK(message_loop_->BelongsToCurrentThread());
OnMoreDataConverter* resampler_callback = NULL;
CallbackMap::iterator it = callbacks_.find(stream_proxy);
@@ -258,12 +253,12 @@ bool AudioOutputResampler::StartStream(
void AudioOutputResampler::StreamVolumeSet(AudioOutputProxy* stream_proxy,
double volume) {
- DCHECK_EQ(base::MessageLoop::current(), message_loop_);
+ DCHECK(message_loop_->BelongsToCurrentThread());
dispatcher_->StreamVolumeSet(stream_proxy, volume);
}
void AudioOutputResampler::StopStream(AudioOutputProxy* stream_proxy) {
- DCHECK_EQ(base::MessageLoop::current(), message_loop_);
+ DCHECK(message_loop_->BelongsToCurrentThread());
dispatcher_->StopStream(stream_proxy);
// Now that StopStream() has completed the underlying physical stream should
@@ -275,7 +270,7 @@ void AudioOutputResampler::StopStream(AudioOutputProxy* stream_proxy) {
}
void AudioOutputResampler::CloseStream(AudioOutputProxy* stream_proxy) {
- DCHECK_EQ(base::MessageLoop::current(), message_loop_);
+ DCHECK(message_loop_->BelongsToCurrentThread());
dispatcher_->CloseStream(stream_proxy);
// We assume that StopStream() is always called prior to CloseStream(), so
@@ -288,7 +283,7 @@ void AudioOutputResampler::CloseStream(AudioOutputProxy* stream_proxy) {
}
void AudioOutputResampler::Shutdown() {
- DCHECK_EQ(base::MessageLoop::current(), message_loop_);
+ DCHECK(message_loop_->BelongsToCurrentThread());
// No AudioOutputProxy objects should hold a reference to us when we get
// to this stage.
@@ -298,16 +293,44 @@ void AudioOutputResampler::Shutdown() {
DCHECK(callbacks_.empty());
}
+void AudioOutputResampler::CloseStreamsForWedgeFix() {
+ DCHECK(message_loop_->BelongsToCurrentThread());
+
+ // Stop and close all active streams. Once all streams across all dispatchers
+ // have been closed the AudioManager will call RestartStreamsForWedgeFix().
+ for (CallbackMap::iterator it = callbacks_.begin(); it != callbacks_.end();
+ ++it) {
+ if (it->second->started())
+ dispatcher_->StopStream(it->first);
+ dispatcher_->CloseStream(it->first);
+ }
+
+ // Close all idle streams as well.
+ dispatcher_->CloseStreamsForWedgeFix();
+}
+
+void AudioOutputResampler::RestartStreamsForWedgeFix() {
+ DCHECK(message_loop_->BelongsToCurrentThread());
+ // By opening all streams first and then starting them one by one we ensure
+ // the dispatcher only opens streams for those which will actually be used.
+ for (CallbackMap::iterator it = callbacks_.begin(); it != callbacks_.end();
+ ++it) {
+ dispatcher_->OpenStream();
+ }
+ for (CallbackMap::iterator it = callbacks_.begin(); it != callbacks_.end();
+ ++it) {
+ if (it->second->started())
+ dispatcher_->StartStream(it->second, it->first);
+ }
+}
+
OnMoreDataConverter::OnMoreDataConverter(const AudioParameters& input_params,
const AudioParameters& output_params)
- : source_callback_(NULL),
- source_bus_(NULL),
+ : io_ratio_(static_cast<double>(input_params.GetBytesPerSecond()) /
+ output_params.GetBytesPerSecond()),
+ source_callback_(NULL),
input_bytes_per_second_(input_params.GetBytesPerSecond()),
- audio_converter_(input_params, output_params, false) {
- io_ratio_ =
- static_cast<double>(input_params.GetBytesPerSecond()) /
- output_params.GetBytesPerSecond();
-}
+ audio_converter_(input_params, output_params, false) {}
OnMoreDataConverter::~OnMoreDataConverter() {
// Ensure Stop() has been called so we don't end up with an AudioOutputStream
@@ -317,7 +340,6 @@ OnMoreDataConverter::~OnMoreDataConverter() {
void OnMoreDataConverter::Start(
AudioOutputStream::AudioSourceCallback* callback) {
- base::AutoLock auto_lock(source_lock_);
CHECK(!source_callback_);
source_callback_ = callback;
@@ -328,7 +350,6 @@ void OnMoreDataConverter::Start(
}
void OnMoreDataConverter::Stop() {
- base::AutoLock auto_lock(source_lock_);
CHECK(source_callback_);
source_callback_ = NULL;
audio_converter_.RemoveInput(this);
@@ -342,26 +363,20 @@ int OnMoreDataConverter::OnMoreData(AudioBus* dest,
int OnMoreDataConverter::OnMoreIOData(AudioBus* source,
AudioBus* dest,
AudioBuffersState buffers_state) {
- base::AutoLock auto_lock(source_lock_);
- // While we waited for |source_lock_| the callback might have been cleared.
- if (!source_callback_) {
- dest->Zero();
- return dest->frames();
- }
+ // Note: The input portion of OnMoreIOData() is not supported when a converter
+ // has been injected. Downstream clients prefer silence to potentially split
+ // apart input data.
- source_bus_ = source;
current_buffers_state_ = buffers_state;
audio_converter_.Convert(dest);
- // Always return the full number of frames requested, ProvideInput_Locked()
+ // Always return the full number of frames requested, ProvideInput()
// will pad with silence if it wasn't able to acquire enough data.
return dest->frames();
}
double OnMoreDataConverter::ProvideInput(AudioBus* dest,
base::TimeDelta buffer_delay) {
- source_lock_.AssertAcquired();
-
// Adjust playback delay to include |buffer_delay|.
// TODO(dalecurtis): Stop passing bytes around, it doesn't make sense since
// AudioBus is just float data. Use TimeDelta instead.
@@ -371,27 +386,18 @@ double OnMoreDataConverter::ProvideInput(AudioBus* dest,
buffer_delay.InSecondsF() * input_bytes_per_second_);
// Retrieve data from the original callback.
- int frames = source_callback_->OnMoreIOData(
- source_bus_, dest, new_buffers_state);
-
- // |source_bus_| should only be provided once.
- // TODO(dalecurtis, crogers): This is not a complete fix. If ProvideInput()
- // is called multiple times, we need to do something more clever here.
- source_bus_ = NULL;
+ const int frames = source_callback_->OnMoreIOData(
+ NULL, dest, new_buffers_state);
// Zero any unfilled frames if anything was filled, otherwise we'll just
// return a volume of zero and let AudioConverter drop the output.
if (frames > 0 && frames < dest->frames())
dest->ZeroFramesPartial(frames, dest->frames() - frames);
-
- // TODO(dalecurtis): Return the correct volume here.
return frames > 0 ? 1 : 0;
}
void OnMoreDataConverter::OnError(AudioOutputStream* stream) {
- base::AutoLock auto_lock(source_lock_);
- if (source_callback_)
- source_callback_->OnError(stream);
+ source_callback_->OnError(stream);
}
} // namespace media
diff --git a/chromium/media/audio/audio_output_resampler.h b/chromium/media/audio/audio_output_resampler.h
index f9a75ac38f5..a8fca232470 100644
--- a/chromium/media/audio/audio_output_resampler.h
+++ b/chromium/media/audio/audio_output_resampler.h
@@ -53,11 +53,17 @@ class MEDIA_EXPORT AudioOutputResampler : public AudioOutputDispatcher {
double volume) OVERRIDE;
virtual void CloseStream(AudioOutputProxy* stream_proxy) OVERRIDE;
virtual void Shutdown() OVERRIDE;
+ virtual void CloseStreamsForWedgeFix() OVERRIDE;
+ virtual void RestartStreamsForWedgeFix() OVERRIDE;
private:
friend class base::RefCountedThreadSafe<AudioOutputResampler>;
virtual ~AudioOutputResampler();
+ // Converts low latency based output parameters into high latency
+ // appropriate output parameters in error situations.
+ void SetupFallbackParams();
+
// Used to initialize and reinitialize |dispatcher_|.
void Initialize();
diff --git a/chromium/media/audio/audio_parameters.cc b/chromium/media/audio/audio_parameters.cc
index 5e77c60cb94..fff815610fe 100644
--- a/chromium/media/audio/audio_parameters.cc
+++ b/chromium/media/audio/audio_parameters.cc
@@ -16,7 +16,8 @@ AudioParameters::AudioParameters()
bits_per_sample_(0),
frames_per_buffer_(0),
channels_(0),
- input_channels_(0) {
+ input_channels_(0),
+ effects_(NO_EFFECTS) {
}
AudioParameters::AudioParameters(Format format, ChannelLayout channel_layout,
@@ -28,20 +29,38 @@ AudioParameters::AudioParameters(Format format, ChannelLayout channel_layout,
bits_per_sample_(bits_per_sample),
frames_per_buffer_(frames_per_buffer),
channels_(ChannelLayoutToChannelCount(channel_layout)),
- input_channels_(0) {
+ input_channels_(0),
+ effects_(NO_EFFECTS) {
}
AudioParameters::AudioParameters(Format format, ChannelLayout channel_layout,
int input_channels,
int sample_rate, int bits_per_sample,
- int frames_per_buffer)
+ int frames_per_buffer, int effects)
: format_(format),
channel_layout_(channel_layout),
sample_rate_(sample_rate),
bits_per_sample_(bits_per_sample),
frames_per_buffer_(frames_per_buffer),
channels_(ChannelLayoutToChannelCount(channel_layout)),
- input_channels_(input_channels) {
+ input_channels_(input_channels),
+ effects_(effects) {
+}
+
+AudioParameters::AudioParameters(Format format, ChannelLayout channel_layout,
+ int channels, int input_channels,
+ int sample_rate, int bits_per_sample,
+ int frames_per_buffer, int effects)
+ : format_(format),
+ channel_layout_(channel_layout),
+ sample_rate_(sample_rate),
+ bits_per_sample_(bits_per_sample),
+ frames_per_buffer_(frames_per_buffer),
+ channels_(channels),
+ input_channels_(input_channels),
+ effects_(effects) {
+ if (channel_layout != CHANNEL_LAYOUT_DISCRETE)
+ DCHECK_EQ(channels, ChannelLayoutToChannelCount(channel_layout));
}
void AudioParameters::Reset(Format format, ChannelLayout channel_layout,
@@ -89,9 +108,10 @@ int AudioParameters::GetBytesPerFrame() const {
return channels_ * bits_per_sample_ / 8;
}
-void AudioParameters::SetDiscreteChannels(int channels) {
- channel_layout_ = CHANNEL_LAYOUT_DISCRETE;
- channels_ = channels;
+base::TimeDelta AudioParameters::GetBufferDuration() const {
+ return base::TimeDelta::FromMicroseconds(
+ frames_per_buffer_ * base::Time::kMicrosecondsPerSecond /
+ static_cast<float>(sample_rate_));
}
} // namespace media
diff --git a/chromium/media/audio/audio_parameters.h b/chromium/media/audio/audio_parameters.h
index bc629a7db00..62ff4fd48f1 100644
--- a/chromium/media/audio/audio_parameters.h
+++ b/chromium/media/audio/audio_parameters.h
@@ -6,6 +6,7 @@
#define MEDIA_AUDIO_AUDIO_PARAMETERS_H_
#include "base/basictypes.h"
+#include "base/time/time.h"
#include "media/base/channel_layout.h"
#include "media/base/media_export.h"
@@ -43,6 +44,13 @@ class MEDIA_EXPORT AudioParameters {
kAudioCDSampleRate = 44100,
};
+ // Bitmasks to determine whether certain platform (typically hardware) audio
+ // effects should be enabled.
+ enum PlatformEffectsMask {
+ NO_EFFECTS = 0x0,
+ ECHO_CANCELLER = 0x1
+ };
+
AudioParameters();
AudioParameters(Format format, ChannelLayout channel_layout,
int sample_rate, int bits_per_sample,
@@ -50,7 +58,12 @@ class MEDIA_EXPORT AudioParameters {
AudioParameters(Format format, ChannelLayout channel_layout,
int input_channels,
int sample_rate, int bits_per_sample,
- int frames_per_buffer);
+ int frames_per_buffer, int effects);
+ AudioParameters(Format format, ChannelLayout channel_layout,
+ int channels, int input_channels,
+ int sample_rate, int bits_per_sample,
+ int frames_per_buffer, int effects);
+
void Reset(Format format, ChannelLayout channel_layout,
int channels, int input_channels,
int sample_rate, int bits_per_sample,
@@ -69,6 +82,10 @@ class MEDIA_EXPORT AudioParameters {
// Returns the number of bytes representing a frame of audio.
int GetBytesPerFrame() const;
+ // Returns the duration of this buffer as calculated from frames_per_buffer()
+ // and sample_rate().
+ base::TimeDelta GetBufferDuration() const;
+
Format format() const { return format_; }
ChannelLayout channel_layout() const { return channel_layout_; }
int sample_rate() const { return sample_rate_; }
@@ -76,9 +93,7 @@ class MEDIA_EXPORT AudioParameters {
int frames_per_buffer() const { return frames_per_buffer_; }
int channels() const { return channels_; }
int input_channels() const { return input_channels_; }
-
- // Set to CHANNEL_LAYOUT_DISCRETE with given number of channels.
- void SetDiscreteChannels(int channels);
+ int effects() const { return effects_; }
// Comparison with other AudioParams.
bool operator==(const AudioParameters& other) const {
@@ -88,10 +103,13 @@ class MEDIA_EXPORT AudioParameters {
channels_ == other.channels() &&
input_channels_ == other.input_channels() &&
bits_per_sample_ == other.bits_per_sample() &&
- frames_per_buffer_ == other.frames_per_buffer();
+ frames_per_buffer_ == other.frames_per_buffer() &&
+ effects_ == other.effects();
}
private:
+ // These members are mutable to support entire struct assignment. They should
+ // not be mutated individually.
Format format_; // Format of the stream.
ChannelLayout channel_layout_; // Order of surround sound channels.
int sample_rate_; // Sampling frequency/rate.
@@ -103,6 +121,7 @@ class MEDIA_EXPORT AudioParameters {
int input_channels_; // Optional number of input channels.
// Normally 0, but can be set to specify
// synchronized I/O.
+ int effects_; // Bitmask using PlatformEffectsMask.
};
// Comparison is useful when AudioParameters is used with std structures.
diff --git a/chromium/media/audio/audio_power_monitor.cc b/chromium/media/audio/audio_power_monitor.cc
index d8b9436060e..6536f464b9c 100644
--- a/chromium/media/audio/audio_power_monitor.cc
+++ b/chromium/media/audio/audio_power_monitor.cc
@@ -11,6 +11,7 @@
#include "base/logging.h"
#include "base/time/time.h"
#include "media/base/audio_bus.h"
+#include "media/base/vector_math.h"
namespace media {
@@ -36,30 +37,19 @@ void AudioPowerMonitor::Scan(const AudioBus& buffer, int num_frames) {
return;
// Calculate a new average power by applying a first-order low-pass filter
- // over the audio samples in |buffer|.
- //
- // TODO(miu): Implement optimized SSE/NEON to more efficiently compute the
- // results (in media/base/vector_math) in soon-upcoming change.
+ // (a.k.a. an exponentially-weighted moving average) over the audio samples in
+ // each channel in |buffer|.
float sum_power = 0.0f;
for (int i = 0; i < num_channels; ++i) {
- float average_power_this_channel = average_power_;
- bool clipped = false;
- const float* p = buffer.channel(i);
- const float* const end_of_samples = p + num_frames;
- for (; p < end_of_samples; ++p) {
- const float sample = *p;
- const float sample_squared = sample * sample;
- clipped |= (sample_squared > 1.0f);
- average_power_this_channel +=
- (sample_squared - average_power_this_channel) * sample_weight_;
- }
+ const std::pair<float, float> ewma_and_max = vector_math::EWMAAndMaxPower(
+ average_power_, buffer.channel(i), num_frames, sample_weight_);
// If data in audio buffer is garbage, ignore its effect on the result.
- if (base::IsNaN(average_power_this_channel)) {
- average_power_this_channel = average_power_;
- clipped = false;
+ if (!base::IsFinite(ewma_and_max.first)) {
+ sum_power += average_power_;
+ } else {
+ sum_power += ewma_and_max.first;
+ has_clipped_ |= (ewma_and_max.second > 1.0f);
}
- sum_power += average_power_this_channel;
- has_clipped_ |= clipped;
}
// Update accumulated results, with clamping for sanity.
diff --git a/chromium/media/audio/audio_util.cc b/chromium/media/audio/audio_util.cc
deleted file mode 100644
index 42c6c9109fd..00000000000
--- a/chromium/media/audio/audio_util.cc
+++ /dev/null
@@ -1,99 +0,0 @@
-// 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.
-
-// Software adjust volume of samples, allows each audio stream its own
-// volume without impacting master volume for chrome and other applications.
-
-// Implemented as templates to allow 8, 16 and 32 bit implementations.
-// 8 bit is unsigned and biased by 128.
-
-// TODO(vrk): This file has been running pretty wild and free, and it's likely
-// that a lot of the functions can be simplified and made more elegant. Revisit
-// after other audio cleanup is done. (crbug.com/120319)
-
-#include "media/audio/audio_util.h"
-
-#include "base/command_line.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/time/time.h"
-#include "media/base/media_switches.h"
-
-#if defined(OS_WIN)
-#include "base/win/windows_version.h"
-#endif
-
-namespace media {
-
-// Returns user buffer size as specified on the command line or 0 if no buffer
-// size has been specified.
-int GetUserBufferSize() {
- const CommandLine* cmd_line = CommandLine::ForCurrentProcess();
- int buffer_size = 0;
- std::string buffer_size_str(cmd_line->GetSwitchValueASCII(
- switches::kAudioBufferSize));
- if (base::StringToInt(buffer_size_str, &buffer_size) && buffer_size > 0)
- return buffer_size;
-
- return 0;
-}
-
-// Computes a buffer size based on the given |sample_rate|. Must be used in
-// conjunction with AUDIO_PCM_LINEAR.
-size_t GetHighLatencyOutputBufferSize(int sample_rate) {
- int user_buffer_size = GetUserBufferSize();
- if (user_buffer_size)
- return user_buffer_size;
-
- // TODO(vrk/crogers): The buffer sizes that this function computes is probably
- // overly conservative. However, reducing the buffer size to 2048-8192 bytes
- // caused crbug.com/108396. This computation should be revisited while making
- // sure crbug.com/108396 doesn't happen again.
-
- // The minimum number of samples in a hardware packet.
- // This value is selected so that we can handle down to 5khz sample rate.
- static const size_t kMinSamplesPerHardwarePacket = 1024;
-
- // The maximum number of samples in a hardware packet.
- // This value is selected so that we can handle up to 192khz sample rate.
- static const size_t kMaxSamplesPerHardwarePacket = 64 * 1024;
-
- // This constant governs the hardware audio buffer size, this value should be
- // chosen carefully.
- // This value is selected so that we have 8192 samples for 48khz streams.
- static const size_t kMillisecondsPerHardwarePacket = 170;
-
- // Select the number of samples that can provide at least
- // |kMillisecondsPerHardwarePacket| worth of audio data.
- size_t samples = kMinSamplesPerHardwarePacket;
- while (samples <= kMaxSamplesPerHardwarePacket &&
- samples * base::Time::kMillisecondsPerSecond <
- sample_rate * kMillisecondsPerHardwarePacket) {
- samples *= 2;
- }
- return samples;
-}
-
-#if defined(OS_WIN)
-
-int NumberOfWaveOutBuffers() {
- // Use the user provided buffer count if provided.
- int buffers = 0;
- std::string buffers_str(CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
- switches::kWaveOutBuffers));
- if (base::StringToInt(buffers_str, &buffers) && buffers > 0) {
- return buffers;
- }
-
- // Use 4 buffers for Vista, 3 for everyone else:
- // - The entire Windows audio stack was rewritten for Windows Vista and wave
- // out performance was degraded compared to XP.
- // - The regression was fixed in Windows 7 and most configurations will work
- // with 2, but some (e.g., some Sound Blasters) still need 3.
- // - Some XP configurations (even multi-processor ones) also need 3.
- return (base::win::GetVersion() == base::win::VERSION_VISTA) ? 4 : 3;
-}
-
-#endif
-
-} // namespace media
diff --git a/chromium/media/audio/audio_util.h b/chromium/media/audio/audio_util.h
deleted file mode 100644
index a11c327aa47..00000000000
--- a/chromium/media/audio/audio_util.h
+++ /dev/null
@@ -1,31 +0,0 @@
-// 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.
-
-#ifndef MEDIA_AUDIO_AUDIO_UTIL_H_
-#define MEDIA_AUDIO_AUDIO_UTIL_H_
-
-#include "base/basictypes.h"
-#include "build/build_config.h"
-#include "media/base/media_export.h"
-
-namespace media {
-
-// Returns user buffer size as specified on the command line or 0 if no buffer
-// size has been specified.
-MEDIA_EXPORT int GetUserBufferSize();
-
-// Computes a buffer size based on the given |sample_rate|. Must be used in
-// conjunction with AUDIO_PCM_LINEAR.
-MEDIA_EXPORT size_t GetHighLatencyOutputBufferSize(int sample_rate);
-
-#if defined(OS_WIN)
-
-// Returns number of buffers to be used by wave out.
-MEDIA_EXPORT int NumberOfWaveOutBuffers();
-
-#endif // defined(OS_WIN)
-
-} // namespace media
-
-#endif // MEDIA_AUDIO_AUDIO_UTIL_H_
diff --git a/chromium/media/audio/cras/audio_manager_cras.cc b/chromium/media/audio/cras/audio_manager_cras.cc
index 14a0c4e86ac..876d6ce8136 100644
--- a/chromium/media/audio/cras/audio_manager_cras.cc
+++ b/chromium/media/audio/cras/audio_manager_cras.cc
@@ -9,7 +9,6 @@
#include "base/logging.h"
#include "base/nix/xdg_util.h"
#include "base/stl_util.h"
-#include "media/audio/audio_util.h"
#include "media/audio/cras/cras_input.h"
#include "media/audio/cras/cras_unified.h"
#include "media/base/channel_layout.h"
@@ -39,7 +38,8 @@ bool AudioManagerCras::HasAudioInputDevices() {
return true;
}
-AudioManagerCras::AudioManagerCras() {
+AudioManagerCras::AudioManagerCras(AudioLogFactory* audio_log_factory)
+ : AudioManagerBase(audio_log_factory) {
SetMaxOutputStreamsAllowed(kMaxOutputStreams);
}
@@ -125,7 +125,7 @@ AudioParameters AudioManagerCras::GetPreferredOutputStreamParameters(
return AudioParameters(
AudioParameters::AUDIO_PCM_LOW_LATENCY, channel_layout, input_channels,
- sample_rate, bits_per_sample, buffer_size);
+ sample_rate, bits_per_sample, buffer_size, AudioParameters::NO_EFFECTS);
}
AudioOutputStream* AudioManagerCras::MakeOutputStream(
diff --git a/chromium/media/audio/cras/audio_manager_cras.h b/chromium/media/audio/cras/audio_manager_cras.h
index 3b0ef530e07..589374ae0b9 100644
--- a/chromium/media/audio/cras/audio_manager_cras.h
+++ b/chromium/media/audio/cras/audio_manager_cras.h
@@ -15,7 +15,7 @@ namespace media {
class MEDIA_EXPORT AudioManagerCras : public AudioManagerBase {
public:
- AudioManagerCras();
+ AudioManagerCras(AudioLogFactory* audio_log_factory);
// AudioManager implementation.
virtual bool HasAudioOutputDevices() OVERRIDE;
diff --git a/chromium/media/audio/cras/cras_input.cc b/chromium/media/audio/cras/cras_input.cc
index fd574dc86e5..c41f3645efd 100644
--- a/chromium/media/audio/cras/cras_input.cc
+++ b/chromium/media/audio/cras/cras_input.cc
@@ -10,9 +10,9 @@
#include "base/bind.h"
#include "base/logging.h"
#include "base/time/time.h"
+#include "media/audio/alsa/alsa_util.h"
#include "media/audio/audio_manager.h"
#include "media/audio/cras/audio_manager_cras.h"
-#include "media/audio/linux/alsa_util.h"
namespace media {
diff --git a/chromium/media/audio/cras/cras_unified.cc b/chromium/media/audio/cras/cras_unified.cc
index a7741864b31..c85cf59dd5f 100644
--- a/chromium/media/audio/cras/cras_unified.cc
+++ b/chromium/media/audio/cras/cras_unified.cc
@@ -8,9 +8,8 @@
#include "base/command_line.h"
#include "base/logging.h"
-#include "media/audio/audio_util.h"
+#include "media/audio/alsa/alsa_util.h"
#include "media/audio/cras/audio_manager_cras.h"
-#include "media/audio/linux/alsa_util.h"
namespace media {
@@ -162,6 +161,23 @@ void CrasUnifiedStream::Close() {
void CrasUnifiedStream::Start(AudioSourceCallback* callback) {
CHECK(callback);
+
+ // Channel map to CRAS_CHANNEL, values in the same order of
+ // corresponding source in Chromium defined Channels.
+ static const int kChannelMap[] = {
+ CRAS_CH_FL,
+ CRAS_CH_FR,
+ CRAS_CH_FC,
+ CRAS_CH_LFE,
+ CRAS_CH_RL,
+ CRAS_CH_RR,
+ CRAS_CH_FLC,
+ CRAS_CH_FRC,
+ CRAS_CH_RC,
+ CRAS_CH_SL,
+ CRAS_CH_SR
+ };
+
source_callback_ = callback;
// Only start if we can enter the playing state.
@@ -180,6 +196,22 @@ void CrasUnifiedStream::Start(AudioSourceCallback* callback) {
return;
}
+ // Initialize channel layout to all -1 to indicate that none of
+ // the channels is set in the layout.
+ int8 layout[CRAS_CH_MAX] = {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 };
+
+ // Converts to CRAS defined channels. ChannelOrder will return -1
+ // for channels that does not present in params_.channel_layout().
+ for (size_t i = 0; i < arraysize(kChannelMap); ++i)
+ layout[kChannelMap[i]] = ChannelOrder(params_.channel_layout(),
+ static_cast<Channels>(i));
+
+ if (cras_audio_format_set_channel_layout(audio_format, layout)) {
+ LOG(WARNING) << "Error setting channel layout.";
+ callback->OnError(this);
+ return;
+ }
+
cras_stream_params* stream_params = cras_client_unified_params_create(
stream_direction_,
params_.frames_per_buffer(),
diff --git a/chromium/media/audio/fake_audio_log_factory.cc b/chromium/media/audio/fake_audio_log_factory.cc
new file mode 100644
index 00000000000..6f752e559fd
--- /dev/null
+++ b/chromium/media/audio/fake_audio_log_factory.cc
@@ -0,0 +1,32 @@
+// Copyright 2013 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/fake_audio_log_factory.h"
+
+namespace media {
+
+class FakeAudioLogImpl : public AudioLog {
+ public:
+ FakeAudioLogImpl() {}
+ virtual ~FakeAudioLogImpl() {}
+ virtual void OnCreated(int component_id,
+ const media::AudioParameters& params,
+ const std::string& input_device_id,
+ const std::string& output_device_id) OVERRIDE {}
+ virtual void OnStarted(int component_id) OVERRIDE {}
+ virtual void OnStopped(int component_id) OVERRIDE {}
+ virtual void OnClosed(int component_id) OVERRIDE {}
+ virtual void OnError(int component_id) OVERRIDE {}
+ virtual void OnSetVolume(int component_id, double volume) OVERRIDE {}
+};
+
+FakeAudioLogFactory::FakeAudioLogFactory() {}
+FakeAudioLogFactory::~FakeAudioLogFactory() {}
+
+scoped_ptr<AudioLog> FakeAudioLogFactory::CreateAudioLog(
+ AudioComponent component) {
+ return scoped_ptr<AudioLog>(new FakeAudioLogImpl());
+}
+
+} // namespace media
diff --git a/chromium/media/audio/fake_audio_log_factory.h b/chromium/media/audio/fake_audio_log_factory.h
new file mode 100644
index 00000000000..30e39e63ff0
--- /dev/null
+++ b/chromium/media/audio/fake_audio_log_factory.h
@@ -0,0 +1,29 @@
+// Copyright 2013 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_FAKE_AUDIO_LOG_FACTORY_H_
+#define MEDIA_AUDIO_FAKE_AUDIO_LOG_FACTORY_H_
+
+#include "base/compiler_specific.h"
+#include "media/audio/audio_logging.h"
+#include "media/base/media_export.h"
+
+namespace media {
+
+// Creates stub AudioLog instances, for testing, which do nothing.
+class MEDIA_EXPORT FakeAudioLogFactory
+ : NON_EXPORTED_BASE(public AudioLogFactory) {
+ public:
+ FakeAudioLogFactory();
+ virtual ~FakeAudioLogFactory();
+ virtual scoped_ptr<AudioLog> CreateAudioLog(
+ AudioComponent component) OVERRIDE;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(FakeAudioLogFactory);
+};
+
+} // namespace media
+
+#endif // MEDIA_AUDIO_FAKE_AUDIO_LOG_FACTORY_H_
diff --git a/chromium/media/audio/fake_audio_manager.cc b/chromium/media/audio/fake_audio_manager.cc
new file mode 100644
index 00000000000..bfe9a0a7ff3
--- /dev/null
+++ b/chromium/media/audio/fake_audio_manager.cc
@@ -0,0 +1,83 @@
+// Copyright 2013 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/fake_audio_manager.h"
+
+namespace media {
+
+namespace {
+
+const int kDefaultInputBufferSize = 1024;
+const int kDefaultSampleRate = 48000;
+
+} // namespace
+
+FakeAudioManager::FakeAudioManager(AudioLogFactory* audio_log_factory)
+ : AudioManagerBase(audio_log_factory) {}
+
+FakeAudioManager::~FakeAudioManager() {
+ Shutdown();
+}
+
+// Implementation of AudioManager.
+bool FakeAudioManager::HasAudioOutputDevices() { return false; }
+
+bool FakeAudioManager::HasAudioInputDevices() { return false; }
+
+// Implementation of AudioManagerBase.
+AudioOutputStream* FakeAudioManager::MakeLinearOutputStream(
+ const AudioParameters& params) {
+ return FakeAudioOutputStream::MakeFakeStream(this, params);
+}
+
+AudioOutputStream* FakeAudioManager::MakeLowLatencyOutputStream(
+ const AudioParameters& params,
+ const std::string& device_id,
+ const std::string& input_device_id) {
+ return FakeAudioOutputStream::MakeFakeStream(this, params);
+}
+
+AudioInputStream* FakeAudioManager::MakeLinearInputStream(
+ const AudioParameters& params,
+ const std::string& device_id) {
+ return FakeAudioInputStream::MakeFakeStream(this, params);
+}
+
+AudioInputStream* FakeAudioManager::MakeLowLatencyInputStream(
+ const AudioParameters& params,
+ const std::string& device_id) {
+ return FakeAudioInputStream::MakeFakeStream(this, params);
+}
+
+AudioParameters FakeAudioManager::GetPreferredOutputStreamParameters(
+ const std::string& output_device_id,
+ const AudioParameters& input_params) {
+ static const int kDefaultOutputBufferSize = 2048;
+ static const int kDefaultSampleRate = 48000;
+ ChannelLayout channel_layout = CHANNEL_LAYOUT_STEREO;
+ int sample_rate = kDefaultSampleRate;
+ int buffer_size = kDefaultOutputBufferSize;
+ int bits_per_sample = 16;
+ int input_channels = 0;
+ if (input_params.IsValid()) {
+ sample_rate = input_params.sample_rate();
+ bits_per_sample = input_params.bits_per_sample();
+ channel_layout = input_params.channel_layout();
+ input_channels = input_params.input_channels();
+ buffer_size = std::min(input_params.frames_per_buffer(), buffer_size);
+ }
+
+ return AudioParameters(
+ AudioParameters::AUDIO_PCM_LOW_LATENCY, channel_layout, input_channels,
+ sample_rate, bits_per_sample, buffer_size, AudioParameters::NO_EFFECTS);
+}
+
+AudioParameters FakeAudioManager::GetInputStreamParameters(
+ const std::string& device_id) {
+ return AudioParameters(
+ AudioParameters::AUDIO_PCM_LOW_LATENCY, CHANNEL_LAYOUT_STEREO,
+ kDefaultSampleRate, 16, kDefaultInputBufferSize);
+}
+
+} // namespace media
diff --git a/chromium/media/audio/fake_audio_manager.h b/chromium/media/audio/fake_audio_manager.h
new file mode 100644
index 00000000000..b5c45201ed1
--- /dev/null
+++ b/chromium/media/audio/fake_audio_manager.h
@@ -0,0 +1,53 @@
+// Copyright 2013 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_FAKE_AUDIO_MANAGER_H_
+#define MEDIA_AUDIO_FAKE_AUDIO_MANAGER_H_
+
+#include <string>
+#include "base/compiler_specific.h"
+#include "media/audio/audio_manager_base.h"
+#include "media/audio/fake_audio_input_stream.h"
+#include "media/audio/fake_audio_output_stream.h"
+
+namespace media {
+
+class MEDIA_EXPORT FakeAudioManager : public AudioManagerBase {
+ public:
+ FakeAudioManager(AudioLogFactory* audio_log_factory);
+
+ // Implementation of AudioManager.
+ virtual bool HasAudioOutputDevices() OVERRIDE;
+ virtual bool HasAudioInputDevices() OVERRIDE;
+
+ // Implementation of AudioManagerBase.
+ virtual AudioOutputStream* MakeLinearOutputStream(
+ const AudioParameters& params) OVERRIDE;
+ virtual AudioOutputStream* MakeLowLatencyOutputStream(
+ const AudioParameters& params,
+ const std::string& device_id,
+ const std::string& input_device_id) OVERRIDE;
+ virtual AudioInputStream* MakeLinearInputStream(const AudioParameters& params,
+ const std::string& device_id)
+ OVERRIDE;
+ virtual AudioInputStream* MakeLowLatencyInputStream(
+ const AudioParameters& params,
+ const std::string& device_id) OVERRIDE;
+ virtual AudioParameters GetInputStreamParameters(
+ const std::string& device_id) OVERRIDE;
+
+ protected:
+ virtual ~FakeAudioManager();
+
+ virtual AudioParameters GetPreferredOutputStreamParameters(
+ const std::string& output_device_id,
+ const AudioParameters& input_params) OVERRIDE;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(FakeAudioManager);
+};
+
+} // namespace media
+
+#endif // MEDIA_AUDIO_FAKE_AUDIO_MANAGER_H_
diff --git a/chromium/media/audio/fake_audio_output_stream.cc b/chromium/media/audio/fake_audio_output_stream.cc
index b21a054f13b..fb460ab6805 100644
--- a/chromium/media/audio/fake_audio_output_stream.cc
+++ b/chromium/media/audio/fake_audio_output_stream.cc
@@ -22,7 +22,7 @@ FakeAudioOutputStream::FakeAudioOutputStream(AudioManagerBase* manager,
const AudioParameters& params)
: audio_manager_(manager),
callback_(NULL),
- fake_consumer_(manager->GetMessageLoop(), params) {
+ fake_consumer_(manager->GetWorkerLoop(), params) {
}
FakeAudioOutputStream::~FakeAudioOutputStream() {
@@ -60,7 +60,7 @@ void FakeAudioOutputStream::GetVolume(double* volume) {
};
void FakeAudioOutputStream::CallOnMoreData(AudioBus* audio_bus) {
- DCHECK(audio_manager_->GetMessageLoop()->BelongsToCurrentThread());
+ DCHECK(audio_manager_->GetWorkerLoop()->BelongsToCurrentThread());
callback_->OnMoreData(audio_bus, AudioBuffersState());
}
diff --git a/chromium/media/audio/linux/audio_manager_linux.cc b/chromium/media/audio/linux/audio_manager_linux.cc
index 708e4f26840..eaeb2f332b9 100644
--- a/chromium/media/audio/linux/audio_manager_linux.cc
+++ b/chromium/media/audio/linux/audio_manager_linux.cc
@@ -2,52 +2,23 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "media/audio/linux/audio_manager_linux.h"
-
#include "base/command_line.h"
-#include "base/environment.h"
-#include "base/files/file_path.h"
-#include "base/logging.h"
#include "base/metrics/histogram.h"
-#include "base/nix/xdg_util.h"
-#include "base/process/launch.h"
-#include "base/stl_util.h"
-#include "media/audio/audio_output_dispatcher.h"
-#include "media/audio/audio_parameters.h"
-#include "media/audio/audio_util.h"
+#if defined(USE_ALSA)
+#include "media/audio/alsa/audio_manager_alsa.h"
+#else
+#include "media/audio/fake_audio_manager.h"
+#endif
#if defined(USE_CRAS)
#include "media/audio/cras/audio_manager_cras.h"
#endif
-#include "media/audio/linux/alsa_input.h"
-#include "media/audio/linux/alsa_output.h"
-#include "media/audio/linux/alsa_wrapper.h"
#if defined(USE_PULSEAUDIO)
#include "media/audio/pulse/audio_manager_pulse.h"
#endif
-#include "media/base/channel_layout.h"
-#include "media/base/limits.h"
#include "media/base/media_switches.h"
namespace media {
-// Maximum number of output streams that can be open simultaneously.
-static const int kMaxOutputStreams = 50;
-
-// Default sample rate for input and output streams.
-static const int kDefaultSampleRate = 48000;
-
-// Since "default", "pulse" and "dmix" devices are virtual devices mapped to
-// real devices, we remove them from the list to avoiding duplicate counting.
-// In addition, note that we support no more than 2 channels for recording,
-// hence surround devices are not stored in the list.
-static const char* kInvalidAudioInputDevices[] = {
- "default",
- "dmix",
- "null",
- "pulse",
- "surround",
-};
-
enum LinuxAudioIO {
kPulse,
kAlsa,
@@ -55,335 +26,28 @@ enum LinuxAudioIO {
kAudioIOMax // Must always be last!
};
-// static
-void AudioManagerLinux::ShowLinuxAudioInputSettings() {
- scoped_ptr<base::Environment> env(base::Environment::Create());
- CommandLine command_line(CommandLine::NO_PROGRAM);
- switch (base::nix::GetDesktopEnvironment(env.get())) {
- case base::nix::DESKTOP_ENVIRONMENT_GNOME:
- command_line.SetProgram(base::FilePath("gnome-volume-control"));
- break;
- case base::nix::DESKTOP_ENVIRONMENT_KDE3:
- case base::nix::DESKTOP_ENVIRONMENT_KDE4:
- command_line.SetProgram(base::FilePath("kmix"));
- break;
- case base::nix::DESKTOP_ENVIRONMENT_UNITY:
- command_line.SetProgram(base::FilePath("gnome-control-center"));
- command_line.AppendArg("sound");
- command_line.AppendArg("input");
- break;
- default:
- LOG(ERROR) << "Failed to show audio input settings: we don't know "
- << "what command to use for your desktop environment.";
- return;
- }
- base::LaunchProcess(command_line, base::LaunchOptions(), NULL);
-}
-
-// Implementation of AudioManager.
-bool AudioManagerLinux::HasAudioOutputDevices() {
- return HasAnyAlsaAudioDevice(kStreamPlayback);
-}
-
-bool AudioManagerLinux::HasAudioInputDevices() {
- return HasAnyAlsaAudioDevice(kStreamCapture);
-}
-
-AudioManagerLinux::AudioManagerLinux()
- : wrapper_(new AlsaWrapper()) {
- SetMaxOutputStreamsAllowed(kMaxOutputStreams);
-}
-
-AudioManagerLinux::~AudioManagerLinux() {
- Shutdown();
-}
-
-void AudioManagerLinux::ShowAudioInputSettings() {
- ShowLinuxAudioInputSettings();
-}
-
-void AudioManagerLinux::GetAudioInputDeviceNames(
- AudioDeviceNames* device_names) {
- DCHECK(device_names->empty());
- GetAlsaAudioDevices(kStreamCapture, device_names);
-}
-
-void AudioManagerLinux::GetAudioOutputDeviceNames(
- AudioDeviceNames* device_names) {
- DCHECK(device_names->empty());
- GetAlsaAudioDevices(kStreamPlayback, device_names);
-}
-
-AudioParameters AudioManagerLinux::GetInputStreamParameters(
- const std::string& device_id) {
- static const int kDefaultInputBufferSize = 1024;
-
- return AudioParameters(
- AudioParameters::AUDIO_PCM_LOW_LATENCY, CHANNEL_LAYOUT_STEREO,
- kDefaultSampleRate, 16, kDefaultInputBufferSize);
-}
-
-void AudioManagerLinux::GetAlsaAudioDevices(
- StreamType type,
- media::AudioDeviceNames* device_names) {
- // Constants specified by the ALSA API for device hints.
- static const char kPcmInterfaceName[] = "pcm";
- int card = -1;
-
- // Loop through the sound cards to get ALSA device hints.
- while (!wrapper_->CardNext(&card) && card >= 0) {
- void** hints = NULL;
- int error = wrapper_->DeviceNameHint(card, kPcmInterfaceName, &hints);
- if (!error) {
- GetAlsaDevicesInfo(type, hints, device_names);
-
- // Destroy the hints now that we're done with it.
- wrapper_->DeviceNameFreeHint(hints);
- } else {
- DLOG(WARNING) << "GetAlsaAudioDevices: unable to get device hints: "
- << wrapper_->StrError(error);
- }
- }
-}
-
-void AudioManagerLinux::GetAlsaDevicesInfo(
- AudioManagerLinux::StreamType type,
- void** hints,
- media::AudioDeviceNames* device_names) {
- static const char kIoHintName[] = "IOID";
- static const char kNameHintName[] = "NAME";
- static const char kDescriptionHintName[] = "DESC";
-
- const char* unwanted_device_type = UnwantedDeviceTypeWhenEnumerating(type);
-
- for (void** hint_iter = hints; *hint_iter != NULL; hint_iter++) {
- // Only examine devices of the right type. Valid values are
- // "Input", "Output", and NULL which means both input and output.
- scoped_ptr_malloc<char> io(wrapper_->DeviceNameGetHint(*hint_iter,
- kIoHintName));
- if (io != NULL && strcmp(unwanted_device_type, io.get()) == 0)
- continue;
-
- // Found a device, prepend the default device since we always want
- // it to be on the top of the list for all platforms. And there is
- // no duplicate counting here since it is only done if the list is
- // still empty. Note, pulse has exclusively opened the default
- // device, so we must open the device via the "default" moniker.
- if (device_names->empty()) {
- device_names->push_front(media::AudioDeviceName(
- AudioManagerBase::kDefaultDeviceName,
- AudioManagerBase::kDefaultDeviceId));
- }
-
- // Get the unique device name for the device.
- scoped_ptr_malloc<char> unique_device_name(
- wrapper_->DeviceNameGetHint(*hint_iter, kNameHintName));
-
- // Find out if the device is available.
- if (IsAlsaDeviceAvailable(type, unique_device_name.get())) {
- // Get the description for the device.
- scoped_ptr_malloc<char> desc(wrapper_->DeviceNameGetHint(
- *hint_iter, kDescriptionHintName));
-
- media::AudioDeviceName name;
- name.unique_id = unique_device_name.get();
- if (desc) {
- // Use the more user friendly description as name.
- // Replace '\n' with '-'.
- char* pret = strchr(desc.get(), '\n');
- if (pret)
- *pret = '-';
- name.device_name = desc.get();
- } else {
- // Virtual devices don't necessarily have descriptions.
- // Use their names instead.
- name.device_name = unique_device_name.get();
- }
-
- // Store the device information.
- device_names->push_back(name);
- }
- }
-}
-
-// static
-bool AudioManagerLinux::IsAlsaDeviceAvailable(
- AudioManagerLinux::StreamType type,
- const char* device_name) {
- if (!device_name)
- return false;
-
- // We do prefix matches on the device name to see whether to include
- // it or not.
- if (type == kStreamCapture) {
- // Check if the device is in the list of invalid devices.
- for (size_t i = 0; i < arraysize(kInvalidAudioInputDevices); ++i) {
- if (strncmp(kInvalidAudioInputDevices[i], device_name,
- strlen(kInvalidAudioInputDevices[i])) == 0)
- return false;
- }
- return true;
- } else {
- DCHECK_EQ(kStreamPlayback, type);
- // We prefer the device type that maps straight to hardware but
- // goes through software conversion if needed (e.g. incompatible
- // sample rate).
- // TODO(joi): Should we prefer "hw" instead?
- static const char kDeviceTypeDesired[] = "plughw";
- return strncmp(kDeviceTypeDesired,
- device_name,
- arraysize(kDeviceTypeDesired) - 1) == 0;
- }
-}
-
-// static
-const char* AudioManagerLinux::UnwantedDeviceTypeWhenEnumerating(
- AudioManagerLinux::StreamType wanted_type) {
- return wanted_type == kStreamPlayback ? "Input" : "Output";
-}
-
-bool AudioManagerLinux::HasAnyAlsaAudioDevice(
- AudioManagerLinux::StreamType stream) {
- static const char kPcmInterfaceName[] = "pcm";
- static const char kIoHintName[] = "IOID";
- void** hints = NULL;
- bool has_device = false;
- int card = -1;
-
- // Loop through the sound cards.
- // Don't use snd_device_name_hint(-1,..) since there is a access violation
- // inside this ALSA API with libasound.so.2.0.0.
- while (!wrapper_->CardNext(&card) && (card >= 0) && !has_device) {
- int error = wrapper_->DeviceNameHint(card, kPcmInterfaceName, &hints);
- if (!error) {
- for (void** hint_iter = hints; *hint_iter != NULL; hint_iter++) {
- // Only examine devices that are |stream| capable. Valid values are
- // "Input", "Output", and NULL which means both input and output.
- scoped_ptr_malloc<char> io(wrapper_->DeviceNameGetHint(*hint_iter,
- kIoHintName));
- const char* unwanted_type = UnwantedDeviceTypeWhenEnumerating(stream);
- if (io != NULL && strcmp(unwanted_type, io.get()) == 0)
- continue; // Wrong type, skip the device.
-
- // Found an input device.
- has_device = true;
- break;
- }
-
- // Destroy the hints now that we're done with it.
- wrapper_->DeviceNameFreeHint(hints);
- hints = NULL;
- } else {
- DLOG(WARNING) << "HasAnyAudioDevice: unable to get device hints: "
- << wrapper_->StrError(error);
- }
- }
-
- return has_device;
-}
-
-AudioOutputStream* AudioManagerLinux::MakeLinearOutputStream(
- const AudioParameters& params) {
- DCHECK_EQ(AudioParameters::AUDIO_PCM_LINEAR, params.format());
- return MakeOutputStream(params);
-}
-
-AudioOutputStream* AudioManagerLinux::MakeLowLatencyOutputStream(
- const AudioParameters& params,
- const std::string& device_id,
- const std::string& input_device_id) {
- DLOG_IF(ERROR, !device_id.empty()) << "Not implemented!";
- DCHECK_EQ(AudioParameters::AUDIO_PCM_LOW_LATENCY, params.format());
- // TODO(xians): Use input_device_id for unified IO.
- return MakeOutputStream(params);
-}
-
-AudioInputStream* AudioManagerLinux::MakeLinearInputStream(
- const AudioParameters& params, const std::string& device_id) {
- DCHECK_EQ(AudioParameters::AUDIO_PCM_LINEAR, params.format());
- return MakeInputStream(params, device_id);
-}
-
-AudioInputStream* AudioManagerLinux::MakeLowLatencyInputStream(
- const AudioParameters& params, const std::string& device_id) {
- DCHECK_EQ(AudioParameters::AUDIO_PCM_LOW_LATENCY, params.format());
- return MakeInputStream(params, device_id);
-}
-
-AudioParameters AudioManagerLinux::GetPreferredOutputStreamParameters(
- const std::string& output_device_id,
- const AudioParameters& input_params) {
- // TODO(tommi): Support |output_device_id|.
- DLOG_IF(ERROR, !output_device_id.empty()) << "Not implemented!";
- static const int kDefaultOutputBufferSize = 2048;
- ChannelLayout channel_layout = CHANNEL_LAYOUT_STEREO;
- int sample_rate = kDefaultSampleRate;
- int buffer_size = kDefaultOutputBufferSize;
- int bits_per_sample = 16;
- int input_channels = 0;
- if (input_params.IsValid()) {
- // Some clients, such as WebRTC, have a more limited use case and work
- // acceptably with a smaller buffer size. The check below allows clients
- // which want to try a smaller buffer size on Linux to do so.
- // TODO(dalecurtis): This should include bits per channel and channel layout
- // eventually.
- sample_rate = input_params.sample_rate();
- bits_per_sample = input_params.bits_per_sample();
- channel_layout = input_params.channel_layout();
- input_channels = input_params.input_channels();
- buffer_size = std::min(input_params.frames_per_buffer(), buffer_size);
- }
-
- int user_buffer_size = GetUserBufferSize();
- if (user_buffer_size)
- buffer_size = user_buffer_size;
-
- return AudioParameters(
- AudioParameters::AUDIO_PCM_LOW_LATENCY, channel_layout, input_channels,
- sample_rate, bits_per_sample, buffer_size);
-}
-
-AudioOutputStream* AudioManagerLinux::MakeOutputStream(
- const AudioParameters& params) {
- std::string device_name = AlsaPcmOutputStream::kAutoSelectDevice;
- if (CommandLine::ForCurrentProcess()->HasSwitch(
- switches::kAlsaOutputDevice)) {
- device_name = CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
- switches::kAlsaOutputDevice);
- }
- return new AlsaPcmOutputStream(device_name, params, wrapper_.get(), this);
-}
-
-AudioInputStream* AudioManagerLinux::MakeInputStream(
- const AudioParameters& params, const std::string& device_id) {
- std::string device_name = (device_id == AudioManagerBase::kDefaultDeviceId) ?
- AlsaPcmInputStream::kAutoSelectDevice : device_id;
- if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAlsaInputDevice)) {
- device_name = CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
- switches::kAlsaInputDevice);
- }
-
- return new AlsaPcmInputStream(this, device_name, params, wrapper_.get());
-}
-
-AudioManager* CreateAudioManager() {
+AudioManager* CreateAudioManager(AudioLogFactory* audio_log_factory) {
#if defined(USE_CRAS)
if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kUseCras)) {
UMA_HISTOGRAM_ENUMERATION("Media.LinuxAudioIO", kCras, kAudioIOMax);
- return new AudioManagerCras();
+ return new AudioManagerCras(audio_log_factory);
}
#endif
#if defined(USE_PULSEAUDIO)
- AudioManager* manager = AudioManagerPulse::Create();
+ AudioManager* manager = AudioManagerPulse::Create(audio_log_factory);
if (manager) {
UMA_HISTOGRAM_ENUMERATION("Media.LinuxAudioIO", kPulse, kAudioIOMax);
return manager;
}
#endif
+#if defined(USE_ALSA)
UMA_HISTOGRAM_ENUMERATION("Media.LinuxAudioIO", kAlsa, kAudioIOMax);
- return new AudioManagerLinux();
+ return new AudioManagerAlsa(audio_log_factory);
+#else
+ return new FakeAudioManager(audio_log_factory);
+#endif
}
} // namespace media
diff --git a/chromium/media/audio/mac/audio_auhal_mac.cc b/chromium/media/audio/mac/audio_auhal_mac.cc
index 051b709c31d..9fcd46a6a95 100644
--- a/chromium/media/audio/mac/audio_auhal_mac.cc
+++ b/chromium/media/audio/mac/audio_auhal_mac.cc
@@ -7,29 +7,14 @@
#include <CoreServices/CoreServices.h>
#include "base/basictypes.h"
-#include "base/command_line.h"
#include "base/debug/trace_event.h"
#include "base/logging.h"
#include "base/mac/mac_logging.h"
-#include "media/audio/audio_util.h"
#include "media/audio/mac/audio_manager_mac.h"
-#include "media/base/media_switches.h"
+#include "media/base/audio_pull_fifo.h"
namespace media {
-static std::ostream& operator<<(std::ostream& os,
- const AudioStreamBasicDescription& format) {
- os << "sample rate : " << format.mSampleRate << std::endl
- << "format ID : " << format.mFormatID << std::endl
- << "format flags : " << format.mFormatFlags << std::endl
- << "bytes per packet : " << format.mBytesPerPacket << std::endl
- << "frames per packet : " << format.mFramesPerPacket << std::endl
- << "bytes per frame : " << format.mBytesPerFrame << std::endl
- << "channels per frame: " << format.mChannelsPerFrame << std::endl
- << "bits per channel : " << format.mBitsPerChannel;
- return os;
-}
-
static void ZeroBufferList(AudioBufferList* buffer_list) {
for (size_t i = 0; i < buffer_list->mNumberBuffers; ++i) {
memset(buffer_list->mBuffers[i].mData,
@@ -45,7 +30,7 @@ static void WrapBufferList(AudioBufferList* buffer_list,
DCHECK(bus);
const int channels = bus->channels();
const int buffer_list_channels = buffer_list->mNumberBuffers;
- DCHECK_EQ(channels, buffer_list_channels);
+ CHECK_EQ(channels, buffer_list_channels);
// Copy pointers from AudioBufferList.
for (int i = 0; i < channels; ++i) {
@@ -72,8 +57,8 @@ AUHALStream::AUHALStream(
volume_(1),
hardware_latency_frames_(0),
stopped_(false),
- notified_for_possible_device_change_(false),
- input_buffer_list_(NULL) {
+ input_buffer_list_(NULL),
+ current_hardware_pending_bytes_(0) {
// We must have a manager.
DCHECK(manager_);
@@ -143,8 +128,12 @@ void AUHALStream::Close() {
}
if (audio_unit_) {
- AudioUnitUninitialize(audio_unit_);
- AudioComponentInstanceDispose(audio_unit_);
+ 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.";
}
// Inform the audio manager that we have been closed. This will cause our
@@ -160,20 +149,30 @@ void AUHALStream::Start(AudioSourceCallback* callback) {
}
stopped_ = false;
- notified_for_possible_device_change_ = false;
+ audio_fifo_.reset();
{
base::AutoLock auto_lock(source_lock_);
source_ = callback;
}
- AudioOutputUnitStart(audio_unit_);
+ OSStatus result = AudioOutputUnitStart(audio_unit_);
+ if (result == noErr)
+ return;
+
+ Stop();
+ OSSTATUS_DLOG(ERROR, result) << "AudioOutputUnitStart() failed.";
+ callback->OnError(this);
}
void AUHALStream::Stop() {
if (stopped_)
return;
- AudioOutputUnitStop(audio_unit_);
+ OSStatus result = AudioOutputUnitStop(audio_unit_);
+ OSSTATUS_DLOG_IF(ERROR, result != noErr, result)
+ << "AudioOutputUnitStop() failed.";
+ if (result != noErr)
+ source_->OnError(this);
base::AutoLock auto_lock(source_lock_);
source_ = NULL;
@@ -200,73 +199,69 @@ OSStatus AUHALStream::Render(
AudioBufferList* io_data) {
TRACE_EVENT0("audio", "AUHALStream::Render");
+ // If the stream parameters change for any reason, we need to insert a FIFO
+ // since the OnMoreData() pipeline can't handle frame size changes. Generally
+ // this is a temporary situation which can occur after a device change has
+ // occurred but the AudioManager hasn't received the notification yet.
if (number_of_frames != number_of_frames_) {
- // This can happen if we've suddenly changed sample-rates.
- // The stream should be stopping very soon.
- //
- // Unfortunately AUAudioInputStream and AUHALStream share the frame
- // size set by kAudioDevicePropertyBufferFrameSize above on a per process
- // basis. What this means is that the |number_of_frames| value may be
- // larger or smaller than the value set during ConfigureAUHAL().
- // In this case either audio input or audio output will be broken,
- // so just output silence.
- ZeroBufferList(io_data);
-
- // In case we missed a device notification, notify the AudioManager that the
- // device has changed. HandleDeviceChanges() will check to make sure the
- // device has actually changed before taking any action.
- if (!notified_for_possible_device_change_) {
- notified_for_possible_device_change_ = true;
- manager_->GetMessageLoop()->PostTask(FROM_HERE, base::Bind(
- &AudioManagerMac::HandleDeviceChanges, base::Unretained(manager_)));
+ // Create a FIFO on the fly to handle any discrepancies in callback rates.
+ if (!audio_fifo_) {
+ VLOG(1) << "Audio frame size change detected; adding FIFO to compensate.";
+ audio_fifo_.reset(new AudioPullFifo(
+ output_channels_,
+ number_of_frames_,
+ base::Bind(&AUHALStream::ProvideInput, base::Unretained(this))));
}
- return noErr;
- }
-
- if (input_channels_ > 0 && input_buffer_list_) {
- // Get the input data. |input_buffer_list_| is wrapped
- // to point to the data allocated in |input_bus_|.
- OSStatus result = AudioUnitRender(
- audio_unit_,
- flags,
- output_time_stamp,
- 1,
- number_of_frames,
- input_buffer_list_);
- if (result != noErr)
- ZeroBufferList(input_buffer_list_);
+ // Synchronous IO is not supported in this state.
+ if (input_channels_ > 0)
+ input_bus_->Zero();
+ } else {
+ if (input_channels_ > 0 && input_buffer_list_) {
+ // Get the input data. |input_buffer_list_| is wrapped
+ // to point to the data allocated in |input_bus_|.
+ OSStatus result = AudioUnitRender(audio_unit_,
+ flags,
+ output_time_stamp,
+ 1,
+ number_of_frames,
+ input_buffer_list_);
+ if (result != noErr)
+ ZeroBufferList(input_buffer_list_);
+ }
}
// Make |output_bus_| wrap the output AudioBufferList.
WrapBufferList(io_data, output_bus_.get(), number_of_frames);
// Update the playout latency.
- double playout_latency_frames = GetPlayoutLatency(output_time_stamp);
+ const double playout_latency_frames = GetPlayoutLatency(output_time_stamp);
+ current_hardware_pending_bytes_ = static_cast<uint32>(
+ (playout_latency_frames + 0.5) * params_.GetBytesPerFrame());
- uint32 hardware_pending_bytes = static_cast<uint32>
- ((playout_latency_frames + 0.5) * output_format_.mBytesPerFrame);
+ if (audio_fifo_)
+ audio_fifo_->Consume(output_bus_.get(), output_bus_->frames());
+ else
+ ProvideInput(0, output_bus_.get());
- {
- // Render() shouldn't be called except between AudioOutputUnitStart() and
- // AudioOutputUnitStop() calls, but crash reports have shown otherwise:
- // http://crbug.com/178765. We use |source_lock_| to prevent races and
- // crashes in Render() when |source_| is cleared.
- base::AutoLock auto_lock(source_lock_);
- if (!source_) {
- ZeroBufferList(io_data);
- return noErr;
- }
+ return noErr;
+}
- // Supply the input data and render the output data.
- source_->OnMoreIOData(
- input_bus_.get(),
- output_bus_.get(),
- AudioBuffersState(0, hardware_pending_bytes));
- output_bus_->Scale(volume_);
+void AUHALStream::ProvideInput(int frame_delay, AudioBus* dest) {
+ base::AutoLock auto_lock(source_lock_);
+ if (!source_) {
+ dest->Zero();
+ return;
}
- return noErr;
+ // Supply the input data and render the output data.
+ source_->OnMoreIOData(
+ input_bus_.get(),
+ dest,
+ AudioBuffersState(0,
+ current_hardware_pending_bytes_ +
+ frame_delay * params_.GetBytesPerFrame()));
+ dest->Scale(volume_);
}
// AUHAL callback.
@@ -453,7 +448,7 @@ bool AUHALStream::ConfigureAUHAL() {
OSStatus result = AudioComponentInstanceNew(comp, &audio_unit_);
if (result != noErr) {
- OSSTATUS_DLOG(WARNING, result) << "AudioComponentInstanceNew() failed.";
+ OSSTATUS_DLOG(ERROR, result) << "AudioComponentInstanceNew() failed.";
return false;
}
@@ -511,7 +506,7 @@ bool AUHALStream::ConfigureAUHAL() {
&buffer_size,
sizeof(buffer_size));
if (result != noErr) {
- OSSTATUS_DLOG(WARNING, result)
+ OSSTATUS_DLOG(ERROR, result)
<< "AudioUnitSetProperty(kAudioDevicePropertyBufferFrameSize) failed.";
return false;
}
@@ -532,7 +527,7 @@ bool AUHALStream::ConfigureAUHAL() {
result = AudioUnitInitialize(audio_unit_);
if (result != noErr) {
- OSSTATUS_DLOG(WARNING, result) << "AudioUnitInitialize() failed.";
+ OSSTATUS_DLOG(ERROR, result) << "AudioUnitInitialize() failed.";
return false;
}
diff --git a/chromium/media/audio/mac/audio_auhal_mac.h b/chromium/media/audio/mac/audio_auhal_mac.h
index 66feb8d0d11..b488b73c0d1 100644
--- a/chromium/media/audio/mac/audio_auhal_mac.h
+++ b/chromium/media/audio/mac/audio_auhal_mac.h
@@ -28,6 +28,7 @@
namespace media {
class AudioManagerMac;
+class AudioPullFifo;
// Implementation of AudioOuputStream for Mac OS X using the
// AUHAL Audio Unit present in OS 10.4 and later.
@@ -83,6 +84,9 @@ class AUHALStream : public AudioOutputStream {
UInt32 number_of_frames,
AudioBufferList* io_data);
+ // Called by either |audio_fifo_| or Render() to provide audio data.
+ void ProvideInput(int frame_delay, AudioBus* dest);
+
// Helper method to enable input and output.
bool EnableIO(bool enable, UInt32 scope);
@@ -108,15 +112,15 @@ class AUHALStream : public AudioOutputStream {
double GetPlayoutLatency(const AudioTimeStamp* output_time_stamp);
// Our creator, the audio manager needs to be notified when we close.
- AudioManagerMac* manager_;
+ AudioManagerMac* const manager_;
- AudioParameters params_;
+ const AudioParameters params_;
// For convenience - same as in params_.
- int input_channels_;
- int output_channels_;
+ const int input_channels_;
+ const int output_channels_;
// Buffer-size.
- size_t number_of_frames_;
+ const size_t number_of_frames_;
// Pointer to the object that will provide the audio samples.
AudioSourceCallback* source_;
@@ -131,7 +135,7 @@ class AUHALStream : public AudioOutputStream {
// The audio device to use with the AUHAL.
// We can potentially handle both input and output with this device.
- AudioDeviceID device_;
+ const AudioDeviceID device_;
// The AUHAL Audio Unit which talks to |device_|.
AudioUnit audio_unit_;
@@ -145,10 +149,6 @@ class AUHALStream : public AudioOutputStream {
// The flag used to stop the streaming.
bool stopped_;
- // The flag used to indicate if the AudioManager has been notified of a
- // potential device change. Reset to false during Start().
- bool notified_for_possible_device_change_;
-
// The input AudioUnit renders its data here.
scoped_ptr<uint8[]> input_buffer_list_storage_;
AudioBufferList* input_buffer_list_;
@@ -159,6 +159,13 @@ class AUHALStream : public AudioOutputStream {
// Container for retrieving data from AudioSourceCallback::OnMoreIOData().
scoped_ptr<AudioBus> output_bus_;
+ // Dynamically allocated FIFO used when CoreAudio asks for unexpected frame
+ // sizes.
+ scoped_ptr<AudioPullFifo> audio_fifo_;
+
+ // Current buffer delay. Set by Render().
+ uint32 current_hardware_pending_bytes_;
+
DISALLOW_COPY_AND_ASSIGN(AUHALStream);
};
diff --git a/chromium/media/audio/mac/audio_auhal_mac_unittest.cc b/chromium/media/audio/mac/audio_auhal_mac_unittest.cc
index 9b699ff10f8..d709554dfaf 100644
--- a/chromium/media/audio/mac/audio_auhal_mac_unittest.cc
+++ b/chromium/media/audio/mac/audio_auhal_mac_unittest.cc
@@ -42,7 +42,7 @@ class MockAudioSourceCallback : public AudioOutputStream::AudioSourceCallback {
class AudioOutputStreamWrapper {
public:
explicit AudioOutputStreamWrapper()
- : audio_man_(AudioManager::Create()),
+ : audio_man_(AudioManager::CreateForTesting()),
format_(AudioParameters::AUDIO_PCM_LOW_LATENCY),
bits_per_sample_(kBitsPerSample) {
AudioParameters preferred_params =
diff --git a/chromium/media/audio/mac/audio_input_mac.cc b/chromium/media/audio/mac/audio_input_mac.cc
index 7930567fd9c..4aee1179cfa 100644
--- a/chromium/media/audio/mac/audio_input_mac.cc
+++ b/chromium/media/audio/mac/audio_input_mac.cc
@@ -10,7 +10,6 @@
#include "base/logging.h"
#include "base/mac/mac_logging.h"
#include "media/audio/audio_manager_base.h"
-#include "media/audio/audio_util.h"
namespace media {
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 d97f453ca99..dbc75bfea31 100644
--- a/chromium/media/audio/mac/audio_low_latency_input_mac.cc
+++ b/chromium/media/audio/mac/audio_low_latency_input_mac.cc
@@ -9,14 +9,11 @@
#include "base/basictypes.h"
#include "base/logging.h"
#include "base/mac/mac_logging.h"
-#include "media/audio/audio_util.h"
#include "media/audio/mac/audio_manager_mac.h"
#include "media/base/data_buffer.h"
namespace media {
-static const int kMinIntervalBetweenVolumeUpdatesMs = 1000;
-
static std::ostream& operator<<(std::ostream& os,
const AudioStreamBasicDescription& format) {
os << "sample rate : " << format.mSampleRate << std::endl
@@ -104,7 +101,7 @@ AUAudioInputStream::AUAudioInputStream(
requested_size_bytes_ = requested_size_frames * format_.mBytesPerFrame;
DVLOG(1) << "Requested buffer size in bytes : " << requested_size_bytes_;
- DLOG_IF(INFO, requested_size_frames > number_of_frames_) << "FIFO is used";
+ DVLOG_IF(0, requested_size_frames > number_of_frames_) << "FIFO is used";
const int number_of_bytes = number_of_frames_ * format_.mBytesPerFrame;
fifo_delay_bytes_ = requested_size_bytes_ - number_of_bytes;
diff --git a/chromium/media/audio/mac/audio_low_latency_input_mac_unittest.cc b/chromium/media/audio/mac/audio_low_latency_input_mac_unittest.cc
index 9b5985117d7..9360befe575 100644
--- a/chromium/media/audio/mac/audio_low_latency_input_mac_unittest.cc
+++ b/chromium/media/audio/mac/audio_low_latency_input_mac_unittest.cc
@@ -95,7 +95,7 @@ class WriteToFileAudioSink : public AudioInputStream::AudioInputCallback {
class MacAudioInputTest : public testing::Test {
protected:
- MacAudioInputTest() : audio_manager_(AudioManager::Create()) {}
+ MacAudioInputTest() : audio_manager_(AudioManager::CreateForTesting()) {}
virtual ~MacAudioInputTest() {}
// Convenience method which ensures that we are not running on the build
diff --git a/chromium/media/audio/mac/audio_manager_mac.cc b/chromium/media/audio/mac/audio_manager_mac.cc
index 8e4b969854e..be7dddd5bb6 100644
--- a/chromium/media/audio/mac/audio_manager_mac.cc
+++ b/chromium/media/audio/mac/audio_manager_mac.cc
@@ -13,7 +13,6 @@
#include "base/mac/scoped_cftyperef.h"
#include "base/strings/sys_string_conversions.h"
#include "media/audio/audio_parameters.h"
-#include "media/audio/audio_util.h"
#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"
@@ -36,23 +35,6 @@ static const int kDefaultLowLatencyBufferSize = 128;
// Default sample-rate on most Apple hardware.
static const int kFallbackSampleRate = 44100;
-static int ChooseBufferSize(int output_sample_rate) {
- int buffer_size = kDefaultLowLatencyBufferSize;
- const int user_buffer_size = GetUserBufferSize();
- if (user_buffer_size) {
- buffer_size = user_buffer_size;
- } else if (output_sample_rate > 48000) {
- // The default buffer size is too small for higher sample rates and may lead
- // to glitching. Adjust upwards by multiples of the default size.
- if (output_sample_rate <= 96000)
- buffer_size = 2 * kDefaultLowLatencyBufferSize;
- else if (output_sample_rate <= 192000)
- buffer_size = 4 * kDefaultLowLatencyBufferSize;
- }
-
- return buffer_size;
-}
-
static bool HasAudioHardware(AudioObjectPropertySelector selector) {
AudioDeviceID output_device_id = kAudioObjectUnknown;
const AudioObjectPropertyAddress property_address = {
@@ -238,8 +220,9 @@ static AudioDeviceID GetAudioDeviceIdByUId(bool is_input,
return audio_device_id;
}
-AudioManagerMac::AudioManagerMac()
- : current_sample_rate_(0) {
+AudioManagerMac::AudioManagerMac(AudioLogFactory* audio_log_factory)
+ : AudioManagerBase(audio_log_factory),
+ current_sample_rate_(0) {
current_output_device_ = kAudioDeviceUnknown;
SetMaxOutputStreamsAllowed(kMaxOutputStreams);
@@ -474,6 +457,7 @@ std::string AudioManagerMac::GetAssociatedOutputDeviceID(
if (result)
return std::string();
+ std::vector<std::string> associated_devices;
for (int i = 0; i < device_count; ++i) {
// Get the number of output channels of the device.
pa.mSelector = kAudioDevicePropertyStreams;
@@ -501,10 +485,30 @@ std::string AudioManagerMac::GetAssociatedOutputDeviceID(
std::string ret(base::SysCFStringRefToUTF8(uid));
CFRelease(uid);
- return ret;
+ associated_devices.push_back(ret);
}
// No matching device found.
+ if (associated_devices.empty())
+ return std::string();
+
+ // Return the device if there is only one associated device.
+ if (associated_devices.size() == 1)
+ return associated_devices[0];
+
+ // When there are multiple associated devices, we currently do not have a way
+ // to detect if a device (e.g. a digital output device) is actually connected
+ // to an endpoint, so we cannot randomly pick a device.
+ // We pick the device iff the associated device is the default output device.
+ const std::string default_device = GetDefaultOutputDeviceID();
+ for (std::vector<std::string>::const_iterator iter =
+ associated_devices.begin();
+ iter != associated_devices.end(); ++iter) {
+ if (default_device == *iter)
+ return *iter;
+ }
+
+ // Failed to figure out which is the matching device, return an emtpy string.
return std::string();
}
@@ -542,7 +546,7 @@ AudioOutputStream* AudioManagerMac::MakeLowLatencyOutputStream(
// For I/O, the simplest case is when the default input and output
// devices are the same.
GetDefaultOutputDevice(&device);
- LOG(INFO) << "UNIFIED: default input and output devices are identical";
+ VLOG(0) << "UNIFIED: default input and output devices are identical";
} else {
// Some audio hardware is presented as separate input and output devices
// even though they are really the same physical hardware and
@@ -555,7 +559,7 @@ AudioOutputStream* AudioManagerMac::MakeLowLatencyOutputStream(
// so we get the lowest latency and use fewer threads.
device = aggregate_device_manager_.GetDefaultAggregateDevice();
if (device != kAudioObjectUnknown)
- LOG(INFO) << "Using AGGREGATE audio device";
+ VLOG(0) << "Using AGGREGATE audio device";
}
if (device != kAudioObjectUnknown &&
@@ -670,16 +674,20 @@ AudioParameters AudioManagerMac::GetPreferredOutputStreamParameters(
}
}
+ if (channel_layout == CHANNEL_LAYOUT_UNSUPPORTED)
+ channel_layout = CHANNEL_LAYOUT_DISCRETE;
+ else
+ hardware_channels = ChannelLayoutToChannelCount(channel_layout);
+
AudioParameters params(
AudioParameters::AUDIO_PCM_LOW_LATENCY,
channel_layout,
+ hardware_channels,
input_channels,
hardware_sample_rate,
16,
- buffer_size);
-
- if (channel_layout == CHANNEL_LAYOUT_UNSUPPORTED)
- params.SetDiscreteChannels(hardware_channels);
+ buffer_size,
+ AudioParameters::NO_EFFECTS);
return params;
}
@@ -722,8 +730,25 @@ void AudioManagerMac::HandleDeviceChanges() {
NotifyAllOutputDeviceChangeListeners();
}
-AudioManager* CreateAudioManager() {
- return new AudioManagerMac();
+int AudioManagerMac::ChooseBufferSize(int output_sample_rate) {
+ int buffer_size = kDefaultLowLatencyBufferSize;
+ const int user_buffer_size = GetUserBufferSize();
+ if (user_buffer_size) {
+ buffer_size = user_buffer_size;
+ } else if (output_sample_rate > 48000) {
+ // The default buffer size is too small for higher sample rates and may lead
+ // to glitching. Adjust upwards by multiples of the default size.
+ if (output_sample_rate <= 96000)
+ buffer_size = 2 * kDefaultLowLatencyBufferSize;
+ else if (output_sample_rate <= 192000)
+ buffer_size = 4 * kDefaultLowLatencyBufferSize;
+ }
+
+ return buffer_size;
+}
+
+AudioManager* CreateAudioManager(AudioLogFactory* audio_log_factory) {
+ return new AudioManagerMac(audio_log_factory);
}
} // namespace media
diff --git a/chromium/media/audio/mac/audio_manager_mac.h b/chromium/media/audio/mac/audio_manager_mac.h
index d162554b405..fb521c940de 100644
--- a/chromium/media/audio/mac/audio_manager_mac.h
+++ b/chromium/media/audio/mac/audio_manager_mac.h
@@ -22,7 +22,7 @@ namespace media {
// the AudioManager class.
class MEDIA_EXPORT AudioManagerMac : public AudioManagerBase {
public:
- AudioManagerMac();
+ AudioManagerMac(AudioLogFactory* audio_log_factory);
// Implementation of AudioManager.
virtual bool HasAudioOutputDevices() OVERRIDE;
@@ -62,10 +62,6 @@ class MEDIA_EXPORT AudioManagerMac : public AudioManagerBase {
static int HardwareSampleRateForDevice(AudioDeviceID device_id);
static int HardwareSampleRate();
- // Notify streams of a device change if the default output device or its
- // sample rate has changed, otherwise does nothing.
- void HandleDeviceChanges();
-
protected:
virtual ~AudioManagerMac();
@@ -80,6 +76,12 @@ class MEDIA_EXPORT AudioManagerMac : public AudioManagerBase {
void CreateDeviceListener();
void DestroyDeviceListener();
+ int ChooseBufferSize(int output_sample_rate);
+
+ // Notify streams of a device change if the default output device or its
+ // sample rate has changed, otherwise does nothing.
+ void HandleDeviceChanges();
+
scoped_ptr<AudioDeviceListenerMac> output_device_listener_;
// Track the output sample-rate and the default output device
diff --git a/chromium/media/audio/mac/audio_synchronized_mac.cc b/chromium/media/audio/mac/audio_synchronized_mac.cc
index a2484ca67fe..a9bc88e2bd3 100644
--- a/chromium/media/audio/mac/audio_synchronized_mac.cc
+++ b/chromium/media/audio/mac/audio_synchronized_mac.cc
@@ -11,7 +11,6 @@
#include "base/debug/trace_event.h"
#include "base/logging.h"
#include "base/mac/mac_logging.h"
-#include "media/audio/audio_util.h"
#include "media/audio/mac/audio_manager_mac.h"
#include "media/base/channel_mixer.h"
diff --git a/chromium/media/audio/mac/audio_unified_mac.cc b/chromium/media/audio/mac/audio_unified_mac.cc
index 67ec2fe6f3e..d1dc007e6a8 100644
--- a/chromium/media/audio/mac/audio_unified_mac.cc
+++ b/chromium/media/audio/mac/audio_unified_mac.cc
@@ -9,7 +9,6 @@
#include "base/basictypes.h"
#include "base/logging.h"
#include "base/mac/mac_logging.h"
-#include "media/audio/audio_util.h"
#include "media/audio/mac/audio_manager_mac.h"
namespace media {
diff --git a/chromium/media/audio/mock_audio_manager.cc b/chromium/media/audio/mock_audio_manager.cc
index a164332a64a..f2074d65357 100644
--- a/chromium/media/audio/mock_audio_manager.cc
+++ b/chromium/media/audio/mock_audio_manager.cc
@@ -25,8 +25,8 @@ bool MockAudioManager::HasAudioInputDevices() {
return true;
}
-string16 MockAudioManager::GetAudioInputDeviceModel() {
- return string16();
+base::string16 MockAudioManager::GetAudioInputDeviceModel() {
+ return base::string16();
}
void MockAudioManager::ShowAudioInputSettings() {
@@ -34,6 +34,11 @@ void MockAudioManager::ShowAudioInputSettings() {
void MockAudioManager::GetAudioInputDeviceNames(
AudioDeviceNames* device_names) {
+ DCHECK(device_names->empty());
+ device_names->push_back(media::AudioDeviceName("fake_device_name_1",
+ "fake_device_id_1"));
+ device_names->push_back(media::AudioDeviceName("fake_device_name_2",
+ "fake_device_id_2"));
}
void MockAudioManager::GetAudioOutputDeviceNames(
@@ -98,4 +103,11 @@ std::string MockAudioManager::GetAssociatedOutputDeviceID(
return std::string();
}
+scoped_ptr<AudioLog> MockAudioManager::CreateAudioLog(
+ AudioLogFactory::AudioComponent component) {
+ return scoped_ptr<AudioLog>();
+}
+
+void MockAudioManager::FixWedgedAudio() {}
+
} // namespace media.
diff --git a/chromium/media/audio/mock_audio_manager.h b/chromium/media/audio/mock_audio_manager.h
index 7bc30f578e7..2d71fe8493f 100644
--- a/chromium/media/audio/mock_audio_manager.h
+++ b/chromium/media/audio/mock_audio_manager.h
@@ -27,7 +27,7 @@ class MockAudioManager : public media::AudioManager {
virtual bool HasAudioInputDevices() OVERRIDE;
- virtual string16 GetAudioInputDeviceModel() OVERRIDE;
+ virtual base::string16 GetAudioInputDeviceModel() OVERRIDE;
virtual void ShowAudioInputSettings() OVERRIDE;
@@ -67,9 +67,15 @@ class MockAudioManager : public media::AudioManager {
virtual std::string GetAssociatedOutputDeviceID(
const std::string& input_device_id) OVERRIDE;
- private:
+ virtual scoped_ptr<AudioLog> CreateAudioLog(
+ AudioLogFactory::AudioComponent component) OVERRIDE;
+
+ virtual void FixWedgedAudio() OVERRIDE;
+
+ protected:
virtual ~MockAudioManager();
+ private:
scoped_refptr<base::MessageLoopProxy> message_loop_proxy_;
DISALLOW_COPY_AND_ASSIGN(MockAudioManager);
diff --git a/chromium/media/audio/openbsd/audio_manager_openbsd.cc b/chromium/media/audio/openbsd/audio_manager_openbsd.cc
index a97ea8f625e..b378b02d0cd 100644
--- a/chromium/media/audio/openbsd/audio_manager_openbsd.cc
+++ b/chromium/media/audio/openbsd/audio_manager_openbsd.cc
@@ -11,7 +11,6 @@
#include "base/stl_util.h"
#include "media/audio/audio_output_dispatcher.h"
#include "media/audio/audio_parameters.h"
-#include "media/audio/audio_util.h"
#include "media/audio/pulse/pulse_output.h"
#include "media/audio/pulse/pulse_stubs.h"
#include "media/base/channel_layout.h"
@@ -65,8 +64,9 @@ AudioParameters AudioManagerOpenBSD::GetInputStreamParameters(
kDefaultSampleRate, 16, kDefaultInputBufferSize);
}
-AudioManagerOpenBSD::AudioManagerOpenBSD()
- : pulse_library_is_initialized_(false) {
+AudioManagerOpenBSD::AudioManagerOpenBSD(AudioLogFactory* audio_log_factory)
+ : AudioManagerBase(audio_log_factory),
+ pulse_library_is_initialized_(false) {
SetMaxOutputStreamsAllowed(kMaxOutputStreams);
StubPathMap paths;
@@ -139,7 +139,7 @@ AudioParameters AudioManagerOpenBSD::GetPreferredOutputStreamParameters(
return AudioParameters(
AudioParameters::AUDIO_PCM_LOW_LATENCY, channel_layout, input_channels,
- sample_rate, bits_per_sample, buffer_size);
+ sample_rate, bits_per_sample, buffer_size, AudioParameters::NO_EFFECTS);
}
AudioOutputStream* AudioManagerOpenBSD::MakeOutputStream(
@@ -152,8 +152,8 @@ AudioOutputStream* AudioManagerOpenBSD::MakeOutputStream(
// TODO(xians): Merge AudioManagerOpenBSD with AudioManagerPulse;
// static
-AudioManager* CreateAudioManager() {
- return new AudioManagerOpenBSD();
+AudioManager* CreateAudioManager(AudioLogFactory* audio_log_factory) {
+ return new AudioManagerOpenBSD(audio_log_factory);
}
} // namespace media
diff --git a/chromium/media/audio/openbsd/audio_manager_openbsd.h b/chromium/media/audio/openbsd/audio_manager_openbsd.h
index e4bb3948d28..113f5915ae1 100644
--- a/chromium/media/audio/openbsd/audio_manager_openbsd.h
+++ b/chromium/media/audio/openbsd/audio_manager_openbsd.h
@@ -14,7 +14,7 @@ namespace media {
class MEDIA_EXPORT AudioManagerOpenBSD : public AudioManagerBase {
public:
- AudioManagerOpenBSD();
+ AudioManagerOpenBSD(AudioLogFactory* audio_log_factory);
// Implementation of AudioManager.
virtual bool HasAudioOutputDevices() OVERRIDE;
diff --git a/chromium/media/audio/pulse/audio_manager_pulse.cc b/chromium/media/audio/pulse/audio_manager_pulse.cc
index 5c09f149057..d369d135bef 100644
--- a/chromium/media/audio/pulse/audio_manager_pulse.cc
+++ b/chromium/media/audio/pulse/audio_manager_pulse.cc
@@ -10,9 +10,8 @@
#include "base/logging.h"
#include "base/nix/xdg_util.h"
#include "base/stl_util.h"
+#include "media/audio/alsa/audio_manager_alsa.h"
#include "media/audio/audio_parameters.h"
-#include "media/audio/audio_util.h"
-#include "media/audio/linux/audio_manager_linux.h"
#include "media/audio/pulse/pulse_input.h"
#include "media/audio/pulse/pulse_output.h"
#include "media/audio/pulse/pulse_unified.h"
@@ -39,8 +38,8 @@ static const base::FilePath::CharType kPulseLib[] =
FILE_PATH_LITERAL("libpulse.so.0");
// static
-AudioManager* AudioManagerPulse::Create() {
- scoped_ptr<AudioManagerPulse> ret(new AudioManagerPulse());
+AudioManager* AudioManagerPulse::Create(AudioLogFactory* audio_log_factory) {
+ scoped_ptr<AudioManagerPulse> ret(new AudioManagerPulse(audio_log_factory));
if (ret->Init())
return ret.release();
@@ -48,8 +47,9 @@ AudioManager* AudioManagerPulse::Create() {
return NULL;
}
-AudioManagerPulse::AudioManagerPulse()
- : input_mainloop_(NULL),
+AudioManagerPulse::AudioManagerPulse(AudioLogFactory* audio_log_factory)
+ : AudioManagerBase(audio_log_factory),
+ input_mainloop_(NULL),
input_context_(NULL),
devices_(NULL),
native_input_sample_rate_(0) {
@@ -78,7 +78,7 @@ bool AudioManagerPulse::HasAudioInputDevices() {
}
void AudioManagerPulse::ShowAudioInputSettings() {
- AudioManagerLinux::ShowLinuxAudioInputSettings();
+ AudioManagerAlsa::ShowLinuxAudioInputSettings();
}
void AudioManagerPulse::GetAudioDeviceNames(
@@ -181,7 +181,7 @@ AudioParameters AudioManagerPulse::GetPreferredOutputStreamParameters(
return AudioParameters(
AudioParameters::AUDIO_PCM_LOW_LATENCY, channel_layout, input_channels,
- sample_rate, bits_per_sample, buffer_size);
+ sample_rate, bits_per_sample, buffer_size, AudioParameters::NO_EFFECTS);
}
AudioOutputStream* AudioManagerPulse::MakeOutputStream(
diff --git a/chromium/media/audio/pulse/audio_manager_pulse.h b/chromium/media/audio/pulse/audio_manager_pulse.h
index 36396639929..45fb8cb56fa 100644
--- a/chromium/media/audio/pulse/audio_manager_pulse.h
+++ b/chromium/media/audio/pulse/audio_manager_pulse.h
@@ -16,10 +16,10 @@ namespace media {
class MEDIA_EXPORT AudioManagerPulse : public AudioManagerBase {
public:
- AudioManagerPulse();
+ AudioManagerPulse(AudioLogFactory* audio_log_factory);
virtual ~AudioManagerPulse();
- static AudioManager* Create();
+ static AudioManager* Create(AudioLogFactory* audio_log_factory);
// Implementation of AudioManager.
virtual bool HasAudioOutputDevices() OVERRIDE;
diff --git a/chromium/media/audio/shared_memory_util.cc b/chromium/media/audio/shared_memory_util.cc
deleted file mode 100644
index 523cdb9646c..00000000000
--- a/chromium/media/audio/shared_memory_util.cc
+++ /dev/null
@@ -1,72 +0,0 @@
-// 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/shared_memory_util.h"
-
-#include <algorithm>
-
-#include "base/atomicops.h"
-#include "base/logging.h"
-
-using base::subtle::Atomic32;
-
-static const uint32 kUnknownDataSize = static_cast<uint32>(-1);
-
-namespace media {
-
-uint32 TotalSharedMemorySizeInBytes(uint32 packet_size) {
- // Need to reserve extra 4 bytes for size of data.
- return packet_size + sizeof(Atomic32);
-}
-
-uint32 PacketSizeInBytes(uint32 shared_memory_created_size) {
- return shared_memory_created_size - sizeof(Atomic32);
-}
-
-uint32 GetActualDataSizeInBytes(base::SharedMemory* shared_memory,
- uint32 packet_size) {
- char* ptr = static_cast<char*>(shared_memory->memory()) + packet_size;
- DCHECK_EQ(0u, reinterpret_cast<size_t>(ptr) & 3);
-
- // Actual data size stored at the end of the buffer.
- uint32 actual_data_size =
- base::subtle::Acquire_Load(reinterpret_cast<volatile Atomic32*>(ptr));
- return std::min(actual_data_size, packet_size);
-}
-
-void SetActualDataSizeInBytes(void* shared_memory_ptr,
- uint32 packet_size,
- uint32 actual_data_size) {
- char* ptr = static_cast<char*>(shared_memory_ptr) + packet_size;
- DCHECK_EQ(0u, reinterpret_cast<size_t>(ptr) & 3);
-
- // Set actual data size at the end of the buffer.
- base::subtle::Release_Store(reinterpret_cast<volatile Atomic32*>(ptr),
- actual_data_size);
-}
-
-void SetActualDataSizeInBytes(base::SharedMemory* shared_memory,
- uint32 packet_size,
- uint32 actual_data_size) {
- SetActualDataSizeInBytes(shared_memory->memory(),
- packet_size, actual_data_size);
-}
-
-void SetUnknownDataSize(base::SharedMemory* shared_memory,
- uint32 packet_size) {
- SetActualDataSizeInBytes(shared_memory, packet_size, kUnknownDataSize);
-}
-
-bool IsUnknownDataSize(base::SharedMemory* shared_memory,
- uint32 packet_size) {
- char* ptr = static_cast<char*>(shared_memory->memory()) + packet_size;
- DCHECK_EQ(0u, reinterpret_cast<size_t>(ptr) & 3);
-
- // Actual data size stored at the end of the buffer.
- uint32 actual_data_size =
- base::subtle::Acquire_Load(reinterpret_cast<volatile Atomic32*>(ptr));
- return actual_data_size == kUnknownDataSize;
-}
-
-} // namespace media
diff --git a/chromium/media/audio/shared_memory_util.h b/chromium/media/audio/shared_memory_util.h
deleted file mode 100644
index 9186d5c9529..00000000000
--- a/chromium/media/audio/shared_memory_util.h
+++ /dev/null
@@ -1,39 +0,0 @@
-// 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.
-
-#ifndef MEDIA_AUDIO_SHARED_MEMORY_UTIL_H_
-#define MEDIA_AUDIO_SHARED_MEMORY_UTIL_H_
-
-#include "base/basictypes.h"
-#include "base/memory/shared_memory.h"
-#include "media/base/media_export.h"
-
-namespace media {
-
-// Value sent by the controller to the renderer in low-latency mode
-// indicating that the stream is paused.
-enum { kPauseMark = -1 };
-
-// Functions that handle data buffer passed between processes in the shared
-// memory. Called on both IPC sides. These are necessary because the shared
-// memory has a layout: the last word in the block is the data size in bytes.
-
-MEDIA_EXPORT uint32 TotalSharedMemorySizeInBytes(uint32 packet_size);
-MEDIA_EXPORT uint32 PacketSizeInBytes(uint32 shared_memory_created_size);
-MEDIA_EXPORT uint32 GetActualDataSizeInBytes(base::SharedMemory* shared_memory,
- uint32 packet_size);
-MEDIA_EXPORT void SetActualDataSizeInBytes(base::SharedMemory* shared_memory,
- uint32 packet_size,
- uint32 actual_data_size);
-MEDIA_EXPORT void SetActualDataSizeInBytes(void* shared_memory_ptr,
- uint32 packet_size,
- uint32 actual_data_size);
-MEDIA_EXPORT void SetUnknownDataSize(base::SharedMemory* shared_memory,
- uint32 packet_size);
-MEDIA_EXPORT bool IsUnknownDataSize(base::SharedMemory* shared_memory,
- uint32 packet_size);
-
-} // namespace media
-
-#endif // MEDIA_AUDIO_SHARED_MEMORY_UTIL_H_
diff --git a/chromium/media/audio/simple_sources.cc b/chromium/media/audio/simple_sources.cc
index 7aa74d6e5f1..275413a232c 100644
--- a/chromium/media/audio/simple_sources.cc
+++ b/chromium/media/audio/simple_sources.cc
@@ -10,7 +10,6 @@
#include <algorithm>
#include "base/logging.h"
-#include "media/audio/audio_util.h"
namespace media {
diff --git a/chromium/media/audio/sounds/audio_stream_handler.cc b/chromium/media/audio/sounds/audio_stream_handler.cc
new file mode 100644
index 00000000000..08608ac4187
--- /dev/null
+++ b/chromium/media/audio/sounds/audio_stream_handler.cc
@@ -0,0 +1,188 @@
+// Copyright 2013 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/sounds/audio_stream_handler.h"
+
+#include <string>
+
+#include "base/logging.h"
+#include "base/message_loop/message_loop_proxy.h"
+#include "media/audio/audio_manager.h"
+#include "media/audio/audio_manager_base.h"
+#include "media/base/channel_layout.h"
+
+namespace media {
+
+namespace {
+
+// Volume percent.
+const double kOutputVolumePercent = 0.8;
+
+// The number of frames each OnMoreData() call will request.
+const int kDefaultFrameCount = 1024;
+
+AudioStreamHandler::TestObserver* g_observer_for_testing = NULL;
+AudioOutputStream::AudioSourceCallback* g_audio_source_for_testing = NULL;
+
+} // namespace
+
+class AudioStreamHandler::AudioStreamContainer
+ : public AudioOutputStream::AudioSourceCallback {
+ public:
+ AudioStreamContainer(const WavAudioHandler& wav_audio,
+ const AudioParameters& params)
+ : stream_(NULL),
+ wav_audio_(wav_audio),
+ params_(params),
+ cursor_(0) {
+ }
+
+ virtual ~AudioStreamContainer() {
+ DCHECK(AudioManager::Get()->GetMessageLoop()->BelongsToCurrentThread());
+ }
+
+ void Play() {
+ DCHECK(AudioManager::Get()->GetMessageLoop()->BelongsToCurrentThread());
+
+ if (!stream_) {
+ stream_ = AudioManager::Get()->MakeAudioOutputStreamProxy(params_,
+ std::string(),
+ std::string());
+ if (!stream_ || !stream_->Open()) {
+ LOG(ERROR) << "Failed to open an output stream.";
+ return;
+ }
+ stream_->SetVolume(kOutputVolumePercent);
+ } else {
+ // TODO (ygorshenin@): implement smart stream rewind.
+ stream_->Stop();
+ }
+
+ cursor_ = 0;
+ if (g_audio_source_for_testing)
+ stream_->Start(g_audio_source_for_testing);
+ else
+ stream_->Start(this);
+
+ if (g_observer_for_testing)
+ g_observer_for_testing->OnPlay();
+ }
+
+ void Stop() {
+ DCHECK(AudioManager::Get()->GetMessageLoop()->BelongsToCurrentThread());
+ if (!stream_)
+ return;
+ stream_->Stop();
+ stream_->Close();
+ stream_ = NULL;
+
+ if (g_observer_for_testing)
+ g_observer_for_testing->OnStop(cursor_);
+ }
+
+ private:
+ // AudioOutputStream::AudioSourceCallback overrides:
+ // Following methods could be called from *ANY* thread.
+ virtual int OnMoreData(AudioBus* dest,
+ AudioBuffersState /* state */) OVERRIDE {
+ size_t bytes_written = 0;
+ if (wav_audio_.AtEnd(cursor_) ||
+ !wav_audio_.CopyTo(dest, cursor_, &bytes_written)) {
+ AudioManager::Get()->GetMessageLoop()->PostTask(
+ FROM_HERE,
+ base::Bind(&AudioStreamContainer::Stop, base::Unretained(this)));
+ return 0;
+ }
+ cursor_ += bytes_written;
+
+ return dest->frames();
+ }
+
+ virtual int OnMoreIOData(AudioBus* /* source */,
+ AudioBus* dest,
+ AudioBuffersState state) OVERRIDE {
+ return OnMoreData(dest, state);
+ }
+
+ virtual void OnError(AudioOutputStream* /* stream */) OVERRIDE {
+ LOG(ERROR) << "Error during system sound reproduction.";
+ }
+
+ AudioOutputStream* stream_;
+
+ const WavAudioHandler wav_audio_;
+ const AudioParameters params_;
+
+ size_t cursor_;
+
+ DISALLOW_COPY_AND_ASSIGN(AudioStreamContainer);
+};
+
+AudioStreamHandler::AudioStreamHandler(const base::StringPiece& wav_data)
+ : wav_audio_(wav_data),
+ initialized_(false) {
+ AudioManager* manager = AudioManager::Get();
+ if (!manager) {
+ LOG(ERROR) << "Can't get access to audio manager.";
+ return;
+ }
+ AudioParameters params(AudioParameters::AUDIO_PCM_LOW_LATENCY,
+ GuessChannelLayout(wav_audio_.num_channels()),
+ wav_audio_.sample_rate(),
+ wav_audio_.bits_per_sample(),
+ kDefaultFrameCount);
+ if (!params.IsValid()) {
+ LOG(ERROR) << "Audio params are invalid.";
+ return;
+ }
+ stream_.reset(new AudioStreamContainer(wav_audio_, params));
+ initialized_ = true;
+}
+
+AudioStreamHandler::~AudioStreamHandler() {
+ DCHECK(CalledOnValidThread());
+ AudioManager::Get()->GetMessageLoop()->PostTask(
+ FROM_HERE,
+ base::Bind(&AudioStreamContainer::Stop, base::Unretained(stream_.get())));
+ AudioManager::Get()->GetMessageLoop()->DeleteSoon(FROM_HERE,
+ stream_.release());
+}
+
+bool AudioStreamHandler::IsInitialized() const {
+ DCHECK(CalledOnValidThread());
+ return initialized_;
+}
+
+bool AudioStreamHandler::Play() {
+ DCHECK(CalledOnValidThread());
+
+ if (!IsInitialized())
+ return false;
+
+ AudioManager::Get()->GetMessageLoop()->PostTask(
+ FROM_HERE,
+ base::Bind(base::IgnoreResult(&AudioStreamContainer::Play),
+ base::Unretained(stream_.get())));
+ return true;
+}
+
+void AudioStreamHandler::Stop() {
+ DCHECK(CalledOnValidThread());
+ AudioManager::Get()->GetMessageLoop()->PostTask(
+ FROM_HERE,
+ base::Bind(&AudioStreamContainer::Stop, base::Unretained(stream_.get())));
+}
+
+// static
+void AudioStreamHandler::SetObserverForTesting(TestObserver* observer) {
+ g_observer_for_testing = observer;
+}
+
+// static
+void AudioStreamHandler::SetAudioSourceForTesting(
+ AudioOutputStream::AudioSourceCallback* source) {
+ g_audio_source_for_testing = source;
+}
+
+} // namespace media
diff --git a/chromium/media/audio/sounds/audio_stream_handler.h b/chromium/media/audio/sounds/audio_stream_handler.h
new file mode 100644
index 00000000000..7c63a24f034
--- /dev/null
+++ b/chromium/media/audio/sounds/audio_stream_handler.h
@@ -0,0 +1,76 @@
+// Copyright 2013 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_SOUNDS_AUDIO_STREAM_HANDLER_H_
+#define MEDIA_AUDIO_SOUNDS_AUDIO_STREAM_HANDLER_H_
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/strings/string_piece.h"
+#include "base/threading/non_thread_safe.h"
+#include "media/audio/audio_io.h"
+#include "media/audio/audio_parameters.h"
+#include "media/audio/sounds/wav_audio_handler.h"
+#include "media/base/media_export.h"
+
+namespace media {
+
+class AudioManager;
+
+// This class sends a sound to the audio manager.
+class MEDIA_EXPORT AudioStreamHandler : public base::NonThreadSafe {
+ public:
+ class TestObserver {
+ public:
+ virtual ~TestObserver() {}
+
+ // Following methods will be called only from the audio thread.
+
+ // Called when AudioOutputStreamProxy::Start() was successfully called.
+ virtual void OnPlay() = 0;
+
+ // Called when AudioOutputStreamProxy::Stop() was successfully called.
+ virtual void OnStop(size_t cursor) = 0;
+ };
+
+ // C-tor for AudioStreamHandler. |wav_data| should be a raw
+ // uncompressed WAVE data which will be sent to the audio manager.
+ explicit AudioStreamHandler(const base::StringPiece& wav_data);
+ virtual ~AudioStreamHandler();
+
+ // Returns true iff AudioStreamHandler is correctly initialized;
+ bool IsInitialized() const;
+
+ // Stops any previous playback if it's still not completed and
+ // starts new playback. Volume level will be set according to
+ // current settings and won't be changed during playback. Returns
+ // true iff new playback was successfully started.
+ bool Play();
+
+ // Stops current playback.
+ void Stop();
+
+ const WavAudioHandler& wav_audio_handler() const { return wav_audio_; }
+
+ private:
+ friend class AudioStreamHandlerTest;
+ friend class SoundsManagerTest;
+
+ class AudioStreamContainer;
+
+ static void SetObserverForTesting(TestObserver* observer);
+ static void SetAudioSourceForTesting(
+ AudioOutputStream::AudioSourceCallback* source);
+
+ WavAudioHandler wav_audio_;
+ scoped_ptr<AudioStreamContainer> stream_;
+
+ bool initialized_;
+
+ DISALLOW_COPY_AND_ASSIGN(AudioStreamHandler);
+};
+
+} // namespace media
+
+#endif // MEDIA_AUDIO_SOUNDS_AUDIO_STREAM_HANDLER_H_
diff --git a/chromium/media/audio/sounds/audio_stream_handler_unittest.cc b/chromium/media/audio/sounds/audio_stream_handler_unittest.cc
new file mode 100644
index 00000000000..50bc301c38a
--- /dev/null
+++ b/chromium/media/audio/sounds/audio_stream_handler_unittest.cc
@@ -0,0 +1,108 @@
+// Copyright 2013 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 "base/basictypes.h"
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/compiler_specific.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "media/audio/audio_io.h"
+#include "media/audio/audio_manager.h"
+#include "media/audio/simple_sources.h"
+#include "media/audio/sounds/audio_stream_handler.h"
+#include "media/audio/sounds/test_data.h"
+#include "media/base/channel_layout.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace media {
+
+class AudioStreamHandlerTest : public testing::Test {
+ public:
+ AudioStreamHandlerTest() {}
+ virtual ~AudioStreamHandlerTest() {}
+
+ virtual void SetUp() OVERRIDE {
+ audio_manager_.reset(AudioManager::CreateForTesting());
+
+ base::StringPiece data(kTestAudioData, arraysize(kTestAudioData));
+ audio_stream_handler_.reset(new AudioStreamHandler(data));
+ }
+
+ virtual void TearDown() OVERRIDE {
+ audio_stream_handler_.reset();
+ audio_manager_.reset();
+ }
+
+ AudioStreamHandler* audio_stream_handler() {
+ return audio_stream_handler_.get();
+ }
+
+ void SetObserverForTesting(AudioStreamHandler::TestObserver* observer) {
+ AudioStreamHandler::SetObserverForTesting(observer);
+ }
+
+ void SetAudioSourceForTesting(
+ AudioOutputStream::AudioSourceCallback* source) {
+ AudioStreamHandler::SetAudioSourceForTesting(source);
+ }
+
+ private:
+ scoped_ptr<AudioManager> audio_manager_;
+ scoped_ptr<AudioStreamHandler> audio_stream_handler_;
+
+ base::MessageLoop message_loop_;
+};
+
+TEST_F(AudioStreamHandlerTest, Play) {
+ base::RunLoop run_loop;
+ TestObserver observer(run_loop.QuitClosure());
+
+ SetObserverForTesting(&observer);
+
+ ASSERT_TRUE(audio_stream_handler()->IsInitialized());
+ ASSERT_TRUE(audio_stream_handler()->Play());
+
+ run_loop.Run();
+
+ SetObserverForTesting(NULL);
+
+ ASSERT_EQ(1, observer.num_play_requests());
+ ASSERT_EQ(1, observer.num_stop_requests());
+ ASSERT_EQ(4, observer.cursor());
+}
+
+TEST_F(AudioStreamHandlerTest, Rewind) {
+ base::RunLoop run_loop;
+ TestObserver observer(run_loop.QuitClosure());
+ SineWaveAudioSource source(CHANNEL_LAYOUT_STEREO, 200.0, 8000);
+
+ SetObserverForTesting(&observer);
+ SetAudioSourceForTesting(&source);
+
+ ASSERT_TRUE(audio_stream_handler()->IsInitialized());
+
+ ASSERT_TRUE(audio_stream_handler()->Play());
+ base::MessageLoop::current()->PostDelayedTask(
+ FROM_HERE,
+ base::Bind(base::IgnoreResult(&AudioStreamHandler::Play),
+ base::Unretained(audio_stream_handler())),
+ base::TimeDelta::FromSeconds(3));
+ base::MessageLoop::current()->PostDelayedTask(
+ FROM_HERE,
+ base::Bind(&AudioStreamHandler::Stop,
+ base::Unretained(audio_stream_handler())),
+ base::TimeDelta::FromSeconds(6));
+
+ run_loop.Run();
+
+ SetObserverForTesting(NULL);
+ SetAudioSourceForTesting(NULL);
+
+ ASSERT_EQ(2, observer.num_play_requests());
+ ASSERT_EQ(1, observer.num_stop_requests());
+}
+
+} // namespace media
diff --git a/chromium/media/audio/sounds/sounds_manager.cc b/chromium/media/audio/sounds/sounds_manager.cc
new file mode 100644
index 00000000000..e93dc6588dd
--- /dev/null
+++ b/chromium/media/audio/sounds/sounds_manager.cc
@@ -0,0 +1,150 @@
+// Copyright 2013 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/sounds/sounds_manager.h"
+
+#include "base/command_line.h"
+#include "base/compiler_specific.h"
+#include "base/logging.h"
+#include "base/memory/linked_ptr.h"
+#include "base/memory/ref_counted.h"
+#include "base/message_loop/message_loop_proxy.h"
+#include "media/audio/audio_manager.h"
+#include "media/audio/sounds/audio_stream_handler.h"
+#include "media/base/media_switches.h"
+
+namespace media {
+
+namespace {
+
+SoundsManager* g_instance = NULL;
+
+// SoundsManagerImpl ---------------------------------------------------
+
+class SoundsManagerImpl : public SoundsManager {
+ public:
+ SoundsManagerImpl();
+ virtual ~SoundsManagerImpl();
+
+ // SoundsManager implementation:
+ virtual bool Initialize(SoundKey key,
+ const base::StringPiece& data) OVERRIDE;
+ virtual bool Play(SoundKey key) OVERRIDE;
+ virtual base::TimeDelta GetDuration(SoundKey key) OVERRIDE;
+
+ private:
+ base::hash_map<SoundKey, linked_ptr<AudioStreamHandler> > handlers_;
+ scoped_refptr<base::MessageLoopProxy> message_loop_;
+
+ DISALLOW_COPY_AND_ASSIGN(SoundsManagerImpl);
+};
+
+SoundsManagerImpl::SoundsManagerImpl()
+ : message_loop_(AudioManager::Get()->GetMessageLoop()) {}
+
+SoundsManagerImpl::~SoundsManagerImpl() { DCHECK(CalledOnValidThread()); }
+
+bool SoundsManagerImpl::Initialize(SoundKey key,
+ const base::StringPiece& data) {
+ if (handlers_.find(key) != handlers_.end() && handlers_[key]->IsInitialized())
+ return true;
+ linked_ptr<AudioStreamHandler> handler(new AudioStreamHandler(data));
+ if (!handler->IsInitialized()) {
+ LOG(WARNING) << "Can't initialize AudioStreamHandler for key=" << key;
+ return false;
+ }
+ handlers_[key] = handler;
+ return true;
+}
+
+bool SoundsManagerImpl::Play(SoundKey key) {
+ DCHECK(CalledOnValidThread());
+ if (handlers_.find(key) == handlers_.end() ||
+ !handlers_[key]->IsInitialized()) {
+ return false;
+ }
+ return handlers_[key]->Play();
+}
+
+base::TimeDelta SoundsManagerImpl::GetDuration(SoundKey key) {
+ DCHECK(CalledOnValidThread());
+ if (handlers_.find(key) == handlers_.end() ||
+ !handlers_[key]->IsInitialized()) {
+ return base::TimeDelta();
+ }
+ const WavAudioHandler& wav_audio = handlers_[key]->wav_audio_handler();
+ const int64 size = wav_audio.size();
+ const int64 rate = wav_audio.byte_rate();
+ return base::TimeDelta::FromMicroseconds(size * 1000000 / rate);
+}
+
+// SoundsManagerStub ---------------------------------------------------
+
+class SoundsManagerStub : public SoundsManager {
+ public:
+ SoundsManagerStub();
+ virtual ~SoundsManagerStub();
+
+ // SoundsManager implementation:
+ virtual bool Initialize(SoundKey key,
+ const base::StringPiece& data) OVERRIDE;
+ virtual bool Play(SoundKey key) OVERRIDE;
+ virtual base::TimeDelta GetDuration(SoundKey key) OVERRIDE;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(SoundsManagerStub);
+};
+
+SoundsManagerStub::SoundsManagerStub() {}
+
+SoundsManagerStub::~SoundsManagerStub() { DCHECK(CalledOnValidThread()); }
+
+bool SoundsManagerStub::Initialize(SoundKey /* key */,
+ const base::StringPiece& /* data */) {
+ DCHECK(CalledOnValidThread());
+ return false;
+}
+
+bool SoundsManagerStub::Play(SoundKey /* key */) {
+ DCHECK(CalledOnValidThread());
+ return false;
+}
+
+base::TimeDelta SoundsManagerStub::GetDuration(SoundKey /* key */) {
+ DCHECK(CalledOnValidThread());
+ return base::TimeDelta();
+}
+
+} // namespace
+
+SoundsManager::SoundsManager() {}
+
+SoundsManager::~SoundsManager() { DCHECK(CalledOnValidThread()); }
+
+// static
+void SoundsManager::Create() {
+ CHECK(!g_instance) << "SoundsManager::Create() is called twice";
+ const bool enabled = !CommandLine::ForCurrentProcess()->HasSwitch(
+ ::switches::kDisableSystemSoundsManager);
+ if (enabled)
+ g_instance = new SoundsManagerImpl();
+ else
+ g_instance = new SoundsManagerStub();
+}
+
+// static
+void SoundsManager::Shutdown() {
+ CHECK(g_instance) << "SoundsManager::Shutdown() is called "
+ << "without previous call to Create()";
+ delete g_instance;
+ g_instance = NULL;
+}
+
+// static
+SoundsManager* SoundsManager::Get() {
+ CHECK(g_instance) << "SoundsManager::Get() is called before Create()";
+ return g_instance;
+}
+
+} // namespace media
diff --git a/chromium/media/audio/sounds/sounds_manager.h b/chromium/media/audio/sounds/sounds_manager.h
new file mode 100644
index 00000000000..7ff6aafffdc
--- /dev/null
+++ b/chromium/media/audio/sounds/sounds_manager.h
@@ -0,0 +1,56 @@
+// Copyright 2013 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_SOUNDS_SOUNDS_MANAGER_H_
+#define MEDIA_AUDIO_SOUNDS_SOUNDS_MANAGER_H_
+
+#include "base/basictypes.h"
+#include "base/containers/hash_tables.h"
+#include "base/strings/string_piece.h"
+#include "base/threading/non_thread_safe.h"
+#include "base/time/time.h"
+#include "media/base/media_export.h"
+
+namespace media {
+
+// This class is used for reproduction of system sounds. All methods
+// should be accessed from the Audio thread.
+class MEDIA_EXPORT SoundsManager : public base::NonThreadSafe {
+ public:
+ typedef int SoundKey;
+
+ // Creates a singleton instance of the SoundsManager.
+ static void Create();
+
+ // Removes a singleton instance of the SoundsManager.
+ static void Shutdown();
+
+ // Returns a pointer to a singleton instance of the SoundsManager.
+ static SoundsManager* Get();
+
+ // Initializes SoundsManager with the wav data for the system
+ // sounds. Returns true if SoundsManager was successfully
+ // initialized.
+ virtual bool Initialize(SoundKey key, const base::StringPiece& data) = 0;
+
+ // Plays sound identified by |key|, returns false if SoundsManager
+ // was not properly initialized.
+ virtual bool Play(SoundKey key) = 0;
+
+ // Returns duration of the sound identified by |key|. If SoundsManager
+ // was not properly initialized or |key| was not registered, this
+ // method returns an empty value.
+ virtual base::TimeDelta GetDuration(SoundKey key) = 0;
+
+ protected:
+ SoundsManager();
+ virtual ~SoundsManager();
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(SoundsManager);
+};
+
+} // namespace media
+
+#endif // MEDIA_AUDIO_SOUNDS_SOUNDS_MANAGER_H_
diff --git a/chromium/media/audio/sounds/sounds_manager_unittest.cc b/chromium/media/audio/sounds/sounds_manager_unittest.cc
new file mode 100644
index 00000000000..5aa3694e838
--- /dev/null
+++ b/chromium/media/audio/sounds/sounds_manager_unittest.cc
@@ -0,0 +1,69 @@
+// Copyright 2013 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 <vector>
+
+#include "base/compiler_specific.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "base/strings/string_piece.h"
+#include "media/audio/audio_manager.h"
+#include "media/audio/sounds/audio_stream_handler.h"
+#include "media/audio/sounds/sounds_manager.h"
+#include "media/audio/sounds/test_data.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace media {
+
+class SoundsManagerTest : public testing::Test {
+ public:
+ SoundsManagerTest() {}
+ virtual ~SoundsManagerTest() {}
+
+ virtual void SetUp() OVERRIDE {
+ audio_manager_.reset(AudioManager::CreateForTesting());
+ SoundsManager::Create();
+ }
+
+ virtual void TearDown() OVERRIDE {
+ SoundsManager::Shutdown();
+ audio_manager_.reset();
+ }
+
+ void SetObserverForTesting(AudioStreamHandler::TestObserver* observer) {
+ AudioStreamHandler::SetObserverForTesting(observer);
+ }
+
+ private:
+ scoped_ptr<AudioManager> audio_manager_;
+
+ base::MessageLoop message_loop_;
+};
+
+TEST_F(SoundsManagerTest, Play) {
+ ASSERT_TRUE(SoundsManager::Get());
+
+ base::RunLoop run_loop;
+ TestObserver observer(run_loop.QuitClosure());
+
+ SetObserverForTesting(&observer);
+
+ ASSERT_TRUE(SoundsManager::Get()->Initialize(
+ kTestAudioKey,
+ base::StringPiece(kTestAudioData, arraysize(kTestAudioData))));
+ ASSERT_EQ(41,
+ SoundsManager::Get()->GetDuration(kTestAudioKey).InMicroseconds());
+ ASSERT_TRUE(SoundsManager::Get()->Play(kTestAudioKey));
+ run_loop.Run();
+
+ ASSERT_EQ(1, observer.num_play_requests());
+ ASSERT_EQ(1, observer.num_stop_requests());
+ ASSERT_EQ(4, observer.cursor());
+
+ SetObserverForTesting(NULL);
+}
+
+} // namespace media
diff --git a/chromium/media/audio/sounds/test_data.cc b/chromium/media/audio/sounds/test_data.cc
new file mode 100644
index 00000000000..dc667c9996d
--- /dev/null
+++ b/chromium/media/audio/sounds/test_data.cc
@@ -0,0 +1,34 @@
+// Copyright 2013 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/sounds/test_data.h"
+
+#include "base/logging.h"
+#include "base/message_loop/message_loop.h"
+
+namespace media {
+
+TestObserver::TestObserver(const base::Closure& quit)
+ : loop_(base::MessageLoop::current()),
+ quit_(quit),
+ num_play_requests_(0),
+ num_stop_requests_(0),
+ cursor_(0) {
+ DCHECK(loop_);
+}
+
+TestObserver::~TestObserver() {
+}
+
+void TestObserver::OnPlay() {
+ ++num_play_requests_;
+}
+
+void TestObserver::OnStop(size_t cursor) {
+ ++num_stop_requests_;
+ cursor_ = cursor;
+ loop_->PostTask(FROM_HERE, quit_);
+}
+
+} // namespace media
diff --git a/chromium/media/audio/sounds/test_data.h b/chromium/media/audio/sounds/test_data.h
new file mode 100644
index 00000000000..d7fb11ddb6b
--- /dev/null
+++ b/chromium/media/audio/sounds/test_data.h
@@ -0,0 +1,51 @@
+// Copyright 2013 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_SOUNDS_TEST_UTILS_H_
+#define MEDIA_AUDIO_SOUNDS_TEST_UTILS_H_
+
+#include "base/basictypes.h"
+#include "base/callback.h"
+#include "base/compiler_specific.h"
+#include "media/audio/sounds/audio_stream_handler.h"
+
+namespace base {
+class MessageLoop;
+}
+
+namespace media {
+
+const int kTestAudioKey = 1000;
+
+const char kTestAudioData[] = "RIFF\x26\x00\x00\x00WAVEfmt \x10\x00\x00\x00"
+ "\x01\x00\x02\x00\x80\xbb\x00\x00\x00\x77\x01\x00\x02\x00\x10\x00"
+ "data\x04\x00\x00\x00\x01\x00\x01\x00";
+
+class TestObserver : public AudioStreamHandler::TestObserver {
+ public:
+ TestObserver(const base::Closure& quit);
+ virtual ~TestObserver();
+
+ // AudioStreamHandler::TestObserver implementation:
+ virtual void OnPlay() OVERRIDE;
+ virtual void OnStop(size_t cursor) OVERRIDE;
+
+ int num_play_requests() const { return num_play_requests_; }
+ int num_stop_requests() const { return num_stop_requests_; }
+ int cursor() const { return cursor_; }
+
+ private:
+ base::MessageLoop* loop_;
+ base::Closure quit_;
+
+ int num_play_requests_;
+ int num_stop_requests_;
+ int cursor_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestObserver);
+};
+
+} // namespace media
+
+#endif // MEDIA_AUDIO_SOUNDS_TEST_UTILS_H_
diff --git a/chromium/media/audio/sounds/wav_audio_handler.cc b/chromium/media/audio/sounds/wav_audio_handler.cc
new file mode 100644
index 00000000000..20eab8be437
--- /dev/null
+++ b/chromium/media/audio/sounds/wav_audio_handler.cc
@@ -0,0 +1,141 @@
+// Copyright 2013 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/sounds/wav_audio_handler.h"
+
+#include <algorithm>
+#include <cstring>
+
+#include "base/logging.h"
+#include "base/sys_byteorder.h"
+#include "media/base/audio_bus.h"
+
+namespace {
+
+const char kChunkId[] = "RIFF";
+const char kFormat[] = "WAVE";
+const char kSubchunk1Id[] = "fmt ";
+const char kSubchunk2Id[] = "data";
+
+// The size of the header of a wav file. The header consists of 'RIFF', 4 bytes
+// of total data length, and 'WAVE'.
+const size_t kWavFileHeaderSize = 12;
+
+// The size of a chunk header in wav file format. A chunk header consists of a
+// tag ('fmt ' or 'data') and 4 bytes of chunk length.
+const size_t kChunkHeaderSize = 8;
+
+// The minimum size of 'fmt' chunk.
+const size_t kFmtChunkMinimumSize = 16;
+
+// The offsets of 'fmt' fields.
+const size_t kAudioFormatOffset = 0;
+const size_t kChannelOffset = 2;
+const size_t kSampleRateOffset = 4;
+const size_t kByteRateOffset = 8;
+const size_t kBitsPerSampleOffset = 14;
+
+// Some constants for audio format.
+const int kAudioFormatPCM = 1;
+
+// Reads an integer from |data| with |offset|.
+template<typename T> T ReadInt(const base::StringPiece& data, size_t offset) {
+ CHECK_LE(offset + sizeof(T), data.size());
+ T result;
+ memcpy(&result, data.data() + offset, sizeof(T));
+#if !defined(ARCH_CPU_LITTLE_ENDIAN)
+ result = base::ByteSwap(result);
+#endif
+ return result;
+}
+
+} // namespace
+
+namespace media {
+
+WavAudioHandler::WavAudioHandler(const base::StringPiece& wav_data)
+ : num_channels_(0),
+ sample_rate_(0),
+ byte_rate_(0),
+ bits_per_sample_(0) {
+ CHECK_LE(kWavFileHeaderSize, wav_data.size()) << "wav data is too small";
+ CHECK(wav_data.starts_with(kChunkId) &&
+ memcmp(wav_data.data() + 8, kFormat, 4) == 0)
+ << "incorrect wav header";
+
+ uint32 total_length = std::min(ReadInt<uint32>(wav_data, 4),
+ static_cast<uint32>(wav_data.size()));
+ uint32 offset = kWavFileHeaderSize;
+ while (offset < total_length) {
+ const int length = ParseSubChunk(wav_data.substr(offset));
+ CHECK_LE(0, length) << "can't parse wav sub-chunk";
+ offset += length;
+ }
+}
+
+WavAudioHandler::~WavAudioHandler() {
+}
+
+bool WavAudioHandler::AtEnd(size_t cursor) const {
+ return data_.size() <= cursor;
+}
+
+bool WavAudioHandler::CopyTo(AudioBus* bus,
+ size_t cursor,
+ size_t* bytes_written) const {
+ if (!bus)
+ return false;
+ if (bus->channels() != num_channels_) {
+ LOG(ERROR) << "Number of channel mismatch.";
+ return false;
+ }
+ if (AtEnd(cursor)) {
+ bus->Zero();
+ return true;
+ }
+ const int remaining_frames = (data_.size() - cursor) / bytes_per_frame_;
+ const int frames = std::min(bus->frames(), remaining_frames);
+ bus->FromInterleaved(data_.data() + cursor, frames, bytes_per_sample_);
+ *bytes_written = frames * bytes_per_frame_;
+ bus->ZeroFramesPartial(frames, bus->frames() - frames);
+ return true;
+}
+
+int WavAudioHandler::ParseSubChunk(const base::StringPiece& data) {
+ if (data.size() < kChunkHeaderSize)
+ return data.size();
+ uint32 chunk_length = ReadInt<uint32>(data, 4);
+ if (data.starts_with(kSubchunk1Id)) {
+ if (!ParseFmtChunk(data.substr(kChunkHeaderSize, chunk_length)))
+ return -1;
+ } else if (data.starts_with(kSubchunk2Id)) {
+ if (!ParseDataChunk(data.substr(kChunkHeaderSize, chunk_length)))
+ return -1;
+ } else {
+ LOG(ERROR) << "Unknown data chunk: " << data.substr(0, 4) << ".";
+ }
+ return chunk_length + kChunkHeaderSize;
+}
+
+bool WavAudioHandler::ParseFmtChunk(const base::StringPiece& data) {
+ if (data.size() < kFmtChunkMinimumSize) {
+ LOG(ERROR) << "Data size " << data.size() << " is too short.";
+ return false;
+ }
+ DCHECK_EQ(ReadInt<uint16>(data, kAudioFormatOffset), kAudioFormatPCM);
+ num_channels_ = ReadInt<uint16>(data, kChannelOffset);
+ sample_rate_ = ReadInt<uint32>(data, kSampleRateOffset);
+ byte_rate_ = ReadInt<uint32>(data, kByteRateOffset);
+ bits_per_sample_ = ReadInt<uint16>(data, kBitsPerSampleOffset);
+ bytes_per_sample_ = bits_per_sample_ >> 3;
+ bytes_per_frame_ = num_channels_ * bytes_per_sample_;
+ return true;
+}
+
+bool WavAudioHandler::ParseDataChunk(const base::StringPiece& data) {
+ data_ = data;
+ return true;
+}
+
+} // namespace media
diff --git a/chromium/media/audio/sounds/wav_audio_handler.h b/chromium/media/audio/sounds/wav_audio_handler.h
new file mode 100644
index 00000000000..a2c3e023650
--- /dev/null
+++ b/chromium/media/audio/sounds/wav_audio_handler.h
@@ -0,0 +1,59 @@
+// Copyright 2013 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_SOUNDS_WAV_AUDIO_HANDLER_H_
+#define MEDIA_AUDIO_SOUNDS_WAV_AUDIO_HANDLER_H_
+
+#include "base/strings/string_piece.h"
+#include "media/base/media_export.h"
+
+namespace media {
+
+class AudioBus;
+
+// This class provides the input from wav file format. See
+// https://ccrma.stanford.edu/courses/422/projects/WaveFormat/
+class MEDIA_EXPORT WavAudioHandler {
+ public:
+ explicit WavAudioHandler(const base::StringPiece& wav_data);
+ virtual ~WavAudioHandler();
+
+ // Returns true when cursor points to the end of the track.
+ bool AtEnd(size_t cursor) const;
+
+ // Copies the audio data to |bus| starting from the |cursor| and in
+ // the case of success stores the number of written bytes in
+ // |bytes_written|. |bytes_written| should not be NULL.
+ bool CopyTo(AudioBus* bus, size_t cursor, size_t* bytes_written) const;
+
+ int size() const { return data_.size(); }
+ uint16 num_channels() const { return num_channels_; }
+ uint32 sample_rate() const { return sample_rate_; }
+ uint32 byte_rate() const { return byte_rate_; }
+ uint16 bits_per_sample() const { return bits_per_sample_; }
+
+ private:
+ // Parses a chunk of wav format data. Returns the length of the chunk.
+ int ParseSubChunk(const base::StringPiece& data);
+
+ // Parses the 'fmt' section chunk and stores |params_|.
+ bool ParseFmtChunk(const base::StringPiece& data);
+
+ // Parses the 'data' section chunk and stores |data_|.
+ bool ParseDataChunk(const base::StringPiece& data);
+
+ // Data part of the |wav_data_|.
+ base::StringPiece data_;
+
+ uint16 num_channels_;
+ uint32 sample_rate_;
+ uint32 byte_rate_;
+ uint16 bits_per_sample_;
+ int bytes_per_sample_;
+ int bytes_per_frame_;
+};
+
+} // namespace media
+
+#endif // MEDIA_AUDIO_SOUNDS_WAV_AUDIO_HANDLER_H_
diff --git a/chromium/media/audio/sounds/wav_audio_handler_unittest.cc b/chromium/media/audio/sounds/wav_audio_handler_unittest.cc
new file mode 100644
index 00000000000..a7f8728be35
--- /dev/null
+++ b/chromium/media/audio/sounds/wav_audio_handler_unittest.cc
@@ -0,0 +1,33 @@
+// Copyright 2013 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 "base/basictypes.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/strings/string_piece.h"
+#include "media/audio/sounds/test_data.h"
+#include "media/audio/sounds/wav_audio_handler.h"
+#include "media/base/audio_bus.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace media {
+
+TEST(WavAudioHandlerTest, SampleDataTest) {
+ WavAudioHandler handler(base::StringPiece(kTestAudioData,
+ arraysize(kTestAudioData)));
+ ASSERT_EQ(static_cast<uint16>(2), handler.num_channels());
+ ASSERT_EQ(static_cast<uint16>(16), handler.bits_per_sample());
+ ASSERT_EQ(static_cast<uint32>(48000), handler.sample_rate());
+ ASSERT_EQ(static_cast<uint32>(96000), handler.byte_rate());
+
+ ASSERT_EQ(4, handler.size());
+ scoped_ptr<AudioBus> bus = AudioBus::Create(
+ handler.num_channels(),
+ handler.size() / handler.num_channels());
+ size_t bytes_written;
+ ASSERT_TRUE(handler.CopyTo(bus.get(), 0, &bytes_written));
+ ASSERT_EQ(static_cast<size_t>(handler.size()), bytes_written);
+}
+
+} // namespace media
diff --git a/chromium/media/audio/test_audio_input_controller_factory.cc b/chromium/media/audio/test_audio_input_controller_factory.cc
index d146231a25d..3aeb7773366 100644
--- a/chromium/media/audio/test_audio_input_controller_factory.cc
+++ b/chromium/media/audio/test_audio_input_controller_factory.cc
@@ -57,11 +57,6 @@ AudioInputController* TestAudioInputControllerFactory::Create(
return controller_;
}
-void TestAudioInputControllerFactory::SetDelegateForTests(
- TestAudioInputControllerDelegate* delegate) {
- delegate_ = delegate;
-}
-
void TestAudioInputControllerFactory::OnTestAudioInputControllerDestroyed(
TestAudioInputController* controller) {
DCHECK_EQ(controller_, controller);
diff --git a/chromium/media/audio/test_audio_input_controller_factory.h b/chromium/media/audio/test_audio_input_controller_factory.h
index 4968c013d97..d49302280f2 100644
--- a/chromium/media/audio/test_audio_input_controller_factory.h
+++ b/chromium/media/audio/test_audio_input_controller_factory.h
@@ -69,6 +69,10 @@ class TestAudioInputController : public AudioInputController {
// Ensure that the closure is run on the audio-manager thread.
virtual void Close(const base::Closure& closed_task) OVERRIDE;
+ const AudioParameters& audio_parameters() const {
+ return audio_parameters_;
+ }
+
protected:
virtual ~TestAudioInputController();
@@ -99,7 +103,9 @@ class TestAudioInputControllerFactory : public AudioInputController::Factory {
AudioParameters params,
UserInputMonitor* user_input_monitor) OVERRIDE;
- void SetDelegateForTests(TestAudioInputControllerDelegate* delegate);
+ void set_delegate(TestAudioInputControllerDelegate* delegate) {
+ delegate_ = delegate;
+ }
TestAudioInputController* controller() const { return controller_; }
diff --git a/chromium/media/audio/win/audio_device_listener_win.cc b/chromium/media/audio/win/audio_device_listener_win.cc
index 8734cf2b78f..adbc9a82e4d 100644
--- a/chromium/media/audio/win/audio_device_listener_win.cc
+++ b/chromium/media/audio/win/audio_device_listener_win.cc
@@ -11,7 +11,6 @@
#include "base/system_monitor/system_monitor.h"
#include "base/win/scoped_co_mem.h"
#include "base/win/windows_version.h"
-#include "media/audio/audio_util.h"
#include "media/audio/win/core_audio_util_win.h"
using base::win::ScopedCoMem;
diff --git a/chromium/media/audio/win/audio_low_latency_input_win.cc b/chromium/media/audio/win/audio_low_latency_input_win.cc
index a174ea2ea0d..b16ef130a9f 100644
--- a/chromium/media/audio/win/audio_low_latency_input_win.cc
+++ b/chromium/media/audio/win/audio_low_latency_input_win.cc
@@ -7,7 +7,6 @@
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
#include "base/strings/utf_string_conversions.h"
-#include "media/audio/audio_util.h"
#include "media/audio/win/audio_manager_win.h"
#include "media/audio/win/avrt_wrapper_win.h"
diff --git a/chromium/media/audio/win/audio_low_latency_input_win_unittest.cc b/chromium/media/audio/win/audio_low_latency_input_win_unittest.cc
index 11fad25d3fe..54bd3f71b26 100644
--- a/chromium/media/audio/win/audio_low_latency_input_win_unittest.cc
+++ b/chromium/media/audio/win/audio_low_latency_input_win_unittest.cc
@@ -100,10 +100,9 @@ class WriteToFileAudioSink : public AudioInputStream::AudioInputCallback {
base::FilePath file_path;
EXPECT_TRUE(PathService::Get(base::DIR_EXE, &file_path));
file_path = file_path.AppendASCII(file_name);
- binary_file_ = file_util::OpenFile(file_path, "wb");
+ binary_file_ = base::OpenFile(file_path, "wb");
DLOG_IF(ERROR, !binary_file_) << "Failed to open binary PCM data file.";
- LOG(INFO) << ">> Output file: " << file_path.value()
- << " has been created.";
+ VLOG(0) << ">> Output file: " << file_path.value() << " has been created.";
}
virtual ~WriteToFileAudioSink() {
@@ -121,7 +120,7 @@ class WriteToFileAudioSink : public AudioInputStream::AudioInputCallback {
buffer_.Seek(chunk_size);
bytes_written += chunk_size;
}
- file_util::CloseFile(binary_file_);
+ base::CloseFile(binary_file_);
}
// AudioInputStream::AudioInputCallback implementation.
@@ -265,7 +264,7 @@ class ScopedAudioInputStream {
// Verify that we can retrieve the current hardware/mixing sample rate
// for all available input devices.
TEST(WinAudioInputTest, WASAPIAudioInputStreamHardwareSampleRate) {
- scoped_ptr<AudioManager> audio_manager(AudioManager::Create());
+ scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting());
if (!CanRunAudioTests(audio_manager.get()))
return;
@@ -288,7 +287,7 @@ TEST(WinAudioInputTest, WASAPIAudioInputStreamHardwareSampleRate) {
// Test Create(), Close() calling sequence.
TEST(WinAudioInputTest, WASAPIAudioInputStreamCreateAndClose) {
- scoped_ptr<AudioManager> audio_manager(AudioManager::Create());
+ scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting());
if (!CanRunAudioTests(audio_manager.get()))
return;
ScopedAudioInputStream ais(
@@ -298,7 +297,7 @@ TEST(WinAudioInputTest, WASAPIAudioInputStreamCreateAndClose) {
// Test Open(), Close() calling sequence.
TEST(WinAudioInputTest, WASAPIAudioInputStreamOpenAndClose) {
- scoped_ptr<AudioManager> audio_manager(AudioManager::Create());
+ scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting());
if (!CanRunAudioTests(audio_manager.get()))
return;
ScopedAudioInputStream ais(
@@ -309,7 +308,7 @@ TEST(WinAudioInputTest, WASAPIAudioInputStreamOpenAndClose) {
// Test Open(), Start(), Close() calling sequence.
TEST(WinAudioInputTest, WASAPIAudioInputStreamOpenStartAndClose) {
- scoped_ptr<AudioManager> audio_manager(AudioManager::Create());
+ scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting());
if (!CanRunAudioTests(audio_manager.get()))
return;
ScopedAudioInputStream ais(
@@ -324,7 +323,7 @@ TEST(WinAudioInputTest, WASAPIAudioInputStreamOpenStartAndClose) {
// Test Open(), Start(), Stop(), Close() calling sequence.
TEST(WinAudioInputTest, WASAPIAudioInputStreamOpenStartStopAndClose) {
- scoped_ptr<AudioManager> audio_manager(AudioManager::Create());
+ scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting());
if (!CanRunAudioTests(audio_manager.get()))
return;
ScopedAudioInputStream ais(
@@ -340,7 +339,7 @@ TEST(WinAudioInputTest, WASAPIAudioInputStreamOpenStartStopAndClose) {
// Test some additional calling sequences.
TEST(WinAudioInputTest, WASAPIAudioInputStreamMiscCallingSequences) {
- scoped_ptr<AudioManager> audio_manager(AudioManager::Create());
+ scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting());
if (!CanRunAudioTests(audio_manager.get()))
return;
ScopedAudioInputStream ais(
@@ -372,7 +371,7 @@ TEST(WinAudioInputTest, WASAPIAudioInputStreamMiscCallingSequences) {
}
TEST(WinAudioInputTest, WASAPIAudioInputStreamTestPacketSizes) {
- scoped_ptr<AudioManager> audio_manager(AudioManager::Create());
+ scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting());
if (!CanRunAudioTests(audio_manager.get()))
return;
@@ -454,7 +453,7 @@ TEST(WinAudioInputTest, WASAPIAudioInputStreamTestPacketSizes) {
// Test that we can capture loopback stream.
TEST(WinAudioInputTest, WASAPIAudioInputStreamLoopback) {
- scoped_ptr<AudioManager> audio_manager(AudioManager::Create());
+ scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting());
if (!audio_manager->HasAudioOutputDevices() || !CoreAudioUtil::IsSupported())
return;
@@ -488,7 +487,7 @@ TEST(WinAudioInputTest, WASAPIAudioInputStreamLoopback) {
// with --gtest_also_run_disabled_tests or set the GTEST_ALSO_RUN_DISABLED_TESTS
// environment variable to a value greater than 0.
TEST(WinAudioInputTest, DISABLED_WASAPIAudioInputStreamRecordToFile) {
- scoped_ptr<AudioManager> audio_manager(AudioManager::Create());
+ scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting());
if (!CanRunAudioTests(audio_manager.get()))
return;
@@ -501,13 +500,13 @@ TEST(WinAudioInputTest, DISABLED_WASAPIAudioInputStreamRecordToFile) {
ScopedAudioInputStream ais(aisw.Create());
EXPECT_TRUE(ais->Open());
- LOG(INFO) << ">> Sample rate: " << aisw.sample_rate() << " [Hz]";
+ VLOG(0) << ">> Sample rate: " << aisw.sample_rate() << " [Hz]";
WriteToFileAudioSink file_sink(file_name);
- LOG(INFO) << ">> Speak into the default microphone while recording.";
+ VLOG(0) << ">> Speak into the default microphone while recording.";
ais->Start(&file_sink);
base::PlatformThread::Sleep(TestTimeouts::action_timeout());
ais->Stop();
- LOG(INFO) << ">> Recording has stopped.";
+ VLOG(0) << ">> Recording has stopped.";
ais.Close();
}
diff --git a/chromium/media/audio/win/audio_low_latency_output_win.cc b/chromium/media/audio/win/audio_low_latency_output_win.cc
index c889c03ef2c..a10e67a46cb 100644
--- a/chromium/media/audio/win/audio_low_latency_output_win.cc
+++ b/chromium/media/audio/win/audio_low_latency_output_win.cc
@@ -25,23 +25,6 @@ using base::win::ScopedCoMem;
namespace media {
-typedef uint32 ChannelConfig;
-
-// Retrieves an integer mask which corresponds to the channel layout the
-// audio engine uses for its internal processing/mixing of shared-mode
-// streams. This mask indicates which channels are present in the multi-
-// channel stream. The least significant bit corresponds with the Front Left
-// speaker, the next least significant bit corresponds to the Front Right
-// speaker, and so on, continuing in the order defined in KsMedia.h.
-// See http://msdn.microsoft.com/en-us/library/windows/hardware/ff537083(v=vs.85).aspx
-// for more details.
-static ChannelConfig GetChannelConfig() {
- WAVEFORMATPCMEX format;
- return SUCCEEDED(CoreAudioUtil::GetDefaultSharedModeMixFormat(
- eRender, eConsole, &format)) ?
- static_cast<int>(format.dwChannelMask) : 0;
-}
-
// Compare two sets of audio parameters and return true if they are equal.
// Note that bits_per_sample() is excluded from this comparison since Core
// Audio can deal with most bit depths. As an example, if the native/mixing
@@ -55,40 +38,6 @@ static bool CompareAudioParametersNoBitDepthOrChannels(
a.frames_per_buffer() == b.frames_per_buffer());
}
-// Converts Microsoft's channel configuration to ChannelLayout.
-// This mapping is not perfect but the best we can do given the current
-// ChannelLayout enumerator and the Windows-specific speaker configurations
-// defined in ksmedia.h. Don't assume that the channel ordering in
-// ChannelLayout is exactly the same as the Windows specific configuration.
-// As an example: KSAUDIO_SPEAKER_7POINT1_SURROUND is mapped to
-// CHANNEL_LAYOUT_7_1 but the positions of Back L, Back R and Side L, Side R
-// speakers are different in these two definitions.
-static ChannelLayout ChannelConfigToChannelLayout(ChannelConfig config) {
- switch (config) {
- case KSAUDIO_SPEAKER_DIRECTOUT:
- return CHANNEL_LAYOUT_NONE;
- case KSAUDIO_SPEAKER_MONO:
- return CHANNEL_LAYOUT_MONO;
- case KSAUDIO_SPEAKER_STEREO:
- return CHANNEL_LAYOUT_STEREO;
- case KSAUDIO_SPEAKER_QUAD:
- return CHANNEL_LAYOUT_QUAD;
- case KSAUDIO_SPEAKER_SURROUND:
- return CHANNEL_LAYOUT_4_0;
- case KSAUDIO_SPEAKER_5POINT1:
- return CHANNEL_LAYOUT_5_1_BACK;
- case KSAUDIO_SPEAKER_5POINT1_SURROUND:
- return CHANNEL_LAYOUT_5_1;
- case KSAUDIO_SPEAKER_7POINT1:
- return CHANNEL_LAYOUT_7_1_WIDE;
- case KSAUDIO_SPEAKER_7POINT1_SURROUND:
- return CHANNEL_LAYOUT_7_1;
- default:
- VLOG(1) << "Unsupported channel layout: " << config;
- return CHANNEL_LAYOUT_UNSUPPORTED;
- }
-}
-
// static
AUDCLNT_SHAREMODE WASAPIAudioOutputStream::GetShareMode() {
const CommandLine* cmd_line = CommandLine::ForCurrentProcess();
@@ -98,19 +47,6 @@ AUDCLNT_SHAREMODE WASAPIAudioOutputStream::GetShareMode() {
}
// static
-int WASAPIAudioOutputStream::HardwareChannelCount() {
- WAVEFORMATPCMEX format;
- return SUCCEEDED(CoreAudioUtil::GetDefaultSharedModeMixFormat(
- eRender, eConsole, &format)) ?
- static_cast<int>(format.Format.nChannels) : 0;
-}
-
-// static
-ChannelLayout WASAPIAudioOutputStream::HardwareChannelLayout() {
- return ChannelConfigToChannelLayout(GetChannelConfig());
-}
-
-// static
int WASAPIAudioOutputStream::HardwareSampleRate(const std::string& device_id) {
WAVEFORMATPCMEX format;
ScopedComPtr<IAudioClient> client;
@@ -135,9 +71,12 @@ WASAPIAudioOutputStream::WASAPIAudioOutputStream(AudioManagerWin* manager,
ERole device_role)
: creating_thread_id_(base::PlatformThread::CurrentId()),
manager_(manager),
+ format_(),
opened_(false),
audio_parameters_are_valid_(false),
volume_(1.0),
+ packet_size_frames_(0),
+ packet_size_bytes_(0),
endpoint_buffer_size_frames_(0),
device_id_(device_id),
device_role_(device_role),
@@ -187,18 +126,18 @@ WASAPIAudioOutputStream::WASAPIAudioOutputStream(AudioManagerWin* manager,
// Add the parts which are unique to WAVE_FORMAT_EXTENSIBLE.
format_.Samples.wValidBitsPerSample = params.bits_per_sample();
- format_.dwChannelMask = GetChannelConfig();
+ format_.dwChannelMask = CoreAudioUtil::GetChannelConfig(device_id, eRender);
format_.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
// Store size (in different units) of audio packets which we expect to
// get from the audio endpoint device in each render event.
packet_size_frames_ = params.frames_per_buffer();
packet_size_bytes_ = params.GetBytesPerBuffer();
- packet_size_ms_ = (1000.0 * packet_size_frames_) / params.sample_rate();
VLOG(1) << "Number of bytes per audio frame : " << format->nBlockAlign;
VLOG(1) << "Number of audio frames per packet: " << packet_size_frames_;
VLOG(1) << "Number of bytes per packet : " << packet_size_bytes_;
- VLOG(1) << "Number of milliseconds per packet: " << packet_size_ms_;
+ VLOG(1) << "Number of milliseconds per packet: "
+ << params.GetBufferDuration().InMillisecondsF();
// All events are auto-reset events and non-signaled initially.
@@ -298,6 +237,13 @@ bool WASAPIAudioOutputStream::Open() {
audio_client_ = audio_client;
audio_render_client_ = audio_render_client;
+ hr = audio_client_->GetService(__uuidof(IAudioClock),
+ audio_clock_.ReceiveVoid());
+ if (FAILED(hr)) {
+ LOG(ERROR) << "Failed to get IAudioClock service.";
+ return false;
+ }
+
opened_ = true;
return true;
}
@@ -315,6 +261,17 @@ void WASAPIAudioOutputStream::Start(AudioSourceCallback* callback) {
source_ = callback;
+ // Ensure that the endpoint buffer is prepared with silence.
+ if (share_mode_ == AUDCLNT_SHAREMODE_SHARED) {
+ if (!CoreAudioUtil::FillRenderEndpointBufferWithSilence(
+ audio_client_, audio_render_client_)) {
+ LOG(ERROR) << "Failed to prepare endpoint buffers with silence.";
+ callback->OnError(this);
+ return;
+ }
+ }
+ num_written_frames_ = endpoint_buffer_size_frames_;
+
// Create and start the thread that will drive the rendering by waiting for
// render events.
render_thread_.reset(
@@ -322,26 +279,18 @@ void WASAPIAudioOutputStream::Start(AudioSourceCallback* callback) {
render_thread_->Start();
if (!render_thread_->HasBeenStarted()) {
LOG(ERROR) << "Failed to start WASAPI render thread.";
+ StopThread();
+ callback->OnError(this);
return;
}
- // Ensure that the endpoint buffer is prepared with silence.
- if (share_mode_ == AUDCLNT_SHAREMODE_SHARED) {
- if (!CoreAudioUtil::FillRenderEndpointBufferWithSilence(
- audio_client_, audio_render_client_)) {
- LOG(WARNING) << "Failed to prepare endpoint buffers with silence.";
- return;
- }
- }
- num_written_frames_ = endpoint_buffer_size_frames_;
-
// Start streaming data between the endpoint buffer and the audio engine.
HRESULT hr = audio_client_->Start();
if (FAILED(hr)) {
- SetEvent(stop_render_event_.Get());
- render_thread_->Join();
- render_thread_.reset();
- HandleError(hr);
+ LOG_GETLASTERROR(ERROR)
+ << "Failed to start output streaming: " << std::hex << hr;
+ StopThread();
+ callback->OnError(this);
}
}
@@ -354,27 +303,21 @@ void WASAPIAudioOutputStream::Stop() {
// Stop output audio streaming.
HRESULT hr = audio_client_->Stop();
if (FAILED(hr)) {
- LOG_IF(ERROR, hr != AUDCLNT_E_NOT_INITIALIZED)
+ LOG_GETLASTERROR(ERROR)
<< "Failed to stop output streaming: " << std::hex << hr;
+ source_->OnError(this);
}
- // Wait until the thread completes and perform cleanup.
- SetEvent(stop_render_event_.Get());
- render_thread_->Join();
- render_thread_.reset();
-
- // Ensure that we don't quit the main thread loop immediately next
- // time Start() is called.
- ResetEvent(stop_render_event_.Get());
-
- // Clear source callback, it'll be set again on the next Start() call.
- source_ = NULL;
+ // Make a local copy of |source_| since StopThread() will clear it.
+ AudioSourceCallback* callback = source_;
+ StopThread();
// Flush all pending data and reset the audio clock stream position to 0.
hr = audio_client_->Reset();
if (FAILED(hr)) {
- LOG_IF(ERROR, hr != AUDCLNT_E_NOT_INITIALIZED)
+ LOG_GETLASTERROR(ERROR)
<< "Failed to reset streaming: " << std::hex << hr;
+ callback->OnError(this);
}
// Extra safety check to ensure that the buffers are cleared.
@@ -443,17 +386,9 @@ void WASAPIAudioOutputStream::Run() {
audio_samples_render_event_ };
UINT64 device_frequency = 0;
- // The IAudioClock interface enables us to monitor a stream's data
- // rate and the current position in the stream. Allocate it before we
- // start spinning.
- ScopedComPtr<IAudioClock> audio_clock;
- hr = audio_client_->GetService(__uuidof(IAudioClock),
- audio_clock.ReceiveVoid());
- if (SUCCEEDED(hr)) {
- // The device frequency is the frequency generated by the hardware clock in
- // the audio device. The GetFrequency() method reports a constant frequency.
- hr = audio_clock->GetFrequency(&device_frequency);
- }
+ // The device frequency is the frequency generated by the hardware clock in
+ // the audio device. The GetFrequency() method reports a constant frequency.
+ hr = audio_clock_->GetFrequency(&device_frequency);
error = FAILED(hr);
PLOG_IF(ERROR, error) << "Failed to acquire IAudioClock interface: "
<< std::hex << hr;
@@ -474,7 +409,7 @@ void WASAPIAudioOutputStream::Run() {
break;
case WAIT_OBJECT_0 + 1:
// |audio_samples_render_event_| has been set.
- RenderAudioFromSource(audio_clock, device_frequency);
+ error = !RenderAudioFromSource(device_frequency);
break;
default:
error = true;
@@ -496,8 +431,7 @@ void WASAPIAudioOutputStream::Run() {
}
}
-void WASAPIAudioOutputStream::RenderAudioFromSource(
- IAudioClock* audio_clock, UINT64 device_frequency) {
+bool WASAPIAudioOutputStream::RenderAudioFromSource(UINT64 device_frequency) {
TRACE_EVENT0("audio", "RenderAudioFromSource");
HRESULT hr = S_FALSE;
@@ -518,7 +452,7 @@ void WASAPIAudioOutputStream::RenderAudioFromSource(
if (FAILED(hr)) {
DLOG(ERROR) << "Failed to retrieve amount of available space: "
<< std::hex << hr;
- return;
+ return false;
}
} else {
// While the stream is running, the system alternately sends one
@@ -536,7 +470,7 @@ void WASAPIAudioOutputStream::RenderAudioFromSource(
// Check if there is enough available space to fit the packet size
// specified by the client.
if (num_available_frames < packet_size_frames_)
- return;
+ return true;
DLOG_IF(ERROR, num_available_frames % packet_size_frames_ != 0)
<< "Non-perfect timing detected (num_available_frames="
@@ -559,7 +493,7 @@ void WASAPIAudioOutputStream::RenderAudioFromSource(
if (FAILED(hr)) {
DLOG(ERROR) << "Failed to use rendering audio buffer: "
<< std::hex << hr;
- return;
+ return false;
}
// Derive the audio delay which corresponds to the delay between
@@ -569,7 +503,7 @@ void WASAPIAudioOutputStream::RenderAudioFromSource(
// unit at the render side.
UINT64 position = 0;
int audio_delay_bytes = 0;
- hr = audio_clock->GetPosition(&position, NULL);
+ hr = audio_clock_->GetPosition(&position, NULL);
if (SUCCEEDED(hr)) {
// Stream position of the sample that is currently playing
// through the speaker.
@@ -617,14 +551,8 @@ void WASAPIAudioOutputStream::RenderAudioFromSource(
num_written_frames_ += packet_size_frames_;
}
-}
-void WASAPIAudioOutputStream::HandleError(HRESULT err) {
- CHECK((started() && GetCurrentThreadId() == render_thread_->tid()) ||
- (!started() && GetCurrentThreadId() == creating_thread_id_));
- NOTREACHED() << "Error code: " << std::hex << err;
- if (source_)
- source_->OnError(this);
+ return true;
}
HRESULT WASAPIAudioOutputStream::ExclusiveModeInitialization(
@@ -706,4 +634,22 @@ HRESULT WASAPIAudioOutputStream::ExclusiveModeInitialization(
return hr;
}
+void WASAPIAudioOutputStream::StopThread() {
+ if (render_thread_ ) {
+ if (render_thread_->HasBeenStarted()) {
+ // Wait until the thread completes and perform cleanup.
+ SetEvent(stop_render_event_.Get());
+ render_thread_->Join();
+ }
+
+ render_thread_.reset();
+
+ // Ensure that we don't quit the main thread loop immediately next
+ // time Start() is called.
+ ResetEvent(stop_render_event_.Get());
+ }
+
+ source_ = NULL;
+}
+
} // namespace media
diff --git a/chromium/media/audio/win/audio_low_latency_output_win.h b/chromium/media/audio/win/audio_low_latency_output_win.h
index 7884d8840f7..2baf6f1ac9a 100644
--- a/chromium/media/audio/win/audio_low_latency_output_win.h
+++ b/chromium/media/audio/win/audio_low_latency_output_win.h
@@ -138,17 +138,6 @@ class MEDIA_EXPORT WASAPIAudioOutputStream :
virtual void SetVolume(double volume) OVERRIDE;
virtual void GetVolume(double* volume) OVERRIDE;
- // Retrieves the number of channels the audio engine uses for its internal
- // processing/mixing of shared-mode streams for the default endpoint device.
- static int HardwareChannelCount();
-
- // Retrieves the channel layout the audio engine uses for its internal
- // processing/mixing of shared-mode streams for the default endpoint device.
- // Note that we convert an internal channel layout mask (see ChannelMask())
- // into a Chrome-specific channel layout enumerator in this method, hence
- // the match might not be perfect.
- static ChannelLayout HardwareChannelLayout();
-
// Retrieves the sample rate the audio engine uses for its internal
// processing/mixing of shared-mode streams. To fetch the settings for the
// default device, pass an empty string as the |device_id|.
@@ -168,10 +157,7 @@ class MEDIA_EXPORT WASAPIAudioOutputStream :
// Checks available amount of space in the endpoint buffer and reads
// data from the client to fill up the buffer without causing audio
// glitches.
- void RenderAudioFromSource(IAudioClock* audio_clock, UINT64 device_frequency);
-
- // Issues the OnError() callback to the |sink_|.
- void HandleError(HRESULT err);
+ bool RenderAudioFromSource(UINT64 device_frequency);
// Called when the device will be opened in exclusive mode and use the
// application specified format.
@@ -181,6 +167,11 @@ class MEDIA_EXPORT WASAPIAudioOutputStream :
HANDLE event_handle,
uint32* endpoint_buffer_size);
+ // If |render_thread_| is valid, sets |stop_render_event_| and blocks until
+ // the thread has stopped. |stop_render_event_| is reset after the call.
+ // |source_| is set to NULL.
+ void StopThread();
+
// Contains the thread ID of the creating thread.
base::PlatformThreadId creating_thread_id_;
@@ -215,9 +206,6 @@ class MEDIA_EXPORT WASAPIAudioOutputStream :
// Size in bytes of each audio packet.
size_t packet_size_bytes_;
- // Size in milliseconds of each audio packet.
- float packet_size_ms_;
-
// Length of the audio endpoint buffer.
uint32 endpoint_buffer_size_frames_;
@@ -238,9 +226,6 @@ class MEDIA_EXPORT WASAPIAudioOutputStream :
// Pointer to the client that will deliver audio samples to be played out.
AudioSourceCallback* source_;
- // An IMMDeviceEnumerator interface which represents a device enumerator.
- base::win::ScopedComPtr<IMMDeviceEnumerator> device_enumerator_;
-
// An IAudioClient interface which enables a client to create and initialize
// an audio stream between an audio application and the audio engine.
base::win::ScopedComPtr<IAudioClient> audio_client_;
@@ -259,6 +244,8 @@ class MEDIA_EXPORT WASAPIAudioOutputStream :
// Container for retrieving data from AudioSourceCallback::OnMoreData().
scoped_ptr<AudioBus> audio_bus_;
+ base::win::ScopedComPtr<IAudioClock> audio_clock_;
+
DISALLOW_COPY_AND_ASSIGN(WASAPIAudioOutputStream);
};
diff --git a/chromium/media/audio/win/audio_low_latency_output_win_unittest.cc b/chromium/media/audio/win/audio_low_latency_output_win_unittest.cc
index 1f78facf91d..5fda4b14509 100644
--- a/chromium/media/audio/win/audio_low_latency_output_win_unittest.cc
+++ b/chromium/media/audio/win/audio_low_latency_output_win_unittest.cc
@@ -16,7 +16,6 @@
#include "base/win/scoped_com_initializer.h"
#include "media/audio/audio_io.h"
#include "media/audio/audio_manager.h"
-#include "media/audio/audio_util.h"
#include "media/audio/win/audio_low_latency_output_win.h"
#include "media/audio/win/core_audio_util_win.h"
#include "media/base/decoder_buffer.h"
@@ -98,7 +97,7 @@ class ReadFromFileAudioSource : public AudioOutputStream::AudioSourceCallback {
file_name = file_name.AppendASCII(kDeltaTimeMsFileName);
EXPECT_TRUE(!text_file_);
- text_file_ = file_util::OpenFile(file_name, "wt");
+ text_file_ = base::OpenFile(file_name, "wt");
DLOG_IF(ERROR, !text_file_) << "Failed to open log file.";
// Write the array which contains delta times to a text file.
@@ -108,7 +107,7 @@ class ReadFromFileAudioSource : public AudioOutputStream::AudioSourceCallback {
++elements_written;
}
- file_util::CloseFile(text_file_);
+ base::CloseFile(text_file_);
}
// AudioOutputStream::AudioSourceCallback implementation.
@@ -261,7 +260,7 @@ static AudioOutputStream* CreateDefaultAudioOutputStream(
TEST(WASAPIAudioOutputStreamTest, HardwareSampleRate) {
// Skip this test in exclusive mode since the resulting rate is only utilized
// for shared mode streams.
- scoped_ptr<AudioManager> audio_manager(AudioManager::Create());
+ scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting());
if (!CanRunAudioTests(audio_manager.get()) || ExclusiveModeIsEnabled())
return;
@@ -274,7 +273,7 @@ TEST(WASAPIAudioOutputStreamTest, HardwareSampleRate) {
// Test Create(), Close() calling sequence.
TEST(WASAPIAudioOutputStreamTest, CreateAndClose) {
- scoped_ptr<AudioManager> audio_manager(AudioManager::Create());
+ scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting());
if (!CanRunAudioTests(audio_manager.get()))
return;
AudioOutputStream* aos = CreateDefaultAudioOutputStream(audio_manager.get());
@@ -283,7 +282,7 @@ TEST(WASAPIAudioOutputStreamTest, CreateAndClose) {
// Test Open(), Close() calling sequence.
TEST(WASAPIAudioOutputStreamTest, OpenAndClose) {
- scoped_ptr<AudioManager> audio_manager(AudioManager::Create());
+ scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting());
if (!CanRunAudioTests(audio_manager.get()))
return;
AudioOutputStream* aos = CreateDefaultAudioOutputStream(audio_manager.get());
@@ -293,7 +292,7 @@ TEST(WASAPIAudioOutputStreamTest, OpenAndClose) {
// Test Open(), Start(), Close() calling sequence.
TEST(WASAPIAudioOutputStreamTest, OpenStartAndClose) {
- scoped_ptr<AudioManager> audio_manager(AudioManager::Create());
+ scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting());
if (!CanRunAudioTests(audio_manager.get()))
return;
AudioOutputStream* aos = CreateDefaultAudioOutputStream(audio_manager.get());
@@ -307,7 +306,7 @@ TEST(WASAPIAudioOutputStreamTest, OpenStartAndClose) {
// Test Open(), Start(), Stop(), Close() calling sequence.
TEST(WASAPIAudioOutputStreamTest, OpenStartStopAndClose) {
- scoped_ptr<AudioManager> audio_manager(AudioManager::Create());
+ scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting());
if (!CanRunAudioTests(audio_manager.get()))
return;
AudioOutputStream* aos = CreateDefaultAudioOutputStream(audio_manager.get());
@@ -322,7 +321,7 @@ TEST(WASAPIAudioOutputStreamTest, OpenStartStopAndClose) {
// Test SetVolume(), GetVolume()
TEST(WASAPIAudioOutputStreamTest, Volume) {
- scoped_ptr<AudioManager> audio_manager(AudioManager::Create());
+ scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting());
if (!CanRunAudioTests(audio_manager.get()))
return;
AudioOutputStream* aos = CreateDefaultAudioOutputStream(audio_manager.get());
@@ -359,7 +358,7 @@ TEST(WASAPIAudioOutputStreamTest, Volume) {
// Test some additional calling sequences.
TEST(WASAPIAudioOutputStreamTest, MiscCallingSequences) {
- scoped_ptr<AudioManager> audio_manager(AudioManager::Create());
+ scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting());
if (!CanRunAudioTests(audio_manager.get()))
return;
@@ -399,7 +398,7 @@ TEST(WASAPIAudioOutputStreamTest, MiscCallingSequences) {
// Use preferred packet size and verify that rendering starts.
TEST(WASAPIAudioOutputStreamTest, ValidPacketSize) {
- scoped_ptr<AudioManager> audio_manager(AudioManager::Create());
+ scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting());
if (!CanRunAudioTests(audio_manager.get()))
return;
@@ -435,7 +434,7 @@ TEST(WASAPIAudioOutputStreamTest, ValidPacketSize) {
// Use a non-preferred packet size and verify that Open() fails.
TEST(WASAPIAudioOutputStreamTest, InvalidPacketSize) {
- scoped_ptr<AudioManager> audio_manager(AudioManager::Create());
+ scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting());
if (!CanRunAudioTests(audio_manager.get()))
return;
@@ -462,7 +461,7 @@ TEST(WASAPIAudioOutputStreamTest, InvalidPacketSize) {
// environment variable to a value greater than 0.
// The test files are approximately 20 seconds long.
TEST(WASAPIAudioOutputStreamTest, DISABLED_ReadFromStereoFile) {
- scoped_ptr<AudioManager> audio_manager(AudioManager::Create());
+ scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting());
if (!CanRunAudioTests(audio_manager.get()))
return;
@@ -484,13 +483,13 @@ TEST(WASAPIAudioOutputStreamTest, DISABLED_ReadFromStereoFile) {
}
ReadFromFileAudioSource file_source(file_name);
- LOG(INFO) << "File name : " << file_name.c_str();
- LOG(INFO) << "Sample rate : " << aosw.sample_rate();
- LOG(INFO) << "Bits per sample: " << aosw.bits_per_sample();
- LOG(INFO) << "#channels : " << aosw.channels();
- LOG(INFO) << "File size : " << file_source.file_size();
- LOG(INFO) << "#file segments : " << kNumFileSegments;
- LOG(INFO) << ">> Listen to the stereo file while playing...";
+ VLOG(0) << "File name : " << file_name.c_str();
+ VLOG(0) << "Sample rate : " << aosw.sample_rate();
+ VLOG(0) << "Bits per sample: " << aosw.bits_per_sample();
+ VLOG(0) << "#channels : " << aosw.channels();
+ VLOG(0) << "File size : " << file_source.file_size();
+ VLOG(0) << "#file segments : " << kNumFileSegments;
+ VLOG(0) << ">> Listen to the stereo file while playing...";
for (int i = 0; i < kNumFileSegments; i++) {
// Each segment will start with a short (~20ms) block of zeros, hence
@@ -503,7 +502,7 @@ TEST(WASAPIAudioOutputStreamTest, DISABLED_ReadFromStereoFile) {
aos->Stop();
}
- LOG(INFO) << ">> Stereo file playout has stopped.";
+ VLOG(0) << ">> Stereo file playout has stopped.";
aos->Close();
}
@@ -515,7 +514,7 @@ TEST(WASAPIAudioOutputStreamTest, ExclusiveModeBufferSizesAt48kHz) {
if (!ExclusiveModeIsEnabled())
return;
- scoped_ptr<AudioManager> audio_manager(AudioManager::Create());
+ scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting());
if (!CanRunAudioTests(audio_manager.get()))
return;
@@ -566,7 +565,7 @@ TEST(WASAPIAudioOutputStreamTest, ExclusiveModeBufferSizesAt44kHz) {
if (!ExclusiveModeIsEnabled())
return;
- scoped_ptr<AudioManager> audio_manager(AudioManager::Create());
+ scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting());
if (!CanRunAudioTests(audio_manager.get()))
return;
@@ -624,7 +623,7 @@ TEST(WASAPIAudioOutputStreamTest, ExclusiveModeMinBufferSizeAt48kHz) {
if (!ExclusiveModeIsEnabled())
return;
- scoped_ptr<AudioManager> audio_manager(AudioManager::Create());
+ scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting());
if (!CanRunAudioTests(audio_manager.get()))
return;
@@ -665,7 +664,7 @@ TEST(WASAPIAudioOutputStreamTest, ExclusiveModeMinBufferSizeAt44kHz) {
if (!ExclusiveModeIsEnabled())
return;
- scoped_ptr<AudioManager> audio_manager(AudioManager::Create());
+ scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting());
if (!CanRunAudioTests(audio_manager.get()))
return;
diff --git a/chromium/media/audio/win/audio_manager_win.cc b/chromium/media/audio/win/audio_manager_win.cc
index 0352e6677d2..242813a8c65 100644
--- a/chromium/media/audio/win/audio_manager_win.cc
+++ b/chromium/media/audio/win/audio_manager_win.cc
@@ -20,8 +20,8 @@
#include "base/process/launch.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
+#include "base/win/windows_version.h"
#include "media/audio/audio_parameters.h"
-#include "media/audio/audio_util.h"
#include "media/audio/win/audio_device_listener_win.h"
#include "media/audio/win/audio_low_latency_input_win.h"
#include "media/audio/win/audio_low_latency_output_win.h"
@@ -73,8 +73,8 @@ static int GetVersionPartAsInt(DWORDLONG num) {
// Returns a string containing the given device's description and installed
// driver version.
-static string16 GetDeviceAndDriverInfo(HDEVINFO device_info,
- SP_DEVINFO_DATA* device_data) {
+static base::string16 GetDeviceAndDriverInfo(HDEVINFO device_info,
+ SP_DEVINFO_DATA* device_data) {
// Save the old install params setting and set a flag for the
// SetupDiBuildDriverInfoList below to return only the installed drivers.
SP_DEVINSTALL_PARAMS old_device_install_params;
@@ -88,13 +88,13 @@ static string16 GetDeviceAndDriverInfo(HDEVINFO device_info,
SP_DRVINFO_DATA driver_data;
driver_data.cbSize = sizeof(driver_data);
- string16 device_and_driver_info;
+ base::string16 device_and_driver_info;
if (SetupDiBuildDriverInfoList(device_info, device_data,
SPDIT_COMPATDRIVER)) {
if (SetupDiEnumDriverInfo(device_info, device_data, SPDIT_COMPATDRIVER, 0,
&driver_data)) {
DWORDLONG version = driver_data.DriverVersion;
- device_and_driver_info = string16(driver_data.Description) + L" v" +
+ device_and_driver_info = base::string16(driver_data.Description) + L" v" +
base::IntToString16(GetVersionPartAsInt((version >> 48))) + L"." +
base::IntToString16(GetVersionPartAsInt((version >> 32))) + L"." +
base::IntToString16(GetVersionPartAsInt((version >> 16))) + L"." +
@@ -109,7 +109,26 @@ static string16 GetDeviceAndDriverInfo(HDEVINFO device_info,
return device_and_driver_info;
}
-AudioManagerWin::AudioManagerWin() {
+static int NumberOfWaveOutBuffers() {
+ // Use the user provided buffer count if provided.
+ int buffers = 0;
+ std::string buffers_str(CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
+ switches::kWaveOutBuffers));
+ if (base::StringToInt(buffers_str, &buffers) && buffers > 0) {
+ return buffers;
+ }
+
+ // Use 4 buffers for Vista, 3 for everyone else:
+ // - The entire Windows audio stack was rewritten for Windows Vista and wave
+ // out performance was degraded compared to XP.
+ // - The regression was fixed in Windows 7 and most configurations will work
+ // with 2, but some (e.g., some Sound Blasters) still need 3.
+ // - Some XP configurations (even multi-processor ones) also need 3.
+ return (base::win::GetVersion() == base::win::VERSION_VISTA) ? 4 : 3;
+}
+
+AudioManagerWin::AudioManagerWin(AudioLogFactory* audio_log_factory)
+ : AudioManagerBase(audio_log_factory) {
if (!CoreAudioUtil::IsSupported()) {
// Use the Wave API for device enumeration if XP or lower.
enumeration_type_ = kWaveEnumeration;
@@ -157,7 +176,7 @@ void AudioManagerWin::DestroyDeviceListener() {
output_device_listener_.reset();
}
-string16 AudioManagerWin::GetAudioInputDeviceModel() {
+base::string16 AudioManagerWin::GetAudioInputDeviceModel() {
// Get the default audio capture device and its device interface name.
DWORD device_id = 0;
waveInMessage(reinterpret_cast<HWAVEIN>(WAVE_MAPPER),
@@ -167,13 +186,13 @@ string16 AudioManagerWin::GetAudioInputDeviceModel() {
waveInMessage(reinterpret_cast<HWAVEIN>(device_id),
DRV_QUERYDEVICEINTERFACESIZE,
reinterpret_cast<DWORD_PTR>(&device_interface_name_size), 0);
- size_t bytes_in_char16 = sizeof(string16::value_type);
+ size_t bytes_in_char16 = sizeof(base::string16::value_type);
DCHECK_EQ(0u, device_interface_name_size % bytes_in_char16);
if (device_interface_name_size <= bytes_in_char16)
- return string16(); // No audio capture device.
+ return base::string16(); // No audio capture device.
- string16 device_interface_name;
- string16::value_type* name_ptr = WriteInto(&device_interface_name,
+ base::string16 device_interface_name;
+ base::string16::value_type* name_ptr = WriteInto(&device_interface_name,
device_interface_name_size / bytes_in_char16);
waveInMessage(reinterpret_cast<HWAVEIN>(device_id),
DRV_QUERYDEVICEINTERFACE,
@@ -185,7 +204,7 @@ string16 AudioManagerWin::GetAudioInputDeviceModel() {
HDEVINFO device_info = SetupDiGetClassDevs(
&AM_KSCATEGORY_AUDIO, 0, 0, DIGCF_DEVICEINTERFACE | DIGCF_PRESENT);
if (device_info == INVALID_HANDLE_VALUE)
- return string16();
+ return base::string16();
DWORD interface_index = 0;
SP_DEVICE_INTERFACE_DATA interface_data;
@@ -210,7 +229,7 @@ string16 AudioManagerWin::GetAudioInputDeviceModel() {
interface_detail,
interface_detail_size, NULL,
&device_data))
- return string16();
+ return base::string16();
bool device_found = (device_interface_name == interface_detail->DevicePath);
@@ -218,7 +237,7 @@ string16 AudioManagerWin::GetAudioInputDeviceModel() {
return GetDeviceAndDriverInfo(device_info, &device_data);
}
- return string16();
+ return base::string16();
}
void AudioManagerWin::ShowAudioInputSettings() {
@@ -337,7 +356,8 @@ AudioOutputStream* AudioManagerWin::MakeLowLatencyOutputStream(
if (!CoreAudioUtil::IsSupported()) {
// Fall back to Windows Wave implementation on Windows XP or lower.
- DLOG_IF(ERROR, !device_id.empty())
+ DLOG_IF(ERROR, !device_id.empty() &&
+ device_id != AudioManagerBase::kDefaultDeviceId)
<< "Opening by device id not supported by PCMWaveOutAudioOutputStream";
DVLOG(1) << "Using WaveOut since WASAPI requires at least Vista.";
return new PCMWaveOutAudioOutputStream(
@@ -347,12 +367,19 @@ AudioOutputStream* AudioManagerWin::MakeLowLatencyOutputStream(
// TODO(rtoy): support more than stereo input.
if (params.input_channels() > 0) {
DVLOG(1) << "WASAPIUnifiedStream is created.";
- DLOG_IF(ERROR, !device_id.empty())
+ DLOG_IF(ERROR, !device_id.empty() &&
+ device_id != AudioManagerBase::kDefaultDeviceId)
<< "Opening by device id not supported by WASAPIUnifiedStream";
return new WASAPIUnifiedStream(this, params, input_device_id);
}
- return new WASAPIAudioOutputStream(this, device_id, params, eConsole);
+ // Pass an empty string to indicate that we want the default device
+ // since we consistently only check for an empty string in
+ // WASAPIAudioOutputStream.
+ return new WASAPIAudioOutputStream(this,
+ device_id == AudioManagerBase::kDefaultDeviceId ?
+ std::string() : device_id,
+ params, eConsole);
}
// Factory for the implementations of AudioInputStream for AUDIO_PCM_LINEAR
@@ -429,21 +456,25 @@ AudioParameters AudioManagerWin::GetPreferredOutputStreamParameters(
}
if (input_params.IsValid()) {
+ // If the user has enabled checking supported channel layouts or we don't
+ // have a valid channel layout yet, try to use the input layout. See bugs
+ // http://crbug.com/259165 and http://crbug.com/311906 for more details.
if (core_audio_supported &&
- cmd_line->HasSwitch(switches::kTrySupportedChannelLayouts)) {
+ (cmd_line->HasSwitch(switches::kTrySupportedChannelLayouts) ||
+ channel_layout == CHANNEL_LAYOUT_UNSUPPORTED)) {
// Check if it is possible to open up at the specified input channel
// layout but avoid checking if the specified layout is the same as the
// hardware (preferred) layout. We do this extra check to avoid the
// CoreAudioUtil::IsChannelLayoutSupported() overhead in most cases.
if (input_params.channel_layout() != channel_layout) {
- // TODO(henrika): Use |output_device_id| here.
- // Internally, IsChannelLayoutSupported does many of the operations
- // that have already been done such as opening up a client and fetching
- // the WAVEFORMATPCMEX format. Ideally we should only do that once and
- // do it for the requested device. Then here, we can check the layout
- // from the data we already hold.
+ // TODO(henrika): Internally, IsChannelLayoutSupported does many of the
+ // operations that have already been done such as opening up a client
+ // and fetching the WAVEFORMATPCMEX format. Ideally we should only do
+ // that once. Then here, we can check the layout from the data we
+ // already hold.
if (CoreAudioUtil::IsChannelLayoutSupported(
- eRender, eConsole, input_params.channel_layout())) {
+ output_device_id, eRender, eConsole,
+ input_params.channel_layout())) {
// Open up using the same channel layout as the source if it is
// supported by the hardware.
channel_layout = input_params.channel_layout();
@@ -472,7 +503,7 @@ AudioParameters AudioManagerWin::GetPreferredOutputStreamParameters(
return AudioParameters(
AudioParameters::AUDIO_PCM_LOW_LATENCY, channel_layout, input_channels,
- sample_rate, bits_per_sample, buffer_size);
+ sample_rate, bits_per_sample, buffer_size, AudioParameters::NO_EFFECTS);
}
AudioInputStream* AudioManagerWin::CreatePCMWaveInAudioInputStream(
@@ -494,8 +525,8 @@ AudioInputStream* AudioManagerWin::CreatePCMWaveInAudioInputStream(
}
/// static
-AudioManager* CreateAudioManager() {
- return new AudioManagerWin();
+AudioManager* CreateAudioManager(AudioLogFactory* audio_log_factory) {
+ return new AudioManagerWin(audio_log_factory);
}
} // namespace media
diff --git a/chromium/media/audio/win/audio_manager_win.h b/chromium/media/audio/win/audio_manager_win.h
index 86e22badc5f..01044da40a0 100644
--- a/chromium/media/audio/win/audio_manager_win.h
+++ b/chromium/media/audio/win/audio_manager_win.h
@@ -18,12 +18,12 @@ class AudioDeviceListenerWin;
// the AudioManager class.
class MEDIA_EXPORT AudioManagerWin : public AudioManagerBase {
public:
- AudioManagerWin();
+ AudioManagerWin(AudioLogFactory* audio_log_factory);
// Implementation of AudioManager.
virtual bool HasAudioOutputDevices() OVERRIDE;
virtual bool HasAudioInputDevices() OVERRIDE;
- virtual string16 GetAudioInputDeviceModel() OVERRIDE;
+ virtual base::string16 GetAudioInputDeviceModel() OVERRIDE;
virtual void ShowAudioInputSettings() OVERRIDE;
virtual void GetAudioInputDeviceNames(
AudioDeviceNames* device_names) OVERRIDE;
diff --git a/chromium/media/audio/win/audio_output_win_unittest.cc b/chromium/media/audio/win/audio_output_win_unittest.cc
index 7ce146b0ab4..2b8036d52a2 100644
--- a/chromium/media/audio/win/audio_output_win_unittest.cc
+++ b/chromium/media/audio/win/audio_output_win_unittest.cc
@@ -14,7 +14,6 @@
#include "base/win/windows_version.h"
#include "media/base/limits.h"
#include "media/audio/audio_io.h"
-#include "media/audio/audio_util.h"
#include "media/audio/audio_manager.h"
#include "media/audio/simple_sources.h"
#include "testing/gmock/include/gmock/gmock.h"
@@ -176,7 +175,7 @@ class ReadOnlyMappedFile {
// Test that can it be created and closed.
TEST(WinAudioTest, PCMWaveStreamGetAndClose) {
- scoped_ptr<AudioManager> audio_man(AudioManager::Create());
+ scoped_ptr<AudioManager> audio_man(AudioManager::CreateForTesting());
if (!audio_man->HasAudioOutputDevices()) {
LOG(WARNING) << "No output device detected.";
return;
@@ -192,7 +191,7 @@ TEST(WinAudioTest, PCMWaveStreamGetAndClose) {
// Test that can it be cannot be created with invalid parameters.
TEST(WinAudioTest, SanityOnMakeParams) {
- scoped_ptr<AudioManager> audio_man(AudioManager::Create());
+ scoped_ptr<AudioManager> audio_man(AudioManager::CreateForTesting());
if (!audio_man->HasAudioOutputDevices()) {
LOG(WARNING) << "No output device detected.";
return;
@@ -228,7 +227,7 @@ TEST(WinAudioTest, SanityOnMakeParams) {
// Test that it can be opened and closed.
TEST(WinAudioTest, PCMWaveStreamOpenAndClose) {
- scoped_ptr<AudioManager> audio_man(AudioManager::Create());
+ scoped_ptr<AudioManager> audio_man(AudioManager::CreateForTesting());
if (!audio_man->HasAudioOutputDevices()) {
LOG(WARNING) << "No output device detected.";
return;
@@ -245,7 +244,7 @@ TEST(WinAudioTest, PCMWaveStreamOpenAndClose) {
// Test that it has a maximum packet size.
TEST(WinAudioTest, PCMWaveStreamOpenLimit) {
- scoped_ptr<AudioManager> audio_man(AudioManager::Create());
+ scoped_ptr<AudioManager> audio_man(AudioManager::CreateForTesting());
if (!audio_man->HasAudioOutputDevices()) {
LOG(WARNING) << "No output device detected.";
return;
@@ -264,7 +263,7 @@ TEST(WinAudioTest, PCMWaveStreamOpenLimit) {
// time. The actual EXPECT_GT are mostly meaningless and the real test is that
// the test completes in reasonable time.
TEST(WinAudioTest, PCMWaveSlowSource) {
- scoped_ptr<AudioManager> audio_man(AudioManager::Create());
+ scoped_ptr<AudioManager> audio_man(AudioManager::CreateForTesting());
if (!audio_man->HasAudioOutputDevices()) {
LOG(WARNING) << "No output device detected.";
return;
@@ -292,7 +291,7 @@ TEST(WinAudioTest, PCMWaveSlowSource) {
// gets paused. This test is best when run over RDP with audio enabled. See
// bug 19276 for more details.
TEST(WinAudioTest, PCMWaveStreamPlaySlowLoop) {
- scoped_ptr<AudioManager> audio_man(AudioManager::Create());
+ scoped_ptr<AudioManager> audio_man(AudioManager::CreateForTesting());
if (!audio_man->HasAudioOutputDevices()) {
LOG(WARNING) << "No output device detected.";
return;
@@ -323,7 +322,7 @@ TEST(WinAudioTest, PCMWaveStreamPlaySlowLoop) {
// device at 44.1K s/sec. Parameters have been chosen carefully so you should
// not hear pops or noises while the sound is playing.
TEST(WinAudioTest, PCMWaveStreamPlay200HzTone44Kss) {
- scoped_ptr<AudioManager> audio_man(AudioManager::Create());
+ scoped_ptr<AudioManager> audio_man(AudioManager::CreateForTesting());
if (!audio_man->HasAudioOutputDevices()) {
LOG(WARNING) << "No output device detected.";
return;
@@ -351,7 +350,7 @@ TEST(WinAudioTest, PCMWaveStreamPlay200HzTone44Kss) {
// not hear pops or noises while the sound is playing. The audio also should
// sound with a lower volume than PCMWaveStreamPlay200HzTone44Kss.
TEST(WinAudioTest, PCMWaveStreamPlay200HzTone22Kss) {
- scoped_ptr<AudioManager> audio_man(AudioManager::Create());
+ scoped_ptr<AudioManager> audio_man(AudioManager::CreateForTesting());
if (!audio_man->HasAudioOutputDevices()) {
LOG(WARNING) << "No output device detected.";
return;
@@ -386,7 +385,7 @@ TEST(WinAudioTest, PCMWaveStreamPlay200HzTone22Kss) {
// try hard to generate situation where the two threads are accessing the
// object roughly at the same time.
TEST(WinAudioTest, PushSourceFile16KHz) {
- scoped_ptr<AudioManager> audio_man(AudioManager::Create());
+ scoped_ptr<AudioManager> audio_man(AudioManager::CreateForTesting());
if (!audio_man->HasAudioOutputDevices()) {
LOG(WARNING) << "No output device detected.";
return;
@@ -429,7 +428,7 @@ TEST(WinAudioTest, PushSourceFile16KHz) {
// stopped. You will here two .5 seconds wave signal separated by 0.5 seconds
// of silence.
TEST(WinAudioTest, PCMWaveStreamPlayTwice200HzTone44Kss) {
- scoped_ptr<AudioManager> audio_man(AudioManager::Create());
+ scoped_ptr<AudioManager> audio_man(AudioManager::CreateForTesting());
if (!audio_man->HasAudioOutputDevices()) {
LOG(WARNING) << "No output device detected.";
return;
@@ -466,7 +465,7 @@ TEST(WinAudioTest, PCMWaveStreamPlayTwice200HzTone44Kss) {
// higher and Wave is used for XP and lower. It is possible to utilize a
// smaller buffer size for WASAPI than for Wave.
TEST(WinAudioTest, PCMWaveStreamPlay200HzToneLowLatency) {
- scoped_ptr<AudioManager> audio_man(AudioManager::Create());
+ scoped_ptr<AudioManager> audio_man(AudioManager::CreateForTesting());
if (!audio_man->HasAudioOutputDevices()) {
LOG(WARNING) << "No output device detected.";
return;
@@ -510,7 +509,7 @@ TEST(WinAudioTest, PCMWaveStreamPlay200HzToneLowLatency) {
// Check that the pending bytes value is correct what the stream starts.
TEST(WinAudioTest, PCMWaveStreamPendingBytes) {
- scoped_ptr<AudioManager> audio_man(AudioManager::Create());
+ scoped_ptr<AudioManager> audio_man(AudioManager::CreateForTesting());
if (!audio_man->HasAudioOutputDevices()) {
LOG(WARNING) << "No output device detected.";
return;
@@ -539,28 +538,22 @@ TEST(WinAudioTest, PCMWaveStreamPendingBytes) {
EXPECT_CALL(source, OnMoreData(NotNull(),
Field(&AudioBuffersState::pending_bytes, 0)))
.WillOnce(Invoke(MockAudioSource::ClearData));
- switch (NumberOfWaveOutBuffers()) {
- case 2:
- break; // Calls are the same as at end of 3-buffer scheme.
- case 3:
- EXPECT_CALL(source, OnMoreData(NotNull(),
- Field(&AudioBuffersState::pending_bytes,
- bytes_100_ms)))
- .WillOnce(Invoke(MockAudioSource::ClearData));
- EXPECT_CALL(source, OnMoreData(NotNull(),
- Field(&AudioBuffersState::pending_bytes,
- 2 * bytes_100_ms)))
- .WillOnce(Invoke(MockAudioSource::ClearData));
- EXPECT_CALL(source, OnMoreData(NotNull(),
- Field(&AudioBuffersState::pending_bytes,
- 2 * bytes_100_ms)))
- .Times(AnyNumber())
- .WillRepeatedly(Return(0));
- break;
- default:
- ASSERT_TRUE(false)
- << "Unexpected number of buffers: " << NumberOfWaveOutBuffers();
- }
+
+ // Note: If AudioManagerWin::NumberOfWaveOutBuffers() ever changes, or if this
+ // test is run on Vista, these expectations will fail.
+ EXPECT_CALL(source, OnMoreData(NotNull(),
+ Field(&AudioBuffersState::pending_bytes,
+ bytes_100_ms)))
+ .WillOnce(Invoke(MockAudioSource::ClearData));
+ EXPECT_CALL(source, OnMoreData(NotNull(),
+ Field(&AudioBuffersState::pending_bytes,
+ 2 * bytes_100_ms)))
+ .WillOnce(Invoke(MockAudioSource::ClearData));
+ EXPECT_CALL(source, OnMoreData(NotNull(),
+ Field(&AudioBuffersState::pending_bytes,
+ 2 * bytes_100_ms)))
+ .Times(AnyNumber())
+ .WillRepeatedly(Return(0));
EXPECT_CALL(source, OnMoreData(NotNull(),
Field(&AudioBuffersState::pending_bytes,
bytes_100_ms)))
@@ -667,7 +660,7 @@ DWORD __stdcall SyncSocketThread(void* context) {
// related to the two different audio-layers for AUDIO_PCM_LOW_LATENCY.
// In this test you should hear a continuous 200Hz tone for 2 seconds.
TEST(WinAudioTest, SyncSocketBasic) {
- scoped_ptr<AudioManager> audio_man(AudioManager::Create());
+ scoped_ptr<AudioManager> audio_man(AudioManager::CreateForTesting());
if (!audio_man->HasAudioOutputDevices()) {
LOG(WARNING) << "No output device detected.";
return;
diff --git a/chromium/media/audio/win/audio_unified_win.cc b/chromium/media/audio/win/audio_unified_win.cc
index 5c1594ef8f8..901c8b897fa 100644
--- a/chromium/media/audio/win/audio_unified_win.cc
+++ b/chromium/media/audio/win/audio_unified_win.cc
@@ -51,23 +51,6 @@ static const char kUnifiedAudioDebugFileName[] = "unified_win_debug.txt";
static const char kUnifiedAudioParamsFileName[] = "unified_win_params.txt";
#endif
-typedef uint32 ChannelConfig;
-
-// Retrieves an integer mask which corresponds to the channel layout the
-// audio engine uses for its internal processing/mixing of shared-mode
-// streams. This mask indicates which channels are present in the multi-
-// channel stream. The least significant bit corresponds with the Front Left
-// speaker, the next least significant bit corresponds to the Front Right
-// speaker, and so on, continuing in the order defined in KsMedia.h.
-// See http://msdn.microsoft.com/en-us/library/windows/hardware/ff537083(v=vs.85).aspx
-// for more details.
-static ChannelConfig GetChannelConfig(EDataFlow data_flow) {
- WAVEFORMATPCMEX format;
- return SUCCEEDED(media::CoreAudioUtil::GetDefaultSharedModeMixFormat(
- data_flow, eConsole, &format)) ?
- static_cast<int>(format.dwChannelMask) : 0;
-}
-
// Use the acquired IAudioClock interface to derive a time stamp of the audio
// sample which is currently playing through the speakers.
static double SpeakerStreamPosInMilliseconds(IAudioClock* clock) {
@@ -178,7 +161,7 @@ WASAPIUnifiedStream::~WASAPIUnifiedStream() {
base::FilePath data_file_name;
PathService::Get(base::DIR_EXE, &data_file_name);
data_file_name = data_file_name.AppendASCII(kUnifiedAudioDebugFileName);
- data_file_ = file_util::OpenFile(data_file_name, "wt");
+ data_file_ = base::OpenFile(data_file_name, "wt");
DVLOG(1) << ">> Output file " << data_file_name.value() << " is created.";
size_t n = 0;
@@ -192,16 +175,16 @@ WASAPIUnifiedStream::~WASAPIUnifiedStream() {
fifo_rate_comps_[n]);
++n;
}
- file_util::CloseFile(data_file_);
+ base::CloseFile(data_file_);
base::FilePath param_file_name;
PathService::Get(base::DIR_EXE, &param_file_name);
param_file_name = param_file_name.AppendASCII(kUnifiedAudioParamsFileName);
- param_file_ = file_util::OpenFile(param_file_name, "wt");
+ param_file_ = base::OpenFile(param_file_name, "wt");
DVLOG(1) << ">> Output file " << param_file_name.value() << " is created.";
fprintf(param_file_, "%d %d\n", input_params_[0], input_params_[1]);
fprintf(param_file_, "%d %d\n", output_params_[0], output_params_[1]);
- file_util::CloseFile(param_file_);
+ base::CloseFile(param_file_);
#endif
}
@@ -575,8 +558,9 @@ void WASAPIUnifiedStream::SetIOFormats(const AudioParameters& input_params,
// Add the parts which are unique to WAVE_FORMAT_EXTENSIBLE.
// Note that we always open up using the native channel layout.
(*xformat).Samples.wValidBitsPerSample = format->wBitsPerSample;
- (*xformat).dwChannelMask = (n == 0) ?
- GetChannelConfig(eCapture) : GetChannelConfig(eRender);
+ (*xformat).dwChannelMask =
+ CoreAudioUtil::GetChannelConfig(
+ std::string(), n == 0 ? eCapture : eRender);
(*xformat).SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
}
diff --git a/chromium/media/audio/win/audio_unified_win_unittest.cc b/chromium/media/audio/win/audio_unified_win_unittest.cc
index 011c36348b5..15573aec76a 100644
--- a/chromium/media/audio/win/audio_unified_win_unittest.cc
+++ b/chromium/media/audio/win/audio_unified_win_unittest.cc
@@ -12,7 +12,6 @@
#include "base/win/scoped_com_initializer.h"
#include "media/audio/audio_io.h"
#include "media/audio/audio_manager.h"
-#include "media/audio/audio_util.h"
#include "media/audio/win/audio_unified_win.h"
#include "media/audio/win/core_audio_util_win.h"
#include "media/base/channel_mixer.h"
@@ -75,9 +74,9 @@ class UnifiedSourceCallback : public AudioOutputStream::AudioSourceCallback {
file_name = file_name.AppendASCII(kDeltaTimeMsFileName);
EXPECT_TRUE(!text_file_);
- text_file_ = file_util::OpenFile(file_name, "wt");
+ text_file_ = base::OpenFile(file_name, "wt");
DLOG_IF(ERROR, !text_file_) << "Failed to open log file.";
- LOG(INFO) << ">> Output file " << file_name.value() << " has been created.";
+ VLOG(0) << ">> Output file " << file_name.value() << " has been created.";
// Write the array which contains delta times to a text file.
size_t elements_written = 0;
@@ -85,7 +84,7 @@ class UnifiedSourceCallback : public AudioOutputStream::AudioSourceCallback {
fprintf(text_file_, "%d\n", delta_times_[elements_written]);
++elements_written;
}
- file_util::CloseFile(text_file_);
+ base::CloseFile(text_file_);
}
virtual int OnMoreData(AudioBus* dest,
@@ -264,7 +263,7 @@ static WASAPIUnifiedStream* CreateDefaultUnifiedStream(
// Test Open(), Close() calling sequence.
TEST(WASAPIUnifiedStreamTest, OpenAndClose) {
- scoped_ptr<AudioManager> audio_manager(AudioManager::Create());
+ scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting());
if (!CanRunUnifiedAudioTests(audio_manager.get()))
return;
@@ -275,7 +274,7 @@ TEST(WASAPIUnifiedStreamTest, OpenAndClose) {
// Test Open(), Close() calling sequence for all available capture devices.
TEST(WASAPIUnifiedStreamTest, OpenAndCloseForAllInputDevices) {
- scoped_ptr<AudioManager> audio_manager(AudioManager::Create());
+ scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting());
if (!CanRunUnifiedAudioTests(audio_manager.get()))
return;
@@ -292,7 +291,7 @@ TEST(WASAPIUnifiedStreamTest, OpenAndCloseForAllInputDevices) {
// Test Open(), Start(), Close() calling sequence.
TEST(WASAPIUnifiedStreamTest, OpenStartAndClose) {
- scoped_ptr<AudioManager> audio_manager(AudioManager::Create());
+ scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting());
if (!CanRunUnifiedAudioTests(audio_manager.get()))
return;
@@ -312,7 +311,7 @@ TEST(WASAPIUnifiedStreamTest, OpenStartAndClose) {
// Verify that IO callbacks starts as they should.
TEST(WASAPIUnifiedStreamTest, StartLoopbackAudio) {
- scoped_ptr<AudioManager> audio_manager(AudioManager::Create());
+ scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting());
if (!CanRunUnifiedAudioTests(audio_manager.get()))
return;
@@ -348,7 +347,7 @@ TEST(WASAPIUnifiedStreamTest, StartLoopbackAudio) {
// back to the speaker. This test allows the user to verify that the audio
// sounds OK. A text file with name |kDeltaTimeMsFileName| is also generated.
TEST(WASAPIUnifiedStreamTest, DISABLED_RealTimePlayThrough) {
- scoped_ptr<AudioManager> audio_manager(AudioManager::Create());
+ scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting());
if (!CanRunUnifiedAudioTests(audio_manager.get()))
return;
diff --git a/chromium/media/audio/win/core_audio_util_win.cc b/chromium/media/audio/win/core_audio_util_win.cc
index 4adfdda090a..790b2b140f7 100644
--- a/chromium/media/audio/win/core_audio_util_win.cc
+++ b/chromium/media/audio/win/core_audio_util_win.cc
@@ -25,8 +25,6 @@ namespace media {
enum { KSAUDIO_SPEAKER_UNSUPPORTED = 0 };
-typedef uint32 ChannelConfig;
-
// Converts Microsoft's channel configuration to ChannelLayout.
// This mapping is not perfect but the best we can do given the current
// ChannelLayout enumerator and the Windows-specific speaker configurations
@@ -401,7 +399,7 @@ std::string CoreAudioUtil::GetMatchingOutputDeviceID(
ScopedComPtr<IMMDevice> output_device;
for (UINT i = 0; i < count; ++i) {
collection->Item(i, output_device.Receive());
- std::string output_controller_id(CoreAudioUtil::GetAudioControllerID(
+ std::string output_controller_id(GetAudioControllerID(
output_device, enumerator));
if (output_controller_id == controller_id)
break;
@@ -478,6 +476,18 @@ ScopedComPtr<IAudioClient> CoreAudioUtil::CreateDefaultClient(
ScopedComPtr<IAudioClient>());
}
+ScopedComPtr<IAudioClient> CoreAudioUtil::CreateClient(
+ const std::string& device_id, EDataFlow data_flow, ERole role) {
+ if (device_id.empty())
+ return CreateDefaultClient(data_flow, role);
+
+ ScopedComPtr<IMMDevice> device(CreateDevice(device_id));
+ if (!device)
+ return ScopedComPtr<IAudioClient>();
+
+ return CreateClient(device);
+}
+
HRESULT CoreAudioUtil::GetSharedModeMixFormat(
IAudioClient* client, WAVEFORMATPCMEX* format) {
DCHECK(IsSupported());
@@ -496,18 +506,6 @@ HRESULT CoreAudioUtil::GetSharedModeMixFormat(
return hr;
}
-HRESULT CoreAudioUtil::GetDefaultSharedModeMixFormat(
- EDataFlow data_flow, ERole role, WAVEFORMATPCMEX* format) {
- DCHECK(IsSupported());
- ScopedComPtr<IAudioClient> client(CreateDefaultClient(data_flow, role));
- if (!client) {
- // Map NULL-pointer to new error code which can be different from the
- // actual error code. The exact value is not important here.
- return AUDCLNT_E_ENDPOINT_CREATE_FAILED;
- }
- return CoreAudioUtil::GetSharedModeMixFormat(client, format);
-}
-
bool CoreAudioUtil::IsFormatSupported(IAudioClient* client,
AUDCLNT_SHAREMODE share_mode,
const WAVEFORMATPCMEX* format) {
@@ -529,18 +527,20 @@ bool CoreAudioUtil::IsFormatSupported(IAudioClient* client,
return (hr == S_OK);
}
-bool CoreAudioUtil::IsChannelLayoutSupported(EDataFlow data_flow, ERole role,
+bool CoreAudioUtil::IsChannelLayoutSupported(const std::string& device_id,
+ EDataFlow data_flow,
+ ERole role,
ChannelLayout channel_layout) {
DCHECK(IsSupported());
// First, get the preferred mixing format for shared mode streams.
- ScopedComPtr<IAudioClient> client(CreateDefaultClient(data_flow, role));
+ ScopedComPtr<IAudioClient> client(CreateClient(device_id, data_flow, role));
if (!client)
return false;
WAVEFORMATPCMEX format;
- HRESULT hr = CoreAudioUtil::GetSharedModeMixFormat(client, &format);
+ HRESULT hr = GetSharedModeMixFormat(client, &format);
if (FAILED(hr))
return false;
@@ -623,6 +623,16 @@ HRESULT CoreAudioUtil::GetPreferredAudioParameters(
// Convert Microsoft's channel configuration to genric ChannelLayout.
ChannelLayout channel_layout = ChannelConfigToChannelLayout(channel_config);
+ // Some devices don't appear to set a valid channel layout, so guess based on
+ // the number of channels. See http://crbug.com/311906.
+ if (channel_layout == CHANNEL_LAYOUT_UNSUPPORTED) {
+ VLOG(1) << "Unsupported channel config: "
+ << std::hex << channel_config
+ << ". Guessing layout by channel count: "
+ << std::dec << mix_format.Format.nChannels;
+ channel_layout = GuessChannelLayout(mix_format.Format.nChannels);
+ }
+
// Preferred sample rate.
int sample_rate = mix_format.Format.nSamplesPerSec;
@@ -684,6 +694,18 @@ HRESULT CoreAudioUtil::GetPreferredAudioParameters(
return GetPreferredAudioParameters(client, params);
}
+ChannelConfig CoreAudioUtil::GetChannelConfig(const std::string& device_id,
+ EDataFlow data_flow) {
+ ScopedComPtr<IAudioClient> client(
+ CreateClient(device_id, data_flow, eConsole));
+
+ WAVEFORMATPCMEX format = {0};
+ if (!client || FAILED(GetSharedModeMixFormat(client, &format)))
+ return 0;
+
+ return static_cast<ChannelConfig>(format.dwChannelMask);
+}
+
HRESULT CoreAudioUtil::SharedModeInitialize(IAudioClient* client,
const WAVEFORMATPCMEX* format,
HANDLE event_handle,
diff --git a/chromium/media/audio/win/core_audio_util_win.h b/chromium/media/audio/win/core_audio_util_win.h
index cdf6dfb11df..a210af906ea 100644
--- a/chromium/media/audio/win/core_audio_util_win.h
+++ b/chromium/media/audio/win/core_audio_util_win.h
@@ -26,6 +26,12 @@ using base::win::ScopedComPtr;
namespace media {
+
+// Represents audio channel configuration constants as understood by Windows.
+// E.g. KSAUDIO_SPEAKER_MONO. For a list of possible values see:
+// http://msdn.microsoft.com/en-us/library/windows/hardware/ff537083(v=vs.85).aspx
+typedef uint32 ChannelConfig;
+
class MEDIA_EXPORT CoreAudioUtil {
public:
// Returns true if Windows Core Audio is supported.
@@ -106,7 +112,7 @@ class MEDIA_EXPORT CoreAudioUtil {
// manage the flow of audio data between the application and an audio endpoint
// device.
- // Create an IAudioClient interface for the default IMMDevice where
+ // Create an IAudioClient instance for the default IMMDevice where
// flow direction and role is define by |data_flow| and |role|.
// The IAudioClient interface enables a client to create and initialize an
// audio stream between an audio application and the audio engine (for a
@@ -115,6 +121,12 @@ class MEDIA_EXPORT CoreAudioUtil {
static ScopedComPtr<IAudioClient> CreateDefaultClient(EDataFlow data_flow,
ERole role);
+ // Create an IAudioClient instance for a specific device _or_ the default
+ // device if |device_id| is empty.
+ static ScopedComPtr<IAudioClient> CreateClient(const std::string& device_id,
+ EDataFlow data_flow,
+ ERole role);
+
// Create an IAudioClient interface for an existing IMMDevice given by
// |audio_device|. Flow direction and role is define by the |audio_device|.
static ScopedComPtr<IAudioClient> CreateClient(IMMDevice* audio_device);
@@ -126,13 +138,6 @@ class MEDIA_EXPORT CoreAudioUtil {
static HRESULT GetSharedModeMixFormat(IAudioClient* client,
WAVEFORMATPCMEX* format);
- // Get the mix format that the audio engine uses internally for processing
- // of shared-mode streams using the default IMMDevice where flow direction
- // and role is define by |data_flow| and |role|.
- static HRESULT GetDefaultSharedModeMixFormat(EDataFlow data_flow,
- ERole role,
- WAVEFORMATPCMEX* format);
-
// Returns true if the specified |client| supports the format in |format|
// for the given |share_mode| (shared or exclusive).
static bool IsFormatSupported(IAudioClient* client,
@@ -144,7 +149,9 @@ class MEDIA_EXPORT CoreAudioUtil {
// and |role|. If this method returns true for a certain channel layout, it
// means that SharedModeInitialize() will succeed using a format based on
// the preferred format where the channel layout has been modified.
- static bool IsChannelLayoutSupported(EDataFlow data_flow, ERole role,
+ static bool IsChannelLayoutSupported(const std::string& device_id,
+ EDataFlow data_flow,
+ ERole role,
ChannelLayout channel_layout);
// For a shared-mode stream, the audio engine periodically processes the
@@ -170,6 +177,19 @@ class MEDIA_EXPORT CoreAudioUtil {
static HRESULT GetPreferredAudioParameters(const std::string& device_id,
AudioParameters* params);
+ // Retrieves an integer mask which corresponds to the channel layout the
+ // audio engine uses for its internal processing/mixing of shared-mode
+ // streams. This mask indicates which channels are present in the multi-
+ // channel stream. The least significant bit corresponds with the Front Left
+ // speaker, the next least significant bit corresponds to the Front Right
+ // speaker, and so on, continuing in the order defined in KsMedia.h.
+ // See http://msdn.microsoft.com/en-us/library/windows/hardware/ff537083(v=vs.85).aspx
+ // for more details.
+ // To get the channel config of the default device, pass an empty string
+ // for |device_id|.
+ static ChannelConfig GetChannelConfig(const std::string& device_id,
+ EDataFlow data_flow);
+
// After activating an IAudioClient interface on an audio endpoint device,
// the client must initialize it once, and only once, to initialize the audio
// stream between the client and the device. In shared mode, the client
diff --git a/chromium/media/audio/win/core_audio_util_win_unittest.cc b/chromium/media/audio/win/core_audio_util_win_unittest.cc
index abef8682020..e9ed0c4f597 100644
--- a/chromium/media/audio/win/core_audio_util_win_unittest.cc
+++ b/chromium/media/audio/win/core_audio_util_win_unittest.cc
@@ -274,7 +274,7 @@ TEST_F(CoreAudioUtilWinTest, IsChannelLayoutSupported) {
EXPECT_TRUE(SUCCEEDED(hr));
EXPECT_TRUE(mix_params.IsValid());
EXPECT_TRUE(CoreAudioUtil::IsChannelLayoutSupported(
- eRender, eConsole, mix_params.channel_layout()));
+ std::string(), eRender, eConsole, mix_params.channel_layout()));
// Check if it is possible to modify the channel layout to stereo for a
// device which reports that it prefers to be openen up in an other
@@ -284,7 +284,7 @@ TEST_F(CoreAudioUtilWinTest, IsChannelLayoutSupported) {
// TODO(henrika): it might be too pessimistic to assume false as return
// value here.
EXPECT_FALSE(CoreAudioUtil::IsChannelLayoutSupported(
- eRender, eConsole, channel_layout));
+ std::string(), eRender, eConsole, channel_layout));
}
}
diff --git a/chromium/media/audio/win/wavein_input_win.cc b/chromium/media/audio/win/wavein_input_win.cc
index 3c4147738df..05771250e01 100644
--- a/chromium/media/audio/win/wavein_input_win.cc
+++ b/chromium/media/audio/win/wavein_input_win.cc
@@ -8,7 +8,6 @@
#include "base/logging.h"
#include "media/audio/audio_io.h"
-#include "media/audio/audio_util.h"
#include "media/audio/win/audio_manager_win.h"
#include "media/audio/win/device_enumeration_win.h"
diff --git a/chromium/media/audio/win/waveout_output_win.cc b/chromium/media/audio/win/waveout_output_win.cc
index 47d4fa65053..0f54817b14a 100644
--- a/chromium/media/audio/win/waveout_output_win.cc
+++ b/chromium/media/audio/win/waveout_output_win.cc
@@ -248,39 +248,25 @@ void PCMWaveOutAudioOutputStream::Stop() {
state_ = PCMA_STOPPING;
base::subtle::MemoryBarrier();
- // Stop watching for buffer event, wait till all the callbacks are complete.
- // Should be done before ::waveOutReset() call to avoid race condition when
- // callback that is currently active and already checked that stream is still
- // being played calls ::waveOutWrite() after ::waveOutReset() returns, later
- // causing ::waveOutClose() to fail with WAVERR_STILLPLAYING.
- // TODO(enal): that delays actual stopping of playback. Alternative can be
- // to call ::waveOutReset() twice, once before
- // ::UnregisterWaitEx() and once after.
+ // Stop watching for buffer event, waits until outstanding callbacks finish.
if (waiting_handle_) {
- if (!::UnregisterWaitEx(waiting_handle_, INVALID_HANDLE_VALUE)) {
- state_ = PCMA_PLAYING;
- HandleError(MMSYSERR_ERROR);
- return;
- }
+ if (!::UnregisterWaitEx(waiting_handle_, INVALID_HANDLE_VALUE))
+ HandleError(::GetLastError());
waiting_handle_ = NULL;
}
// Stop playback.
MMRESULT res = ::waveOutReset(waveout_);
- if (res != MMSYSERR_NOERROR) {
- state_ = PCMA_PLAYING;
+ if (res != MMSYSERR_NOERROR)
HandleError(res);
- return;
- }
// Wait for lock to ensure all outstanding callbacks have completed.
base::AutoLock auto_lock(lock_);
// waveOutReset() leaves buffers in the unpredictable state, causing
// problems if we want to close, release, or reuse them. Fix the states.
- for (int ix = 0; ix != num_buffers_; ++ix) {
+ for (int ix = 0; ix != num_buffers_; ++ix)
GetBuffer(ix)->dwFlags = WHDR_PREPARED;
- }
// Don't use callback after Stop().
callback_ = NULL;