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