android_media_MediaCodec.cpp revision 8d5e556b2b76278000a506382fdf594c071e1fbd
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
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};
60
61struct CryptoErrorCodes {
62    jint cryptoErrorNoKey;
63    jint cryptoErrorKeyExpired;
64    jint cryptoErrorResourceBusy;
65} gCryptoErrorCodes;
66
67struct fields_t {
68    jfieldID context;
69    jmethodID postEventFromNativeID;
70    jfieldID cryptoInfoNumSubSamplesID;
71    jfieldID cryptoInfoNumBytesOfClearDataID;
72    jfieldID cryptoInfoNumBytesOfEncryptedDataID;
73    jfieldID cryptoInfoKeyID;
74    jfieldID cryptoInfoIVID;
75    jfieldID cryptoInfoModeID;
76};
77
78static fields_t gFields;
79
80////////////////////////////////////////////////////////////////////////////////
81
82JMediaCodec::JMediaCodec(
83        JNIEnv *env, jobject thiz,
84        const char *name, bool nameIsType, bool encoder)
85    : mClass(NULL),
86      mObject(NULL) {
87    jclass clazz = env->GetObjectClass(thiz);
88    CHECK(clazz != NULL);
89
90    mClass = (jclass)env->NewGlobalRef(clazz);
91    mObject = env->NewWeakGlobalRef(thiz);
92
93    mLooper = new ALooper;
94    mLooper->setName("MediaCodec_looper");
95
96    mLooper->start(
97            false,      // runOnCallingThread
98            true,       // canCallJava
99            PRIORITY_FOREGROUND);
100
101    if (nameIsType) {
102        mCodec = MediaCodec::CreateByType(mLooper, name, encoder);
103    } else {
104        mCodec = MediaCodec::CreateByComponentName(mLooper, name);
105    }
106}
107
108status_t JMediaCodec::initCheck() const {
109    return mCodec != NULL ? OK : NO_INIT;
110}
111
112void JMediaCodec::registerSelf() {
113    mLooper->registerHandler(this);
114}
115
116void JMediaCodec::release() {
117    if (mCodec != NULL) {
118        mCodec->release();
119        mCodec.clear();
120    }
121
122    if (mLooper != NULL) {
123        mLooper->unregisterHandler(id());
124        mLooper->stop();
125        mLooper.clear();
126    }
127}
128
129JMediaCodec::~JMediaCodec() {
130    if (mCodec != NULL || mLooper != NULL) {
131        /* MediaCodec and looper should have been released explicitly already
132         * in setMediaCodec() (see comments in setMediaCodec()).
133         *
134         * Otherwise JMediaCodec::~JMediaCodec() might be called from within the
135         * message handler, doing release() there risks deadlock as MediaCodec::
136         * release() post synchronous message to the same looper.
137         *
138         * Print a warning and try to proceed with releasing.
139         */
140        ALOGW("try to release MediaCodec from JMediaCodec::~JMediaCodec()...");
141        release();
142        ALOGW("done releasing MediaCodec from JMediaCodec::~JMediaCodec().");
143    }
144
145    JNIEnv *env = AndroidRuntime::getJNIEnv();
146
147    env->DeleteWeakGlobalRef(mObject);
148    mObject = NULL;
149    env->DeleteGlobalRef(mClass);
150    mClass = NULL;
151}
152
153status_t JMediaCodec::setCallback(jobject cb) {
154    if (cb != NULL) {
155        if (mCallbackNotification == NULL) {
156            mCallbackNotification = new AMessage(kWhatCallbackNotify, id());
157        }
158    } else {
159        mCallbackNotification.clear();
160    }
161
162    return mCodec->setCallback(mCallbackNotification);
163}
164
165status_t JMediaCodec::configure(
166        const sp<AMessage> &format,
167        const sp<IGraphicBufferProducer> &bufferProducer,
168        const sp<ICrypto> &crypto,
169        int flags) {
170    sp<Surface> client;
171    if (bufferProducer != NULL) {
172        mSurfaceTextureClient =
173            new Surface(bufferProducer, true /* controlledByApp */);
174    } else {
175        mSurfaceTextureClient.clear();
176    }
177
178    return mCodec->configure(format, mSurfaceTextureClient, crypto, flags);
179}
180
181status_t JMediaCodec::createInputSurface(
182        sp<IGraphicBufferProducer>* bufferProducer) {
183    return mCodec->createInputSurface(bufferProducer);
184}
185
186status_t JMediaCodec::start() {
187    return mCodec->start();
188}
189
190status_t JMediaCodec::stop() {
191    mSurfaceTextureClient.clear();
192
193    return mCodec->stop();
194}
195
196status_t JMediaCodec::flush() {
197    return mCodec->flush();
198}
199
200status_t JMediaCodec::queueInputBuffer(
201        size_t index,
202        size_t offset, size_t size, int64_t timeUs, uint32_t flags,
203        AString *errorDetailMsg) {
204    return mCodec->queueInputBuffer(
205            index, offset, size, timeUs, flags, errorDetailMsg);
206}
207
208status_t JMediaCodec::queueSecureInputBuffer(
209        size_t index,
210        size_t offset,
211        const CryptoPlugin::SubSample *subSamples,
212        size_t numSubSamples,
213        const uint8_t key[16],
214        const uint8_t iv[16],
215        CryptoPlugin::Mode mode,
216        int64_t presentationTimeUs,
217        uint32_t flags,
218        AString *errorDetailMsg) {
219    return mCodec->queueSecureInputBuffer(
220            index, offset, subSamples, numSubSamples, key, iv, mode,
221            presentationTimeUs, flags, errorDetailMsg);
222}
223
224status_t JMediaCodec::dequeueInputBuffer(size_t *index, int64_t timeoutUs) {
225    return mCodec->dequeueInputBuffer(index, timeoutUs);
226}
227
228status_t JMediaCodec::dequeueOutputBuffer(
229        JNIEnv *env, jobject bufferInfo, size_t *index, int64_t timeoutUs) {
230    size_t size, offset;
231    int64_t timeUs;
232    uint32_t flags;
233    status_t err = mCodec->dequeueOutputBuffer(
234            index, &offset, &size, &timeUs, &flags, timeoutUs);
235
236    if (err != OK) {
237        return err;
238    }
239
240    ScopedLocalRef<jclass> clazz(
241            env, env->FindClass("android/media/MediaCodec$BufferInfo"));
242
243    jmethodID method = env->GetMethodID(clazz.get(), "set", "(IIJI)V");
244    env->CallVoidMethod(bufferInfo, method, (jint)offset, (jint)size, timeUs, flags);
245
246    return OK;
247}
248
249status_t JMediaCodec::releaseOutputBuffer(
250        size_t index, bool render, bool updatePTS, int64_t timestampNs) {
251    if (updatePTS) {
252        return mCodec->renderOutputBufferAndRelease(index, timestampNs);
253    }
254    return render
255        ? mCodec->renderOutputBufferAndRelease(index)
256        : mCodec->releaseOutputBuffer(index);
257}
258
259status_t JMediaCodec::signalEndOfInputStream() {
260    return mCodec->signalEndOfInputStream();
261}
262
263status_t JMediaCodec::getOutputFormat(JNIEnv *env, jobject *format) const {
264    sp<AMessage> msg;
265    status_t err;
266    if ((err = mCodec->getOutputFormat(&msg)) != OK) {
267        return err;
268    }
269
270    return ConvertMessageToMap(env, msg, format);
271}
272
273status_t JMediaCodec::getBuffers(
274        JNIEnv *env, bool input, jobjectArray *bufArray) const {
275    Vector<sp<ABuffer> > buffers;
276
277    status_t err =
278        input
279            ? mCodec->getInputBuffers(&buffers)
280            : mCodec->getOutputBuffers(&buffers);
281
282    if (err != OK) {
283        return err;
284    }
285
286    ScopedLocalRef<jclass> byteBufferClass(
287            env, env->FindClass("java/nio/ByteBuffer"));
288
289    CHECK(byteBufferClass.get() != NULL);
290
291    jmethodID orderID = env->GetMethodID(
292            byteBufferClass.get(),
293            "order",
294            "(Ljava/nio/ByteOrder;)Ljava/nio/ByteBuffer;");
295
296    CHECK(orderID != NULL);
297
298    ScopedLocalRef<jclass> byteOrderClass(
299            env, env->FindClass("java/nio/ByteOrder"));
300
301    CHECK(byteOrderClass.get() != NULL);
302
303    jmethodID nativeOrderID = env->GetStaticMethodID(
304            byteOrderClass.get(), "nativeOrder", "()Ljava/nio/ByteOrder;");
305    CHECK(nativeOrderID != NULL);
306
307    jobject nativeByteOrderObj =
308        env->CallStaticObjectMethod(byteOrderClass.get(), nativeOrderID);
309    CHECK(nativeByteOrderObj != NULL);
310
311    *bufArray = (jobjectArray)env->NewObjectArray(
312            buffers.size(), byteBufferClass.get(), NULL);
313    if (*bufArray == NULL) {
314        env->DeleteLocalRef(nativeByteOrderObj);
315        return NO_MEMORY;
316    }
317
318    for (size_t i = 0; i < buffers.size(); ++i) {
319        const sp<ABuffer> &buffer = buffers.itemAt(i);
320
321        // if this is an ABuffer that doesn't actually hold any accessible memory,
322        // use a null ByteBuffer
323        if (buffer->base() == NULL) {
324            continue;
325        }
326        jobject byteBuffer =
327            env->NewDirectByteBuffer(
328                buffer->base(),
329                buffer->capacity());
330        if (byteBuffer == NULL) {
331            env->DeleteLocalRef(nativeByteOrderObj);
332            return NO_MEMORY;
333        }
334        jobject me = env->CallObjectMethod(
335                byteBuffer, orderID, nativeByteOrderObj);
336        env->DeleteLocalRef(me);
337        me = NULL;
338
339        env->SetObjectArrayElement(
340                *bufArray, i, byteBuffer);
341
342        env->DeleteLocalRef(byteBuffer);
343        byteBuffer = NULL;
344    }
345
346    env->DeleteLocalRef(nativeByteOrderObj);
347    nativeByteOrderObj = NULL;
348
349    return OK;
350}
351
352status_t JMediaCodec::getName(JNIEnv *env, jstring *nameStr) const {
353    AString name;
354
355    status_t err = mCodec->getName(&name);
356
357    if (err != OK) {
358        return err;
359    }
360
361    *nameStr = env->NewStringUTF(name.c_str());
362
363    return OK;
364}
365
366status_t JMediaCodec::setParameters(const sp<AMessage> &msg) {
367    return mCodec->setParameters(msg);
368}
369
370void JMediaCodec::setVideoScalingMode(int mode) {
371    if (mSurfaceTextureClient != NULL) {
372        native_window_set_scaling_mode(mSurfaceTextureClient.get(), mode);
373    }
374}
375
376void JMediaCodec::handleCallback(const sp<AMessage> &msg) {
377    int32_t arg1, arg2 = 0;
378    jobject obj = NULL;
379    CHECK(msg->findInt32("callbackID", &arg1));
380    JNIEnv *env = AndroidRuntime::getJNIEnv();
381
382    switch (arg1) {
383        case MediaCodec::CB_INPUT_AVAILABLE:
384        {
385            CHECK(msg->findInt32("index", &arg2));
386            break;
387        }
388
389        case MediaCodec::CB_OUTPUT_AVAILABLE:
390        {
391            CHECK(msg->findInt32("index", &arg2));
392
393            size_t size, offset;
394            int64_t timeUs;
395            uint32_t flags;
396            CHECK(msg->findSize("size", &size));
397            CHECK(msg->findSize("offset", &offset));
398            CHECK(msg->findInt64("timeUs", &timeUs));
399            CHECK(msg->findInt32("flags", (int32_t *)&flags));
400
401            ScopedLocalRef<jclass> clazz(
402                    env, env->FindClass("android/media/MediaCodec$BufferInfo"));
403            jmethodID ctor = env->GetMethodID(clazz.get(), "<init>", "()V");
404            jmethodID method = env->GetMethodID(clazz.get(), "set", "(IIJI)V");
405
406            obj = env->NewObject(clazz.get(), ctor);
407
408            if (obj == NULL) {
409                if (env->ExceptionCheck()) {
410                    ALOGE("Could not create MediaCodec.BufferInfo.");
411                    env->ExceptionClear();
412                }
413                jniThrowException(env, "java/lang/IllegalStateException", NULL);
414                return;
415            }
416
417            env->CallVoidMethod(obj, method, (jint)offset, (jint)size, timeUs, flags);
418            break;
419        }
420
421        case MediaCodec::CB_ERROR:
422        {
423            CHECK(msg->findInt32("err", &arg2));
424
425            int32_t actionCode;
426            CHECK(msg->findInt32("actionCode", &actionCode));
427
428            // use Integer object to pass the action code
429            ScopedLocalRef<jclass> clazz(
430                    env, env->FindClass("java/lang/Integer"));
431            jmethodID ctor = env->GetMethodID(clazz.get(), "<init>", "(I)V");
432            obj = env->NewObject(clazz.get(), ctor, actionCode);
433
434            if (obj == NULL) {
435                if (env->ExceptionCheck()) {
436                    ALOGE("Could not create Integer object for actionCode.");
437                    env->ExceptionClear();
438                }
439                jniThrowException(env, "java/lang/IllegalStateException", NULL);
440                return;
441            }
442
443            break;
444        }
445
446        case MediaCodec::CB_OUTPUT_FORMAT_CHANGED:
447        {
448            sp<AMessage> format;
449            CHECK(msg->findMessage("format", &format));
450
451            if (OK != ConvertMessageToMap(env, format, &obj)) {
452                jniThrowException(env, "java/lang/IllegalStateException", NULL);
453                return;
454            }
455
456            break;
457        }
458
459        default:
460            TRESPASS();
461    }
462
463    env->CallVoidMethod(
464            mObject,
465            gFields.postEventFromNativeID,
466            EVENT_CALLBACK,
467            arg1,
468            arg2,
469            obj);
470
471    env->DeleteLocalRef(obj);
472}
473
474void JMediaCodec::onMessageReceived(const sp<AMessage> &msg) {
475    switch (msg->what()) {
476        case kWhatCallbackNotify:
477        {
478            handleCallback(msg);
479            break;
480        }
481        default:
482            TRESPASS();
483    }
484}
485
486}  // namespace android
487
488////////////////////////////////////////////////////////////////////////////////
489
490using namespace android;
491
492static sp<JMediaCodec> setMediaCodec(
493        JNIEnv *env, jobject thiz, const sp<JMediaCodec> &codec) {
494    sp<JMediaCodec> old = (JMediaCodec *)env->GetLongField(thiz, gFields.context);
495    if (codec != NULL) {
496        codec->incStrong(thiz);
497    }
498    if (old != NULL) {
499        /* release MediaCodec and stop the looper now before decStrong.
500         * otherwise JMediaCodec::~JMediaCodec() could be called from within
501         * its message handler, doing release() from there will deadlock
502         * (as MediaCodec::release() post synchronous message to the same looper)
503         */
504        old->release();
505        old->decStrong(thiz);
506    }
507    env->SetLongField(thiz, gFields.context, (jlong)codec.get());
508
509    return old;
510}
511
512static sp<JMediaCodec> getMediaCodec(JNIEnv *env, jobject thiz) {
513    return (JMediaCodec *)env->GetLongField(thiz, gFields.context);
514}
515
516static void android_media_MediaCodec_release(JNIEnv *env, jobject thiz) {
517    setMediaCodec(env, thiz, NULL);
518}
519
520static void throwCryptoException(JNIEnv *env, status_t err, const char *msg) {
521    ScopedLocalRef<jclass> clazz(
522            env, env->FindClass("android/media/MediaCodec$CryptoException"));
523    CHECK(clazz.get() != NULL);
524
525    jmethodID constructID =
526        env->GetMethodID(clazz.get(), "<init>", "(ILjava/lang/String;)V");
527    CHECK(constructID != NULL);
528
529    jstring msgObj = env->NewStringUTF(msg != NULL ? msg : "Unknown Error");
530
531    /* translate OS errors to Java API CryptoException errorCodes */
532    switch (err) {
533        case ERROR_DRM_NO_LICENSE:
534            err = gCryptoErrorCodes.cryptoErrorNoKey;
535            break;
536        case ERROR_DRM_LICENSE_EXPIRED:
537            err = gCryptoErrorCodes.cryptoErrorKeyExpired;
538            break;
539        case ERROR_DRM_RESOURCE_BUSY:
540            err = gCryptoErrorCodes.cryptoErrorResourceBusy;
541            break;
542        default:
543            break;
544    }
545
546    jthrowable exception =
547        (jthrowable)env->NewObject(clazz.get(), constructID, err, msgObj);
548
549    env->Throw(exception);
550}
551
552static jint throwExceptionAsNecessary(
553        JNIEnv *env, status_t err, const char *msg = NULL) {
554    if (err >= ERROR_DRM_VENDOR_MIN && err <= ERROR_DRM_VENDOR_MAX) {
555        // We'll throw our custom MediaCodec.CryptoException
556        throwCryptoException(env, err, msg);
557        return 0;
558    }
559
560    switch (err) {
561        case OK:
562            return 0;
563
564        case -EAGAIN:
565            return DEQUEUE_INFO_TRY_AGAIN_LATER;
566
567        case INFO_FORMAT_CHANGED:
568            return DEQUEUE_INFO_OUTPUT_FORMAT_CHANGED;
569
570        case INFO_OUTPUT_BUFFERS_CHANGED:
571            return DEQUEUE_INFO_OUTPUT_BUFFERS_CHANGED;
572
573        case ERROR_DRM_NO_LICENSE:
574        case ERROR_DRM_LICENSE_EXPIRED:
575        case ERROR_DRM_RESOURCE_BUSY:
576            throwCryptoException(env, err, msg);
577            break;
578
579        default:
580        {
581            jniThrowException(env, "java/lang/IllegalStateException", msg);
582            break;
583        }
584    }
585
586    return 0;
587}
588
589static void android_media_MediaCodec_native_setCallback(
590        JNIEnv *env,
591        jobject thiz,
592        jobject cb) {
593    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
594
595    if (codec == NULL) {
596        jniThrowException(env, "java/lang/IllegalStateException", NULL);
597        return;
598    }
599
600    status_t err = codec->setCallback(cb);
601
602    throwExceptionAsNecessary(env, err);
603}
604
605static void android_media_MediaCodec_native_configure(
606        JNIEnv *env,
607        jobject thiz,
608        jobjectArray keys, jobjectArray values,
609        jobject jsurface,
610        jobject jcrypto,
611        jint flags) {
612    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
613
614    if (codec == NULL) {
615        jniThrowException(env, "java/lang/IllegalStateException", NULL);
616        return;
617    }
618
619    sp<AMessage> format;
620    status_t err = ConvertKeyValueArraysToMessage(env, keys, values, &format);
621
622    if (err != OK) {
623        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
624        return;
625    }
626
627    sp<IGraphicBufferProducer> bufferProducer;
628    if (jsurface != NULL) {
629        sp<Surface> surface(android_view_Surface_getSurface(env, jsurface));
630        if (surface != NULL) {
631            bufferProducer = surface->getIGraphicBufferProducer();
632        } else {
633            jniThrowException(
634                    env,
635                    "java/lang/IllegalArgumentException",
636                    "The surface has been released");
637            return;
638        }
639    }
640
641    sp<ICrypto> crypto;
642    if (jcrypto != NULL) {
643        crypto = JCrypto::GetCrypto(env, jcrypto);
644    }
645
646    err = codec->configure(format, bufferProducer, crypto, flags);
647
648    throwExceptionAsNecessary(env, err);
649}
650
651static jobject android_media_MediaCodec_createInputSurface(JNIEnv* env,
652        jobject thiz) {
653    ALOGV("android_media_MediaCodec_createInputSurface");
654
655    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
656    if (codec == NULL) {
657        jniThrowException(env, "java/lang/IllegalStateException", NULL);
658        return NULL;
659    }
660
661    // Tell the MediaCodec that we want to use a Surface as input.
662    sp<IGraphicBufferProducer> bufferProducer;
663    status_t err = codec->createInputSurface(&bufferProducer);
664    if (err != NO_ERROR) {
665        throwExceptionAsNecessary(env, err);
666        return NULL;
667    }
668
669    // Wrap the IGBP in a Java-language Surface.
670    return android_view_Surface_createFromIGraphicBufferProducer(env,
671            bufferProducer);
672}
673
674static void android_media_MediaCodec_start(JNIEnv *env, jobject thiz) {
675    ALOGV("android_media_MediaCodec_start");
676
677    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
678
679    if (codec == NULL) {
680        jniThrowException(env, "java/lang/IllegalStateException", "no codec found");
681        return;
682    }
683
684    status_t err = codec->start();
685
686    throwExceptionAsNecessary(env, err, "start failed");
687}
688
689static void android_media_MediaCodec_stop(JNIEnv *env, jobject thiz) {
690    ALOGV("android_media_MediaCodec_stop");
691
692    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
693
694    if (codec == NULL) {
695        jniThrowException(env, "java/lang/IllegalStateException", NULL);
696        return;
697    }
698
699    status_t err = codec->stop();
700
701    throwExceptionAsNecessary(env, err);
702}
703
704static void android_media_MediaCodec_flush(JNIEnv *env, jobject thiz) {
705    ALOGV("android_media_MediaCodec_flush");
706
707    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
708
709    if (codec == NULL) {
710        jniThrowException(env, "java/lang/IllegalStateException", NULL);
711        return;
712    }
713
714    status_t err = codec->flush();
715
716    throwExceptionAsNecessary(env, err);
717}
718
719static void android_media_MediaCodec_queueInputBuffer(
720        JNIEnv *env,
721        jobject thiz,
722        jint index,
723        jint offset,
724        jint size,
725        jlong timestampUs,
726        jint flags) {
727    ALOGV("android_media_MediaCodec_queueInputBuffer");
728
729    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
730
731    if (codec == NULL) {
732        jniThrowException(env, "java/lang/IllegalStateException", NULL);
733        return;
734    }
735
736    AString errorDetailMsg;
737
738    status_t err = codec->queueInputBuffer(
739            index, offset, size, timestampUs, flags, &errorDetailMsg);
740
741    throwExceptionAsNecessary(
742            env, err, errorDetailMsg.empty() ? NULL : errorDetailMsg.c_str());
743}
744
745static void android_media_MediaCodec_queueSecureInputBuffer(
746        JNIEnv *env,
747        jobject thiz,
748        jint index,
749        jint offset,
750        jobject cryptoInfoObj,
751        jlong timestampUs,
752        jint flags) {
753    ALOGV("android_media_MediaCodec_queueSecureInputBuffer");
754
755    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
756
757    if (codec == NULL) {
758        jniThrowException(env, "java/lang/IllegalStateException", NULL);
759        return;
760    }
761
762    jint numSubSamples =
763        env->GetIntField(cryptoInfoObj, gFields.cryptoInfoNumSubSamplesID);
764
765    jintArray numBytesOfClearDataObj =
766        (jintArray)env->GetObjectField(
767                cryptoInfoObj, gFields.cryptoInfoNumBytesOfClearDataID);
768
769    jintArray numBytesOfEncryptedDataObj =
770        (jintArray)env->GetObjectField(
771                cryptoInfoObj, gFields.cryptoInfoNumBytesOfEncryptedDataID);
772
773    jbyteArray keyObj =
774        (jbyteArray)env->GetObjectField(cryptoInfoObj, gFields.cryptoInfoKeyID);
775
776    jbyteArray ivObj =
777        (jbyteArray)env->GetObjectField(cryptoInfoObj, gFields.cryptoInfoIVID);
778
779    jint mode = env->GetIntField(cryptoInfoObj, gFields.cryptoInfoModeID);
780
781    status_t err = OK;
782
783    CryptoPlugin::SubSample *subSamples = NULL;
784    jbyte *key = NULL;
785    jbyte *iv = NULL;
786
787    if (numSubSamples <= 0) {
788        err = -EINVAL;
789    } else if (numBytesOfClearDataObj == NULL
790            && numBytesOfEncryptedDataObj == NULL) {
791        err = -EINVAL;
792    } else if (numBytesOfEncryptedDataObj != NULL
793            && env->GetArrayLength(numBytesOfEncryptedDataObj) < numSubSamples) {
794        err = -ERANGE;
795    } else if (numBytesOfClearDataObj != NULL
796            && env->GetArrayLength(numBytesOfClearDataObj) < numSubSamples) {
797        err = -ERANGE;
798    // subSamples array may silently overflow if number of samples are too large.  Use
799    // INT32_MAX as maximum allocation size may be less than SIZE_MAX on some platforms
800    } else if ( CC_UNLIKELY(numSubSamples >= INT32_MAX / sizeof(*subSamples)) ) {
801        err = -EINVAL;
802    } else {
803        jboolean isCopy;
804
805        jint *numBytesOfClearData =
806            (numBytesOfClearDataObj == NULL)
807                ? NULL
808                : env->GetIntArrayElements(numBytesOfClearDataObj, &isCopy);
809
810        jint *numBytesOfEncryptedData =
811            (numBytesOfEncryptedDataObj == NULL)
812                ? NULL
813                : env->GetIntArrayElements(numBytesOfEncryptedDataObj, &isCopy);
814
815        subSamples = new CryptoPlugin::SubSample[numSubSamples];
816
817        for (jint i = 0; i < numSubSamples; ++i) {
818            subSamples[i].mNumBytesOfClearData =
819                (numBytesOfClearData == NULL) ? 0 : numBytesOfClearData[i];
820
821            subSamples[i].mNumBytesOfEncryptedData =
822                (numBytesOfEncryptedData == NULL)
823                    ? 0 : numBytesOfEncryptedData[i];
824        }
825
826        if (numBytesOfEncryptedData != NULL) {
827            env->ReleaseIntArrayElements(
828                    numBytesOfEncryptedDataObj, numBytesOfEncryptedData, 0);
829            numBytesOfEncryptedData = NULL;
830        }
831
832        if (numBytesOfClearData != NULL) {
833            env->ReleaseIntArrayElements(
834                    numBytesOfClearDataObj, numBytesOfClearData, 0);
835            numBytesOfClearData = NULL;
836        }
837    }
838
839    if (err == OK && keyObj != NULL) {
840        if (env->GetArrayLength(keyObj) != 16) {
841            err = -EINVAL;
842        } else {
843            jboolean isCopy;
844            key = env->GetByteArrayElements(keyObj, &isCopy);
845        }
846    }
847
848    if (err == OK && ivObj != NULL) {
849        if (env->GetArrayLength(ivObj) != 16) {
850            err = -EINVAL;
851        } else {
852            jboolean isCopy;
853            iv = env->GetByteArrayElements(ivObj, &isCopy);
854        }
855    }
856
857    AString errorDetailMsg;
858
859    if (err == OK) {
860        err = codec->queueSecureInputBuffer(
861                index, offset,
862                subSamples, numSubSamples,
863                (const uint8_t *)key, (const uint8_t *)iv,
864                (CryptoPlugin::Mode)mode,
865                timestampUs,
866                flags,
867                &errorDetailMsg);
868    }
869
870    if (iv != NULL) {
871        env->ReleaseByteArrayElements(ivObj, iv, 0);
872        iv = NULL;
873    }
874
875    if (key != NULL) {
876        env->ReleaseByteArrayElements(keyObj, key, 0);
877        key = NULL;
878    }
879
880    delete[] subSamples;
881    subSamples = NULL;
882
883    throwExceptionAsNecessary(
884            env, err, errorDetailMsg.empty() ? NULL : errorDetailMsg.c_str());
885}
886
887static jint android_media_MediaCodec_dequeueInputBuffer(
888        JNIEnv *env, jobject thiz, jlong timeoutUs) {
889    ALOGV("android_media_MediaCodec_dequeueInputBuffer");
890
891    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
892
893    if (codec == NULL) {
894        jniThrowException(env, "java/lang/IllegalStateException", NULL);
895        return -1;
896    }
897
898    size_t index;
899    status_t err = codec->dequeueInputBuffer(&index, timeoutUs);
900
901    if (err == OK) {
902        return (jint) index;
903    }
904
905    return throwExceptionAsNecessary(env, err);
906}
907
908static jint android_media_MediaCodec_dequeueOutputBuffer(
909        JNIEnv *env, jobject thiz, jobject bufferInfo, jlong timeoutUs) {
910    ALOGV("android_media_MediaCodec_dequeueOutputBuffer");
911
912    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
913
914    if (codec == NULL) {
915        jniThrowException(env, "java/lang/IllegalStateException", NULL);
916        return 0;
917    }
918
919    size_t index;
920    status_t err = codec->dequeueOutputBuffer(
921            env, bufferInfo, &index, timeoutUs);
922
923    if (err == OK) {
924        return (jint) index;
925    }
926
927    return throwExceptionAsNecessary(env, err);
928}
929
930static void android_media_MediaCodec_releaseOutputBuffer(
931        JNIEnv *env, jobject thiz,
932        jint index, jboolean render, jboolean updatePTS, jlong timestampNs) {
933    ALOGV("android_media_MediaCodec_renderOutputBufferAndRelease");
934
935    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
936
937    if (codec == NULL) {
938        jniThrowException(env, "java/lang/IllegalStateException", NULL);
939        return;
940    }
941
942    status_t err = codec->releaseOutputBuffer(index, render, updatePTS, timestampNs);
943
944    throwExceptionAsNecessary(env, err);
945}
946
947static void android_media_MediaCodec_signalEndOfInputStream(JNIEnv* env,
948        jobject thiz) {
949    ALOGV("android_media_MediaCodec_signalEndOfInputStream");
950
951    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
952    if (codec == NULL) {
953        jniThrowException(env, "java/lang/IllegalStateException", NULL);
954        return;
955    }
956
957    status_t err = codec->signalEndOfInputStream();
958
959    throwExceptionAsNecessary(env, err);
960}
961
962static jobject android_media_MediaCodec_getOutputFormatNative(
963        JNIEnv *env, jobject thiz) {
964    ALOGV("android_media_MediaCodec_getOutputFormatNative");
965
966    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
967
968    if (codec == NULL) {
969        jniThrowException(env, "java/lang/IllegalStateException", NULL);
970        return NULL;
971    }
972
973    jobject format;
974    status_t err = codec->getOutputFormat(env, &format);
975
976    if (err == OK) {
977        return format;
978    }
979
980    throwExceptionAsNecessary(env, err);
981
982    return NULL;
983}
984
985static jobjectArray android_media_MediaCodec_getBuffers(
986        JNIEnv *env, jobject thiz, jboolean input) {
987    ALOGV("android_media_MediaCodec_getBuffers");
988
989    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
990
991    if (codec == NULL) {
992        jniThrowException(env, "java/lang/IllegalStateException", NULL);
993        return NULL;
994    }
995
996    jobjectArray buffers;
997    status_t err = codec->getBuffers(env, input, &buffers);
998
999    if (err == OK) {
1000        return buffers;
1001    }
1002
1003    // if we're out of memory, an exception was already thrown
1004    if (err != NO_MEMORY) {
1005        throwExceptionAsNecessary(env, err);
1006    }
1007
1008    return NULL;
1009}
1010
1011static jobject android_media_MediaCodec_getName(
1012        JNIEnv *env, jobject thiz) {
1013    ALOGV("android_media_MediaCodec_getName");
1014
1015    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
1016
1017    if (codec == NULL) {
1018        jniThrowException(env, "java/lang/IllegalStateException", NULL);
1019        return NULL;
1020    }
1021
1022    jstring name;
1023    status_t err = codec->getName(env, &name);
1024
1025    if (err == OK) {
1026        return name;
1027    }
1028
1029    throwExceptionAsNecessary(env, err);
1030
1031    return NULL;
1032}
1033
1034static void android_media_MediaCodec_setParameters(
1035        JNIEnv *env, jobject thiz, jobjectArray keys, jobjectArray vals) {
1036    ALOGV("android_media_MediaCodec_setParameters");
1037
1038    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
1039
1040    if (codec == NULL) {
1041        jniThrowException(env, "java/lang/IllegalStateException", NULL);
1042        return;
1043    }
1044
1045    sp<AMessage> params;
1046    status_t err = ConvertKeyValueArraysToMessage(env, keys, vals, &params);
1047
1048    if (err == OK) {
1049        err = codec->setParameters(params);
1050    }
1051
1052    throwExceptionAsNecessary(env, err);
1053}
1054
1055static void android_media_MediaCodec_setVideoScalingMode(
1056        JNIEnv *env, jobject thiz, jint mode) {
1057    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
1058
1059    if (codec == NULL) {
1060        jniThrowException(env, "java/lang/IllegalStateException", NULL);
1061        return;
1062    }
1063
1064    if (mode != NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW
1065            && mode != NATIVE_WINDOW_SCALING_MODE_SCALE_CROP) {
1066        jniThrowException(env, "java/lang/InvalidArgumentException", NULL);
1067        return;
1068    }
1069
1070    codec->setVideoScalingMode(mode);
1071}
1072
1073static void android_media_MediaCodec_native_init(JNIEnv *env) {
1074    ScopedLocalRef<jclass> clazz(
1075            env, env->FindClass("android/media/MediaCodec"));
1076    CHECK(clazz.get() != NULL);
1077
1078    gFields.context = env->GetFieldID(clazz.get(), "mNativeContext", "J");
1079    CHECK(gFields.context != NULL);
1080
1081    gFields.postEventFromNativeID =
1082        env->GetMethodID(
1083                clazz.get(), "postEventFromNative", "(IIILjava/lang/Object;)V");
1084
1085    CHECK(gFields.postEventFromNativeID != NULL);
1086
1087    clazz.reset(env->FindClass("android/media/MediaCodec$CryptoInfo"));
1088    CHECK(clazz.get() != NULL);
1089
1090    gFields.cryptoInfoNumSubSamplesID =
1091        env->GetFieldID(clazz.get(), "numSubSamples", "I");
1092    CHECK(gFields.cryptoInfoNumSubSamplesID != NULL);
1093
1094    gFields.cryptoInfoNumBytesOfClearDataID =
1095        env->GetFieldID(clazz.get(), "numBytesOfClearData", "[I");
1096    CHECK(gFields.cryptoInfoNumBytesOfClearDataID != NULL);
1097
1098    gFields.cryptoInfoNumBytesOfEncryptedDataID =
1099        env->GetFieldID(clazz.get(), "numBytesOfEncryptedData", "[I");
1100    CHECK(gFields.cryptoInfoNumBytesOfEncryptedDataID != NULL);
1101
1102    gFields.cryptoInfoKeyID = env->GetFieldID(clazz.get(), "key", "[B");
1103    CHECK(gFields.cryptoInfoKeyID != NULL);
1104
1105    gFields.cryptoInfoIVID = env->GetFieldID(clazz.get(), "iv", "[B");
1106    CHECK(gFields.cryptoInfoIVID != NULL);
1107
1108    gFields.cryptoInfoModeID = env->GetFieldID(clazz.get(), "mode", "I");
1109    CHECK(gFields.cryptoInfoModeID != NULL);
1110
1111    clazz.reset(env->FindClass("android/media/MediaCodec$CryptoException"));
1112    CHECK(clazz.get() != NULL);
1113
1114    jfieldID field;
1115    field = env->GetStaticFieldID(clazz.get(), "ERROR_NO_KEY", "I");
1116    CHECK(field != NULL);
1117    gCryptoErrorCodes.cryptoErrorNoKey =
1118        env->GetStaticIntField(clazz.get(), field);
1119
1120    field = env->GetStaticFieldID(clazz.get(), "ERROR_KEY_EXPIRED", "I");
1121    CHECK(field != NULL);
1122    gCryptoErrorCodes.cryptoErrorKeyExpired =
1123        env->GetStaticIntField(clazz.get(), field);
1124
1125    field = env->GetStaticFieldID(clazz.get(), "ERROR_RESOURCE_BUSY", "I");
1126    CHECK(field != NULL);
1127    gCryptoErrorCodes.cryptoErrorResourceBusy =
1128        env->GetStaticIntField(clazz.get(), field);
1129}
1130
1131static void android_media_MediaCodec_native_setup(
1132        JNIEnv *env, jobject thiz,
1133        jstring name, jboolean nameIsType, jboolean encoder) {
1134    if (name == NULL) {
1135        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
1136        return;
1137    }
1138
1139    const char *tmp = env->GetStringUTFChars(name, NULL);
1140
1141    if (tmp == NULL) {
1142        return;
1143    }
1144
1145    sp<JMediaCodec> codec = new JMediaCodec(env, thiz, tmp, nameIsType, encoder);
1146
1147    status_t err = codec->initCheck();
1148
1149    env->ReleaseStringUTFChars(name, tmp);
1150    tmp = NULL;
1151
1152    if (err != OK) {
1153        jniThrowException(
1154                env,
1155                "java/io/IOException",
1156                "Failed to allocate component instance");
1157        return;
1158    }
1159
1160    codec->registerSelf();
1161
1162    setMediaCodec(env,thiz, codec);
1163}
1164
1165static void android_media_MediaCodec_native_finalize(
1166        JNIEnv *env, jobject thiz) {
1167    android_media_MediaCodec_release(env, thiz);
1168}
1169
1170static JNINativeMethod gMethods[] = {
1171    { "release", "()V", (void *)android_media_MediaCodec_release },
1172
1173    { "native_setCallback",
1174      "(Landroid/media/MediaCodec$Callback;)V",
1175      (void *)android_media_MediaCodec_native_setCallback },
1176
1177    { "native_configure",
1178      "([Ljava/lang/String;[Ljava/lang/Object;Landroid/view/Surface;"
1179      "Landroid/media/MediaCrypto;I)V",
1180      (void *)android_media_MediaCodec_native_configure },
1181
1182    { "createInputSurface", "()Landroid/view/Surface;",
1183      (void *)android_media_MediaCodec_createInputSurface },
1184
1185    { "start", "()V", (void *)android_media_MediaCodec_start },
1186    { "native_stop", "()V", (void *)android_media_MediaCodec_stop },
1187    { "flush", "()V", (void *)android_media_MediaCodec_flush },
1188
1189    { "queueInputBuffer", "(IIIJI)V",
1190      (void *)android_media_MediaCodec_queueInputBuffer },
1191
1192    { "queueSecureInputBuffer", "(IILandroid/media/MediaCodec$CryptoInfo;JI)V",
1193      (void *)android_media_MediaCodec_queueSecureInputBuffer },
1194
1195    { "dequeueInputBuffer", "(J)I",
1196      (void *)android_media_MediaCodec_dequeueInputBuffer },
1197
1198    { "dequeueOutputBuffer", "(Landroid/media/MediaCodec$BufferInfo;J)I",
1199      (void *)android_media_MediaCodec_dequeueOutputBuffer },
1200
1201    { "releaseOutputBuffer", "(IZZJ)V",
1202      (void *)android_media_MediaCodec_releaseOutputBuffer },
1203
1204    { "signalEndOfInputStream", "()V",
1205      (void *)android_media_MediaCodec_signalEndOfInputStream },
1206
1207    { "getOutputFormatNative", "()Ljava/util/Map;",
1208      (void *)android_media_MediaCodec_getOutputFormatNative },
1209
1210    { "getBuffers", "(Z)[Ljava/nio/ByteBuffer;",
1211      (void *)android_media_MediaCodec_getBuffers },
1212
1213    { "getName", "()Ljava/lang/String;",
1214      (void *)android_media_MediaCodec_getName },
1215
1216    { "setParameters", "([Ljava/lang/String;[Ljava/lang/Object;)V",
1217      (void *)android_media_MediaCodec_setParameters },
1218
1219    { "setVideoScalingMode", "(I)V",
1220      (void *)android_media_MediaCodec_setVideoScalingMode },
1221
1222    { "native_init", "()V", (void *)android_media_MediaCodec_native_init },
1223
1224    { "native_setup", "(Ljava/lang/String;ZZ)V",
1225      (void *)android_media_MediaCodec_native_setup },
1226
1227    { "native_finalize", "()V",
1228      (void *)android_media_MediaCodec_native_finalize },
1229};
1230
1231int register_android_media_MediaCodec(JNIEnv *env) {
1232    return AndroidRuntime::registerNativeMethods(env,
1233                "android/media/MediaCodec", gMethods, NELEM(gMethods));
1234}
1235