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