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