2 * Copyright 2012, The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 //#define LOG_NDEBUG 0
18 #define LOG_TAG "MediaCodec-JNI"
19 #include <utils/Log.h>
21 #include "android_media_MediaCodec.h"
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"
30 #include <cutils/compiler.h>
32 #include <gui/Surface.h>
34 #include <media/ICrypto.h>
35 #include <media/stagefright/MediaCodec.h>
36 #include <media/stagefright/foundation/ABuffer.h>
37 #include <media/stagefright/foundation/ADebug.h>
38 #include <media/stagefright/foundation/ALooper.h>
39 #include <media/stagefright/foundation/AMessage.h>
40 #include <media/stagefright/foundation/AString.h>
41 #include <media/stagefright/MediaErrors.h>
43 #include <nativehelper/ScopedLocalRef.h>
45 #include <system/window.h>
49 // Keep these in sync with their equivalents in MediaCodec.java !!!
51 DEQUEUE_INFO_TRY_AGAIN_LATER = -1,
52 DEQUEUE_INFO_OUTPUT_FORMAT_CHANGED = -2,
53 DEQUEUE_INFO_OUTPUT_BUFFERS_CHANGED = -3,
58 EVENT_SET_CALLBACK = 2,
61 struct CryptoErrorCodes {
62 jint cryptoErrorNoKey;
63 jint cryptoErrorKeyExpired;
64 jint cryptoErrorResourceBusy;
69 jmethodID postEventFromNativeID;
70 jfieldID cryptoInfoNumSubSamplesID;
71 jfieldID cryptoInfoNumBytesOfClearDataID;
72 jfieldID cryptoInfoNumBytesOfEncryptedDataID;
73 jfieldID cryptoInfoKeyID;
74 jfieldID cryptoInfoIVID;
75 jfieldID cryptoInfoModeID;
78 static fields_t gFields;
80 ////////////////////////////////////////////////////////////////////////////////
82 JMediaCodec::JMediaCodec(
83 JNIEnv *env, jobject thiz,
84 const char *name, bool nameIsType, bool encoder)
87 jclass clazz = env->GetObjectClass(thiz);
90 mClass = (jclass)env->NewGlobalRef(clazz);
91 mObject = env->NewWeakGlobalRef(thiz);
93 cacheJavaObjects(env);
95 mLooper = new ALooper;
96 mLooper->setName("MediaCodec_looper");
99 false, // runOnCallingThread
101 PRIORITY_FOREGROUND);
104 mCodec = MediaCodec::CreateByType(mLooper, name, encoder);
106 mCodec = MediaCodec::CreateByComponentName(mLooper, name);
110 void JMediaCodec::cacheJavaObjects(JNIEnv *env) {
111 jclass clazz = (jclass)env->FindClass("java/nio/ByteBuffer");
112 mByteBufferClass = (jclass)env->NewGlobalRef(clazz);
113 CHECK(mByteBufferClass != NULL);
115 ScopedLocalRef<jclass> byteOrderClass(
116 env, env->FindClass("java/nio/ByteOrder"));
117 CHECK(byteOrderClass.get() != NULL);
119 jmethodID nativeOrderID = env->GetStaticMethodID(
120 byteOrderClass.get(), "nativeOrder", "()Ljava/nio/ByteOrder;");
121 CHECK(nativeOrderID != NULL);
123 jobject nativeByteOrderObj =
124 env->CallStaticObjectMethod(byteOrderClass.get(), nativeOrderID);
125 mNativeByteOrderObj = env->NewGlobalRef(nativeByteOrderObj);
126 CHECK(mNativeByteOrderObj != NULL);
127 env->DeleteLocalRef(nativeByteOrderObj);
128 nativeByteOrderObj = NULL;
130 mByteBufferOrderMethodID = env->GetMethodID(
133 "(Ljava/nio/ByteOrder;)Ljava/nio/ByteBuffer;");
134 CHECK(mByteBufferOrderMethodID != NULL);
136 mByteBufferAsReadOnlyBufferMethodID = env->GetMethodID(
137 mByteBufferClass, "asReadOnlyBuffer", "()Ljava/nio/ByteBuffer;");
138 CHECK(mByteBufferAsReadOnlyBufferMethodID != NULL);
140 mByteBufferPositionMethodID = env->GetMethodID(
141 mByteBufferClass, "position", "(I)Ljava/nio/Buffer;");
142 CHECK(mByteBufferPositionMethodID != NULL);
144 mByteBufferLimitMethodID = env->GetMethodID(
145 mByteBufferClass, "limit", "(I)Ljava/nio/Buffer;");
146 CHECK(mByteBufferLimitMethodID != NULL);
149 status_t JMediaCodec::initCheck() const {
150 return mCodec != NULL ? OK : NO_INIT;
153 void JMediaCodec::registerSelf() {
154 mLooper->registerHandler(this);
157 void JMediaCodec::release() {
158 if (mCodec != NULL) {
163 if (mLooper != NULL) {
164 mLooper->unregisterHandler(id());
170 JMediaCodec::~JMediaCodec() {
171 if (mCodec != NULL || mLooper != NULL) {
172 /* MediaCodec and looper should have been released explicitly already
173 * in setMediaCodec() (see comments in setMediaCodec()).
175 * Otherwise JMediaCodec::~JMediaCodec() might be called from within the
176 * message handler, doing release() there risks deadlock as MediaCodec::
177 * release() post synchronous message to the same looper.
179 * Print a warning and try to proceed with releasing.
181 ALOGW("try to release MediaCodec from JMediaCodec::~JMediaCodec()...");
183 ALOGW("done releasing MediaCodec from JMediaCodec::~JMediaCodec().");
186 JNIEnv *env = AndroidRuntime::getJNIEnv();
188 env->DeleteWeakGlobalRef(mObject);
190 env->DeleteGlobalRef(mClass);
192 deleteJavaObjects(env);
195 void JMediaCodec::deleteJavaObjects(JNIEnv *env) {
196 env->DeleteGlobalRef(mByteBufferClass);
197 mByteBufferClass = NULL;
198 env->DeleteGlobalRef(mNativeByteOrderObj);
199 mNativeByteOrderObj = NULL;
201 mByteBufferOrderMethodID = NULL;
202 mByteBufferAsReadOnlyBufferMethodID = NULL;
203 mByteBufferPositionMethodID = NULL;
204 mByteBufferLimitMethodID = NULL;
207 status_t JMediaCodec::setCallback(jobject cb) {
209 if (mCallbackNotification == NULL) {
210 mCallbackNotification = new AMessage(kWhatCallbackNotify, id());
213 mCallbackNotification.clear();
216 return mCodec->setCallback(mCallbackNotification);
219 status_t JMediaCodec::configure(
220 const sp<AMessage> &format,
221 const sp<IGraphicBufferProducer> &bufferProducer,
222 const sp<ICrypto> &crypto,
225 if (bufferProducer != NULL) {
226 mSurfaceTextureClient =
227 new Surface(bufferProducer, true /* controlledByApp */);
229 mSurfaceTextureClient.clear();
232 return mCodec->configure(format, mSurfaceTextureClient, crypto, flags);
235 status_t JMediaCodec::createInputSurface(
236 sp<IGraphicBufferProducer>* bufferProducer) {
237 return mCodec->createInputSurface(bufferProducer);
240 status_t JMediaCodec::start() {
241 return mCodec->start();
244 status_t JMediaCodec::stop() {
245 mSurfaceTextureClient.clear();
247 return mCodec->stop();
250 status_t JMediaCodec::flush() {
251 return mCodec->flush();
254 status_t JMediaCodec::reset() {
255 return mCodec->reset();
258 status_t JMediaCodec::queueInputBuffer(
260 size_t offset, size_t size, int64_t timeUs, uint32_t flags,
261 AString *errorDetailMsg) {
262 return mCodec->queueInputBuffer(
263 index, offset, size, timeUs, flags, errorDetailMsg);
266 status_t JMediaCodec::queueSecureInputBuffer(
269 const CryptoPlugin::SubSample *subSamples,
270 size_t numSubSamples,
271 const uint8_t key[16],
272 const uint8_t iv[16],
273 CryptoPlugin::Mode mode,
274 int64_t presentationTimeUs,
276 AString *errorDetailMsg) {
277 return mCodec->queueSecureInputBuffer(
278 index, offset, subSamples, numSubSamples, key, iv, mode,
279 presentationTimeUs, flags, errorDetailMsg);
282 status_t JMediaCodec::dequeueInputBuffer(size_t *index, int64_t timeoutUs) {
283 return mCodec->dequeueInputBuffer(index, timeoutUs);
286 status_t JMediaCodec::dequeueOutputBuffer(
287 JNIEnv *env, jobject bufferInfo, size_t *index, int64_t timeoutUs) {
291 status_t err = mCodec->dequeueOutputBuffer(
292 index, &offset, &size, &timeUs, &flags, timeoutUs);
298 ScopedLocalRef<jclass> clazz(
299 env, env->FindClass("android/media/MediaCodec$BufferInfo"));
301 jmethodID method = env->GetMethodID(clazz.get(), "set", "(IIJI)V");
302 env->CallVoidMethod(bufferInfo, method, (jint)offset, (jint)size, timeUs, flags);
307 status_t JMediaCodec::releaseOutputBuffer(
308 size_t index, bool render, bool updatePTS, int64_t timestampNs) {
310 return mCodec->renderOutputBufferAndRelease(index, timestampNs);
313 ? mCodec->renderOutputBufferAndRelease(index)
314 : mCodec->releaseOutputBuffer(index);
317 status_t JMediaCodec::signalEndOfInputStream() {
318 return mCodec->signalEndOfInputStream();
321 status_t JMediaCodec::getFormat(JNIEnv *env, bool input, jobject *format) const {
324 err = input ? mCodec->getInputFormat(&msg) : mCodec->getOutputFormat(&msg);
329 return ConvertMessageToMap(env, msg, format);
332 status_t JMediaCodec::getOutputFormat(JNIEnv *env, size_t index, jobject *format) const {
335 if ((err = mCodec->getOutputFormat(index, &msg)) != OK) {
339 return ConvertMessageToMap(env, msg, format);
342 status_t JMediaCodec::getBuffers(
343 JNIEnv *env, bool input, jobjectArray *bufArray) const {
344 Vector<sp<ABuffer> > buffers;
348 ? mCodec->getInputBuffers(&buffers)
349 : mCodec->getOutputBuffers(&buffers);
355 *bufArray = (jobjectArray)env->NewObjectArray(
356 buffers.size(), mByteBufferClass, NULL);
357 if (*bufArray == NULL) {
361 for (size_t i = 0; i < buffers.size(); ++i) {
362 const sp<ABuffer> &buffer = buffers.itemAt(i);
364 jobject byteBuffer = NULL;
365 err = createByteBufferFromABuffer(
366 env, !input /* readOnly */, true /* clearBuffer */, buffer, &byteBuffer);
370 if (byteBuffer != NULL) {
371 env->SetObjectArrayElement(
372 *bufArray, i, byteBuffer);
374 env->DeleteLocalRef(byteBuffer);
383 status_t JMediaCodec::createByteBufferFromABuffer(
384 JNIEnv *env, bool readOnly, bool clearBuffer, const sp<ABuffer> &buffer,
385 jobject *buf) const {
386 // if this is an ABuffer that doesn't actually hold any accessible memory,
387 // use a null ByteBuffer
389 if (buffer->base() == NULL) {
394 env->NewDirectByteBuffer(buffer->base(), buffer->capacity());
395 if (readOnly && byteBuffer != NULL) {
396 jobject readOnlyBuffer = env->CallObjectMethod(
397 byteBuffer, mByteBufferAsReadOnlyBufferMethodID);
398 env->DeleteLocalRef(byteBuffer);
399 byteBuffer = readOnlyBuffer;
401 if (byteBuffer == NULL) {
404 jobject me = env->CallObjectMethod(
405 byteBuffer, mByteBufferOrderMethodID, mNativeByteOrderObj);
406 env->DeleteLocalRef(me);
407 me = env->CallObjectMethod(
408 byteBuffer, mByteBufferLimitMethodID,
409 clearBuffer ? buffer->capacity() : (buffer->offset() + buffer->size()));
410 env->DeleteLocalRef(me);
411 me = env->CallObjectMethod(
412 byteBuffer, mByteBufferPositionMethodID,
413 clearBuffer ? 0 : buffer->offset());
414 env->DeleteLocalRef(me);
421 status_t JMediaCodec::getBuffer(
422 JNIEnv *env, bool input, size_t index, jobject *buf) const {
427 ? mCodec->getInputBuffer(index, &buffer)
428 : mCodec->getOutputBuffer(index, &buffer);
434 return createByteBufferFromABuffer(
435 env, !input /* readOnly */, input /* clearBuffer */, buffer, buf);
438 status_t JMediaCodec::getImage(
439 JNIEnv *env, bool input, size_t index, jobject *buf) const {
444 ? mCodec->getInputBuffer(index, &buffer)
445 : mCodec->getOutputBuffer(index, &buffer);
451 // if this is an ABuffer that doesn't actually hold any accessible memory,
452 // use a null ByteBuffer
454 if (buffer->base() == NULL) {
458 // check if buffer is an image
459 sp<ABuffer> imageData;
460 if (!buffer->meta()->findBuffer("image-data", &imageData)) {
464 int64_t timestamp = 0;
465 if (!input && buffer->meta()->findInt64("timeUs", ×tamp)) {
466 timestamp *= 1000; // adjust to ns
469 jobject byteBuffer = NULL;
470 err = createByteBufferFromABuffer(
471 env, !input /* readOnly */, input /* clearBuffer */, buffer, &byteBuffer);
476 jobject infoBuffer = NULL;
477 err = createByteBufferFromABuffer(
478 env, true /* readOnly */, true /* clearBuffer */, imageData, &infoBuffer);
480 env->DeleteLocalRef(byteBuffer);
485 jobject cropRect = NULL;
486 int32_t left, top, right, bottom;
487 if (buffer->meta()->findRect("crop-rect", &left, &top, &right, &bottom)) {
488 ScopedLocalRef<jclass> rectClazz(
489 env, env->FindClass("android/graphics/Rect"));
490 CHECK(rectClazz.get() != NULL);
492 jmethodID rectConstructID = env->GetMethodID(
493 rectClazz.get(), "<init>", "(IIII)V");
495 cropRect = env->NewObject(
496 rectClazz.get(), rectConstructID, left, top, right + 1, bottom + 1);
499 ScopedLocalRef<jclass> imageClazz(
500 env, env->FindClass("android/media/MediaCodec$MediaImage"));
501 CHECK(imageClazz.get() != NULL);
503 jmethodID imageConstructID = env->GetMethodID(imageClazz.get(), "<init>",
504 "(Ljava/nio/ByteBuffer;Ljava/nio/ByteBuffer;ZJIILandroid/graphics/Rect;)V");
506 *buf = env->NewObject(imageClazz.get(), imageConstructID,
507 byteBuffer, infoBuffer,
508 (jboolean)!input /* readOnly */,
510 (jint)0 /* xOffset */, (jint)0 /* yOffset */, cropRect);
512 // if MediaImage creation fails, return null
513 if (env->ExceptionCheck()) {
514 env->ExceptionDescribe();
515 env->ExceptionClear();
519 if (cropRect != NULL) {
520 env->DeleteLocalRef(cropRect);
524 env->DeleteLocalRef(byteBuffer);
527 env->DeleteLocalRef(infoBuffer);
533 status_t JMediaCodec::getName(JNIEnv *env, jstring *nameStr) const {
536 status_t err = mCodec->getName(&name);
542 *nameStr = env->NewStringUTF(name.c_str());
547 status_t JMediaCodec::setParameters(const sp<AMessage> &msg) {
548 return mCodec->setParameters(msg);
551 void JMediaCodec::setVideoScalingMode(int mode) {
552 if (mSurfaceTextureClient != NULL) {
553 native_window_set_scaling_mode(mSurfaceTextureClient.get(), mode);
557 void JMediaCodec::handleCallback(const sp<AMessage> &msg) {
558 int32_t arg1, arg2 = 0;
560 CHECK(msg->findInt32("callbackID", &arg1));
561 JNIEnv *env = AndroidRuntime::getJNIEnv();
564 case MediaCodec::CB_INPUT_AVAILABLE:
566 CHECK(msg->findInt32("index", &arg2));
570 case MediaCodec::CB_OUTPUT_AVAILABLE:
572 CHECK(msg->findInt32("index", &arg2));
577 CHECK(msg->findSize("size", &size));
578 CHECK(msg->findSize("offset", &offset));
579 CHECK(msg->findInt64("timeUs", &timeUs));
580 CHECK(msg->findInt32("flags", (int32_t *)&flags));
582 ScopedLocalRef<jclass> clazz(
583 env, env->FindClass("android/media/MediaCodec$BufferInfo"));
584 jmethodID ctor = env->GetMethodID(clazz.get(), "<init>", "()V");
585 jmethodID method = env->GetMethodID(clazz.get(), "set", "(IIJI)V");
587 obj = env->NewObject(clazz.get(), ctor);
590 if (env->ExceptionCheck()) {
591 ALOGE("Could not create MediaCodec.BufferInfo.");
592 env->ExceptionClear();
594 jniThrowException(env, "java/lang/IllegalStateException", NULL);
598 env->CallVoidMethod(obj, method, (jint)offset, (jint)size, timeUs, flags);
602 case MediaCodec::CB_ERROR:
604 int32_t err, actionCode;
605 CHECK(msg->findInt32("err", &err));
606 CHECK(msg->findInt32("actionCode", &actionCode));
608 // use Integer object to pass the action code
609 ScopedLocalRef<jclass> clazz(
610 env, env->FindClass("android/media/MediaCodec$CodecException"));
611 jmethodID ctor = env->GetMethodID(clazz.get(), "<init>", "(IILjava/lang/String;)V");
614 const char *detail = "Unknown error";
615 if (msg->findString("detail", &str)) {
616 detail = str.c_str();
618 jstring msgObj = env->NewStringUTF(detail);
620 obj = env->NewObject(clazz.get(), ctor, err, actionCode, msgObj);
623 if (env->ExceptionCheck()) {
624 ALOGE("Could not create CodecException object.");
625 env->ExceptionClear();
627 jniThrowException(env, "java/lang/IllegalStateException", NULL);
634 case MediaCodec::CB_OUTPUT_FORMAT_CHANGED:
637 CHECK(msg->findMessage("format", &format));
639 if (OK != ConvertMessageToMap(env, format, &obj)) {
640 jniThrowException(env, "java/lang/IllegalStateException", NULL);
653 gFields.postEventFromNativeID,
659 env->DeleteLocalRef(obj);
662 void JMediaCodec::onMessageReceived(const sp<AMessage> &msg) {
663 switch (msg->what()) {
664 case kWhatCallbackNotify:
674 } // namespace android
676 ////////////////////////////////////////////////////////////////////////////////
678 using namespace android;
680 static sp<JMediaCodec> setMediaCodec(
681 JNIEnv *env, jobject thiz, const sp<JMediaCodec> &codec) {
682 sp<JMediaCodec> old = (JMediaCodec *)env->GetLongField(thiz, gFields.context);
684 codec->incStrong(thiz);
687 /* release MediaCodec and stop the looper now before decStrong.
688 * otherwise JMediaCodec::~JMediaCodec() could be called from within
689 * its message handler, doing release() from there will deadlock
690 * (as MediaCodec::release() post synchronous message to the same looper)
693 old->decStrong(thiz);
695 env->SetLongField(thiz, gFields.context, (jlong)codec.get());
700 static sp<JMediaCodec> getMediaCodec(JNIEnv *env, jobject thiz) {
701 return (JMediaCodec *)env->GetLongField(thiz, gFields.context);
704 static void android_media_MediaCodec_release(JNIEnv *env, jobject thiz) {
705 setMediaCodec(env, thiz, NULL);
708 static void throwCryptoException(JNIEnv *env, status_t err, const char *msg) {
709 ScopedLocalRef<jclass> clazz(
710 env, env->FindClass("android/media/MediaCodec$CryptoException"));
711 CHECK(clazz.get() != NULL);
713 jmethodID constructID =
714 env->GetMethodID(clazz.get(), "<init>", "(ILjava/lang/String;)V");
715 CHECK(constructID != NULL);
717 jstring msgObj = env->NewStringUTF(msg != NULL ? msg : "Unknown Error");
719 /* translate OS errors to Java API CryptoException errorCodes */
721 case ERROR_DRM_NO_LICENSE:
722 err = gCryptoErrorCodes.cryptoErrorNoKey;
724 case ERROR_DRM_LICENSE_EXPIRED:
725 err = gCryptoErrorCodes.cryptoErrorKeyExpired;
727 case ERROR_DRM_RESOURCE_BUSY:
728 err = gCryptoErrorCodes.cryptoErrorResourceBusy;
734 jthrowable exception =
735 (jthrowable)env->NewObject(clazz.get(), constructID, err, msgObj);
737 env->Throw(exception);
740 static jint throwExceptionAsNecessary(
741 JNIEnv *env, status_t err, const char *msg = NULL) {
742 if (err >= ERROR_DRM_VENDOR_MIN && err <= ERROR_DRM_VENDOR_MAX) {
743 // We'll throw our custom MediaCodec.CryptoException
744 throwCryptoException(env, err, msg);
753 return DEQUEUE_INFO_TRY_AGAIN_LATER;
755 case INFO_FORMAT_CHANGED:
756 return DEQUEUE_INFO_OUTPUT_FORMAT_CHANGED;
758 case INFO_OUTPUT_BUFFERS_CHANGED:
759 return DEQUEUE_INFO_OUTPUT_BUFFERS_CHANGED;
761 case ERROR_DRM_NO_LICENSE:
762 case ERROR_DRM_LICENSE_EXPIRED:
763 case ERROR_DRM_RESOURCE_BUSY:
764 throwCryptoException(env, err, msg);
769 jniThrowException(env, "java/lang/IllegalStateException", msg);
777 static void android_media_MediaCodec_native_setCallback(
781 sp<JMediaCodec> codec = getMediaCodec(env, thiz);
784 jniThrowException(env, "java/lang/IllegalStateException", NULL);
788 status_t err = codec->setCallback(cb);
790 throwExceptionAsNecessary(env, err);
793 static void android_media_MediaCodec_native_configure(
796 jobjectArray keys, jobjectArray values,
800 sp<JMediaCodec> codec = getMediaCodec(env, thiz);
803 jniThrowException(env, "java/lang/IllegalStateException", NULL);
808 status_t err = ConvertKeyValueArraysToMessage(env, keys, values, &format);
811 jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
815 sp<IGraphicBufferProducer> bufferProducer;
816 if (jsurface != NULL) {
817 sp<Surface> surface(android_view_Surface_getSurface(env, jsurface));
818 if (surface != NULL) {
819 bufferProducer = surface->getIGraphicBufferProducer();
823 "java/lang/IllegalArgumentException",
824 "The surface has been released");
830 if (jcrypto != NULL) {
831 crypto = JCrypto::GetCrypto(env, jcrypto);
834 err = codec->configure(format, bufferProducer, crypto, flags);
836 throwExceptionAsNecessary(env, err);
839 static jobject android_media_MediaCodec_createInputSurface(JNIEnv* env,
841 ALOGV("android_media_MediaCodec_createInputSurface");
843 sp<JMediaCodec> codec = getMediaCodec(env, thiz);
845 jniThrowException(env, "java/lang/IllegalStateException", NULL);
849 // Tell the MediaCodec that we want to use a Surface as input.
850 sp<IGraphicBufferProducer> bufferProducer;
851 status_t err = codec->createInputSurface(&bufferProducer);
852 if (err != NO_ERROR) {
853 throwExceptionAsNecessary(env, err);
857 // Wrap the IGBP in a Java-language Surface.
858 return android_view_Surface_createFromIGraphicBufferProducer(env,
862 static void android_media_MediaCodec_start(JNIEnv *env, jobject thiz) {
863 ALOGV("android_media_MediaCodec_start");
865 sp<JMediaCodec> codec = getMediaCodec(env, thiz);
868 jniThrowException(env, "java/lang/IllegalStateException", "no codec found");
872 status_t err = codec->start();
874 throwExceptionAsNecessary(env, err, "start failed");
877 static void android_media_MediaCodec_stop(JNIEnv *env, jobject thiz) {
878 ALOGV("android_media_MediaCodec_stop");
880 sp<JMediaCodec> codec = getMediaCodec(env, thiz);
883 jniThrowException(env, "java/lang/IllegalStateException", NULL);
887 status_t err = codec->stop();
889 throwExceptionAsNecessary(env, err);
892 static void android_media_MediaCodec_reset(JNIEnv *env, jobject thiz) {
893 ALOGV("android_media_MediaCodec_reset");
895 sp<JMediaCodec> codec = getMediaCodec(env, thiz);
898 // should never be here
899 jniThrowException(env, "java/lang/IllegalStateException", NULL);
903 status_t err = codec->reset();
905 // treat all errors as fatal for now, though resource not available
906 // errors could be treated as transient.
909 throwExceptionAsNecessary(env, err);
912 static void android_media_MediaCodec_flush(JNIEnv *env, jobject thiz) {
913 ALOGV("android_media_MediaCodec_flush");
915 sp<JMediaCodec> codec = getMediaCodec(env, thiz);
918 jniThrowException(env, "java/lang/IllegalStateException", NULL);
922 status_t err = codec->flush();
924 throwExceptionAsNecessary(env, err);
927 static void android_media_MediaCodec_queueInputBuffer(
935 ALOGV("android_media_MediaCodec_queueInputBuffer");
937 sp<JMediaCodec> codec = getMediaCodec(env, thiz);
940 jniThrowException(env, "java/lang/IllegalStateException", NULL);
944 AString errorDetailMsg;
946 status_t err = codec->queueInputBuffer(
947 index, offset, size, timestampUs, flags, &errorDetailMsg);
949 throwExceptionAsNecessary(
950 env, err, errorDetailMsg.empty() ? NULL : errorDetailMsg.c_str());
953 static void android_media_MediaCodec_queueSecureInputBuffer(
958 jobject cryptoInfoObj,
961 ALOGV("android_media_MediaCodec_queueSecureInputBuffer");
963 sp<JMediaCodec> codec = getMediaCodec(env, thiz);
966 jniThrowException(env, "java/lang/IllegalStateException", NULL);
971 env->GetIntField(cryptoInfoObj, gFields.cryptoInfoNumSubSamplesID);
973 jintArray numBytesOfClearDataObj =
974 (jintArray)env->GetObjectField(
975 cryptoInfoObj, gFields.cryptoInfoNumBytesOfClearDataID);
977 jintArray numBytesOfEncryptedDataObj =
978 (jintArray)env->GetObjectField(
979 cryptoInfoObj, gFields.cryptoInfoNumBytesOfEncryptedDataID);
982 (jbyteArray)env->GetObjectField(cryptoInfoObj, gFields.cryptoInfoKeyID);
985 (jbyteArray)env->GetObjectField(cryptoInfoObj, gFields.cryptoInfoIVID);
987 jint mode = env->GetIntField(cryptoInfoObj, gFields.cryptoInfoModeID);
991 CryptoPlugin::SubSample *subSamples = NULL;
995 if (numSubSamples <= 0) {
997 } else if (numBytesOfClearDataObj == NULL
998 && numBytesOfEncryptedDataObj == NULL) {
1000 } else if (numBytesOfEncryptedDataObj != NULL
1001 && env->GetArrayLength(numBytesOfEncryptedDataObj) < numSubSamples) {
1003 } else if (numBytesOfClearDataObj != NULL
1004 && env->GetArrayLength(numBytesOfClearDataObj) < numSubSamples) {
1006 // subSamples array may silently overflow if number of samples are too large. Use
1007 // INT32_MAX as maximum allocation size may be less than SIZE_MAX on some platforms
1008 } else if ( CC_UNLIKELY(numSubSamples >= INT32_MAX / sizeof(*subSamples)) ) {
1013 jint *numBytesOfClearData =
1014 (numBytesOfClearDataObj == NULL)
1016 : env->GetIntArrayElements(numBytesOfClearDataObj, &isCopy);
1018 jint *numBytesOfEncryptedData =
1019 (numBytesOfEncryptedDataObj == NULL)
1021 : env->GetIntArrayElements(numBytesOfEncryptedDataObj, &isCopy);
1023 subSamples = new CryptoPlugin::SubSample[numSubSamples];
1025 for (jint i = 0; i < numSubSamples; ++i) {
1026 subSamples[i].mNumBytesOfClearData =
1027 (numBytesOfClearData == NULL) ? 0 : numBytesOfClearData[i];
1029 subSamples[i].mNumBytesOfEncryptedData =
1030 (numBytesOfEncryptedData == NULL)
1031 ? 0 : numBytesOfEncryptedData[i];
1034 if (numBytesOfEncryptedData != NULL) {
1035 env->ReleaseIntArrayElements(
1036 numBytesOfEncryptedDataObj, numBytesOfEncryptedData, 0);
1037 numBytesOfEncryptedData = NULL;
1040 if (numBytesOfClearData != NULL) {
1041 env->ReleaseIntArrayElements(
1042 numBytesOfClearDataObj, numBytesOfClearData, 0);
1043 numBytesOfClearData = NULL;
1047 if (err == OK && keyObj != NULL) {
1048 if (env->GetArrayLength(keyObj) != 16) {
1052 key = env->GetByteArrayElements(keyObj, &isCopy);
1056 if (err == OK && ivObj != NULL) {
1057 if (env->GetArrayLength(ivObj) != 16) {
1061 iv = env->GetByteArrayElements(ivObj, &isCopy);
1065 AString errorDetailMsg;
1068 err = codec->queueSecureInputBuffer(
1070 subSamples, numSubSamples,
1071 (const uint8_t *)key, (const uint8_t *)iv,
1072 (CryptoPlugin::Mode)mode,
1079 env->ReleaseByteArrayElements(ivObj, iv, 0);
1084 env->ReleaseByteArrayElements(keyObj, key, 0);
1088 delete[] subSamples;
1091 throwExceptionAsNecessary(
1092 env, err, errorDetailMsg.empty() ? NULL : errorDetailMsg.c_str());
1095 static jint android_media_MediaCodec_dequeueInputBuffer(
1096 JNIEnv *env, jobject thiz, jlong timeoutUs) {
1097 ALOGV("android_media_MediaCodec_dequeueInputBuffer");
1099 sp<JMediaCodec> codec = getMediaCodec(env, thiz);
1101 if (codec == NULL) {
1102 jniThrowException(env, "java/lang/IllegalStateException", NULL);
1107 status_t err = codec->dequeueInputBuffer(&index, timeoutUs);
1110 return (jint) index;
1113 return throwExceptionAsNecessary(env, err);
1116 static jint android_media_MediaCodec_dequeueOutputBuffer(
1117 JNIEnv *env, jobject thiz, jobject bufferInfo, jlong timeoutUs) {
1118 ALOGV("android_media_MediaCodec_dequeueOutputBuffer");
1120 sp<JMediaCodec> codec = getMediaCodec(env, thiz);
1122 if (codec == NULL) {
1123 jniThrowException(env, "java/lang/IllegalStateException", NULL);
1128 status_t err = codec->dequeueOutputBuffer(
1129 env, bufferInfo, &index, timeoutUs);
1132 return (jint) index;
1135 return throwExceptionAsNecessary(env, err);
1138 static void android_media_MediaCodec_releaseOutputBuffer(
1139 JNIEnv *env, jobject thiz,
1140 jint index, jboolean render, jboolean updatePTS, jlong timestampNs) {
1141 ALOGV("android_media_MediaCodec_renderOutputBufferAndRelease");
1143 sp<JMediaCodec> codec = getMediaCodec(env, thiz);
1145 if (codec == NULL) {
1146 jniThrowException(env, "java/lang/IllegalStateException", NULL);
1150 status_t err = codec->releaseOutputBuffer(index, render, updatePTS, timestampNs);
1152 throwExceptionAsNecessary(env, err);
1155 static void android_media_MediaCodec_signalEndOfInputStream(JNIEnv* env,
1157 ALOGV("android_media_MediaCodec_signalEndOfInputStream");
1159 sp<JMediaCodec> codec = getMediaCodec(env, thiz);
1160 if (codec == NULL) {
1161 jniThrowException(env, "java/lang/IllegalStateException", NULL);
1165 status_t err = codec->signalEndOfInputStream();
1167 throwExceptionAsNecessary(env, err);
1170 static jobject android_media_MediaCodec_getFormatNative(
1171 JNIEnv *env, jobject thiz, jboolean input) {
1172 ALOGV("android_media_MediaCodec_getFormatNative");
1174 sp<JMediaCodec> codec = getMediaCodec(env, thiz);
1176 if (codec == NULL) {
1177 jniThrowException(env, "java/lang/IllegalStateException", NULL);
1182 status_t err = codec->getFormat(env, input, &format);
1188 throwExceptionAsNecessary(env, err);
1193 static jobject android_media_MediaCodec_getOutputFormatForIndexNative(
1194 JNIEnv *env, jobject thiz, jint index) {
1195 ALOGV("android_media_MediaCodec_getOutputFormatForIndexNative");
1197 sp<JMediaCodec> codec = getMediaCodec(env, thiz);
1199 if (codec == NULL) {
1200 jniThrowException(env, "java/lang/IllegalStateException", NULL);
1205 status_t err = codec->getOutputFormat(env, index, &format);
1211 throwExceptionAsNecessary(env, err);
1216 static jobjectArray android_media_MediaCodec_getBuffers(
1217 JNIEnv *env, jobject thiz, jboolean input) {
1218 ALOGV("android_media_MediaCodec_getBuffers");
1220 sp<JMediaCodec> codec = getMediaCodec(env, thiz);
1222 if (codec == NULL) {
1223 jniThrowException(env, "java/lang/IllegalStateException", NULL);
1227 jobjectArray buffers;
1228 status_t err = codec->getBuffers(env, input, &buffers);
1234 // if we're out of memory, an exception was already thrown
1235 if (err != NO_MEMORY) {
1236 throwExceptionAsNecessary(env, err);
1242 static jobject android_media_MediaCodec_getBuffer(
1243 JNIEnv *env, jobject thiz, jboolean input, jint index) {
1244 ALOGV("android_media_MediaCodec_getBuffer");
1246 sp<JMediaCodec> codec = getMediaCodec(env, thiz);
1248 if (codec == NULL) {
1249 jniThrowException(env, "java/lang/IllegalStateException", NULL);
1254 status_t err = codec->getBuffer(env, input, index, &buffer);
1260 // if we're out of memory, an exception was already thrown
1261 if (err != NO_MEMORY) {
1262 throwExceptionAsNecessary(env, err);
1268 static jobject android_media_MediaCodec_getImage(
1269 JNIEnv *env, jobject thiz, jboolean input, jint index) {
1270 ALOGV("android_media_MediaCodec_getImage");
1272 sp<JMediaCodec> codec = getMediaCodec(env, thiz);
1274 if (codec == NULL) {
1275 jniThrowException(env, "java/lang/IllegalStateException", NULL);
1280 status_t err = codec->getImage(env, input, index, &image);
1286 // if we're out of memory, an exception was already thrown
1287 if (err != NO_MEMORY) {
1288 throwExceptionAsNecessary(env, err);
1294 static jobject android_media_MediaCodec_getName(
1295 JNIEnv *env, jobject thiz) {
1296 ALOGV("android_media_MediaCodec_getName");
1298 sp<JMediaCodec> codec = getMediaCodec(env, thiz);
1300 if (codec == NULL) {
1301 jniThrowException(env, "java/lang/IllegalStateException", NULL);
1306 status_t err = codec->getName(env, &name);
1312 throwExceptionAsNecessary(env, err);
1317 static void android_media_MediaCodec_setParameters(
1318 JNIEnv *env, jobject thiz, jobjectArray keys, jobjectArray vals) {
1319 ALOGV("android_media_MediaCodec_setParameters");
1321 sp<JMediaCodec> codec = getMediaCodec(env, thiz);
1323 if (codec == NULL) {
1324 jniThrowException(env, "java/lang/IllegalStateException", NULL);
1328 sp<AMessage> params;
1329 status_t err = ConvertKeyValueArraysToMessage(env, keys, vals, ¶ms);
1332 err = codec->setParameters(params);
1335 throwExceptionAsNecessary(env, err);
1338 static void android_media_MediaCodec_setVideoScalingMode(
1339 JNIEnv *env, jobject thiz, jint mode) {
1340 sp<JMediaCodec> codec = getMediaCodec(env, thiz);
1342 if (codec == NULL) {
1343 jniThrowException(env, "java/lang/IllegalStateException", NULL);
1347 if (mode != NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW
1348 && mode != NATIVE_WINDOW_SCALING_MODE_SCALE_CROP) {
1349 jniThrowException(env, "java/lang/InvalidArgumentException", NULL);
1353 codec->setVideoScalingMode(mode);
1356 static void android_media_MediaCodec_native_init(JNIEnv *env) {
1357 ScopedLocalRef<jclass> clazz(
1358 env, env->FindClass("android/media/MediaCodec"));
1359 CHECK(clazz.get() != NULL);
1361 gFields.context = env->GetFieldID(clazz.get(), "mNativeContext", "J");
1362 CHECK(gFields.context != NULL);
1364 gFields.postEventFromNativeID =
1366 clazz.get(), "postEventFromNative", "(IIILjava/lang/Object;)V");
1368 CHECK(gFields.postEventFromNativeID != NULL);
1370 clazz.reset(env->FindClass("android/media/MediaCodec$CryptoInfo"));
1371 CHECK(clazz.get() != NULL);
1373 gFields.cryptoInfoNumSubSamplesID =
1374 env->GetFieldID(clazz.get(), "numSubSamples", "I");
1375 CHECK(gFields.cryptoInfoNumSubSamplesID != NULL);
1377 gFields.cryptoInfoNumBytesOfClearDataID =
1378 env->GetFieldID(clazz.get(), "numBytesOfClearData", "[I");
1379 CHECK(gFields.cryptoInfoNumBytesOfClearDataID != NULL);
1381 gFields.cryptoInfoNumBytesOfEncryptedDataID =
1382 env->GetFieldID(clazz.get(), "numBytesOfEncryptedData", "[I");
1383 CHECK(gFields.cryptoInfoNumBytesOfEncryptedDataID != NULL);
1385 gFields.cryptoInfoKeyID = env->GetFieldID(clazz.get(), "key", "[B");
1386 CHECK(gFields.cryptoInfoKeyID != NULL);
1388 gFields.cryptoInfoIVID = env->GetFieldID(clazz.get(), "iv", "[B");
1389 CHECK(gFields.cryptoInfoIVID != NULL);
1391 gFields.cryptoInfoModeID = env->GetFieldID(clazz.get(), "mode", "I");
1392 CHECK(gFields.cryptoInfoModeID != NULL);
1394 clazz.reset(env->FindClass("android/media/MediaCodec$CryptoException"));
1395 CHECK(clazz.get() != NULL);
1398 field = env->GetStaticFieldID(clazz.get(), "ERROR_NO_KEY", "I");
1399 CHECK(field != NULL);
1400 gCryptoErrorCodes.cryptoErrorNoKey =
1401 env->GetStaticIntField(clazz.get(), field);
1403 field = env->GetStaticFieldID(clazz.get(), "ERROR_KEY_EXPIRED", "I");
1404 CHECK(field != NULL);
1405 gCryptoErrorCodes.cryptoErrorKeyExpired =
1406 env->GetStaticIntField(clazz.get(), field);
1408 field = env->GetStaticFieldID(clazz.get(), "ERROR_RESOURCE_BUSY", "I");
1409 CHECK(field != NULL);
1410 gCryptoErrorCodes.cryptoErrorResourceBusy =
1411 env->GetStaticIntField(clazz.get(), field);
1414 static void android_media_MediaCodec_native_setup(
1415 JNIEnv *env, jobject thiz,
1416 jstring name, jboolean nameIsType, jboolean encoder) {
1418 jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
1422 const char *tmp = env->GetStringUTFChars(name, NULL);
1428 sp<JMediaCodec> codec = new JMediaCodec(env, thiz, tmp, nameIsType, encoder);
1430 status_t err = codec->initCheck();
1432 env->ReleaseStringUTFChars(name, tmp);
1438 "java/io/IOException",
1439 "Failed to allocate component instance");
1443 codec->registerSelf();
1445 setMediaCodec(env,thiz, codec);
1448 static void android_media_MediaCodec_native_finalize(
1449 JNIEnv *env, jobject thiz) {
1450 android_media_MediaCodec_release(env, thiz);
1453 static JNINativeMethod gMethods[] = {
1454 { "native_release", "()V", (void *)android_media_MediaCodec_release },
1456 { "native_reset", "()V", (void *)android_media_MediaCodec_reset },
1458 { "native_setCallback",
1459 "(Landroid/media/MediaCodec$Callback;)V",
1460 (void *)android_media_MediaCodec_native_setCallback },
1462 { "native_configure",
1463 "([Ljava/lang/String;[Ljava/lang/Object;Landroid/view/Surface;"
1464 "Landroid/media/MediaCrypto;I)V",
1465 (void *)android_media_MediaCodec_native_configure },
1467 { "createInputSurface", "()Landroid/view/Surface;",
1468 (void *)android_media_MediaCodec_createInputSurface },
1470 { "native_start", "()V", (void *)android_media_MediaCodec_start },
1471 { "native_stop", "()V", (void *)android_media_MediaCodec_stop },
1472 { "native_flush", "()V", (void *)android_media_MediaCodec_flush },
1474 { "native_queueInputBuffer", "(IIIJI)V",
1475 (void *)android_media_MediaCodec_queueInputBuffer },
1477 { "native_queueSecureInputBuffer", "(IILandroid/media/MediaCodec$CryptoInfo;JI)V",
1478 (void *)android_media_MediaCodec_queueSecureInputBuffer },
1480 { "native_dequeueInputBuffer", "(J)I",
1481 (void *)android_media_MediaCodec_dequeueInputBuffer },
1483 { "native_dequeueOutputBuffer", "(Landroid/media/MediaCodec$BufferInfo;J)I",
1484 (void *)android_media_MediaCodec_dequeueOutputBuffer },
1486 { "releaseOutputBuffer", "(IZZJ)V",
1487 (void *)android_media_MediaCodec_releaseOutputBuffer },
1489 { "signalEndOfInputStream", "()V",
1490 (void *)android_media_MediaCodec_signalEndOfInputStream },
1492 { "getFormatNative", "(Z)Ljava/util/Map;",
1493 (void *)android_media_MediaCodec_getFormatNative },
1495 { "getOutputFormatNative", "(I)Ljava/util/Map;",
1496 (void *)android_media_MediaCodec_getOutputFormatForIndexNative },
1498 { "getBuffers", "(Z)[Ljava/nio/ByteBuffer;",
1499 (void *)android_media_MediaCodec_getBuffers },
1501 { "getBuffer", "(ZI)Ljava/nio/ByteBuffer;",
1502 (void *)android_media_MediaCodec_getBuffer },
1504 { "getImage", "(ZI)Landroid/media/Image;",
1505 (void *)android_media_MediaCodec_getImage },
1507 { "getName", "()Ljava/lang/String;",
1508 (void *)android_media_MediaCodec_getName },
1510 { "setParameters", "([Ljava/lang/String;[Ljava/lang/Object;)V",
1511 (void *)android_media_MediaCodec_setParameters },
1513 { "setVideoScalingMode", "(I)V",
1514 (void *)android_media_MediaCodec_setVideoScalingMode },
1516 { "native_init", "()V", (void *)android_media_MediaCodec_native_init },
1518 { "native_setup", "(Ljava/lang/String;ZZ)V",
1519 (void *)android_media_MediaCodec_native_setup },
1521 { "native_finalize", "()V",
1522 (void *)android_media_MediaCodec_native_finalize },
1525 int register_android_media_MediaCodec(JNIEnv *env) {
1526 return AndroidRuntime::registerNativeMethods(env,
1527 "android/media/MediaCodec", gMethods, NELEM(gMethods));