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