summaryrefslogtreecommitdiffstats
path: root/media/audio
diff options
context:
space:
mode:
authordalecurtis@chromium.org <dalecurtis@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-10-18 22:12:05 +0000
committerdalecurtis@chromium.org <dalecurtis@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-10-18 22:12:05 +0000
commit2b392e59ab9a72858d224bad9e422fe40a696a69 (patch)
tree124296c1b0473baa88c55a66f95d22b3e4623f04 /media/audio
parentc9b6e6a5b77de034a13c5d26b9084c40bb0e74c7 (diff)
downloadchromium_src-2b392e59ab9a72858d224bad9e422fe40a696a69.zip
chromium_src-2b392e59ab9a72858d224bad9e422fe40a696a69.tar.gz
chromium_src-2b392e59ab9a72858d224bad9e422fe40a696a69.tar.bz2
Simulate an audio output stream when a real one isn't available.
Upgrades FakeAudioOutputStream to fake OnMoreData calls using a PostDelayedTask approach similar to NullAudioSink. This is necessary for playback of HTML5 videos when a real audio device is not present. I.e. over remote desktop w/ remote audio disabled on on some of the Buildbot machines (this should allow us to remove --disable-audio from many of our tests). This is a two fold change: 1. AudioOutputResampler will now request an AUDIO_FAKE device when invalid output parameters are provided. 2. AudioManagerBase will check for AUDIO_FAKE as it always has but will additionally check if there are any output devices and if not return a fake stream. 2 is necessary for HTML5 audio since it doesn't use the low latency output path. BUG=120749 TEST=unittests. HTML5/Flash video playback over Remote Desktop w/o Remote Audio works. Review URL: https://chromiumcodereview.appspot.com/10987087 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@162810 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'media/audio')
-rw-r--r--media/audio/audio_input_controller_unittest.cc10
-rw-r--r--media/audio/audio_manager_base.cc40
-rw-r--r--media/audio/audio_output_proxy_unittest.cc134
-rw-r--r--media/audio/audio_output_resampler.cc16
-rw-r--r--media/audio/audio_parameters.h4
-rw-r--r--media/audio/fake_audio_output_stream.cc84
-rw-r--r--media/audio/fake_audio_output_stream.h30
-rw-r--r--media/audio/fake_audio_output_stream_unittest.cc67
-rw-r--r--media/audio/ios/audio_manager_ios.mm2
-rw-r--r--media/audio/simple_sources.cc10
-rw-r--r--media/audio/simple_sources.h6
-rw-r--r--media/audio/simple_sources_unittest.cc67
-rw-r--r--media/audio/win/audio_output_win_unittest.cc17
13 files changed, 260 insertions, 227 deletions
diff --git a/media/audio/audio_input_controller_unittest.cc b/media/audio/audio_input_controller_unittest.cc
index 58cb5a3..0a2a39b 100644
--- a/media/audio/audio_input_controller_unittest.cc
+++ b/media/audio/audio_input_controller_unittest.cc
@@ -80,7 +80,7 @@ TEST_F(AudioInputControllerTest, CreateAndClose) {
.WillOnce(QuitMessageLoop(&message_loop_));
scoped_ptr<AudioManager> audio_manager(AudioManager::Create());
- AudioParameters params(AudioParameters::AUDIO_MOCK, kChannelLayout,
+ AudioParameters params(AudioParameters::AUDIO_FAKE, kChannelLayout,
kSampleRate, kBitsPerSample, kSamplesPerPacket);
scoped_refptr<AudioInputController> controller =
AudioInputController::Create(audio_manager.get(), &event_handler, params);
@@ -113,7 +113,7 @@ TEST_F(AudioInputControllerTest, RecordAndClose) {
message_loop_.message_loop_proxy()));
scoped_ptr<AudioManager> audio_manager(AudioManager::Create());
- AudioParameters params(AudioParameters::AUDIO_MOCK, kChannelLayout,
+ AudioParameters params(AudioParameters::AUDIO_FAKE, kChannelLayout,
kSampleRate, kBitsPerSample, kSamplesPerPacket);
// Creating the AudioInputController should render an OnCreated() call.
@@ -159,7 +159,7 @@ TEST_F(AudioInputControllerTest, RecordAndError) {
.WillOnce(QuitMessageLoop(&message_loop_));
scoped_ptr<AudioManager> audio_manager(AudioManager::Create());
- AudioParameters params(AudioParameters::AUDIO_MOCK, kChannelLayout,
+ AudioParameters params(AudioParameters::AUDIO_FAKE, kChannelLayout,
kSampleRate, kBitsPerSample, kSamplesPerPacket);
// Creating the AudioInputController should render an OnCreated() call.
@@ -192,7 +192,7 @@ TEST_F(AudioInputControllerTest, SamplesPerPacketTooLarge) {
.Times(Exactly(0));
scoped_ptr<AudioManager> audio_manager(AudioManager::Create());
- AudioParameters params(AudioParameters::AUDIO_MOCK, kChannelLayout,
+ AudioParameters params(AudioParameters::AUDIO_FAKE, kChannelLayout,
kSampleRate, kBitsPerSample, kSamplesPerPacket * 1000);
scoped_refptr<AudioInputController> controller =
AudioInputController::Create(audio_manager.get(), &event_handler, params);
@@ -211,7 +211,7 @@ TEST_F(AudioInputControllerTest, CloseTwice) {
.Times(Exactly(1));
scoped_ptr<AudioManager> audio_manager(AudioManager::Create());
- AudioParameters params(AudioParameters::AUDIO_MOCK, kChannelLayout,
+ AudioParameters params(AudioParameters::AUDIO_FAKE, kChannelLayout,
kSampleRate, kBitsPerSample, kSamplesPerPacket);
scoped_refptr<AudioInputController> controller =
AudioInputController::Create(audio_manager.get(), &event_handler, params);
diff --git a/media/audio/audio_manager_base.cc b/media/audio/audio_manager_base.cc
index 9ae0166..8c09ebb 100644
--- a/media/audio/audio_manager_base.cc
+++ b/media/audio/audio_manager_base.cc
@@ -112,8 +112,14 @@ AudioOutputStream* AudioManagerBase::MakeAudioOutputStream(
return NULL;
}
+ // If there are no audio output devices we should use a FakeAudioOutputStream
+ // to ensure video playback continues to work.
+ bool audio_output_disabled =
+ params.format() == AudioParameters::AUDIO_FAKE ||
+ !HasAudioOutputDevices();
+
AudioOutputStream* stream = NULL;
- if (params.format() == AudioParameters::AUDIO_MOCK) {
+ if (audio_output_disabled) {
stream = FakeAudioOutputStream::MakeFakeStream(this, params);
} else if (params.format() == AudioParameters::AUDIO_PCM_LINEAR) {
stream = MakeLinearOutputStream(params);
@@ -143,7 +149,7 @@ AudioInputStream* AudioManagerBase::MakeAudioInputStream(
}
AudioInputStream* stream = NULL;
- if (params.format() == AudioParameters::AUDIO_MOCK) {
+ if (params.format() == AudioParameters::AUDIO_FAKE) {
stream = FakeAudioInputStream::MakeFakeStream(this, params);
} else if (params.format() == AudioParameters::AUDIO_PCM_LINEAR) {
stream = MakeLinearInputStream(params, device_id);
@@ -176,11 +182,31 @@ AudioOutputStream* AudioManagerBase::MakeAudioOutputStreamProxy(
const CommandLine* cmd_line = CommandLine::ForCurrentProcess();
if (!cmd_line->HasSwitch(switches::kDisableAudioOutputResampler) &&
params.format() == AudioParameters::AUDIO_PCM_LOW_LATENCY) {
- scoped_refptr<AudioOutputDispatcher> dispatcher = new AudioOutputResampler(
- this, params, GetPreferredLowLatencyOutputStreamParameters(params),
- close_delay);
- output_dispatchers_[params] = dispatcher;
- return new AudioOutputProxy(dispatcher);
+ AudioParameters output_params =
+ GetPreferredLowLatencyOutputStreamParameters(params);
+
+ // Ensure we only pass on valid output parameters.
+ if (output_params.IsValid()) {
+ scoped_refptr<AudioOutputDispatcher> dispatcher =
+ new AudioOutputResampler(this, params, output_params, close_delay);
+ output_dispatchers_[params] = dispatcher;
+ return new AudioOutputProxy(dispatcher);
+ }
+
+ // We've received invalid audio output parameters, so switch to a mock
+ // output device based on the input parameters. This may happen if the OS
+ // provided us junk values for the hardware configuration.
+ LOG(ERROR) << "Invalid audio output parameters received; using fake audio "
+ << "path. Channels: " << output_params.channels() << ", "
+ << "Sample Rate: " << output_params.sample_rate() << ", "
+ << "Bits Per Sample: " << output_params.bits_per_sample()
+ << ", Frames Per Buffer: " << output_params.frames_per_buffer();
+
+ // Passing AUDIO_FAKE tells the AudioManager to create a fake output device.
+ output_params = AudioParameters(
+ AudioParameters::AUDIO_FAKE, params.channel_layout(),
+ params.sample_rate(), params.bits_per_sample(),
+ params.frames_per_buffer());
}
#if defined(ENABLE_AUDIO_MIXER)
diff --git a/media/audio/audio_output_proxy_unittest.cc b/media/audio/audio_output_proxy_unittest.cc
index fcf52cb..0968611 100644
--- a/media/audio/audio_output_proxy_unittest.cc
+++ b/media/audio/audio_output_proxy_unittest.cc
@@ -7,12 +7,12 @@
#include "base/command_line.h"
#include "base/message_loop.h"
#include "base/message_loop_proxy.h"
-#include "base/threading/platform_thread.h"
-#include "base/threading/simple_thread.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_manager.h"
+#include "media/audio/audio_manager_base.h"
+#include "media/audio/fake_audio_output_stream.h"
#include "media/base/media_switches.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -35,10 +35,12 @@ using media::AudioBus;
using media::AudioBuffersState;
using media::AudioInputStream;
using media::AudioManager;
+using media::AudioManagerBase;
using media::AudioOutputDispatcher;
using media::AudioOutputProxy;
using media::AudioOutputStream;
using media::AudioParameters;
+using media::FakeAudioOutputStream;
namespace {
@@ -53,86 +55,28 @@ static const int kOnMoreDataCallbackDelayMs = 10;
// Let start run long enough for many OnMoreData callbacks to occur.
static const int kStartRunTimeMs = kOnMoreDataCallbackDelayMs * 10;
-
-// Simple class for repeatedly calling OnMoreData() to expose shutdown issues
-// with AudioSourceCallback users.
-class AudioThreadRunner : public base::DelegateSimpleThread::Delegate {
- public:
- AudioThreadRunner(AudioOutputStream::AudioSourceCallback* callback,
- base::TimeDelta delay, const AudioParameters& params)
- : delay_(delay),
- callback_(callback),
- bus_(media::AudioBus::Create(params)),
- running_(true) {
- CHECK(delay_ > base::TimeDelta());
- }
-
- virtual void Run() {
- while (true) {
- {
- base::AutoLock auto_lock(lock_);
- if (!running_)
- return;
- }
- base::PlatformThread::Sleep(delay_);
- callback_->OnMoreData(bus_.get(), AudioBuffersState());
- }
- }
-
- void Stop() {
- base::AutoLock auto_lock(lock_);
- running_ = false;
- }
-
- private:
- base::TimeDelta delay_;
- AudioOutputStream::AudioSourceCallback* callback_;
- scoped_ptr<media::AudioBus> bus_;
- bool running_;
- base::Lock lock_;
-
- DISALLOW_COPY_AND_ASSIGN(AudioThreadRunner);
-};
-
class MockAudioOutputStream : public AudioOutputStream {
public:
- explicit MockAudioOutputStream(const AudioParameters& params)
+ MockAudioOutputStream(AudioManagerBase* manager,
+ const AudioParameters& params)
: start_called_(false),
stop_called_(false),
- params_(params) {
+ params_(params),
+ fake_output_stream_(
+ FakeAudioOutputStream::MakeFakeStream(manager, params_)) {
}
void Start(AudioSourceCallback* callback) {
start_called_ = true;
- audio_thread_runner_.reset(new AudioThreadRunner(
- callback,
- base::TimeDelta::FromMilliseconds(kOnMoreDataCallbackDelayMs),
- params_));
- audio_thread_.reset(new base::DelegateSimpleThread(
- audio_thread_runner_.get(), "AudioThreadRunner"));
- audio_thread_->Start();
- }
-
- void ShutdownAudioThread() {
- if (!audio_thread_.get()) {
- ASSERT_FALSE(audio_thread_runner_.get());
- return;
- }
- ASSERT_TRUE(audio_thread_runner_.get());
- audio_thread_runner_->Stop();
- audio_thread_->Join();
- audio_thread_runner_.reset();
- audio_thread_.reset();
+ fake_output_stream_->Start(callback);
}
void Stop() {
stop_called_ = true;
- ShutdownAudioThread();
+ fake_output_stream_->Stop();
}
- ~MockAudioOutputStream() {
- ShutdownAudioThread();
- }
+ ~MockAudioOutputStream() {}
bool start_called() { return start_called_; }
bool stop_called() { return stop_called_; }
@@ -146,11 +90,10 @@ class MockAudioOutputStream : public AudioOutputStream {
bool start_called_;
bool stop_called_;
AudioParameters params_;
- scoped_ptr<AudioThreadRunner> audio_thread_runner_;
- scoped_ptr<base::DelegateSimpleThread> audio_thread_;
+ scoped_ptr<AudioOutputStream> fake_output_stream_;
};
-class MockAudioManager : public AudioManager {
+class MockAudioManager : public AudioManagerBase {
public:
MockAudioManager() {}
@@ -170,6 +113,15 @@ class MockAudioManager : public AudioManager {
MOCK_METHOD1(GetAudioInputDeviceNames, void(
media::AudioDeviceNames* device_name));
MOCK_METHOD0(IsRecordingInProcess, bool());
+
+ MOCK_METHOD1(MakeLinearOutputStream, AudioOutputStream*(
+ const AudioParameters& params));
+ MOCK_METHOD1(MakeLowLatencyOutputStream, AudioOutputStream*(
+ const AudioParameters& params));
+ MOCK_METHOD2(MakeLinearInputStream, AudioInputStream*(
+ const AudioParameters& params, const std::string& device_id));
+ MOCK_METHOD2(MakeLowLatencyInputStream, AudioInputStream*(
+ const AudioParameters& params, const std::string& device_id));
};
class MockAudioSourceCallback : public AudioOutputStream::AudioSourceCallback {
@@ -237,7 +189,7 @@ class AudioOutputProxyTest : public testing::Test {
// Methods that do actual tests.
void OpenAndClose(AudioOutputDispatcher* dispatcher) {
- MockAudioOutputStream stream(params_);
+ MockAudioOutputStream stream(&manager_, params_);
EXPECT_CALL(manager(), MakeAudioOutputStream(_))
.WillOnce(Return(&stream));
@@ -254,7 +206,7 @@ class AudioOutputProxyTest : public testing::Test {
// Create a stream, and then calls Start() and Stop().
void StartAndStop(AudioOutputDispatcher* dispatcher) {
- MockAudioOutputStream stream(params_);
+ MockAudioOutputStream stream(&manager_, params_);
EXPECT_CALL(manager(), MakeAudioOutputStream(_))
.WillOnce(Return(&stream));
@@ -280,7 +232,7 @@ class AudioOutputProxyTest : public testing::Test {
// Verify that the stream is closed after Stop is called.
void CloseAfterStop(AudioOutputDispatcher* dispatcher) {
- MockAudioOutputStream stream(params_);
+ MockAudioOutputStream stream(&manager_, params_);
EXPECT_CALL(manager(), MakeAudioOutputStream(_))
.WillOnce(Return(&stream));
@@ -312,7 +264,7 @@ class AudioOutputProxyTest : public testing::Test {
// Create two streams, but don't start them. Only one device must be open.
void TwoStreams(AudioOutputDispatcher* dispatcher) {
- MockAudioOutputStream stream(params_);
+ MockAudioOutputStream stream(&manager_, params_);
EXPECT_CALL(manager(), MakeAudioOutputStream(_))
.WillOnce(Return(&stream));
@@ -334,7 +286,7 @@ class AudioOutputProxyTest : public testing::Test {
// Open() method failed.
void OpenFailed(AudioOutputDispatcher* dispatcher) {
- MockAudioOutputStream stream(params_);
+ MockAudioOutputStream stream(&manager_, params_);
EXPECT_CALL(manager(), MakeAudioOutputStream(_))
.WillOnce(Return(&stream));
@@ -352,7 +304,7 @@ class AudioOutputProxyTest : public testing::Test {
}
void CreateAndWait(AudioOutputDispatcher* dispatcher) {
- MockAudioOutputStream stream(params_);
+ MockAudioOutputStream stream(&manager_, params_);
EXPECT_CALL(manager(), MakeAudioOutputStream(_))
.WillOnce(Return(&stream));
@@ -378,8 +330,8 @@ class AudioOutputProxyTest : public testing::Test {
}
void TwoStreams_OnePlaying(AudioOutputDispatcher* dispatcher) {
- MockAudioOutputStream stream1(params_);
- MockAudioOutputStream stream2(params_);
+ MockAudioOutputStream stream1(&manager_, params_);
+ MockAudioOutputStream stream2(&manager_, params_);
EXPECT_CALL(manager(), MakeAudioOutputStream(_))
.WillOnce(Return(&stream1))
@@ -416,8 +368,8 @@ class AudioOutputProxyTest : public testing::Test {
}
void TwoStreams_BothPlaying(AudioOutputDispatcher* dispatcher) {
- MockAudioOutputStream stream1(params_);
- MockAudioOutputStream stream2(params_);
+ MockAudioOutputStream stream1(&manager_, params_);
+ MockAudioOutputStream stream2(&manager_, params_);
EXPECT_CALL(manager(), MakeAudioOutputStream(_))
.WillOnce(Return(&stream1))
@@ -457,7 +409,7 @@ class AudioOutputProxyTest : public testing::Test {
}
void StartFailed(AudioOutputDispatcher* dispatcher) {
- MockAudioOutputStream stream(params_);
+ MockAudioOutputStream stream(&manager_, params_);
EXPECT_CALL(manager(), MakeAudioOutputStream(_))
.WillOnce(Return(&stream));
@@ -627,7 +579,7 @@ TEST_F(AudioOutputProxyTest, TwoStreams_OnePlaying) {
#if defined(ENABLE_AUDIO_MIXER)
// Two streams: verify that only one device will be created.
TEST_F(AudioOutputProxyTest, TwoStreams_OnePlaying_Mixer) {
- MockAudioOutputStream stream(params_);
+ MockAudioOutputStream stream(&manager_, params_);
InitDispatcher(base::TimeDelta::FromMilliseconds(kTestCloseDelayMs));
@@ -674,7 +626,7 @@ TEST_F(AudioOutputProxyTest, TwoStreams_BothPlaying) {
// Two streams, both are playing. Still have to use single device.
// Also verifies that every proxy stream gets its own pending_bytes.
TEST_F(AudioOutputProxyTest, TwoStreams_BothPlaying_Mixer) {
- MockAudioOutputStream stream(params_);
+ MockAudioOutputStream stream(&manager_, params_);
InitDispatcher(base::TimeDelta::FromMilliseconds(kTestCloseDelayMs));
@@ -764,7 +716,7 @@ TEST_F(AudioOutputProxyTest, StartFailed) {
#if defined(ENABLE_AUDIO_MIXER)
// Start() method failed.
TEST_F(AudioOutputProxyTest, StartFailed_Mixer) {
- MockAudioOutputStream stream(params_);
+ MockAudioOutputStream stream(&manager_, params_);
EXPECT_CALL(manager(), MakeAudioOutputStream(_))
.WillOnce(Return(&stream));
@@ -814,7 +766,7 @@ TEST_F(AudioOutputResamplerTest, StartFailed) {
// Simulate AudioOutputStream::Create() failure with a low latency stream and
// ensure AudioOutputResampler falls back to the high latency path.
TEST_F(AudioOutputResamplerTest, LowLatencyCreateFailedFallback) {
- MockAudioOutputStream stream(params_);
+ MockAudioOutputStream stream(&manager_, params_);
EXPECT_CALL(manager(), MakeAudioOutputStream(_))
.Times(2)
.WillOnce(Return(static_cast<AudioOutputStream*>(NULL)))
@@ -833,8 +785,8 @@ TEST_F(AudioOutputResamplerTest, LowLatencyCreateFailedFallback) {
// Simulate AudioOutputStream::Open() failure with a low latency stream and
// ensure AudioOutputResampler falls back to the high latency path.
TEST_F(AudioOutputResamplerTest, LowLatencyOpenFailedFallback) {
- MockAudioOutputStream failed_stream(params_);
- MockAudioOutputStream okay_stream(params_);
+ MockAudioOutputStream failed_stream(&manager_, params_);
+ MockAudioOutputStream okay_stream(&manager_, params_);
EXPECT_CALL(manager(), MakeAudioOutputStream(_))
.Times(2)
.WillOnce(Return(&failed_stream))
@@ -870,9 +822,9 @@ TEST_F(AudioOutputResamplerTest, LowLatencyFallbackFailed) {
// Simulate an eventual OpenStream() failure; i.e. successful OpenStream() calls
// eventually followed by one which fails; root cause of http://crbug.com/150619
TEST_F(AudioOutputResamplerTest, LowLatencyOpenEventuallyFails) {
- MockAudioOutputStream stream1(params_);
- MockAudioOutputStream stream2(params_);
- MockAudioOutputStream stream3(params_);
+ 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(_))
diff --git a/media/audio/audio_output_resampler.cc b/media/audio/audio_output_resampler.cc
index 12fe139..9658642 100644
--- a/media/audio/audio_output_resampler.cc
+++ b/media/audio/audio_output_resampler.cc
@@ -176,25 +176,13 @@ AudioOutputResampler::AudioOutputResampler(AudioManager* audio_manager,
close_delay_(close_delay),
output_params_(output_params),
streams_opened_(false) {
+ DCHECK(input_params.IsValid());
+ DCHECK(output_params.IsValid());
DCHECK_EQ(output_params_.format(), AudioParameters::AUDIO_PCM_LOW_LATENCY);
// Record UMA statistics for the hardware configuration.
RecordStats(output_params);
- // Immediately fallback if we're given invalid output parameters. This may
- // happen if the OS provided us junk values for the hardware configuration.
- if (!output_params_.IsValid()) {
- LOG(ERROR) << "Invalid audio output parameters received; using fallback "
- << "path. Channels: " << output_params_.channels() << ", "
- << "Sample Rate: " << output_params_.sample_rate() << ", "
- << "Bits Per Sample: " << output_params_.bits_per_sample()
- << ", Frames Per Buffer: " << output_params_.frames_per_buffer();
- // Record UMA statistics about the hardware which triggered the failure so
- // we can debug and triage later.
- RecordFallbackStats(output_params);
- output_params_ = SetupFallbackParams(input_params, output_params);
- }
-
Initialize();
}
diff --git a/media/audio/audio_parameters.h b/media/audio/audio_parameters.h
index 01d8869..be904f4 100644
--- a/media/audio/audio_parameters.h
+++ b/media/audio/audio_parameters.h
@@ -35,8 +35,8 @@ class MEDIA_EXPORT AudioParameters {
enum Format {
AUDIO_PCM_LINEAR = 0, // PCM is 'raw' amplitude samples.
AUDIO_PCM_LOW_LATENCY, // Linear PCM, low latency requested.
- AUDIO_MOCK, // Creates a dummy AudioOutputStream object.
- AUDIO_LAST_FORMAT // Only used for validation of format.y
+ AUDIO_FAKE, // Creates a fake AudioOutputStream object.
+ AUDIO_LAST_FORMAT // Only used for validation of format.
};
enum {
diff --git a/media/audio/fake_audio_output_stream.cc b/media/audio/fake_audio_output_stream.cc
index f09fe5d..fe03859 100644
--- a/media/audio/fake_audio_output_stream.cc
+++ b/media/audio/fake_audio_output_stream.cc
@@ -4,69 +4,81 @@
#include "media/audio/fake_audio_output_stream.h"
-#include "base/at_exit.h"
+#include "base/bind.h"
+#include "base/bind_helpers.h"
#include "base/logging.h"
#include "media/audio/audio_manager_base.h"
namespace media {
-FakeAudioOutputStream* FakeAudioOutputStream::current_fake_stream_ = NULL;
-
// static
AudioOutputStream* FakeAudioOutputStream::MakeFakeStream(
- AudioManagerBase* manager,
- const AudioParameters& params) {
- FakeAudioOutputStream* new_stream = new FakeAudioOutputStream(manager,
- params);
- DCHECK(current_fake_stream_ == NULL);
- current_fake_stream_ = new_stream;
- return new_stream;
+ AudioManagerBase* manager, const AudioParameters& params) {
+ return new FakeAudioOutputStream(manager, params);
}
-bool FakeAudioOutputStream::Open() {
- return true;
+FakeAudioOutputStream::FakeAudioOutputStream(AudioManagerBase* manager,
+ const AudioParameters& params)
+ : audio_manager_(manager),
+ callback_(NULL),
+ audio_bus_(AudioBus::Create(params)),
+ frames_per_millisecond_(
+ params.sample_rate() / static_cast<float>(
+ base::Time::kMillisecondsPerSecond)),
+ ALLOW_THIS_IN_INITIALIZER_LIST(weak_this_(this)) {
}
-// static
-FakeAudioOutputStream* FakeAudioOutputStream::GetCurrentFakeStream() {
- return current_fake_stream_;
+FakeAudioOutputStream::~FakeAudioOutputStream() {
+ DCHECK(!callback_);
+}
+
+bool FakeAudioOutputStream::Open() {
+ DCHECK(audio_manager_->GetMessageLoop()->BelongsToCurrentThread());
+ return true;
}
void FakeAudioOutputStream::Start(AudioSourceCallback* callback) {
+ DCHECK(audio_manager_->GetMessageLoop()->BelongsToCurrentThread());
callback_ = callback;
- audio_bus_->Zero();
- callback_->OnMoreData(audio_bus_.get(), AudioBuffersState(0, 0));
+ audio_manager_->GetMessageLoop()->PostTask(FROM_HERE, base::Bind(
+ &FakeAudioOutputStream::OnMoreDataTask, weak_this_.GetWeakPtr()));
}
void FakeAudioOutputStream::Stop() {
+ DCHECK(audio_manager_->GetMessageLoop()->BelongsToCurrentThread());
callback_ = NULL;
}
-void FakeAudioOutputStream::SetVolume(double volume) {
- volume_ = volume;
+void FakeAudioOutputStream::Close() {
+ DCHECK(!callback_);
+ DCHECK(audio_manager_->GetMessageLoop()->BelongsToCurrentThread());
+ weak_this_.InvalidateWeakPtrs();
+ audio_manager_->ReleaseOutputStream(this);
}
+void FakeAudioOutputStream::SetVolume(double volume) {};
+
void FakeAudioOutputStream::GetVolume(double* volume) {
- *volume = volume_;
-}
+ *volume = 0;
+};
-void FakeAudioOutputStream::Close() {
- closed_ = true;
- audio_manager_->ReleaseOutputStream(this);
-}
+void FakeAudioOutputStream::OnMoreDataTask() {
+ DCHECK(audio_manager_->GetMessageLoop()->BelongsToCurrentThread());
-FakeAudioOutputStream::FakeAudioOutputStream(AudioManagerBase* manager,
- const AudioParameters& params)
- : audio_manager_(manager),
- volume_(0),
- callback_(NULL),
- closed_(false) {
- audio_bus_ = AudioBus::Create(params);
-}
+ audio_bus_->Zero();
+ int frames_received = audio_bus_->frames();
+ if (callback_) {
+ frames_received = callback_->OnMoreData(
+ audio_bus_.get(), AudioBuffersState());
+ }
-FakeAudioOutputStream::~FakeAudioOutputStream() {
- if (current_fake_stream_ == this)
- current_fake_stream_ = NULL;
+ // Calculate our sleep duration for simulated playback. Sleep for at least
+ // one millisecond so we don't spin the CPU.
+ MessageLoop::current()->PostDelayedTask(
+ FROM_HERE, base::Bind(
+ &FakeAudioOutputStream::OnMoreDataTask, weak_this_.GetWeakPtr()),
+ base::TimeDelta::FromMilliseconds(
+ std::max(1.0f, frames_received / frames_per_millisecond_)));
}
} // namespace media
diff --git a/media/audio/fake_audio_output_stream.h b/media/audio/fake_audio_output_stream.h
index f4c52e2..6d4f19b 100644
--- a/media/audio/fake_audio_output_stream.h
+++ b/media/audio/fake_audio_output_stream.h
@@ -1,17 +1,12 @@
// 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.
-//
-// A fake implementation of AudioOutputStream. It is used for testing purpose.
-// TODO(hclam): Implement a thread in this fake output stream to simulate an
-// audio output stream reading from AudioSourceCallback.
#ifndef MEDIA_AUDIO_FAKE_AUDIO_OUTPUT_STREAM_H_
#define MEDIA_AUDIO_FAKE_AUDIO_OUTOUT_STREAM_H_
-#include <vector>
-
#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
#include "media/audio/audio_io.h"
#include "media/audio/audio_parameters.h"
@@ -19,13 +14,14 @@ namespace media {
class AudioManagerBase;
+// A fake implementation of AudioOutputStream. Used for testing and when a real
+// audio output device is unavailable or refusing output (e.g. remote desktop).
class MEDIA_EXPORT FakeAudioOutputStream : public AudioOutputStream {
public:
static AudioOutputStream* MakeFakeStream(AudioManagerBase* manager,
const AudioParameters& params);
- static FakeAudioOutputStream* GetCurrentFakeStream();
-
+ // AudioOutputStream implementation.
virtual bool Open() OVERRIDE;
virtual void Start(AudioSourceCallback* callback) OVERRIDE;
virtual void Stop() OVERRIDE;
@@ -33,21 +29,23 @@ class MEDIA_EXPORT FakeAudioOutputStream : public AudioOutputStream {
virtual void GetVolume(double* volume) OVERRIDE;
virtual void Close() OVERRIDE;
- AudioBus* audio_bus() { return audio_bus_.get(); }
-
private:
- explicit FakeAudioOutputStream(AudioManagerBase* manager,
- const AudioParameters& params);
-
+ FakeAudioOutputStream(AudioManagerBase* manager,
+ const AudioParameters& params);
virtual ~FakeAudioOutputStream();
- static FakeAudioOutputStream* current_fake_stream_;
+ // Task that regularly calls |callback_->OnMoreData()| according to the
+ // playback rate as determined by the audio parameters given during
+ // construction. Runs on AudioManager's message loop.
+ void OnMoreDataTask();
AudioManagerBase* audio_manager_;
- double volume_;
AudioSourceCallback* callback_;
scoped_ptr<AudioBus> audio_bus_;
- bool closed_;
+ float frames_per_millisecond_;
+
+ // Used to post delayed tasks to the AudioThread that we can cancel.
+ base::WeakPtrFactory<FakeAudioOutputStream> weak_this_;
DISALLOW_COPY_AND_ASSIGN(FakeAudioOutputStream);
};
diff --git a/media/audio/fake_audio_output_stream_unittest.cc b/media/audio/fake_audio_output_stream_unittest.cc
new file mode 100644
index 0000000..8318c2b
--- /dev/null
+++ b/media/audio/fake_audio_output_stream_unittest.cc
@@ -0,0 +1,67 @@
+// 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 "base/bind.h"
+#include "base/message_loop.h"
+#include "base/synchronization/waitable_event.h"
+#include "media/audio/fake_audio_output_stream.h"
+#include "media/audio/audio_manager.h"
+#include "media/audio/simple_sources.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace media {
+
+class FakeAudioOutputStreamTest : public testing::Test {
+ public:
+ FakeAudioOutputStreamTest()
+ : audio_manager_(AudioManager::Create()),
+ params_(
+ AudioParameters::AUDIO_FAKE, CHANNEL_LAYOUT_STEREO, 8000, 8, 128),
+ source_(params_.channels(), 200.0, params_.sample_rate()),
+ done_(false, false) {
+ stream_ = audio_manager_->MakeAudioOutputStream(AudioParameters(params_));
+ CHECK(stream_);
+ }
+
+ virtual ~FakeAudioOutputStreamTest() {}
+
+ void RunOnAudioThread() {
+ ASSERT_TRUE(audio_manager_->GetMessageLoop()->BelongsToCurrentThread());
+ ASSERT_TRUE(stream_->Open());
+ stream_->Start(&source_);
+ // Start() should immediately post a task to run the source callback, so we
+ // should end up with only a single callback being run.
+ audio_manager_->GetMessageLoop()->PostTask(FROM_HERE, base::Bind(
+ &FakeAudioOutputStreamTest::EndTest, base::Unretained(this)));
+ }
+
+ void EndTest() {
+ ASSERT_TRUE(audio_manager_->GetMessageLoop()->BelongsToCurrentThread());
+ stream_->Stop();
+ stream_->Close();
+ EXPECT_EQ(1, source_.callbacks());
+ EXPECT_EQ(0, source_.errors());
+ done_.Signal();
+ }
+
+ protected:
+ scoped_ptr<AudioManager> audio_manager_;
+ AudioParameters params_;
+ AudioOutputStream* stream_;
+ SineWaveAudioSource source_;
+ base::WaitableEvent done_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(FakeAudioOutputStreamTest);
+};
+
+// Ensure the fake audio stream runs on the audio thread and handles fires
+// callbacks to the AudioSourceCallback.
+TEST_F(FakeAudioOutputStreamTest, FakeStreamBasicCallback) {
+ audio_manager_->GetMessageLoop()->PostTask(FROM_HERE, base::Bind(
+ &FakeAudioOutputStreamTest::RunOnAudioThread, base::Unretained(this)));
+ done_.Wait();
+}
+
+} // namespace media
diff --git a/media/audio/ios/audio_manager_ios.mm b/media/audio/ios/audio_manager_ios.mm
index fe93aee..7bf8b27 100644
--- a/media/audio/ios/audio_manager_ios.mm
+++ b/media/audio/ios/audio_manager_ios.mm
@@ -66,7 +66,7 @@ AudioInputStream* AudioManagerIOS::MakeAudioInputStream(
if (!params.IsValid() || (params.channels() > kMaxInputChannels))
return NULL;
- if (params.format() == AudioParameters::AUDIO_MOCK)
+ if (params.format() == AudioParameters::AUDIO_FAKE)
return FakeAudioInputStream::MakeFakeStream(this, params);
else if (params.format() == AudioParameters::AUDIO_PCM_LINEAR)
return new PCMQueueInAudioInputStream(this, params);
diff --git a/media/audio/simple_sources.cc b/media/audio/simple_sources.cc
index 6e8e37f..aeac86d 100644
--- a/media/audio/simple_sources.cc
+++ b/media/audio/simple_sources.cc
@@ -22,7 +22,9 @@ SineWaveAudioSource::SineWaveAudioSource(int channels,
: channels_(channels),
f_(freq / sample_freq),
time_state_(0),
- cap_(0) {
+ cap_(0),
+ callbacks_(0),
+ errors_(0) {
}
// The implementation could be more efficient if a lookup table is constructed
@@ -30,6 +32,7 @@ SineWaveAudioSource::SineWaveAudioSource(int channels,
int SineWaveAudioSource::OnMoreData(AudioBus* audio_bus,
AudioBuffersState audio_buffers) {
base::AutoLock auto_lock(time_lock_);
+ callbacks_++;
// The table is filled with s(t) = kint16max*sin(Theta*t),
// where Theta = 2*PI*fs.
@@ -49,12 +52,11 @@ int SineWaveAudioSource::OnMoreData(AudioBus* audio_bus,
int SineWaveAudioSource::OnMoreIOData(AudioBus* source,
AudioBus* dest,
AudioBuffersState audio_buffers) {
- NOTREACHED();
- return 0;
+ return OnMoreData(dest, audio_buffers);
}
void SineWaveAudioSource::OnError(AudioOutputStream* stream, int code) {
- NOTREACHED();
+ errors_++;
}
void SineWaveAudioSource::CapSamples(int cap) {
diff --git a/media/audio/simple_sources.h b/media/audio/simple_sources.h
index 41cbd3f..80bd517 100644
--- a/media/audio/simple_sources.h
+++ b/media/audio/simple_sources.h
@@ -34,11 +34,17 @@ class MEDIA_EXPORT SineWaveAudioSource
AudioBuffersState audio_buffers) OVERRIDE;
virtual void OnError(AudioOutputStream* stream, int code) OVERRIDE;
+ // The number of OnMoreData()+OnMoreIOData() and OnError() calls respectively.
+ int callbacks() { return callbacks_; }
+ int errors() { return errors_; }
+
protected:
int channels_;
double f_;
int time_state_;
int cap_;
+ int callbacks_;
+ int errors_;
base::Lock time_lock_;
};
diff --git a/media/audio/simple_sources_unittest.cc b/media/audio/simple_sources_unittest.cc
index 47f18b4..cee5d8a 100644
--- a/media/audio/simple_sources_unittest.cc
+++ b/media/audio/simple_sources_unittest.cc
@@ -2,15 +2,12 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include <algorithm> // std::min
#include <limits>
#include "base/logging.h"
#include "base/basictypes.h"
#include "base/memory/scoped_ptr.h"
-#include "base/time.h"
-#include "media/audio/audio_manager.h"
-#include "media/audio/fake_audio_output_stream.h"
+#include "media/audio/audio_parameters.h"
#include "media/audio/simple_sources.h"
#include "media/base/audio_bus.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -19,46 +16,36 @@ namespace media {
// Validate that the SineWaveAudioSource writes the expected values.
TEST(SimpleSources, SineWaveAudioSource) {
- const uint32 samples = 1024;
- const uint32 bytes_per_sample = 2;
- const int freq = 200;
+ static const uint32 samples = 1024;
+ static const uint32 bytes_per_sample = 2;
+ static const int freq = 200;
- SineWaveAudioSource source(1, freq, AudioParameters::kTelephoneSampleRate);
-
- scoped_ptr<AudioManager> audio_man(AudioManager::Create());
AudioParameters params(
- AudioParameters::AUDIO_MOCK, CHANNEL_LAYOUT_MONO,
- AudioParameters::kTelephoneSampleRate, bytes_per_sample * 8, samples);
- AudioOutputStream* oas = audio_man->MakeAudioOutputStream(params);
- ASSERT_TRUE(NULL != oas);
- EXPECT_TRUE(oas->Open());
-
- oas->Start(&source);
- oas->Stop();
+ AudioParameters::AUDIO_PCM_LINEAR, CHANNEL_LAYOUT_MONO,
+ AudioParameters::kTelephoneSampleRate, bytes_per_sample * 8, samples);
- ASSERT_TRUE(FakeAudioOutputStream::GetCurrentFakeStream());
- const AudioBus* last_audio_bus =
- FakeAudioOutputStream::GetCurrentFakeStream()->audio_bus();
- ASSERT_TRUE(NULL != last_audio_bus);
+ SineWaveAudioSource source(1, freq, params.sample_rate());
+ scoped_ptr<AudioBus> audio_bus = AudioBus::Create(params);
+ source.OnMoreData(audio_bus.get(), AudioBuffersState());
+ EXPECT_EQ(1, source.callbacks());
+ EXPECT_EQ(0, source.errors());
uint32 half_period = AudioParameters::kTelephoneSampleRate / (freq * 2);
// Spot test positive incursion of sine wave.
- EXPECT_NEAR(0, last_audio_bus->channel(0)[0],
+ EXPECT_NEAR(0, audio_bus->channel(0)[0],
std::numeric_limits<float>::epsilon());
- EXPECT_FLOAT_EQ(0.15643446f, last_audio_bus->channel(0)[1]);
- EXPECT_LT(last_audio_bus->channel(0)[1], last_audio_bus->channel(0)[2]);
- EXPECT_LT(last_audio_bus->channel(0)[2], last_audio_bus->channel(0)[3]);
+ EXPECT_FLOAT_EQ(0.15643446f, audio_bus->channel(0)[1]);
+ EXPECT_LT(audio_bus->channel(0)[1], audio_bus->channel(0)[2]);
+ EXPECT_LT(audio_bus->channel(0)[2], audio_bus->channel(0)[3]);
// Spot test negative incursion of sine wave.
- EXPECT_NEAR(0, last_audio_bus->channel(0)[half_period],
+ EXPECT_NEAR(0, audio_bus->channel(0)[half_period],
std::numeric_limits<float>::epsilon());
- EXPECT_FLOAT_EQ(-0.15643446f, last_audio_bus->channel(0)[half_period + 1]);
- EXPECT_GT(last_audio_bus->channel(0)[half_period + 1],
- last_audio_bus->channel(0)[half_period + 2]);
- EXPECT_GT(last_audio_bus->channel(0)[half_period + 2],
- last_audio_bus->channel(0)[half_period + 3]);
-
- oas->Close();
+ EXPECT_FLOAT_EQ(-0.15643446f, audio_bus->channel(0)[half_period + 1]);
+ EXPECT_GT(audio_bus->channel(0)[half_period + 1],
+ audio_bus->channel(0)[half_period + 2]);
+ EXPECT_GT(audio_bus->channel(0)[half_period + 2],
+ audio_bus->channel(0)[half_period + 3]);
}
TEST(SimpleSources, SineWaveAudioCapped) {
@@ -70,10 +57,22 @@ TEST(SimpleSources, SineWaveAudioCapped) {
scoped_ptr<AudioBus> audio_bus = AudioBus::Create(1, 2 * kSampleCap);
EXPECT_EQ(source.OnMoreData(
audio_bus.get(), AudioBuffersState()), kSampleCap);
+ EXPECT_EQ(1, source.callbacks());
EXPECT_EQ(source.OnMoreData(audio_bus.get(), AudioBuffersState()), 0);
+ EXPECT_EQ(2, source.callbacks());
source.Reset();
EXPECT_EQ(source.OnMoreData(
audio_bus.get(), AudioBuffersState()), kSampleCap);
+ EXPECT_EQ(3, source.callbacks());
+ EXPECT_EQ(0, source.errors());
+}
+
+TEST(SimpleSources, OnError) {
+ SineWaveAudioSource source(1, 200, AudioParameters::kTelephoneSampleRate);
+ source.OnError(NULL, 0);
+ EXPECT_EQ(1, source.errors());
+ source.OnError(NULL, 0);
+ EXPECT_EQ(2, source.errors());
}
} // namespace media
diff --git a/media/audio/win/audio_output_win_unittest.cc b/media/audio/win/audio_output_win_unittest.cc
index 030c8ab..40b4d81 100644
--- a/media/audio/win/audio_output_win_unittest.cc
+++ b/media/audio/win/audio_output_win_unittest.cc
@@ -167,23 +167,6 @@ class ReadOnlyMappedFile {
uint32 size_;
};
-// ============================================================================
-// Validate that the AudioManager::AUDIO_MOCK callbacks work.
-TEST(WinAudioTest, MockStreamBasicCallbacks) {
- scoped_ptr<AudioManager> audio_man(AudioManager::Create());
- AudioOutputStream* oas = audio_man->MakeAudioOutputStream(
- AudioParameters(AudioParameters::AUDIO_MOCK, CHANNEL_LAYOUT_STEREO, 8000,
- 8, 128));
- ASSERT_TRUE(NULL != oas);
- EXPECT_TRUE(oas->Open());
- TestSourceBasic source;
- oas->Start(&source);
- EXPECT_GT(source.callback_count(), 0);
- oas->Stop();
- oas->Close();
- EXPECT_EQ(0, source.had_error());
-}
-
// ===========================================================================
// Validation of AudioManager::AUDIO_PCM_LINEAR
//