1c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// Copyright 2013 The Chromium Authors. All rights reserved. 2c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be 3c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// found in the LICENSE file. 4c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) 5c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "base/synchronization/waitable_event.h" 6c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "base/test/test_timeouts.h" 70529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch#include "content/renderer/media/media_stream_audio_source.h" 8a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "content/renderer/media/mock_media_constraint_factory.h" 95d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "content/renderer/media/webrtc/webrtc_local_audio_track_adapter.h" 10c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "content/renderer/media/webrtc_audio_capturer.h" 11a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)#include "content/renderer/media/webrtc_audio_device_impl.h" 12c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "content/renderer/media/webrtc_local_audio_track.h" 13c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "media/audio/audio_parameters.h" 14c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "media/base/audio_bus.h" 15c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "media/base/audio_capturer_source.h" 16c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "testing/gmock/include/gmock/gmock.h" 17c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "testing/gtest/include/gtest/gtest.h" 185d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "third_party/WebKit/public/platform/WebMediaConstraints.h" 191320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "third_party/WebKit/public/web/WebHeap.h" 20bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch#include "third_party/libjingle/source/talk/app/webrtc/mediastreaminterface.h" 21c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) 22c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)using ::testing::_; 237d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)using ::testing::AnyNumber; 24c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)using ::testing::AtLeast; 25c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)using ::testing::Return; 26c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) 27c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)namespace content { 28c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) 29c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)namespace { 30c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) 31c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)ACTION_P(SignalEvent, event) { 32c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) event->Signal(); 33c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)} 34c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) 35c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// A simple thread that we use to fake the audio thread which provides data to 36c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// the |WebRtcAudioCapturer|. 37c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)class FakeAudioThread : public base::PlatformThread::Delegate { 38c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) public: 390529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch FakeAudioThread(WebRtcAudioCapturer* capturer, 40f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) const media::AudioParameters& params) 41c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) : capturer_(capturer), 4290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) thread_(), 43c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) closure_(false, false) { 440529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch DCHECK(capturer); 45f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) audio_bus_ = media::AudioBus::Create(params); 46c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) } 47c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) 4890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) virtual ~FakeAudioThread() { DCHECK(thread_.is_null()); } 49c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) 50c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) // base::PlatformThread::Delegate: 51c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) virtual void ThreadMain() OVERRIDE { 52c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) while (true) { 53c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) if (closure_.IsSignaled()) 54c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) return; 55c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) 56c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) media::AudioCapturerSource::CaptureCallback* callback = 57868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) static_cast<media::AudioCapturerSource::CaptureCallback*>( 580529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch capturer_); 59c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) audio_bus_->Zero(); 603551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) callback->Capture(audio_bus_.get(), 0, 0, false); 61c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) 62c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) // Sleep 1ms to yield the resource for the main thread. 63c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(1)); 64c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) } 65c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) } 66c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) 67c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) void Start() { 68c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) base::PlatformThread::CreateWithPriority( 69c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) 0, this, &thread_, base::kThreadPriority_RealtimeAudio); 7090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) CHECK(!thread_.is_null()); 71c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) } 72c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) 73c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) void Stop() { 74c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) closure_.Signal(); 75c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) base::PlatformThread::Join(thread_); 7690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) thread_ = base::PlatformThreadHandle(); 77c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) } 78c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) 79c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) private: 80c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) scoped_ptr<media::AudioBus> audio_bus_; 810529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch WebRtcAudioCapturer* capturer_; 82c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) base::PlatformThreadHandle thread_; 83c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) base::WaitableEvent closure_; 84c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) DISALLOW_COPY_AND_ASSIGN(FakeAudioThread); 85c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}; 86c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) 87c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)class MockCapturerSource : public media::AudioCapturerSource { 88c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) public: 89f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) explicit MockCapturerSource(WebRtcAudioCapturer* capturer) 90f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) : capturer_(capturer) {} 91f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) MOCK_METHOD3(OnInitialize, void(const media::AudioParameters& params, 92f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) CaptureCallback* callback, 93f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) int session_id)); 94f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) MOCK_METHOD0(OnStart, void()); 95f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) MOCK_METHOD0(OnStop, void()); 96c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) MOCK_METHOD1(SetVolume, void(double volume)); 97c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) MOCK_METHOD1(SetAutomaticGainControl, void(bool enable)); 98c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) 99f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) virtual void Initialize(const media::AudioParameters& params, 100f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) CaptureCallback* callback, 101f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) int session_id) OVERRIDE { 102f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) DCHECK(params.IsValid()); 103f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) params_ = params; 104f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) OnInitialize(params, callback, session_id); 105f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) } 106f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) virtual void Start() OVERRIDE { 107f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) audio_thread_.reset(new FakeAudioThread(capturer_, params_)); 108f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) audio_thread_->Start(); 109f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) OnStart(); 110f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) } 111f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) virtual void Stop() OVERRIDE { 112f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) audio_thread_->Stop(); 113f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) audio_thread_.reset(); 114f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) OnStop(); 115f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) } 116c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) protected: 117c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) virtual ~MockCapturerSource() {} 118f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 119f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) private: 120f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) scoped_ptr<FakeAudioThread> audio_thread_; 121f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) WebRtcAudioCapturer* capturer_; 122f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) media::AudioParameters params_; 123c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}; 124c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) 125a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)// TODO(xians): Use MediaStreamAudioSink. 126a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)class MockMediaStreamAudioSink : public PeerConnectionAudioSink { 127c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) public: 128a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) MockMediaStreamAudioSink() {} 129a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) ~MockMediaStreamAudioSink() {} 130a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) int OnData(const int16* audio_data, 131a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) int sample_rate, 132a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) int number_of_channels, 133a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) int number_of_frames, 134a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) const std::vector<int>& channels, 135a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) int audio_delay_milliseconds, 136a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) int current_volume, 137a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) bool need_audio_processing, 138a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) bool key_pressed) OVERRIDE { 139a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) EXPECT_EQ(params_.sample_rate(), sample_rate); 140a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) EXPECT_EQ(params_.channels(), number_of_channels); 141a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) EXPECT_EQ(params_.frames_per_buffer(), number_of_frames); 1423551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) CaptureData(channels.size(), 1433551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) audio_delay_milliseconds, 1443551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) current_volume, 1453551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) need_audio_processing, 1463551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) key_pressed); 147bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch return 0; 148bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch } 149a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) MOCK_METHOD5(CaptureData, 1503551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) void(int number_of_network_channels, 1513551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) int audio_delay_milliseconds, 1523551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) int current_volume, 1533551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) bool need_audio_processing, 1543551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) bool key_pressed)); 155a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) void OnSetFormat(const media::AudioParameters& params) { 156a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) params_ = params; 157a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) FormatIsSet(); 158a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) } 159a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) MOCK_METHOD0(FormatIsSet, void()); 160a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 161a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) const media::AudioParameters& audio_params() const { return params_; } 162a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 163a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) private: 164a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) media::AudioParameters params_; 165c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}; 166c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) 167c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)} // namespace 168c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) 169c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)class WebRtcLocalAudioTrackTest : public ::testing::Test { 170c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) protected: 171c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) virtual void SetUp() OVERRIDE { 172d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) params_.Reset(media::AudioParameters::AUDIO_PCM_LOW_LATENCY, 1731320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci media::CHANNEL_LAYOUT_STEREO, 2, 48000, 16, 480); 174cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) MockMediaConstraintFactory constraint_factory; 1750529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch blink_source_.initialize("dummy", blink::WebMediaStreamSource::TypeAudio, 1760529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch "dummy"); 1770529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch MediaStreamAudioSource* audio_source = new MediaStreamAudioSource(); 1780529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch blink_source_.setExtraData(audio_source); 1790529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch 180a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) StreamDeviceInfo device(MEDIA_DEVICE_AUDIO_CAPTURE, 181a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) std::string(), std::string()); 182cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) capturer_ = WebRtcAudioCapturer::CreateCapturer( 183cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) -1, device, constraint_factory.CreateWebMediaConstraints(), NULL, 184cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) audio_source); 1851320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci audio_source->SetAudioCapturer(capturer_.get()); 186f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) capturer_source_ = new MockCapturerSource(capturer_.get()); 1875d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) EXPECT_CALL(*capturer_source_.get(), OnInitialize(_, capturer_.get(), -1)) 188c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) .WillOnce(Return()); 1890529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch EXPECT_CALL(*capturer_source_.get(), SetAutomaticGainControl(true)); 1900529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch EXPECT_CALL(*capturer_source_.get(), OnStart()); 1915d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) capturer_->SetCapturerSourceForTesting(capturer_source_, params_); 192c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) } 193c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) 1941320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci virtual void TearDown() OVERRIDE { 1951320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci blink_source_.reset(); 1961320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci blink::WebHeap::collectAllGarbageForTesting(); 1971320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci } 1981320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 199d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) media::AudioParameters params_; 2000529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch blink::WebMediaStreamSource blink_source_; 201c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) scoped_refptr<MockCapturerSource> capturer_source_; 202c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) scoped_refptr<WebRtcAudioCapturer> capturer_; 203c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}; 204c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) 205c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// Creates a capturer and audio track, fakes its audio thread, and 206c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// connect/disconnect the sink to the audio track on the fly, the sink should 207c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// get data callback when the track is connected to the capturer but not when 208c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// the track is disconnected from the capturer. 209c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)TEST_F(WebRtcLocalAudioTrackTest, ConnectAndDisconnectOneSink) { 2105d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) scoped_refptr<WebRtcLocalAudioTrackAdapter> adapter( 2115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) WebRtcLocalAudioTrackAdapter::Create(std::string(), NULL)); 2125d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) scoped_ptr<WebRtcLocalAudioTrack> track( 2131320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci new WebRtcLocalAudioTrack(adapter.get(), capturer_, NULL)); 2147d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles) track->Start(); 215a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) EXPECT_TRUE(track->GetAudioAdapter()->enabled()); 216bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch 217a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) scoped_ptr<MockMediaStreamAudioSink> sink(new MockMediaStreamAudioSink()); 218c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) base::WaitableEvent event(false, false); 219a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) EXPECT_CALL(*sink, FormatIsSet()); 2203551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) EXPECT_CALL(*sink, 221f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) CaptureData(0, 22258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles) 0, 22358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles) 0, 224a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) _, 22558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles) false)).Times(AtLeast(1)) 2263551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) .WillRepeatedly(SignalEvent(&event)); 227c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) track->AddSink(sink.get()); 228c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) EXPECT_TRUE(event.TimedWait(TestTimeouts::tiny_timeout())); 229c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) track->RemoveSink(sink.get()); 2307d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles) 231f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) EXPECT_CALL(*capturer_source_.get(), OnStop()).WillOnce(Return()); 232f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) capturer_->Stop(); 233c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)} 234c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) 235c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// The same setup as ConnectAndDisconnectOneSink, but enable and disable the 236c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// audio track on the fly. When the audio track is disabled, there is no data 237c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// callback to the sink; when the audio track is enabled, there comes data 238c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// callback. 239c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// TODO(xians): Enable this test after resolving the racing issue that TSAN 240c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// reports on MediaStreamTrack::enabled(); 241f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)TEST_F(WebRtcLocalAudioTrackTest, DISABLED_DisableEnableAudioTrack) { 242f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) EXPECT_CALL(*capturer_source_.get(), SetAutomaticGainControl(true)); 243f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) EXPECT_CALL(*capturer_source_.get(), OnStart()); 2445d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) scoped_refptr<WebRtcLocalAudioTrackAdapter> adapter( 2455d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) WebRtcLocalAudioTrackAdapter::Create(std::string(), NULL)); 2465d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) scoped_ptr<WebRtcLocalAudioTrack> track( 2471320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci new WebRtcLocalAudioTrack(adapter.get(), capturer_, NULL)); 2487d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles) track->Start(); 249a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) EXPECT_TRUE(track->GetAudioAdapter()->enabled()); 250a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) EXPECT_TRUE(track->GetAudioAdapter()->set_enabled(false)); 251a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) scoped_ptr<MockMediaStreamAudioSink> sink(new MockMediaStreamAudioSink()); 2525d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) const media::AudioParameters params = capturer_->source_audio_parameters(); 253c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) base::WaitableEvent event(false, false); 254a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) EXPECT_CALL(*sink, FormatIsSet()).Times(1); 2553551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) EXPECT_CALL(*sink, 256f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) CaptureData(0, 0, 0, _, false)).Times(0); 257a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) EXPECT_EQ(sink->audio_params().frames_per_buffer(), 258a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) params.sample_rate() / 100); 259c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) track->AddSink(sink.get()); 260c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) EXPECT_FALSE(event.TimedWait(TestTimeouts::tiny_timeout())); 261c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) 262c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) event.Reset(); 263f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) EXPECT_CALL(*sink, CaptureData(0, 0, 0, _, false)).Times(AtLeast(1)) 2643551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) .WillRepeatedly(SignalEvent(&event)); 265a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) EXPECT_TRUE(track->GetAudioAdapter()->set_enabled(true)); 266c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) EXPECT_TRUE(event.TimedWait(TestTimeouts::tiny_timeout())); 267c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) track->RemoveSink(sink.get()); 2687d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles) 269f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) EXPECT_CALL(*capturer_source_.get(), OnStop()).WillOnce(Return()); 270f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) capturer_->Stop(); 2715d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) track.reset(); 272c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)} 273c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) 274c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// Create multiple audio tracks and enable/disable them, verify that the audio 275c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// callbacks appear/disappear. 27668043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)// Flaky due to a data race, see http://crbug.com/295418 27768043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)TEST_F(WebRtcLocalAudioTrackTest, DISABLED_MultipleAudioTracks) { 2785d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) scoped_refptr<WebRtcLocalAudioTrackAdapter> adapter_1( 2795d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) WebRtcLocalAudioTrackAdapter::Create(std::string(), NULL)); 2805d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) scoped_ptr<WebRtcLocalAudioTrack> track_1( 2811320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci new WebRtcLocalAudioTrack(adapter_1.get(), capturer_, NULL)); 2827d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles) track_1->Start(); 283a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) EXPECT_TRUE(track_1->GetAudioAdapter()->enabled()); 284a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) scoped_ptr<MockMediaStreamAudioSink> sink_1(new MockMediaStreamAudioSink()); 2855d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) const media::AudioParameters params = capturer_->source_audio_parameters(); 286c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) base::WaitableEvent event_1(false, false); 287a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) EXPECT_CALL(*sink_1, FormatIsSet()).WillOnce(Return()); 2883551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) EXPECT_CALL(*sink_1, 289f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) CaptureData(0, 0, 0, _, false)).Times(AtLeast(1)) 2903551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) .WillRepeatedly(SignalEvent(&event_1)); 291a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) EXPECT_EQ(sink_1->audio_params().frames_per_buffer(), 292a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) params.sample_rate() / 100); 293c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) track_1->AddSink(sink_1.get()); 2947d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles) EXPECT_TRUE(event_1.TimedWait(TestTimeouts::tiny_timeout())); 295c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) 2965d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) scoped_refptr<WebRtcLocalAudioTrackAdapter> adapter_2( 2975d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) WebRtcLocalAudioTrackAdapter::Create(std::string(), NULL)); 2985d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) scoped_ptr<WebRtcLocalAudioTrack> track_2( 2991320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci new WebRtcLocalAudioTrack(adapter_2.get(), capturer_, NULL)); 3007d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles) track_2->Start(); 301a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) EXPECT_TRUE(track_2->GetAudioAdapter()->enabled()); 302c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) 3037d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles) // Verify both |sink_1| and |sink_2| get data. 304c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) event_1.Reset(); 305c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) base::WaitableEvent event_2(false, false); 3067d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles) 307a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) scoped_ptr<MockMediaStreamAudioSink> sink_2(new MockMediaStreamAudioSink()); 308a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) EXPECT_CALL(*sink_2, FormatIsSet()).WillOnce(Return()); 309f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) EXPECT_CALL(*sink_1, CaptureData(0, 0, 0, _, false)).Times(AtLeast(1)) 3103551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) .WillRepeatedly(SignalEvent(&event_1)); 311a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) EXPECT_EQ(sink_1->audio_params().frames_per_buffer(), 312a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) params.sample_rate() / 100); 313f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) EXPECT_CALL(*sink_2, CaptureData(0, 0, 0, _, false)).Times(AtLeast(1)) 3143551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) .WillRepeatedly(SignalEvent(&event_2)); 315a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) EXPECT_EQ(sink_2->audio_params().frames_per_buffer(), 316a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) params.sample_rate() / 100); 3177d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles) track_2->AddSink(sink_2.get()); 318c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) EXPECT_TRUE(event_1.TimedWait(TestTimeouts::tiny_timeout())); 319c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) EXPECT_TRUE(event_2.TimedWait(TestTimeouts::tiny_timeout())); 320c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) 321c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) track_1->RemoveSink(sink_1.get()); 3227d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles) track_1->Stop(); 3235d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) track_1.reset(); 3247d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles) 325f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) EXPECT_CALL(*capturer_source_.get(), OnStop()).WillOnce(Return()); 326c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) track_2->RemoveSink(sink_2.get()); 3277d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles) track_2->Stop(); 3285d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) track_2.reset(); 3297d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)} 3307d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles) 3317d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles) 3327d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)// Start one track and verify the capturer is correctly starting its source. 3337d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)// And it should be fine to not to call Stop() explicitly. 3347d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)TEST_F(WebRtcLocalAudioTrackTest, StartOneAudioTrack) { 3355d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) scoped_refptr<WebRtcLocalAudioTrackAdapter> adapter( 3365d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) WebRtcLocalAudioTrackAdapter::Create(std::string(), NULL)); 3375d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) scoped_ptr<WebRtcLocalAudioTrack> track( 3381320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci new WebRtcLocalAudioTrack(adapter.get(), capturer_, NULL)); 3397d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles) track->Start(); 3407d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles) 3417d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles) // When the track goes away, it will automatically stop the 3427d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles) // |capturer_source_|. 343f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) EXPECT_CALL(*capturer_source_.get(), OnStop()); 3445d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) track.reset(); 3457d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)} 3467d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles) 3470529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch// Start two tracks and verify the capturer is correctly starting its source. 3480529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch// When the last track connected to the capturer is stopped, the source is 3490529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch// stopped. 3500529e5d033099cbfc42635f6f6183833b09dff6eBen MurdochTEST_F(WebRtcLocalAudioTrackTest, StartTwoAudioTracks) { 3510529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch scoped_refptr<WebRtcLocalAudioTrackAdapter> adapter1( 3520529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch WebRtcLocalAudioTrackAdapter::Create(std::string(), NULL)); 3530529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch scoped_ptr<WebRtcLocalAudioTrack> track1( 3541320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci new WebRtcLocalAudioTrack(adapter1.get(), capturer_, NULL)); 3550529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch track1->Start(); 3560529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch 3570529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch scoped_refptr<WebRtcLocalAudioTrackAdapter> adapter2( 3580529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch WebRtcLocalAudioTrackAdapter::Create(std::string(), NULL)); 3590529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch scoped_ptr<WebRtcLocalAudioTrack> track2( 3601320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci new WebRtcLocalAudioTrack(adapter2.get(), capturer_, NULL)); 3610529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch track2->Start(); 3620529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch 3630529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch track1->Stop(); 3640529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch // When the last track is stopped, it will automatically stop the 3650529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch // |capturer_source_|. 3660529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch EXPECT_CALL(*capturer_source_.get(), OnStop()); 3670529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch track2->Stop(); 3680529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch} 3690529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch 3707d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)// Start/Stop tracks and verify the capturer is correctly starting/stopping 3717d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)// its source. 3727d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)TEST_F(WebRtcLocalAudioTrackTest, StartAndStopAudioTracks) { 373bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch base::WaitableEvent event(false, false); 3745d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) scoped_refptr<WebRtcLocalAudioTrackAdapter> adapter_1( 3755d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) WebRtcLocalAudioTrackAdapter::Create(std::string(), NULL)); 3765d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) scoped_ptr<WebRtcLocalAudioTrack> track_1( 3771320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci new WebRtcLocalAudioTrack(adapter_1.get(), capturer_, NULL)); 3787d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles) track_1->Start(); 3797d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles) 380bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch // Verify the data flow by connecting the sink to |track_1|. 381a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) scoped_ptr<MockMediaStreamAudioSink> sink(new MockMediaStreamAudioSink()); 382bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch event.Reset(); 383a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) EXPECT_CALL(*sink, FormatIsSet()).WillOnce(SignalEvent(&event)); 384a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) EXPECT_CALL(*sink, CaptureData(_, 0, 0, _, false)) 385bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch .Times(AnyNumber()).WillRepeatedly(Return()); 386bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch track_1->AddSink(sink.get()); 387f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) EXPECT_TRUE(event.TimedWait(TestTimeouts::tiny_timeout())); 388bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch 3897d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles) // Start the second audio track will not start the |capturer_source_| 3907d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles) // since it has been started. 391f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) EXPECT_CALL(*capturer_source_.get(), OnStart()).Times(0); 3925d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) scoped_refptr<WebRtcLocalAudioTrackAdapter> adapter_2( 3935d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) WebRtcLocalAudioTrackAdapter::Create(std::string(), NULL)); 3945d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) scoped_ptr<WebRtcLocalAudioTrack> track_2( 3951320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci new WebRtcLocalAudioTrack(adapter_2.get(), capturer_, NULL)); 3967d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles) track_2->Start(); 3977d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles) 398f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) // Stop the capturer will clear up the track lists in the capturer. 399f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) EXPECT_CALL(*capturer_source_.get(), OnStop()); 400f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) capturer_->Stop(); 4017d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles) 402f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) // Adding a new track to the capturer. 403bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch track_2->AddSink(sink.get()); 404a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) EXPECT_CALL(*sink, FormatIsSet()).Times(0); 405bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch 406f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) // Stop the capturer again will not trigger stopping the source of the 407f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) // capturer again.. 4087d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles) event.Reset(); 409f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) EXPECT_CALL(*capturer_source_.get(), OnStop()).Times(0); 410f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) capturer_->Stop(); 4117d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)} 4127d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles) 4136e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)// Contains data races reported by tsan: crbug.com/404133 4146e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)#if defined(THREAD_SANITIZER) 4156e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) #define DISABLE_ON_TSAN(function) DISABLED_##function 4166e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)#else 4176e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) #define DISABLE_ON_TSAN(function) function 4186e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)#endif 4196e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) 420bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch// Create a new capturer with new source, connect it to a new audio track. 4216e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)TEST_F(WebRtcLocalAudioTrackTest, 4226e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) DISABLE_ON_TSAN(ConnectTracksToDifferentCapturers)) { 423bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch // Setup the first audio track and start it. 4245d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) scoped_refptr<WebRtcLocalAudioTrackAdapter> adapter_1( 4255d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) WebRtcLocalAudioTrackAdapter::Create(std::string(), NULL)); 4265d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) scoped_ptr<WebRtcLocalAudioTrack> track_1( 4271320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci new WebRtcLocalAudioTrack(adapter_1.get(), capturer_, NULL)); 428bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch track_1->Start(); 429bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch 430bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch // Verify the data flow by connecting the |sink_1| to |track_1|. 431a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) scoped_ptr<MockMediaStreamAudioSink> sink_1(new MockMediaStreamAudioSink()); 432f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) EXPECT_CALL(*sink_1.get(), CaptureData(0, 0, 0, _, false)) 433bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch .Times(AnyNumber()).WillRepeatedly(Return()); 434a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) EXPECT_CALL(*sink_1.get(), FormatIsSet()).Times(AnyNumber()); 435bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch track_1->AddSink(sink_1.get()); 436bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch 437bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch // Create a new capturer with new source with different audio format. 438cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) MockMediaConstraintFactory constraint_factory; 439a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) StreamDeviceInfo device(MEDIA_DEVICE_AUDIO_CAPTURE, 440a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) std::string(), std::string()); 441bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch scoped_refptr<WebRtcAudioCapturer> new_capturer( 442cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) WebRtcAudioCapturer::CreateCapturer( 443cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) -1, device, constraint_factory.CreateWebMediaConstraints(), NULL, 444cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) NULL)); 445f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) scoped_refptr<MockCapturerSource> new_source( 446f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) new MockCapturerSource(new_capturer.get())); 4475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) EXPECT_CALL(*new_source.get(), OnInitialize(_, new_capturer.get(), -1)); 4480529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch EXPECT_CALL(*new_source.get(), SetAutomaticGainControl(true)); 4490529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch EXPECT_CALL(*new_source.get(), OnStart()); 4500529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch 4515d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) media::AudioParameters new_param( 4525d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) media::AudioParameters::AUDIO_PCM_LOW_LATENCY, 4535d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) media::CHANNEL_LAYOUT_MONO, 44100, 16, 441); 4545d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) new_capturer->SetCapturerSourceForTesting(new_source, new_param); 455bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch 456bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch // Setup the second audio track, connect it to the new capturer and start it. 4575d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) scoped_refptr<WebRtcLocalAudioTrackAdapter> adapter_2( 4585d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) WebRtcLocalAudioTrackAdapter::Create(std::string(), NULL)); 4595d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) scoped_ptr<WebRtcLocalAudioTrack> track_2( 4601320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci new WebRtcLocalAudioTrack(adapter_2.get(), new_capturer, NULL)); 461bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch track_2->Start(); 462bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch 463bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch // Verify the data flow by connecting the |sink_2| to |track_2|. 464a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) scoped_ptr<MockMediaStreamAudioSink> sink_2(new MockMediaStreamAudioSink()); 465f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) base::WaitableEvent event(false, false); 466f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) EXPECT_CALL(*sink_2, CaptureData(0, 0, 0, _, false)) 467bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch .Times(AnyNumber()).WillRepeatedly(Return()); 468a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) EXPECT_CALL(*sink_2, FormatIsSet()).WillOnce(SignalEvent(&event)); 469bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch track_2->AddSink(sink_2.get()); 470f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) EXPECT_TRUE(event.TimedWait(TestTimeouts::tiny_timeout())); 471f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 472f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) // Stopping the new source will stop the second track. 473f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) event.Reset(); 474f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) EXPECT_CALL(*new_source.get(), OnStop()) 475f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) .Times(1).WillOnce(SignalEvent(&event)); 476f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) new_capturer->Stop(); 477f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) EXPECT_TRUE(event.TimedWait(TestTimeouts::tiny_timeout())); 478f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 479f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) // Stop the capturer of the first audio track. 480f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) EXPECT_CALL(*capturer_source_.get(), OnStop()); 481f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) capturer_->Stop(); 482f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)} 483f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 484f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)// Make sure a audio track can deliver packets with a buffer size smaller than 485f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)// 10ms when it is not connected with a peer connection. 486f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)TEST_F(WebRtcLocalAudioTrackTest, TrackWorkWithSmallBufferSize) { 487f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) // Setup a capturer which works with a buffer size smaller than 10ms. 488f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) media::AudioParameters params(media::AudioParameters::AUDIO_PCM_LOW_LATENCY, 489f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) media::CHANNEL_LAYOUT_STEREO, 48000, 16, 128); 490f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 491f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) // Create a capturer with new source which works with the format above. 492a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) MockMediaConstraintFactory factory; 493a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) factory.DisableDefaultAudioConstraints(); 494f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) scoped_refptr<WebRtcAudioCapturer> capturer( 4955d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) WebRtcAudioCapturer::CreateCapturer( 4965d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) -1, 4975d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) StreamDeviceInfo(MEDIA_DEVICE_AUDIO_CAPTURE, 4985d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) "", "", params.sample_rate(), 4995d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) params.channel_layout(), 5005d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) params.frames_per_buffer()), 501a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) factory.CreateWebMediaConstraints(), 5020529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch NULL, NULL)); 503f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) scoped_refptr<MockCapturerSource> source( 504f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) new MockCapturerSource(capturer.get())); 5055d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) EXPECT_CALL(*source.get(), OnInitialize(_, capturer.get(), -1)); 5060529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch EXPECT_CALL(*source.get(), SetAutomaticGainControl(true)); 5070529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch EXPECT_CALL(*source.get(), OnStart()); 5085d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) capturer->SetCapturerSourceForTesting(source, params); 509f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 510f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) // Setup a audio track, connect it to the capturer and start it. 5115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) scoped_refptr<WebRtcLocalAudioTrackAdapter> adapter( 5125d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) WebRtcLocalAudioTrackAdapter::Create(std::string(), NULL)); 5135d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) scoped_ptr<WebRtcLocalAudioTrack> track( 5141320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci new WebRtcLocalAudioTrack(adapter.get(), capturer, NULL)); 515f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) track->Start(); 516f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 517f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) // Verify the data flow by connecting the |sink| to |track|. 518a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) scoped_ptr<MockMediaStreamAudioSink> sink(new MockMediaStreamAudioSink()); 519bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch base::WaitableEvent event(false, false); 520a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) EXPECT_CALL(*sink, FormatIsSet()).Times(1); 521f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) // Verify the sinks are getting the packets with an expecting buffer size. 522f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#if defined(OS_ANDROID) 523f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) const int expected_buffer_size = params.sample_rate() / 100; 524f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#else 525f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) const int expected_buffer_size = params.frames_per_buffer(); 526f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#endif 527f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) EXPECT_CALL(*sink, CaptureData( 528a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 0, 0, 0, _, false)) 529f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) .Times(AtLeast(1)).WillRepeatedly(SignalEvent(&event)); 530f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) track->AddSink(sink.get()); 531bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch EXPECT_TRUE(event.TimedWait(TestTimeouts::tiny_timeout())); 532a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) EXPECT_EQ(expected_buffer_size, sink->audio_params().frames_per_buffer()); 533bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch 534f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) // Stopping the new source will stop the second track. 5351320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci EXPECT_CALL(*source.get(), OnStop()).Times(1); 536f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) capturer->Stop(); 5370529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch 5380529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch // Even though this test don't use |capturer_source_| it will be stopped 5390529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch // during teardown of the test harness. 5400529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch EXPECT_CALL(*capturer_source_.get(), OnStop()); 541bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch} 542bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch 543c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)} // namespace content 544