blob: a7ea3f5b2cbf9dfe85b1300e968824122369d8d2 [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 <stdint.h>
#include <utility>
#include "base/callback.h"
#include "base/memory/ptr_util.h"
#include "base/run_loop.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/bind.h"
#include "base/test/gmock_callback_support.h"
#include "base/threading/thread_checker.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/blink/renderer/modules/mediastream/media_stream_video_track.h"
#include "third_party/blink/renderer/modules/mediastream/mock_encoded_video_frame.h"
#include "third_party/blink/renderer/modules/mediastream/mock_media_stream_video_sink.h"
#include "third_party/blink/renderer/modules/mediastream/mock_media_stream_video_source.h"
#include "third_party/blink/renderer/modules/mediastream/video_track_adapter_settings.h"
#include "third_party/blink/renderer/modules/peerconnection/media_stream_video_webrtc_sink.h"
#include "third_party/blink/renderer/modules/peerconnection/mock_peer_connection_dependency_factory.h"
#include "third_party/blink/renderer/platform/mediastream/media_stream_component.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"
#include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
#include "third_party/blink/renderer/platform/wtf/functional.h"
namespace blink {
// To avoid symbol collisions in jumbo builds.
namespace media_stream_video_track_test {
using base::test::RunOnceClosure;
using ::testing::InSequence;
using ::testing::Invoke;
using ::testing::Mock;
using ::testing::Return;
using ::testing::Values;
using ContentHintType = WebMediaStreamTrack::ContentHintType;
const uint8_t kBlackValue = 0x00;
const uint8_t kColorValue = 0xAB;
const int kMockSourceWidth = 640;
const int kMockSourceHeight = 480;
const double kMinFrameRate = 30.0;
class MediaStreamVideoTrackTest
: public testing::TestWithParam<ContentHintType> {
public:
MediaStreamVideoTrackTest() : mock_source_(nullptr), source_started_(false) {}
~MediaStreamVideoTrackTest() override {}
void TearDown() override {
source_ = nullptr;
WebHeap::CollectAllGarbageForTesting();
}
void DeliverVideoFrameAndWaitForRenderer(
scoped_refptr<media::VideoFrame> frame,
MockMediaStreamVideoSink* sink) {
base::RunLoop run_loop;
base::RepeatingClosure quit_closure = run_loop.QuitClosure();
EXPECT_CALL(*sink, OnVideoFrame)
.WillOnce(RunOnceClosure(std::move(quit_closure)));
mock_source()->DeliverVideoFrame(std::move(frame));
run_loop.Run();
}
void DeliverEncodedVideoFrameAndWait(scoped_refptr<EncodedVideoFrame> frame,
MockMediaStreamVideoSink* sink) {
base::RunLoop run_loop;
base::RepeatingClosure quit_closure = run_loop.QuitClosure();
EXPECT_CALL(*sink, OnEncodedVideoFrame)
.WillOnce(
Invoke([&](base::TimeTicks) { std::move(quit_closure).Run(); }));
mock_source()->DeliverEncodedVideoFrame(frame);
run_loop.Run();
}
void DeliverDefaultSizeVideoFrameAndWaitForRenderer(
MockMediaStreamVideoSink* sink) {
const scoped_refptr<media::VideoFrame> frame =
media::VideoFrame::CreateColorFrame(
gfx::Size(MediaStreamVideoSource::kDefaultWidth,
MediaStreamVideoSource::kDefaultHeight),
kColorValue, kColorValue, kColorValue, base::TimeDelta());
DeliverVideoFrameAndWaitForRenderer(frame, sink);
}
protected:
virtual void InitializeSource() {
source_ = nullptr;
mock_source_ = new MockMediaStreamVideoSource(
media::VideoCaptureFormat(
gfx::Size(kMockSourceWidth, kMockSourceHeight), 30.0,
media::PIXEL_FORMAT_I420),
false);
source_ = MakeGarbageCollected<MediaStreamSource>(
"dummy_source_id", MediaStreamSource::kTypeVideo, "dummy_source_name",
false /* remote */);
source_->SetPlatformSource(base::WrapUnique(mock_source_));
}
// Create a track that's associated with |mock_source_|.
WebMediaStreamTrack CreateTrack() {
const bool enabled = true;
WebMediaStreamTrack track = MediaStreamVideoTrack::CreateVideoTrack(
mock_source_, WebPlatformMediaStreamSource::ConstraintsOnceCallback(),
enabled);
if (!source_started_) {
mock_source_->StartMockedSource();
source_started_ = true;
}
return track;
}
// Create a track that's associated with |mock_source_| and has the given
// |adapter_settings|.
WebMediaStreamTrack CreateTrackWithSettings(
const VideoTrackAdapterSettings& adapter_settings) {
const bool enabled = true;
WebMediaStreamTrack track = MediaStreamVideoTrack::CreateVideoTrack(
mock_source_, adapter_settings, base::Optional<bool>(), false, 0.0,
base::nullopt, base::nullopt, base::nullopt, false,
WebPlatformMediaStreamSource::ConstraintsOnceCallback(), enabled);
if (!source_started_) {
mock_source_->StartMockedSource();
source_started_ = true;
}
return track;
}
void UpdateVideoSourceToRespondToRequestRefreshFrame() {
source_ = nullptr;
mock_source_ = new MockMediaStreamVideoSource(
media::VideoCaptureFormat(
gfx::Size(kMockSourceWidth, kMockSourceHeight), 30.0,
media::PIXEL_FORMAT_I420),
true);
source_ = MakeGarbageCollected<MediaStreamSource>(
"dummy_source_id", MediaStreamSource::kTypeVideo, "dummy_source_name",
false /* remote */);
source_->SetPlatformSource(base::WrapUnique(mock_source_));
}
void DepleteIOCallbacks() {
base::RunLoop run_loop;
base::RepeatingClosure quit_closure = run_loop.QuitClosure();
mock_source()->io_task_runner()->PostTask(
FROM_HERE,
base::BindLambdaForTesting([&] { std::move(quit_closure).Run(); }));
run_loop.Run();
}
MockMediaStreamVideoSource* mock_source() { return mock_source_; }
MediaStreamSource* stream_source() const { return source_; }
private:
ScopedTestingPlatformSupport<IOTaskRunnerTestingPlatformSupport> platform_;
Persistent<MediaStreamSource> source_;
// |mock_source_| is owned by |source_|.
MockMediaStreamVideoSource* mock_source_;
bool source_started_;
};
TEST_F(MediaStreamVideoTrackTest, AddAndRemoveSink) {
InitializeSource();
MockMediaStreamVideoSink sink;
WebMediaStreamTrack track = CreateTrack();
sink.ConnectToTrack(track);
DeliverDefaultSizeVideoFrameAndWaitForRenderer(&sink);
EXPECT_EQ(1, sink.number_of_frames());
DeliverDefaultSizeVideoFrameAndWaitForRenderer(&sink);
sink.DisconnectFromTrack();
scoped_refptr<media::VideoFrame> frame = media::VideoFrame::CreateBlackFrame(
gfx::Size(MediaStreamVideoSource::kDefaultWidth,
MediaStreamVideoSource::kDefaultHeight));
mock_source()->DeliverVideoFrame(frame);
// Wait for the IO thread to complete delivering frames.
base::RunLoop().RunUntilIdle();
EXPECT_EQ(2, sink.number_of_frames());
}
class CheckThreadHelper {
public:
CheckThreadHelper(base::OnceClosure callback, bool* correct)
: callback_(std::move(callback)), correct_(correct) {}
~CheckThreadHelper() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
*correct_ = true;
std::move(callback_).Run();
}
private:
base::OnceClosure callback_;
bool* correct_;
THREAD_CHECKER(thread_checker_);
};
void CheckThreadVideoFrameReceiver(
CheckThreadHelper* helper,
scoped_refptr<media::VideoFrame> frame,
std::vector<scoped_refptr<media::VideoFrame>> scaled_frames,
base::TimeTicks estimated_capture_time) {
// Do nothing.
}
// Checks that the callback given to the track is reset on the right thread.
TEST_F(MediaStreamVideoTrackTest, ResetCallbackOnThread) {
InitializeSource();
MockMediaStreamVideoSink sink;
WebMediaStreamTrack track = CreateTrack();
base::RunLoop run_loop;
bool correct = false;
sink.ConnectToTrackWithCallback(
track, WTF::BindRepeating(&CheckThreadVideoFrameReceiver,
base::Owned(new CheckThreadHelper(
run_loop.QuitClosure(), &correct))));
sink.DisconnectFromTrack();
run_loop.Run();
EXPECT_TRUE(correct) << "Not called on correct thread.";
}
TEST_F(MediaStreamVideoTrackTest, SetEnabled) {
InitializeSource();
MockMediaStreamVideoSink sink;
WebMediaStreamTrack track = CreateTrack();
sink.ConnectToTrack(track);
MediaStreamVideoTrack* video_track = MediaStreamVideoTrack::From(track);
DeliverDefaultSizeVideoFrameAndWaitForRenderer(&sink);
EXPECT_EQ(1, sink.number_of_frames());
EXPECT_EQ(kColorValue, *sink.last_frame()->data(media::VideoFrame::kYPlane));
video_track->SetEnabled(false);
EXPECT_FALSE(sink.enabled());
DeliverDefaultSizeVideoFrameAndWaitForRenderer(&sink);
EXPECT_EQ(2, sink.number_of_frames());
EXPECT_EQ(kBlackValue, *sink.last_frame()->data(media::VideoFrame::kYPlane));
video_track->SetEnabled(true);
EXPECT_TRUE(sink.enabled());
DeliverDefaultSizeVideoFrameAndWaitForRenderer(&sink);
EXPECT_EQ(3, sink.number_of_frames());
EXPECT_EQ(kColorValue, *sink.last_frame()->data(media::VideoFrame::kYPlane));
sink.DisconnectFromTrack();
}
TEST_F(MediaStreamVideoTrackTest, SourceDetached) {
InitializeSource();
WebMediaStreamTrack track = CreateTrack();
MockMediaStreamVideoSink sink;
auto* video_track = MediaStreamVideoTrack::From(track);
video_track->StopAndNotify(base::BindOnce([] {}));
sink.ConnectToTrack(track);
sink.ConnectEncodedToTrack(track);
video_track->SetEnabled(true);
video_track->SetEnabled(false);
MediaStreamTrackPlatform::Settings settings;
video_track->GetSettings(settings);
sink.DisconnectFromTrack();
sink.DisconnectEncodedFromTrack();
}
TEST_F(MediaStreamVideoTrackTest, SourceStopped) {
InitializeSource();
MockMediaStreamVideoSink sink;
WebMediaStreamTrack track = CreateTrack();
sink.ConnectToTrack(track);
EXPECT_EQ(WebMediaStreamSource::kReadyStateLive, sink.state());
mock_source()->StopSource();
EXPECT_EQ(WebMediaStreamSource::kReadyStateEnded, sink.state());
sink.DisconnectFromTrack();
}
TEST_F(MediaStreamVideoTrackTest, StopLastTrack) {
InitializeSource();
MockMediaStreamVideoSink sink1;
WebMediaStreamTrack track1 = CreateTrack();
sink1.ConnectToTrack(track1);
EXPECT_EQ(WebMediaStreamSource::kReadyStateLive, sink1.state());
EXPECT_EQ(MediaStreamSource::kReadyStateLive,
stream_source()->GetReadyState());
MockMediaStreamVideoSink sink2;
WebMediaStreamTrack track2 = CreateTrack();
sink2.ConnectToTrack(track2);
EXPECT_EQ(WebMediaStreamSource::kReadyStateLive, sink2.state());
MediaStreamVideoTrack* const native_track1 =
MediaStreamVideoTrack::From(track1);
native_track1->Stop();
EXPECT_EQ(WebMediaStreamSource::kReadyStateEnded, sink1.state());
EXPECT_EQ(MediaStreamSource::kReadyStateLive,
stream_source()->GetReadyState());
sink1.DisconnectFromTrack();
MediaStreamVideoTrack* const native_track2 =
MediaStreamVideoTrack::From(track2);
native_track2->Stop();
EXPECT_EQ(WebMediaStreamSource::kReadyStateEnded, sink2.state());
EXPECT_EQ(MediaStreamSource::kReadyStateEnded,
stream_source()->GetReadyState());
sink2.DisconnectFromTrack();
}
TEST_F(MediaStreamVideoTrackTest, CheckTrackRequestsFrame) {
InitializeSource();
UpdateVideoSourceToRespondToRequestRefreshFrame();
WebMediaStreamTrack track = CreateTrack();
// Add sink and expect to get a frame.
MockMediaStreamVideoSink sink;
base::RunLoop run_loop;
base::RepeatingClosure quit_closure = run_loop.QuitClosure();
EXPECT_CALL(sink, OnVideoFrame)
.WillOnce(RunOnceClosure(std::move(quit_closure)));
sink.ConnectToTrack(track);
run_loop.Run();
EXPECT_EQ(1, sink.number_of_frames());
sink.DisconnectFromTrack();
}
TEST_F(MediaStreamVideoTrackTest, GetSettings) {
InitializeSource();
WebMediaStreamTrack track = CreateTrack();
MediaStreamVideoTrack* const native_track =
MediaStreamVideoTrack::From(track);
MediaStreamTrackPlatform::Settings settings;
native_track->GetSettings(settings);
// These values come straight from the mock video track implementation.
EXPECT_EQ(640, settings.width);
EXPECT_EQ(480, settings.height);
EXPECT_EQ(30.0, settings.frame_rate);
EXPECT_EQ(MediaStreamTrackPlatform::FacingMode::kNone, settings.facing_mode);
}
TEST_F(MediaStreamVideoTrackTest, GetSettingsWithAdjustment) {
InitializeSource();
const int kAdjustedWidth = 600;
const int kAdjustedHeight = 400;
const double kAdjustedFrameRate = 20.0;
VideoTrackAdapterSettings adapter_settings(
gfx::Size(kAdjustedWidth, kAdjustedHeight), 0.0, 10000.0,
kAdjustedFrameRate);
WebMediaStreamTrack track = CreateTrackWithSettings(adapter_settings);
MediaStreamVideoTrack* const native_track =
MediaStreamVideoTrack::From(track);
MediaStreamTrackPlatform::Settings settings;
native_track->GetSettings(settings);
EXPECT_EQ(kAdjustedWidth, settings.width);
EXPECT_EQ(kAdjustedHeight, settings.height);
EXPECT_EQ(kAdjustedFrameRate, settings.frame_rate);
EXPECT_EQ(MediaStreamTrackPlatform::FacingMode::kNone, settings.facing_mode);
}
TEST_F(MediaStreamVideoTrackTest, GetSettingsStopped) {
InitializeSource();
WebMediaStreamTrack track = CreateTrack();
MediaStreamVideoTrack* const native_track =
MediaStreamVideoTrack::From(track);
native_track->Stop();
MediaStreamTrackPlatform::Settings settings;
native_track->GetSettings(settings);
EXPECT_EQ(-1, settings.width);
EXPECT_EQ(-1, settings.height);
EXPECT_EQ(-1, settings.frame_rate);
EXPECT_EQ(MediaStreamTrackPlatform::FacingMode::kNone, settings.facing_mode);
EXPECT_TRUE(settings.device_id.IsNull());
}
TEST_F(MediaStreamVideoTrackTest, DeliverFramesAndGetSettings) {
InitializeSource();
MockMediaStreamVideoSink sink;
WebMediaStreamTrack track = CreateTrack();
sink.ConnectToTrack(track);
MediaStreamVideoTrack* const native_track =
MediaStreamVideoTrack::From(track);
MediaStreamTrackPlatform::Settings settings;
auto frame1 = media::VideoFrame::CreateBlackFrame(gfx::Size(600, 400));
DeliverVideoFrameAndWaitForRenderer(std::move(frame1), &sink);
native_track->GetSettings(settings);
EXPECT_EQ(600, settings.width);
EXPECT_EQ(400, settings.height);
auto frame2 = media::VideoFrame::CreateBlackFrame(gfx::Size(200, 300));
DeliverVideoFrameAndWaitForRenderer(std::move(frame2), &sink);
native_track->GetSettings(settings);
EXPECT_EQ(200, settings.width);
EXPECT_EQ(300, settings.height);
sink.DisconnectFromTrack();
}
TEST_P(MediaStreamVideoTrackTest, PropagatesContentHintType) {
InitializeSource();
MockMediaStreamVideoSink sink;
WebMediaStreamTrack track = CreateTrack();
sink.ConnectToTrack(track);
MediaStreamVideoTrack::From(track)->SetContentHint(GetParam());
EXPECT_EQ(sink.content_hint(), GetParam());
sink.DisconnectFromTrack();
}
class MediaStreamVideoTrackEncodedTest : public MediaStreamVideoTrackTest {
public:
void InitializeSource() override {
MediaStreamVideoTrackTest::InitializeSource();
ON_CALL(*mock_source(), SupportsEncodedOutput).WillByDefault(Return(true));
}
};
TEST_F(MediaStreamVideoTrackEncodedTest, ConnectEncodedSink) {
InitializeSource();
MockMediaStreamVideoSink sink;
WebMediaStreamTrack track = CreateTrack();
EXPECT_CALL(*mock_source(), OnEncodedSinkEnabled);
EXPECT_CALL(*mock_source(), OnCapturingLinkSecured(false));
EXPECT_CALL(*mock_source(), OnCapturingLinkSecured(true)).Times(0);
EXPECT_CALL(*mock_source(), OnEncodedSinkDisabled).Times(0);
sink.ConnectEncodedToTrack(track);
Mock::VerifyAndClearExpectations(mock_source());
sink.DisconnectEncodedFromTrack();
}
TEST_F(MediaStreamVideoTrackEncodedTest, DisconnectEncodedSink) {
InitializeSource();
MockMediaStreamVideoSink sink;
WebMediaStreamTrack track = CreateTrack();
sink.ConnectEncodedToTrack(track);
EXPECT_CALL(*mock_source(), OnEncodedSinkDisabled);
EXPECT_CALL(*mock_source(), OnCapturingLinkSecured(true));
sink.DisconnectEncodedFromTrack();
}
TEST_F(MediaStreamVideoTrackEncodedTest,
CapturingLinkSecuredWithEncodedAndNormalTracks) {
InitializeSource();
MockMediaStreamVideoSink sink1;
WebMediaStreamTrack track1 = CreateTrack();
InSequence s;
EXPECT_CALL(*mock_source(), OnCapturingLinkSecured(true));
sink1.ConnectToTrack(track1);
EXPECT_CALL(*mock_source(), OnCapturingLinkSecured(false));
MockMediaStreamVideoSink sink2;
WebMediaStreamTrack track2 = CreateTrack();
sink2.ConnectEncodedToTrack(track2);
EXPECT_CALL(*mock_source(), OnCapturingLinkSecured(false));
sink1.DisconnectFromTrack();
EXPECT_CALL(*mock_source(), OnCapturingLinkSecured(true));
sink2.DisconnectEncodedFromTrack();
}
TEST_F(MediaStreamVideoTrackEncodedTest, TransferOneEncodedVideoFrame) {
InitializeSource();
MockMediaStreamVideoSink sink;
WebMediaStreamTrack track = CreateTrack();
sink.ConnectEncodedToTrack(track);
base::RunLoop run_loop;
base::RepeatingClosure quit_closure = run_loop.QuitClosure();
EXPECT_CALL(sink, OnEncodedVideoFrame).WillOnce(Invoke([&](base::TimeTicks) {
std::move(quit_closure).Run();
}));
mock_source()->DeliverEncodedVideoFrame(
base::MakeRefCounted<MockEncodedVideoFrame>());
run_loop.Run();
Mock::VerifyAndClearExpectations(mock_source());
sink.DisconnectEncodedFromTrack();
}
TEST_F(MediaStreamVideoTrackEncodedTest, SupportsEncodedDisableEnable) {
InitializeSource();
MockMediaStreamVideoSink sink;
WebMediaStreamTrack track = CreateTrack();
sink.ConnectEncodedToTrack(track);
auto key_frame = base::MakeRefCounted<MockEncodedVideoFrame>();
EXPECT_CALL(*key_frame, IsKeyFrame).WillRepeatedly(Return(true));
auto delta_frame = base::MakeRefCounted<MockEncodedVideoFrame>();
EXPECT_CALL(*delta_frame, IsKeyFrame).WillRepeatedly(Return(false));
DeliverEncodedVideoFrameAndWait(key_frame, &sink);
// Key frame when disabled -> shouldn't get dispatched
MediaStreamVideoTrack::From(track)->SetEnabled(false);
EXPECT_FALSE(sink.enabled());
{
EXPECT_CALL(sink, OnEncodedVideoFrame).Times(0);
mock_source()->DeliverEncodedVideoFrame(key_frame);
DepleteIOCallbacks();
}
// Delta frame when disabled -> shouldn't get dispatched until key frame
// appears.
EXPECT_CALL(*mock_source(), OnRequestRefreshFrame);
MediaStreamVideoTrack::From(track)->SetEnabled(true);
EXPECT_TRUE(sink.enabled());
{
EXPECT_CALL(sink, OnEncodedVideoFrame).Times(0);
mock_source()->DeliverEncodedVideoFrame(delta_frame);
DepleteIOCallbacks();
}
// After a key frame things should be flowing again.
DeliverEncodedVideoFrameAndWait(key_frame, &sink);
DeliverEncodedVideoFrameAndWait(delta_frame, &sink);
Mock::VerifyAndClearExpectations(mock_source());
sink.DisconnectEncodedFromTrack();
}
TEST_P(MediaStreamVideoTrackEncodedTest, PropagatesContentHintType) {
InitializeSource();
MockMediaStreamVideoSink sink;
WebMediaStreamTrack track = CreateTrack();
sink.ConnectEncodedToTrack(track);
MediaStreamVideoTrack::From(track)->SetContentHint(GetParam());
EXPECT_EQ(sink.content_hint(), GetParam());
sink.DisconnectEncodedFromTrack();
}
TEST_F(MediaStreamVideoTrackEncodedTest, SourceStopped) {
InitializeSource();
MockMediaStreamVideoSink sink;
WebMediaStreamTrack track = CreateTrack();
sink.ConnectEncodedToTrack(track);
EXPECT_EQ(WebMediaStreamSource::kReadyStateLive, sink.state());
mock_source()->StopSource();
EXPECT_EQ(WebMediaStreamSource::kReadyStateEnded, sink.state());
sink.DisconnectEncodedFromTrack();
}
INSTANTIATE_TEST_SUITE_P(,
MediaStreamVideoTrackTest,
Values(ContentHintType::kVideoMotion,
ContentHintType::kVideoDetail,
ContentHintType::kVideoText));
INSTANTIATE_TEST_SUITE_P(,
MediaStreamVideoTrackEncodedTest,
Values(ContentHintType::kVideoMotion,
ContentHintType::kVideoDetail,
ContentHintType::kVideoText));
class MediaStreamVideoTrackRefreshFrameTimerTest
: public MediaStreamVideoTrackTest {
public:
void SetUp() override { InitializeSource(); }
};
TEST_F(MediaStreamVideoTrackRefreshFrameTimerTest,
SetMinFrameRateForScreenCastTrack) {
// |RequestRefreshFrame| should be called exactly twice within kMinFrameRate
// interval: First time from |AddSink| and second time from the refresh timer.
EXPECT_CALL(*mock_source(), OnRequestRefreshFrame).Times(2);
MockMediaStreamVideoSink sink;
WebMediaStreamTrack track =
CreateTrackWithSettings(VideoTrackAdapterSettings());
auto* video_track = MediaStreamVideoTrack::From(track);
video_track->SetMinimumFrameRate(kMinFrameRate);
video_track->SetIsScreencastForTesting(true);
sink.ConnectToTrack(track);
test::RunDelayedTasks(base::TimeDelta::FromHz(kMinFrameRate));
EXPECT_TRUE(video_track->IsRefreshFrameTimerRunningForTesting());
video_track->StopAndNotify(base::BindOnce([] {}));
EXPECT_FALSE(video_track->IsRefreshFrameTimerRunningForTesting());
}
TEST_F(MediaStreamVideoTrackRefreshFrameTimerTest,
SetMinFrameRateForNonScreenCastTrack) {
// |RequestRefreshFrame| should only be called once from |AddSink| since
// refresh frame timer is not running.
EXPECT_CALL(*mock_source(), OnRequestRefreshFrame).Times(1);
MockMediaStreamVideoSink sink;
WebMediaStreamTrack track =
CreateTrackWithSettings(VideoTrackAdapterSettings());
auto* video_track = MediaStreamVideoTrack::From(track);
video_track->SetMinimumFrameRate(kMinFrameRate);
// Refresh frame timer will not be run when |is_screencast_| is false.
video_track->SetIsScreencastForTesting(false);
sink.ConnectToTrack(track);
test::RunDelayedTasks(base::TimeDelta::FromHz(kMinFrameRate));
EXPECT_FALSE(video_track->IsRefreshFrameTimerRunningForTesting());
}
TEST_F(MediaStreamVideoTrackRefreshFrameTimerTest, RequiredRefreshRate) {
// Sinks that have a required min frames per sec as 0 will not lead
// to video track running the refresh frame timer.
EXPECT_CALL(*mock_source(), OnRequestRefreshFrame).Times(1);
MockMediaStreamVideoSink sink;
EXPECT_EQ(sink.GetRequiredMinFramesPerSec(), 0);
WebMediaStreamTrack track =
CreateTrackWithSettings(VideoTrackAdapterSettings());
auto* video_track = MediaStreamVideoTrack::From(track);
video_track->SetIsScreencastForTesting(true);
sink.ConnectToTrack(track);
test::RunDelayedTasks(base::TimeDelta::FromSeconds(1));
}
TEST_F(MediaStreamVideoTrackRefreshFrameTimerTest,
RequiredRefreshRateWebRTCSink) {
// WebRTC sink has a required min frames per sec set to 1 so when we do
// not have any min frame rate set on the video track, this required rate will
// be used by the timer.
EXPECT_CALL(*mock_source(), OnRequestRefreshFrame).Times(2);
WebMediaStreamTrack track = MediaStreamVideoTrack::CreateVideoTrack(
mock_source(), WebPlatformMediaStreamSource::ConstraintsOnceCallback(),
true);
MediaStreamVideoTrack::From(track)->SetIsScreencastForTesting(true);
Persistent<MediaStreamComponent> media_stream_component = *track;
blink::MediaStreamVideoWebRtcSink webrtc_sink(
media_stream_component, new blink::MockPeerConnectionDependencyFactory(),
blink::scheduler::GetSingleThreadTaskRunnerForTesting());
EXPECT_EQ(webrtc_sink.GetRequiredMinFramesPerSec(), 1);
test::RunDelayedTasks(base::TimeDelta::FromSeconds(1));
}
TEST_F(MediaStreamVideoTrackRefreshFrameTimerTest,
RequiredRefreshRateMultipleSinksAdded) {
// |RequestRefreshFrame| will be called once for every sink added (twice here)
// and third time from the refresh frame timer started by WebRTC sink. We will
// pick the maximum of all the required refresh rates to run the timer.
EXPECT_CALL(*mock_source(), OnRequestRefreshFrame).Times(3);
WebMediaStreamTrack track = MediaStreamVideoTrack::CreateVideoTrack(
mock_source(), WebPlatformMediaStreamSource::ConstraintsOnceCallback(),
true);
MediaStreamVideoTrack::From(track)->SetIsScreencastForTesting(true);
// First sink.
MockMediaStreamVideoSink sink;
EXPECT_EQ(sink.GetRequiredMinFramesPerSec(), 0);
sink.ConnectToTrack(track);
// Second sink.
Persistent<MediaStreamComponent> media_stream_component = *track;
blink::MediaStreamVideoWebRtcSink webrtc_sink(
media_stream_component, new blink::MockPeerConnectionDependencyFactory(),
blink::scheduler::GetSingleThreadTaskRunnerForTesting());
EXPECT_EQ(webrtc_sink.GetRequiredMinFramesPerSec(), 1);
test::RunDelayedTasks(base::TimeDelta::FromSeconds(1));
}
TEST_F(MediaStreamVideoTrackRefreshFrameTimerTest,
RequiredRefreshRateMultipleSinksAddedAndRemoved) {
// |RequestRefreshFrame| will be called once for every sink added (twice
// here). The second sink (webrtc sink) does have a required min frames per
// sec but it is removed.
EXPECT_CALL(*mock_source(), OnRequestRefreshFrame).Times(2);
WebMediaStreamTrack track = MediaStreamVideoTrack::CreateVideoTrack(
mock_source(), WebPlatformMediaStreamSource::ConstraintsOnceCallback(),
true);
MediaStreamVideoTrack::From(track)->SetIsScreencastForTesting(true);
// First sink.
MockMediaStreamVideoSink sink;
EXPECT_EQ(sink.GetRequiredMinFramesPerSec(), 0);
sink.ConnectToTrack(track);
// Second sink added and then removed. The destructor for
// MediaStreamVideoWebRtcSink calls DisconnectFromTrack.
{
Persistent<MediaStreamComponent> media_stream_component = *track;
blink::MediaStreamVideoWebRtcSink webrtc_sink(
media_stream_component,
new blink::MockPeerConnectionDependencyFactory(),
blink::scheduler::GetSingleThreadTaskRunnerForTesting());
EXPECT_EQ(webrtc_sink.GetRequiredMinFramesPerSec(), 1);
}
test::RunDelayedTasks(base::TimeDelta::FromSeconds(1));
}
} // namespace media_stream_video_track_test
} // namespace blink