webrtc_local_audio_track_unittest.cc revision d0247b1b59f9c528cb6df88b4f2b9afaf80d181e
1// Copyright 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 "base/synchronization/waitable_event.h" 6#include "base/test/test_timeouts.h" 7#include "content/renderer/media/rtc_media_constraints.h" 8#include "content/renderer/media/webrtc_audio_capturer.h" 9#include "content/renderer/media/webrtc_local_audio_source_provider.h" 10#include "content/renderer/media/webrtc_local_audio_track.h" 11#include "media/audio/audio_parameters.h" 12#include "media/base/audio_bus.h" 13#include "media/base/audio_capturer_source.h" 14#include "testing/gmock/include/gmock/gmock.h" 15#include "testing/gtest/include/gtest/gtest.h" 16#include "third_party/libjingle/source/talk/app/webrtc/mediastreaminterface.h" 17 18using ::testing::_; 19using ::testing::AnyNumber; 20using ::testing::AtLeast; 21using ::testing::Return; 22 23namespace content { 24 25namespace { 26 27ACTION_P(SignalEvent, event) { 28 event->Signal(); 29} 30 31// A simple thread that we use to fake the audio thread which provides data to 32// the |WebRtcAudioCapturer|. 33class FakeAudioThread : public base::PlatformThread::Delegate { 34 public: 35 explicit FakeAudioThread(const scoped_refptr<WebRtcAudioCapturer>& capturer) 36 : capturer_(capturer), 37 thread_(), 38 closure_(false, false) { 39 DCHECK(capturer.get()); 40 audio_bus_ = media::AudioBus::Create(capturer_->audio_parameters()); 41 } 42 43 virtual ~FakeAudioThread() { DCHECK(thread_.is_null()); } 44 45 // base::PlatformThread::Delegate: 46 virtual void ThreadMain() OVERRIDE { 47 while (true) { 48 if (closure_.IsSignaled()) 49 return; 50 51 media::AudioCapturerSource::CaptureCallback* callback = 52 static_cast<media::AudioCapturerSource::CaptureCallback*>( 53 capturer_.get()); 54 audio_bus_->Zero(); 55 callback->Capture(audio_bus_.get(), 0, 0, false); 56 57 // Sleep 1ms to yield the resource for the main thread. 58 base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(1)); 59 } 60 } 61 62 void Start() { 63 base::PlatformThread::CreateWithPriority( 64 0, this, &thread_, base::kThreadPriority_RealtimeAudio); 65 CHECK(!thread_.is_null()); 66 } 67 68 void Stop() { 69 closure_.Signal(); 70 base::PlatformThread::Join(thread_); 71 thread_ = base::PlatformThreadHandle(); 72 } 73 74 private: 75 scoped_ptr<media::AudioBus> audio_bus_; 76 scoped_refptr<WebRtcAudioCapturer> capturer_; 77 base::PlatformThreadHandle thread_; 78 base::WaitableEvent closure_; 79 DISALLOW_COPY_AND_ASSIGN(FakeAudioThread); 80}; 81 82class MockCapturerSource : public media::AudioCapturerSource { 83 public: 84 MockCapturerSource() {} 85 MOCK_METHOD3(Initialize, void(const media::AudioParameters& params, 86 CaptureCallback* callback, 87 int session_id)); 88 MOCK_METHOD0(Start, void()); 89 MOCK_METHOD0(Stop, void()); 90 MOCK_METHOD1(SetVolume, void(double volume)); 91 MOCK_METHOD1(SetAutomaticGainControl, void(bool enable)); 92 93 protected: 94 virtual ~MockCapturerSource() {} 95}; 96 97class MockWebRtcAudioCapturerSink : public WebRtcAudioCapturerSink { 98 public: 99 MockWebRtcAudioCapturerSink() {} 100 ~MockWebRtcAudioCapturerSink() {} 101 int CaptureData(const std::vector<int>& channels, 102 const int16* audio_data, 103 int sample_rate, 104 int number_of_channels, 105 int number_of_frames, 106 int audio_delay_milliseconds, 107 int current_volume, 108 bool need_audio_processing, 109 bool key_pressed) OVERRIDE { 110 CaptureData(channels.size(), 111 sample_rate, 112 number_of_channels, 113 number_of_frames, 114 audio_delay_milliseconds, 115 current_volume, 116 need_audio_processing, 117 key_pressed); 118 return 0; 119 } 120 MOCK_METHOD8(CaptureData, 121 void(int number_of_network_channels, 122 int sample_rate, 123 int number_of_channels, 124 int number_of_frames, 125 int audio_delay_milliseconds, 126 int current_volume, 127 bool need_audio_processing, 128 bool key_pressed)); 129 MOCK_METHOD1(SetCaptureFormat, void(const media::AudioParameters& params)); 130}; 131 132} // namespace 133 134class WebRtcLocalAudioTrackTest : public ::testing::Test { 135 protected: 136 virtual void SetUp() OVERRIDE { 137 params_.Reset(media::AudioParameters::AUDIO_PCM_LOW_LATENCY, 138 media::CHANNEL_LAYOUT_STEREO, 2, 0, 48000, 16, 480); 139 capturer_ = WebRtcAudioCapturer::CreateCapturer(); 140 WebRtcLocalAudioSourceProvider* source_provider = 141 static_cast<WebRtcLocalAudioSourceProvider*>( 142 capturer_->audio_source_provider()); 143 source_provider->SetSinkParamsForTesting(params_); 144 capturer_source_ = new MockCapturerSource(); 145 EXPECT_CALL(*capturer_source_.get(), Initialize(_, capturer_.get(), 0)) 146 .WillOnce(Return()); 147 capturer_->SetCapturerSource(capturer_source_, 148 params_.channel_layout(), 149 params_.sample_rate()); 150 151 EXPECT_CALL(*capturer_source_.get(), SetAutomaticGainControl(false)) 152 .WillOnce(Return()); 153 154 // Start the audio thread used by the |capturer_source_|. 155 audio_thread_.reset(new FakeAudioThread(capturer_)); 156 audio_thread_->Start(); 157 } 158 159 virtual void TearDown() { 160 audio_thread_->Stop(); 161 audio_thread_.reset(); 162 } 163 164 media::AudioParameters params_; 165 scoped_refptr<MockCapturerSource> capturer_source_; 166 scoped_refptr<WebRtcAudioCapturer> capturer_; 167 scoped_ptr<FakeAudioThread> audio_thread_; 168}; 169 170// Creates a capturer and audio track, fakes its audio thread, and 171// connect/disconnect the sink to the audio track on the fly, the sink should 172// get data callback when the track is connected to the capturer but not when 173// the track is disconnected from the capturer. 174TEST_F(WebRtcLocalAudioTrackTest, ConnectAndDisconnectOneSink) { 175 EXPECT_CALL(*capturer_source_.get(), Start()).WillOnce(Return()); 176 RTCMediaConstraints constraints; 177 scoped_refptr<WebRtcLocalAudioTrack> track = 178 WebRtcLocalAudioTrack::Create(std::string(), capturer_, NULL, NULL, 179 &constraints); 180 track->Start(); 181 EXPECT_TRUE(track->enabled()); 182 183 // Connect a number of network channels to the audio track. 184 static const int kNumberOfNetworkChannels = 4; 185 for (int i = 0; i < kNumberOfNetworkChannels; ++i) { 186 static_cast<webrtc::AudioTrackInterface*>(track.get())-> 187 GetRenderer()->AddChannel(i); 188 } 189 scoped_ptr<MockWebRtcAudioCapturerSink> sink( 190 new MockWebRtcAudioCapturerSink()); 191 const media::AudioParameters params = capturer_->audio_parameters(); 192 base::WaitableEvent event(false, false); 193 EXPECT_CALL(*sink, SetCaptureFormat(_)).WillOnce(Return()); 194 EXPECT_CALL(*sink, 195 CaptureData(kNumberOfNetworkChannels, 196 params.sample_rate(), 197 params.channels(), 198 params.sample_rate() / 100, 199 0, 200 0, 201 false, 202 false)).Times(AtLeast(1)) 203 .WillRepeatedly(SignalEvent(&event)); 204 track->AddSink(sink.get()); 205 206 EXPECT_TRUE(event.TimedWait(TestTimeouts::tiny_timeout())); 207 track->RemoveSink(sink.get()); 208 209 EXPECT_CALL(*capturer_source_.get(), Stop()).WillOnce(Return()); 210 track->Stop(); 211 track = NULL; 212} 213 214// The same setup as ConnectAndDisconnectOneSink, but enable and disable the 215// audio track on the fly. When the audio track is disabled, there is no data 216// callback to the sink; when the audio track is enabled, there comes data 217// callback. 218// TODO(xians): Enable this test after resolving the racing issue that TSAN 219// reports on MediaStreamTrack::enabled(); 220TEST_F(WebRtcLocalAudioTrackTest, DISABLED_DisableEnableAudioTrack) { 221 EXPECT_CALL(*capturer_source_.get(), Start()).WillOnce(Return()); 222 RTCMediaConstraints constraints; 223 scoped_refptr<WebRtcLocalAudioTrack> track = 224 WebRtcLocalAudioTrack::Create(std::string(), capturer_, NULL, NULL, 225 &constraints); 226 track->Start(); 227 static_cast<webrtc::AudioTrackInterface*>(track.get())-> 228 GetRenderer()->AddChannel(0); 229 EXPECT_TRUE(track->enabled()); 230 EXPECT_TRUE(track->set_enabled(false)); 231 scoped_ptr<MockWebRtcAudioCapturerSink> sink( 232 new MockWebRtcAudioCapturerSink()); 233 const media::AudioParameters params = capturer_->audio_parameters(); 234 base::WaitableEvent event(false, false); 235 EXPECT_CALL(*sink, SetCaptureFormat(_)).WillOnce(Return()); 236 EXPECT_CALL(*sink, 237 CaptureData(1, 238 params.sample_rate(), 239 params.channels(), 240 params.sample_rate() / 100, 241 0, 242 0, 243 false, 244 false)).Times(0); 245 track->AddSink(sink.get()); 246 EXPECT_FALSE(event.TimedWait(TestTimeouts::tiny_timeout())); 247 248 event.Reset(); 249 EXPECT_CALL(*sink, 250 CaptureData(1, 251 params.sample_rate(), 252 params.channels(), 253 params.sample_rate() / 100, 254 0, 255 0, 256 false, 257 false)).Times(AtLeast(1)) 258 .WillRepeatedly(SignalEvent(&event)); 259 EXPECT_TRUE(track->set_enabled(true)); 260 EXPECT_TRUE(event.TimedWait(TestTimeouts::tiny_timeout())); 261 track->RemoveSink(sink.get()); 262 263 EXPECT_CALL(*capturer_source_.get(), Stop()).WillOnce(Return()); 264 track->Stop(); 265 track = NULL; 266} 267 268// Create multiple audio tracks and enable/disable them, verify that the audio 269// callbacks appear/disappear. 270TEST_F(WebRtcLocalAudioTrackTest, MultipleAudioTracks) { 271 EXPECT_CALL(*capturer_source_.get(), Start()).WillOnce(Return()); 272 RTCMediaConstraints constraints; 273 scoped_refptr<WebRtcLocalAudioTrack> track_1 = 274 WebRtcLocalAudioTrack::Create(std::string(), capturer_, NULL, NULL, 275 &constraints); 276 track_1->Start(); 277 static_cast<webrtc::AudioTrackInterface*>(track_1.get())-> 278 GetRenderer()->AddChannel(0); 279 EXPECT_TRUE(track_1->enabled()); 280 scoped_ptr<MockWebRtcAudioCapturerSink> sink_1( 281 new MockWebRtcAudioCapturerSink()); 282 const media::AudioParameters params = capturer_->audio_parameters(); 283 base::WaitableEvent event_1(false, false); 284 EXPECT_CALL(*sink_1, SetCaptureFormat(_)).WillOnce(Return()); 285 EXPECT_CALL(*sink_1, 286 CaptureData(1, 287 params.sample_rate(), 288 params.channels(), 289 params.sample_rate() / 100, 290 0, 291 0, 292 false, 293 false)).Times(AtLeast(1)) 294 .WillRepeatedly(SignalEvent(&event_1)); 295 track_1->AddSink(sink_1.get()); 296 EXPECT_TRUE(event_1.TimedWait(TestTimeouts::tiny_timeout())); 297 298 scoped_refptr<WebRtcLocalAudioTrack> track_2 = 299 WebRtcLocalAudioTrack::Create(std::string(), capturer_, NULL, NULL, 300 &constraints); 301 track_2->Start(); 302 static_cast<webrtc::AudioTrackInterface*>(track_2.get())-> 303 GetRenderer()->AddChannel(1); 304 EXPECT_TRUE(track_2->enabled()); 305 306 // Verify both |sink_1| and |sink_2| get data. 307 event_1.Reset(); 308 base::WaitableEvent event_2(false, false); 309 310 scoped_ptr<MockWebRtcAudioCapturerSink> sink_2( 311 new MockWebRtcAudioCapturerSink()); 312 EXPECT_CALL(*sink_2, SetCaptureFormat(_)).WillOnce(Return()); 313 EXPECT_CALL(*sink_1, 314 CaptureData(1, 315 params.sample_rate(), 316 params.channels(), 317 params.sample_rate() / 100, 318 0, 319 0, 320 false, 321 false)).Times(AtLeast(1)) 322 .WillRepeatedly(SignalEvent(&event_1)); 323 EXPECT_CALL(*sink_2, 324 CaptureData(1, 325 params.sample_rate(), 326 params.channels(), 327 params.sample_rate() / 100, 328 0, 329 0, 330 false, 331 false)).Times(AtLeast(1)) 332 .WillRepeatedly(SignalEvent(&event_2)); 333 track_2->AddSink(sink_2.get()); 334 EXPECT_TRUE(event_1.TimedWait(TestTimeouts::tiny_timeout())); 335 EXPECT_TRUE(event_2.TimedWait(TestTimeouts::tiny_timeout())); 336 337 track_1->RemoveSink(sink_1.get()); 338 track_1->Stop(); 339 track_1 = NULL; 340 341 EXPECT_CALL(*capturer_source_.get(), Stop()).WillOnce(Return()); 342 track_2->RemoveSink(sink_2.get()); 343 track_2->Stop(); 344 track_2 = NULL; 345} 346 347 348// Start one track and verify the capturer is correctly starting its source. 349// And it should be fine to not to call Stop() explicitly. 350TEST_F(WebRtcLocalAudioTrackTest, StartOneAudioTrack) { 351 EXPECT_CALL(*capturer_source_.get(), Start()).Times(1); 352 RTCMediaConstraints constraints; 353 scoped_refptr<WebRtcLocalAudioTrack> track = 354 WebRtcLocalAudioTrack::Create(std::string(), capturer_, NULL, NULL, 355 &constraints); 356 track->Start(); 357 358 // When the track goes away, it will automatically stop the 359 // |capturer_source_|. 360 EXPECT_CALL(*capturer_source_.get(), Stop()); 361 track->Stop(); 362 track = NULL; 363} 364 365// Start/Stop tracks and verify the capturer is correctly starting/stopping 366// its source. 367TEST_F(WebRtcLocalAudioTrackTest, StartAndStopAudioTracks) { 368 // Starting the first audio track will start the |capturer_source_|. 369 base::WaitableEvent event(false, false); 370 EXPECT_CALL(*capturer_source_.get(), Start()).WillOnce(SignalEvent(&event)); 371 RTCMediaConstraints constraints; 372 scoped_refptr<WebRtcLocalAudioTrack> track_1 = 373 WebRtcLocalAudioTrack::Create(std::string(), capturer_, NULL, NULL, 374 &constraints); 375 static_cast<webrtc::AudioTrackInterface*>(track_1.get())-> 376 GetRenderer()->AddChannel(0); 377 track_1->Start(); 378 EXPECT_TRUE(event.TimedWait(TestTimeouts::tiny_timeout())); 379 380 // Verify the data flow by connecting the sink to |track_1|. 381 scoped_ptr<MockWebRtcAudioCapturerSink> sink( 382 new MockWebRtcAudioCapturerSink()); 383 event.Reset(); 384 EXPECT_CALL(*sink, CaptureData(_, _, _, _, 0, 0, false, false)) 385 .Times(AnyNumber()).WillRepeatedly(Return()); 386 EXPECT_CALL(*sink, SetCaptureFormat(_)).Times(1); 387 track_1->AddSink(sink.get()); 388 389 // Start the second audio track will not start the |capturer_source_| 390 // since it has been started. 391 EXPECT_CALL(*capturer_source_.get(), Start()).Times(0); 392 scoped_refptr<WebRtcLocalAudioTrack> track_2 = 393 WebRtcLocalAudioTrack::Create(std::string(), capturer_, NULL, NULL, 394 &constraints); 395 track_2->Start(); 396 static_cast<webrtc::AudioTrackInterface*>(track_2.get())-> 397 GetRenderer()->AddChannel(1); 398 399 // Stop the first audio track will not stop the |capturer_source_|. 400 EXPECT_CALL(*capturer_source_.get(), Stop()).Times(0); 401 track_1->RemoveSink(sink.get()); 402 track_1->Stop(); 403 track_1 = NULL; 404 405 EXPECT_CALL(*sink, CaptureData(_, _, _, _, 0, 0, false, false)) 406 .Times(AnyNumber()).WillRepeatedly(Return()); 407 EXPECT_CALL(*sink, SetCaptureFormat(_)).Times(1); 408 track_2->AddSink(sink.get()); 409 410 // Stop the last audio track will stop the |capturer_source_|. 411 event.Reset(); 412 EXPECT_CALL(*capturer_source_.get(), Stop()) 413 .Times(1).WillOnce(SignalEvent(&event)); 414 track_2->Stop(); 415 track_2->RemoveSink(sink.get()); 416 track_2 = NULL; 417 EXPECT_TRUE(event.TimedWait(TestTimeouts::tiny_timeout())); 418} 419 420// Set new source to the existing capturer. 421TEST_F(WebRtcLocalAudioTrackTest, SetNewSourceForCapturerAfterStartTrack) { 422 // Setup the audio track and start the track. 423 EXPECT_CALL(*capturer_source_.get(), Start()).Times(1); 424 RTCMediaConstraints constraints; 425 scoped_refptr<WebRtcLocalAudioTrack> track = 426 WebRtcLocalAudioTrack::Create(std::string(), capturer_, NULL, NULL, 427 &constraints); 428 track->Start(); 429 430 // Setting new source to the capturer and the track should still get packets. 431 scoped_refptr<MockCapturerSource> new_source(new MockCapturerSource()); 432 EXPECT_CALL(*capturer_source_.get(), Stop()); 433 EXPECT_CALL(*new_source.get(), SetAutomaticGainControl(false)); 434 EXPECT_CALL(*new_source.get(), Initialize(_, capturer_.get(), 0)) 435 .WillOnce(Return()); 436 EXPECT_CALL(*new_source.get(), Start()).WillOnce(Return()); 437 capturer_->SetCapturerSource(new_source, 438 params_.channel_layout(), 439 params_.sample_rate()); 440 441 // Stop the track. 442 EXPECT_CALL(*new_source.get(), Stop()); 443 track->Stop(); 444 track = NULL; 445} 446 447// Create a new capturer with new source, connect it to a new audio track. 448TEST_F(WebRtcLocalAudioTrackTest, ConnectTracksToDifferentCapturers) { 449 // Setup the first audio track and start it. 450 EXPECT_CALL(*capturer_source_.get(), Start()).Times(1); 451 RTCMediaConstraints constraints; 452 scoped_refptr<WebRtcLocalAudioTrack> track_1 = 453 WebRtcLocalAudioTrack::Create(std::string(), capturer_, NULL, NULL, 454 &constraints); 455 track_1->Start(); 456 457 // Connect a number of network channels to the |track_1|. 458 static const int kNumberOfNetworkChannelsForTrack1 = 2; 459 for (int i = 0; i < kNumberOfNetworkChannelsForTrack1; ++i) { 460 static_cast<webrtc::AudioTrackInterface*>(track_1.get())-> 461 GetRenderer()->AddChannel(i); 462 } 463 // Verify the data flow by connecting the |sink_1| to |track_1|. 464 scoped_ptr<MockWebRtcAudioCapturerSink> sink_1( 465 new MockWebRtcAudioCapturerSink()); 466 EXPECT_CALL( 467 *sink_1.get(), 468 CaptureData( 469 kNumberOfNetworkChannelsForTrack1, 48000, 2, _, 0, 0, false, false)) 470 .Times(AnyNumber()).WillRepeatedly(Return()); 471 EXPECT_CALL(*sink_1.get(), SetCaptureFormat(_)).Times(1); 472 track_1->AddSink(sink_1.get()); 473 474 // Create a new capturer with new source with different audio format. 475 scoped_refptr<WebRtcAudioCapturer> new_capturer( 476 WebRtcAudioCapturer::CreateCapturer()); 477 WebRtcLocalAudioSourceProvider* source_provider = 478 static_cast<WebRtcLocalAudioSourceProvider*>( 479 new_capturer->audio_source_provider()); 480 source_provider->SetSinkParamsForTesting(params_); 481 scoped_refptr<MockCapturerSource> new_source(new MockCapturerSource()); 482 EXPECT_CALL(*new_source.get(), Initialize(_, new_capturer.get(), 0)) 483 .WillOnce(Return()); 484 EXPECT_CALL(*new_source.get(), SetAutomaticGainControl(false)) 485 .WillOnce(Return()); 486 new_capturer->SetCapturerSource(new_source, 487 media::CHANNEL_LAYOUT_MONO, 488 44100); 489 490 // Start the audio thread used by the new source. 491 scoped_ptr<FakeAudioThread> audio_thread(new FakeAudioThread(new_capturer)); 492 audio_thread->Start(); 493 494 // Setup the second audio track, connect it to the new capturer and start it. 495 EXPECT_CALL(*new_source.get(), Start()).Times(1); 496 scoped_refptr<WebRtcLocalAudioTrack> track_2 = 497 WebRtcLocalAudioTrack::Create(std::string(), new_capturer, NULL, NULL, 498 &constraints); 499 track_2->Start(); 500 501 // Connect a number of network channels to the |track_2|. 502 static const int kNumberOfNetworkChannelsForTrack2 = 3; 503 for (int i = 0; i < kNumberOfNetworkChannelsForTrack2; ++i) { 504 static_cast<webrtc::AudioTrackInterface*>(track_2.get())-> 505 GetRenderer()->AddChannel(i); 506 } 507 // Verify the data flow by connecting the |sink_2| to |track_2|. 508 scoped_ptr<MockWebRtcAudioCapturerSink> sink_2( 509 new MockWebRtcAudioCapturerSink()); 510 EXPECT_CALL( 511 *sink_2, 512 CaptureData( 513 kNumberOfNetworkChannelsForTrack2, 44100, 1, _, 0, 0, false, false)) 514 .Times(AnyNumber()).WillRepeatedly(Return()); 515 EXPECT_CALL(*sink_2, SetCaptureFormat(_)).Times(1); 516 track_2->AddSink(sink_2.get()); 517 518 // Stop the second audio track will stop the new source. 519 base::WaitableEvent event(false, false); 520 EXPECT_CALL(*new_source.get(), Stop()).Times(1).WillOnce(SignalEvent(&event)); 521 track_2->Stop(); 522 track_2->RemoveSink(sink_2.get()); 523 track_2 = NULL; 524 EXPECT_TRUE(event.TimedWait(TestTimeouts::tiny_timeout())); 525 audio_thread->Stop(); 526 audio_thread.reset(); 527 528 // Stop the first audio track. 529 EXPECT_CALL(*capturer_source_.get(), Stop()); 530 track_1->Stop(); 531 track_1 = NULL; 532} 533 534} // namespace content 535