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