media_drm_bridge.cc revision 58537e28ecd584eab876aee8be7156509866d23a
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 "jni/MediaDrmBridge_jni.h" 15#include "media/base/android/media_player_manager.h" 16 17using base::android::AttachCurrentThread; 18using base::android::ConvertUTF8ToJavaString; 19using base::android::ConvertJavaStringToUTF8; 20using base::android::JavaByteArrayToByteVector; 21using base::android::ScopedJavaLocalRef; 22 23namespace media { 24 25static uint32 ReadUint32(const uint8_t* data) { 26 uint32 value = 0; 27 for (int i = 0; i < 4; ++i) 28 value = (value << 8) | data[i]; 29 return value; 30} 31 32static uint64 ReadUint64(const uint8_t* data) { 33 uint64 value = 0; 34 for (int i = 0; i < 8; ++i) 35 value = (value << 8) | data[i]; 36 return value; 37} 38 39// The structure of an ISO CENC Protection System Specific Header (PSSH) box is 40// as follows. (See ISO/IEC FDIS 23001-7:2011(E).) 41// Note: ISO boxes use big-endian values. 42// 43// PSSH { 44// uint32 Size 45// uint32 Type 46// uint64 LargeSize # Field is only present if value(Size) == 1. 47// uint32 VersionAndFlags 48// uint8[16] SystemId 49// uint32 DataSize 50// uint8[DataSize] Data 51// } 52static const int kBoxHeaderSize = 8; // Box's header contains Size and Type. 53static const int kBoxLargeSizeSize = 8; 54static const int kPsshVersionFlagSize = 4; 55static const int kPsshSystemIdSize = 16; 56static const int kPsshDataSizeSize = 4; 57static const uint32 kTencType = 0x74656e63; 58static const uint32 kPsshType = 0x70737368; 59 60// Tries to find a PSSH box whose "SystemId" is |uuid| in |data|, parses the 61// "Data" of the box and put it in |pssh_data|. Returns true if such a box is 62// found and successfully parsed. Returns false otherwise. 63// Notes: 64// 1, If multiple PSSH boxes are found,the "Data" of the first matching PSSH box 65// will be set in |pssh_data|. 66// 2, Only PSSH and TENC boxes are allowed in |data|. TENC boxes are skipped. 67static bool GetPsshData(const uint8* data, int data_size, 68 const std::vector<uint8>& uuid, 69 std::vector<uint8>* pssh_data) { 70 const uint8* cur = data; 71 const uint8* data_end = data + data_size; 72 int bytes_left = data_size; 73 74 while (bytes_left > 0) { 75 const uint8* box_head = cur; 76 77 if (bytes_left < kBoxHeaderSize) 78 return false; 79 80 uint64_t box_size = ReadUint32(cur); 81 uint32 type = ReadUint32(cur + 4); 82 cur += kBoxHeaderSize; 83 bytes_left -= kBoxHeaderSize; 84 85 if (box_size == 1) { // LargeSize is present. 86 if (bytes_left < kBoxLargeSizeSize) 87 return false; 88 89 box_size = ReadUint64(cur); 90 cur += kBoxLargeSizeSize; 91 bytes_left -= kBoxLargeSizeSize; 92 } else if (box_size == 0) { 93 box_size = bytes_left + kBoxHeaderSize; 94 } 95 96 const uint8* box_end = box_head + box_size; 97 if (data_end < box_end) 98 return false; 99 100 if (type == kTencType) { 101 // Skip 'tenc' box. 102 cur = box_end; 103 bytes_left = data_end - cur; 104 continue; 105 } else if (type != kPsshType) { 106 return false; 107 } 108 109 const int kPsshBoxMinimumSize = 110 kPsshVersionFlagSize + kPsshSystemIdSize + kPsshDataSizeSize; 111 if (box_end < cur + kPsshBoxMinimumSize) 112 return false; 113 114 uint32 version_and_flags = ReadUint32(cur); 115 cur += kPsshVersionFlagSize; 116 bytes_left -= kPsshVersionFlagSize; 117 if (version_and_flags != 0) 118 return false; 119 120 DCHECK_GE(bytes_left, kPsshSystemIdSize); 121 if (!std::equal(uuid.begin(), uuid.end(), cur)) { 122 cur = box_end; 123 bytes_left = data_end - cur; 124 continue; 125 } 126 127 cur += kPsshSystemIdSize; 128 bytes_left -= kPsshSystemIdSize; 129 130 uint32 data_size = ReadUint32(cur); 131 cur += kPsshDataSizeSize; 132 bytes_left -= kPsshDataSizeSize; 133 134 if (box_end < cur + data_size) 135 return false; 136 137 pssh_data->assign(cur, cur + data_size); 138 return true; 139 } 140 141 return false; 142} 143 144static MediaDrmBridge::SecurityLevel GetSecurityLevelFromString( 145 const std::string& security_level_str) { 146 if (0 == security_level_str.compare("L1")) 147 return MediaDrmBridge::SECURITY_LEVEL_1; 148 if (0 == security_level_str.compare("L3")) 149 return MediaDrmBridge::SECURITY_LEVEL_3; 150 DCHECK(security_level_str.empty()); 151 return MediaDrmBridge::SECURITY_LEVEL_NONE; 152} 153 154// static 155scoped_ptr<MediaDrmBridge> MediaDrmBridge::Create( 156 int media_keys_id, 157 const std::vector<uint8>& scheme_uuid, 158 const std::string& security_level, 159 MediaPlayerManager* manager) { 160 scoped_ptr<MediaDrmBridge> media_drm_bridge; 161 162 if (IsAvailable() && !scheme_uuid.empty()) { 163 // TODO(qinmin): check whether the uuid is valid. 164 media_drm_bridge.reset( 165 new MediaDrmBridge(media_keys_id, scheme_uuid, security_level, manager)); 166 if (media_drm_bridge->j_media_drm_.is_null()) 167 media_drm_bridge.reset(); 168 } 169 170 return media_drm_bridge.Pass(); 171} 172 173// static 174bool MediaDrmBridge::IsAvailable() { 175 return base::android::BuildInfo::GetInstance()->sdk_int() >= 18; 176} 177 178// static 179bool MediaDrmBridge::IsSecureDecoderRequired( 180 const std::string& security_level_str) { 181 return IsSecureDecoderRequired( 182 GetSecurityLevelFromString(security_level_str)); 183} 184 185bool MediaDrmBridge::IsSecurityLevelSupported( 186 const std::vector<uint8>& scheme_uuid, 187 const std::string& security_level) { 188 // Pass 0 as |media_keys_id| and NULL as |manager| as they are not used in 189 // creation time of MediaDrmBridge. 190 return MediaDrmBridge::Create(0, scheme_uuid, security_level, NULL) != NULL; 191} 192 193bool MediaDrmBridge::IsCryptoSchemeSupported( 194 const std::vector<uint8>& scheme_uuid, 195 const std::string& container_mime_type) { 196 JNIEnv* env = AttachCurrentThread(); 197 ScopedJavaLocalRef<jbyteArray> j_scheme_uuid = 198 base::android::ToJavaByteArray(env, &scheme_uuid[0], scheme_uuid.size()); 199 ScopedJavaLocalRef<jstring> j_container_mime_type = 200 ConvertUTF8ToJavaString(env, container_mime_type); 201 return Java_MediaDrmBridge_isCryptoSchemeSupported( 202 env, j_scheme_uuid.obj(), j_container_mime_type.obj()); 203} 204 205bool MediaDrmBridge::RegisterMediaDrmBridge(JNIEnv* env) { 206 return RegisterNativesImpl(env); 207} 208 209MediaDrmBridge::MediaDrmBridge(int media_keys_id, 210 const std::vector<uint8>& scheme_uuid, 211 const std::string& security_level, 212 MediaPlayerManager* manager) 213 : media_keys_id_(media_keys_id), 214 scheme_uuid_(scheme_uuid), 215 manager_(manager) { 216 JNIEnv* env = AttachCurrentThread(); 217 CHECK(env); 218 219 ScopedJavaLocalRef<jbyteArray> j_scheme_uuid = 220 base::android::ToJavaByteArray(env, &scheme_uuid[0], scheme_uuid.size()); 221 ScopedJavaLocalRef<jstring> j_security_level = 222 ConvertUTF8ToJavaString(env, security_level); 223 j_media_drm_.Reset(Java_MediaDrmBridge_create( 224 env, j_scheme_uuid.obj(), j_security_level.obj(), 225 reinterpret_cast<intptr_t>(this))); 226} 227 228MediaDrmBridge::~MediaDrmBridge() { 229 JNIEnv* env = AttachCurrentThread(); 230 if (!j_media_drm_.is_null()) 231 Java_MediaDrmBridge_release(env, j_media_drm_.obj()); 232} 233 234bool MediaDrmBridge::GenerateKeyRequest(const std::string& type, 235 const uint8* init_data, 236 int init_data_length) { 237 std::vector<uint8> pssh_data; 238 if (!GetPsshData(init_data, init_data_length, scheme_uuid_, &pssh_data)) 239 return false; 240 241 JNIEnv* env = AttachCurrentThread(); 242 ScopedJavaLocalRef<jbyteArray> j_pssh_data = 243 base::android::ToJavaByteArray(env, &pssh_data[0], pssh_data.size()); 244 ScopedJavaLocalRef<jstring> j_mime = ConvertUTF8ToJavaString(env, type); 245 Java_MediaDrmBridge_generateKeyRequest( 246 env, j_media_drm_.obj(), j_pssh_data.obj(), j_mime.obj()); 247 return true; 248} 249 250void MediaDrmBridge::AddKey(const uint8* key, int key_length, 251 const uint8* init_data, int init_data_length, 252 const std::string& session_id) { 253 DVLOG(1) << __FUNCTION__; 254 JNIEnv* env = AttachCurrentThread(); 255 ScopedJavaLocalRef<jbyteArray> j_key_data = 256 base::android::ToJavaByteArray(env, key, key_length); 257 ScopedJavaLocalRef<jstring> j_session_id = 258 ConvertUTF8ToJavaString(env, session_id); 259 Java_MediaDrmBridge_addKey( 260 env, j_media_drm_.obj(), j_session_id.obj(), j_key_data.obj()); 261} 262 263void MediaDrmBridge::CancelKeyRequest(const std::string& session_id) { 264 JNIEnv* env = AttachCurrentThread(); 265 ScopedJavaLocalRef<jstring> j_session_id = 266 ConvertUTF8ToJavaString(env, session_id); 267 Java_MediaDrmBridge_cancelKeyRequest( 268 env, j_media_drm_.obj(), j_session_id.obj()); 269} 270 271void MediaDrmBridge::SetMediaCryptoReadyCB(const base::Closure& closure) { 272 if (closure.is_null()) { 273 media_crypto_ready_cb_.Reset(); 274 return; 275 } 276 277 DCHECK(media_crypto_ready_cb_.is_null()); 278 279 if (!GetMediaCrypto().is_null()) { 280 base::MessageLoopProxy::current()->PostTask(FROM_HERE, closure); 281 return; 282 } 283 284 media_crypto_ready_cb_ = closure; 285} 286 287void MediaDrmBridge::OnMediaCryptoReady(JNIEnv* env, jobject) { 288 DCHECK(!GetMediaCrypto().is_null()); 289 if (!media_crypto_ready_cb_.is_null()) 290 base::ResetAndReturn(&media_crypto_ready_cb_).Run(); 291} 292 293void MediaDrmBridge::OnKeyMessage(JNIEnv* env, 294 jobject j_media_drm, 295 jstring j_session_id, 296 jbyteArray j_message, 297 jstring j_destination_url) { 298 std::string session_id = ConvertJavaStringToUTF8(env, j_session_id); 299 std::vector<uint8> message; 300 JavaByteArrayToByteVector(env, j_message, &message); 301 std::string destination_url = ConvertJavaStringToUTF8(env, j_destination_url); 302 303 manager_->OnKeyMessage(media_keys_id_, session_id, message, destination_url); 304} 305 306void MediaDrmBridge::OnKeyAdded(JNIEnv* env, jobject, jstring j_session_id) { 307 std::string session_id = ConvertJavaStringToUTF8(env, j_session_id); 308 manager_->OnKeyAdded(media_keys_id_, session_id); 309} 310 311void MediaDrmBridge::OnKeyError(JNIEnv* env, jobject, jstring j_session_id) { 312 // |j_session_id| can be NULL, in which case we'll return an empty string. 313 std::string session_id = ConvertJavaStringToUTF8(env, j_session_id); 314 manager_->OnKeyError(media_keys_id_, session_id, MediaKeys::kUnknownError, 0); 315} 316 317ScopedJavaLocalRef<jobject> MediaDrmBridge::GetMediaCrypto() { 318 JNIEnv* env = AttachCurrentThread(); 319 return Java_MediaDrmBridge_getMediaCrypto(env, j_media_drm_.obj()); 320} 321 322// static 323bool MediaDrmBridge::IsSecureDecoderRequired(SecurityLevel security_level) { 324 return MediaDrmBridge::SECURITY_LEVEL_1 == security_level; 325} 326 327MediaDrmBridge::SecurityLevel MediaDrmBridge::GetSecurityLevel() { 328 JNIEnv* env = AttachCurrentThread(); 329 ScopedJavaLocalRef<jstring> j_security_level = 330 Java_MediaDrmBridge_getSecurityLevel(env, j_media_drm_.obj()); 331 std::string security_level_str = 332 ConvertJavaStringToUTF8(env, j_security_level.obj()); 333 return GetSecurityLevelFromString(security_level_str); 334} 335 336bool MediaDrmBridge::IsProtectedSurfaceRequired() { 337 return IsSecureDecoderRequired(GetSecurityLevel()); 338} 339 340} // namespace media 341