diff options
author | Andy Hung <hunga@google.com> | 2014-07-30 15:48:21 -0700 |
---|---|---|
committer | Andy Hung <hunga@google.com> | 2014-08-08 16:15:07 +0000 |
commit | 5f9aa0bcea8be860fdf5a35476435616cb5f44f3 (patch) | |
tree | bf3a6a08cd86f9ec47b934c7991401f89475deba /media/jni/android_media_MediaCodec.cpp | |
parent | fa387ad6cfa996d09050f8f6c39eaa0537893d20 (diff) | |
download | frameworks_base-5f9aa0bcea8be860fdf5a35476435616cb5f44f3.zip frameworks_base-5f9aa0bcea8be860fdf5a35476435616cb5f44f3.tar.gz frameworks_base-5f9aa0bcea8be860fdf5a35476435616cb5f44f3.tar.bz2 |
Update exception handling for MediaCodec
Throw MediaCodec.CodecException (versus IllegalStateException).
Print out status error codes as found (See MediaError.h for details).
Update error code mapping to CryptoException.
Bug: 12034929
Bug: 13976475
Change-Id: I23b951cb06807413f087c238a1d3526659f06e7b
Diffstat (limited to 'media/jni/android_media_MediaCodec.cpp')
-rw-r--r-- | media/jni/android_media_MediaCodec.cpp | 190 |
1 files changed, 115 insertions, 75 deletions
diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp index d033f76..f1e1099 100644 --- a/media/jni/android_media_MediaCodec.cpp +++ b/media/jni/android_media_MediaCodec.cpp @@ -58,12 +58,17 @@ enum { EVENT_SET_CALLBACK = 2, }; -struct CryptoErrorCodes { +static struct CryptoErrorCodes { jint cryptoErrorNoKey; jint cryptoErrorKeyExpired; jint cryptoErrorResourceBusy; } gCryptoErrorCodes; +static struct CodecActionCodes { + jint codecActionTransient; + jint codecActionRecoverable; +} gCodecActionCodes; + struct fields_t { jfieldID context; jmethodID postEventFromNativeID; @@ -101,10 +106,11 @@ JMediaCodec::JMediaCodec( PRIORITY_FOREGROUND); if (nameIsType) { - mCodec = MediaCodec::CreateByType(mLooper, name, encoder); + mCodec = MediaCodec::CreateByType(mLooper, name, encoder, &mInitStatus); } else { - mCodec = MediaCodec::CreateByComponentName(mLooper, name); + mCodec = MediaCodec::CreateByComponentName(mLooper, name, &mInitStatus); } + CHECK((mCodec != NULL) != (mInitStatus != OK)); } void JMediaCodec::cacheJavaObjects(JNIEnv *env) { @@ -147,7 +153,7 @@ void JMediaCodec::cacheJavaObjects(JNIEnv *env) { } status_t JMediaCodec::initCheck() const { - return mCodec != NULL ? OK : NO_INIT; + return mInitStatus; } void JMediaCodec::registerSelf() { @@ -158,6 +164,7 @@ void JMediaCodec::release() { if (mCodec != NULL) { mCodec->release(); mCodec.clear(); + mInitStatus = NO_INIT; } if (mLooper != NULL) { @@ -554,6 +561,34 @@ void JMediaCodec::setVideoScalingMode(int mode) { } } +static jthrowable createCodecException( + JNIEnv *env, status_t err, int32_t actionCode, const char *msg = NULL) { + ScopedLocalRef<jclass> clazz( + env, env->FindClass("android/media/MediaCodec$CodecException")); + CHECK(clazz.get() != NULL); + + const jmethodID ctor = env->GetMethodID(clazz.get(), "<init>", "(IILjava/lang/String;)V"); + CHECK(ctor != NULL); + + ScopedLocalRef<jstring> msgObj( + env, env->NewStringUTF(msg != NULL ? msg : String8::format("Error %#x", err))); + + // translate action code to Java equivalent + switch (actionCode) { + case ACTION_CODE_TRANSIENT: + actionCode = gCodecActionCodes.codecActionTransient; + break; + case ACTION_CODE_RECOVERABLE: + actionCode = gCodecActionCodes.codecActionRecoverable; + break; + default: + actionCode = 0; // everything else is fatal + break; + } + + return (jthrowable)env->NewObject(clazz.get(), ctor, err, actionCode, msgObj.get()); +} + void JMediaCodec::handleCallback(const sp<AMessage> &msg) { int32_t arg1, arg2 = 0; jobject obj = NULL; @@ -605,19 +640,8 @@ void JMediaCodec::handleCallback(const sp<AMessage> &msg) { CHECK(msg->findInt32("err", &err)); CHECK(msg->findInt32("actionCode", &actionCode)); - // use Integer object to pass the action code - ScopedLocalRef<jclass> clazz( - env, env->FindClass("android/media/MediaCodec$CodecException")); - jmethodID ctor = env->GetMethodID(clazz.get(), "<init>", "(IILjava/lang/String;)V"); - - AString str; - const char *detail = "Unknown error"; - if (msg->findString("detail", &str)) { - detail = str.c_str(); - } - jstring msgObj = env->NewStringUTF(detail); - - obj = env->NewObject(clazz.get(), ctor, err, actionCode, msgObj); + // note that DRM errors could conceivably alias into a CodecException + obj = (jobject)createCodecException(env, err, actionCode); if (obj == NULL) { if (env->ExceptionCheck()) { @@ -705,6 +729,11 @@ static void android_media_MediaCodec_release(JNIEnv *env, jobject thiz) { setMediaCodec(env, thiz, NULL); } +static void throwCodecException(JNIEnv *env, status_t err, int32_t actionCode, const char *msg) { + jthrowable exception = createCodecException(env, err, actionCode, msg); + env->Throw(exception); +} + static void throwCryptoException(JNIEnv *env, status_t err, const char *msg) { ScopedLocalRef<jclass> clazz( env, env->FindClass("android/media/MediaCodec$CryptoException")); @@ -716,7 +745,7 @@ static void throwCryptoException(JNIEnv *env, status_t err, const char *msg) { jstring msgObj = env->NewStringUTF(msg != NULL ? msg : "Unknown Error"); - /* translate OS errors to Java API CryptoException errorCodes */ + /* translate OS errors to Java API CryptoException errorCodes (which are positive) */ switch (err) { case ERROR_DRM_NO_LICENSE: err = gCryptoErrorCodes.cryptoErrorNoKey; @@ -727,7 +756,7 @@ static void throwCryptoException(JNIEnv *env, status_t err, const char *msg) { case ERROR_DRM_RESOURCE_BUSY: err = gCryptoErrorCodes.cryptoErrorResourceBusy; break; - default: + default: /* Other negative DRM error codes go out as is. */ break; } @@ -738,13 +767,8 @@ static void throwCryptoException(JNIEnv *env, status_t err, const char *msg) { } static jint throwExceptionAsNecessary( - JNIEnv *env, status_t err, const char *msg = NULL) { - if (err >= ERROR_DRM_VENDOR_MIN && err <= ERROR_DRM_VENDOR_MAX) { - // We'll throw our custom MediaCodec.CryptoException - throwCryptoException(env, err, msg); - return 0; - } - + JNIEnv *env, status_t err, int32_t actionCode = ACTION_CODE_FATAL, + const char *msg = NULL) { switch (err) { case OK: return 0; @@ -758,20 +782,18 @@ static jint throwExceptionAsNecessary( case INFO_OUTPUT_BUFFERS_CHANGED: return DEQUEUE_INFO_OUTPUT_BUFFERS_CHANGED; - case ERROR_DRM_NO_LICENSE: - case ERROR_DRM_LICENSE_EXPIRED: - case ERROR_DRM_RESOURCE_BUSY: - throwCryptoException(env, err, msg); - break; + case INVALID_OPERATION: + jniThrowException(env, "java/lang/IllegalStateException", msg); + return 0; default: - { - jniThrowException(env, "java/lang/IllegalStateException", msg); - break; - } + if (isCryptoError(err)) { + throwCryptoException(env, err, msg); + return 0; + } + throwCodecException(env, err, actionCode, msg); + return 0; } - - return 0; } static void android_media_MediaCodec_native_setCallback( @@ -781,7 +803,7 @@ static void android_media_MediaCodec_native_setCallback( sp<JMediaCodec> codec = getMediaCodec(env, thiz); if (codec == NULL) { - jniThrowException(env, "java/lang/IllegalStateException", NULL); + throwExceptionAsNecessary(env, INVALID_OPERATION); return; } @@ -800,7 +822,7 @@ static void android_media_MediaCodec_native_configure( sp<JMediaCodec> codec = getMediaCodec(env, thiz); if (codec == NULL) { - jniThrowException(env, "java/lang/IllegalStateException", NULL); + throwExceptionAsNecessary(env, INVALID_OPERATION); return; } @@ -842,7 +864,7 @@ static jobject android_media_MediaCodec_createInputSurface(JNIEnv* env, sp<JMediaCodec> codec = getMediaCodec(env, thiz); if (codec == NULL) { - jniThrowException(env, "java/lang/IllegalStateException", NULL); + throwExceptionAsNecessary(env, INVALID_OPERATION); return NULL; } @@ -865,13 +887,13 @@ static void android_media_MediaCodec_start(JNIEnv *env, jobject thiz) { sp<JMediaCodec> codec = getMediaCodec(env, thiz); if (codec == NULL) { - jniThrowException(env, "java/lang/IllegalStateException", "no codec found"); + throwExceptionAsNecessary(env, INVALID_OPERATION); return; } status_t err = codec->start(); - throwExceptionAsNecessary(env, err, "start failed"); + throwExceptionAsNecessary(env, err, ACTION_CODE_FATAL, "start failed"); } static void android_media_MediaCodec_stop(JNIEnv *env, jobject thiz) { @@ -880,7 +902,7 @@ static void android_media_MediaCodec_stop(JNIEnv *env, jobject thiz) { sp<JMediaCodec> codec = getMediaCodec(env, thiz); if (codec == NULL) { - jniThrowException(env, "java/lang/IllegalStateException", NULL); + throwExceptionAsNecessary(env, INVALID_OPERATION); return; } @@ -895,8 +917,7 @@ static void android_media_MediaCodec_reset(JNIEnv *env, jobject thiz) { sp<JMediaCodec> codec = getMediaCodec(env, thiz); if (codec == NULL) { - // should never be here - jniThrowException(env, "java/lang/IllegalStateException", NULL); + throwExceptionAsNecessary(env, INVALID_OPERATION); return; } @@ -904,7 +925,10 @@ static void android_media_MediaCodec_reset(JNIEnv *env, jobject thiz) { if (err != OK) { // treat all errors as fatal for now, though resource not available // errors could be treated as transient. - err = 0x80000000; + // we also should avoid sending INVALID_OPERATION here due to + // the transitory nature of reset(), it should not inadvertently + // trigger an IllegalStateException. + err = UNKNOWN_ERROR; } throwExceptionAsNecessary(env, err); } @@ -915,7 +939,7 @@ static void android_media_MediaCodec_flush(JNIEnv *env, jobject thiz) { sp<JMediaCodec> codec = getMediaCodec(env, thiz); if (codec == NULL) { - jniThrowException(env, "java/lang/IllegalStateException", NULL); + throwExceptionAsNecessary(env, INVALID_OPERATION); return; } @@ -937,7 +961,7 @@ static void android_media_MediaCodec_queueInputBuffer( sp<JMediaCodec> codec = getMediaCodec(env, thiz); if (codec == NULL) { - jniThrowException(env, "java/lang/IllegalStateException", NULL); + throwExceptionAsNecessary(env, INVALID_OPERATION); return; } @@ -947,7 +971,7 @@ static void android_media_MediaCodec_queueInputBuffer( index, offset, size, timestampUs, flags, &errorDetailMsg); throwExceptionAsNecessary( - env, err, errorDetailMsg.empty() ? NULL : errorDetailMsg.c_str()); + env, err, ACTION_CODE_FATAL, errorDetailMsg.empty() ? NULL : errorDetailMsg.c_str()); } static void android_media_MediaCodec_queueSecureInputBuffer( @@ -963,7 +987,7 @@ static void android_media_MediaCodec_queueSecureInputBuffer( sp<JMediaCodec> codec = getMediaCodec(env, thiz); if (codec == NULL) { - jniThrowException(env, "java/lang/IllegalStateException", NULL); + throwExceptionAsNecessary(env, INVALID_OPERATION); return; } @@ -1005,7 +1029,7 @@ static void android_media_MediaCodec_queueSecureInputBuffer( err = -ERANGE; // subSamples array may silently overflow if number of samples are too large. Use // INT32_MAX as maximum allocation size may be less than SIZE_MAX on some platforms - } else if ( CC_UNLIKELY(numSubSamples >= INT32_MAX / sizeof(*subSamples)) ) { + } else if ( CC_UNLIKELY(numSubSamples >= (signed)(INT32_MAX / sizeof(*subSamples))) ) { err = -EINVAL; } else { jboolean isCopy; @@ -1089,7 +1113,7 @@ static void android_media_MediaCodec_queueSecureInputBuffer( subSamples = NULL; throwExceptionAsNecessary( - env, err, errorDetailMsg.empty() ? NULL : errorDetailMsg.c_str()); + env, err, ACTION_CODE_FATAL, errorDetailMsg.empty() ? NULL : errorDetailMsg.c_str()); } static jint android_media_MediaCodec_dequeueInputBuffer( @@ -1099,7 +1123,7 @@ static jint android_media_MediaCodec_dequeueInputBuffer( sp<JMediaCodec> codec = getMediaCodec(env, thiz); if (codec == NULL) { - jniThrowException(env, "java/lang/IllegalStateException", NULL); + throwExceptionAsNecessary(env, INVALID_OPERATION); return -1; } @@ -1120,7 +1144,7 @@ static jint android_media_MediaCodec_dequeueOutputBuffer( sp<JMediaCodec> codec = getMediaCodec(env, thiz); if (codec == NULL) { - jniThrowException(env, "java/lang/IllegalStateException", NULL); + throwExceptionAsNecessary(env, INVALID_OPERATION); return 0; } @@ -1143,7 +1167,7 @@ static void android_media_MediaCodec_releaseOutputBuffer( sp<JMediaCodec> codec = getMediaCodec(env, thiz); if (codec == NULL) { - jniThrowException(env, "java/lang/IllegalStateException", NULL); + throwExceptionAsNecessary(env, INVALID_OPERATION); return; } @@ -1158,7 +1182,7 @@ static void android_media_MediaCodec_signalEndOfInputStream(JNIEnv* env, sp<JMediaCodec> codec = getMediaCodec(env, thiz); if (codec == NULL) { - jniThrowException(env, "java/lang/IllegalStateException", NULL); + throwExceptionAsNecessary(env, INVALID_OPERATION); return; } @@ -1174,7 +1198,7 @@ static jobject android_media_MediaCodec_getFormatNative( sp<JMediaCodec> codec = getMediaCodec(env, thiz); if (codec == NULL) { - jniThrowException(env, "java/lang/IllegalStateException", NULL); + throwExceptionAsNecessary(env, INVALID_OPERATION); return NULL; } @@ -1197,7 +1221,7 @@ static jobject android_media_MediaCodec_getOutputFormatForIndexNative( sp<JMediaCodec> codec = getMediaCodec(env, thiz); if (codec == NULL) { - jniThrowException(env, "java/lang/IllegalStateException", NULL); + throwExceptionAsNecessary(env, INVALID_OPERATION); return NULL; } @@ -1220,7 +1244,7 @@ static jobjectArray android_media_MediaCodec_getBuffers( sp<JMediaCodec> codec = getMediaCodec(env, thiz); if (codec == NULL) { - jniThrowException(env, "java/lang/IllegalStateException", NULL); + throwExceptionAsNecessary(env, INVALID_OPERATION); return NULL; } @@ -1246,7 +1270,7 @@ static jobject android_media_MediaCodec_getBuffer( sp<JMediaCodec> codec = getMediaCodec(env, thiz); if (codec == NULL) { - jniThrowException(env, "java/lang/IllegalStateException", NULL); + throwExceptionAsNecessary(env, INVALID_OPERATION); return NULL; } @@ -1272,7 +1296,7 @@ static jobject android_media_MediaCodec_getImage( sp<JMediaCodec> codec = getMediaCodec(env, thiz); if (codec == NULL) { - jniThrowException(env, "java/lang/IllegalStateException", NULL); + throwExceptionAsNecessary(env, INVALID_OPERATION); return NULL; } @@ -1298,7 +1322,7 @@ static jobject android_media_MediaCodec_getName( sp<JMediaCodec> codec = getMediaCodec(env, thiz); if (codec == NULL) { - jniThrowException(env, "java/lang/IllegalStateException", NULL); + throwExceptionAsNecessary(env, INVALID_OPERATION); return NULL; } @@ -1321,7 +1345,7 @@ static void android_media_MediaCodec_setParameters( sp<JMediaCodec> codec = getMediaCodec(env, thiz); if (codec == NULL) { - jniThrowException(env, "java/lang/IllegalStateException", NULL); + throwExceptionAsNecessary(env, INVALID_OPERATION); return; } @@ -1340,7 +1364,7 @@ static void android_media_MediaCodec_setVideoScalingMode( sp<JMediaCodec> codec = getMediaCodec(env, thiz); if (codec == NULL) { - jniThrowException(env, "java/lang/IllegalStateException", NULL); + throwExceptionAsNecessary(env, INVALID_OPERATION); return; } @@ -1409,13 +1433,25 @@ static void android_media_MediaCodec_native_init(JNIEnv *env) { CHECK(field != NULL); gCryptoErrorCodes.cryptoErrorResourceBusy = env->GetStaticIntField(clazz.get(), field); + + clazz.reset(env->FindClass("android/media/MediaCodec$CodecException")); + CHECK(clazz.get() != NULL); + field = env->GetStaticFieldID(clazz.get(), "ACTION_TRANSIENT", "I"); + CHECK(field != NULL); + gCodecActionCodes.codecActionTransient = + env->GetStaticIntField(clazz.get(), field); + + field = env->GetStaticFieldID(clazz.get(), "ACTION_RECOVERABLE", "I"); + CHECK(field != NULL); + gCodecActionCodes.codecActionRecoverable = + env->GetStaticIntField(clazz.get(), field); } static void android_media_MediaCodec_native_setup( JNIEnv *env, jobject thiz, jstring name, jboolean nameIsType, jboolean encoder) { if (name == NULL) { - jniThrowException(env, "java/lang/IllegalArgumentException", NULL); + jniThrowException(env, "java/lang/NullPointerException", NULL); return; } @@ -1427,19 +1463,23 @@ static void android_media_MediaCodec_native_setup( sp<JMediaCodec> codec = new JMediaCodec(env, thiz, tmp, nameIsType, encoder); - status_t err = codec->initCheck(); - - env->ReleaseStringUTFChars(name, tmp); - tmp = NULL; - - if (err != OK) { - jniThrowException( - env, - "java/io/IOException", - "Failed to allocate component instance"); + const status_t err = codec->initCheck(); + if (err == NAME_NOT_FOUND) { + // fail and do not try again. + jniThrowException(env, "java/lang/IllegalArgumentException", + String8::format("Failed to initialize %s, error %#x", tmp, err)); + env->ReleaseStringUTFChars(name, tmp); + return; + } else if (err != OK) { + // believed possible to try again + jniThrowException(env, "java/io/IOException", + String8::format("Failed to find matching codec %s, error %#x", tmp, err)); + env->ReleaseStringUTFChars(name, tmp); return; } + env->ReleaseStringUTFChars(name, tmp); + codec->registerSelf(); setMediaCodec(env,thiz, codec); |