blob: 59c887adccdc145ee3706691e8dba4262cb1e302 [file] [log] [blame]
// Copyright 2021 The Chromium 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 "content/browser/media/service_factory.h"
#include <map>
#include <string>
#include "base/bind.h"
#include "base/feature_list.h"
#include "base/logging.h"
#include "base/macros.h"
#include "base/no_destructor.h"
#include "base/threading/sequence_local_storage_slot.h"
#include "base/time/time.h"
#include "content/browser/service_sandbox_type.h"
#include "content/public/browser/content_browser_client.h"
#include "content/public/browser/service_process_host.h"
#include "content/public/common/content_client.h"
#include "media/base/cdm_context.h"
#include "media/base/media_switches.h"
#include "media/media_buildflags.h"
namespace content {
namespace {
// How long an instance of the service is allowed to sit idle before we
// disconnect and effectively kill it.
constexpr auto kServiceIdleTimeout = base::TimeDelta::FromSeconds(5);
// Services are keyed on CDM type, user profile and site URL. Note that site
// is not normal URL nor origin. See chrome/browser/site_isolation for details.
using ServiceKey = std::tuple<base::Token, const BrowserContext*, GURL>;
std::ostream& operator<<(std::ostream& os, const ServiceKey& key) {
return os << "{" << std::get<0>(key).ToString() << ", " << std::get<1>(key)
<< ", " << std::get<2>(key) << "}";
}
// A map hosts all service remotes, each of which corresponds to one service
// process. There should be only one instance of this class stored in
// base::SequenceLocalStorageSlot. See below.
template <typename T>
class ServiceMap {
public:
ServiceMap() = default;
~ServiceMap() = default;
// Gets or creates a service remote. The returned remote might not be bound,
// e.g. if it's newly created.
auto& GetOrCreateRemote(const ServiceKey& key) { return remotes_[key]; }
void EraseRemote(const ServiceKey& key) {
DCHECK(remotes_.count(key));
remotes_.erase(key);
}
private:
std::map<ServiceKey, mojo::Remote<T>> remotes_;
};
template <typename T>
ServiceMap<T>& GetServiceMap() {
// NOTE: Sequence-local storage is used to limit the lifetime of the Remote
// objects to that of the UI-thread sequence. This ensures the Remotes are
// destroyed when the task environment is torn down and reinitialized, e.g.,
// between unit tests.
static base::NoDestructor<base::SequenceLocalStorageSlot<ServiceMap<T>>> slot;
return slot->GetOrCreateValue();
}
// Erases the service instance identified by `key`.
template <typename T>
void EraseCdmService(const ServiceKey& key) {
DVLOG(2) << __func__ << ": key=" << key;
GetServiceMap<T>().EraseRemote(key);
}
// Gets an instance of the service for `guid`, `browser_context` and `site`.
// Instances are started lazily as needed.
template <typename T>
T& GetService(const base::Token& guid,
BrowserContext* browser_context,
const GURL& site,
const std::string& service_name) {
ServiceKey key;
std::string display_name = service_name;
if (base::FeatureList::IsEnabled(media::kCdmProcessSiteIsolation)) {
key = {guid, browser_context, site};
auto site_display_name =
GetContentClient()->browser()->GetSiteDisplayNameForCdmProcess(
browser_context, site);
if (!site_display_name.empty())
display_name += " (" + site_display_name + ")";
} else {
key = {guid, nullptr, GURL()};
}
DVLOG(2) << __func__ << ": key=" << key;
auto& remote = GetServiceMap<T>().GetOrCreateRemote(key);
if (!remote) {
ServiceProcessHost::Options options;
options.WithDisplayName(display_name);
ServiceProcessHost::Launch(remote.BindNewPipeAndPassReceiver(),
options.Pass());
remote.set_disconnect_handler(base::BindOnce(&EraseCdmService<T>, key));
remote.set_idle_handler(kServiceIdleTimeout,
base::BindRepeating(EraseCdmService<T>, key));
}
return *remote.get();
}
} // namespace
media::mojom::CdmService& GetCdmService(const base::Token& guid,
BrowserContext* browser_context,
const GURL& site,
const CdmInfo& cdm_info) {
return GetService<media::mojom::CdmService>(guid, browser_context, site,
cdm_info.name);
}
#if defined(OS_WIN)
media::mojom::MediaFoundationService& GetMediaFoundationService(
BrowserContext* browser_context,
const GURL& site) {
return GetService<media::mojom::MediaFoundationService>(
base::Token(), browser_context, site, "Media Foundation Service");
}
#endif // defined(OS_WIN)
} // namespace content