summaryrefslogtreecommitdiffstats
path: root/media/jni/android_media_MediaCodec.cpp
diff options
context:
space:
mode:
authorAndy Hung <hunga@google.com>2014-07-30 15:48:21 -0700
committerAndy Hung <hunga@google.com>2014-08-08 16:15:07 +0000
commit5f9aa0bcea8be860fdf5a35476435616cb5f44f3 (patch)
treebf3a6a08cd86f9ec47b934c7991401f89475deba /media/jni/android_media_MediaCodec.cpp
parentfa387ad6cfa996d09050f8f6c39eaa0537893d20 (diff)
downloadframeworks_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.cpp190
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);