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