mirror of
				https://bitbucket.org/chromiumembedded/cef
				synced 2025-06-05 21:39:12 +02:00 
			
		
		
		
	- Windows: 10.0.19041 SDK is now required. - macOS: 10.15.1 SDK (at least Xcode 11.2) is now required. - Remove CefMediaSource::IsValid and CefMediaSink::IsValid which would always return true.
		
			
				
	
	
		
			291 lines
		
	
	
		
			9.3 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			291 lines
		
	
	
		
			9.3 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| // Copyright (c) 2020 The Chromium Embedded Framework 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 "libcef/browser/media_router/media_router_impl.h"
 | |
| 
 | |
| #include "libcef/browser/media_router/media_route_impl.h"
 | |
| #include "libcef/browser/media_router/media_router_manager.h"
 | |
| #include "libcef/browser/media_router/media_sink_impl.h"
 | |
| #include "libcef/browser/media_router/media_source_impl.h"
 | |
| #include "libcef/browser/thread_util.h"
 | |
| 
 | |
| namespace {
 | |
| 
 | |
| // Do not keep a reference to the object returned by this method.
 | |
| CefBrowserContext* GetBrowserContext(const CefBrowserContext::Getter& getter) {
 | |
|   CEF_REQUIRE_UIT();
 | |
|   DCHECK(!getter.is_null());
 | |
| 
 | |
|   // Will return nullptr if the BrowserContext has been destroyed.
 | |
|   return getter.Run();
 | |
| }
 | |
| 
 | |
| }  // namespace
 | |
| 
 | |
| class CefRegistrationImpl : public CefRegistration,
 | |
