blob: 9ea0b426f6e1c6223f52245755b4dc35fdd2e9c2 [file] [log] [blame]
// Copyright 2014 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/peerconnection/media_stream_remote_video_source.h"
#include <memory>
#include <utility>
#include "base/bind.h"
#include "base/run_loop.h"
#include "base/single_thread_task_runner.h"
#include "base/synchronization/waitable_event.h"
#include "media/base/video_frame.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/mojom/mediastream/media_stream.mojom-blink.h"
#include "third_party/blink/public/platform/scheduler/test/renderer_scheduler_test_support.h"
#include "third_party/blink/public/web/modules/mediastream/media_stream_video_source.h"
#include "third_party/blink/public/web/modules/mediastream/media_stream_video_track.h"
#include "third_party/blink/public/web/web_heap.h"
#include "third_party/blink/renderer/modules/mediastream/mock_media_stream_video_sink.h"
#include "third_party/blink/renderer/modules/peerconnection/adapters/web_rtc_cross_thread_copier.h"
#include "third_party/blink/renderer/modules/peerconnection/mock_peer_connection_dependency_factory.h"
#include "third_party/blink/renderer/platform/testing/io_task_runner_testing_platform_support.h"
#include "third_party/blink/renderer/platform/webrtc/track_observer.h"
#include "third_party/blink/renderer/platform/wtf/cross_thread_functional.h"
#include "third_party/webrtc/api/video/color_space.h"
#include "third_party/webrtc/api/video/i420_buffer.h"
#include "ui/gfx/color_space.h"
namespace blink {
ACTION_P(RunClosure, closure) {
closure.Run();
}
class MediaStreamRemoteVideoSourceUnderTest
: public blink::MediaStreamRemoteVideoSource {
public:
explicit MediaStreamRemoteVideoSourceUnderTest(
std::unique_ptr<blink::TrackObserver> observer)
: MediaStreamRemoteVideoSource(std::move(observer)) {}
using MediaStreamRemoteVideoSource::EncodedSinkInterfaceForTesting;
using MediaStreamRemoteVideoSource::SinkInterfaceForTesting;
using MediaStreamRemoteVideoSource::StartSourceImpl;
};
class MediaStreamRemoteVideoSourceTest : public ::testing::Test {
public:
MediaStreamRemoteVideoSourceTest()
: mock_factory_(new blink::MockPeerConnectionDependencyFactory()),
webrtc_video_source_(blink::MockWebRtcVideoTrackSource::Create(
/*supports_encoded_output=*/true)),
webrtc_video_track_(
blink::MockWebRtcVideoTrack::Create("test", webrtc_video_source_)),
remote_source_(nullptr),
number_of_successful_track_starts_(0),
number_of_failed_track_starts_(0) {}
void SetUp() override {
scoped_refptr<base::SingleThreadTaskRunner> main_thread =
blink::scheduler::GetSingleThreadTaskRunnerForTesting();
base::WaitableEvent waitable_event(
base::WaitableEvent::ResetPolicy::MANUAL,
base::WaitableEvent::InitialState::NOT_SIGNALED);
std::unique_ptr<blink::TrackObserver> track_observer;
mock_factory_->GetWebRtcSignalingTaskRunner()->PostTask(
FROM_HERE,
ConvertToBaseOnceCallback(CrossThreadBindOnce(
[](scoped_refptr<base::SingleThreadTaskRunner> main_thread,
webrtc::MediaStreamTrackInterface* webrtc_track,
std::unique_ptr<blink::TrackObserver>* track_observer,
base::WaitableEvent* waitable_event) {
track_observer->reset(
new blink::TrackObserver(main_thread, webrtc_track));
waitable_event->Signal();
},
main_thread, CrossThreadUnretained(webrtc_video_track_.get()),
CrossThreadUnretained(&track_observer),
CrossThreadUnretained(&waitable_event))));
waitable_event.Wait();
remote_source_ =
new MediaStreamRemoteVideoSourceUnderTest(std::move(track_observer));
web_source_.Initialize(blink::WebString::FromASCII("dummy_source_id"),
blink::WebMediaStreamSource::kTypeVideo,
blink::WebString::FromASCII("dummy_source_name"),
true /* remote */);
web_source_.SetPlatformSource(base::WrapUnique(remote_source_));
}
void TearDown() override {
remote_source_->OnSourceTerminated();
web_source_.Reset();
blink::WebHeap::CollectAllGarbageForTesting();
}
MediaStreamRemoteVideoSourceUnderTest* source() { return remote_source_; }
blink::MediaStreamVideoTrack* CreateTrack() {
bool enabled = true;
return new blink::MediaStreamVideoTrack(
source(),
ConvertToBaseOnceCallback(CrossThreadBindOnce(
&MediaStreamRemoteVideoSourceTest::OnTrackStarted,
CrossThreadUnretained(this))),
enabled);
}
int NumberOfSuccessConstraintsCallbacks() const {
return number_of_successful_track_starts_;
}
int NumberOfFailedConstraintsCallbacks() const {
return number_of_failed_track_starts_;
}
void StopWebRtcTrack() {
base::WaitableEvent waitable_event(
base::WaitableEvent::ResetPolicy::MANUAL,
base::WaitableEvent::InitialState::NOT_SIGNALED);
mock_factory_->GetWebRtcSignalingTaskRunner()->PostTask(
FROM_HERE,
ConvertToBaseOnceCallback(CrossThreadBindOnce(
[](blink::MockWebRtcVideoTrack* video_track,
base::WaitableEvent* waitable_event) {
video_track->SetEnded();
waitable_event->Signal();
},
CrossThreadUnretained(static_cast<blink::MockWebRtcVideoTrack*>(
webrtc_video_track_.get())),
CrossThreadUnretained(&waitable_event))));
waitable_event.Wait();
}
const blink::WebMediaStreamSource& webkit_source() const {
return web_source_;
}
private:
void OnTrackStarted(blink::WebPlatformMediaStreamSource* source,
blink::mojom::MediaStreamRequestResult result,
const blink::WebString& result_name) {
ASSERT_EQ(source, remote_source_);
if (result == blink::mojom::MediaStreamRequestResult::OK)
++number_of_successful_track_starts_;
else
++number_of_failed_track_starts_;
}
ScopedTestingPlatformSupport<IOTaskRunnerTestingPlatformSupport> platform_;
std::unique_ptr<blink::MockPeerConnectionDependencyFactory> mock_factory_;
scoped_refptr<webrtc::VideoTrackSourceInterface> webrtc_video_source_;
scoped_refptr<webrtc::VideoTrackInterface> webrtc_video_track_;
// |remote_source_| is owned by |web_source_|.
MediaStreamRemoteVideoSourceUnderTest* remote_source_;
blink::WebMediaStreamSource web_source_;
int number_of_successful_track_starts_;
int number_of_failed_track_starts_;
};
TEST_F(MediaStreamRemoteVideoSourceTest, StartTrack) {
std::unique_ptr<blink::MediaStreamVideoTrack> track(CreateTrack());
EXPECT_EQ(1, NumberOfSuccessConstraintsCallbacks());
blink::MockMediaStreamVideoSink sink;
track->AddSink(&sink, sink.GetDeliverFrameCB(), false);
base::RunLoop run_loop;
base::RepeatingClosure quit_closure = run_loop.QuitClosure();
EXPECT_CALL(sink, OnVideoFrame())
.WillOnce(RunClosure(std::move(quit_closure)));
rtc::scoped_refptr<webrtc::I420Buffer> buffer(
new rtc::RefCountedObject<webrtc::I420Buffer>(320, 240));
webrtc::I420Buffer::SetBlack(buffer);
source()->SinkInterfaceForTesting()->OnFrame(
webrtc::VideoFrame::Builder()
.set_video_frame_buffer(buffer)
.set_rotation(webrtc::kVideoRotation_0)
.set_timestamp_us(1000)
.build());
run_loop.Run();
EXPECT_EQ(1, sink.number_of_frames());
track->RemoveSink(&sink);
}
TEST_F(MediaStreamRemoteVideoSourceTest, RemoteTrackStop) {
std::unique_ptr<blink::MediaStreamVideoTrack> track(CreateTrack());
blink::MockMediaStreamVideoSink sink;
track->AddSink(&sink, sink.GetDeliverFrameCB(), false);
EXPECT_EQ(blink::WebMediaStreamSource::kReadyStateLive, sink.state());
EXPECT_EQ(blink::WebMediaStreamSource::kReadyStateLive,
webkit_source().GetReadyState());
StopWebRtcTrack();
base::RunLoop().RunUntilIdle();
EXPECT_EQ(blink::WebMediaStreamSource::kReadyStateEnded,
webkit_source().GetReadyState());
EXPECT_EQ(blink::WebMediaStreamSource::kReadyStateEnded, sink.state());
track->RemoveSink(&sink);
}
TEST_F(MediaStreamRemoteVideoSourceTest, PreservesColorSpace) {
std::unique_ptr<blink::MediaStreamVideoTrack> track(CreateTrack());
blink::MockMediaStreamVideoSink sink;
track->AddSink(&sink, sink.GetDeliverFrameCB(), false);
base::RunLoop run_loop;
EXPECT_CALL(sink, OnVideoFrame())
.WillOnce(RunClosure(run_loop.QuitClosure()));
rtc::scoped_refptr<webrtc::I420Buffer> buffer(
new rtc::RefCountedObject<webrtc::I420Buffer>(320, 240));
webrtc::ColorSpace kColorSpace(webrtc::ColorSpace::PrimaryID::kSMPTE240M,
webrtc::ColorSpace::TransferID::kSMPTE240M,
webrtc::ColorSpace::MatrixID::kSMPTE240M,
webrtc::ColorSpace::RangeID::kLimited);
const webrtc::VideoFrame& input_frame =
webrtc::VideoFrame::Builder()
.set_video_frame_buffer(buffer)
.set_timestamp_ms(0)
.set_rotation(webrtc::kVideoRotation_0)
.set_color_space(kColorSpace)
.build();
source()->SinkInterfaceForTesting()->OnFrame(input_frame);
run_loop.Run();
EXPECT_EQ(1, sink.number_of_frames());
scoped_refptr<media::VideoFrame> output_frame = sink.last_frame();
EXPECT_TRUE(output_frame);
EXPECT_TRUE(output_frame->ColorSpace() ==
gfx::ColorSpace(gfx::ColorSpace::PrimaryID::SMPTE240M,
gfx::ColorSpace::TransferID::SMPTE240M,
gfx::ColorSpace::MatrixID::SMPTE240M,
gfx::ColorSpace::RangeID::LIMITED));
track->RemoveSink(&sink);
}
class TestEncodedVideoFrame : public webrtc::RecordableEncodedFrame {
public:
rtc::scoped_refptr<const webrtc::EncodedImageBufferInterface> encoded_buffer()
const override {
return nullptr;
}
absl::optional<webrtc::ColorSpace> color_space() const override {
return absl::nullopt;
}
webrtc::VideoCodecType codec() const override {
return webrtc::kVideoCodecVP8;
}
bool is_key_frame() const override { return true; }
EncodedResolution resolution() const override {
return EncodedResolution{0, 0};
}
webrtc::Timestamp render_time() const override {
return webrtc::Timestamp::ms(0);
}
};
TEST_F(MediaStreamRemoteVideoSourceTest, ForwardsEncodedVideoFrames) {
std::unique_ptr<blink::MediaStreamVideoTrack> track(CreateTrack());
blink::MockMediaStreamVideoSink sink;
track->AddEncodedSink(&sink, sink.GetDeliverEncodedVideoFrameCB());
base::RunLoop run_loop;
base::RepeatingClosure quit_closure = run_loop.QuitClosure();
EXPECT_CALL(sink, OnEncodedVideoFrame())
.WillOnce(RunClosure(std::move(quit_closure)));
source()->EncodedSinkInterfaceForTesting()->OnFrame(TestEncodedVideoFrame());
run_loop.Run();
track->RemoveEncodedSink(&sink);
}
} // namespace blink