• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include <list>
6 
7 #include "base/bind.h"
8 #include "base/bind_helpers.h"
9 #include "base/rand_util.h"
10 #include "base/synchronization/waitable_event.h"
11 #include "base/threading/thread.h"
12 #include "media/audio/audio_io.h"
13 #include "media/audio/simple_sources.h"
14 #include "media/audio/virtual_audio_input_stream.h"
15 #include "media/audio/virtual_audio_output_stream.h"
16 #include "testing/gmock/include/gmock/gmock.h"
17 #include "testing/gtest/include/gtest/gtest.h"
18 
19 using ::testing::_;
20 using ::testing::AtLeast;
21 using ::testing::InvokeWithoutArgs;
22 using ::testing::NotNull;
23 
24 namespace media {
25 
26 namespace {
27 
28 const AudioParameters kParams(
29     AudioParameters::AUDIO_PCM_LOW_LATENCY, CHANNEL_LAYOUT_STEREO, 8000, 8, 10);
30 
31 class MockInputCallback : public AudioInputStream::AudioInputCallback {
32  public:
MockInputCallback()33   MockInputCallback()
34       : data_pushed_(false, false) {
35     ON_CALL(*this, OnData(_, _, _, _)).WillByDefault(
36         InvokeWithoutArgs(&data_pushed_, &base::WaitableEvent::Signal));
37   }
38 
~MockInputCallback()39   virtual ~MockInputCallback() {}
40 
41   MOCK_METHOD4(OnData,
42                void(AudioInputStream* stream,
43                     const AudioBus* source,
44                     uint32 hardware_delay_bytes,
45                     double volume));
46   MOCK_METHOD1(OnError, void(AudioInputStream* stream));
47 
WaitForDataPushes()48   void WaitForDataPushes() {
49     for (int i = 0; i < 3; ++i) {
50       data_pushed_.Wait();
51     }
52   }
53 
54  private:
55   base::WaitableEvent data_pushed_;
56 
57   DISALLOW_COPY_AND_ASSIGN(MockInputCallback);
58 };
59 
60 class TestAudioSource : public SineWaveAudioSource {
61  public:
TestAudioSource()62   TestAudioSource()
63       : SineWaveAudioSource(
64             kParams.channel_layout(), 200.0, kParams.sample_rate()),
65         data_pulled_(false, false) {}
66 
~TestAudioSource()67   virtual ~TestAudioSource() {}
68 
OnMoreData(AudioBus * audio_bus,AudioBuffersState audio_buffers)69   virtual int OnMoreData(AudioBus* audio_bus,
70                          AudioBuffersState audio_buffers) OVERRIDE {
71     const int ret = SineWaveAudioSource::OnMoreData(audio_bus, audio_buffers);
72     data_pulled_.Signal();
73     return ret;
74   }
75 
WaitForDataPulls()76   void WaitForDataPulls() {
77     for (int i = 0; i < 3; ++i) {
78       data_pulled_.Wait();
79     }
80   }
81 
82  private:
83   base::WaitableEvent data_pulled_;
84 
85   DISALLOW_COPY_AND_ASSIGN(TestAudioSource);
86 };
87 
88 }  // namespace
89 
90 class VirtualAudioInputStreamTest : public testing::TestWithParam<bool> {
91  public:
VirtualAudioInputStreamTest()92   VirtualAudioInputStreamTest()
93       : audio_thread_(new base::Thread("AudioThread")),
94         worker_thread_(new base::Thread("AudioWorkerThread")),
95         stream_(NULL),
96         closed_stream_(false, false) {
97     audio_thread_->Start();
98     audio_task_runner_ = audio_thread_->message_loop_proxy();
99   }
100 
~VirtualAudioInputStreamTest()101   virtual ~VirtualAudioInputStreamTest() {
102     SyncWithAudioThread();
103 
104     DCHECK(output_streams_.empty());
105     DCHECK(stopped_output_streams_.empty());
106   }
107 
Create()108   void Create() {
109     const bool worker_is_separate_thread = GetParam();
110     stream_ = new VirtualAudioInputStream(
111         kParams, GetWorkerTaskRunner(worker_is_separate_thread),
112         base::Bind(&base::DeletePointer<VirtualAudioInputStream>));
113     stream_->Open();
114   }
115 
Start()116   void Start() {
117     EXPECT_CALL(input_callback_, OnData(_, NotNull(), _, _)).Times(AtLeast(1));
118 
119     ASSERT_TRUE(!!stream_);
120     stream_->Start(&input_callback_);
121   }
122 
CreateAndStartOneOutputStream()123   void CreateAndStartOneOutputStream() {
124     ASSERT_TRUE(!!stream_);
125     AudioOutputStream* const output_stream = new VirtualAudioOutputStream(
126         kParams,
127         stream_,
128         base::Bind(&base::DeletePointer<VirtualAudioOutputStream>));
129     output_streams_.push_back(output_stream);
130 
131     output_stream->Open();
132     output_stream->Start(&source_);
133   }
134 
Stop()135   void Stop() {
136     ASSERT_TRUE(!!stream_);
137     stream_->Stop();
138   }
139 
Close()140   void Close() {
141     ASSERT_TRUE(!!stream_);
142     stream_->Close();
143     stream_ = NULL;
144     closed_stream_.Signal();
145   }
146 
WaitForDataToFlow()147   void WaitForDataToFlow() {
148     // Wait until audio thread is idle before calling output_streams_.size().
149     SyncWithAudioThread();
150 
151     const int count = output_streams_.size();
152     for (int i = 0; i < count; ++i) {
153       source_.WaitForDataPulls();
154     }
155 
156     input_callback_.WaitForDataPushes();
157   }
158 
WaitUntilClosed()159   void WaitUntilClosed() {
160     closed_stream_.Wait();
161   }
162 
StopAndCloseOneOutputStream()163   void StopAndCloseOneOutputStream() {
164     ASSERT_TRUE(!output_streams_.empty());
165     AudioOutputStream* const output_stream = output_streams_.front();
166     ASSERT_TRUE(!!output_stream);
167     output_streams_.pop_front();
168 
169     output_stream->Stop();
170     output_stream->Close();
171   }
172 
StopFirstOutputStream()173   void StopFirstOutputStream() {
174     ASSERT_TRUE(!output_streams_.empty());
175     AudioOutputStream* const output_stream = output_streams_.front();
176     ASSERT_TRUE(!!output_stream);
177     output_streams_.pop_front();
178     output_stream->Stop();
179     stopped_output_streams_.push_back(output_stream);
180   }
181 
StopSomeOutputStreams()182   void StopSomeOutputStreams() {
183     ASSERT_LE(2, static_cast<int>(output_streams_.size()));
184     for (int remaning = base::RandInt(1, output_streams_.size() - 1);
185          remaning > 0; --remaning) {
186       StopFirstOutputStream();
187     }
188   }
189 
RestartAllStoppedOutputStreams()190   void RestartAllStoppedOutputStreams() {
191     typedef std::list<AudioOutputStream*>::const_iterator ConstIter;
192     for (ConstIter it = stopped_output_streams_.begin();
193          it != stopped_output_streams_.end(); ++it) {
194       (*it)->Start(&source_);
195       output_streams_.push_back(*it);
196     }
197     stopped_output_streams_.clear();
198   }
199 
audio_task_runner() const200   const scoped_refptr<base::SingleThreadTaskRunner>& audio_task_runner() const {
201     return audio_task_runner_;
202   }
203 
GetWorkerTaskRunner(bool worker_is_separate_thread)204   const scoped_refptr<base::SingleThreadTaskRunner>& GetWorkerTaskRunner(
205       bool worker_is_separate_thread) {
206     if (worker_is_separate_thread) {
207       if (!worker_thread_->IsRunning()) {
208         worker_thread_->Start();
209         worker_task_runner_ = worker_thread_->message_loop_proxy();
210       }
211       return worker_task_runner_;
212     } else {
213       return audio_task_runner_;
214     }
215   }
216 
217  private:
SyncWithAudioThread()218   void SyncWithAudioThread() {
219     base::WaitableEvent done(false, false);
220     audio_task_runner_->PostTask(
221         FROM_HERE,
222         base::Bind(&base::WaitableEvent::Signal, base::Unretained(&done)));
223     done.Wait();
224   }
225 
226   scoped_ptr<base::Thread> audio_thread_;
227   scoped_refptr<base::SingleThreadTaskRunner> audio_task_runner_;
228   scoped_ptr<base::Thread> worker_thread_;
229   scoped_refptr<base::SingleThreadTaskRunner> worker_task_runner_;
230 
231   VirtualAudioInputStream* stream_;
232   MockInputCallback input_callback_;
233   base::WaitableEvent closed_stream_;
234 
235   std::list<AudioOutputStream*> output_streams_;
236   std::list<AudioOutputStream*> stopped_output_streams_;
237   TestAudioSource source_;
238 
239   DISALLOW_COPY_AND_ASSIGN(VirtualAudioInputStreamTest);
240 };
241 
242 #define RUN_ON_AUDIO_THREAD(method)  \
243   audio_task_runner()->PostTask(  \
244       FROM_HERE, base::Bind(&VirtualAudioInputStreamTest::method,  \
245                             base::Unretained(this)))
246 
TEST_P(VirtualAudioInputStreamTest,CreateAndClose)247 TEST_P(VirtualAudioInputStreamTest, CreateAndClose) {
248   RUN_ON_AUDIO_THREAD(Create);
249   RUN_ON_AUDIO_THREAD(Close);
250   WaitUntilClosed();
251 }
252 
TEST_P(VirtualAudioInputStreamTest,NoOutputs)253 TEST_P(VirtualAudioInputStreamTest, NoOutputs) {
254   RUN_ON_AUDIO_THREAD(Create);
255   RUN_ON_AUDIO_THREAD(Start);
256   WaitForDataToFlow();
257   RUN_ON_AUDIO_THREAD(Stop);
258   RUN_ON_AUDIO_THREAD(Close);
259   WaitUntilClosed();
260 }
261 
TEST_P(VirtualAudioInputStreamTest,SingleOutput)262 TEST_P(VirtualAudioInputStreamTest, SingleOutput) {
263   RUN_ON_AUDIO_THREAD(Create);
264   RUN_ON_AUDIO_THREAD(Start);
265   RUN_ON_AUDIO_THREAD(CreateAndStartOneOutputStream);
266   WaitForDataToFlow();
267   RUN_ON_AUDIO_THREAD(StopAndCloseOneOutputStream);
268   RUN_ON_AUDIO_THREAD(Stop);
269   RUN_ON_AUDIO_THREAD(Close);
270   WaitUntilClosed();
271 }
272 
TEST_P(VirtualAudioInputStreamTest,SingleOutputPausedAndRestarted)273 TEST_P(VirtualAudioInputStreamTest, SingleOutputPausedAndRestarted) {
274   RUN_ON_AUDIO_THREAD(Create);
275   RUN_ON_AUDIO_THREAD(Start);
276   RUN_ON_AUDIO_THREAD(CreateAndStartOneOutputStream);
277   WaitForDataToFlow();
278   RUN_ON_AUDIO_THREAD(StopFirstOutputStream);
279   RUN_ON_AUDIO_THREAD(RestartAllStoppedOutputStreams);
280   WaitForDataToFlow();
281   RUN_ON_AUDIO_THREAD(StopAndCloseOneOutputStream);
282   RUN_ON_AUDIO_THREAD(Stop);
283   RUN_ON_AUDIO_THREAD(Close);
284   WaitUntilClosed();
285 }
286 
TEST_P(VirtualAudioInputStreamTest,MultipleOutputs)287 TEST_P(VirtualAudioInputStreamTest, MultipleOutputs) {
288   RUN_ON_AUDIO_THREAD(Create);
289   RUN_ON_AUDIO_THREAD(Start);
290   RUN_ON_AUDIO_THREAD(CreateAndStartOneOutputStream);
291   WaitForDataToFlow();
292   RUN_ON_AUDIO_THREAD(CreateAndStartOneOutputStream);
293   RUN_ON_AUDIO_THREAD(CreateAndStartOneOutputStream);
294   WaitForDataToFlow();
295   RUN_ON_AUDIO_THREAD(StopFirstOutputStream);
296   RUN_ON_AUDIO_THREAD(StopFirstOutputStream);
297   WaitForDataToFlow();
298   RUN_ON_AUDIO_THREAD(StopFirstOutputStream);
299   RUN_ON_AUDIO_THREAD(RestartAllStoppedOutputStreams);
300   WaitForDataToFlow();
301   RUN_ON_AUDIO_THREAD(StopAndCloseOneOutputStream);
302   RUN_ON_AUDIO_THREAD(StopAndCloseOneOutputStream);
303   RUN_ON_AUDIO_THREAD(Stop);
304   RUN_ON_AUDIO_THREAD(StopAndCloseOneOutputStream);
305   RUN_ON_AUDIO_THREAD(Close);
306   WaitUntilClosed();
307 }
308 
309 // A combination of all of the above tests with many output streams.
TEST_P(VirtualAudioInputStreamTest,ComprehensiveTest)310 TEST_P(VirtualAudioInputStreamTest, ComprehensiveTest) {
311   static const int kNumOutputs = 8;
312   static const int kHalfNumOutputs = kNumOutputs / 2;
313   static const int kPauseIterations = 5;
314 
315   RUN_ON_AUDIO_THREAD(Create);
316   for (int i = 0; i < kHalfNumOutputs; ++i) {
317     RUN_ON_AUDIO_THREAD(CreateAndStartOneOutputStream);
318   }
319   RUN_ON_AUDIO_THREAD(Start);
320   WaitForDataToFlow();
321   for (int i = 0; i < kHalfNumOutputs; ++i) {
322     RUN_ON_AUDIO_THREAD(CreateAndStartOneOutputStream);
323   }
324   WaitForDataToFlow();
325   for (int i = 0; i < kPauseIterations; ++i) {
326     RUN_ON_AUDIO_THREAD(StopSomeOutputStreams);
327     WaitForDataToFlow();
328     RUN_ON_AUDIO_THREAD(RestartAllStoppedOutputStreams);
329     WaitForDataToFlow();
330   }
331   for (int i = 0; i < kHalfNumOutputs; ++i) {
332     RUN_ON_AUDIO_THREAD(StopAndCloseOneOutputStream);
333   }
334   RUN_ON_AUDIO_THREAD(Stop);
335   for (int i = 0; i < kHalfNumOutputs; ++i) {
336     RUN_ON_AUDIO_THREAD(StopAndCloseOneOutputStream);
337   }
338   RUN_ON_AUDIO_THREAD(Close);
339   WaitUntilClosed();
340 }
341 
342 INSTANTIATE_TEST_CASE_P(SingleVersusMultithreaded,
343                         VirtualAudioInputStreamTest,
344                         ::testing::Values(false, true));
345 
346 }  // namespace media
347