android_media_MediaExtractor.cpp revision 2b9d6bd092ef1d0cc142bf16f671648d1c84c307
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/NuMediaExtractor.h>
34
35namespace android {
36
37struct fields_t {
38    jfieldID context;
39};
40
41static fields_t gFields;
42
43////////////////////////////////////////////////////////////////////////////////
44
45JMediaExtractor::JMediaExtractor(JNIEnv *env, jobject thiz)
46    : mClass(NULL),
47      mObject(NULL) {
48    jclass clazz = env->GetObjectClass(thiz);
49    CHECK(clazz != NULL);
50
51    mClass = (jclass)env->NewGlobalRef(clazz);
52    mObject = env->NewWeakGlobalRef(thiz);
53
54    mImpl = new NuMediaExtractor;
55}
56
57JMediaExtractor::~JMediaExtractor() {
58    JNIEnv *env = AndroidRuntime::getJNIEnv();
59
60    env->DeleteWeakGlobalRef(mObject);
61    mObject = NULL;
62    env->DeleteGlobalRef(mClass);
63    mClass = NULL;
64}
65
66status_t JMediaExtractor::setDataSource(
67        const char *path, const KeyedVector<String8, String8> *headers) {
68    return mImpl->setDataSource(path, headers);
69}
70
71status_t JMediaExtractor::setDataSource(int fd, off64_t offset, off64_t size) {
72    return mImpl->setDataSource(fd, offset, size);
73}
74
75size_t JMediaExtractor::countTracks() const {
76    return mImpl->countTracks();
77}
78
79status_t JMediaExtractor::getTrackFormat(size_t index, jobject *format) const {
80    sp<AMessage> msg;
81    status_t err;
82    if ((err = mImpl->getTrackFormat(index, &msg)) != OK) {
83        return err;
84    }
85
86    JNIEnv *env = AndroidRuntime::getJNIEnv();
87
88    return ConvertMessageToMap(env, msg, format);
89}
90
91status_t JMediaExtractor::selectTrack(size_t index) {
92    return mImpl->selectTrack(index);
93}
94
95status_t JMediaExtractor::seekTo(int64_t timeUs) {
96    return mImpl->seekTo(timeUs);
97}
98
99status_t JMediaExtractor::advance() {
100    return mImpl->advance();
101}
102
103status_t JMediaExtractor::readSampleData(
104        jobject byteBuf, size_t offset, size_t *sampleSize) {
105    JNIEnv *env = AndroidRuntime::getJNIEnv();
106
107    void *dst = env->GetDirectBufferAddress(byteBuf);
108
109    jlong dstSize;
110    jbyteArray byteArray = NULL;
111
112    if (dst == NULL) {
113        jclass byteBufClass = env->FindClass("java/nio/ByteBuffer");
114        CHECK(byteBufClass != NULL);
115
116        jmethodID arrayID =
117            env->GetMethodID(byteBufClass, "array", "()[B");
118        CHECK(arrayID != NULL);
119
120        byteArray =
121            (jbyteArray)env->CallObjectMethod(byteBuf, arrayID);
122
123        if (byteArray == NULL) {
124            return INVALID_OPERATION;
125        }
126
127        jboolean isCopy;
128        dst = env->GetByteArrayElements(byteArray, &isCopy);
129
130        dstSize = env->GetArrayLength(byteArray);
131    } else {
132        dstSize = env->GetDirectBufferCapacity(byteBuf);
133    }
134
135    if (dstSize < offset) {
136        if (byteArray != NULL) {
137            env->ReleaseByteArrayElements(byteArray, (jbyte *)dst, 0);
138        }
139
140        return -ERANGE;
141    }
142
143    sp<ABuffer> buffer = new ABuffer((char *)dst + offset, dstSize - offset);
144
145    status_t err = mImpl->readSampleData(buffer);
146
147    if (byteArray != NULL) {
148        env->ReleaseByteArrayElements(byteArray, (jbyte *)dst, 0);
149    }
150
151    if (err != OK) {
152        return err;
153    }
154
155    *sampleSize = buffer->size();
156
157    return OK;
158}
159
160status_t JMediaExtractor::getSampleTrackIndex(size_t *trackIndex) {
161    return mImpl->getSampleTrackIndex(trackIndex);
162}
163
164status_t JMediaExtractor::getSampleTime(int64_t *sampleTimeUs) {
165    return mImpl->getSampleTime(sampleTimeUs);
166}
167
168status_t JMediaExtractor::getSampleFlags(uint32_t *sampleFlags) {
169    return mImpl->getSampleFlags(sampleFlags);
170}
171
172}  // namespace android
173
174////////////////////////////////////////////////////////////////////////////////
175
176using namespace android;
177
178static sp<JMediaExtractor> setMediaExtractor(
179        JNIEnv *env, jobject thiz, const sp<JMediaExtractor> &extractor) {
180    sp<JMediaExtractor> old =
181        (JMediaExtractor *)env->GetIntField(thiz, gFields.context);
182
183    if (extractor != NULL) {
184        extractor->incStrong(thiz);
185    }
186    if (old != NULL) {
187        old->decStrong(thiz);
188    }
189    env->SetIntField(thiz, gFields.context, (int)extractor.get());
190
191    return old;
192}
193
194static sp<JMediaExtractor> getMediaExtractor(JNIEnv *env, jobject thiz) {
195    return (JMediaExtractor *)env->GetIntField(thiz, gFields.context);
196}
197
198static void android_media_MediaExtractor_release(JNIEnv *env, jobject thiz) {
199    setMediaExtractor(env, thiz, NULL);
200}
201
202static jint android_media_MediaExtractor_countTracks(
203        JNIEnv *env, jobject thiz) {
204    sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
205
206    if (extractor == NULL) {
207        jniThrowException(env, "java/lang/IllegalStateException", NULL);
208        return -1;
209    }
210
211    return extractor->countTracks();
212}
213
214static jobject android_media_MediaExtractor_getTrackFormat(
215        JNIEnv *env, jobject thiz, jint index) {
216    sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
217
218    if (extractor == NULL) {
219        jniThrowException(env, "java/lang/IllegalStateException", NULL);
220        return NULL;
221    }
222
223    jobject format;
224    status_t err = extractor->getTrackFormat(index, &format);
225
226    if (err != OK) {
227        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
228        return NULL;
229    }
230
231    return format;
232}
233
234static void android_media_MediaExtractor_selectTrack(
235        JNIEnv *env, jobject thiz, jint index) {
236    sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
237
238    if (extractor == NULL) {
239        jniThrowException(env, "java/lang/IllegalStateException", NULL);
240        return;
241    }
242
243    status_t err = extractor->selectTrack(index);
244
245    if (err != OK) {
246        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
247        return;
248    }
249}
250
251static void android_media_MediaExtractor_seekTo(
252        JNIEnv *env, jobject thiz, jlong timeUs) {
253    sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
254
255    if (extractor == NULL) {
256        jniThrowException(env, "java/lang/IllegalStateException", NULL);
257        return;
258    }
259
260    extractor->seekTo(timeUs);
261}
262
263static jboolean android_media_MediaExtractor_advance(
264        JNIEnv *env, jobject thiz) {
265    sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
266
267    if (extractor == NULL) {
268        jniThrowException(env, "java/lang/IllegalStateException", NULL);
269        return false;
270    }
271
272    status_t err = extractor->advance();
273
274    if (err == ERROR_END_OF_STREAM) {
275        return false;
276    } else if (err != OK) {
277        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
278        return false;
279    }
280
281    return true;
282}
283
284static jint android_media_MediaExtractor_readSampleData(
285        JNIEnv *env, jobject thiz, jobject byteBuf, jint offset) {
286    sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
287
288    if (extractor == NULL) {
289        jniThrowException(env, "java/lang/IllegalStateException", NULL);
290        return -1;
291    }
292
293    size_t sampleSize;
294    status_t err = extractor->readSampleData(byteBuf, offset, &sampleSize);
295
296    if (err == ERROR_END_OF_STREAM) {
297        return -1;
298    } else if (err != OK) {
299        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
300        return false;
301    }
302
303    return sampleSize;
304}
305
306static jint android_media_MediaExtractor_getSampleTrackIndex(
307        JNIEnv *env, jobject thiz) {
308    sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
309
310    if (extractor == NULL) {
311        jniThrowException(env, "java/lang/IllegalStateException", NULL);
312        return -1;
313    }
314
315    size_t trackIndex;
316    status_t err = extractor->getSampleTrackIndex(&trackIndex);
317
318    if (err == ERROR_END_OF_STREAM) {
319        return -1;
320    } else if (err != OK) {
321        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
322        return false;
323    }
324
325    return trackIndex;
326}
327
328static jlong android_media_MediaExtractor_getSampleTime(
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 -1ll;
335    }
336
337    int64_t sampleTimeUs;
338    status_t err = extractor->getSampleTime(&sampleTimeUs);
339
340    if (err == ERROR_END_OF_STREAM) {
341        return -1ll;
342    } else if (err != OK) {
343        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
344        return false;
345    }
346
347    return sampleTimeUs;
348}
349
350static jint android_media_MediaExtractor_getSampleFlags(
351        JNIEnv *env, jobject thiz) {
352    sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
353
354    if (extractor == NULL) {
355        jniThrowException(env, "java/lang/IllegalStateException", NULL);
356        return -1ll;
357    }
358
359    uint32_t sampleFlags;
360    status_t err = extractor->getSampleFlags(&sampleFlags);
361
362    if (err == ERROR_END_OF_STREAM) {
363        return -1ll;
364    } else if (err != OK) {
365        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
366        return false;
367    }
368
369    return sampleFlags;
370}
371
372static void android_media_MediaExtractor_native_init(JNIEnv *env) {
373    jclass clazz = env->FindClass("android/media/MediaExtractor");
374    CHECK(clazz != NULL);
375
376    gFields.context = env->GetFieldID(clazz, "mNativeContext", "I");
377    CHECK(gFields.context != NULL);
378
379    DataSource::RegisterDefaultSniffers();
380}
381
382static void android_media_MediaExtractor_native_setup(
383        JNIEnv *env, jobject thiz) {
384    sp<JMediaExtractor> extractor = new JMediaExtractor(env, thiz);
385    setMediaExtractor(env,thiz, extractor);
386}
387
388static void android_media_MediaExtractor_setDataSource(
389        JNIEnv *env, jobject thiz,
390        jstring pathObj, jobjectArray keysArray, jobjectArray valuesArray) {
391    sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
392
393    if (extractor == NULL) {
394        jniThrowException(env, "java/lang/IllegalStateException", NULL);
395        return;
396    }
397
398    if (pathObj == NULL) {
399        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
400        return;
401    }
402
403    KeyedVector<String8, String8> headers;
404    if (!ConvertKeyValueArraysToKeyedVector(
405                env, keysArray, valuesArray, &headers)) {
406        return;
407    }
408
409    const char *path = env->GetStringUTFChars(pathObj, NULL);
410
411    if (path == NULL) {
412        return;
413    }
414
415    status_t err = extractor->setDataSource(path, &headers);
416
417    env->ReleaseStringUTFChars(pathObj, path);
418    path = NULL;
419
420    if (err != OK) {
421        jniThrowException(
422                env,
423                "java/io/IOException",
424                "Failed to instantiate extractor.");
425        return;
426    }
427}
428
429static void android_media_MediaExtractor_setDataSourceFd(
430        JNIEnv *env, jobject thiz,
431        jobject fileDescObj, jlong offset, jlong length) {
432    sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
433
434    if (extractor == NULL) {
435        jniThrowException(env, "java/lang/IllegalStateException", NULL);
436        return;
437    }
438
439    if (fileDescObj == NULL) {
440        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
441        return;
442    }
443
444    int fd = jniGetFDFromFileDescriptor(env, fileDescObj);
445
446    status_t err = extractor->setDataSource(fd, offset, length);
447
448    if (err != OK) {
449        jniThrowException(
450                env,
451                "java/io/IOException",
452                "Failed to instantiate extractor.");
453        return;
454    }
455}
456
457static void android_media_MediaExtractor_native_finalize(
458        JNIEnv *env, jobject thiz) {
459    android_media_MediaExtractor_release(env, thiz);
460}
461
462static JNINativeMethod gMethods[] = {
463    { "release", "()V", (void *)android_media_MediaExtractor_release },
464
465    { "countTracks", "()I", (void *)android_media_MediaExtractor_countTracks },
466
467    { "getTrackFormat", "(I)Ljava/util/Map;",
468        (void *)android_media_MediaExtractor_getTrackFormat },
469
470    { "selectTrack", "(I)V", (void *)android_media_MediaExtractor_selectTrack },
471
472    { "seekTo", "(J)V", (void *)android_media_MediaExtractor_seekTo },
473
474    { "advance", "()Z", (void *)android_media_MediaExtractor_advance },
475
476    { "readSampleData", "(Ljava/nio/ByteBuffer;I)I",
477        (void *)android_media_MediaExtractor_readSampleData },
478
479    { "getSampleTrackIndex", "()I",
480        (void *)android_media_MediaExtractor_getSampleTrackIndex },
481
482    { "getSampleTime", "()J",
483        (void *)android_media_MediaExtractor_getSampleTime },
484
485    { "getSampleFlags", "()I",
486        (void *)android_media_MediaExtractor_getSampleFlags },
487
488    { "native_init", "()V", (void *)android_media_MediaExtractor_native_init },
489
490    { "native_setup", "()V",
491      (void *)android_media_MediaExtractor_native_setup },
492
493    { "native_finalize", "()V",
494      (void *)android_media_MediaExtractor_native_finalize },
495
496    { "setDataSource", "(Ljava/lang/String;[Ljava/lang/String;"
497                       "[Ljava/lang/String;)V",
498      (void *)android_media_MediaExtractor_setDataSource },
499
500    { "setDataSource", "(Ljava/io/FileDescriptor;JJ)V",
501      (void *)android_media_MediaExtractor_setDataSourceFd },
502};
503
504int register_android_media_MediaExtractor(JNIEnv *env) {
505    return AndroidRuntime::registerNativeMethods(env,
506                "android/media/MediaExtractor", gMethods, NELEM(gMethods));
507}
508