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