blob: 2261a12f249dc4fd201afaab6fb9a97542566402 [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 "content/renderer/media/webrtc/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/strings/utf_string_conversions.h"
#include "base/synchronization/waitable_event.h"
#include "base/test/scoped_task_environment.h"
#include "base/threading/thread_task_runner_handle.h"
#include "content/child/child_process.h"
#include "content/renderer/media/stream/media_stream_video_track.h"
#include "content/renderer/media/stream/mock_media_stream_video_sink.h"
#include "content/renderer/media/webrtc/mock_peer_connection_dependency_factory.h"
#include "content/renderer/media/webrtc/track_observer.h"
#include "media/base/video_frame.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/platform/scheduler/test/renderer_scheduler_test_support.h"
#include "third_party/blink/public/web/web_heap.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 content {
ACTION_P(RunClosure, closure) {
closure.Run();
}
class MediaStreamRemoteVideoSourceUnderTest
: public MediaStreamRemoteVideoSource {
public:
explicit MediaStreamRemoteVideoSourceUnderTest(
std::unique_ptr<TrackObserver> observer)
: MediaStreamRemoteVideoSource(std::move(observer)) {}
using MediaStreamRemoteVideoSource::SinkInterfaceForTesting;
using MediaStreamRemoteVideoSource::StartSourceImpl;
};
class MediaStreamRemoteVideoSourceTest
: public ::testing::Test {
public:
MediaStreamRemoteVideoSourceTest()
: scoped_task_environment_(
base::test::ScopedTaskEnvironment::MainThreadType::UI),
child_process_(new ChildProcess()),
mock_factory_(new MockPeerConnectionDependencyFactory()),
webrtc_video_track_(MockWebRtcVideoTrack::Create("test")),
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<TrackObserver> track_observer;
mock_factory_->GetWebRtcSignalingThread()->PostTask(
FROM_HERE,
base::BindOnce(
[](scoped_refptr<base::SingleThreadTaskRunner> main_thread,
webrtc::MediaStreamTrackInterface* webrtc_track,
std::unique_ptr<TrackObserver>* track_observer,
base::WaitableEvent* waitable_event) {
track_observer->reset(
new TrackObserver(main_thread, webrtc_track));
waitable_event->Signal();
},
main_thread, base::Unretained(webrtc_video_track_.get()),
base::Unretained(&track_observer),
base::Unretained(&waitable_event)));
waitable_event.Wait();
remote_source_ =
new MediaStreamRemoteVideoSourceUnderTest(std::move(track_observer));
webkit_source_.Initialize(blink::WebString::FromASCII("dummy_source_id"),
blink::WebMediaStreamSource::kTypeVideo,
blink::WebString::FromASCII("dummy_source_name"),
true /* remote */);
webkit_source_.SetPlatformSource(base::WrapUnique(remote_source_));
}
void TearDown() override {
remote_source_->OnSourceTerminated();
webkit_source_.Reset();
blink::WebHeap::CollectAllGarbageForTesting();
}
MediaStreamRemoteVideoSourceUnderTest* source() {
return remote_source_;
}
MediaStreamVideoTrack* CreateTrack() {
bool enabled = true;
return new MediaStreamVideoTrack(
source(),
base::Bind(&MediaStreamRemoteVideoSourceTest::OnTrackStarted,
base::Unretained(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_->GetWebRtcSignalingThread()->PostTask(
FROM_HERE, base::BindOnce(
[](MockWebRtcVideoTrack* video_track,
base::WaitableEvent* waitable_event) {
video_track->SetEnded();
waitable_event->Signal();
},
base::Unretained(static_cast<MockWebRtcVideoTrack*>(
webrtc_video_track_.get())),
base::Unretained(&waitable_event)));
waitable_event.Wait();
}
const blink::WebMediaStreamSource& webkit_source() const {
return webkit_source_;
}
private:
void OnTrackStarted(blink::WebPlatformMediaStreamSource* source,
blink::MediaStreamRequestResult result,
const blink::WebString& result_name) {
ASSERT_EQ(source, remote_source_);
if (result == blink::MEDIA_DEVICE_OK)
++number_of_successful_track_starts_;
else
++number_of_failed_track_starts_;
}
base::test::ScopedTaskEnvironment scoped_task_environment_;
std::unique_ptr<ChildProcess> child_process_;
std::unique_ptr<MockPeerConnectionDependencyFactory> mock_factory_;
scoped_refptr<webrtc::VideoTrackInterface> webrtc_video_track_;
// |remote_source_| is owned by |webkit_source_|.
MediaStreamRemoteVideoSourceUnderTest* remote_source_;
blink::WebMediaStreamSource webkit_source_;
int number_of_successful_track_starts_;
int number_of_failed_track_starts_;
};
TEST_F(MediaStreamRemoteVideoSourceTest, StartTrack) {
std::unique_ptr<MediaStreamVideoTrack> track(CreateTrack());
EXPECT_EQ(1, NumberOfSuccessConstraintsCallbacks());
MockMediaStreamVideoSink sink;
track->AddSink(&sink, sink.GetDeliverFrameCB(), false);
base::RunLoop run_loop;
base::Closure 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(buffer, webrtc::kVideoRotation_0, 1000));
run_loop.Run();
EXPECT_EQ(1, sink.number_of_frames());
track->RemoveSink(&sink);
}
TEST_F(MediaStreamRemoteVideoSourceTest, RemoteTrackStop) {
std::unique_ptr<MediaStreamVideoTrack> track(CreateTrack());
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<MediaStreamVideoTrack> track(CreateTrack());
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);
}
} // namespace content