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.
5 #include "content/browser/media/capture/web_contents_audio_input_stream.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/media/capture/audio_mirroring_manager.h"
15 #include "content/browser/media/capture/web_contents_tracker.h"
16 #include "content/public/browser/browser_thread.h"
17 #include "content/public/test/test_browser_thread_bundle.h"
18 #include "media/audio/simple_sources.h"
19 #include "media/audio/virtual_audio_input_stream.h"
20 #include "testing/gmock/include/gmock/gmock.h"
21 #include "testing/gtest/include/gtest/gtest.h"
24 using ::testing::Assign
;
25 using ::testing::DoAll
;
26 using ::testing::Invoke
;
27 using ::testing::InvokeWithoutArgs
;
28 using ::testing::NotNull
;
29 using ::testing::SaveArg
;
30 using ::testing::WithArgs
;
32 using media::AudioInputStream
;
33 using media::AudioOutputStream
;
34 using media::AudioParameters
;
35 using media::SineWaveAudioSource
;
36 using media::VirtualAudioInputStream
;
37 using media::VirtualAudioOutputStream
;
43 const int kRenderProcessId
= 123;
44 const int kRenderFrameId
= 456;
45 const int kAnotherRenderProcessId
= 789;
46 const int kAnotherRenderFrameId
= 1;
48 const AudioParameters
& TestAudioParameters() {
49 static const AudioParameters
params(
50 AudioParameters::AUDIO_FAKE
,
51 media::CHANNEL_LAYOUT_STEREO
,
52 AudioParameters::kAudioCDSampleRate
, 16,
53 AudioParameters::kAudioCDSampleRate
/ 100);
57 class MockAudioMirroringManager
: public AudioMirroringManager
{
59 MockAudioMirroringManager() : AudioMirroringManager() {}
60 virtual ~MockAudioMirroringManager() {}
62 MOCK_METHOD1(StartMirroring
, void(MirroringDestination
* destination
));
63 MOCK_METHOD1(StopMirroring
, void(MirroringDestination
* destination
));
66 DISALLOW_COPY_AND_ASSIGN(MockAudioMirroringManager
);
69 class MockWebContentsTracker
: public WebContentsTracker
{
71 MockWebContentsTracker() : WebContentsTracker(false) {}
74 void(int render_process_id
, int render_frame_id
,
75 const ChangeCallback
& callback
));
76 MOCK_METHOD0(Stop
, void());
79 virtual ~MockWebContentsTracker() {}
81 DISALLOW_COPY_AND_ASSIGN(MockWebContentsTracker
);
84 // A fully-functional VirtualAudioInputStream, but methods are mocked to allow
85 // tests to check how/when they are invoked.
86 class MockVirtualAudioInputStream
: public VirtualAudioInputStream
{
88 explicit MockVirtualAudioInputStream(
89 const scoped_refptr
<base::MessageLoopProxy
>& worker_loop
)
90 : VirtualAudioInputStream(TestAudioParameters(), worker_loop
,
91 VirtualAudioInputStream::AfterCloseCallback()),
92 real_(TestAudioParameters(), worker_loop
,
93 base::Bind(&MockVirtualAudioInputStream::OnRealStreamHasClosed
,
94 base::Unretained(this))),
95 real_stream_is_closed_(false) {
96 // Set default actions of mocked methods to delegate to the concrete
98 ON_CALL(*this, Open())
99 .WillByDefault(Invoke(&real_
, &VirtualAudioInputStream::Open
));
100 ON_CALL(*this, Start(_
))
101 .WillByDefault(Invoke(&real_
, &VirtualAudioInputStream::Start
));
102 ON_CALL(*this, Stop())
103 .WillByDefault(Invoke(&real_
, &VirtualAudioInputStream::Stop
));
104 ON_CALL(*this, Close())
105 .WillByDefault(Invoke(&real_
, &VirtualAudioInputStream::Close
));
106 ON_CALL(*this, GetMaxVolume())
107 .WillByDefault(Invoke(&real_
, &VirtualAudioInputStream::GetMaxVolume
));
108 ON_CALL(*this, SetVolume(_
))
109 .WillByDefault(Invoke(&real_
, &VirtualAudioInputStream::SetVolume
));
110 ON_CALL(*this, GetVolume())
111 .WillByDefault(Invoke(&real_
, &VirtualAudioInputStream::GetVolume
));
112 ON_CALL(*this, SetAutomaticGainControl(_
))
114 Invoke(&real_
, &VirtualAudioInputStream::SetAutomaticGainControl
));
115 ON_CALL(*this, GetAutomaticGainControl())
117 Invoke(&real_
, &VirtualAudioInputStream::GetAutomaticGainControl
));
118 ON_CALL(*this, AddOutputStream(NotNull(), _
))
120 Invoke(&real_
, &VirtualAudioInputStream::AddOutputStream
));
121 ON_CALL(*this, RemoveOutputStream(NotNull(), _
))
123 Invoke(&real_
, &VirtualAudioInputStream::RemoveOutputStream
));
126 ~MockVirtualAudioInputStream() {
127 DCHECK(real_stream_is_closed_
);
130 MOCK_METHOD0(Open
, bool());
131 MOCK_METHOD1(Start
, void(AudioInputStream::AudioInputCallback
*));
132 MOCK_METHOD0(Stop
, void());
133 MOCK_METHOD0(Close
, void());
134 MOCK_METHOD0(GetMaxVolume
, double());
135 MOCK_METHOD1(SetVolume
, void(double));
136 MOCK_METHOD0(GetVolume
, double());
137 MOCK_METHOD1(SetAutomaticGainControl
, bool(bool));
138 MOCK_METHOD0(GetAutomaticGainControl
, bool());
139 MOCK_METHOD2(AddOutputStream
, void(VirtualAudioOutputStream
*,
140 const AudioParameters
&));
141 MOCK_METHOD2(RemoveOutputStream
, void(VirtualAudioOutputStream
*,
142 const AudioParameters
&));
145 void OnRealStreamHasClosed(VirtualAudioInputStream
* stream
) {
146 DCHECK_EQ(&real_
, stream
);
147 DCHECK(!real_stream_is_closed_
);
148 real_stream_is_closed_
= true;
151 VirtualAudioInputStream real_
;
152 bool real_stream_is_closed_
;
154 DISALLOW_COPY_AND_ASSIGN(MockVirtualAudioInputStream
);
157 class MockAudioInputCallback
: public AudioInputStream::AudioInputCallback
{
159 MockAudioInputCallback() {}
162 void(AudioInputStream
* stream
,
163 const media::AudioBus
* src
,
164 uint32 hardware_delay_bytes
,
166 MOCK_METHOD1(OnError
, void(AudioInputStream
* stream
));
169 DISALLOW_COPY_AND_ASSIGN(MockAudioInputCallback
);
174 class WebContentsAudioInputStreamTest
: public testing::Test
{
176 WebContentsAudioInputStreamTest()
177 : thread_bundle_(new TestBrowserThreadBundle(
178 TestBrowserThreadBundle::REAL_IO_THREAD
)),
179 audio_thread_("Audio thread"),
180 mock_mirroring_manager_(new MockAudioMirroringManager()),
181 mock_tracker_(new MockWebContentsTracker()),
185 current_render_process_id_(kRenderProcessId
),
186 current_render_frame_id_(kRenderFrameId
),
187 on_data_event_(false, false) {
188 audio_thread_
.Start();
191 ~WebContentsAudioInputStreamTest() override
{
192 audio_thread_
.Stop();
193 thread_bundle_
.reset();
197 EXPECT_FALSE(destination_
);
198 DCHECK(streams_
.empty());
199 DCHECK(sources_
.empty());
204 new MockVirtualAudioInputStream(audio_thread_
.message_loop_proxy());
205 EXPECT_CALL(*mock_vais_
, Open());
206 EXPECT_CALL(*mock_vais_
, Close()); // At Close() time.
208 ASSERT_EQ(kRenderProcessId
, current_render_process_id_
);
209 ASSERT_EQ(kRenderFrameId
, current_render_frame_id_
);
210 EXPECT_CALL(*mock_tracker_
.get(),
211 Start(kRenderProcessId
, kRenderFrameId
, _
))
213 SaveArg
<2>(&change_callback_
),
214 WithArgs
<0, 1>(Invoke(this,
215 &WebContentsAudioInputStreamTest::
216 SimulateChangeCallback
))));
218 EXPECT_CALL(*mock_tracker_
.get(), Stop()); // At Close() time.
220 wcais_
= new WebContentsAudioInputStream(
221 current_render_process_id_
, current_render_frame_id_
,
222 mock_mirroring_manager_
.get(),
223 mock_tracker_
, mock_vais_
);
228 EXPECT_CALL(*mock_vais_
, Start(&mock_input_callback_
));
229 EXPECT_CALL(*mock_vais_
, Stop()); // At Stop() time.
231 EXPECT_CALL(*mock_mirroring_manager_
, StartMirroring(NotNull()))
232 .WillOnce(SaveArg
<0>(&destination_
))
233 .RetiresOnSaturation();
234 // At Stop() time, or when the mirroring target changes:
235 EXPECT_CALL(*mock_mirroring_manager_
, StopMirroring(NotNull()))
238 static_cast<AudioMirroringManager::MirroringDestination
*>(NULL
)))
239 .RetiresOnSaturation();
241 EXPECT_CALL(mock_input_callback_
, OnData(NotNull(), NotNull(), _
, _
))
243 InvokeWithoutArgs(&on_data_event_
, &base::WaitableEvent::Signal
));
245 wcais_
->Start(&mock_input_callback_
);
247 // Test plumbing of volume controls and automatic gain controls. Calls to
248 // wcais_ methods should delegate directly to mock_vais_.
249 EXPECT_CALL(*mock_vais_
, GetVolume());
250 double volume
= wcais_
->GetVolume();
251 EXPECT_CALL(*mock_vais_
, GetMaxVolume());
252 const double max_volume
= wcais_
->GetMaxVolume();
254 if (volume
< max_volume
) {
257 EXPECT_CALL(*mock_vais_
, SetVolume(volume
));
258 wcais_
->SetVolume(volume
);
259 EXPECT_CALL(*mock_vais_
, GetAutomaticGainControl());
260 bool auto_gain
= wcais_
->GetAutomaticGainControl();
261 auto_gain
= !auto_gain
;
262 EXPECT_CALL(*mock_vais_
, SetAutomaticGainControl(auto_gain
));
263 wcais_
->SetAutomaticGainControl(auto_gain
);
266 void AddAnotherInput() {
267 // Note: WCAIS posts a task to invoke
268 // MockAudioMirroringManager::StartMirroring() on the IO thread, which
269 // causes our mock to set |destination_|. Block until that has happened.
270 base::WaitableEvent
done(false, false);
271 BrowserThread::PostTask(
272 BrowserThread::IO
, FROM_HERE
, base::Bind(
273 &base::WaitableEvent::Signal
, base::Unretained(&done
)));
275 ASSERT_TRUE(destination_
);
277 EXPECT_CALL(*mock_vais_
, AddOutputStream(NotNull(), _
))
278 .RetiresOnSaturation();
279 // Later, when stream is closed:
280 EXPECT_CALL(*mock_vais_
, RemoveOutputStream(NotNull(), _
))
281 .RetiresOnSaturation();
283 const AudioParameters
& params
= TestAudioParameters();
284 AudioOutputStream
* const out
= destination_
->AddInput(params
);
286 streams_
.push_back(out
);
287 EXPECT_TRUE(out
->Open());
288 SineWaveAudioSource
* const source
= new SineWaveAudioSource(
289 params
.channel_layout(), 200.0, params
.sample_rate());
290 sources_
.push_back(source
);
294 void RemoveOneInputInFIFOOrder() {
295 ASSERT_FALSE(streams_
.empty());
296 AudioOutputStream
* const out
= streams_
.front();
297 streams_
.pop_front();
299 out
->Close(); // Self-deletes.
300 ASSERT_TRUE(!sources_
.empty());
301 delete sources_
.front();
302 sources_
.pop_front();
305 void ChangeMirroringTarget() {
306 const int next_render_process_id
=
307 current_render_process_id_
== kRenderProcessId
?
308 kAnotherRenderProcessId
: kRenderProcessId
;
309 const int next_render_frame_id
=
310 current_render_frame_id_
== kRenderFrameId
?
311 kAnotherRenderFrameId
: kRenderFrameId
;
313 EXPECT_CALL(*mock_mirroring_manager_
, StartMirroring(NotNull()))
314 .WillOnce(SaveArg
<0>(&destination_
))
315 .RetiresOnSaturation();
317 SimulateChangeCallback(next_render_process_id
, next_render_frame_id
);
319 current_render_process_id_
= next_render_process_id
;
320 current_render_frame_id_
= next_render_frame_id
;
323 void LoseMirroringTarget() {
324 EXPECT_CALL(mock_input_callback_
, OnError(_
));
326 SimulateChangeCallback(-1, -1);
334 // WebContentsAudioInputStream self-destructs on Close(). Its internal
335 // objects hang around until they are no longer referred to (e.g., as tasks
336 // on other threads shut things down).
342 void RunOnAudioThread(const base::Closure
& closure
) {
343 audio_thread_
.message_loop()->PostTask(FROM_HERE
, closure
);
346 // Block the calling thread until OnData() callbacks are being made.
348 // Note: Arbitrarily chosen, but more iterations causes tests to take
349 // significantly more time.
350 static const int kNumIterations
= 3;
351 for (int i
= 0; i
< kNumIterations
; ++i
)
352 on_data_event_
.Wait();
356 void SimulateChangeCallback(int render_process_id
, int render_frame_id
) {
357 ASSERT_FALSE(change_callback_
.is_null());
358 if (render_process_id
== -1 || render_frame_id
== -1) {
359 change_callback_
.Run(NULL
);
361 // For our tests, any non-NULL value will suffice since it will not be
363 change_callback_
.Run(reinterpret_cast<RenderWidgetHost
*>(0xdeadbee5));
367 scoped_ptr
<TestBrowserThreadBundle
> thread_bundle_
;
368 base::Thread audio_thread_
;
370 scoped_ptr
<MockAudioMirroringManager
> mock_mirroring_manager_
;
371 scoped_refptr
<MockWebContentsTracker
> mock_tracker_
;
373 MockVirtualAudioInputStream
* mock_vais_
; // Owned by wcais_.
374 WebContentsAudioInputStream
* wcais_
; // Self-destructs on Close().
376 // Mock consumer of audio data.
377 MockAudioInputCallback mock_input_callback_
;
379 // Provided by WebContentsAudioInputStream to the mock WebContentsTracker.
380 // This callback is saved here, and test code will invoke it to simulate
381 // target change events.
382 WebContentsTracker::ChangeCallback change_callback_
;
384 // Provided by WebContentsAudioInputStream to the mock AudioMirroringManager.
385 // A pointer to the implementation is saved here, and test code will invoke it
386 // to simulate: 1) calls to AddInput(); and 2) diverting audio data.
387 AudioMirroringManager::MirroringDestination
* destination_
;
389 // Current target RenderFrame. These get flipped in ChangedMirroringTarget().
390 int current_render_process_id_
;
391 int current_render_frame_id_
;
393 // Streams provided by calls to WebContentsAudioInputStream::AddInput(). Each
394 // is started with a simulated source of audio data.
395 std::list
<AudioOutputStream
*> streams_
;
396 std::list
<SineWaveAudioSource
*> sources_
; // 1:1 with elements in streams_.
398 base::WaitableEvent on_data_event_
;
400 DISALLOW_COPY_AND_ASSIGN(WebContentsAudioInputStreamTest
);
403 #define RUN_ON_AUDIO_THREAD(method) \
404 RunOnAudioThread(base::Bind(&WebContentsAudioInputStreamTest::method, \
405 base::Unretained(this)))
407 TEST_F(WebContentsAudioInputStreamTest
, OpenedButNeverStarted
) {
408 RUN_ON_AUDIO_THREAD(Open
);
409 RUN_ON_AUDIO_THREAD(Close
);
412 TEST_F(WebContentsAudioInputStreamTest
, MirroringNothing
) {
413 RUN_ON_AUDIO_THREAD(Open
);
414 RUN_ON_AUDIO_THREAD(Start
);
416 RUN_ON_AUDIO_THREAD(Stop
);
417 RUN_ON_AUDIO_THREAD(Close
);
420 TEST_F(WebContentsAudioInputStreamTest
, MirroringOutputOutlivesSession
) {
421 RUN_ON_AUDIO_THREAD(Open
);
422 RUN_ON_AUDIO_THREAD(Start
);
423 RUN_ON_AUDIO_THREAD(AddAnotherInput
);
425 RUN_ON_AUDIO_THREAD(Stop
);
426 RUN_ON_AUDIO_THREAD(Close
);
427 RUN_ON_AUDIO_THREAD(RemoveOneInputInFIFOOrder
);
430 TEST_F(WebContentsAudioInputStreamTest
, MirroringOutputWithinSession
) {
431 RUN_ON_AUDIO_THREAD(Open
);
432 RUN_ON_AUDIO_THREAD(Start
);
433 RUN_ON_AUDIO_THREAD(AddAnotherInput
);
435 RUN_ON_AUDIO_THREAD(RemoveOneInputInFIFOOrder
);
436 RUN_ON_AUDIO_THREAD(Stop
);
437 RUN_ON_AUDIO_THREAD(Close
);
440 TEST_F(WebContentsAudioInputStreamTest
, MirroringNothingWithTargetChange
) {
441 RUN_ON_AUDIO_THREAD(Open
);
442 RUN_ON_AUDIO_THREAD(Start
);
443 RUN_ON_AUDIO_THREAD(ChangeMirroringTarget
);
444 RUN_ON_AUDIO_THREAD(Stop
);
445 RUN_ON_AUDIO_THREAD(Close
);
448 TEST_F(WebContentsAudioInputStreamTest
, MirroringOneStreamAfterTargetChange
) {
449 RUN_ON_AUDIO_THREAD(Open
);
450 RUN_ON_AUDIO_THREAD(Start
);
451 RUN_ON_AUDIO_THREAD(ChangeMirroringTarget
);
452 RUN_ON_AUDIO_THREAD(AddAnotherInput
);
454 RUN_ON_AUDIO_THREAD(Stop
);
455 RUN_ON_AUDIO_THREAD(Close
);
456 RUN_ON_AUDIO_THREAD(RemoveOneInputInFIFOOrder
);
459 TEST_F(WebContentsAudioInputStreamTest
, MirroringOneStreamWithTargetChange
) {
460 RUN_ON_AUDIO_THREAD(Open
);
461 RUN_ON_AUDIO_THREAD(Start
);
462 RUN_ON_AUDIO_THREAD(AddAnotherInput
);
464 RUN_ON_AUDIO_THREAD(ChangeMirroringTarget
);
465 RUN_ON_AUDIO_THREAD(RemoveOneInputInFIFOOrder
);
466 RUN_ON_AUDIO_THREAD(AddAnotherInput
);
468 RUN_ON_AUDIO_THREAD(Stop
);
469 RUN_ON_AUDIO_THREAD(Close
);
470 RUN_ON_AUDIO_THREAD(RemoveOneInputInFIFOOrder
);
473 TEST_F(WebContentsAudioInputStreamTest
, MirroringLostTarget
) {
474 RUN_ON_AUDIO_THREAD(Open
);
475 RUN_ON_AUDIO_THREAD(Start
);
476 RUN_ON_AUDIO_THREAD(AddAnotherInput
);
478 RUN_ON_AUDIO_THREAD(LoseMirroringTarget
);
479 RUN_ON_AUDIO_THREAD(RemoveOneInputInFIFOOrder
);
480 RUN_ON_AUDIO_THREAD(Stop
);
481 RUN_ON_AUDIO_THREAD(Close
);
484 TEST_F(WebContentsAudioInputStreamTest
, MirroringMultipleStreamsAndTargets
) {
485 RUN_ON_AUDIO_THREAD(Open
);
486 RUN_ON_AUDIO_THREAD(Start
);
487 RUN_ON_AUDIO_THREAD(AddAnotherInput
);
489 RUN_ON_AUDIO_THREAD(AddAnotherInput
);
490 RUN_ON_AUDIO_THREAD(AddAnotherInput
);
491 RUN_ON_AUDIO_THREAD(AddAnotherInput
);
493 RUN_ON_AUDIO_THREAD(ChangeMirroringTarget
);
494 RUN_ON_AUDIO_THREAD(RemoveOneInputInFIFOOrder
);
496 RUN_ON_AUDIO_THREAD(RemoveOneInputInFIFOOrder
);
497 RUN_ON_AUDIO_THREAD(RemoveOneInputInFIFOOrder
);
498 RUN_ON_AUDIO_THREAD(AddAnotherInput
);
500 RUN_ON_AUDIO_THREAD(RemoveOneInputInFIFOOrder
);
502 RUN_ON_AUDIO_THREAD(ChangeMirroringTarget
);
503 RUN_ON_AUDIO_THREAD(RemoveOneInputInFIFOOrder
);
504 RUN_ON_AUDIO_THREAD(Stop
);
505 RUN_ON_AUDIO_THREAD(Close
);
508 } // namespace content