android_media_MediaExtractor.cpp revision 74a78b0f6e8c07cfc7da8f043987f6de0648bc05
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
47////////////////////////////////////////////////////////////////////////////////
48
49JMediaExtractor::JMediaExtractor(JNIEnv *env, jobject thiz)
50    : mClass(NULL),
51      mObject(NULL) {
52    jclass clazz = env->GetObjectClass(thiz);
53    CHECK(clazz != NULL);
54
55    mClass = (jclass)env->NewGlobalRef(clazz);
56    mObject = env->NewWeakGlobalRef(thiz);
57
58    mImpl = new NuMediaExtractor;
59}
60
61JMediaExtractor::~JMediaExtractor() {
62    JNIEnv *env = AndroidRuntime::getJNIEnv();
63
64    env->DeleteWeakGlobalRef(mObject);
65    mObject = NULL;
66    env->DeleteGlobalRef(mClass);
67    mClass = NULL;
68}
69
70status_t JMediaExtractor::setDataSource(
71        const char *path, const KeyedVector<String8, String8> *headers) {
72    return mImpl->setDataSource(path, headers);
73}
74
75status_t JMediaExtractor::setDataSource(int fd, off64_t offset, off64_t size) {
76    return mImpl->setDataSource(fd, offset, size);
77}
78
79size_t JMediaExtractor::countTracks() const {
80    return mImpl->countTracks();
81}
82
83status_t JMediaExtractor::getTrackFormat(size_t index, jobject *format) const {
84    sp<AMessage> msg;
85    status_t err;
86    if ((err = mImpl->getTrackFormat(index, &msg)) != OK) {
87        return err;
88    }
89
90    JNIEnv *env = AndroidRuntime::getJNIEnv();
91
92    return ConvertMessageToMap(env, msg, format);
93}
94
95status_t JMediaExtractor::selectTrack(size_t index) {
96    return mImpl->selectTrack(index);
97}
98
99status_t JMediaExtractor::seekTo(int64_t timeUs) {
100    return mImpl->seekTo(timeUs);
101}
102
103status_t JMediaExtractor::advance() {
104    return mImpl->advance();
105}
106
107status_t JMediaExtractor::readSampleData(
108        jobject byteBuf, size_t offset, size_t *sampleSize) {
109    JNIEnv *env = AndroidRuntime::getJNIEnv();
110
111    void *dst = env->GetDirectBufferAddress(byteBuf);
112
113    jlong dstSize;
114    jbyteArray byteArray = NULL;
115
116    if (dst == NULL) {
117        jclass byteBufClass = env->FindClass("java/nio/ByteBuffer");
118        CHECK(byteBufClass != NULL);
119
120        jmethodID arrayID =
121            env->GetMethodID(byteBufClass, "array", "()[B");
122        CHECK(arrayID != NULL);
123
124        byteArray =
125            (jbyteArray)env->CallObjectMethod(byteBuf, arrayID);
126
127        if (byteArray == NULL) {
128            return INVALID_OPERATION;
129        }
130
131        jboolean isCopy;
132        dst = env->GetByteArrayElements(byteArray, &isCopy);
133
134        dstSize = env->GetArrayLength(byteArray);
135    } else {
136        dstSize = env->GetDirectBufferCapacity(byteBuf);
137    }
138
139    if (dstSize < offset) {
140        if (byteArray != NULL) {
141            env->ReleaseByteArrayElements(byteArray, (jbyte *)dst, 0);
142        }
143
144        return -ERANGE;
145    }
146
147    sp<ABuffer> buffer = new ABuffer((char *)dst + offset, dstSize - offset);
148
149    status_t err = mImpl->readSampleData(buffer);
150
151    if (byteArray != NULL) {
152        env->ReleaseByteArrayElements(byteArray, (jbyte *)dst, 0);
153    }
154
155    if (err != OK) {
156        return err;
157    }
158
159    *sampleSize = buffer->size();
160
161    return OK;
162}
163
164status_t JMediaExtractor::getSampleTrackIndex(size_t *trackIndex) {
165    return mImpl->getSampleTrackIndex(trackIndex);
166}
167
168status_t JMediaExtractor::getSampleTime(int64_t *sampleTimeUs) {
169    return mImpl->getSampleTime(sampleTimeUs);
170}
171
172status_t JMediaExtractor::getSampleFlags(uint32_t *sampleFlags) {
173    *sampleFlags = 0;
174
175    sp<MetaData> meta;
176    status_t err = mImpl->getSampleMeta(&meta);
177
178    if (err != OK) {
179        return err;
180    }
181
182    int32_t val;
183    if (meta->findInt32(kKeyIsSyncFrame, &val) && val != 0) {
184        (*sampleFlags) |= NuMediaExtractor::SAMPLE_FLAG_SYNC;
185    }
186
187    uint32_t type;
188    const void *data;
189    size_t size;
190    if (meta->findData(kKeyEncryptedSizes, &type, &data, &size)) {
191        (*sampleFlags) |= NuMediaExtractor::SAMPLE_FLAG_ENCRYPTED;
192    }
193
194    return OK;
195}
196
197status_t JMediaExtractor::getSampleMeta(sp<MetaData> *sampleMeta) {
198    return mImpl->getSampleMeta(sampleMeta);
199}
200
201bool JMediaExtractor::getCachedDuration(int64_t *durationUs, bool *eos) const {
202    return mImpl->getCachedDuration(durationUs, eos);
203}
204
205}  // namespace android
206
207////////////////////////////////////////////////////////////////////////////////
208
209using namespace android;
210
211static sp<JMediaExtractor> setMediaExtractor(
212        JNIEnv *env, jobject thiz, const sp<JMediaExtractor> &extractor) {
213    sp<JMediaExtractor> old =
214        (JMediaExtractor *)env->GetIntField(thiz, gFields.context);
215
216    if (extractor != NULL) {
217        extractor->incStrong(thiz);
218    }
219    if (old != NULL) {
220        old->decStrong(thiz);
221    }
222    env->SetIntField(thiz, gFields.context, (int)extractor.get());
223
224    return old;
225}
226
227static sp<JMediaExtractor> getMediaExtractor(JNIEnv *env, jobject thiz) {
228    return (JMediaExtractor *)env->GetIntField(thiz, gFields.context);
229}
230
231static void android_media_MediaExtractor_release(JNIEnv *env, jobject thiz) {
232    setMediaExtractor(env, thiz, NULL);
233}
234
235static jint android_media_MediaExtractor_countTracks(
236        JNIEnv *env, jobject thiz) {
237    sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
238
239    if (extractor == NULL) {
240        jniThrowException(env, "java/lang/IllegalStateException", NULL);
241        return -1;
242    }
243
244    return extractor->countTracks();
245}
246
247static jobject android_media_MediaExtractor_getTrackFormat(
248        JNIEnv *env, jobject thiz, jint index) {
249    sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
250
251    if (extractor == NULL) {
252        jniThrowException(env, "java/lang/IllegalStateException", NULL);
253        return NULL;
254    }
255
256    jobject format;
257    status_t err = extractor->getTrackFormat(index, &format);
258
259    if (err != OK) {
260        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
261        return NULL;
262    }
263
264    return format;
265}
266
267static void android_media_MediaExtractor_selectTrack(
268        JNIEnv *env, jobject thiz, jint index) {
269    sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
270
271    if (extractor == NULL) {
272        jniThrowException(env, "java/lang/IllegalStateException", NULL);
273        return;
274    }
275
276    status_t err = extractor->selectTrack(index);
277
278    if (err != OK) {
279        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
280        return;
281    }
282}
283
284static void android_media_MediaExtractor_seekTo(
285        JNIEnv *env, jobject thiz, jlong timeUs) {
286    sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
287
288    if (extractor == NULL) {
289        jniThrowException(env, "java/lang/IllegalStateException", NULL);
290        return;
291    }
292
293    extractor->seekTo(timeUs);
294}
295
296static jboolean android_media_MediaExtractor_advance(
297        JNIEnv *env, jobject thiz) {
298    sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
299
300    if (extractor == NULL) {
301        jniThrowException(env, "java/lang/IllegalStateException", NULL);
302        return false;
303    }
304
305    status_t err = extractor->advance();
306
307    if (err == ERROR_END_OF_STREAM) {
308        return false;
309    } else if (err != OK) {
310        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
311        return false;
312    }
313
314    return true;
315}
316
317static jint android_media_MediaExtractor_readSampleData(
318        JNIEnv *env, jobject thiz, jobject byteBuf, jint offset) {
319    sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
320
321    if (extractor == NULL) {
322        jniThrowException(env, "java/lang/IllegalStateException", NULL);
323        return -1;
324    }
325
326    size_t sampleSize;
327    status_t err = extractor->readSampleData(byteBuf, offset, &sampleSize);
328
329    if (err == ERROR_END_OF_STREAM) {
330        return -1;
331    } else if (err != OK) {
332        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
333        return false;
334    }
335
336    return sampleSize;
337}
338
339static jint android_media_MediaExtractor_getSampleTrackIndex(
340        JNIEnv *env, jobject thiz) {
341    sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
342
343    if (extractor == NULL) {
344        jniThrowException(env, "java/lang/IllegalStateException", NULL);
345        return -1;
346    }
347
348    size_t trackIndex;
349    status_t err = extractor->getSampleTrackIndex(&trackIndex);
350
351    if (err == ERROR_END_OF_STREAM) {
352        return -1;
353    } else if (err != OK) {
354        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
355        return false;
356    }
357
358    return trackIndex;
359}
360
361static jlong android_media_MediaExtractor_getSampleTime(
362        JNIEnv *env, jobject thiz) {
363    sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
364
365    if (extractor == NULL) {
366        jniThrowException(env, "java/lang/IllegalStateException", NULL);
367        return -1ll;
368    }
369
370    int64_t sampleTimeUs;
371    status_t err = extractor->getSampleTime(&sampleTimeUs);
372
373    if (err == ERROR_END_OF_STREAM) {
374        return -1ll;
375    } else if (err != OK) {
376        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
377        return false;
378    }
379
380    return sampleTimeUs;
381}
382
383static jint android_media_MediaExtractor_getSampleFlags(
384        JNIEnv *env, jobject thiz) {
385    sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
386
387    if (extractor == NULL) {
388        jniThrowException(env, "java/lang/IllegalStateException", NULL);
389        return -1ll;
390    }
391
392    uint32_t sampleFlags;
393    status_t err = extractor->getSampleFlags(&sampleFlags);
394
395    if (err == ERROR_END_OF_STREAM) {
396        return -1ll;
397    } else if (err != OK) {
398        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
399        return false;
400    }
401
402    return sampleFlags;
403}
404
405static jboolean android_media_MediaExtractor_getSampleCryptoInfo(
406        JNIEnv *env, jobject thiz, jobject cryptoInfoObj) {
407    sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
408
409    if (extractor == NULL) {
410        jniThrowException(env, "java/lang/IllegalStateException", NULL);
411        return -1ll;
412    }
413
414    sp<MetaData> meta;
415    status_t err = extractor->getSampleMeta(&meta);
416
417    if (err != OK) {
418        return false;
419    }
420
421    uint32_t type;
422    const void *data;
423    size_t size;
424    if (!meta->findData(kKeyEncryptedSizes, &type, &data, &size)) {
425        return false;
426    }
427
428    size_t numSubSamples = size / sizeof(size_t);
429
430    if (numSubSamples == 0) {
431        return false;
432    }
433
434    jintArray numBytesOfEncryptedDataObj = env->NewIntArray(numSubSamples);
435    jboolean isCopy;
436    jint *dst = env->GetIntArrayElements(numBytesOfEncryptedDataObj, &isCopy);
437    for (size_t i = 0; i < numSubSamples; ++i) {
438        dst[i] = ((const size_t *)data)[i];
439    }
440    env->ReleaseIntArrayElements(numBytesOfEncryptedDataObj, dst, 0);
441    dst = NULL;
442
443    size_t encSize = size;
444    jintArray numBytesOfPlainDataObj = NULL;
445    if (meta->findData(kKeyPlainSizes, &type, &data, &size)) {
446        if (size != encSize) {
447            // The two must be of the same length.
448            return false;
449        }
450
451        numBytesOfPlainDataObj = env->NewIntArray(numSubSamples);
452        jboolean isCopy;
453        jint *dst = env->GetIntArrayElements(numBytesOfPlainDataObj, &isCopy);
454        for (size_t i = 0; i < numSubSamples; ++i) {
455            dst[i] = ((const size_t *)data)[i];
456        }
457        env->ReleaseIntArrayElements(numBytesOfPlainDataObj, dst, 0);
458        dst = NULL;
459    }
460
461    jbyteArray keyObj = NULL;
462    if (meta->findData(kKeyCryptoKey, &type, &data, &size)) {
463        if (size != 16) {
464            // Keys must be 16 bytes in length.
465            return false;
466        }
467
468        keyObj = env->NewByteArray(size);
469        jboolean isCopy;
470        jbyte *dst = env->GetByteArrayElements(keyObj, &isCopy);
471        memcpy(dst, data, size);
472        env->ReleaseByteArrayElements(keyObj, dst, 0);
473        dst = NULL;
474    }
475
476    jbyteArray ivObj = NULL;
477    if (meta->findData(kKeyCryptoIV, &type, &data, &size)) {
478        if (size != 16) {
479            // IVs must be 16 bytes in length.
480            return false;
481        }
482
483        ivObj = env->NewByteArray(size);
484        jboolean isCopy;
485        jbyte *dst = env->GetByteArrayElements(ivObj, &isCopy);
486        memcpy(dst, data, size);
487        env->ReleaseByteArrayElements(ivObj, dst, 0);
488        dst = NULL;
489    }
490
491    int32_t mode;
492    if (!meta->findInt32(kKeyCryptoMode, &mode)) {
493        mode = CryptoPlugin::kMode_AES_CTR;
494    }
495
496    env->CallVoidMethod(
497            cryptoInfoObj,
498            gFields.cryptoInfoSetID,
499            numSubSamples,
500            numBytesOfPlainDataObj,
501            numBytesOfEncryptedDataObj,
502            keyObj,
503            ivObj,
504            mode);
505
506    return true;
507}
508
509static void android_media_MediaExtractor_native_init(JNIEnv *env) {
510    jclass clazz = env->FindClass("android/media/MediaExtractor");
511    CHECK(clazz != NULL);
512
513    gFields.context = env->GetFieldID(clazz, "mNativeContext", "I");
514    CHECK(gFields.context != NULL);
515
516    clazz = env->FindClass("android/media/MediaCodec$CryptoInfo");
517    CHECK(clazz != NULL);
518
519    gFields.cryptoInfoSetID =
520        env->GetMethodID(clazz, "set", "(I[I[I[B[BI)V");
521
522    DataSource::RegisterDefaultSniffers();
523}
524
525static void android_media_MediaExtractor_native_setup(
526        JNIEnv *env, jobject thiz) {
527    sp<JMediaExtractor> extractor = new JMediaExtractor(env, thiz);
528    setMediaExtractor(env,thiz, extractor);
529}
530
531static void android_media_MediaExtractor_setDataSource(
532        JNIEnv *env, jobject thiz,
533        jstring pathObj, jobjectArray keysArray, jobjectArray valuesArray) {
534    sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
535
536    if (extractor == NULL) {
537        jniThrowException(env, "java/lang/IllegalStateException", NULL);
538        return;
539    }
540
541    if (pathObj == NULL) {
542        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
543        return;
544    }
545
546    KeyedVector<String8, String8> headers;
547    if (!ConvertKeyValueArraysToKeyedVector(
548                env, keysArray, valuesArray, &headers)) {
549        return;
550    }
551
552    const char *path = env->GetStringUTFChars(pathObj, NULL);
553
554    if (path == NULL) {
555        return;
556    }
557
558    status_t err = extractor->setDataSource(path, &headers);
559
560    env->ReleaseStringUTFChars(pathObj, path);
561    path = NULL;
562
563    if (err != OK) {
564        jniThrowException(
565                env,
566                "java/io/IOException",
567                "Failed to instantiate extractor.");
568        return;
569    }
570}
571
572static void android_media_MediaExtractor_setDataSourceFd(
573        JNIEnv *env, jobject thiz,
574        jobject fileDescObj, jlong offset, jlong length) {
575    sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
576
577    if (extractor == NULL) {
578        jniThrowException(env, "java/lang/IllegalStateException", NULL);
579        return;
580    }
581
582    if (fileDescObj == NULL) {
583        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
584        return;
585    }
586
587    int fd = jniGetFDFromFileDescriptor(env, fileDescObj);
588
589    status_t err = extractor->setDataSource(fd, offset, length);
590
591    if (err != OK) {
592        jniThrowException(
593                env,
594                "java/io/IOException",
595                "Failed to instantiate extractor.");
596        return;
597    }
598}
599
600static jlong android_media_MediaExtractor_getCachedDurationUs(
601        JNIEnv *env, jobject thiz) {
602    sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
603
604    if (extractor == NULL) {
605        jniThrowException(env, "java/lang/IllegalStateException", NULL);
606        return -1ll;
607    }
608
609    int64_t cachedDurationUs;
610    bool eos;
611    if (!extractor->getCachedDuration(&cachedDurationUs, &eos)) {
612        return -1ll;
613    }
614
615    return cachedDurationUs;
616}
617
618static jboolean android_media_MediaExtractor_hasCacheReachedEOS(
619        JNIEnv *env, jobject thiz) {
620    sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
621
622    if (extractor == NULL) {
623        jniThrowException(env, "java/lang/IllegalStateException", NULL);
624        return true;
625    }
626
627    int64_t cachedDurationUs;
628    bool eos;
629    if (!extractor->getCachedDuration(&cachedDurationUs, &eos)) {
630        return true;
631    }
632
633    return eos;
634}
635
636static void android_media_MediaExtractor_native_finalize(
637        JNIEnv *env, jobject thiz) {
638    android_media_MediaExtractor_release(env, thiz);
639}
640
641static JNINativeMethod gMethods[] = {
642    { "release", "()V", (void *)android_media_MediaExtractor_release },
643
644    { "countTracks", "()I", (void *)android_media_MediaExtractor_countTracks },
645
646    { "getTrackFormat", "(I)Ljava/util/Map;",
647        (void *)android_media_MediaExtractor_getTrackFormat },
648
649    { "selectTrack", "(I)V", (void *)android_media_MediaExtractor_selectTrack },
650
651    { "seekTo", "(J)V", (void *)android_media_MediaExtractor_seekTo },
652
653    { "advance", "()Z", (void *)android_media_MediaExtractor_advance },
654
655    { "readSampleData", "(Ljava/nio/ByteBuffer;I)I",
656        (void *)android_media_MediaExtractor_readSampleData },
657
658    { "getSampleTrackIndex", "()I",
659        (void *)android_media_MediaExtractor_getSampleTrackIndex },
660
661    { "getSampleTime", "()J",
662        (void *)android_media_MediaExtractor_getSampleTime },
663
664    { "getSampleFlags", "()I",
665        (void *)android_media_MediaExtractor_getSampleFlags },
666
667    { "getSampleCryptoInfo", "(Landroid/media/MediaCodec$CryptoInfo;)Z",
668        (void *)android_media_MediaExtractor_getSampleCryptoInfo },
669
670    { "native_init", "()V", (void *)android_media_MediaExtractor_native_init },
671
672    { "native_setup", "()V",
673      (void *)android_media_MediaExtractor_native_setup },
674
675    { "native_finalize", "()V",
676      (void *)android_media_MediaExtractor_native_finalize },
677
678    { "setDataSource", "(Ljava/lang/String;[Ljava/lang/String;"
679                       "[Ljava/lang/String;)V",
680      (void *)android_media_MediaExtractor_setDataSource },
681
682    { "setDataSource", "(Ljava/io/FileDescriptor;JJ)V",
683      (void *)android_media_MediaExtractor_setDataSourceFd },
684
685    { "getCachedDuration", "()J",
686      (void *)android_media_MediaExtractor_getCachedDurationUs },
687
688    { "hasCacheReachedEndOfStream", "()Z",
689      (void *)android_media_MediaExtractor_hasCacheReachedEOS },
690};
691
692int register_android_media_MediaExtractor(JNIEnv *env) {
693    return AndroidRuntime::registerNativeMethods(env,
694                "android/media/MediaExtractor", gMethods, NELEM(gMethods));
695}
696