webrtc_local_audio_track_unittest.cc revision 68043e1e95eeb07d5cae7aca370b26518b0867d6
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. 270// Flaky due to a data race, see http://crbug.com/295418 271TEST_F(WebRtcLocalAudioTrackTest, DISABLED_MultipleAudioTracks) { 272 EXPECT_CALL(*capturer_source_.get(), Start()).WillOnce(Return()); 273 RTCMediaConstraints constraints; 274 scoped_refptr<WebRtcLocalAudioTrack> track_1 = 275 WebRtcLocalAudioTrack::Create(std::string(), capturer_, NULL, NULL, 276 &constraints); 277 track_1->Start(); 278 static_cast<webrtc::AudioTrackInterface*>(track_1.get())-> 279 GetRenderer()->AddChannel(0); 280 EXPECT_TRUE(track_1->enabled()); 281 scoped_ptr<MockWebRtcAudioCapturerSink> sink_1( 282 new MockWebRtcAudioCapturerSink()); 283 const media::AudioParameters params = capturer_->audio_parameters(); 284 base::WaitableEvent event_1(false, false); 285 EXPECT_CALL(*sink_1, SetCaptureFormat(_)).WillOnce(Return()); 286 EXPECT_CALL(*sink_1, 287 CaptureData(1, 288 params.sample_rate(), 289 params.channels(), 290 params.sample_rate() / 100, 291 0, 292 0, 293 false, 294 false)).Times(AtLeast(1)) 295 .WillRepeatedly(SignalEvent(&event_1)); 296 track_1->AddSink(sink_1.get()); 297 EXPECT_TRUE(event_1.TimedWait(TestTimeouts::tiny_timeout())); 298 299 scoped_refptr<WebRtcLocalAudioTrack> track_2 = 300 WebRtcLocalAudioTrack::Create(std::string(), capturer_, NULL, NULL, 301 &constraints); 302 track_2->Start(); 303 static_cast<webrtc::AudioTrackInterface*>(track_2.get())-> 304 GetRenderer()->AddChannel(1); 305 EXPECT_TRUE(track_2->enabled()); 306 307 // Verify both |sink_1| and |sink_2| get data. 308 event_1.Reset(); 309 base::WaitableEvent event_2(false, false); 310 311 scoped_ptr<MockWebRtcAudioCapturerSink> sink_2( 312 new MockWebRtcAudioCapturerSink()); 313 EXPECT_CALL(*sink_2, SetCaptureFormat(_)).WillOnce(Return()); 314 EXPECT_CALL(*sink_1, 315 CaptureData(1, 316 params.sample_rate(), 317 params.channels(), 318 params.sample_rate() / 100, 319 0, 320 0, 321 false, 322 false)).Times(AtLeast(1)) 323 .WillRepeatedly(SignalEvent(&event_1)); 324 EXPECT_CALL(*sink_2, 325 CaptureData(1, 326 params.sample_rate(), 327 params.channels(), 328 params.sample_rate() / 100, 329 0, 330 0, 331 false, 332 false)).Times(AtLeast(1)) 333 .WillRepeatedly(SignalEvent(&event_2)); 334 track_2->AddSink(sink_2.get()); 335 EXPECT_TRUE(event_1.TimedWait(TestTimeouts::tiny_timeout())); 336 EXPECT_TRUE(event_2.TimedWait(TestTimeouts::tiny_timeout())); 337 338 track_1->RemoveSink(sink_1.get()); 339 track_1->Stop(); 340 track_1 = NULL; 341 342 EXPECT_CALL(*capturer_source_.get(), Stop()).WillOnce(Return()); 343 track_2->RemoveSink(sink_2.get()); 344 track_2->Stop(); 345 track_2 = NULL; 346} 347 348 349// Start one track and verify the capturer is correctly starting its source. 350// And it should be fine to not to call Stop() explicitly. 351TEST_F(WebRtcLocalAudioTrackTest, StartOneAudioTrack) { 352 EXPECT_CALL(*capturer_source_.get(), Start()).Times(1); 353 RTCMediaConstraints constraints; 354 scoped_refptr<WebRtcLocalAudioTrack> track = 355 WebRtcLocalAudioTrack::Create(std::string(), capturer_, NULL, NULL, 356 &constraints); 357 track->Start(); 358 359 // When the track goes away, it will automatically stop the 360 // |capturer_source_|. 361 EXPECT_CALL(*capturer_source_.get(), Stop()); 362 track->Stop(); 363 track = NULL; 364} 365 366// Start/Stop tracks and verify the capturer is correctly starting/stopping 367// its source. 368TEST_F(WebRtcLocalAudioTrackTest, StartAndStopAudioTracks) { 369 // Starting the first audio track will start the |capturer_source_|. 370 base::WaitableEvent event(false, false); 371 EXPECT_CALL(*capturer_source_.get(), Start()).WillOnce(SignalEvent(&event)); 372 RTCMediaConstraints constraints; 373 scoped_refptr<WebRtcLocalAudioTrack> track_1 = 374 WebRtcLocalAudioTrack::Create(std::string(), capturer_, NULL, NULL, 375 &constraints); 376 static_cast<webrtc::AudioTrackInterface*>(track_1.get())-> 377 GetRenderer()->AddChannel(0); 378 track_1->Start(); 379 EXPECT_TRUE(event.TimedWait(TestTimeouts::tiny_timeout())); 380 381 // Verify the data flow by connecting the sink to |track_1|. 382 scoped_ptr<MockWebRtcAudioCapturerSink> sink( 383 new MockWebRtcAudioCapturerSink()); 384 event.Reset(); 385 EXPECT_CALL(*sink, CaptureData(_, _, _, _, 0, 0, false, false)) 386 .Times(AnyNumber()).WillRepeatedly(Return()); 387 EXPECT_CALL(*sink, SetCaptureFormat(_)).Times(1); 388 track_1->AddSink(sink.get()); 389 390 // Start the second audio track will not start the |capturer_source_| 391 // since it has been started. 392 EXPECT_CALL(*capturer_source_.get(), Start()).Times(0); 393 scoped_refptr<WebRtcLocalAudioTrack> track_2 = 394 WebRtcLocalAudioTrack::Create(std::string(), capturer_, NULL, NULL, 395 &constraints); 396 track_2->Start(); 397 static_cast<webrtc::AudioTrackInterface*>(track_2.get())-> 398 GetRenderer()->AddChannel(1); 399 400 // Stop the first audio track will not stop the |capturer_source_|. 401 EXPECT_CALL(*capturer_source_.get(), Stop()).Times(0); 402 track_1->RemoveSink(sink.get()); 403 track_1->Stop(); 404 track_1 = NULL; 405 406 EXPECT_CALL(*sink, CaptureData(_, _, _, _, 0, 0, false, false)) 407 .Times(AnyNumber()).WillRepeatedly(Return()); 408 EXPECT_CALL(*sink, SetCaptureFormat(_)).Times(1); 409 track_2->AddSink(sink.get()); 410 411 // Stop the last audio track will stop the |capturer_source_|. 412 event.Reset(); 413 EXPECT_CALL(*capturer_source_.get(), Stop()) 414 .Times(1).WillOnce(SignalEvent(&event)); 415 track_2->Stop(); 416 track_2->RemoveSink(sink.get()); 417 track_2 = NULL; 418 EXPECT_TRUE(event.TimedWait(TestTimeouts::tiny_timeout())); 419} 420 421// Set new source to the existing capturer. 422TEST_F(WebRtcLocalAudioTrackTest, SetNewSourceForCapturerAfterStartTrack) { 423 // Setup the audio track and start the track. 424 EXPECT_CALL(*capturer_source_.get(), Start()).Times(1); 425 RTCMediaConstraints constraints; 426 scoped_refptr<WebRtcLocalAudioTrack> track = 427 WebRtcLocalAudioTrack::Create(std::string(), capturer_, NULL, NULL, 428 &constraints); 429 track->Start(); 430 431 // Setting new source to the capturer and the track should still get packets. 432 scoped_refptr<MockCapturerSource> new_source(new MockCapturerSource()); 433 EXPECT_CALL(*capturer_source_.get(), Stop()); 434 EXPECT_CALL(*new_source.get(), SetAutomaticGainControl(false)); 435 EXPECT_CALL(*new_source.get(), Initialize(_, capturer_.get(), 0)) 436 .WillOnce(Return()); 437 EXPECT_CALL(*new_source.get(), Start()).WillOnce(Return()); 438 capturer_->SetCapturerSource(new_source, 439 params_.channel_layout(), 440 params_.sample_rate()); 441 442 // Stop the track. 443 EXPECT_CALL(*new_source.get(), Stop()); 444 track->Stop(); 445 track = NULL; 446} 447 448// Create a new capturer with new source, connect it to a new audio track. 449TEST_F(WebRtcLocalAudioTrackTest, ConnectTracksToDifferentCapturers) { 450 // Setup the first audio track and start it. 451 EXPECT_CALL(*capturer_source_.get(), Start()).Times(1); 452 RTCMediaConstraints constraints; 453 scoped_refptr<WebRtcLocalAudioTrack> track_1 = 454 WebRtcLocalAudioTrack::Create(std::string(), capturer_, NULL, NULL, 455 &constraints); 456 track_1->Start(); 457 458 // Connect a number of network channels to the |track_1|. 459 static const int kNumberOfNetworkChannelsForTrack1 = 2; 460 for (int i = 0; i < kNumberOfNetworkChannelsForTrack1; ++i) { 461 static_cast<webrtc::AudioTrackInterface*>(track_1.get())-> 462 GetRenderer()->AddChannel(i); 463 } 464 // Verify the data flow by connecting the |sink_1| to |track_1|. 465 scoped_ptr<MockWebRtcAudioCapturerSink> sink_1( 466 new MockWebRtcAudioCapturerSink()); 467 EXPECT_CALL( 468 *sink_1.get(), 469 CaptureData( 470 kNumberOfNetworkChannelsForTrack1, 48000, 2, _, 0, 0, false, false)) 471 .Times(AnyNumber()).WillRepeatedly(Return()); 472 EXPECT_CALL(*sink_1.get(), SetCaptureFormat(_)).Times(1); 473 track_1->AddSink(sink_1.get()); 474 475 // Create a new capturer with new source with different audio format. 476 scoped_refptr<WebRtcAudioCapturer> new_capturer( 477 WebRtcAudioCapturer::CreateCapturer()); 478 WebRtcLocalAudioSourceProvider* source_provider = 479 static_cast<WebRtcLocalAudioSourceProvider*>( 480 new_capturer->audio_source_provider()); 481 source_provider->SetSinkParamsForTesting(params_); 482 scoped_refptr<MockCapturerSource> new_source(new MockCapturerSource()); 483 EXPECT_CALL(*new_source.get(), Initialize(_, new_capturer.get(), 0)) 484 .WillOnce(Return()); 485 EXPECT_CALL(*new_source.get(), SetAutomaticGainControl(false)) 486 .WillOnce(Return()); 487 new_capturer->SetCapturerSource(new_source, 488 media::CHANNEL_LAYOUT_MONO, 489 44100); 490 491 // Start the audio thread used by the new source. 492 scoped_ptr<FakeAudioThread> audio_thread(new FakeAudioThread(new_capturer)); 493 audio_thread->Start(); 494 495 // Setup the second audio track, connect it to the new capturer and start it. 496 EXPECT_CALL(*new_source.get(), Start()).Times(1); 497 scoped_refptr<WebRtcLocalAudioTrack> track_2 = 498 WebRtcLocalAudioTrack::Create(std::string(), new_capturer, NULL, NULL, 499 &constraints); 500 track_2->Start(); 501 502 // Connect a number of network channels to the |track_2|. 503 static const int kNumberOfNetworkChannelsForTrack2 = 3; 504 for (int i = 0; i < kNumberOfNetworkChannelsForTrack2; ++i) { 505 static_cast<webrtc::AudioTrackInterface*>(track_2.get())-> 506 GetRenderer()->AddChannel(i); 507 } 508 // Verify the data flow by connecting the |sink_2| to |track_2|. 509 scoped_ptr<MockWebRtcAudioCapturerSink> sink_2( 510 new MockWebRtcAudioCapturerSink()); 511 EXPECT_CALL( 512 *sink_2, 513 CaptureData( 514 kNumberOfNetworkChannelsForTrack2, 44100, 1, _, 0, 0, false, false)) 515 .Times(AnyNumber()).WillRepeatedly(Return()); 516 EXPECT_CALL(*sink_2, SetCaptureFormat(_)).Times(1); 517 track_2->AddSink(sink_2.get()); 518 519 // Stop the second audio track will stop the new source. 520 base::WaitableEvent event(false, false); 521 EXPECT_CALL(*new_source.get(), Stop()).Times(1).WillOnce(SignalEvent(&event)); 522 track_2->Stop(); 523 track_2->RemoveSink(sink_2.get()); 524 track_2 = NULL; 525 EXPECT_TRUE(event.TimedWait(TestTimeouts::tiny_timeout())); 526 audio_thread->Stop(); 527 audio_thread.reset(); 528 529 // Stop the first audio track. 530 EXPECT_CALL(*capturer_source_.get(), Stop()); 531 track_1->Stop(); 532 track_1 = NULL; 533} 534 535} // namespace content 536