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/renderer_host/media/web_contents_audio_input_stream.h"
10 #include "base/bind_helpers.h"
11 #include "base/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"
23 using ::testing::Assign
;
24 using ::testing::DoAll
;
25 using ::testing::Invoke
;
26 using ::testing::InvokeWithoutArgs
;
27 using ::testing::NotNull
;
28 using ::testing::SaveArg
;
29 using ::testing::WithArgs
;
31 using media::AudioInputStream
;
32 using media::AudioOutputStream
;
33 using media::AudioParameters
;
34 using media::SineWaveAudioSource
;
35 using media::VirtualAudioInputStream
;
36 using media::VirtualAudioOutputStream
;
42 const int kRenderProcessId
= 123;
43 const int kRenderViewId
= 456;
44 const int kAnotherRenderProcessId
= 789;
45 const int kAnotherRenderViewId
= 1;
47 const AudioParameters
& TestAudioParameters() {
48 static const AudioParameters
params(
49 AudioParameters::AUDIO_FAKE
,
50 media::CHANNEL_LAYOUT_STEREO
,
51 AudioParameters::kAudioCDSampleRate
, 16,
52 AudioParameters::kAudioCDSampleRate
/ 100);
56 class MockAudioMirroringManager
: public AudioMirroringManager
{
58 MockAudioMirroringManager() : AudioMirroringManager() {}
59 virtual ~MockAudioMirroringManager() {}
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
));
69 DISALLOW_COPY_AND_ASSIGN(MockAudioMirroringManager
);
72 class MockWebContentsTracker
: public WebContentsTracker
{
74 MockWebContentsTracker() : WebContentsTracker() {}
77 void(int render_process_id
, int render_view_id
,
78 const ChangeCallback
& callback
));
79 MOCK_METHOD0(Stop
, void());
82 virtual ~MockWebContentsTracker() {}
84 DISALLOW_COPY_AND_ASSIGN(MockWebContentsTracker
);
87 // A fully-functional VirtualAudioInputStream, but methods are mocked to allow
88 // tests to check how/when they are invoked.
89 class MockVirtualAudioInputStream
: public VirtualAudioInputStream
{
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
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(_
))
117 Invoke(&real_
, &VirtualAudioInputStream::SetAutomaticGainControl
));
118 ON_CALL(*this, GetAutomaticGainControl())
120 Invoke(&real_
, &VirtualAudioInputStream::GetAutomaticGainControl
));
121 ON_CALL(*this, AddOutputStream(NotNull(), _
))
123 Invoke(&real_
, &VirtualAudioInputStream::AddOutputStream
));
124 ON_CALL(*this, RemoveOutputStream(NotNull(), _
))
126 Invoke(&real_
, &VirtualAudioInputStream::RemoveOutputStream
));
129 ~MockVirtualAudioInputStream() {
130 DCHECK(real_stream_is_closed_
);
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
&));
148 void OnRealStreamHasClosed(VirtualAudioInputStream
* stream
) {
149 DCHECK_EQ(&real_
, stream
);
150 DCHECK(!real_stream_is_closed_
);
151 real_stream_is_closed_
= true;
154 VirtualAudioInputStream real_
;
155 bool real_stream_is_closed_
;
157 DISALLOW_COPY_AND_ASSIGN(MockVirtualAudioInputStream
);
160 class MockAudioInputCallback
: public AudioInputStream::AudioInputCallback
{
162 MockAudioInputCallback() {}
164 MOCK_METHOD5(OnData
, void(AudioInputStream
* stream
, const uint8
* src
,
165 uint32 size
, uint32 hardware_delay_bytes
,
167 MOCK_METHOD1(OnClose
, void(AudioInputStream
* stream
));
168 MOCK_METHOD1(OnError
, void(AudioInputStream
* stream
));
171 DISALLOW_COPY_AND_ASSIGN(MockAudioInputCallback
);
176 class WebContentsAudioInputStreamTest
: public testing::Test
{
178 WebContentsAudioInputStreamTest()
179 : audio_thread_("Audio thread"),
180 io_thread_(BrowserThread::IO
),
181 mock_mirroring_manager_(new MockAudioMirroringManager()),
182 mock_tracker_(new MockWebContentsTracker()),
186 current_render_process_id_(kRenderProcessId
),
187 current_render_view_id_(kRenderViewId
),
188 on_data_event_(false, false) {
189 audio_thread_
.Start();
193 virtual ~WebContentsAudioInputStreamTest() {
194 audio_thread_
.Stop();
199 EXPECT_FALSE(destination_
);
200 DCHECK(streams_
.empty());
201 DCHECK(sources_
.empty());
206 new MockVirtualAudioInputStream(audio_thread_
.message_loop_proxy());
207 EXPECT_CALL(*mock_vais_
, Open());
208 EXPECT_CALL(*mock_vais_
, Close()); // At Close() time.
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
, _
))
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.
219 wcais_
= new WebContentsAudioInputStream(
220 current_render_process_id_
, current_render_view_id_
,
221 mock_mirroring_manager_
.get(),
222 mock_tracker_
, mock_vais_
);
227 EXPECT_CALL(*mock_vais_
, Start(&mock_input_callback_
));
228 EXPECT_CALL(*mock_vais_
, Stop()); // At Stop() time.
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()))
239 static_cast<AudioMirroringManager::MirroringDestination
*>(NULL
)))
240 .RetiresOnSaturation();
242 EXPECT_CALL(mock_input_callback_
, OnData(NotNull(), NotNull(), _
, _
, _
))
244 InvokeWithoutArgs(&on_data_event_
, &base::WaitableEvent::Signal
));
245 EXPECT_CALL(mock_input_callback_
, OnClose(_
)); // At Stop() time.
247 wcais_
->Start(&mock_input_callback_
);
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();
256 if (volume
< max_volume
) {
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
);
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
)));
277 ASSERT_TRUE(destination_
);
279 EXPECT_CALL(*mock_vais_
, AddOutputStream(NotNull(), _
))
280 .RetiresOnSaturation();
281 // Later, when stream is closed:
282 EXPECT_CALL(*mock_vais_
, RemoveOutputStream(NotNull(), _
))
283 .RetiresOnSaturation();
285 const AudioParameters
& params
= TestAudioParameters();
286 AudioOutputStream
* const out
= destination_
->AddInput(params
);
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
);
296 void RemoveOneInputInFIFOOrder() {
297 ASSERT_FALSE(streams_
.empty());
298 AudioOutputStream
* const out
= streams_
.front();
299 streams_
.pop_front();
301 out
->Close(); // Self-deletes.
302 ASSERT_TRUE(!sources_
.empty());
303 delete sources_
.front();
304 sources_
.pop_front();
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
;
315 EXPECT_CALL(*mock_mirroring_manager_
,
316 StartMirroring(next_render_process_id
, next_render_view_id
,
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
,
326 static_cast<AudioMirroringManager::MirroringDestination
*>(NULL
)))
327 .RetiresOnSaturation();
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
);
333 current_render_process_id_
= next_render_process_id
;
334 current_render_view_id_
= next_render_view_id
;
337 void LoseMirroringTarget() {
338 EXPECT_CALL(mock_input_callback_
, OnError(_
));
340 // Simulate OnTargetChange() callback from WebContentsTracker.
341 EXPECT_FALSE(change_callback_
.is_null());
342 change_callback_
.Run(-1, -1);
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).
358 void RunOnAudioThread(const base::Closure
& closure
) {
359 audio_thread_
.message_loop()->PostTask(FROM_HERE
, closure
);
362 // Block the calling thread until OnData() callbacks are being made.
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();
372 base::Thread audio_thread_
;
373 BrowserThreadImpl io_thread_
;
375 scoped_ptr
<MockAudioMirroringManager
> mock_mirroring_manager_
;
376 scoped_refptr
<MockWebContentsTracker
> mock_tracker_
;
378 MockVirtualAudioInputStream
* mock_vais_
; // Owned by wcais_.
379 WebContentsAudioInputStream
* wcais_
; // Self-destructs on Close().
381 // Mock consumer of audio data.
382 MockAudioInputCallback mock_input_callback_
;
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_
;
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_
;
394 // Current target RenderView. These get flipped in ChangedMirroringTarget().
395 int current_render_process_id_
;
396 int current_render_view_id_
;
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_.
403 base::WaitableEvent on_data_event_
;
405 DISALLOW_COPY_AND_ASSIGN(WebContentsAudioInputStreamTest
);
408 #define RUN_ON_AUDIO_THREAD(method) \
409 RunOnAudioThread(base::Bind(&WebContentsAudioInputStreamTest::method, \
410 base::Unretained(this)))
412 TEST_F(WebContentsAudioInputStreamTest
, OpenedButNeverStarted
) {
413 RUN_ON_AUDIO_THREAD(Open
);
414 RUN_ON_AUDIO_THREAD(Close
);
417 TEST_F(WebContentsAudioInputStreamTest
, MirroringNothing
) {
418 RUN_ON_AUDIO_THREAD(Open
);
419 RUN_ON_AUDIO_THREAD(Start
);
421 RUN_ON_AUDIO_THREAD(Stop
);
422 RUN_ON_AUDIO_THREAD(Close
);
425 TEST_F(WebContentsAudioInputStreamTest
, MirroringOutputOutlivesSession
) {
426 RUN_ON_AUDIO_THREAD(Open
);
427 RUN_ON_AUDIO_THREAD(Start
);
428 RUN_ON_AUDIO_THREAD(AddAnotherInput
);
430 RUN_ON_AUDIO_THREAD(Stop
);
431 RUN_ON_AUDIO_THREAD(Close
);
432 RUN_ON_AUDIO_THREAD(RemoveOneInputInFIFOOrder
);
435 TEST_F(WebContentsAudioInputStreamTest
, MirroringOutputWithinSession
) {
436 RUN_ON_AUDIO_THREAD(Open
);
437 RUN_ON_AUDIO_THREAD(Start
);
438 RUN_ON_AUDIO_THREAD(AddAnotherInput
);
440 RUN_ON_AUDIO_THREAD(RemoveOneInputInFIFOOrder
);
441 RUN_ON_AUDIO_THREAD(Stop
);
442 RUN_ON_AUDIO_THREAD(Close
);
445 TEST_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
);
453 TEST_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
);
459 RUN_ON_AUDIO_THREAD(Stop
);
460 RUN_ON_AUDIO_THREAD(Close
);
461 RUN_ON_AUDIO_THREAD(RemoveOneInputInFIFOOrder
);
464 TEST_F(WebContentsAudioInputStreamTest
, MirroringOneStreamWithTargetChange
) {
465 RUN_ON_AUDIO_THREAD(Open
);
466 RUN_ON_AUDIO_THREAD(Start
);
467 RUN_ON_AUDIO_THREAD(AddAnotherInput
);
469 RUN_ON_AUDIO_THREAD(ChangeMirroringTarget
);
470 RUN_ON_AUDIO_THREAD(RemoveOneInputInFIFOOrder
);
471 RUN_ON_AUDIO_THREAD(AddAnotherInput
);
473 RUN_ON_AUDIO_THREAD(Stop
);
474 RUN_ON_AUDIO_THREAD(Close
);
475 RUN_ON_AUDIO_THREAD(RemoveOneInputInFIFOOrder
);
478 TEST_F(WebContentsAudioInputStreamTest
, MirroringLostTarget
) {
479 RUN_ON_AUDIO_THREAD(Open
);
480 RUN_ON_AUDIO_THREAD(Start
);
481 RUN_ON_AUDIO_THREAD(AddAnotherInput
);
483 RUN_ON_AUDIO_THREAD(LoseMirroringTarget
);
484 RUN_ON_AUDIO_THREAD(RemoveOneInputInFIFOOrder
);
485 RUN_ON_AUDIO_THREAD(Stop
);
486 RUN_ON_AUDIO_THREAD(Close
);
489 TEST_F(WebContentsAudioInputStreamTest
, MirroringMultipleStreamsAndTargets
) {
490 RUN_ON_AUDIO_THREAD(Open
);
491 RUN_ON_AUDIO_THREAD(Start
);
492 RUN_ON_AUDIO_THREAD(AddAnotherInput
);
494 RUN_ON_AUDIO_THREAD(AddAnotherInput
);
495 RUN_ON_AUDIO_THREAD(AddAnotherInput
);
496 RUN_ON_AUDIO_THREAD(AddAnotherInput
);
498 RUN_ON_AUDIO_THREAD(ChangeMirroringTarget
);
499 RUN_ON_AUDIO_THREAD(RemoveOneInputInFIFOOrder
);
501 RUN_ON_AUDIO_THREAD(RemoveOneInputInFIFOOrder
);
502 RUN_ON_AUDIO_THREAD(RemoveOneInputInFIFOOrder
);
503 RUN_ON_AUDIO_THREAD(AddAnotherInput
);
505 RUN_ON_AUDIO_THREAD(RemoveOneInputInFIFOOrder
);
507 RUN_ON_AUDIO_THREAD(ChangeMirroringTarget
);
508 RUN_ON_AUDIO_THREAD(RemoveOneInputInFIFOOrder
);
509 RUN_ON_AUDIO_THREAD(Stop
);
510 RUN_ON_AUDIO_THREAD(Close
);
513 } // namespace content