Skip to content

Commit

Permalink
[FreezeCast] Add MirroringMediaControllerHost
Browse files Browse the repository at this point in the history
Add a basic MirroringMediaControllerHost, which is a host object for
a MediaController that interacts with mirroring media.

Bug: b/271440459
Change-Id: Iaf7fae8737893e66c070ebda1107a16f58f86692
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4309820
Reviewed-by: Takumi Fujimoto <takumif@chromium.org>
Commit-Queue: Benjamin Zielinski <bzielinski@google.com>
Cr-Commit-Position: refs/heads/main@{#1115809}
  • Loading branch information
Benjamin Zielinski authored and Chromium LUCI CQ committed Mar 10, 2023
1 parent 16952f5 commit ccfe0e2
Show file tree
Hide file tree
Showing 12 changed files with 223 additions and 6 deletions.
65 changes: 65 additions & 0 deletions chrome/browser/media/router/mojo/media_router_mojo_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,29 @@ DesktopMediaPickerController::Params MakeDesktopPickerParams(
return params;
}

// Returns a vector of media routes that are in |routes_a| and not in
// |routes_b|. Compares routes only by route id, and returns the version of
// the routes from |routes_a|.
std::vector<MediaRoute> GetRouteSetDifference(
std::vector<MediaRoute> routes_a,
std::vector<MediaRoute> routes_b) {
std::vector<MediaRoute> routes;
for (auto route_a : routes_a) {
bool route_seen = false;
for (auto route_b : routes_b) {
if (route_a.media_route_id() == route_b.media_route_id()) {
route_seen = true;
}
}

if (!route_seen) {
routes.emplace_back(route_a);
}
}

return routes;
}

} // namespace

MediaRouterMojoImpl::MediaRoutesQuery::MediaRoutesQuery() = default;
Expand Down Expand Up @@ -163,6 +186,24 @@ void MediaRouterMojoImpl::OnRoutesUpdated(
mojom::MediaRouteProviderId provider_id,
const std::vector<MediaRoute>& routes) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);

auto current_routes = GetCurrentRoutes();

std::vector<MediaRoute> added_routes =
GetRouteSetDifference(routes, current_routes);
std::vector<MediaRoute> removed_routes =
GetRouteSetDifference(current_routes, routes);

for (const auto& route : added_routes) {
if (route.IsLocalMirroringRoute()) {
AddMirroringMediaControllerHost(route);
}
}

for (const auto& route : removed_routes) {
mirroring_media_controller_hosts_.erase(route.media_route_id());
}

routes_query_.SetRoutesForProvider(provider_id, routes);
routes_query_.NotifyObservers();
}
Expand Down Expand Up @@ -372,6 +413,17 @@ MediaRouterMojoImpl::GetFlingingController(const MediaRoute::Id& route_id) {
return nullptr;
}

MirroringMediaControllerHost*
MediaRouterMojoImpl::GetMirroringMediaControllerHost(
const MediaRoute::Id& route_id) {
auto it = mirroring_media_controller_hosts_.find(route_id);
if (it != mirroring_media_controller_hosts_.end()) {
return it->second.get();
} else {
return nullptr;
}
}

