Home | History | Annotate | Download | only in jni
      1 /*
      2  * Copyright 2012, 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 "MediaExtractor-JNI"
     19 #include <utils/Log.h>
     20 
     21 #include "android_media_MediaExtractor.h"
     22 
     23 #include "android_media_Utils.h"
     24 #include "android_runtime/AndroidRuntime.h"
     25 #include "android_runtime/Log.h"
     26 #include "jni.h"
     27 #include "JNIHelp.h"
     28 
     29 #include <media/IMediaHTTPService.h>
     30 #include <media/hardware/CryptoAPI.h>
     31 #include <media/stagefright/foundation/ABuffer.h>
     32 #include <media/stagefright/foundation/ADebug.h>
     33 #include <media/stagefright/foundation/AMessage.h>
     34 #include <media/stagefright/DataSource.h>
     35 #include <media/stagefright/MediaErrors.h>
     36 #include <media/stagefright/MetaData.h>
     37 #include <media/stagefright/NuMediaExtractor.h>
     38 
     39 #include <nativehelper/ScopedLocalRef.h>
     40 
     41 #include "android_util_Binder.h"
     42 
     43 namespace android {
     44 
     45 struct fields_t {
     46     jfieldID context;
     47 
     48     jmethodID cryptoInfoSetID;
     49 };
     50 
     51 static fields_t gFields;
     52 
     53 class JavaDataSourceBridge : public DataSource {
     54     jmethodID mReadMethod;
     55     jmethodID mGetSizeMethod;
     56     jmethodID mCloseMethod;
     57     jobject   mDataSource;
     58  public:
     59     JavaDataSourceBridge(JNIEnv *env, jobject source) {
     60         mDataSource = env->NewGlobalRef(source);
     61 
     62         jclass datasourceclass = env->GetObjectClass(mDataSource);
     63         CHECK(datasourceclass != NULL);
     64 
     65         mReadMethod = env->GetMethodID(datasourceclass, "readAt", "(J[BI)I");
     66         CHECK(mReadMethod != NULL);
     67 
     68         mGetSizeMethod = env->GetMethodID(datasourceclass, "getSize", "()J");
     69         CHECK(mGetSizeMethod != NULL);
     70 
     71         mCloseMethod = env->GetMethodID(datasourceclass, "close", "()V");
     72         CHECK(mCloseMethod != NULL);
     73     }
     74 
     75     ~JavaDataSourceBridge() {
     76         JNIEnv *env = AndroidRuntime::getJNIEnv();
     77         env->CallVoidMethod(mDataSource, mCloseMethod);
     78         env->DeleteGlobalRef(mDataSource);
     79     }
     80 
     81     virtual status_t initCheck() const {
     82         return OK;
     83     }
     84 
     85     virtual ssize_t readAt(off64_t offset, void* buffer, size_t size) {
     86         JNIEnv *env = AndroidRuntime::getJNIEnv();
     87 
     88         // XXX could optimize this by reusing the same array
     89         jbyteArray byteArrayObj = env->NewByteArray(size);
     90         env->DeleteLocalRef(env->GetObjectClass(mDataSource));
     91         env->DeleteLocalRef(env->GetObjectClass(byteArrayObj));
     92         ssize_t numread = env->CallIntMethod(mDataSource, mReadMethod, offset, byteArrayObj, (jint)size);
     93         env->GetByteArrayRegion(byteArrayObj, 0, size, (jbyte*) buffer);
     94         env->DeleteLocalRef(byteArrayObj);
     95         if (env->ExceptionCheck()) {
     96             ALOGW("Exception occurred while reading %zu at %lld", size, offset);
     97             LOGW_EX(env);
     98             env->ExceptionClear();
     99             return -1;
    100         }
    101         return numread;
    102     }
    103 
    104     virtual status_t getSize(off64_t *size) {
    105         JNIEnv *env = AndroidRuntime::getJNIEnv();
    106 
    107         CHECK(size != NULL);
    108 
    109         int64_t len = env->CallLongMethod(mDataSource, mGetSizeMethod);
    110         if (len < 0) {
    111             *size = ERROR_UNSUPPORTED;
    112         } else {
    113             *size = len;
    114         }
    115         return OK;
    116     }
    117 };
    118 
    119 ////////////////////////////////////////////////////////////////////////////////
    120 
    121 JMediaExtractor::JMediaExtractor(JNIEnv *env, jobject thiz)
    122     : mClass(NULL),
    123       mObject(NULL) {
    124     jclass clazz = env->GetObjectClass(thiz);
    125     CHECK(clazz != NULL);
    126 
    127     mClass = (jclass)env->NewGlobalRef(clazz);
    128     mObject = env->NewWeakGlobalRef(thiz);
    129 
    130     mImpl = new NuMediaExtractor;
    131 }
    132 
    133 JMediaExtractor::~JMediaExtractor() {
    134     JNIEnv *env = AndroidRuntime::getJNIEnv();
    135 
    136     env->DeleteWeakGlobalRef(mObject);
    137     mObject = NULL;
    138     env->DeleteGlobalRef(mClass);
    139     mClass = NULL;
    140 }
    141 
    142 status_t JMediaExtractor::setDataSource(
    143         const sp<IMediaHTTPService> &httpService,
    144         const char *path,
    145         const KeyedVector<String8, String8> *headers) {
    146     return mImpl->setDataSource(httpService, path, headers);
    147 }
    148 
    149 status_t JMediaExtractor::setDataSource(int fd, off64_t offset, off64_t size) {
    150     return mImpl->setDataSource(fd, offset, size);
    151 }
    152 
    153 status_t JMediaExtractor::setDataSource(const sp<DataSource> &datasource) {
    154     return mImpl->setDataSource(datasource);
    155 }
    156 
    157 size_t JMediaExtractor::countTracks() const {
    158     return mImpl->countTracks();
    159 }
    160 
    161 status_t JMediaExtractor::getTrackFormat(size_t index, jobject *format) const {
    162     sp<AMessage> msg;
    163     status_t err;
    164     if ((err = mImpl->getTrackFormat(index, &msg)) != OK) {
    165         return err;
    166     }
    167 
    168     JNIEnv *env = AndroidRuntime::getJNIEnv();
    169 
    170     return ConvertMessageToMap(env, msg, format);
    171 }
    172 
    173 status_t JMediaExtractor::getFileFormat(jobject *format) const {
    174     sp<AMessage> msg;
    175     status_t err;
    176     if ((err = mImpl->getFileFormat(&msg)) != OK) {
    177         return err;
    178     }
    179 
    180     JNIEnv *env = AndroidRuntime::getJNIEnv();
    181 
    182     return ConvertMessageToMap(env, msg, format);
    183 }
    184 
    185 status_t JMediaExtractor::selectTrack(size_t index) {
    186     return mImpl->selectTrack(index);
    187 }
    188 
    189 status_t JMediaExtractor::unselectTrack(size_t index) {
    190     return mImpl->unselectTrack(index);
    191 }
    192 
    193 status_t JMediaExtractor::seekTo(
    194         int64_t timeUs, MediaSource::ReadOptions::SeekMode mode) {
    195     return mImpl->seekTo(timeUs, mode);
    196 }
    197 
    198 status_t JMediaExtractor::advance() {
    199     return mImpl->advance();
    200 }
    201 
    202 status_t JMediaExtractor::readSampleData(
    203         jobject byteBuf, size_t offset, size_t *sampleSize) {
    204     JNIEnv *env = AndroidRuntime::getJNIEnv();
    205 
    206     void *dst = env->GetDirectBufferAddress(byteBuf);
    207 
    208     size_t dstSize;
    209     jbyteArray byteArray = NULL;
    210 
    211     ScopedLocalRef<jclass> byteBufClass(env, env->FindClass("java/nio/ByteBuffer"));
    212     CHECK(byteBufClass.get() != NULL);
    213 
    214     if (dst == NULL) {
    215         jmethodID arrayID =
    216             env->GetMethodID(byteBufClass.get(), "array", "()[B");
    217         CHECK(arrayID != NULL);
    218 
    219         byteArray =
    220             (jbyteArray)env->CallObjectMethod(byteBuf, arrayID);
    221 
    222         if (byteArray == NULL) {
    223             return INVALID_OPERATION;
    224         }
    225 
    226         jboolean isCopy;
    227         dst = env->GetByteArrayElements(byteArray, &isCopy);
    228 
    229         dstSize = (size_t) env->GetArrayLength(byteArray);
    230     } else {
    231         dstSize = (size_t) env->GetDirectBufferCapacity(byteBuf);
    232     }
    233 
    234     if (dstSize < offset) {
    235         if (byteArray != NULL) {
    236             env->ReleaseByteArrayElements(byteArray, (jbyte *)dst, 0);
    237         }
    238 
    239         return -ERANGE;
    240     }
    241 
    242     sp<ABuffer> buffer = new ABuffer((char *)dst + offset, dstSize - offset);
    243 
    244     status_t err = mImpl->readSampleData(buffer);
    245 
    246     if (byteArray != NULL) {
    247         env->ReleaseByteArrayElements(byteArray, (jbyte *)dst, 0);
    248     }
    249 
    250     if (err != OK) {
    251         return err;
    252     }
    253 
    254     *sampleSize = buffer->size();
    255 
    256     jmethodID positionID = env->GetMethodID(
    257             byteBufClass.get(), "position", "(I)Ljava/nio/Buffer;");
    258 
    259     CHECK(positionID != NULL);
    260 
    261     jmethodID limitID = env->GetMethodID(
    262             byteBufClass.get(), "limit", "(I)Ljava/nio/Buffer;");
    263 
    264     CHECK(limitID != NULL);
    265 
    266     jobject me = env->CallObjectMethod(
    267             byteBuf, limitID, offset + *sampleSize);
    268     env->DeleteLocalRef(me);
    269     me = env->CallObjectMethod(
    270             byteBuf, positionID, offset);
    271     env->DeleteLocalRef(me);
    272     me = NULL;
    273 
    274     return OK;
    275 }
    276 
    277 status_t JMediaExtractor::getSampleTrackIndex(size_t *trackIndex) {
    278     return mImpl->getSampleTrackIndex(trackIndex);
    279 }
    280 
    281 status_t JMediaExtractor::getSampleTime(int64_t *sampleTimeUs) {
    282     return mImpl->getSampleTime(sampleTimeUs);
    283 }
    284 
    285 status_t JMediaExtractor::getSampleFlags(uint32_t *sampleFlags) {
    286     *sampleFlags = 0;
    287 
    288     sp<MetaData> meta;
    289     status_t err = mImpl->getSampleMeta(&meta);
    290 
    291     if (err != OK) {
    292         return err;
    293     }
    294 
    295     int32_t val;
    296     if (meta->findInt32(kKeyIsSyncFrame, &val) && val != 0) {
    297         (*sampleFlags) |= NuMediaExtractor::SAMPLE_FLAG_SYNC;
    298     }
    299 
    300     uint32_t type;
    301     const void *data;
    302     size_t size;
    303     if (meta->findData(kKeyEncryptedSizes, &type, &data, &size)) {
    304         (*sampleFlags) |= NuMediaExtractor::SAMPLE_FLAG_ENCRYPTED;
    305     }
    306 
    307     return OK;
    308 }
    309 
    310 status_t JMediaExtractor::getSampleMeta(sp<MetaData> *sampleMeta) {
    311     return mImpl->getSampleMeta(sampleMeta);
    312 }
    313 
    314 bool JMediaExtractor::getCachedDuration(int64_t *durationUs, bool *eos) const {
    315     return mImpl->getCachedDuration(durationUs, eos);
    316 }
    317 
    318 }  // namespace android
    319 
    320 ////////////////////////////////////////////////////////////////////////////////
    321 
    322 using namespace android;
    323 
    324 static sp<JMediaExtractor> setMediaExtractor(
    325         JNIEnv *env, jobject thiz, const sp<JMediaExtractor> &extractor) {
    326     sp<JMediaExtractor> old =
    327         (JMediaExtractor *)env->GetLongField(thiz, gFields.context);
    328 
    329     if (extractor != NULL) {
    330         extractor->incStrong(thiz);
    331     }
    332     if (old != NULL) {
    333         old->decStrong(thiz);
    334     }
    335     env->SetLongField(thiz, gFields.context, (jlong)extractor.get());
    336 
    337     return old;
    338 }
    339 
    340 static sp<JMediaExtractor> getMediaExtractor(JNIEnv *env, jobject thiz) {
    341     return (JMediaExtractor *)env->GetLongField(thiz, gFields.context);
    342 }
    343 
    344 static void android_media_MediaExtractor_release(JNIEnv *env, jobject thiz) {
    345     setMediaExtractor(env, thiz, NULL);
    346 }
    347 
    348 static jint android_media_MediaExtractor_getTrackCount(
    349         JNIEnv *env, jobject thiz) {
    350     sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
    351 
    352     if (extractor == NULL) {
    353         jniThrowException(env, "java/lang/IllegalStateException", NULL);
    354         return -1;
    355     }
    356 
    357     return (jint) extractor->countTracks();
    358 }
    359 
    360 static jobject android_media_MediaExtractor_getTrackFormatNative(
    361         JNIEnv *env, jobject thiz, jint index) {
    362     sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
    363 
    364     if (extractor == NULL) {
    365         jniThrowException(env, "java/lang/IllegalStateException", NULL);
    366         return NULL;
    367     }
    368 
    369     jobject format;
    370     status_t err = extractor->getTrackFormat(index, &format);
    371 
    372     if (err != OK) {
    373         jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
    374         return NULL;
    375     }
    376 
    377     return format;
    378 }
    379 
    380 static jobject android_media_MediaExtractor_getFileFormatNative(
    381         JNIEnv *env, jobject thiz) {
    382     sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
    383 
    384     if (extractor == NULL) {
    385         jniThrowException(env, "java/lang/IllegalStateException", NULL);
    386         return NULL;
    387     }
    388 
    389     jobject format;
    390     status_t err = extractor->getFileFormat(&format);
    391 
    392     if (err != OK) {
    393         jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
    394         return NULL;
    395     }
    396 
    397     return format;
    398 }
    399 
    400 static void android_media_MediaExtractor_selectTrack(
    401         JNIEnv *env, jobject thiz, jint index) {
    402     sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
    403 
    404     if (extractor == NULL) {
    405         jniThrowException(env, "java/lang/IllegalStateException", NULL);
    406         return;
    407     }
    408 
    409     status_t err = extractor->selectTrack(index);
    410 
    411     if (err != OK) {
    412         jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
    413         return;
    414     }
    415 }
    416 
    417 static void android_media_MediaExtractor_unselectTrack(
    418         JNIEnv *env, jobject thiz, jint index) {
    419     sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
    420 
    421     if (extractor == NULL) {
    422         jniThrowException(env, "java/lang/IllegalStateException", NULL);
    423         return;
    424     }
    425 
    426     status_t err = extractor->unselectTrack(index);
    427 
    428     if (err != OK) {
    429         jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
    430         return;
    431     }
    432 }
    433 
    434 static void android_media_MediaExtractor_seekTo(
    435         JNIEnv *env, jobject thiz, jlong timeUs, jint mode) {
    436     sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
    437 
    438     if (extractor == NULL) {
    439         jniThrowException(env, "java/lang/IllegalStateException", NULL);
    440         return;
    441     }
    442 
    443     if (mode < MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC
    444             || mode >= MediaSource::ReadOptions::SEEK_CLOSEST) {
    445         jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
    446         return;
    447     }
    448 
    449     extractor->seekTo(timeUs, (MediaSource::ReadOptions::SeekMode)mode);
    450 }
    451 
    452 static jboolean android_media_MediaExtractor_advance(
    453         JNIEnv *env, jobject thiz) {
    454     sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
    455 
    456     if (extractor == NULL) {
    457         jniThrowException(env, "java/lang/IllegalStateException", NULL);
    458         return JNI_FALSE;
    459     }
    460 
    461     status_t err = extractor->advance();
    462 
    463     if (err == ERROR_END_OF_STREAM) {
    464         return JNI_FALSE;
    465     } else if (err != OK) {
    466         jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
    467         return JNI_FALSE;
    468     }
    469 
    470     return JNI_TRUE;
    471 }
    472 
    473 static jint android_media_MediaExtractor_readSampleData(
    474         JNIEnv *env, jobject thiz, jobject byteBuf, jint offset) {
    475     sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
    476 
    477     if (extractor == NULL) {
    478         jniThrowException(env, "java/lang/IllegalStateException", NULL);
    479         return -1;
    480     }
    481 
    482     size_t sampleSize;
    483     status_t err = extractor->readSampleData(byteBuf, offset, &sampleSize);
    484 
    485     if (err == ERROR_END_OF_STREAM) {
    486         return -1;
    487     } else if (err != OK) {
    488         jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
    489         return -1;
    490     }
    491 
    492     return (jint) sampleSize;
    493 }
    494 
    495 static jint android_media_MediaExtractor_getSampleTrackIndex(
    496         JNIEnv *env, jobject thiz) {
    497     sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
    498 
    499     if (extractor == NULL) {
    500         jniThrowException(env, "java/lang/IllegalStateException", NULL);
    501         return -1;
    502     }
    503 
    504     size_t trackIndex;
    505     status_t err = extractor->getSampleTrackIndex(&trackIndex);
    506 
    507     if (err == ERROR_END_OF_STREAM) {
    508         return -1;
    509     } else if (err != OK) {
    510         jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
    511         return -1;
    512     }
    513 
    514     return (jint) trackIndex;
    515 }
    516 
    517 static jlong android_media_MediaExtractor_getSampleTime(
    518         JNIEnv *env, jobject thiz) {
    519     sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
    520 
    521     if (extractor == NULL) {
    522         jniThrowException(env, "java/lang/IllegalStateException", NULL);
    523         return -1ll;
    524     }
    525 
    526     int64_t sampleTimeUs;
    527     status_t err = extractor->getSampleTime(&sampleTimeUs);
    528 
    529     if (err == ERROR_END_OF_STREAM) {
    530         return -1ll;
    531     } else if (err != OK) {
    532         jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
    533         return -1ll;
    534     }
    535 
    536     return (jlong) sampleTimeUs;
    537 }
    538 
    539 static jint android_media_MediaExtractor_getSampleFlags(
    540         JNIEnv *env, jobject thiz) {
    541     sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
    542 
    543     if (extractor == NULL) {
    544         jniThrowException(env, "java/lang/IllegalStateException", NULL);
    545         return -1;
    546     }
    547 
    548     uint32_t sampleFlags;
    549     status_t err = extractor->getSampleFlags(&sampleFlags);
    550 
    551     if (err == ERROR_END_OF_STREAM) {
    552         return -1;
    553     } else if (err != OK) {
    554         jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
    555         return -1;
    556     }
    557 
    558     return (jint) sampleFlags;
    559 }
    560 
    561 static jboolean android_media_MediaExtractor_getSampleCryptoInfo(
    562         JNIEnv *env, jobject thiz, jobject cryptoInfoObj) {
    563     sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
    564 
    565     if (extractor == NULL) {
    566         jniThrowException(env, "java/lang/IllegalStateException", NULL);
    567         return JNI_FALSE;
    568     }
    569 
    570     sp<MetaData> meta;
    571     status_t err = extractor->getSampleMeta(&meta);
    572 
    573     if (err != OK) {
    574         return JNI_FALSE;
    575     }
    576 
    577     uint32_t type;
    578     const void *data;
    579     size_t size;
    580     if (!meta->findData(kKeyEncryptedSizes, &type, &data, &size)) {
    581         return JNI_FALSE;
    582     }
    583 
    584     size_t numSubSamples = size / sizeof(int32_t);
    585 
    586     if (numSubSamples == 0) {
    587         return JNI_FALSE;
    588     }
    589 
    590     jintArray numBytesOfEncryptedDataObj = env->NewIntArray(numSubSamples);
    591     jboolean isCopy;
    592     jint *dst = env->GetIntArrayElements(numBytesOfEncryptedDataObj, &isCopy);
    593     for (size_t i = 0; i < numSubSamples; ++i) {
    594         dst[i] = ((const int32_t *)data)[i];
    595     }
    596     env->ReleaseIntArrayElements(numBytesOfEncryptedDataObj, dst, 0);
    597     dst = NULL;
    598 
    599     size_t encSize = size;
    600     jintArray numBytesOfPlainDataObj = NULL;
    601     if (meta->findData(kKeyPlainSizes, &type, &data, &size)) {
    602         if (size != encSize) {
    603             // The two must be of the same length.
    604             return JNI_FALSE;
    605         }
    606 
    607         numBytesOfPlainDataObj = env->NewIntArray(numSubSamples);
    608         jboolean isCopy;
    609         jint *dst = env->GetIntArrayElements(numBytesOfPlainDataObj, &isCopy);
    610         for (size_t i = 0; i < numSubSamples; ++i) {
    611             dst[i] = ((const int32_t *)data)[i];
    612         }
    613         env->ReleaseIntArrayElements(numBytesOfPlainDataObj, dst, 0);
    614         dst = NULL;
    615     }
    616 
    617     jbyteArray keyObj = NULL;
    618     if (meta->findData(kKeyCryptoKey, &type, &data, &size)) {
    619         if (size != 16) {
    620             // Keys must be 16 bytes in length.
    621             return JNI_FALSE;
    622         }
    623 
    624         keyObj = env->NewByteArray(size);
    625         jboolean isCopy;
    626         jbyte *dst = env->GetByteArrayElements(keyObj, &isCopy);
    627         memcpy(dst, data, size);
    628         env->ReleaseByteArrayElements(keyObj, dst, 0);
    629         dst = NULL;
    630     }
    631 
    632     jbyteArray ivObj = NULL;
    633     if (meta->findData(kKeyCryptoIV, &type, &data, &size)) {
    634         if (size != 16) {
    635             // IVs must be 16 bytes in length.
    636             return JNI_FALSE;
    637         }
    638 
    639         ivObj = env->NewByteArray(size);
    640         jboolean isCopy;
    641         jbyte *dst = env->GetByteArrayElements(ivObj, &isCopy);
    642         memcpy(dst, data, size);
    643         env->ReleaseByteArrayElements(ivObj, dst, 0);
    644         dst = NULL;
    645     }
    646 
    647     int32_t mode;
    648     if (!meta->findInt32(kKeyCryptoMode, &mode)) {
    649         mode = CryptoPlugin::kMode_AES_CTR;
    650     }
    651 
    652     env->CallVoidMethod(
    653             cryptoInfoObj,
    654             gFields.cryptoInfoSetID,
    655             (jint)numSubSamples,
    656             numBytesOfPlainDataObj,
    657             numBytesOfEncryptedDataObj,
    658             keyObj,
    659             ivObj,
    660             mode);
    661 
    662     return JNI_TRUE;
    663 }
    664 
    665 static void android_media_MediaExtractor_native_init(JNIEnv *env) {
    666     jclass clazz = env->FindClass("android/media/MediaExtractor");
    667     CHECK(clazz != NULL);
    668 
    669     gFields.context = env->GetFieldID(clazz, "mNativeContext", "J");
    670     CHECK(gFields.context != NULL);
    671 
    672     clazz = env->FindClass("android/media/MediaCodec$CryptoInfo");
    673     CHECK(clazz != NULL);
    674 
    675     gFields.cryptoInfoSetID =
    676         env->GetMethodID(clazz, "set", "(I[I[I[B[BI)V");
    677 
    678     DataSource::RegisterDefaultSniffers();
    679 }
    680 
    681 static void android_media_MediaExtractor_native_setup(
    682         JNIEnv *env, jobject thiz) {
    683     sp<JMediaExtractor> extractor = new JMediaExtractor(env, thiz);
    684     setMediaExtractor(env,thiz, extractor);
    685 }
    686 
    687 static void android_media_MediaExtractor_setDataSource(
    688         JNIEnv *env, jobject thiz,
    689         jobject httpServiceBinderObj,
    690         jstring pathObj,
    691         jobjectArray keysArray,
    692         jobjectArray valuesArray) {
    693     sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
    694 
    695     if (extractor == NULL) {
    696         jniThrowException(env, "java/lang/IllegalStateException", NULL);
    697         return;
    698     }
    699 
    700     if (pathObj == NULL) {
    701         jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
    702         return;
    703     }
    704 
    705     KeyedVector<String8, String8> headers;
    706     if (!ConvertKeyValueArraysToKeyedVector(
    707                 env, keysArray, valuesArray, &headers)) {
    708         return;
    709     }
    710 
    711     const char *path = env->GetStringUTFChars(pathObj, NULL);
    712 
    713     if (path == NULL) {
    714         return;
    715     }
    716 
    717     sp<IMediaHTTPService> httpService;
    718     if (httpServiceBinderObj != NULL) {
    719         sp<IBinder> binder = ibinderForJavaObject(env, httpServiceBinderObj);
    720         httpService = interface_cast<IMediaHTTPService>(binder);
    721     }
    722 
    723     status_t err = extractor->setDataSource(httpService, path, &headers);
    724 
    725     env->ReleaseStringUTFChars(pathObj, path);
    726     path = NULL;
    727 
    728     if (err != OK) {
    729         jniThrowException(
    730                 env,
    731                 "java/io/IOException",
    732                 "Failed to instantiate extractor.");
    733         return;
    734     }
    735 }
    736 
    737 static void android_media_MediaExtractor_setDataSourceFd(
    738         JNIEnv *env, jobject thiz,
    739         jobject fileDescObj, jlong offset, jlong length) {
    740     sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
    741 
    742     if (extractor == NULL) {
    743         jniThrowException(env, "java/lang/IllegalStateException", NULL);
    744         return;
    745     }
    746 
    747     if (fileDescObj == NULL) {
    748         jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
    749         return;
    750     }
    751 
    752     int fd = jniGetFDFromFileDescriptor(env, fileDescObj);
    753 
    754     status_t err = extractor->setDataSource(fd, offset, length);
    755 
    756     if (err != OK) {
    757         jniThrowException(
    758                 env,
    759                 "java/io/IOException",
    760                 "Failed to instantiate extractor.");
    761         return;
    762     }
    763 }
    764 
    765 static void android_media_MediaExtractor_setDataSourceCallback(
    766         JNIEnv *env, jobject thiz,
    767         jobject callbackObj) {
    768     sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
    769 
    770     if (extractor == NULL) {
    771         jniThrowException(env, "java/lang/IllegalStateException", NULL);
    772         return;
    773     }
    774 
    775     if (callbackObj == NULL) {
    776         jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
    777         return;
    778     }
    779 
    780     sp<JavaDataSourceBridge> bridge = new JavaDataSourceBridge(env, callbackObj);
    781     status_t err = extractor->setDataSource(bridge);
    782 
    783     if (err != OK) {
    784         jniThrowException(
    785                 env,
    786                 "java/io/IOException",
    787                 "Failed to instantiate extractor.");
    788         return;
    789     }
    790 }
    791 
    792 static jlong android_media_MediaExtractor_getCachedDurationUs(
    793         JNIEnv *env, jobject thiz) {
    794     sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
    795 
    796     if (extractor == NULL) {
    797         jniThrowException(env, "java/lang/IllegalStateException", NULL);
    798         return -1ll;
    799     }
    800 
    801     int64_t cachedDurationUs;
    802     bool eos;
    803     if (!extractor->getCachedDuration(&cachedDurationUs, &eos)) {
    804         return -1ll;
    805     }
    806 
    807     return (jlong) cachedDurationUs;
    808 }
    809 
    810 static jboolean android_media_MediaExtractor_hasCacheReachedEOS(
    811         JNIEnv *env, jobject thiz) {
    812     sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
    813 
    814     if (extractor == NULL) {
    815         jniThrowException(env, "java/lang/IllegalStateException", NULL);
    816         return JNI_TRUE;
    817     }
    818 
    819     int64_t cachedDurationUs;
    820     bool eos;
    821     if (!extractor->getCachedDuration(&cachedDurationUs, &eos)) {
    822         return JNI_TRUE;
    823     }
    824 
    825     return eos ? JNI_TRUE : JNI_FALSE;
    826 }
    827 
    828 static void android_media_MediaExtractor_native_finalize(
    829         JNIEnv *env, jobject thiz) {
    830     android_media_MediaExtractor_release(env, thiz);
    831 }
    832 
    833 static JNINativeMethod gMethods[] = {
    834     { "release", "()V", (void *)android_media_MediaExtractor_release },
    835 
    836     { "getTrackCount", "()I", (void *)android_media_MediaExtractor_getTrackCount },
    837 
    838     { "getFileFormatNative", "()Ljava/util/Map;",
    839         (void *)android_media_MediaExtractor_getFileFormatNative },
    840 
    841     { "getTrackFormatNative", "(I)Ljava/util/Map;",
    842         (void *)android_media_MediaExtractor_getTrackFormatNative },
    843 
    844     { "selectTrack", "(I)V", (void *)android_media_MediaExtractor_selectTrack },
    845 
    846     { "unselectTrack", "(I)V",
    847         (void *)android_media_MediaExtractor_unselectTrack },
    848 
    849     { "seekTo", "(JI)V", (void *)android_media_MediaExtractor_seekTo },
    850 
    851     { "advance", "()Z", (void *)android_media_MediaExtractor_advance },
    852 
    853     { "readSampleData", "(Ljava/nio/ByteBuffer;I)I",
    854         (void *)android_media_MediaExtractor_readSampleData },
    855 
    856     { "getSampleTrackIndex", "()I",
    857         (void *)android_media_MediaExtractor_getSampleTrackIndex },
    858 
    859     { "getSampleTime", "()J",
    860         (void *)android_media_MediaExtractor_getSampleTime },
    861 
    862     { "getSampleFlags", "()I",
    863         (void *)android_media_MediaExtractor_getSampleFlags },
    864 
    865     { "getSampleCryptoInfo", "(Landroid/media/MediaCodec$CryptoInfo;)Z",
    866         (void *)android_media_MediaExtractor_getSampleCryptoInfo },
    867 
    868     { "native_init", "()V", (void *)android_media_MediaExtractor_native_init },
    869 
    870     { "native_setup", "()V",
    871       (void *)android_media_MediaExtractor_native_setup },
    872 
    873     { "native_finalize", "()V",
    874       (void *)android_media_MediaExtractor_native_finalize },
    875 
    876     { "nativeSetDataSource",
    877         "(Landroid/os/IBinder;Ljava/lang/String;[Ljava/lang/String;"
    878         "[Ljava/lang/String;)V",
    879       (void *)android_media_MediaExtractor_setDataSource },
    880 
    881     { "setDataSource", "(Ljava/io/FileDescriptor;JJ)V",
    882       (void *)android_media_MediaExtractor_setDataSourceFd },
    883 
    884     { "setDataSource", "(Landroid/media/DataSource;)V",
    885       (void *)android_media_MediaExtractor_setDataSourceCallback },
    886 
    887     { "getCachedDuration", "()J",
    888       (void *)android_media_MediaExtractor_getCachedDurationUs },
    889 
    890     { "hasCacheReachedEndOfStream", "()Z",
    891       (void *)android_media_MediaExtractor_hasCacheReachedEOS },
    892 };
    893 
    894 int register_android_media_MediaExtractor(JNIEnv *env) {
    895     return AndroidRuntime::registerNativeMethods(env,
    896                 "android/media/MediaExtractor", gMethods, NELEM(gMethods));
    897 }
    898