android_media_MediaExtractor.cpp revision 136c08a7d3678e9fcf22667e75b0904a94add6bb
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
38namespace android {
39
40struct fields_t {
41    jfieldID context;
42
43    jmethodID cryptoInfoSetID;
44};
45
46static fields_t gFields;
47
48class 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, (jint)size);
88        env->GetByteArrayRegion(byteArrayObj, 0, size, (jbyte*) buffer);
89        env->DeleteLocalRef(byteArrayObj);
90        if (env->ExceptionCheck()) {
91            ALOGW("Exception occurred while reading %zu 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
116JMediaExtractor::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
128JMediaExtractor::~JMediaExtractor() {
129    JNIEnv *env = AndroidRuntime::getJNIEnv();
130
131    env->DeleteWeakGlobalRef(mObject);
132    mObject = NULL;
133    env->DeleteGlobalRef(mClass);
134    mClass = NULL;
135}
136
137status_t JMediaExtractor::setDataSource(
138        const char *path, const KeyedVector<String8, String8> *headers) {
139    return mImpl->setDataSource(path, headers);
140}
141
142status_t JMediaExtractor::setDataSource(int fd, off64_t offset, off64_t size) {
143    return mImpl->setDataSource(fd, offset, size);
144}
145
146status_t JMediaExtractor::setDataSource(const sp<DataSource> &datasource) {
147    return mImpl->setDataSource(datasource);
148}
149
150size_t JMediaExtractor::countTracks() const {
151    return mImpl->countTracks();
152}
153
154status_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
166status_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
178status_t JMediaExtractor::selectTrack(size_t index) {
179    return mImpl->selectTrack(index);
180}
181
182status_t JMediaExtractor::unselectTrack(size_t index) {
183    return mImpl->unselectTrack(index);
184}
185
186status_t JMediaExtractor::seekTo(
187        int64_t timeUs, MediaSource::ReadOptions::SeekMode mode) {
188    return mImpl->seekTo(timeUs, mode);
189}
190
191status_t JMediaExtractor::advance() {
192    return mImpl->advance();
193}
194
195status_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    size_t 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 = (size_t) env->GetArrayLength(byteArray);
223    } else {
224        dstSize = (size_t) 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
252status_t JMediaExtractor::getSampleTrackIndex(size_t *trackIndex) {
253    return mImpl->getSampleTrackIndex(trackIndex);
254}
255
256status_t JMediaExtractor::getSampleTime(int64_t *sampleTimeUs) {
257    return mImpl->getSampleTime(sampleTimeUs);
258}
259
260status_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
285status_t JMediaExtractor::getSampleMeta(sp<MetaData> *sampleMeta) {
286    return mImpl->getSampleMeta(sampleMeta);
287}
288
289bool JMediaExtractor::getCachedDuration(int64_t *durationUs, bool *eos) const {
290    return mImpl->getCachedDuration(durationUs, eos);
291}
292
293}  // namespace android
294
295////////////////////////////////////////////////////////////////////////////////
296
297using namespace android;
298
299static sp<JMediaExtractor> setMediaExtractor(
300        JNIEnv *env, jobject thiz, const sp<JMediaExtractor> &extractor) {
301    sp<JMediaExtractor> old =
302        (JMediaExtractor *)env->GetLongField(thiz, gFields.context);
303
304    if (extractor != NULL) {
305        extractor->incStrong(thiz);
306    }
307    if (old != NULL) {
308        old->decStrong(thiz);
309    }
310    env->SetLongField(thiz, gFields.context, (jlong)extractor.get());
311
312    return old;
313}
314
315static sp<JMediaExtractor> getMediaExtractor(JNIEnv *env, jobject thiz) {
316    return (JMediaExtractor *)env->GetLongField(thiz, gFields.context);
317}
318
319static void android_media_MediaExtractor_release(JNIEnv *env, jobject thiz) {
320    setMediaExtractor(env, thiz, NULL);
321}
322
323static 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 (jint) extractor->countTracks();
333}
334
335static 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
355static 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
375static 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
392static 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
409static 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
427static 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 JNI_FALSE;
434    }
435
436    status_t err = extractor->advance();
437
438    if (err == ERROR_END_OF_STREAM) {
439        return JNI_FALSE;
440    } else if (err != OK) {
441        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
442        return JNI_FALSE;
443    }
444
445    return JNI_TRUE;
446}
447
448static 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 -1;
465    }
466
467    return (jint) sampleSize;
468}
469
470static 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 -1;
487    }
488
489    return (jint) trackIndex;
490}
491
492static 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 -1ll;
509    }
510
511    return (jlong) sampleTimeUs;
512}
513
514static 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 -1;
521    }
522
523    uint32_t sampleFlags;
524    status_t err = extractor->getSampleFlags(&sampleFlags);
525
526    if (err == ERROR_END_OF_STREAM) {
527        return -1;
528    } else if (err != OK) {
529        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
530        return -1;
531    }
532
533    return (jint) sampleFlags;
534}
535
536static 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 JNI_FALSE;
543    }
544
545    sp<MetaData> meta;
546    status_t err = extractor->getSampleMeta(&meta);
547
548    if (err != OK) {
549        return JNI_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 JNI_FALSE;
557    }
558
559    size_t numSubSamples = size / sizeof(int32_t);
560
561    if (numSubSamples == 0) {
562        return JNI_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 int32_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 JNI_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 int32_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 JNI_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 JNI_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            (jint)numSubSamples,
631            numBytesOfPlainDataObj,
632            numBytesOfEncryptedDataObj,
633            keyObj,
634            ivObj,
635            mode);
636
637    return JNI_TRUE;
638}
639
640static 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", "J");
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
656static 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
662static 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
703static 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
731static 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
758static 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 (jlong) cachedDurationUs;
774}
775
776static 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 JNI_TRUE;
783    }
784
785    int64_t cachedDurationUs;
786    bool eos;
787    if (!extractor->getCachedDuration(&cachedDurationUs, &eos)) {
788        return JNI_TRUE;
789    }
790
791    return eos ? JNI_TRUE : JNI_FALSE;
792}
793
794static void android_media_MediaExtractor_native_finalize(
795        JNIEnv *env, jobject thiz) {
796    android_media_MediaExtractor_release(env, thiz);
797}
798
799static 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
859int register_android_media_MediaExtractor(JNIEnv *env) {
860    return AndroidRuntime::registerNativeMethods(env,
861                "android/media/MediaExtractor", gMethods, NELEM(gMethods));
862}
863