blob: 217216ae70e7142aa55bec58559fe648ef75ca3e [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 "MediaCodec-JNI"
19#include <utils/Log.h>
20
21#include "android_media_MediaCodec.h"
22
Andreas Huber8240d922012-04-04 14:06:32 -070023#include "android_media_Crypto.h"
Andreas Huber88572f72012-02-21 11:47:18 -080024#include "android_media_Utils.h"
25#include "android_runtime/AndroidRuntime.h"
26#include "android_runtime/android_view_Surface.h"
27#include "jni.h"
28#include "JNIHelp.h"
29
Mathias Agopian8335f1c2012-02-25 18:48:35 -080030#include <gui/Surface.h>
31#include <gui/SurfaceTextureClient.h>
32
Andreas Huber0e97fc22012-04-03 13:32:16 -070033#include <media/ICrypto.h>
Andreas Huber88572f72012-02-21 11:47:18 -080034#include <media/stagefright/MediaCodec.h>
35#include <media/stagefright/foundation/ABuffer.h>
36#include <media/stagefright/foundation/ADebug.h>
37#include <media/stagefright/foundation/ALooper.h>
38#include <media/stagefright/foundation/AMessage.h>
39#include <media/stagefright/MediaErrors.h>
Andreas Huber88572f72012-02-21 11:47:18 -080040
41namespace android {
42
43// Keep these in sync with their equivalents in MediaCodec.java !!!
44enum {
45 DEQUEUE_INFO_TRY_AGAIN_LATER = -1,
46 DEQUEUE_INFO_OUTPUT_FORMAT_CHANGED = -2,
47 DEQUEUE_INFO_OUTPUT_BUFFERS_CHANGED = -3,
48};
49
50struct fields_t {
51 jfieldID context;
52};
53
54static fields_t gFields;
55
56////////////////////////////////////////////////////////////////////////////////
57
58JMediaCodec::JMediaCodec(
59 JNIEnv *env, jobject thiz,
60 const char *name, bool nameIsType, bool encoder)
61 : mClass(NULL),
62 mObject(NULL) {
63 jclass clazz = env->GetObjectClass(thiz);
64 CHECK(clazz != NULL);
65
66 mClass = (jclass)env->NewGlobalRef(clazz);
67 mObject = env->NewWeakGlobalRef(thiz);
68
69 mLooper = new ALooper;
70 mLooper->setName("MediaCodec_looper");
71
72 mLooper->start(
73 false, // runOnCallingThread
74 false, // canCallJava
75 PRIORITY_DEFAULT);
76
77 if (nameIsType) {
78 mCodec = MediaCodec::CreateByType(mLooper, name, encoder);
79 } else {
80 mCodec = MediaCodec::CreateByComponentName(mLooper, name);
81 }
82}
83
84status_t JMediaCodec::initCheck() const {
85 return mCodec != NULL ? OK : NO_INIT;
86}
87
88JMediaCodec::~JMediaCodec() {
Andreas Huber4484bdd2012-02-28 15:54:51 -080089 mCodec->release();
Andreas Huber88572f72012-02-21 11:47:18 -080090
91 JNIEnv *env = AndroidRuntime::getJNIEnv();
92
93 env->DeleteWeakGlobalRef(mObject);
94 mObject = NULL;
95 env->DeleteGlobalRef(mClass);
96 mClass = NULL;
97}
98
99status_t JMediaCodec::configure(
100 const sp<AMessage> &format,
101 const sp<ISurfaceTexture> &surfaceTexture,
Andreas Huber8240d922012-04-04 14:06:32 -0700102 const sp<ICrypto> &crypto,
Andreas Huber88572f72012-02-21 11:47:18 -0800103 int flags) {
104 sp<SurfaceTextureClient> client;
105 if (surfaceTexture != NULL) {
106 client = new SurfaceTextureClient(surfaceTexture);
107 }
Andreas Huber8240d922012-04-04 14:06:32 -0700108 return mCodec->configure(format, client, crypto, flags);
Andreas Huber88572f72012-02-21 11:47:18 -0800109}
110
111status_t JMediaCodec::start() {
112 return mCodec->start();
113}
114
115status_t JMediaCodec::stop() {
116 return mCodec->stop();
117}
118
119status_t JMediaCodec::flush() {
120 return mCodec->flush();
121}
122
123status_t JMediaCodec::queueInputBuffer(
124 size_t index,
125 size_t offset, size_t size, int64_t timeUs, uint32_t flags) {
126 return mCodec->queueInputBuffer(index, offset, size, timeUs, flags);
127}
128
129status_t JMediaCodec::dequeueInputBuffer(size_t *index, int64_t timeoutUs) {
130 return mCodec->dequeueInputBuffer(index, timeoutUs);
131}
132
133status_t JMediaCodec::dequeueOutputBuffer(
134 JNIEnv *env, jobject bufferInfo, size_t *index, int64_t timeoutUs) {
135 size_t size, offset;
136 int64_t timeUs;
137 uint32_t flags;
138 status_t err;
139 if ((err = mCodec->dequeueOutputBuffer(
Andreas Huberc52b9802012-03-12 14:04:01 -0700140 index, &offset, &size, &timeUs, &flags, timeoutUs)) != OK) {
Andreas Huber88572f72012-02-21 11:47:18 -0800141 return err;
142 }
143
144 jclass clazz = env->FindClass("android/media/MediaCodec$BufferInfo");
145
146 jmethodID method = env->GetMethodID(clazz, "set", "(IIJI)V");
147 env->CallVoidMethod(bufferInfo, method, offset, size, timeUs, flags);
148
149 return OK;
150}
151
152status_t JMediaCodec::releaseOutputBuffer(size_t index, bool render) {
153 return render
154 ? mCodec->renderOutputBufferAndRelease(index)
155 : mCodec->releaseOutputBuffer(index);
156}
157
158status_t JMediaCodec::getOutputFormat(JNIEnv *env, jobject *format) const {
159 sp<AMessage> msg;
160 status_t err;
161 if ((err = mCodec->getOutputFormat(&msg)) != OK) {
162 return err;
163 }
164
165 return ConvertMessageToMap(env, msg, format);
166}
167
168status_t JMediaCodec::getBuffers(
169 JNIEnv *env, bool input, jobjectArray *bufArray) const {
170 Vector<sp<ABuffer> > buffers;
171
172 status_t err =
173 input
174 ? mCodec->getInputBuffers(&buffers)
175 : mCodec->getOutputBuffers(&buffers);
176
177 if (err != OK) {
178 return err;
179 }
180
181 jclass byteBufferClass = env->FindClass("java/nio/ByteBuffer");
182
183 *bufArray = (jobjectArray)env->NewObjectArray(
184 buffers.size(), byteBufferClass, NULL);
185
186 for (size_t i = 0; i < buffers.size(); ++i) {
187 const sp<ABuffer> &buffer = buffers.itemAt(i);
188
189 jobject byteBuffer =
190 env->NewDirectByteBuffer(
191 buffer->base(),
192 buffer->capacity());
193
194 env->SetObjectArrayElement(
195 *bufArray, i, byteBuffer);
196
197 env->DeleteLocalRef(byteBuffer);
198 byteBuffer = NULL;
199 }
200
201 return OK;
202}
203
204} // namespace android
205
206////////////////////////////////////////////////////////////////////////////////
207
208using namespace android;
209
210static sp<JMediaCodec> setMediaCodec(
211 JNIEnv *env, jobject thiz, const sp<JMediaCodec> &codec) {
212 sp<JMediaCodec> old = (JMediaCodec *)env->GetIntField(thiz, gFields.context);
213 if (codec != NULL) {
214 codec->incStrong(thiz);
215 }
216 if (old != NULL) {
217 old->decStrong(thiz);
218 }
219 env->SetIntField(thiz, gFields.context, (int)codec.get());
220
221 return old;
222}
223
224static sp<JMediaCodec> getMediaCodec(JNIEnv *env, jobject thiz) {
225 return (JMediaCodec *)env->GetIntField(thiz, gFields.context);
226}
227
228static void android_media_MediaCodec_release(JNIEnv *env, jobject thiz) {
229 setMediaCodec(env, thiz, NULL);
230}
231
232static jint throwExceptionAsNecessary(JNIEnv *env, status_t err) {
233 switch (err) {
234 case OK:
235 return 0;
236
237 case -EAGAIN:
238 return DEQUEUE_INFO_TRY_AGAIN_LATER;
239
240 case INFO_FORMAT_CHANGED:
241 return DEQUEUE_INFO_OUTPUT_FORMAT_CHANGED;
242
243 case INFO_OUTPUT_BUFFERS_CHANGED:
244 return DEQUEUE_INFO_OUTPUT_BUFFERS_CHANGED;
245
246 default:
247 {
248 jniThrowException(env, "java/lang/IllegalStateException", NULL);
249 break;
250 }
251 }
252
253 return 0;
254}
255
256static void android_media_MediaCodec_native_configure(
257 JNIEnv *env,
258 jobject thiz,
259 jobjectArray keys, jobjectArray values,
260 jobject jsurface,
Andreas Huber8240d922012-04-04 14:06:32 -0700261 jobject jcrypto,
Andreas Huber88572f72012-02-21 11:47:18 -0800262 jint flags) {
263 sp<JMediaCodec> codec = getMediaCodec(env, thiz);
264
265 if (codec == NULL) {
266 jniThrowException(env, "java/lang/IllegalStateException", NULL);
267 return;
268 }
269
270 sp<AMessage> format;
271 status_t err = ConvertKeyValueArraysToMessage(env, keys, values, &format);
272
273 if (err != OK) {
274 jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
275 return;
276 }
277
278 sp<ISurfaceTexture> surfaceTexture;
279 if (jsurface != NULL) {
280 sp<Surface> surface(Surface_getSurface(env, jsurface));
281 if (surface != NULL) {
282 surfaceTexture = surface->getSurfaceTexture();
283 } else {
284 jniThrowException(
285 env,
286 "java/lang/IllegalArgumentException",
287 "The surface has been released");
288 return;
289 }
290 }
291
Andreas Huber8240d922012-04-04 14:06:32 -0700292 sp<ICrypto> crypto;
293 if (jcrypto != NULL) {
294 crypto = JCrypto::GetCrypto(env, jcrypto);
295 }
296
297 err = codec->configure(format, surfaceTexture, crypto, flags);
Andreas Huber88572f72012-02-21 11:47:18 -0800298
299 throwExceptionAsNecessary(env, err);
300}
301
302static void android_media_MediaCodec_start(JNIEnv *env, jobject thiz) {
303 ALOGV("android_media_MediaCodec_start");
304
305 sp<JMediaCodec> codec = getMediaCodec(env, thiz);
306
307 if (codec == NULL) {
308 jniThrowException(env, "java/lang/IllegalStateException", NULL);
309 return;
310 }
311
312 status_t err = codec->start();
313
314 throwExceptionAsNecessary(env, err);
315}
316
317static void android_media_MediaCodec_stop(JNIEnv *env, jobject thiz) {
318 ALOGV("android_media_MediaCodec_stop");
319
320 sp<JMediaCodec> codec = getMediaCodec(env, thiz);
321
322 if (codec == NULL) {
323 jniThrowException(env, "java/lang/IllegalStateException", NULL);
324 return;
325 }
326
327 status_t err = codec->stop();
328
329 throwExceptionAsNecessary(env, err);
330}
331
332static void android_media_MediaCodec_flush(JNIEnv *env, jobject thiz) {
333 ALOGV("android_media_MediaCodec_flush");
334
335 sp<JMediaCodec> codec = getMediaCodec(env, thiz);
336
337 if (codec == NULL) {
338 jniThrowException(env, "java/lang/IllegalStateException", NULL);
339 return;
340 }
341
342 status_t err = codec->flush();
343
344 throwExceptionAsNecessary(env, err);
345}
346
347static void android_media_MediaCodec_queueInputBuffer(
348 JNIEnv *env,
349 jobject thiz,
350 jint index,
351 jint offset,
352 jint size,
353 jlong timestampUs,
354 jint flags) {
355 ALOGV("android_media_MediaCodec_queueInputBuffer");
356
357 sp<JMediaCodec> codec = getMediaCodec(env, thiz);
358
359 if (codec == NULL) {
360 jniThrowException(env, "java/lang/IllegalStateException", NULL);
361 return;
362 }
363
364 status_t err = codec->queueInputBuffer(
365 index, offset, size, timestampUs, flags);
366
367 throwExceptionAsNecessary(env, err);
368}
369
370static jint android_media_MediaCodec_dequeueInputBuffer(
371 JNIEnv *env, jobject thiz, jlong timeoutUs) {
372 ALOGV("android_media_MediaCodec_dequeueInputBuffer");
373
374 sp<JMediaCodec> codec = getMediaCodec(env, thiz);
375
376 if (codec == NULL) {
377 jniThrowException(env, "java/lang/IllegalStateException", NULL);
378 return -1;
379 }
380
381 size_t index;
382 status_t err = codec->dequeueInputBuffer(&index, timeoutUs);
383
384 if (err == OK) {
385 return index;
386 }
387
388 return throwExceptionAsNecessary(env, err);
389}
390
391static jint android_media_MediaCodec_dequeueOutputBuffer(
392 JNIEnv *env, jobject thiz, jobject bufferInfo, jlong timeoutUs) {
393 ALOGV("android_media_MediaCodec_dequeueOutputBuffer");
394
395 sp<JMediaCodec> codec = getMediaCodec(env, thiz);
396
397 if (codec == NULL) {
398 jniThrowException(env, "java/lang/IllegalStateException", NULL);
Andreas Huber0e97fc22012-04-03 13:32:16 -0700399 return 0;
Andreas Huber88572f72012-02-21 11:47:18 -0800400 }
401
402 size_t index;
403 status_t err = codec->dequeueOutputBuffer(
404 env, bufferInfo, &index, timeoutUs);
405
406 if (err == OK) {
407 return index;
408 }
409
410 return throwExceptionAsNecessary(env, err);
411}
412
413static void android_media_MediaCodec_releaseOutputBuffer(
414 JNIEnv *env, jobject thiz, jint index, jboolean render) {
415 ALOGV("android_media_MediaCodec_renderOutputBufferAndRelease");
416
417 sp<JMediaCodec> codec = getMediaCodec(env, thiz);
418
419 if (codec == NULL) {
420 jniThrowException(env, "java/lang/IllegalStateException", NULL);
421 return;
422 }
423
424 status_t err = codec->releaseOutputBuffer(index, render);
425
426 throwExceptionAsNecessary(env, err);
427}
428
429static jobject android_media_MediaCodec_getOutputFormat(
430 JNIEnv *env, jobject thiz) {
431 ALOGV("android_media_MediaCodec_getOutputFormat");
432
433 sp<JMediaCodec> codec = getMediaCodec(env, thiz);
434
435 if (codec == NULL) {
436 jniThrowException(env, "java/lang/IllegalStateException", NULL);
437 return NULL;
438 }
439
440 jobject format;
441 status_t err = codec->getOutputFormat(env, &format);
442
443 if (err == OK) {
444 return format;
445 }
446
447 throwExceptionAsNecessary(env, err);
448
449 return NULL;
450}
451
452static jobjectArray android_media_MediaCodec_getBuffers(
453 JNIEnv *env, jobject thiz, jboolean input) {
454 ALOGV("android_media_MediaCodec_getBuffers");
455
456 sp<JMediaCodec> codec = getMediaCodec(env, thiz);
457
458 if (codec == NULL) {
459 jniThrowException(env, "java/lang/IllegalStateException", NULL);
460 return NULL;
461 }
462
463 jobjectArray buffers;
464 status_t err = codec->getBuffers(env, input, &buffers);
465
466 if (err == OK) {
467 return buffers;
468 }
469
470 throwExceptionAsNecessary(env, err);
471
472 return NULL;
473}
474
475static void android_media_MediaCodec_native_init(JNIEnv *env) {
476 jclass clazz = env->FindClass("android/media/MediaCodec");
477 CHECK(clazz != NULL);
478
479 gFields.context = env->GetFieldID(clazz, "mNativeContext", "I");
480 CHECK(gFields.context != NULL);
481}
482
483static void android_media_MediaCodec_native_setup(
484 JNIEnv *env, jobject thiz,
485 jstring name, jboolean nameIsType, jboolean encoder) {
486 if (name == NULL) {
487 jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
488 return;
489 }
490
491 const char *tmp = env->GetStringUTFChars(name, NULL);
492
493 if (tmp == NULL) {
494 return;
495 }
496
497 sp<JMediaCodec> codec = new JMediaCodec(env, thiz, tmp, nameIsType, encoder);
498
499 status_t err = codec->initCheck();
500
501 env->ReleaseStringUTFChars(name, tmp);
502 tmp = NULL;
503
504 if (err != OK) {
505 jniThrowException(
506 env,
507 "java/io/IOException",
508 "Failed to allocate component instance");
509 return;
510 }
511
512 setMediaCodec(env,thiz, codec);
513}
514
515static void android_media_MediaCodec_native_finalize(
516 JNIEnv *env, jobject thiz) {
517 android_media_MediaCodec_release(env, thiz);
518}
519
520static JNINativeMethod gMethods[] = {
521 { "release", "()V", (void *)android_media_MediaCodec_release },
522
523 { "native_configure",
Andreas Huber8240d922012-04-04 14:06:32 -0700524 "([Ljava/lang/String;[Ljava/lang/Object;Landroid/view/Surface;"
525 "Landroid/media/Crypto;I)V",
Andreas Huber88572f72012-02-21 11:47:18 -0800526 (void *)android_media_MediaCodec_native_configure },
527
528 { "start", "()V", (void *)android_media_MediaCodec_start },
529 { "stop", "()V", (void *)android_media_MediaCodec_stop },
530 { "flush", "()V", (void *)android_media_MediaCodec_flush },
531
532 { "queueInputBuffer", "(IIIJI)V",
533 (void *)android_media_MediaCodec_queueInputBuffer },
534
535 { "dequeueInputBuffer", "(J)I",
536 (void *)android_media_MediaCodec_dequeueInputBuffer },
537
538 { "dequeueOutputBuffer", "(Landroid/media/MediaCodec$BufferInfo;J)I",
539 (void *)android_media_MediaCodec_dequeueOutputBuffer },
540
541 { "releaseOutputBuffer", "(IZ)V",
542 (void *)android_media_MediaCodec_releaseOutputBuffer },
543
544 { "getOutputFormat", "()Ljava/util/Map;",
545 (void *)android_media_MediaCodec_getOutputFormat },
546
547 { "getBuffers", "(Z)[Ljava/nio/ByteBuffer;",
548 (void *)android_media_MediaCodec_getBuffers },
549
550 { "native_init", "()V", (void *)android_media_MediaCodec_native_init },
551
552 { "native_setup", "(Ljava/lang/String;ZZ)V",
553 (void *)android_media_MediaCodec_native_setup },
554
555 { "native_finalize", "()V",
556 (void *)android_media_MediaCodec_native_finalize },
557};
558
559int register_android_media_MediaCodec(JNIEnv *env) {
560 return AndroidRuntime::registerNativeMethods(env,
561 "android/media/MediaCodec", gMethods, NELEM(gMethods));
562}