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