mirror of
				https://bitbucket.org/chromiumembedded/cef
				synced 2025-06-05 21:39:12 +02:00 
			
		
		
		
	
		
			
				
	
	
		
			304 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			304 lines
		
	
	
		
			10 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 "cef/libcef/browser/media_router/media_router_manager.h"
 | |
| 
 | |
| #include "base/memory/raw_ptr.h"
 | |
| #include "cef/libcef/browser/browser_context.h"
 | |
| #include "cef/libcef/browser/thread_util.h"
 | |
| #include "components/media_router/browser/media_router_factory.h"
 | |
| #include "components/media_router/browser/media_routes_observer.h"
 | |
| #include "components/media_router/browser/presentation_connection_message_observer.h"
 | |
| #include "components/media_router/browser/route_message_util.h"
 | |
| 
 | |
| namespace {
 | |
| 
 | |
| const int kTimeoutMs = 5 * 1000;
 | |
| const char kDefaultPresentationUrl[] = "https://google.com";
 | |
| 
 | |
| }  // namespace
 | |
| 
 | |
| class CefMediaRoutesObserver : public media_router::MediaRoutesObserver {
 | |
|  public:
 | |
|   explicit CefMediaRoutesObserver(CefMediaRouterManager* manager)
 | |
|       : media_router::MediaRoutesObserver(manager->GetMediaRouter()),
 | |
|         manager_(manager) {}
 | |
| 
 | |
|   CefMediaRoutesObserver(const CefMediaRoutesObserver&) = delete;
 | |
|   CefMediaRoutesObserver& operator=(const CefMediaRoutesObserver&) = delete;
 | |
| 
 | |
|   void OnRoutesUpdated(
 | |
|       const std::vector<media_router::MediaRoute>& routes) override {
 | |
|     manager_->routes_ = routes;
 | |
|     manager_->NotifyCurrentRoutes();
 | |
|   }
 | |
| 
 | |
|  private:
 | |
|   const raw_ptr<CefMediaRouterManager> manager_;
 | |
| };
 | |
| 
 | |
| // Used to receive messages if PresentationConnection is not supported.
 | |
| class CefPresentationConnectionMessageObserver
 | |
|     : public media_router::PresentationConnectionMessageObserver {
 | |
|  public:
 | |
|   CefPresentationConnectionMessageObserver(
 | |
|       CefMediaRouterManager* manager,
 | |
|       const media_router::MediaRoute& route)
 | |
|       : media_router::PresentationConnectionMessageObserver(
 | |
|             manager->GetMediaRouter(),
 | |
|             route.media_route_id()),
 | |
|         manager_(manager),
 | |
|         route_(route) {}
 | |
| 
 | |
|   CefPresentationConnectionMessageObserver(
 | |
|       const CefPresentationConnectionMessageObserver&) = delete;
 | |
|   CefPresentationConnectionMessageObserver& operator=(
 | |
|       const CefPresentationConnectionMessageObserver&) = delete;
 | |
| 
 | |
|   void OnMessagesReceived(
 | |
|       CefMediaRouterManager::MediaMessageVector messages) override {
 | |
|     manager_->OnMessagesReceived(route_, messages);
 | |
|   }
 | |
| 
 | |
|  private:
 | |
|   const raw_ptr<CefMediaRouterManager> manager_;
 | |
|   const media_router::MediaRoute route_;
 | |
| };
 | |
| 
 | |
| // Used for messaging and route status notifications with Cast.
 | |
