| 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..d898ee255d2bef66c1c6460842d334969f5eac72 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";
|
| +
|
| typedef std::vector<uint8> UUID;
|
|
|
| class KeySystemUuidManager {
|
| @@ -253,6 +259,20 @@ 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());
|
| +}
|
| +
|
| +// Similar to string_as_array() in stl_util.h. Cannot use string_as_array()
|
| +// directly because we are dealing with const strings.
|
| +static const uint8_t* StringAsArray(const std::string& str) {
|
| + return str.empty() ? nullptr
|
| + : reinterpret_cast<const uint8_t*>(&*str.begin());
|
| +}
|
| +
|
| // static
|
| bool MediaDrmBridge::IsAvailable() {
|
| if (base::android::BuildInfo::GetInstance()->sdk_int() < 19)
|
| @@ -322,18 +342,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 +370,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 +386,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();
|
| @@ -382,12 +399,9 @@ scoped_ptr<MediaDrmBridge> MediaDrmBridge::Create(
|
| // static
|
| scoped_ptr<MediaDrmBridge> MediaDrmBridge::CreateSessionless(
|
| 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 +417,36 @@ 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) {
|
| - DVLOG(1) << __FUNCTION__;
|
| +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.");
|
| +}
|
|
|
| - DCHECK(!session_created_cb_.is_null())
|
| - << "CreateSession called on a sessionless MediaDrmBridge object.";
|
| +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__;
|
|
|
| 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 +455,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) {
|
| + NOTREACHED() << "Persistent session not supported";
|
| + 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, StringAsArray(session_id), 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, StringAsArray(session_id), 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) {
|
| + NOTREACHED() << "Persistent session not supported.";
|
| + 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 +540,79 @@ 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;
|
| 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()) {
|
| + GURL destination_url = GURL(ConvertJavaStringToUTF8(env, j_destination_url));
|
| + if (!destination_url.is_valid() && !destination_url.is_empty()) {
|
| DLOG(WARNING) << "SessionMessage destination_url is invalid : "
|
| - << destination_gurl.possibly_invalid_spec();
|
| - destination_gurl = GURL::EmptyGURL(); // Replace invalid destination_url.
|
| + << destination_url.possibly_invalid_spec();
|
| + destination_url = GURL::EmptyGURL(); // Replace invalid destination_url.
|
| }
|
| - session_message_cb_.Run(session_id, message, destination_gurl);
|
| -}
|
| -
|
| -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,
|
| + 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() {
|
|
|