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