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