summaryrefslogtreecommitdiffstats
path: root/media/audio
diff options
context:
space:
mode:
authormiu@chromium.org <miu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-06-22 14:18:29 +0000
committermiu@chromium.org <miu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-06-22 14:18:29 +0000
commit6c1c700baf3bc0920f35d145ce3b9aa4160ebafe (patch)
tree392d488b0013a227990f708a8fafbc8bc4c5cb47 /media/audio
parent9661fdbf87d2d4fb8208b464a760591266a87fa6 (diff)
downloadchromium_src-6c1c700baf3bc0920f35d145ce3b9aa4160ebafe.zip
chromium_src-6c1c700baf3bc0920f35d145ce3b9aa4160ebafe.tar.gz
chromium_src-6c1c700baf3bc0920f35d145ce3b9aa4160ebafe.tar.bz2
Re-land r207105 (Mac audio capture threading fix) with unit test memory leak issue resolved.
Last attempt: https://codereview.chromium.org/17122006/ BUG=249089 Review URL: https://chromiumcodereview.appspot.com/17334005 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@208033 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'media/audio')
-rw-r--r--media/audio/fake_audio_consumer.cc131
-rw-r--r--media/audio/fake_audio_consumer.h45
-rw-r--r--media/audio/fake_audio_consumer_unittest.cc1
-rw-r--r--media/audio/virtual_audio_input_stream.cc50
-rw-r--r--media/audio/virtual_audio_input_stream.h24
-rw-r--r--media/audio/virtual_audio_input_stream_unittest.cc38
-rw-r--r--media/audio/virtual_audio_output_stream.cc33
-rw-r--r--media/audio/virtual_audio_output_stream.h9
-rw-r--r--media/audio/virtual_audio_output_stream_unittest.cc5
9 files changed, 235 insertions, 101 deletions
diff --git a/media/audio/fake_audio_consumer.cc b/media/audio/fake_audio_consumer.cc
index 5232170..1608592 100644
--- a/media/audio/fake_audio_consumer.cc
+++ b/media/audio/fake_audio_consumer.cc
@@ -6,50 +6,145 @@
#include "base/bind.h"
#include "base/bind_helpers.h"
+#include "base/cancelable_callback.h"
#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
#include "base/message_loop.h"
#include "base/message_loop/message_loop_proxy.h"
+#include "base/synchronization/lock.h"
+#include "base/threading/thread_checker.h"
+#include "base/time.h"
+#include "media/audio/audio_parameters.h"
#include "media/base/audio_bus.h"
namespace media {
+class FakeAudioConsumer::Worker
+ : public base::RefCountedThreadSafe<FakeAudioConsumer::Worker> {
+ public:
+ Worker(const scoped_refptr<base::MessageLoopProxy>& worker_loop,
+ const AudioParameters& params);
+
+ bool IsStopped();
+ void Start(const ReadCB& read_cb);
+ void Stop();
+
+ private:
+ friend class base::RefCountedThreadSafe<Worker>;
+ ~Worker();
+
+ // Initialize and start regular calls to DoRead() on the worker thread.
+ void DoStart();
+
+ // Cancel any delayed callbacks to DoRead() in the worker loop's queue.
+ void DoCancel();
+
+ // Task that regularly calls |read_cb_| according to the playback rate as
+ // determined by the audio parameters given during construction. Runs on
+ // the worker loop.
+ void DoRead();
+
+ const scoped_refptr<base::MessageLoopProxy> worker_loop_;
+ const scoped_ptr<AudioBus> audio_bus_;
+ const base::TimeDelta buffer_duration_;
+
+ base::Lock read_cb_lock_; // Held while mutating or running |read_cb_|.
+ ReadCB read_cb_;
+ base::TimeTicks next_read_time_;
+
+ // Used to cancel any delayed tasks still inside the worker loop's queue.
+ base::CancelableClosure read_task_cb_;
+
+ base::ThreadChecker thread_checker_;
+
+ DISALLOW_COPY_AND_ASSIGN(Worker);
+};
+
FakeAudioConsumer::FakeAudioConsumer(
- const scoped_refptr<base::MessageLoopProxy>& message_loop,
+ const scoped_refptr<base::MessageLoopProxy>& worker_loop,
+ const AudioParameters& params)
+ : worker_(new Worker(worker_loop, params)) {
+}
+
+FakeAudioConsumer::~FakeAudioConsumer() {
+ DCHECK(worker_->IsStopped());
+}
+
+void FakeAudioConsumer::Start(const ReadCB& read_cb) {
+ DCHECK(worker_->IsStopped());
+ worker_->Start(read_cb);
+}
+
+void FakeAudioConsumer::Stop() {
+ worker_->Stop();
+}
+
+FakeAudioConsumer::Worker::Worker(
+ const scoped_refptr<base::MessageLoopProxy>& worker_loop,
const AudioParameters& params)
- : message_loop_(message_loop),
+ : worker_loop_(worker_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();
+
+ // Worker can be constructed on any thread, but will DCHECK that its
+ // Start/Stop methods are called from the same thread.
+ thread_checker_.DetachFromThread();
}
-FakeAudioConsumer::~FakeAudioConsumer() {
+FakeAudioConsumer::Worker::~Worker() {
DCHECK(read_cb_.is_null());
}
-void FakeAudioConsumer::Start(const ReadCB& read_cb) {
- DCHECK(message_loop_->BelongsToCurrentThread());
- DCHECK(read_cb_.is_null());
+bool FakeAudioConsumer::Worker::IsStopped() {
+ base::AutoLock scoped_lock(read_cb_lock_);
+ return read_cb_.is_null();
+}
+
+void FakeAudioConsumer::Worker::Start(const ReadCB& read_cb) {
+ DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(!read_cb.is_null());
- read_cb_ = read_cb;
+ {
+ base::AutoLock scoped_lock(read_cb_lock_);
+ DCHECK(read_cb_.is_null());
+ read_cb_ = read_cb;
+ }
+ worker_loop_->PostTask(FROM_HERE, base::Bind(&Worker::DoStart, this));
+}
+
+void FakeAudioConsumer::Worker::DoStart() {
+ DCHECK(worker_loop_->BelongsToCurrentThread());
next_read_time_ = base::TimeTicks::Now();
- read_task_cb_.Reset(base::Bind(
- &FakeAudioConsumer::DoRead, base::Unretained(this)));
- message_loop_->PostTask(FROM_HERE, read_task_cb_.callback());
+ read_task_cb_.Reset(base::Bind(&Worker::DoRead, this));
+ read_task_cb_.callback().Run();
}
-void FakeAudioConsumer::Stop() {
- DCHECK(message_loop_->BelongsToCurrentThread());
- read_cb_.Reset();
+void FakeAudioConsumer::Worker::Stop() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ {
+ base::AutoLock scoped_lock(read_cb_lock_);
+ if (read_cb_.is_null())
+ return;
+ read_cb_.Reset();
+ }
+ worker_loop_->PostTask(FROM_HERE, base::Bind(&Worker::DoCancel, this));
+}
+
+void FakeAudioConsumer::Worker::DoCancel() {
+ DCHECK(worker_loop_->BelongsToCurrentThread());
read_task_cb_.Cancel();
}
-void FakeAudioConsumer::DoRead() {
- DCHECK(message_loop_->BelongsToCurrentThread());
- DCHECK(!read_cb_.is_null());
+void FakeAudioConsumer::Worker::DoRead() {
+ DCHECK(worker_loop_->BelongsToCurrentThread());
- read_cb_.Run(audio_bus_.get());
+ {
+ base::AutoLock scoped_lock(read_cb_lock_);
+ if (!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().
@@ -61,7 +156,7 @@ void FakeAudioConsumer::DoRead() {
delay += buffer_duration_ * (-delay / buffer_duration_ + 1);
next_read_time_ = now + delay;
- message_loop_->PostDelayedTask(FROM_HERE, read_task_cb_.callback(), delay);
+ worker_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
index 57da74f..5037356 100644
--- a/media/audio/fake_audio_consumer.h
+++ b/media/audio/fake_audio_consumer.h
@@ -5,10 +5,9 @@
#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"
+#include "base/callback_forward.h"
+#include "base/memory/ref_counted.h"
+#include "media/base/media_export.h"
namespace base {
class MessageLoopProxy;
@@ -16,42 +15,36 @@ class MessageLoopProxy;
namespace media {
class AudioBus;
+class AudioParameters;
// 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,
+ // |worker_loop| is the loop on which the ReadCB provided to Start() will be
+ // executed on. This may or may not be the be for the same thread that
+ // invokes the Start/Stop methods.
+ // |params| is used to determine the frequency of callbacks.
+ FakeAudioConsumer(const scoped_refptr<base::MessageLoopProxy>& worker_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.
+ // Start executing |read_cb| at a regular intervals. Stop() must be called by
+ // the same thread 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.
+ // Stop executing the ReadCB provided to Start(). Blocks until the worker
+ // loop is not inside a ReadCB invocation. Safe to call multiple times. Must
+ // be called on the same thread that called Start().
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::TimeTicks next_read_time_;
-
- // Used to post delayed tasks to the AudioThread that we can cancel.
- base::CancelableClosure read_task_cb_;
+ // All state and implementation is kept within this ref-counted class because
+ // cancellation of posted tasks must happen on the worker thread some time
+ // after the call to Stop() (on the main thread) returns.
+ class Worker;
+ const scoped_refptr<Worker> worker_;
DISALLOW_COPY_AND_ASSIGN(FakeAudioConsumer);
};
diff --git a/media/audio/fake_audio_consumer_unittest.cc b/media/audio/fake_audio_consumer_unittest.cc
index 9ffc149..f218cb6 100644
--- a/media/audio/fake_audio_consumer_unittest.cc
+++ b/media/audio/fake_audio_consumer_unittest.cc
@@ -6,6 +6,7 @@
#include "base/message_loop.h"
#include "base/time.h"
#include "media/audio/audio_buffers_state.h"
+#include "media/audio/audio_parameters.h"
#include "media/audio/fake_audio_consumer.h"
#include "media/audio/simple_sources.h"
#include "testing/gtest/include/gtest/gtest.h"
diff --git a/media/audio/virtual_audio_input_stream.cc b/media/audio/virtual_audio_input_stream.cc
index b03bb83..d79ca9f 100644
--- a/media/audio/virtual_audio_input_stream.cc
+++ b/media/audio/virtual_audio_input_stream.cc
@@ -8,6 +8,7 @@
#include <utility>
#include "base/bind.h"
+#include "base/message_loop.h"
#include "base/message_loop/message_loop_proxy.h"
#include "media/audio/virtual_audio_output_stream.h"
@@ -49,50 +50,60 @@ class LoopbackAudioConverter : public AudioConverter::InputCallback {
VirtualAudioInputStream::VirtualAudioInputStream(
const AudioParameters& params,
- const scoped_refptr<base::MessageLoopProxy>& message_loop,
+ const scoped_refptr<base::MessageLoopProxy>& worker_loop,
const AfterCloseCallback& after_close_cb)
- : message_loop_(message_loop),
+ : worker_loop_(worker_loop),
after_close_cb_(after_close_cb),
callback_(NULL),
buffer_(new uint8[params.GetBytesPerBuffer()]),
params_(params),
mixer_(params_, params_, false),
num_attached_output_streams_(0),
- fake_consumer_(message_loop_, params_) {
+ fake_consumer_(worker_loop_, params_) {
DCHECK(params_.IsValid());
- DCHECK(message_loop_.get());
+ DCHECK(worker_loop_.get());
+
+ // VAIS can be constructed on any thread, but will DCHECK that all
+ // AudioInputStream methods are called from the same thread.
+ thread_checker_.DetachFromThread();
}
VirtualAudioInputStream::~VirtualAudioInputStream() {
+ DCHECK(!callback_);
+
+ // Sanity-check: Contract for Add/RemoveOutputStream() requires that all
+ // output streams be removed before VirtualAudioInputStream is destroyed.
+ DCHECK_EQ(0, num_attached_output_streams_);
+
for (AudioConvertersMap::iterator it = converters_.begin();
it != converters_.end(); ++it) {
delete it->second;
}
-
- DCHECK_EQ(0, num_attached_output_streams_);
}
bool VirtualAudioInputStream::Open() {
- DCHECK(message_loop_->BelongsToCurrentThread());
+ DCHECK(thread_checker_.CalledOnValidThread());
memset(buffer_.get(), 0, params_.GetBytesPerBuffer());
return true;
}
void VirtualAudioInputStream::Start(AudioInputCallback* callback) {
- DCHECK(message_loop_->BelongsToCurrentThread());
+ DCHECK(thread_checker_.CalledOnValidThread());
callback_ = callback;
fake_consumer_.Start(base::Bind(
- &VirtualAudioInputStream::ReadAudio, base::Unretained(this)));
+ &VirtualAudioInputStream::PumpAudio, base::Unretained(this)));
}
void VirtualAudioInputStream::Stop() {
- DCHECK(message_loop_->BelongsToCurrentThread());
+ DCHECK(thread_checker_.CalledOnValidThread());
fake_consumer_.Stop();
}
void VirtualAudioInputStream::AddOutputStream(
VirtualAudioOutputStream* stream, const AudioParameters& output_params) {
- DCHECK(message_loop_->BelongsToCurrentThread());
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ base::AutoLock scoped_lock(converter_network_lock_);
AudioConvertersMap::iterator converter = converters_.find(output_params);
if (converter == converters_.end()) {
@@ -110,7 +121,9 @@ void VirtualAudioInputStream::AddOutputStream(
void VirtualAudioInputStream::RemoveOutputStream(
VirtualAudioOutputStream* stream, const AudioParameters& output_params) {
- DCHECK(message_loop_->BelongsToCurrentThread());
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ base::AutoLock scoped_lock(converter_network_lock_);
DCHECK(converters_.find(output_params) != converters_.end());
converters_[output_params]->RemoveInput(stream);
@@ -119,15 +132,17 @@ void VirtualAudioInputStream::RemoveOutputStream(
DCHECK_LE(0, num_attached_output_streams_);
}
-void VirtualAudioInputStream::ReadAudio(AudioBus* audio_bus) {
- DCHECK(message_loop_->BelongsToCurrentThread());
+void VirtualAudioInputStream::PumpAudio(AudioBus* audio_bus) {
+ DCHECK(worker_loop_->BelongsToCurrentThread());
DCHECK(callback_);
- mixer_.Convert(audio_bus);
+ {
+ base::AutoLock scoped_lock(converter_network_lock_);
+ mixer_.Convert(audio_bus);
+ }
audio_bus->ToInterleaved(params_.frames_per_buffer(),
params_.bits_per_sample() / 8,
buffer_.get());
-
callback_->OnData(this,
buffer_.get(),
params_.GetBytesPerBuffer(),
@@ -136,8 +151,9 @@ void VirtualAudioInputStream::ReadAudio(AudioBus* audio_bus) {
}
void VirtualAudioInputStream::Close() {
- DCHECK(message_loop_->BelongsToCurrentThread());
+ DCHECK(thread_checker_.CalledOnValidThread());
+ Stop(); // Make sure callback_ is no longer being used.
if (callback_) {
callback_->OnClose(this);
callback_ = NULL;
diff --git a/media/audio/virtual_audio_input_stream.h b/media/audio/virtual_audio_input_stream.h
index 72ca86c..53a1073 100644
--- a/media/audio/virtual_audio_input_stream.h
+++ b/media/audio/virtual_audio_input_stream.h
@@ -10,6 +10,8 @@
#include "base/gtest_prod_util.h"
#include "base/memory/scoped_ptr.h"
+#include "base/synchronization/lock.h"
+#include "base/threading/thread_checker.h"
#include "media/audio/audio_io.h"
#include "media/audio/audio_parameters.h"
#include "media/audio/fake_audio_consumer.h"
@@ -34,10 +36,12 @@ class MEDIA_EXPORT VirtualAudioInputStream : public AudioInputStream {
AfterCloseCallback;
// Construct a target for audio loopback which mixes multiple data streams
- // into a single stream having the given |params|.
+ // into a single stream having the given |params|. |worker_loop| is the loop
+ // on which AudioInputCallback methods are called and may or may not be the
+ // single thread that invokes the AudioInputStream methods.
VirtualAudioInputStream(
const AudioParameters& params,
- const scoped_refptr<base::MessageLoopProxy>& message_loop,
+ const scoped_refptr<base::MessageLoopProxy>& worker_loop,
const AfterCloseCallback& after_close_cb);
virtual ~VirtualAudioInputStream();
@@ -69,12 +73,12 @@ class MEDIA_EXPORT VirtualAudioInputStream : public AudioInputStream {
typedef std::map<AudioParameters, LoopbackAudioConverter*> AudioConvertersMap;
- // When Start() is called on this class, we continuously schedule this
- // callback to render audio using any attached VirtualAudioOutputStreams until
- // Stop() is called.
- void ReadAudio(AudioBus* audio_bus);
+ // Pulls audio data from all attached VirtualAudioOutputStreams, mixes and
+ // converts the streams into one, and pushes the result to |callback_|.
+ // Invoked on the worker thread.
+ void PumpAudio(AudioBus* audio_bus);
- const scoped_refptr<base::MessageLoopProxy> message_loop_;
+ const scoped_refptr<base::MessageLoopProxy> worker_loop_;
AfterCloseCallback after_close_cb_;
@@ -84,6 +88,10 @@ class MEDIA_EXPORT VirtualAudioInputStream : public AudioInputStream {
scoped_ptr<uint8[]> buffer_;
AudioParameters params_;
+ // Guards concurrent access to the converter network: converters_, mixer_, and
+ // num_attached_output_streams_.
+ base::Lock converter_network_lock_;
+
// AudioConverters associated with the attached VirtualAudioOutputStreams,
// partitioned by common AudioParameters.
AudioConvertersMap converters_;
@@ -98,6 +106,8 @@ class MEDIA_EXPORT VirtualAudioInputStream : public AudioInputStream {
// Handles callback timing for consumption of audio data.
FakeAudioConsumer fake_consumer_;
+ base::ThreadChecker thread_checker_;
+
DISALLOW_COPY_AND_ASSIGN(VirtualAudioInputStream);
};
diff --git a/media/audio/virtual_audio_input_stream_unittest.cc b/media/audio/virtual_audio_input_stream_unittest.cc
index 144ce82..d444cf7 100644
--- a/media/audio/virtual_audio_input_stream_unittest.cc
+++ b/media/audio/virtual_audio_input_stream_unittest.cc
@@ -97,10 +97,11 @@ class TestAudioSource : public SineWaveAudioSource {
} // namespace
-class VirtualAudioInputStreamTest : public testing::Test {
+class VirtualAudioInputStreamTest : public testing::TestWithParam<bool> {
public:
VirtualAudioInputStreamTest()
: audio_thread_(new base::Thread("AudioThread")),
+ worker_thread_(new base::Thread("AudioWorkerThread")),
stream_(NULL),
closed_stream_(false, false) {
audio_thread_->Start();
@@ -115,8 +116,9 @@ class VirtualAudioInputStreamTest : public testing::Test {
}
void Create() {
+ const bool worker_is_separate_thread = GetParam();
stream_ = new VirtualAudioInputStream(
- kParams, audio_message_loop_,
+ kParams, GetWorkerLoop(worker_is_separate_thread),
base::Bind(&base::DeletePointer<VirtualAudioInputStream>));
stream_->Open();
}
@@ -134,7 +136,6 @@ class VirtualAudioInputStreamTest : public testing::Test {
ASSERT_TRUE(!!stream_);
AudioOutputStream* const output_stream = new VirtualAudioOutputStream(
kParams,
- audio_message_loop_.get(),
stream_,
base::Bind(&base::DeletePointer<VirtualAudioOutputStream>));
output_streams_.push_back(output_stream);
@@ -212,6 +213,19 @@ class VirtualAudioInputStreamTest : public testing::Test {
return audio_message_loop_;
}
+ const scoped_refptr<base::MessageLoopProxy>& GetWorkerLoop(
+ bool worker_is_separate_thread) {
+ if (worker_is_separate_thread) {
+ if (!worker_thread_->IsRunning()) {
+ worker_thread_->Start();
+ worker_message_loop_ = worker_thread_->message_loop_proxy();
+ }
+ return worker_message_loop_;
+ } else {
+ return audio_message_loop_;
+ }
+ }
+
private:
void SyncWithAudioThread() {
base::WaitableEvent done(false, false);
@@ -223,6 +237,8 @@ class VirtualAudioInputStreamTest : public testing::Test {
scoped_ptr<base::Thread> audio_thread_;
scoped_refptr<base::MessageLoopProxy> audio_message_loop_;
+ scoped_ptr<base::Thread> worker_thread_;
+ scoped_refptr<base::MessageLoopProxy> worker_message_loop_;
VirtualAudioInputStream* stream_;
MockInputCallback input_callback_;
@@ -240,13 +256,13 @@ class VirtualAudioInputStreamTest : public testing::Test {
FROM_HERE, base::Bind(&VirtualAudioInputStreamTest::method, \
base::Unretained(this)))
-TEST_F(VirtualAudioInputStreamTest, CreateAndClose) {
+TEST_P(VirtualAudioInputStreamTest, CreateAndClose) {
RUN_ON_AUDIO_THREAD(Create);
RUN_ON_AUDIO_THREAD(Close);
WaitUntilClosed();
}
-TEST_F(VirtualAudioInputStreamTest, NoOutputs) {
+TEST_P(VirtualAudioInputStreamTest, NoOutputs) {
RUN_ON_AUDIO_THREAD(Create);
RUN_ON_AUDIO_THREAD(Start);
WaitForDataToFlow();
@@ -255,7 +271,7 @@ TEST_F(VirtualAudioInputStreamTest, NoOutputs) {
WaitUntilClosed();
}
-TEST_F(VirtualAudioInputStreamTest, SingleOutput) {
+TEST_P(VirtualAudioInputStreamTest, SingleOutput) {
RUN_ON_AUDIO_THREAD(Create);
RUN_ON_AUDIO_THREAD(Start);
RUN_ON_AUDIO_THREAD(CreateAndStartOneOutputStream);
@@ -266,7 +282,7 @@ TEST_F(VirtualAudioInputStreamTest, SingleOutput) {
WaitUntilClosed();
}
-TEST_F(VirtualAudioInputStreamTest, SingleOutputPausedAndRestarted) {
+TEST_P(VirtualAudioInputStreamTest, SingleOutputPausedAndRestarted) {
RUN_ON_AUDIO_THREAD(Create);
RUN_ON_AUDIO_THREAD(Start);
RUN_ON_AUDIO_THREAD(CreateAndStartOneOutputStream);
@@ -280,7 +296,7 @@ TEST_F(VirtualAudioInputStreamTest, SingleOutputPausedAndRestarted) {
WaitUntilClosed();
}
-TEST_F(VirtualAudioInputStreamTest, MultipleOutputs) {
+TEST_P(VirtualAudioInputStreamTest, MultipleOutputs) {
RUN_ON_AUDIO_THREAD(Create);
RUN_ON_AUDIO_THREAD(Start);
RUN_ON_AUDIO_THREAD(CreateAndStartOneOutputStream);
@@ -303,7 +319,7 @@ TEST_F(VirtualAudioInputStreamTest, MultipleOutputs) {
}
// A combination of all of the above tests with many output streams.
-TEST_F(VirtualAudioInputStreamTest, ComprehensiveTest) {
+TEST_P(VirtualAudioInputStreamTest, ComprehensiveTest) {
static const int kNumOutputs = 8;
static const int kHalfNumOutputs = kNumOutputs / 2;
static const int kPauseIterations = 5;
@@ -335,4 +351,8 @@ TEST_F(VirtualAudioInputStreamTest, ComprehensiveTest) {
WaitUntilClosed();
}
+INSTANTIATE_TEST_CASE_P(SingleVersusMultithreaded,
+ VirtualAudioInputStreamTest,
+ ::testing::Values(false, true));
+
} // namespace media
diff --git a/media/audio/virtual_audio_output_stream.cc b/media/audio/virtual_audio_output_stream.cc
index 43c449b..43b83cf 100644
--- a/media/audio/virtual_audio_output_stream.cc
+++ b/media/audio/virtual_audio_output_stream.cc
@@ -4,20 +4,22 @@
#include "media/audio/virtual_audio_output_stream.h"
-#include "base/message_loop/message_loop_proxy.h"
+#include "base/logging.h"
#include "media/audio/virtual_audio_input_stream.h"
namespace media {
VirtualAudioOutputStream::VirtualAudioOutputStream(
- const AudioParameters& params, base::MessageLoopProxy* message_loop,
- VirtualAudioInputStream* target, const AfterCloseCallback& after_close_cb)
- : params_(params), message_loop_(message_loop),
- target_input_stream_(target), after_close_cb_(after_close_cb),
- callback_(NULL), volume_(1.0f) {
+ const AudioParameters& params, VirtualAudioInputStream* target,
+ const AfterCloseCallback& after_close_cb)
+ : params_(params), target_input_stream_(target),
+ after_close_cb_(after_close_cb), callback_(NULL), volume_(1.0f) {
DCHECK(params_.IsValid());
- DCHECK(message_loop_);
DCHECK(target);
+
+ // VAOS can be constructed on any thread, but will DCHECK that all
+ // AudioOutputStream methods are called from the same thread.
+ thread_checker_.DetachFromThread();
}
VirtualAudioOutputStream::~VirtualAudioOutputStream() {
@@ -25,27 +27,27 @@ VirtualAudioOutputStream::~VirtualAudioOutputStream() {
}
bool VirtualAudioOutputStream::Open() {
- DCHECK(message_loop_->BelongsToCurrentThread());
+ DCHECK(thread_checker_.CalledOnValidThread());
return true;
}
void VirtualAudioOutputStream::Start(AudioSourceCallback* callback) {
- DCHECK(message_loop_->BelongsToCurrentThread());
+ DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(!callback_);
callback_ = callback;
target_input_stream_->AddOutputStream(this, params_);
}
void VirtualAudioOutputStream::Stop() {
- DCHECK(message_loop_->BelongsToCurrentThread());
+ DCHECK(thread_checker_.CalledOnValidThread());
if (callback_) {
- callback_ = NULL;
target_input_stream_->RemoveOutputStream(this, params_);
+ callback_ = NULL;
}
}
void VirtualAudioOutputStream::Close() {
- DCHECK(message_loop_->BelongsToCurrentThread());
+ DCHECK(thread_checker_.CalledOnValidThread());
Stop();
@@ -60,18 +62,19 @@ void VirtualAudioOutputStream::Close() {
}
void VirtualAudioOutputStream::SetVolume(double volume) {
- DCHECK(message_loop_->BelongsToCurrentThread());
+ DCHECK(thread_checker_.CalledOnValidThread());
volume_ = volume;
}
void VirtualAudioOutputStream::GetVolume(double* volume) {
- DCHECK(message_loop_->BelongsToCurrentThread());
+ DCHECK(thread_checker_.CalledOnValidThread());
*volume = volume_;
}
double VirtualAudioOutputStream::ProvideInput(AudioBus* audio_bus,
base::TimeDelta buffer_delay) {
- DCHECK(message_loop_->BelongsToCurrentThread());
+ // Note: This method may be invoked on any one thread, depending on the
+ // platform.
DCHECK(callback_);
const int frames = callback_->OnMoreData(audio_bus, AudioBuffersState());
diff --git a/media/audio/virtual_audio_output_stream.h b/media/audio/virtual_audio_output_stream.h
index 32afe30..0df6d5b 100644
--- a/media/audio/virtual_audio_output_stream.h
+++ b/media/audio/virtual_audio_output_stream.h
@@ -6,14 +6,11 @@
#define MEDIA_AUDIO_VIRTUAL_AUDIO_OUTPUT_STREAM_H_
#include "base/callback.h"
+#include "base/threading/thread_checker.h"
#include "media/audio/audio_io.h"
#include "media/audio/audio_parameters.h"
#include "media/base/audio_converter.h"
-namespace base {
-class MessageLoopProxy;
-}
-
namespace media {
class VirtualAudioInputStream;
@@ -34,7 +31,6 @@ class MEDIA_EXPORT VirtualAudioOutputStream
// Construct an audio loopback pathway to the given |target| (not owned).
// |target| must outlive this instance.
VirtualAudioOutputStream(const AudioParameters& params,
- base::MessageLoopProxy* message_loop,
VirtualAudioInputStream* target,
const AfterCloseCallback& after_close_cb);
@@ -54,7 +50,6 @@ class MEDIA_EXPORT VirtualAudioOutputStream
base::TimeDelta buffer_delay) OVERRIDE;
const AudioParameters params_;
- base::MessageLoopProxy* const message_loop_;
// Pointer to the VirtualAudioInputStream to attach to when Start() is called.
// This pointer should always be valid because VirtualAudioInputStream should
// outlive this class.
@@ -65,6 +60,8 @@ class MEDIA_EXPORT VirtualAudioOutputStream
AudioSourceCallback* callback_;
double volume_;
+ base::ThreadChecker thread_checker_;
+
DISALLOW_COPY_AND_ASSIGN(VirtualAudioOutputStream);
};
diff --git a/media/audio/virtual_audio_output_stream_unittest.cc b/media/audio/virtual_audio_output_stream_unittest.cc
index fc2f2e0..290b807 100644
--- a/media/audio/virtual_audio_output_stream_unittest.cc
+++ b/media/audio/virtual_audio_output_stream_unittest.cc
@@ -27,10 +27,10 @@ const AudioParameters kParams(
class MockVirtualAudioInputStream : public VirtualAudioInputStream {
public:
explicit MockVirtualAudioInputStream(
- const scoped_refptr<base::MessageLoopProxy>& message_loop)
+ const scoped_refptr<base::MessageLoopProxy>& worker_loop)
: VirtualAudioInputStream(
kParams,
- message_loop,
+ worker_loop,
base::Bind(&base::DeletePointer<VirtualAudioInputStream>)) {}
~MockVirtualAudioInputStream() {}
@@ -87,7 +87,6 @@ TEST_F(VirtualAudioOutputStreamTest, StartStopStartStop) {
VirtualAudioOutputStream* const output_stream = new VirtualAudioOutputStream(
kParams,
- audio_message_loop().get(),
input_stream,
base::Bind(&base::DeletePointer<VirtualAudioOutputStream>));