From 9bd8c733478345d0331fcdfd79e0bc61e22be68a Mon Sep 17 00:00:00 2001 From: xhwang Date: Mon, 13 Apr 2015 16:27:53 -0700 Subject: media: CdmFactory creates CDM (MediaKeys) asynchronously. This CL fixes the EME stack down to CdmFactory. The real aync CDM creation will be fixed in a separate CL. For unprefixed EME, since it's promise based, async creation of CDM fits easily. For prefixed EME, GenerateKeyRequest() can be called multiple times without waiting for the real CDM to be created/loaded. ProxyDecryptor is modified to handle this case. BUG=469003 TEST=All existing tests pass. Review URL: https://codereview.chromium.org/1070853004 Cr-Commit-Position: refs/heads/master@{#324942} --- media/cdm/default_cdm_factory.cc | 26 +++--- media/cdm/default_cdm_factory.h | 20 ++--- media/cdm/proxy_decryptor.cc | 165 ++++++++++++++++++++++++++------------- media/cdm/proxy_decryptor.h | 50 ++++++++---- 4 files changed, 171 insertions(+), 90 deletions(-) (limited to 'media/cdm') diff --git a/media/cdm/default_cdm_factory.cc b/media/cdm/default_cdm_factory.cc index a87760c..202f3cd 100644 --- a/media/cdm/default_cdm_factory.cc +++ b/media/cdm/default_cdm_factory.cc @@ -4,6 +4,10 @@ #include "media/cdm/default_cdm_factory.h" +#include "base/bind.h" +#include "base/bind_helpers.h" +#include "base/location.h" +#include "base/message_loop/message_loop_proxy.h" #include "media/base/key_systems.h" #include "media/cdm/aes_decryptor.h" #include "url/gurl.h" @@ -16,7 +20,7 @@ DefaultCdmFactory::DefaultCdmFactory() { DefaultCdmFactory::~DefaultCdmFactory() { } -scoped_ptr DefaultCdmFactory::Create( +void DefaultCdmFactory::Create( const std::string& key_system, bool allow_distinctive_identifier, bool allow_persistent_state, @@ -25,17 +29,19 @@ scoped_ptr DefaultCdmFactory::Create( const SessionClosedCB& session_closed_cb, const LegacySessionErrorCB& legacy_session_error_cb, const SessionKeysChangeCB& session_keys_change_cb, - const SessionExpirationUpdateCB& session_expiration_update_cb) { - if (!security_origin.is_valid()) - return nullptr; - - if (CanUseAesDecryptor(key_system)) { - return make_scoped_ptr(new AesDecryptor(security_origin, session_message_cb, - session_closed_cb, - session_keys_change_cb)); + const SessionExpirationUpdateCB& session_expiration_update_cb, + const CdmCreatedCB& cdm_created_cb) { + if (!security_origin.is_valid() || !CanUseAesDecryptor(key_system)) { + base::MessageLoopProxy::current()->PostTask( + FROM_HERE, base::Bind(cdm_created_cb, nullptr)); + return; } - return nullptr; + scoped_ptr cdm( + new AesDecryptor(security_origin, session_message_cb, session_closed_cb, + session_keys_change_cb)); + base::MessageLoopProxy::current()->PostTask( + FROM_HERE, base::Bind(cdm_created_cb, base::Passed(&cdm))); } } // namespace media diff --git a/media/cdm/default_cdm_factory.h b/media/cdm/default_cdm_factory.h index 72e0e8d..7060c7c 100644 --- a/media/cdm/default_cdm_factory.h +++ b/media/cdm/default_cdm_factory.h @@ -16,16 +16,16 @@ class DefaultCdmFactory : public CdmFactory { ~DefaultCdmFactory() final; // CdmFactory implementation. - scoped_ptr Create( - const std::string& key_system, - bool allow_distinctive_identifier, - bool allow_persistent_state, - const GURL& security_origin, - const SessionMessageCB& session_message_cb, - const SessionClosedCB& session_closed_cb, - const LegacySessionErrorCB& legacy_session_error_cb, - const SessionKeysChangeCB& session_keys_change_cb, - const SessionExpirationUpdateCB& session_expiration_update_cb) final; + void Create(const std::string& key_system, + bool allow_distinctive_identifier, + bool allow_persistent_state, + const GURL& security_origin, + const SessionMessageCB& session_message_cb, + const SessionClosedCB& session_closed_cb, + const LegacySessionErrorCB& legacy_session_error_cb, + const SessionKeysChangeCB& session_keys_change_cb, + const SessionExpirationUpdateCB& session_expiration_update_cb, + const CdmCreatedCB& cdm_created_cb) final; private: DISALLOW_COPY_AND_ASSIGN(DefaultCdmFactory); diff --git a/media/cdm/proxy_decryptor.cc b/media/cdm/proxy_decryptor.cc index 944b614..9260e14 100644 --- a/media/cdm/proxy_decryptor.cc +++ b/media/cdm/proxy_decryptor.cc @@ -25,11 +25,22 @@ namespace media { // EME API. const int kSessionClosedSystemCode = 29127; +ProxyDecryptor::PendingGenerateKeyRequestData::PendingGenerateKeyRequestData( + EmeInitDataType init_data_type, + const std::vector& init_data) + : init_data_type(init_data_type), init_data(init_data) { +} + +ProxyDecryptor::PendingGenerateKeyRequestData:: + ~PendingGenerateKeyRequestData() { +} + ProxyDecryptor::ProxyDecryptor(MediaPermission* media_permission, const KeyAddedCB& key_added_cb, const KeyErrorCB& key_error_cb, const KeyMessageCB& key_message_cb) - : media_permission_(media_permission), + : is_creating_cdm_(false), + media_permission_(media_permission), key_added_cb_(key_added_cb), key_error_cb_(key_error_cb), key_message_cb_(key_message_cb), @@ -46,33 +57,77 @@ ProxyDecryptor::~ProxyDecryptor() { media_keys_.reset(); } -CdmContext* ProxyDecryptor::GetCdmContext() { - return media_keys_ ? media_keys_->GetCdmContext() : nullptr; +void ProxyDecryptor::CreateCdm(CdmFactory* cdm_factory, + const std::string& key_system, + const GURL& security_origin, + const CdmContextReadyCB& cdm_context_ready_cb) { + DVLOG(1) << __FUNCTION__ << ": key_system = " << key_system; + DCHECK(!is_creating_cdm_); + DCHECK(!media_keys_); + + // TODO(sandersd): Trigger permissions check here and use it to determine + // distinctive identifier support, instead of always requiring the + // permission. http://crbug.com/455271 + bool allow_distinctive_identifier = true; + bool allow_persistent_state = true; + + is_creating_cdm_ = true; + + base::WeakPtr weak_this = weak_ptr_factory_.GetWeakPtr(); + cdm_factory->Create( + key_system, allow_distinctive_identifier, allow_persistent_state, + security_origin, base::Bind(&ProxyDecryptor::OnSessionMessage, weak_this), + base::Bind(&ProxyDecryptor::OnSessionClosed, weak_this), + base::Bind(&ProxyDecryptor::OnLegacySessionError, weak_this), + base::Bind(&ProxyDecryptor::OnSessionKeysChange, weak_this), + base::Bind(&ProxyDecryptor::OnSessionExpirationUpdate, weak_this), + base::Bind(&ProxyDecryptor::OnCdmCreated, weak_this, key_system, + security_origin, cdm_context_ready_cb)); } -bool ProxyDecryptor::InitializeCDM(CdmFactory* cdm_factory, - const std::string& key_system, - const GURL& security_origin) { - DVLOG(1) << "InitializeCDM: key_system = " << key_system; +void ProxyDecryptor::OnCdmCreated(const std::string& key_system, + const GURL& security_origin, + const CdmContextReadyCB& cdm_context_ready_cb, + scoped_ptr cdm) { + is_creating_cdm_ = false; - DCHECK(!media_keys_); - media_keys_ = CreateMediaKeys(cdm_factory, key_system, security_origin); - if (!media_keys_) - return false; + if (!cdm) { + cdm_context_ready_cb.Run(nullptr); + return; + } key_system_ = key_system; security_origin_ = security_origin; + is_clear_key_ = IsClearKey(key_system) || IsExternalClearKey(key_system); + media_keys_ = cdm.Pass(); + + cdm_context_ready_cb.Run(media_keys_->GetCdmContext()); + + for (const auto& request : pending_requests_) + GenerateKeyRequestInternal(request->init_data_type, request->init_data); + + pending_requests_.clear(); +} + +void ProxyDecryptor::GenerateKeyRequest(EmeInitDataType init_data_type, + const uint8* init_data, + int init_data_length) { + std::vector init_data_vector(init_data, init_data + init_data_length); + + if (is_creating_cdm_) { + pending_requests_.push_back( + new PendingGenerateKeyRequestData(init_data_type, init_data_vector)); + return; + } - is_clear_key_ = - IsClearKey(key_system) || IsExternalClearKey(key_system); - return true; + GenerateKeyRequestInternal(init_data_type, init_data_vector); } // Returns true if |data| is prefixed with |header| and has data after the // |header|. -bool HasHeader(const uint8* data, int data_length, const std::string& header) { - return static_cast(data_length) > header.size() && - std::equal(data, data + header.size(), header.begin()); +bool HasHeader(const std::vector& data, const std::string& header) { + return data.size() > header.size() && + std::equal(header.begin(), header.end(), data.begin()); } // Removes the first |length| items from |data|. @@ -80,23 +135,30 @@ void StripHeader(std::vector& data, size_t length) { data.erase(data.begin(), data.begin() + length); } -bool ProxyDecryptor::GenerateKeyRequest(EmeInitDataType init_data_type, - const uint8* init_data, - int init_data_length) { - DVLOG(1) << "GenerateKeyRequest()"; +void ProxyDecryptor::GenerateKeyRequestInternal( + EmeInitDataType init_data_type, + const std::vector& init_data) { + DVLOG(1) << __FUNCTION__; + DCHECK(!is_creating_cdm_); + + if (!media_keys_) { + OnLegacySessionError(std::string(), MediaKeys::NOT_SUPPORTED_ERROR, 0, + "CDM creation failed."); + return; + } + const char kPrefixedApiPersistentSessionHeader[] = "PERSISTENT|"; const char kPrefixedApiLoadSessionHeader[] = "LOAD_SESSION|"; SessionCreationType session_creation_type = TemporarySession; - std::vector init_data_vector(init_data, init_data + init_data_length); - if (HasHeader(init_data, init_data_length, kPrefixedApiLoadSessionHeader)) { + std::vector stripped_init_data = init_data; + if (HasHeader(init_data, kPrefixedApiLoadSessionHeader)) { session_creation_type = LoadSession; - StripHeader(init_data_vector, strlen(kPrefixedApiLoadSessionHeader)); - } else if (HasHeader(init_data, - init_data_length, - kPrefixedApiPersistentSessionHeader)) { + StripHeader(stripped_init_data, strlen(kPrefixedApiLoadSessionHeader)); + } else if (HasHeader(init_data, kPrefixedApiPersistentSessionHeader)) { session_creation_type = PersistentSession; - StripHeader(init_data_vector, strlen(kPrefixedApiPersistentSessionHeader)); + StripHeader(stripped_init_data, + strlen(kPrefixedApiPersistentSessionHeader)); } scoped_ptr promise(new CdmCallbackPromise( @@ -110,10 +172,10 @@ bool ProxyDecryptor::GenerateKeyRequest(EmeInitDataType init_data_type, media_keys_->LoadSession( MediaKeys::PERSISTENT_LICENSE_SESSION, std::string( - reinterpret_cast(vector_as_array(&init_data_vector)), - init_data_vector.size()), + reinterpret_cast(vector_as_array(&stripped_init_data)), + stripped_init_data.size()), promise.Pass()); - return true; + return; } MediaKeys::SessionType session_type = @@ -125,9 +187,9 @@ bool ProxyDecryptor::GenerateKeyRequest(EmeInitDataType init_data_type, // external clear key. DCHECK(!key_system_.empty()); if (CanUseAesDecryptor(key_system_) || IsExternalClearKey(key_system_)) { - OnPermissionStatus(session_type, init_data_type, init_data_vector, + OnPermissionStatus(session_type, init_data_type, stripped_init_data, promise.Pass(), true /* granted */); - return true; + return; } #if defined(OS_CHROMEOS) || defined(OS_ANDROID) @@ -135,13 +197,11 @@ bool ProxyDecryptor::GenerateKeyRequest(EmeInitDataType init_data_type, MediaPermission::PROTECTED_MEDIA_IDENTIFIER, security_origin_, base::Bind(&ProxyDecryptor::OnPermissionStatus, weak_ptr_factory_.GetWeakPtr(), session_type, init_data_type, - init_data_vector, base::Passed(&promise))); + stripped_init_data, base::Passed(&promise))); #else - OnPermissionStatus(session_type, init_data_type, init_data_vector, + OnPermissionStatus(session_type, init_data_type, stripped_init_data, promise.Pass(), true /* granted */); #endif - - return true; } void ProxyDecryptor::OnPermissionStatus( @@ -168,6 +228,12 @@ void ProxyDecryptor::AddKey(const uint8* key, const std::string& session_id) { DVLOG(1) << "AddKey()"; + if (!media_keys_) { + OnLegacySessionError(std::string(), MediaKeys::INVALID_STATE_ERROR, 0, + "CDM is not available."); + return; + } + // In the prefixed API, the session parameter provided to addKey() is // optional, so use the single existing session if it exists. std::string new_session_id(session_id); @@ -216,6 +282,12 @@ void ProxyDecryptor::AddKey(const uint8* key, void ProxyDecryptor::CancelKeyRequest(const std::string& session_id) { DVLOG(1) << "CancelKeyRequest()"; + if (!media_keys_) { + OnLegacySessionError(std::string(), MediaKeys::INVALID_STATE_ERROR, 0, + "CDM is not available."); + return; + } + scoped_ptr promise(new CdmCallbackPromise<>( base::Bind(&ProxyDecryptor::OnSessionClosed, weak_ptr_factory_.GetWeakPtr(), session_id), @@ -224,25 +296,6 @@ void ProxyDecryptor::CancelKeyRequest(const std::string& session_id) { media_keys_->RemoveSession(session_id, promise.Pass()); } -scoped_ptr ProxyDecryptor::CreateMediaKeys( - CdmFactory* cdm_factory, - const std::string& key_system, - const GURL& security_origin) { - // TODO(sandersd): Trigger permissions check here and use it to determine - // distinctive identifier support, instead of always requiring the - // permission. http://crbug.com/455271 - bool allow_distinctive_identifier = true; - bool allow_persistent_state = true; - base::WeakPtr weak_this = weak_ptr_factory_.GetWeakPtr(); - return cdm_factory->Create( - key_system, allow_distinctive_identifier, allow_persistent_state, - security_origin, base::Bind(&ProxyDecryptor::OnSessionMessage, weak_this), - base::Bind(&ProxyDecryptor::OnSessionClosed, weak_this), - base::Bind(&ProxyDecryptor::OnLegacySessionError, weak_this), - base::Bind(&ProxyDecryptor::OnSessionKeysChange, weak_this), - base::Bind(&ProxyDecryptor::OnSessionExpirationUpdate, weak_this)); -} - void ProxyDecryptor::OnSessionMessage(const std::string& session_id, MediaKeys::MessageType message_type, const std::vector& message, diff --git a/media/cdm/proxy_decryptor.h b/media/cdm/proxy_decryptor.h index ff611ad..9a17a98 100644 --- a/media/cdm/proxy_decryptor.h +++ b/media/cdm/proxy_decryptor.h @@ -11,7 +11,9 @@ #include "base/basictypes.h" #include "base/containers/hash_tables.h" #include "base/memory/scoped_ptr.h" +#include "base/memory/scoped_vector.h" #include "base/memory/weak_ptr.h" +#include "media/base/cdm_context.h" #include "media/base/decryptor.h" #include "media/base/eme_constants.h" #include "media/base/media_export.h" @@ -32,6 +34,10 @@ class MediaPermission; // TODO(xhwang): The ProxyDecryptor is not a Decryptor. Find a better name! class MEDIA_EXPORT ProxyDecryptor { public: + // Callback to provide a CdmContext when the CDM creation is finished. + // If CDM creation failed, |cdm_context| will be null. + typedef base::Callback CdmContextReadyCB; + // These are similar to the callbacks in media_keys.h, but pass back the // session ID rather than the internal session ID. typedef base::Callback KeyAddedCB; @@ -48,16 +54,16 @@ class MEDIA_EXPORT ProxyDecryptor { const KeyMessageCB& key_message_cb); virtual ~ProxyDecryptor(); - // Returns the CdmContext associated with this object. - CdmContext* GetCdmContext(); - - // Only call this once. - bool InitializeCDM(CdmFactory* cdm_factory, - const std::string& key_system, - const GURL& security_origin); + // Creates the CDM and fires |cdm_created_cb|. This method should only be + // called once. If CDM creation failed, all following GenerateKeyRequest, + // AddKey and CancelKeyRequest calls will result in a KeyError. + void CreateCdm(CdmFactory* cdm_factory, + const std::string& key_system, + const GURL& security_origin, + const CdmContextReadyCB& cdm_context_ready_cb); - // May only be called after InitializeCDM() succeeds. - bool GenerateKeyRequest(EmeInitDataType init_data_type, + // May only be called after CreateCDM(). + void GenerateKeyRequest(EmeInitDataType init_data_type, const uint8* init_data, int init_data_length); void AddKey(const uint8* key, int key_length, @@ -66,11 +72,14 @@ class MEDIA_EXPORT ProxyDecryptor { void CancelKeyRequest(const std::string& session_id); private: - // Helper function to create MediaKeys to handle the given |key_system|. - scoped_ptr CreateMediaKeys( - CdmFactory* cdm_factory, - const std::string& key_system, - const GURL& security_origin); + // Callback for CreateCdm(). + void OnCdmCreated(const std::string& key_system, + const GURL& security_origin, + const CdmContextReadyCB& cdm_context_ready_cb, + scoped_ptr cdm); + + void GenerateKeyRequestInternal(EmeInitDataType init_data_type, + const std::vector& init_data); // Callbacks for firing session events. void OnSessionMessage(const std::string& session_id, @@ -106,6 +115,17 @@ class MEDIA_EXPORT ProxyDecryptor { void SetSessionId(SessionCreationType session_type, const std::string& session_id); + struct PendingGenerateKeyRequestData { + PendingGenerateKeyRequestData(EmeInitDataType init_data_type, + const std::vector& init_data); + ~PendingGenerateKeyRequestData(); + + const EmeInitDataType init_data_type; + const std::vector init_data; + }; + + bool is_creating_cdm_; + // The real MediaKeys that manages key operations for the ProxyDecryptor. scoped_ptr media_keys_; @@ -124,6 +144,8 @@ class MEDIA_EXPORT ProxyDecryptor { bool is_clear_key_; + ScopedVector pending_requests_; + // NOTE: Weak pointers must be invalidated before all other member variables. base::WeakPtrFactory weak_ptr_factory_; -- cgit v1.1