Chromium Code Reviews| Index: media/base/android/media_drm_bridge.cc |
| diff --git a/media/base/android/media_drm_bridge.cc b/media/base/android/media_drm_bridge.cc |
| index 91c08d2033fdb32e6d2a78a90a8edaacfedab030..2e2b50266d4740466e554fbe83d1fd376f4c3e8a 100644 |
| --- a/media/base/android/media_drm_bridge.cc |
| +++ b/media/base/android/media_drm_bridge.cc |
| @@ -19,6 +19,7 @@ |
| #include "base/sys_byteorder.h" |
| #include "base/sys_info.h" |
| #include "jni/MediaDrmBridge_jni.h" |
| +#include "media/base/cdm_key_information.h" |
| #include "widevine_cdm_version.h" // In SHARED_INTERMEDIATE_DIR. |
| @@ -68,6 +69,11 @@ const uint8 kWidevineUuid[16] = { |
| 0xED, 0xEF, 0x8B, 0xA9, 0x79, 0xD6, 0x4A, 0xCE, |
| 0xA3, 0xC8, 0x27, 0xDC, 0xD5, 0x1D, 0x21, 0xED }; |
| +// DrmBridge supports session expiration event but doesn't provide detailed |
| +// status for each key ID, which is required by the EME spec. Use a dummy key ID |
| +// here to report session expiration info. |
| +const char kDummyKeyId[] = "Dummy Key Id"; |
|
dcheng
2015/01/17 00:08:16
These constants should probably all be in an unnam
xhwang
2015/01/17 01:20:40
consts are by default static, which is equivalent
|
| + |
| typedef std::vector<uint8> UUID; |
| class KeySystemUuidManager { |
| @@ -253,6 +259,13 @@ static bool IsKeySystemSupportedWithTypeImpl( |
| env, j_scheme_uuid.obj(), j_container_mime_type.obj()); |
| } |
| +// Returns string session ID from jbyteArray (byte[] in Java). |
| +static std::string GetSessionId(JNIEnv* env, jbyteArray j_session_id) { |
| + std::vector<uint8> session_id_vector; |
| + JavaByteArrayToByteVector(env, j_session_id, &session_id_vector); |
| + return std::string(session_id_vector.begin(), session_id_vector.end()); |
| +} |
| + |
| // static |
| bool MediaDrmBridge::IsAvailable() { |
| if (base::android::BuildInfo::GetInstance()->sdk_int() < 19) |
| @@ -283,7 +296,7 @@ bool MediaDrmBridge::IsSecurityLevelSupported(const std::string& key_system, |
| return false; |
| scoped_ptr<MediaDrmBridge> media_drm_bridge = |
| - MediaDrmBridge::CreateSessionless(key_system); |
| + MediaDrmBridge::CreateWithoutSessionSupport(key_system); |
| if (!media_drm_bridge) |
| return false; |
| @@ -322,18 +335,17 @@ bool MediaDrmBridge::RegisterMediaDrmBridge(JNIEnv* env) { |
| return RegisterNativesImpl(env); |
| } |
| -MediaDrmBridge::MediaDrmBridge(const std::vector<uint8>& scheme_uuid, |
| - const SessionCreatedCB& session_created_cb, |
| - const SessionMessageCB& session_message_cb, |
| - const SessionReadyCB& session_ready_cb, |
| - const SessionClosedCB& session_closed_cb, |
| - const SessionErrorCB& session_error_cb) |
| +MediaDrmBridge::MediaDrmBridge( |
| + const std::vector<uint8>& scheme_uuid, |
| + const SessionMessageCB& session_message_cb, |
| + const SessionClosedCB& session_closed_cb, |
| + const SessionErrorCB& session_error_cb, |
| + const SessionKeysChangeCB& session_keys_change_cb) |
| : scheme_uuid_(scheme_uuid), |
| - session_created_cb_(session_created_cb), |
| session_message_cb_(session_message_cb), |
| - session_ready_cb_(session_ready_cb), |
| session_closed_cb_(session_closed_cb), |
| - session_error_cb_(session_error_cb) { |
| + session_error_cb_(session_error_cb), |
| + session_keys_change_cb_(session_keys_change_cb) { |
| JNIEnv* env = AttachCurrentThread(); |
| CHECK(env); |
| @@ -351,13 +363,14 @@ MediaDrmBridge::~MediaDrmBridge() { |
| } |
| // static |
| +// TODO(xhwang): Enable SessionExpirationUpdateCB when it is supported. |
| scoped_ptr<MediaDrmBridge> MediaDrmBridge::Create( |
| const std::string& key_system, |
| - const SessionCreatedCB& session_created_cb, |
| const SessionMessageCB& session_message_cb, |
| - const SessionReadyCB& session_ready_cb, |
| const SessionClosedCB& session_closed_cb, |
| - const SessionErrorCB& session_error_cb) { |
| + const SessionErrorCB& session_error_cb, |
| + const SessionKeysChangeCB& session_keys_change_cb, |
| + const SessionExpirationUpdateCB& /* session_expiration_update_cb */) { |
| scoped_ptr<MediaDrmBridge> media_drm_bridge; |
| if (!IsAvailable()) |
| return media_drm_bridge.Pass(); |
| @@ -366,12 +379,9 @@ scoped_ptr<MediaDrmBridge> MediaDrmBridge::Create( |
| if (scheme_uuid.empty()) |
| return media_drm_bridge.Pass(); |
| - media_drm_bridge.reset(new MediaDrmBridge(scheme_uuid, |
| - session_created_cb, |
| - session_message_cb, |
| - session_ready_cb, |
| - session_closed_cb, |
| - session_error_cb)); |
| + media_drm_bridge.reset(new MediaDrmBridge(scheme_uuid, session_message_cb, |
| + session_closed_cb, session_error_cb, |
| + session_keys_change_cb)); |
| if (media_drm_bridge->j_media_drm_.is_null()) |
| media_drm_bridge.reset(); |
| @@ -380,14 +390,11 @@ scoped_ptr<MediaDrmBridge> MediaDrmBridge::Create( |
| } |
| // static |
| -scoped_ptr<MediaDrmBridge> MediaDrmBridge::CreateSessionless( |
| +scoped_ptr<MediaDrmBridge> MediaDrmBridge::CreateWithoutSessionSupport( |
| const std::string& key_system) { |
| - return MediaDrmBridge::Create(key_system, |
| - SessionCreatedCB(), |
| - SessionMessageCB(), |
| - SessionReadyCB(), |
| - SessionClosedCB(), |
| - SessionErrorCB()); |
| + return MediaDrmBridge::Create( |
| + key_system, SessionMessageCB(), SessionClosedCB(), SessionErrorCB(), |
| + SessionKeysChangeCB(), SessionExpirationUpdateCB()); |
| } |
| bool MediaDrmBridge::SetSecurityLevel(SecurityLevel security_level) { |
| @@ -403,27 +410,42 @@ bool MediaDrmBridge::SetSecurityLevel(SecurityLevel security_level) { |
| env, j_media_drm_.obj(), j_security_level.obj()); |
| } |
| -bool MediaDrmBridge::CreateSession(uint32 session_id, |
| - const std::string& content_type, |
| - const uint8* init_data, |
| - int init_data_length) { |
| +void MediaDrmBridge::SetServerCertificate( |
| + const uint8* certificate_data, |
| + int certificate_data_length, |
| + scoped_ptr<media::SimpleCdmPromise> promise) { |
| + promise->reject(NOT_SUPPORTED_ERROR, 0, |
| + "SetServerCertificate() is not supported."); |
| +} |
| + |
| +void MediaDrmBridge::CreateSessionAndGenerateRequest( |
| + SessionType session_type, |
| + const std::string& init_data_type, |
| + const uint8* init_data, |
| + int init_data_length, |
| + scoped_ptr<media::NewSessionCdmPromise> promise) { |
| DVLOG(1) << __FUNCTION__; |
| - DCHECK(!session_created_cb_.is_null()) |
| - << "CreateSession called on a sessionless MediaDrmBridge object."; |
| + if (session_type != media::MediaKeys::TEMPORARY_SESSION) { |
| + promise->reject(NOT_SUPPORTED_ERROR, 0, |
| + "Only the temporary session type is supported."); |
| + return; |
| + } |
| JNIEnv* env = AttachCurrentThread(); |
| ScopedJavaLocalRef<jbyteArray> j_init_data; |
| // Caller should always use "video/*" content types. |
| - DCHECK_EQ(0u, content_type.find("video/")); |
| + DCHECK_EQ(0u, init_data_type.find("video/")); |
| // Widevine MediaDrm plugin only accepts the "data" part of the PSSH box as |
| // the init data when using MP4 container. |
| if (std::equal(scheme_uuid_.begin(), scheme_uuid_.end(), kWidevineUuid) && |
| - content_type == "video/mp4") { |
| + init_data_type == "video/mp4") { |
| std::vector<uint8> pssh_data; |
| - if (!GetPsshData(init_data, init_data_length, scheme_uuid_, &pssh_data)) |
| - return false; |
| + if (!GetPsshData(init_data, init_data_length, scheme_uuid_, &pssh_data)) { |
| + promise->reject(INVALID_ACCESS_ERROR, 0, "Invalid PSSH data."); |
| + return; |
| + } |
| j_init_data = |
| base::android::ToJavaByteArray(env, &pssh_data[0], pssh_data.size()); |
| } else { |
| @@ -432,41 +454,58 @@ bool MediaDrmBridge::CreateSession(uint32 session_id, |
| } |
| ScopedJavaLocalRef<jstring> j_mime = |
| - ConvertUTF8ToJavaString(env, content_type); |
| - Java_MediaDrmBridge_createSession( |
| - env, j_media_drm_.obj(), session_id, j_init_data.obj(), j_mime.obj()); |
| - return true; |
| + ConvertUTF8ToJavaString(env, init_data_type); |
| + uint32_t promise_id = cdm_promise_adapter_.SavePromise(promise.Pass()); |
| + Java_MediaDrmBridge_createSession(env, j_media_drm_.obj(), j_init_data.obj(), |
| + j_mime.obj(), promise_id); |
| } |
| -void MediaDrmBridge::LoadSession(uint32 session_id, |
| - const std::string& web_session_id) { |
| - // MediaDrmBridge doesn't support loading sessions. |
| - NOTREACHED(); |
| +void MediaDrmBridge::LoadSession( |
| + SessionType session_type, |
| + const std::string& session_id, |
| + scoped_ptr<media::NewSessionCdmPromise> promise) { |
| + promise->reject(NOT_SUPPORTED_ERROR, 0, "LoadSession() is not supported."); |
| } |
| -void MediaDrmBridge::UpdateSession(uint32 session_id, |
| - const uint8* response, |
| - int response_length) { |
| +void MediaDrmBridge::UpdateSession( |
| + const std::string& session_id, |
| + const uint8* response, |
| + int response_length, |
| + scoped_ptr<media::SimpleCdmPromise> promise) { |
| DVLOG(1) << __FUNCTION__; |
| - DCHECK(!session_ready_cb_.is_null()) |
| - << __FUNCTION__ << " called on a sessionless MediaDrmBridge object."; |
| - |
| JNIEnv* env = AttachCurrentThread(); |
| ScopedJavaLocalRef<jbyteArray> j_response = |
| base::android::ToJavaByteArray(env, response, response_length); |
| - Java_MediaDrmBridge_updateSession( |
| - env, j_media_drm_.obj(), session_id, j_response.obj()); |
| + ScopedJavaLocalRef<jbyteArray> j_session_id = base::android::ToJavaByteArray( |
| + env, reinterpret_cast<const uint8_t*>(session_id.data()), |
| + session_id.size()); |
| + uint32_t promise_id = cdm_promise_adapter_.SavePromise(promise.Pass()); |
| + Java_MediaDrmBridge_updateSession(env, j_media_drm_.obj(), j_session_id.obj(), |
| + j_response.obj(), promise_id); |
| } |
| -void MediaDrmBridge::ReleaseSession(uint32 session_id) { |
| +void MediaDrmBridge::CloseSession(const std::string& session_id, |
| + scoped_ptr<media::SimpleCdmPromise> promise) { |
| DVLOG(1) << __FUNCTION__; |
| + JNIEnv* env = AttachCurrentThread(); |
| + ScopedJavaLocalRef<jbyteArray> j_session_id = base::android::ToJavaByteArray( |
| + env, reinterpret_cast<const uint8_t*>(session_id.data()), |
| + session_id.size()); |
| + uint32_t promise_id = cdm_promise_adapter_.SavePromise(promise.Pass()); |
| + Java_MediaDrmBridge_closeSession(env, j_media_drm_.obj(), j_session_id.obj(), |
| + promise_id); |
| +} |
| - DCHECK(!session_closed_cb_.is_null()) |
| - << __FUNCTION__ << " called on a sessionless MediaDrmBridge object."; |
| +void MediaDrmBridge::RemoveSession( |
| + const std::string& session_id, |
| + scoped_ptr<media::SimpleCdmPromise> promise) { |
| + promise->reject(NOT_SUPPORTED_ERROR, 0, "RemoveSession() is not supported."); |
| +} |
| - JNIEnv* env = AttachCurrentThread(); |
| - Java_MediaDrmBridge_releaseSession(env, j_media_drm_.obj(), session_id); |
| +CdmContext* MediaDrmBridge::GetCdmContext() { |
| + NOTREACHED(); |
| + return nullptr; |
| } |
| int MediaDrmBridge::RegisterPlayer(const base::Closure& new_key_cb, |
| @@ -500,54 +539,80 @@ void MediaDrmBridge::OnMediaCryptoReady(JNIEnv* env, jobject) { |
| base::ResetAndReturn(&media_crypto_ready_cb_).Run(); |
| } |
| -void MediaDrmBridge::OnSessionCreated(JNIEnv* env, |
| - jobject j_media_drm, |
| - jint j_session_id, |
| - jstring j_web_session_id) { |
| - uint32 session_id = j_session_id; |
| - std::string web_session_id = ConvertJavaStringToUTF8(env, j_web_session_id); |
| - session_created_cb_.Run(session_id, web_session_id); |
| +void MediaDrmBridge::OnPromiseResolved(JNIEnv* env, |
| + jobject j_media_drm, |
| + jint j_promise_id) { |
| + cdm_promise_adapter_.ResolvePromise(j_promise_id); |
| +} |
| + |
| +void MediaDrmBridge::OnPromiseResolvedWithSession(JNIEnv* env, |
| + jobject j_media_drm, |
| + jint j_promise_id, |
| + jbyteArray j_session_id) { |
| + cdm_promise_adapter_.ResolvePromise(j_promise_id, |
| + GetSessionId(env, j_session_id)); |
| +} |
| + |
| +void MediaDrmBridge::OnPromiseRejected(JNIEnv* env, |
| + jobject j_media_drm, |
| + jint j_promise_id, |
| + jstring j_error_message) { |
| + std::string error_message = ConvertJavaStringToUTF8(env, j_error_message); |
| + cdm_promise_adapter_.RejectPromise(j_promise_id, MediaKeys::UNKNOWN_ERROR, 0, |
| + error_message); |
| } |
| void MediaDrmBridge::OnSessionMessage(JNIEnv* env, |
| jobject j_media_drm, |
| - jint j_session_id, |
| + jbyteArray j_session_id, |
| jbyteArray j_message, |
| - jstring j_destination_url) { |
| - uint32 session_id = j_session_id; |
| + jstring j_legacy_destination_url) { |
| std::vector<uint8> message; |
| JavaByteArrayToByteVector(env, j_message, &message); |
| - GURL destination_gurl = GURL(ConvertJavaStringToUTF8(env, j_destination_url)); |
| - if (!destination_gurl.is_valid() && !destination_gurl.is_empty()) { |
| - DLOG(WARNING) << "SessionMessage destination_url is invalid : " |
| - << destination_gurl.possibly_invalid_spec(); |
| - destination_gurl = GURL::EmptyGURL(); // Replace invalid destination_url. |
| - } |
| - session_message_cb_.Run(session_id, message, destination_gurl); |
| -} |
| + GURL legacy_destination_url = |
| + GURL(ConvertJavaStringToUTF8(env, j_legacy_destination_url)); |
| + // Note: Message type is not supported in MediaDrm. Do our best guess here. |
| + media::MediaKeys::MessageType message_type = |
| + legacy_destination_url.is_empty() ? media::MediaKeys::LICENSE_REQUEST |
| + : media::MediaKeys::LICENSE_RENEWAL; |
| -void MediaDrmBridge::OnSessionReady(JNIEnv* env, |
| - jobject j_media_drm, |
| - jint j_session_id) { |
| - uint32 session_id = j_session_id; |
| - session_ready_cb_.Run(session_id); |
| - // TODO(xhwang/jrummell): Move this when usableKeyIds/keyschange are |
| - // implemented. |
| - player_tracker_.NotifyNewKey(); |
| + session_message_cb_.Run(GetSessionId(env, j_session_id), message_type, |
| + message, legacy_destination_url); |
| } |
| void MediaDrmBridge::OnSessionClosed(JNIEnv* env, |
| jobject j_media_drm, |
| - jint j_session_id) { |
| - uint32 session_id = j_session_id; |
| - session_closed_cb_.Run(session_id); |
| -} |
| - |
| -void MediaDrmBridge::OnSessionError(JNIEnv* env, |
| - jobject j_media_drm, |
| - jint j_session_id) { |
| - uint32 session_id = j_session_id; |
| - session_error_cb_.Run(session_id, MediaKeys::kUnknownError, 0); |
| + jbyteArray j_session_id) { |
| + session_closed_cb_.Run(GetSessionId(env, j_session_id)); |
| +} |
| + |
| +void MediaDrmBridge::OnSessionKeysChange(JNIEnv* env, |
| + jobject j_media_drm, |
| + jbyteArray j_session_id, |
| + bool has_additional_usable_key, |
| + jint j_key_status) { |
| + if (has_additional_usable_key) |
| + player_tracker_.NotifyNewKey(); |
| + |
| + scoped_ptr<CdmKeyInformation> cdm_key_information(new CdmKeyInformation()); |
| + cdm_key_information->key_id.assign(kDummyKeyId, |
| + kDummyKeyId + sizeof(kDummyKeyId)); |
| + cdm_key_information->status = |
| + static_cast<CdmKeyInformation::KeyStatus>(j_key_status); |
| + CdmKeysInfo cdm_keys_info; |
| + cdm_keys_info.push_back(cdm_key_information.release()); |
| + |
| + session_keys_change_cb_.Run(GetSessionId(env, j_session_id), |
| + has_additional_usable_key, cdm_keys_info.Pass()); |
| +} |
| + |
| +void MediaDrmBridge::OnLegacySessionError(JNIEnv* env, |
| + jobject j_media_drm, |
| + jbyteArray j_session_id, |
| + jstring j_error_message) { |
| + std::string error_message = ConvertJavaStringToUTF8(env, j_error_message); |
| + session_error_cb_.Run(GetSessionId(env, j_session_id), |
| + MediaKeys::UNKNOWN_ERROR, 0, error_message); |
| } |
| ScopedJavaLocalRef<jobject> MediaDrmBridge::GetMediaCrypto() { |