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