diff options
author | dalecurtis@chromium.org <dalecurtis@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-10-18 22:12:05 +0000 |
---|---|---|
committer | dalecurtis@chromium.org <dalecurtis@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-10-18 22:12:05 +0000 |
commit | 2b392e59ab9a72858d224bad9e422fe40a696a69 (patch) | |
tree | 124296c1b0473baa88c55a66f95d22b3e4623f04 /media/audio | |
parent | c9b6e6a5b77de034a13c5d26b9084c40bb0e74c7 (diff) | |
download | chromium_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.cc | 10 | ||||
-rw-r--r-- | media/audio/audio_manager_base.cc | 40 | ||||
-rw-r--r-- | media/audio/audio_output_proxy_unittest.cc | 134 | ||||
-rw-r--r-- | media/audio/audio_output_resampler.cc | 16 | ||||
-rw-r--r-- | media/audio/audio_parameters.h | 4 | ||||
-rw-r--r-- | media/audio/fake_audio_output_stream.cc | 84 | ||||
-rw-r--r-- | media/audio/fake_audio_output_stream.h | 30 | ||||
-rw-r--r-- | media/audio/fake_audio_output_stream_unittest.cc | 67 | ||||
-rw-r--r-- | media/audio/ios/audio_manager_ios.mm | 2 | ||||
-rw-r--r-- | media/audio/simple_sources.cc | 10 | ||||
-rw-r--r-- | media/audio/simple_sources.h | 6 | ||||
-rw-r--r-- | media/audio/simple_sources_unittest.cc | 67 | ||||
-rw-r--r-- | media/audio/win/audio_output_win_unittest.cc | 17 |
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 // |