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