Index: content/renderer/media/media_stream_audio_unittest.cc |
diff --git a/content/renderer/media/media_stream_audio_unittest.cc b/content/renderer/media/media_stream_audio_unittest.cc |
deleted file mode 100644 |
index 060dca37b0706d0276decba38199d7792efaf851..0000000000000000000000000000000000000000 |
--- a/content/renderer/media/media_stream_audio_unittest.cc |
+++ /dev/null |
@@ -1,450 +0,0 @@ |
-// Copyright 2016 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 <stdint.h> |
- |
-#include "base/atomicops.h" |
-#include "base/synchronization/lock.h" |
-#include "base/synchronization/waitable_event.h" |
-#include "base/test/test_timeouts.h" |
-#include "base/threading/platform_thread.h" |
-#include "base/threading/thread_checker.h" |
-#include "content/public/renderer/media_stream_audio_sink.h" |
-#include "content/renderer/media/media_stream_audio_source.h" |
-#include "content/renderer/media/media_stream_audio_track.h" |
-#include "media/base/audio_bus.h" |
-#include "media/base/audio_parameters.h" |
-#include "testing/gtest/include/gtest/gtest.h" |
-#include "third_party/WebKit/public/platform/WebString.h" |
-#include "third_party/WebKit/public/web/WebHeap.h" |
- |
-namespace content { |
- |
-namespace { |
- |
-constexpr int kSampleRate = 8000; |
-constexpr int kBufferSize = kSampleRate / 100; |
- |
-// The maximum integer that can be exactly represented by the float data type. |
-constexpr int kMaxValueSafelyConvertableToFloat = 1 << 24; |
- |
-// A simple MediaStreamAudioSource that spawns a real-time audio thread and |
-// emits audio samples with monotonically-increasing sample values. Includes |
-// hooks for the unit tests to confirm lifecycle status and to change audio |
-// format. |
-class FakeMediaStreamAudioSource |
- : public MediaStreamAudioSource, |
- public base::PlatformThread::Delegate { |
- public: |
- FakeMediaStreamAudioSource() |
- : MediaStreamAudioSource(true), stop_event_(true, false), |
- next_buffer_size_(kBufferSize), sample_count_(0) {} |
- |
- ~FakeMediaStreamAudioSource() final { |
- CHECK(main_thread_checker_.CalledOnValidThread()); |
- EnsureSourceIsStopped(); |
- } |
- |
- bool was_started() const { |
- CHECK(main_thread_checker_.CalledOnValidThread()); |
- return !thread_.is_null(); |
- } |
- |
- bool was_stopped() const { |
- CHECK(main_thread_checker_.CalledOnValidThread()); |
- return stop_event_.IsSignaled(); |
- } |
- |
- void SetBufferSize(int new_buffer_size) { |
- CHECK(main_thread_checker_.CalledOnValidThread()); |
- base::subtle::NoBarrier_Store(&next_buffer_size_, new_buffer_size); |
- } |
- |
- protected: |
- bool EnsureSourceIsStarted() final { |
- CHECK(main_thread_checker_.CalledOnValidThread()); |
- if (was_started()) |
- return true; |
- if (was_stopped()) |
- return false; |
- base::PlatformThread::CreateWithPriority( |
- 0, this, &thread_, base::ThreadPriority::REALTIME_AUDIO); |
- return true; |
- } |
- |
- void EnsureSourceIsStopped() final { |
- CHECK(main_thread_checker_.CalledOnValidThread()); |
- if (was_stopped()) |
- return; |
- stop_event_.Signal(); |
- if (was_started()) |
- base::PlatformThread::Join(thread_); |
- } |
- |
- void ThreadMain() override { |
- while (!stop_event_.IsSignaled()) { |
- // If needed, notify of the new format and re-create |audio_bus_|. |
- const int buffer_size = base::subtle::NoBarrier_Load(&next_buffer_size_); |
- if (!audio_bus_ || audio_bus_->frames() != buffer_size) { |
- MediaStreamAudioSource::SetFormat(media::AudioParameters( |
- media::AudioParameters::AUDIO_PCM_LOW_LATENCY, |
- media::CHANNEL_LAYOUT_MONO, kSampleRate, 16, buffer_size)); |
- audio_bus_ = media::AudioBus::Create(1, buffer_size); |
- } |
- |
- // Deliver the next chunk of audio data. Each sample value is its offset |
- // from the very first sample. |
- float* const data = audio_bus_->channel(0); |
- for (int i = 0; i < buffer_size; ++i) |
- data[i] = ++sample_count_; |
- CHECK_LT(sample_count_, kMaxValueSafelyConvertableToFloat); |
- MediaStreamAudioSource::DeliverDataToTracks(*audio_bus_, |
- base::TimeTicks::Now()); |
- |
- // Sleep before producing the next chunk of audio. |
- base::PlatformThread::Sleep(base::TimeDelta::FromMicroseconds( |
- base::Time::kMicrosecondsPerSecond * buffer_size / kSampleRate)); |
- } |
- } |
- |
- private: |
- base::ThreadChecker main_thread_checker_; |
- |
- base::PlatformThreadHandle thread_; |
- mutable base::WaitableEvent stop_event_; |
- |
- base::subtle::Atomic32 next_buffer_size_; |
- std::unique_ptr<media::AudioBus> audio_bus_; |
- int sample_count_; |
- |
- DISALLOW_COPY_AND_ASSIGN(FakeMediaStreamAudioSource); |
-}; |
- |
-// A simple MediaStreamAudioSink that consumes audio and confirms the sample |
-// values. Includes hooks for the unit tests to monitor the format and flow of |
-// audio, whether the audio is silent, and the propagation of the "enabled" |
-// state. |
-class FakeMediaStreamAudioSink : public MediaStreamAudioSink { |
- public: |
- enum EnableState { |
- NO_ENABLE_NOTIFICATION, |
- WAS_ENABLED, |
- WAS_DISABLED |
- }; |
- |
- FakeMediaStreamAudioSink() |
- : MediaStreamAudioSink(), expected_sample_count_(-1), |
- num_on_data_calls_(0), audio_is_silent_(true), was_ended_(false), |
- enable_state_(NO_ENABLE_NOTIFICATION) {} |
- |
- ~FakeMediaStreamAudioSink() final { |
- CHECK(main_thread_checker_.CalledOnValidThread()); |
- } |
- |
- media::AudioParameters params() const { |
- CHECK(main_thread_checker_.CalledOnValidThread()); |
- base::AutoLock auto_lock(params_lock_); |
- return params_; |
- } |
- |
- int num_on_data_calls() const { |
- CHECK(main_thread_checker_.CalledOnValidThread()); |
- return base::subtle::NoBarrier_Load(&num_on_data_calls_); |
- } |
- |
- bool is_audio_silent() const { |
- CHECK(main_thread_checker_.CalledOnValidThread()); |
- return !!base::subtle::NoBarrier_Load(&audio_is_silent_); |
- } |
- |
- bool was_ended() const { |
- CHECK(main_thread_checker_.CalledOnValidThread()); |
- return was_ended_; |
- } |
- |
- EnableState enable_state() const { |
- CHECK(main_thread_checker_.CalledOnValidThread()); |
- return enable_state_; |
- } |
- |
- void OnSetFormat(const media::AudioParameters& params) final { |
- ASSERT_TRUE(params.IsValid()); |
- base::AutoLock auto_lock(params_lock_); |
- params_ = params; |
- } |
- |
- void OnData(const media::AudioBus& audio_bus, |
- base::TimeTicks estimated_capture_time) final { |
- ASSERT_TRUE(params_.IsValid()); |
- ASSERT_FALSE(was_ended_); |
- |
- ASSERT_EQ(params_.channels(), audio_bus.channels()); |
- ASSERT_EQ(params_.frames_per_buffer(), audio_bus.frames()); |
- if (audio_bus.AreFramesZero()) { |
- base::subtle::NoBarrier_Store(&audio_is_silent_, 1); |
- expected_sample_count_ = -1; // Reset for when audio comes back. |
- } else { |
- base::subtle::NoBarrier_Store(&audio_is_silent_, 0); |
- const float* const data = audio_bus.channel(0); |
- if (expected_sample_count_ == -1) |
- expected_sample_count_ = static_cast<int64_t>(data[0]); |
- CHECK_LE(expected_sample_count_ + audio_bus.frames(), |
- kMaxValueSafelyConvertableToFloat); |
- for (int i = 0; i < audio_bus.frames(); ++i) { |
- const float expected_sample_value = expected_sample_count_; |
- ASSERT_EQ(expected_sample_value, data[i]); |
- ++expected_sample_count_; |
- } |
- } |
- |
- ASSERT_TRUE(!estimated_capture_time.is_null()); |
- ASSERT_LT(last_estimated_capture_time_, estimated_capture_time); |
- last_estimated_capture_time_ = estimated_capture_time; |
- |
- base::subtle::NoBarrier_AtomicIncrement(&num_on_data_calls_, 1); |
- } |
- |
- void OnReadyStateChanged( |
- blink::WebMediaStreamSource::ReadyState state) final { |
- CHECK(main_thread_checker_.CalledOnValidThread()); |
- if (state == blink::WebMediaStreamSource::ReadyStateEnded) |
- was_ended_ = true; |
- } |
- |
- void OnEnabledChanged(bool enabled) final { |
- CHECK(main_thread_checker_.CalledOnValidThread()); |
- enable_state_ = enabled ? WAS_ENABLED : WAS_DISABLED; |
- } |
- |
- private: |
- base::ThreadChecker main_thread_checker_; |
- |
- mutable base::Lock params_lock_; |
- media::AudioParameters params_; |
- int expected_sample_count_; |
- base::TimeTicks last_estimated_capture_time_; |
- base::subtle::Atomic32 num_on_data_calls_; |
- base::subtle::Atomic32 audio_is_silent_; |
- bool was_ended_; |
- EnableState enable_state_; |
- |
- DISALLOW_COPY_AND_ASSIGN(FakeMediaStreamAudioSink); |
-}; |
- |
-} // namespace |
- |
-class MediaStreamAudioTest : public ::testing::Test { |
- protected: |
- void SetUp() override { |
- blink_audio_source_.initialize(blink::WebString::fromUTF8("audio_id"), |
- blink::WebMediaStreamSource::TypeAudio, |
- blink::WebString::fromUTF8("audio_track"), |
- false /* remote */); |
- blink_audio_track_.initialize(blink_audio_source_.id(), |
- blink_audio_source_); |
- } |
- |
- void TearDown() override { |
- blink_audio_track_.reset(); |
- blink_audio_source_.reset(); |
- blink::WebHeap::collectAllGarbageForTesting(); |
- } |
- |
- FakeMediaStreamAudioSource* source() const { |
- return static_cast<FakeMediaStreamAudioSource*>( |
- MediaStreamAudioSource::From(blink_audio_source_)); |
- } |
- |
- MediaStreamAudioTrack* track() const { |
- return MediaStreamAudioTrack::From(blink_audio_track_); |
- } |
- |
- blink::WebMediaStreamSource blink_audio_source_; |
- blink::WebMediaStreamTrack blink_audio_track_; |
-}; |
- |
-// Tests that a simple source-->track-->sink connection and audio data flow |
-// works. |
-TEST_F(MediaStreamAudioTest, BasicUsage) { |
- // Create the source, but it should not be started yet. |
- ASSERT_FALSE(source()); |
- blink_audio_source_.setExtraData(new FakeMediaStreamAudioSource()); |
- ASSERT_TRUE(source()); |
- EXPECT_FALSE(source()->was_started()); |
- EXPECT_FALSE(source()->was_stopped()); |
- |
- // Connect a track to the source. This should auto-start the source. |
- ASSERT_FALSE(track()); |
- EXPECT_TRUE(source()->ConnectToTrack(blink_audio_track_)); |
- ASSERT_TRUE(track()); |
- EXPECT_TRUE(source()->was_started()); |
- EXPECT_FALSE(source()->was_stopped()); |
- |
- // Connect a sink to the track. This should begin audio flow to the |
- // sink. Wait and confirm that three OnData() calls were made from the audio |
- // thread. |
- FakeMediaStreamAudioSink sink; |
- EXPECT_FALSE(sink.was_ended()); |
- track()->AddSink(&sink); |
- const int start_count = sink.num_on_data_calls(); |
- while (sink.num_on_data_calls() - start_count < 3) |
- base::PlatformThread::Sleep(TestTimeouts::tiny_timeout()); |
- |
- // Check that the audio parameters propagated to the track and sink. |
- const media::AudioParameters expected_params( |
- media::AudioParameters::AUDIO_PCM_LOW_LATENCY, media::CHANNEL_LAYOUT_MONO, |
- kSampleRate, 16, kBufferSize); |
- EXPECT_TRUE(expected_params.Equals(track()->GetOutputFormat())); |
- EXPECT_TRUE(expected_params.Equals(sink.params())); |
- |
- // Stop the track. Since this was the last track connected to the source, the |
- // source should automatically stop. In addition, the sink should receive a |
- // ReadyStateEnded notification. |
- track()->Stop(); |
- EXPECT_TRUE(source()->was_started()); |
- EXPECT_TRUE(source()->was_stopped()); |
- EXPECT_TRUE(sink.was_ended()); |
- |
- track()->RemoveSink(&sink); |
-} |
- |
-// Tests that "ended" tracks can be connected after the source has stopped. |
-TEST_F(MediaStreamAudioTest, ConnectTrackAfterSourceStopped) { |
- // Create the source, connect one track, and stop it. This should |
- // automatically stop the source. |
- blink_audio_source_.setExtraData(new FakeMediaStreamAudioSource()); |
- ASSERT_TRUE(source()); |
- EXPECT_TRUE(source()->ConnectToTrack(blink_audio_track_)); |
- track()->Stop(); |
- EXPECT_TRUE(source()->was_started()); |
- EXPECT_TRUE(source()->was_stopped()); |
- |
- // Now, connect another track. ConnectToTrack() will return false, but there |
- // should be a MediaStreamAudioTrack instance created and owned by the |
- // blink::WebMediaStreamTrack. |
- blink::WebMediaStreamTrack another_blink_track; |
- another_blink_track.initialize(blink_audio_source_.id(), blink_audio_source_); |
- EXPECT_FALSE(MediaStreamAudioTrack::From(another_blink_track)); |
- EXPECT_FALSE(source()->ConnectToTrack(another_blink_track)); |
- EXPECT_TRUE(MediaStreamAudioTrack::From(another_blink_track)); |
-} |
- |
-// Tests that a sink is immediately "ended" when connected to a stopped track. |
-TEST_F(MediaStreamAudioTest, AddSinkToStoppedTrack) { |
- // Create a track and stop it. Then, when adding a sink, the sink should get |
- // the ReadyStateEnded notification immediately. |
- MediaStreamAudioTrack track(true); |
- track.Stop(); |
- FakeMediaStreamAudioSink sink; |
- EXPECT_FALSE(sink.was_ended()); |
- track.AddSink(&sink); |
- EXPECT_TRUE(sink.was_ended()); |
- EXPECT_EQ(0, sink.num_on_data_calls()); |
- track.RemoveSink(&sink); |
-} |
- |
-// Tests that audio format changes at the source propagate to the track and |
-// sink. |
-TEST_F(MediaStreamAudioTest, FormatChangesPropagate) { |
- // Create a source, connect it to track, and connect the track to a |
- // sink. |
- blink_audio_source_.setExtraData(new FakeMediaStreamAudioSource()); |
- ASSERT_TRUE(source()); |
- EXPECT_TRUE(source()->ConnectToTrack(blink_audio_track_)); |
- ASSERT_TRUE(track()); |
- FakeMediaStreamAudioSink sink; |
- ASSERT_TRUE(!sink.params().IsValid()); |
- track()->AddSink(&sink); |
- |
- // Wait until valid parameters are propagated to the sink, and then confirm |
- // the parameters are correct at the track and the sink. |
- while (!sink.params().IsValid()) |
- base::PlatformThread::Sleep(TestTimeouts::tiny_timeout()); |
- const media::AudioParameters expected_params( |
- media::AudioParameters::AUDIO_PCM_LOW_LATENCY, media::CHANNEL_LAYOUT_MONO, |
- kSampleRate, 16, kBufferSize); |
- EXPECT_TRUE(expected_params.Equals(track()->GetOutputFormat())); |
- EXPECT_TRUE(expected_params.Equals(sink.params())); |
- |
- // Now, trigger a format change by doubling the buffer size. |
- source()->SetBufferSize(kBufferSize * 2); |
- |
- // Wait until the new buffer size propagates to the sink. |
- while (sink.params().frames_per_buffer() == kBufferSize) |
- base::PlatformThread::Sleep(TestTimeouts::tiny_timeout()); |
- EXPECT_EQ(kBufferSize * 2, track()->GetOutputFormat().frames_per_buffer()); |
- EXPECT_EQ(kBufferSize * 2, sink.params().frames_per_buffer()); |
- |
- track()->RemoveSink(&sink); |
-} |
- |
-// Tests that tracks deliver audio when enabled and silent audio when |
-// disabled. Whenever a track is enabled or disabled, the sink's |
-// OnEnabledChanged() method should be called. |
-TEST_F(MediaStreamAudioTest, EnableAndDisableTracks) { |
- // Create a source and connect it to track. |
- blink_audio_source_.setExtraData(new FakeMediaStreamAudioSource()); |
- ASSERT_TRUE(source()); |
- EXPECT_TRUE(source()->ConnectToTrack(blink_audio_track_)); |
- ASSERT_TRUE(track()); |
- |
- // Connect the track to a sink and expect the sink to be notified that the |
- // track is enabled. |
- FakeMediaStreamAudioSink sink; |
- EXPECT_TRUE(sink.is_audio_silent()); |
- EXPECT_EQ(FakeMediaStreamAudioSink::NO_ENABLE_NOTIFICATION, |
- sink.enable_state()); |
- track()->AddSink(&sink); |
- EXPECT_EQ(FakeMediaStreamAudioSink::WAS_ENABLED, sink.enable_state()); |
- |
- // Wait until non-silent audio reaches the sink. |
- while (sink.is_audio_silent()) |
- base::PlatformThread::Sleep(TestTimeouts::tiny_timeout()); |
- |
- // Now, disable the track and expect the sink to be notified. |
- track()->SetEnabled(false); |
- EXPECT_EQ(FakeMediaStreamAudioSink::WAS_DISABLED, sink.enable_state()); |
- |
- // Wait until silent audio reaches the sink. |
- while (!sink.is_audio_silent()) |
- base::PlatformThread::Sleep(TestTimeouts::tiny_timeout()); |
- |
- // Create a second track and a second sink, but this time the track starts out |
- // disabled. Expect the sink to be notified at the start that the track is |
- // disabled. |
- blink::WebMediaStreamTrack another_blink_track; |
- another_blink_track.initialize(blink_audio_source_.id(), blink_audio_source_); |
- EXPECT_TRUE(source()->ConnectToTrack(another_blink_track)); |
- MediaStreamAudioTrack::From(another_blink_track)->SetEnabled(false); |
- FakeMediaStreamAudioSink another_sink; |
- MediaStreamAudioTrack::From(another_blink_track)->AddSink(&another_sink); |
- EXPECT_EQ(FakeMediaStreamAudioSink::WAS_DISABLED, |
- another_sink.enable_state()); |
- |
- // Wait until OnData() is called on the second sink. Expect the audio to be |
- // silent. |
- const int start_count = another_sink.num_on_data_calls(); |
- while (another_sink.num_on_data_calls() == start_count) |
- base::PlatformThread::Sleep(TestTimeouts::tiny_timeout()); |
- EXPECT_TRUE(another_sink.is_audio_silent()); |
- |
- // Now, enable the second track and expect the second sink to be notified. |
- MediaStreamAudioTrack::From(another_blink_track)->SetEnabled(true); |
- EXPECT_EQ(FakeMediaStreamAudioSink::WAS_ENABLED, another_sink.enable_state()); |
- |
- // Wait until non-silent audio reaches the second sink. |
- while (another_sink.is_audio_silent()) |
- base::PlatformThread::Sleep(TestTimeouts::tiny_timeout()); |
- |
- // The first track and sink should not have been affected by changing the |
- // enabled state of the second track and sink. They should still be disabled, |
- // with silent audio being consumed at the sink. |
- EXPECT_EQ(FakeMediaStreamAudioSink::WAS_DISABLED, sink.enable_state()); |
- EXPECT_TRUE(sink.is_audio_silent()); |
- |
- MediaStreamAudioTrack::From(another_blink_track)->RemoveSink(&another_sink); |
- track()->RemoveSink(&sink); |
-} |
- |
-} // namespace content |