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