media_drm_bridge.cc revision 3551c9c881056c480085172ff9840cab31610854
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/logging.h" 11#include "jni/MediaDrmBridge_jni.h" 12#include "media/base/android/media_player_manager.h" 13 14using base::android::AttachCurrentThread; 15using base::android::ConvertUTF8ToJavaString; 16using base::android::ConvertJavaStringToUTF8; 17using base::android::JavaByteArrayToByteVector; 18using base::android::ScopedJavaLocalRef; 19 20namespace media { 21 22static uint32 ReadUint32(const uint8_t* data) { 23 uint32 value = 0; 24 for (int i = 0; i < 4; ++i) 25 value = (value << 8) | data[i]; 26 return value; 27} 28 29static uint64 ReadUint64(const uint8_t* data) { 30 uint64 value = 0; 31 for (int i = 0; i < 8; ++i) 32 value = (value << 8) | data[i]; 33 return value; 34} 35 36// The structure of an ISO CENC Protection System Specific Header (PSSH) box is 37// as follows. (See ISO/IEC FDIS 23001-7:2011(E).) 38// Note: ISO boxes use big-endian values. 39// 40// PSSH { 41// uint32 Size 42// uint32 Type 43// uint64 LargeSize # Field is only present if value(Size) == 1. 44// uint32 VersionAndFlags 45// uint8[16] SystemId 46// uint32 DataSize 47// uint8[DataSize] Data 48// } 49static const int kBoxHeaderSize = 8; // Box's header contains Size and Type. 50static const int kBoxLargeSizeSize = 8; 51static const int kPsshVersionFlagSize = 4; 52static const int kPsshSystemIdSize = 16; 53static const int kPsshDataSizeSize = 4; 54static const uint32 kTencType = 0x74656e63; 55static const uint32 kPsshType = 0x70737368; 56 57// Tries to find a PSSH box whose "SystemId" is |uuid| in |data|, parses the 58// "Data" of the box and put it in |pssh_data|. Returns true if such a box is 59// found and successfully parsed. Returns false otherwise. 60// Notes: 61// 1, If multiple PSSH boxes are found,the "Data" of the first matching PSSH box 62// will be set in |pssh_data|. 63// 2, Only PSSH and TENC boxes are allowed in |data|. TENC boxes are skipped. 64static bool GetPsshData(const uint8* data, int data_size, 65 const std::vector<uint8>& uuid, 66 std::vector<uint8>* pssh_data) { 67 const uint8* cur = data; 68 const uint8* data_end = data + data_size; 69 int bytes_left = data_size; 70 71 while (bytes_left > 0) { 72 const uint8* box_head = cur; 73 74 if (bytes_left < kBoxHeaderSize) 75 return false; 76 77 uint64_t box_size = ReadUint32(cur); 78 uint32 type = ReadUint32(cur + 4); 79 cur += kBoxHeaderSize; 80 bytes_left -= kBoxHeaderSize; 81 82 if (box_size == 1) { // LargeSize is present. 83 if (bytes_left < kBoxLargeSizeSize) 84 return false; 85 86 box_size = ReadUint64(cur); 87 cur += kBoxLargeSizeSize; 88 bytes_left -= kBoxLargeSizeSize; 89 } else if (box_size == 0) { 90 box_size = bytes_left + kBoxHeaderSize; 91 } 92 93 const uint8* box_end = box_head + box_size; 94 if (data_end < box_end) 95 return false; 96 97 if (type == kTencType) { 98 // Skip 'tenc' box. 99 cur = box_end; 100 bytes_left = data_end - cur; 101 continue; 102 } else if (type != kPsshType) { 103 return false; 104 } 105 106 const int kPsshBoxMinimumSize = 107 kPsshVersionFlagSize + kPsshSystemIdSize + kPsshDataSizeSize; 108 if (box_end < cur + kPsshBoxMinimumSize) 109 return false; 110 111 uint32 version_and_flags = ReadUint32(cur); 112 cur += kPsshVersionFlagSize; 113 bytes_left -= kPsshVersionFlagSize; 114 if (version_and_flags != 0) 115 return false; 116 117 DCHECK_GE(bytes_left, kPsshSystemIdSize); 118 if (!std::equal(uuid.begin(), uuid.end(), cur)) { 119 cur = box_end; 120 bytes_left = data_end - cur; 121 continue; 122 } 123 124 cur += kPsshSystemIdSize; 125 bytes_left -= kPsshSystemIdSize; 126 127 uint32 data_size = ReadUint32(cur); 128 cur += kPsshDataSizeSize; 129 bytes_left -= kPsshDataSizeSize; 130 131 if (box_end < cur + data_size) 132 return false; 133 134 pssh_data->assign(cur, cur + data_size); 135 return true; 136 } 137 138 return false; 139} 140 141// static 142MediaDrmBridge* MediaDrmBridge::Create(int media_keys_id, 143 const std::vector<uint8>& scheme_uuid, 144 MediaPlayerManager* manager) { 145 if (!IsAvailable() || scheme_uuid.empty()) 146 return NULL; 147 148 // TODO(qinmin): check whether the uuid is valid. 149 return new MediaDrmBridge(media_keys_id, scheme_uuid, manager); 150} 151 152bool MediaDrmBridge::IsAvailable() { 153 return base::android::BuildInfo::GetInstance()->sdk_int() >= 18; 154} 155 156bool MediaDrmBridge::RegisterMediaDrmBridge(JNIEnv* env) { 157 return RegisterNativesImpl(env); 158} 159 160MediaDrmBridge::MediaDrmBridge(int media_keys_id, 161 const std::vector<uint8>& scheme_uuid, 162 MediaPlayerManager* manager) 163 : media_keys_id_(media_keys_id), 164 scheme_uuid_(scheme_uuid), 165 manager_(manager) { 166 JNIEnv* env = AttachCurrentThread(); 167 CHECK(env); 168 169 ScopedJavaLocalRef<jbyteArray> j_scheme_uuid = 170 base::android::ToJavaByteArray(env, &scheme_uuid[0], scheme_uuid.size()); 171 j_media_drm_.Reset(Java_MediaDrmBridge_create( 172 env, j_scheme_uuid.obj(), reinterpret_cast<intptr_t>(this))); 173} 174 175MediaDrmBridge::~MediaDrmBridge() { 176 JNIEnv* env = AttachCurrentThread(); 177 Java_MediaDrmBridge_release(env, j_media_drm_.obj()); 178} 179 180bool MediaDrmBridge::GenerateKeyRequest(const std::string& type, 181 const uint8* init_data, 182 int init_data_length) { 183 std::vector<uint8> pssh_data; 184 if (!GetPsshData(init_data, init_data_length, scheme_uuid_, &pssh_data)) 185 return false; 186 187 JNIEnv* env = AttachCurrentThread(); 188 ScopedJavaLocalRef<jbyteArray> j_pssh_data = 189 base::android::ToJavaByteArray(env, &pssh_data[0], pssh_data.size()); 190 ScopedJavaLocalRef<jstring> j_mime = ConvertUTF8ToJavaString(env, type); 191 Java_MediaDrmBridge_generateKeyRequest( 192 env, j_media_drm_.obj(), j_pssh_data.obj(), j_mime.obj()); 193 return true; 194} 195 196void MediaDrmBridge::AddKey(const uint8* key, int key_length, 197 const uint8* init_data, int init_data_length, 198 const std::string& session_id) { 199 JNIEnv* env = AttachCurrentThread(); 200 ScopedJavaLocalRef<jbyteArray> j_key_data = 201 base::android::ToJavaByteArray(env, key, key_length); 202 ScopedJavaLocalRef<jstring> j_session_id = 203 ConvertUTF8ToJavaString(env, session_id); 204 Java_MediaDrmBridge_addKey( 205 env, j_media_drm_.obj(), j_session_id.obj(), j_key_data.obj()); 206} 207 208ScopedJavaLocalRef<jobject> MediaDrmBridge::GetMediaCrypto() { 209 JNIEnv* env = AttachCurrentThread(); 210 return Java_MediaDrmBridge_getMediaCrypto(env, j_media_drm_.obj()); 211} 212 213void MediaDrmBridge::CancelKeyRequest(const std::string& session_id) { 214 JNIEnv* env = AttachCurrentThread(); 215 ScopedJavaLocalRef<jstring> j_session_id = 216 ConvertUTF8ToJavaString(env, session_id); 217 Java_MediaDrmBridge_cancelKeyRequest( 218 env, j_media_drm_.obj(), j_session_id.obj()); 219} 220 221void MediaDrmBridge::OnKeyMessage(JNIEnv* env, 222 jobject j_media_drm, 223 jstring j_session_id, 224 jbyteArray j_message, 225 jstring j_destination_url) { 226 std::string session_id = ConvertJavaStringToUTF8(env, j_session_id); 227 std::vector<uint8> message; 228 JavaByteArrayToByteVector(env, j_message, &message); 229 std::string destination_url = ConvertJavaStringToUTF8(env, j_destination_url); 230 231 manager_->OnKeyMessage(media_keys_id_, session_id, message, destination_url); 232} 233 234void MediaDrmBridge::OnKeyAdded(JNIEnv* env, jobject, jstring j_session_id) { 235 std::string session_id = ConvertJavaStringToUTF8(env, j_session_id); 236 manager_->OnKeyAdded(media_keys_id_, session_id); 237} 238 239void MediaDrmBridge::OnKeyError(JNIEnv* env, jobject, jstring j_session_id) { 240 std::string session_id = ConvertJavaStringToUTF8(env, j_session_id); 241 manager_->OnKeyError(media_keys_id_, session_id, MediaKeys::kUnknownError, 0); 242} 243 244} // namespace media 245