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