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