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 env->ExceptionClear(); 90 return AMEDIA_ERROR_UNSUPPORTED; 91 } 92 93 jclass mediahttpclass = env->FindClass("android/media/MediaHTTPService"); 94 if (mediahttpclass == NULL) { 95 ALOGE("can't find MediaHttpService"); 96 env->ExceptionClear(); 97 return AMEDIA_ERROR_UNSUPPORTED; 98 } 99 100 jmethodID mediaHttpCreateMethod = env->GetStaticMethodID(mediahttpclass, 101 "createHttpServiceBinderIfNecessary", "(Ljava/lang/String;)Landroid/os/IBinder;"); 102 if (mediaHttpCreateMethod == NULL) { 103 ALOGE("can't find method"); 104 env->ExceptionClear(); 105 return AMEDIA_ERROR_UNSUPPORTED; 106 } 107 108 jstring jloc = env->NewStringUTF(location); 109 110 service = env->CallStaticObjectMethod(mediahttpclass, mediaHttpCreateMethod, jloc); 111 env->DeleteLocalRef(jloc); 112 113 sp<IMediaHTTPService> httpService; 114 if (service != NULL) { 115 sp<IBinder> binder = ibinderForJavaObject(env, service); 116 httpService = interface_cast<IMediaHTTPService>(binder); 117 } 118 119 status_t err = mData->mImpl->setDataSource(httpService, location, NULL); 120 env->ExceptionClear(); 121 return translate_error(err); 122 } 123 124 EXPORT 125 size_t AMediaExtractor_getTrackCount(AMediaExtractor *mData) { 126 return mData->mImpl->countTracks(); 127 } 128 129 EXPORT 130 AMediaFormat* AMediaExtractor_getTrackFormat(AMediaExtractor *mData, size_t idx) { 131 sp<AMessage> format; 132 mData->mImpl->getTrackFormat(idx, &format); 133 return AMediaFormat_fromMsg(&format); 134 } 135 136 EXPORT 137 media_status_t AMediaExtractor_selectTrack(AMediaExtractor *mData, size_t idx) { 138 ALOGV("selectTrack(%zu)", idx); 139 return translate_error(mData->mImpl->selectTrack(idx)); 140 } 141 142 EXPORT 143 media_status_t AMediaExtractor_unselectTrack(AMediaExtractor *mData, size_t idx) { 144 ALOGV("unselectTrack(%zu)", idx); 145 return translate_error(mData->mImpl->unselectTrack(idx)); 146 } 147 148 EXPORT 149 bool AMediaExtractor_advance(AMediaExtractor *mData) { 150 //ALOGV("advance"); 151 return mData->mImpl->advance(); 152 } 153 154 EXPORT 155 media_status_t AMediaExtractor_seekTo(AMediaExtractor *ex, int64_t seekPosUs, SeekMode mode) { 156 android::MediaSource::ReadOptions::SeekMode sfmode; 157 if (mode == AMEDIAEXTRACTOR_SEEK_PREVIOUS_SYNC) { 158 sfmode = android::MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC; 159 } else if (mode == AMEDIAEXTRACTOR_SEEK_CLOSEST_SYNC) { 160 sfmode = android::MediaSource::ReadOptions::SEEK_CLOSEST_SYNC; 161 } else { 162 sfmode = android::MediaSource::ReadOptions::SEEK_NEXT_SYNC; 163 } 164 165 return translate_error(ex->mImpl->seekTo(seekPosUs, sfmode)); 166 } 167 168 EXPORT 169 ssize_t AMediaExtractor_readSampleData(AMediaExtractor *mData, uint8_t *buffer, size_t capacity) { 170 //ALOGV("readSampleData"); 171 sp<ABuffer> tmp = new ABuffer(buffer, capacity); 172 if (mData->mImpl->readSampleData(tmp) == OK) { 173 return tmp->size(); 174 } 175 return -1; 176 } 177 178 EXPORT 179 uint32_t AMediaExtractor_getSampleFlags(AMediaExtractor *mData) { 180 int sampleFlags = 0; 181 sp<MetaData> meta; 182 status_t err = mData->mImpl->getSampleMeta(&meta); 183 if (err != OK) { 184 return -1; 185 } 186 int32_t val; 187 if (meta->findInt32(kKeyIsSyncFrame, &val) && val != 0) { 188 sampleFlags |= AMEDIAEXTRACTOR_SAMPLE_FLAG_SYNC; 189 } 190 191 uint32_t type; 192 const void *data; 193 size_t size; 194 if (meta->findData(kKeyEncryptedSizes, &type, &data, &size)) { 195 sampleFlags |= AMEDIAEXTRACTOR_SAMPLE_FLAG_ENCRYPTED; 196 } 197 return sampleFlags; 198 } 199 200 EXPORT 201 int AMediaExtractor_getSampleTrackIndex(AMediaExtractor *mData) { 202 size_t idx; 203 if (mData->mImpl->getSampleTrackIndex(&idx) != OK) { 204 return -1; 205 } 206 return idx; 207 } 208 209 EXPORT 210 int64_t AMediaExtractor_getSampleTime(AMediaExtractor *mData) { 211 int64_t time; 212 if (mData->mImpl->getSampleTime(&time) != OK) { 213 return -1; 214 } 215 return time; 216 } 217 218 EXPORT 219 PsshInfo* AMediaExtractor_getPsshInfo(AMediaExtractor *ex) { 220 221 if (ex->mPsshBuf != NULL) { 222 return (PsshInfo*) ex->mPsshBuf->data(); 223 } 224 225 sp<AMessage> format; 226 ex->mImpl->getFileFormat(&format); 227 sp<ABuffer> buffer; 228 if(!format->findBuffer("pssh", &buffer)) { 229 return NULL; 230 } 231 232 // the format of the buffer is 1 or more of: 233 // { 234 // 16 byte uuid 235 // 4 byte data length N 236 // N bytes of data 237 // } 238 239 // Determine the number of entries in the source data. 240 // Since we got the data from stagefright, we trust it is valid and properly formatted. 241 const uint8_t* data = buffer->data(); 242 size_t len = buffer->size(); 243 size_t numentries = 0; 244 while (len > 0) { 245 numentries++; 246 247 if (len < 16) { 248 ALOGE("invalid PSSH data"); 249 return NULL; 250 } 251 // skip uuid 252 data += 16; 253 len -= 16; 254 255 // get data length 256 if (len < 4) { 257 ALOGE("invalid PSSH data"); 258 return NULL; 259 } 260 uint32_t datalen = *((uint32_t*)data); 261 data += 4; 262 len -= 4; 263 264 if (len < datalen) { 265 ALOGE("invalid PSSH data"); 266 return NULL; 267 } 268 // skip the data 269 data += datalen; 270 len -= datalen; 271 } 272 273 // there are <numentries> in the source buffer, we need 274 // (source buffer size) - (sizeof(uint32_t) * numentries) + sizeof(size_t) 275 // + ((sizeof(void*) + sizeof(size_t)) * numentries) bytes for the PsshInfo structure 276 // Or in other words, the data lengths in the source structure are replaced by size_t 277 // (which may be the same size or larger, for 64 bit), and in addition there is an 278 // extra pointer for each entry, and an extra size_t for the entire PsshInfo. 279 size_t newsize = buffer->size() - (sizeof(uint32_t) * numentries) + sizeof(size_t) 280 + ((sizeof(void*) + sizeof(size_t)) * numentries); 281 if (newsize <= buffer->size()) { 282 ALOGE("invalid PSSH data"); 283 return NULL; 284 } 285 ex->mPsshBuf = new ABuffer(newsize); 286 ex->mPsshBuf->setRange(0, newsize); 287 288 // copy data 289 const uint8_t* src = buffer->data(); 290 uint8_t* dst = ex->mPsshBuf->data(); 291 uint8_t* dstdata = dst + sizeof(size_t) + numentries * sizeof(PsshEntry); 292 *((size_t*)dst) = numentries; 293 dst += sizeof(size_t); 294 for (size_t i = 0; i < numentries; i++) { 295 // copy uuid 296 memcpy(dst, src, 16); 297 src += 16; 298 dst += 16; 299 300 // get/copy data length 301 uint32_t datalen = *((uint32_t*)src); 302 *((size_t*)dst) = datalen; 303 src += sizeof(uint32_t); 304 dst += sizeof(size_t); 305 306 // the next entry in the destination is a pointer to the actual data, which we store 307 // after the array of PsshEntry 308 *((void**)dst) = dstdata; 309 dst += sizeof(void*); 310 311 // copy the actual data 312 memcpy(dstdata, src, datalen); 313 dstdata += datalen; 314 src += datalen; 315 } 316 317 return (PsshInfo*) ex->mPsshBuf->data(); 318 } 319 320 EXPORT 321 AMediaCodecCryptoInfo *AMediaExtractor_getSampleCryptoInfo(AMediaExtractor *ex) { 322 sp<MetaData> meta; 323 if(ex->mImpl->getSampleMeta(&meta) != 0) { 324 return NULL; 325 } 326 327 uint32_t type; 328 const void *crypteddata; 329 size_t cryptedsize; 330 if (!meta->findData(kKeyEncryptedSizes, &type, &crypteddata, &cryptedsize)) { 331 return NULL; 332 } 333 size_t numSubSamples = cryptedsize / sizeof(size_t); 334 335 const void *cleardata; 336 size_t clearsize; 337 if (meta->findData(kKeyPlainSizes, &type, &cleardata, &clearsize)) { 338 if (clearsize != cryptedsize) { 339 // The two must be of the same length. 340 return NULL; 341 } 342 } 343 344 const void *key; 345 size_t keysize; 346 if (meta->findData(kKeyCryptoIV, &type, &key, &keysize)) { 347 if (keysize != 16) { 348 // IVs must be 16 bytes in length. 349 return NULL; 350 } 351 } 352 353 const void *iv; 354 size_t ivsize; 355 if (meta->findData(kKeyCryptoIV, &type, &iv, &ivsize)) { 356 if (ivsize != 16) { 357 // IVs must be 16 bytes in length. 358 return NULL; 359 } 360 } 361 362 int32_t mode; 363 if (!meta->findInt32(kKeyCryptoMode, &mode)) { 364 mode = CryptoPlugin::kMode_AES_CTR; 365 } 366 367 return AMediaCodecCryptoInfo_new( 368 numSubSamples, 369 (uint8_t*) key, 370 (uint8_t*) iv, 371 (cryptoinfo_mode_t) mode, 372 (size_t*) cleardata, 373 (size_t*) crypteddata); 374 } 375 376 377 } // extern "C" 378 379