diff options
Diffstat (limited to 'chromium/third_party/blink/renderer/modules/mediastream/media_stream_audio_track_underlying_source_test.cc')
-rw-r--r-- | chromium/third_party/blink/renderer/modules/mediastream/media_stream_audio_track_underlying_source_test.cc | 245 |
1 files changed, 245 insertions, 0 deletions
diff --git a/chromium/third_party/blink/renderer/modules/mediastream/media_stream_audio_track_underlying_source_test.cc b/chromium/third_party/blink/renderer/modules/mediastream/media_stream_audio_track_underlying_source_test.cc new file mode 100644 index 00000000000..16a78179027 --- /dev/null +++ b/chromium/third_party/blink/renderer/modules/mediastream/media_stream_audio_track_underlying_source_test.cc @@ -0,0 +1,245 @@ +// Copyright 2021 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 "third_party/blink/renderer/modules/mediastream/media_stream_audio_track_underlying_source.h" + +#include "base/run_loop.h" +#include "base/test/gmock_callback_support.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/blink/public/platform/modules/mediastream/web_media_stream_track.h" +#include "third_party/blink/public/web/web_heap.h" +#include "third_party/blink/renderer/bindings/core/v8/script_promise_tester.h" +#include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_testing.h" +#include "third_party/blink/renderer/core/execution_context/execution_context.h" +#include "third_party/blink/renderer/core/streams/readable_stream.h" +#include "third_party/blink/renderer/core/streams/readable_stream_default_controller_with_script_scope.h" +#include "third_party/blink/renderer/modules/mediastream/media_stream_track.h" +#include "third_party/blink/renderer/modules/mediastream/mock_media_stream_audio_sink.h" +#include "third_party/blink/renderer/modules/mediastream/pushable_media_stream_audio_source.h" +#include "third_party/blink/renderer/modules/webcodecs/audio_frame_serialization_data.h" +#include "third_party/blink/renderer/platform/bindings/exception_state.h" +#include "third_party/blink/renderer/platform/mediastream/media_stream_audio_track.h" +#include "third_party/blink/renderer/platform/testing/io_task_runner_testing_platform_support.h" +#include "third_party/blink/renderer/platform/testing/testing_platform_support.h" + +using testing::_; +using testing::AnyNumber; + +namespace blink { + +class MediaStreamAudioTrackUnderlyingSourceTest : public testing::Test { + public: + MediaStreamAudioTrackUnderlyingSourceTest() + : media_stream_source_(MakeGarbageCollected<MediaStreamSource>( + "dummy_source_id", + MediaStreamSource::kTypeAudio, + "dummy_source_name", + false /* remote */)), + pushable_audio_source_(new PushableMediaStreamAudioSource( + Thread::MainThread()->GetTaskRunner(), + Platform::Current()->GetIOTaskRunner())) { + media_stream_source_->SetPlatformSource( + base::WrapUnique(pushable_audio_source_)); + + component_ = MakeGarbageCollected<MediaStreamComponent>( + String::FromUTF8("audio_track"), media_stream_source_); + pushable_audio_source_->ConnectToTrack(component_); + } + + ~MediaStreamAudioTrackUnderlyingSourceTest() override { + platform_->RunUntilIdle(); + component_ = nullptr; + media_stream_source_ = nullptr; + WebHeap::CollectAllGarbageForTesting(); + } + + MediaStreamComponent* CreateTrack(ExecutionContext* execution_context) { + return MakeGarbageCollected<MediaStreamTrack>(execution_context, component_) + ->Component(); + } + + MediaStreamAudioTrackUnderlyingSource* CreateSource(ScriptState* script_state, + wtf_size_t buffer_size) { + MediaStreamComponent* track = + MakeGarbageCollected<MediaStreamTrack>( + ExecutionContext::From(script_state), component_) + ->Component(); + return MakeGarbageCollected<MediaStreamAudioTrackUnderlyingSource>( + script_state, track, buffer_size); + } + + MediaStreamAudioTrackUnderlyingSource* CreateSource( + ScriptState* script_state) { + return CreateSource(script_state, 1u); + } + + protected: + void PushFrame( + const base::Optional<base::TimeDelta>& timestamp = base::nullopt) { + auto data = AudioFrameSerializationData::Wrap( + media::AudioBus::Create(/*channels=*/2, /*frames=*/10), + /*sample_rate=*/8000, + timestamp.value_or(base::TimeDelta::FromSeconds(1))); + pushable_audio_source_->PushAudioData(std::move(data)); + platform_->RunUntilIdle(); + } + + ScopedTestingPlatformSupport<IOTaskRunnerTestingPlatformSupport> platform_; + Persistent<MediaStreamSource> media_stream_source_; + Persistent<MediaStreamComponent> component_; + PushableMediaStreamAudioSource* const pushable_audio_source_; +}; + +TEST_F(MediaStreamAudioTrackUnderlyingSourceTest, + AudioFrameFlowsThroughStreamAndCloses) { + V8TestingScope v8_scope; + ScriptState* script_state = v8_scope.GetScriptState(); + auto* source = CreateSource(script_state); + auto* stream = + ReadableStream::CreateWithCountQueueingStrategy(script_state, source, 0); + + NonThrowableExceptionState exception_state; + auto* reader = + stream->GetDefaultReaderForTesting(script_state, exception_state); + + ScriptPromiseTester read_tester(script_state, + reader->read(script_state, exception_state)); + EXPECT_FALSE(read_tester.IsFulfilled()); + PushFrame(); + read_tester.WaitUntilSettled(); + EXPECT_TRUE(read_tester.IsFulfilled()); + + source->Close(); +} + +TEST_F(MediaStreamAudioTrackUnderlyingSourceTest, + CancelStreamDisconnectsFromTrack) { + V8TestingScope v8_scope; + ScriptState* script_state = v8_scope.GetScriptState(); + auto* source = CreateSource(script_state); + auto* stream = + ReadableStream::CreateWithCountQueueingStrategy(script_state, source, 0); + + // The stream is connected to a sink. + EXPECT_TRUE(source->Track()); + + NonThrowableExceptionState exception_state; + stream->cancel(script_state, exception_state); + + // Canceling the stream disconnects it from the track. + EXPECT_FALSE(source->Track()); +} + +// TODO(crbug.com/1174118): Fix and re-enable. +TEST_F(MediaStreamAudioTrackUnderlyingSourceTest, + DISABLED_DropOldFramesWhenQueueIsFull) { + V8TestingScope v8_scope; + ScriptState* script_state = v8_scope.GetScriptState(); + const wtf_size_t buffer_size = 5; + auto* source = CreateSource(script_state, buffer_size); + EXPECT_EQ(source->MaxQueueSize(), buffer_size); + // Create a stream to ensure there is a controller associated to the source. + ReadableStream::CreateWithCountQueueingStrategy(script_state, source, 0); + + // Add a sink to the track to make it possible to wait until a pushed frame + // is delivered to sinks, including |source|, which is a sink of the track. + MockMediaStreamAudioSink mock_sink; + WebMediaStreamTrack track(source->Track()); + WebMediaStreamAudioSink::AddToAudioTrack(&mock_sink, track); + + auto push_frame_sync = [&mock_sink, this](const base::TimeDelta timestamp) { + base::RunLoop sink_loop; + EXPECT_CALL(mock_sink, OnData(_, _)) + .WillOnce(base::test::RunOnceClosure(sink_loop.QuitClosure())); + PushFrame(timestamp); + sink_loop.Run(); + }; + + const auto& queue = source->QueueForTesting(); + for (wtf_size_t i = 0; i < buffer_size; ++i) { + EXPECT_EQ(queue.size(), i); + base::TimeDelta timestamp = base::TimeDelta::FromSeconds(i); + push_frame_sync(timestamp); + EXPECT_EQ(queue.back()->timestamp(), timestamp); + EXPECT_EQ(queue.front()->timestamp(), base::TimeDelta()); + } + + // Push another frame while the queue is full. + EXPECT_EQ(queue.size(), buffer_size); + push_frame_sync(base::TimeDelta::FromSeconds(buffer_size)); + + // Since the queue was full, the oldest frame from the queue should have been + // dropped. + EXPECT_EQ(queue.size(), buffer_size); + EXPECT_EQ(queue.back()->timestamp(), + base::TimeDelta::FromSeconds(buffer_size)); + EXPECT_EQ(queue.front()->timestamp(), base::TimeDelta::FromSeconds(1)); + + // Pulling with frames in the queue should move the oldest frame in the queue + // to the stream's controller. + EXPECT_EQ(source->DesiredSizeForTesting(), 0); + EXPECT_FALSE(source->IsPendingPullForTesting()); + source->pull(script_state); + EXPECT_EQ(source->DesiredSizeForTesting(), -1); + EXPECT_FALSE(source->IsPendingPullForTesting()); + EXPECT_EQ(queue.size(), buffer_size - 1); + EXPECT_EQ(queue.front()->timestamp(), base::TimeDelta::FromSeconds(2)); + + source->Close(); + EXPECT_EQ(queue.size(), 0u); + + WebMediaStreamAudioSink::RemoveFromAudioTrack(&mock_sink, track); +} + +TEST_F(MediaStreamAudioTrackUnderlyingSourceTest, + BypassQueueAfterPullWithEmptyBuffer) { + V8TestingScope v8_scope; + ScriptState* script_state = v8_scope.GetScriptState(); + auto* source = CreateSource(script_state); + // Create a stream to ensure there is a controller associated to the source. + ReadableStream::CreateWithCountQueueingStrategy(script_state, source, 0); + + MockMediaStreamAudioSink mock_sink; + WebMediaStreamTrack track(source->Track()); + WebMediaStreamAudioSink::AddToAudioTrack(&mock_sink, track); + + auto push_frame_sync = [&mock_sink, this]() { + base::RunLoop sink_loop; + EXPECT_CALL(mock_sink, OnData(_, _)) + .WillOnce(base::test::RunOnceClosure(sink_loop.QuitClosure())); + PushFrame(); + sink_loop.Run(); + }; + + // At first, the queue is empty and the desired size is empty as well. + EXPECT_TRUE(source->QueueForTesting().empty()); + EXPECT_EQ(source->DesiredSizeForTesting(), 0); + EXPECT_FALSE(source->IsPendingPullForTesting()); + + source->pull(script_state); + EXPECT_TRUE(source->QueueForTesting().empty()); + EXPECT_EQ(source->DesiredSizeForTesting(), 0); + EXPECT_TRUE(source->IsPendingPullForTesting()); + + push_frame_sync(); + // Since a pull was pending, the frame is put directly in the stream + // controller, bypassing the source queue. + EXPECT_TRUE(source->QueueForTesting().empty()); + EXPECT_EQ(source->DesiredSizeForTesting(), -1); + EXPECT_FALSE(source->IsPendingPullForTesting()); + + source->Close(); + WebMediaStreamAudioSink::RemoveFromAudioTrack(&mock_sink, track); +} + +TEST_F(MediaStreamAudioTrackUnderlyingSourceTest, QueueSizeCannotBeZero) { + V8TestingScope v8_scope; + ScriptState* script_state = v8_scope.GetScriptState(); + auto* source = CreateSource(script_state, 0u); + // Queue size is always at least 1, even if 0 is requested. + EXPECT_EQ(source->MaxQueueSize(), 1u); + source->Close(); +} + +} // namespace blink |