void MediaRouterMojoImpl::GetMediaController(
const MediaRoute::Id& route_id,
mojo::PendingReceiver<mojom::MediaController> controller,
Expand Down Expand Up @@ -731,6 +783,19 @@ void MediaRouterMojoImpl::OnMediaControllerCreated(
MediaRouterMojoMetrics::RecordMediaRouteControllerCreationResult(success);
}

void MediaRouterMojoImpl::AddMirroringMediaControllerHost(
const MediaRoute& route) {
mojo::Remote<media_router::mojom::MediaController> controller_remote;
mojo::PendingReceiver<media_router::mojom::MediaController>
controller_receiver = controller_remote.BindNewPipeAndPassReceiver();
auto host = std::make_unique<MirroringMediaControllerHost>(
std::move(controller_remote));
auto observer_remote = host->GetMediaStatusObserverPendingRemote();
GetMediaController(route.media_route_id(), std::move(controller_receiver),
std::move(observer_remote));
mirroring_media_controller_hosts_[route.media_route_id()] = std::move(host);
}

void MediaRouterMojoImpl::OnProviderConnectionError(
mojom::MediaRouteProviderId provider_id) {
media_route_providers_.erase(provider_id);
Expand Down
8 changes: 8 additions & 0 deletions chrome/browser/media/router/mojo/media_router_mojo_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include "components/media_router/browser/media_router_base.h"
#include "components/media_router/browser/media_router_debugger.h"
#include "components/media_router/browser/media_routes_observer.h"
#include "components/media_router/browser/mirroring_media_controller_host.h"
#include "components/media_router/common/issue.h"
#include "components/media_router/common/mojom/logger.mojom.h"
#include "components/media_router/common/mojom/media_router.mojom.h"
Expand Down Expand Up @@ -85,6 +86,8 @@ class MediaRouterMojoImpl : public MediaRouterBase,
std::vector<MediaRoute> GetCurrentRoutes() const override;
std::unique_ptr<media::FlingingController> GetFlingingController(
const MediaRoute::Id& route_id) override;
MirroringMediaControllerHost* GetMirroringMediaControllerHost(
const MediaRoute::Id& route_id) override;
void GetMediaController(
const MediaRoute::Id& route_id,
mojo::PendingReceiver<mojom::MediaController> controller,
Expand Down Expand Up @@ -363,6 +366,8 @@ class MediaRouterMojoImpl : public MediaRouterBase,
// Callback called by MRP's CreateMediaRouteController().
void OnMediaControllerCreated(const MediaRoute::Id& route_id, bool success);

void AddMirroringMediaControllerHost(const MediaRoute& route);

// Method for obtaining a pointer to the provider associated with the given
// object. Returns a nullopt when such a provider is not found.
absl::optional<mojom::MediaRouteProviderId> GetProviderIdForSink(
Expand Down Expand Up @@ -422,6 +427,9 @@ class MediaRouterMojoImpl : public MediaRouterBase,
std::unique_ptr<PresentationConnectionMessageObserverList>>
message_observers_;

base::flat_map<MediaRoute::Id, std::unique_ptr<MirroringMediaControllerHost>>
mirroring_media_controller_hosts_;

// Receivers for Mojo remotes to |this| held by media route providers.
mojo::ReceiverSet<mojom::MediaRouter> receivers_;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1095,11 +1095,33 @@ TEST_F(MediaRouterMojoImplTest, TestGetMirroringStatsNoProviderId) {
}));
}

// TODO(gbj): Add a test case where a nonempty dict is returned. Probably need
// to create a setter in MockMediaRouteProvider.
TEST_F(MediaRouterMojoImplTest, TestGetMirroringStats) {
ProvideTestRoute(mojom::MediaRouteProviderId::CAST, kRouteId);
router()->GetMirroringStats(kRouteId, base::BindOnce([](base::Value dict) {
EXPECT_EQ(base::Value(), dict);
}));
}

TEST_F(MediaRouterMojoImplTest, GetMirroringMediaControllerHost) {
MediaSource tab_source(kTabSourceOne);
std::vector<MediaRoute> local_mirroring_routes{
MediaRoute(kRouteId, tab_source, kSinkId, kDescription, true)};
UpdateRoutes(mojom::MediaRouteProviderId::CAST, local_mirroring_routes);

// Expect the host to exist for a local mirroring source.
EXPECT_NE(nullptr, router()->GetMirroringMediaControllerHost(kRouteId));

std::vector<MediaRoute> nonlocal_mirroring_routes{
MediaRoute(kRouteId2, tab_source, kSinkId, kDescription, false)};
UpdateRoutes(mojom::MediaRouteProviderId::CAST, nonlocal_mirroring_routes);

// Expect that the host for kRouteId no longer exists.
EXPECT_EQ(nullptr, router()->GetMirroringMediaControllerHost(kRouteId));

// Expect that no host for kRouteId2 exists, as it is not a local source.
EXPECT_EQ(nullptr, router()->GetMirroringMediaControllerHost(kRouteId2));
}

} // namespace media_router
3 changes: 3 additions & 0 deletions components/media_router/browser/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ source_set("browser") {
"media_routes_observer.h",
"media_sinks_observer.cc",
"media_sinks_observer.h",
"mirroring_media_controller_host.cc",
"mirroring_media_controller_host.h",
"presentation/browser_presentation_connection_proxy.cc",
"presentation/browser_presentation_connection_proxy.h",
"presentation/local_presentation_manager.cc",
Expand Down Expand Up @@ -123,6 +125,7 @@ source_set("unit_tests") {
"media_router_factory_unittest.cc",
"media_router_metrics_unittest.cc",
"media_sinks_observer_unittest.cc",
"mirroring_media_controller_host_unittest.cc",
"presentation/browser_presentation_connection_proxy_unittest.cc",
"presentation/local_presentation_manager_unittest.cc",
"presentation/presentation_media_sinks_observer_unittest.cc",
Expand Down
6 changes: 6 additions & 0 deletions components/media_router/browser/media_router.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include "base/values.h"
#include "build/build_config.h"
#include "components/keyed_service/core/keyed_service.h"
#include "components/media_router/browser/mirroring_media_controller_host.h"
#include "components/media_router/browser/presentation_connection_message_observer.h"
#include "components/media_router/common/media_route.h"
#include "components/media_router/common/media_route_provider_helper.h"
Expand Down Expand Up @@ -158,6 +159,11 @@ class MediaRouter : public KeyedService {
const MediaRoute::Id& route_id) = 0;

#if !BUILDFLAG(IS_ANDROID)
// Returns a pointer to a controller host that sends media commands related to
// mirroring within a route.
virtual MirroringMediaControllerHost* GetMirroringMediaControllerHost(
const MediaRoute::Id& route_id) = 0;

// Returns the IssueManager owned by the MediaRouter. Guaranteed to be
// non-null.
virtual IssueManager* GetIssueManager() = 0;
Expand Down
23 changes: 23 additions & 0 deletions components/media_router/browser/mirroring_media_controller_host.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "components/media_router/browser/mirroring_media_controller_host.h"

namespace media_router {

MirroringMediaControllerHost::MirroringMediaControllerHost(
mojo::Remote<media_router::mojom::MediaController> mirroring_controller)
: mirroring_controller_(std::move(mirroring_controller)) {}

MirroringMediaControllerHost::~MirroringMediaControllerHost() = default;

mojo::PendingRemote<media_router::mojom::MediaStatusObserver>
MirroringMediaControllerHost::GetMediaStatusObserverPendingRemote() {
return observer_receiver_.BindNewPipeAndPassRemote();
}

void MirroringMediaControllerHost::OnMediaStatusUpdated(
media_router::mojom::MediaStatusPtr status) {}

} // namespace media_router
40 changes: 40 additions & 0 deletions components/media_router/browser/mirroring_media_controller_host.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef COMPONENTS_MEDIA_ROUTER_BROWSER_MIRRORING_MEDIA_CONTROLLER_HOST_H_
#define COMPONENTS_MEDIA_ROUTER_BROWSER_MIRRORING_MEDIA_CONTROLLER_HOST_H_

#include "components/media_router/common/media_route.h"
#include "components/media_router/common/mojom/media_controller.mojom.h"
#include "components/media_router/common/mojom/media_status.mojom.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "mojo/public/cpp/bindings/remote.h"

namespace media_router {

class MirroringMediaControllerHost : public mojom::MediaStatusObserver {
public:
explicit MirroringMediaControllerHost(
mojo::Remote<media_router::mojom::MediaController> mirroring_controller);
MirroringMediaControllerHost(const MirroringMediaControllerHost&) = delete;
MirroringMediaControllerHost& operator=(const MirroringMediaControllerHost&) =
delete;
~MirroringMediaControllerHost() override;

mojo::PendingRemote<media_router::mojom::MediaStatusObserver>
GetMediaStatusObserverPendingRemote();

// mojom::MediaStatusObserver:
void OnMediaStatusUpdated(
media_router::mojom::MediaStatusPtr status) override;

private:
mojo::Remote<media_router::mojom::MediaController> mirroring_controller_;
mojo::Receiver<media_router::mojom::MediaStatusObserver> observer_receiver_{
this};
};

} // namespace media_router

#endif // COMPONENTS_MEDIA_ROUTER_BROWSER_MIRRORING_MEDIA_CONTROLLER_HOST_H_
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "components/media_router/browser/mirroring_media_controller_host.h"

#include "base/test/task_environment.h"
#include "components/media_router/common/media_route.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace media_router {

TEST(MirroringMediaControllerHostTest, GetMediaStatusObserverPendingRemote) {
base::test::SingleThreadTaskEnvironment task_environment;
mojo::Remote<mojom::MediaController> controller_remote;
MirroringMediaControllerHost host(std::move(controller_remote));
auto observer_remote = host.GetMediaStatusObserverPendingRemote();
EXPECT_TRUE(observer_remote.is_valid());
}

} // namespace media_router
2 changes: 2 additions & 0 deletions components/media_router/browser/test/mock_media_router.h
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,8 @@ class MockMediaRouter : public MediaRouterBase {
const MediaRoute::Id& route_id));

