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