• 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_impl.h"
6 
7 #include "libcef/browser/media_router/media_route_impl.h"
8 #include "libcef/browser/media_router/media_router_manager.h"
9 #include "libcef/browser/media_router/media_sink_impl.h"
10 #include "libcef/browser/media_router/media_source_impl.h"
11 #include "libcef/browser/thread_util.h"
12 
13 namespace {
14 
15 // Do not keep a reference to the object returned by this method.
GetBrowserContext(const CefBrowserContext::Getter & getter)16 CefBrowserContext* GetBrowserContext(const CefBrowserContext::Getter& getter) {
17   CEF_REQUIRE_UIT();
18   DCHECK(!getter.is_null());
19 
20   // Will return nullptr if the BrowserContext has been shut down.
21   return getter.Run();
22 }
23 
24 }  // namespace
25 
26 class CefRegistrationImpl : public CefRegistration,
27                             public CefMediaRouterManager::Observer {
28  public:
CefRegistrationImpl(CefRefPtr<CefMediaObserver> observer)29   explicit CefRegistrationImpl(CefRefPtr<CefMediaObserver> observer)
30       : observer_(observer) {
31     DCHECK(observer_);
32   }
33 
34   CefRegistrationImpl(const CefRegistrationImpl&) = delete;
35   CefRegistrationImpl& operator=(const CefRegistrationImpl&) = delete;
36 
~CefRegistrationImpl()37   ~CefRegistrationImpl() override {
38     CEF_REQUIRE_UIT();
39 
40     // May be null if OnMediaRouterDestroyed was called.
41     if (browser_context_getter_.is_null())
42       return;
43 
44     auto browser_context = GetBrowserContext(browser_context_getter_);
45     if (!browser_context)
46       return;
47 
48     browser_context->GetMediaRouterManager()->RemoveObserver(this);
49   }
50 
Initialize(const CefBrowserContext::Getter & browser_context_getter)51   void Initialize(const CefBrowserContext::Getter& browser_context_getter) {
52     CEF_REQUIRE_UIT();
53     DCHECK(!browser_context_getter.is_null());
54     DCHECK(browser_context_getter_.is_null());
55     browser_context_getter_ = browser_context_getter;
56 
57     auto browser_context = GetBrowserContext(browser_context_getter_);
58     if (!browser_context)
59       return;
60 
61     browser_context->GetMediaRouterManager()->AddObserver(this);
62   }
63 
64  private:
65   // CefMediaRouterManager::Observer methods:
OnMediaRouterDestroyed()66   void OnMediaRouterDestroyed() override { browser_context_getter_.Reset(); }
67 
OnMediaSinks(const CefMediaRouterManager::MediaSinkVector & sinks)68   void OnMediaSinks(
69       const CefMediaRouterManager::MediaSinkVector& sinks) override {
70     std::vector<CefRefPtr<CefMediaSink>> cef_sinks;
71     for (const auto& sink : sinks) {
72       cef_sinks.push_back(new CefMediaSinkImpl(sink.sink));
73     }
74     observer_->OnSinks(cef_sinks);
75   }
76 
OnMediaRoutes(const CefMediaRouterManager::MediaRouteVector & routes)77   void OnMediaRoutes(
78       const CefMediaRouterManager::MediaRouteVector& routes) override {
79     std::vector<CefRefPtr<CefMediaRoute>> cef_routes;
80     for (const auto& route : routes) {
81       cef_routes.push_back(MakeCefRoute(route));
82     }
83     observer_->OnRoutes(cef_routes);
84   }
85 
OnMediaRouteMessages(const media_router::MediaRoute & route,const CefMediaRouterManager::MediaMessageVector & messages)86   void OnMediaRouteMessages(
87       const media_router::MediaRoute& route,
88       const CefMediaRouterManager::MediaMessageVector& messages) override {
89     CefRefPtr<CefMediaRoute> cef_route = MakeCefRoute(route);
90     for (const auto& message : messages) {
91       if (message->type == media_router::mojom::RouteMessage::Type::TEXT) {
92         if (message->message.has_value()) {
93           const std::string& str = *(message->message);
94           observer_->OnRouteMessageReceived(cef_route, str.c_str(), str.size());
95         }
96       } else if (message->type ==
97                  media_router::mojom::RouteMessage::Type::BINARY) {
98         if (message->data.has_value()) {
99           const std::vector<uint8_t>& data = *(message->data);
100           observer_->OnRouteMessageReceived(cef_route, data.data(),
101                                             data.size());
102         }
103       }
104     }
105   }
106 
OnMediaRouteStateChange(const media_router::MediaRoute & route,const content::PresentationConnectionStateChangeInfo & info)107   void OnMediaRouteStateChange(
108       const media_router::MediaRoute& route,
109       const content::PresentationConnectionStateChangeInfo& info) override {
110     observer_->OnRouteStateChanged(MakeCefRoute(route),
111                                    ToConnectionState(info.state));
112   }
113 
MakeCefRoute(const media_router::MediaRoute & route)114   CefRefPtr<CefMediaRoute> MakeCefRoute(const media_router::MediaRoute& route) {
115     return new CefMediaRouteImpl(route, browser_context_getter_);
116   }
117 
ToConnectionState(blink::mojom::PresentationConnectionState state)118   static CefMediaObserver::ConnectionState ToConnectionState(
119       blink::mojom::PresentationConnectionState state) {
120     switch (state) {
121       case blink::mojom::PresentationConnectionState::CONNECTING:
122         return CEF_MRCS_CONNECTING;
123       case blink::mojom::PresentationConnectionState::CONNECTED:
124         return CEF_MRCS_CONNECTED;
125       case blink::mojom::PresentationConnectionState::CLOSED:
126         return CEF_MRCS_CLOSED;
127       case blink::mojom::PresentationConnectionState::TERMINATED:
128         return CEF_MRCS_TERMINATED;
129     }
130     NOTREACHED();
131     return CEF_MRCS_UNKNOWN;
132   }
133 
134   CefRefPtr<CefMediaObserver> observer_;
135   CefBrowserContext::Getter browser_context_getter_;
136 
137   IMPLEMENT_REFCOUNTING_DELETE_ON_UIT(CefRegistrationImpl);
138 };
139 
CefMediaRouterImpl()140 CefMediaRouterImpl::CefMediaRouterImpl() {
141   // Verify that our enum matches Chromium's values.
142   static_assert(
143       static_cast<int>(CEF_MRCR_TOTAL_COUNT) ==
144           static_cast<int>(media_router::RouteRequestResult::TOTAL_COUNT),
145       "enum mismatch");
146 }
147 
Initialize(const CefBrowserContext::Getter & browser_context_getter,CefRefPtr<CefCompletionCallback> callback)148 void CefMediaRouterImpl::Initialize(
149     const CefBrowserContext::Getter& browser_context_getter,
150     CefRefPtr<CefCompletionCallback> callback) {
151   CEF_REQUIRE_UIT();
152   DCHECK(!initialized_);
153   DCHECK(!browser_context_getter.is_null());
154   DCHECK(browser_context_getter_.is_null());
155   browser_context_getter_ = browser_context_getter;
156 
157   initialized_ = true;
158   if (!init_callbacks_.empty()) {
159     for (auto& callback : init_callbacks_) {
160       std::move(callback).Run();
161     }
162     init_callbacks_.clear();
163   }
164 
165   if (callback) {
166     // Execute client callback asynchronously for consistency.
167     CEF_POST_TASK(CEF_UIT, base::BindOnce(&CefCompletionCallback::OnComplete,
168                                           callback.get()));
169   }
170 }
171 
AddObserver(CefRefPtr<CefMediaObserver> observer)172 CefRefPtr<CefRegistration> CefMediaRouterImpl::AddObserver(
173     CefRefPtr<CefMediaObserver> observer) {
174   if (!observer)
175     return nullptr;
176   CefRefPtr<CefRegistrationImpl> registration =
177       new CefRegistrationImpl(observer);
178   StoreOrTriggerInitCallback(base::BindOnce(
179       &CefMediaRouterImpl::InitializeRegistrationInternal, this, registration));
180   return registration.get();
181 }
182 
GetSource(const CefString & urn)183 CefRefPtr<CefMediaSource> CefMediaRouterImpl::GetSource(const CefString& urn) {
184   if (urn.empty())
185     return nullptr;
186 
187   // Check for a valid URL and supported Cast/DIAL schemes.
188   GURL presentation_url(urn.ToString());
189   if (!media_router::IsValidPresentationUrl(presentation_url)) {
190     return nullptr;
191   }
192 
193   if (presentation_url.SchemeIsHTTPOrHTTPS()) {
194     // We don't support tab/desktop mirroring, which is what Cast uses for
195     // arbitrary HTTP/HTTPS URLs (see CastMediaSource).
196     return nullptr;
197   }
198 
199   return new CefMediaSourceImpl(presentation_url);
200 }
201 
NotifyCurrentSinks()202 void CefMediaRouterImpl::NotifyCurrentSinks() {
203   StoreOrTriggerInitCallback(
204       base::BindOnce(&CefMediaRouterImpl::NotifyCurrentSinksInternal, this));
205 }
206 
CreateRoute(CefRefPtr<CefMediaSource> source,CefRefPtr<CefMediaSink> sink,CefRefPtr<CefMediaRouteCreateCallback> callback)207 void CefMediaRouterImpl::CreateRoute(
208     CefRefPtr<CefMediaSource> source,
209     CefRefPtr<CefMediaSink> sink,
210     CefRefPtr<CefMediaRouteCreateCallback> callback) {
211   StoreOrTriggerInitCallback(base::BindOnce(
212       &CefMediaRouterImpl::CreateRouteInternal, this, source, sink, callback));
213 }
214 
NotifyCurrentRoutes()215 void CefMediaRouterImpl::NotifyCurrentRoutes() {
216   StoreOrTriggerInitCallback(
217       base::BindOnce(&CefMediaRouterImpl::NotifyCurrentRoutesInternal, this));
218 }
219 
InitializeRegistrationInternal(CefRefPtr<CefRegistrationImpl> registration)220 void CefMediaRouterImpl::InitializeRegistrationInternal(
221     CefRefPtr<CefRegistrationImpl> registration) {
222   DCHECK(ValidContext());
223 
224   registration->Initialize(browser_context_getter_);
225 }
226 
NotifyCurrentSinksInternal()227 void CefMediaRouterImpl::NotifyCurrentSinksInternal() {
228   DCHECK(ValidContext());
229 
230   auto browser_context = GetBrowserContext(browser_context_getter_);
231   if (!browser_context)
232     return;
233 
234   browser_context->GetMediaRouterManager()->NotifyCurrentSinks();
235 }
236 
CreateRouteInternal(CefRefPtr<CefMediaSource> source,CefRefPtr<CefMediaSink> sink,CefRefPtr<CefMediaRouteCreateCallback> callback)237 void CefMediaRouterImpl::CreateRouteInternal(
238     CefRefPtr<CefMediaSource> source,
239     CefRefPtr<CefMediaSink> sink,
240     CefRefPtr<CefMediaRouteCreateCallback> callback) {
241   DCHECK(ValidContext());
242 
243   std::string error;
244 
245   auto browser_context = GetBrowserContext(browser_context_getter_);
246   if (!browser_context) {
247     error = "Context is not valid";
248   } else if (!source) {
249     error = "Source is empty or invalid";
250   } else if (!sink) {
251     error = "Sink is empty or invalid";
252   } else if (!sink->IsCompatibleWith(source)) {
253     error = "Sink is not compatible with source";
254   }
255 
256   if (!error.empty()) {
257     LOG(WARNING) << "Media route creation failed: " << error;
258     if (callback) {
259       callback->OnMediaRouteCreateFinished(CEF_MRCR_UNKNOWN_ERROR, error,
260                                            nullptr);
261     }
262     return;
263   }
264 
265   auto source_impl = static_cast<CefMediaSourceImpl*>(source.get());
266   auto sink_impl = static_cast<CefMediaSinkImpl*>(sink.get());
267 
268   browser_context->GetMediaRouterManager()->CreateRoute(
269       source_impl->source().id(), sink_impl->sink().id(), url::Origin(),
270       base::BindOnce(&CefMediaRouterImpl::CreateRouteCallback, this, callback));
271 }
272 
NotifyCurrentRoutesInternal()273 void CefMediaRouterImpl::NotifyCurrentRoutesInternal() {
274   DCHECK(ValidContext());
275 
276   auto browser_context = GetBrowserContext(browser_context_getter_);
277   if (!browser_context)
278     return;
279 
280   browser_context->GetMediaRouterManager()->NotifyCurrentRoutes();
281 }
282 
CreateRouteCallback(CefRefPtr<CefMediaRouteCreateCallback> callback,const media_router::RouteRequestResult & result)283 void CefMediaRouterImpl::CreateRouteCallback(
284     CefRefPtr<CefMediaRouteCreateCallback> callback,
285     const media_router::RouteRequestResult& result) {
286   DCHECK(ValidContext());
287 
288   if (result.result_code() != media_router::RouteRequestResult::OK) {
289     LOG(WARNING) << "Media route creation failed: " << result.error() << " ("
290                  << result.result_code() << ")";
291   }
292 
293   if (!callback)
294     return;
295 
296   CefRefPtr<CefMediaRoute> route;
297   if (result.result_code() == media_router::RouteRequestResult::OK &&
298       result.route()) {
299     route = new CefMediaRouteImpl(*result.route(), browser_context_getter_);
300   }
301 
302   callback->OnMediaRouteCreateFinished(
303       static_cast<cef_media_route_create_result_t>(result.result_code()),
304       result.error(), route);
305 }
306 
StoreOrTriggerInitCallback(base::OnceClosure callback)307 void CefMediaRouterImpl::StoreOrTriggerInitCallback(
308     base::OnceClosure callback) {
309   if (!CEF_CURRENTLY_ON_UIT()) {
310     CEF_POST_TASK(
311         CEF_UIT, base::BindOnce(&CefMediaRouterImpl::StoreOrTriggerInitCallback,
312                                 this, std::move(callback)));
313     return;
314   }
315 
316   if (initialized_) {
317     std::move(callback).Run();
318   } else {
319     init_callbacks_.emplace_back(std::move(callback));
320   }
321 }
322 
ValidContext() const323 bool CefMediaRouterImpl::ValidContext() const {
324   return CEF_CURRENTLY_ON_UIT() && initialized_;
325 }
326 
327 // CefMediaRouter methods ------------------------------------------------------
328 
329 // static
GetGlobalMediaRouter(CefRefPtr<CefCompletionCallback> callback)330 CefRefPtr<CefMediaRouter> CefMediaRouter::GetGlobalMediaRouter(
331     CefRefPtr<CefCompletionCallback> callback) {
332   return CefRequestContext::GetGlobalContext()->GetMediaRouter(callback);
333 }
334