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