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/base/cdm_factory.h | 9 +- media/blink/cdm_session_adapter.cc | 67 ++++++---- media/blink/cdm_session_adapter.h | 24 ++-- media/blink/encrypted_media_player_support.cc | 49 +++----- media/blink/encrypted_media_player_support.h | 8 +- media/blink/webcontentdecryptionmodule_impl.cc | 22 ++-- media/blink/webcontentdecryptionmodule_impl.h | 2 + media/blink/webmediaplayer_impl.cc | 25 ++-- media/blink/webmediaplayer_impl.h | 4 +- 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 +++++--- 13 files changed, 288 insertions(+), 183 deletions(-) (limited to 'media') diff --git a/media/base/cdm_factory.h b/media/base/cdm_factory.h index 2a9b42d..f9c5541 100644 --- a/media/base/cdm_factory.h +++ b/media/base/cdm_factory.h @@ -17,10 +17,14 @@ namespace media { class MEDIA_EXPORT CdmFactory { public: + using CdmCreatedCB = base::Callback)>; + CdmFactory(); virtual ~CdmFactory(); - virtual scoped_ptr Create( + // Creates a CDM for |key_system| and returns it through |cdm_created_cb| + // asynchronously. + virtual void Create( const std::string& key_system, bool allow_distinctive_identifier, bool allow_persistent_state, @@ -29,7 +33,8 @@ class MEDIA_EXPORT CdmFactory { const SessionClosedCB& session_closed_cb, const LegacySessionErrorCB& legacy_session_error_cb, const SessionKeysChangeCB& session_keys_change_cb, - const SessionExpirationUpdateCB& session_expiration_update_cb) = 0; + const SessionExpirationUpdateCB& session_expiration_update_cb, + const CdmCreatedCB& cdm_created_cb) = 0; private: DISALLOW_COPY_AND_ASSIGN(CdmFactory); diff --git a/media/blink/cdm_session_adapter.cc b/media/blink/cdm_session_adapter.cc index 79f5054..8e62bce 100644 --- a/media/blink/cdm_session_adapter.cc +++ b/media/blink/cdm_session_adapter.cc @@ -13,6 +13,7 @@ #include "media/base/cdm_promise.h" #include "media/base/key_systems.h" #include "media/base/media_keys.h" +#include "media/blink/webcontentdecryptionmodule_impl.h" #include "media/blink/webcontentdecryptionmodulesession_impl.h" #include "url/gurl.h" @@ -26,33 +27,34 @@ CdmSessionAdapter::CdmSessionAdapter() : weak_ptr_factory_(this) { CdmSessionAdapter::~CdmSessionAdapter() {} -bool CdmSessionAdapter::Initialize(CdmFactory* cdm_factory, - const std::string& key_system, - bool allow_distinctive_identifier, - bool allow_persistent_state, - const GURL& security_origin) { - key_system_ = key_system; - key_system_uma_prefix_ = - kMediaEME + GetKeySystemNameForUMA(key_system) + kDot; - +void CdmSessionAdapter::CreateCdm( + CdmFactory* cdm_factory, + const std::string& key_system, + bool allow_distinctive_identifier, + bool allow_persistent_state, + const GURL& security_origin, + blink::WebContentDecryptionModuleResult result) { + // Note: WebContentDecryptionModuleImpl::Create() calls this method without + // holding a reference to the CdmSessionAdapter. Bind OnCdmCreated() with + // |this| instead of |weak_this| to prevent |this| from being desctructed. base::WeakPtr weak_this = weak_ptr_factory_.GetWeakPtr(); - media_keys_ = cdm_factory->Create( + cdm_factory->Create( key_system, allow_distinctive_identifier, allow_persistent_state, security_origin, base::Bind(&CdmSessionAdapter::OnSessionMessage, weak_this), base::Bind(&CdmSessionAdapter::OnSessionClosed, weak_this), base::Bind(&CdmSessionAdapter::OnLegacySessionError, weak_this), base::Bind(&CdmSessionAdapter::OnSessionKeysChange, weak_this), - base::Bind(&CdmSessionAdapter::OnSessionExpirationUpdate, weak_this)); - return media_keys_.get() != nullptr; + base::Bind(&CdmSessionAdapter::OnSessionExpirationUpdate, weak_this), + base::Bind(&CdmSessionAdapter::OnCdmCreated, this, key_system, result)); } void CdmSessionAdapter::SetServerCertificate( const uint8* server_certificate, int server_certificate_length, scoped_ptr promise) { - media_keys_->SetServerCertificate( - server_certificate, server_certificate_length, promise.Pass()); + cdm_->SetServerCertificate(server_certificate, server_certificate_length, + promise.Pass()); } WebContentDecryptionModuleSessionImpl* CdmSessionAdapter::CreateSession() { @@ -81,37 +83,35 @@ void CdmSessionAdapter::InitializeNewSession( int init_data_length, MediaKeys::SessionType session_type, scoped_ptr promise) { - media_keys_->CreateSessionAndGenerateRequest(session_type, init_data_type, - init_data, init_data_length, - promise.Pass()); + cdm_->CreateSessionAndGenerateRequest(session_type, init_data_type, init_data, + init_data_length, promise.Pass()); } void CdmSessionAdapter::LoadSession(MediaKeys::SessionType session_type, const std::string& session_id, scoped_ptr promise) { - media_keys_->LoadSession(session_type, session_id, promise.Pass()); + cdm_->LoadSession(session_type, session_id, promise.Pass()); } void CdmSessionAdapter::UpdateSession(const std::string& session_id, const uint8* response, int response_length, scoped_ptr promise) { - media_keys_->UpdateSession(session_id, response, response_length, - promise.Pass()); + cdm_->UpdateSession(session_id, response, response_length, promise.Pass()); } void CdmSessionAdapter::CloseSession(const std::string& session_id, scoped_ptr promise) { - media_keys_->CloseSession(session_id, promise.Pass()); + cdm_->CloseSession(session_id, promise.Pass()); } void CdmSessionAdapter::RemoveSession(const std::string& session_id, scoped_ptr promise) { - media_keys_->RemoveSession(session_id, promise.Pass()); + cdm_->RemoveSession(session_id, promise.Pass()); } CdmContext* CdmSessionAdapter::GetCdmContext() { - return media_keys_->GetCdmContext(); + return cdm_->GetCdmContext(); } const std::string& CdmSessionAdapter::GetKeySystem() const { @@ -122,6 +122,27 @@ const std::string& CdmSessionAdapter::GetKeySystemUMAPrefix() const { return key_system_uma_prefix_; } +void CdmSessionAdapter::OnCdmCreated( + const std::string& key_system, + blink::WebContentDecryptionModuleResult result, + scoped_ptr cdm) { + DVLOG(2) << __FUNCTION__; + if (!cdm) { + result.completeWithError( + blink::WebContentDecryptionModuleExceptionNotSupportedError, 0, + "Failed to create the CDM instance."); + return; + } + + key_system_ = key_system; + key_system_uma_prefix_ = + kMediaEME + GetKeySystemNameForUMA(key_system) + kDot; + cdm_ = cdm.Pass(); + + result.completeWithContentDecryptionModule( + new WebContentDecryptionModuleImpl(this)); +} + void CdmSessionAdapter::OnSessionMessage( const std::string& session_id, MediaKeys::MessageType message_type, diff --git a/media/blink/cdm_session_adapter.h b/media/blink/cdm_session_adapter.h index e35c4cb..5ed7550e 100644 --- a/media/blink/cdm_session_adapter.h +++ b/media/blink/cdm_session_adapter.h @@ -13,6 +13,7 @@ #include "base/memory/ref_counted.h" #include "base/memory/weak_ptr.h" #include "media/base/media_keys.h" +#include "third_party/WebKit/public/platform/WebContentDecryptionModuleResult.h" #include "third_party/WebKit/public/platform/WebContentDecryptionModuleSession.h" class GURL; @@ -30,12 +31,14 @@ class CdmSessionAdapter : public base::RefCounted { public: CdmSessionAdapter(); - // Returns true on success. - bool Initialize(CdmFactory* cdm_factory, - const std::string& key_system, - bool allow_distinctive_identifier, - bool allow_persistent_state, - const GURL& security_origin); + // Creates the CDM for |key_system| using |cdm_factory| and returns the result + // via |result|. + void CreateCdm(CdmFactory* cdm_factory, + const std::string& key_system, + bool allow_distinctive_identifier, + bool allow_persistent_state, + const GURL& security_origin, + blink::WebContentDecryptionModuleResult result); // Provides a server certificate to be used to encrypt messages to the // license server. @@ -99,12 +102,19 @@ class CdmSessionAdapter : public base::RefCounted { private: friend class base::RefCounted; + + // Session ID to WebContentDecryptionModuleSessionImpl mapping. typedef base::hash_map > SessionMap; ~CdmSessionAdapter(); + // Callback for CreateCdm(). + void OnCdmCreated(const std::string& key_system, + blink::WebContentDecryptionModuleResult result, + scoped_ptr cdm); + // Callbacks for firing session events. void OnSessionMessage(const std::string& session_id, MediaKeys::MessageType message_type, @@ -125,7 +135,7 @@ class CdmSessionAdapter : public base::RefCounted { WebContentDecryptionModuleSessionImpl* GetSession( const std::string& session_id); - scoped_ptr media_keys_; + scoped_ptr cdm_; SessionMap sessions_; diff --git a/media/blink/encrypted_media_player_support.cc b/media/blink/encrypted_media_player_support.cc index a84e24c..cc11a2e 100644 --- a/media/blink/encrypted_media_player_support.cc +++ b/media/blink/encrypted_media_player_support.cc @@ -116,12 +116,12 @@ EncryptedMediaPlayerSupport::EncryptedMediaPlayerSupport( CdmFactory* cdm_factory, blink::WebMediaPlayerClient* client, MediaPermission* media_permission, - const SetCdmContextCB& set_cdm_context_cb) + const CdmContextReadyCB& cdm_context_ready_cb) : cdm_factory_(cdm_factory), client_(client), media_permission_(media_permission), init_data_type_(EmeInitDataType::UNKNOWN), - set_cdm_context_cb_(set_cdm_context_cb) { + cdm_context_ready_cb_(cdm_context_ready_cb) { } EncryptedMediaPlayerSupport::~EncryptedMediaPlayerSupport() { @@ -155,43 +155,32 @@ EncryptedMediaPlayerSupport::GenerateKeyRequestInternal( if (!PrefixedIsSupportedConcreteKeySystem(key_system)) return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported; - // We do not support run-time switching between key systems for now. - if (current_key_system_.empty()) { - if (!proxy_decryptor_) { - proxy_decryptor_.reset(new ProxyDecryptor( - media_permission_, - BIND_TO_RENDER_LOOP(&EncryptedMediaPlayerSupport::OnKeyAdded), - BIND_TO_RENDER_LOOP(&EncryptedMediaPlayerSupport::OnKeyError), - BIND_TO_RENDER_LOOP(&EncryptedMediaPlayerSupport::OnKeyMessage))); - } + if (!proxy_decryptor_) { + DCHECK(current_key_system_.empty()); + DCHECK(!cdm_context_ready_cb_.is_null()); + proxy_decryptor_.reset(new ProxyDecryptor( + media_permission_, + BIND_TO_RENDER_LOOP(&EncryptedMediaPlayerSupport::OnKeyAdded), + BIND_TO_RENDER_LOOP(&EncryptedMediaPlayerSupport::OnKeyError), + BIND_TO_RENDER_LOOP(&EncryptedMediaPlayerSupport::OnKeyMessage))); GURL security_origin(frame->document().securityOrigin().toString()); - - if (!proxy_decryptor_->InitializeCDM(cdm_factory_, key_system, - security_origin)) { - return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported; - } - - if (proxy_decryptor_ && !set_cdm_context_cb_.is_null()) { - base::ResetAndReturn(&set_cdm_context_cb_) - .Run(proxy_decryptor_->GetCdmContext(), - base::Bind(&IgnoreCdmAttached)); - } - + proxy_decryptor_->CreateCdm(cdm_factory_, key_system, security_origin, + cdm_context_ready_cb_); current_key_system_ = key_system; - } else if (key_system != current_key_system_) { - return WebMediaPlayer::MediaKeyExceptionInvalidPlayerState; } + // We do not support run-time switching between key systems for now. + DCHECK(!current_key_system_.empty()); + if (key_system != current_key_system_) + return WebMediaPlayer::MediaKeyExceptionInvalidPlayerState; + EmeInitDataType init_data_type = init_data_type_; if (init_data_type == EmeInitDataType::UNKNOWN) init_data_type = GuessInitDataType(init_data, init_data_length); - if (!proxy_decryptor_->GenerateKeyRequest(init_data_type, init_data, - init_data_length)) { - current_key_system_.clear(); - return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported; - } + proxy_decryptor_->GenerateKeyRequest(init_data_type, init_data, + init_data_length); return WebMediaPlayer::MediaKeyExceptionNoError; } diff --git a/media/blink/encrypted_media_player_support.h b/media/blink/encrypted_media_player_support.h index 30d8708..d3fd454 100644 --- a/media/blink/encrypted_media_player_support.h +++ b/media/blink/encrypted_media_player_support.h @@ -37,13 +37,13 @@ class WebContentDecryptionModuleImpl; class EncryptedMediaPlayerSupport : public base::SupportsWeakPtr { public: - typedef base::Callback - SetCdmContextCB; + using CdmContextReadyCB = ProxyDecryptor::CdmContextReadyCB; + // |cdm_context_ready_cb| is called when the CDM instance creation completes. EncryptedMediaPlayerSupport(CdmFactory* cdm_factory, blink::WebMediaPlayerClient* client, MediaPermission* media_permission, - const SetCdmContextCB& set_cdm_context_cb); + const CdmContextReadyCB& cdm_context_ready_cb); ~EncryptedMediaPlayerSupport(); blink::WebMediaPlayer::MediaKeyException GenerateKeyRequest( @@ -109,7 +109,7 @@ class EncryptedMediaPlayerSupport // init data type. EmeInitDataType init_data_type_; - SetCdmContextCB set_cdm_context_cb_; + CdmContextReadyCB cdm_context_ready_cb_; // Manages decryption keys and decrypts encrypted frames. scoped_ptr proxy_decryptor_; diff --git a/media/blink/webcontentdecryptionmodule_impl.cc b/media/blink/webcontentdecryptionmodule_impl.cc index 9d60220..7bdd9c9 100644 --- a/media/blink/webcontentdecryptionmodule_impl.cc +++ b/media/blink/webcontentdecryptionmodule_impl.cc @@ -62,21 +62,15 @@ void WebContentDecryptionModuleImpl::Create( } GURL security_origin_as_gurl(security_origin.toString()); - scoped_refptr adapter(new CdmSessionAdapter()); - // TODO(jrummell): Pass WebContentDecryptionModuleResult (or similar) to - // Initialize() so that more specific errors can be reported. - if (!adapter->Initialize(cdm_factory, key_system_ascii, - allow_distinctive_identifier, - allow_persistent_state, security_origin_as_gurl)) { - result.completeWithError( - blink::WebContentDecryptionModuleExceptionNotSupportedError, 0, - "Failed to initialize CDM."); - return; - } - - result.completeWithContentDecryptionModule( - new WebContentDecryptionModuleImpl(adapter)); + // CdmSessionAdapter::CreateCdm() will keep a reference to |adapter|. Then + // if WebContentDecryptionModuleImpl is successfully created (returned in + // |result|), it will keep a reference to |adapter|. Otherwise, |adapter| will + // be destructed. + scoped_refptr adapter(new CdmSessionAdapter()); + adapter->CreateCdm(cdm_factory, key_system_ascii, + allow_distinctive_identifier, allow_persistent_state, + security_origin_as_gurl, result); } WebContentDecryptionModuleImpl::WebContentDecryptionModuleImpl( diff --git a/media/blink/webcontentdecryptionmodule_impl.h b/media/blink/webcontentdecryptionmodule_impl.h index 8b38cc6..aa7fa16 100644 --- a/media/blink/webcontentdecryptionmodule_impl.h +++ b/media/blink/webcontentdecryptionmodule_impl.h @@ -52,6 +52,8 @@ class MEDIA_EXPORT WebContentDecryptionModuleImpl CdmContext* GetCdmContext(); private: + friend CdmSessionAdapter; + // Takes reference to |adapter|. WebContentDecryptionModuleImpl(scoped_refptr adapter); diff --git a/media/blink/webmediaplayer_impl.cc b/media/blink/webmediaplayer_impl.cc index fe30c5e..ab62772 100644 --- a/media/blink/webmediaplayer_impl.cc +++ b/media/blink/webmediaplayer_impl.cc @@ -137,11 +137,12 @@ WebMediaPlayerImpl::WebMediaPlayerImpl( compositor_(new VideoFrameCompositor( BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnNaturalSizeChanged), BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnOpacityChanged))), - encrypted_media_support_( - cdm_factory, - client, - params.media_permission(), - base::Bind(&WebMediaPlayerImpl::SetCdm, AsWeakPtr())), + encrypted_media_support_(cdm_factory, + client, + params.media_permission(), + base::Bind(&WebMediaPlayerImpl::SetCdm, + AsWeakPtr(), + base::Bind(&IgnoreCdmAttached))), renderer_factory_(renderer_factory.Pass()) { // Threaded compositing isn't enabled universally yet. if (!compositor_task_runner_.get()) @@ -151,9 +152,9 @@ WebMediaPlayerImpl::WebMediaPlayerImpl( media_log_->CreateEvent(MediaLogEvent::WEBMEDIAPLAYER_CREATED)); if (params.initial_cdm()) { - SetCdm( - ToWebContentDecryptionModuleImpl(params.initial_cdm())->GetCdmContext(), - base::Bind(&IgnoreCdmAttached)); + SetCdm(base::Bind(&IgnoreCdmAttached), + ToWebContentDecryptionModuleImpl(params.initial_cdm()) + ->GetCdmContext()); } // TODO(xhwang): When we use an external Renderer, many methods won't work, @@ -660,8 +661,8 @@ void WebMediaPlayerImpl::setContentDecryptionModule( return; } - SetCdm(ToWebContentDecryptionModuleImpl(cdm)->GetCdmContext(), - BIND_TO_RENDER_LOOP1(&WebMediaPlayerImpl::OnCdmAttached, result)); + SetCdm(BIND_TO_RENDER_LOOP1(&WebMediaPlayerImpl::OnCdmAttached, result), + ToWebContentDecryptionModuleImpl(cdm)->GetCdmContext()); } void WebMediaPlayerImpl::OnEncryptedMediaInitData( @@ -695,8 +696,8 @@ void WebMediaPlayerImpl::OnWaitingForDecryptionKey() { client_->didResumePlaybackBlockedForKey(); } -void WebMediaPlayerImpl::SetCdm(CdmContext* cdm_context, - const CdmAttachedCB& cdm_attached_cb) { +void WebMediaPlayerImpl::SetCdm(const CdmAttachedCB& cdm_attached_cb, + CdmContext* cdm_context) { pipeline_.SetCdm(cdm_context, cdm_attached_cb); } diff --git a/media/blink/webmediaplayer_impl.h b/media/blink/webmediaplayer_impl.h index 80bea8f..427357b 100644 --- a/media/blink/webmediaplayer_impl.h +++ b/media/blink/webmediaplayer_impl.h @@ -224,7 +224,9 @@ class MEDIA_EXPORT WebMediaPlayerImpl // is not available. void OnWaitingForDecryptionKey(); - void SetCdm(CdmContext* cdm_context, const CdmAttachedCB& cdm_attached_cb); + // Sets |cdm_context| on the pipeline and fires |cdm_attached_cb| when done. + // Parameter order is reversed for easy binding. + void SetCdm(const CdmAttachedCB& cdm_attached_cb, CdmContext* cdm_context); // Called when a CDM has been attached to the |pipeline_|. void OnCdmAttached(blink::WebContentDecryptionModuleResult result, 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