summaryrefslogtreecommitdiffstats
path: root/chromium/content/renderer/media/crypto/proxy_decryptor.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/content/renderer/media/crypto/proxy_decryptor.cc')
-rw-r--r--chromium/content/renderer/media/crypto/proxy_decryptor.cc310
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