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