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