diff options
author | miu@chromium.org <miu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-06-22 14:18:29 +0000 |
---|---|---|
committer | miu@chromium.org <miu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-06-22 14:18:29 +0000 |
commit | 6c1c700baf3bc0920f35d145ce3b9aa4160ebafe (patch) | |
tree | 392d488b0013a227990f708a8fafbc8bc4c5cb47 /media/audio | |
parent | 9661fdbf87d2d4fb8208b464a760591266a87fa6 (diff) | |
download | chromium_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.cc | 131 | ||||
-rw-r--r-- | media/audio/fake_audio_consumer.h | 45 | ||||
-rw-r--r-- | media/audio/fake_audio_consumer_unittest.cc | 1 | ||||
-rw-r--r-- | media/audio/virtual_audio_input_stream.cc | 50 | ||||
-rw-r--r-- | media/audio/virtual_audio_input_stream.h | 24 | ||||
-rw-r--r-- | media/audio/virtual_audio_input_stream_unittest.cc | 38 | ||||
-rw-r--r-- | media/audio/virtual_audio_output_stream.cc | 33 | ||||
-rw-r--r-- | media/audio/virtual_audio_output_stream.h | 9 | ||||
-rw-r--r-- | media/audio/virtual_audio_output_stream_unittest.cc | 5 |
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>)); |