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