• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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