Index: media/cdm/ppapi/external_clear_key/clear_key_cdm.cc |
diff --git a/media/cdm/ppapi/external_clear_key/clear_key_cdm.cc b/media/cdm/ppapi/external_clear_key/clear_key_cdm.cc |
index e98db7c824176aec0173cbbaca2497933cad167f..6e1496da5bdddf8fb11d19738a42dd196d16ddde 100644 |
--- a/media/cdm/ppapi/external_clear_key/clear_key_cdm.cc |
+++ b/media/cdm/ppapi/external_clear_key/clear_key_cdm.cc |
@@ -14,6 +14,7 @@ |
#include "base/debug/trace_event.h" |
#include "base/logging.h" |
#include "base/time/time.h" |
+#include "media/base/cdm_promise.h" |
#include "media/base/decoder_buffer.h" |
#include "media/base/decrypt_config.h" |
#include "media/cdm/json_web_key.h" |
@@ -41,6 +42,46 @@ MSVC_PUSH_DISABLE_WARNING(4244); |
MSVC_POP_WARNING(); |
} // extern "C" |
+#define COMPILE_ASSERT_MATCHING_ENUM(cdm_name, media_name) \ |
+ COMPILE_ASSERT(static_cast<int>(cdm::cdm_name) == \ |
+ static_cast<int>(media::MediaKeys::media_name), \ |
+ mismatching_enums) |
+// Validate cdm::SessionType and MediaKeys::SessionType |
+COMPILE_ASSERT_MATCHING_ENUM(kTemporary, SESSION_TYPE_TEMPORARY); |
+COMPILE_ASSERT_MATCHING_ENUM(kPersistent, SESSION_TYPE_PERSISTENT); |
+ |
+// Validate cdm::MediaKeyException and MediaKeys::MediaKeysException |
+COMPILE_ASSERT_MATCHING_ENUM( |
+ kNoModificationAllowedError, |
+ MEDIA_KEYS_EXCEPTION_NO_MODIFICATION_ALLOWED_ERROR); |
+COMPILE_ASSERT_MATCHING_ENUM(kNotFoundError, |
+ MEDIA_KEYS_EXCEPTION_NOT_FOUND_ERROR); |
+COMPILE_ASSERT_MATCHING_ENUM(kNotSupportedError, |
+ MEDIA_KEYS_EXCEPTION_NOT_SUPPORTED_ERROR); |
+COMPILE_ASSERT_MATCHING_ENUM(kInvalidStateError, |
+ MEDIA_KEYS_EXCEPTION_INVALID_STATE_ERROR); |
+COMPILE_ASSERT_MATCHING_ENUM(kSyntaxError, MEDIA_KEYS_EXCEPTION_SYNTAX_ERROR); |
+COMPILE_ASSERT_MATCHING_ENUM(kInvalidModificationError, |
+ MEDIA_KEYS_EXCEPTION_INVALID_MODIFICATION_ERROR); |
+COMPILE_ASSERT_MATCHING_ENUM(kInvalidAccessError, |
+ MEDIA_KEYS_EXCEPTION_INVALID_ACCESS_ERROR); |
+COMPILE_ASSERT_MATCHING_ENUM(kSecurityError, |
+ MEDIA_KEYS_EXCEPTION_SECURITY_ERROR); |
+COMPILE_ASSERT_MATCHING_ENUM(kAbortError, MEDIA_KEYS_EXCEPTION_ABORT_ERROR); |
+COMPILE_ASSERT_MATCHING_ENUM(kQuotaExceededError, |
+ MEDIA_KEYS_EXCEPTION_QUOTA_EXCEEDED_ERROR); |
+COMPILE_ASSERT_MATCHING_ENUM(kTimeoutError, MEDIA_KEYS_EXCEPTION_TIMEOUT_ERROR); |
+COMPILE_ASSERT_MATCHING_ENUM(kNotReadableError, |
+ MEDIA_KEYS_EXCEPTION_NOT_READABLE_ERROR); |
+COMPILE_ASSERT_MATCHING_ENUM(kDataError, MEDIA_KEYS_EXCEPTION_DATA_ERROR); |
+COMPILE_ASSERT_MATCHING_ENUM(kOperationError, |
+ MEDIA_KEYS_EXCEPTION_OPERATION_ERROR); |
+COMPILE_ASSERT_MATCHING_ENUM(kVersionError, MEDIA_KEYS_EXCEPTION_VERSION_ERROR); |
+COMPILE_ASSERT_MATCHING_ENUM(kUnknownError, MEDIA_KEYS_EXCEPTION_UNKNOWN_ERROR); |
+COMPILE_ASSERT_MATCHING_ENUM(kClientError, MEDIA_KEYS_EXCEPTION_CLIENT_ERROR); |
+COMPILE_ASSERT_MATCHING_ENUM(kOutputError, MEDIA_KEYS_EXCEPTION_OUTPUT_ERROR); |
+#undef COMPILE_ASSERT_MATCHING_ENUM |
+ |
// TODO(tomfinegan): When COMPONENT_BUILD is not defined an AtExitManager must |
// exist before the call to InitializeFFmpegLibraries(). This should no longer |
// be required after http://crbug.com/91970 because we'll be able to get rid of |
@@ -187,15 +228,9 @@ namespace media { |
ClearKeyCdm::ClearKeyCdm(ClearKeyCdmHost* host, const std::string& key_system) |
: decryptor_( |
- base::Bind(&ClearKeyCdm::OnSessionCreated, base::Unretained(this)), |
- base::Bind(&ClearKeyCdm::OnSessionMessage, base::Unretained(this)), |
- base::Bind(&ClearKeyCdm::OnSessionReady, base::Unretained(this)), |
- base::Bind(&ClearKeyCdm::OnSessionClosed, base::Unretained(this)), |
- base::Bind(&ClearKeyCdm::OnSessionError, base::Unretained(this))), |
+ base::Bind(&ClearKeyCdm::OnSessionMessage, base::Unretained(this))), |
host_(host), |
key_system_(key_system), |
- last_session_id_(MediaKeys::kInvalidSessionId), |
- session_id_for_emulated_loadsession_(MediaKeys::kInvalidSessionId), |
timer_delay_ms_(kInitialTimerDelayMs), |
heartbeat_timer_set_(false) { |
#if defined(CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER) |
@@ -209,17 +244,27 @@ ClearKeyCdm::ClearKeyCdm(ClearKeyCdmHost* host, const std::string& key_system) |
ClearKeyCdm::~ClearKeyCdm() {} |
-void ClearKeyCdm::CreateSession(uint32 session_id, |
- const char* type, |
- uint32 type_size, |
+void ClearKeyCdm::CreateSession(uint32 promise_id, |
+ const char* init_data_type, |
+ uint32 init_data_type_size, |
const uint8* init_data, |
- uint32 init_data_size) { |
+ uint32 init_data_size, |
+ cdm::SessionType session_type) { |
DVLOG(1) << __FUNCTION__; |
- decryptor_.CreateSession( |
- session_id, std::string(type, type_size), init_data, init_data_size); |
- // Save the latest session ID for heartbeat and file IO test messages. |
- last_session_id_ = session_id; |
+ scoped_ptr<media::CdmNewSessionPromise> promise( |
+ new media::CdmNewSessionPromise( |
+ base::Bind(&ClearKeyCdm::OnSessionCreated, |
+ base::Unretained(this), |
+ promise_id), |
+ base::Bind(&ClearKeyCdm::OnSessionCreateFailed, |
+ base::Unretained(this), |
+ promise_id))); |
+ decryptor_.CreateSession(std::string(init_data_type, init_data_type_size), |
+ init_data, |
+ init_data_size, |
+ static_cast<MediaKeys::SessionType>(session_type), |
+ promise.Pass()); |
if (key_system_ == kExternalClearKeyFileIOTestKeySystem) |
StartFileIOTest(); |
@@ -228,28 +273,53 @@ void ClearKeyCdm::CreateSession(uint32 session_id, |
// Loads a emulated stored session. Currently only |kLoadableWebSessionId| |
// (containing a |kLoadableSessionKey| for |kLoadableSessionKeyId|) is |
// supported. |
-void ClearKeyCdm::LoadSession(uint32_t session_id, |
+void ClearKeyCdm::LoadSession(uint32 promise_id, |
const char* web_session_id, |
uint32_t web_session_id_length) { |
DVLOG(1) << __FUNCTION__; |
if (std::string(kLoadableWebSessionId) != |
std::string(web_session_id, web_session_id_length)) { |
- // TODO(xhwang): Report "NotFoundError" when we support DOMError style. |
- OnSessionError(session_id, MediaKeys::kUnknownError, 0); |
+ std::string message("Incorrect session id specified for LoadSession()."); |
+ host_->OnRejectPromise( |
+ promise_id, cdm::kNotFoundError, 0, message.data(), message.length()); |
return; |
} |
- session_id_for_emulated_loadsession_ = session_id; |
- |
- decryptor_.CreateSession(session_id, kLoadableSessionContentType, NULL, 0); |
-} |
- |
-void ClearKeyCdm::UpdateSession(uint32 session_id, |
+ scoped_ptr<media::CdmNewSessionPromise> promise( |
+ new media::CdmNewSessionPromise( |
+ base::Bind(&ClearKeyCdm::OnSessionLoaded, |
+ base::Unretained(this), |
+ promise_id), |
+ base::Bind(&ClearKeyCdm::OnSessionLoadFailed, |
+ base::Unretained(this), |
+ promise_id))); |
+ decryptor_.CreateSession(std::string(kLoadableSessionContentType), |
+ NULL, |
+ 0, |
+ MediaKeys::SESSION_TYPE_TEMPORARY, |
+ promise.Pass()); |
+} |
+ |
+void ClearKeyCdm::UpdateSession(uint32 promise_id, |
+ const char* web_session_id, |
+ uint32_t web_session_id_size, |
const uint8* response, |
uint32 response_size) { |
DVLOG(1) << __FUNCTION__; |
- decryptor_.UpdateSession(session_id, response, response_size); |
+ std::string web_session_str(web_session_id, web_session_id_size); |
+ |
+ scoped_ptr<media::CdmChangeSessionPromise> promise( |
+ new media::CdmChangeSessionPromise( |
+ base::Bind(&ClearKeyCdm::OnSessionUpdated, |
+ base::Unretained(this), |
+ promise_id, |
+ web_session_str), |
+ base::Bind(&ClearKeyCdm::OnSessionUpdateFailed, |
+ base::Unretained(this), |
+ promise_id))); |
+ decryptor_.UpdateSession( |
+ web_session_str, response, response_size, promise.Pass()); |
if (!heartbeat_timer_set_) { |
ScheduleNextHeartBeat(); |
@@ -257,9 +327,29 @@ void ClearKeyCdm::UpdateSession(uint32 session_id, |
} |
} |
-void ClearKeyCdm::ReleaseSession(uint32 session_id) { |
+void ClearKeyCdm::ReleaseSession(uint32 promise_id, |
+ const char* web_session_id, |
+ uint32_t web_session_id_size) { |
DVLOG(1) << __FUNCTION__; |
- decryptor_.ReleaseSession(session_id); |
+ std::string web_session_str(web_session_id, web_session_id_size); |
+ |
+ scoped_ptr<media::CdmChangeSessionPromise> promise( |
+ new media::CdmChangeSessionPromise( |
+ base::Bind(&ClearKeyCdm::OnSessionReleased, |
+ base::Unretained(this), |
+ promise_id, |
+ web_session_str), |
+ base::Bind(&ClearKeyCdm::OnSessionReleaseFailed, |
+ base::Unretained(this), |
+ promise_id))); |
+ decryptor_.ReleaseSession(web_session_str, promise.Pass()); |
+} |
+ |
+void ClearKeyCdm::SetServerCertificate(uint32 promise_id, |
+ const uint8_t* server_certificate_data, |
+ uint32_t server_certificate_data_size) { |
+ // ClearKey doesn't use a server certificate. |
+ host_->OnResolvePromise(promise_id); |
} |
void ClearKeyCdm::TimerExpired(void* context) { |
@@ -281,9 +371,10 @@ void ClearKeyCdm::TimerExpired(void* context) { |
// There is no service at this URL, so applications should ignore it. |
const char url[] = "http://test.externalclearkey.chromium.org"; |
- host_->OnSessionMessage(last_session_id_, |
+ host_->OnSessionMessage(last_session_id_.data(), |
+ last_session_id_.length(), |
heartbeat_message.data(), |
- heartbeat_message.size(), |
+ heartbeat_message.length(), |
url, |
arraysize(url) - 1); |
@@ -476,7 +567,7 @@ void ClearKeyCdm::ScheduleNextHeartBeat() { |
// Prepare the next heartbeat message and set timer. |
std::ostringstream msg_stream; |
msg_stream << kHeartBeatHeader << " from ClearKey CDM set at time " |
- << host_->GetCurrentWallTimeInSeconds() << "."; |
+ << host_->GetCurrentTime() << "."; |
next_heartbeat_message_ = msg_stream.str(); |
host_->SetTimer(timer_delay_ms_, &next_heartbeat_message_[0]); |
@@ -535,66 +626,135 @@ void ClearKeyCdm::LoadLoadableSession() { |
sizeof(kLoadableSessionKeyId) - 1); |
// TODO(xhwang): This triggers OnSessionUpdated(). For prefixed EME support, |
// this is okay. Check WD EME support. |
+ scoped_ptr<media::CdmChangeSessionPromise> promise( |
+ new media::CdmChangeSessionPromise( |
+ base::Bind(&ClearKeyCdm::OnSessionUpdated, |
+ base::Unretained(this), |
+ promise_id_for_emulated_loadsession_, |
+ session_id_for_emulated_loadsession_), |
+ base::Bind(&ClearKeyCdm::OnSessionUpdateFailed, |
+ base::Unretained(this), |
+ promise_id_for_emulated_loadsession_))); |
decryptor_.UpdateSession(session_id_for_emulated_loadsession_, |
reinterpret_cast<const uint8*>(jwk_set.data()), |
- jwk_set.size()); |
-} |
- |
-void ClearKeyCdm::OnSessionCreated(uint32 session_id, |
- const std::string& web_session_id) { |
- std::string new_web_session_id = web_session_id; |
- |
- if (session_id == session_id_for_emulated_loadsession_) { |
- // Delay LoadLoadableSession() to test the case where Decrypt*() calls are |
- // made before the session is fully loaded. |
- const int64 kDelayToLoadSessionMs = 500; |
- // Use the address of |session_id_for_emulated_loadsession_| as the timer |
- // context so that we can call LoadLoadableSession() when the timer expires. |
- host_->SetTimer(kDelayToLoadSessionMs, |
- &session_id_for_emulated_loadsession_); |
- // Defer OnSessionCreated() until the session is loaded. |
- return; |
- } |
- |
- host_->OnSessionCreated( |
- session_id, web_session_id.data(), web_session_id.size()); |
+ jwk_set.size(), |
+ promise.Pass()); |
} |
-void ClearKeyCdm::OnSessionMessage(uint32 session_id, |
+void ClearKeyCdm::OnSessionMessage(const std::string& web_session_id, |
const std::vector<uint8>& message, |
const std::string& destination_url) { |
DVLOG(1) << "OnSessionMessage: " << message.size(); |
// Ignore the message when we are waiting to update the loadable session. |
- if (session_id == session_id_for_emulated_loadsession_) |
+ if (web_session_id == session_id_for_emulated_loadsession_) |
return; |
- host_->OnSessionMessage(session_id, |
+ // OnSessionMessage() only called during CreateSession(), so no promise |
+ // involved (OnSessionCreated() called to resolve the CreateSession() |
+ // promise). |
+ host_->OnSessionMessage(web_session_id.data(), |
+ web_session_id.length(), |
reinterpret_cast<const char*>(message.data()), |
message.size(), |
destination_url.data(), |
- destination_url.size()); |
+ destination_url.length()); |
+} |
+ |
+void ClearKeyCdm::OnSessionCreated(uint32 promise_id, |
+ const std::string& web_session_id) { |
+ // Save the latest session ID for heartbeat and file IO test messages. |
+ last_session_id_ = web_session_id; |
+ |
+ host_->OnResolveNewSessionPromise( |
+ promise_id, web_session_id.data(), web_session_id.length()); |
+} |
+ |
+void ClearKeyCdm::OnSessionCreateFailed( |
+ uint32 promise_id, |
+ MediaKeys::MediaKeysException exception_code, |
+ uint32 system_code, |
+ const std::string& error_message) { |
+ RejectPromise(promise_id, exception_code, system_code, error_message); |
+} |
+ |
+void ClearKeyCdm::OnSessionLoaded(uint32 promise_id, |
+ const std::string& web_session_id) { |
+ // Save the latest session ID for heartbeat and file IO test messages. |
+ last_session_id_ = web_session_id; |
+ |
+ // |decryptor_| created some session as |web_session_id|, but going forward |
+ // we need to map that to |kLoadableWebSessionId|, as that is what callers |
+ // expect. |
+ session_id_for_emulated_loadsession_ = web_session_id; |
+ |
+ // Delay LoadLoadableSession() to test the case where Decrypt*() calls are |
+ // made before the session is fully loaded. |
+ const int64 kDelayToLoadSessionMs = 500; |
+ |
+ // Defer resolving the promise until the session is loaded. |
+ promise_id_for_emulated_loadsession_ = promise_id; |
+ |
+ // Use the address of |session_id_for_emulated_loadsession_| as the timer |
+ // context so that we can call LoadLoadableSession() when the timer expires. |
+ host_->SetTimer(kDelayToLoadSessionMs, &session_id_for_emulated_loadsession_); |
+} |
+ |
+void ClearKeyCdm::OnSessionLoadFailed( |
+ uint32 promise_id, |
+ MediaKeys::MediaKeysException exception_code, |
+ uint32 system_code, |
+ const std::string& error_message) { |
+ RejectPromise(promise_id, exception_code, system_code, error_message); |
} |
-void ClearKeyCdm::OnSessionReady(uint32 session_id) { |
- if (session_id == session_id_for_emulated_loadsession_) { |
- session_id_for_emulated_loadsession_ = MediaKeys::kInvalidSessionId; |
- host_->OnSessionCreated( |
- session_id, kLoadableWebSessionId, strlen(kLoadableWebSessionId)); |
+void ClearKeyCdm::OnSessionUpdated(uint32 promise_id, |
+ const std::string& web_session_id) { |
+ // OnSessionReady() only called as success for UpdateSession(). However, |
+ // UpdateSession() also called to finish loading sessions, so handle |
+ // appropriately. |
+ if (web_session_id == session_id_for_emulated_loadsession_) { |
+ session_id_for_emulated_loadsession_ = std::string(); |
+ // |promise_id| is the LoadSession() promise, so resolve appropriately. |
+ host_->OnResolveNewSessionPromise( |
+ promise_id, kLoadableWebSessionId, strlen(kLoadableWebSessionId)); |
+ host_->OnSessionReady(kLoadableWebSessionId, strlen(kLoadableWebSessionId)); |
+ return; |
} |
- host_->OnSessionReady(session_id); |
+ host_->OnResolvePromise(promise_id); |
+} |
+ |
+void ClearKeyCdm::OnSessionUpdateFailed( |
+ uint32 promise_id, |
+ MediaKeys::MediaKeysException exception_code, |
+ uint32 system_code, |
+ const std::string& error_message) { |
+ RejectPromise(promise_id, exception_code, system_code, error_message); |
+} |
+ |
+void ClearKeyCdm::OnSessionReleased(uint32 promise_id, |
+ const std::string& web_session_id) { |
+ host_->OnResolvePromise(promise_id); |
} |
-void ClearKeyCdm::OnSessionClosed(uint32 session_id) { |
- host_->OnSessionClosed(session_id); |
+void ClearKeyCdm::OnSessionReleaseFailed( |
+ uint32 promise_id, |
+ MediaKeys::MediaKeysException exception_code, |
+ uint32 system_code, |
+ const std::string& error_message) { |
+ RejectPromise(promise_id, exception_code, system_code, error_message); |
} |
-void ClearKeyCdm::OnSessionError(uint32 session_id, |
- media::MediaKeys::KeyError error_code, |
- uint32 system_code) { |
- host_->OnSessionError( |
- session_id, static_cast<cdm::MediaKeyError>(error_code), system_code); |
+void ClearKeyCdm::RejectPromise(uint32 promise_id, |
+ MediaKeys::MediaKeysException exception_code, |
+ uint32 system_code, |
+ const std::string& error_message) { |
+ host_->OnRejectPromise(promise_id, |
+ static_cast<cdm::Error>(exception_code), |
+ system_code, |
+ error_message.data(), |
+ error_message.length()); |
} |
#if defined(CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER) |
@@ -666,8 +826,12 @@ void ClearKeyCdm::StartFileIOTest() { |
void ClearKeyCdm::OnFileIOTestComplete(bool success) { |
DVLOG(1) << __FUNCTION__ << ": " << success; |
std::string message = GetFileIOTestResultMessage(success); |
- host_->OnSessionMessage( |
- last_session_id_, message.data(), message.size(), NULL, 0); |
+ host_->OnSessionMessage(last_session_id_.data(), |
+ last_session_id_.length(), |
+ message.data(), |
+ message.length(), |
+ NULL, |
+ 0); |
file_io_test_runner_.reset(); |
} |