blob: 8c661b70f8e218ac6f66a9aafdbf7f827f89936b [file] [log] [blame]
Andreas Huber88572f72012-02-21 11:47:18 -08001/*
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
Andreas Huberc52b9802012-03-12 14:04:01 -0700104 jlong dstSize;
105 jbyteArray byteArray = NULL;
106
Andreas Huber88572f72012-02-21 11:47:18 -0800107 if (dst == NULL) {
Andreas Huberc52b9802012-03-12 14:04:01 -0700108 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);
Andreas Huber88572f72012-02-21 11:47:18 -0800128 }
129
Andreas Huber88572f72012-02-21 11:47:18 -0800130 if (dstSize < offset) {
Andreas Huberc52b9802012-03-12 14:04:01 -0700131 if (byteArray != NULL) {
132 env->ReleaseByteArrayElements(byteArray, (jbyte *)dst, 0);
133 }
134
Andreas Huber88572f72012-02-21 11:47:18 -0800135 return -ERANGE;
136 }
137
138 sp<ABuffer> buffer = new ABuffer((char *)dst + offset, dstSize - offset);
139
140 status_t err = mImpl->readSampleData(buffer);
141
Andreas Huberc52b9802012-03-12 14:04:01 -0700142 if (byteArray != NULL) {
143 env->ReleaseByteArrayElements(byteArray, (jbyte *)dst, 0);
144 }
145
Andreas Huber88572f72012-02-21 11:47:18 -0800146 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
Andreas Huber9b8e4962012-03-26 11:13:27 -0700163status_t JMediaExtractor::getSampleFlags(uint32_t *sampleFlags) {
164 return mImpl->getSampleFlags(sampleFlags);
165}
166
Andreas Huber88572f72012-02-21 11:47:18 -0800167} // 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
Andreas Huber9b8e4962012-03-26 11:13:27 -0700350static 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
Andreas Huber88572f72012-02-21 11:47:18 -0800372static 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
Andreas Huber9b8e4962012-03-26 11:13:27 -0700441 { "getSampleFlags", "()I",
442 (void *)android_media_MediaExtractor_getSampleFlags },
443
Andreas Huber88572f72012-02-21 11:47:18 -0800444 { "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}