android_media_MediaExtractor.cpp revision c52b980277f08aee7981b1fdbca7a89464cf66d9
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(const char *path) {
67    return mImpl->setDataSource(path);
68}
69
70size_t JMediaExtractor::countTracks() const {
71    return mImpl->countTracks();
72}
73
74status_t JMediaExtractor::getTrackFormat(size_t index, jobject *format) const {
75    sp<AMessage> msg;
76    status_t err;
77    if ((err = mImpl->getTrackFormat(index, &msg)) != OK) {
78        return err;
79    }
80
81    JNIEnv *env = AndroidRuntime::getJNIEnv();
82
83    return ConvertMessageToMap(env, msg, format);
84}
85
86status_t JMediaExtractor::selectTrack(size_t index) {
87    return mImpl->selectTrack(index);
88}
89
90status_t JMediaExtractor::seekTo(int64_t timeUs) {
91    return mImpl->seekTo(timeUs);
92}
93
94status_t JMediaExtractor::advance() {
95    return mImpl->advance();
96}
97
98status_t JMediaExtractor::readSampleData(
99        jobject byteBuf, size_t offset, size_t *sampleSize) {
100    JNIEnv *env = AndroidRuntime::getJNIEnv();
101
102    void *dst = env->GetDirectBufferAddress(byteBuf);
103
104    jlong dstSize;
105    jbyteArray byteArray = NULL;
106
107    if (dst == NULL) {
108        jclass byteBufClass = env->FindClass("java/nio/ByteBuffer");
109        CHECK(byteBufClass != NULL);
110
111        jmethodID arrayID =
112            env->GetMethodID(byteBufClass, "array", "()[B");
113        CHECK(arrayID != NULL);
114
115        byteArray =
116            (jbyteArray)env->CallObjectMethod(byteBuf, arrayID);
117
118        if (byteArray == NULL) {
119            return INVALID_OPERATION;
120        }
121
122        jboolean isCopy;
123        dst = env->GetByteArrayElements(byteArray, &isCopy);
124
125        dstSize = env->GetArrayLength(byteArray);
126    } else {
127        dstSize = env->GetDirectBufferCapacity(byteBuf);
128    }
129
130    if (dstSize < offset) {
131        if (byteArray != NULL) {
132            env->ReleaseByteArrayElements(byteArray, (jbyte *)dst, 0);
133        }
134
135        return -ERANGE;
136    }
137
138    sp<ABuffer> buffer = new ABuffer((char *)dst + offset, dstSize - offset);
139
140    status_t err = mImpl->readSampleData(buffer);
141
142    if (byteArray != NULL) {
143        env->ReleaseByteArrayElements(byteArray, (jbyte *)dst, 0);
144    }
145
146    if (err != OK) {
147        return err;
148    }
149
150    *sampleSize = buffer->size();
151
152    return OK;
153}
154
155status_t JMediaExtractor::getSampleTrackIndex(size_t *trackIndex) {
156    return mImpl->getSampleTrackIndex(trackIndex);
157}
158
159status_t JMediaExtractor::getSampleTime(int64_t *sampleTimeUs) {
160    return mImpl->getSampleTime(sampleTimeUs);
161}
162
163}  // namespace android
164
165////////////////////////////////////////////////////////////////////////////////
166
167using namespace android;
168
169static sp<JMediaExtractor> setMediaExtractor(
170        JNIEnv *env, jobject thiz, const sp<JMediaExtractor> &extractor) {
171    sp<JMediaExtractor> old =
172        (JMediaExtractor *)env->GetIntField(thiz, gFields.context);
173
174    if (extractor != NULL) {
175        extractor->incStrong(thiz);
176    }
177    if (old != NULL) {
178        old->decStrong(thiz);
179    }
180    env->SetIntField(thiz, gFields.context, (int)extractor.get());
181
182    return old;
183}
184
185static sp<JMediaExtractor> getMediaExtractor(JNIEnv *env, jobject thiz) {
186    return (JMediaExtractor *)env->GetIntField(thiz, gFields.context);
187}
188
189static void android_media_MediaExtractor_release(JNIEnv *env, jobject thiz) {
190    setMediaExtractor(env, thiz, NULL);
191}
192
193static jint android_media_MediaExtractor_countTracks(
194        JNIEnv *env, jobject thiz) {
195    sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
196
197    if (extractor == NULL) {
198        jniThrowException(env, "java/lang/IllegalStateException", NULL);
199        return NULL;
200    }
201
202    return extractor->countTracks();
203}
204
205static jobject android_media_MediaExtractor_getTrackFormat(
206        JNIEnv *env, jobject thiz, jint index) {
207    sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
208
209    if (extractor == NULL) {
210        jniThrowException(env, "java/lang/IllegalStateException", NULL);
211        return NULL;
212    }
213
214    jobject format;
215    status_t err = extractor->getTrackFormat(index, &format);
216
217    if (err != OK) {
218        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
219        return NULL;
220    }
221
222    return format;
223}
224
225static void android_media_MediaExtractor_selectTrack(
226        JNIEnv *env, jobject thiz, jint index) {
227    sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
228
229    if (extractor == NULL) {
230        jniThrowException(env, "java/lang/IllegalStateException", NULL);
231        return;
232    }
233
234    status_t err = extractor->selectTrack(index);
235
236    if (err != OK) {
237        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
238        return;
239    }
240}
241
242static void android_media_MediaExtractor_seekTo(
243        JNIEnv *env, jobject thiz, jlong timeUs) {
244    sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
245
246    if (extractor == NULL) {
247        jniThrowException(env, "java/lang/IllegalStateException", NULL);
248        return;
249    }
250
251    status_t err = extractor->seekTo(timeUs);
252
253    if (err != OK) {
254        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
255        return;
256    }
257}
258
259static jboolean android_media_MediaExtractor_advance(
260        JNIEnv *env, jobject thiz) {
261    sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
262
263    if (extractor == NULL) {
264        jniThrowException(env, "java/lang/IllegalStateException", NULL);
265        return false;
266    }
267
268    status_t err = extractor->advance();
269
270    if (err == ERROR_END_OF_STREAM) {
271        return false;
272    } else if (err != OK) {
273        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
274        return false;
275    }
276
277    return true;
278}
279
280static jint android_media_MediaExtractor_readSampleData(
281        JNIEnv *env, jobject thiz, jobject byteBuf, jint offset) {
282    sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
283
284    if (extractor == NULL) {
285        jniThrowException(env, "java/lang/IllegalStateException", NULL);
286        return -1;
287    }
288
289    size_t sampleSize;
290    status_t err = extractor->readSampleData(byteBuf, offset, &sampleSize);
291
292    if (err == ERROR_END_OF_STREAM) {
293        return -1;
294    } else if (err != OK) {
295        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
296        return false;
297    }
298
299    return sampleSize;
300}
301
302static jint android_media_MediaExtractor_getSampleTrackIndex(
303        JNIEnv *env, jobject thiz) {
304    sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
305
306    if (extractor == NULL) {
307        jniThrowException(env, "java/lang/IllegalStateException", NULL);
308        return -1;
309    }
310
311    size_t trackIndex;
312    status_t err = extractor->getSampleTrackIndex(&trackIndex);
313
314    if (err == ERROR_END_OF_STREAM) {
315        return -1;
316    } else if (err != OK) {
317        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
318        return false;
319    }
320
321    return trackIndex;
322}
323
324static jlong android_media_MediaExtractor_getSampleTime(
325        JNIEnv *env, jobject thiz) {
326    sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
327
328    if (extractor == NULL) {
329        jniThrowException(env, "java/lang/IllegalStateException", NULL);
330        return -1ll;
331    }
332
333    int64_t sampleTimeUs;
334    status_t err = extractor->getSampleTime(&sampleTimeUs);
335
336    if (err == ERROR_END_OF_STREAM) {
337        return -1ll;
338    } else if (err != OK) {
339        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
340        return false;
341    }
342
343    return sampleTimeUs;
344}
345
346static void android_media_MediaExtractor_native_init(JNIEnv *env) {
347    jclass clazz = env->FindClass("android/media/MediaExtractor");
348    CHECK(clazz != NULL);
349
350    gFields.context = env->GetFieldID(clazz, "mNativeContext", "I");
351    CHECK(gFields.context != NULL);
352
353    DataSource::RegisterDefaultSniffers();
354}
355
356static void android_media_MediaExtractor_native_setup(
357        JNIEnv *env, jobject thiz, jstring path) {
358    sp<JMediaExtractor> extractor = new JMediaExtractor(env, thiz);
359
360    if (path == NULL) {
361        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
362        return;
363    }
364
365    const char *tmp = env->GetStringUTFChars(path, NULL);
366
367    if (tmp == NULL) {
368        return;
369    }
370
371    status_t err = extractor->setDataSource(tmp);
372
373    env->ReleaseStringUTFChars(path, tmp);
374    tmp = NULL;
375
376    if (err != OK) {
377        jniThrowException(
378                env,
379                "java/io/IOException",
380                "Failed to instantiate extractor.");
381        return;
382    }
383
384    setMediaExtractor(env,thiz, extractor);
385}
386
387static void android_media_MediaExtractor_native_finalize(
388        JNIEnv *env, jobject thiz) {
389    android_media_MediaExtractor_release(env, thiz);
390}
391
392static JNINativeMethod gMethods[] = {
393    { "release", "()V", (void *)android_media_MediaExtractor_release },
394
395    { "countTracks", "()I", (void *)android_media_MediaExtractor_countTracks },
396
397    { "getTrackFormat", "(I)Ljava/util/Map;",
398        (void *)android_media_MediaExtractor_getTrackFormat },
399
400    { "selectTrack", "(I)V", (void *)android_media_MediaExtractor_selectTrack },
401
402    { "seekTo", "(J)V", (void *)android_media_MediaExtractor_seekTo },
403
404    { "advance", "()Z", (void *)android_media_MediaExtractor_advance },
405
406    { "readSampleData", "(Ljava/nio/ByteBuffer;I)I",
407        (void *)android_media_MediaExtractor_readSampleData },
408
409    { "getSampleTrackIndex", "()I",
410        (void *)android_media_MediaExtractor_getSampleTrackIndex },
411
412    { "getSampleTime", "()J",
413        (void *)android_media_MediaExtractor_getSampleTime },
414
415    { "native_init", "()V", (void *)android_media_MediaExtractor_native_init },
416
417    { "native_setup", "(Ljava/lang/String;)V",
418      (void *)android_media_MediaExtractor_native_setup },
419
420    { "native_finalize", "()V",
421      (void *)android_media_MediaExtractor_native_finalize },
422};
423
424int register_android_media_MediaExtractor(JNIEnv *env) {
425    return AndroidRuntime::registerNativeMethods(env,
426                "android/media/MediaExtractor", gMethods, NELEM(gMethods));
427}
428