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