media_drm_bridge.cc revision 5d1f7b1de12d16ceb2c938c56701a3e8bfa558f7
1// Copyright (c) 2013 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "media/base/android/media_drm_bridge.h" 6 7#include "base/android/build_info.h" 8#include "base/android/jni_array.h" 9#include "base/android/jni_string.h" 10#include "base/callback_helpers.h" 11#include "base/location.h" 12#include "base/logging.h" 13#include "base/message_loop/message_loop_proxy.h" 14#include "base/strings/string_util.h" 15#include "jni/MediaDrmBridge_jni.h" 16#include "media/base/android/media_player_manager.h" 17 18using base::android::AttachCurrentThread; 19using base::android::ConvertUTF8ToJavaString; 20using base::android::ConvertJavaStringToUTF8; 21using base::android::JavaByteArrayToByteVector; 22using base::android::ScopedJavaLocalRef; 23 24namespace media { 25 26static uint32 ReadUint32(const uint8_t* data) { 27 uint32 value = 0; 28 for (int i = 0; i < 4; ++i) 29 value = (value << 8) | data[i]; 30 return value; 31} 32 33static uint64 ReadUint64(const uint8_t* data) { 34 uint64 value = 0; 35 for (int i = 0; i < 8; ++i) 36 value = (value << 8) | data[i]; 37 return value; 38} 39 40// The structure of an ISO CENC Protection System Specific Header (PSSH) box is 41// as follows. (See ISO/IEC FDIS 23001-7:2011(E).) 42// Note: ISO boxes use big-endian values. 43// 44// PSSH { 45// uint32 Size 46// uint32 Type 47// uint64 LargeSize # Field is only present if value(Size) == 1. 48// uint32 VersionAndFlags 49// uint8[16] SystemId 50// uint32 DataSize 51// uint8[DataSize] Data 52// } 53static const int kBoxHeaderSize = 8; // Box's header contains Size and Type. 54static const int kBoxLargeSizeSize = 8; 55static const int kPsshVersionFlagSize = 4; 56static const int kPsshSystemIdSize = 16; 57static const int kPsshDataSizeSize = 4; 58static const uint32 kTencType = 0x74656e63; 59static const uint32 kPsshType = 0x70737368; 60 61// Tries to find a PSSH box whose "SystemId" is |uuid| in |data|, parses the 62// "Data" of the box and put it in |pssh_data|. Returns true if such a box is 63// found and successfully parsed. Returns false otherwise. 64// Notes: 65// 1, If multiple PSSH boxes are found,the "Data" of the first matching PSSH box 66// will be set in |pssh_data|. 67// 2, Only PSSH and TENC boxes are allowed in |data|. TENC boxes are skipped. 68static bool GetPsshData(const uint8* data, int data_size, 69 const std::vector<uint8>& uuid, 70 std::vector<uint8>* pssh_data) { 71 const uint8* cur = data; 72 const uint8* data_end = data + data_size; 73 int bytes_left = data_size; 74 75 while (bytes_left > 0) { 76 const uint8* box_head = cur; 77 78 if (bytes_left < kBoxHeaderSize) 79 return false; 80 81 uint64_t box_size = ReadUint32(cur); 82 uint32 type = ReadUint32(cur + 4); 83 cur += kBoxHeaderSize; 84 bytes_left -= kBoxHeaderSize; 85 86 if (box_size == 1) { // LargeSize is present. 87 if (bytes_left < kBoxLargeSizeSize) 88 return false; 89 90 box_size = ReadUint64(cur); 91 cur += kBoxLargeSizeSize; 92 bytes_left -= kBoxLargeSizeSize; 93 } else if (box_size == 0) { 94 box_size = bytes_left + kBoxHeaderSize; 95 } 96 97 const uint8* box_end = box_head + box_size; 98 if (data_end < box_end) 99 return false; 100 101 if (type == kTencType) { 102 // Skip 'tenc' box. 103 cur = box_end; 104 bytes_left = data_end - cur; 105 continue; 106 } else if (type != kPsshType) { 107 return false; 108 } 109 110 const int kPsshBoxMinimumSize = 111 kPsshVersionFlagSize + kPsshSystemIdSize + kPsshDataSizeSize; 112 if (box_end < cur + kPsshBoxMinimumSize) 113 return false; 114 115 uint32 version_and_flags = ReadUint32(cur); 116 cur += kPsshVersionFlagSize; 117 bytes_left -= kPsshVersionFlagSize; 118 if (version_and_flags != 0) 119 return false; 120 121 DCHECK_GE(bytes_left, kPsshSystemIdSize); 122 if (!std::equal(uuid.begin(), uuid.end(), cur)) { 123 cur = box_end; 124 bytes_left = data_end - cur; 125 continue; 126 } 127 128 cur += kPsshSystemIdSize; 129 bytes_left -= kPsshSystemIdSize; 130 131 uint32 data_size = ReadUint32(cur); 132 cur += kPsshDataSizeSize; 133 bytes_left -= kPsshDataSizeSize; 134 135 if (box_end < cur + data_size) 136 return false; 137 138 pssh_data->assign(cur, cur + data_size); 139 return true; 140 } 141 142 return false; 143} 144 145static MediaDrmBridge::SecurityLevel GetSecurityLevelFromString( 146 const std::string& security_level_str) { 147 if (0 == security_level_str.compare("L1")) 148 return MediaDrmBridge::SECURITY_LEVEL_1; 149 if (0 == security_level_str.compare("L3")) 150 return MediaDrmBridge::SECURITY_LEVEL_3; 151 DCHECK(security_level_str.empty()); 152 return MediaDrmBridge::SECURITY_LEVEL_NONE; 153} 154 155static std::string GetSecurityLevelString( 156 MediaDrmBridge::SecurityLevel security_level) { 157 switch (security_level) { 158 case MediaDrmBridge::SECURITY_LEVEL_NONE: 159 return ""; 160 case MediaDrmBridge::SECURITY_LEVEL_1: 161 return "L1"; 162 case MediaDrmBridge::SECURITY_LEVEL_3: 163 return "L3"; 164 } 165 return ""; 166} 167 168// static 169bool MediaDrmBridge::IsAvailable() { 170 return base::android::BuildInfo::GetInstance()->sdk_int() >= 19; 171} 172 173// static 174bool MediaDrmBridge::IsSecureDecoderRequired(SecurityLevel security_level) { 175 return SECURITY_LEVEL_1 == security_level; 176} 177 178bool MediaDrmBridge::IsSecurityLevelSupported( 179 const std::vector<uint8>& scheme_uuid, 180 SecurityLevel security_level) { 181 // Pass 0 as |media_keys_id| and NULL as |manager| as they are not used in 182 // creation time of MediaDrmBridge. 183 scoped_ptr<MediaDrmBridge> media_drm_bridge = 184 MediaDrmBridge::Create(0, scheme_uuid, GURL(), NULL); 185 if (!media_drm_bridge) 186 return false; 187 188 return media_drm_bridge->SetSecurityLevel(security_level); 189} 190 191bool MediaDrmBridge::IsCryptoSchemeSupported( 192 const std::vector<uint8>& scheme_uuid, 193 const std::string& container_mime_type) { 194 JNIEnv* env = AttachCurrentThread(); 195 ScopedJavaLocalRef<jbyteArray> j_scheme_uuid = 196 base::android::ToJavaByteArray(env, &scheme_uuid[0], scheme_uuid.size()); 197 ScopedJavaLocalRef<jstring> j_container_mime_type = 198 ConvertUTF8ToJavaString(env, container_mime_type); 199 return Java_MediaDrmBridge_isCryptoSchemeSupported( 200 env, j_scheme_uuid.obj(), j_container_mime_type.obj()); 201} 202 203bool MediaDrmBridge::RegisterMediaDrmBridge(JNIEnv* env) { 204 return RegisterNativesImpl(env); 205} 206 207MediaDrmBridge::MediaDrmBridge(int media_keys_id, 208 const std::vector<uint8>& scheme_uuid, 209 const GURL& frame_url, 210 MediaPlayerManager* manager) 211 : media_keys_id_(media_keys_id), 212 scheme_uuid_(scheme_uuid), 213 frame_url_(frame_url), 214 manager_(manager) { 215 JNIEnv* env = AttachCurrentThread(); 216 CHECK(env); 217 218 ScopedJavaLocalRef<jbyteArray> j_scheme_uuid = 219 base::android::ToJavaByteArray(env, &scheme_uuid[0], scheme_uuid.size()); 220 j_media_drm_.Reset(Java_MediaDrmBridge_create( 221 env, j_scheme_uuid.obj(), reinterpret_cast<intptr_t>(this))); 222} 223 224MediaDrmBridge::~MediaDrmBridge() { 225 JNIEnv* env = AttachCurrentThread(); 226 if (!j_media_drm_.is_null()) 227 Java_MediaDrmBridge_release(env, j_media_drm_.obj()); 228} 229 230// static 231scoped_ptr<MediaDrmBridge> MediaDrmBridge::Create( 232 int media_keys_id, 233 const std::vector<uint8>& scheme_uuid, 234 const GURL& frame_url, 235 MediaPlayerManager* manager) { 236 scoped_ptr<MediaDrmBridge> media_drm_bridge; 237 238 if (IsAvailable() && !scheme_uuid.empty()) { 239 // TODO(qinmin): check whether the uuid is valid. 240 media_drm_bridge.reset(new MediaDrmBridge( 241 media_keys_id, scheme_uuid, frame_url, manager)); 242 if (media_drm_bridge->j_media_drm_.is_null()) 243 media_drm_bridge.reset(); 244 } 245 246 return media_drm_bridge.Pass(); 247} 248 249bool MediaDrmBridge::SetSecurityLevel(SecurityLevel security_level) { 250 JNIEnv* env = AttachCurrentThread(); 251 252 std::string security_level_str = GetSecurityLevelString(security_level); 253 if (security_level_str.empty()) 254 return false; 255 256 ScopedJavaLocalRef<jstring> j_security_level = 257 ConvertUTF8ToJavaString(env, security_level_str); 258 return Java_MediaDrmBridge_setSecurityLevel( 259 env, j_media_drm_.obj(), j_security_level.obj()); 260} 261 262bool MediaDrmBridge::CreateSession(uint32 session_id, 263 const std::string& content_type, 264 const uint8* init_data, 265 int init_data_length) { 266 std::vector<uint8> pssh_data; 267 if (!GetPsshData(init_data, init_data_length, scheme_uuid_, &pssh_data)) 268 return false; 269 270 JNIEnv* env = AttachCurrentThread(); 271 ScopedJavaLocalRef<jbyteArray> j_pssh_data = 272 base::android::ToJavaByteArray(env, &pssh_data[0], pssh_data.size()); 273 ScopedJavaLocalRef<jstring> j_mime = 274 ConvertUTF8ToJavaString(env, content_type); 275 Java_MediaDrmBridge_createSession( 276 env, j_media_drm_.obj(), session_id, j_pssh_data.obj(), j_mime.obj()); 277 return true; 278} 279 280void MediaDrmBridge::LoadSession(uint32 session_id, 281 const std::string& web_session_id) { 282 // MediaDrmBridge doesn't support loading sessions. 283 NOTREACHED(); 284} 285 286void MediaDrmBridge::UpdateSession(uint32 session_id, 287 const uint8* response, 288 int response_length) { 289 DVLOG(1) << __FUNCTION__; 290 JNIEnv* env = AttachCurrentThread(); 291 ScopedJavaLocalRef<jbyteArray> j_response = 292 base::android::ToJavaByteArray(env, response, response_length); 293 Java_MediaDrmBridge_updateSession( 294 env, j_media_drm_.obj(), session_id, j_response.obj()); 295} 296 297void MediaDrmBridge::ReleaseSession(uint32 session_id) { 298 DVLOG(1) << __FUNCTION__; 299 JNIEnv* env = AttachCurrentThread(); 300 Java_MediaDrmBridge_releaseSession(env, j_media_drm_.obj(), session_id); 301} 302 303void MediaDrmBridge::SetMediaCryptoReadyCB(const base::Closure& closure) { 304 if (closure.is_null()) { 305 media_crypto_ready_cb_.Reset(); 306 return; 307 } 308 309 DCHECK(media_crypto_ready_cb_.is_null()); 310 311 if (!GetMediaCrypto().is_null()) { 312 base::MessageLoopProxy::current()->PostTask(FROM_HERE, closure); 313 return; 314 } 315 316 media_crypto_ready_cb_ = closure; 317} 318 319void MediaDrmBridge::OnMediaCryptoReady(JNIEnv* env, jobject) { 320 DCHECK(!GetMediaCrypto().is_null()); 321 if (!media_crypto_ready_cb_.is_null()) 322 base::ResetAndReturn(&media_crypto_ready_cb_).Run(); 323} 324 325void MediaDrmBridge::OnSessionCreated(JNIEnv* env, 326 jobject j_media_drm, 327 jint j_session_id, 328 jstring j_web_session_id) { 329 uint32 session_id = j_session_id; 330 std::string web_session_id = ConvertJavaStringToUTF8(env, j_web_session_id); 331 manager_->OnSessionCreated(media_keys_id_, session_id, web_session_id); 332} 333 334void MediaDrmBridge::OnSessionMessage(JNIEnv* env, 335 jobject j_media_drm, 336 jint j_session_id, 337 jbyteArray j_message, 338 jstring j_destination_url) { 339 uint32 session_id = j_session_id; 340 std::vector<uint8> message; 341 JavaByteArrayToByteVector(env, j_message, &message); 342 std::string destination_url = ConvertJavaStringToUTF8(env, j_destination_url); 343 GURL destination_gurl(destination_url); 344 if (!destination_gurl.is_valid() && !destination_gurl.is_empty()) { 345 DLOG(WARNING) << "SessionMessage destination_url is invalid : " 346 << destination_gurl.possibly_invalid_spec(); 347 destination_gurl = GURL::EmptyGURL(); // Replace invalid destination_url. 348 } 349 350 manager_->OnSessionMessage( 351 media_keys_id_, session_id, message, destination_gurl); 352} 353 354void MediaDrmBridge::OnSessionReady(JNIEnv* env, 355 jobject j_media_drm, 356 jint j_session_id) { 357 uint32 session_id = j_session_id; 358 manager_->OnSessionReady(media_keys_id_, session_id); 359} 360 361void MediaDrmBridge::OnSessionClosed(JNIEnv* env, 362 jobject j_media_drm, 363 jint j_session_id) { 364 uint32 session_id = j_session_id; 365 manager_->OnSessionClosed(media_keys_id_, session_id); 366} 367 368void MediaDrmBridge::OnSessionError(JNIEnv* env, 369 jobject j_media_drm, 370 jint j_session_id) { 371 uint32 session_id = j_session_id; 372 manager_->OnSessionError( 373 media_keys_id_, session_id, MediaKeys::kUnknownError, 0); 374} 375 376ScopedJavaLocalRef<jobject> MediaDrmBridge::GetMediaCrypto() { 377 JNIEnv* env = AttachCurrentThread(); 378 return Java_MediaDrmBridge_getMediaCrypto(env, j_media_drm_.obj()); 379} 380 381MediaDrmBridge::SecurityLevel MediaDrmBridge::GetSecurityLevel() { 382 JNIEnv* env = AttachCurrentThread(); 383 ScopedJavaLocalRef<jstring> j_security_level = 384 Java_MediaDrmBridge_getSecurityLevel(env, j_media_drm_.obj()); 385 std::string security_level_str = 386 ConvertJavaStringToUTF8(env, j_security_level.obj()); 387 return GetSecurityLevelFromString(security_level_str); 388} 389 390bool MediaDrmBridge::IsProtectedSurfaceRequired() { 391 return IsSecureDecoderRequired(GetSecurityLevel()); 392} 393 394void MediaDrmBridge::ResetDeviceCredentials( 395 const ResetCredentialsCB& callback) { 396 DCHECK(reset_credentials_cb_.is_null()); 397 reset_credentials_cb_ = callback; 398 JNIEnv* env = AttachCurrentThread(); 399 Java_MediaDrmBridge_resetDeviceCredentials(env, j_media_drm_.obj()); 400} 401 402void MediaDrmBridge::OnResetDeviceCredentialsCompleted( 403 JNIEnv* env, jobject, bool success) { 404 base::ResetAndReturn(&reset_credentials_cb_).Run(success); 405} 406 407} // namespace media 408