1 // Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
2 // reserved. Use of this source code is governed by a BSD-style license that
3 // can be found in the LICENSE file.
4
5 #include "libcef/browser/media_router/media_router_manager.h"
6
7 #include "libcef/browser/browser_context.h"
8 #include "libcef/browser/thread_util.h"
9
10 #include "components/media_router/browser/media_router_factory.h"
11 #include "components/media_router/browser/media_routes_observer.h"
12 #include "components/media_router/browser/route_message_observer.h"
13 #include "components/media_router/browser/route_message_util.h"
14
15 namespace {
16
17 const int kTimeoutMs = 5 * 1000;
18 const char kDefaultPresentationUrl[] = "https://google.com";
19
20 } // namespace
21
22 class CefMediaRoutesObserver : public media_router::MediaRoutesObserver {
23 public:
CefMediaRoutesObserver(CefMediaRouterManager * manager)24 explicit CefMediaRoutesObserver(CefMediaRouterManager* manager)
25 : media_router::MediaRoutesObserver(manager->GetMediaRouter()),
26 manager_(manager) {}
27
28 CefMediaRoutesObserver(const CefMediaRoutesObserver&) = delete;
29 CefMediaRoutesObserver& operator=(const CefMediaRoutesObserver&) = delete;
30
OnRoutesUpdated(const std::vector<media_router::MediaRoute> & routes)31 void OnRoutesUpdated(
32 const std::vector<media_router::MediaRoute>& routes) override {
33 manager_->routes_ = routes;
34 manager_->NotifyCurrentRoutes();
35 }
36
37 private:
38 CefMediaRouterManager* const manager_;
39 };
40
41 // Used to receive messages if PresentationConnection is not supported.
42 class CefRouteMessageObserver : public media_router::RouteMessageObserver {
43 public:
CefRouteMessageObserver(CefMediaRouterManager * manager,const media_router::MediaRoute & route)44 CefRouteMessageObserver(CefMediaRouterManager* manager,
45 const media_router::MediaRoute& route)
46 : media_router::RouteMessageObserver(manager->GetMediaRouter(),
47 route.media_route_id()),
48 manager_(manager),
49 route_(route) {}
50
51 CefRouteMessageObserver(const CefRouteMessageObserver&) = delete;
52 CefRouteMessageObserver& operator=(const CefRouteMessageObserver&) = delete;
53
OnMessagesReceived(CefMediaRouterManager::MediaMessageVector messages)54 void OnMessagesReceived(
55 CefMediaRouterManager::MediaMessageVector messages) override {
56 manager_->OnMessagesReceived(route_, messages);
57 }
58
59 private:
60 CefMediaRouterManager* const manager_;
61 const media_router::MediaRoute route_;
62 };
63
64 // Used for messaging and route status notifications with Cast.
65 class CefPresentationConnection : public blink::mojom::PresentationConnection {
66 public:
CefPresentationConnection(CefMediaRouterManager * manager,const media_router::MediaRoute & route,media_router::mojom::RoutePresentationConnectionPtr connections)67 explicit CefPresentationConnection(
68 CefMediaRouterManager* manager,
69 const media_router::MediaRoute& route,
70 media_router::mojom::RoutePresentationConnectionPtr connections)
71 : manager_(manager),
72 route_(route),
73 connection_receiver_(this, std::move(connections->connection_receiver)),
74 connection_remote_(std::move(connections->connection_remote)) {}
75
76 CefPresentationConnection(const CefPresentationConnection&) = delete;
77 CefPresentationConnection& operator=(const CefPresentationConnection&) =
78 delete;
79
OnMessage(blink::mojom::PresentationConnectionMessagePtr message)80 void OnMessage(
81 blink::mojom::PresentationConnectionMessagePtr message) override {
82 CefMediaRouterManager::MediaMessageVector messages;
83 if (message->is_message()) {
84 messages.push_back(media_router::message_util::RouteMessageFromString(
85 message->get_message()));
86 } else if (message->is_data()) {
87 messages.push_back(media_router::message_util::RouteMessageFromData(
88 message->get_data()));
89 }
90 if (!messages.empty()) {
91 manager_->OnMessagesReceived(route_, messages);
92 }
93 }
94
DidChangeState(blink::mojom::PresentationConnectionState state)95 void DidChangeState(
96 blink::mojom::PresentationConnectionState state) override {
97 // May result in |this| being deleted, so post async and allow the call
98 // stack to unwind.
99 CEF_POST_TASK(
100 CEF_UIT,
101 base::BindOnce(&CefMediaRouterManager::OnRouteStateChange,
102 manager_->weak_ptr_factory_.GetWeakPtr(), route_,
103 content::PresentationConnectionStateChangeInfo(state)));
104 }
105
DidClose(blink::mojom::PresentationConnectionCloseReason reason)106 void DidClose(
107 blink::mojom::PresentationConnectionCloseReason reason) override {
108 DidChangeState(blink::mojom::PresentationConnectionState::CLOSED);
109 }
110
SendRouteMessage(const std::string & message)111 void SendRouteMessage(const std::string& message) {
112 connection_remote_->OnMessage(
113 blink::mojom::PresentationConnectionMessage::NewMessage(message));
114 }
115
116 private:
117 CefMediaRouterManager* const manager_;
118 const media_router::MediaRoute route_;
119
120 // Used to receive messages from the MRP.
121 mojo::Receiver<blink::mojom::PresentationConnection> connection_receiver_;
122
123 // Used to send messages to the MRP.
124 mojo::Remote<blink::mojom::PresentationConnection> connection_remote_;
125 };
126
CefMediaRouterManager(content::BrowserContext * browser_context)127 CefMediaRouterManager::CefMediaRouterManager(
128 content::BrowserContext* browser_context)
129 : browser_context_(browser_context),
130 query_result_manager_(GetMediaRouter()),
131 weak_ptr_factory_(this) {
132 // Perform initialization.
133 GetMediaRouter()->OnUserGesture();
134
135 query_result_manager_.AddObserver(this);
136
137 // A non-empty presentation URL to required for discovery of Cast devices.
138 query_result_manager_.SetSourcesForCastMode(
139 media_router::MediaCastMode::PRESENTATION,
140 {media_router::MediaSource::ForPresentationUrl(
141 GURL(kDefaultPresentationUrl))},
142 url::Origin());
143
144 routes_observer_ = std::make_unique<CefMediaRoutesObserver>(this);
145 }
146
~CefMediaRouterManager()147 CefMediaRouterManager::~CefMediaRouterManager() {
148 CEF_REQUIRE_UIT();
149 for (auto& observer : observers_) {
150 observers_.RemoveObserver(&observer);
151 observer.OnMediaRouterDestroyed();
152 }
153
154 query_result_manager_.RemoveObserver(this);
155 }
156
AddObserver(Observer * observer)157 void CefMediaRouterManager::AddObserver(Observer* observer) {
158 CEF_REQUIRE_UIT();
159 observers_.AddObserver(observer);
160 }
161
RemoveObserver(Observer * observer)162 void CefMediaRouterManager::RemoveObserver(Observer* observer) {
163 CEF_REQUIRE_UIT();
164 observers_.RemoveObserver(observer);
165 }
166
NotifyCurrentSinks()167 void CefMediaRouterManager::NotifyCurrentSinks() {
168 CEF_REQUIRE_UIT();
169 for (auto& observer : observers_) {
170 observer.OnMediaSinks(sinks_);
171 }
172 }
173
NotifyCurrentRoutes()174 void CefMediaRouterManager::NotifyCurrentRoutes() {
175 CEF_REQUIRE_UIT();
176 for (auto& observer : observers_) {
177 observer.OnMediaRoutes(routes_);
178 }
179 }
180
CreateRoute(const media_router::MediaSource::Id & source_id,const media_router::MediaSink::Id & sink_id,const url::Origin & origin,CreateRouteResultCallback callback)181 void CefMediaRouterManager::CreateRoute(
182 const media_router::MediaSource::Id& source_id,
183 const media_router::MediaSink::Id& sink_id,
184 const url::Origin& origin,
185 CreateRouteResultCallback callback) {
186 GetMediaRouter()->CreateRoute(
187 source_id, sink_id, origin, nullptr /* web_contents */,
188 base::BindOnce(&CefMediaRouterManager::OnCreateRoute,
189 weak_ptr_factory_.GetWeakPtr(), std::move(callback)),
190 base::Milliseconds(kTimeoutMs), false /* incognito */);
191 }
192
SendRouteMessage(const media_router::MediaRoute::Id & route_id,const std::string & message)193 void CefMediaRouterManager::SendRouteMessage(
194 const media_router::MediaRoute::Id& route_id,
195 const std::string& message) {
196 // Must use PresentationConnection to send messages if it exists.
197 auto state = GetRouteState(route_id);
198 if (state && state->presentation_connection_) {
199 state->presentation_connection_->SendRouteMessage(message);
200 return;
201 }
202
203 GetMediaRouter()->SendRouteMessage(route_id, message);
204 }
205
TerminateRoute(const media_router::MediaRoute::Id & route_id)206 void CefMediaRouterManager::TerminateRoute(
207 const media_router::MediaRoute::Id& route_id) {
208 GetMediaRouter()->TerminateRoute(route_id);
209 }
210
OnResultsUpdated(const MediaSinkVector & sinks)211 void CefMediaRouterManager::OnResultsUpdated(const MediaSinkVector& sinks) {
212 sinks_ = sinks;
213 NotifyCurrentSinks();
214 }
215
GetMediaRouter() const216 media_router::MediaRouter* CefMediaRouterManager::GetMediaRouter() const {
217 CEF_REQUIRE_UIT();
218 return media_router::MediaRouterFactory::GetApiForBrowserContext(
219 browser_context_);
220 }
221
OnCreateRoute(CreateRouteResultCallback callback,media_router::mojom::RoutePresentationConnectionPtr connection,const media_router::RouteRequestResult & result)222 void CefMediaRouterManager::OnCreateRoute(
223 CreateRouteResultCallback callback,
224 media_router::mojom::RoutePresentationConnectionPtr connection,
225 const media_router::RouteRequestResult& result) {
226 CEF_REQUIRE_UIT();
227 if (result.route()) {
228 CreateRouteState(*result.route(), std::move(connection));
229 }
230
231 std::move(callback).Run(result);
232 }
233
OnRouteStateChange(const media_router::MediaRoute & route,const content::PresentationConnectionStateChangeInfo & info)234 void CefMediaRouterManager::OnRouteStateChange(
235 const media_router::MediaRoute& route,
236 const content::PresentationConnectionStateChangeInfo& info) {
237 CEF_REQUIRE_UIT();
238 if (info.state == blink::mojom::PresentationConnectionState::CLOSED ||
239 info.state == blink::mojom::PresentationConnectionState::TERMINATED) {
240 RemoveRouteState(route.media_route_id());
241 }
242
243 for (auto& observer : observers_) {
244 observer.OnMediaRouteStateChange(route, info);
245 }
246 }
247
OnMessagesReceived(const media_router::MediaRoute & route,const MediaMessageVector & messages)248 void CefMediaRouterManager::OnMessagesReceived(
249 const media_router::MediaRoute& route,
250 const MediaMessageVector& messages) {
251 CEF_REQUIRE_UIT();
252 for (auto& observer : observers_) {
253 observer.OnMediaRouteMessages(route, messages);
254 }
255 }
256
CreateRouteState(const media_router::MediaRoute & route,media_router::mojom::RoutePresentationConnectionPtr connection)257 void CefMediaRouterManager::CreateRouteState(
258 const media_router::MediaRoute& route,
259 media_router::mojom::RoutePresentationConnectionPtr connection) {
260 const auto route_id = route.media_route_id();
261 auto state = std::make_unique<RouteState>();
262
263 if (!connection.is_null()) {
264 // PresentationConnection must be used for messaging and status
265 // notifications if it exists.
266 state->presentation_connection_ =
267 std::make_unique<CefPresentationConnection>(this, route,
268 std::move(connection));
269 } else {
270 // Fallback if PresentationConnection is not supported.
271 state->message_observer_ =
272 std::make_unique<CefRouteMessageObserver>(this, route);
273 state->state_subscription_ =
274 GetMediaRouter()->AddPresentationConnectionStateChangedCallback(
275 route_id,
276 base::BindRepeating(&CefMediaRouterManager::OnRouteStateChange,
277 weak_ptr_factory_.GetWeakPtr(), route));
278 }
279
280 route_state_map_.insert(std::make_pair(route_id, std::move(state)));
281 }
282
GetRouteState(const media_router::MediaRoute::Id & route_id)283 CefMediaRouterManager::RouteState* CefMediaRouterManager::GetRouteState(
284 const media_router::MediaRoute::Id& route_id) {
285 const auto it = route_state_map_.find(route_id);
286 if (it != route_state_map_.end())
287 return it->second.get();
288 return nullptr;
289 }
290
RemoveRouteState(const media_router::MediaRoute::Id & route_id)291 void CefMediaRouterManager::RemoveRouteState(
292 const media_router::MediaRoute::Id& route_id) {
293 auto it = route_state_map_.find(route_id);
294 if (it != route_state_map_.end())
295 route_state_map_.erase(it);
296 }
297