android_media_MediaExtractor.cpp revision ab57d03bdb3010d4957fb88c8acb30575ca02ec9
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
201}  // namespace android
202
203////////////////////////////////////////////////////////////////////////////////
204
205using namespace android;
206
207static sp<JMediaExtractor> setMediaExtractor(
208        JNIEnv *env, jobject thiz, const sp<JMediaExtractor> &extractor) {
209    sp<JMediaExtractor> old =
210        (JMediaExtractor *)env->GetIntField(thiz, gFields.context);
211
212    if (extractor != NULL) {
213        extractor->incStrong(thiz);
214    }
215    if (old != NULL) {
216        old->decStrong(thiz);
217    }
218    env->SetIntField(thiz, gFields.context, (int)extractor.get());
219
220    return old;
221}
222
223static sp<JMediaExtractor> getMediaExtractor(JNIEnv *env, jobject thiz) {
224    return (JMediaExtractor *)env->GetIntField(thiz, gFields.context);
225}
226
227static void android_media_MediaExtractor_release(JNIEnv *env, jobject thiz) {
228    setMediaExtractor(env, thiz, NULL);
229}
230
231static jint android_media_MediaExtractor_countTracks(
232        JNIEnv *env, jobject thiz) {
233    sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
234
235    if (extractor == NULL) {
236        jniThrowException(env, "java/lang/IllegalStateException", NULL);
237        return -1;
238    }
239
240    return extractor->countTracks();
241}
242
243static jobject android_media_MediaExtractor_getTrackFormat(
244        JNIEnv *env, jobject thiz, jint index) {
245    sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
246
247    if (extractor == NULL) {
248        jniThrowException(env, "java/lang/IllegalStateException", NULL);
249        return NULL;
250    }
251
252    jobject format;
253    status_t err = extractor->getTrackFormat(index, &format);
254
255    if (err != OK) {
256        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
257        return NULL;
258    }
259
260    return format;
261}
262
263static void android_media_MediaExtractor_selectTrack(
264        JNIEnv *env, jobject thiz, jint index) {
265    sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
266
267    if (extractor == NULL) {
268        jniThrowException(env, "java/lang/IllegalStateException", NULL);
269        return;
270    }
271
272    status_t err = extractor->selectTrack(index);
273
274    if (err != OK) {
275        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
276        return;
277    }
278}
279
280static void android_media_MediaExtractor_seekTo(
281        JNIEnv *env, jobject thiz, jlong timeUs) {
282    sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
283
284    if (extractor == NULL) {
285        jniThrowException(env, "java/lang/IllegalStateException", NULL);
286        return;
287    }
288
289    extractor->seekTo(timeUs);
290}
291
292static jboolean android_media_MediaExtractor_advance(
293        JNIEnv *env, jobject thiz) {
294    sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
295
296    if (extractor == NULL) {
297        jniThrowException(env, "java/lang/IllegalStateException", NULL);
298        return false;
299    }
300
301    status_t err = extractor->advance();
302
303    if (err == ERROR_END_OF_STREAM) {
304        return false;
305    } else if (err != OK) {
306        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
307        return false;
308    }
309
310    return true;
311}
312
313static jint android_media_MediaExtractor_readSampleData(
314        JNIEnv *env, jobject thiz, jobject byteBuf, jint offset) {
315    sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
316
317    if (extractor == NULL) {
318        jniThrowException(env, "java/lang/IllegalStateException", NULL);
319        return -1;
320    }
321
322    size_t sampleSize;
323    status_t err = extractor->readSampleData(byteBuf, offset, &sampleSize);
324
325    if (err == ERROR_END_OF_STREAM) {
326        return -1;
327    } else if (err != OK) {
328        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
329        return false;
330    }
331
332    return sampleSize;
333}
334
335static jint android_media_MediaExtractor_getSampleTrackIndex(
336        JNIEnv *env, jobject thiz) {
337    sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
338
339    if (extractor == NULL) {
340        jniThrowException(env, "java/lang/IllegalStateException", NULL);
341        return -1;
342    }
343
344    size_t trackIndex;
345    status_t err = extractor->getSampleTrackIndex(&trackIndex);
346
347    if (err == ERROR_END_OF_STREAM) {
348        return -1;
349    } else if (err != OK) {
350        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
351        return false;
352    }
353
354    return trackIndex;
355}
356
357static jlong android_media_MediaExtractor_getSampleTime(
358        JNIEnv *env, jobject thiz) {
359    sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
360
361    if (extractor == NULL) {
362        jniThrowException(env, "java/lang/IllegalStateException", NULL);
363        return -1ll;
364    }
365
366    int64_t sampleTimeUs;
367    status_t err = extractor->getSampleTime(&sampleTimeUs);
368
369    if (err == ERROR_END_OF_STREAM) {
370        return -1ll;
371    } else if (err != OK) {
372        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
373        return false;
374    }
375
376    return sampleTimeUs;
377}
378
379static jint android_media_MediaExtractor_getSampleFlags(
380        JNIEnv *env, jobject thiz) {
381    sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
382
383    if (extractor == NULL) {
384        jniThrowException(env, "java/lang/IllegalStateException", NULL);
385        return -1ll;
386    }
387
388    uint32_t sampleFlags;
389    status_t err = extractor->getSampleFlags(&sampleFlags);
390
391    if (err == ERROR_END_OF_STREAM) {
392        return -1ll;
393    } else if (err != OK) {
394        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
395        return false;
396    }
397
398    return sampleFlags;
399}
400
401static jboolean android_media_MediaExtractor_getSampleCryptoInfo(
402        JNIEnv *env, jobject thiz, jobject cryptoInfoObj) {
403    sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
404
405    if (extractor == NULL) {
406        jniThrowException(env, "java/lang/IllegalStateException", NULL);
407        return -1ll;
408    }
409
410    sp<MetaData> meta;
411    status_t err = extractor->getSampleMeta(&meta);
412
413    if (err != OK) {
414        return false;
415    }
416
417    uint32_t type;
418    const void *data;
419    size_t size;
420    if (!meta->findData(kKeyEncryptedSizes, &type, &data, &size)) {
421        return false;
422    }
423
424    size_t numSubSamples = size / sizeof(size_t);
425
426    if (numSubSamples == 0) {
427        return false;
428    }
429
430    jintArray numBytesOfEncryptedDataObj = env->NewIntArray(numSubSamples);
431    jboolean isCopy;
432    jint *dst = env->GetIntArrayElements(numBytesOfEncryptedDataObj, &isCopy);
433    for (size_t i = 0; i < numSubSamples; ++i) {
434        dst[i] = ((const size_t *)data)[i];
435    }
436    env->ReleaseIntArrayElements(numBytesOfEncryptedDataObj, dst, 0);
437    dst = NULL;
438
439    size_t encSize = size;
440    jintArray numBytesOfPlainDataObj = NULL;
441    if (meta->findData(kKeyPlainSizes, &type, &data, &size)) {
442        if (size != encSize) {
443            // The two must be of the same length.
444            return false;
445        }
446
447        numBytesOfPlainDataObj = env->NewIntArray(numSubSamples);
448        jboolean isCopy;
449        jint *dst = env->GetIntArrayElements(numBytesOfPlainDataObj, &isCopy);
450        for (size_t i = 0; i < numSubSamples; ++i) {
451            dst[i] = ((const size_t *)data)[i];
452        }
453        env->ReleaseIntArrayElements(numBytesOfPlainDataObj, dst, 0);
454        dst = NULL;
455    }
456
457    jbyteArray keyObj = NULL;
458    if (meta->findData(kKeyCryptoKey, &type, &data, &size)) {
459        if (size != 16) {
460            // Keys must be 16 bytes in length.
461            return false;
462        }
463
464        keyObj = env->NewByteArray(size);
465        jboolean isCopy;
466        jbyte *dst = env->GetByteArrayElements(keyObj, &isCopy);
467        memcpy(dst, data, size);
468        env->ReleaseByteArrayElements(keyObj, dst, 0);
469        dst = NULL;
470    }
471
472    jbyteArray ivObj = NULL;
473    if (meta->findData(kKeyCryptoIV, &type, &data, &size)) {
474        if (size != 16) {
475            // IVs must be 16 bytes in length.
476            return false;
477        }
478
479        ivObj = env->NewByteArray(size);
480        jboolean isCopy;
481        jbyte *dst = env->GetByteArrayElements(ivObj, &isCopy);
482        memcpy(dst, data, size);
483        env->ReleaseByteArrayElements(ivObj, dst, 0);
484        dst = NULL;
485    }
486
487    int32_t mode;
488    if (!meta->findInt32(kKeyCryptoMode, &mode)) {
489        mode = CryptoPlugin::kMode_AES_CTR;
490    }
491
492    env->CallVoidMethod(
493            cryptoInfoObj,
494            gFields.cryptoInfoSetID,
495            numSubSamples,
496            numBytesOfPlainDataObj,
497            numBytesOfEncryptedDataObj,
498            keyObj,
499            ivObj,
500            mode);
501
502    return true;
503}
504
505static void android_media_MediaExtractor_native_init(JNIEnv *env) {
506    jclass clazz = env->FindClass("android/media/MediaExtractor");
507    CHECK(clazz != NULL);
508
509    gFields.context = env->GetFieldID(clazz, "mNativeContext", "I");
510    CHECK(gFields.context != NULL);
511
512    clazz = env->FindClass("android/media/MediaCodec$CryptoInfo");
513    CHECK(clazz != NULL);
514
515    gFields.cryptoInfoSetID =
516        env->GetMethodID(clazz, "set", "(I[I[I[B[BI)V");
517
518    DataSource::RegisterDefaultSniffers();
519}
520
521static void android_media_MediaExtractor_native_setup(
522        JNIEnv *env, jobject thiz) {
523    sp<JMediaExtractor> extractor = new JMediaExtractor(env, thiz);
524    setMediaExtractor(env,thiz, extractor);
525}
526
527static void android_media_MediaExtractor_setDataSource(
528        JNIEnv *env, jobject thiz,
529        jstring pathObj, jobjectArray keysArray, jobjectArray valuesArray) {
530    sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
531
532    if (extractor == NULL) {
533        jniThrowException(env, "java/lang/IllegalStateException", NULL);
534        return;
535    }
536
537    if (pathObj == NULL) {
538        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
539        return;
540    }
541
542    KeyedVector<String8, String8> headers;
543    if (!ConvertKeyValueArraysToKeyedVector(
544                env, keysArray, valuesArray, &headers)) {
545        return;
546    }
547
548    const char *path = env->GetStringUTFChars(pathObj, NULL);
549
550    if (path == NULL) {
551        return;
552    }
553
554    status_t err = extractor->setDataSource(path, &headers);
555
556    env->ReleaseStringUTFChars(pathObj, path);
557    path = NULL;
558
559    if (err != OK) {
560        jniThrowException(
561                env,
562                "java/io/IOException",
563                "Failed to instantiate extractor.");
564        return;
565    }
566}
567
568static void android_media_MediaExtractor_setDataSourceFd(
569        JNIEnv *env, jobject thiz,
570        jobject fileDescObj, jlong offset, jlong length) {
571    sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
572
573    if (extractor == NULL) {
574        jniThrowException(env, "java/lang/IllegalStateException", NULL);
575        return;
576    }
577
578    if (fileDescObj == NULL) {
579        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
580        return;
581    }
582
583    int fd = jniGetFDFromFileDescriptor(env, fileDescObj);
584
585    status_t err = extractor->setDataSource(fd, offset, length);
586
587    if (err != OK) {
588        jniThrowException(
589                env,
590                "java/io/IOException",
591                "Failed to instantiate extractor.");
592        return;
593    }
594}
595
596static void android_media_MediaExtractor_native_finalize(
597        JNIEnv *env, jobject thiz) {
598    android_media_MediaExtractor_release(env, thiz);
599}
600
601static JNINativeMethod gMethods[] = {
602    { "release", "()V", (void *)android_media_MediaExtractor_release },
603
604    { "countTracks", "()I", (void *)android_media_MediaExtractor_countTracks },
605
606    { "getTrackFormat", "(I)Ljava/util/Map;",
607        (void *)android_media_MediaExtractor_getTrackFormat },
608
609    { "selectTrack", "(I)V", (void *)android_media_MediaExtractor_selectTrack },
610
611    { "seekTo", "(J)V", (void *)android_media_MediaExtractor_seekTo },
612
613    { "advance", "()Z", (void *)android_media_MediaExtractor_advance },
614
615    { "readSampleData", "(Ljava/nio/ByteBuffer;I)I",
616        (void *)android_media_MediaExtractor_readSampleData },
617
618    { "getSampleTrackIndex", "()I",
619        (void *)android_media_MediaExtractor_getSampleTrackIndex },
620
621    { "getSampleTime", "()J",
622        (void *)android_media_MediaExtractor_getSampleTime },
623
624    { "getSampleFlags", "()I",
625        (void *)android_media_MediaExtractor_getSampleFlags },
626
627    { "getSampleCryptoInfo", "(Landroid/media/MediaCodec$CryptoInfo;)Z",
628        (void *)android_media_MediaExtractor_getSampleCryptoInfo },
629
630    { "native_init", "()V", (void *)android_media_MediaExtractor_native_init },
631
632    { "native_setup", "()V",
633      (void *)android_media_MediaExtractor_native_setup },
634
635    { "native_finalize", "()V",
636      (void *)android_media_MediaExtractor_native_finalize },
637
638    { "setDataSource", "(Ljava/lang/String;[Ljava/lang/String;"
639                       "[Ljava/lang/String;)V",
640      (void *)android_media_MediaExtractor_setDataSource },
641
642    { "setDataSource", "(Ljava/io/FileDescriptor;JJ)V",
643      (void *)android_media_MediaExtractor_setDataSourceFd },
644};
645
646int register_android_media_MediaExtractor(JNIEnv *env) {
647    return AndroidRuntime::registerNativeMethods(env,
648                "android/media/MediaExtractor", gMethods, NELEM(gMethods));
649}
650