summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorpfeldman@chromium.org <pfeldman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-03-15 06:00:21 +0000
committerpfeldman@chromium.org <pfeldman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-03-15 06:00:21 +0000
commit16cea069862a432f798ee6b01d7e4e399c9fddda (patch)
tree93b8f0219f99939cd68fa30ada349b81739e652d
parentfc555154e737b03dc0d8fc87b468e65baf5c14f7 (diff)
downloadchromium_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.cc67
-rw-r--r--media/audio/fake_audio_consumer.h61
-rw-r--r--media/audio/fake_audio_output_stream.cc34
-rw-r--r--media/audio/fake_audio_output_stream.h17
-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.cc129
-rw-r--r--media/audio/null_audio_sink.h42
-rw-r--r--media/filters/pipeline_integration_test_base.cc2
-rw-r--r--media/media.gyp7
-rw-r--r--media/tools/player_x11/player_x11.cc2
-rw-r--r--webkit/media/webmediaplayer_impl.cc2
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(),