blob: f47919e5efa29b88165ea92475a3d0fcc562e958 [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 <string>
#include <utility>
#include "base/memory/ptr_util.h"
#include "base/run_loop.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "build/build_config.h"
#include "media/base/limits.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/web/modules/mediastream/media_stream_video_source.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_constraint_factory.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/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/wtf/functional.h"
using ::testing::_;
using ::testing::DoAll;
using ::testing::InSequence;
using ::testing::Mock;
using ::testing::Return;
using ::testing::SaveArg;
namespace blink {
class MediaStreamVideoSourceTest : public testing::Test {
public:
MediaStreamVideoSourceTest()
: number_of_successful_constraints_applied_(0),
number_of_failed_constraints_applied_(0),
result_(mojom::MediaStreamRequestResult::OK),
result_name_(""),
mock_stream_video_source_(new MockMediaStreamVideoSource(
media::VideoCaptureFormat(gfx::Size(1280, 720),
1000.0,
media::PIXEL_FORMAT_I420),
false)) {
mock_stream_video_source_->DisableStopForRestart();
stream_source_ = MakeGarbageCollected<MediaStreamSource>(
String::FromUTF8("dummy_source_id"), MediaStreamSource::kTypeVideo,
String::FromUTF8("dummy_source_name"), false /* remote */);
stream_source_->SetPlatformSource(
base::WrapUnique(mock_stream_video_source_));
ON_CALL(*mock_stream_video_source_, SupportsEncodedOutput)
.WillByDefault(Return(true));
}
void TearDown() override {
stream_source_ = nullptr;
WebHeap::CollectAllGarbageForTesting();
}
MOCK_METHOD0(MockNotification, void());
protected:
MediaStreamVideoSource* source() { return mock_stream_video_source_; }
// Create a track that's associated with |stream_source_|.
WebMediaStreamTrack CreateTrack(const std::string& id) {
bool enabled = true;
return MediaStreamVideoTrack::CreateVideoTrack(
mock_stream_video_source_,
WTF::Bind(&MediaStreamVideoSourceTest::OnConstraintsApplied,
base::Unretained(this)),
enabled);
}
WebMediaStreamTrack CreateTrack(
const std::string& id,
const VideoTrackAdapterSettings& adapter_settings,
const base::Optional<bool>& noise_reduction,
bool is_screencast,
double min_frame_rate) {
bool enabled = true;
return MediaStreamVideoTrack::CreateVideoTrack(
mock_stream_video_source_, adapter_settings, noise_reduction,
is_screencast, min_frame_rate, base::nullopt, base::nullopt,
base::nullopt, false,
WTF::Bind(&MediaStreamVideoSourceTest::OnConstraintsApplied,
base::Unretained(this)),
enabled);
}
WebMediaStreamTrack CreateTrack() {
return CreateTrack("123",
VideoTrackAdapterSettings(gfx::Size(100, 100), 30.0),
base::Optional<bool>(), false, 0.0);
}
WebMediaStreamTrack CreateTrackAndStartSource(int width,
int height,
double frame_rate,
bool detect_rotation = false) {
WebMediaStreamTrack track = CreateTrack(
"123", VideoTrackAdapterSettings(gfx::Size(width, height), frame_rate),
base::Optional<bool>(), false, 0.0);
EXPECT_EQ(0, NumberOfSuccessConstraintsCallbacks());
mock_stream_video_source_->StartMockedSource();
// Once the source has started successfully we expect that the
// ConstraintsOnceCallback in WebPlatformMediaStreamSource::AddTrack
// completes.
EXPECT_EQ(1, NumberOfSuccessConstraintsCallbacks());
return track;
}
int NumberOfSuccessConstraintsCallbacks() const {
return number_of_successful_constraints_applied_;
}
int NumberOfFailedConstraintsCallbacks() const {
return number_of_failed_constraints_applied_;
}
mojom::MediaStreamRequestResult error_type() const { return result_; }
WebString error_name() const { return result_name_; }
MockMediaStreamVideoSource* mock_source() {
return mock_stream_video_source_;
}
MediaStreamSource* stream_source() { return stream_source_.Get(); }
void TestSourceCropFrame(int capture_width,
int capture_height,
int expected_width,
int expected_height) {
// Configure the track to crop to the expected resolution.
WebMediaStreamTrack track =
CreateTrackAndStartSource(expected_width, expected_height, 30.0);
// Produce frames at the capture resolution.
MockMediaStreamVideoSink sink;
sink.ConnectToTrack(track);
DeliverVideoFrameAndWaitForRenderer(capture_width, capture_height, &sink);
EXPECT_EQ(1, sink.number_of_frames());
// Expect the delivered frame to be cropped.
EXPECT_EQ(expected_height, sink.frame_size().height());
EXPECT_EQ(expected_width, sink.frame_size().width());
sink.DisconnectFromTrack();
}
void DeliverVideoFrameAndWaitForRenderer(int width,
int height,
MockMediaStreamVideoSink* sink) {
base::RunLoop run_loop;
base::OnceClosure quit_closure = run_loop.QuitClosure();
EXPECT_CALL(*sink, OnVideoFrame).WillOnce([&](base::TimeTicks) {
std::move(quit_closure).Run();
});
scoped_refptr<media::VideoFrame> frame =
media::VideoFrame::CreateBlackFrame(gfx::Size(width, height));
mock_source()->DeliverVideoFrame(frame);
run_loop.Run();
}
void DeliverRotatedVideoFrameAndWaitForRenderer(
int width,
int height,
MockMediaStreamVideoSink* sink) {
DeliverVideoFrameAndWaitForRenderer(height, width, sink);
}
void DeliverVideoFrameAndWaitForTwoRenderers(
int width,
int height,
MockMediaStreamVideoSink* sink1,
MockMediaStreamVideoSink* sink2) {
base::RunLoop run_loop;
base::OnceClosure quit_closure = run_loop.QuitClosure();
EXPECT_CALL(*sink1, OnVideoFrame);
EXPECT_CALL(*sink2, OnVideoFrame).WillOnce([&](base::TimeTicks) {
std::move(quit_closure).Run();
});
scoped_refptr<media::VideoFrame> frame =
media::VideoFrame::CreateBlackFrame(gfx::Size(width, height));
mock_source()->DeliverVideoFrame(frame);
run_loop.Run();
}
void TestTwoTracksWithDifferentSettings(int capture_width,
int capture_height,
int expected_width1,
int expected_height1,
int expected_width2,
int expected_height2) {
WebMediaStreamTrack track1 =
CreateTrackAndStartSource(expected_width1, expected_height1,
MediaStreamVideoSource::kDefaultFrameRate);
WebMediaStreamTrack track2 = CreateTrack(
"dummy",
VideoTrackAdapterSettings(gfx::Size(expected_width2, expected_height2),
MediaStreamVideoSource::kDefaultFrameRate),
base::Optional<bool>(), false, 0.0);
MockMediaStreamVideoSink sink1;
sink1.ConnectToTrack(track1);
EXPECT_EQ(0, sink1.number_of_frames());
MockMediaStreamVideoSink sink2;
sink2.ConnectToTrack(track2);
EXPECT_EQ(0, sink2.number_of_frames());
DeliverVideoFrameAndWaitForTwoRenderers(capture_width, capture_height,
&sink1, &sink2);
EXPECT_EQ(1, sink1.number_of_frames());
EXPECT_EQ(expected_width1, sink1.frame_size().width());
EXPECT_EQ(expected_height1, sink1.frame_size().height());
EXPECT_EQ(1, sink2.number_of_frames());
EXPECT_EQ(expected_width2, sink2.frame_size().width());
EXPECT_EQ(expected_height2, sink2.frame_size().height());
sink1.DisconnectFromTrack();
sink2.DisconnectFromTrack();
}
void ReleaseTrackAndSourceOnAddTrackCallback(
const WebMediaStreamTrack& track_to_release) {
track_to_release_ = track_to_release;
}
private:
void OnConstraintsApplied(WebPlatformMediaStreamSource* source,
mojom::MediaStreamRequestResult result,
const WebString& result_name) {
ASSERT_EQ(source, stream_source()->GetPlatformSource());
if (result == mojom::MediaStreamRequestResult::OK) {
++number_of_successful_constraints_applied_;
} else {
result_ = result;
result_name_ = result_name;
++number_of_failed_constraints_applied_;
}
if (!track_to_release_.IsNull()) {
mock_stream_video_source_ = nullptr;
stream_source_ = nullptr;
track_to_release_.Reset();
}
}
ScopedTestingPlatformSupport<IOTaskRunnerTestingPlatformSupport> platform_;
WebMediaStreamTrack track_to_release_;
int number_of_successful_constraints_applied_;
int number_of_failed_constraints_applied_;
mojom::MediaStreamRequestResult result_;
WebString result_name_;
Persistent<MediaStreamSource> stream_source_;
// |mock_stream_video_source_| is owned by |stream_source_|.
MockMediaStreamVideoSource* mock_stream_video_source_;
};
TEST_F(MediaStreamVideoSourceTest, AddTrackAndStartSource) {
WebMediaStreamTrack track = CreateTrack("123");
mock_source()->StartMockedSource();
EXPECT_EQ(1, NumberOfSuccessConstraintsCallbacks());
}
TEST_F(MediaStreamVideoSourceTest, AddTwoTracksBeforeSourceStarts) {
WebMediaStreamTrack track1 = CreateTrack("123");
WebMediaStreamTrack track2 = CreateTrack("123");
EXPECT_EQ(0, NumberOfSuccessConstraintsCallbacks());
mock_source()->StartMockedSource();
EXPECT_EQ(2, NumberOfSuccessConstraintsCallbacks());
}
TEST_F(MediaStreamVideoSourceTest, AddTrackAfterSourceStarts) {
WebMediaStreamTrack track1 = CreateTrack("123");
mock_source()->StartMockedSource();
EXPECT_EQ(1, NumberOfSuccessConstraintsCallbacks());
WebMediaStreamTrack track2 = CreateTrack("123");
EXPECT_EQ(2, NumberOfSuccessConstraintsCallbacks());
}
TEST_F(MediaStreamVideoSourceTest, AddTrackAndFailToStartSource) {
WebMediaStreamTrack track = CreateTrack("123");
mock_source()->FailToStartMockedSource();
EXPECT_EQ(1, NumberOfFailedConstraintsCallbacks());
}
TEST_F(MediaStreamVideoSourceTest, MandatoryAspectRatio4To3) {
TestSourceCropFrame(1280, 720, 960, 720);
}
TEST_F(MediaStreamVideoSourceTest, ReleaseTrackAndSourceOnSuccessCallBack) {
WebMediaStreamTrack track = CreateTrack("123");
ReleaseTrackAndSourceOnAddTrackCallback(track);
mock_source()->StartMockedSource();
EXPECT_EQ(1, NumberOfSuccessConstraintsCallbacks());
}
TEST_F(MediaStreamVideoSourceTest, TwoTracksWithVGAAndWVGA) {
TestTwoTracksWithDifferentSettings(640, 480, 640, 480, 640, 360);
}
TEST_F(MediaStreamVideoSourceTest, TwoTracksWith720AndWVGA) {
TestTwoTracksWithDifferentSettings(1280, 720, 1280, 720, 640, 360);
}
TEST_F(MediaStreamVideoSourceTest, SourceChangeFrameSize) {
// Expect the source to start capture with the supported resolution.
// Disable frame-rate adjustment in spec-compliant mode to ensure no frames
// are dropped.
WebMediaStreamTrack track = CreateTrackAndStartSource(800, 700, 0.0);
MockMediaStreamVideoSink sink;
sink.ConnectToTrack(track);
EXPECT_EQ(0, sink.number_of_frames());
DeliverVideoFrameAndWaitForRenderer(320, 240, &sink);
EXPECT_EQ(1, sink.number_of_frames());
// Expect the delivered frame to be passed unchanged since its smaller than
// max requested.
EXPECT_EQ(320, sink.frame_size().width());
EXPECT_EQ(240, sink.frame_size().height());
DeliverVideoFrameAndWaitForRenderer(640, 480, &sink);
EXPECT_EQ(2, sink.number_of_frames());
// Expect the delivered frame to be passed unchanged since its smaller than
// max requested.
EXPECT_EQ(640, sink.frame_size().width());
EXPECT_EQ(480, sink.frame_size().height());
DeliverVideoFrameAndWaitForRenderer(1280, 720, &sink);
EXPECT_EQ(3, sink.number_of_frames());
// Expect a frame to be cropped since its larger than max requested.
EXPECT_EQ(800, sink.frame_size().width());
EXPECT_EQ(700, sink.frame_size().height());
sink.DisconnectFromTrack();
}
TEST_F(MediaStreamVideoSourceTest, RotatedSourceDetectionDisabled) {
source()->SetDeviceRotationDetection(false /* enabled */);
// Expect the source to start capture with the supported resolution.
// Disable frame-rate adjustment in spec-compliant mode to ensure no frames
// are dropped.
WebMediaStreamTrack track = CreateTrackAndStartSource(1280, 720, 0.0, true);
MockMediaStreamVideoSink sink;
sink.ConnectToTrack(track);
EXPECT_EQ(0, sink.number_of_frames());
DeliverVideoFrameAndWaitForRenderer(1280, 720, &sink);
EXPECT_EQ(1, sink.number_of_frames());
// Expect the delivered frame to be passed unchanged since it is the same size
// as the source native format.
EXPECT_EQ(1280, sink.frame_size().width());
EXPECT_EQ(720, sink.frame_size().height());
DeliverRotatedVideoFrameAndWaitForRenderer(1280, 720, &sink);
EXPECT_EQ(2, sink.number_of_frames());
// Expect the delivered frame to be cropped because the rotation is not
// detected.
EXPECT_EQ(720, sink.frame_size().width());
EXPECT_EQ(720, sink.frame_size().height());
sink.DisconnectFromTrack();
}
TEST_F(MediaStreamVideoSourceTest, RotatedSourceDetectionEnabled) {
source()->SetDeviceRotationDetection(true /* enabled */);
// Expect the source to start capture with the supported resolution.
// Disable frame-rate adjustment in spec-compliant mode to ensure no frames
// are dropped.
WebMediaStreamTrack track = CreateTrackAndStartSource(1280, 720, 0.0, true);
MockMediaStreamVideoSink sink;
sink.ConnectToTrack(track);
EXPECT_EQ(0, sink.number_of_frames());
DeliverVideoFrameAndWaitForRenderer(1280, 720, &sink);
EXPECT_EQ(1, sink.number_of_frames());
// Expect the delivered frame to be passed unchanged since it is the same size
// as the source native format.
EXPECT_EQ(1280, sink.frame_size().width());
EXPECT_EQ(720, sink.frame_size().height());
DeliverRotatedVideoFrameAndWaitForRenderer(1280, 720, &sink);
EXPECT_EQ(2, sink.number_of_frames());
// Expect the delivered frame to be passed unchanged since it is detected as
// a valid frame on a rotated device.
EXPECT_EQ(720, sink.frame_size().width());
EXPECT_EQ(1280, sink.frame_size().height());
sink.DisconnectFromTrack();
}
// Test that a source producing no frames change the source ReadyState to muted.
// that in a reasonable time frame the muted state turns to false.
TEST_F(MediaStreamVideoSourceTest, MutedSource) {
// Setup the source for support a frame rate of 999 fps in order to test
// the muted event faster. This is since the frame monitoring uses
// PostDelayedTask that is dependent on the source frame rate.
// Note that media::limits::kMaxFramesPerSecond is 1000.
WebMediaStreamTrack track = CreateTrackAndStartSource(
640, 480, media::limits::kMaxFramesPerSecond - 2);
MockMediaStreamVideoSink sink;
sink.ConnectToTrack(track);
EXPECT_EQ(track.Source().GetReadyState(),
WebMediaStreamSource::kReadyStateLive);
base::RunLoop run_loop;
base::OnceClosure quit_closure = run_loop.QuitClosure();
bool muted_state = false;
EXPECT_CALL(*mock_source(), DoSetMutedState(_))
.WillOnce(DoAll(SaveArg<0>(&muted_state),
[&](auto) { std::move(quit_closure).Run(); }));
run_loop.Run();
EXPECT_EQ(muted_state, true);
EXPECT_EQ(track.Source().GetReadyState(),
WebMediaStreamSource::kReadyStateMuted);
base::RunLoop run_loop2;
base::OnceClosure quit_closure2 = run_loop2.QuitClosure();
EXPECT_CALL(*mock_source(), DoSetMutedState(_))
.WillOnce(DoAll(SaveArg<0>(&muted_state),
[&](auto) { std::move(quit_closure2).Run(); }));
DeliverVideoFrameAndWaitForRenderer(640, 480, &sink);
run_loop2.Run();
EXPECT_EQ(muted_state, false);
EXPECT_EQ(track.Source().GetReadyState(),
WebMediaStreamSource::kReadyStateLive);
sink.DisconnectFromTrack();
}
TEST_F(MediaStreamVideoSourceTest, ReconfigureTrack) {
WebMediaStreamTrack track = CreateTrackAndStartSource(
640, 480, media::limits::kMaxFramesPerSecond - 2);
MockMediaStreamVideoSink sink;
sink.ConnectToTrack(track);
EXPECT_EQ(track.Source().GetReadyState(),
WebMediaStreamSource::kReadyStateLive);
MediaStreamVideoTrack* native_track = MediaStreamVideoTrack::From(track);
MediaStreamTrackPlatform::Settings settings;
native_track->GetSettings(settings);
EXPECT_EQ(settings.width, 640);
EXPECT_EQ(settings.height, 480);
EXPECT_EQ(settings.frame_rate, media::limits::kMaxFramesPerSecond - 2);
EXPECT_EQ(settings.aspect_ratio, 640.0 / 480.0);
source()->ReconfigureTrack(
native_track, VideoTrackAdapterSettings(gfx::Size(630, 470), 30.0));
native_track->GetSettings(settings);
EXPECT_EQ(settings.width, 630);
EXPECT_EQ(settings.height, 470);
EXPECT_EQ(settings.frame_rate, 30.0);
EXPECT_EQ(settings.aspect_ratio, 630.0 / 470.0);
// Produce a frame in the source native format and expect the delivered frame
// to have the new track format.
DeliverVideoFrameAndWaitForRenderer(640, 480, &sink);
EXPECT_EQ(1, sink.number_of_frames());
EXPECT_EQ(630, sink.frame_size().width());
EXPECT_EQ(470, sink.frame_size().height());
}
TEST_F(MediaStreamVideoSourceTest, ReconfigureStoppedTrack) {
WebMediaStreamTrack track = CreateTrackAndStartSource(
640, 480, media::limits::kMaxFramesPerSecond - 2);
EXPECT_EQ(track.Source().GetReadyState(),
WebMediaStreamSource::kReadyStateLive);
MediaStreamVideoTrack* native_track = MediaStreamVideoTrack::From(track);
MediaStreamTrackPlatform::Settings settings;
native_track->GetSettings(settings);
EXPECT_EQ(settings.width, 640);
EXPECT_EQ(settings.height, 480);
EXPECT_EQ(settings.frame_rate, media::limits::kMaxFramesPerSecond - 2);
EXPECT_EQ(settings.aspect_ratio, 640.0 / 480.0);
// Reconfiguring a stopped track should have no effect since it is no longer
// associated with the source.
native_track->Stop();
EXPECT_EQ(track.Source().GetReadyState(),
WebMediaStreamSource::kReadyStateEnded);
source()->ReconfigureTrack(
native_track, VideoTrackAdapterSettings(gfx::Size(630, 470), 30.0));
MediaStreamTrackPlatform::Settings stopped_settings;
native_track->GetSettings(stopped_settings);
EXPECT_EQ(stopped_settings.width, -1);
EXPECT_EQ(stopped_settings.height, -1);
EXPECT_EQ(stopped_settings.frame_rate, -1);
EXPECT_EQ(stopped_settings.aspect_ratio, -1);
}
// Test that restart fails on a source without restart support.
TEST_F(MediaStreamVideoSourceTest, FailedRestart) {
WebMediaStreamTrack track = CreateTrack("123");
mock_source()->StartMockedSource();
EXPECT_EQ(1, NumberOfSuccessConstraintsCallbacks());
EXPECT_EQ(track.Source().GetReadyState(),
WebMediaStreamSource::kReadyStateLive);
// The source does not support Restart/StopForRestart.
mock_source()->StopForRestart(
WTF::Bind([](MediaStreamVideoSource::RestartResult result) {
EXPECT_EQ(result, MediaStreamVideoSource::RestartResult::IS_RUNNING);
}));
base::RunLoop().RunUntilIdle();
EXPECT_EQ(track.Source().GetReadyState(),
WebMediaStreamSource::kReadyStateLive);
// Verify that Restart() fails with INVALID_STATE when not called after a
// successful StopForRestart().
mock_source()->Restart(
media::VideoCaptureFormat(),
WTF::Bind([](MediaStreamVideoSource::RestartResult result) {
EXPECT_EQ(result, MediaStreamVideoSource::RestartResult::INVALID_STATE);
}));
base::RunLoop().RunUntilIdle();
EXPECT_EQ(track.Source().GetReadyState(),
WebMediaStreamSource::kReadyStateLive);
mock_source()->StopSource();
// Verify that StopForRestart() fails with INVALID_STATE when called when the
// source is not running.
mock_source()->StopForRestart(
WTF::Bind([](MediaStreamVideoSource::RestartResult result) {
EXPECT_EQ(result, MediaStreamVideoSource::RestartResult::INVALID_STATE);
}));
}
// Test that restart succeeds on a source with restart support.
TEST_F(MediaStreamVideoSourceTest, SuccessfulRestart) {
WebMediaStreamTrack track = CreateTrack("123");
mock_source()->EnableStopForRestart();
mock_source()->EnableRestart();
mock_source()->StartMockedSource();
EXPECT_EQ(NumberOfSuccessConstraintsCallbacks(), 1);
EXPECT_EQ(track.Source().GetReadyState(),
WebMediaStreamSource::kReadyStateLive);
mock_source()->StopForRestart(
WTF::Bind([](MediaStreamVideoSource::RestartResult result) {
EXPECT_EQ(result, MediaStreamVideoSource::RestartResult::IS_STOPPED);
}));
base::RunLoop().RunUntilIdle();
EXPECT_EQ(track.Source().GetReadyState(),
WebMediaStreamSource::kReadyStateLive);
// Verify that StopForRestart() fails with INVALID_STATE called after the
// source is already stopped.
mock_source()->StopForRestart(
WTF::Bind([](MediaStreamVideoSource::RestartResult result) {
EXPECT_EQ(result, MediaStreamVideoSource::RestartResult::INVALID_STATE);
}));
base::RunLoop().RunUntilIdle();
EXPECT_EQ(track.Source().GetReadyState(),
WebMediaStreamSource::kReadyStateLive);
mock_source()->Restart(
media::VideoCaptureFormat(),
WTF::Bind([](MediaStreamVideoSource::RestartResult result) {
EXPECT_EQ(result, MediaStreamVideoSource::RestartResult::IS_RUNNING);
}));
base::RunLoop().RunUntilIdle();
EXPECT_EQ(track.Source().GetReadyState(),
WebMediaStreamSource::kReadyStateLive);
// Verify that Restart() fails with INVALID_STATE if the source has already
// started.
mock_source()->Restart(
media::VideoCaptureFormat(),
WTF::Bind([](MediaStreamVideoSource::RestartResult result) {
EXPECT_EQ(result, MediaStreamVideoSource::RestartResult::INVALID_STATE);
}));
base::RunLoop().RunUntilIdle();
EXPECT_EQ(track.Source().GetReadyState(),
WebMediaStreamSource::kReadyStateLive);
mock_source()->StopSource();
base::RunLoop().RunUntilIdle();
EXPECT_EQ(track.Source().GetReadyState(),
WebMediaStreamSource::kReadyStateEnded);
}
// Test that restart fails on a source without restart support.
TEST_F(MediaStreamVideoSourceTest, FailedRestartAfterStopForRestart) {
WebMediaStreamTrack track = CreateTrack("123");
mock_source()->EnableStopForRestart();
mock_source()->DisableRestart();
mock_source()->StartMockedSource();
EXPECT_EQ(NumberOfSuccessConstraintsCallbacks(), 1);
EXPECT_EQ(track.Source().GetReadyState(),
WebMediaStreamSource::kReadyStateLive);
mock_source()->StopForRestart(
WTF::Bind([](MediaStreamVideoSource::RestartResult result) {
EXPECT_EQ(result, MediaStreamVideoSource::RestartResult::IS_STOPPED);
}));
base::RunLoop().RunUntilIdle();
EXPECT_EQ(track.Source().GetReadyState(),
WebMediaStreamSource::kReadyStateLive);
mock_source()->Restart(
media::VideoCaptureFormat(),
WTF::Bind([](MediaStreamVideoSource::RestartResult result) {
EXPECT_EQ(result, MediaStreamVideoSource::RestartResult::IS_STOPPED);
}));
base::RunLoop().RunUntilIdle();
EXPECT_EQ(track.Source().GetReadyState(),
WebMediaStreamSource::kReadyStateLive);
// Another failed attempt to verify that the source remains in the correct
// state.
mock_source()->Restart(
media::VideoCaptureFormat(),
WTF::Bind([](MediaStreamVideoSource::RestartResult result) {
EXPECT_EQ(result, MediaStreamVideoSource::RestartResult::IS_STOPPED);
}));
base::RunLoop().RunUntilIdle();
EXPECT_EQ(track.Source().GetReadyState(),
WebMediaStreamSource::kReadyStateLive);
mock_source()->StopSource();
base::RunLoop().RunUntilIdle();
EXPECT_EQ(track.Source().GetReadyState(),
WebMediaStreamSource::kReadyStateEnded);
}
TEST_F(MediaStreamVideoSourceTest, StartStopAndNotifyRestartSupported) {
WebMediaStreamTrack web_track = CreateTrack("123");
mock_source()->EnableStopForRestart();
mock_source()->StartMockedSource();
EXPECT_EQ(NumberOfSuccessConstraintsCallbacks(), 1);
EXPECT_EQ(web_track.Source().GetReadyState(),
WebMediaStreamSource::kReadyStateLive);
EXPECT_CALL(*this, MockNotification());
MediaStreamTrackPlatform* track =
MediaStreamTrackPlatform::GetTrack(web_track);
track->StopAndNotify(WTF::Bind(&MediaStreamVideoSourceTest::MockNotification,
base::Unretained(this)));
EXPECT_EQ(web_track.Source().GetReadyState(),
WebMediaStreamSource::kReadyStateEnded);
base::RunLoop().RunUntilIdle();
}
TEST_F(MediaStreamVideoSourceTest, StartStopAndNotifyRestartNotSupported) {
WebMediaStreamTrack web_track = CreateTrack("123");
mock_source()->DisableStopForRestart();
mock_source()->StartMockedSource();
EXPECT_EQ(NumberOfSuccessConstraintsCallbacks(), 1);
EXPECT_EQ(web_track.Source().GetReadyState(),
WebMediaStreamSource::kReadyStateLive);
EXPECT_CALL(*this, MockNotification());
MediaStreamTrackPlatform* track =
MediaStreamTrackPlatform::GetTrack(web_track);
track->StopAndNotify(WTF::Bind(&MediaStreamVideoSourceTest::MockNotification,
base::Unretained(this)));
EXPECT_EQ(web_track.Source().GetReadyState(),
WebMediaStreamSource::kReadyStateEnded);
base::RunLoop().RunUntilIdle();
}
TEST_F(MediaStreamVideoSourceTest, StopSuspendedTrack) {
WebMediaStreamTrack web_track1 = CreateTrack("123");
mock_source()->StartMockedSource();
WebMediaStreamTrack web_track2 = CreateTrack("123");
// Simulate assigning |track1| to a sink, then removing it from the sink, and
// then stopping it.
MediaStreamVideoTrack* track1 = MediaStreamVideoTrack::From(web_track1);
mock_source()->UpdateHasConsumers(track1, true);
mock_source()->UpdateHasConsumers(track1, false);
track1->Stop();
// Simulate assigning |track2| to a sink. The source should not be suspended.
MediaStreamVideoTrack* track2 = MediaStreamVideoTrack::From(web_track2);
mock_source()->UpdateHasConsumers(track2, true);
EXPECT_FALSE(mock_source()->is_suspended());
}
TEST_F(MediaStreamVideoSourceTest, AddTrackAfterStoppingSource) {
WebMediaStreamTrack web_track1 = CreateTrack("123");
mock_source()->StartMockedSource();
EXPECT_EQ(1, NumberOfSuccessConstraintsCallbacks());
EXPECT_EQ(0, NumberOfFailedConstraintsCallbacks());
MediaStreamVideoTrack* track1 = MediaStreamVideoTrack::From(web_track1);
EXPECT_CALL(*this, MockNotification());
// This is equivalent to track.stop() in JavaScript.
track1->StopAndNotify(WTF::Bind(&MediaStreamVideoSourceTest::MockNotification,
base::Unretained(this)));
WebMediaStreamTrack track2 = CreateTrack("456");
base::RunLoop().RunUntilIdle();
EXPECT_EQ(1, NumberOfSuccessConstraintsCallbacks());
EXPECT_EQ(1, NumberOfFailedConstraintsCallbacks());
}
TEST_F(MediaStreamVideoSourceTest, AddsEncodedSinkWhenEncodedConsumerAppears) {
EXPECT_CALL(*mock_source(), OnEncodedSinkEnabled).Times(1);
EXPECT_CALL(*mock_source(), OnEncodedSinkDisabled).Times(0);
WebMediaStreamTrack track = CreateTrack();
MockMediaStreamVideoSink sink;
sink.ConnectEncodedToTrack(track);
Mock::VerifyAndClearExpectations(mock_source());
sink.DisconnectEncodedFromTrack();
}
TEST_F(MediaStreamVideoSourceTest, AddsEncodedSinkWhenEncodedConsumersAppear) {
EXPECT_CALL(*mock_source(), OnEncodedSinkEnabled).Times(1);
EXPECT_CALL(*mock_source(), OnEncodedSinkDisabled).Times(0);
WebMediaStreamTrack track1 = CreateTrack();
MockMediaStreamVideoSink sink1;
sink1.ConnectEncodedToTrack(track1);
WebMediaStreamTrack track2 = CreateTrack();
MockMediaStreamVideoSink sink2;
sink2.ConnectEncodedToTrack(track2);
Mock::VerifyAndClearExpectations(mock_source());
sink1.DisconnectEncodedFromTrack();
sink2.DisconnectEncodedFromTrack();
}
TEST_F(MediaStreamVideoSourceTest,
RemovesEncodedSinkWhenEncodedConsumerDisappears) {
EXPECT_CALL(*mock_source(), OnEncodedSinkDisabled).Times(1);
WebMediaStreamTrack track = CreateTrack();
MockMediaStreamVideoSink sink;
sink.ConnectEncodedToTrack(track);
sink.DisconnectEncodedFromTrack();
}
TEST_F(MediaStreamVideoSourceTest,
RemovesEncodedSinkWhenEncodedConsumersDisappear) {
EXPECT_CALL(*mock_source(), OnEncodedSinkDisabled).Times(1);
WebMediaStreamTrack track1 = CreateTrack();
MockMediaStreamVideoSink sink1;
sink1.ConnectEncodedToTrack(track1);
WebMediaStreamTrack track2 = CreateTrack();
MockMediaStreamVideoSink sink2;
sink2.ConnectEncodedToTrack(track2);
sink1.DisconnectEncodedFromTrack();
sink2.DisconnectEncodedFromTrack();
}
TEST_F(MediaStreamVideoSourceTest, CapturingLinkSecureOnlyEncodedSinks) {
InSequence s;
EXPECT_CALL(*mock_source(), OnCapturingLinkSecured(false));
WebMediaStreamTrack track = CreateTrack();
MockMediaStreamVideoSink sink;
sink.ConnectEncodedToTrack(track);
EXPECT_CALL(*mock_source(), OnCapturingLinkSecured(true));
sink.DisconnectEncodedFromTrack();
}
TEST_F(MediaStreamVideoSourceTest, CapturingLinkSecureTracksAndEncodedSinks) {
InSequence s;
EXPECT_CALL(*mock_source(), OnCapturingLinkSecured(true));
WebMediaStreamTrack track = CreateTrack();
mock_source()->UpdateCapturingLinkSecure(MediaStreamVideoTrack::From(track),
true);
EXPECT_CALL(*mock_source(), OnCapturingLinkSecured(false));
MockMediaStreamVideoSink sink;
sink.ConnectEncodedToTrack(track);
EXPECT_CALL(*mock_source(), OnCapturingLinkSecured(true));
sink.DisconnectEncodedFromTrack();
EXPECT_CALL(*mock_source(), OnCapturingLinkSecured(false));
mock_source()->UpdateCapturingLinkSecure(MediaStreamVideoTrack::From(track),
false);
}
} // namespace blink