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
19using ::testing::_;
20using ::testing::AtLeast;
21using ::testing::InvokeWithoutArgs;
22using ::testing::NotNull;
23
24namespace media {
25
26namespace {
27
28const AudioParameters kParams(
29    AudioParameters::AUDIO_PCM_LOW_LATENCY, CHANNEL_LAYOUT_STEREO, 8000, 8, 10);
30
31class MockInputCallback : public AudioInputStream::AudioInputCallback {
32 public:
33  MockInputCallback()
34      : data_pushed_(false, false) {
35    ON_CALL(*this, OnData(_, _, _, _)).WillByDefault(
36        InvokeWithoutArgs(&data_pushed_, &base::WaitableEvent::Signal));
37  }
38
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
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
60class TestAudioSource : public SineWaveAudioSource {
61 public:
62  TestAudioSource()
63      : SineWaveAudioSource(
64            kParams.channel_layout(), 200.0, kParams.sample_rate()),
65        data_pulled_(false, false) {}
66
67  virtual ~TestAudioSource() {}
68
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
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
90class VirtualAudioInputStreamTest : public testing::TestWithParam<bool> {
91 public:
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
101  virtual ~VirtualAudioInputStreamTest() {
102    SyncWithAudioThread();
103
104    DCHECK(output_streams_.empty());
105    DCHECK(stopped_output_streams_.empty());
106  }
107
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
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
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
135  void Stop() {
136    ASSERT_TRUE(!!stream_);
137    stream_->Stop();
138  }
139
140  void Close() {
141    ASSERT_TRUE(!!stream_);
142    stream_->Close();
143    stream_ = NULL;
144    closed_stream_.Signal();
145  }
146
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
159  void WaitUntilClosed() {
160    closed_stream_.Wait();
161  }
162
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
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
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
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
200  const scoped_refptr<base::SingleThreadTaskRunner>& audio_task_runner() const {
201    return audio_task_runner_;
202  }
203
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:
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
247TEST_P(VirtualAudioInputStreamTest, CreateAndClose) {
248  RUN_ON_AUDIO_THREAD(Create);
249  RUN_ON_AUDIO_THREAD(Close);
250  WaitUntilClosed();
251}
252
253TEST_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
262TEST_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
273TEST_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
287TEST_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.
310TEST_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
342INSTANTIATE_TEST_CASE_P(SingleVersusMultithreaded,
343                        VirtualAudioInputStreamTest,
344                        ::testing::Values(false, true));
345
346}  // namespace media
347