android_media_MediaCodec.cpp revision 1e6e8018a4f914210b615bfca0f818fd13574228
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 <cutils/compiler.h>
31
32#include <gui/Surface.h>
33
34#include <media/ICrypto.h>
35#include <media/stagefright/MediaCodec.h>
36#include <media/stagefright/foundation/ABuffer.h>
37#include <media/stagefright/foundation/ADebug.h>
38#include <media/stagefright/foundation/ALooper.h>
39#include <media/stagefright/foundation/AMessage.h>
40#include <media/stagefright/foundation/AString.h>
41#include <media/stagefright/MediaErrors.h>
42
43#include <nativehelper/ScopedLocalRef.h>
44
45#include <system/window.h>
46
47namespace android {
48
49// Keep these in sync with their equivalents in MediaCodec.java !!!
50enum {
51    DEQUEUE_INFO_TRY_AGAIN_LATER            = -1,
52    DEQUEUE_INFO_OUTPUT_FORMAT_CHANGED      = -2,
53    DEQUEUE_INFO_OUTPUT_BUFFERS_CHANGED     = -3,
54};
55
56enum {
57    EVENT_CALLBACK = 1,
58    EVENT_SET_CALLBACK = 2,
59};
60
61struct CryptoErrorCodes {
62    jint cryptoErrorNoKey;
63    jint cryptoErrorKeyExpired;
64    jint cryptoErrorResourceBusy;
65} gCryptoErrorCodes;
66
67struct fields_t {
68    jfieldID context;
69    jmethodID postEventFromNativeID;
70    jfieldID cryptoInfoNumSubSamplesID;
71    jfieldID cryptoInfoNumBytesOfClearDataID;
72    jfieldID cryptoInfoNumBytesOfEncryptedDataID;
73    jfieldID cryptoInfoKeyID;
74    jfieldID cryptoInfoIVID;
75    jfieldID cryptoInfoModeID;
76};
77
78static fields_t gFields;
79
80////////////////////////////////////////////////////////////////////////////////
81
82JMediaCodec::JMediaCodec(
83        JNIEnv *env, jobject thiz,
84        const char *name, bool nameIsType, bool encoder)
85    : mClass(NULL),
86      mObject(NULL) {
87    jclass clazz = env->GetObjectClass(thiz);
88    CHECK(clazz != NULL);
89
90    mClass = (jclass)env->NewGlobalRef(clazz);
91    mObject = env->NewWeakGlobalRef(thiz);
92
93    mLooper = new ALooper;
94    mLooper->setName("MediaCodec_looper");
95
96    mLooper->start(
97            false,      // runOnCallingThread
98            true,       // canCallJava
99            PRIORITY_FOREGROUND);
100
101    if (nameIsType) {
102        mCodec = MediaCodec::CreateByType(mLooper, name, encoder);
103    } else {
104        mCodec = MediaCodec::CreateByComponentName(mLooper, name);
105    }
106}
107
108status_t JMediaCodec::initCheck() const {
109    return mCodec != NULL ? OK : NO_INIT;
110}
111
112void JMediaCodec::registerSelf() {
113    mLooper->registerHandler(this);
114}
115
116void JMediaCodec::release() {
117    if (mCodec != NULL) {
118        mCodec->release();
119        mCodec.clear();
120    }
121
122    if (mLooper != NULL) {
123        mLooper->unregisterHandler(id());
124        mLooper->stop();
125        mLooper.clear();
126    }
127}
128
129JMediaCodec::~JMediaCodec() {
130    if (mCodec != NULL || mLooper != NULL) {
131        /* MediaCodec and looper should have been released explicitly already
132         * in setMediaCodec() (see comments in setMediaCodec()).
133         *
134         * Otherwise JMediaCodec::~JMediaCodec() might be called from within the
135         * message handler, doing release() there risks deadlock as MediaCodec::
136         * release() post synchronous message to the same looper.
137         *
138         * Print a warning and try to proceed with releasing.
139         */
140        ALOGW("try to release MediaCodec from JMediaCodec::~JMediaCodec()...");
141        release();
142        ALOGW("done releasing MediaCodec from JMediaCodec::~JMediaCodec().");
143    }
144
145    JNIEnv *env = AndroidRuntime::getJNIEnv();
146
147    env->DeleteWeakGlobalRef(mObject);
148    mObject = NULL;
149    env->DeleteGlobalRef(mClass);
150    mClass = NULL;
151}
152
153status_t JMediaCodec::setCallback(jobject cb) {
154    if (cb != NULL) {
155        if (mCallbackNotification == NULL) {
156            mCallbackNotification = new AMessage(kWhatCallbackNotify, id());
157        }
158    } else {
159        mCallbackNotification.clear();
160    }
161
162    return mCodec->setCallback(mCallbackNotification);
163}
164
165status_t JMediaCodec::configure(
166        const sp<AMessage> &format,
167        const sp<IGraphicBufferProducer> &bufferProducer,
168        const sp<ICrypto> &crypto,
169        int flags) {
170    sp<Surface> client;
171    if (bufferProducer != NULL) {
172        mSurfaceTextureClient =
173            new Surface(bufferProducer, true /* controlledByApp */);
174    } else {
175        mSurfaceTextureClient.clear();
176    }
177
178    return mCodec->configure(format, mSurfaceTextureClient, crypto, flags);
179}
180
181status_t JMediaCodec::createInputSurface(
182        sp<IGraphicBufferProducer>* bufferProducer) {
183    return mCodec->createInputSurface(bufferProducer);
184}
185
186status_t JMediaCodec::start() {
187    return mCodec->start();
188}
189
190status_t JMediaCodec::stop() {
191    mSurfaceTextureClient.clear();
192
193    return mCodec->stop();
194}
195
196status_t JMediaCodec::flush() {
197    return mCodec->flush();
198}
199
200status_t JMediaCodec::reset() {
201    return mCodec->reset();
202}
203
204status_t JMediaCodec::queueInputBuffer(
205        size_t index,
206        size_t offset, size_t size, int64_t timeUs, uint32_t flags,
207        AString *errorDetailMsg) {
208    return mCodec->queueInputBuffer(
209            index, offset, size, timeUs, flags, errorDetailMsg);
210}
211
212status_t JMediaCodec::queueSecureInputBuffer(
213        size_t index,
214        size_t offset,
215        const CryptoPlugin::SubSample *subSamples,
216        size_t numSubSamples,
217        const uint8_t key[16],
218        const uint8_t iv[16],
219        CryptoPlugin::Mode mode,
220        int64_t presentationTimeUs,
221        uint32_t flags,
222        AString *errorDetailMsg) {
223    return mCodec->queueSecureInputBuffer(
224            index, offset, subSamples, numSubSamples, key, iv, mode,
225            presentationTimeUs, flags, errorDetailMsg);
226}
227
228status_t JMediaCodec::dequeueInputBuffer(size_t *index, int64_t timeoutUs) {
229    return mCodec->dequeueInputBuffer(index, timeoutUs);
230}
231
232status_t JMediaCodec::dequeueOutputBuffer(
233        JNIEnv *env, jobject bufferInfo, size_t *index, int64_t timeoutUs) {
234    size_t size, offset;
235    int64_t timeUs;
236    uint32_t flags;
237    status_t err = mCodec->dequeueOutputBuffer(
238            index, &offset, &size, &timeUs, &flags, timeoutUs);
239
240    if (err != OK) {
241        return err;
242    }
243
244    ScopedLocalRef<jclass> clazz(
245            env, env->FindClass("android/media/MediaCodec$BufferInfo"));
246
247    jmethodID method = env->GetMethodID(clazz.get(), "set", "(IIJI)V");
248    env->CallVoidMethod(bufferInfo, method, (jint)offset, (jint)size, timeUs, flags);
249
250    return OK;
251}
252
253status_t JMediaCodec::releaseOutputBuffer(
254        size_t index, bool render, bool updatePTS, int64_t timestampNs) {
255    if (updatePTS) {
256        return mCodec->renderOutputBufferAndRelease(index, timestampNs);
257    }
258    return render
259        ? mCodec->renderOutputBufferAndRelease(index)
260        : mCodec->releaseOutputBuffer(index);
261}
262
263status_t JMediaCodec::signalEndOfInputStream() {
264    return mCodec->signalEndOfInputStream();
265}
266
267status_t JMediaCodec::getFormat(JNIEnv *env, bool input, jobject *format) const {
268    sp<AMessage> msg;
269    status_t err;
270    err = input ? mCodec->getInputFormat(&msg) : mCodec->getOutputFormat(&msg);
271    if (err != OK) {
272        return err;
273    }
274
275    return ConvertMessageToMap(env, msg, format);
276}
277
278status_t JMediaCodec::getOutputFormat(JNIEnv *env, size_t index, jobject *format) const {
279    sp<AMessage> msg;
280    status_t err;
281    if ((err = mCodec->getOutputFormat(index, &msg)) != OK) {
282        return err;
283    }
284
285    return ConvertMessageToMap(env, msg, format);
286}
287
288status_t JMediaCodec::getBuffers(
289        JNIEnv *env, bool input, jobjectArray *bufArray) const {
290    Vector<sp<ABuffer> > buffers;
291
292    status_t err =
293        input
294            ? mCodec->getInputBuffers(&buffers)
295            : mCodec->getOutputBuffers(&buffers);
296
297    if (err != OK) {
298        return err;
299    }
300
301    ScopedLocalRef<jclass> byteBufferClass(
302            env, env->FindClass("java/nio/ByteBuffer"));
303
304    CHECK(byteBufferClass.get() != NULL);
305
306    jmethodID orderID = env->GetMethodID(
307            byteBufferClass.get(),
308            "order",
309            "(Ljava/nio/ByteOrder;)Ljava/nio/ByteBuffer;");
310
311    CHECK(orderID != NULL);
312
313    jmethodID asReadOnlyBufferID = env->GetMethodID(
314            byteBufferClass.get(), "asReadOnlyBuffer", "()Ljava/nio/ByteBuffer;");
315
316    CHECK(asReadOnlyBufferID != NULL);
317
318    ScopedLocalRef<jclass> byteOrderClass(
319            env, env->FindClass("java/nio/ByteOrder"));
320
321    CHECK(byteOrderClass.get() != NULL);
322
323    jmethodID nativeOrderID = env->GetStaticMethodID(
324            byteOrderClass.get(), "nativeOrder", "()Ljava/nio/ByteOrder;");
325    CHECK(nativeOrderID != NULL);
326
327    jobject nativeByteOrderObj =
328        env->CallStaticObjectMethod(byteOrderClass.get(), nativeOrderID);
329    CHECK(nativeByteOrderObj != NULL);
330
331    *bufArray = (jobjectArray)env->NewObjectArray(
332            buffers.size(), byteBufferClass.get(), NULL);
333    if (*bufArray == NULL) {
334        env->DeleteLocalRef(nativeByteOrderObj);
335        return NO_MEMORY;
336    }
337
338    for (size_t i = 0; i < buffers.size(); ++i) {
339        const sp<ABuffer> &buffer = buffers.itemAt(i);
340
341        // if this is an ABuffer that doesn't actually hold any accessible memory,
342        // use a null ByteBuffer
343        if (buffer->base() == NULL) {
344            continue;
345        }
346        jobject byteBuffer =
347            env->NewDirectByteBuffer(
348                buffer->base(),
349                buffer->capacity());
350        if (!input && byteBuffer != NULL) {
351            jobject readOnlyBuffer = env->CallObjectMethod(
352                    byteBuffer, asReadOnlyBufferID);
353            env->DeleteLocalRef(byteBuffer);
354            byteBuffer = readOnlyBuffer;
355        }
356        if (byteBuffer == NULL) {
357            env->DeleteLocalRef(nativeByteOrderObj);
358            return NO_MEMORY;
359        }
360        jobject me = env->CallObjectMethod(
361                byteBuffer, orderID, nativeByteOrderObj);
362        env->DeleteLocalRef(me);
363        me = NULL;
364
365        env->SetObjectArrayElement(
366                *bufArray, i, byteBuffer);
367
368        env->DeleteLocalRef(byteBuffer);
369        byteBuffer = NULL;
370    }
371
372    env->DeleteLocalRef(nativeByteOrderObj);
373    nativeByteOrderObj = NULL;
374
375    return OK;
376}
377
378status_t JMediaCodec::getBuffer(
379        JNIEnv *env, bool input, size_t index, jobject *buf) const {
380    sp<ABuffer> buffer;
381
382    status_t err =
383        input
384            ? mCodec->getInputBuffer(index, &buffer)
385            : mCodec->getOutputBuffer(index, &buffer);
386
387    if (err != OK) {
388        return err;
389    }
390
391    ScopedLocalRef<jclass> byteBufferClass(
392            env, env->FindClass("java/nio/ByteBuffer"));
393
394    CHECK(byteBufferClass.get() != NULL);
395
396    jmethodID orderID = env->GetMethodID(
397            byteBufferClass.get(),
398            "order",
399            "(Ljava/nio/ByteOrder;)Ljava/nio/ByteBuffer;");
400
401    CHECK(orderID != NULL);
402
403    jmethodID asReadOnlyBufferID = env->GetMethodID(
404            byteBufferClass.get(), "asReadOnlyBuffer", "()Ljava/nio/ByteBuffer;");
405
406    CHECK(asReadOnlyBufferID != NULL);
407
408    jmethodID positionID = env->GetMethodID(
409            byteBufferClass.get(), "position", "(I)Ljava/nio/Buffer;");
410
411    CHECK(positionID != NULL);
412
413    jmethodID limitID = env->GetMethodID(
414            byteBufferClass.get(), "limit", "(I)Ljava/nio/Buffer;");
415
416    CHECK(limitID != NULL);
417
418    ScopedLocalRef<jclass> byteOrderClass(
419            env, env->FindClass("java/nio/ByteOrder"));
420
421    CHECK(byteOrderClass.get() != NULL);
422
423    jmethodID nativeOrderID = env->GetStaticMethodID(
424            byteOrderClass.get(), "nativeOrder", "()Ljava/nio/ByteOrder;");
425    CHECK(nativeOrderID != NULL);
426
427    jobject nativeByteOrderObj =
428        env->CallStaticObjectMethod(byteOrderClass.get(), nativeOrderID);
429    CHECK(nativeByteOrderObj != NULL);
430
431    // if this is an ABuffer that doesn't actually hold any accessible memory,
432    // use a null ByteBuffer
433    if (buffer->base() == NULL) {
434        *buf = NULL;
435        return OK;
436    }
437
438    jobject byteBuffer =
439        env->NewDirectByteBuffer(
440            buffer->base(),
441            buffer->capacity());
442    if (!input && byteBuffer != NULL) {
443        jobject readOnlyBuffer = env->CallObjectMethod(
444                byteBuffer, asReadOnlyBufferID);
445        env->DeleteLocalRef(byteBuffer);
446        byteBuffer = readOnlyBuffer;
447    }
448    if (byteBuffer == NULL) {
449        env->DeleteLocalRef(nativeByteOrderObj);
450        return NO_MEMORY;
451    }
452    jobject me = env->CallObjectMethod(
453            byteBuffer, orderID, nativeByteOrderObj);
454    env->DeleteLocalRef(me);
455    me = env->CallObjectMethod(
456            byteBuffer, limitID,
457            input ? buffer->capacity() : (buffer->offset() + buffer->size()));
458    env->DeleteLocalRef(me);
459    me = env->CallObjectMethod(
460            byteBuffer, positionID,
461            input ? 0 : buffer->offset());
462    env->DeleteLocalRef(me);
463    me = NULL;
464
465    env->DeleteLocalRef(nativeByteOrderObj);
466    nativeByteOrderObj = NULL;
467
468    *buf = byteBuffer;
469    return OK;
470}
471
472status_t JMediaCodec::getImage(
473        JNIEnv *env, bool input, size_t index, jobject *buf) const {
474    sp<ABuffer> buffer;
475
476    status_t err =
477        input
478            ? mCodec->getInputBuffer(index, &buffer)
479            : mCodec->getOutputBuffer(index, &buffer);
480
481    if (err != OK) {
482        return err;
483    }
484
485    // if this is an ABuffer that doesn't actually hold any accessible memory,
486    // use a null ByteBuffer
487    *buf = NULL;
488    if (buffer->base() == NULL) {
489        return OK;
490    }
491
492    // check if buffer is an image
493    AString imageData;
494    if (!buffer->meta()->findString("image-data", &imageData)) {
495        return OK;
496    }
497
498    return OK;
499}
500
501
502status_t JMediaCodec::getName(JNIEnv *env, jstring *nameStr) const {
503    AString name;
504
505    status_t err = mCodec->getName(&name);
506
507    if (err != OK) {
508        return err;
509    }
510
511    *nameStr = env->NewStringUTF(name.c_str());
512
513    return OK;
514}
515
516status_t JMediaCodec::setParameters(const sp<AMessage> &msg) {
517    return mCodec->setParameters(msg);
518}
519
520void JMediaCodec::setVideoScalingMode(int mode) {
521    if (mSurfaceTextureClient != NULL) {
522        native_window_set_scaling_mode(mSurfaceTextureClient.get(), mode);
523    }
524}
525
526void JMediaCodec::handleCallback(const sp<AMessage> &msg) {
527    int32_t arg1, arg2 = 0;
528    jobject obj = NULL;
529    CHECK(msg->findInt32("callbackID", &arg1));
530    JNIEnv *env = AndroidRuntime::getJNIEnv();
531
532    switch (arg1) {
533        case MediaCodec::CB_INPUT_AVAILABLE:
534        {
535            CHECK(msg->findInt32("index", &arg2));
536            break;
537        }
538
539        case MediaCodec::CB_OUTPUT_AVAILABLE:
540        {
541            CHECK(msg->findInt32("index", &arg2));
542
543            size_t size, offset;
544            int64_t timeUs;
545            uint32_t flags;
546            CHECK(msg->findSize("size", &size));
547            CHECK(msg->findSize("offset", &offset));
548            CHECK(msg->findInt64("timeUs", &timeUs));
549            CHECK(msg->findInt32("flags", (int32_t *)&flags));
550
551            ScopedLocalRef<jclass> clazz(
552                    env, env->FindClass("android/media/MediaCodec$BufferInfo"));
553            jmethodID ctor = env->GetMethodID(clazz.get(), "<init>", "()V");
554            jmethodID method = env->GetMethodID(clazz.get(), "set", "(IIJI)V");
555
556            obj = env->NewObject(clazz.get(), ctor);
557
558            if (obj == NULL) {
559                if (env->ExceptionCheck()) {
560                    ALOGE("Could not create MediaCodec.BufferInfo.");
561                    env->ExceptionClear();
562                }
563                jniThrowException(env, "java/lang/IllegalStateException", NULL);
564                return;
565            }
566
567            env->CallVoidMethod(obj, method, (jint)offset, (jint)size, timeUs, flags);
568            break;
569        }
570
571        case MediaCodec::CB_ERROR:
572        {
573            int32_t err, actionCode;
574            CHECK(msg->findInt32("err", &err));
575            CHECK(msg->findInt32("actionCode", &actionCode));
576
577            // use Integer object to pass the action code
578            ScopedLocalRef<jclass> clazz(
579                    env, env->FindClass("android/media/MediaCodec$CodecException"));
580            jmethodID ctor = env->GetMethodID(clazz.get(), "<init>", "(IILjava/lang/String;)V");
581
582            AString str;
583            const char *detail = "Unknown error";
584            if (msg->findString("detail", &str)) {
585                detail = str.c_str();
586            }
587            jstring msgObj = env->NewStringUTF(detail);
588
589            obj = env->NewObject(clazz.get(), ctor, err, actionCode, msgObj);
590
591            if (obj == NULL) {
592                if (env->ExceptionCheck()) {
593                    ALOGE("Could not create CodecException object.");
594                    env->ExceptionClear();
595                }
596                jniThrowException(env, "java/lang/IllegalStateException", NULL);
597                return;
598            }
599
600            break;
601        }
602
603        case MediaCodec::CB_OUTPUT_FORMAT_CHANGED:
604        {
605            sp<AMessage> format;
606            CHECK(msg->findMessage("format", &format));
607
608            if (OK != ConvertMessageToMap(env, format, &obj)) {
609                jniThrowException(env, "java/lang/IllegalStateException", NULL);
610                return;
611            }
612
613            break;
614        }
615
616        default:
617            TRESPASS();
618    }
619
620    env->CallVoidMethod(
621            mObject,
622            gFields.postEventFromNativeID,
623            EVENT_CALLBACK,
624            arg1,
625            arg2,
626            obj);
627
628    env->DeleteLocalRef(obj);
629}
630
631void JMediaCodec::onMessageReceived(const sp<AMessage> &msg) {
632    switch (msg->what()) {
633        case kWhatCallbackNotify:
634        {
635            handleCallback(msg);
636            break;
637        }
638        default:
639            TRESPASS();
640    }
641}
642
643}  // namespace android
644
645////////////////////////////////////////////////////////////////////////////////
646
647using namespace android;
648
649static sp<JMediaCodec> setMediaCodec(
650        JNIEnv *env, jobject thiz, const sp<JMediaCodec> &codec) {
651    sp<JMediaCodec> old = (JMediaCodec *)env->GetLongField(thiz, gFields.context);
652    if (codec != NULL) {
653        codec->incStrong(thiz);
654    }
655    if (old != NULL) {
656        /* release MediaCodec and stop the looper now before decStrong.
657         * otherwise JMediaCodec::~JMediaCodec() could be called from within
658         * its message handler, doing release() from there will deadlock
659         * (as MediaCodec::release() post synchronous message to the same looper)
660         */
661        old->release();
662        old->decStrong(thiz);
663    }
664    env->SetLongField(thiz, gFields.context, (jlong)codec.get());
665
666    return old;
667}
668
669static sp<JMediaCodec> getMediaCodec(JNIEnv *env, jobject thiz) {
670    return (JMediaCodec *)env->GetLongField(thiz, gFields.context);
671}
672
673static void android_media_MediaCodec_release(JNIEnv *env, jobject thiz) {
674    setMediaCodec(env, thiz, NULL);
675}
676
677static void throwCryptoException(JNIEnv *env, status_t err, const char *msg) {
678    ScopedLocalRef<jclass> clazz(
679            env, env->FindClass("android/media/MediaCodec$CryptoException"));
680    CHECK(clazz.get() != NULL);
681
682    jmethodID constructID =
683        env->GetMethodID(clazz.get(), "<init>", "(ILjava/lang/String;)V");
684    CHECK(constructID != NULL);
685
686    jstring msgObj = env->NewStringUTF(msg != NULL ? msg : "Unknown Error");
687
688    /* translate OS errors to Java API CryptoException errorCodes */
689    switch (err) {
690        case ERROR_DRM_NO_LICENSE:
691            err = gCryptoErrorCodes.cryptoErrorNoKey;
692            break;
693        case ERROR_DRM_LICENSE_EXPIRED:
694            err = gCryptoErrorCodes.cryptoErrorKeyExpired;
695            break;
696        case ERROR_DRM_RESOURCE_BUSY:
697            err = gCryptoErrorCodes.cryptoErrorResourceBusy;
698            break;
699        default:
700            break;
701    }
702
703    jthrowable exception =
704        (jthrowable)env->NewObject(clazz.get(), constructID, err, msgObj);
705
706    env->Throw(exception);
707}
708
709static jint throwExceptionAsNecessary(
710        JNIEnv *env, status_t err, const char *msg = NULL) {
711    if (err >= ERROR_DRM_VENDOR_MIN && err <= ERROR_DRM_VENDOR_MAX) {
712        // We'll throw our custom MediaCodec.CryptoException
713        throwCryptoException(env, err, msg);
714        return 0;
715    }
716
717    switch (err) {
718        case OK:
719            return 0;
720
721        case -EAGAIN:
722            return DEQUEUE_INFO_TRY_AGAIN_LATER;
723
724        case INFO_FORMAT_CHANGED:
725            return DEQUEUE_INFO_OUTPUT_FORMAT_CHANGED;
726
727        case INFO_OUTPUT_BUFFERS_CHANGED:
728            return DEQUEUE_INFO_OUTPUT_BUFFERS_CHANGED;
729
730        case ERROR_DRM_NO_LICENSE:
731        case ERROR_DRM_LICENSE_EXPIRED:
732        case ERROR_DRM_RESOURCE_BUSY:
733            throwCryptoException(env, err, msg);
734            break;
735
736        default:
737        {
738            jniThrowException(env, "java/lang/IllegalStateException", msg);
739            break;
740        }
741    }
742
743    return 0;
744}
745
746static void android_media_MediaCodec_native_setCallback(
747        JNIEnv *env,
748        jobject thiz,
749        jobject cb) {
750    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
751
752    if (codec == NULL) {
753        jniThrowException(env, "java/lang/IllegalStateException", NULL);
754        return;
755    }
756
757    status_t err = codec->setCallback(cb);
758
759    throwExceptionAsNecessary(env, err);
760}
761
762static void android_media_MediaCodec_native_configure(
763        JNIEnv *env,
764        jobject thiz,
765        jobjectArray keys, jobjectArray values,
766        jobject jsurface,
767        jobject jcrypto,
768        jint flags) {
769    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
770
771    if (codec == NULL) {
772        jniThrowException(env, "java/lang/IllegalStateException", NULL);
773        return;
774    }
775
776    sp<AMessage> format;
777    status_t err = ConvertKeyValueArraysToMessage(env, keys, values, &format);
778
779    if (err != OK) {
780        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
781        return;
782    }
783
784    sp<IGraphicBufferProducer> bufferProducer;
785    if (jsurface != NULL) {
786        sp<Surface> surface(android_view_Surface_getSurface(env, jsurface));
787        if (surface != NULL) {
788            bufferProducer = surface->getIGraphicBufferProducer();
789        } else {
790            jniThrowException(
791                    env,
792                    "java/lang/IllegalArgumentException",
793                    "The surface has been released");
794            return;
795        }
796    }
797
798    sp<ICrypto> crypto;
799    if (jcrypto != NULL) {
800        crypto = JCrypto::GetCrypto(env, jcrypto);
801    }
802
803    err = codec->configure(format, bufferProducer, crypto, flags);
804
805    throwExceptionAsNecessary(env, err);
806}
807
808static jobject android_media_MediaCodec_createInputSurface(JNIEnv* env,
809        jobject thiz) {
810    ALOGV("android_media_MediaCodec_createInputSurface");
811
812    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
813    if (codec == NULL) {
814        jniThrowException(env, "java/lang/IllegalStateException", NULL);
815        return NULL;
816    }
817
818    // Tell the MediaCodec that we want to use a Surface as input.
819    sp<IGraphicBufferProducer> bufferProducer;
820    status_t err = codec->createInputSurface(&bufferProducer);
821    if (err != NO_ERROR) {
822        throwExceptionAsNecessary(env, err);
823        return NULL;
824    }
825
826    // Wrap the IGBP in a Java-language Surface.
827    return android_view_Surface_createFromIGraphicBufferProducer(env,
828            bufferProducer);
829}
830
831static void android_media_MediaCodec_start(JNIEnv *env, jobject thiz) {
832    ALOGV("android_media_MediaCodec_start");
833
834    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
835
836    if (codec == NULL) {
837        jniThrowException(env, "java/lang/IllegalStateException", "no codec found");
838        return;
839    }
840
841    status_t err = codec->start();
842
843    throwExceptionAsNecessary(env, err, "start failed");
844}
845
846static void android_media_MediaCodec_stop(JNIEnv *env, jobject thiz) {
847    ALOGV("android_media_MediaCodec_stop");
848
849    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
850
851    if (codec == NULL) {
852        jniThrowException(env, "java/lang/IllegalStateException", NULL);
853        return;
854    }
855
856    status_t err = codec->stop();
857
858    throwExceptionAsNecessary(env, err);
859}
860
861static void android_media_MediaCodec_reset(JNIEnv *env, jobject thiz) {
862    ALOGV("android_media_MediaCodec_reset");
863
864    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
865
866    if (codec == NULL) {
867        // should never be here
868        jniThrowException(env, "java/lang/IllegalStateException", NULL);
869        return;
870    }
871
872    status_t err = codec->reset();
873    if (err != OK) {
874        // treat all errors as fatal for now, though resource not available
875        // errors could be treated as transient.
876        err = 0x80000000;
877    }
878    throwExceptionAsNecessary(env, err);
879}
880
881static void android_media_MediaCodec_flush(JNIEnv *env, jobject thiz) {
882    ALOGV("android_media_MediaCodec_flush");
883
884    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
885
886    if (codec == NULL) {
887        jniThrowException(env, "java/lang/IllegalStateException", NULL);
888        return;
889    }
890
891    status_t err = codec->flush();
892
893    throwExceptionAsNecessary(env, err);
894}
895
896static void android_media_MediaCodec_queueInputBuffer(
897        JNIEnv *env,
898        jobject thiz,
899        jint index,
900        jint offset,
901        jint size,
902        jlong timestampUs,
903        jint flags) {
904    ALOGV("android_media_MediaCodec_queueInputBuffer");
905
906    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
907
908    if (codec == NULL) {
909        jniThrowException(env, "java/lang/IllegalStateException", NULL);
910        return;
911    }
912
913    AString errorDetailMsg;
914
915    status_t err = codec->queueInputBuffer(
916            index, offset, size, timestampUs, flags, &errorDetailMsg);
917
918    throwExceptionAsNecessary(
919            env, err, errorDetailMsg.empty() ? NULL : errorDetailMsg.c_str());
920}
921
922static void android_media_MediaCodec_queueSecureInputBuffer(
923        JNIEnv *env,
924        jobject thiz,
925        jint index,
926        jint offset,
927        jobject cryptoInfoObj,
928        jlong timestampUs,
929        jint flags) {
930    ALOGV("android_media_MediaCodec_queueSecureInputBuffer");
931
932    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
933
934    if (codec == NULL) {
935        jniThrowException(env, "java/lang/IllegalStateException", NULL);
936        return;
937    }
938
939    jint numSubSamples =
940        env->GetIntField(cryptoInfoObj, gFields.cryptoInfoNumSubSamplesID);
941
942    jintArray numBytesOfClearDataObj =
943        (jintArray)env->GetObjectField(
944                cryptoInfoObj, gFields.cryptoInfoNumBytesOfClearDataID);
945
946    jintArray numBytesOfEncryptedDataObj =
947        (jintArray)env->GetObjectField(
948                cryptoInfoObj, gFields.cryptoInfoNumBytesOfEncryptedDataID);
949
950    jbyteArray keyObj =
951        (jbyteArray)env->GetObjectField(cryptoInfoObj, gFields.cryptoInfoKeyID);
952
953    jbyteArray ivObj =
954        (jbyteArray)env->GetObjectField(cryptoInfoObj, gFields.cryptoInfoIVID);
955
956    jint mode = env->GetIntField(cryptoInfoObj, gFields.cryptoInfoModeID);
957
958    status_t err = OK;
959
960    CryptoPlugin::SubSample *subSamples = NULL;
961    jbyte *key = NULL;
962    jbyte *iv = NULL;
963
964    if (numSubSamples <= 0) {
965        err = -EINVAL;
966    } else if (numBytesOfClearDataObj == NULL
967            && numBytesOfEncryptedDataObj == NULL) {
968        err = -EINVAL;
969    } else if (numBytesOfEncryptedDataObj != NULL
970            && env->GetArrayLength(numBytesOfEncryptedDataObj) < numSubSamples) {
971        err = -ERANGE;
972    } else if (numBytesOfClearDataObj != NULL
973            && env->GetArrayLength(numBytesOfClearDataObj) < numSubSamples) {
974        err = -ERANGE;
975    // subSamples array may silently overflow if number of samples are too large.  Use
976    // INT32_MAX as maximum allocation size may be less than SIZE_MAX on some platforms
977    } else if ( CC_UNLIKELY(numSubSamples >= INT32_MAX / sizeof(*subSamples)) ) {
978        err = -EINVAL;
979    } else {
980        jboolean isCopy;
981
982        jint *numBytesOfClearData =
983            (numBytesOfClearDataObj == NULL)
984                ? NULL
985                : env->GetIntArrayElements(numBytesOfClearDataObj, &isCopy);
986
987        jint *numBytesOfEncryptedData =
988            (numBytesOfEncryptedDataObj == NULL)
989                ? NULL
990                : env->GetIntArrayElements(numBytesOfEncryptedDataObj, &isCopy);
991
992        subSamples = new CryptoPlugin::SubSample[numSubSamples];
993
994        for (jint i = 0; i < numSubSamples; ++i) {
995            subSamples[i].mNumBytesOfClearData =
996                (numBytesOfClearData == NULL) ? 0 : numBytesOfClearData[i];
997
998            subSamples[i].mNumBytesOfEncryptedData =
999                (numBytesOfEncryptedData == NULL)
1000                    ? 0 : numBytesOfEncryptedData[i];
1001        }
1002
1003        if (numBytesOfEncryptedData != NULL) {
1004            env->ReleaseIntArrayElements(
1005                    numBytesOfEncryptedDataObj, numBytesOfEncryptedData, 0);
1006            numBytesOfEncryptedData = NULL;
1007        }
1008
1009        if (numBytesOfClearData != NULL) {
1010            env->ReleaseIntArrayElements(
1011                    numBytesOfClearDataObj, numBytesOfClearData, 0);
1012            numBytesOfClearData = NULL;
1013        }
1014    }
1015
1016    if (err == OK && keyObj != NULL) {
1017        if (env->GetArrayLength(keyObj) != 16) {
1018            err = -EINVAL;
1019        } else {
1020            jboolean isCopy;
1021            key = env->GetByteArrayElements(keyObj, &isCopy);
1022        }
1023    }
1024
1025    if (err == OK && ivObj != NULL) {
1026        if (env->GetArrayLength(ivObj) != 16) {
1027            err = -EINVAL;
1028        } else {
1029            jboolean isCopy;
1030            iv = env->GetByteArrayElements(ivObj, &isCopy);
1031        }
1032    }
1033
1034    AString errorDetailMsg;
1035
1036    if (err == OK) {
1037        err = codec->queueSecureInputBuffer(
1038                index, offset,
1039                subSamples, numSubSamples,
1040                (const uint8_t *)key, (const uint8_t *)iv,
1041                (CryptoPlugin::Mode)mode,
1042                timestampUs,
1043                flags,
1044                &errorDetailMsg);
1045    }
1046
1047    if (iv != NULL) {
1048        env->ReleaseByteArrayElements(ivObj, iv, 0);
1049        iv = NULL;
1050    }
1051
1052    if (key != NULL) {
1053        env->ReleaseByteArrayElements(keyObj, key, 0);
1054        key = NULL;
1055    }
1056
1057    delete[] subSamples;
1058    subSamples = NULL;
1059
1060    throwExceptionAsNecessary(
1061            env, err, errorDetailMsg.empty() ? NULL : errorDetailMsg.c_str());
1062}
1063
1064static jint android_media_MediaCodec_dequeueInputBuffer(
1065        JNIEnv *env, jobject thiz, jlong timeoutUs) {
1066    ALOGV("android_media_MediaCodec_dequeueInputBuffer");
1067
1068    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
1069
1070    if (codec == NULL) {
1071        jniThrowException(env, "java/lang/IllegalStateException", NULL);
1072        return -1;
1073    }
1074
1075    size_t index;
1076    status_t err = codec->dequeueInputBuffer(&index, timeoutUs);
1077
1078    if (err == OK) {
1079        return (jint) index;
1080    }
1081
1082    return throwExceptionAsNecessary(env, err);
1083}
1084
1085static jint android_media_MediaCodec_dequeueOutputBuffer(
1086        JNIEnv *env, jobject thiz, jobject bufferInfo, jlong timeoutUs) {
1087    ALOGV("android_media_MediaCodec_dequeueOutputBuffer");
1088
1089    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
1090
1091    if (codec == NULL) {
1092        jniThrowException(env, "java/lang/IllegalStateException", NULL);
1093        return 0;
1094    }
1095
1096    size_t index;
1097    status_t err = codec->dequeueOutputBuffer(
1098            env, bufferInfo, &index, timeoutUs);
1099
1100    if (err == OK) {
1101        return (jint) index;
1102    }
1103
1104    return throwExceptionAsNecessary(env, err);
1105}
1106
1107static void android_media_MediaCodec_releaseOutputBuffer(
1108        JNIEnv *env, jobject thiz,
1109        jint index, jboolean render, jboolean updatePTS, jlong timestampNs) {
1110    ALOGV("android_media_MediaCodec_renderOutputBufferAndRelease");
1111
1112    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
1113
1114    if (codec == NULL) {
1115        jniThrowException(env, "java/lang/IllegalStateException", NULL);
1116        return;
1117    }
1118
1119    status_t err = codec->releaseOutputBuffer(index, render, updatePTS, timestampNs);
1120
1121    throwExceptionAsNecessary(env, err);
1122}
1123
1124static void android_media_MediaCodec_signalEndOfInputStream(JNIEnv* env,
1125        jobject thiz) {
1126    ALOGV("android_media_MediaCodec_signalEndOfInputStream");
1127
1128    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
1129    if (codec == NULL) {
1130        jniThrowException(env, "java/lang/IllegalStateException", NULL);
1131        return;
1132    }
1133
1134    status_t err = codec->signalEndOfInputStream();
1135
1136    throwExceptionAsNecessary(env, err);
1137}
1138
1139static jobject android_media_MediaCodec_getFormatNative(
1140        JNIEnv *env, jobject thiz, jboolean input) {
1141    ALOGV("android_media_MediaCodec_getFormatNative");
1142
1143    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
1144
1145    if (codec == NULL) {
1146        jniThrowException(env, "java/lang/IllegalStateException", NULL);
1147        return NULL;
1148    }
1149
1150    jobject format;
1151    status_t err = codec->getFormat(env, input, &format);
1152
1153    if (err == OK) {
1154        return format;
1155    }
1156
1157    throwExceptionAsNecessary(env, err);
1158
1159    return NULL;
1160}
1161
1162static jobject android_media_MediaCodec_getOutputFormatForIndexNative(
1163        JNIEnv *env, jobject thiz, jint index) {
1164    ALOGV("android_media_MediaCodec_getOutputFormatForIndexNative");
1165
1166    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
1167
1168    if (codec == NULL) {
1169        jniThrowException(env, "java/lang/IllegalStateException", NULL);
1170        return NULL;
1171    }
1172
1173    jobject format;
1174    status_t err = codec->getOutputFormat(env, index, &format);
1175
1176    if (err == OK) {
1177        return format;
1178    }
1179
1180    throwExceptionAsNecessary(env, err);
1181
1182    return NULL;
1183}
1184
1185static jobjectArray android_media_MediaCodec_getBuffers(
1186        JNIEnv *env, jobject thiz, jboolean input) {
1187    ALOGV("android_media_MediaCodec_getBuffers");
1188
1189    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
1190
1191    if (codec == NULL) {
1192        jniThrowException(env, "java/lang/IllegalStateException", NULL);
1193        return NULL;
1194    }
1195
1196    jobjectArray buffers;
1197    status_t err = codec->getBuffers(env, input, &buffers);
1198
1199    if (err == OK) {
1200        return buffers;
1201    }
1202
1203    // if we're out of memory, an exception was already thrown
1204    if (err != NO_MEMORY) {
1205        throwExceptionAsNecessary(env, err);
1206    }
1207
1208    return NULL;
1209}
1210
1211static jobject android_media_MediaCodec_getBuffer(
1212        JNIEnv *env, jobject thiz, jboolean input, jint index) {
1213    ALOGV("android_media_MediaCodec_getBuffer");
1214
1215    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
1216
1217    if (codec == NULL) {
1218        jniThrowException(env, "java/lang/IllegalStateException", NULL);
1219        return NULL;
1220    }
1221
1222    jobject buffer;
1223    status_t err = codec->getBuffer(env, input, index, &buffer);
1224
1225    if (err == OK) {
1226        return buffer;
1227    }
1228
1229    // if we're out of memory, an exception was already thrown
1230    if (err != NO_MEMORY) {
1231        throwExceptionAsNecessary(env, err);
1232    }
1233
1234    return NULL;
1235}
1236
1237static jobject android_media_MediaCodec_getImage(
1238        JNIEnv *env, jobject thiz, jboolean input, jint index) {
1239    ALOGV("android_media_MediaCodec_getImage");
1240
1241    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
1242
1243    if (codec == NULL) {
1244        jniThrowException(env, "java/lang/IllegalStateException", NULL);
1245        return NULL;
1246    }
1247
1248    jobject image;
1249    status_t err = codec->getImage(env, input, index, &image);
1250
1251    if (err == OK) {
1252        return image;
1253    }
1254
1255    // if we're out of memory, an exception was already thrown
1256    if (err != NO_MEMORY) {
1257        throwExceptionAsNecessary(env, err);
1258    }
1259
1260    return NULL;
1261}
1262
1263static jobject android_media_MediaCodec_getName(
1264        JNIEnv *env, jobject thiz) {
1265    ALOGV("android_media_MediaCodec_getName");
1266
1267    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
1268
1269    if (codec == NULL) {
1270        jniThrowException(env, "java/lang/IllegalStateException", NULL);
1271        return NULL;
1272    }
1273
1274    jstring name;
1275    status_t err = codec->getName(env, &name);
1276
1277    if (err == OK) {
1278        return name;
1279    }
1280
1281    throwExceptionAsNecessary(env, err);
1282
1283    return NULL;
1284}
1285
1286static void android_media_MediaCodec_setParameters(
1287        JNIEnv *env, jobject thiz, jobjectArray keys, jobjectArray vals) {
1288    ALOGV("android_media_MediaCodec_setParameters");
1289
1290    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
1291
1292    if (codec == NULL) {
1293        jniThrowException(env, "java/lang/IllegalStateException", NULL);
1294        return;
1295    }
1296
1297    sp<AMessage> params;
1298    status_t err = ConvertKeyValueArraysToMessage(env, keys, vals, &params);
1299
1300    if (err == OK) {
1301        err = codec->setParameters(params);
1302    }
1303
1304    throwExceptionAsNecessary(env, err);
1305}
1306
1307static void android_media_MediaCodec_setVideoScalingMode(
1308        JNIEnv *env, jobject thiz, jint mode) {
1309    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
1310
1311    if (codec == NULL) {
1312        jniThrowException(env, "java/lang/IllegalStateException", NULL);
1313        return;
1314    }
1315
1316    if (mode != NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW
1317            && mode != NATIVE_WINDOW_SCALING_MODE_SCALE_CROP) {
1318        jniThrowException(env, "java/lang/InvalidArgumentException", NULL);
1319        return;
1320    }
1321
1322    codec->setVideoScalingMode(mode);
1323}
1324
1325static void android_media_MediaCodec_native_init(JNIEnv *env) {
1326    ScopedLocalRef<jclass> clazz(
1327            env, env->FindClass("android/media/MediaCodec"));
1328    CHECK(clazz.get() != NULL);
1329
1330    gFields.context = env->GetFieldID(clazz.get(), "mNativeContext", "J");
1331    CHECK(gFields.context != NULL);
1332
1333    gFields.postEventFromNativeID =
1334        env->GetMethodID(
1335                clazz.get(), "postEventFromNative", "(IIILjava/lang/Object;)V");
1336
1337    CHECK(gFields.postEventFromNativeID != NULL);
1338
1339    clazz.reset(env->FindClass("android/media/MediaCodec$CryptoInfo"));
1340    CHECK(clazz.get() != NULL);
1341
1342    gFields.cryptoInfoNumSubSamplesID =
1343        env->GetFieldID(clazz.get(), "numSubSamples", "I");
1344    CHECK(gFields.cryptoInfoNumSubSamplesID != NULL);
1345
1346    gFields.cryptoInfoNumBytesOfClearDataID =
1347        env->GetFieldID(clazz.get(), "numBytesOfClearData", "[I");
1348    CHECK(gFields.cryptoInfoNumBytesOfClearDataID != NULL);
1349
1350    gFields.cryptoInfoNumBytesOfEncryptedDataID =
1351        env->GetFieldID(clazz.get(), "numBytesOfEncryptedData", "[I");
1352    CHECK(gFields.cryptoInfoNumBytesOfEncryptedDataID != NULL);
1353
1354    gFields.cryptoInfoKeyID = env->GetFieldID(clazz.get(), "key", "[B");
1355    CHECK(gFields.cryptoInfoKeyID != NULL);
1356
1357    gFields.cryptoInfoIVID = env->GetFieldID(clazz.get(), "iv", "[B");
1358    CHECK(gFields.cryptoInfoIVID != NULL);
1359
1360    gFields.cryptoInfoModeID = env->GetFieldID(clazz.get(), "mode", "I");
1361    CHECK(gFields.cryptoInfoModeID != NULL);
1362
1363    clazz.reset(env->FindClass("android/media/MediaCodec$CryptoException"));
1364    CHECK(clazz.get() != NULL);
1365
1366    jfieldID field;
1367    field = env->GetStaticFieldID(clazz.get(), "ERROR_NO_KEY", "I");
1368    CHECK(field != NULL);
1369    gCryptoErrorCodes.cryptoErrorNoKey =
1370        env->GetStaticIntField(clazz.get(), field);
1371
1372    field = env->GetStaticFieldID(clazz.get(), "ERROR_KEY_EXPIRED", "I");
1373    CHECK(field != NULL);
1374    gCryptoErrorCodes.cryptoErrorKeyExpired =
1375        env->GetStaticIntField(clazz.get(), field);
1376
1377    field = env->GetStaticFieldID(clazz.get(), "ERROR_RESOURCE_BUSY", "I");
1378    CHECK(field != NULL);
1379    gCryptoErrorCodes.cryptoErrorResourceBusy =
1380        env->GetStaticIntField(clazz.get(), field);
1381}
1382
1383static void android_media_MediaCodec_native_setup(
1384        JNIEnv *env, jobject thiz,
1385        jstring name, jboolean nameIsType, jboolean encoder) {
1386    if (name == NULL) {
1387        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
1388        return;
1389    }
1390
1391    const char *tmp = env->GetStringUTFChars(name, NULL);
1392
1393    if (tmp == NULL) {
1394        return;
1395    }
1396
1397    sp<JMediaCodec> codec = new JMediaCodec(env, thiz, tmp, nameIsType, encoder);
1398
1399    status_t err = codec->initCheck();
1400
1401    env->ReleaseStringUTFChars(name, tmp);
1402    tmp = NULL;
1403
1404    if (err != OK) {
1405        jniThrowException(
1406                env,
1407                "java/io/IOException",
1408                "Failed to allocate component instance");
1409        return;
1410    }
1411
1412    codec->registerSelf();
1413
1414    setMediaCodec(env,thiz, codec);
1415}
1416
1417static void android_media_MediaCodec_native_finalize(
1418        JNIEnv *env, jobject thiz) {
1419    android_media_MediaCodec_release(env, thiz);
1420}
1421
1422static JNINativeMethod gMethods[] = {
1423    { "native_release", "()V", (void *)android_media_MediaCodec_release },
1424
1425    { "native_reset", "()V", (void *)android_media_MediaCodec_reset },
1426
1427    { "native_setCallback",
1428      "(Landroid/media/MediaCodec$Callback;)V",
1429      (void *)android_media_MediaCodec_native_setCallback },
1430
1431    { "native_configure",
1432      "([Ljava/lang/String;[Ljava/lang/Object;Landroid/view/Surface;"
1433      "Landroid/media/MediaCrypto;I)V",
1434      (void *)android_media_MediaCodec_native_configure },
1435
1436    { "createInputSurface", "()Landroid/view/Surface;",
1437      (void *)android_media_MediaCodec_createInputSurface },
1438
1439    { "native_start", "()V", (void *)android_media_MediaCodec_start },
1440    { "native_stop", "()V", (void *)android_media_MediaCodec_stop },
1441    { "native_flush", "()V", (void *)android_media_MediaCodec_flush },
1442
1443    { "native_queueInputBuffer", "(IIIJI)V",
1444      (void *)android_media_MediaCodec_queueInputBuffer },
1445
1446    { "native_queueSecureInputBuffer", "(IILandroid/media/MediaCodec$CryptoInfo;JI)V",
1447      (void *)android_media_MediaCodec_queueSecureInputBuffer },
1448
1449    { "native_dequeueInputBuffer", "(J)I",
1450      (void *)android_media_MediaCodec_dequeueInputBuffer },
1451
1452    { "native_dequeueOutputBuffer", "(Landroid/media/MediaCodec$BufferInfo;J)I",
1453      (void *)android_media_MediaCodec_dequeueOutputBuffer },
1454
1455    { "releaseOutputBuffer", "(IZZJ)V",
1456      (void *)android_media_MediaCodec_releaseOutputBuffer },
1457
1458    { "signalEndOfInputStream", "()V",
1459      (void *)android_media_MediaCodec_signalEndOfInputStream },
1460
1461    { "getFormatNative", "(Z)Ljava/util/Map;",
1462      (void *)android_media_MediaCodec_getFormatNative },
1463
1464    { "getOutputFormatNative", "(I)Ljava/util/Map;",
1465      (void *)android_media_MediaCodec_getOutputFormatForIndexNative },
1466
1467    { "getBuffers", "(Z)[Ljava/nio/ByteBuffer;",
1468      (void *)android_media_MediaCodec_getBuffers },
1469
1470    { "getBuffer", "(ZI)Ljava/nio/ByteBuffer;",
1471      (void *)android_media_MediaCodec_getBuffer },
1472
1473    { "getImage", "(ZI)Landroid/media/Image;",
1474      (void *)android_media_MediaCodec_getImage },
1475
1476    { "getName", "()Ljava/lang/String;",
1477      (void *)android_media_MediaCodec_getName },
1478
1479    { "setParameters", "([Ljava/lang/String;[Ljava/lang/Object;)V",
1480      (void *)android_media_MediaCodec_setParameters },
1481
1482    { "setVideoScalingMode", "(I)V",
1483      (void *)android_media_MediaCodec_setVideoScalingMode },
1484
1485    { "native_init", "()V", (void *)android_media_MediaCodec_native_init },
1486
1487    { "native_setup", "(Ljava/lang/String;ZZ)V",
1488      (void *)android_media_MediaCodec_native_setup },
1489
1490    { "native_finalize", "()V",
1491      (void *)android_media_MediaCodec_native_finalize },
1492};
1493
1494int register_android_media_MediaCodec(JNIEnv *env) {
1495    return AndroidRuntime::registerNativeMethods(env,
1496                "android/media/MediaCodec", gMethods, NELEM(gMethods));
1497}
1498