291 lines
9.4 KiB
C++
291 lines
9.4 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 || !source->IsValid()) {
|
||
|
error = "Source is empty or invalid";
|
||
|
} else if (!sink || !sink->IsValid()) {
|
||
|
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();
|
||
|
}
|