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#include <gui/SurfaceTextureClient.h>
32
33#include <media/ICrypto.h>
34#include <media/stagefright/MediaCodec.h>
35#include <media/stagefright/foundation/ABuffer.h>
36#include <media/stagefright/foundation/ADebug.h>
37#include <media/stagefright/foundation/ALooper.h>
38#include <media/stagefright/foundation/AMessage.h>
39#include <media/stagefright/foundation/AString.h>
40#include <media/stagefright/MediaErrors.h>
41
42#include <system/window.h>
43
44namespace android {
45
46// Keep these in sync with their equivalents in MediaCodec.java !!!
47enum {
48    DEQUEUE_INFO_TRY_AGAIN_LATER            = -1,
49    DEQUEUE_INFO_OUTPUT_FORMAT_CHANGED      = -2,
50    DEQUEUE_INFO_OUTPUT_BUFFERS_CHANGED     = -3,
51};
52
53struct fields_t {
54    jfieldID context;
55
56    jfieldID cryptoInfoNumSubSamplesID;
57    jfieldID cryptoInfoNumBytesOfClearDataID;
58    jfieldID cryptoInfoNumBytesOfEncryptedDataID;
59    jfieldID cryptoInfoKeyID;
60    jfieldID cryptoInfoIVID;
61    jfieldID cryptoInfoModeID;
62};
63
64static fields_t gFields;
65
66////////////////////////////////////////////////////////////////////////////////
67
68JMediaCodec::JMediaCodec(
69        JNIEnv *env, jobject thiz,
70        const char *name, bool nameIsType, bool encoder)
71    : mClass(NULL),
72      mObject(NULL) {
73    jclass clazz = env->GetObjectClass(thiz);
74    CHECK(clazz != NULL);
75
76    mClass = (jclass)env->NewGlobalRef(clazz);
77    mObject = env->NewWeakGlobalRef(thiz);
78
79    mLooper = new ALooper;
80    mLooper->setName("MediaCodec_looper");
81
82    mLooper->start(
83            false,      // runOnCallingThread
84            false,       // canCallJava
85            PRIORITY_DEFAULT);
86
87    if (nameIsType) {
88        mCodec = MediaCodec::CreateByType(mLooper, name, encoder);
89    } else {
90        mCodec = MediaCodec::CreateByComponentName(mLooper, name);
91    }
92}
93
94status_t JMediaCodec::initCheck() const {
95    return mCodec != NULL ? OK : NO_INIT;
96}
97
98JMediaCodec::~JMediaCodec() {
99    if (mCodec != NULL) {
100        mCodec->release();
101        mCodec.clear();
102    }
103
104    JNIEnv *env = AndroidRuntime::getJNIEnv();
105
106    env->DeleteWeakGlobalRef(mObject);
107    mObject = NULL;
108    env->DeleteGlobalRef(mClass);
109    mClass = NULL;
110}
111
112status_t JMediaCodec::configure(
113        const sp<AMessage> &format,
114        const sp<ISurfaceTexture> &surfaceTexture,
115        const sp<ICrypto> &crypto,
116        int flags) {
117    sp<SurfaceTextureClient> client;
118    if (surfaceTexture != NULL) {
119        mSurfaceTextureClient = new SurfaceTextureClient(surfaceTexture);
120    } else {
121        mSurfaceTextureClient.clear();
122    }
123
124    return mCodec->configure(format, mSurfaceTextureClient, crypto, flags);
125}
126
127status_t JMediaCodec::start() {
128    return mCodec->start();
129}
130
131status_t JMediaCodec::stop() {
132    mSurfaceTextureClient.clear();
133
134    return mCodec->stop();
135}
136
137status_t JMediaCodec::flush() {
138    return mCodec->flush();
139}
140
141status_t JMediaCodec::queueInputBuffer(
142        size_t index,
143        size_t offset, size_t size, int64_t timeUs, uint32_t flags,
144        AString *errorDetailMsg) {
145    return mCodec->queueInputBuffer(
146            index, offset, size, timeUs, flags, errorDetailMsg);
147}
148
149status_t JMediaCodec::queueSecureInputBuffer(
150        size_t index,
151        size_t offset,
152        const CryptoPlugin::SubSample *subSamples,
153        size_t numSubSamples,
154        const uint8_t key[16],
155        const uint8_t iv[16],
156        CryptoPlugin::Mode mode,
157        int64_t presentationTimeUs,
158        uint32_t flags,
159        AString *errorDetailMsg) {
160    return mCodec->queueSecureInputBuffer(
161            index, offset, subSamples, numSubSamples, key, iv, mode,
162            presentationTimeUs, flags, errorDetailMsg);
163}
164
165status_t JMediaCodec::dequeueInputBuffer(size_t *index, int64_t timeoutUs) {
166    return mCodec->dequeueInputBuffer(index, timeoutUs);
167}
168
169status_t JMediaCodec::dequeueOutputBuffer(
170        JNIEnv *env, jobject bufferInfo, size_t *index, int64_t timeoutUs) {
171    size_t size, offset;
172    int64_t timeUs;
173    uint32_t flags;
174    status_t err;
175    if ((err = mCodec->dequeueOutputBuffer(
176                    index, &offset, &size, &timeUs, &flags, timeoutUs)) != OK) {
177        return err;
178    }
179
180    jclass clazz = env->FindClass("android/media/MediaCodec$BufferInfo");
181
182    jmethodID method = env->GetMethodID(clazz, "set", "(IIJI)V");
183    env->CallVoidMethod(bufferInfo, method, offset, size, timeUs, flags);
184
185    return OK;
186}
187
188status_t JMediaCodec::releaseOutputBuffer(size_t index, bool render) {
189    return render
190        ? mCodec->renderOutputBufferAndRelease(index)
191        : mCodec->releaseOutputBuffer(index);
192}
193
194status_t JMediaCodec::getOutputFormat(JNIEnv *env, jobject *format) const {
195    sp<AMessage> msg;
196    status_t err;
197    if ((err = mCodec->getOutputFormat(&msg)) != OK) {
198        return err;
199    }
200
201    return ConvertMessageToMap(env, msg, format);
202}
203
204status_t JMediaCodec::getBuffers(
205        JNIEnv *env, bool input, jobjectArray *bufArray) const {
206    Vector<sp<ABuffer> > buffers;
207
208    status_t err =
209        input
210            ? mCodec->getInputBuffers(&buffers)
211            : mCodec->getOutputBuffers(&buffers);
212
213    if (err != OK) {
214        return err;
215    }
216
217    jclass byteBufferClass = env->FindClass("java/nio/ByteBuffer");
218    CHECK(byteBufferClass != NULL);
219
220    jmethodID orderID = env->GetMethodID(
221            byteBufferClass,
222            "order",
223            "(Ljava/nio/ByteOrder;)Ljava/nio/ByteBuffer;");
224
225    CHECK(orderID != NULL);
226
227    jclass byteOrderClass = env->FindClass("java/nio/ByteOrder");
228    CHECK(byteOrderClass != NULL);
229
230    jmethodID nativeOrderID = env->GetStaticMethodID(
231            byteOrderClass, "nativeOrder", "()Ljava/nio/ByteOrder;");
232    CHECK(nativeOrderID != NULL);
233
234    jobject nativeByteOrderObj =
235        env->CallStaticObjectMethod(byteOrderClass, nativeOrderID);
236    CHECK(nativeByteOrderObj != NULL);
237
238    *bufArray = (jobjectArray)env->NewObjectArray(
239            buffers.size(), byteBufferClass, NULL);
240
241    for (size_t i = 0; i < buffers.size(); ++i) {
242        const sp<ABuffer> &buffer = buffers.itemAt(i);
243
244        jobject byteBuffer =
245            env->NewDirectByteBuffer(
246                buffer->base(),
247                buffer->capacity());
248
249        jobject me = env->CallObjectMethod(
250                byteBuffer, orderID, nativeByteOrderObj);
251        env->DeleteLocalRef(me);
252        me = NULL;
253
254        env->SetObjectArrayElement(
255                *bufArray, i, byteBuffer);
256
257        env->DeleteLocalRef(byteBuffer);
258        byteBuffer = NULL;
259    }
260
261    env->DeleteLocalRef(nativeByteOrderObj);
262    nativeByteOrderObj = NULL;
263
264    return OK;
265}
266
267void JMediaCodec::setVideoScalingMode(int mode) {
268    if (mSurfaceTextureClient != NULL) {
269        native_window_set_scaling_mode(mSurfaceTextureClient.get(), mode);
270    }
271}
272
273}  // namespace android
274
275////////////////////////////////////////////////////////////////////////////////
276
277using namespace android;
278
279static sp<JMediaCodec> setMediaCodec(
280        JNIEnv *env, jobject thiz, const sp<JMediaCodec> &codec) {
281    sp<JMediaCodec> old = (JMediaCodec *)env->GetIntField(thiz, gFields.context);
282    if (codec != NULL) {
283        codec->incStrong(thiz);
284    }
285    if (old != NULL) {
286        old->decStrong(thiz);
287    }
288    env->SetIntField(thiz, gFields.context, (int)codec.get());
289
290    return old;
291}
292
293static sp<JMediaCodec> getMediaCodec(JNIEnv *env, jobject thiz) {
294    return (JMediaCodec *)env->GetIntField(thiz, gFields.context);
295}
296
297static void android_media_MediaCodec_release(JNIEnv *env, jobject thiz) {
298    setMediaCodec(env, thiz, NULL);
299}
300
301static void throwCryptoException(JNIEnv *env, status_t err, const char *msg) {
302    jclass clazz = env->FindClass("android/media/MediaCodec$CryptoException");
303    CHECK(clazz != NULL);
304
305    jmethodID constructID =
306        env->GetMethodID(clazz, "<init>", "(ILjava/lang/String;)V");
307    CHECK(constructID != NULL);
308
309    jstring msgObj = env->NewStringUTF(msg != NULL ? msg : "Unknown Error");
310
311    jthrowable exception =
312        (jthrowable)env->NewObject(clazz, constructID, err, msgObj);
313
314    env->Throw(exception);
315}
316
317static jint throwExceptionAsNecessary(
318        JNIEnv *env, status_t err, const char *msg = NULL) {
319    if (err >= ERROR_DRM_WV_VENDOR_MIN && err <= ERROR_DRM_WV_VENDOR_MAX) {
320        // We'll throw our custom MediaCodec.CryptoException
321
322        throwCryptoException(env, err, msg);
323        return 0;
324    }
325
326    switch (err) {
327        case OK:
328            return 0;
329
330        case -EAGAIN:
331            return DEQUEUE_INFO_TRY_AGAIN_LATER;
332
333        case INFO_FORMAT_CHANGED:
334            return DEQUEUE_INFO_OUTPUT_FORMAT_CHANGED;
335
336        case INFO_OUTPUT_BUFFERS_CHANGED:
337            return DEQUEUE_INFO_OUTPUT_BUFFERS_CHANGED;
338
339        default:
340        {
341            jniThrowException(env, "java/lang/IllegalStateException", NULL);
342            break;
343        }
344    }
345
346    return 0;
347}
348
349static void android_media_MediaCodec_native_configure(
350        JNIEnv *env,
351        jobject thiz,
352        jobjectArray keys, jobjectArray values,
353        jobject jsurface,
354        jobject jcrypto,
355        jint flags) {
356    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
357
358    if (codec == NULL) {
359        jniThrowException(env, "java/lang/IllegalStateException", NULL);
360        return;
361    }
362
363    sp<AMessage> format;
364    status_t err = ConvertKeyValueArraysToMessage(env, keys, values, &format);
365
366    if (err != OK) {
367        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
368        return;
369    }
370
371    sp<ISurfaceTexture> surfaceTexture;
372    if (jsurface != NULL) {
373        sp<Surface> surface(android_view_Surface_getSurface(env, jsurface));
374        if (surface != NULL) {
375            surfaceTexture = surface->getSurfaceTexture();
376        } else {
377            jniThrowException(
378                    env,
379                    "java/lang/IllegalArgumentException",
380                    "The surface has been released");
381            return;
382        }
383    }
384
385    sp<ICrypto> crypto;
386    if (jcrypto != NULL) {
387        crypto = JCrypto::GetCrypto(env, jcrypto);
388    }
389
390    err = codec->configure(format, surfaceTexture, crypto, flags);
391
392    throwExceptionAsNecessary(env, err);
393}
394
395static void android_media_MediaCodec_start(JNIEnv *env, jobject thiz) {
396    ALOGV("android_media_MediaCodec_start");
397
398    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
399
400    if (codec == NULL) {
401        jniThrowException(env, "java/lang/IllegalStateException", NULL);
402        return;
403    }
404
405    status_t err = codec->start();
406
407    throwExceptionAsNecessary(env, err);
408}
409
410static void android_media_MediaCodec_stop(JNIEnv *env, jobject thiz) {
411    ALOGV("android_media_MediaCodec_stop");
412
413    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
414
415    if (codec == NULL) {
416        jniThrowException(env, "java/lang/IllegalStateException", NULL);
417        return;
418    }
419
420    status_t err = codec->stop();
421
422    throwExceptionAsNecessary(env, err);
423}
424
425static void android_media_MediaCodec_flush(JNIEnv *env, jobject thiz) {
426    ALOGV("android_media_MediaCodec_flush");
427
428    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
429
430    if (codec == NULL) {
431        jniThrowException(env, "java/lang/IllegalStateException", NULL);
432        return;
433    }
434
435    status_t err = codec->flush();
436
437    throwExceptionAsNecessary(env, err);
438}
439
440static void android_media_MediaCodec_queueInputBuffer(
441        JNIEnv *env,
442        jobject thiz,
443        jint index,
444        jint offset,
445        jint size,
446        jlong timestampUs,
447        jint flags) {
448    ALOGV("android_media_MediaCodec_queueInputBuffer");
449
450    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
451
452    if (codec == NULL) {
453        jniThrowException(env, "java/lang/IllegalStateException", NULL);
454        return;
455    }
456
457    AString errorDetailMsg;
458
459    status_t err = codec->queueInputBuffer(
460            index, offset, size, timestampUs, flags, &errorDetailMsg);
461
462    throwExceptionAsNecessary(
463            env, err, errorDetailMsg.empty() ? NULL : errorDetailMsg.c_str());
464}
465
466static void android_media_MediaCodec_queueSecureInputBuffer(
467        JNIEnv *env,
468        jobject thiz,
469        jint index,
470        jint offset,
471        jobject cryptoInfoObj,
472        jlong timestampUs,
473        jint flags) {
474    ALOGV("android_media_MediaCodec_queueSecureInputBuffer");
475
476    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
477
478    if (codec == NULL) {
479        jniThrowException(env, "java/lang/IllegalStateException", NULL);
480        return;
481    }
482
483    jint numSubSamples =
484        env->GetIntField(cryptoInfoObj, gFields.cryptoInfoNumSubSamplesID);
485
486    jintArray numBytesOfClearDataObj =
487        (jintArray)env->GetObjectField(
488                cryptoInfoObj, gFields.cryptoInfoNumBytesOfClearDataID);
489
490    jintArray numBytesOfEncryptedDataObj =
491        (jintArray)env->GetObjectField(
492                cryptoInfoObj, gFields.cryptoInfoNumBytesOfEncryptedDataID);
493
494    jbyteArray keyObj =
495        (jbyteArray)env->GetObjectField(cryptoInfoObj, gFields.cryptoInfoKeyID);
496
497    jbyteArray ivObj =
498        (jbyteArray)env->GetObjectField(cryptoInfoObj, gFields.cryptoInfoIVID);
499
500    jint mode = env->GetIntField(cryptoInfoObj, gFields.cryptoInfoModeID);
501
502    status_t err = OK;
503
504    CryptoPlugin::SubSample *subSamples = NULL;
505    jbyte *key = NULL;
506    jbyte *iv = NULL;
507
508    if (numSubSamples <= 0) {
509        err = -EINVAL;
510    } else if (numBytesOfClearDataObj == NULL
511            && numBytesOfEncryptedDataObj == NULL) {
512        err = -EINVAL;
513    } else if (numBytesOfEncryptedDataObj != NULL
514            && env->GetArrayLength(numBytesOfEncryptedDataObj) < numSubSamples) {
515        err = -ERANGE;
516    } else if (numBytesOfClearDataObj != NULL
517            && env->GetArrayLength(numBytesOfClearDataObj) < numSubSamples) {
518        err = -ERANGE;
519    } else {
520        jboolean isCopy;
521
522        jint *numBytesOfClearData =
523            (numBytesOfClearDataObj == NULL)
524                ? NULL
525                : env->GetIntArrayElements(numBytesOfClearDataObj, &isCopy);
526
527        jint *numBytesOfEncryptedData =
528            (numBytesOfEncryptedDataObj == NULL)
529                ? NULL
530                : env->GetIntArrayElements(numBytesOfEncryptedDataObj, &isCopy);
531
532        subSamples = new CryptoPlugin::SubSample[numSubSamples];
533
534        for (jint i = 0; i < numSubSamples; ++i) {
535            subSamples[i].mNumBytesOfClearData =
536                (numBytesOfClearData == NULL) ? 0 : numBytesOfClearData[i];
537
538            subSamples[i].mNumBytesOfEncryptedData =
539                (numBytesOfEncryptedData == NULL)
540                    ? 0 : numBytesOfEncryptedData[i];
541        }
542
543        if (numBytesOfEncryptedData != NULL) {
544            env->ReleaseIntArrayElements(
545                    numBytesOfEncryptedDataObj, numBytesOfEncryptedData, 0);
546            numBytesOfEncryptedData = NULL;
547        }
548
549        if (numBytesOfClearData != NULL) {
550            env->ReleaseIntArrayElements(
551                    numBytesOfClearDataObj, numBytesOfClearData, 0);
552            numBytesOfClearData = NULL;
553        }
554    }
555
556    if (err == OK && keyObj != NULL) {
557        if (env->GetArrayLength(keyObj) != 16) {
558            err = -EINVAL;
559        } else {
560            jboolean isCopy;
561            key = env->GetByteArrayElements(keyObj, &isCopy);
562        }
563    }
564
565    if (err == OK && ivObj != NULL) {
566        if (env->GetArrayLength(ivObj) != 16) {
567            err = -EINVAL;
568        } else {
569            jboolean isCopy;
570            iv = env->GetByteArrayElements(ivObj, &isCopy);
571        }
572    }
573
574    AString errorDetailMsg;
575
576    if (err == OK) {
577        err = codec->queueSecureInputBuffer(
578                index, offset,
579                subSamples, numSubSamples,
580                (const uint8_t *)key, (const uint8_t *)iv,
581                (CryptoPlugin::Mode)mode,
582                timestampUs,
583                flags,
584                &errorDetailMsg);
585    }
586
587    if (iv != NULL) {
588        env->ReleaseByteArrayElements(ivObj, iv, 0);
589        iv = NULL;
590    }
591
592    if (key != NULL) {
593        env->ReleaseByteArrayElements(keyObj, key, 0);
594        key = NULL;
595    }
596
597    delete[] subSamples;
598    subSamples = NULL;
599
600    throwExceptionAsNecessary(
601            env, err, errorDetailMsg.empty() ? NULL : errorDetailMsg.c_str());
602}
603
604static jint android_media_MediaCodec_dequeueInputBuffer(
605        JNIEnv *env, jobject thiz, jlong timeoutUs) {
606    ALOGV("android_media_MediaCodec_dequeueInputBuffer");
607
608    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
609
610    if (codec == NULL) {
611        jniThrowException(env, "java/lang/IllegalStateException", NULL);
612        return -1;
613    }
614
615    size_t index;
616    status_t err = codec->dequeueInputBuffer(&index, timeoutUs);
617
618    if (err == OK) {
619        return index;
620    }
621
622    return throwExceptionAsNecessary(env, err);
623}
624
625static jint android_media_MediaCodec_dequeueOutputBuffer(
626        JNIEnv *env, jobject thiz, jobject bufferInfo, jlong timeoutUs) {
627    ALOGV("android_media_MediaCodec_dequeueOutputBuffer");
628
629    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
630
631    if (codec == NULL) {
632        jniThrowException(env, "java/lang/IllegalStateException", NULL);
633        return 0;
634    }
635
636    size_t index;
637    status_t err = codec->dequeueOutputBuffer(
638            env, bufferInfo, &index, timeoutUs);
639
640    if (err == OK) {
641        return index;
642    }
643
644    return throwExceptionAsNecessary(env, err);
645}
646
647static void android_media_MediaCodec_releaseOutputBuffer(
648        JNIEnv *env, jobject thiz, jint index, jboolean render) {
649    ALOGV("android_media_MediaCodec_renderOutputBufferAndRelease");
650
651    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
652
653    if (codec == NULL) {
654        jniThrowException(env, "java/lang/IllegalStateException", NULL);
655        return;
656    }
657
658    status_t err = codec->releaseOutputBuffer(index, render);
659
660    throwExceptionAsNecessary(env, err);
661}
662
663static jobject android_media_MediaCodec_getOutputFormatNative(
664        JNIEnv *env, jobject thiz) {
665    ALOGV("android_media_MediaCodec_getOutputFormatNative");
666
667    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
668
669    if (codec == NULL) {
670        jniThrowException(env, "java/lang/IllegalStateException", NULL);
671        return NULL;
672    }
673
674    jobject format;
675    status_t err = codec->getOutputFormat(env, &format);
676
677    if (err == OK) {
678        return format;
679    }
680
681    throwExceptionAsNecessary(env, err);
682
683    return NULL;
684}
685
686static jobjectArray android_media_MediaCodec_getBuffers(
687        JNIEnv *env, jobject thiz, jboolean input) {
688    ALOGV("android_media_MediaCodec_getBuffers");
689
690    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
691
692    if (codec == NULL) {
693        jniThrowException(env, "java/lang/IllegalStateException", NULL);
694        return NULL;
695    }
696
697    jobjectArray buffers;
698    status_t err = codec->getBuffers(env, input, &buffers);
699
700    if (err == OK) {
701        return buffers;
702    }
703
704    throwExceptionAsNecessary(env, err);
705
706    return NULL;
707}
708
709static void android_media_MediaCodec_setVideoScalingMode(
710        JNIEnv *env, jobject thiz, jint mode) {
711    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
712
713    if (codec == NULL) {
714        jniThrowException(env, "java/lang/IllegalStateException", NULL);
715        return;
716    }
717
718    if (mode != NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW
719            && mode != NATIVE_WINDOW_SCALING_MODE_SCALE_CROP) {
720        jniThrowException(env, "java/lang/InvalidArgumentException", NULL);
721        return;
722    }
723
724    codec->setVideoScalingMode(mode);
725}
726
727static void android_media_MediaCodec_native_init(JNIEnv *env) {
728    jclass clazz = env->FindClass("android/media/MediaCodec");
729    CHECK(clazz != NULL);
730
731    gFields.context = env->GetFieldID(clazz, "mNativeContext", "I");
732    CHECK(gFields.context != NULL);
733
734    clazz = env->FindClass("android/media/MediaCodec$CryptoInfo");
735    CHECK(clazz != NULL);
736
737    gFields.cryptoInfoNumSubSamplesID =
738        env->GetFieldID(clazz, "numSubSamples", "I");
739    CHECK(gFields.cryptoInfoNumSubSamplesID != NULL);
740
741    gFields.cryptoInfoNumBytesOfClearDataID =
742        env->GetFieldID(clazz, "numBytesOfClearData", "[I");
743    CHECK(gFields.cryptoInfoNumBytesOfClearDataID != NULL);
744
745    gFields.cryptoInfoNumBytesOfEncryptedDataID =
746        env->GetFieldID(clazz, "numBytesOfEncryptedData", "[I");
747    CHECK(gFields.cryptoInfoNumBytesOfEncryptedDataID != NULL);
748
749    gFields.cryptoInfoKeyID = env->GetFieldID(clazz, "key", "[B");
750    CHECK(gFields.cryptoInfoKeyID != NULL);
751
752    gFields.cryptoInfoIVID = env->GetFieldID(clazz, "iv", "[B");
753    CHECK(gFields.cryptoInfoIVID != NULL);
754
755    gFields.cryptoInfoModeID = env->GetFieldID(clazz, "mode", "I");
756    CHECK(gFields.cryptoInfoModeID != NULL);
757}
758
759static void android_media_MediaCodec_native_setup(
760        JNIEnv *env, jobject thiz,
761        jstring name, jboolean nameIsType, jboolean encoder) {
762    if (name == NULL) {
763        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
764        return;
765    }
766
767    const char *tmp = env->GetStringUTFChars(name, NULL);
768
769    if (tmp == NULL) {
770        return;
771    }
772
773    sp<JMediaCodec> codec = new JMediaCodec(env, thiz, tmp, nameIsType, encoder);
774
775    status_t err = codec->initCheck();
776
777    env->ReleaseStringUTFChars(name, tmp);
778    tmp = NULL;
779
780    if (err != OK) {
781        jniThrowException(
782                env,
783                "java/io/IOException",
784                "Failed to allocate component instance");
785        return;
786    }
787
788    setMediaCodec(env,thiz, codec);
789}
790
791static void android_media_MediaCodec_native_finalize(
792        JNIEnv *env, jobject thiz) {
793    android_media_MediaCodec_release(env, thiz);
794}
795
796static JNINativeMethod gMethods[] = {
797    { "release", "()V", (void *)android_media_MediaCodec_release },
798
799    { "native_configure",
800      "([Ljava/lang/String;[Ljava/lang/Object;Landroid/view/Surface;"
801      "Landroid/media/MediaCrypto;I)V",
802      (void *)android_media_MediaCodec_native_configure },
803
804    { "start", "()V", (void *)android_media_MediaCodec_start },
805    { "stop", "()V", (void *)android_media_MediaCodec_stop },
806    { "flush", "()V", (void *)android_media_MediaCodec_flush },
807
808    { "queueInputBuffer", "(IIIJI)V",
809      (void *)android_media_MediaCodec_queueInputBuffer },
810
811    { "queueSecureInputBuffer", "(IILandroid/media/MediaCodec$CryptoInfo;JI)V",
812      (void *)android_media_MediaCodec_queueSecureInputBuffer },
813
814    { "dequeueInputBuffer", "(J)I",
815      (void *)android_media_MediaCodec_dequeueInputBuffer },
816
817    { "dequeueOutputBuffer", "(Landroid/media/MediaCodec$BufferInfo;J)I",
818      (void *)android_media_MediaCodec_dequeueOutputBuffer },
819
820    { "releaseOutputBuffer", "(IZ)V",
821      (void *)android_media_MediaCodec_releaseOutputBuffer },
822
823    { "getOutputFormatNative", "()Ljava/util/Map;",
824      (void *)android_media_MediaCodec_getOutputFormatNative },
825
826    { "getBuffers", "(Z)[Ljava/nio/ByteBuffer;",
827      (void *)android_media_MediaCodec_getBuffers },
828
829    { "setVideoScalingMode", "(I)V",
830      (void *)android_media_MediaCodec_setVideoScalingMode },
831
832    { "native_init", "()V", (void *)android_media_MediaCodec_native_init },
833
834    { "native_setup", "(Ljava/lang/String;ZZ)V",
835      (void *)android_media_MediaCodec_native_setup },
836
837    { "native_finalize", "()V",
838      (void *)android_media_MediaCodec_native_finalize },
839};
840
841int register_android_media_MediaCodec(JNIEnv *env) {
842    return AndroidRuntime::registerNativeMethods(env,
843                "android/media/MediaCodec", gMethods, NELEM(gMethods));
844}
845