diff options
author | xhwang <xhwang@chromium.org> | 2015-06-11 15:38:20 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-06-11 22:40:06 +0000 |
commit | ec48647eb0ed539e7436b3a299f8d5e5073c76f8 (patch) | |
tree | 1cce56ae881b2ecf082a5597a236036c23a7af89 /media | |
parent | ae9e23babfc3a0c15f02c8dafe30e56297b50c3e (diff) | |
download | chromium_src-ec48647eb0ed539e7436b3a299f8d5e5073c76f8.zip chromium_src-ec48647eb0ed539e7436b3a299f8d5e5073c76f8.tar.gz chromium_src-ec48647eb0ed539e7436b3a299f8d5e5073c76f8.tar.bz2 |
media: Add MojoCdmServiceContext.
MojoCdmServiceContext will serve as a bridge between a MojoRendererService and a
MojoCdmService such that the renderer service can use the CDM service to decrypt
(and decode) encrypted contents.
With this change, now MojoCdmServiceContext owns and manages all MojoCdmService
instances so that MojoRendererService can find the correct CDM to use given a
|cdm_id|. As a result, MojoCdmService is not strongly bound to the pipe anymore.
An alternative to this is to have MojoCdmService still strongly bound to the
pipe, but notifies the MojoCdmServiceContext when the service is created and
destroyed. Here I was just following existing code like PermissionServiceContext
and GeolocationServiceContext.
TBR=sky@chromium.org
BUG=432998
Review URL: https://codereview.chromium.org/1165633004
Cr-Commit-Position: refs/heads/master@{#334061}
Diffstat (limited to 'media')
-rw-r--r-- | media/base/cdm_context.cc | 12 | ||||
-rw-r--r-- | media/base/cdm_context.h | 36 | ||||
-rw-r--r-- | media/mojo/services/BUILD.gn | 2 | ||||
-rw-r--r-- | media/mojo/services/mojo_cdm_service.cc | 82 | ||||
-rw-r--r-- | media/mojo/services/mojo_cdm_service.h | 33 | ||||
-rw-r--r-- | media/mojo/services/mojo_cdm_service_context.cc | 53 | ||||
-rw-r--r-- | media/mojo/services/mojo_cdm_service_context.h | 50 |
7 files changed, 228 insertions, 40 deletions
diff --git a/media/base/cdm_context.cc b/media/base/cdm_context.cc index ec2b633..b53fc7d 100644 --- a/media/base/cdm_context.cc +++ b/media/base/cdm_context.cc @@ -6,9 +6,17 @@ namespace media { -CdmContext::CdmContext() {} +CdmContext::CdmContext() { +} + +CdmContext::~CdmContext() { +} -CdmContext::~CdmContext() {} +CdmContextProvider::CdmContextProvider() { +} + +CdmContextProvider::~CdmContextProvider() { +} void IgnoreCdmAttached(bool success) { } diff --git a/media/base/cdm_context.h b/media/base/cdm_context.h index a0861c4..7d89760 100644 --- a/media/base/cdm_context.h +++ b/media/base/cdm_context.h @@ -15,6 +15,7 @@ class Decryptor; // An interface representing the context that a media pipeline needs from a // content decryption module (CDM) to decrypt (and decode) encrypted buffers. +// Only used for implementing SetCdm(). class MEDIA_EXPORT CdmContext { public: // Indicates an invalid CDM ID. See GetCdmId() for details. @@ -22,16 +23,16 @@ class MEDIA_EXPORT CdmContext { virtual ~CdmContext(); - // Gets the Decryptor object associated with the CDM. Returns NULL if the CDM - // does not support a Decryptor. The returned object is only guaranteed to be - // valid during the CDM's lifetime. + // Gets the Decryptor object associated with the CDM. Returns nullptr if the + // CDM does not support a Decryptor. Must not return nullptr if GetCdmId() + // returns kInvalidCdmId. The returned object is only guaranteed to be valid + // during the CDM's lifetime. virtual Decryptor* GetDecryptor() = 0; - // Returns an ID associated with the CDM, which can be used to locate the real - // CDM instance. This is useful when the CDM is hosted remotely, e.g. in a - // different process. - // Returns kInvalidCdmId if the CDM cannot be used remotely. In this case, - // GetDecryptor() should return a non-null Decryptor. + // Returns an ID that identifies a CDM, or kInvalidCdmId. The interpretation + // is implementation-specific; current implementations use the ID to locate a + // remote CDM in a different process. The return value will not be + // kInvalidCdmId if GetDecryptor() returns nullptr. virtual int GetCdmId() const = 0; protected: @@ -41,6 +42,25 @@ class MEDIA_EXPORT CdmContext { DISALLOW_COPY_AND_ASSIGN(CdmContext); }; +// An interface for looking up CdmContext objects by the CDM ID. +class MEDIA_EXPORT CdmContextProvider { + public: + virtual ~CdmContextProvider(); + + // Returns the CdmContext corresponding to |cdm_id|. Returns nullptr if no + // such CdmContext can be found. + // Note: Calling GetCdmId() on the returned CdmContext returns kInvalidCdmId + // (in all current cases) because the CDM will be local in the process where + // GetCdmContext() is called. + virtual CdmContext* GetCdmContext(int cdm_id) = 0; + + protected: + CdmContextProvider(); + + private: + DISALLOW_COPY_AND_ASSIGN(CdmContextProvider); +}; + // Callback to notify that the CdmContext has been completely attached to // the media pipeline. Parameter indicates whether the operation succeeded. typedef base::Callback<void(bool)> CdmAttachedCB; diff --git a/media/mojo/services/BUILD.gn b/media/mojo/services/BUILD.gn index 58efa7e..7796db5 100644 --- a/media/mojo/services/BUILD.gn +++ b/media/mojo/services/BUILD.gn @@ -84,6 +84,8 @@ source_set("cdm_service") { "mojo_cdm_promise.h", "mojo_cdm_service.cc", "mojo_cdm_service.h", + "mojo_cdm_service_context.cc", + "mojo_cdm_service_context.h", "mojo_type_trait.h", ] diff --git a/media/mojo/services/mojo_cdm_service.cc b/media/mojo/services/mojo_cdm_service.cc index 926535b..171b2e5 100644 --- a/media/mojo/services/mojo_cdm_service.cc +++ b/media/mojo/services/mojo_cdm_service.cc @@ -10,6 +10,7 @@ #include "media/cdm/aes_decryptor.h" #include "media/mojo/services/media_type_converters.h" #include "media/mojo/services/mojo_cdm_promise.h" +#include "media/mojo/services/mojo_cdm_service_context.h" #include "mojo/common/common_type_converters.h" #include "mojo/common/url_type_converters.h" #include "url/gurl.h" @@ -19,23 +20,35 @@ namespace media { typedef MojoCdmPromise<> SimpleMojoCdmPromise; typedef MojoCdmPromise<std::string> NewSessionMojoCdmPromise; +scopted_ptr<MojoCdmService> MojoCdmService::Create( + const mojo::String& key_system, + MojoCdmServiceContext* context, + mojo::InterfaceRequest<mojo::ContentDecryptionModule> request) { + // Only AesDecryptor is supported. + // TODO(xhwang): Use a CdmFactory to create the CDM here. See + // http://crbug.com/495273 + if (!CanUseAesDecryptor(key_system)) + return nullptr; + + // TODO(xhwang): Pass security origin through. + return make_scoped_ptr( + new MojoCdmService(key_system, context, request.Pass())); +} + MojoCdmService::MojoCdmService( const mojo::String& key_system, + MojoCdmServiceContext* context, mojo::InterfaceRequest<mojo::ContentDecryptionModule> request) - : binding_(this, request.Pass()), weak_factory_(this) { - base::WeakPtr<MojoCdmService> weak_this = weak_factory_.GetWeakPtr(); + : binding_(this, request.Pass()), context_(context), weak_factory_(this) { + DVLOG(1) << __FUNCTION__ << ": " << key_system; + DCHECK(CanUseAesDecryptor(key_system)); - if (CanUseAesDecryptor(key_system)) { - // TODO(jrummell): Determine proper origin. - cdm_.reset(new AesDecryptor( - GURL::EmptyGURL(), - base::Bind(&MojoCdmService::OnSessionMessage, weak_this), - base::Bind(&MojoCdmService::OnSessionClosed, weak_this), - base::Bind(&MojoCdmService::OnSessionKeysChange, weak_this))); - } - - // TODO(xhwang): Check key system support in the app. - NOTREACHED(); + base::WeakPtr<MojoCdmService> weak_this = weak_factory_.GetWeakPtr(); + cdm_.reset(new AesDecryptor( + GURL::EmptyGURL(), + base::Bind(&MojoCdmService::OnSessionMessage, weak_this), + base::Bind(&MojoCdmService::OnSessionClosed, weak_this), + base::Bind(&MojoCdmService::OnSessionKeysChange, weak_this))); } MojoCdmService::~MojoCdmService() { @@ -49,9 +62,10 @@ void MojoCdmService::SetClient(mojo::ContentDecryptionModuleClientPtr client) { void MojoCdmService::SetServerCertificate( mojo::Array<uint8_t> certificate_data, const mojo::Callback<void(mojo::CdmPromiseResultPtr)>& callback) { + DVLOG(2) << __FUNCTION__; cdm_->SetServerCertificate( certificate_data.storage(), - scoped_ptr<SimpleCdmPromise>(new SimpleMojoCdmPromise(callback))); + make_scoped_ptr(new SimpleMojoCdmPromise(callback))); } void MojoCdmService::CreateSessionAndGenerateRequest( @@ -60,10 +74,11 @@ void MojoCdmService::CreateSessionAndGenerateRequest( mojo::Array<uint8_t> init_data, const mojo::Callback<void(mojo::CdmPromiseResultPtr, mojo::String)>& callback) { + DVLOG(2) << __FUNCTION__; cdm_->CreateSessionAndGenerateRequest( static_cast<MediaKeys::SessionType>(session_type), static_cast<EmeInitDataType>(init_data_type), init_data.storage(), - scoped_ptr<NewSessionCdmPromise>(new NewSessionMojoCdmPromise(callback))); + make_scoped_ptr(new NewSessionMojoCdmPromise(callback))); } void MojoCdmService::LoadSession( @@ -71,16 +86,17 @@ void MojoCdmService::LoadSession( const mojo::String& session_id, const mojo::Callback<void(mojo::CdmPromiseResultPtr, mojo::String)>& callback) { - cdm_->LoadSession( - static_cast<MediaKeys::SessionType>(session_type), - session_id.To<std::string>(), - scoped_ptr<NewSessionCdmPromise>(new NewSessionMojoCdmPromise(callback))); + DVLOG(2) << __FUNCTION__; + cdm_->LoadSession(static_cast<MediaKeys::SessionType>(session_type), + session_id.To<std::string>(), + make_scoped_ptr(new NewSessionMojoCdmPromise(callback))); } void MojoCdmService::UpdateSession( const mojo::String& session_id, mojo::Array<uint8_t> response, const mojo::Callback<void(mojo::CdmPromiseResultPtr)>& callback) { + DVLOG(2) << __FUNCTION__; cdm_->UpdateSession( session_id.To<std::string>(), response.storage(), scoped_ptr<SimpleCdmPromise>(new SimpleMojoCdmPromise(callback))); @@ -89,17 +105,17 @@ void MojoCdmService::UpdateSession( void MojoCdmService::CloseSession( const mojo::String& session_id, const mojo::Callback<void(mojo::CdmPromiseResultPtr)>& callback) { - cdm_->CloseSession( - session_id.To<std::string>(), - scoped_ptr<SimpleCdmPromise>(new SimpleMojoCdmPromise(callback))); + DVLOG(2) << __FUNCTION__; + cdm_->CloseSession(session_id.To<std::string>(), + make_scoped_ptr(new SimpleMojoCdmPromise(callback))); } void MojoCdmService::RemoveSession( const mojo::String& session_id, const mojo::Callback<void(mojo::CdmPromiseResultPtr)>& callback) { - cdm_->RemoveSession( - session_id.To<std::string>(), - scoped_ptr<SimpleCdmPromise>(new SimpleMojoCdmPromise(callback))); + DVLOG(2) << __FUNCTION__; + cdm_->RemoveSession(session_id.To<std::string>(), + make_scoped_ptr(new SimpleMojoCdmPromise(callback))); } void MojoCdmService::GetCdmContext( @@ -108,10 +124,22 @@ void MojoCdmService::GetCdmContext( NOTIMPLEMENTED(); } +CdmContext* MojoCdmService::GetCdmContext() { + return cdm_->GetCdmContext(); +} + +void MojoCdmService::OnConnectionError() { + DVLOG(1) << __FUNCTION__; + context_->ServiceHadConnectionError(this); + // The above call deleted this instance, so the only safe thing to do is + // return. +} + void MojoCdmService::OnSessionMessage(const std::string& session_id, MediaKeys::MessageType message_type, const std::vector<uint8_t>& message, const GURL& legacy_destination_url) { + DVLOG(2) << __FUNCTION__; client_->OnSessionMessage(session_id, static_cast<mojo::CdmMessageType>(message_type), mojo::Array<uint8_t>::From(message), @@ -121,6 +149,7 @@ void MojoCdmService::OnSessionMessage(const std::string& session_id, void MojoCdmService::OnSessionKeysChange(const std::string& session_id, bool has_additional_usable_key, CdmKeysInfo keys_info) { + DVLOG(2) << __FUNCTION__; mojo::Array<mojo::CdmKeyInformationPtr> keys_data; for (const auto& key : keys_info) keys_data.push_back(mojo::CdmKeyInformation::From(*key)); @@ -131,11 +160,13 @@ void MojoCdmService::OnSessionKeysChange(const std::string& session_id, void MojoCdmService::OnSessionExpirationUpdate( const std::string& session_id, const base::Time& new_expiry_time_sec) { + DVLOG(2) << __FUNCTION__; client_->OnSessionExpirationUpdate(session_id, new_expiry_time_sec.ToDoubleT()); } void MojoCdmService::OnSessionClosed(const std::string& session_id) { + DVLOG(2) << __FUNCTION__; client_->OnSessionClosed(session_id); } @@ -143,6 +174,7 @@ void MojoCdmService::OnLegacySessionError(const std::string& session_id, MediaKeys::Exception exception, uint32_t system_code, const std::string& error_message) { + DVLOG(2) << __FUNCTION__; client_->OnLegacySessionError(session_id, static_cast<mojo::CdmException>(exception), system_code, error_message); diff --git a/media/mojo/services/mojo_cdm_service.h b/media/mojo/services/mojo_cdm_service.h index 8e1198af..73497c9 100644 --- a/media/mojo/services/mojo_cdm_service.h +++ b/media/mojo/services/mojo_cdm_service.h @@ -11,15 +11,26 @@ #include "base/memory/weak_ptr.h" #include "media/base/media_keys.h" #include "media/mojo/interfaces/content_decryption_module.mojom.h" -#include "third_party/mojo/src/mojo/public/cpp/bindings/strong_binding.h" +#include "third_party/mojo/src/mojo/public/cpp/bindings/binding.h" +#include "third_party/mojo/src/mojo/public/cpp/bindings/error_handler.h" namespace media { +class MojoCdmServiceContext; + // A mojo::ContentDecryptionModule implementation backed by a media::MediaKeys. -class MojoCdmService : public mojo::ContentDecryptionModule { +class MojoCdmService : public mojo::ContentDecryptionModule, + public mojo::ErrorHandler { public: - MojoCdmService(const mojo::String& key_system, - mojo::InterfaceRequest<mojo::ContentDecryptionModule> request); + // Creates a MojoCdmService for |key_system| and weakly binds it to the + // |request|. Returns null and drops the |request| if no MojoCdmService can be + // created for |key_system|. When connection error happens, + // ServiceHadConnectionError() will be called on the |context|. + static scoped_ptr<MojoCdmService> Create( + const mojo::String& key_system, + MojoCdmServiceContext* context, + mojo::InterfaceRequest<mojo::ContentDecryptionModule> request); + ~MojoCdmService() final; // mojo::ContentDecryptionModule implementation. @@ -47,10 +58,21 @@ class MojoCdmService : public mojo::ContentDecryptionModule { void RemoveSession( const mojo::String& session_id, const mojo::Callback<void(mojo::CdmPromiseResultPtr)>& callback) final; + // TODO(xhwang): Rename this to GetDecryptor in the mojom interface. void GetCdmContext(int32_t cdm_id, mojo::InterfaceRequest<mojo::Decryptor> decryptor) final; + // Get CdmContext to be used by the media pipeline. + CdmContext* GetCdmContext(); + private: + MojoCdmService(const mojo::String& key_system, + MojoCdmServiceContext* context, + mojo::InterfaceRequest<mojo::ContentDecryptionModule> request); + + // mojo::ErrorHandler implementation. + void OnConnectionError() override; + // Callbacks for firing session events. void OnSessionMessage(const std::string& session_id, MediaKeys::MessageType message_type, @@ -67,8 +89,9 @@ class MojoCdmService : public mojo::ContentDecryptionModule { uint32_t system_code, const std::string& error_message); - mojo::StrongBinding<mojo::ContentDecryptionModule> binding_; + mojo::Binding<mojo::ContentDecryptionModule> binding_; + MojoCdmServiceContext* context_; scoped_ptr<MediaKeys> cdm_; mojo::ContentDecryptionModuleClientPtr client_; diff --git a/media/mojo/services/mojo_cdm_service_context.cc b/media/mojo/services/mojo_cdm_service_context.cc new file mode 100644 index 0000000..ddcf62c --- /dev/null +++ b/media/mojo/services/mojo_cdm_service_context.cc @@ -0,0 +1,53 @@ +// Copyright 2015 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 "media/mojo/services/mojo_cdm_service_context.h" + +#include "base/bind.h" +#include "base/logging.h" + +namespace media { + +MojoCdmServiceContext::MojoCdmServiceContext() { +} + +MojoCdmServiceContext::~MojoCdmServiceContext() { +} + +void MojoCdmServiceContext::CreateCdmService( + const mojo::String& key_system, + const mojo::String& security_origin, + int32_t cdm_id, + mojo::InterfaceRequest<mojo::ContentDecryptionModule> request) { + DVLOG(1) << __FUNCTION__ << ": " << key_system; + + // TODO(xhwang): pass |security_origin| down because CdmFactory needs it. + scopted_ptr<MojoCdmService> cdm_service = + MojoCdmService::Create(key_system, this, request.Pass()); + if (cdm_service) + services_.add(cdm_id, cdm_service.Pass()); +} + +CdmContext* MojoCdmServiceContext::GetCdmContext(int32_t cdm_id) { + MojoCdmService* service = services_.get(cdm_id); + if (!service) { + LOG(ERROR) << "CDM context not found: " << cdm_id; + return nullptr; + } + + return service->GetCdmContext(); +} + +void MojoCdmServiceContext::ServiceHadConnectionError(MojoCdmService* service) { + for (auto it = services_.begin(); it != services_.end(); ++it) { + if (it->second == service) { + services_.erase(it); // This destroys the |service|. + return; + } + } + + NOTREACHED() << "Service " << service << " not found."; +} + +} // namespace media diff --git a/media/mojo/services/mojo_cdm_service_context.h b/media/mojo/services/mojo_cdm_service_context.h new file mode 100644 index 0000000..28bc266 --- /dev/null +++ b/media/mojo/services/mojo_cdm_service_context.h @@ -0,0 +1,50 @@ +// Copyright 2015 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. + +#ifndef MEDIA_MOJO_SERVICES_MOJO_CDM_SERVICE_CONTEXT_H_ +#define MEDIA_MOJO_SERVICES_MOJO_CDM_SERVICE_CONTEXT_H_ + +#include "base/containers/scoped_ptr_hash_map.h" +#include "base/macros.h" +#include "media/base/cdm_context.h" +#include "media/base/media_export.h" +#include "media/mojo/services/mojo_cdm_service.h" + +namespace media { + +// A class that creates, owns and manages all MojoCdmService instances. +class MEDIA_EXPORT MojoCdmServiceContext : public CdmContextProvider { + public: + MojoCdmServiceContext(); + ~MojoCdmServiceContext() override; + + // Creates a MojoCdmService for |key_system| and weakly binds it to |request|. + // The created MojoCdmService is owned by |this|. The request will be dropped + // if no MojoCdmService can be created, resulting in a connection error. + void CreateCdmService( + const mojo::String& key_system, + const mojo::String& security_origin, + int32_t cdm_id, + mojo::InterfaceRequest<mojo::ContentDecryptionModule> request); + + // CdmContextProvider implementation. + // The returned CdmContext can be destroyed at any time if the pipe is + // disconnected. + // TODO(xhwang): When implementing SetCdm(), make sure we never dereference + // garbage. For example, use media::PlayerTracker. + CdmContext* GetCdmContext(int32_t cdm_id) override; + + // Called when there is a connection error with |service|. + void ServiceHadConnectionError(MojoCdmService* service); + + private: + // A map between CDM ID and MojoCdmService. Owns all MojoCdmService created. + base::ScopedPtrHashMap<int32_t, scoped_ptr<MojoCdmService>> services_; + + DISALLOW_COPY_AND_ASSIGN(MojoCdmServiceContext); +}; + +} // namespace media + +#endif // MEDIA_MOJO_SERVICES_MOJO_CDM_SERVICE_CONTEXT_H_ |