1// Copyright (c) 2013 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 "content/browser/renderer_host/media/web_contents_audio_input_stream.h"
6
7#include <list>
8
9#include "base/bind.h"
10#include "base/bind_helpers.h"
11#include "base/message_loop/message_loop.h"
12#include "base/synchronization/waitable_event.h"
13#include "base/threading/thread.h"
14#include "content/browser/browser_thread_impl.h"
15#include "content/browser/renderer_host/media/audio_mirroring_manager.h"
16#include "content/browser/renderer_host/media/web_contents_tracker.h"
17#include "media/audio/simple_sources.h"
18#include "media/audio/virtual_audio_input_stream.h"
19#include "testing/gmock/include/gmock/gmock.h"
20#include "testing/gtest/include/gtest/gtest.h"
21
22using ::testing::_;
23using ::testing::Assign;
24using ::testing::DoAll;
25using ::testing::Invoke;
26using ::testing::InvokeWithoutArgs;
27using ::testing::NotNull;
28using ::testing::SaveArg;
29using ::testing::WithArgs;
30
31using media::AudioInputStream;
32using media::AudioOutputStream;
33using media::AudioParameters;
34using media::SineWaveAudioSource;
35using media::VirtualAudioInputStream;
36using media::VirtualAudioOutputStream;
37
38namespace content {
39
40namespace {
41
42const int kRenderProcessId = 123;
43const int kRenderViewId = 456;
44const int kAnotherRenderProcessId = 789;
45const int kAnotherRenderViewId = 1;
46
47const AudioParameters& TestAudioParameters() {
48  static const AudioParameters params(
49      AudioParameters::AUDIO_FAKE,
50      media::CHANNEL_LAYOUT_STEREO,
51      AudioParameters::kAudioCDSampleRate, 16,
52      AudioParameters::kAudioCDSampleRate / 100);
53  return params;
54}
55
56class MockAudioMirroringManager : public AudioMirroringManager {
57 public:
58  MockAudioMirroringManager() : AudioMirroringManager() {}
59  virtual ~MockAudioMirroringManager() {}
60
61  MOCK_METHOD3(StartMirroring,
62               void(int render_process_id, int render_view_id,
63                    MirroringDestination* destination));
64  MOCK_METHOD3(StopMirroring,
65               void(int render_process_id, int render_view_id,
66                    MirroringDestination* destination));
67
68 private:
69  DISALLOW_COPY_AND_ASSIGN(MockAudioMirroringManager);
70};
71
72class MockWebContentsTracker : public WebContentsTracker {
73 public:
74  MockWebContentsTracker() : WebContentsTracker() {}
75
76  MOCK_METHOD3(Start,
77               void(int render_process_id, int render_view_id,
78                    const ChangeCallback& callback));
79  MOCK_METHOD0(Stop, void());
80
81 private:
82  virtual ~MockWebContentsTracker() {}
83
84  DISALLOW_COPY_AND_ASSIGN(MockWebContentsTracker);
85};
86
87// A fully-functional VirtualAudioInputStream, but methods are mocked to allow
88// tests to check how/when they are invoked.
89class MockVirtualAudioInputStream : public VirtualAudioInputStream {
90 public:
91  explicit MockVirtualAudioInputStream(
92      const scoped_refptr<base::MessageLoopProxy>& worker_loop)
93      : VirtualAudioInputStream(TestAudioParameters(), worker_loop,
94                                VirtualAudioInputStream::AfterCloseCallback()),
95        real_(TestAudioParameters(), worker_loop,
96              base::Bind(&MockVirtualAudioInputStream::OnRealStreamHasClosed,
97                         base::Unretained(this))),
98        real_stream_is_closed_(false) {
99    // Set default actions of mocked methods to delegate to the concrete
100    // implementation.
101    ON_CALL(*this, Open())
102        .WillByDefault(Invoke(&real_, &VirtualAudioInputStream::Open));
103    ON_CALL(*this, Start(_))
104        .WillByDefault(Invoke(&real_, &VirtualAudioInputStream::Start));
105    ON_CALL(*this, Stop())
106        .WillByDefault(Invoke(&real_, &VirtualAudioInputStream::Stop));
107    ON_CALL(*this, Close())
108        .WillByDefault(Invoke(&real_, &VirtualAudioInputStream::Close));
109    ON_CALL(*this, GetMaxVolume())
110        .WillByDefault(Invoke(&real_, &VirtualAudioInputStream::GetMaxVolume));
111    ON_CALL(*this, SetVolume(_))
112        .WillByDefault(Invoke(&real_, &VirtualAudioInputStream::SetVolume));
113    ON_CALL(*this, GetVolume())
114        .WillByDefault(Invoke(&real_, &VirtualAudioInputStream::GetVolume));
115    ON_CALL(*this, SetAutomaticGainControl(_))
116        .WillByDefault(
117            Invoke(&real_, &VirtualAudioInputStream::SetAutomaticGainControl));
118    ON_CALL(*this, GetAutomaticGainControl())
119        .WillByDefault(
120            Invoke(&real_, &VirtualAudioInputStream::GetAutomaticGainControl));
121    ON_CALL(*this, AddOutputStream(NotNull(), _))
122        .WillByDefault(
123            Invoke(&real_, &VirtualAudioInputStream::AddOutputStream));
124    ON_CALL(*this, RemoveOutputStream(NotNull(), _))
125        .WillByDefault(
126            Invoke(&real_, &VirtualAudioInputStream::RemoveOutputStream));
127  }
128
129  ~MockVirtualAudioInputStream() {
130    DCHECK(real_stream_is_closed_);
131  }
132
133  MOCK_METHOD0(Open, bool());
134  MOCK_METHOD1(Start, void(AudioInputStream::AudioInputCallback*));
135  MOCK_METHOD0(Stop, void());
136  MOCK_METHOD0(Close, void());
137  MOCK_METHOD0(GetMaxVolume, double());
138  MOCK_METHOD1(SetVolume, void(double));
139  MOCK_METHOD0(GetVolume, double());
140  MOCK_METHOD1(SetAutomaticGainControl, void(bool));
141  MOCK_METHOD0(GetAutomaticGainControl, bool());
142  MOCK_METHOD2(AddOutputStream, void(VirtualAudioOutputStream*,
143                                     const AudioParameters&));
144  MOCK_METHOD2(RemoveOutputStream, void(VirtualAudioOutputStream*,
145                                        const AudioParameters&));
146
147 private:
148  void OnRealStreamHasClosed(VirtualAudioInputStream* stream) {
149    DCHECK_EQ(&real_, stream);
150    DCHECK(!real_stream_is_closed_);
151    real_stream_is_closed_ = true;
152  }
153
154  VirtualAudioInputStream real_;
155  bool real_stream_is_closed_;
156
157  DISALLOW_COPY_AND_ASSIGN(MockVirtualAudioInputStream);
158};
159
160class MockAudioInputCallback : public AudioInputStream::AudioInputCallback {
161 public:
162  MockAudioInputCallback() {}
163
164  MOCK_METHOD5(OnData, void(AudioInputStream* stream, const uint8* src,
165                            uint32 size, uint32 hardware_delay_bytes,
166                            double volume));
167  MOCK_METHOD1(OnClose, void(AudioInputStream* stream));
168  MOCK_METHOD1(OnError, void(AudioInputStream* stream));
169
170 private:
171  DISALLOW_COPY_AND_ASSIGN(MockAudioInputCallback);
172};
173
174}  // namespace
175
176class WebContentsAudioInputStreamTest : public testing::Test {
177 public:
178  WebContentsAudioInputStreamTest()
179      : audio_thread_("Audio thread"),
180        io_thread_(BrowserThread::IO),
181        mock_mirroring_manager_(new MockAudioMirroringManager()),
182        mock_tracker_(new MockWebContentsTracker()),
183        mock_vais_(NULL),
184        wcais_(NULL),
185        destination_(NULL),
186        current_render_process_id_(kRenderProcessId),
187        current_render_view_id_(kRenderViewId),
188        on_data_event_(false, false) {
189    audio_thread_.Start();
190    io_thread_.Start();
191  }
192
193  virtual ~WebContentsAudioInputStreamTest() {
194    audio_thread_.Stop();
195    io_thread_.Stop();
196
197    DCHECK(!mock_vais_);
198    DCHECK(!wcais_);
199    EXPECT_FALSE(destination_);
200    DCHECK(streams_.empty());
201    DCHECK(sources_.empty());
202  }
203
204  void Open() {
205    mock_vais_ =
206        new MockVirtualAudioInputStream(audio_thread_.message_loop_proxy());
207    EXPECT_CALL(*mock_vais_, Open());
208    EXPECT_CALL(*mock_vais_, Close());  // At Close() time.
209
210    ASSERT_EQ(kRenderProcessId, current_render_process_id_);
211    ASSERT_EQ(kRenderViewId, current_render_view_id_);
212    EXPECT_CALL(*mock_tracker_.get(), Start(kRenderProcessId, kRenderViewId, _))
213        .WillOnce(DoAll(
214             SaveArg<2>(&change_callback_),
215             WithArgs<0, 1>(Invoke(&change_callback_,
216                                   &WebContentsTracker::ChangeCallback::Run))));
217    EXPECT_CALL(*mock_tracker_.get(), Stop());  // At Close() time.
218
219    wcais_ = new WebContentsAudioInputStream(
220        current_render_process_id_, current_render_view_id_,
221        mock_mirroring_manager_.get(),
222        mock_tracker_, mock_vais_);
223    wcais_->Open();
224  }
225
226  void Start() {
227    EXPECT_CALL(*mock_vais_, Start(&mock_input_callback_));
228    EXPECT_CALL(*mock_vais_, Stop());  // At Stop() time.
229
230    EXPECT_CALL(*mock_mirroring_manager_,
231                StartMirroring(kRenderProcessId, kRenderViewId, NotNull()))
232        .WillOnce(SaveArg<2>(&destination_))
233        .RetiresOnSaturation();
234    // At Stop() time, or when the mirroring target changes:
235    EXPECT_CALL(*mock_mirroring_manager_,
236                StopMirroring(kRenderProcessId, kRenderViewId, NotNull()))
237        .WillOnce(Assign(
238            &destination_,
239            static_cast<AudioMirroringManager::MirroringDestination*>(NULL)))
240        .RetiresOnSaturation();
241
242    EXPECT_CALL(mock_input_callback_, OnData(NotNull(), NotNull(), _, _, _))
243        .WillRepeatedly(
244            InvokeWithoutArgs(&on_data_event_, &base::WaitableEvent::Signal));
245    EXPECT_CALL(mock_input_callback_, OnClose(_));  // At Stop() time.
246
247    wcais_->Start(&mock_input_callback_);
248
249    // Test plumbing of volume controls and automatic gain controls.  Calls to
250    // wcais_ methods should delegate directly to mock_vais_.
251    EXPECT_CALL(*mock_vais_, GetVolume());
252    double volume = wcais_->GetVolume();
253    EXPECT_CALL(*mock_vais_, GetMaxVolume());
254    const double max_volume = wcais_->GetMaxVolume();
255    volume *= 2.0;
256    if (volume < max_volume) {
257      volume = max_volume;
258    }
259    EXPECT_CALL(*mock_vais_, SetVolume(volume));
260    wcais_->SetVolume(volume);
261    EXPECT_CALL(*mock_vais_, GetAutomaticGainControl());
262    bool auto_gain = wcais_->GetAutomaticGainControl();
263    auto_gain = !auto_gain;
264    EXPECT_CALL(*mock_vais_, SetAutomaticGainControl(auto_gain));
265    wcais_->SetAutomaticGainControl(auto_gain);
266  }
267
268  void AddAnotherInput() {
269    // Note: WCAIS posts a task to invoke
270    // MockAudioMirroringManager::StartMirroring() on the IO thread, which
271    // causes our mock to set |destination_|.  Block until that has happened.
272    base::WaitableEvent done(false, false);
273    BrowserThread::PostTask(
274        BrowserThread::IO, FROM_HERE, base::Bind(
275            &base::WaitableEvent::Signal, base::Unretained(&done)));
276    done.Wait();
277    ASSERT_TRUE(destination_);
278
279    EXPECT_CALL(*mock_vais_, AddOutputStream(NotNull(), _))
280        .RetiresOnSaturation();
281    // Later, when stream is closed:
282    EXPECT_CALL(*mock_vais_, RemoveOutputStream(NotNull(), _))
283        .RetiresOnSaturation();
284
285    const AudioParameters& params = TestAudioParameters();
286    AudioOutputStream* const out = destination_->AddInput(params);
287    ASSERT_TRUE(out);
288    streams_.push_back(out);
289    EXPECT_TRUE(out->Open());
290    SineWaveAudioSource* const source = new SineWaveAudioSource(
291        params.channel_layout(), 200.0, params.sample_rate());
292    sources_.push_back(source);
293    out->Start(source);
294  }
295
296  void RemoveOneInputInFIFOOrder() {
297    ASSERT_FALSE(streams_.empty());
298    AudioOutputStream* const out = streams_.front();
299    streams_.pop_front();
300    out->Stop();
301    out->Close();  // Self-deletes.
302    ASSERT_TRUE(!sources_.empty());
303    delete sources_.front();
304    sources_.pop_front();
305  }
306
307  void ChangeMirroringTarget() {
308    const int next_render_process_id =
309        current_render_process_id_ == kRenderProcessId ?
310            kAnotherRenderProcessId : kRenderProcessId;
311    const int next_render_view_id =
312        current_render_view_id_ == kRenderViewId ?
313            kAnotherRenderViewId : kRenderViewId;
314
315    EXPECT_CALL(*mock_mirroring_manager_,
316                StartMirroring(next_render_process_id, next_render_view_id,
317                               NotNull()))
318        .WillOnce(SaveArg<2>(&destination_))
319        .RetiresOnSaturation();
320    // At Stop() time, or when the mirroring target changes:
321    EXPECT_CALL(*mock_mirroring_manager_,
322                StopMirroring(next_render_process_id, next_render_view_id,
323                              NotNull()))
324        .WillOnce(Assign(
325            &destination_,
326            static_cast<AudioMirroringManager::MirroringDestination*>(NULL)))
327        .RetiresOnSaturation();
328
329    // Simulate OnTargetChange() callback from WebContentsTracker.
330    EXPECT_FALSE(change_callback_.is_null());
331    change_callback_.Run(next_render_process_id, next_render_view_id);
332
333    current_render_process_id_ = next_render_process_id;
334    current_render_view_id_ = next_render_view_id;
335  }
336
337  void LoseMirroringTarget() {
338    EXPECT_CALL(mock_input_callback_, OnError(_));
339
340    // Simulate OnTargetChange() callback from WebContentsTracker.
341    EXPECT_FALSE(change_callback_.is_null());
342    change_callback_.Run(-1, -1);
343  }
344
345  void Stop() {
346    wcais_->Stop();
347  }
348
349  void Close() {
350    // WebContentsAudioInputStream self-destructs on Close().  Its internal
351    // objects hang around until they are no longer referred to (e.g., as tasks
352    // on other threads shut things down).
353    wcais_->Close();
354    wcais_ = NULL;
355    mock_vais_ = NULL;
356  }
357
358  void RunOnAudioThread(const base::Closure& closure) {
359    audio_thread_.message_loop()->PostTask(FROM_HERE, closure);
360  }
361
362  // Block the calling thread until OnData() callbacks are being made.
363  void WaitForData() {
364    // Note: Arbitrarily chosen, but more iterations causes tests to take
365    // significantly more time.
366    static const int kNumIterations = 3;
367    for (int i = 0; i < kNumIterations; ++i)
368      on_data_event_.Wait();
369  }
370
371 private:
372  base::Thread audio_thread_;
373  BrowserThreadImpl io_thread_;
374
375  scoped_ptr<MockAudioMirroringManager> mock_mirroring_manager_;
376  scoped_refptr<MockWebContentsTracker> mock_tracker_;
377
378  MockVirtualAudioInputStream* mock_vais_;  // Owned by wcais_.
379  WebContentsAudioInputStream* wcais_;  // Self-destructs on Close().
380
381  // Mock consumer of audio data.
382  MockAudioInputCallback mock_input_callback_;
383
384  // Provided by WebContentsAudioInputStream to the mock WebContentsTracker.
385  // This callback is saved here, and test code will invoke it to simulate
386  // target change events.
387  WebContentsTracker::ChangeCallback change_callback_;
388
389  // Provided by WebContentsAudioInputStream to the mock AudioMirroringManager.
390  // A pointer to the implementation is saved here, and test code will invoke it
391  // to simulate: 1) calls to AddInput(); and 2) diverting audio data.
392  AudioMirroringManager::MirroringDestination* destination_;
393
394  // Current target RenderView.  These get flipped in ChangedMirroringTarget().
395  int current_render_process_id_;
396  int current_render_view_id_;
397
398  // Streams provided by calls to WebContentsAudioInputStream::AddInput().  Each
399  // is started with a simulated source of audio data.
400  std::list<AudioOutputStream*> streams_;
401  std::list<SineWaveAudioSource*> sources_;  // 1:1 with elements in streams_.
402
403  base::WaitableEvent on_data_event_;
404
405  DISALLOW_COPY_AND_ASSIGN(WebContentsAudioInputStreamTest);
406};
407
408#define RUN_ON_AUDIO_THREAD(method) \
409  RunOnAudioThread(base::Bind(&WebContentsAudioInputStreamTest::method,  \
410                              base::Unretained(this)))
411
412TEST_F(WebContentsAudioInputStreamTest, OpenedButNeverStarted) {
413  RUN_ON_AUDIO_THREAD(Open);
414  RUN_ON_AUDIO_THREAD(Close);
415}
416
417TEST_F(WebContentsAudioInputStreamTest, MirroringNothing) {
418  RUN_ON_AUDIO_THREAD(Open);
419  RUN_ON_AUDIO_THREAD(Start);
420  WaitForData();
421  RUN_ON_AUDIO_THREAD(Stop);
422  RUN_ON_AUDIO_THREAD(Close);
423}
424
425TEST_F(WebContentsAudioInputStreamTest, MirroringOutputOutlivesSession) {
426  RUN_ON_AUDIO_THREAD(Open);
427  RUN_ON_AUDIO_THREAD(Start);
428  RUN_ON_AUDIO_THREAD(AddAnotherInput);
429  WaitForData();
430  RUN_ON_AUDIO_THREAD(Stop);
431  RUN_ON_AUDIO_THREAD(Close);
432  RUN_ON_AUDIO_THREAD(RemoveOneInputInFIFOOrder);
433}
434
435TEST_F(WebContentsAudioInputStreamTest, MirroringOutputWithinSession) {
436  RUN_ON_AUDIO_THREAD(Open);
437  RUN_ON_AUDIO_THREAD(Start);
438  RUN_ON_AUDIO_THREAD(AddAnotherInput);
439  WaitForData();
440  RUN_ON_AUDIO_THREAD(RemoveOneInputInFIFOOrder);
441  RUN_ON_AUDIO_THREAD(Stop);
442  RUN_ON_AUDIO_THREAD(Close);
443}
444
445TEST_F(WebContentsAudioInputStreamTest, MirroringNothingWithTargetChange) {
446  RUN_ON_AUDIO_THREAD(Open);
447  RUN_ON_AUDIO_THREAD(Start);
448  RUN_ON_AUDIO_THREAD(ChangeMirroringTarget);
449  RUN_ON_AUDIO_THREAD(Stop);
450  RUN_ON_AUDIO_THREAD(Close);
451}
452
453TEST_F(WebContentsAudioInputStreamTest, MirroringOneStreamAfterTargetChange) {
454  RUN_ON_AUDIO_THREAD(Open);
455  RUN_ON_AUDIO_THREAD(Start);
456  RUN_ON_AUDIO_THREAD(ChangeMirroringTarget);
457  RUN_ON_AUDIO_THREAD(AddAnotherInput);
458  WaitForData();
459  RUN_ON_AUDIO_THREAD(Stop);
460  RUN_ON_AUDIO_THREAD(Close);
461  RUN_ON_AUDIO_THREAD(RemoveOneInputInFIFOOrder);
462}
463
464TEST_F(WebContentsAudioInputStreamTest, MirroringOneStreamWithTargetChange) {
465  RUN_ON_AUDIO_THREAD(Open);
466  RUN_ON_AUDIO_THREAD(Start);
467  RUN_ON_AUDIO_THREAD(AddAnotherInput);
468  WaitForData();
469  RUN_ON_AUDIO_THREAD(ChangeMirroringTarget);
470  RUN_ON_AUDIO_THREAD(RemoveOneInputInFIFOOrder);
471  RUN_ON_AUDIO_THREAD(AddAnotherInput);
472  WaitForData();
473  RUN_ON_AUDIO_THREAD(Stop);
474  RUN_ON_AUDIO_THREAD(Close);
475  RUN_ON_AUDIO_THREAD(RemoveOneInputInFIFOOrder);
476}
477
478TEST_F(WebContentsAudioInputStreamTest, MirroringLostTarget) {
479  RUN_ON_AUDIO_THREAD(Open);
480  RUN_ON_AUDIO_THREAD(Start);
481  RUN_ON_AUDIO_THREAD(AddAnotherInput);
482  WaitForData();
483  RUN_ON_AUDIO_THREAD(LoseMirroringTarget);
484  RUN_ON_AUDIO_THREAD(RemoveOneInputInFIFOOrder);
485  RUN_ON_AUDIO_THREAD(Stop);
486  RUN_ON_AUDIO_THREAD(Close);
487}
488
489TEST_F(WebContentsAudioInputStreamTest, MirroringMultipleStreamsAndTargets) {
490  RUN_ON_AUDIO_THREAD(Open);
491  RUN_ON_AUDIO_THREAD(Start);
492  RUN_ON_AUDIO_THREAD(AddAnotherInput);
493  WaitForData();
494  RUN_ON_AUDIO_THREAD(AddAnotherInput);
495  RUN_ON_AUDIO_THREAD(AddAnotherInput);
496  RUN_ON_AUDIO_THREAD(AddAnotherInput);
497  WaitForData();
498  RUN_ON_AUDIO_THREAD(ChangeMirroringTarget);
499  RUN_ON_AUDIO_THREAD(RemoveOneInputInFIFOOrder);
500  WaitForData();
501  RUN_ON_AUDIO_THREAD(RemoveOneInputInFIFOOrder);
502  RUN_ON_AUDIO_THREAD(RemoveOneInputInFIFOOrder);
503  RUN_ON_AUDIO_THREAD(AddAnotherInput);
504  WaitForData();
505  RUN_ON_AUDIO_THREAD(RemoveOneInputInFIFOOrder);
506  WaitForData();
507  RUN_ON_AUDIO_THREAD(ChangeMirroringTarget);
508  RUN_ON_AUDIO_THREAD(RemoveOneInputInFIFOOrder);
509  RUN_ON_AUDIO_THREAD(Stop);
510  RUN_ON_AUDIO_THREAD(Close);
511}
512
513}  // namespace content
514