android_media_MediaCodec.cpp revision d47f7d8b5fe3a3861d7cbdc5f912235407823c8e
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<IGraphicBufferProducer> &bufferProducer,
115        const sp<ICrypto> &crypto,
116        int flags) {
117    sp<SurfaceTextureClient> client;
118    if (bufferProducer != NULL) {
119        mSurfaceTextureClient = new SurfaceTextureClient(bufferProducer);
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
267status_t JMediaCodec::getName(JNIEnv *env, jstring *nameStr) const {
268    AString name;
269
270    status_t err = mCodec->getName(&name);
271
272    if (err != OK) {
273        return err;
274    }
275
276    *nameStr = env->NewStringUTF(name.c_str());
277
278    return OK;
279}
280
281void JMediaCodec::setVideoScalingMode(int mode) {
282    if (mSurfaceTextureClient != NULL) {
283        native_window_set_scaling_mode(mSurfaceTextureClient.get(), mode);
284    }
285}
286
287}  // namespace android
288
289////////////////////////////////////////////////////////////////////////////////
290
291using namespace android;
292
293static sp<JMediaCodec> setMediaCodec(
294        JNIEnv *env, jobject thiz, const sp<JMediaCodec> &codec) {
295    sp<JMediaCodec> old = (JMediaCodec *)env->GetIntField(thiz, gFields.context);
296    if (codec != NULL) {
297        codec->incStrong(thiz);
298    }
299    if (old != NULL) {
300        old->decStrong(thiz);
301    }
302    env->SetIntField(thiz, gFields.context, (int)codec.get());
303
304    return old;
305}
306
307static sp<JMediaCodec> getMediaCodec(JNIEnv *env, jobject thiz) {
308    return (JMediaCodec *)env->GetIntField(thiz, gFields.context);
309}
310
311static void android_media_MediaCodec_release(JNIEnv *env, jobject thiz) {
312    setMediaCodec(env, thiz, NULL);
313}
314
315static void throwCryptoException(JNIEnv *env, status_t err, const char *msg) {
316    jclass clazz = env->FindClass("android/media/MediaCodec$CryptoException");
317    CHECK(clazz != NULL);
318
319    jmethodID constructID =
320        env->GetMethodID(clazz, "<init>", "(ILjava/lang/String;)V");
321    CHECK(constructID != NULL);
322
323    jstring msgObj = env->NewStringUTF(msg != NULL ? msg : "Unknown Error");
324
325    jthrowable exception =
326        (jthrowable)env->NewObject(clazz, constructID, err, msgObj);
327
328    env->Throw(exception);
329}
330
331static jint throwExceptionAsNecessary(
332        JNIEnv *env, status_t err, const char *msg = NULL) {
333    if (err >= ERROR_DRM_WV_VENDOR_MIN && err <= ERROR_DRM_WV_VENDOR_MAX) {
334        // We'll throw our custom MediaCodec.CryptoException
335
336        throwCryptoException(env, err, msg);
337        return 0;
338    }
339
340    switch (err) {
341        case OK:
342            return 0;
343
344        case -EAGAIN:
345            return DEQUEUE_INFO_TRY_AGAIN_LATER;
346
347        case INFO_FORMAT_CHANGED:
348            return DEQUEUE_INFO_OUTPUT_FORMAT_CHANGED;
349
350        case INFO_OUTPUT_BUFFERS_CHANGED:
351            return DEQUEUE_INFO_OUTPUT_BUFFERS_CHANGED;
352
353        default:
354        {
355            jniThrowException(env, "java/lang/IllegalStateException", NULL);
356            break;
357        }
358    }
359
360    return 0;
361}
362
363static void android_media_MediaCodec_native_configure(
364        JNIEnv *env,
365        jobject thiz,
366        jobjectArray keys, jobjectArray values,
367        jobject jsurface,
368        jobject jcrypto,
369        jint flags) {
370    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
371
372    if (codec == NULL) {
373        jniThrowException(env, "java/lang/IllegalStateException", NULL);
374        return;
375    }
376
377    sp<AMessage> format;
378    status_t err = ConvertKeyValueArraysToMessage(env, keys, values, &format);
379
380    if (err != OK) {
381        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
382        return;
383    }
384
385    sp<IGraphicBufferProducer> bufferProducer;
386    if (jsurface != NULL) {
387        sp<Surface> surface(android_view_Surface_getSurface(env, jsurface));
388        if (surface != NULL) {
389            bufferProducer = surface->getSurfaceTexture();
390        } else {
391            jniThrowException(
392                    env,
393                    "java/lang/IllegalArgumentException",
394                    "The surface has been released");
395            return;
396        }
397    }
398
399    sp<ICrypto> crypto;
400    if (jcrypto != NULL) {
401        crypto = JCrypto::GetCrypto(env, jcrypto);
402    }
403
404    err = codec->configure(format, bufferProducer, crypto, flags);
405
406    throwExceptionAsNecessary(env, err);
407}
408
409static void android_media_MediaCodec_start(JNIEnv *env, jobject thiz) {
410    ALOGV("android_media_MediaCodec_start");
411
412    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
413
414    if (codec == NULL) {
415        jniThrowException(env, "java/lang/IllegalStateException", NULL);
416        return;
417    }
418
419    status_t err = codec->start();
420
421    throwExceptionAsNecessary(env, err);
422}
423
424static void android_media_MediaCodec_stop(JNIEnv *env, jobject thiz) {
425    ALOGV("android_media_MediaCodec_stop");
426
427    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
428
429    if (codec == NULL) {
430        jniThrowException(env, "java/lang/IllegalStateException", NULL);
431        return;
432    }
433
434    status_t err = codec->stop();
435
436    throwExceptionAsNecessary(env, err);
437}
438
439static void android_media_MediaCodec_flush(JNIEnv *env, jobject thiz) {
440    ALOGV("android_media_MediaCodec_flush");
441
442    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
443
444    if (codec == NULL) {
445        jniThrowException(env, "java/lang/IllegalStateException", NULL);
446        return;
447    }
448
449    status_t err = codec->flush();
450
451    throwExceptionAsNecessary(env, err);
452}
453
454static void android_media_MediaCodec_queueInputBuffer(
455        JNIEnv *env,
456        jobject thiz,
457        jint index,
458        jint offset,
459        jint size,
460        jlong timestampUs,
461        jint flags) {
462    ALOGV("android_media_MediaCodec_queueInputBuffer");
463
464    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
465
466    if (codec == NULL) {
467        jniThrowException(env, "java/lang/IllegalStateException", NULL);
468        return;
469    }
470
471    AString errorDetailMsg;
472
473    status_t err = codec->queueInputBuffer(
474            index, offset, size, timestampUs, flags, &errorDetailMsg);
475
476    throwExceptionAsNecessary(
477            env, err, errorDetailMsg.empty() ? NULL : errorDetailMsg.c_str());
478}
479
480static void android_media_MediaCodec_queueSecureInputBuffer(
481        JNIEnv *env,
482        jobject thiz,
483        jint index,
484        jint offset,
485        jobject cryptoInfoObj,
486        jlong timestampUs,
487        jint flags) {
488    ALOGV("android_media_MediaCodec_queueSecureInputBuffer");
489
490    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
491
492    if (codec == NULL) {
493        jniThrowException(env, "java/lang/IllegalStateException", NULL);
494        return;
495    }
496
497    jint numSubSamples =
498        env->GetIntField(cryptoInfoObj, gFields.cryptoInfoNumSubSamplesID);
499
500    jintArray numBytesOfClearDataObj =
501        (jintArray)env->GetObjectField(
502                cryptoInfoObj, gFields.cryptoInfoNumBytesOfClearDataID);
503
504    jintArray numBytesOfEncryptedDataObj =
505        (jintArray)env->GetObjectField(
506                cryptoInfoObj, gFields.cryptoInfoNumBytesOfEncryptedDataID);
507
508    jbyteArray keyObj =
509        (jbyteArray)env->GetObjectField(cryptoInfoObj, gFields.cryptoInfoKeyID);
510
511    jbyteArray ivObj =
512        (jbyteArray)env->GetObjectField(cryptoInfoObj, gFields.cryptoInfoIVID);
513
514    jint mode = env->GetIntField(cryptoInfoObj, gFields.cryptoInfoModeID);
515
516    status_t err = OK;
517
518    CryptoPlugin::SubSample *subSamples = NULL;
519    jbyte *key = NULL;
520    jbyte *iv = NULL;
521
522    if (numSubSamples <= 0) {
523        err = -EINVAL;
524    } else if (numBytesOfClearDataObj == NULL
525            && numBytesOfEncryptedDataObj == NULL) {
526        err = -EINVAL;
527    } else if (numBytesOfEncryptedDataObj != NULL
528            && env->GetArrayLength(numBytesOfEncryptedDataObj) < numSubSamples) {
529        err = -ERANGE;
530    } else if (numBytesOfClearDataObj != NULL
531            && env->GetArrayLength(numBytesOfClearDataObj) < numSubSamples) {
532        err = -ERANGE;
533    } else {
534        jboolean isCopy;
535
536        jint *numBytesOfClearData =
537            (numBytesOfClearDataObj == NULL)
538                ? NULL
539                : env->GetIntArrayElements(numBytesOfClearDataObj, &isCopy);
540
541        jint *numBytesOfEncryptedData =
542            (numBytesOfEncryptedDataObj == NULL)
543                ? NULL
544                : env->GetIntArrayElements(numBytesOfEncryptedDataObj, &isCopy);
545
546        subSamples = new CryptoPlugin::SubSample[numSubSamples];
547
548        for (jint i = 0; i < numSubSamples; ++i) {
549            subSamples[i].mNumBytesOfClearData =
550                (numBytesOfClearData == NULL) ? 0 : numBytesOfClearData[i];
551
552            subSamples[i].mNumBytesOfEncryptedData =
553                (numBytesOfEncryptedData == NULL)
554                    ? 0 : numBytesOfEncryptedData[i];
555        }
556
557        if (numBytesOfEncryptedData != NULL) {
558            env->ReleaseIntArrayElements(
559                    numBytesOfEncryptedDataObj, numBytesOfEncryptedData, 0);
560            numBytesOfEncryptedData = NULL;
561        }
562
563        if (numBytesOfClearData != NULL) {
564            env->ReleaseIntArrayElements(
565                    numBytesOfClearDataObj, numBytesOfClearData, 0);
566            numBytesOfClearData = NULL;
567        }
568    }
569
570    if (err == OK && keyObj != NULL) {
571        if (env->GetArrayLength(keyObj) != 16) {
572            err = -EINVAL;
573        } else {
574            jboolean isCopy;
575            key = env->GetByteArrayElements(keyObj, &isCopy);
576        }
577    }
578
579    if (err == OK && ivObj != NULL) {
580        if (env->GetArrayLength(ivObj) != 16) {
581            err = -EINVAL;
582        } else {
583            jboolean isCopy;
584            iv = env->GetByteArrayElements(ivObj, &isCopy);
585        }
586    }
587
588    AString errorDetailMsg;
589
590    if (err == OK) {
591        err = codec->queueSecureInputBuffer(
592                index, offset,
593                subSamples, numSubSamples,
594                (const uint8_t *)key, (const uint8_t *)iv,
595                (CryptoPlugin::Mode)mode,
596                timestampUs,
597                flags,
598                &errorDetailMsg);
599    }
600
601    if (iv != NULL) {
602        env->ReleaseByteArrayElements(ivObj, iv, 0);
603        iv = NULL;
604    }
605
606    if (key != NULL) {
607        env->ReleaseByteArrayElements(keyObj, key, 0);
608        key = NULL;
609    }
610
611    delete[] subSamples;
612    subSamples = NULL;
613
614    throwExceptionAsNecessary(
615            env, err, errorDetailMsg.empty() ? NULL : errorDetailMsg.c_str());
616}
617
618static jint android_media_MediaCodec_dequeueInputBuffer(
619        JNIEnv *env, jobject thiz, jlong timeoutUs) {
620    ALOGV("android_media_MediaCodec_dequeueInputBuffer");
621
622    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
623
624    if (codec == NULL) {
625        jniThrowException(env, "java/lang/IllegalStateException", NULL);
626        return -1;
627    }
628
629    size_t index;
630    status_t err = codec->dequeueInputBuffer(&index, timeoutUs);
631
632    if (err == OK) {
633        return index;
634    }
635
636    return throwExceptionAsNecessary(env, err);
637}
638
639static jint android_media_MediaCodec_dequeueOutputBuffer(
640        JNIEnv *env, jobject thiz, jobject bufferInfo, jlong timeoutUs) {
641    ALOGV("android_media_MediaCodec_dequeueOutputBuffer");
642
643    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
644
645    if (codec == NULL) {
646        jniThrowException(env, "java/lang/IllegalStateException", NULL);
647        return 0;
648    }
649
650    size_t index;
651    status_t err = codec->dequeueOutputBuffer(
652            env, bufferInfo, &index, timeoutUs);
653
654    if (err == OK) {
655        return index;
656    }
657
658    return throwExceptionAsNecessary(env, err);
659}
660
661static void android_media_MediaCodec_releaseOutputBuffer(
662        JNIEnv *env, jobject thiz, jint index, jboolean render) {
663    ALOGV("android_media_MediaCodec_renderOutputBufferAndRelease");
664
665    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
666
667    if (codec == NULL) {
668        jniThrowException(env, "java/lang/IllegalStateException", NULL);
669        return;
670    }
671
672    status_t err = codec->releaseOutputBuffer(index, render);
673
674    throwExceptionAsNecessary(env, err);
675}
676
677static jobject android_media_MediaCodec_getOutputFormatNative(
678        JNIEnv *env, jobject thiz) {
679    ALOGV("android_media_MediaCodec_getOutputFormatNative");
680
681    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
682
683    if (codec == NULL) {
684        jniThrowException(env, "java/lang/IllegalStateException", NULL);
685        return NULL;
686    }
687
688    jobject format;
689    status_t err = codec->getOutputFormat(env, &format);
690
691    if (err == OK) {
692        return format;
693    }
694
695    throwExceptionAsNecessary(env, err);
696
697    return NULL;
698}
699
700static jobjectArray android_media_MediaCodec_getBuffers(
701        JNIEnv *env, jobject thiz, jboolean input) {
702    ALOGV("android_media_MediaCodec_getBuffers");
703
704    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
705
706    if (codec == NULL) {
707        jniThrowException(env, "java/lang/IllegalStateException", NULL);
708        return NULL;
709    }
710
711    jobjectArray buffers;
712    status_t err = codec->getBuffers(env, input, &buffers);
713
714    if (err == OK) {
715        return buffers;
716    }
717
718    throwExceptionAsNecessary(env, err);
719
720    return NULL;
721}
722
723static jobject android_media_MediaCodec_getName(
724        JNIEnv *env, jobject thiz) {
725    ALOGV("android_media_MediaCodec_getName");
726
727    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
728
729    if (codec == NULL) {
730        jniThrowException(env, "java/lang/IllegalStateException", NULL);
731        return NULL;
732    }
733
734    jstring name;
735    status_t err = codec->getName(env, &name);
736
737    if (err == OK) {
738        return name;
739    }
740
741    throwExceptionAsNecessary(env, err);
742
743    return NULL;
744}
745
746static void android_media_MediaCodec_setVideoScalingMode(
747        JNIEnv *env, jobject thiz, jint mode) {
748    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
749
750    if (codec == NULL) {
751        jniThrowException(env, "java/lang/IllegalStateException", NULL);
752        return;
753    }
754
755    if (mode != NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW
756            && mode != NATIVE_WINDOW_SCALING_MODE_SCALE_CROP) {
757        jniThrowException(env, "java/lang/InvalidArgumentException", NULL);
758        return;
759    }
760
761    codec->setVideoScalingMode(mode);
762}
763
764static void android_media_MediaCodec_native_init(JNIEnv *env) {
765    jclass clazz = env->FindClass("android/media/MediaCodec");
766    CHECK(clazz != NULL);
767
768    gFields.context = env->GetFieldID(clazz, "mNativeContext", "I");
769    CHECK(gFields.context != NULL);
770
771    clazz = env->FindClass("android/media/MediaCodec$CryptoInfo");
772    CHECK(clazz != NULL);
773
774    gFields.cryptoInfoNumSubSamplesID =
775        env->GetFieldID(clazz, "numSubSamples", "I");
776    CHECK(gFields.cryptoInfoNumSubSamplesID != NULL);
777
778    gFields.cryptoInfoNumBytesOfClearDataID =
779        env->GetFieldID(clazz, "numBytesOfClearData", "[I");
780    CHECK(gFields.cryptoInfoNumBytesOfClearDataID != NULL);
781
782    gFields.cryptoInfoNumBytesOfEncryptedDataID =
783        env->GetFieldID(clazz, "numBytesOfEncryptedData", "[I");
784    CHECK(gFields.cryptoInfoNumBytesOfEncryptedDataID != NULL);
785
786    gFields.cryptoInfoKeyID = env->GetFieldID(clazz, "key", "[B");
787    CHECK(gFields.cryptoInfoKeyID != NULL);
788
789    gFields.cryptoInfoIVID = env->GetFieldID(clazz, "iv", "[B");
790    CHECK(gFields.cryptoInfoIVID != NULL);
791
792    gFields.cryptoInfoModeID = env->GetFieldID(clazz, "mode", "I");
793    CHECK(gFields.cryptoInfoModeID != NULL);
794}
795
796static void android_media_MediaCodec_native_setup(
797        JNIEnv *env, jobject thiz,
798        jstring name, jboolean nameIsType, jboolean encoder) {
799    if (name == NULL) {
800        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
801        return;
802    }
803
804    const char *tmp = env->GetStringUTFChars(name, NULL);
805
806    if (tmp == NULL) {
807        return;
808    }
809
810    sp<JMediaCodec> codec = new JMediaCodec(env, thiz, tmp, nameIsType, encoder);
811
812    status_t err = codec->initCheck();
813
814    env->ReleaseStringUTFChars(name, tmp);
815    tmp = NULL;
816
817    if (err != OK) {
818        jniThrowException(
819                env,
820                "java/io/IOException",
821                "Failed to allocate component instance");
822        return;
823    }
824
825    setMediaCodec(env,thiz, codec);
826}
827
828static void android_media_MediaCodec_native_finalize(
829        JNIEnv *env, jobject thiz) {
830    android_media_MediaCodec_release(env, thiz);
831}
832
833static JNINativeMethod gMethods[] = {
834    { "release", "()V", (void *)android_media_MediaCodec_release },
835
836    { "native_configure",
837      "([Ljava/lang/String;[Ljava/lang/Object;Landroid/view/Surface;"
838      "Landroid/media/MediaCrypto;I)V",
839      (void *)android_media_MediaCodec_native_configure },
840
841    { "start", "()V", (void *)android_media_MediaCodec_start },
842    { "stop", "()V", (void *)android_media_MediaCodec_stop },
843    { "flush", "()V", (void *)android_media_MediaCodec_flush },
844
845    { "queueInputBuffer", "(IIIJI)V",
846      (void *)android_media_MediaCodec_queueInputBuffer },
847
848    { "queueSecureInputBuffer", "(IILandroid/media/MediaCodec$CryptoInfo;JI)V",
849      (void *)android_media_MediaCodec_queueSecureInputBuffer },
850
851    { "dequeueInputBuffer", "(J)I",
852      (void *)android_media_MediaCodec_dequeueInputBuffer },
853
854    { "dequeueOutputBuffer", "(Landroid/media/MediaCodec$BufferInfo;J)I",
855      (void *)android_media_MediaCodec_dequeueOutputBuffer },
856
857    { "releaseOutputBuffer", "(IZ)V",
858      (void *)android_media_MediaCodec_releaseOutputBuffer },
859
860    { "getOutputFormatNative", "()Ljava/util/Map;",
861      (void *)android_media_MediaCodec_getOutputFormatNative },
862
863    { "getBuffers", "(Z)[Ljava/nio/ByteBuffer;",
864      (void *)android_media_MediaCodec_getBuffers },
865
866    { "getName", "()Ljava/lang/String;",
867      (void *)android_media_MediaCodec_getName },
868
869    { "setVideoScalingMode", "(I)V",
870      (void *)android_media_MediaCodec_setVideoScalingMode },
871
872    { "native_init", "()V", (void *)android_media_MediaCodec_native_init },
873
874    { "native_setup", "(Ljava/lang/String;ZZ)V",
875      (void *)android_media_MediaCodec_native_setup },
876
877    { "native_finalize", "()V",
878      (void *)android_media_MediaCodec_native_finalize },
879};
880
881int register_android_media_MediaCodec(JNIEnv *env) {
882    return AndroidRuntime::registerNativeMethods(env,
883                "android/media/MediaCodec", gMethods, NELEM(gMethods));
884}
885