Commit 77761f32 authored by Guido Urdaneta's avatar Guido Urdaneta Committed by Commit Bot

[BreakoutBox] Add MediaStreamVideoTrackUnderlyingSink

This CL adds an UnderlyingSinkBase implementation that
takes VideoFrames and sends them to a PushableMediaStreamVideoSource.

This is intended to be used by Raw Access Insertable Streams
(a.k.a. BreakoutBox)

Bug: 1142955
Change-Id: I0da46c1867e1ce745f69da7ea10be4d9a00a8606
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2514400
Commit-Queue: Guido Urdaneta <guidou@chromium.org>
Reviewed-by: default avatarHarald Alvestrand <hta@chromium.org>
Reviewed-by: default avatarKent Tamura <tkent@chromium.org>
Cr-Commit-Position: refs/heads/master@{#825037}
parent c977d8c7
...@@ -352,6 +352,7 @@ source_set("unit_tests") { ...@@ -352,6 +352,7 @@ source_set("unit_tests") {
"mediastream/media_stream_video_renderer_sink_test.cc", "mediastream/media_stream_video_renderer_sink_test.cc",
"mediastream/media_stream_video_source_test.cc", "mediastream/media_stream_video_source_test.cc",
"mediastream/media_stream_video_track_test.cc", "mediastream/media_stream_video_track_test.cc",
"mediastream/media_stream_video_track_underlying_sink_test.cc",
"mediastream/media_stream_video_track_underlying_source_test.cc", "mediastream/media_stream_video_track_underlying_source_test.cc",
"mediastream/mock_mojo_media_stream_dispatcher_host.cc", "mediastream/mock_mojo_media_stream_dispatcher_host.cc",
"mediastream/mock_mojo_media_stream_dispatcher_host.h", "mediastream/mock_mojo_media_stream_dispatcher_host.h",
......
...@@ -64,6 +64,8 @@ blink_modules_sources("mediastream") { ...@@ -64,6 +64,8 @@ blink_modules_sources("mediastream") {
"media_stream_video_source.cc", "media_stream_video_source.cc",
"media_stream_video_track.cc", "media_stream_video_track.cc",
"media_stream_video_track.h", "media_stream_video_track.h",
"media_stream_video_track_underlying_sink.cc",
"media_stream_video_track_underlying_sink.h",
"media_stream_video_track_underlying_source.cc", "media_stream_video_track_underlying_source.cc",
"media_stream_video_track_underlying_source.h", "media_stream_video_track_underlying_source.h",
"navigator_media_stream.cc", "navigator_media_stream.cc",
......
// Copyright 2020 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_video_track_underlying_sink.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_video_frame.h"
#include "third_party/blink/renderer/modules/mediastream/pushable_media_stream_video_source.h"
#include "third_party/blink/renderer/platform/bindings/exception_state.h"
namespace blink {
MediaStreamVideoTrackUnderlyingSink::MediaStreamVideoTrackUnderlyingSink(
PushableMediaStreamVideoSource* source)
: source_(source) {}
ScriptPromise MediaStreamVideoTrackUnderlyingSink::start(
ScriptState* script_state,
WritableStreamDefaultController* controller,
ExceptionState& exception_state) {
return ScriptPromise::CastUndefined(script_state);
}
ScriptPromise MediaStreamVideoTrackUnderlyingSink::write(
ScriptState* script_state,
ScriptValue chunk,
WritableStreamDefaultController* controller,
ExceptionState& exception_state) {
VideoFrame* video_frame = V8VideoFrame::ToImplWithTypeCheck(
script_state->GetIsolate(), chunk.V8Value());
if (!video_frame) {
exception_state.ThrowTypeError("Invalid video frame.");
return ScriptPromise();
}
if (!source_->running()) {
exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
"Stream closed");
return ScriptPromise();
}
base::TimeTicks estimated_capture_time = base::TimeTicks::Now();
source_->PushFrame(video_frame->frame(), estimated_capture_time);
return ScriptPromise::CastUndefined(script_state);
}
ScriptPromise MediaStreamVideoTrackUnderlyingSink::abort(
ScriptState* script_state,
ScriptValue reason,
ExceptionState& exception_state) {
source_->StopSource();
return ScriptPromise::CastUndefined(script_state);
}
ScriptPromise MediaStreamVideoTrackUnderlyingSink::close(
ScriptState* script_state,
ExceptionState& exception_state) {
source_->StopSource();
return ScriptPromise::CastUndefined(script_state);
}
} // namespace blink
// Copyright 2020 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.
#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_MEDIASTREAM_MEDIA_STREAM_VIDEO_TRACK_UNDERLYING_SINK_H_
#define THIRD_PARTY_BLINK_RENDERER_MODULES_MEDIASTREAM_MEDIA_STREAM_VIDEO_TRACK_UNDERLYING_SINK_H_
#include "third_party/blink/renderer/core/streams/underlying_sink_base.h"
#include "third_party/blink/renderer/modules/modules_export.h"
namespace blink {
class PushableMediaStreamVideoSource;
class MODULES_EXPORT MediaStreamVideoTrackUnderlyingSink
: public UnderlyingSinkBase {
public:
// |source| must outlive this MediaStreamVideoTrackUnderlyingSink.
explicit MediaStreamVideoTrackUnderlyingSink(
PushableMediaStreamVideoSource* source);
// UnderlyingSinkBase overrides.
ScriptPromise start(ScriptState* script_state,
WritableStreamDefaultController* controller,
ExceptionState& exception_state) override;
ScriptPromise write(ScriptState* script_state,
ScriptValue chunk,
WritableStreamDefaultController* controller,
ExceptionState& exception_state) override;
ScriptPromise abort(ScriptState* script_state,
ScriptValue reason,
ExceptionState& exception_state) override;
ScriptPromise close(ScriptState* script_state,
ExceptionState& exception_state) override;
private:
PushableMediaStreamVideoSource* source_;
};
} // namespace blink
#endif // THIRD_PARTY_BLINK_RENDERER_MODULES_MEDIASTREAM_MEDIA_STREAM_VIDEO_TRACK_UNDERLYING_SINK_H_
// Copyright 2020 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_video_track_underlying_sink.h"
#include "base/memory/scoped_refptr.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.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/bindings/modules/v8/v8_video_frame.h"
#include "third_party/blink/renderer/core/streams/writable_stream.h"
#include "third_party/blink/renderer/core/streams/writable_stream_default_writer.h"
#include "third_party/blink/renderer/modules/mediastream/media_stream_video_track.h"
#include "third_party/blink/renderer/modules/mediastream/mock_media_stream_video_sink.h"
#include "third_party/blink/renderer/modules/mediastream/pushable_media_stream_video_source.h"
#include "third_party/blink/renderer/platform/bindings/exception_state.h"
#include "third_party/blink/renderer/platform/mediastream/media_stream_source.h"
#include "third_party/blink/renderer/platform/testing/io_task_runner_testing_platform_support.h"
using testing::_;
namespace blink {
class MediaStreamVideoTrackUnderlyingSinkTest : public testing::Test {
public:
MediaStreamVideoTrackUnderlyingSinkTest() {
pushable_video_source_ = new PushableMediaStreamVideoSource();
media_stream_source_ = MakeGarbageCollected<MediaStreamSource>(
"dummy_source_id", MediaStreamSource::kTypeVideo, "dummy_source_name",
/*remote=*/false);
media_stream_source_->SetPlatformSource(
base::WrapUnique(pushable_video_source_));
}
~MediaStreamVideoTrackUnderlyingSinkTest() override {
platform_->RunUntilIdle();
WebHeap::CollectAllGarbageForTesting();
}
MediaStreamVideoTrackUnderlyingSink* CreateUnderlyingSink(
ScriptState* script_state) {
return MakeGarbageCollected<MediaStreamVideoTrackUnderlyingSink>(
pushable_video_source_);
}
WebMediaStreamTrack CreateTrack() {
return MediaStreamVideoTrack::CreateVideoTrack(
pushable_video_source_,
MediaStreamVideoSource::ConstraintsOnceCallback(),
/*enabled=*/true);
}
ScriptValue CreateVideoFrameChunk(ScriptState* script_state) {
const scoped_refptr<media::VideoFrame> media_frame =
media::VideoFrame::CreateBlackFrame(gfx::Size(100, 50));
VideoFrame* video_frame =
MakeGarbageCollected<VideoFrame>(std::move(media_frame));
return ScriptValue(script_state->GetIsolate(),
ToV8(video_frame, script_state->GetContext()->Global(),
script_state->GetIsolate()));
}
protected:
ScopedTestingPlatformSupport<IOTaskRunnerTestingPlatformSupport> platform_;
Persistent<MediaStreamSource> media_stream_source_;
PushableMediaStreamVideoSource* pushable_video_source_;
};
TEST_F(MediaStreamVideoTrackUnderlyingSinkTest,
WriteToStreamForwardsToMediaStreamSink) {
V8TestingScope v8_scope;
ScriptState* script_state = v8_scope.GetScriptState();
auto* underlying_sink = CreateUnderlyingSink(script_state);
auto* writable_stream = WritableStream::CreateWithCountQueueingStrategy(
script_state, underlying_sink, 1u);
auto track = CreateTrack();
MockMediaStreamVideoSink media_stream_video_sink;
media_stream_video_sink.ConnectToTrack(track);
NonThrowableExceptionState exception_state;
auto* writer = writable_stream->getWriter(script_state, exception_state);
EXPECT_CALL(media_stream_video_sink, OnVideoFrame(_));
ScriptPromiseTester write_tester(
script_state,
writer->write(script_state, CreateVideoFrameChunk(script_state),
exception_state));
EXPECT_FALSE(write_tester.IsFulfilled());
writer->releaseLock(script_state);
ScriptPromiseTester close_tester(
script_state, writable_stream->close(script_state, exception_state));
close_tester.WaitUntilSettled();
// Writing to the sink after the stream closes should fail.
DummyExceptionStateForTesting dummy_exception_state;
underlying_sink->write(script_state, CreateVideoFrameChunk(script_state),
nullptr, dummy_exception_state);
EXPECT_TRUE(dummy_exception_state.HadException());
EXPECT_EQ(dummy_exception_state.Code(),
static_cast<ExceptionCode>(DOMExceptionCode::kInvalidStateError));
}
TEST_F(MediaStreamVideoTrackUnderlyingSinkTest, WriteInvalidDataFails) {
V8TestingScope v8_scope;
ScriptState* script_state = v8_scope.GetScriptState();
auto* sink = CreateUnderlyingSink(script_state);
ScriptValue v8_integer = ScriptValue::From(script_state, 0);
// Writing something that is not a VideoFrame to the sink should fail.
DummyExceptionStateForTesting dummy_exception_state;
sink->write(script_state, v8_integer, nullptr, dummy_exception_state);
EXPECT_TRUE(dummy_exception_state.HadException());
}
TEST_F(MediaStreamVideoTrackUnderlyingSinkTest, WriteToAbortedSinkFails) {
V8TestingScope v8_scope;
ScriptState* script_state = v8_scope.GetScriptState();
auto* underlying_sink = CreateUnderlyingSink(script_state);
auto* writable_stream = WritableStream::CreateWithCountQueueingStrategy(
script_state, underlying_sink, 1u);
NonThrowableExceptionState exception_state;
ScriptPromiseTester abort_tester(
script_state, writable_stream->abort(script_state, exception_state));
abort_tester.WaitUntilSettled();
// Writing to the sink after the stream closes should fail.
DummyExceptionStateForTesting dummy_exception_state;
underlying_sink->write(script_state, CreateVideoFrameChunk(script_state),
nullptr, dummy_exception_state);
EXPECT_TRUE(dummy_exception_state.HadException());
EXPECT_EQ(dummy_exception_state.Code(),
static_cast<ExceptionCode>(DOMExceptionCode::kInvalidStateError));
}
} // namespace blink
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment