1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 //#define LOG_NDEBUG 0 18 #define LOG_TAG "NdkMediaExtractor" 19 20 21 #include "NdkMediaError.h" 22 #include "NdkMediaExtractor.h" 23 #include "NdkMediaFormatPriv.h" 24 25 26 #include <inttypes.h> 27 #include <utils/Log.h> 28 #include <utils/StrongPointer.h> 29 #include <media/hardware/CryptoAPI.h> 30 #include <media/stagefright/foundation/ABuffer.h> 31 #include <media/stagefright/foundation/AMessage.h> 32 #include <media/stagefright/MetaData.h> 33 #include <media/stagefright/NuMediaExtractor.h> 34 #include <media/IMediaHTTPService.h> 35 #include <android_runtime/AndroidRuntime.h> 36 #include <android_util_Binder.h> 37 38 #include <jni.h> 39 40 using namespace android; 41 42 static media_status_t translate_error(status_t err) { 43 if (err == OK) { 44 return AMEDIA_OK; 45 } 46 ALOGE("sf error code: %d", err); 47 return AMEDIA_ERROR_UNKNOWN; 48 } 49 50 struct AMediaExtractor { 51 sp<NuMediaExtractor> mImpl; 52 sp<ABuffer> mPsshBuf; 53 54 }; 55 56 extern "C" { 57 58 EXPORT 59 AMediaExtractor* AMediaExtractor_new() { 60 ALOGV("ctor"); 61 AMediaExtractor *mData = new AMediaExtractor(); 62 mData->mImpl = new NuMediaExtractor(); 63 return mData; 64 } 65 66 EXPORT 67 media_status_t AMediaExtractor_delete(AMediaExtractor *mData) { 68 ALOGV("dtor"); 69 delete mData; 70 return AMEDIA_OK; 71 } 72 73 EXPORT 74 media_status_t AMediaExtractor_setDataSourceFd(AMediaExtractor *mData, int fd, off64_t offset, 75 off64_t length) { 76 ALOGV("setDataSource(%d, %" PRId64 ", %" PRId64 ")", fd, offset, length); 77 return translate_error(mData->mImpl->setDataSource(fd, offset, length)); 78 } 79 80 EXPORT 81 media_status_t AMediaExtractor_setDataSource(AMediaExtractor *mData, const char *location) { 82 ALOGV("setDataSource(%s)", location); 83 // TODO: add header support 84 85 JNIEnv *env = AndroidRuntime::getJNIEnv(); 86 jobject service = NULL; 87 if (env == NULL) { 88 ALOGE("setDataSource(path) must be called from Java thread"); 89 return AMEDIA_ERROR_UNSUPPORTED; 90 } 91 92 jclass mediahttpclass = env->FindClass("android/media/MediaHTTPService"); 93 if (mediahttpclass == NULL) { 94 ALOGE("can't find MediaHttpService"); 95 env->ExceptionClear(); 96 return AMEDIA_ERROR_UNSUPPORTED; 97 } 98 99 jmethodID mediaHttpCreateMethod = env->GetStaticMethodID(mediahttpclass, 100 "createHttpServiceBinderIfNecessary", "(Ljava/lang/String;)Landroid/os/IBinder;"); 101 if (mediaHttpCreateMethod == NULL) { 102 ALOGE("can't find method"); 103 env->ExceptionClear(); 104 return AMEDIA_ERROR_UNSUPPORTED; 105 } 106 107 jstring jloc = env->NewStringUTF(location); 108 109 service = env->CallStaticObjectMethod(mediahttpclass, mediaHttpCreateMethod, jloc); 110 env->DeleteLocalRef(jloc); 111 112 sp<IMediaHTTPService> httpService; 113 if (service != NULL) { 114 sp<IBinder> binder = ibinderForJavaObject(env, service); 115 httpService = interface_cast<IMediaHTTPService>(binder); 116 } 117 118 status_t err = mData->mImpl->setDataSource(httpService, location, NULL); 119 env->ExceptionClear(); 120 return translate_error(err); 121 } 122 123 EXPORT 124 size_t AMediaExtractor_getTrackCount(AMediaExtractor *mData) { 125 return mData->mImpl->countTracks(); 126 } 127 128 EXPORT 129 AMediaFormat* AMediaExtractor_getTrackFormat(AMediaExtractor *mData, size_t idx) { 130 sp<AMessage> format; 131 mData->mImpl->getTrackFormat(idx, &format); 132 return AMediaFormat_fromMsg(&format); 133 } 134 135 EXPORT 136 media_status_t AMediaExtractor_selectTrack(AMediaExtractor *mData, size_t idx) { 137 ALOGV("selectTrack(%zu)", idx); 138 return translate_error(mData->mImpl->selectTrack(idx)); 139 } 140 141 EXPORT 142 media_status_t AMediaExtractor_unselectTrack(AMediaExtractor *mData, size_t idx) { 143 ALOGV("unselectTrack(%zu)", idx); 144 return translate_error(mData->mImpl->unselectTrack(idx)); 145 } 146 147 EXPORT 148 bool AMediaExtractor_advance(AMediaExtractor *mData) { 149 //ALOGV("advance"); 150 status_t err = mData->mImpl->advance(); 151 if (err == ERROR_END_OF_STREAM) { 152 return false; 153 } else if (err != OK) { 154 ALOGE("sf error code: %d", err); 155 return false; 156 } 157 return true; 158 } 159 160 EXPORT 161 media_status_t AMediaExtractor_seekTo(AMediaExtractor *ex, int64_t seekPosUs, SeekMode mode) { 162 android::MediaSource::ReadOptions::SeekMode sfmode; 163 if (mode == AMEDIAEXTRACTOR_SEEK_PREVIOUS_SYNC) { 164 sfmode = android::MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC; 165 } else if (mode == AMEDIAEXTRACTOR_SEEK_CLOSEST_SYNC) { 166 sfmode = android::MediaSource::ReadOptions::SEEK_CLOSEST_SYNC; 167 } else { 168 sfmode = android::MediaSource::ReadOptions::SEEK_NEXT_SYNC; 169 } 170 171 return translate_error(ex->mImpl->seekTo(seekPosUs, sfmode)); 172 } 173 174 EXPORT 175 ssize_t AMediaExtractor_readSampleData(AMediaExtractor *mData, uint8_t *buffer, size_t capacity) { 176 //ALOGV("readSampleData"); 177 sp<ABuffer> tmp = new ABuffer(buffer, capacity); 178 if (mData->mImpl->readSampleData(tmp) == OK) { 179 return tmp->size(); 180 } 181 return -1; 182 } 183 184 EXPORT 185 uint32_t AMediaExtractor_getSampleFlags(AMediaExtractor *mData) { 186 int sampleFlags = 0; 187 sp<MetaData> meta; 188 status_t err = mData->mImpl->getSampleMeta(&meta); 189 if (err != OK) { 190 return -1; 191 } 192 int32_t val; 193 if (meta->findInt32(kKeyIsSyncFrame, &val) && val != 0) { 194 sampleFlags |= AMEDIAEXTRACTOR_SAMPLE_FLAG_SYNC; 195 } 196 197 uint32_t type; 198 const void *data; 199 size_t size; 200 if (meta->findData(kKeyEncryptedSizes, &type, &data, &size)) { 201 sampleFlags |= AMEDIAEXTRACTOR_SAMPLE_FLAG_ENCRYPTED; 202 } 203 return sampleFlags; 204 } 205 206 EXPORT 207 int AMediaExtractor_getSampleTrackIndex(AMediaExtractor *mData) { 208 size_t idx; 209 if (mData->mImpl->getSampleTrackIndex(&idx) != OK) { 210 return -1; 211 } 212 return idx; 213 } 214 215 EXPORT 216 int64_t AMediaExtractor_getSampleTime(AMediaExtractor *mData) { 217 int64_t time; 218 if (mData->mImpl->getSampleTime(&time) != OK) { 219 return -1; 220 } 221 return time; 222 } 223 224 EXPORT 225 PsshInfo* AMediaExtractor_getPsshInfo(AMediaExtractor *ex) { 226 227 if (ex->mPsshBuf != NULL) { 228 return (PsshInfo*) ex->mPsshBuf->data(); 229 } 230 231 sp<AMessage> format; 232 ex->mImpl->getFileFormat(&format); 233 sp<ABuffer> buffer; 234 if(!format->findBuffer("pssh", &buffer)) { 235 return NULL; 236 } 237 238 // the format of the buffer is 1 or more of: 239 // { 240 // 16 byte uuid 241 // 4 byte data length N 242 // N bytes of data 243 // } 244 245 // Determine the number of entries in the source data. 246 // Since we got the data from stagefright, we trust it is valid and properly formatted. 247 const uint8_t* data = buffer->data(); 248 size_t len = buffer->size(); 249 size_t numentries = 0; 250 while (len > 0) { 251 numentries++; 252 253 if (len < 16) { 254 ALOGE("invalid PSSH data"); 255 return NULL; 256 } 257 // skip uuid 258 data += 16; 259 len -= 16; 260 261 // get data length 262 if (len < 4) { 263 ALOGE("invalid PSSH data"); 264 return NULL; 265 } 266 uint32_t datalen = *((uint32_t*)data); 267 data += 4; 268 len -= 4; 269 270 if (len < datalen) { 271 ALOGE("invalid PSSH data"); 272 return NULL; 273 } 274 // skip the data 275 data += datalen; 276 len -= datalen; 277 } 278 279 // there are <numentries> in the source buffer, we need 280 // (source buffer size) - (sizeof(uint32_t) * numentries) + sizeof(size_t) 281 // + ((sizeof(void*) + sizeof(size_t)) * numentries) bytes for the PsshInfo structure 282 // Or in other words, the data lengths in the source structure are replaced by size_t 283 // (which may be the same size or larger, for 64 bit), and in addition there is an 284 // extra pointer for each entry, and an extra size_t for the entire PsshInfo. 285 size_t newsize = buffer->size() - (sizeof(uint32_t) * numentries) + sizeof(size_t) 286 + ((sizeof(void*) + sizeof(size_t)) * numentries); 287 if (newsize <= buffer->size()) { 288 ALOGE("invalid PSSH data"); 289 return NULL; 290 } 291 ex->mPsshBuf = new ABuffer(newsize); 292 ex->mPsshBuf->setRange(0, newsize); 293 294 // copy data 295 const uint8_t* src = buffer->data(); 296 uint8_t* dst = ex->mPsshBuf->data(); 297 uint8_t* dstdata = dst + sizeof(size_t) + numentries * sizeof(PsshEntry); 298 *((size_t*)dst) = numentries; 299 dst += sizeof(size_t); 300 for (size_t i = 0; i < numentries; i++) { 301 // copy uuid 302 memcpy(dst, src, 16); 303 src += 16; 304 dst += 16; 305 306 // get/copy data length 307 uint32_t datalen = *((uint32_t*)src); 308 *((size_t*)dst) = datalen; 309 src += sizeof(uint32_t); 310 dst += sizeof(size_t); 311 312 // the next entry in the destination is a pointer to the actual data, which we store 313 // after the array of PsshEntry 314 *((void**)dst) = dstdata; 315 dst += sizeof(void*); 316 317 // copy the actual data 318 memcpy(dstdata, src, datalen); 319 dstdata += datalen; 320 src += datalen; 321 } 322 323 return (PsshInfo*) ex->mPsshBuf->data(); 324 } 325 326 EXPORT 327 AMediaCodecCryptoInfo *AMediaExtractor_getSampleCryptoInfo(AMediaExtractor *ex) { 328 sp<MetaData> meta; 329 if(ex->mImpl->getSampleMeta(&meta) != 0) { 330 return NULL; 331 } 332 333 uint32_t type; 334 const void *crypteddata; 335 size_t cryptedsize; 336 if (!meta->findData(kKeyEncryptedSizes, &type, &crypteddata, &cryptedsize)) { 337 return NULL; 338 } 339 size_t numSubSamples = cryptedsize / sizeof(size_t); 340 341 const void *cleardata; 342 size_t clearsize; 343 if (meta->findData(kKeyPlainSizes, &type, &cleardata, &clearsize)) { 344 if (clearsize != cryptedsize) { 345 // The two must be of the same length. 346 return NULL; 347 } 348 } 349 350 const void *key; 351 size_t keysize; 352 if (meta->findData(kKeyCryptoKey, &type, &key, &keysize)) { 353 if (keysize != 16) { 354 // Keys must be 16 bytes in length. 355 return NULL; 356 } 357 } 358 359 const void *iv; 360 size_t ivsize; 361 if (meta->findData(kKeyCryptoIV, &type, &iv, &ivsize)) { 362 if (ivsize != 16) { 363 // IVs must be 16 bytes in length. 364 return NULL; 365 } 366 } 367 368 int32_t mode; 369 if (!meta->findInt32(kKeyCryptoMode, &mode)) { 370 mode = CryptoPlugin::kMode_AES_CTR; 371 } 372 373 return AMediaCodecCryptoInfo_new( 374 numSubSamples, 375 (uint8_t*) key, 376 (uint8_t*) iv, 377 (cryptoinfo_mode_t) mode, 378 (size_t*) cleardata, 379 (size_t*) crypteddata); 380 } 381 382 383 } // extern "C" 384 385