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