| class CefPresentationConnection : public blink::mojom::PresentationConnection {
 | |
|  public:
 | |
|   explicit CefPresentationConnection(
 | |
|       CefMediaRouterManager* manager,
 | |
|       const media_router::MediaRoute& route,
 | |
|       media_router::mojom::RoutePresentationConnectionPtr connections)
 | |
|       : manager_(manager),
 | |
|         route_(route),
 | |
|         connection_receiver_(this, std::move(connections->connection_receiver)),
 | |
|         connection_remote_(std::move(connections->connection_remote)) {}
 | |
| 
 | |
|   CefPresentationConnection(const CefPresentationConnection&) = delete;
 | |
|   CefPresentationConnection& operator=(const CefPresentationConnection&) =
 | |
|       delete;
 | |
| 
 | |
|   void OnMessage(
 | |
|       blink::mojom::PresentationConnectionMessagePtr message) override {
 | |
|     CefMediaRouterManager::MediaMessageVector messages;
 | |
|     if (message->is_message()) {
 | |
|       messages.push_back(media_router::message_util::RouteMessageFromString(
 | |
|           message->get_message()));
 | |
|     } else if (message->is_data()) {
 | |
|       messages.push_back(media_router::message_util::RouteMessageFromData(
 | |
|           message->get_data()));
 | |
|     }
 | |
|     if (!messages.empty()) {
 | |
|       manager_->OnMessagesReceived(route_, messages);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   void DidChangeState(
 | |
|       blink::mojom::PresentationConnectionState state) override {
 | |
|     // May result in |this| being deleted, so post async and allow the call
 | |
|     // stack to unwind.
 | |
|     CEF_POST_TASK(
 | |
|         CEF_UIT,
 | |
|         base::BindOnce(&CefMediaRouterManager::OnRouteStateChange,
 | |
|                        manager_->weak_ptr_factory_.GetWeakPtr(), route_,
 | |
|                        content::PresentationConnectionStateChangeInfo(state)));
 | |
|   }
 | |
| 
 | |
|   void DidClose(
 | |
|       blink::mojom::PresentationConnectionCloseReason reason) override {
 | |
|     DidChangeState(blink::mojom::PresentationConnectionState::CLOSED);
 | |
|   }
 | |
| 
 | |
|   void SendRouteMessage(const std::string& message) {
 | |
|     connection_remote_->OnMessage(
 | |
|         blink::mojom::PresentationConnectionMessage::NewMessage(message));
 | |
|   }
 | |
| 
 | |
|  private:
 | |
|   const raw_ptr<CefMediaRouterManager> manager_;
 | |
|   const media_router::MediaRoute route_;
 | |
| 
 | |
|   // Used to receive messages from the MRP.
 | |
|   mojo::Receiver<blink::mojom::PresentationConnection> connection_receiver_;
 | |
| 
 | |
|   // Used to send messages to the MRP.
 | |
|   mojo::Remote<blink::mojom::PresentationConnection> connection_remote_;
 | |
| };
 | |
| 
 | |
| CefMediaRouterManager::CefMediaRouterManager(
 | |
|     content::BrowserContext* browser_context)
 | |
|     : browser_context_(browser_context),
 | |
|       query_result_manager_(GetMediaRouter()),
 | |
|       weak_ptr_factory_(this) {
 | |
|   // Perform initialization.
 | |
|   GetMediaRouter()->OnUserGesture();
 | |
| 
 | |
|   query_result_manager_.AddObserver(this);
 | |
| 
 | |
|   // A non-empty presentation URL to required for discovery of Cast devices.
 | |
|   query_result_manager_.SetSourcesForCastMode(
 | |
|       media_router::MediaCastMode::PRESENTATION,
 | |
|       {media_router::MediaSource::ForPresentationUrl(
 | |
|           GURL(kDefaultPresentationUrl))},
 | |
|       url::Origin());
 | |
| 
 | |
|   routes_observer_ = std::make_unique<CefMediaRoutesObserver>(this);
 | |
| }
 | |
| 
 | |
| CefMediaRouterManager::~CefMediaRouterManager() {
 | |
|   CEF_REQUIRE_UIT();
 | |
|   for (auto& observer : observers_) {
 | |
|     observers_.RemoveObserver(&observer);
 | |
|     observer.OnMediaRouterDestroyed();
 | |
|   }
 | |
| 
 | |
|   query_result_manager_.RemoveObserver(this);
 | |
| }
 | |
| 
 | |
| void CefMediaRouterManager::AddObserver(Observer* observer) {
 | |
|   CEF_REQUIRE_UIT();
 | |
|   observers_.AddObserver(observer);
 | |
| }
 | |
| 
 | |
| void CefMediaRouterManager::RemoveObserver(Observer* observer) {
 | |
|   CEF_REQUIRE_UIT();
 | |
|   observers_.RemoveObserver(observer);
 | |
| }
 | |
| 
 | |
| void CefMediaRouterManager::NotifyCurrentSinks() {
 | |
|   CEF_REQUIRE_UIT();
 | |
|   for (auto& observer : observers_) {
 | |
|     observer.OnMediaSinks(sinks_);
 | |
|   }
 | |
| }
 | |
| 
 | |
| void CefMediaRouterManager::NotifyCurrentRoutes() {
 | |
|   CEF_REQUIRE_UIT();
 | |
|   for (auto& observer : observers_) {
 | |
|     observer.OnMediaRoutes(routes_);
 | |
|   }
 | |
| }
 | |
| 
 | |
| void CefMediaRouterManager::CreateRoute(
 | |
|     const media_router::MediaSource::Id& source_id,
 | |
|     const media_router::MediaSink::Id& sink_id,
 | |
|     const url::Origin& origin,
 | |
|     CreateRouteResultCallback callback) {
 | |
|   GetMediaRouter()->CreateRoute(
 | |
|       source_id, sink_id, origin, /*web_contents=*/nullptr,
 | |
|       base::BindOnce(&CefMediaRouterManager::OnCreateRoute,
 | |
|                      weak_ptr_factory_.GetWeakPtr(), std::move(callback)),
 | |
|       base::Milliseconds(kTimeoutMs));
 | |
| }
 | |
| 
 | |
| void CefMediaRouterManager::SendRouteMessage(
 | |
|     const media_router::MediaRoute::Id& route_id,
 | |
|     const std::string& message) {
 | |
|   // Must use PresentationConnection to send messages if it exists.
 | |
|   auto state = GetRouteState(route_id);
 | |
|   if (state && state->presentation_connection_) {
 | |
|     state->presentation_connection_->SendRouteMessage(message);
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   GetMediaRouter()->SendRouteMessage(route_id, message);
 | |
| }
 | |
| 
 | |
| void CefMediaRouterManager::TerminateRoute(
 | |
|     const media_router::MediaRoute::Id& route_id) {
 | |
|   GetMediaRouter()->TerminateRoute(route_id);
 | |
| }
 | |
| 
 | |
| void CefMediaRouterManager::OnSinksUpdated(const MediaSinkVector& sinks) {
 | |
|   sinks_ = sinks;
 | |
|   NotifyCurrentSinks();
 | |
| }
 | |
| 
 | |
| media_router::MediaRouter* CefMediaRouterManager::GetMediaRouter() const {
 | |
|   CEF_REQUIRE_UIT();
 | |
|   return media_router::MediaRouterFactory::GetApiForBrowserContext(
 | |
|       browser_context_);
 | |
| }
 | |
| 
 | |
| void CefMediaRouterManager::OnCreateRoute(
 | |
|     CreateRouteResultCallback callback,
 | |
|     media_router::mojom::RoutePresentationConnectionPtr connection,
 | |
|     const media_router::RouteRequestResult& result) {
 | |
|   CEF_REQUIRE_UIT();
 | |
|   if (result.route()) {
 | |
|     CreateRouteState(*result.route(), std::move(connection));
 | |
|   }
 | |
| 
 | |
|   std::move(callback).Run(result);
 | |
| }
 | |
| 
 | |
| void CefMediaRouterManager::OnRouteStateChange(
 | |
|     const media_router::MediaRoute& route,
 | |
|     const content::PresentationConnectionStateChangeInfo& info) {
 | |
|   CEF_REQUIRE_UIT();
 | |
|   if (info.state == blink::mojom::PresentationConnectionState::CLOSED ||
 | |
|       info.state == blink::mojom::PresentationConnectionState::TERMINATED) {
 | |
|     RemoveRouteState(route.media_route_id());
 | |
|   }
 | |
| 
 | |
|   for (auto& observer : observers_) {
 | |
|     observer.OnMediaRouteStateChange(route, info);
 | |
|   }
 | |
| }
 | |
| 
 | |
| void CefMediaRouterManager::OnMessagesReceived(
 | |
|     const media_router::MediaRoute& route,
 | |
|     const MediaMessageVector& messages) {
 | |
|   CEF_REQUIRE_UIT();
 | |
|   for (auto& observer : observers_) {
 | |
|     observer.OnMediaRouteMessages(route, messages);
 | |
|   }
 | |
| }
 | |
| 
 | |
| void CefMediaRouterManager::CreateRouteState(
 | |
|     const media_router::MediaRoute& route,
 | |
|     media_router::mojom::RoutePresentationConnectionPtr connection) {
 | |
|   const auto route_id = route.media_route_id();
 | |
|   auto state = std::make_unique<RouteState>();
 | |
| 
 | |
|   if (!connection.is_null()) {
 | |
|     // PresentationConnection must be used for messaging and status
 | |
|     // notifications if it exists.
 | |
|     state->presentation_connection_ =
 | |
|         std::make_unique<CefPresentationConnection>(this, route,
 | |
|                                                     std::move(connection));
 | |
|   } else {
 | |
|     // Fallback if PresentationConnection is not supported.
 | |
|     state->message_observer_ =
 | |
|         std::make_unique<CefPresentationConnectionMessageObserver>(this, route);
 | |
|     state->state_subscription_ =
 | |
|         GetMediaRouter()->AddPresentationConnectionStateChangedCallback(
 | |
|             route_id,
 | |
|             base::BindRepeating(&CefMediaRouterManager::OnRouteStateChange,
 | |
|                                 weak_ptr_factory_.GetWeakPtr(), route));
 | |
|   }
 | |
| 
 | |
|   route_state_map_.insert(std::make_pair(route_id, std::move(state)));
 | |
| }
 | |
| 
 | |
| CefMediaRouterManager::RouteState* CefMediaRouterManager::GetRouteState(
 | |
|     const media_router::MediaRoute::Id& route_id) {
 | |
|   const auto it = route_state_map_.find(route_id);
 | |
|   if (it != route_state_map_.end()) {
 | |
|     return it->second.get();
 | |
|   }
 | |
|   return nullptr;
 | |
| }
 | |
| 
 | |
| void CefMediaRouterManager::RemoveRouteState(
 | |
|     const media_router::MediaRoute::Id& route_id) {
 | |
|   auto it = route_state_map_.find(route_id);
 | |
|   if (it != route_state_map_.end()) {
 | |
|     route_state_map_.erase(it);
 | |
|   }
 | |
| }
 |