|                             public CefMediaRouterManager::Observer {
 | |
|  public:
 | |
|   explicit CefRegistrationImpl(CefRefPtr<CefMediaObserver> observer)
 | |
|       : observer_(observer) {
 | |
|     DCHECK(observer_);
 | |
|   }
 | |
| 
 | |
|   ~CefRegistrationImpl() override {
 | |
|     CEF_REQUIRE_UIT();
 | |
| 
 | |
|     // May be null if OnMediaRouterDestroyed was called.
 | |
|     if (browser_context_getter_.is_null())
 | |
|       return;
 | |
| 
 | |
|     auto browser_context = GetBrowserContext(browser_context_getter_);
 | |
|     if (!browser_context)
 | |
|       return;
 | |
| 
 | |
|     browser_context->GetMediaRouterManager()->RemoveObserver(this);
 | |
|   }
 | |
| 
 | |
|   void Initialize(const CefBrowserContext::Getter& browser_context_getter) {
 | |
|     CEF_REQUIRE_UIT();
 | |
|     DCHECK(!browser_context_getter.is_null());
 | |
|     DCHECK(browser_context_getter_.is_null());
 | |
|     browser_context_getter_ = browser_context_getter;
 | |
| 
 | |
|     auto browser_context = GetBrowserContext(browser_context_getter_);
 | |
|     if (!browser_context)
 | |
|       return;
 | |
| 
 | |
|     browser_context->GetMediaRouterManager()->AddObserver(this);
 | |
|   }
 | |
| 
 | |
|  private:
 | |
|   // CefMediaRouterManager::Observer methods:
 | |
|   void OnMediaRouterDestroyed() override { browser_context_getter_.Reset(); }
 | |
| 
 | |
|   void OnMediaSinks(
 | |
|       const CefMediaRouterManager::MediaSinkVector& sinks) override {
 | |
|     std::vector<CefRefPtr<CefMediaSink>> cef_sinks;
 | |
|     for (const auto& sink : sinks) {
 | |
|       cef_sinks.push_back(new CefMediaSinkImpl(sink.sink));
 | |
|     }
 | |
|     observer_->OnSinks(cef_sinks);
 | |
|   }
 | |
| 
 | |
|   void OnMediaRoutes(
 | |
|       const CefMediaRouterManager::MediaRouteVector& routes) override {
 | |
|     std::vector<CefRefPtr<CefMediaRoute>> cef_routes;
 | |
|     for (const auto& route : routes) {
 | |
|       cef_routes.push_back(MakeCefRoute(route));
 | |
|     }
 | |
|     observer_->OnRoutes(cef_routes);
 | |
|   }
 | |
| 
 | |
|   void OnMediaRouteMessages(
 | |
|       const media_router::MediaRoute& route,
 | |
|       const CefMediaRouterManager::MediaMessageVector& messages) override {
 | |
|     CefRefPtr<CefMediaRoute> cef_route = MakeCefRoute(route);
 | |
|     for (const auto& message : messages) {
 | |
|       if (message->type == media_router::mojom::RouteMessage::Type::TEXT) {
 | |
|         if (message->message.has_value()) {
 | |
|           const std::string& str = *(message->message);
 | |
|           observer_->OnRouteMessageReceived(cef_route, str.c_str(), str.size());
 | |
|         }
 | |
|       } else if (message->type ==
 | |
|                  media_router::mojom::RouteMessage::Type::BINARY) {
 | |
|         if (message->data.has_value()) {
 | |
|           const std::vector<uint8_t>& data = *(message->data);
 | |
|           observer_->OnRouteMessageReceived(cef_route, data.data(),
 | |
|                                             data.size());
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   void OnMediaRouteStateChange(
 | |
|       const media_router::MediaRoute& route,
 | |
|       const content::PresentationConnectionStateChangeInfo& info) override {
 | |
|     observer_->OnRouteStateChanged(MakeCefRoute(route),
 | |
|                                    ToConnectionState(info.state));
 | |
|   }
 | |
| 
 | |
|   CefRefPtr<CefMediaRoute> MakeCefRoute(const media_router::MediaRoute& route) {
 | |
|     return new CefMediaRouteImpl(route, browser_context_getter_);
 | |
|   }
 | |
| 
 | |
|   static CefMediaObserver::ConnectionState ToConnectionState(
 | |
|       blink::mojom::PresentationConnectionState state) {
 | |
|     switch (state) {
 | |
|       case blink::mojom::PresentationConnectionState::CONNECTING:
 | |
|         return CEF_MRCS_CONNECTING;
 | |
|       case blink::mojom::PresentationConnectionState::CONNECTED:
 | |
|         return CEF_MRCS_CONNECTED;
 | |
|       case blink::mojom::PresentationConnectionState::CLOSED:
 | |
|         return CEF_MRCS_CLOSED;
 | |
|       case blink::mojom::PresentationConnectionState::TERMINATED:
 | |
|         return CEF_MRCS_TERMINATED;
 | |
|     }
 | |
|     NOTREACHED();
 | |
|     return CEF_MRCS_UNKNOWN;
 | |
|   }
 | |
| 
 | |
|   CefRefPtr<CefMediaObserver> observer_;
 | |
|   CefBrowserContext::Getter browser_context_getter_;
 | |
| 
 | |
|   IMPLEMENT_REFCOUNTING_DELETE_ON_UIT(CefRegistrationImpl);
 | |
|   DISALLOW_COPY_AND_ASSIGN(CefRegistrationImpl);
 | |
| };
 | |
| 
 | |
| CefMediaRouterImpl::CefMediaRouterImpl() {
 | |
|   // Verify that our enum matches Chromium's values.
 | |
|   static_assert(
 | |
|       static_cast<int>(CEF_MRCR_TOTAL_COUNT) ==
 | |
|           static_cast<int>(media_router::RouteRequestResult::TOTAL_COUNT),
 | |
|       "enum mismatch");
 | |
| }
 | |
| 
 | |
| void CefMediaRouterImpl::Initialize(
 | |
|     const CefBrowserContext::Getter& browser_context_getter) {
 | |
|   CEF_REQUIRE_UIT();
 | |
|   DCHECK(!browser_context_getter.is_null());
 | |
|   DCHECK(browser_context_getter_.is_null());
 | |
|   browser_context_getter_ = browser_context_getter;
 | |
| }
 | |
| 
 | |
| CefRefPtr<CefRegistration> CefMediaRouterImpl::AddObserver(
 | |
|     CefRefPtr<CefMediaObserver> observer) {
 | |
|   if (!observer)
 | |
|     return nullptr;
 | |
|   CefRefPtr<CefRegistrationImpl> registration =
 | |
|       new CefRegistrationImpl(observer);
 | |
|   InitializeRegistrationOnUIThread(registration);
 | |
|   return registration.get();
 | |
| }
 | |
| 
 | |
| CefRefPtr<CefMediaSource> CefMediaRouterImpl::GetSource(const CefString& urn) {
 | |
|   if (urn.empty())
 | |
|     return nullptr;
 | |
| 
 | |
|   // Check for a valid URL and supported Cast/DIAL schemes.
 | |
|   GURL presentation_url(urn.ToString());
 | |
|   if (!media_router::IsValidPresentationUrl(presentation_url)) {
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|   if (presentation_url.SchemeIsHTTPOrHTTPS()) {
 | |
|     // We don't support tab/desktop mirroring, which is what Cast uses for
 | |
|     // arbitrary HTTP/HTTPS URLs (see CastMediaSource).
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|   return new CefMediaSourceImpl(presentation_url);
 | |
| }
 | |
| 
 | |
| void CefMediaRouterImpl::NotifyCurrentSinks() {
 | |
|   if (!CEF_CURRENTLY_ON_UIT()) {
 | |
|     CEF_POST_TASK(
 | |
|         CEF_UIT, base::BindOnce(&CefMediaRouterImpl::NotifyCurrentSinks, this));
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   auto browser_context = GetBrowserContext(browser_context_getter_);
 | |
|   if (!browser_context)
 | |
|     return;
 | |
| 
 | |
|   browser_context->GetMediaRouterManager()->NotifyCurrentSinks();
 | |
| }
 | |
| 
 | |
| void CefMediaRouterImpl::CreateRoute(
 | |
|     CefRefPtr<CefMediaSource> source,
 | |
|     CefRefPtr<CefMediaSink> sink,
 | |
|     CefRefPtr<CefMediaRouteCreateCallback> callback) {
 | |
|   if (!CEF_CURRENTLY_ON_UIT()) {
 | |
|     CEF_POST_TASK(CEF_UIT, base::BindOnce(&CefMediaRouterImpl::CreateRoute,
 | |
|                                           this, source, sink, callback));
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   std::string error;
 | |
| 
 | |
|   auto browser_context = GetBrowserContext(browser_context_getter_);
 | |
|   if (!browser_context) {
 | |
|     error = "Context has already been destroyed";
 | |
|   } else if (!source) {
 | |
|     error = "Source is empty or invalid";
 | |
|   } else if (!sink) {
 | |
|     error = "Sink is empty or invalid";
 | |
|   } else if (!sink->IsCompatibleWith(source)) {
 | |
|     error = "Sink is not compatible with source";
 | |
|   }
 | |
| 
 | |
|   if (!error.empty()) {
 | |
|     LOG(WARNING) << "Media route creation failed: " << error;
 | |
|     if (callback) {
 | |
|       callback->OnMediaRouteCreateFinished(CEF_MRCR_UNKNOWN_ERROR, error,
 | |
|                                            nullptr);
 | |
|     }
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   auto source_impl = static_cast<CefMediaSourceImpl*>(source.get());
 | |
|   auto sink_impl = static_cast<CefMediaSinkImpl*>(sink.get());
 | |
| 
 | |
|   browser_context->GetMediaRouterManager()->CreateRoute(
 | |
|       source_impl->source().id(), sink_impl->sink().id(), url::Origin(),
 | |
|       base::BindOnce(&CefMediaRouterImpl::CreateRouteCallback, this, callback));
 | |
| }
 | |
| 
 | |
| void CefMediaRouterImpl::NotifyCurrentRoutes() {
 | |
|   if (!CEF_CURRENTLY_ON_UIT()) {
 | |
|     CEF_POST_TASK(CEF_UIT, base::BindOnce(
 | |
|                                &CefMediaRouterImpl::NotifyCurrentRoutes, this));
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   auto browser_context = GetBrowserContext(browser_context_getter_);
 | |
|   if (!browser_context)
 | |
|     return;
 | |
| 
 | |
|   browser_context->GetMediaRouterManager()->NotifyCurrentRoutes();
 | |
| }
 | |
| 
 | |
| void CefMediaRouterImpl::InitializeRegistrationOnUIThread(
 | |
|     CefRefPtr<CefRegistrationImpl> registration) {
 | |
|   if (!CEF_CURRENTLY_ON_UIT()) {
 | |
|     CEF_POST_TASK(
 | |
|         CEF_UIT,
 | |
|         base::BindOnce(&CefMediaRouterImpl::InitializeRegistrationOnUIThread,
 | |
|                        this, registration));
 | |
|     return;
 | |
|   }
 | |
|   registration->Initialize(browser_context_getter_);
 | |
| }
 | |
| 
 | |
| void CefMediaRouterImpl::CreateRouteCallback(
 | |
|     CefRefPtr<CefMediaRouteCreateCallback> callback,
 | |
|     const media_router::RouteRequestResult& result) {
 | |
|   CEF_REQUIRE_UIT();
 | |
| 
 | |
|   if (result.result_code() != media_router::RouteRequestResult::OK) {
 | |
|     LOG(WARNING) << "Media route creation failed: " << result.error() << " ("
 | |
|                  << result.result_code() << ")";
 | |
|   }
 | |
| 
 | |
|   if (!callback)
 | |
|     return;
 | |
| 
 | |
|   CefRefPtr<CefMediaRoute> route;
 | |
|   if (result.result_code() == media_router::RouteRequestResult::OK &&
 | |
|       result.route()) {
 | |
|     route = new CefMediaRouteImpl(*result.route(), browser_context_getter_);
 | |
|   }
 | |
| 
 | |
|   callback->OnMediaRouteCreateFinished(
 | |
|       static_cast<cef_media_route_create_result_t>(result.result_code()),
 | |
|       result.error(), route);
 | |
| }
 | |
| 
 | |
| // static
 | |
| CefRefPtr<CefMediaRouter> CefMediaRouter::GetGlobalMediaRouter() {
 | |
|   return CefRequestContext::GetGlobalContext()->GetMediaRouter();
 | |
| }
 |