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