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