blob: 01d3833fabdb09e116b46ada35d9371edc4cd59c [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
Andreas Huber9e6bcce2012-04-06 12:14:47 -0700129status_t JMediaCodec::queueSecureInputBuffer(
130 size_t index,
131 size_t offset,
132 const CryptoPlugin::SubSample *subSamples,
133 size_t numSubSamples,
134 const uint8_t key[16],
135 const uint8_t iv[16],
136 CryptoPlugin::Mode mode,
137 int64_t presentationTimeUs,
138 uint32_t flags) {
139 return mCodec->queueSecureInputBuffer(
140 index, offset, subSamples, numSubSamples, key, iv, mode,
141 presentationTimeUs, flags);
142}
143
Andreas Huber88572f72012-02-21 11:47:18 -0800144status_t JMediaCodec::dequeueInputBuffer(size_t *index, int64_t timeoutUs) {
145 return mCodec->dequeueInputBuffer(index, timeoutUs);
146}
147
148status_t JMediaCodec::dequeueOutputBuffer(
149 JNIEnv *env, jobject bufferInfo, size_t *index, int64_t timeoutUs) {
150 size_t size, offset;
151 int64_t timeUs;
152 uint32_t flags;
153 status_t err;
154 if ((err = mCodec->dequeueOutputBuffer(
Andreas Huberc52b9802012-03-12 14:04:01 -0700155 index, &offset, &size, &timeUs, &flags, timeoutUs)) != OK) {
Andreas Huber88572f72012-02-21 11:47:18 -0800156 return err;
157 }
158
159 jclass clazz = env->FindClass("android/media/MediaCodec$BufferInfo");
160
161 jmethodID method = env->GetMethodID(clazz, "set", "(IIJI)V");
162 env->CallVoidMethod(bufferInfo, method, offset, size, timeUs, flags);
163
164 return OK;
165}
166
167status_t JMediaCodec::releaseOutputBuffer(size_t index, bool render) {
168 return render
169 ? mCodec->renderOutputBufferAndRelease(index)
170 : mCodec->releaseOutputBuffer(index);
171}
172
173status_t JMediaCodec::getOutputFormat(JNIEnv *env, jobject *format) const {
174 sp<AMessage> msg;
175 status_t err;
176 if ((err = mCodec->getOutputFormat(&msg)) != OK) {
177 return err;
178 }
179
180 return ConvertMessageToMap(env, msg, format);
181}
182
183status_t JMediaCodec::getBuffers(
184 JNIEnv *env, bool input, jobjectArray *bufArray) const {
185 Vector<sp<ABuffer> > buffers;
186
187 status_t err =
188 input
189 ? mCodec->getInputBuffers(&buffers)
190 : mCodec->getOutputBuffers(&buffers);
191
192 if (err != OK) {
193 return err;
194 }
195
196 jclass byteBufferClass = env->FindClass("java/nio/ByteBuffer");
197
198 *bufArray = (jobjectArray)env->NewObjectArray(
199 buffers.size(), byteBufferClass, NULL);
200
201 for (size_t i = 0; i < buffers.size(); ++i) {
202 const sp<ABuffer> &buffer = buffers.itemAt(i);
203
204 jobject byteBuffer =
205 env->NewDirectByteBuffer(
206 buffer->base(),
207 buffer->capacity());
208
209 env->SetObjectArrayElement(
210 *bufArray, i, byteBuffer);
211
212 env->DeleteLocalRef(byteBuffer);
213 byteBuffer = NULL;
214 }
215
216 return OK;
217}
218
219} // namespace android
220
221////////////////////////////////////////////////////////////////////////////////
222
223using namespace android;
224
225static sp<JMediaCodec> setMediaCodec(
226 JNIEnv *env, jobject thiz, const sp<JMediaCodec> &codec) {
227 sp<JMediaCodec> old = (JMediaCodec *)env->GetIntField(thiz, gFields.context);
228 if (codec != NULL) {
229 codec->incStrong(thiz);
230 }
231 if (old != NULL) {
232 old->decStrong(thiz);
233 }
234 env->SetIntField(thiz, gFields.context, (int)codec.get());
235
236 return old;
237}
238
239static sp<JMediaCodec> getMediaCodec(JNIEnv *env, jobject thiz) {
240 return (JMediaCodec *)env->GetIntField(thiz, gFields.context);
241}
242
243static void android_media_MediaCodec_release(JNIEnv *env, jobject thiz) {
244 setMediaCodec(env, thiz, NULL);
245}
246
247static jint throwExceptionAsNecessary(JNIEnv *env, status_t err) {
248 switch (err) {
249 case OK:
250 return 0;
251
252 case -EAGAIN:
253 return DEQUEUE_INFO_TRY_AGAIN_LATER;
254
255 case INFO_FORMAT_CHANGED:
256 return DEQUEUE_INFO_OUTPUT_FORMAT_CHANGED;
257
258 case INFO_OUTPUT_BUFFERS_CHANGED:
259 return DEQUEUE_INFO_OUTPUT_BUFFERS_CHANGED;
260
261 default:
262 {
263 jniThrowException(env, "java/lang/IllegalStateException", NULL);
264 break;
265 }
266 }
267
268 return 0;
269}
270
271static void android_media_MediaCodec_native_configure(
272 JNIEnv *env,
273 jobject thiz,
274 jobjectArray keys, jobjectArray values,
275 jobject jsurface,
Andreas Huber8240d922012-04-04 14:06:32 -0700276 jobject jcrypto,
Andreas Huber88572f72012-02-21 11:47:18 -0800277 jint flags) {
278 sp<JMediaCodec> codec = getMediaCodec(env, thiz);
279
280 if (codec == NULL) {
281 jniThrowException(env, "java/lang/IllegalStateException", NULL);
282 return;
283 }
284
285 sp<AMessage> format;
286 status_t err = ConvertKeyValueArraysToMessage(env, keys, values, &format);
287
288 if (err != OK) {
289 jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
290 return;
291 }
292
293 sp<ISurfaceTexture> surfaceTexture;
294 if (jsurface != NULL) {
295 sp<Surface> surface(Surface_getSurface(env, jsurface));
296 if (surface != NULL) {
297 surfaceTexture = surface->getSurfaceTexture();
298 } else {
299 jniThrowException(
300 env,
301 "java/lang/IllegalArgumentException",
302 "The surface has been released");
303 return;
304 }
305 }
306
Andreas Huber8240d922012-04-04 14:06:32 -0700307 sp<ICrypto> crypto;
308 if (jcrypto != NULL) {
309 crypto = JCrypto::GetCrypto(env, jcrypto);
310 }
311
312 err = codec->configure(format, surfaceTexture, crypto, flags);
Andreas Huber88572f72012-02-21 11:47:18 -0800313
314 throwExceptionAsNecessary(env, err);
315}
316
317static void android_media_MediaCodec_start(JNIEnv *env, jobject thiz) {
318 ALOGV("android_media_MediaCodec_start");
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->start();
328
329 throwExceptionAsNecessary(env, err);
330}
331
332static void android_media_MediaCodec_stop(JNIEnv *env, jobject thiz) {
333 ALOGV("android_media_MediaCodec_stop");
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->stop();
343
344 throwExceptionAsNecessary(env, err);
345}
346
347static void android_media_MediaCodec_flush(JNIEnv *env, jobject thiz) {
348 ALOGV("android_media_MediaCodec_flush");
349
350 sp<JMediaCodec> codec = getMediaCodec(env, thiz);
351
352 if (codec == NULL) {
353 jniThrowException(env, "java/lang/IllegalStateException", NULL);
354 return;
355 }
356
357 status_t err = codec->flush();
358
359 throwExceptionAsNecessary(env, err);
360}
361
362static void android_media_MediaCodec_queueInputBuffer(
363 JNIEnv *env,
364 jobject thiz,
365 jint index,
366 jint offset,
367 jint size,
368 jlong timestampUs,
369 jint flags) {
370 ALOGV("android_media_MediaCodec_queueInputBuffer");
371
372 sp<JMediaCodec> codec = getMediaCodec(env, thiz);
373
374 if (codec == NULL) {
375 jniThrowException(env, "java/lang/IllegalStateException", NULL);
376 return;
377 }
378
379 status_t err = codec->queueInputBuffer(
380 index, offset, size, timestampUs, flags);
381
382 throwExceptionAsNecessary(env, err);
383}
384
Andreas Huber9e6bcce2012-04-06 12:14:47 -0700385static void android_media_MediaCodec_queueSecureInputBuffer(
386 JNIEnv *env,
387 jobject thiz,
388 jint index,
389 jint offset,
390 jintArray numBytesOfClearDataObj,
391 jintArray numBytesOfEncryptedDataObj,
392 jint numSubSamples,
393 jbyteArray keyObj,
394 jbyteArray ivObj,
395 jint mode,
396 jlong timestampUs,
397 jint flags) {
398 ALOGV("android_media_MediaCodec_queueSecureInputBuffer");
399
400 sp<JMediaCodec> codec = getMediaCodec(env, thiz);
401
402 if (codec == NULL) {
403 jniThrowException(env, "java/lang/IllegalStateException", NULL);
404 return;
405 }
406
407 status_t err = OK;
408
409 CryptoPlugin::SubSample *subSamples = NULL;
410 jbyte *key = NULL;
411 jbyte *iv = NULL;
412
413 if (numSubSamples <= 0) {
414 err = -EINVAL;
415 } else if (numBytesOfClearDataObj == NULL
416 && numBytesOfEncryptedDataObj == NULL) {
417 err = -EINVAL;
418 } else if (numBytesOfEncryptedDataObj != NULL
419 && env->GetArrayLength(numBytesOfEncryptedDataObj) < numSubSamples) {
420 err = -ERANGE;
421 } else if (numBytesOfClearDataObj != NULL
422 && env->GetArrayLength(numBytesOfClearDataObj) < numSubSamples) {
423 err = -ERANGE;
424 } else {
425 jboolean isCopy;
426
427 jint *numBytesOfClearData =
428 (numBytesOfClearDataObj == NULL)
429 ? NULL
430 : env->GetIntArrayElements(numBytesOfClearDataObj, &isCopy);
431
432 jint *numBytesOfEncryptedData =
433 (numBytesOfEncryptedDataObj == NULL)
434 ? NULL
435 : env->GetIntArrayElements(numBytesOfEncryptedDataObj, &isCopy);
436
437 subSamples = new CryptoPlugin::SubSample[numSubSamples];
438
439 for (jint i = 0; i < numSubSamples; ++i) {
440 subSamples[i].mNumBytesOfClearData =
441 (numBytesOfClearData == NULL) ? 0 : numBytesOfClearData[i];
442
443 subSamples[i].mNumBytesOfEncryptedData =
444 (numBytesOfEncryptedData == NULL)
445 ? 0 : numBytesOfEncryptedData[i];
446 }
447
448 if (numBytesOfEncryptedData != NULL) {
449 env->ReleaseIntArrayElements(
450 numBytesOfEncryptedDataObj, numBytesOfEncryptedData, 0);
451 numBytesOfEncryptedData = NULL;
452 }
453
454 if (numBytesOfClearData != NULL) {
455 env->ReleaseIntArrayElements(
456 numBytesOfClearDataObj, numBytesOfClearData, 0);
457 numBytesOfClearData = NULL;
458 }
459 }
460
461 if (err == OK && keyObj != NULL) {
462 if (env->GetArrayLength(keyObj) != 16) {
463 err = -EINVAL;
464 } else {
465 jboolean isCopy;
466 key = env->GetByteArrayElements(keyObj, &isCopy);
467 }
468 }
469
470 if (err == OK && ivObj != NULL) {
471 if (env->GetArrayLength(ivObj) != 16) {
472 err = -EINVAL;
473 } else {
474 jboolean isCopy;
475 iv = env->GetByteArrayElements(ivObj, &isCopy);
476 }
477 }
478
479 if (err == OK) {
480 err = codec->queueSecureInputBuffer(
481 index, offset,
482 subSamples, numSubSamples,
483 (const uint8_t *)key, (const uint8_t *)iv,
484 (CryptoPlugin::Mode)mode,
485 timestampUs, flags);
486 }
487
488 if (iv != NULL) {
489 env->ReleaseByteArrayElements(ivObj, iv, 0);
490 iv = NULL;
491 }
492
493 if (key != NULL) {
494 env->ReleaseByteArrayElements(keyObj, key, 0);
495 key = NULL;
496 }
497
498 delete[] subSamples;
499 subSamples = NULL;
500
501 throwExceptionAsNecessary(env, err);
502}
503
Andreas Huber88572f72012-02-21 11:47:18 -0800504static jint android_media_MediaCodec_dequeueInputBuffer(
505 JNIEnv *env, jobject thiz, jlong timeoutUs) {
506 ALOGV("android_media_MediaCodec_dequeueInputBuffer");
507
508 sp<JMediaCodec> codec = getMediaCodec(env, thiz);
509
510 if (codec == NULL) {
511 jniThrowException(env, "java/lang/IllegalStateException", NULL);
512 return -1;
513 }
514
515 size_t index;
516 status_t err = codec->dequeueInputBuffer(&index, timeoutUs);
517
518 if (err == OK) {
519 return index;
520 }
521
522 return throwExceptionAsNecessary(env, err);
523}
524
525static jint android_media_MediaCodec_dequeueOutputBuffer(
526 JNIEnv *env, jobject thiz, jobject bufferInfo, jlong timeoutUs) {
527 ALOGV("android_media_MediaCodec_dequeueOutputBuffer");
528
529 sp<JMediaCodec> codec = getMediaCodec(env, thiz);
530
531 if (codec == NULL) {
532 jniThrowException(env, "java/lang/IllegalStateException", NULL);
Andreas Huber0e97fc22012-04-03 13:32:16 -0700533 return 0;
Andreas Huber88572f72012-02-21 11:47:18 -0800534 }
535
536 size_t index;
537 status_t err = codec->dequeueOutputBuffer(
538 env, bufferInfo, &index, timeoutUs);
539
540 if (err == OK) {
541 return index;
542 }
543
544 return throwExceptionAsNecessary(env, err);
545}
546
547static void android_media_MediaCodec_releaseOutputBuffer(
548 JNIEnv *env, jobject thiz, jint index, jboolean render) {
549 ALOGV("android_media_MediaCodec_renderOutputBufferAndRelease");
550
551 sp<JMediaCodec> codec = getMediaCodec(env, thiz);
552
553 if (codec == NULL) {
554 jniThrowException(env, "java/lang/IllegalStateException", NULL);
555 return;
556 }
557
558 status_t err = codec->releaseOutputBuffer(index, render);
559
560 throwExceptionAsNecessary(env, err);
561}
562
563static jobject android_media_MediaCodec_getOutputFormat(
564 JNIEnv *env, jobject thiz) {
565 ALOGV("android_media_MediaCodec_getOutputFormat");
566
567 sp<JMediaCodec> codec = getMediaCodec(env, thiz);
568
569 if (codec == NULL) {
570 jniThrowException(env, "java/lang/IllegalStateException", NULL);
571 return NULL;
572 }
573
574 jobject format;
575 status_t err = codec->getOutputFormat(env, &format);
576
577 if (err == OK) {
578 return format;
579 }
580
581 throwExceptionAsNecessary(env, err);
582
583 return NULL;
584}
585
586static jobjectArray android_media_MediaCodec_getBuffers(
587 JNIEnv *env, jobject thiz, jboolean input) {
588 ALOGV("android_media_MediaCodec_getBuffers");
589
590 sp<JMediaCodec> codec = getMediaCodec(env, thiz);
591
592 if (codec == NULL) {
593 jniThrowException(env, "java/lang/IllegalStateException", NULL);
594 return NULL;
595 }
596
597 jobjectArray buffers;
598 status_t err = codec->getBuffers(env, input, &buffers);
599
600 if (err == OK) {
601 return buffers;
602 }
603
604 throwExceptionAsNecessary(env, err);
605
606 return NULL;
607}
608
609static void android_media_MediaCodec_native_init(JNIEnv *env) {
610 jclass clazz = env->FindClass("android/media/MediaCodec");
611 CHECK(clazz != NULL);
612
613 gFields.context = env->GetFieldID(clazz, "mNativeContext", "I");
614 CHECK(gFields.context != NULL);
615}
616
617static void android_media_MediaCodec_native_setup(
618 JNIEnv *env, jobject thiz,
619 jstring name, jboolean nameIsType, jboolean encoder) {
620 if (name == NULL) {
621 jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
622 return;
623 }
624
625 const char *tmp = env->GetStringUTFChars(name, NULL);
626
627 if (tmp == NULL) {
628 return;
629 }
630
631 sp<JMediaCodec> codec = new JMediaCodec(env, thiz, tmp, nameIsType, encoder);
632
633 status_t err = codec->initCheck();
634
635 env->ReleaseStringUTFChars(name, tmp);
636 tmp = NULL;
637
638 if (err != OK) {
639 jniThrowException(
640 env,
641 "java/io/IOException",
642 "Failed to allocate component instance");
643 return;
644 }
645
646 setMediaCodec(env,thiz, codec);
647}
648
649static void android_media_MediaCodec_native_finalize(
650 JNIEnv *env, jobject thiz) {
651 android_media_MediaCodec_release(env, thiz);
652}
653
654static JNINativeMethod gMethods[] = {
655 { "release", "()V", (void *)android_media_MediaCodec_release },
656
657 { "native_configure",
Andreas Huber8240d922012-04-04 14:06:32 -0700658 "([Ljava/lang/String;[Ljava/lang/Object;Landroid/view/Surface;"
659 "Landroid/media/Crypto;I)V",
Andreas Huber88572f72012-02-21 11:47:18 -0800660 (void *)android_media_MediaCodec_native_configure },
661
662 { "start", "()V", (void *)android_media_MediaCodec_start },
663 { "stop", "()V", (void *)android_media_MediaCodec_stop },
664 { "flush", "()V", (void *)android_media_MediaCodec_flush },
665
666 { "queueInputBuffer", "(IIIJI)V",
667 (void *)android_media_MediaCodec_queueInputBuffer },
668
Andreas Huber9e6bcce2012-04-06 12:14:47 -0700669 { "queueSecureInputBuffer", "(II[I[II[B[BIJI)V",
670 (void *)android_media_MediaCodec_queueSecureInputBuffer },
671
Andreas Huber88572f72012-02-21 11:47:18 -0800672 { "dequeueInputBuffer", "(J)I",
673 (void *)android_media_MediaCodec_dequeueInputBuffer },
674
675 { "dequeueOutputBuffer", "(Landroid/media/MediaCodec$BufferInfo;J)I",
676 (void *)android_media_MediaCodec_dequeueOutputBuffer },
677
678 { "releaseOutputBuffer", "(IZ)V",
679 (void *)android_media_MediaCodec_releaseOutputBuffer },
680
681 { "getOutputFormat", "()Ljava/util/Map;",
682 (void *)android_media_MediaCodec_getOutputFormat },
683
684 { "getBuffers", "(Z)[Ljava/nio/ByteBuffer;",
685 (void *)android_media_MediaCodec_getBuffers },
686
687 { "native_init", "()V", (void *)android_media_MediaCodec_native_init },
688
689 { "native_setup", "(Ljava/lang/String;ZZ)V",
690 (void *)android_media_MediaCodec_native_setup },
691
692 { "native_finalize", "()V",
693 (void *)android_media_MediaCodec_native_finalize },
694};
695
696int register_android_media_MediaCodec(JNIEnv *env) {
697 return AndroidRuntime::registerNativeMethods(env,
698 "android/media/MediaCodec", gMethods, NELEM(gMethods));
699}