android_media_MediaExtractor.cpp revision 9b8e496f4d143280deff137c5f30ca8907bc28db
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
163status_t JMediaExtractor::getSampleFlags(uint32_t *sampleFlags) {
164    return mImpl->getSampleFlags(sampleFlags);
165}
166
167}  // namespace android
168
169////////////////////////////////////////////////////////////////////////////////
170
171using namespace android;
172
173static sp<JMediaExtractor> setMediaExtractor(
174        JNIEnv *env, jobject thiz, const sp<JMediaExtractor> &extractor) {
175    sp<JMediaExtractor> old =
176        (JMediaExtractor *)env->GetIntField(thiz, gFields.context);
177
178    if (extractor != NULL) {
179        extractor->incStrong(thiz);
180    }
181    if (old != NULL) {
182        old->decStrong(thiz);
183    }
184    env->SetIntField(thiz, gFields.context, (int)extractor.get());
185
186    return old;
187}
188
189static sp<JMediaExtractor> getMediaExtractor(JNIEnv *env, jobject thiz) {
190    return (JMediaExtractor *)env->GetIntField(thiz, gFields.context);
191}
192
193static void android_media_MediaExtractor_release(JNIEnv *env, jobject thiz) {
194    setMediaExtractor(env, thiz, NULL);
195}
196
197static jint android_media_MediaExtractor_countTracks(
198        JNIEnv *env, jobject thiz) {
199    sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
200
201    if (extractor == NULL) {
202        jniThrowException(env, "java/lang/IllegalStateException", NULL);
203        return NULL;
204    }
205
206    return extractor->countTracks();
207}
208
209static jobject android_media_MediaExtractor_getTrackFormat(
210        JNIEnv *env, jobject thiz, jint index) {
211    sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
212
213    if (extractor == NULL) {
214        jniThrowException(env, "java/lang/IllegalStateException", NULL);
215        return NULL;
216    }
217
218    jobject format;
219    status_t err = extractor->getTrackFormat(index, &format);
220
221    if (err != OK) {
222        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
223        return NULL;
224    }
225
226    return format;
227}
228
229static void android_media_MediaExtractor_selectTrack(
230        JNIEnv *env, jobject thiz, jint index) {
231    sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
232
233    if (extractor == NULL) {
234        jniThrowException(env, "java/lang/IllegalStateException", NULL);
235        return;
236    }
237
238    status_t err = extractor->selectTrack(index);
239
240    if (err != OK) {
241        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
242        return;
243    }
244}
245
246static void android_media_MediaExtractor_seekTo(
247        JNIEnv *env, jobject thiz, jlong timeUs) {
248    sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
249
250    if (extractor == NULL) {
251        jniThrowException(env, "java/lang/IllegalStateException", NULL);
252        return;
253    }
254
255    status_t err = extractor->seekTo(timeUs);
256
257    if (err != OK) {
258        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
259        return;
260    }
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, jstring path) {
384    sp<JMediaExtractor> extractor = new JMediaExtractor(env, thiz);
385
386    if (path == NULL) {
387        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
388        return;
389    }
390
391    const char *tmp = env->GetStringUTFChars(path, NULL);
392
393    if (tmp == NULL) {
394        return;
395    }
396
397    status_t err = extractor->setDataSource(tmp);
398
399    env->ReleaseStringUTFChars(path, tmp);
400    tmp = NULL;
401
402    if (err != OK) {
403        jniThrowException(
404                env,
405                "java/io/IOException",
406                "Failed to instantiate extractor.");
407        return;
408    }
409
410    setMediaExtractor(env,thiz, extractor);
411}
412
413static void android_media_MediaExtractor_native_finalize(
414        JNIEnv *env, jobject thiz) {
415    android_media_MediaExtractor_release(env, thiz);
416}
417
418static JNINativeMethod gMethods[] = {
419    { "release", "()V", (void *)android_media_MediaExtractor_release },
420
421    { "countTracks", "()I", (void *)android_media_MediaExtractor_countTracks },
422
423    { "getTrackFormat", "(I)Ljava/util/Map;",
424        (void *)android_media_MediaExtractor_getTrackFormat },
425
426    { "selectTrack", "(I)V", (void *)android_media_MediaExtractor_selectTrack },
427
428    { "seekTo", "(J)V", (void *)android_media_MediaExtractor_seekTo },
429
430    { "advance", "()Z", (void *)android_media_MediaExtractor_advance },
431
432    { "readSampleData", "(Ljava/nio/ByteBuffer;I)I",
433        (void *)android_media_MediaExtractor_readSampleData },
434
435    { "getSampleTrackIndex", "()I",
436        (void *)android_media_MediaExtractor_getSampleTrackIndex },
437
438    { "getSampleTime", "()J",
439        (void *)android_media_MediaExtractor_getSampleTime },
440
441    { "getSampleFlags", "()I",
442        (void *)android_media_MediaExtractor_getSampleFlags },
443
444    { "native_init", "()V", (void *)android_media_MediaExtractor_native_init },
445
446    { "native_setup", "(Ljava/lang/String;)V",
447      (void *)android_media_MediaExtractor_native_setup },
448
449    { "native_finalize", "()V",
450      (void *)android_media_MediaExtractor_native_finalize },
451};
452
453int register_android_media_MediaExtractor(JNIEnv *env) {
454    return AndroidRuntime::registerNativeMethods(env,
455                "android/media/MediaExtractor", gMethods, NELEM(gMethods));
456}
457