media_drm_bridge.cc revision 424c4d7b64af9d0d8fd9624f381f469654d5e3d2
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 144// static 145MediaDrmBridge* MediaDrmBridge::Create(int media_keys_id, 146 const std::vector<uint8>& scheme_uuid, 147 MediaPlayerManager* manager) { 148 if (!IsAvailable() || scheme_uuid.empty()) 149 return NULL; 150 151 // TODO(qinmin): check whether the uuid is valid. 152 return new MediaDrmBridge(media_keys_id, scheme_uuid, manager); 153} 154 155bool MediaDrmBridge::IsAvailable() { 156 return base::android::BuildInfo::GetInstance()->sdk_int() >= 18; 157} 158 159bool MediaDrmBridge::RegisterMediaDrmBridge(JNIEnv* env) { 160 return RegisterNativesImpl(env); 161} 162 163MediaDrmBridge::MediaDrmBridge(int media_keys_id, 164 const std::vector<uint8>& scheme_uuid, 165 MediaPlayerManager* manager) 166 : media_keys_id_(media_keys_id), 167 scheme_uuid_(scheme_uuid), 168 manager_(manager) { 169 JNIEnv* env = AttachCurrentThread(); 170 CHECK(env); 171 172 ScopedJavaLocalRef<jbyteArray> j_scheme_uuid = 173 base::android::ToJavaByteArray(env, &scheme_uuid[0], scheme_uuid.size()); 174 j_media_drm_.Reset(Java_MediaDrmBridge_create( 175 env, j_scheme_uuid.obj(), reinterpret_cast<intptr_t>(this))); 176} 177 178MediaDrmBridge::~MediaDrmBridge() { 179 JNIEnv* env = AttachCurrentThread(); 180 Java_MediaDrmBridge_release(env, j_media_drm_.obj()); 181} 182 183bool MediaDrmBridge::GenerateKeyRequest(const std::string& type, 184 const uint8* init_data, 185 int init_data_length) { 186 std::vector<uint8> pssh_data; 187 if (!GetPsshData(init_data, init_data_length, scheme_uuid_, &pssh_data)) 188 return false; 189 190 JNIEnv* env = AttachCurrentThread(); 191 ScopedJavaLocalRef<jbyteArray> j_pssh_data = 192 base::android::ToJavaByteArray(env, &pssh_data[0], pssh_data.size()); 193 ScopedJavaLocalRef<jstring> j_mime = ConvertUTF8ToJavaString(env, type); 194 Java_MediaDrmBridge_generateKeyRequest( 195 env, j_media_drm_.obj(), j_pssh_data.obj(), j_mime.obj()); 196 return true; 197} 198 199void MediaDrmBridge::AddKey(const uint8* key, int key_length, 200 const uint8* init_data, int init_data_length, 201 const std::string& session_id) { 202 JNIEnv* env = AttachCurrentThread(); 203 ScopedJavaLocalRef<jbyteArray> j_key_data = 204 base::android::ToJavaByteArray(env, key, key_length); 205 ScopedJavaLocalRef<jstring> j_session_id = 206 ConvertUTF8ToJavaString(env, session_id); 207 Java_MediaDrmBridge_addKey( 208 env, j_media_drm_.obj(), j_session_id.obj(), j_key_data.obj()); 209} 210 211void MediaDrmBridge::CancelKeyRequest(const std::string& session_id) { 212 JNIEnv* env = AttachCurrentThread(); 213 ScopedJavaLocalRef<jstring> j_session_id = 214 ConvertUTF8ToJavaString(env, session_id); 215 Java_MediaDrmBridge_cancelKeyRequest( 216 env, j_media_drm_.obj(), j_session_id.obj()); 217} 218 219void MediaDrmBridge::SetMediaCryptoReadyCB(const base::Closure& closure) { 220 if (closure.is_null()) { 221 media_crypto_ready_cb_.Reset(); 222 return; 223 } 224 225 DCHECK(media_crypto_ready_cb_.is_null()); 226 227 if (!GetMediaCrypto().is_null()) { 228 base::MessageLoopProxy::current()->PostTask(FROM_HERE, closure); 229 return; 230 } 231 232 media_crypto_ready_cb_ = closure; 233} 234 235void MediaDrmBridge::OnMediaCryptoReady(JNIEnv* env, jobject) { 236 DCHECK(!GetMediaCrypto().is_null()); 237 if (!media_crypto_ready_cb_.is_null()) 238 base::ResetAndReturn(&media_crypto_ready_cb_).Run(); 239} 240 241void MediaDrmBridge::OnKeyMessage(JNIEnv* env, 242 jobject j_media_drm, 243 jstring j_session_id, 244 jbyteArray j_message, 245 jstring j_destination_url) { 246 std::string session_id = ConvertJavaStringToUTF8(env, j_session_id); 247 std::vector<uint8> message; 248 JavaByteArrayToByteVector(env, j_message, &message); 249 std::string destination_url = ConvertJavaStringToUTF8(env, j_destination_url); 250 251 manager_->OnKeyMessage(media_keys_id_, session_id, message, destination_url); 252} 253 254void MediaDrmBridge::OnKeyAdded(JNIEnv* env, jobject, jstring j_session_id) { 255 std::string session_id = ConvertJavaStringToUTF8(env, j_session_id); 256 manager_->OnKeyAdded(media_keys_id_, session_id); 257} 258 259void MediaDrmBridge::OnKeyError(JNIEnv* env, jobject, jstring j_session_id) { 260 // |j_session_id| can be NULL, in which case we'll return an empty string. 261 std::string session_id = ConvertJavaStringToUTF8(env, j_session_id); 262 manager_->OnKeyError(media_keys_id_, session_id, MediaKeys::kUnknownError, 0); 263} 264 265ScopedJavaLocalRef<jobject> MediaDrmBridge::GetMediaCrypto() { 266 JNIEnv* env = AttachCurrentThread(); 267 return Java_MediaDrmBridge_getMediaCrypto(env, j_media_drm_.obj()); 268} 269 270MediaDrmBridge::SecurityLevel MediaDrmBridge::GetSecurityLevel() { 271 JNIEnv* env = AttachCurrentThread(); 272 ScopedJavaLocalRef<jstring> j_security_level = 273 Java_MediaDrmBridge_getSecurityLevel(env, j_media_drm_.obj()); 274 std::string security_level = 275 ConvertJavaStringToUTF8(env, j_security_level.obj()); 276 if (0 == security_level.compare("L1")) 277 return SECURITY_LEVEL_1; 278 if (0 == security_level.compare("L3")) 279 return SECURITY_LEVEL_3; 280 DCHECK(security_level.empty()); 281 return SECURITY_LEVEL_NONE; 282} 283 284bool MediaDrmBridge::IsProtectedSurfaceRequired() { 285 return MediaDrmBridge::SECURITY_LEVEL_1 == GetSecurityLevel(); 286} 287 288} // namespace media 289