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