123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487 |
- // Copyright 2018 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 "components/mirroring/service/session.h"
- #include <memory>
- #include <string>
- #include <utility>
- #include <vector>
- #include "base/bind.h"
- #include "base/callback.h"
- #include "base/json/json_reader.h"
- #include "base/run_loop.h"
- #include "base/test/task_environment.h"
- #include "base/time/time.h"
- #include "base/values.h"
- #include "components/mirroring/service/fake_network_service.h"
- #include "components/mirroring/service/fake_video_capture_host.h"
- #include "components/mirroring/service/mirror_settings.h"
- #include "components/mirroring/service/receiver_response.h"
- #include "components/mirroring/service/value_util.h"
- #include "media/cast/test/utility/default_config.h"
- #include "media/cast/test/utility/net_utility.h"
- #include "mojo/public/cpp/bindings/pending_receiver.h"
- #include "mojo/public/cpp/bindings/pending_remote.h"
- #include "mojo/public/cpp/bindings/receiver.h"
- #include "mojo/public/cpp/bindings/remote.h"
- #include "net/base/ip_address.h"
- #include "services/viz/public/cpp/gpu/gpu.h"
- #include "testing/gmock/include/gmock/gmock.h"
- #include "testing/gtest/include/gtest/gtest.h"
- #include "third_party/openscreen/src/cast/streaming/ssrc.h"
- using media::cast::FrameSenderConfig;
- using media::cast::Packet;
- using media::mojom::RemotingSinkMetadata;
- using media::mojom::RemotingSinkMetadataPtr;
- using media::mojom::RemotingStartFailReason;
- using media::mojom::RemotingStopReason;
- using mirroring::mojom::SessionError;
- using mirroring::mojom::SessionType;
- using ::testing::_;
- using ::testing::AtLeast;
- using ::testing::InvokeWithoutArgs;
- using ::testing::Mock;
- using ::testing::NiceMock;
- namespace mirroring {
- namespace {
- constexpr int kDefaultPlayoutDelay = 400; // ms
- const openscreen::cast::Answer kAnswerWithConstraints{
- 1234,
- // Send indexes and SSRCs are set later.
- {},
- {},
- openscreen::cast::Constraints{
- openscreen::cast::AudioConstraints{44100, 2, 32000, 960000,
- std::chrono::milliseconds(4000)},
- openscreen::cast::VideoConstraints{
- 40000.0, openscreen::cast::Dimensions{320, 480, {30, 1}},
- openscreen::cast::Dimensions{1920, 1080, {60, 1}}, 300000,
- 144000000, std::chrono::milliseconds(4000)}},
- openscreen::cast::DisplayDescription{
- openscreen::cast::Dimensions{1280, 720, {60, 1}},
- openscreen::cast::AspectRatio{16, 9},
- openscreen::cast::AspectRatioConstraint::kFixed,
- },
- };
- class MockRemotingSource : public media::mojom::RemotingSource {
- public:
- MockRemotingSource() {}
- ~MockRemotingSource() override {}
- void Bind(mojo::PendingReceiver<media::mojom::RemotingSource> receiver) {
- receiver_.Bind(std::move(receiver));
- }
- MOCK_METHOD0(OnSinkGone, void());
- MOCK_METHOD0(OnStarted, void());
- MOCK_METHOD1(OnStartFailed, void(RemotingStartFailReason));
- MOCK_METHOD1(OnMessageFromSink, void(const std::vector<uint8_t>&));
- MOCK_METHOD1(OnStopped, void(RemotingStopReason));
- MOCK_METHOD1(OnSinkAvailable, void(const RemotingSinkMetadata&));
- void OnSinkAvailable(RemotingSinkMetadataPtr metadata) override {
- OnSinkAvailable(*metadata);
- }
- private:
- mojo::Receiver<media::mojom::RemotingSource> receiver_{this};
- };
- } // namespace
- class SessionTest : public mojom::ResourceProvider,
- public mojom::SessionObserver,
- public mojom::CastMessageChannel,
- public ::testing::Test {
- public:
- SessionTest() = default;
- SessionTest(const SessionTest&) = delete;
- SessionTest& operator=(const SessionTest&) = delete;
- ~SessionTest() override { task_environment_.RunUntilIdle(); }
- protected:
- // mojom::SessionObserver implemenation.
- MOCK_METHOD1(OnError, void(SessionError));
- MOCK_METHOD0(DidStart, void());
- MOCK_METHOD0(DidStop, void());
- MOCK_METHOD1(LogInfoMessage, void(const std::string&));
- MOCK_METHOD1(LogErrorMessage, void(const std::string&));
- MOCK_METHOD0(OnGetVideoCaptureHost, void());
- MOCK_METHOD0(OnGetNetworkContext, void());
- MOCK_METHOD0(OnCreateAudioStream, void());
- MOCK_METHOD0(OnConnectToRemotingSource, void());
- // Called when sends an outbound message.
- MOCK_METHOD1(OnOutboundMessage, void(const std::string& message_type));
- MOCK_METHOD0(OnInitDone, void());
- // mojom::CastMessageHandler implementation. For outbound messages.
- void Send(mojom::CastMessagePtr message) override {
- EXPECT_TRUE(message->message_namespace == mojom::kWebRtcNamespace ||
- message->message_namespace == mojom::kRemotingNamespace);
- absl::optional<base::Value> value =
- base::JSONReader::Read(message->json_format_data);
- ASSERT_TRUE(value);
- std::string message_type;
- EXPECT_TRUE(GetString(*value, "type", &message_type));
- if (message_type == "OFFER") {
- EXPECT_TRUE(GetInt(*value, "seqNum", &offer_sequence_number_));
- base::Value::Dict* offer = value->GetDict().FindDict("offer");
- ASSERT_TRUE(offer);
- base::Value* raw_streams = offer->Find("supportedStreams");
- if (raw_streams) {
- for (auto& value : raw_streams->GetList()) {
- EXPECT_EQ(*value.GetDict().FindInt("targetDelay"),
- target_playout_delay_ms_);
- }
- }
- } else if (message_type == "GET_CAPABILITIES") {
- EXPECT_TRUE(GetInt(*value, "seqNum", &capability_sequence_number_));
- }
- OnOutboundMessage(message_type);
- }
- // mojom::ResourceProvider implemenation.
- void BindGpu(mojo::PendingReceiver<viz::mojom::Gpu> receiver) override {}
- void GetVideoCaptureHost(
- mojo::PendingReceiver<media::mojom::VideoCaptureHost> receiver) override {
- video_host_ =
- std::make_unique<NiceMock<FakeVideoCaptureHost>>(std::move(receiver));
- OnGetVideoCaptureHost();
- }
- void GetNetworkContext(
- mojo::PendingReceiver<network::mojom::NetworkContext> receiver) override {
- network_context_ =
- std::make_unique<NiceMock<MockNetworkContext>>(std::move(receiver));
- OnGetNetworkContext();
- }
- void CreateAudioStream(
- mojo::PendingRemote<mojom::AudioStreamCreatorClient> client,
- const media::AudioParameters& params,
- uint32_t total_segments) override {
- OnCreateAudioStream();
- }
- void ConnectToRemotingSource(
- mojo::PendingRemote<media::mojom::Remoter> remoter,
- mojo::PendingReceiver<media::mojom::RemotingSource> receiver) override {
- remoter_.Bind(std::move(remoter));
- remoting_source_.Bind(std::move(receiver));
- OnConnectToRemotingSource();
- }
- void SendAnswer() {
- ASSERT_TRUE(session_);
- std::vector<FrameSenderConfig> audio_configs;
- std::vector<FrameSenderConfig> video_configs;
- if (session_type_ != SessionType::VIDEO_ONLY) {
- if (cast_mode_ == "remoting") {
- audio_configs.emplace_back(MirrorSettings::GetDefaultAudioConfig(
- media::cast::RtpPayloadType::REMOTE_AUDIO,
- media::cast::Codec::CODEC_AUDIO_REMOTE));
- } else {
- EXPECT_EQ("mirroring", cast_mode_);
- audio_configs.emplace_back(MirrorSettings::GetDefaultAudioConfig(
- media::cast::RtpPayloadType::AUDIO_OPUS,
- media::cast::Codec::CODEC_AUDIO_OPUS));
- }
- }
- if (session_type_ != SessionType::AUDIO_ONLY) {
- if (cast_mode_ == "remoting") {
- video_configs.emplace_back(MirrorSettings::GetDefaultVideoConfig(
- media::cast::RtpPayloadType::REMOTE_VIDEO,
- media::cast::Codec::CODEC_VIDEO_REMOTE));
- } else {
- EXPECT_EQ("mirroring", cast_mode_);
- video_configs.emplace_back(MirrorSettings::GetDefaultVideoConfig(
- media::cast::RtpPayloadType::VIDEO_VP8,
- media::cast::Codec::CODEC_VIDEO_VP8));
- }
- }
- std::unique_ptr<openscreen::cast::Answer> answer;
- if (answer_) {
- answer.swap(answer_);
- } else {
- answer = std::make_unique<openscreen::cast::Answer>();
- }
- answer->udp_port = receiver_endpoint_.port();
- const int number_of_configs = audio_configs.size() + video_configs.size();
- for (int i = 0; i < number_of_configs; ++i) {
- answer->send_indexes.push_back(i);
- answer->ssrcs.push_back(31 + i); // Arbitrary receiver SSRCs.
- }
- auto response = ReceiverResponse::CreateAnswerResponseForTesting(
- offer_sequence_number_, std::move(answer));
- session_->OnAnswer(audio_configs, video_configs, response);
- task_environment_.RunUntilIdle();
- }
- Session::AsyncInitializeDoneCB MakeInitDoneCB() {
- return base::BindOnce(&SessionTest::OnInitDone, base::Unretained(this));
- }
- // Create a mirroring session. Expect to send OFFER message.
- void CreateSession(SessionType session_type) {
- session_type_ = session_type;
- mojom::SessionParametersPtr session_params =
- mojom::SessionParameters::New();
- session_params->receiver_address = receiver_endpoint_.address();
- session_params->type = session_type_;
- session_params->receiver_model_name = "Chromecast";
- if (target_playout_delay_ms_ != kDefaultPlayoutDelay) {
- session_params->target_playout_delay =
- base::Milliseconds(target_playout_delay_ms_);
- }
- cast_mode_ = "mirroring";
- mojo::PendingRemote<mojom::ResourceProvider> resource_provider_remote;
- mojo::PendingRemote<mojom::SessionObserver> session_observer_remote;
- mojo::PendingRemote<mojom::CastMessageChannel> outbound_channel_remote;
- resource_provider_receiver_.Bind(
- resource_provider_remote.InitWithNewPipeAndPassReceiver());
- session_observer_receiver_.Bind(
- session_observer_remote.InitWithNewPipeAndPassReceiver());
- outbound_channel_receiver_.Bind(
- outbound_channel_remote.InitWithNewPipeAndPassReceiver());
- // Expect to send OFFER message when session is created.
- EXPECT_CALL(*this, OnGetNetworkContext()).Times(1);
- EXPECT_CALL(*this, OnError(_)).Times(0);
- EXPECT_CALL(*this, OnOutboundMessage("OFFER")).Times(1);
- EXPECT_CALL(*this, OnInitDone()).Times(1);
- session_ = std::make_unique<Session>(
- std::move(session_params), gfx::Size(1920, 1080),
- std::move(session_observer_remote), std::move(resource_provider_remote),
- std::move(outbound_channel_remote),
- inbound_channel_.BindNewPipeAndPassReceiver(), nullptr);
- session_->AsyncInitialize(MakeInitDoneCB());
- task_environment_.RunUntilIdle();
- Mock::VerifyAndClear(this);
- }
- // Starts the mirroring session.
- void StartSession() {
- ASSERT_EQ(cast_mode_, "mirroring");
- // Except mirroing session starts after receiving ANSWER message.
- const int num_to_get_video_host =
- session_type_ == SessionType::AUDIO_ONLY ? 0 : 1;
- const int num_to_create_audio_stream =
- session_type_ == SessionType::VIDEO_ONLY ? 0 : 1;
- EXPECT_CALL(*this, OnGetVideoCaptureHost()).Times(num_to_get_video_host);
- EXPECT_CALL(*this, OnCreateAudioStream()).Times(num_to_create_audio_stream);
- EXPECT_CALL(*this, OnError(_)).Times(0);
- EXPECT_CALL(*this, OnOutboundMessage("GET_CAPABILITIES")).Times(1);
- EXPECT_CALL(*this, DidStart()).Times(1);
- SendAnswer();
- task_environment_.RunUntilIdle();
- Mock::VerifyAndClear(this);
- }
- void StopSession() {
- if (video_host_)
- EXPECT_CALL(*video_host_, OnStopped()).Times(1);
- EXPECT_CALL(*this, DidStop()).Times(1);
- session_.reset();
- task_environment_.RunUntilIdle();
- Mock::VerifyAndClear(this);
- }
- void CaptureOneVideoFrame() {
- ASSERT_EQ(cast_mode_, "mirroring");
- ASSERT_TRUE(video_host_);
- // Expect to send out some UDP packets.
- EXPECT_CALL(*network_context_->udp_socket(), OnSend()).Times(AtLeast(1));
- EXPECT_CALL(*video_host_, ReleaseBuffer(_, _, _)).Times(1);
- // Send one video frame to the consumer.
- video_host_->SendOneFrame(gfx::Size(64, 32), base::TimeTicks::Now());
- task_environment_.RunUntilIdle();
- Mock::VerifyAndClear(network_context_.get());
- Mock::VerifyAndClear(video_host_.get());
- }
- void SignalAnswerTimeout() {
- if (cast_mode_ == "mirroring") {
- EXPECT_CALL(*this, DidStop()).Times(1);
- EXPECT_CALL(*this, OnError(SessionError::ANSWER_TIME_OUT)).Times(1);
- } else {
- EXPECT_CALL(*this, DidStop()).Times(0);
- EXPECT_CALL(*this, OnError(SessionError::ANSWER_TIME_OUT)).Times(0);
- // Expect to send OFFER message to fallback on mirroring.
- EXPECT_CALL(*this, OnOutboundMessage("OFFER")).Times(1);
- // The start of remoting is expected to fail.
- EXPECT_CALL(remoting_source_,
- OnStartFailed(RemotingStartFailReason::INVALID_ANSWER_MESSAGE))
- .Times(1);
- EXPECT_CALL(remoting_source_, OnSinkGone()).Times(AtLeast(1));
- }
- session_->OnAnswer(std::vector<FrameSenderConfig>(),
- std::vector<FrameSenderConfig>(), ReceiverResponse());
- task_environment_.RunUntilIdle();
- cast_mode_ = "mirroring";
- Mock::VerifyAndClear(this);
- Mock::VerifyAndClear(&remoting_source_);
- }
- void SendRemotingCapabilities() {
- EXPECT_CALL(*this, OnConnectToRemotingSource()).Times(1);
- EXPECT_CALL(remoting_source_, OnSinkAvailable(_)).Times(1);
- auto capabilities = std::make_unique<ReceiverCapability>();
- capabilities->remoting = 2;
- capabilities->media_caps =
- std::vector<std::string>({"video", "audio", "vp8", "opus"});
- auto response = ReceiverResponse::CreateCapabilitiesResponseForTesting(
- capability_sequence_number_, std::move(capabilities));
- session_->OnCapabilitiesResponse(response);
- task_environment_.RunUntilIdle();
- Mock::VerifyAndClear(this);
- Mock::VerifyAndClear(&remoting_source_);
- }
- void StartRemoting() {
- base::RunLoop run_loop;
- ASSERT_TRUE(remoter_.is_bound());
- // GET_CAPABILITIES is only sent once at the start of mirroring.
- EXPECT_CALL(*this, OnOutboundMessage("GET_CAPABILITIES")).Times(0);
- EXPECT_CALL(*this, OnOutboundMessage("OFFER"))
- .WillOnce(InvokeWithoutArgs(&run_loop, &base::RunLoop::Quit));
- remoter_->Start();
- run_loop.Run();
- task_environment_.RunUntilIdle();
- cast_mode_ = "remoting";
- Mock::VerifyAndClear(this);
- }
- void RemotingStarted() {
- ASSERT_EQ(cast_mode_, "remoting");
- EXPECT_CALL(remoting_source_, OnStarted()).Times(1);
- SendAnswer();
- task_environment_.RunUntilIdle();
- Mock::VerifyAndClear(this);
- Mock::VerifyAndClear(&remoting_source_);
- }
- void StopRemoting() {
- ASSERT_EQ(cast_mode_, "remoting");
- const RemotingStopReason reason = RemotingStopReason::LOCAL_PLAYBACK;
- // Expect to send OFFER message to fallback on mirroring.
- EXPECT_CALL(*this, OnOutboundMessage("OFFER")).Times(1);
- EXPECT_CALL(remoting_source_, OnStopped(reason)).Times(1);
- remoter_->Stop(reason);
- task_environment_.RunUntilIdle();
- cast_mode_ = "mirroring";
- Mock::VerifyAndClear(this);
- Mock::VerifyAndClear(&remoting_source_);
- }
- void SetTargetPlayoutDelay(int target_playout_delay_ms) {
- target_playout_delay_ms_ = target_playout_delay_ms;
- }
- void SetAnswer(std::unique_ptr<openscreen::cast::Answer> answer) {
- answer_ = std::move(answer);
- }
- private:
- base::test::TaskEnvironment task_environment_;
- const net::IPEndPoint receiver_endpoint_ =
- media::cast::test::GetFreeLocalPort();
- mojo::Receiver<mojom::ResourceProvider> resource_provider_receiver_{this};
- mojo::Receiver<mojom::SessionObserver> session_observer_receiver_{this};
- mojo::Receiver<mojom::CastMessageChannel> outbound_channel_receiver_{this};
- mojo::Remote<mojom::CastMessageChannel> inbound_channel_;
- SessionType session_type_ = SessionType::AUDIO_AND_VIDEO;
- mojo::Remote<media::mojom::Remoter> remoter_;
- NiceMock<MockRemotingSource> remoting_source_;
- std::string cast_mode_;
- int32_t offer_sequence_number_ = -1;
- int32_t capability_sequence_number_ = -1;
- int32_t target_playout_delay_ms_ = kDefaultPlayoutDelay;
- std::unique_ptr<Session> session_;
- std::unique_ptr<FakeVideoCaptureHost> video_host_;
- std::unique_ptr<MockNetworkContext> network_context_;
- std::unique_ptr<openscreen::cast::Answer> answer_;
- };
- TEST_F(SessionTest, AudioOnlyMirroring) {
- CreateSession(SessionType::AUDIO_ONLY);
- StartSession();
- StopSession();
- }
- TEST_F(SessionTest, VideoOnlyMirroring) {
- SetTargetPlayoutDelay(1000);
- CreateSession(SessionType::VIDEO_ONLY);
- StartSession();
- CaptureOneVideoFrame();
- StopSession();
- }
- TEST_F(SessionTest, AudioAndVideoMirroring) {
- SetTargetPlayoutDelay(150);
- CreateSession(SessionType::AUDIO_AND_VIDEO);
- StartSession();
- StopSession();
- }
- TEST_F(SessionTest, AnswerWithConstraints) {
- SetAnswer(std::make_unique<openscreen::cast::Answer>(kAnswerWithConstraints));
- CreateSession(SessionType::AUDIO_AND_VIDEO);
- StartSession();
- StopSession();
- }
- TEST_F(SessionTest, AnswerTimeout) {
- CreateSession(SessionType::AUDIO_AND_VIDEO);
- SignalAnswerTimeout();
- }
- TEST_F(SessionTest, SwitchToAndFromRemoting) {
- CreateSession(SessionType::AUDIO_AND_VIDEO);
- StartSession();
- SendRemotingCapabilities();
- StartRemoting();
- RemotingStarted();
- StopRemoting();
- StopSession();
- }
- TEST_F(SessionTest, StopSessionWhileRemoting) {
- CreateSession(SessionType::AUDIO_AND_VIDEO);
- StartSession();
- SendRemotingCapabilities();
- StartRemoting();
- RemotingStarted();
- StopSession();
- }
- TEST_F(SessionTest, StartRemotingFailed) {
- CreateSession(SessionType::AUDIO_AND_VIDEO);
- StartSession();
- SendRemotingCapabilities();
- StartRemoting();
- SignalAnswerTimeout();
- // Resume mirroring.
- SendAnswer();
- CaptureOneVideoFrame();
- StopSession();
- }
- } // namespace mirroring
|