// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include #include "base/bind.h" #include "base/bind_helpers.h" #include "base/message_loop.h" #include "base/rand_util.h" #include "base/synchronization/waitable_event.h" #include "base/threading/thread.h" #include "media/audio/audio_io.h" #include "media/audio/simple_sources.h" #include "media/audio/virtual_audio_input_stream.h" #include "media/audio/virtual_audio_output_stream.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" using ::testing::_; using ::testing::AtLeast; using ::testing::InvokeWithoutArgs; using ::testing::NotNull; namespace media { namespace { const AudioParameters kParams( AudioParameters::AUDIO_PCM_LOW_LATENCY, CHANNEL_LAYOUT_STEREO, 8000, 8, 10); class MockInputCallback : public AudioInputStream::AudioInputCallback { public: MockInputCallback() : data_pushed_(false, false) { ON_CALL(*this, OnData(_, _, _, _, _)) .WillByDefault(InvokeWithoutArgs(&data_pushed_, &base::WaitableEvent::Signal)); } virtual ~MockInputCallback() {} MOCK_METHOD5(OnData, void(AudioInputStream* stream, const uint8* data, uint32 size, uint32 hardware_delay_bytes, double volume)); MOCK_METHOD1(OnClose, void(AudioInputStream* stream)); MOCK_METHOD1(OnError, void(AudioInputStream* stream)); void WaitForDataPushes() { for (int i = 0; i < 3; ++i) { data_pushed_.Wait(); } } private: base::WaitableEvent data_pushed_; DISALLOW_COPY_AND_ASSIGN(MockInputCallback); }; class TestAudioSource : public SineWaveAudioSource { public: TestAudioSource() : SineWaveAudioSource( kParams.channel_layout(), 200.0, kParams.sample_rate()), data_pulled_(false, false) {} virtual ~TestAudioSource() {} virtual int OnMoreData(AudioBus* audio_bus, AudioBuffersState audio_buffers) OVERRIDE { const int ret = SineWaveAudioSource::OnMoreData(audio_bus, audio_buffers); data_pulled_.Signal(); return ret; } virtual int OnMoreIOData(AudioBus* source, AudioBus* dest, AudioBuffersState audio_buffers) OVERRIDE { const int ret = SineWaveAudioSource::OnMoreIOData(source, dest, audio_buffers); data_pulled_.Signal(); return ret; } void WaitForDataPulls() { for (int i = 0; i < 3; ++i) { data_pulled_.Wait(); } } private: base::WaitableEvent data_pulled_; DISALLOW_COPY_AND_ASSIGN(TestAudioSource); }; } // namespace class VirtualAudioInputStreamTest : public testing::Test { public: VirtualAudioInputStreamTest() : audio_thread_(new base::Thread("AudioThread")), stream_(NULL), closed_stream_(false, false) { audio_thread_->Start(); audio_message_loop_ = audio_thread_->message_loop_proxy(); } virtual ~VirtualAudioInputStreamTest() { SyncWithAudioThread(); DCHECK(output_streams_.empty()); DCHECK(stopped_output_streams_.empty()); } void Create() { stream_ = new VirtualAudioInputStream( kParams, audio_message_loop_, base::Bind(&base::DeletePointer)); stream_->Open(); } void Start() { EXPECT_CALL(input_callback_, OnClose(_)); EXPECT_CALL(input_callback_, OnData(_, NotNull(), _, _, _)) .Times(AtLeast(1)); ASSERT_TRUE(!!stream_); stream_->Start(&input_callback_); } void CreateAndStartOneOutputStream() { ASSERT_TRUE(!!stream_); AudioOutputStream* const output_stream = new VirtualAudioOutputStream( kParams, audio_message_loop_.get(), stream_, base::Bind(&base::DeletePointer)); output_streams_.push_back(output_stream); output_stream->Open(); output_stream->Start(&source_); } void Stop() { ASSERT_TRUE(!!stream_); stream_->Stop(); } void Close() { ASSERT_TRUE(!!stream_); stream_->Close(); stream_ = NULL; closed_stream_.Signal(); } void WaitForDataToFlow() { // Wait until audio thread is idle before calling output_streams_.size(). SyncWithAudioThread(); const int count = output_streams_.size(); for (int i = 0; i < count; ++i) { source_.WaitForDataPulls(); } input_callback_.WaitForDataPushes(); } void WaitUntilClosed() { closed_stream_.Wait(); } void StopAndCloseOneOutputStream() { ASSERT_TRUE(!output_streams_.empty()); AudioOutputStream* const output_stream = output_streams_.front(); ASSERT_TRUE(!!output_stream); output_streams_.pop_front(); output_stream->Stop(); output_stream->Close(); } void StopFirstOutputStream() { ASSERT_TRUE(!output_streams_.empty()); AudioOutputStream* const output_stream = output_streams_.front(); ASSERT_TRUE(!!output_stream); output_streams_.pop_front(); output_stream->Stop(); stopped_output_streams_.push_back(output_stream); } void StopSomeOutputStreams() { ASSERT_LE(2, static_cast(output_streams_.size())); for (int remaning = base::RandInt(1, output_streams_.size() - 1); remaning > 0; --remaning) { StopFirstOutputStream(); } } void RestartAllStoppedOutputStreams() { typedef std::list::const_iterator ConstIter; for (ConstIter it = stopped_output_streams_.begin(); it != stopped_output_streams_.end(); ++it) { (*it)->Start(&source_); output_streams_.push_back(*it); } stopped_output_streams_.clear(); } const scoped_refptr& audio_message_loop() const { return audio_message_loop_; } private: void SyncWithAudioThread() { base::WaitableEvent done(false, false); audio_message_loop_->PostTask( FROM_HERE, base::Bind(&base::WaitableEvent::Signal, base::Unretained(&done))); done.Wait(); } scoped_ptr audio_thread_; scoped_refptr audio_message_loop_; VirtualAudioInputStream* stream_; MockInputCallback input_callback_; base::WaitableEvent closed_stream_; std::list output_streams_; std::list stopped_output_streams_; TestAudioSource source_; DISALLOW_COPY_AND_ASSIGN(VirtualAudioInputStreamTest); }; #define RUN_ON_AUDIO_THREAD(method) \ audio_message_loop()->PostTask( \ FROM_HERE, base::Bind(&VirtualAudioInputStreamTest::method, \ base::Unretained(this))) TEST_F(VirtualAudioInputStreamTest, CreateAndClose) { RUN_ON_AUDIO_THREAD(Create); RUN_ON_AUDIO_THREAD(Close); WaitUntilClosed(); } TEST_F(VirtualAudioInputStreamTest, NoOutputs) { RUN_ON_AUDIO_THREAD(Create); RUN_ON_AUDIO_THREAD(Start); WaitForDataToFlow(); RUN_ON_AUDIO_THREAD(Stop); RUN_ON_AUDIO_THREAD(Close); WaitUntilClosed(); } TEST_F(VirtualAudioInputStreamTest, SingleOutput) { RUN_ON_AUDIO_THREAD(Create); RUN_ON_AUDIO_THREAD(Start); RUN_ON_AUDIO_THREAD(CreateAndStartOneOutputStream); WaitForDataToFlow(); RUN_ON_AUDIO_THREAD(StopAndCloseOneOutputStream); RUN_ON_AUDIO_THREAD(Stop); RUN_ON_AUDIO_THREAD(Close); WaitUntilClosed(); } TEST_F(VirtualAudioInputStreamTest, SingleOutputPausedAndRestarted) { RUN_ON_AUDIO_THREAD(Create); RUN_ON_AUDIO_THREAD(Start); RUN_ON_AUDIO_THREAD(CreateAndStartOneOutputStream); WaitForDataToFlow(); RUN_ON_AUDIO_THREAD(StopFirstOutputStream); RUN_ON_AUDIO_THREAD(RestartAllStoppedOutputStreams); WaitForDataToFlow(); RUN_ON_AUDIO_THREAD(StopAndCloseOneOutputStream); RUN_ON_AUDIO_THREAD(Stop); RUN_ON_AUDIO_THREAD(Close); WaitUntilClosed(); } TEST_F(VirtualAudioInputStreamTest, MultipleOutputs) { RUN_ON_AUDIO_THREAD(Create); RUN_ON_AUDIO_THREAD(Start); RUN_ON_AUDIO_THREAD(CreateAndStartOneOutputStream); WaitForDataToFlow(); RUN_ON_AUDIO_THREAD(CreateAndStartOneOutputStream); RUN_ON_AUDIO_THREAD(CreateAndStartOneOutputStream); WaitForDataToFlow(); RUN_ON_AUDIO_THREAD(StopFirstOutputStream); RUN_ON_AUDIO_THREAD(StopFirstOutputStream); WaitForDataToFlow(); RUN_ON_AUDIO_THREAD(StopFirstOutputStream); RUN_ON_AUDIO_THREAD(RestartAllStoppedOutputStreams); WaitForDataToFlow(); RUN_ON_AUDIO_THREAD(StopAndCloseOneOutputStream); RUN_ON_AUDIO_THREAD(StopAndCloseOneOutputStream); RUN_ON_AUDIO_THREAD(Stop); RUN_ON_AUDIO_THREAD(StopAndCloseOneOutputStream); RUN_ON_AUDIO_THREAD(Close); WaitUntilClosed(); } // A combination of all of the above tests with many output streams. TEST_F(VirtualAudioInputStreamTest, ComprehensiveTest) { static const int kNumOutputs = 8; static const int kHalfNumOutputs = kNumOutputs / 2; static const int kPauseIterations = 5; RUN_ON_AUDIO_THREAD(Create); for (int i = 0; i < kHalfNumOutputs; ++i) { RUN_ON_AUDIO_THREAD(CreateAndStartOneOutputStream); } RUN_ON_AUDIO_THREAD(Start); WaitForDataToFlow(); for (int i = 0; i < kHalfNumOutputs; ++i) { RUN_ON_AUDIO_THREAD(CreateAndStartOneOutputStream); } WaitForDataToFlow(); for (int i = 0; i < kPauseIterations; ++i) { RUN_ON_AUDIO_THREAD(StopSomeOutputStreams); WaitForDataToFlow(); RUN_ON_AUDIO_THREAD(RestartAllStoppedOutputStreams); WaitForDataToFlow(); } for (int i = 0; i < kHalfNumOutputs; ++i) { RUN_ON_AUDIO_THREAD(StopAndCloseOneOutputStream); } RUN_ON_AUDIO_THREAD(Stop); for (int i = 0; i < kHalfNumOutputs; ++i) { RUN_ON_AUDIO_THREAD(StopAndCloseOneOutputStream); } RUN_ON_AUDIO_THREAD(Close); WaitUntilClosed(); } } // namespace media