| Index: content/renderer/media/webrtc_local_audio_track_unittest.cc
|
| diff --git a/content/renderer/media/webrtc_local_audio_track_unittest.cc b/content/renderer/media/webrtc_local_audio_track_unittest.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..51af138563639080e6770681332ea88ed4b5508a
|
| --- /dev/null
|
| +++ b/content/renderer/media/webrtc_local_audio_track_unittest.cc
|
| @@ -0,0 +1,579 @@
|
| +// Copyright 2013 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +#include "base/macros.h"
|
| +#include "base/synchronization/waitable_event.h"
|
| +#include "base/test/test_timeouts.h"
|
| +#include "build/build_config.h"
|
| +#include "content/public/renderer/media_stream_audio_sink.h"
|
| +#include "content/renderer/media/media_stream_audio_source.h"
|
| +#include "content/renderer/media/mock_constraint_factory.h"
|
| +#include "content/renderer/media/webrtc/webrtc_local_audio_track_adapter.h"
|
| +#include "content/renderer/media/webrtc_audio_capturer.h"
|
| +#include "content/renderer/media/webrtc_local_audio_track.h"
|
| +#include "media/base/audio_bus.h"
|
| +#include "media/base/audio_capturer_source.h"
|
| +#include "media/base/audio_parameters.h"
|
| +#include "testing/gmock/include/gmock/gmock.h"
|
| +#include "testing/gtest/include/gtest/gtest.h"
|
| +#include "third_party/WebKit/public/platform/WebMediaConstraints.h"
|
| +#include "third_party/WebKit/public/web/WebHeap.h"
|
| +#include "third_party/webrtc/api/mediastreaminterface.h"
|
| +
|
| +using ::testing::_;
|
| +using ::testing::AnyNumber;
|
| +using ::testing::AtLeast;
|
| +using ::testing::Return;
|
| +
|
| +namespace content {
|
| +
|
| +namespace {
|
| +
|
| +ACTION_P(SignalEvent, event) {
|
| + event->Signal();
|
| +}
|
| +
|
| +// A simple thread that we use to fake the audio thread which provides data to
|
| +// the |WebRtcAudioCapturer|.
|
| +class FakeAudioThread : public base::PlatformThread::Delegate {
|
| + public:
|
| + FakeAudioThread(WebRtcAudioCapturer* capturer,
|
| + const media::AudioParameters& params)
|
| + : capturer_(capturer),
|
| + thread_(),
|
| + closure_(false, false) {
|
| + DCHECK(capturer);
|
| + audio_bus_ = media::AudioBus::Create(params);
|
| + }
|
| +
|
| + ~FakeAudioThread() override { DCHECK(thread_.is_null()); }
|
| +
|
| + // base::PlatformThread::Delegate:
|
| + void ThreadMain() override {
|
| + while (true) {
|
| + if (closure_.IsSignaled())
|
| + return;
|
| +
|
| + media::AudioCapturerSource::CaptureCallback* callback =
|
| + static_cast<media::AudioCapturerSource::CaptureCallback*>(
|
| + capturer_);
|
| + audio_bus_->Zero();
|
| + callback->Capture(audio_bus_.get(), 0, 0, false);
|
| +
|
| + // Sleep 1ms to yield the resource for the main thread.
|
| + base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(1));
|
| + }
|
| + }
|
| +
|
| + void Start() {
|
| + base::PlatformThread::CreateWithPriority(
|
| + 0, this, &thread_, base::ThreadPriority::REALTIME_AUDIO);
|
| + CHECK(!thread_.is_null());
|
| + }
|
| +
|
| + void Stop() {
|
| + closure_.Signal();
|
| + base::PlatformThread::Join(thread_);
|
| + thread_ = base::PlatformThreadHandle();
|
| + }
|
| +
|
| + private:
|
| + std::unique_ptr<media::AudioBus> audio_bus_;
|
| + WebRtcAudioCapturer* capturer_;
|
| + base::PlatformThreadHandle thread_;
|
| + base::WaitableEvent closure_;
|
| + DISALLOW_COPY_AND_ASSIGN(FakeAudioThread);
|
| +};
|
| +
|
| +class MockCapturerSource : public media::AudioCapturerSource {
|
| + public:
|
| + explicit MockCapturerSource(WebRtcAudioCapturer* capturer)
|
| + : capturer_(capturer) {}
|
| + MOCK_METHOD3(OnInitialize, void(const media::AudioParameters& params,
|
| + CaptureCallback* callback,
|
| + int session_id));
|
| + MOCK_METHOD0(OnStart, void());
|
| + MOCK_METHOD0(OnStop, void());
|
| + void SetVolume(double volume) final {}
|
| + MOCK_METHOD1(SetAutomaticGainControl, void(bool enable));
|
| +
|
| + void Initialize(const media::AudioParameters& params,
|
| + CaptureCallback* callback,
|
| + int session_id) override {
|
| + DCHECK(params.IsValid());
|
| + params_ = params;
|
| + OnInitialize(params, callback, session_id);
|
| + }
|
| + void Start() override {
|
| + audio_thread_.reset(new FakeAudioThread(capturer_, params_));
|
| + audio_thread_->Start();
|
| + OnStart();
|
| + }
|
| + void Stop() override {
|
| + audio_thread_->Stop();
|
| + audio_thread_.reset();
|
| + OnStop();
|
| + }
|
| +
|
| + protected:
|
| + ~MockCapturerSource() override {}
|
| +
|
| + private:
|
| + std::unique_ptr<FakeAudioThread> audio_thread_;
|
| + WebRtcAudioCapturer* capturer_;
|
| + media::AudioParameters params_;
|
| +};
|
| +
|
| +class MockMediaStreamAudioSink : public MediaStreamAudioSink {
|
| + public:
|
| + MockMediaStreamAudioSink() {}
|
| + ~MockMediaStreamAudioSink() {}
|
| + void OnData(const media::AudioBus& audio_bus,
|
| + base::TimeTicks estimated_capture_time) override {
|
| + EXPECT_EQ(params_.channels(), audio_bus.channels());
|
| + EXPECT_EQ(params_.frames_per_buffer(), audio_bus.frames());
|
| + EXPECT_FALSE(estimated_capture_time.is_null());
|
| + CaptureData();
|
| + }
|
| + MOCK_METHOD0(CaptureData, void());
|
| + void OnSetFormat(const media::AudioParameters& params) {
|
| + params_ = params;
|
| + FormatIsSet();
|
| + }
|
| + MOCK_METHOD0(FormatIsSet, void());
|
| +
|
| + const media::AudioParameters& audio_params() const { return params_; }
|
| +
|
| + private:
|
| + media::AudioParameters params_;
|
| +};
|
| +
|
| +} // namespace
|
| +
|
| +class WebRtcLocalAudioTrackTest : public ::testing::Test {
|
| + protected:
|
| + void SetUp() override {
|
| + params_.Reset(media::AudioParameters::AUDIO_PCM_LOW_LATENCY,
|
| + media::CHANNEL_LAYOUT_STEREO, 48000, 16, 480);
|
| + MockConstraintFactory constraint_factory;
|
| + blink_source_.initialize("dummy", blink::WebMediaStreamSource::TypeAudio,
|
| + "dummy",
|
| + false /* remote */);
|
| + MediaStreamAudioSource* audio_source = new MediaStreamAudioSource();
|
| + blink_source_.setExtraData(audio_source);
|
| +
|
| + StreamDeviceInfo device(MEDIA_DEVICE_AUDIO_CAPTURE,
|
| + std::string(), std::string());
|
| + {
|
| + std::unique_ptr<WebRtcAudioCapturer> capturer =
|
| + WebRtcAudioCapturer::CreateCapturer(
|
| + -1, device, constraint_factory.CreateWebMediaConstraints(),
|
| + nullptr, audio_source);
|
| + capturer_ = capturer.get();
|
| + audio_source->SetAudioCapturer(std::move(capturer));
|
| + }
|
| + capturer_source_ = new MockCapturerSource(capturer_);
|
| + EXPECT_CALL(*capturer_source_.get(), OnInitialize(_, capturer_, -1))
|
| + .WillOnce(Return());
|
| + EXPECT_CALL(*capturer_source_.get(), SetAutomaticGainControl(true));
|
| + EXPECT_CALL(*capturer_source_.get(), OnStart());
|
| + capturer_->SetCapturerSource(capturer_source_, params_);
|
| + }
|
| +
|
| + void TearDown() override {
|
| + blink_source_.reset();
|
| + blink::WebHeap::collectAllGarbageForTesting();
|
| + }
|
| +
|
| + media::AudioParameters params_;
|
| + blink::WebMediaStreamSource blink_source_;
|
| + WebRtcAudioCapturer* capturer_; // Owned by |blink_source_|.
|
| + scoped_refptr<MockCapturerSource> capturer_source_;
|
| +};
|
| +
|
| +// Creates a capturer and audio track, fakes its audio thread, and
|
| +// connect/disconnect the sink to the audio track on the fly, the sink should
|
| +// get data callback when the track is connected to the capturer but not when
|
| +// the track is disconnected from the capturer.
|
| +TEST_F(WebRtcLocalAudioTrackTest, ConnectAndDisconnectOneSink) {
|
| + scoped_refptr<WebRtcLocalAudioTrackAdapter> adapter(
|
| + WebRtcLocalAudioTrackAdapter::Create(std::string(), NULL));
|
| + std::unique_ptr<WebRtcLocalAudioTrack> track(
|
| + new WebRtcLocalAudioTrack(adapter.get()));
|
| + track->Start(
|
| + base::Bind(&MediaStreamAudioSource::StopAudioDeliveryTo,
|
| + MediaStreamAudioSource::From(blink_source_)->GetWeakPtr(),
|
| + track.get()));
|
| + capturer_->AddTrack(track.get());
|
| + EXPECT_TRUE(track->GetAudioAdapter()->enabled());
|
| +
|
| + std::unique_ptr<MockMediaStreamAudioSink> sink(
|
| + new MockMediaStreamAudioSink());
|
| + base::WaitableEvent event(false, false);
|
| + EXPECT_CALL(*sink, FormatIsSet());
|
| + EXPECT_CALL(*sink,
|
| + CaptureData()).Times(AtLeast(1))
|
| + .WillRepeatedly(SignalEvent(&event));
|
| + track->AddSink(sink.get());
|
| + EXPECT_TRUE(event.TimedWait(TestTimeouts::tiny_timeout()));
|
| + track->RemoveSink(sink.get());
|
| +
|
| + EXPECT_CALL(*capturer_source_.get(), OnStop()).WillOnce(Return());
|
| + capturer_->Stop();
|
| +}
|
| +
|
| +// The same setup as ConnectAndDisconnectOneSink, but enable and disable the
|
| +// audio track on the fly. When the audio track is disabled, there is no data
|
| +// callback to the sink; when the audio track is enabled, there comes data
|
| +// callback.
|
| +// TODO(xians): Enable this test after resolving the racing issue that TSAN
|
| +// reports on MediaStreamTrack::enabled();
|
| +TEST_F(WebRtcLocalAudioTrackTest, DISABLED_DisableEnableAudioTrack) {
|
| + EXPECT_CALL(*capturer_source_.get(), SetAutomaticGainControl(true));
|
| + EXPECT_CALL(*capturer_source_.get(), OnStart());
|
| + scoped_refptr<WebRtcLocalAudioTrackAdapter> adapter(
|
| + WebRtcLocalAudioTrackAdapter::Create(std::string(), NULL));
|
| + std::unique_ptr<WebRtcLocalAudioTrack> track(
|
| + new WebRtcLocalAudioTrack(adapter.get()));
|
| + track->Start(
|
| + base::Bind(&MediaStreamAudioSource::StopAudioDeliveryTo,
|
| + MediaStreamAudioSource::From(blink_source_)->GetWeakPtr(),
|
| + track.get()));
|
| + capturer_->AddTrack(track.get());
|
| + EXPECT_TRUE(track->GetAudioAdapter()->enabled());
|
| + EXPECT_TRUE(track->GetAudioAdapter()->set_enabled(false));
|
| + std::unique_ptr<MockMediaStreamAudioSink> sink(
|
| + new MockMediaStreamAudioSink());
|
| + const media::AudioParameters params = capturer_->GetInputFormat();
|
| + base::WaitableEvent event(false, false);
|
| + EXPECT_CALL(*sink, FormatIsSet()).Times(1);
|
| + EXPECT_CALL(*sink, CaptureData()).Times(0);
|
| + EXPECT_EQ(sink->audio_params().frames_per_buffer(),
|
| + params.sample_rate() / 100);
|
| + track->AddSink(sink.get());
|
| + EXPECT_FALSE(event.TimedWait(TestTimeouts::tiny_timeout()));
|
| +
|
| + event.Reset();
|
| + EXPECT_CALL(*sink, CaptureData()).Times(AtLeast(1))
|
| + .WillRepeatedly(SignalEvent(&event));
|
| + EXPECT_TRUE(track->GetAudioAdapter()->set_enabled(true));
|
| + EXPECT_TRUE(event.TimedWait(TestTimeouts::tiny_timeout()));
|
| + track->RemoveSink(sink.get());
|
| +
|
| + EXPECT_CALL(*capturer_source_.get(), OnStop()).WillOnce(Return());
|
| + capturer_->Stop();
|
| + track.reset();
|
| +}
|
| +
|
| +// Create multiple audio tracks and enable/disable them, verify that the audio
|
| +// callbacks appear/disappear.
|
| +// Flaky due to a data race, see http://crbug.com/295418
|
| +TEST_F(WebRtcLocalAudioTrackTest, DISABLED_MultipleAudioTracks) {
|
| + scoped_refptr<WebRtcLocalAudioTrackAdapter> adapter_1(
|
| + WebRtcLocalAudioTrackAdapter::Create(std::string(), NULL));
|
| + std::unique_ptr<WebRtcLocalAudioTrack> track_1(
|
| + new WebRtcLocalAudioTrack(adapter_1.get()));
|
| + track_1->Start(
|
| + base::Bind(&MediaStreamAudioSource::StopAudioDeliveryTo,
|
| + MediaStreamAudioSource::From(blink_source_)->GetWeakPtr(),
|
| + track_1.get()));
|
| + capturer_->AddTrack(track_1.get());
|
| + EXPECT_TRUE(track_1->GetAudioAdapter()->enabled());
|
| + std::unique_ptr<MockMediaStreamAudioSink> sink_1(
|
| + new MockMediaStreamAudioSink());
|
| + const media::AudioParameters params = capturer_->GetInputFormat();
|
| + base::WaitableEvent event_1(false, false);
|
| + EXPECT_CALL(*sink_1, FormatIsSet()).WillOnce(Return());
|
| + EXPECT_CALL(*sink_1, CaptureData()).Times(AtLeast(1))
|
| + .WillRepeatedly(SignalEvent(&event_1));
|
| + EXPECT_EQ(sink_1->audio_params().frames_per_buffer(),
|
| + params.sample_rate() / 100);
|
| + track_1->AddSink(sink_1.get());
|
| + EXPECT_TRUE(event_1.TimedWait(TestTimeouts::tiny_timeout()));
|
| +
|
| + scoped_refptr<WebRtcLocalAudioTrackAdapter> adapter_2(
|
| + WebRtcLocalAudioTrackAdapter::Create(std::string(), NULL));
|
| + std::unique_ptr<WebRtcLocalAudioTrack> track_2(
|
| + new WebRtcLocalAudioTrack(adapter_2.get()));
|
| + track_2->Start(
|
| + base::Bind(&MediaStreamAudioSource::StopAudioDeliveryTo,
|
| + MediaStreamAudioSource::From(blink_source_)->GetWeakPtr(),
|
| + track_2.get()));
|
| + capturer_->AddTrack(track_2.get());
|
| + EXPECT_TRUE(track_2->GetAudioAdapter()->enabled());
|
| +
|
| + // Verify both |sink_1| and |sink_2| get data.
|
| + event_1.Reset();
|
| + base::WaitableEvent event_2(false, false);
|
| +
|
| + std::unique_ptr<MockMediaStreamAudioSink> sink_2(
|
| + new MockMediaStreamAudioSink());
|
| + EXPECT_CALL(*sink_2, FormatIsSet()).WillOnce(Return());
|
| + EXPECT_CALL(*sink_1, CaptureData()).Times(AtLeast(1))
|
| + .WillRepeatedly(SignalEvent(&event_1));
|
| + EXPECT_EQ(sink_1->audio_params().frames_per_buffer(),
|
| + params.sample_rate() / 100);
|
| + EXPECT_CALL(*sink_2, CaptureData()).Times(AtLeast(1))
|
| + .WillRepeatedly(SignalEvent(&event_2));
|
| + EXPECT_EQ(sink_2->audio_params().frames_per_buffer(),
|
| + params.sample_rate() / 100);
|
| + track_2->AddSink(sink_2.get());
|
| + EXPECT_TRUE(event_1.TimedWait(TestTimeouts::tiny_timeout()));
|
| + EXPECT_TRUE(event_2.TimedWait(TestTimeouts::tiny_timeout()));
|
| +
|
| + track_1->RemoveSink(sink_1.get());
|
| + track_1->Stop();
|
| + track_1.reset();
|
| +
|
| + EXPECT_CALL(*capturer_source_.get(), OnStop()).WillOnce(Return());
|
| + track_2->RemoveSink(sink_2.get());
|
| + track_2->Stop();
|
| + track_2.reset();
|
| +}
|
| +
|
| +
|
| +// Start one track and verify the capturer is correctly starting its source.
|
| +// And it should be fine to not to call Stop() explicitly.
|
| +TEST_F(WebRtcLocalAudioTrackTest, StartOneAudioTrack) {
|
| + scoped_refptr<WebRtcLocalAudioTrackAdapter> adapter(
|
| + WebRtcLocalAudioTrackAdapter::Create(std::string(), NULL));
|
| + std::unique_ptr<WebRtcLocalAudioTrack> track(
|
| + new WebRtcLocalAudioTrack(adapter.get()));
|
| + track->Start(
|
| + base::Bind(&MediaStreamAudioSource::StopAudioDeliveryTo,
|
| + MediaStreamAudioSource::From(blink_source_)->GetWeakPtr(),
|
| + track.get()));
|
| + capturer_->AddTrack(track.get());
|
| +
|
| + // When the track goes away, it will automatically stop the
|
| + // |capturer_source_|.
|
| + EXPECT_CALL(*capturer_source_.get(), OnStop());
|
| + track.reset();
|
| +}
|
| +
|
| +// Start two tracks and verify the capturer is correctly starting its source.
|
| +// When the last track connected to the capturer is stopped, the source is
|
| +// stopped.
|
| +TEST_F(WebRtcLocalAudioTrackTest, StartTwoAudioTracks) {
|
| + scoped_refptr<WebRtcLocalAudioTrackAdapter> adapter1(
|
| + WebRtcLocalAudioTrackAdapter::Create(std::string(), NULL));
|
| + std::unique_ptr<WebRtcLocalAudioTrack> track1(
|
| + new WebRtcLocalAudioTrack(adapter1.get()));
|
| + track1->Start(
|
| + base::Bind(&MediaStreamAudioSource::StopAudioDeliveryTo,
|
| + MediaStreamAudioSource::From(blink_source_)->GetWeakPtr(),
|
| + track1.get()));
|
| + capturer_->AddTrack(track1.get());
|
| +
|
| + scoped_refptr<WebRtcLocalAudioTrackAdapter> adapter2(
|
| + WebRtcLocalAudioTrackAdapter::Create(std::string(), NULL));
|
| + std::unique_ptr<WebRtcLocalAudioTrack> track2(
|
| + new WebRtcLocalAudioTrack(adapter2.get()));
|
| + track2->Start(
|
| + base::Bind(&MediaStreamAudioSource::StopAudioDeliveryTo,
|
| + MediaStreamAudioSource::From(blink_source_)->GetWeakPtr(),
|
| + track2.get()));
|
| + capturer_->AddTrack(track2.get());
|
| +
|
| + track1->Stop();
|
| + // When the last track is stopped, it will automatically stop the
|
| + // |capturer_source_|.
|
| + EXPECT_CALL(*capturer_source_.get(), OnStop());
|
| + track2->Stop();
|
| +}
|
| +
|
| +// Start/Stop tracks and verify the capturer is correctly starting/stopping
|
| +// its source.
|
| +TEST_F(WebRtcLocalAudioTrackTest, StartAndStopAudioTracks) {
|
| + base::WaitableEvent event(false, false);
|
| + scoped_refptr<WebRtcLocalAudioTrackAdapter> adapter_1(
|
| + WebRtcLocalAudioTrackAdapter::Create(std::string(), NULL));
|
| + std::unique_ptr<WebRtcLocalAudioTrack> track_1(
|
| + new WebRtcLocalAudioTrack(adapter_1.get()));
|
| + track_1->Start(
|
| + base::Bind(&MediaStreamAudioSource::StopAudioDeliveryTo,
|
| + MediaStreamAudioSource::From(blink_source_)->GetWeakPtr(),
|
| + track_1.get()));
|
| + capturer_->AddTrack(track_1.get());
|
| +
|
| + // Verify the data flow by connecting the sink to |track_1|.
|
| + std::unique_ptr<MockMediaStreamAudioSink> sink(
|
| + new MockMediaStreamAudioSink());
|
| + event.Reset();
|
| + EXPECT_CALL(*sink, FormatIsSet()).WillOnce(SignalEvent(&event));
|
| + EXPECT_CALL(*sink, CaptureData())
|
| + .Times(AnyNumber()).WillRepeatedly(Return());
|
| + track_1->AddSink(sink.get());
|
| + EXPECT_TRUE(event.TimedWait(TestTimeouts::tiny_timeout()));
|
| +
|
| + // Start the second audio track will not start the |capturer_source_|
|
| + // since it has been started.
|
| + EXPECT_CALL(*capturer_source_.get(), OnStart()).Times(0);
|
| + scoped_refptr<WebRtcLocalAudioTrackAdapter> adapter_2(
|
| + WebRtcLocalAudioTrackAdapter::Create(std::string(), NULL));
|
| + std::unique_ptr<WebRtcLocalAudioTrack> track_2(
|
| + new WebRtcLocalAudioTrack(adapter_2.get()));
|
| + track_2->Start(
|
| + base::Bind(&MediaStreamAudioSource::StopAudioDeliveryTo,
|
| + MediaStreamAudioSource::From(blink_source_)->GetWeakPtr(),
|
| + track_2.get()));
|
| + capturer_->AddTrack(track_2.get());
|
| +
|
| + // Stop the capturer will clear up the track lists in the capturer.
|
| + EXPECT_CALL(*capturer_source_.get(), OnStop());
|
| + capturer_->Stop();
|
| +
|
| + // Adding a new track to the capturer.
|
| + track_2->AddSink(sink.get());
|
| + EXPECT_CALL(*sink, FormatIsSet()).Times(0);
|
| +
|
| + // Stop the capturer again will not trigger stopping the source of the
|
| + // capturer again..
|
| + event.Reset();
|
| + EXPECT_CALL(*capturer_source_.get(), OnStop()).Times(0);
|
| + capturer_->Stop();
|
| +}
|
| +
|
| +// Create a new capturer with new source, connect it to a new audio track.
|
| +#if defined(THREAD_SANITIZER)
|
| +// Fails under TSan, see https://crbug.com/576634.
|
| +#define MAYBE_ConnectTracksToDifferentCapturers \
|
| + DISABLED_ConnectTracksToDifferentCapturers
|
| +#else
|
| +#define MAYBE_ConnectTracksToDifferentCapturers \
|
| + ConnectTracksToDifferentCapturers
|
| +#endif
|
| +TEST_F(WebRtcLocalAudioTrackTest, MAYBE_ConnectTracksToDifferentCapturers) {
|
| + // Setup the first audio track and start it.
|
| + scoped_refptr<WebRtcLocalAudioTrackAdapter> adapter_1(
|
| + WebRtcLocalAudioTrackAdapter::Create(std::string(), NULL));
|
| + std::unique_ptr<WebRtcLocalAudioTrack> track_1(
|
| + new WebRtcLocalAudioTrack(adapter_1.get()));
|
| + track_1->Start(
|
| + base::Bind(&MediaStreamAudioSource::StopAudioDeliveryTo,
|
| + MediaStreamAudioSource::From(blink_source_)->GetWeakPtr(),
|
| + track_1.get()));
|
| + capturer_->AddTrack(track_1.get());
|
| +
|
| + // Verify the data flow by connecting the |sink_1| to |track_1|.
|
| + std::unique_ptr<MockMediaStreamAudioSink> sink_1(
|
| + new MockMediaStreamAudioSink());
|
| + EXPECT_CALL(*sink_1.get(), CaptureData())
|
| + .Times(AnyNumber()).WillRepeatedly(Return());
|
| + EXPECT_CALL(*sink_1.get(), FormatIsSet()).Times(AnyNumber());
|
| + track_1->AddSink(sink_1.get());
|
| +
|
| + // Create a new capturer with new source with different audio format.
|
| + MockConstraintFactory constraint_factory;
|
| + StreamDeviceInfo device(MEDIA_DEVICE_AUDIO_CAPTURE,
|
| + std::string(), std::string());
|
| + std::unique_ptr<WebRtcAudioCapturer> new_capturer(
|
| + WebRtcAudioCapturer::CreateCapturer(
|
| + -1, device, constraint_factory.CreateWebMediaConstraints(), NULL,
|
| + NULL));
|
| + scoped_refptr<MockCapturerSource> new_source(
|
| + new MockCapturerSource(new_capturer.get()));
|
| + EXPECT_CALL(*new_source.get(), OnInitialize(_, new_capturer.get(), -1));
|
| + EXPECT_CALL(*new_source.get(), SetAutomaticGainControl(true));
|
| + EXPECT_CALL(*new_source.get(), OnStart());
|
| +
|
| + media::AudioParameters new_param(
|
| + media::AudioParameters::AUDIO_PCM_LOW_LATENCY,
|
| + media::CHANNEL_LAYOUT_MONO, 44100, 16, 441);
|
| + new_capturer->SetCapturerSource(new_source, new_param);
|
| +
|
| + // Setup the second audio track, connect it to the new capturer and start it.
|
| + scoped_refptr<WebRtcLocalAudioTrackAdapter> adapter_2(
|
| + WebRtcLocalAudioTrackAdapter::Create(std::string(), NULL));
|
| + std::unique_ptr<WebRtcLocalAudioTrack> track_2(
|
| + new WebRtcLocalAudioTrack(adapter_2.get()));
|
| + track_2->Start(
|
| + base::Bind(&MediaStreamAudioSource::StopAudioDeliveryTo,
|
| + MediaStreamAudioSource::From(blink_source_)->GetWeakPtr(),
|
| + track_2.get()));
|
| + new_capturer->AddTrack(track_2.get());
|
| +
|
| + // Verify the data flow by connecting the |sink_2| to |track_2|.
|
| + std::unique_ptr<MockMediaStreamAudioSink> sink_2(
|
| + new MockMediaStreamAudioSink());
|
| + base::WaitableEvent event(false, false);
|
| + EXPECT_CALL(*sink_2, CaptureData())
|
| + .Times(AnyNumber()).WillRepeatedly(Return());
|
| + EXPECT_CALL(*sink_2, FormatIsSet()).WillOnce(SignalEvent(&event));
|
| + track_2->AddSink(sink_2.get());
|
| + EXPECT_TRUE(event.TimedWait(TestTimeouts::tiny_timeout()));
|
| +
|
| + // Stopping the new source will stop the second track.
|
| + event.Reset();
|
| + EXPECT_CALL(*new_source.get(), OnStop())
|
| + .Times(1).WillOnce(SignalEvent(&event));
|
| + new_capturer->Stop();
|
| + EXPECT_TRUE(event.TimedWait(TestTimeouts::tiny_timeout()));
|
| +
|
| + // Stop the capturer of the first audio track.
|
| + EXPECT_CALL(*capturer_source_.get(), OnStop());
|
| + capturer_->Stop();
|
| +}
|
| +
|
| +// Make sure a audio track can deliver packets with a buffer size smaller than
|
| +// 10ms when it is not connected with a peer connection.
|
| +TEST_F(WebRtcLocalAudioTrackTest, TrackWorkWithSmallBufferSize) {
|
| + // Setup a capturer which works with a buffer size smaller than 10ms.
|
| + media::AudioParameters params(media::AudioParameters::AUDIO_PCM_LOW_LATENCY,
|
| + media::CHANNEL_LAYOUT_STEREO, 48000, 16, 128);
|
| +
|
| + // Create a capturer with new source which works with the format above.
|
| + MockConstraintFactory factory;
|
| + factory.DisableDefaultAudioConstraints();
|
| + std::unique_ptr<WebRtcAudioCapturer> capturer(
|
| + WebRtcAudioCapturer::CreateCapturer(
|
| + -1, StreamDeviceInfo(MEDIA_DEVICE_AUDIO_CAPTURE, "", "",
|
| + params.sample_rate(), params.channel_layout(),
|
| + params.frames_per_buffer()),
|
| + factory.CreateWebMediaConstraints(), NULL, NULL));
|
| + scoped_refptr<MockCapturerSource> source(
|
| + new MockCapturerSource(capturer.get()));
|
| + EXPECT_CALL(*source.get(), OnInitialize(_, capturer.get(), -1));
|
| + EXPECT_CALL(*source.get(), SetAutomaticGainControl(true));
|
| + EXPECT_CALL(*source.get(), OnStart());
|
| + capturer->SetCapturerSource(source, params);
|
| +
|
| + // Setup a audio track, connect it to the capturer and start it.
|
| + scoped_refptr<WebRtcLocalAudioTrackAdapter> adapter(
|
| + WebRtcLocalAudioTrackAdapter::Create(std::string(), NULL));
|
| + std::unique_ptr<WebRtcLocalAudioTrack> track(
|
| + new WebRtcLocalAudioTrack(adapter.get()));
|
| + track->Start(
|
| + base::Bind(&MediaStreamAudioSource::StopAudioDeliveryTo,
|
| + MediaStreamAudioSource::From(blink_source_)->GetWeakPtr(),
|
| + track.get()));
|
| + capturer->AddTrack(track.get());
|
| +
|
| + // Verify the data flow by connecting the |sink| to |track|.
|
| + std::unique_ptr<MockMediaStreamAudioSink> sink(
|
| + new MockMediaStreamAudioSink());
|
| + base::WaitableEvent event(false, false);
|
| + EXPECT_CALL(*sink, FormatIsSet()).Times(1);
|
| + // Verify the sinks are getting the packets with an expecting buffer size.
|
| +#if defined(OS_ANDROID)
|
| + const int expected_buffer_size = params.sample_rate() / 100;
|
| +#else
|
| + const int expected_buffer_size = params.frames_per_buffer();
|
| +#endif
|
| + EXPECT_CALL(*sink, CaptureData())
|
| + .Times(AtLeast(1)).WillRepeatedly(SignalEvent(&event));
|
| + track->AddSink(sink.get());
|
| + EXPECT_TRUE(event.TimedWait(TestTimeouts::tiny_timeout()));
|
| + EXPECT_EQ(expected_buffer_size, sink->audio_params().frames_per_buffer());
|
| +
|
| + // Stopping the new source will stop the second track.
|
| + EXPECT_CALL(*source.get(), OnStop()).Times(1);
|
| + capturer->Stop();
|
| +
|
| + // Even though this test don't use |capturer_source_| it will be stopped
|
| + // during teardown of the test harness.
|
| + EXPECT_CALL(*capturer_source_.get(), OnStop());
|
| +}
|
| +
|
| +} // namespace content
|
|
|