android_media_MediaExtractor.cpp revision 07ea426e3ae8915ca6bf67135f523f42cd920af0
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    status_t err = extractor->seekTo(timeUs);
261
262    if (err != OK) {
263        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
264        return;
265    }
266}
267
268static jboolean android_media_MediaExtractor_advance(
269        JNIEnv *env, jobject thiz) {
270    sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
271
272    if (extractor == NULL) {
273        jniThrowException(env, "java/lang/IllegalStateException", NULL);
274        return false;
275    }
276
277    status_t err = extractor->advance();
278
279    if (err == ERROR_END_OF_STREAM) {
280        return false;
281    } else if (err != OK) {
282        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
283        return false;
284    }
285
286    return true;
287}
288
289static jint android_media_MediaExtractor_readSampleData(
290        JNIEnv *env, jobject thiz, jobject byteBuf, jint offset) {
291    sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
292
293    if (extractor == NULL) {
294        jniThrowException(env, "java/lang/IllegalStateException", NULL);
295        return -1;
296    }
297
298    size_t sampleSize;
299    status_t err = extractor->readSampleData(byteBuf, offset, &sampleSize);
300
301    if (err == ERROR_END_OF_STREAM) {
302        return -1;
303    } else if (err != OK) {
304        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
305        return false;
306    }
307
308    return sampleSize;
309}
310
311static jint android_media_MediaExtractor_getSampleTrackIndex(
312        JNIEnv *env, jobject thiz) {
313    sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
314
315    if (extractor == NULL) {
316        jniThrowException(env, "java/lang/IllegalStateException", NULL);
317        return -1;
318    }
319
320    size_t trackIndex;
321    status_t err = extractor->getSampleTrackIndex(&trackIndex);
322
323    if (err == ERROR_END_OF_STREAM) {
324        return -1;
325    } else if (err != OK) {
326        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
327        return false;
328    }
329
330    return trackIndex;
331}
332
333static jlong android_media_MediaExtractor_getSampleTime(
334        JNIEnv *env, jobject thiz) {
335    sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
336
337    if (extractor == NULL) {
338        jniThrowException(env, "java/lang/IllegalStateException", NULL);
339        return -1ll;
340    }
341
342    int64_t sampleTimeUs;
343    status_t err = extractor->getSampleTime(&sampleTimeUs);
344
345    if (err == ERROR_END_OF_STREAM) {
346        return -1ll;
347    } else if (err != OK) {
348        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
349        return false;
350    }
351
352    return sampleTimeUs;
353}
354
355static jint android_media_MediaExtractor_getSampleFlags(
356        JNIEnv *env, jobject thiz) {
357    sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
358
359    if (extractor == NULL) {
360        jniThrowException(env, "java/lang/IllegalStateException", NULL);
361        return -1ll;
362    }
363
364    uint32_t sampleFlags;
365    status_t err = extractor->getSampleFlags(&sampleFlags);
366
367    if (err == ERROR_END_OF_STREAM) {
368        return -1ll;
369    } else if (err != OK) {
370        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
371        return false;
372    }
373
374    return sampleFlags;
375}
376
377static void android_media_MediaExtractor_native_init(JNIEnv *env) {
378    jclass clazz = env->FindClass("android/media/MediaExtractor");
379    CHECK(clazz != NULL);
380
381    gFields.context = env->GetFieldID(clazz, "mNativeContext", "I");
382    CHECK(gFields.context != NULL);
383
384    DataSource::RegisterDefaultSniffers();
385}
386
387static void android_media_MediaExtractor_native_setup(
388        JNIEnv *env, jobject thiz) {
389    sp<JMediaExtractor> extractor = new JMediaExtractor(env, thiz);
390    setMediaExtractor(env,thiz, extractor);
391}
392
393static void android_media_MediaExtractor_setDataSource(
394        JNIEnv *env, jobject thiz,
395        jstring pathObj, jobjectArray keysArray, jobjectArray valuesArray) {
396    sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
397
398    if (extractor == NULL) {
399        jniThrowException(env, "java/lang/IllegalStateException", NULL);
400        return;
401    }
402
403    if (pathObj == NULL) {
404        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
405        return;
406    }
407
408    KeyedVector<String8, String8> headers;
409    if (!ConvertKeyValueArraysToKeyedVector(
410                env, keysArray, valuesArray, &headers)) {
411        return;
412    }
413
414    const char *path = env->GetStringUTFChars(pathObj, NULL);
415
416    if (path == NULL) {
417        return;
418    }
419
420    status_t err = extractor->setDataSource(path, &headers);
421
422    env->ReleaseStringUTFChars(pathObj, path);
423    path = NULL;
424
425    if (err != OK) {
426        jniThrowException(
427                env,
428                "java/io/IOException",
429                "Failed to instantiate extractor.");
430        return;
431    }
432}
433
434static void android_media_MediaExtractor_setDataSourceFd(
435        JNIEnv *env, jobject thiz,
436        jobject fileDescObj, jlong offset, jlong length) {
437    sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
438
439    if (extractor == NULL) {
440        jniThrowException(env, "java/lang/IllegalStateException", NULL);
441        return;
442    }
443
444    if (fileDescObj == NULL) {
445        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
446        return;
447    }
448
449    int fd = jniGetFDFromFileDescriptor(env, fileDescObj);
450
451    status_t err = extractor->setDataSource(fd, offset, length);
452
453    if (err != OK) {
454        jniThrowException(
455                env,
456                "java/io/IOException",
457                "Failed to instantiate extractor.");
458        return;
459    }
460}
461
462static void android_media_MediaExtractor_native_finalize(
463        JNIEnv *env, jobject thiz) {
464    android_media_MediaExtractor_release(env, thiz);
465}
466
467static JNINativeMethod gMethods[] = {
468    { "release", "()V", (void *)android_media_MediaExtractor_release },
469
470    { "countTracks", "()I", (void *)android_media_MediaExtractor_countTracks },
471
472    { "getTrackFormat", "(I)Ljava/util/Map;",
473        (void *)android_media_MediaExtractor_getTrackFormat },
474
475    { "selectTrack", "(I)V", (void *)android_media_MediaExtractor_selectTrack },
476
477    { "seekTo", "(J)V", (void *)android_media_MediaExtractor_seekTo },
478
479    { "advance", "()Z", (void *)android_media_MediaExtractor_advance },
480
481    { "readSampleData", "(Ljava/nio/ByteBuffer;I)I",
482        (void *)android_media_MediaExtractor_readSampleData },
483
484    { "getSampleTrackIndex", "()I",
485        (void *)android_media_MediaExtractor_getSampleTrackIndex },
486
487    { "getSampleTime", "()J",
488        (void *)android_media_MediaExtractor_getSampleTime },
489
490    { "getSampleFlags", "()I",
491        (void *)android_media_MediaExtractor_getSampleFlags },
492
493    { "native_init", "()V", (void *)android_media_MediaExtractor_native_init },
494
495    { "native_setup", "()V",
496      (void *)android_media_MediaExtractor_native_setup },
497
498    { "native_finalize", "()V",
499      (void *)android_media_MediaExtractor_native_finalize },
500
501    { "setDataSource", "(Ljava/lang/String;[Ljava/lang/String;"
502                       "[Ljava/lang/String;)V",
503      (void *)android_media_MediaExtractor_setDataSource },
504
505    { "setDataSource", "(Ljava/io/FileDescriptor;JJ)V",
506      (void *)android_media_MediaExtractor_setDataSourceFd },
507};
508
509int register_android_media_MediaExtractor(JNIEnv *env) {
510    return AndroidRuntime::registerNativeMethods(env,
511                "android/media/MediaExtractor", gMethods, NELEM(gMethods));
512}
513