diff options
author | pfeldman@chromium.org <pfeldman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-03-15 06:00:21 +0000 |
---|---|---|
committer | pfeldman@chromium.org <pfeldman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-03-15 06:00:21 +0000 |
commit | 16cea069862a432f798ee6b01d7e4e399c9fddda (patch) | |
tree | 93b8f0219f99939cd68fa30ada349b81739e652d | |
parent | fc555154e737b03dc0d8fc87b468e65baf5c14f7 (diff) | |
download | chromium_src-16cea069862a432f798ee6b01d7e4e399c9fddda.zip chromium_src-16cea069862a432f798ee6b01d7e4e399c9fddda.tar.gz chromium_src-16cea069862a432f798ee6b01d7e4e399c9fddda.tar.bz2 |
Revert 188251 "Refactor FakeAudioOutputStream, NullAudioSink int..." for crashing multiple WebKit dbg tests.
> Refactor FakeAudioOutputStream, NullAudioSink into FakeAudioConsumer.
>
> We have two fake audio consumers: FakeAudioOutputStream on the browser
> side and NullAudioSink on the renderer side. At present we can't remove
> NullAudioSink since its used for PipelineIntegrationTests.
>
> This change collapses the common functionality between both consumers
> and as an added bonus replaces NullAudioSink's private thread in favor
> of sharing the MediaThread.
>
> BUG=none
> TEST=unit tests, remote desktop session w/o audio, --disable-audio.
>
>
> Review URL: https://chromiumcodereview.appspot.com/12334061
TBR=dalecurtis@chromium.org
Review URL: https://codereview.chromium.org/12642013
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@188265 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | media/audio/fake_audio_consumer.cc | 67 | ||||
-rw-r--r-- | media/audio/fake_audio_consumer.h | 61 | ||||
-rw-r--r-- | media/audio/fake_audio_output_stream.cc | 34 | ||||
-rw-r--r-- | media/audio/fake_audio_output_stream.h | 17 | ||||
-rw-r--r-- | media/audio/fake_audio_output_stream_unittest.cc (renamed from media/audio/fake_audio_consumer_unittest.cc) | 93 | ||||
-rw-r--r-- | media/audio/null_audio_sink.cc | 129 | ||||
-rw-r--r-- | media/audio/null_audio_sink.h | 42 | ||||
-rw-r--r-- | media/filters/pipeline_integration_test_base.cc | 2 | ||||
-rw-r--r-- | media/media.gyp | 7 | ||||
-rw-r--r-- | media/tools/player_x11/player_x11.cc | 2 | ||||
-rw-r--r-- | webkit/media/webmediaplayer_impl.cc | 2 |
11 files changed, 198 insertions, 258 deletions
diff --git a/media/audio/fake_audio_consumer.cc b/media/audio/fake_audio_consumer.cc deleted file mode 100644 index 95db9cd..0000000 --- a/media/audio/fake_audio_consumer.cc +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright (c) 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_consumer.h" - -#include "base/bind.h" -#include "base/bind_helpers.h" -#include "base/logging.h" -#include "base/message_loop.h" -#include "base/message_loop_proxy.h" -#include "media/base/audio_bus.h" - -namespace media { - -FakeAudioConsumer::FakeAudioConsumer( - const scoped_refptr<base::MessageLoopProxy>& message_loop, - const AudioParameters& params) - : message_loop_(message_loop), - audio_bus_(AudioBus::Create(params)), - buffer_duration_(base::TimeDelta::FromMicroseconds( - params.frames_per_buffer() * base::Time::kMicrosecondsPerSecond / - static_cast<float>(params.sample_rate()))) { - audio_bus_->Zero(); -} - -FakeAudioConsumer::~FakeAudioConsumer() { - DCHECK(read_cb_.is_null()); -} - -void FakeAudioConsumer::Start(const ReadCB& read_cb) { - DCHECK(message_loop_->BelongsToCurrentThread()); - DCHECK(read_cb_.is_null()); - DCHECK(!read_cb.is_null()); - read_cb_ = read_cb; - next_read_time_ = base::Time::Now(); - read_task_cb_.Reset(base::Bind( - &FakeAudioConsumer::DoRead, base::Unretained(this))); - message_loop_->PostTask(FROM_HERE, read_task_cb_.callback()); -} - -void FakeAudioConsumer::Stop() { - DCHECK(message_loop_->BelongsToCurrentThread()); - read_cb_.Reset(); - read_task_cb_.Cancel(); -} - -void FakeAudioConsumer::DoRead() { - DCHECK(message_loop_->BelongsToCurrentThread()); - DCHECK(!read_cb_.is_null()); - - read_cb_.Run(audio_bus_.get()); - - // Need to account for time spent here due to the cost of |read_cb_| as well - // as the imprecision of PostDelayedTask(). - base::Time now = base::Time::Now(); - base::TimeDelta delay = next_read_time_ + buffer_duration_ - now; - - // If we're behind, find the next nearest ontime interval. - if (delay < base::TimeDelta()) - delay += buffer_duration_ * (-delay / buffer_duration_ + 1); - next_read_time_ = now + delay; - - message_loop_->PostDelayedTask(FROM_HERE, read_task_cb_.callback(), delay); -} - -} // namespace media diff --git a/media/audio/fake_audio_consumer.h b/media/audio/fake_audio_consumer.h deleted file mode 100644 index 793e553..0000000 --- a/media/audio/fake_audio_consumer.h +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright (c) 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_CONSUMER_H_ -#define MEDIA_AUDIO_FAKE_AUDIO_CONSUMER_H_ - -#include "base/cancelable_callback.h" -#include "base/memory/scoped_ptr.h" -#include "base/time.h" -#include "media/audio/audio_parameters.h" - -namespace base { -class MessageLoopProxy; -} - -namespace media { -class AudioBus; - -// A fake audio consumer. Using a provided message loop, FakeAudioConsumer will -// simulate a real time consumer of audio data. -class MEDIA_EXPORT FakeAudioConsumer { - public: - // |message_loop| is the loop on which the ReadCB provided to Start() will be - // executed on. |params| is used to determine the frequency of callbacks. - FakeAudioConsumer(const scoped_refptr<base::MessageLoopProxy>& message_loop, - const AudioParameters& params); - ~FakeAudioConsumer(); - - // Start executing |read_cb| at a regular interval. Must be called on the - // message loop provided during construction. Stop() must be called before - // destroying FakeAudioConsumer. - typedef base::Callback<void(AudioBus* audio_bus)> ReadCB; - void Start(const ReadCB& read_cb); - - // Stop executing the ReadCB provided to Start(). Cancels any outstanding - // callbacks. Safe to call multiple times. Must be called on the message - // loop provided during construction. - void Stop(); - - private: - // Task that regularly calls |read_cb_| according to the playback rate as - // determined by the audio parameters given during construction. Runs on - // |message_loop_|. - void DoRead(); - - scoped_refptr<base::MessageLoopProxy> message_loop_; - ReadCB read_cb_; - scoped_ptr<AudioBus> audio_bus_; - base::TimeDelta buffer_duration_; - base::Time next_read_time_; - - // Used to post delayed tasks to the AudioThread that we can cancel. - base::CancelableClosure read_task_cb_; - - DISALLOW_COPY_AND_ASSIGN(FakeAudioConsumer); -}; - -} // namespace media - -#endif // MEDIA_AUDIO_FAKE_AUDIO_CONSUMER_H_ diff --git a/media/audio/fake_audio_output_stream.cc b/media/audio/fake_audio_output_stream.cc index 7b85eb0..8db548c 100644 --- a/media/audio/fake_audio_output_stream.cc +++ b/media/audio/fake_audio_output_stream.cc @@ -22,7 +22,11 @@ FakeAudioOutputStream::FakeAudioOutputStream(AudioManagerBase* manager, const AudioParameters& params) : audio_manager_(manager), callback_(NULL), - fake_consumer_(manager->GetMessageLoop(), params) { + audio_bus_(AudioBus::Create(params)), + buffer_duration_(base::TimeDelta::FromMicroseconds( + params.frames_per_buffer() * base::Time::kMicrosecondsPerSecond / + static_cast<float>(params.sample_rate()))) { + audio_bus_->Zero(); } FakeAudioOutputStream::~FakeAudioOutputStream() { @@ -37,14 +41,17 @@ bool FakeAudioOutputStream::Open() { void FakeAudioOutputStream::Start(AudioSourceCallback* callback) { DCHECK(audio_manager_->GetMessageLoop()->BelongsToCurrentThread()); callback_ = callback; - fake_consumer_.Start(base::Bind( - &FakeAudioOutputStream::CallOnMoreData, base::Unretained(this))); + next_read_time_ = base::Time::Now(); + on_more_data_cb_.Reset(base::Bind( + &FakeAudioOutputStream::OnMoreDataTask, base::Unretained(this))); + audio_manager_->GetMessageLoop()->PostTask( + FROM_HERE, on_more_data_cb_.callback()); } void FakeAudioOutputStream::Stop() { DCHECK(audio_manager_->GetMessageLoop()->BelongsToCurrentThread()); - fake_consumer_.Stop(); callback_ = NULL; + on_more_data_cb_.Cancel(); } void FakeAudioOutputStream::Close() { @@ -59,9 +66,24 @@ void FakeAudioOutputStream::GetVolume(double* volume) { *volume = 0; }; -void FakeAudioOutputStream::CallOnMoreData(AudioBus* audio_bus) { +void FakeAudioOutputStream::OnMoreDataTask() { DCHECK(audio_manager_->GetMessageLoop()->BelongsToCurrentThread()); - callback_->OnMoreData(audio_bus, AudioBuffersState()); + DCHECK(callback_); + + callback_->OnMoreData(audio_bus_.get(), AudioBuffersState()); + + // Need to account for time spent here due to the cost of OnMoreData() as well + // as the imprecision of PostDelayedTask(). + base::Time now = base::Time::Now(); + base::TimeDelta delay = next_read_time_ + buffer_duration_ - now; + + // If we're behind, find the next nearest ontime interval. + if (delay < base::TimeDelta()) + delay += buffer_duration_ * (-delay / buffer_duration_ + 1); + next_read_time_ = now + delay; + + audio_manager_->GetMessageLoop()->PostDelayedTask( + FROM_HERE, on_more_data_cb_.callback(), delay); } } // namespace media diff --git a/media/audio/fake_audio_output_stream.h b/media/audio/fake_audio_output_stream.h index 399d72b..ec3a592 100644 --- a/media/audio/fake_audio_output_stream.h +++ b/media/audio/fake_audio_output_stream.h @@ -5,10 +5,11 @@ #ifndef MEDIA_AUDIO_FAKE_AUDIO_OUTPUT_STREAM_H_ #define MEDIA_AUDIO_FAKE_AUDIO_OUTOUT_STREAM_H_ +#include "base/cancelable_callback.h" #include "base/memory/scoped_ptr.h" +#include "base/time.h" #include "media/audio/audio_io.h" #include "media/audio/audio_parameters.h" -#include "media/audio/fake_audio_consumer.h" namespace media { @@ -16,7 +17,6 @@ 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). -// Callbacks are driven on the AudioManager's message loop. class MEDIA_EXPORT FakeAudioOutputStream : public AudioOutputStream { public: static AudioOutputStream* MakeFakeStream(AudioManagerBase* manager, @@ -35,12 +35,19 @@ class MEDIA_EXPORT FakeAudioOutputStream : public AudioOutputStream { const AudioParameters& params); virtual ~FakeAudioOutputStream(); - // Task that periodically calls OnMoreData() to consume audio data. - void CallOnMoreData(AudioBus* audio_bus); + // 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_; AudioSourceCallback* callback_; - FakeAudioConsumer fake_consumer_; + scoped_ptr<AudioBus> audio_bus_; + base::TimeDelta buffer_duration_; + base::Time next_read_time_; + + // Used to post delayed tasks to the AudioThread that we can cancel. + base::CancelableClosure on_more_data_cb_; DISALLOW_COPY_AND_ASSIGN(FakeAudioOutputStream); }; diff --git a/media/audio/fake_audio_consumer_unittest.cc b/media/audio/fake_audio_output_stream_unittest.cc index 66077d5..6599b76 100644 --- a/media/audio/fake_audio_consumer_unittest.cc +++ b/media/audio/fake_audio_output_stream_unittest.cc @@ -4,9 +4,10 @@ #include "base/bind.h" #include "base/message_loop.h" +#include "base/synchronization/waitable_event.h" #include "base/time.h" -#include "media/audio/audio_buffers_state.h" -#include "media/audio/fake_audio_consumer.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" @@ -14,47 +15,47 @@ namespace media { static const int kTestCallbacks = 5; -class FakeAudioConsumerTest : public testing::Test { +class FakeAudioOutputStreamTest : public testing::Test { public: - FakeAudioConsumerTest() - : params_( + FakeAudioOutputStreamTest() + : audio_manager_(AudioManager::Create()), + params_( AudioParameters::AUDIO_FAKE, CHANNEL_LAYOUT_STEREO, 44100, 8, 128), - fake_consumer_(message_loop_.message_loop_proxy(), params_), - source_(params_.channels(), 200.0, params_.sample_rate()) { + source_(params_.channels(), 200.0, params_.sample_rate()), + done_(false, false) { + stream_ = audio_manager_->MakeAudioOutputStream(AudioParameters(params_)); + CHECK(stream_); + time_between_callbacks_ = base::TimeDelta::FromMicroseconds( params_.frames_per_buffer() * base::Time::kMicrosecondsPerSecond / static_cast<float>(params_.sample_rate())); } - virtual ~FakeAudioConsumerTest() {} - - void ConsumeData(AudioBus* audio_bus) { - source_.OnMoreData(audio_bus, AudioBuffersState()); - } + virtual ~FakeAudioOutputStreamTest() {} void RunOnAudioThread() { - ASSERT_TRUE(message_loop_.message_loop_proxy()->BelongsToCurrentThread()); - fake_consumer_.Start(base::Bind( - &FakeAudioConsumerTest::ConsumeData, base::Unretained(this))); + ASSERT_TRUE(audio_manager_->GetMessageLoop()->BelongsToCurrentThread()); + ASSERT_TRUE(stream_->Open()); + stream_->Start(&source_); } void RunOnceOnAudioThread() { - ASSERT_TRUE(message_loop_.message_loop_proxy()->BelongsToCurrentThread()); + ASSERT_TRUE(audio_manager_->GetMessageLoop()->BelongsToCurrentThread()); RunOnAudioThread(); // Start() should immediately post a task to run the source callback, so we // should end up with only a single callback being run. - message_loop_.PostTask(FROM_HERE, base::Bind( - &FakeAudioConsumerTest::EndTest, base::Unretained(this), 1)); + audio_manager_->GetMessageLoop()->PostTask(FROM_HERE, base::Bind( + &FakeAudioOutputStreamTest::EndTest, base::Unretained(this), 1)); } void StopStartOnAudioThread() { - ASSERT_TRUE(message_loop_.message_loop_proxy()->BelongsToCurrentThread()); - fake_consumer_.Stop(); - RunOnAudioThread(); + ASSERT_TRUE(audio_manager_->GetMessageLoop()->BelongsToCurrentThread()); + stream_->Stop(); + stream_->Start(&source_); } void TimeCallbacksOnAudioThread(int callbacks) { - ASSERT_TRUE(message_loop_.message_loop_proxy()->BelongsToCurrentThread()); + ASSERT_TRUE(audio_manager_->GetMessageLoop()->BelongsToCurrentThread()); if (source_.callbacks() == 0) { RunOnAudioThread(); @@ -63,8 +64,8 @@ class FakeAudioConsumerTest : public testing::Test { // Keep going until we've seen the requested number of callbacks. if (source_.callbacks() < callbacks) { - message_loop_.PostDelayedTask(FROM_HERE, base::Bind( - &FakeAudioConsumerTest::TimeCallbacksOnAudioThread, + audio_manager_->GetMessageLoop()->PostDelayedTask(FROM_HERE, base::Bind( + &FakeAudioOutputStreamTest::TimeCallbacksOnAudioThread, base::Unretained(this), callbacks), time_between_callbacks_ / 2); } else { end_time_ = base::Time::Now(); @@ -73,40 +74,44 @@ class FakeAudioConsumerTest : public testing::Test { } void EndTest(int callbacks) { - ASSERT_TRUE(message_loop_.message_loop_proxy()->BelongsToCurrentThread()); - fake_consumer_.Stop(); + ASSERT_TRUE(audio_manager_->GetMessageLoop()->BelongsToCurrentThread()); + stream_->Stop(); + stream_->Close(); EXPECT_LE(callbacks, source_.callbacks()); - message_loop_.PostTask(FROM_HERE, MessageLoop::QuitClosure()); + EXPECT_EQ(0, source_.errors()); + done_.Signal(); } protected: - MessageLoop message_loop_; + scoped_ptr<AudioManager> audio_manager_; AudioParameters params_; - FakeAudioConsumer fake_consumer_; + AudioOutputStream* stream_; SineWaveAudioSource source_; + base::WaitableEvent done_; base::Time start_time_; base::Time end_time_; base::TimeDelta time_between_callbacks_; private: - DISALLOW_COPY_AND_ASSIGN(FakeAudioConsumerTest); + DISALLOW_COPY_AND_ASSIGN(FakeAudioOutputStreamTest); }; // Ensure the fake audio stream runs on the audio thread and handles fires // callbacks to the AudioSourceCallback. -TEST_F(FakeAudioConsumerTest, FakeStreamBasicCallback) { - message_loop_.PostTask(FROM_HERE, base::Bind( - &FakeAudioConsumerTest::RunOnceOnAudioThread, +TEST_F(FakeAudioOutputStreamTest, FakeStreamBasicCallback) { + audio_manager_->GetMessageLoop()->PostTask(FROM_HERE, base::Bind( + &FakeAudioOutputStreamTest::RunOnceOnAudioThread, base::Unretained(this))); - message_loop_.Run(); + done_.Wait(); } // Ensure the time between callbacks is sane. -TEST_F(FakeAudioConsumerTest, TimeBetweenCallbacks) { - message_loop_.PostTask(FROM_HERE, base::Bind( - &FakeAudioConsumerTest::TimeCallbacksOnAudioThread, +TEST_F(FakeAudioOutputStreamTest, TimeBetweenCallbacks) { + audio_manager_->GetMessageLoop()->PostTask(FROM_HERE, base::Bind( + &FakeAudioOutputStreamTest::TimeCallbacksOnAudioThread, base::Unretained(this), kTestCallbacks)); - message_loop_.Run(); + + done_.Wait(); // There are only (kTestCallbacks - 1) intervals between kTestCallbacks. base::TimeDelta actual_time_between_callbacks = @@ -124,19 +129,19 @@ TEST_F(FakeAudioConsumerTest, TimeBetweenCallbacks) { // Ensure Start()/Stop() on the stream doesn't generate too many callbacks. See // http://crbug.com/159049 -TEST_F(FakeAudioConsumerTest, StartStopClearsCallbacks) { - message_loop_.PostTask(FROM_HERE, base::Bind( - &FakeAudioConsumerTest::TimeCallbacksOnAudioThread, +TEST_F(FakeAudioOutputStreamTest, StartStopClearsCallbacks) { + audio_manager_->GetMessageLoop()->PostTask(FROM_HERE, base::Bind( + &FakeAudioOutputStreamTest::TimeCallbacksOnAudioThread, base::Unretained(this), kTestCallbacks)); // Issue a Stop() / Start() in between expected callbacks to maximize the // chance of catching the FakeAudioOutputStream doing the wrong thing. - message_loop_.PostDelayedTask(FROM_HERE, base::Bind( - &FakeAudioConsumerTest::StopStartOnAudioThread, + audio_manager_->GetMessageLoop()->PostDelayedTask(FROM_HERE, base::Bind( + &FakeAudioOutputStreamTest::StopStartOnAudioThread, base::Unretained(this)), time_between_callbacks_ / 2); // EndTest() will ensure the proper number of callbacks have occurred. - message_loop_.Run(); + done_.Wait(); } } // namespace media diff --git a/media/audio/null_audio_sink.cc b/media/audio/null_audio_sink.cc index 37d3ce0..c93ceb1 100644 --- a/media/audio/null_audio_sink.cc +++ b/media/audio/null_audio_sink.cc @@ -5,34 +5,30 @@ #include "media/audio/null_audio_sink.h" #include "base/bind.h" -#include "base/message_loop_proxy.h" #include "base/stringprintf.h" #include "base/sys_byteorder.h" -#include "media/audio/fake_audio_consumer.h" +#include "base/threading/platform_thread.h" namespace media { -NullAudioSink::NullAudioSink( - const scoped_refptr<base::MessageLoopProxy>& message_loop) +NullAudioSink::NullAudioSink() : initialized_(false), + playing_(false), callback_(NULL), - hash_audio_for_testing_(false), - channels_(0), - message_loop_(message_loop) { + thread_("NullAudioThread"), + hash_audio_for_testing_(false) { } -NullAudioSink::~NullAudioSink() {} - void NullAudioSink::Initialize(const AudioParameters& params, RenderCallback* callback) { DCHECK(!initialized_); + params_ = params; - fake_consumer_.reset(new FakeAudioConsumer(message_loop_, params)); + audio_bus_ = AudioBus::Create(params_); if (hash_audio_for_testing_) { - channels_ = params.channels(); - md5_channel_contexts_.reset(new base::MD5Context[params.channels()]); - for (int i = 0; i < params.channels(); i++) + md5_channel_contexts_.reset(new base::MD5Context[params_.channels()]); + for (int i = 0; i < params_.channels(); i++) base::MD5Init(&md5_channel_contexts_[i]); } @@ -41,27 +37,24 @@ void NullAudioSink::Initialize(const AudioParameters& params, } void NullAudioSink::Start() { - DCHECK(message_loop_->BelongsToCurrentThread()); + if (!thread_.Start()) + return; + + thread_.message_loop()->PostTask(FROM_HERE, base::Bind( + &NullAudioSink::FillBufferTask, this)); } void NullAudioSink::Stop() { - DCHECK(message_loop_->BelongsToCurrentThread()); - - // Stop may be called at any time, so we have to check before stopping. - if (fake_consumer_) - fake_consumer_->Stop(); + SetPlaying(false); + thread_.Stop(); } void NullAudioSink::Play() { - DCHECK(message_loop_->BelongsToCurrentThread()); - DCHECK(initialized_); - fake_consumer_->Start(base::Bind( - &NullAudioSink::CallRender, base::Unretained(this))); + SetPlaying(true); } void NullAudioSink::Pause(bool /* flush */) { - DCHECK(message_loop_->BelongsToCurrentThread()); - fake_consumer_->Stop(); + SetPlaying(false); } bool NullAudioSink::SetVolume(double volume) { @@ -69,24 +62,54 @@ bool NullAudioSink::SetVolume(double volume) { return volume == 0.0; } -void NullAudioSink::CallRender(AudioBus* audio_bus) { - DCHECK(message_loop_->BelongsToCurrentThread()); +void NullAudioSink::SetPlaying(bool is_playing) { + base::AutoLock auto_lock(lock_); + playing_ = is_playing; +} - int frames_received = callback_->Render(audio_bus, 0); - if (!hash_audio_for_testing_ || frames_received <= 0) - return; +NullAudioSink::~NullAudioSink() { + DCHECK(!thread_.IsRunning()); +} - DCHECK_EQ(sizeof(float), sizeof(uint32)); - int channels = audio_bus->channels(); - for (int channel_idx = 0; channel_idx < channels; ++channel_idx) { - float* channel = audio_bus->channel(channel_idx); - for (int frame_idx = 0; frame_idx < frames_received; frame_idx++) { - // Convert float to uint32 w/o conversion loss. - uint32 frame = base::ByteSwapToLE32(bit_cast<uint32>(channel[frame_idx])); - base::MD5Update(&md5_channel_contexts_[channel_idx], base::StringPiece( - reinterpret_cast<char*>(&frame), sizeof(frame))); +void NullAudioSink::FillBufferTask() { + base::AutoLock auto_lock(lock_); + + base::TimeDelta delay; + // Only consume buffers when actually playing. + if (playing_) { + int frames_received = callback_->Render(audio_bus_.get(), 0); + int frames_per_millisecond = + params_.sample_rate() / base::Time::kMillisecondsPerSecond; + + if (hash_audio_for_testing_ && frames_received > 0) { + DCHECK_EQ(sizeof(float), sizeof(uint32)); + int channels = audio_bus_->channels(); + for (int channel_idx = 0; channel_idx < channels; ++channel_idx) { + float* channel = audio_bus_->channel(channel_idx); + for (int frame_idx = 0; frame_idx < frames_received; frame_idx++) { + // Convert float to uint32 w/o conversion loss. + uint32 frame = base::ByteSwapToLE32( + bit_cast<uint32>(channel[frame_idx])); + base::MD5Update( + &md5_channel_contexts_[channel_idx], base::StringPiece( + reinterpret_cast<char*>(&frame), sizeof(frame))); + } + } } + + // Calculate our sleep duration. + delay = base::TimeDelta::FromMilliseconds( + frames_received / frames_per_millisecond); + } else { + // If paused, sleep for 10 milliseconds before polling again. + delay = base::TimeDelta::FromMilliseconds(10); } + + // Sleep for at least one millisecond so we don't spin the CPU. + MessageLoop::current()->PostDelayedTask( + FROM_HERE, + base::Bind(&NullAudioSink::FillBufferTask, this), + std::max(delay, base::TimeDelta::FromMilliseconds(1))); } void NullAudioSink::StartAudioHashForTesting() { @@ -97,22 +120,24 @@ void NullAudioSink::StartAudioHashForTesting() { std::string NullAudioSink::GetAudioHashForTesting() { DCHECK(hash_audio_for_testing_); - base::MD5Digest digest; - if (channels_ == 0) { - // If initialize failed or was never called, ensure we return an empty hash. - base::MD5Context context; - base::MD5Init(&context); - base::MD5Final(&digest, &context); + // If initialize failed or was never called, ensure we return an empty hash. + int channels = 1; + if (!initialized_) { + md5_channel_contexts_.reset(new base::MD5Context[1]); + base::MD5Init(&md5_channel_contexts_[0]); } else { - // Hash all channels into the first channel. - for (int i = 1; i < channels_; i++) { - base::MD5Final(&digest, &md5_channel_contexts_[i]); - base::MD5Update(&md5_channel_contexts_[0], base::StringPiece( - reinterpret_cast<char*>(&digest), sizeof(base::MD5Digest))); - } - base::MD5Final(&digest, &md5_channel_contexts_[0]); + channels = audio_bus_->channels(); + } + + // Hash all channels into the first channel. + base::MD5Digest digest; + for (int i = 1; i < channels; i++) { + base::MD5Final(&digest, &md5_channel_contexts_[i]); + base::MD5Update(&md5_channel_contexts_[0], base::StringPiece( + reinterpret_cast<char*>(&digest), sizeof(base::MD5Digest))); } + base::MD5Final(&digest, &md5_channel_contexts_[0]); return base::MD5DigestToBase16(digest); } diff --git a/media/audio/null_audio_sink.h b/media/audio/null_audio_sink.h index f892ab4..c528473 100644 --- a/media/audio/null_audio_sink.h +++ b/media/audio/null_audio_sink.h @@ -2,25 +2,29 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef MEDIA_AUDIO_NULL_AUDIO_SINK_H_ -#define MEDIA_AUDIO_NULL_AUDIO_SINK_H_ +#ifndef MEDIA_FILTERS_NULL_AUDIO_RENDERER_H_ +#define MEDIA_FILTERS_NULL_AUDIO_RENDERER_H_ + +// NullAudioSink effectively uses an extra thread to "throw away" the +// audio data at a rate resembling normal playback speed. It's just like +// decoding to /dev/null! +// +// NullAudioSink can also be used in situations where the client has no +// audio device or we haven't written an audio implementation for a particular +// platform yet. #include "base/md5.h" #include "base/memory/scoped_ptr.h" +#include "base/threading/thread.h" #include "media/base/audio_renderer_sink.h" -namespace base { -class MessageLoopProxy; -} - namespace media { class AudioBus; -class FakeAudioConsumer; class MEDIA_EXPORT NullAudioSink : NON_EXPORTED_BASE(public AudioRendererSink) { public: - NullAudioSink(const scoped_refptr<base::MessageLoopProxy>& message_loop); + NullAudioSink(); // AudioRendererSink implementation. virtual void Initialize(const AudioParameters& params, @@ -42,23 +46,31 @@ class MEDIA_EXPORT NullAudioSink virtual ~NullAudioSink(); private: - // Task that periodically calls Render() to consume audio data. - void CallRender(AudioBus* audio_bus); + // Audio thread task that periodically calls FillBuffer() to consume + // audio data. + void FillBufferTask(); + + void SetPlaying(bool is_playing); + // A buffer passed to FillBuffer to advance playback. + scoped_ptr<AudioBus> audio_bus_; + + AudioParameters params_; bool initialized_; + bool playing_; RenderCallback* callback_; + // Separate thread used to throw away data. + base::Thread thread_; + base::Lock lock_; + // Controls whether or not a running MD5 hash is computed for audio frames. bool hash_audio_for_testing_; - int channels_; scoped_array<base::MD5Context> md5_channel_contexts_; - scoped_refptr<base::MessageLoopProxy> message_loop_; - scoped_ptr<FakeAudioConsumer> fake_consumer_; - DISALLOW_COPY_AND_ASSIGN(NullAudioSink); }; } // namespace media -#endif // MEDIA_AUDIO_NULL_AUDIO_SINK_H_ +#endif // MEDIA_FILTERS_NULL_AUDIO_RENDERER_H_ diff --git a/media/filters/pipeline_integration_test_base.cc b/media/filters/pipeline_integration_test_base.cc index f5d0c40..ed47c41 100644 --- a/media/filters/pipeline_integration_test_base.cc +++ b/media/filters/pipeline_integration_test_base.cc @@ -246,7 +246,7 @@ PipelineIntegrationTestBase::CreateFilterCollection( !hashing_enabled_)); collection->SetVideoRenderer(renderer.Pass()); - audio_sink_ = new NullAudioSink(message_loop_.message_loop_proxy()); + audio_sink_ = new NullAudioSink(); AudioRendererImpl* audio_renderer_impl = new AudioRendererImpl( message_loop_.message_loop_proxy(), audio_sink_, diff --git a/media/media.gyp b/media/media.gyp index d36628c..2b64447 100644 --- a/media/media.gyp +++ b/media/media.gyp @@ -105,8 +105,6 @@ 'audio/cross_process_notification.h', 'audio/cross_process_notification_posix.cc', 'audio/cross_process_notification_win.cc', - 'audio/fake_audio_consumer.cc', - 'audio/fake_audio_consumer.h', 'audio/fake_audio_input_stream.cc', 'audio/fake_audio_input_stream.h', 'audio/fake_audio_output_stream.cc', @@ -528,7 +526,6 @@ ['include', '^audio/audio_manager\\.'], ['include', '^audio/audio_manager_base\\.'], ['include', '^audio/audio_parameters\\.'], - ['include', '^audio/fake_audio_consumer\\.'], ['include', '^audio/fake_audio_input_stream\\.'], ['include', '^audio/fake_audio_output_stream\\.'], ['include', '^base/audio_bus\\.'], @@ -689,7 +686,7 @@ 'message': 'Generating Pulse stubs for dynamic loading.', }, ], - 'conditions': [ + 'conditions': [ # Linux/Solaris need libdl for dlopen() and friends. ['OS == "linux" or OS == "solaris"', { 'link_settings': { @@ -853,7 +850,7 @@ 'audio/audio_parameters_unittest.cc', 'audio/audio_util_unittest.cc', 'audio/cross_process_notification_unittest.cc', - 'audio/fake_audio_consumer_unittest.cc', + 'audio/fake_audio_output_stream_unittest.cc', 'audio/ios/audio_manager_ios_unittest.cc', 'audio/linux/alsa_output_unittest.cc', 'audio/mac/audio_device_listener_mac_unittest.cc', diff --git a/media/tools/player_x11/player_x11.cc b/media/tools/player_x11/player_x11.cc index 7b6e5c5..6fc4e47 100644 --- a/media/tools/player_x11/player_x11.cc +++ b/media/tools/player_x11/player_x11.cc @@ -128,7 +128,7 @@ bool InitPipeline(const scoped_refptr<base::MessageLoopProxy>& message_loop, scoped_ptr<media::AudioRenderer> audio_renderer(new media::AudioRendererImpl( message_loop, - new media::NullAudioSink(message_loop), + new media::NullAudioSink(), media::SetDecryptorReadyCB())); collection->SetAudioRenderer(audio_renderer.Pass()); diff --git a/webkit/media/webmediaplayer_impl.cc b/webkit/media/webmediaplayer_impl.cc index f4e3456..981ed30 100644 --- a/webkit/media/webmediaplayer_impl.cc +++ b/webkit/media/webmediaplayer_impl.cc @@ -201,7 +201,7 @@ WebMediaPlayerImpl::WebMediaPlayerImpl( // Create default audio renderer using the null sink if no sink was provided. audio_source_provider_ = new WebAudioSourceProviderImpl( params.audio_renderer_sink() ? params.audio_renderer_sink() : - new media::NullAudioSink(media_thread_.message_loop_proxy())); + new media::NullAudioSink()); scoped_ptr<media::AudioRenderer> audio_renderer( new media::AudioRendererImpl( media_thread_.message_loop_proxy(), |