#if !BUILDFLAG(IS_ANDROID)
MOCK_METHOD1(GetMirroringMediaControllerHost,
MirroringMediaControllerHost*(const MediaRoute::Id& route_id));
IssueManager* GetIssueManager() override { return &issue_manager_; }
MOCK_METHOD3(GetMediaController,
void(const MediaRoute::Id& route_id,
Expand Down
6 changes: 6 additions & 0 deletions components/media_router/common/media_route.cc
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,12 @@ MediaRoute& MediaRoute::operator=(MediaRoute&&) = default;

MediaRoute::~MediaRoute() = default;

bool MediaRoute::IsLocalMirroringRoute() const {
// TODO(b/272368609): Update this to include site-intiated mirroring sources.
return is_local_ && (media_source_.IsTabMirroringSource() ||
media_source_.IsDesktopMirroringSource());
}

bool MediaRoute::operator==(const MediaRoute& other) const {
return media_route_id_ == other.media_route_id_ &&
presentation_id_ == other.presentation_id_ &&
Expand Down
2 changes: 2 additions & 0 deletions components/media_router/common/media_route.h
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,8 @@ class MediaRoute {
void set_is_connecting(bool is_connecting) { is_connecting_ = is_connecting; }
bool is_connecting() const { return is_connecting_; }

bool IsLocalMirroringRoute() const;

bool operator==(const MediaRoute& other) const;

private:
Expand Down
31 changes: 25 additions & 6 deletions components/media_router/common/media_route_unittest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -14,38 +14,42 @@ constexpr char kRouteId1[] =
constexpr char kRouteId2[] =
"urn:x-org.chromium:media:route:2/cast-sink2/http://foo.com";
constexpr char kPresentationUrl[] = "http://www.example.com/presentation.html";
constexpr char kDescription[] = "Description";
constexpr char kSource[] = "not-a-mirroring_source";
constexpr char kTabSource[] = "urn:x-org.chromium.media:source:tab:1";
constexpr char kSinkId[] = "sinkId";
} // namespace

namespace media_router {

TEST(MediaRouteTest, TestEquals) {
const MediaSource& media_source =
MediaSource::ForPresentationUrl(GURL(kPresentationUrl));
MediaRoute route1(kRouteId1, media_source, "sinkId", "Description", false);
MediaRoute route1(kRouteId1, media_source, kSinkId, kDescription, false);

MediaRoute route1_copy(route1);
EXPECT_EQ(route1, route1_copy);

// Same as route1 with different sink ID.
MediaRoute route2(kRouteId1, media_source, "differentSinkId", "Description",
MediaRoute route2(kRouteId1, media_source, "differentSinkId", kDescription,
false);
EXPECT_FALSE(route1 == route2);

// Same as route1 with different description.
MediaRoute route3(kRouteId1, media_source, "sinkId", "differentDescription",
MediaRoute route3(kRouteId1, media_source, kSinkId, "differentDescription",
false);
EXPECT_FALSE(route1 == route3);

// Same as route1 with different is_local.
MediaRoute route4(kRouteId1, media_source, "sinkId", "Description", true);
MediaRoute route4(kRouteId1, media_source, kSinkId, kDescription, true);
EXPECT_FALSE(route1 == route4);

// The ID is different from route1's.
MediaRoute route5(kRouteId2, media_source, "sinkId", "Description", false);
MediaRoute route5(kRouteId2, media_source, kSinkId, kDescription, false);
EXPECT_FALSE(route1 == route5);

// Same as route1 with different off_the_record.
MediaRoute route6(kRouteId1, media_source, "sinkId", "Description", true);
MediaRoute route6(kRouteId1, media_source, kSinkId, kDescription, true);
route6.set_off_the_record(true);
EXPECT_FALSE(route1 == route6);
}
Expand Down Expand Up @@ -73,4 +77,19 @@ TEST(MediaRouteTest, TestParsingMediaRouteId) {
"");
}

TEST(MediaRouteTest, TestIsLocalMirroringRoute) {
MediaRoute local_nonmirroring_route(kRouteId1, MediaSource(kSource), kSinkId,
kDescription, /*is_local=*/true);
EXPECT_FALSE(local_nonmirroring_route.IsLocalMirroringRoute());

MediaRoute nonlocal_mirroring_route(kRouteId1, MediaSource(kTabSource),
kSinkId, kDescription,
/*is_local=*/false);
EXPECT_FALSE(nonlocal_mirroring_route.IsLocalMirroringRoute());

MediaRoute local_mirroring_route(kRouteId1, MediaSource(kTabSource), kSinkId,
kDescription, /*is_local=*/true);
EXPECT_TRUE(local_mirroring_route.IsLocalMirroringRoute());
}

} // namespace media_router

0 comments on commit ccfe0e2

Please sign in to comment.