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