android_media_MediaCodec.cpp revision 8d5f3e31c914e29129f50fe9830d71adf52ab5cf
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);
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
313void JMediaCodec::setVideoScalingMode(int mode) {
314    if (mSurfaceTextureClient != NULL) {
315        native_window_set_scaling_mode(mSurfaceTextureClient.get(), mode);
316    }
317}
318
319}  // namespace android
320
321////////////////////////////////////////////////////////////////////////////////
322
323using namespace android;
324
325static sp<JMediaCodec> setMediaCodec(
326        JNIEnv *env, jobject thiz, const sp<JMediaCodec> &codec) {
327    sp<JMediaCodec> old = (JMediaCodec *)env->GetIntField(thiz, gFields.context);
328    if (codec != NULL) {
329        codec->incStrong(thiz);
330    }
331    if (old != NULL) {
332        old->decStrong(thiz);
333    }
334    env->SetIntField(thiz, gFields.context, (int)codec.get());
335
336    return old;
337}
338
339static sp<JMediaCodec> getMediaCodec(JNIEnv *env, jobject thiz) {
340    return (JMediaCodec *)env->GetIntField(thiz, gFields.context);
341}
342
343static void android_media_MediaCodec_release(JNIEnv *env, jobject thiz) {
344    setMediaCodec(env, thiz, NULL);
345}
346
347static void throwCryptoException(JNIEnv *env, status_t err, const char *msg) {
348    ScopedLocalRef<jclass> clazz(
349            env, env->FindClass("android/media/MediaCodec$CryptoException"));
350    CHECK(clazz.get() != NULL);
351
352    jmethodID constructID =
353        env->GetMethodID(clazz.get(), "<init>", "(ILjava/lang/String;)V");
354    CHECK(constructID != NULL);
355
356    jstring msgObj = env->NewStringUTF(msg != NULL ? msg : "Unknown Error");
357
358    /* translate OS errors to Java API CryptoException errorCodes */
359    switch (err) {
360        case ERROR_DRM_NO_LICENSE:
361            err = gCryptoErrorCodes.cryptoErrorNoKey;
362            break;
363        case ERROR_DRM_LICENSE_EXPIRED:
364            err = gCryptoErrorCodes.cryptoErrorKeyExpired;
365            break;
366        case ERROR_DRM_RESOURCE_BUSY:
367            err = gCryptoErrorCodes.cryptoErrorResourceBusy;
368            break;
369        default:
370            break;
371    }
372
373    jthrowable exception =
374        (jthrowable)env->NewObject(clazz.get(), constructID, err, msgObj);
375
376    env->Throw(exception);
377}
378
379static jint throwExceptionAsNecessary(
380        JNIEnv *env, status_t err, const char *msg = NULL) {
381    if (err >= ERROR_DRM_VENDOR_MIN && err <= ERROR_DRM_VENDOR_MAX) {
382        // We'll throw our custom MediaCodec.CryptoException
383        throwCryptoException(env, err, msg);
384        return 0;
385    }
386
387    switch (err) {
388        case OK:
389            return 0;
390
391        case -EAGAIN:
392            return DEQUEUE_INFO_TRY_AGAIN_LATER;
393
394        case INFO_FORMAT_CHANGED:
395            return DEQUEUE_INFO_OUTPUT_FORMAT_CHANGED;
396
397        case INFO_OUTPUT_BUFFERS_CHANGED:
398            return DEQUEUE_INFO_OUTPUT_BUFFERS_CHANGED;
399
400        case ERROR_DRM_NO_LICENSE:
401        case ERROR_DRM_LICENSE_EXPIRED:
402        case ERROR_DRM_RESOURCE_BUSY:
403            throwCryptoException(env, err, msg);
404            break;
405
406        default:
407        {
408            jniThrowException(env, "java/lang/IllegalStateException", msg);
409            break;
410        }
411    }
412
413    return 0;
414}
415
416static void android_media_MediaCodec_native_configure(
417        JNIEnv *env,
418        jobject thiz,
419        jobjectArray keys, jobjectArray values,
420        jobject jsurface,
421        jobject jcrypto,
422        jint flags) {
423    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
424
425    if (codec == NULL) {
426        jniThrowException(env, "java/lang/IllegalStateException", NULL);
427        return;
428    }
429
430    sp<AMessage> format;
431    status_t err = ConvertKeyValueArraysToMessage(env, keys, values, &format);
432
433    if (err != OK) {
434        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
435        return;
436    }
437
438    sp<IGraphicBufferProducer> bufferProducer;
439    if (jsurface != NULL) {
440        sp<Surface> surface(android_view_Surface_getSurface(env, jsurface));
441        if (surface != NULL) {
442            bufferProducer = surface->getIGraphicBufferProducer();
443        } else {
444            jniThrowException(
445                    env,
446                    "java/lang/IllegalArgumentException",
447                    "The surface has been released");
448            return;
449        }
450    }
451
452    sp<ICrypto> crypto;
453    if (jcrypto != NULL) {
454        crypto = JCrypto::GetCrypto(env, jcrypto);
455    }
456
457    err = codec->configure(format, bufferProducer, crypto, flags);
458
459    throwExceptionAsNecessary(env, err);
460}
461
462static jobject android_media_MediaCodec_createInputSurface(JNIEnv* env,
463        jobject thiz) {
464    ALOGV("android_media_MediaCodec_createInputSurface");
465
466    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
467    if (codec == NULL) {
468        jniThrowException(env, "java/lang/IllegalStateException", NULL);
469        return NULL;
470    }
471
472    // Tell the MediaCodec that we want to use a Surface as input.
473    sp<IGraphicBufferProducer> bufferProducer;
474    status_t err = codec->createInputSurface(&bufferProducer);
475    if (err != NO_ERROR) {
476        throwExceptionAsNecessary(env, err);
477        return NULL;
478    }
479
480    // Wrap the IGBP in a Java-language Surface.
481    return android_view_Surface_createFromIGraphicBufferProducer(env,
482            bufferProducer);
483}
484
485static void android_media_MediaCodec_start(JNIEnv *env, jobject thiz) {
486    ALOGV("android_media_MediaCodec_start");
487
488    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
489
490    if (codec == NULL) {
491        jniThrowException(env, "java/lang/IllegalStateException", "no codec found");
492        return;
493    }
494
495    status_t err = codec->start();
496
497    throwExceptionAsNecessary(env, err, "start failed");
498}
499
500static void android_media_MediaCodec_stop(JNIEnv *env, jobject thiz) {
501    ALOGV("android_media_MediaCodec_stop");
502
503    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
504
505    if (codec == NULL) {
506        jniThrowException(env, "java/lang/IllegalStateException", NULL);
507        return;
508    }
509
510    status_t err = codec->stop();
511
512    throwExceptionAsNecessary(env, err);
513}
514
515static void android_media_MediaCodec_flush(JNIEnv *env, jobject thiz) {
516    ALOGV("android_media_MediaCodec_flush");
517
518    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
519
520    if (codec == NULL) {
521        jniThrowException(env, "java/lang/IllegalStateException", NULL);
522        return;
523    }
524
525    status_t err = codec->flush();
526
527    throwExceptionAsNecessary(env, err);
528}
529
530static void android_media_MediaCodec_queueInputBuffer(
531        JNIEnv *env,
532        jobject thiz,
533        jint index,
534        jint offset,
535        jint size,
536        jlong timestampUs,
537        jint flags) {
538    ALOGV("android_media_MediaCodec_queueInputBuffer");
539
540    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
541
542    if (codec == NULL) {
543        jniThrowException(env, "java/lang/IllegalStateException", NULL);
544        return;
545    }
546
547    AString errorDetailMsg;
548
549    status_t err = codec->queueInputBuffer(
550            index, offset, size, timestampUs, flags, &errorDetailMsg);
551
552    throwExceptionAsNecessary(
553            env, err, errorDetailMsg.empty() ? NULL : errorDetailMsg.c_str());
554}
555
556static void android_media_MediaCodec_queueSecureInputBuffer(
557        JNIEnv *env,
558        jobject thiz,
559        jint index,
560        jint offset,
561        jobject cryptoInfoObj,
562        jlong timestampUs,
563        jint flags) {
564    ALOGV("android_media_MediaCodec_queueSecureInputBuffer");
565
566    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
567
568    if (codec == NULL) {
569        jniThrowException(env, "java/lang/IllegalStateException", NULL);
570        return;
571    }
572
573    jint numSubSamples =
574        env->GetIntField(cryptoInfoObj, gFields.cryptoInfoNumSubSamplesID);
575
576    jintArray numBytesOfClearDataObj =
577        (jintArray)env->GetObjectField(
578                cryptoInfoObj, gFields.cryptoInfoNumBytesOfClearDataID);
579
580    jintArray numBytesOfEncryptedDataObj =
581        (jintArray)env->GetObjectField(
582                cryptoInfoObj, gFields.cryptoInfoNumBytesOfEncryptedDataID);
583
584    jbyteArray keyObj =
585        (jbyteArray)env->GetObjectField(cryptoInfoObj, gFields.cryptoInfoKeyID);
586
587    jbyteArray ivObj =
588        (jbyteArray)env->GetObjectField(cryptoInfoObj, gFields.cryptoInfoIVID);
589
590    jint mode = env->GetIntField(cryptoInfoObj, gFields.cryptoInfoModeID);
591
592    status_t err = OK;
593
594    CryptoPlugin::SubSample *subSamples = NULL;
595    jbyte *key = NULL;
596    jbyte *iv = NULL;
597
598    if (numSubSamples <= 0) {
599        err = -EINVAL;
600    } else if (numBytesOfClearDataObj == NULL
601            && numBytesOfEncryptedDataObj == NULL) {
602        err = -EINVAL;
603    } else if (numBytesOfEncryptedDataObj != NULL
604            && env->GetArrayLength(numBytesOfEncryptedDataObj) < numSubSamples) {
605        err = -ERANGE;
606    } else if (numBytesOfClearDataObj != NULL
607            && env->GetArrayLength(numBytesOfClearDataObj) < numSubSamples) {
608        err = -ERANGE;
609    } else {
610        jboolean isCopy;
611
612        jint *numBytesOfClearData =
613            (numBytesOfClearDataObj == NULL)
614                ? NULL
615                : env->GetIntArrayElements(numBytesOfClearDataObj, &isCopy);
616
617        jint *numBytesOfEncryptedData =
618            (numBytesOfEncryptedDataObj == NULL)
619                ? NULL
620                : env->GetIntArrayElements(numBytesOfEncryptedDataObj, &isCopy);
621
622        subSamples = new CryptoPlugin::SubSample[numSubSamples];
623
624        for (jint i = 0; i < numSubSamples; ++i) {
625            subSamples[i].mNumBytesOfClearData =
626                (numBytesOfClearData == NULL) ? 0 : numBytesOfClearData[i];
627
628            subSamples[i].mNumBytesOfEncryptedData =
629                (numBytesOfEncryptedData == NULL)
630                    ? 0 : numBytesOfEncryptedData[i];
631        }
632
633        if (numBytesOfEncryptedData != NULL) {
634            env->ReleaseIntArrayElements(
635                    numBytesOfEncryptedDataObj, numBytesOfEncryptedData, 0);
636            numBytesOfEncryptedData = NULL;
637        }
638
639        if (numBytesOfClearData != NULL) {
640            env->ReleaseIntArrayElements(
641                    numBytesOfClearDataObj, numBytesOfClearData, 0);
642            numBytesOfClearData = NULL;
643        }
644    }
645
646    if (err == OK && keyObj != NULL) {
647        if (env->GetArrayLength(keyObj) != 16) {
648            err = -EINVAL;
649        } else {
650            jboolean isCopy;
651            key = env->GetByteArrayElements(keyObj, &isCopy);
652        }
653    }
654
655    if (err == OK && ivObj != NULL) {
656        if (env->GetArrayLength(ivObj) != 16) {
657            err = -EINVAL;
658        } else {
659            jboolean isCopy;
660            iv = env->GetByteArrayElements(ivObj, &isCopy);
661        }
662    }
663
664    AString errorDetailMsg;
665
666    if (err == OK) {
667        err = codec->queueSecureInputBuffer(
668                index, offset,
669                subSamples, numSubSamples,
670                (const uint8_t *)key, (const uint8_t *)iv,
671                (CryptoPlugin::Mode)mode,
672                timestampUs,
673                flags,
674                &errorDetailMsg);
675    }
676
677    if (iv != NULL) {
678        env->ReleaseByteArrayElements(ivObj, iv, 0);
679        iv = NULL;
680    }
681
682    if (key != NULL) {
683        env->ReleaseByteArrayElements(keyObj, key, 0);
684        key = NULL;
685    }
686
687    delete[] subSamples;
688    subSamples = NULL;
689
690    throwExceptionAsNecessary(
691            env, err, errorDetailMsg.empty() ? NULL : errorDetailMsg.c_str());
692}
693
694static jint android_media_MediaCodec_dequeueInputBuffer(
695        JNIEnv *env, jobject thiz, jlong timeoutUs) {
696    ALOGV("android_media_MediaCodec_dequeueInputBuffer");
697
698    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
699
700    if (codec == NULL) {
701        jniThrowException(env, "java/lang/IllegalStateException", NULL);
702        return -1;
703    }
704
705    size_t index;
706    status_t err = codec->dequeueInputBuffer(&index, timeoutUs);
707
708    if (err == OK) {
709        return index;
710    }
711
712    return throwExceptionAsNecessary(env, err);
713}
714
715static jint android_media_MediaCodec_dequeueOutputBuffer(
716        JNIEnv *env, jobject thiz, jobject bufferInfo, jlong timeoutUs) {
717    ALOGV("android_media_MediaCodec_dequeueOutputBuffer");
718
719    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
720
721    if (codec == NULL) {
722        jniThrowException(env, "java/lang/IllegalStateException", NULL);
723        return 0;
724    }
725
726    size_t index;
727    status_t err = codec->dequeueOutputBuffer(
728            env, bufferInfo, &index, timeoutUs);
729
730    if (err == OK) {
731        return index;
732    }
733
734    return throwExceptionAsNecessary(env, err);
735}
736
737static void android_media_MediaCodec_releaseOutputBuffer(
738        JNIEnv *env, jobject thiz, jint index, jboolean render) {
739    ALOGV("android_media_MediaCodec_renderOutputBufferAndRelease");
740
741    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
742
743    if (codec == NULL) {
744        jniThrowException(env, "java/lang/IllegalStateException", NULL);
745        return;
746    }
747
748    status_t err = codec->releaseOutputBuffer(index, render);
749
750    throwExceptionAsNecessary(env, err);
751}
752
753static void android_media_MediaCodec_signalEndOfInputStream(JNIEnv* env,
754        jobject thiz) {
755    ALOGV("android_media_MediaCodec_signalEndOfInputStream");
756
757    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
758    if (codec == NULL) {
759        jniThrowException(env, "java/lang/IllegalStateException", NULL);
760        return;
761    }
762
763    status_t err = codec->signalEndOfInputStream();
764
765    throwExceptionAsNecessary(env, err);
766}
767
768static jobject android_media_MediaCodec_getOutputFormatNative(
769        JNIEnv *env, jobject thiz) {
770    ALOGV("android_media_MediaCodec_getOutputFormatNative");
771
772    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
773
774    if (codec == NULL) {
775        jniThrowException(env, "java/lang/IllegalStateException", NULL);
776        return NULL;
777    }
778
779    jobject format;
780    status_t err = codec->getOutputFormat(env, &format);
781
782    if (err == OK) {
783        return format;
784    }
785
786    throwExceptionAsNecessary(env, err);
787
788    return NULL;
789}
790
791static jobjectArray android_media_MediaCodec_getBuffers(
792        JNIEnv *env, jobject thiz, jboolean input) {
793    ALOGV("android_media_MediaCodec_getBuffers");
794
795    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
796
797    if (codec == NULL) {
798        jniThrowException(env, "java/lang/IllegalStateException", NULL);
799        return NULL;
800    }
801
802    jobjectArray buffers;
803    status_t err = codec->getBuffers(env, input, &buffers);
804
805    if (err == OK) {
806        return buffers;
807    }
808
809    // if we're out of memory, an exception was already thrown
810    if (err != NO_MEMORY) {
811        throwExceptionAsNecessary(env, err);
812    }
813
814    return NULL;
815}
816
817static jobject android_media_MediaCodec_getName(
818        JNIEnv *env, jobject thiz) {
819    ALOGV("android_media_MediaCodec_getName");
820
821    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
822
823    if (codec == NULL) {
824        jniThrowException(env, "java/lang/IllegalStateException", NULL);
825        return NULL;
826    }
827
828    jstring name;
829    status_t err = codec->getName(env, &name);
830
831    if (err == OK) {
832        return name;
833    }
834
835    throwExceptionAsNecessary(env, err);
836
837    return NULL;
838}
839
840static void android_media_MediaCodec_setVideoScalingMode(
841        JNIEnv *env, jobject thiz, jint mode) {
842    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
843
844    if (codec == NULL) {
845        jniThrowException(env, "java/lang/IllegalStateException", NULL);
846        return;
847    }
848
849    if (mode != NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW
850            && mode != NATIVE_WINDOW_SCALING_MODE_SCALE_CROP) {
851        jniThrowException(env, "java/lang/InvalidArgumentException", NULL);
852        return;
853    }
854
855    codec->setVideoScalingMode(mode);
856}
857
858static void android_media_MediaCodec_native_init(JNIEnv *env) {
859    ScopedLocalRef<jclass> clazz(
860            env, env->FindClass("android/media/MediaCodec"));
861    CHECK(clazz.get() != NULL);
862
863    gFields.context = env->GetFieldID(clazz.get(), "mNativeContext", "I");
864    CHECK(gFields.context != NULL);
865
866    clazz.reset(env->FindClass("android/media/MediaCodec$CryptoInfo"));
867    CHECK(clazz.get() != NULL);
868
869    gFields.cryptoInfoNumSubSamplesID =
870        env->GetFieldID(clazz.get(), "numSubSamples", "I");
871    CHECK(gFields.cryptoInfoNumSubSamplesID != NULL);
872
873    gFields.cryptoInfoNumBytesOfClearDataID =
874        env->GetFieldID(clazz.get(), "numBytesOfClearData", "[I");
875    CHECK(gFields.cryptoInfoNumBytesOfClearDataID != NULL);
876
877    gFields.cryptoInfoNumBytesOfEncryptedDataID =
878        env->GetFieldID(clazz.get(), "numBytesOfEncryptedData", "[I");
879    CHECK(gFields.cryptoInfoNumBytesOfEncryptedDataID != NULL);
880
881    gFields.cryptoInfoKeyID = env->GetFieldID(clazz.get(), "key", "[B");
882    CHECK(gFields.cryptoInfoKeyID != NULL);
883
884    gFields.cryptoInfoIVID = env->GetFieldID(clazz.get(), "iv", "[B");
885    CHECK(gFields.cryptoInfoIVID != NULL);
886
887    gFields.cryptoInfoModeID = env->GetFieldID(clazz.get(), "mode", "I");
888    CHECK(gFields.cryptoInfoModeID != NULL);
889
890    clazz.reset(env->FindClass("android/media/MediaCodec$CryptoException"));
891    CHECK(clazz.get() != NULL);
892
893    jfieldID field;
894    field = env->GetStaticFieldID(clazz.get(), "ERROR_NO_KEY", "I");
895    CHECK(field != NULL);
896    gCryptoErrorCodes.cryptoErrorNoKey =
897        env->GetStaticIntField(clazz.get(), field);
898
899    field = env->GetStaticFieldID(clazz.get(), "ERROR_KEY_EXPIRED", "I");
900    CHECK(field != NULL);
901    gCryptoErrorCodes.cryptoErrorKeyExpired =
902        env->GetStaticIntField(clazz.get(), field);
903
904    field = env->GetStaticFieldID(clazz.get(), "ERROR_RESOURCE_BUSY", "I");
905    CHECK(field != NULL);
906    gCryptoErrorCodes.cryptoErrorResourceBusy =
907        env->GetStaticIntField(clazz.get(), field);
908}
909
910static void android_media_MediaCodec_native_setup(
911        JNIEnv *env, jobject thiz,
912        jstring name, jboolean nameIsType, jboolean encoder) {
913    if (name == NULL) {
914        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
915        return;
916    }
917
918    const char *tmp = env->GetStringUTFChars(name, NULL);
919
920    if (tmp == NULL) {
921        return;
922    }
923
924    sp<JMediaCodec> codec = new JMediaCodec(env, thiz, tmp, nameIsType, encoder);
925
926    status_t err = codec->initCheck();
927
928    env->ReleaseStringUTFChars(name, tmp);
929    tmp = NULL;
930
931    if (err != OK) {
932        jniThrowException(
933                env,
934                "java/io/IOException",
935                "Failed to allocate component instance");
936        return;
937    }
938
939    setMediaCodec(env,thiz, codec);
940}
941
942static void android_media_MediaCodec_native_finalize(
943        JNIEnv *env, jobject thiz) {
944    android_media_MediaCodec_release(env, thiz);
945}
946
947static JNINativeMethod gMethods[] = {
948    { "release", "()V", (void *)android_media_MediaCodec_release },
949
950    { "native_configure",
951      "([Ljava/lang/String;[Ljava/lang/Object;Landroid/view/Surface;"
952      "Landroid/media/MediaCrypto;I)V",
953      (void *)android_media_MediaCodec_native_configure },
954
955    { "createInputSurface", "()Landroid/view/Surface;",
956      (void *)android_media_MediaCodec_createInputSurface },
957
958    { "start", "()V", (void *)android_media_MediaCodec_start },
959    { "stop", "()V", (void *)android_media_MediaCodec_stop },
960    { "flush", "()V", (void *)android_media_MediaCodec_flush },
961
962    { "queueInputBuffer", "(IIIJI)V",
963      (void *)android_media_MediaCodec_queueInputBuffer },
964
965    { "queueSecureInputBuffer", "(IILandroid/media/MediaCodec$CryptoInfo;JI)V",
966      (void *)android_media_MediaCodec_queueSecureInputBuffer },
967
968    { "dequeueInputBuffer", "(J)I",
969      (void *)android_media_MediaCodec_dequeueInputBuffer },
970
971    { "dequeueOutputBuffer", "(Landroid/media/MediaCodec$BufferInfo;J)I",
972      (void *)android_media_MediaCodec_dequeueOutputBuffer },
973
974    { "releaseOutputBuffer", "(IZ)V",
975      (void *)android_media_MediaCodec_releaseOutputBuffer },
976
977    { "signalEndOfInputStream", "()V",
978      (void *)android_media_MediaCodec_signalEndOfInputStream },
979
980    { "getOutputFormatNative", "()Ljava/util/Map;",
981      (void *)android_media_MediaCodec_getOutputFormatNative },
982
983    { "getBuffers", "(Z)[Ljava/nio/ByteBuffer;",
984      (void *)android_media_MediaCodec_getBuffers },
985
986    { "getName", "()Ljava/lang/String;",
987      (void *)android_media_MediaCodec_getName },
988
989    { "setVideoScalingMode", "(I)V",
990      (void *)android_media_MediaCodec_setVideoScalingMode },
991
992    { "native_init", "()V", (void *)android_media_MediaCodec_native_init },
993
994    { "native_setup", "(Ljava/lang/String;ZZ)V",
995      (void *)android_media_MediaCodec_native_setup },
996
997    { "native_finalize", "()V",
998      (void *)android_media_MediaCodec_native_finalize },
999};
1000
1001int register_android_media_MediaCodec(JNIEnv *env) {
1002    return AndroidRuntime::registerNativeMethods(env,
1003                "android/media/MediaCodec", gMethods, NELEM(gMethods));
1004}
1005