android_media_MediaCodec.cpp revision 91befdc0c4710234840cdfd853e7d30e8f9de62c
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 "MediaCodec-JNI"
19#include <utils/Log.h>
20
21#include "android_media_MediaCodec.h"
22
23#include "android_media_MediaCrypto.h"
24#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
30#include <gui/Surface.h>
31#include <gui/SurfaceTextureClient.h>
32
33#include <media/ICrypto.h>
34#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>
40
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    jfieldID cryptoInfoNumSubSamplesID;
54    jfieldID cryptoInfoNumBytesOfClearDataID;
55    jfieldID cryptoInfoNumBytesOfEncryptedDataID;
56    jfieldID cryptoInfoKeyID;
57    jfieldID cryptoInfoIVID;
58    jfieldID cryptoInfoModeID;
59};
60
61static fields_t gFields;
62
63////////////////////////////////////////////////////////////////////////////////
64
65JMediaCodec::JMediaCodec(
66        JNIEnv *env, jobject thiz,
67        const char *name, bool nameIsType, bool encoder)
68    : mClass(NULL),
69      mObject(NULL) {
70    jclass clazz = env->GetObjectClass(thiz);
71    CHECK(clazz != NULL);
72
73    mClass = (jclass)env->NewGlobalRef(clazz);
74    mObject = env->NewWeakGlobalRef(thiz);
75
76    mLooper = new ALooper;
77    mLooper->setName("MediaCodec_looper");
78
79    mLooper->start(
80            false,      // runOnCallingThread
81            false,       // canCallJava
82            PRIORITY_DEFAULT);
83
84    if (nameIsType) {
85        mCodec = MediaCodec::CreateByType(mLooper, name, encoder);
86    } else {
87        mCodec = MediaCodec::CreateByComponentName(mLooper, name);
88    }
89}
90
91status_t JMediaCodec::initCheck() const {
92    return mCodec != NULL ? OK : NO_INIT;
93}
94
95JMediaCodec::~JMediaCodec() {
96    mCodec->release();
97
98    JNIEnv *env = AndroidRuntime::getJNIEnv();
99
100    env->DeleteWeakGlobalRef(mObject);
101    mObject = NULL;
102    env->DeleteGlobalRef(mClass);
103    mClass = NULL;
104}
105
106status_t JMediaCodec::configure(
107        const sp<AMessage> &format,
108        const sp<ISurfaceTexture> &surfaceTexture,
109        const sp<ICrypto> &crypto,
110        int flags) {
111    sp<SurfaceTextureClient> client;
112    if (surfaceTexture != NULL) {
113        client = new SurfaceTextureClient(surfaceTexture);
114    }
115    return mCodec->configure(format, client, crypto, flags);
116}
117
118status_t JMediaCodec::start() {
119    return mCodec->start();
120}
121
122status_t JMediaCodec::stop() {
123    return mCodec->stop();
124}
125
126status_t JMediaCodec::flush() {
127    return mCodec->flush();
128}
129
130status_t JMediaCodec::queueInputBuffer(
131        size_t index,
132        size_t offset, size_t size, int64_t timeUs, uint32_t flags) {
133    return mCodec->queueInputBuffer(index, offset, size, timeUs, flags);
134}
135
136status_t JMediaCodec::queueSecureInputBuffer(
137        size_t index,
138        size_t offset,
139        const CryptoPlugin::SubSample *subSamples,
140        size_t numSubSamples,
141        const uint8_t key[16],
142        const uint8_t iv[16],
143        CryptoPlugin::Mode mode,
144        int64_t presentationTimeUs,
145        uint32_t flags) {
146    return mCodec->queueSecureInputBuffer(
147            index, offset, subSamples, numSubSamples, key, iv, mode,
148            presentationTimeUs, flags);
149}
150
151status_t JMediaCodec::dequeueInputBuffer(size_t *index, int64_t timeoutUs) {
152    return mCodec->dequeueInputBuffer(index, timeoutUs);
153}
154
155status_t JMediaCodec::dequeueOutputBuffer(
156        JNIEnv *env, jobject bufferInfo, size_t *index, int64_t timeoutUs) {
157    size_t size, offset;
158    int64_t timeUs;
159    uint32_t flags;
160    status_t err;
161    if ((err = mCodec->dequeueOutputBuffer(
162                    index, &offset, &size, &timeUs, &flags, timeoutUs)) != OK) {
163        return err;
164    }
165
166    jclass clazz = env->FindClass("android/media/MediaCodec$BufferInfo");
167
168    jmethodID method = env->GetMethodID(clazz, "set", "(IIJI)V");
169    env->CallVoidMethod(bufferInfo, method, offset, size, timeUs, flags);
170
171    return OK;
172}
173
174status_t JMediaCodec::releaseOutputBuffer(size_t index, bool render) {
175    return render
176        ? mCodec->renderOutputBufferAndRelease(index)
177        : mCodec->releaseOutputBuffer(index);
178}
179
180status_t JMediaCodec::getOutputFormat(JNIEnv *env, jobject *format) const {
181    sp<AMessage> msg;
182    status_t err;
183    if ((err = mCodec->getOutputFormat(&msg)) != OK) {
184        return err;
185    }
186
187    return ConvertMessageToMap(env, msg, format);
188}
189
190status_t JMediaCodec::getBuffers(
191        JNIEnv *env, bool input, jobjectArray *bufArray) const {
192    Vector<sp<ABuffer> > buffers;
193
194    status_t err =
195        input
196            ? mCodec->getInputBuffers(&buffers)
197            : mCodec->getOutputBuffers(&buffers);
198
199    if (err != OK) {
200        return err;
201    }
202
203    jclass byteBufferClass = env->FindClass("java/nio/ByteBuffer");
204
205    *bufArray = (jobjectArray)env->NewObjectArray(
206            buffers.size(), byteBufferClass, NULL);
207
208    for (size_t i = 0; i < buffers.size(); ++i) {
209        const sp<ABuffer> &buffer = buffers.itemAt(i);
210
211        jobject byteBuffer =
212            env->NewDirectByteBuffer(
213                buffer->base(),
214                buffer->capacity());
215
216        env->SetObjectArrayElement(
217                *bufArray, i, byteBuffer);
218
219        env->DeleteLocalRef(byteBuffer);
220        byteBuffer = NULL;
221    }
222
223    return OK;
224}
225
226}  // namespace android
227
228////////////////////////////////////////////////////////////////////////////////
229
230using namespace android;
231
232static sp<JMediaCodec> setMediaCodec(
233        JNIEnv *env, jobject thiz, const sp<JMediaCodec> &codec) {
234    sp<JMediaCodec> old = (JMediaCodec *)env->GetIntField(thiz, gFields.context);
235    if (codec != NULL) {
236        codec->incStrong(thiz);
237    }
238    if (old != NULL) {
239        old->decStrong(thiz);
240    }
241    env->SetIntField(thiz, gFields.context, (int)codec.get());
242
243    return old;
244}
245
246static sp<JMediaCodec> getMediaCodec(JNIEnv *env, jobject thiz) {
247    return (JMediaCodec *)env->GetIntField(thiz, gFields.context);
248}
249
250static void android_media_MediaCodec_release(JNIEnv *env, jobject thiz) {
251    setMediaCodec(env, thiz, NULL);
252}
253
254static jint throwExceptionAsNecessary(JNIEnv *env, status_t err) {
255    switch (err) {
256        case OK:
257            return 0;
258
259        case -EAGAIN:
260            return DEQUEUE_INFO_TRY_AGAIN_LATER;
261
262        case INFO_FORMAT_CHANGED:
263            return DEQUEUE_INFO_OUTPUT_FORMAT_CHANGED;
264
265        case INFO_OUTPUT_BUFFERS_CHANGED:
266            return DEQUEUE_INFO_OUTPUT_BUFFERS_CHANGED;
267
268        default:
269        {
270            jniThrowException(env, "java/lang/IllegalStateException", NULL);
271            break;
272        }
273    }
274
275    return 0;
276}
277
278static void android_media_MediaCodec_native_configure(
279        JNIEnv *env,
280        jobject thiz,
281        jobjectArray keys, jobjectArray values,
282        jobject jsurface,
283        jobject jcrypto,
284        jint flags) {
285    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
286
287    if (codec == NULL) {
288        jniThrowException(env, "java/lang/IllegalStateException", NULL);
289        return;
290    }
291
292    sp<AMessage> format;
293    status_t err = ConvertKeyValueArraysToMessage(env, keys, values, &format);
294
295    if (err != OK) {
296        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
297        return;
298    }
299
300    sp<ISurfaceTexture> surfaceTexture;
301    if (jsurface != NULL) {
302        sp<Surface> surface(Surface_getSurface(env, jsurface));
303        if (surface != NULL) {
304            surfaceTexture = surface->getSurfaceTexture();
305        } else {
306            jniThrowException(
307                    env,
308                    "java/lang/IllegalArgumentException",
309                    "The surface has been released");
310            return;
311        }
312    }
313
314    sp<ICrypto> crypto;
315    if (jcrypto != NULL) {
316        crypto = JCrypto::GetCrypto(env, jcrypto);
317    }
318
319    err = codec->configure(format, surfaceTexture, crypto, flags);
320
321    throwExceptionAsNecessary(env, err);
322}
323
324static void android_media_MediaCodec_start(JNIEnv *env, jobject thiz) {
325    ALOGV("android_media_MediaCodec_start");
326
327    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
328
329    if (codec == NULL) {
330        jniThrowException(env, "java/lang/IllegalStateException", NULL);
331        return;
332    }
333
334    status_t err = codec->start();
335
336    throwExceptionAsNecessary(env, err);
337}
338
339static void android_media_MediaCodec_stop(JNIEnv *env, jobject thiz) {
340    ALOGV("android_media_MediaCodec_stop");
341
342    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
343
344    if (codec == NULL) {
345        jniThrowException(env, "java/lang/IllegalStateException", NULL);
346        return;
347    }
348
349    status_t err = codec->stop();
350
351    throwExceptionAsNecessary(env, err);
352}
353
354static void android_media_MediaCodec_flush(JNIEnv *env, jobject thiz) {
355    ALOGV("android_media_MediaCodec_flush");
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->flush();
365
366    throwExceptionAsNecessary(env, err);
367}
368
369static void android_media_MediaCodec_queueInputBuffer(
370        JNIEnv *env,
371        jobject thiz,
372        jint index,
373        jint offset,
374        jint size,
375        jlong timestampUs,
376        jint flags) {
377    ALOGV("android_media_MediaCodec_queueInputBuffer");
378
379    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
380
381    if (codec == NULL) {
382        jniThrowException(env, "java/lang/IllegalStateException", NULL);
383        return;
384    }
385
386    status_t err = codec->queueInputBuffer(
387            index, offset, size, timestampUs, flags);
388
389    throwExceptionAsNecessary(env, err);
390}
391
392static void android_media_MediaCodec_queueSecureInputBuffer(
393        JNIEnv *env,
394        jobject thiz,
395        jint index,
396        jint offset,
397        jobject cryptoInfoObj,
398        jlong timestampUs,
399        jint flags) {
400    ALOGV("android_media_MediaCodec_queueSecureInputBuffer");
401
402    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
403
404    if (codec == NULL) {
405        jniThrowException(env, "java/lang/IllegalStateException", NULL);
406        return;
407    }
408
409    jint numSubSamples =
410        env->GetIntField(cryptoInfoObj, gFields.cryptoInfoNumSubSamplesID);
411
412    jintArray numBytesOfClearDataObj =
413        (jintArray)env->GetObjectField(
414                cryptoInfoObj, gFields.cryptoInfoNumBytesOfClearDataID);
415
416    jintArray numBytesOfEncryptedDataObj =
417        (jintArray)env->GetObjectField(
418                cryptoInfoObj, gFields.cryptoInfoNumBytesOfEncryptedDataID);
419
420    jbyteArray keyObj =
421        (jbyteArray)env->GetObjectField(cryptoInfoObj, gFields.cryptoInfoKeyID);
422
423    jbyteArray ivObj =
424        (jbyteArray)env->GetObjectField(cryptoInfoObj, gFields.cryptoInfoIVID);
425
426    jint mode = env->GetIntField(cryptoInfoObj, gFields.cryptoInfoModeID);
427
428    status_t err = OK;
429
430    CryptoPlugin::SubSample *subSamples = NULL;
431    jbyte *key = NULL;
432    jbyte *iv = NULL;
433
434    if (numSubSamples <= 0) {
435        err = -EINVAL;
436    } else if (numBytesOfClearDataObj == NULL
437            && numBytesOfEncryptedDataObj == NULL) {
438        err = -EINVAL;
439    } else if (numBytesOfEncryptedDataObj != NULL
440            && env->GetArrayLength(numBytesOfEncryptedDataObj) < numSubSamples) {
441        err = -ERANGE;
442    } else if (numBytesOfClearDataObj != NULL
443            && env->GetArrayLength(numBytesOfClearDataObj) < numSubSamples) {
444        err = -ERANGE;
445    } else {
446        jboolean isCopy;
447
448        jint *numBytesOfClearData =
449            (numBytesOfClearDataObj == NULL)
450                ? NULL
451                : env->GetIntArrayElements(numBytesOfClearDataObj, &isCopy);
452
453        jint *numBytesOfEncryptedData =
454            (numBytesOfEncryptedDataObj == NULL)
455                ? NULL
456                : env->GetIntArrayElements(numBytesOfEncryptedDataObj, &isCopy);
457
458        subSamples = new CryptoPlugin::SubSample[numSubSamples];
459
460        for (jint i = 0; i < numSubSamples; ++i) {
461            subSamples[i].mNumBytesOfClearData =
462                (numBytesOfClearData == NULL) ? 0 : numBytesOfClearData[i];
463
464            subSamples[i].mNumBytesOfEncryptedData =
465                (numBytesOfEncryptedData == NULL)
466                    ? 0 : numBytesOfEncryptedData[i];
467        }
468
469        if (numBytesOfEncryptedData != NULL) {
470            env->ReleaseIntArrayElements(
471                    numBytesOfEncryptedDataObj, numBytesOfEncryptedData, 0);
472            numBytesOfEncryptedData = NULL;
473        }
474
475        if (numBytesOfClearData != NULL) {
476            env->ReleaseIntArrayElements(
477                    numBytesOfClearDataObj, numBytesOfClearData, 0);
478            numBytesOfClearData = NULL;
479        }
480    }
481
482    if (err == OK && keyObj != NULL) {
483        if (env->GetArrayLength(keyObj) != 16) {
484            err = -EINVAL;
485        } else {
486            jboolean isCopy;
487            key = env->GetByteArrayElements(keyObj, &isCopy);
488        }
489    }
490
491    if (err == OK && ivObj != NULL) {
492        if (env->GetArrayLength(ivObj) != 16) {
493            err = -EINVAL;
494        } else {
495            jboolean isCopy;
496            iv = env->GetByteArrayElements(ivObj, &isCopy);
497        }
498    }
499
500    if (err == OK) {
501        err = codec->queueSecureInputBuffer(
502                index, offset,
503                subSamples, numSubSamples,
504                (const uint8_t *)key, (const uint8_t *)iv,
505                (CryptoPlugin::Mode)mode,
506                timestampUs, flags);
507    }
508
509    if (iv != NULL) {
510        env->ReleaseByteArrayElements(ivObj, iv, 0);
511        iv = NULL;
512    }
513
514    if (key != NULL) {
515        env->ReleaseByteArrayElements(keyObj, key, 0);
516        key = NULL;
517    }
518
519    delete[] subSamples;
520    subSamples = NULL;
521
522    throwExceptionAsNecessary(env, err);
523}
524
525static jint android_media_MediaCodec_dequeueInputBuffer(
526        JNIEnv *env, jobject thiz, jlong timeoutUs) {
527    ALOGV("android_media_MediaCodec_dequeueInputBuffer");
528
529    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
530
531    if (codec == NULL) {
532        jniThrowException(env, "java/lang/IllegalStateException", NULL);
533        return -1;
534    }
535
536    size_t index;
537    status_t err = codec->dequeueInputBuffer(&index, timeoutUs);
538
539    if (err == OK) {
540        return index;
541    }
542
543    return throwExceptionAsNecessary(env, err);
544}
545
546static jint android_media_MediaCodec_dequeueOutputBuffer(
547        JNIEnv *env, jobject thiz, jobject bufferInfo, jlong timeoutUs) {
548    ALOGV("android_media_MediaCodec_dequeueOutputBuffer");
549
550    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
551
552    if (codec == NULL) {
553        jniThrowException(env, "java/lang/IllegalStateException", NULL);
554        return 0;
555    }
556
557    size_t index;
558    status_t err = codec->dequeueOutputBuffer(
559            env, bufferInfo, &index, timeoutUs);
560
561    if (err == OK) {
562        return index;
563    }
564
565    return throwExceptionAsNecessary(env, err);
566}
567
568static void android_media_MediaCodec_releaseOutputBuffer(
569        JNIEnv *env, jobject thiz, jint index, jboolean render) {
570    ALOGV("android_media_MediaCodec_renderOutputBufferAndRelease");
571
572    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
573
574    if (codec == NULL) {
575        jniThrowException(env, "java/lang/IllegalStateException", NULL);
576        return;
577    }
578
579    status_t err = codec->releaseOutputBuffer(index, render);
580
581    throwExceptionAsNecessary(env, err);
582}
583
584static jobject android_media_MediaCodec_getOutputFormat(
585        JNIEnv *env, jobject thiz) {
586    ALOGV("android_media_MediaCodec_getOutputFormat");
587
588    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
589
590    if (codec == NULL) {
591        jniThrowException(env, "java/lang/IllegalStateException", NULL);
592        return NULL;
593    }
594
595    jobject format;
596    status_t err = codec->getOutputFormat(env, &format);
597
598    if (err == OK) {
599        return format;
600    }
601
602    throwExceptionAsNecessary(env, err);
603
604    return NULL;
605}
606
607static jobjectArray android_media_MediaCodec_getBuffers(
608        JNIEnv *env, jobject thiz, jboolean input) {
609    ALOGV("android_media_MediaCodec_getBuffers");
610
611    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
612
613    if (codec == NULL) {
614        jniThrowException(env, "java/lang/IllegalStateException", NULL);
615        return NULL;
616    }
617
618    jobjectArray buffers;
619    status_t err = codec->getBuffers(env, input, &buffers);
620
621    if (err == OK) {
622        return buffers;
623    }
624
625    throwExceptionAsNecessary(env, err);
626
627    return NULL;
628}
629
630static void android_media_MediaCodec_native_init(JNIEnv *env) {
631    jclass clazz = env->FindClass("android/media/MediaCodec");
632    CHECK(clazz != NULL);
633
634    gFields.context = env->GetFieldID(clazz, "mNativeContext", "I");
635    CHECK(gFields.context != NULL);
636
637    clazz = env->FindClass("android/media/MediaCodec$CryptoInfo");
638    CHECK(clazz != NULL);
639
640    gFields.cryptoInfoNumSubSamplesID =
641        env->GetFieldID(clazz, "numSubSamples", "I");
642    CHECK(gFields.cryptoInfoNumSubSamplesID != NULL);
643
644    gFields.cryptoInfoNumBytesOfClearDataID =
645        env->GetFieldID(clazz, "numBytesOfClearData", "[I");
646    CHECK(gFields.cryptoInfoNumBytesOfClearDataID != NULL);
647
648    gFields.cryptoInfoNumBytesOfEncryptedDataID =
649        env->GetFieldID(clazz, "numBytesOfEncryptedData", "[I");
650    CHECK(gFields.cryptoInfoNumBytesOfEncryptedDataID != NULL);
651
652    gFields.cryptoInfoKeyID = env->GetFieldID(clazz, "key", "[B");
653    CHECK(gFields.cryptoInfoKeyID != NULL);
654
655    gFields.cryptoInfoIVID = env->GetFieldID(clazz, "iv", "[B");
656    CHECK(gFields.cryptoInfoIVID != NULL);
657
658    gFields.cryptoInfoModeID = env->GetFieldID(clazz, "mode", "I");
659    CHECK(gFields.cryptoInfoModeID != NULL);
660}
661
662static void android_media_MediaCodec_native_setup(
663        JNIEnv *env, jobject thiz,
664        jstring name, jboolean nameIsType, jboolean encoder) {
665    if (name == NULL) {
666        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
667        return;
668    }
669
670    const char *tmp = env->GetStringUTFChars(name, NULL);
671
672    if (tmp == NULL) {
673        return;
674    }
675
676    sp<JMediaCodec> codec = new JMediaCodec(env, thiz, tmp, nameIsType, encoder);
677
678    status_t err = codec->initCheck();
679
680    env->ReleaseStringUTFChars(name, tmp);
681    tmp = NULL;
682
683    if (err != OK) {
684        jniThrowException(
685                env,
686                "java/io/IOException",
687                "Failed to allocate component instance");
688        return;
689    }
690
691    setMediaCodec(env,thiz, codec);
692}
693
694static void android_media_MediaCodec_native_finalize(
695        JNIEnv *env, jobject thiz) {
696    android_media_MediaCodec_release(env, thiz);
697}
698
699static JNINativeMethod gMethods[] = {
700    { "release", "()V", (void *)android_media_MediaCodec_release },
701
702    { "native_configure",
703      "([Ljava/lang/String;[Ljava/lang/Object;Landroid/view/Surface;"
704      "Landroid/media/MediaCrypto;I)V",
705      (void *)android_media_MediaCodec_native_configure },
706
707    { "start", "()V", (void *)android_media_MediaCodec_start },
708    { "stop", "()V", (void *)android_media_MediaCodec_stop },
709    { "flush", "()V", (void *)android_media_MediaCodec_flush },
710
711    { "queueInputBuffer", "(IIIJI)V",
712      (void *)android_media_MediaCodec_queueInputBuffer },
713
714    { "queueSecureInputBuffer", "(IILandroid/media/MediaCodec$CryptoInfo;JI)V",
715      (void *)android_media_MediaCodec_queueSecureInputBuffer },
716
717    { "dequeueInputBuffer", "(J)I",
718      (void *)android_media_MediaCodec_dequeueInputBuffer },
719
720    { "dequeueOutputBuffer", "(Landroid/media/MediaCodec$BufferInfo;J)I",
721      (void *)android_media_MediaCodec_dequeueOutputBuffer },
722
723    { "releaseOutputBuffer", "(IZ)V",
724      (void *)android_media_MediaCodec_releaseOutputBuffer },
725
726    { "getOutputFormat", "()Ljava/util/Map;",
727      (void *)android_media_MediaCodec_getOutputFormat },
728
729    { "getBuffers", "(Z)[Ljava/nio/ByteBuffer;",
730      (void *)android_media_MediaCodec_getBuffers },
731
732    { "native_init", "()V", (void *)android_media_MediaCodec_native_init },
733
734    { "native_setup", "(Ljava/lang/String;ZZ)V",
735      (void *)android_media_MediaCodec_native_setup },
736
737    { "native_finalize", "()V",
738      (void *)android_media_MediaCodec_native_finalize },
739};
740
741int register_android_media_MediaCodec(JNIEnv *env) {
742    return AndroidRuntime::registerNativeMethods(env,
743                "android/media/MediaCodec", gMethods, NELEM(gMethods));
744}
745