diff options
Diffstat (limited to 'chromium/content/renderer/media/crypto/proxy_decryptor.cc')
-rw-r--r-- | chromium/content/renderer/media/crypto/proxy_decryptor.cc | 310 |
1 files changed, 167 insertions, 143 deletions
diff --git a/chromium/content/renderer/media/crypto/proxy_decryptor.cc b/chromium/content/renderer/media/crypto/proxy_decryptor.cc index 0016d19a7e8..28cd421e85f 100644 --- a/chromium/content/renderer/media/crypto/proxy_decryptor.cc +++ b/chromium/content/renderer/media/crypto/proxy_decryptor.cc @@ -4,57 +4,56 @@ #include "content/renderer/media/crypto/proxy_decryptor.h" +#include <cstring> + #include "base/bind.h" #include "base/callback_helpers.h" #include "base/logging.h" #include "base/strings/string_util.h" #include "content/renderer/media/crypto/content_decryption_module_factory.h" -#if defined(OS_ANDROID) -#include "content/renderer/media/android/renderer_media_player_manager.h" -#endif // defined(OS_ANDROID) +#include "media/base/cdm_promise.h" #include "media/cdm/json_web_key.h" #include "media/cdm/key_system_names.h" -namespace content { +#if defined(ENABLE_PEPPER_CDMS) +#include "content/renderer/media/crypto/pepper_cdm_wrapper.h" +#endif // defined(ENABLE_PEPPER_CDMS) -// Since these reference IDs may conflict with the ones generated in -// WebContentDecryptionModuleSessionImpl for the short time both paths are -// active, start with 100000 and generate the IDs from there. -// TODO(jrummell): Only allow one path http://crbug.com/306680. -uint32 ProxyDecryptor::next_session_id_ = 100000; +#if defined(ENABLE_BROWSER_CDMS) +#include "content/renderer/media/crypto/renderer_cdm_manager.h" +#endif // defined(ENABLE_BROWSER_CDMS) -const uint32 kInvalidSessionId = 0; +namespace content { -#if defined(ENABLE_PEPPER_CDMS) -void ProxyDecryptor::DestroyHelperPlugin() { - ContentDecryptionModuleFactory::DestroyHelperPlugin( - web_media_player_client_, web_frame_); -} -#endif // defined(ENABLE_PEPPER_CDMS) +// Special system code to signal a closed persistent session in a SessionError() +// call. This is needed because there is no SessionClosed() call in the prefixed +// EME API. +const int kSessionClosedSystemCode = 29127; ProxyDecryptor::ProxyDecryptor( #if defined(ENABLE_PEPPER_CDMS) - blink::WebMediaPlayerClient* web_media_player_client, - blink::WebFrame* web_frame, -#elif defined(OS_ANDROID) - RendererMediaPlayerManager* manager, - int media_keys_id, + const CreatePepperCdmCB& create_pepper_cdm_cb, +#elif defined(ENABLE_BROWSER_CDMS) + RendererCdmManager* manager, #endif // defined(ENABLE_PEPPER_CDMS) const KeyAddedCB& key_added_cb, const KeyErrorCB& key_error_cb, const KeyMessageCB& key_message_cb) - : weak_ptr_factory_(this), + : #if defined(ENABLE_PEPPER_CDMS) - web_media_player_client_(web_media_player_client), - web_frame_(web_frame), -#elif defined(OS_ANDROID) + create_pepper_cdm_cb_(create_pepper_cdm_cb), +#elif defined(ENABLE_BROWSER_CDMS) manager_(manager), - media_keys_id_(media_keys_id), + cdm_id_(RendererCdmManager::kInvalidCdmId), #endif // defined(ENABLE_PEPPER_CDMS) key_added_cb_(key_added_cb), key_error_cb_(key_error_cb), key_message_cb_(key_message_cb), - is_clear_key_(false) { + is_clear_key_(false), + weak_ptr_factory_(this) { +#if defined(ENABLE_PEPPER_CDMS) + DCHECK(!create_pepper_cdm_cb_.is_null()); +#endif // defined(ENABLE_PEPPER_CDMS) DCHECK(!key_added_cb_.is_null()); DCHECK(!key_error_cb_.is_null()); DCHECK(!key_message_cb_.is_null()); @@ -62,67 +61,75 @@ ProxyDecryptor::ProxyDecryptor( ProxyDecryptor::~ProxyDecryptor() { // Destroy the decryptor explicitly before destroying the plugin. - { - base::AutoLock auto_lock(lock_); - media_keys_.reset(); - } + media_keys_.reset(); } -// TODO(xhwang): Support multiple decryptor notification request (e.g. from -// video and audio decoders). The current implementation is okay for the current -// media pipeline since we initialize audio and video decoders in sequence. -// But ProxyDecryptor should not depend on media pipeline's implementation -// detail. -void ProxyDecryptor::SetDecryptorReadyCB( - const media::DecryptorReadyCB& decryptor_ready_cb) { - base::AutoLock auto_lock(lock_); - - // Cancels the previous decryptor request. - if (decryptor_ready_cb.is_null()) { - if (!decryptor_ready_cb_.is_null()) - base::ResetAndReturn(&decryptor_ready_cb_).Run(NULL); - return; - } +media::Decryptor* ProxyDecryptor::GetDecryptor() { + return media_keys_ ? media_keys_->GetDecryptor() : NULL; +} - // Normal decryptor request. - DCHECK(decryptor_ready_cb_.is_null()); - if (media_keys_) { - decryptor_ready_cb.Run(media_keys_->GetDecryptor()); - return; - } - decryptor_ready_cb_ = decryptor_ready_cb; +#if defined(ENABLE_BROWSER_CDMS) +int ProxyDecryptor::GetCdmId() { + return cdm_id_; } +#endif bool ProxyDecryptor::InitializeCDM(const std::string& key_system, - const GURL& frame_url) { + const GURL& security_origin) { DVLOG(1) << "InitializeCDM: key_system = " << key_system; - base::AutoLock auto_lock(lock_); - DCHECK(!media_keys_); - media_keys_ = CreateMediaKeys(key_system, frame_url); + media_keys_ = CreateMediaKeys(key_system, security_origin); if (!media_keys_) return false; - if (!decryptor_ready_cb_.is_null()) - base::ResetAndReturn(&decryptor_ready_cb_).Run(media_keys_->GetDecryptor()); - is_clear_key_ = media::IsClearKey(key_system) || media::IsExternalClearKey(key_system); return true; } -bool ProxyDecryptor::GenerateKeyRequest(const std::string& type, +// 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<size_t>(data_length) > header.size() && + std::equal(data, data + header.size(), header.begin()); +} + +bool ProxyDecryptor::GenerateKeyRequest(const std::string& content_type, const uint8* init_data, int init_data_length) { - // Use a unique reference id for this request. - uint32 session_id = next_session_id_++; - if (!media_keys_->CreateSession( - session_id, type, init_data, init_data_length)) { - media_keys_.reset(); - return false; + DVLOG(1) << "GenerateKeyRequest()"; + const char kPrefixedApiPersistentSessionHeader[] = "PERSISTENT|"; + const char kPrefixedApiLoadSessionHeader[] = "LOAD_SESSION|"; + + bool loadSession = + HasHeader(init_data, init_data_length, kPrefixedApiLoadSessionHeader); + bool persistent = HasHeader( + init_data, init_data_length, kPrefixedApiPersistentSessionHeader); + + scoped_ptr<media::NewSessionCdmPromise> promise( + new media::NewSessionCdmPromise( + base::Bind(&ProxyDecryptor::SetSessionId, + weak_ptr_factory_.GetWeakPtr(), + persistent || loadSession), + base::Bind(&ProxyDecryptor::OnSessionError, + weak_ptr_factory_.GetWeakPtr(), + std::string()))); // No session id until created. + + if (loadSession) { + media_keys_->LoadSession( + std::string(reinterpret_cast<const char*>( + init_data + strlen(kPrefixedApiLoadSessionHeader)), + init_data_length - strlen(kPrefixedApiLoadSessionHeader)), + promise.Pass()); + return true; } + media::MediaKeys::SessionType session_type = + persistent ? media::MediaKeys::PERSISTENT_SESSION + : media::MediaKeys::TEMPORARY_SESSION; + media_keys_->CreateSession( + content_type, init_data, init_data_length, session_type, promise.Pass()); return true; } @@ -133,18 +140,31 @@ void ProxyDecryptor::AddKey(const uint8* key, const std::string& web_session_id) { DVLOG(1) << "AddKey()"; - // WebMediaPlayerImpl ensures GenerateKeyRequest() has been called. - uint32 session_id = LookupSessionId(web_session_id); - if (session_id == kInvalidSessionId) { - // Session hasn't been referenced before, so it is an error. - // Note that the specification says "If sessionId is not null and is - // unrecognized, throw an INVALID_ACCESS_ERR." However, for backwards - // compatibility the error is not thrown, but rather reported as a - // KeyError. - key_error_cb_.Run(std::string(), media::MediaKeys::kUnknownError, 0); - return; + // In the prefixed API, the session parameter provided to addKey() is + // optional, so use the single existing session if it exists. + // TODO(jrummell): remove when the prefixed API is removed. + std::string session_id(web_session_id); + if (session_id.empty()) { + if (active_sessions_.size() == 1) { + base::hash_map<std::string, bool>::iterator it = active_sessions_.begin(); + session_id = it->first; + } else { + OnSessionError(std::string(), + media::MediaKeys::NOT_SUPPORTED_ERROR, + 0, + "SessionId not specified."); + return; + } } + scoped_ptr<media::SimpleCdmPromise> promise( + new media::SimpleCdmPromise(base::Bind(&ProxyDecryptor::OnSessionReady, + weak_ptr_factory_.GetWeakPtr(), + web_session_id), + base::Bind(&ProxyDecryptor::OnSessionError, + weak_ptr_factory_.GetWeakPtr(), + web_session_id))); + // EME WD spec only supports a single array passed to the CDM. For // Clear Key using v0.1b, both arrays are used (|init_data| is key_id). // Since the EME WD spec supports the key as a JSON Web Key, @@ -161,46 +181,41 @@ void ProxyDecryptor::AddKey(const uint8* key, std::string jwk = media::GenerateJWKSet(key, key_length, init_data, init_data_length); DCHECK(!jwk.empty()); - media_keys_->UpdateSession( - session_id, reinterpret_cast<const uint8*>(jwk.data()), jwk.size()); + media_keys_->UpdateSession(session_id, + reinterpret_cast<const uint8*>(jwk.data()), + jwk.size(), + promise.Pass()); return; } - media_keys_->UpdateSession(session_id, key, key_length); + media_keys_->UpdateSession(session_id, key, key_length, promise.Pass()); } -void ProxyDecryptor::CancelKeyRequest(const std::string& session_id) { +void ProxyDecryptor::CancelKeyRequest(const std::string& web_session_id) { DVLOG(1) << "CancelKeyRequest()"; - // WebMediaPlayerImpl ensures GenerateKeyRequest() has been called. - uint32 session_reference_id = LookupSessionId(session_id); - if (session_reference_id == kInvalidSessionId) { - // Session hasn't been created, so it is an error. - key_error_cb_.Run( - std::string(), media::MediaKeys::kUnknownError, 0); - } - else { - media_keys_->ReleaseSession(session_reference_id); - } + scoped_ptr<media::SimpleCdmPromise> promise( + new media::SimpleCdmPromise(base::Bind(&ProxyDecryptor::OnSessionClosed, + weak_ptr_factory_.GetWeakPtr(), + web_session_id), + base::Bind(&ProxyDecryptor::OnSessionError, + weak_ptr_factory_.GetWeakPtr(), + web_session_id))); + media_keys_->ReleaseSession(web_session_id, promise.Pass()); } scoped_ptr<media::MediaKeys> ProxyDecryptor::CreateMediaKeys( const std::string& key_system, - const GURL& frame_url) { + const GURL& security_origin) { return ContentDecryptionModuleFactory::Create( key_system, + security_origin, #if defined(ENABLE_PEPPER_CDMS) - web_media_player_client_, - web_frame_, - base::Bind(&ProxyDecryptor::DestroyHelperPlugin, - weak_ptr_factory_.GetWeakPtr()), -#elif defined(OS_ANDROID) + create_pepper_cdm_cb_, +#elif defined(ENABLE_BROWSER_CDMS) manager_, - media_keys_id_, - frame_url, + &cdm_id_, #endif // defined(ENABLE_PEPPER_CDMS) - base::Bind(&ProxyDecryptor::OnSessionCreated, - weak_ptr_factory_.GetWeakPtr()), base::Bind(&ProxyDecryptor::OnSessionMessage, weak_ptr_factory_.GetWeakPtr()), base::Bind(&ProxyDecryptor::OnSessionReady, @@ -211,60 +226,69 @@ scoped_ptr<media::MediaKeys> ProxyDecryptor::CreateMediaKeys( weak_ptr_factory_.GetWeakPtr())); } -void ProxyDecryptor::OnSessionCreated(uint32 session_id, - const std::string& web_session_id) { - // Due to heartbeat messages, OnSessionCreated() can get called multiple - // times. - SessionIdMap::iterator it = sessions_.find(session_id); - DCHECK(it == sessions_.end() || it->second == web_session_id); - if (it == sessions_.end()) - sessions_[session_id] = web_session_id; -} - -void ProxyDecryptor::OnSessionMessage(uint32 session_id, +void ProxyDecryptor::OnSessionMessage(const std::string& web_session_id, const std::vector<uint8>& message, - const std::string& destination_url) { + const GURL& destination_url) { // Assumes that OnSessionCreated() has been called before this. - key_message_cb_.Run(LookupWebSessionId(session_id), message, destination_url); + key_message_cb_.Run(web_session_id, message, destination_url); } -void ProxyDecryptor::OnSessionReady(uint32 session_id) { - // Assumes that OnSessionCreated() has been called before this. - key_added_cb_.Run(LookupWebSessionId(session_id)); +void ProxyDecryptor::OnSessionReady(const std::string& web_session_id) { + key_added_cb_.Run(web_session_id); } -void ProxyDecryptor::OnSessionClosed(uint32 session_id) { - // No closed event in EME v0.1b. -} +void ProxyDecryptor::OnSessionClosed(const std::string& web_session_id) { + base::hash_map<std::string, bool>::iterator it = + active_sessions_.find(web_session_id); + + // Latest EME spec separates closing a session ("allows an application to + // indicate that it no longer needs the session") and actually closing the + // session (done by the CDM at any point "such as in response to a close() + // call, when the session is no longer needed, or when system resources are + // lost.") Thus the CDM may cause 2 close() events -- one to resolve the + // close() promise, and a second to actually close the session. Prefixed EME + // only expects 1 close event, so drop the second (and subsequent) events. + // However, this means we can't tell if the CDM is generating spurious close() + // events. + if (it == active_sessions_.end()) + return; -void ProxyDecryptor::OnSessionError(uint32 session_id, - media::MediaKeys::KeyError error_code, - int system_code) { - // Assumes that OnSessionCreated() has been called before this. - key_error_cb_.Run(LookupWebSessionId(session_id), error_code, system_code); + if (it->second) { + OnSessionError(web_session_id, + media::MediaKeys::NOT_SUPPORTED_ERROR, + kSessionClosedSystemCode, + "Do not close persistent sessions."); + } + active_sessions_.erase(it); } -uint32 ProxyDecryptor::LookupSessionId(const std::string& session_id) { - for (SessionIdMap::iterator it = sessions_.begin(); - it != sessions_.end(); - ++it) { - if (it->second == session_id) - return it->first; +void ProxyDecryptor::OnSessionError(const std::string& web_session_id, + media::MediaKeys::Exception exception_code, + uint32 system_code, + const std::string& error_message) { + // Convert |error_name| back to MediaKeys::KeyError if possible. Prefixed + // EME has different error message, so all the specific error events will + // get lost. + media::MediaKeys::KeyError error_code; + switch (exception_code) { + case media::MediaKeys::CLIENT_ERROR: + error_code = media::MediaKeys::kClientError; + break; + case media::MediaKeys::OUTPUT_ERROR: + error_code = media::MediaKeys::kOutputError; + break; + default: + // This will include all other CDM4 errors and any error generated + // by CDM5 or later. + error_code = media::MediaKeys::kUnknownError; + break; } - - // If |session_id| is null, then use the single reference id. - if (session_id.empty() && sessions_.size() == 1) - return sessions_.begin()->first; - - return kInvalidSessionId; + key_error_cb_.Run(web_session_id, error_code, system_code); } -const std::string& ProxyDecryptor::LookupWebSessionId(uint32 session_id) { - DCHECK_NE(session_id, kInvalidSessionId); - - // Session may not exist if error happens during GenerateKeyRequest(). - SessionIdMap::iterator it = sessions_.find(session_id); - return (it != sessions_.end()) ? it->second : base::EmptyString(); +void ProxyDecryptor::SetSessionId(bool persistent, + const std::string& web_session_id) { + active_sessions_.insert(std::make_pair(web_session_id, persistent)); } } // namespace content |