blob: e584cbadeb6ffe79038aef2245483ca29e906382 [file] [log] [blame]
// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "media/mojo/services/cdm_service.h"
#include <utility>
#include "base/functional/bind.h"
#include "base/logging.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "media/base/cdm_factory.h"
#include "media/mojo/services/mojo_cdm_service.h"
#include "media/mojo/services/mojo_cdm_service_context.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "mojo/public/cpp/bindings/unique_receiver_set.h"
namespace media {
namespace {
// Implementation of mojom::CdmFactory that creates and hosts MojoCdmServices
// which then host CDMs created by the media::CdmFactory provided by the
// CdmService::Client.
//
// Lifetime Note:
// 1. CdmFactoryImpl instances are owned by a DeferredDestroyUniqueReceiverSet
// directly, which is owned by CdmService.
// 2. CdmFactoryImpl is destroyed in any of the following two cases:
// - CdmService is destroyed. Because of (2) this should not happen except for
// during browser shutdown, when the Cdservice could be destroyed directly,
// ignoring any outstanding interface connections.
// - mojo::CdmFactory disconnection happens, AND CdmFactoryImpl doesn't own
// any CDMs (|cdm_receivers_| is empty). This is to prevent destroying the
// CDMs too early (e.g. during page navigation) which could cause errors
// (session closed) on the client side. See https://crbug.com/821171 for
// details.
class CdmFactoryImpl final : public DeferredDestroy<mojom::CdmFactory> {
public:
CdmFactoryImpl(CdmService::Client* client,
mojo::PendingRemote<mojom::FrameInterfaceFactory> interfaces)
: client_(client), interfaces_(std::move(interfaces)) {
DVLOG(1) << __func__;
// base::Unretained is safe because |cdm_receivers_| is owned by |this|. If
// |this| is destructed, |cdm_receivers_| will be destructed as well and the
// error handler should never be called.
cdm_receivers_.set_disconnect_handler(base::BindRepeating(
&CdmFactoryImpl::OnReceiverDisconnect, base::Unretained(this)));
}
CdmFactoryImpl(const CdmFactoryImpl&) = delete;
CdmFactoryImpl operator=(const CdmFactoryImpl&) = delete;
~CdmFactoryImpl() final { DVLOG(1) << __func__; }
// mojom::CdmFactory implementation.
void CreateCdm(const CdmConfig& cdm_config,
CreateCdmCallback callback) final {
DVLOG(2) << __func__;
auto* cdm_factory = GetCdmFactory();
if (!cdm_factory) {
std::move(callback).Run(mojo::NullRemote(), nullptr,
"CDM Factory creation failed");
return;
}
auto mojo_cdm_service =
std::make_unique<MojoCdmService>(&cdm_service_context_);
auto* raw_mojo_cdm_service = mojo_cdm_service.get();
DCHECK(!pending_mojo_cdm_services_.count(raw_mojo_cdm_service));
pending_mojo_cdm_services_[raw_mojo_cdm_service] =
std::move(mojo_cdm_service);
raw_mojo_cdm_service->Initialize(
cdm_factory, cdm_config,
base::BindOnce(&CdmFactoryImpl::OnCdmServiceInitialized,
weak_ptr_factory_.GetWeakPtr(), raw_mojo_cdm_service,
std::move(callback)));
}
// DeferredDestroy<mojom::CdmFactory> implemenation.
void OnDestroyPending(base::OnceClosure destroy_cb) final {
destroy_cb_ = std::move(destroy_cb);
if (cdm_receivers_.empty())
std::move(destroy_cb_).Run();
// else the callback will be called when |cdm_receivers_| become empty.
}
private:
media::CdmFactory* GetCdmFactory() {
if (!cdm_factory_) {
cdm_factory_ = client_->CreateCdmFactory(interfaces_.get());
DLOG_IF(ERROR, !cdm_factory_) << "CdmFactory not available.";
}
return cdm_factory_.get();
}
void OnReceiverDisconnect() {
if (destroy_cb_ && cdm_receivers_.empty())
std::move(destroy_cb_).Run();
}
void OnCdmServiceInitialized(MojoCdmService* raw_mojo_cdm_service,
CreateCdmCallback callback,
mojom::CdmContextPtr cdm_context,
const std::string& error_message) {
DCHECK(raw_mojo_cdm_service);
// Remove pending MojoCdmService from the mapping in all cases.
DCHECK(pending_mojo_cdm_services_.count(raw_mojo_cdm_service));
auto mojo_cdm_service =
std::move(pending_mojo_cdm_services_[raw_mojo_cdm_service]);
pending_mojo_cdm_services_.erase(raw_mojo_cdm_service);
if (!cdm_context) {
std::move(callback).Run(mojo::NullRemote(), nullptr, error_message);
return;
}
mojo::PendingRemote<mojom::ContentDecryptionModule> remote;
cdm_receivers_.Add(std::move(mojo_cdm_service),
remote.InitWithNewPipeAndPassReceiver());
std::move(callback).Run(std::move(remote), std::move(cdm_context), "");
}
// Must be declared before the receivers below because the bound objects might
// take a raw pointer of |cdm_service_context_| and assume it's always
// available.
MojoCdmServiceContext cdm_service_context_;
raw_ptr<CdmService::Client> client_;
mojo::Remote<mojom::FrameInterfaceFactory> interfaces_;
mojo::UniqueReceiverSet<mojom::ContentDecryptionModule> cdm_receivers_;
std::unique_ptr<media::CdmFactory> cdm_factory_;
base::OnceClosure destroy_cb_;
// MojoCdmServices pending initialization.
std::map<MojoCdmService*, std::unique_ptr<MojoCdmService>>
pending_mojo_cdm_services_;
// NOTE: Weak pointers must be invalidated before all other member variables.
base::WeakPtrFactory<CdmFactoryImpl> weak_ptr_factory_{this};
};
} // namespace
CdmService::CdmService(std::unique_ptr<Client> client,
mojo::PendingReceiver<mojom::CdmService> receiver)
: receiver_(this, std::move(receiver)), client_(std::move(client)) {
DVLOG(1) << __func__;
DCHECK(client_);
}
CdmService::~CdmService() {
DVLOG(1) << __func__;
}
void CdmService::CreateCdmFactory(
mojo::PendingReceiver<mojom::CdmFactory> receiver,
mojo::PendingRemote<mojom::FrameInterfaceFactory> frame_interfaces) {
// Ignore receiver if service has already stopped.
if (!client_)
return;
cdm_factory_receivers_.Add(std::make_unique<CdmFactoryImpl>(
client_.get(), std::move(frame_interfaces)),
std::move(receiver));
}
} // namespace media