| // 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 |