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