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/media/capture/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/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" 22 23 using ::testing::_; 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; 31 32 using media::AudioInputStream; 33 using media::AudioOutputStream; 34 using media::AudioParameters; 35 using media::SineWaveAudioSource; 36 using media::VirtualAudioInputStream; 37 using media::VirtualAudioOutputStream; 38 39 namespace content { 40 41 namespace { 42 43 const int kRenderProcessId = 123; 44 const int kRenderFrameId = 456; 45 const int kAnotherRenderProcessId = 789; 46 const int kAnotherRenderFrameId = 1; 47 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); 54 return params; 55 } 56 57 class MockAudioMirroringManager : public AudioMirroringManager { 58 public: 59 MockAudioMirroringManager() : AudioMirroringManager() {} 60 virtual ~MockAudioMirroringManager() {} 61 62 MOCK_METHOD1(StartMirroring, void(MirroringDestination* destination)); 63 MOCK_METHOD1(StopMirroring, void(MirroringDestination* destination)); 64 65 private: 66 DISALLOW_COPY_AND_ASSIGN(MockAudioMirroringManager); 67 }; 68 69 class MockWebContentsTracker : public WebContentsTracker { 70 public: 71 MockWebContentsTracker() : WebContentsTracker(false) {} 72 73 MOCK_METHOD3(Start, 74 void(int render_process_id, int render_frame_id, 75 const ChangeCallback& callback)); 76 MOCK_METHOD0(Stop, void()); 77 78 private: 79 virtual ~MockWebContentsTracker() {} 80 81 DISALLOW_COPY_AND_ASSIGN(MockWebContentsTracker); 82 }; 83 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 { 87 public: 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 97 // implementation. 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(_)) 113 .WillByDefault( 114 Invoke(&real_, &VirtualAudioInputStream::SetAutomaticGainControl)); 115 ON_CALL(*this, GetAutomaticGainControl()) 116 .WillByDefault( 117 Invoke(&real_, &VirtualAudioInputStream::GetAutomaticGainControl)); 118 ON_CALL(*this, AddOutputStream(NotNull(), _)) 119 .WillByDefault( 120 Invoke(&real_, &VirtualAudioInputStream::AddOutputStream)); 121 ON_CALL(*this, RemoveOutputStream(NotNull(), _)) 122 .WillByDefault( 123 Invoke(&real_, &VirtualAudioInputStream::RemoveOutputStream)); 124 } 125 126 ~MockVirtualAudioInputStream() { 127 DCHECK(real_stream_is_closed_); 128 } 129 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, void(bool)); 138 MOCK_METHOD0(GetAutomaticGainControl, bool()); 139 MOCK_METHOD2(AddOutputStream, void(VirtualAudioOutputStream*, 140 const AudioParameters&)); 141 MOCK_METHOD2(RemoveOutputStream, void(VirtualAudioOutputStream*, 142 const AudioParameters&)); 143 144 private: 145 void OnRealStreamHasClosed(VirtualAudioInputStream* stream) { 146 DCHECK_EQ(&real_, stream); 147 DCHECK(!real_stream_is_closed_); 148 real_stream_is_closed_ = true; 149 } 150 151 VirtualAudioInputStream real_; 152 bool real_stream_is_closed_; 153 154 DISALLOW_COPY_AND_ASSIGN(MockVirtualAudioInputStream); 155 }; 156 157 class MockAudioInputCallback : public AudioInputStream::AudioInputCallback { 158 public: 159 MockAudioInputCallback() {} 160 161 MOCK_METHOD4(OnData, 162 void(AudioInputStream* stream, 163 const media::AudioBus* src, 164 uint32 hardware_delay_bytes, 165 double volume)); 166 MOCK_METHOD1(OnError, void(AudioInputStream* stream)); 167 168 private: 169 DISALLOW_COPY_AND_ASSIGN(MockAudioInputCallback); 170 }; 171 172 } // namespace 173 174 class WebContentsAudioInputStreamTest : public testing::Test { 175 public: 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()), 182 mock_vais_(NULL), 183 wcais_(NULL), 184 destination_(NULL), 185 current_render_process_id_(kRenderProcessId), 186 current_render_frame_id_(kRenderFrameId), 187 on_data_event_(false, false) { 188 audio_thread_.Start(); 189 } 190 191 virtual ~WebContentsAudioInputStreamTest() { 192 audio_thread_.Stop(); 193 thread_bundle_.reset(); 194 195 DCHECK(!mock_vais_); 196 DCHECK(!wcais_); 197 EXPECT_FALSE(destination_); 198 DCHECK(streams_.empty()); 199 DCHECK(sources_.empty()); 200 } 201 202 void Open() { 203 mock_vais_ = 204 new MockVirtualAudioInputStream(audio_thread_.message_loop_proxy()); 205 EXPECT_CALL(*mock_vais_, Open()); 206 EXPECT_CALL(*mock_vais_, Close()); // At Close() time. 207 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, _)) 212 .WillOnce(DoAll( 213 SaveArg<2>(&change_callback_), 214 WithArgs<0, 1>(Invoke(this, 215 &WebContentsAudioInputStreamTest:: 216 SimulateChangeCallback)))); 217 218 EXPECT_CALL(*mock_tracker_.get(), Stop()); // At Close() time. 219 220 wcais_ = new WebContentsAudioInputStream( 221 current_render_process_id_, current_render_frame_id_, 222 mock_mirroring_manager_.get(), 223 mock_tracker_, mock_vais_); 224 wcais_->Open(); 225 } 226 227 void Start() { 228 EXPECT_CALL(*mock_vais_, Start(&mock_input_callback_)); 229 EXPECT_CALL(*mock_vais_, Stop()); // At Stop() time. 230 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())) 236 .WillOnce(Assign( 237 &destination_, 238 static_cast<AudioMirroringManager::MirroringDestination*>(NULL))) 239 .RetiresOnSaturation(); 240 241 EXPECT_CALL(mock_input_callback_, OnData(NotNull(), NotNull(), _, _)) 242 .WillRepeatedly( 243 InvokeWithoutArgs(&on_data_event_, &base::WaitableEvent::Signal)); 244 245 wcais_->Start(&mock_input_callback_); 246 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(); 253 volume *= 2.0; 254 if (volume < max_volume) { 255 volume = max_volume; 256 } 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); 264 } 265 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))); 274 done.Wait(); 275 ASSERT_TRUE(destination_); 276 277 EXPECT_CALL(*mock_vais_, AddOutputStream(NotNull(), _)) 278 .RetiresOnSaturation(); 279 // Later, when stream is closed: 280 EXPECT_CALL(*mock_vais_, RemoveOutputStream(NotNull(), _)) 281 .RetiresOnSaturation(); 282 283 const AudioParameters& params = TestAudioParameters(); 284 AudioOutputStream* const out = destination_->AddInput(params); 285 ASSERT_TRUE(out); 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); 291 out->Start(source); 292 } 293 294 void RemoveOneInputInFIFOOrder() { 295 ASSERT_FALSE(streams_.empty()); 296 AudioOutputStream* const out = streams_.front(); 297 streams_.pop_front(); 298 out->Stop(); 299 out->Close(); // Self-deletes. 300 ASSERT_TRUE(!sources_.empty()); 301 delete sources_.front(); 302 sources_.pop_front(); 303 } 304 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; 312 313 EXPECT_CALL(*mock_mirroring_manager_, StartMirroring(NotNull())) 314 .WillOnce(SaveArg<0>(&destination_)) 315 .RetiresOnSaturation(); 316 317 SimulateChangeCallback(next_render_process_id, next_render_frame_id); 318 319 current_render_process_id_ = next_render_process_id; 320 current_render_frame_id_ = next_render_frame_id; 321 } 322 323 void LoseMirroringTarget() { 324 EXPECT_CALL(mock_input_callback_, OnError(_)); 325 326 SimulateChangeCallback(-1, -1); 327 } 328 329 void Stop() { 330 wcais_->Stop(); 331 } 332 333 void Close() { 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). 337 wcais_->Close(); 338 wcais_ = NULL; 339 mock_vais_ = NULL; 340 } 341 342 void RunOnAudioThread(const base::Closure& closure) { 343 audio_thread_.message_loop()->PostTask(FROM_HERE, closure); 344 } 345 346 // Block the calling thread until OnData() callbacks are being made. 347 void WaitForData() { 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(); 353 } 354 355 private: 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); 360 } else { 361 // For our tests, any non-NULL value will suffice since it will not be 362 // dereferenced. 363 change_callback_.Run(reinterpret_cast<RenderWidgetHost*>(0xdeadbee5)); 364 } 365 } 366 367 scoped_ptr<TestBrowserThreadBundle> thread_bundle_; 368 base::Thread audio_thread_; 369 370 scoped_ptr<MockAudioMirroringManager> mock_mirroring_manager_; 371 scoped_refptr<MockWebContentsTracker> mock_tracker_; 372 373 MockVirtualAudioInputStream* mock_vais_; // Owned by wcais_. 374 WebContentsAudioInputStream* wcais_; // Self-destructs on Close(). 375 376 // Mock consumer of audio data. 377 MockAudioInputCallback mock_input_callback_; 378 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_; 383 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_; 388 389 // Current target RenderFrame. These get flipped in ChangedMirroringTarget(). 390 int current_render_process_id_; 391 int current_render_frame_id_; 392 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_. 397 398 base::WaitableEvent on_data_event_; 399 400 DISALLOW_COPY_AND_ASSIGN(WebContentsAudioInputStreamTest); 401 }; 402 403 #define RUN_ON_AUDIO_THREAD(method) \ 404 RunOnAudioThread(base::Bind(&WebContentsAudioInputStreamTest::method, \ 405 base::Unretained(this))) 406 407 TEST_F(WebContentsAudioInputStreamTest, OpenedButNeverStarted) { 408 RUN_ON_AUDIO_THREAD(Open); 409 RUN_ON_AUDIO_THREAD(Close); 410 } 411 412 TEST_F(WebContentsAudioInputStreamTest, MirroringNothing) { 413 RUN_ON_AUDIO_THREAD(Open); 414 RUN_ON_AUDIO_THREAD(Start); 415 WaitForData(); 416 RUN_ON_AUDIO_THREAD(Stop); 417 RUN_ON_AUDIO_THREAD(Close); 418 } 419 420 TEST_F(WebContentsAudioInputStreamTest, MirroringOutputOutlivesSession) { 421 RUN_ON_AUDIO_THREAD(Open); 422 RUN_ON_AUDIO_THREAD(Start); 423 RUN_ON_AUDIO_THREAD(AddAnotherInput); 424 WaitForData(); 425 RUN_ON_AUDIO_THREAD(Stop); 426 RUN_ON_AUDIO_THREAD(Close); 427 RUN_ON_AUDIO_THREAD(RemoveOneInputInFIFOOrder); 428 } 429 430 TEST_F(WebContentsAudioInputStreamTest, MirroringOutputWithinSession) { 431 RUN_ON_AUDIO_THREAD(Open); 432 RUN_ON_AUDIO_THREAD(Start); 433 RUN_ON_AUDIO_THREAD(AddAnotherInput); 434 WaitForData(); 435 RUN_ON_AUDIO_THREAD(RemoveOneInputInFIFOOrder); 436 RUN_ON_AUDIO_THREAD(Stop); 437 RUN_ON_AUDIO_THREAD(Close); 438 } 439 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); 446 } 447 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); 453 WaitForData(); 454 RUN_ON_AUDIO_THREAD(Stop); 455 RUN_ON_AUDIO_THREAD(Close); 456 RUN_ON_AUDIO_THREAD(RemoveOneInputInFIFOOrder); 457 } 458 459 TEST_F(WebContentsAudioInputStreamTest, MirroringOneStreamWithTargetChange) { 460 RUN_ON_AUDIO_THREAD(Open); 461 RUN_ON_AUDIO_THREAD(Start); 462 RUN_ON_AUDIO_THREAD(AddAnotherInput); 463 WaitForData(); 464 RUN_ON_AUDIO_THREAD(ChangeMirroringTarget); 465 RUN_ON_AUDIO_THREAD(RemoveOneInputInFIFOOrder); 466 RUN_ON_AUDIO_THREAD(AddAnotherInput); 467 WaitForData(); 468 RUN_ON_AUDIO_THREAD(Stop); 469 RUN_ON_AUDIO_THREAD(Close); 470 RUN_ON_AUDIO_THREAD(RemoveOneInputInFIFOOrder); 471 } 472 473 TEST_F(WebContentsAudioInputStreamTest, MirroringLostTarget) { 474 RUN_ON_AUDIO_THREAD(Open); 475 RUN_ON_AUDIO_THREAD(Start); 476 RUN_ON_AUDIO_THREAD(AddAnotherInput); 477 WaitForData(); 478 RUN_ON_AUDIO_THREAD(LoseMirroringTarget); 479 RUN_ON_AUDIO_THREAD(RemoveOneInputInFIFOOrder); 480 RUN_ON_AUDIO_THREAD(Stop); 481 RUN_ON_AUDIO_THREAD(Close); 482 } 483 484 TEST_F(WebContentsAudioInputStreamTest, MirroringMultipleStreamsAndTargets) { 485 RUN_ON_AUDIO_THREAD(Open); 486 RUN_ON_AUDIO_THREAD(Start); 487 RUN_ON_AUDIO_THREAD(AddAnotherInput); 488 WaitForData(); 489 RUN_ON_AUDIO_THREAD(AddAnotherInput); 490 RUN_ON_AUDIO_THREAD(AddAnotherInput); 491 RUN_ON_AUDIO_THREAD(AddAnotherInput); 492 WaitForData(); 493 RUN_ON_AUDIO_THREAD(ChangeMirroringTarget); 494 RUN_ON_AUDIO_THREAD(RemoveOneInputInFIFOOrder); 495 WaitForData(); 496 RUN_ON_AUDIO_THREAD(RemoveOneInputInFIFOOrder); 497 RUN_ON_AUDIO_THREAD(RemoveOneInputInFIFOOrder); 498 RUN_ON_AUDIO_THREAD(AddAnotherInput); 499 WaitForData(); 500 RUN_ON_AUDIO_THREAD(RemoveOneInputInFIFOOrder); 501 WaitForData(); 502 RUN_ON_AUDIO_THREAD(ChangeMirroringTarget); 503 RUN_ON_AUDIO_THREAD(RemoveOneInputInFIFOOrder); 504 RUN_ON_AUDIO_THREAD(Stop); 505 RUN_ON_AUDIO_THREAD(Close); 506 } 507 508 } // namespace content 509