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 22 using ::testing::_; 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; 30 31 using media::AudioInputStream; 32 using media::AudioOutputStream; 33 using media::AudioParameters; 34 using media::SineWaveAudioSource; 35 using media::VirtualAudioInputStream; 36 using media::VirtualAudioOutputStream; 37 38 namespace content { 39 40 namespace { 41 42 const int kRenderProcessId = 123; 43 const int kRenderViewId = 456; 44 const int kAnotherRenderProcessId = 789; 45 const int kAnotherRenderViewId = 1; 46 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); 53 return params; 54 } 55 56 class 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 72 class 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. 89 class 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 160 class 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 176 class 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 412 TEST_F(WebContentsAudioInputStreamTest, OpenedButNeverStarted) { 413 RUN_ON_AUDIO_THREAD(Open); 414 RUN_ON_AUDIO_THREAD(Close); 415 } 416 417 TEST_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 425 TEST_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 435 TEST_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 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); 451 } 452 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); 458 WaitForData(); 459 RUN_ON_AUDIO_THREAD(Stop); 460 RUN_ON_AUDIO_THREAD(Close); 461 RUN_ON_AUDIO_THREAD(RemoveOneInputInFIFOOrder); 462 } 463 464 TEST_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 478 TEST_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 489 TEST_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