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