android_media_MediaCodec.cpp revision 3ccb34d7070d73ba6c7842801b070b41de32b842
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    const char *defaultMsg = "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            defaultMsg = "Crypto key not available";
844            break;
845        case ERROR_DRM_LICENSE_EXPIRED:
846            err = gCryptoErrorCodes.cryptoErrorKeyExpired;
847            defaultMsg = "License expired";
848            break;
849        case ERROR_DRM_RESOURCE_BUSY:
850            err = gCryptoErrorCodes.cryptoErrorResourceBusy;
851            defaultMsg = "Resource busy or unavailable";
852            break;
853        case ERROR_DRM_INSUFFICIENT_OUTPUT_PROTECTION:
854            err = gCryptoErrorCodes.cryptoErrorInsufficientOutputProtection;
855            defaultMsg = "Required output protections are not active";
856            break;
857        case ERROR_DRM_SESSION_NOT_OPENED:
858            err = gCryptoErrorCodes.cryptoErrorSessionNotOpened;
859            defaultMsg = "Attempted to use a closed session";
860            break;
861        default:  /* Other negative DRM error codes go out as is. */
862            break;
863    }
864
865    jstring msgObj = env->NewStringUTF(msg != NULL ? msg : defaultMsg);
866
867    jthrowable exception =
868        (jthrowable)env->NewObject(clazz.get(), constructID, err, msgObj);
869
870    env->Throw(exception);
871}
872
873static jint throwExceptionAsNecessary(
874        JNIEnv *env, status_t err, int32_t actionCode = ACTION_CODE_FATAL,
875        const char *msg = NULL) {
876    switch (err) {
877        case OK:
878            return 0;
879
880        case -EAGAIN:
881            return DEQUEUE_INFO_TRY_AGAIN_LATER;
882
883        case INFO_FORMAT_CHANGED:
884            return DEQUEUE_INFO_OUTPUT_FORMAT_CHANGED;
885
886        case INFO_OUTPUT_BUFFERS_CHANGED:
887            return DEQUEUE_INFO_OUTPUT_BUFFERS_CHANGED;
888
889        case INVALID_OPERATION:
890            jniThrowException(env, "java/lang/IllegalStateException", msg);
891            return 0;
892
893        case BAD_VALUE:
894            jniThrowException(env, "java/lang/IllegalArgumentException", msg);
895            return 0;
896
897        default:
898            if (isCryptoError(err)) {
899                throwCryptoException(env, err, msg);
900                return 0;
901            }
902            throwCodecException(env, err, actionCode, msg);
903            return 0;
904    }
905}
906
907static void android_media_MediaCodec_native_enableOnFrameRenderedListener(
908        JNIEnv *env,
909        jobject thiz,
910        jboolean enabled) {
911    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
912
913    if (codec == NULL) {
914        throwExceptionAsNecessary(env, INVALID_OPERATION);
915        return;
916    }
917
918    status_t err = codec->enableOnFrameRenderedListener(enabled);
919
920    throwExceptionAsNecessary(env, err);
921}
922
923static void android_media_MediaCodec_native_setCallback(
924        JNIEnv *env,
925        jobject thiz,
926        jobject cb) {
927    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
928
929    if (codec == NULL) {
930        throwExceptionAsNecessary(env, INVALID_OPERATION);
931        return;
932    }
933
934    status_t err = codec->setCallback(cb);
935
936    throwExceptionAsNecessary(env, err);
937}
938
939static void android_media_MediaCodec_native_configure(
940        JNIEnv *env,
941        jobject thiz,
942        jobjectArray keys, jobjectArray values,
943        jobject jsurface,
944        jobject jcrypto,
945        jint flags) {
946    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
947
948    if (codec == NULL) {
949        throwExceptionAsNecessary(env, INVALID_OPERATION);
950        return;
951    }
952
953    sp<AMessage> format;
954    status_t err = ConvertKeyValueArraysToMessage(env, keys, values, &format);
955
956    if (err != OK) {
957        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
958        return;
959    }
960
961    sp<IGraphicBufferProducer> bufferProducer;
962    if (jsurface != NULL) {
963        sp<Surface> surface(android_view_Surface_getSurface(env, jsurface));
964        if (surface != NULL) {
965            bufferProducer = surface->getIGraphicBufferProducer();
966        } else {
967            jniThrowException(
968                    env,
969                    "java/lang/IllegalArgumentException",
970                    "The surface has been released");
971            return;
972        }
973    }
974
975    sp<ICrypto> crypto;
976    if (jcrypto != NULL) {
977        crypto = JCrypto::GetCrypto(env, jcrypto);
978    }
979
980    err = codec->configure(format, bufferProducer, crypto, flags);
981
982    throwExceptionAsNecessary(env, err);
983}
984
985static void android_media_MediaCodec_native_setSurface(
986        JNIEnv *env,
987        jobject thiz,
988        jobject jsurface) {
989    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
990
991    if (codec == NULL) {
992        throwExceptionAsNecessary(env, INVALID_OPERATION);
993        return;
994    }
995
996    sp<IGraphicBufferProducer> bufferProducer;
997    if (jsurface != NULL) {
998        sp<Surface> surface(android_view_Surface_getSurface(env, jsurface));
999        if (surface != NULL) {
1000            bufferProducer = surface->getIGraphicBufferProducer();
1001        } else {
1002            jniThrowException(
1003                    env,
1004                    "java/lang/IllegalArgumentException",
1005                    "The surface has been released");
1006            return;
1007        }
1008    }
1009
1010    status_t err = codec->setSurface(bufferProducer);
1011    throwExceptionAsNecessary(env, err);
1012}
1013
1014sp<PersistentSurface> android_media_MediaCodec_getPersistentInputSurface(
1015        JNIEnv* env, jobject object) {
1016    sp<PersistentSurface> persistentSurface;
1017
1018    jobject lock = env->GetObjectField(
1019            object, gPersistentSurfaceClassInfo.mLock);
1020    if (env->MonitorEnter(lock) == JNI_OK) {
1021        persistentSurface = reinterpret_cast<PersistentSurface *>(
1022                env->GetLongField(object,
1023                        gPersistentSurfaceClassInfo.mPersistentObject));
1024        env->MonitorExit(lock);
1025    }
1026    env->DeleteLocalRef(lock);
1027
1028    return persistentSurface;
1029}
1030
1031static jobject android_media_MediaCodec_createPersistentInputSurface(
1032        JNIEnv* env, jclass /* clazz */) {
1033    ALOGV("android_media_MediaCodec_createPersistentInputSurface");
1034    sp<PersistentSurface> persistentSurface =
1035        MediaCodec::CreatePersistentInputSurface();
1036
1037    if (persistentSurface == NULL) {
1038        return NULL;
1039    }
1040
1041    sp<Surface> surface = new Surface(
1042            persistentSurface->getBufferProducer(), true);
1043    if (surface == NULL) {
1044        return NULL;
1045    }
1046
1047    jobject object = env->NewObject(
1048            gPersistentSurfaceClassInfo.clazz,
1049            gPersistentSurfaceClassInfo.ctor);
1050
1051    if (object == NULL) {
1052        if (env->ExceptionCheck()) {
1053            ALOGE("Could not create PersistentSurface.");
1054            env->ExceptionClear();
1055        }
1056        return NULL;
1057    }
1058
1059    jobject lock = env->GetObjectField(
1060            object, gPersistentSurfaceClassInfo.mLock);
1061    if (env->MonitorEnter(lock) == JNI_OK) {
1062        env->CallVoidMethod(
1063                object,
1064                gPersistentSurfaceClassInfo.setNativeObjectLocked,
1065                (jlong)surface.get());
1066        env->SetLongField(
1067                object,
1068                gPersistentSurfaceClassInfo.mPersistentObject,
1069                (jlong)persistentSurface.get());
1070        env->MonitorExit(lock);
1071    } else {
1072        env->DeleteLocalRef(object);
1073        object = NULL;
1074    }
1075    env->DeleteLocalRef(lock);
1076
1077    if (object != NULL) {
1078        surface->incStrong(&sRefBaseOwner);
1079        persistentSurface->incStrong(&sRefBaseOwner);
1080    }
1081
1082    return object;
1083}
1084
1085static void android_media_MediaCodec_releasePersistentInputSurface(
1086        JNIEnv* env, jclass /* clazz */, jobject object) {
1087    sp<PersistentSurface> persistentSurface;
1088
1089    jobject lock = env->GetObjectField(
1090            object, gPersistentSurfaceClassInfo.mLock);
1091    if (env->MonitorEnter(lock) == JNI_OK) {
1092        persistentSurface = reinterpret_cast<PersistentSurface *>(
1093            env->GetLongField(
1094                    object, gPersistentSurfaceClassInfo.mPersistentObject));
1095        env->SetLongField(
1096                object,
1097                gPersistentSurfaceClassInfo.mPersistentObject,
1098                (jlong)0);
1099        env->MonitorExit(lock);
1100    }
1101    env->DeleteLocalRef(lock);
1102
1103    if (persistentSurface != NULL) {
1104        persistentSurface->decStrong(&sRefBaseOwner);
1105    }
1106    // no need to release surface as it will be released by Surface's jni
1107}
1108
1109static void android_media_MediaCodec_setInputSurface(
1110        JNIEnv* env, jobject thiz, jobject object) {
1111    ALOGV("android_media_MediaCodec_setInputSurface");
1112
1113    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
1114    if (codec == NULL) {
1115        throwExceptionAsNecessary(env, INVALID_OPERATION);
1116        return;
1117    }
1118
1119    sp<PersistentSurface> persistentSurface =
1120        android_media_MediaCodec_getPersistentInputSurface(env, object);
1121
1122    status_t err = codec->setInputSurface(persistentSurface);
1123    if (err != NO_ERROR) {
1124        throwExceptionAsNecessary(env, err);
1125    }
1126}
1127
1128static jobject android_media_MediaCodec_createInputSurface(JNIEnv* env,
1129        jobject thiz) {
1130    ALOGV("android_media_MediaCodec_createInputSurface");
1131
1132    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
1133    if (codec == NULL) {
1134        throwExceptionAsNecessary(env, INVALID_OPERATION);
1135        return NULL;
1136    }
1137
1138    // Tell the MediaCodec that we want to use a Surface as input.
1139    sp<IGraphicBufferProducer> bufferProducer;
1140    status_t err = codec->createInputSurface(&bufferProducer);
1141    if (err != NO_ERROR) {
1142        throwExceptionAsNecessary(env, err);
1143        return NULL;
1144    }
1145
1146    // Wrap the IGBP in a Java-language Surface.
1147    return android_view_Surface_createFromIGraphicBufferProducer(env,
1148            bufferProducer);
1149}
1150
1151static void android_media_MediaCodec_start(JNIEnv *env, jobject thiz) {
1152    ALOGV("android_media_MediaCodec_start");
1153
1154    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
1155
1156    if (codec == NULL) {
1157        throwExceptionAsNecessary(env, INVALID_OPERATION);
1158        return;
1159    }
1160
1161    status_t err = codec->start();
1162
1163    throwExceptionAsNecessary(env, err, ACTION_CODE_FATAL, "start failed");
1164}
1165
1166static void android_media_MediaCodec_stop(JNIEnv *env, jobject thiz) {
1167    ALOGV("android_media_MediaCodec_stop");
1168
1169    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
1170
1171    if (codec == NULL) {
1172        throwExceptionAsNecessary(env, INVALID_OPERATION);
1173        return;
1174    }
1175
1176    status_t err = codec->stop();
1177
1178    throwExceptionAsNecessary(env, err);
1179}
1180
1181static void android_media_MediaCodec_reset(JNIEnv *env, jobject thiz) {
1182    ALOGV("android_media_MediaCodec_reset");
1183
1184    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
1185
1186    if (codec == NULL) {
1187        throwExceptionAsNecessary(env, INVALID_OPERATION);
1188        return;
1189    }
1190
1191    status_t err = codec->reset();
1192    if (err != OK) {
1193        // treat all errors as fatal for now, though resource not available
1194        // errors could be treated as transient.
1195        // we also should avoid sending INVALID_OPERATION here due to
1196        // the transitory nature of reset(), it should not inadvertently
1197        // trigger an IllegalStateException.
1198        err = UNKNOWN_ERROR;
1199    }
1200    throwExceptionAsNecessary(env, err);
1201}
1202
1203static void android_media_MediaCodec_flush(JNIEnv *env, jobject thiz) {
1204    ALOGV("android_media_MediaCodec_flush");
1205
1206    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
1207
1208    if (codec == NULL) {
1209        throwExceptionAsNecessary(env, INVALID_OPERATION);
1210        return;
1211    }
1212
1213    status_t err = codec->flush();
1214
1215    throwExceptionAsNecessary(env, err);
1216}
1217
1218static void android_media_MediaCodec_queueInputBuffer(
1219        JNIEnv *env,
1220        jobject thiz,
1221        jint index,
1222        jint offset,
1223        jint size,
1224        jlong timestampUs,
1225        jint flags) {
1226    ALOGV("android_media_MediaCodec_queueInputBuffer");
1227
1228    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
1229
1230    if (codec == NULL) {
1231        throwExceptionAsNecessary(env, INVALID_OPERATION);
1232        return;
1233    }
1234
1235    AString errorDetailMsg;
1236
1237    status_t err = codec->queueInputBuffer(
1238            index, offset, size, timestampUs, flags, &errorDetailMsg);
1239
1240    throwExceptionAsNecessary(
1241            env, err, ACTION_CODE_FATAL, errorDetailMsg.empty() ? NULL : errorDetailMsg.c_str());
1242}
1243
1244static void android_media_MediaCodec_queueSecureInputBuffer(
1245        JNIEnv *env,
1246        jobject thiz,
1247        jint index,
1248        jint offset,
1249        jobject cryptoInfoObj,
1250        jlong timestampUs,
1251        jint flags) {
1252    ALOGV("android_media_MediaCodec_queueSecureInputBuffer");
1253
1254    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
1255
1256    if (codec == NULL) {
1257        throwExceptionAsNecessary(env, INVALID_OPERATION);
1258        return;
1259    }
1260
1261    jint numSubSamples =
1262        env->GetIntField(cryptoInfoObj, gFields.cryptoInfoNumSubSamplesID);
1263
1264    jintArray numBytesOfClearDataObj =
1265        (jintArray)env->GetObjectField(
1266                cryptoInfoObj, gFields.cryptoInfoNumBytesOfClearDataID);
1267
1268    jintArray numBytesOfEncryptedDataObj =
1269        (jintArray)env->GetObjectField(
1270                cryptoInfoObj, gFields.cryptoInfoNumBytesOfEncryptedDataID);
1271
1272    jbyteArray keyObj =
1273        (jbyteArray)env->GetObjectField(cryptoInfoObj, gFields.cryptoInfoKeyID);
1274
1275    jbyteArray ivObj =
1276        (jbyteArray)env->GetObjectField(cryptoInfoObj, gFields.cryptoInfoIVID);
1277
1278    jint mode = env->GetIntField(cryptoInfoObj, gFields.cryptoInfoModeID);
1279
1280    status_t err = OK;
1281
1282    CryptoPlugin::SubSample *subSamples = NULL;
1283    jbyte *key = NULL;
1284    jbyte *iv = NULL;
1285
1286    if (numSubSamples <= 0) {
1287        err = -EINVAL;
1288    } else if (numBytesOfClearDataObj == NULL
1289            && numBytesOfEncryptedDataObj == NULL) {
1290        err = -EINVAL;
1291    } else if (numBytesOfEncryptedDataObj != NULL
1292            && env->GetArrayLength(numBytesOfEncryptedDataObj) < numSubSamples) {
1293        err = -ERANGE;
1294    } else if (numBytesOfClearDataObj != NULL
1295            && env->GetArrayLength(numBytesOfClearDataObj) < numSubSamples) {
1296        err = -ERANGE;
1297    // subSamples array may silently overflow if number of samples are too large.  Use
1298    // INT32_MAX as maximum allocation size may be less than SIZE_MAX on some platforms
1299    } else if ( CC_UNLIKELY(numSubSamples >= (signed)(INT32_MAX / sizeof(*subSamples))) ) {
1300        err = -EINVAL;
1301    } else {
1302        jboolean isCopy;
1303
1304        jint *numBytesOfClearData =
1305            (numBytesOfClearDataObj == NULL)
1306                ? NULL
1307                : env->GetIntArrayElements(numBytesOfClearDataObj, &isCopy);
1308
1309        jint *numBytesOfEncryptedData =
1310            (numBytesOfEncryptedDataObj == NULL)
1311                ? NULL
1312                : env->GetIntArrayElements(numBytesOfEncryptedDataObj, &isCopy);
1313
1314        subSamples = new CryptoPlugin::SubSample[numSubSamples];
1315
1316        for (jint i = 0; i < numSubSamples; ++i) {
1317            subSamples[i].mNumBytesOfClearData =
1318                (numBytesOfClearData == NULL) ? 0 : numBytesOfClearData[i];
1319
1320            subSamples[i].mNumBytesOfEncryptedData =
1321                (numBytesOfEncryptedData == NULL)
1322                    ? 0 : numBytesOfEncryptedData[i];
1323        }
1324
1325        if (numBytesOfEncryptedData != NULL) {
1326            env->ReleaseIntArrayElements(
1327                    numBytesOfEncryptedDataObj, numBytesOfEncryptedData, 0);
1328            numBytesOfEncryptedData = NULL;
1329        }
1330
1331        if (numBytesOfClearData != NULL) {
1332            env->ReleaseIntArrayElements(
1333                    numBytesOfClearDataObj, numBytesOfClearData, 0);
1334            numBytesOfClearData = NULL;
1335        }
1336    }
1337
1338    if (err == OK && keyObj != NULL) {
1339        if (env->GetArrayLength(keyObj) != 16) {
1340            err = -EINVAL;
1341        } else {
1342            jboolean isCopy;
1343            key = env->GetByteArrayElements(keyObj, &isCopy);
1344        }
1345    }
1346
1347    if (err == OK && ivObj != NULL) {
1348        if (env->GetArrayLength(ivObj) != 16) {
1349            err = -EINVAL;
1350        } else {
1351            jboolean isCopy;
1352            iv = env->GetByteArrayElements(ivObj, &isCopy);
1353        }
1354    }
1355
1356    AString errorDetailMsg;
1357
1358    if (err == OK) {
1359        err = codec->queueSecureInputBuffer(
1360                index, offset,
1361                subSamples, numSubSamples,
1362                (const uint8_t *)key, (const uint8_t *)iv,
1363                (CryptoPlugin::Mode)mode,
1364                timestampUs,
1365                flags,
1366                &errorDetailMsg);
1367    }
1368
1369    if (iv != NULL) {
1370        env->ReleaseByteArrayElements(ivObj, iv, 0);
1371        iv = NULL;
1372    }
1373
1374    if (key != NULL) {
1375        env->ReleaseByteArrayElements(keyObj, key, 0);
1376        key = NULL;
1377    }
1378
1379    delete[] subSamples;
1380    subSamples = NULL;
1381
1382    throwExceptionAsNecessary(
1383            env, err, ACTION_CODE_FATAL, errorDetailMsg.empty() ? NULL : errorDetailMsg.c_str());
1384}
1385
1386static jint android_media_MediaCodec_dequeueInputBuffer(
1387        JNIEnv *env, jobject thiz, jlong timeoutUs) {
1388    ALOGV("android_media_MediaCodec_dequeueInputBuffer");
1389
1390    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
1391
1392    if (codec == NULL) {
1393        throwExceptionAsNecessary(env, INVALID_OPERATION);
1394        return -1;
1395    }
1396
1397    size_t index;
1398    status_t err = codec->dequeueInputBuffer(&index, timeoutUs);
1399
1400    if (err == OK) {
1401        return (jint) index;
1402    }
1403
1404    return throwExceptionAsNecessary(env, err);
1405}
1406
1407static jint android_media_MediaCodec_dequeueOutputBuffer(
1408        JNIEnv *env, jobject thiz, jobject bufferInfo, jlong timeoutUs) {
1409    ALOGV("android_media_MediaCodec_dequeueOutputBuffer");
1410
1411    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
1412
1413    if (codec == NULL) {
1414        throwExceptionAsNecessary(env, INVALID_OPERATION);
1415        return 0;
1416    }
1417
1418    size_t index;
1419    status_t err = codec->dequeueOutputBuffer(
1420            env, bufferInfo, &index, timeoutUs);
1421
1422    if (err == OK) {
1423        return (jint) index;
1424    }
1425
1426    return throwExceptionAsNecessary(env, err);
1427}
1428
1429static void android_media_MediaCodec_releaseOutputBuffer(
1430        JNIEnv *env, jobject thiz,
1431        jint index, jboolean render, jboolean updatePTS, jlong timestampNs) {
1432    ALOGV("android_media_MediaCodec_renderOutputBufferAndRelease");
1433
1434    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
1435
1436    if (codec == NULL) {
1437        throwExceptionAsNecessary(env, INVALID_OPERATION);
1438        return;
1439    }
1440
1441    status_t err = codec->releaseOutputBuffer(index, render, updatePTS, timestampNs);
1442
1443    throwExceptionAsNecessary(env, err);
1444}
1445
1446static void android_media_MediaCodec_signalEndOfInputStream(JNIEnv* env,
1447        jobject thiz) {
1448    ALOGV("android_media_MediaCodec_signalEndOfInputStream");
1449
1450    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
1451    if (codec == NULL) {
1452        throwExceptionAsNecessary(env, INVALID_OPERATION);
1453        return;
1454    }
1455
1456    status_t err = codec->signalEndOfInputStream();
1457
1458    throwExceptionAsNecessary(env, err);
1459}
1460
1461static jobject android_media_MediaCodec_getFormatNative(
1462        JNIEnv *env, jobject thiz, jboolean input) {
1463    ALOGV("android_media_MediaCodec_getFormatNative");
1464
1465    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
1466
1467    if (codec == NULL) {
1468        throwExceptionAsNecessary(env, INVALID_OPERATION);
1469        return NULL;
1470    }
1471
1472    jobject format;
1473    status_t err = codec->getFormat(env, input, &format);
1474
1475    if (err == OK) {
1476        return format;
1477    }
1478
1479    throwExceptionAsNecessary(env, err);
1480
1481    return NULL;
1482}
1483
1484static jobject android_media_MediaCodec_getOutputFormatForIndexNative(
1485        JNIEnv *env, jobject thiz, jint index) {
1486    ALOGV("android_media_MediaCodec_getOutputFormatForIndexNative");
1487
1488    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
1489
1490    if (codec == NULL) {
1491        throwExceptionAsNecessary(env, INVALID_OPERATION);
1492        return NULL;
1493    }
1494
1495    jobject format;
1496    status_t err = codec->getOutputFormat(env, index, &format);
1497
1498    if (err == OK) {
1499        return format;
1500    }
1501
1502    throwExceptionAsNecessary(env, err);
1503
1504    return NULL;
1505}
1506
1507static jobjectArray android_media_MediaCodec_getBuffers(
1508        JNIEnv *env, jobject thiz, jboolean input) {
1509    ALOGV("android_media_MediaCodec_getBuffers");
1510
1511    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
1512
1513    if (codec == NULL) {
1514        throwExceptionAsNecessary(env, INVALID_OPERATION);
1515        return NULL;
1516    }
1517
1518    jobjectArray buffers;
1519    status_t err = codec->getBuffers(env, input, &buffers);
1520
1521    if (err == OK) {
1522        return buffers;
1523    }
1524
1525    // if we're out of memory, an exception was already thrown
1526    if (err != NO_MEMORY) {
1527        throwExceptionAsNecessary(env, err);
1528    }
1529
1530    return NULL;
1531}
1532
1533static jobject android_media_MediaCodec_getBuffer(
1534        JNIEnv *env, jobject thiz, jboolean input, jint index) {
1535    ALOGV("android_media_MediaCodec_getBuffer");
1536
1537    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
1538
1539    if (codec == NULL) {
1540        throwExceptionAsNecessary(env, INVALID_OPERATION);
1541        return NULL;
1542    }
1543
1544    jobject buffer;
1545    status_t err = codec->getBuffer(env, input, index, &buffer);
1546
1547    if (err == OK) {
1548        return buffer;
1549    }
1550
1551    // if we're out of memory, an exception was already thrown
1552    if (err != NO_MEMORY) {
1553        throwExceptionAsNecessary(env, err);
1554    }
1555
1556    return NULL;
1557}
1558
1559static jobject android_media_MediaCodec_getImage(
1560        JNIEnv *env, jobject thiz, jboolean input, jint index) {
1561    ALOGV("android_media_MediaCodec_getImage");
1562
1563    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
1564
1565    if (codec == NULL) {
1566        throwExceptionAsNecessary(env, INVALID_OPERATION);
1567        return NULL;
1568    }
1569
1570    jobject image;
1571    status_t err = codec->getImage(env, input, index, &image);
1572
1573    if (err == OK) {
1574        return image;
1575    }
1576
1577    // if we're out of memory, an exception was already thrown
1578    if (err != NO_MEMORY) {
1579        throwExceptionAsNecessary(env, err);
1580    }
1581
1582    return NULL;
1583}
1584
1585static jobject android_media_MediaCodec_getName(
1586        JNIEnv *env, jobject thiz) {
1587    ALOGV("android_media_MediaCodec_getName");
1588
1589    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
1590
1591    if (codec == NULL) {
1592        throwExceptionAsNecessary(env, INVALID_OPERATION);
1593        return NULL;
1594    }
1595
1596    jstring name;
1597    status_t err = codec->getName(env, &name);
1598
1599    if (err == OK) {
1600        return name;
1601    }
1602
1603    throwExceptionAsNecessary(env, err);
1604
1605    return NULL;
1606}
1607
1608static void android_media_MediaCodec_setParameters(
1609        JNIEnv *env, jobject thiz, jobjectArray keys, jobjectArray vals) {
1610    ALOGV("android_media_MediaCodec_setParameters");
1611
1612    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
1613
1614    if (codec == NULL) {
1615        throwExceptionAsNecessary(env, INVALID_OPERATION);
1616        return;
1617    }
1618
1619    sp<AMessage> params;
1620    status_t err = ConvertKeyValueArraysToMessage(env, keys, vals, &params);
1621
1622    if (err == OK) {
1623        err = codec->setParameters(params);
1624    }
1625
1626    throwExceptionAsNecessary(env, err);
1627}
1628
1629static void android_media_MediaCodec_setVideoScalingMode(
1630        JNIEnv *env, jobject thiz, jint mode) {
1631    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
1632
1633    if (codec == NULL) {
1634        throwExceptionAsNecessary(env, INVALID_OPERATION);
1635        return;
1636    }
1637
1638    if (mode != NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW
1639            && mode != NATIVE_WINDOW_SCALING_MODE_SCALE_CROP) {
1640        jniThrowException(env, "java/lang/InvalidArgumentException", NULL);
1641        return;
1642    }
1643
1644    codec->setVideoScalingMode(mode);
1645}
1646
1647static void android_media_MediaCodec_native_init(JNIEnv *env) {
1648    ScopedLocalRef<jclass> clazz(
1649            env, env->FindClass("android/media/MediaCodec"));
1650    CHECK(clazz.get() != NULL);
1651
1652    gFields.context = env->GetFieldID(clazz.get(), "mNativeContext", "J");
1653    CHECK(gFields.context != NULL);
1654
1655    gFields.postEventFromNativeID =
1656        env->GetMethodID(
1657                clazz.get(), "postEventFromNative", "(IIILjava/lang/Object;)V");
1658
1659    CHECK(gFields.postEventFromNativeID != NULL);
1660
1661    clazz.reset(env->FindClass("android/media/MediaCodec$CryptoInfo"));
1662    CHECK(clazz.get() != NULL);
1663
1664    gFields.cryptoInfoNumSubSamplesID =
1665        env->GetFieldID(clazz.get(), "numSubSamples", "I");
1666    CHECK(gFields.cryptoInfoNumSubSamplesID != NULL);
1667
1668    gFields.cryptoInfoNumBytesOfClearDataID =
1669        env->GetFieldID(clazz.get(), "numBytesOfClearData", "[I");
1670    CHECK(gFields.cryptoInfoNumBytesOfClearDataID != NULL);
1671
1672    gFields.cryptoInfoNumBytesOfEncryptedDataID =
1673        env->GetFieldID(clazz.get(), "numBytesOfEncryptedData", "[I");
1674    CHECK(gFields.cryptoInfoNumBytesOfEncryptedDataID != NULL);
1675
1676    gFields.cryptoInfoKeyID = env->GetFieldID(clazz.get(), "key", "[B");
1677    CHECK(gFields.cryptoInfoKeyID != NULL);
1678
1679    gFields.cryptoInfoIVID = env->GetFieldID(clazz.get(), "iv", "[B");
1680    CHECK(gFields.cryptoInfoIVID != NULL);
1681
1682    gFields.cryptoInfoModeID = env->GetFieldID(clazz.get(), "mode", "I");
1683    CHECK(gFields.cryptoInfoModeID != NULL);
1684
1685    clazz.reset(env->FindClass("android/media/MediaCodec$CryptoException"));
1686    CHECK(clazz.get() != NULL);
1687
1688    jfieldID field;
1689    field = env->GetStaticFieldID(clazz.get(), "ERROR_NO_KEY", "I");
1690    CHECK(field != NULL);
1691    gCryptoErrorCodes.cryptoErrorNoKey =
1692        env->GetStaticIntField(clazz.get(), field);
1693
1694    field = env->GetStaticFieldID(clazz.get(), "ERROR_KEY_EXPIRED", "I");
1695    CHECK(field != NULL);
1696    gCryptoErrorCodes.cryptoErrorKeyExpired =
1697        env->GetStaticIntField(clazz.get(), field);
1698
1699    field = env->GetStaticFieldID(clazz.get(), "ERROR_RESOURCE_BUSY", "I");
1700    CHECK(field != NULL);
1701    gCryptoErrorCodes.cryptoErrorResourceBusy =
1702        env->GetStaticIntField(clazz.get(), field);
1703
1704    field = env->GetStaticFieldID(clazz.get(), "ERROR_INSUFFICIENT_OUTPUT_PROTECTION", "I");
1705    CHECK(field != NULL);
1706    gCryptoErrorCodes.cryptoErrorInsufficientOutputProtection =
1707        env->GetStaticIntField(clazz.get(), field);
1708
1709    field = env->GetStaticFieldID(clazz.get(), "ERROR_SESSION_NOT_OPENED", "I");
1710    CHECK(field != NULL);
1711    gCryptoErrorCodes.cryptoErrorSessionNotOpened =
1712        env->GetStaticIntField(clazz.get(), field);
1713
1714    clazz.reset(env->FindClass("android/media/MediaCodec$CodecException"));
1715    CHECK(clazz.get() != NULL);
1716    field = env->GetStaticFieldID(clazz.get(), "ACTION_TRANSIENT", "I");
1717    CHECK(field != NULL);
1718    gCodecActionCodes.codecActionTransient =
1719        env->GetStaticIntField(clazz.get(), field);
1720
1721    field = env->GetStaticFieldID(clazz.get(), "ACTION_RECOVERABLE", "I");
1722    CHECK(field != NULL);
1723    gCodecActionCodes.codecActionRecoverable =
1724        env->GetStaticIntField(clazz.get(), field);
1725
1726    field = env->GetStaticFieldID(clazz.get(), "ERROR_INSUFFICIENT_RESOURCE", "I");
1727    CHECK(field != NULL);
1728    gCodecErrorCodes.errorInsufficientResource =
1729        env->GetStaticIntField(clazz.get(), field);
1730
1731    field = env->GetStaticFieldID(clazz.get(), "ERROR_RECLAIMED", "I");
1732    CHECK(field != NULL);
1733    gCodecErrorCodes.errorReclaimed =
1734        env->GetStaticIntField(clazz.get(), field);
1735
1736    clazz.reset(env->FindClass("android/view/Surface"));
1737    CHECK(clazz.get() != NULL);
1738
1739    field = env->GetFieldID(clazz.get(), "mLock", "Ljava/lang/Object;");
1740    CHECK(field != NULL);
1741    gPersistentSurfaceClassInfo.mLock = field;
1742
1743    jmethodID method = env->GetMethodID(clazz.get(), "setNativeObjectLocked", "(J)V");
1744    CHECK(method != NULL);
1745    gPersistentSurfaceClassInfo.setNativeObjectLocked = method;
1746
1747    clazz.reset(env->FindClass("android/media/MediaCodec$PersistentSurface"));
1748    CHECK(clazz.get() != NULL);
1749    gPersistentSurfaceClassInfo.clazz = (jclass)env->NewGlobalRef(clazz.get());
1750
1751    method = env->GetMethodID(clazz.get(), "<init>", "()V");
1752    CHECK(method != NULL);
1753    gPersistentSurfaceClassInfo.ctor = method;
1754
1755    field = env->GetFieldID(clazz.get(), "mPersistentObject", "J");
1756    CHECK(field != NULL);
1757    gPersistentSurfaceClassInfo.mPersistentObject = field;
1758}
1759
1760static void android_media_MediaCodec_native_setup(
1761        JNIEnv *env, jobject thiz,
1762        jstring name, jboolean nameIsType, jboolean encoder) {
1763    if (name == NULL) {
1764        jniThrowException(env, "java/lang/NullPointerException", NULL);
1765        return;
1766    }
1767
1768    const char *tmp = env->GetStringUTFChars(name, NULL);
1769
1770    if (tmp == NULL) {
1771        return;
1772    }
1773
1774    sp<JMediaCodec> codec = new JMediaCodec(env, thiz, tmp, nameIsType, encoder);
1775
1776    const status_t err = codec->initCheck();
1777    if (err == NAME_NOT_FOUND) {
1778        // fail and do not try again.
1779        jniThrowException(env, "java/lang/IllegalArgumentException",
1780                String8::format("Failed to initialize %s, error %#x", tmp, err));
1781        env->ReleaseStringUTFChars(name, tmp);
1782        return;
1783    } if (err == NO_MEMORY) {
1784        throwCodecException(env, err, ACTION_CODE_TRANSIENT,
1785                String8::format("Failed to initialize %s, error %#x", tmp, err));
1786        env->ReleaseStringUTFChars(name, tmp);
1787        return;
1788    } else if (err != OK) {
1789        // believed possible to try again
1790        jniThrowException(env, "java/io/IOException",
1791                String8::format("Failed to find matching codec %s, error %#x", tmp, err));
1792        env->ReleaseStringUTFChars(name, tmp);
1793        return;
1794    }
1795
1796    env->ReleaseStringUTFChars(name, tmp);
1797
1798    codec->registerSelf();
1799
1800    setMediaCodec(env,thiz, codec);
1801}
1802
1803static void android_media_MediaCodec_native_finalize(
1804        JNIEnv *env, jobject thiz) {
1805    android_media_MediaCodec_release(env, thiz);
1806}
1807
1808static const JNINativeMethod gMethods[] = {
1809    { "native_release", "()V", (void *)android_media_MediaCodec_release },
1810
1811    { "native_reset", "()V", (void *)android_media_MediaCodec_reset },
1812
1813    { "native_releasePersistentInputSurface",
1814      "(Landroid/view/Surface;)V",
1815       (void *)android_media_MediaCodec_releasePersistentInputSurface},
1816
1817    { "native_createPersistentInputSurface",
1818      "()Landroid/media/MediaCodec$PersistentSurface;",
1819      (void *)android_media_MediaCodec_createPersistentInputSurface },
1820
1821    { "native_setInputSurface", "(Landroid/view/Surface;)V",
1822      (void *)android_media_MediaCodec_setInputSurface },
1823
1824    { "native_enableOnFrameRenderedListener", "(Z)V",
1825      (void *)android_media_MediaCodec_native_enableOnFrameRenderedListener },
1826
1827    { "native_setCallback",
1828      "(Landroid/media/MediaCodec$Callback;)V",
1829      (void *)android_media_MediaCodec_native_setCallback },
1830
1831    { "native_configure",
1832      "([Ljava/lang/String;[Ljava/lang/Object;Landroid/view/Surface;"
1833      "Landroid/media/MediaCrypto;I)V",
1834      (void *)android_media_MediaCodec_native_configure },
1835
1836    { "native_setSurface",
1837      "(Landroid/view/Surface;)V",
1838      (void *)android_media_MediaCodec_native_setSurface },
1839
1840    { "createInputSurface", "()Landroid/view/Surface;",
1841      (void *)android_media_MediaCodec_createInputSurface },
1842
1843    { "native_start", "()V", (void *)android_media_MediaCodec_start },
1844    { "native_stop", "()V", (void *)android_media_MediaCodec_stop },
1845    { "native_flush", "()V", (void *)android_media_MediaCodec_flush },
1846
1847    { "native_queueInputBuffer", "(IIIJI)V",
1848      (void *)android_media_MediaCodec_queueInputBuffer },
1849
1850    { "native_queueSecureInputBuffer", "(IILandroid/media/MediaCodec$CryptoInfo;JI)V",
1851      (void *)android_media_MediaCodec_queueSecureInputBuffer },
1852
1853    { "native_dequeueInputBuffer", "(J)I",
1854      (void *)android_media_MediaCodec_dequeueInputBuffer },
1855
1856    { "native_dequeueOutputBuffer", "(Landroid/media/MediaCodec$BufferInfo;J)I",
1857      (void *)android_media_MediaCodec_dequeueOutputBuffer },
1858
1859    { "releaseOutputBuffer", "(IZZJ)V",
1860      (void *)android_media_MediaCodec_releaseOutputBuffer },
1861
1862    { "signalEndOfInputStream", "()V",
1863      (void *)android_media_MediaCodec_signalEndOfInputStream },
1864
1865    { "getFormatNative", "(Z)Ljava/util/Map;",
1866      (void *)android_media_MediaCodec_getFormatNative },
1867
1868    { "getOutputFormatNative", "(I)Ljava/util/Map;",
1869      (void *)android_media_MediaCodec_getOutputFormatForIndexNative },
1870
1871    { "getBuffers", "(Z)[Ljava/nio/ByteBuffer;",
1872      (void *)android_media_MediaCodec_getBuffers },
1873
1874    { "getBuffer", "(ZI)Ljava/nio/ByteBuffer;",
1875      (void *)android_media_MediaCodec_getBuffer },
1876
1877    { "getImage", "(ZI)Landroid/media/Image;",
1878      (void *)android_media_MediaCodec_getImage },
1879
1880    { "getName", "()Ljava/lang/String;",
1881      (void *)android_media_MediaCodec_getName },
1882
1883    { "setParameters", "([Ljava/lang/String;[Ljava/lang/Object;)V",
1884      (void *)android_media_MediaCodec_setParameters },
1885
1886    { "setVideoScalingMode", "(I)V",
1887      (void *)android_media_MediaCodec_setVideoScalingMode },
1888
1889    { "native_init", "()V", (void *)android_media_MediaCodec_native_init },
1890
1891    { "native_setup", "(Ljava/lang/String;ZZ)V",
1892      (void *)android_media_MediaCodec_native_setup },
1893
1894    { "native_finalize", "()V",
1895      (void *)android_media_MediaCodec_native_finalize },
1896};
1897
1898int register_android_media_MediaCodec(JNIEnv *env) {
1899    return AndroidRuntime::registerNativeMethods(env,
1900                "android/media/MediaCodec", gMethods, NELEM(gMethods));
1901}
1902