android_media_MediaCodec.cpp revision d4023114e8cf7ec7db4d07958a303699b658f2c0
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, positionID,
453            input ? 0 : buffer->offset());
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 = 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            CHECK(msg->findInt32("err", &arg2));
570
571            int32_t actionCode;
572            CHECK(msg->findInt32("actionCode", &actionCode));
573
574            // use Integer object to pass the action code
575            ScopedLocalRef<jclass> clazz(
576                    env, env->FindClass("java/lang/Integer"));
577            jmethodID ctor = env->GetMethodID(clazz.get(), "<init>", "(I)V");
578            obj = env->NewObject(clazz.get(), ctor, actionCode);
579
580            if (obj == NULL) {
581                if (env->ExceptionCheck()) {
582                    ALOGE("Could not create Integer object for actionCode.");
583                    env->ExceptionClear();
584                }
585                jniThrowException(env, "java/lang/IllegalStateException", NULL);
586                return;
587            }
588
589            break;
590        }
591
592        case MediaCodec::CB_OUTPUT_FORMAT_CHANGED:
593        {
594            sp<AMessage> format;
595            CHECK(msg->findMessage("format", &format));
596
597            if (OK != ConvertMessageToMap(env, format, &obj)) {
598                jniThrowException(env, "java/lang/IllegalStateException", NULL);
599                return;
600            }
601
602            break;
603        }
604
605        default:
606            TRESPASS();
607    }
608
609    env->CallVoidMethod(
610            mObject,
611            gFields.postEventFromNativeID,
612            EVENT_CALLBACK,
613            arg1,
614            arg2,
615            obj);
616
617    env->DeleteLocalRef(obj);
618}
619
620void JMediaCodec::onMessageReceived(const sp<AMessage> &msg) {
621    switch (msg->what()) {
622        case kWhatCallbackNotify:
623        {
624            handleCallback(msg);
625            break;
626        }
627        default:
628            TRESPASS();
629    }
630}
631
632}  // namespace android
633
634////////////////////////////////////////////////////////////////////////////////
635
636using namespace android;
637
638static sp<JMediaCodec> setMediaCodec(
639        JNIEnv *env, jobject thiz, const sp<JMediaCodec> &codec) {
640    sp<JMediaCodec> old = (JMediaCodec *)env->GetLongField(thiz, gFields.context);
641    if (codec != NULL) {
642        codec->incStrong(thiz);
643    }
644    if (old != NULL) {
645        /* release MediaCodec and stop the looper now before decStrong.
646         * otherwise JMediaCodec::~JMediaCodec() could be called from within
647         * its message handler, doing release() from there will deadlock
648         * (as MediaCodec::release() post synchronous message to the same looper)
649         */
650        old->release();
651        old->decStrong(thiz);
652    }
653    env->SetLongField(thiz, gFields.context, (jlong)codec.get());
654
655    return old;
656}
657
658static sp<JMediaCodec> getMediaCodec(JNIEnv *env, jobject thiz) {
659    return (JMediaCodec *)env->GetLongField(thiz, gFields.context);
660}
661
662static void android_media_MediaCodec_release(JNIEnv *env, jobject thiz) {
663    setMediaCodec(env, thiz, NULL);
664}
665
666static void throwCryptoException(JNIEnv *env, status_t err, const char *msg) {
667    ScopedLocalRef<jclass> clazz(
668            env, env->FindClass("android/media/MediaCodec$CryptoException"));
669    CHECK(clazz.get() != NULL);
670
671    jmethodID constructID =
672        env->GetMethodID(clazz.get(), "<init>", "(ILjava/lang/String;)V");
673    CHECK(constructID != NULL);
674
675    jstring msgObj = env->NewStringUTF(msg != NULL ? msg : "Unknown Error");
676
677    /* translate OS errors to Java API CryptoException errorCodes */
678    switch (err) {
679        case ERROR_DRM_NO_LICENSE:
680            err = gCryptoErrorCodes.cryptoErrorNoKey;
681            break;
682        case ERROR_DRM_LICENSE_EXPIRED:
683            err = gCryptoErrorCodes.cryptoErrorKeyExpired;
684            break;
685        case ERROR_DRM_RESOURCE_BUSY:
686            err = gCryptoErrorCodes.cryptoErrorResourceBusy;
687            break;
688        default:
689            break;
690    }
691
692    jthrowable exception =
693        (jthrowable)env->NewObject(clazz.get(), constructID, err, msgObj);
694
695    env->Throw(exception);
696}
697
698static jint throwExceptionAsNecessary(
699        JNIEnv *env, status_t err, const char *msg = NULL) {
700    if (err >= ERROR_DRM_VENDOR_MIN && err <= ERROR_DRM_VENDOR_MAX) {
701        // We'll throw our custom MediaCodec.CryptoException
702        throwCryptoException(env, err, msg);
703        return 0;
704    }
705
706    switch (err) {
707        case OK:
708            return 0;
709
710        case -EAGAIN:
711            return DEQUEUE_INFO_TRY_AGAIN_LATER;
712
713        case INFO_FORMAT_CHANGED:
714            return DEQUEUE_INFO_OUTPUT_FORMAT_CHANGED;
715
716        case INFO_OUTPUT_BUFFERS_CHANGED:
717            return DEQUEUE_INFO_OUTPUT_BUFFERS_CHANGED;
718
719        case ERROR_DRM_NO_LICENSE:
720        case ERROR_DRM_LICENSE_EXPIRED:
721        case ERROR_DRM_RESOURCE_BUSY:
722            throwCryptoException(env, err, msg);
723            break;
724
725        default:
726        {
727            jniThrowException(env, "java/lang/IllegalStateException", msg);
728            break;
729        }
730    }
731
732    return 0;
733}
734
735static void android_media_MediaCodec_native_setCallback(
736        JNIEnv *env,
737        jobject thiz,
738        jobject cb) {
739    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
740
741    if (codec == NULL) {
742        jniThrowException(env, "java/lang/IllegalStateException", NULL);
743        return;
744    }
745
746    status_t err = codec->setCallback(cb);
747
748    throwExceptionAsNecessary(env, err);
749}
750
751static void android_media_MediaCodec_native_configure(
752        JNIEnv *env,
753        jobject thiz,
754        jobjectArray keys, jobjectArray values,
755        jobject jsurface,
756        jobject jcrypto,
757        jint flags) {
758    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
759
760    if (codec == NULL) {
761        jniThrowException(env, "java/lang/IllegalStateException", NULL);
762        return;
763    }
764
765    sp<AMessage> format;
766    status_t err = ConvertKeyValueArraysToMessage(env, keys, values, &format);
767
768    if (err != OK) {
769        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
770        return;
771    }
772
773    sp<IGraphicBufferProducer> bufferProducer;
774    if (jsurface != NULL) {
775        sp<Surface> surface(android_view_Surface_getSurface(env, jsurface));
776        if (surface != NULL) {
777            bufferProducer = surface->getIGraphicBufferProducer();
778        } else {
779            jniThrowException(
780                    env,
781                    "java/lang/IllegalArgumentException",
782                    "The surface has been released");
783            return;
784        }
785    }
786
787    sp<ICrypto> crypto;
788    if (jcrypto != NULL) {
789        crypto = JCrypto::GetCrypto(env, jcrypto);
790    }
791
792    err = codec->configure(format, bufferProducer, crypto, flags);
793
794    throwExceptionAsNecessary(env, err);
795}
796
797static jobject android_media_MediaCodec_createInputSurface(JNIEnv* env,
798        jobject thiz) {
799    ALOGV("android_media_MediaCodec_createInputSurface");
800
801    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
802    if (codec == NULL) {
803        jniThrowException(env, "java/lang/IllegalStateException", NULL);
804        return NULL;
805    }
806
807    // Tell the MediaCodec that we want to use a Surface as input.
808    sp<IGraphicBufferProducer> bufferProducer;
809    status_t err = codec->createInputSurface(&bufferProducer);
810    if (err != NO_ERROR) {
811        throwExceptionAsNecessary(env, err);
812        return NULL;
813    }
814
815    // Wrap the IGBP in a Java-language Surface.
816    return android_view_Surface_createFromIGraphicBufferProducer(env,
817            bufferProducer);
818}
819
820static void android_media_MediaCodec_start(JNIEnv *env, jobject thiz) {
821    ALOGV("android_media_MediaCodec_start");
822
823    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
824
825    if (codec == NULL) {
826        jniThrowException(env, "java/lang/IllegalStateException", "no codec found");
827        return;
828    }
829
830    status_t err = codec->start();
831
832    throwExceptionAsNecessary(env, err, "start failed");
833}
834
835static void android_media_MediaCodec_stop(JNIEnv *env, jobject thiz) {
836    ALOGV("android_media_MediaCodec_stop");
837
838    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
839
840    if (codec == NULL) {
841        jniThrowException(env, "java/lang/IllegalStateException", NULL);
842        return;
843    }
844
845    status_t err = codec->stop();
846
847    throwExceptionAsNecessary(env, err);
848}
849
850static void android_media_MediaCodec_flush(JNIEnv *env, jobject thiz) {
851    ALOGV("android_media_MediaCodec_flush");
852
853    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
854
855    if (codec == NULL) {
856        jniThrowException(env, "java/lang/IllegalStateException", NULL);
857        return;
858    }
859
860    status_t err = codec->flush();
861
862    throwExceptionAsNecessary(env, err);
863}
864
865static void android_media_MediaCodec_queueInputBuffer(
866        JNIEnv *env,
867        jobject thiz,
868        jint index,
869        jint offset,
870        jint size,
871        jlong timestampUs,
872        jint flags) {
873    ALOGV("android_media_MediaCodec_queueInputBuffer");
874
875    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
876
877    if (codec == NULL) {
878        jniThrowException(env, "java/lang/IllegalStateException", NULL);
879        return;
880    }
881
882    AString errorDetailMsg;
883
884    status_t err = codec->queueInputBuffer(
885            index, offset, size, timestampUs, flags, &errorDetailMsg);
886
887    throwExceptionAsNecessary(
888            env, err, errorDetailMsg.empty() ? NULL : errorDetailMsg.c_str());
889}
890
891static void android_media_MediaCodec_queueSecureInputBuffer(
892        JNIEnv *env,
893        jobject thiz,
894        jint index,
895        jint offset,
896        jobject cryptoInfoObj,
897        jlong timestampUs,
898        jint flags) {
899    ALOGV("android_media_MediaCodec_queueSecureInputBuffer");
900
901    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
902
903    if (codec == NULL) {
904        jniThrowException(env, "java/lang/IllegalStateException", NULL);
905        return;
906    }
907
908    jint numSubSamples =
909        env->GetIntField(cryptoInfoObj, gFields.cryptoInfoNumSubSamplesID);
910
911    jintArray numBytesOfClearDataObj =
912        (jintArray)env->GetObjectField(
913                cryptoInfoObj, gFields.cryptoInfoNumBytesOfClearDataID);
914
915    jintArray numBytesOfEncryptedDataObj =
916        (jintArray)env->GetObjectField(
917                cryptoInfoObj, gFields.cryptoInfoNumBytesOfEncryptedDataID);
918
919    jbyteArray keyObj =
920        (jbyteArray)env->GetObjectField(cryptoInfoObj, gFields.cryptoInfoKeyID);
921
922    jbyteArray ivObj =
923        (jbyteArray)env->GetObjectField(cryptoInfoObj, gFields.cryptoInfoIVID);
924
925    jint mode = env->GetIntField(cryptoInfoObj, gFields.cryptoInfoModeID);
926
927    status_t err = OK;
928
929    CryptoPlugin::SubSample *subSamples = NULL;
930    jbyte *key = NULL;
931    jbyte *iv = NULL;
932
933    if (numSubSamples <= 0) {
934        err = -EINVAL;
935    } else if (numBytesOfClearDataObj == NULL
936            && numBytesOfEncryptedDataObj == NULL) {
937        err = -EINVAL;
938    } else if (numBytesOfEncryptedDataObj != NULL
939            && env->GetArrayLength(numBytesOfEncryptedDataObj) < numSubSamples) {
940        err = -ERANGE;
941    } else if (numBytesOfClearDataObj != NULL
942            && env->GetArrayLength(numBytesOfClearDataObj) < numSubSamples) {
943        err = -ERANGE;
944    // subSamples array may silently overflow if number of samples are too large.  Use
945    // INT32_MAX as maximum allocation size may be less than SIZE_MAX on some platforms
946    } else if ( CC_UNLIKELY(numSubSamples >= INT32_MAX / sizeof(*subSamples)) ) {
947        err = -EINVAL;
948    } else {
949        jboolean isCopy;
950
951        jint *numBytesOfClearData =
952            (numBytesOfClearDataObj == NULL)
953                ? NULL
954                : env->GetIntArrayElements(numBytesOfClearDataObj, &isCopy);
955
956        jint *numBytesOfEncryptedData =
957            (numBytesOfEncryptedDataObj == NULL)
958                ? NULL
959                : env->GetIntArrayElements(numBytesOfEncryptedDataObj, &isCopy);
960
961        subSamples = new CryptoPlugin::SubSample[numSubSamples];
962
963        for (jint i = 0; i < numSubSamples; ++i) {
964            subSamples[i].mNumBytesOfClearData =
965                (numBytesOfClearData == NULL) ? 0 : numBytesOfClearData[i];
966
967            subSamples[i].mNumBytesOfEncryptedData =
968                (numBytesOfEncryptedData == NULL)
969                    ? 0 : numBytesOfEncryptedData[i];
970        }
971
972        if (numBytesOfEncryptedData != NULL) {
973            env->ReleaseIntArrayElements(
974                    numBytesOfEncryptedDataObj, numBytesOfEncryptedData, 0);
975            numBytesOfEncryptedData = NULL;
976        }
977
978        if (numBytesOfClearData != NULL) {
979            env->ReleaseIntArrayElements(
980                    numBytesOfClearDataObj, numBytesOfClearData, 0);
981            numBytesOfClearData = NULL;
982        }
983    }
984
985    if (err == OK && keyObj != NULL) {
986        if (env->GetArrayLength(keyObj) != 16) {
987            err = -EINVAL;
988        } else {
989            jboolean isCopy;
990            key = env->GetByteArrayElements(keyObj, &isCopy);
991        }
992    }
993
994    if (err == OK && ivObj != NULL) {
995        if (env->GetArrayLength(ivObj) != 16) {
996            err = -EINVAL;
997        } else {
998            jboolean isCopy;
999            iv = env->GetByteArrayElements(ivObj, &isCopy);
1000        }
1001    }
1002
1003    AString errorDetailMsg;
1004
1005    if (err == OK) {
1006        err = codec->queueSecureInputBuffer(
1007                index, offset,
1008                subSamples, numSubSamples,
1009                (const uint8_t *)key, (const uint8_t *)iv,
1010                (CryptoPlugin::Mode)mode,
1011                timestampUs,
1012                flags,
1013                &errorDetailMsg);
1014    }
1015
1016    if (iv != NULL) {
1017        env->ReleaseByteArrayElements(ivObj, iv, 0);
1018        iv = NULL;
1019    }
1020
1021    if (key != NULL) {
1022        env->ReleaseByteArrayElements(keyObj, key, 0);
1023        key = NULL;
1024    }
1025
1026    delete[] subSamples;
1027    subSamples = NULL;
1028
1029    throwExceptionAsNecessary(
1030            env, err, errorDetailMsg.empty() ? NULL : errorDetailMsg.c_str());
1031}
1032
1033static jint android_media_MediaCodec_dequeueInputBuffer(
1034        JNIEnv *env, jobject thiz, jlong timeoutUs) {
1035    ALOGV("android_media_MediaCodec_dequeueInputBuffer");
1036
1037    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
1038
1039    if (codec == NULL) {
1040        jniThrowException(env, "java/lang/IllegalStateException", NULL);
1041        return -1;
1042    }
1043
1044    size_t index;
1045    status_t err = codec->dequeueInputBuffer(&index, timeoutUs);
1046
1047    if (err == OK) {
1048        return (jint) index;
1049    }
1050
1051    return throwExceptionAsNecessary(env, err);
1052}
1053
1054static jint android_media_MediaCodec_dequeueOutputBuffer(
1055        JNIEnv *env, jobject thiz, jobject bufferInfo, jlong timeoutUs) {
1056    ALOGV("android_media_MediaCodec_dequeueOutputBuffer");
1057
1058    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
1059
1060    if (codec == NULL) {
1061        jniThrowException(env, "java/lang/IllegalStateException", NULL);
1062        return 0;
1063    }
1064
1065    size_t index;
1066    status_t err = codec->dequeueOutputBuffer(
1067            env, bufferInfo, &index, timeoutUs);
1068
1069    if (err == OK) {
1070        return (jint) index;
1071    }
1072
1073    return throwExceptionAsNecessary(env, err);
1074}
1075
1076static void android_media_MediaCodec_releaseOutputBuffer(
1077        JNIEnv *env, jobject thiz,
1078        jint index, jboolean render, jboolean updatePTS, jlong timestampNs) {
1079    ALOGV("android_media_MediaCodec_renderOutputBufferAndRelease");
1080
1081    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
1082
1083    if (codec == NULL) {
1084        jniThrowException(env, "java/lang/IllegalStateException", NULL);
1085        return;
1086    }
1087
1088    status_t err = codec->releaseOutputBuffer(index, render, updatePTS, timestampNs);
1089
1090    throwExceptionAsNecessary(env, err);
1091}
1092
1093static void android_media_MediaCodec_signalEndOfInputStream(JNIEnv* env,
1094        jobject thiz) {
1095    ALOGV("android_media_MediaCodec_signalEndOfInputStream");
1096
1097    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
1098    if (codec == NULL) {
1099        jniThrowException(env, "java/lang/IllegalStateException", NULL);
1100        return;
1101    }
1102
1103    status_t err = codec->signalEndOfInputStream();
1104
1105    throwExceptionAsNecessary(env, err);
1106}
1107
1108static jobject android_media_MediaCodec_getFormatNative(
1109        JNIEnv *env, jobject thiz, jboolean input) {
1110    ALOGV("android_media_MediaCodec_getFormatNative");
1111
1112    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
1113
1114    if (codec == NULL) {
1115        jniThrowException(env, "java/lang/IllegalStateException", NULL);
1116        return NULL;
1117    }
1118
1119    jobject format;
1120    status_t err = codec->getFormat(env, input, &format);
1121
1122    if (err == OK) {
1123        return format;
1124    }
1125
1126    throwExceptionAsNecessary(env, err);
1127
1128    return NULL;
1129}
1130
1131static jobject android_media_MediaCodec_getOutputFormatForIndexNative(
1132        JNIEnv *env, jobject thiz, jint index) {
1133    ALOGV("android_media_MediaCodec_getOutputFormatForIndexNative");
1134
1135    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
1136
1137    if (codec == NULL) {
1138        jniThrowException(env, "java/lang/IllegalStateException", NULL);
1139        return NULL;
1140    }
1141
1142    jobject format;
1143    status_t err = codec->getOutputFormat(env, index, &format);
1144
1145    if (err == OK) {
1146        return format;
1147    }
1148
1149    throwExceptionAsNecessary(env, err);
1150
1151    return NULL;
1152}
1153
1154static jobjectArray android_media_MediaCodec_getBuffers(
1155        JNIEnv *env, jobject thiz, jboolean input) {
1156    ALOGV("android_media_MediaCodec_getBuffers");
1157
1158    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
1159
1160    if (codec == NULL) {
1161        jniThrowException(env, "java/lang/IllegalStateException", NULL);
1162        return NULL;
1163    }
1164
1165    jobjectArray buffers;
1166    status_t err = codec->getBuffers(env, input, &buffers);
1167
1168    if (err == OK) {
1169        return buffers;
1170    }
1171
1172    // if we're out of memory, an exception was already thrown
1173    if (err != NO_MEMORY) {
1174        throwExceptionAsNecessary(env, err);
1175    }
1176
1177    return NULL;
1178}
1179
1180static jobject android_media_MediaCodec_getBuffer(
1181        JNIEnv *env, jobject thiz, jboolean input, jint index) {
1182    ALOGV("android_media_MediaCodec_getBuffer");
1183
1184    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
1185
1186    if (codec == NULL) {
1187        jniThrowException(env, "java/lang/IllegalStateException", NULL);
1188        return NULL;
1189    }
1190
1191    jobject buffer;
1192    status_t err = codec->getBuffer(env, input, index, &buffer);
1193
1194    if (err == OK) {
1195        return buffer;
1196    }
1197
1198    // if we're out of memory, an exception was already thrown
1199    if (err != NO_MEMORY) {
1200        throwExceptionAsNecessary(env, err);
1201    }
1202
1203    return NULL;
1204}
1205
1206static jobject android_media_MediaCodec_getImage(
1207        JNIEnv *env, jobject thiz, jboolean input, jint index) {
1208    ALOGV("android_media_MediaCodec_getImage");
1209
1210    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
1211
1212    if (codec == NULL) {
1213        jniThrowException(env, "java/lang/IllegalStateException", NULL);
1214        return NULL;
1215    }
1216
1217    jobject image;
1218    status_t err = codec->getImage(env, input, index, &image);
1219
1220    if (err == OK) {
1221        return image;
1222    }
1223
1224    // if we're out of memory, an exception was already thrown
1225    if (err != NO_MEMORY) {
1226        throwExceptionAsNecessary(env, err);
1227    }
1228
1229    return NULL;
1230}
1231
1232static jobject android_media_MediaCodec_getName(
1233        JNIEnv *env, jobject thiz) {
1234    ALOGV("android_media_MediaCodec_getName");
1235
1236    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
1237
1238    if (codec == NULL) {
1239        jniThrowException(env, "java/lang/IllegalStateException", NULL);
1240        return NULL;
1241    }
1242
1243    jstring name;
1244    status_t err = codec->getName(env, &name);
1245
1246    if (err == OK) {
1247        return name;
1248    }
1249
1250    throwExceptionAsNecessary(env, err);
1251
1252    return NULL;
1253}
1254
1255static void android_media_MediaCodec_setParameters(
1256        JNIEnv *env, jobject thiz, jobjectArray keys, jobjectArray vals) {
1257    ALOGV("android_media_MediaCodec_setParameters");
1258
1259    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
1260
1261    if (codec == NULL) {
1262        jniThrowException(env, "java/lang/IllegalStateException", NULL);
1263        return;
1264    }
1265
1266    sp<AMessage> params;
1267    status_t err = ConvertKeyValueArraysToMessage(env, keys, vals, &params);
1268
1269    if (err == OK) {
1270        err = codec->setParameters(params);
1271    }
1272
1273    throwExceptionAsNecessary(env, err);
1274}
1275
1276static void android_media_MediaCodec_setVideoScalingMode(
1277        JNIEnv *env, jobject thiz, jint mode) {
1278    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
1279
1280    if (codec == NULL) {
1281        jniThrowException(env, "java/lang/IllegalStateException", NULL);
1282        return;
1283    }
1284
1285    if (mode != NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW
1286            && mode != NATIVE_WINDOW_SCALING_MODE_SCALE_CROP) {
1287        jniThrowException(env, "java/lang/InvalidArgumentException", NULL);
1288        return;
1289    }
1290
1291    codec->setVideoScalingMode(mode);
1292}
1293
1294static void android_media_MediaCodec_native_init(JNIEnv *env) {
1295    ScopedLocalRef<jclass> clazz(
1296            env, env->FindClass("android/media/MediaCodec"));
1297    CHECK(clazz.get() != NULL);
1298
1299    gFields.context = env->GetFieldID(clazz.get(), "mNativeContext", "J");
1300    CHECK(gFields.context != NULL);
1301
1302    gFields.postEventFromNativeID =
1303        env->GetMethodID(
1304                clazz.get(), "postEventFromNative", "(IIILjava/lang/Object;)V");
1305
1306    CHECK(gFields.postEventFromNativeID != NULL);
1307
1308    clazz.reset(env->FindClass("android/media/MediaCodec$CryptoInfo"));
1309    CHECK(clazz.get() != NULL);
1310
1311    gFields.cryptoInfoNumSubSamplesID =
1312        env->GetFieldID(clazz.get(), "numSubSamples", "I");
1313    CHECK(gFields.cryptoInfoNumSubSamplesID != NULL);
1314
1315    gFields.cryptoInfoNumBytesOfClearDataID =
1316        env->GetFieldID(clazz.get(), "numBytesOfClearData", "[I");
1317    CHECK(gFields.cryptoInfoNumBytesOfClearDataID != NULL);
1318
1319    gFields.cryptoInfoNumBytesOfEncryptedDataID =
1320        env->GetFieldID(clazz.get(), "numBytesOfEncryptedData", "[I");
1321    CHECK(gFields.cryptoInfoNumBytesOfEncryptedDataID != NULL);
1322
1323    gFields.cryptoInfoKeyID = env->GetFieldID(clazz.get(), "key", "[B");
1324    CHECK(gFields.cryptoInfoKeyID != NULL);
1325
1326    gFields.cryptoInfoIVID = env->GetFieldID(clazz.get(), "iv", "[B");
1327    CHECK(gFields.cryptoInfoIVID != NULL);
1328
1329    gFields.cryptoInfoModeID = env->GetFieldID(clazz.get(), "mode", "I");
1330    CHECK(gFields.cryptoInfoModeID != NULL);
1331
1332    clazz.reset(env->FindClass("android/media/MediaCodec$CryptoException"));
1333    CHECK(clazz.get() != NULL);
1334
1335    jfieldID field;
1336    field = env->GetStaticFieldID(clazz.get(), "ERROR_NO_KEY", "I");
1337    CHECK(field != NULL);
1338    gCryptoErrorCodes.cryptoErrorNoKey =
1339        env->GetStaticIntField(clazz.get(), field);
1340
1341    field = env->GetStaticFieldID(clazz.get(), "ERROR_KEY_EXPIRED", "I");
1342    CHECK(field != NULL);
1343    gCryptoErrorCodes.cryptoErrorKeyExpired =
1344        env->GetStaticIntField(clazz.get(), field);
1345
1346    field = env->GetStaticFieldID(clazz.get(), "ERROR_RESOURCE_BUSY", "I");
1347    CHECK(field != NULL);
1348    gCryptoErrorCodes.cryptoErrorResourceBusy =
1349        env->GetStaticIntField(clazz.get(), field);
1350}
1351
1352static void android_media_MediaCodec_native_setup(
1353        JNIEnv *env, jobject thiz,
1354        jstring name, jboolean nameIsType, jboolean encoder) {
1355    if (name == NULL) {
1356        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
1357        return;
1358    }
1359
1360    const char *tmp = env->GetStringUTFChars(name, NULL);
1361
1362    if (tmp == NULL) {
1363        return;
1364    }
1365
1366    sp<JMediaCodec> codec = new JMediaCodec(env, thiz, tmp, nameIsType, encoder);
1367
1368    status_t err = codec->initCheck();
1369
1370    env->ReleaseStringUTFChars(name, tmp);
1371    tmp = NULL;
1372
1373    if (err != OK) {
1374        jniThrowException(
1375                env,
1376                "java/io/IOException",
1377                "Failed to allocate component instance");
1378        return;
1379    }
1380
1381    codec->registerSelf();
1382
1383    setMediaCodec(env,thiz, codec);
1384}
1385
1386static void android_media_MediaCodec_native_finalize(
1387        JNIEnv *env, jobject thiz) {
1388    android_media_MediaCodec_release(env, thiz);
1389}
1390
1391static JNINativeMethod gMethods[] = {
1392    { "native_release", "()V", (void *)android_media_MediaCodec_release },
1393
1394    { "native_setCallback",
1395      "(Landroid/media/MediaCodec$Callback;)V",
1396      (void *)android_media_MediaCodec_native_setCallback },
1397
1398    { "native_configure",
1399      "([Ljava/lang/String;[Ljava/lang/Object;Landroid/view/Surface;"
1400      "Landroid/media/MediaCrypto;I)V",
1401      (void *)android_media_MediaCodec_native_configure },
1402
1403    { "createInputSurface", "()Landroid/view/Surface;",
1404      (void *)android_media_MediaCodec_createInputSurface },
1405
1406    { "native_start", "()V", (void *)android_media_MediaCodec_start },
1407    { "native_stop", "()V", (void *)android_media_MediaCodec_stop },
1408    { "native_flush", "()V", (void *)android_media_MediaCodec_flush },
1409
1410    { "native_queueInputBuffer", "(IIIJI)V",
1411      (void *)android_media_MediaCodec_queueInputBuffer },
1412
1413    { "native_queueSecureInputBuffer", "(IILandroid/media/MediaCodec$CryptoInfo;JI)V",
1414      (void *)android_media_MediaCodec_queueSecureInputBuffer },
1415
1416    { "native_dequeueInputBuffer", "(J)I",
1417      (void *)android_media_MediaCodec_dequeueInputBuffer },
1418
1419    { "native_dequeueOutputBuffer", "(Landroid/media/MediaCodec$BufferInfo;J)I",
1420      (void *)android_media_MediaCodec_dequeueOutputBuffer },
1421
1422    { "releaseOutputBuffer", "(IZZJ)V",
1423      (void *)android_media_MediaCodec_releaseOutputBuffer },
1424
1425    { "signalEndOfInputStream", "()V",
1426      (void *)android_media_MediaCodec_signalEndOfInputStream },
1427
1428    { "getFormatNative", "(Z)Ljava/util/Map;",
1429      (void *)android_media_MediaCodec_getFormatNative },
1430
1431    { "getOutputFormatNative", "(I)Ljava/util/Map;",
1432      (void *)android_media_MediaCodec_getOutputFormatForIndexNative },
1433
1434    { "getBuffers", "(Z)[Ljava/nio/ByteBuffer;",
1435      (void *)android_media_MediaCodec_getBuffers },
1436
1437    { "getBuffer", "(ZI)Ljava/nio/ByteBuffer;",
1438      (void *)android_media_MediaCodec_getBuffer },
1439
1440    { "getImage", "(ZI)Landroid/media/Image;",
1441      (void *)android_media_MediaCodec_getImage },
1442
1443    { "getName", "()Ljava/lang/String;",
1444      (void *)android_media_MediaCodec_getName },
1445
1446    { "setParameters", "([Ljava/lang/String;[Ljava/lang/Object;)V",
1447      (void *)android_media_MediaCodec_setParameters },
1448
1449    { "setVideoScalingMode", "(I)V",
1450      (void *)android_media_MediaCodec_setVideoScalingMode },
1451
1452    { "native_init", "()V", (void *)android_media_MediaCodec_native_init },
1453
1454    { "native_setup", "(Ljava/lang/String;ZZ)V",
1455      (void *)android_media_MediaCodec_native_setup },
1456
1457    { "native_finalize", "()V",
1458      (void *)android_media_MediaCodec_native_finalize },
1459};
1460
1461int register_android_media_MediaCodec(JNIEnv *env) {
1462    return AndroidRuntime::registerNativeMethods(env,
1463                "android/media/MediaCodec", gMethods, NELEM(gMethods));
1464}
1465