diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java index 3d5f6bc9ac713..8a742b75cdf8e 100644 --- a/media/java/android/media/MediaCodec.java +++ b/media/java/android/media/MediaCodec.java @@ -1602,7 +1602,9 @@ final public class MediaCodec { private EventHandler mCallbackHandler; private Callback mCallback; private OnFrameRenderedListener mOnFrameRenderedListener; - private Object mListenerLock = new Object(); + private final Object mListenerLock = new Object(); + private MediaCodecInfo mCodecInfo; + private final Object mCodecInfoLock = new Object(); private static final int EVENT_CALLBACK = 1; private static final int EVENT_SET_CALLBACK = 2; @@ -3469,9 +3471,25 @@ final public class MediaCodec { */ @NonNull public MediaCodecInfo getCodecInfo() { - return MediaCodecList.getInfoFor(getName()); + // Get the codec name first. If the codec is already released, + // IllegalStateException will be thrown here. + String name = getName(); + synchronized (mCodecInfoLock) { + if (mCodecInfo == null) { + // Get the codec info for this codec itself first. Only initialize + // the full codec list if this somehow fails because it can be slow. + mCodecInfo = getOwnCodecInfo(); + if (mCodecInfo == null) { + mCodecInfo = MediaCodecList.getInfoFor(name); + } + } + return mCodecInfo; + } } + @NonNull + private native final MediaCodecInfo getOwnCodecInfo(); + @NonNull private native final ByteBuffer[] getBuffers(boolean input); diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java index 3dd6879e44823..2a601f9b4cdb7 100644 --- a/media/java/android/media/MediaCodecInfo.java +++ b/media/java/android/media/MediaCodecInfo.java @@ -829,14 +829,24 @@ public final class MediaCodecInfo { /** @hide */ public CodecCapabilities dup() { - return new CodecCapabilities( - // clone writable arrays - Arrays.copyOf(profileLevels, profileLevels.length), - Arrays.copyOf(colorFormats, colorFormats.length), - isEncoder(), - mFlagsVerified, - mDefaultFormat, - mCapabilitiesInfo); + CodecCapabilities caps = new CodecCapabilities(); + + // profileLevels and colorFormats may be modified by client. + caps.profileLevels = Arrays.copyOf(profileLevels, profileLevels.length); + caps.colorFormats = Arrays.copyOf(colorFormats, colorFormats.length); + + caps.mMime = mMime; + caps.mMaxSupportedInstances = mMaxSupportedInstances; + caps.mFlagsRequired = mFlagsRequired; + caps.mFlagsSupported = mFlagsSupported; + caps.mFlagsVerified = mFlagsVerified; + caps.mAudioCaps = mAudioCaps; + caps.mVideoCaps = mVideoCaps; + caps.mEncoderCaps = mEncoderCaps; + caps.mDefaultFormat = mDefaultFormat; + caps.mCapabilitiesInfo = mCapabilitiesInfo; + + return caps; } /** @@ -898,13 +908,13 @@ public final class MediaCodecInfo { if (mMime.toLowerCase().startsWith("audio/")) { mAudioCaps = AudioCapabilities.create(info, this); - mAudioCaps.setDefaultFormat(mDefaultFormat); + mAudioCaps.getDefaultFormat(mDefaultFormat); } else if (mMime.toLowerCase().startsWith("video/")) { mVideoCaps = VideoCapabilities.create(info, this); } if (encoder) { mEncoderCaps = EncoderCapabilities.create(info, this); - mEncoderCaps.setDefaultFormat(mDefaultFormat); + mEncoderCaps.getDefaultFormat(mDefaultFormat); } final Map global = MediaCodecList.getGlobalSettings(); @@ -990,8 +1000,7 @@ public final class MediaCodecInfo { return caps; } - /** @hide */ - public void init(MediaFormat info, CodecCapabilities parent) { + private void init(MediaFormat info, CodecCapabilities parent) { mParent = parent; initWithPlatformLimits(); applyLevelLimits(); @@ -1171,7 +1180,7 @@ public final class MediaCodecInfo { } /** @hide */ - public void setDefaultFormat(MediaFormat format) { + public void getDefaultFormat(MediaFormat format) { // report settings that have only a single choice if (mBitrateRange.getLower().equals(mBitrateRange.getUpper())) { format.setInteger(MediaFormat.KEY_BIT_RATE, mBitrateRange.getLower()); @@ -1585,8 +1594,7 @@ public final class MediaCodecInfo { return caps; } - /** @hide */ - public void init(MediaFormat info, CodecCapabilities parent) { + private void init(MediaFormat info, CodecCapabilities parent) { mParent = parent; initWithPlatformLimits(); applyLevelLimits(); @@ -2707,8 +2715,7 @@ public final class MediaCodecInfo { return caps; } - /** @hide */ - public void init(MediaFormat info, CodecCapabilities parent) { + private void init(MediaFormat info, CodecCapabilities parent) { // no support for complexity or quality yet mParent = parent; mComplexityRange = Range.create(0, 0); @@ -2789,7 +2796,7 @@ public final class MediaCodecInfo { } /** @hide */ - public void setDefaultFormat(MediaFormat format) { + public void getDefaultFormat(MediaFormat format) { // don't list trivial quality/complexity as default for now if (!mQualityRange.getUpper().equals(mQualityRange.getLower()) && mDefaultQuality != null) { diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp index a1022c0829099..000317e688dab 100644 --- a/media/jni/android_media_MediaCodec.cpp +++ b/media/jni/android_media_MediaCodec.cpp @@ -29,6 +29,7 @@ #include "android_util_Binder.h" #include "jni.h" #include +#include #include @@ -98,6 +99,13 @@ static struct { jint AesCbc; } gCryptoModes; +static struct { + jclass capsClazz; + jmethodID capsCtorId; + jclass profileLevelClazz; + jfieldID profileField; + jfieldID levelField; +} gCodecInfo; struct fields_t { jfieldID context; @@ -625,6 +633,103 @@ status_t JMediaCodec::getName(JNIEnv *env, jstring *nameStr) const { return OK; } +static jobject getCodecCapabilitiesObject( + JNIEnv *env, const char *mime, bool isEncoder, + const sp &capabilities) { + Vector profileLevels; + Vector colorFormats; + + sp defaultFormat = new AMessage(); + defaultFormat->setString("mime", mime); + + capabilities->getSupportedColorFormats(&colorFormats); + capabilities->getSupportedProfileLevels(&profileLevels); + uint32_t flags = capabilities->getFlags(); + sp details = capabilities->getDetails(); + + jobject defaultFormatObj = NULL; + if (ConvertMessageToMap(env, defaultFormat, &defaultFormatObj)) { + return NULL; + } + ScopedLocalRef defaultFormatRef(env, defaultFormatObj); + + jobject detailsObj = NULL; + if (ConvertMessageToMap(env, details, &detailsObj)) { + return NULL; + } + ScopedLocalRef detailsRef(env, detailsObj); + + ScopedLocalRef profileLevelArray(env, env->NewObjectArray( + profileLevels.size(), gCodecInfo.profileLevelClazz, NULL)); + + for (size_t i = 0; i < profileLevels.size(); ++i) { + const MediaCodecInfo::ProfileLevel &src = profileLevels.itemAt(i); + + ScopedLocalRef srcRef(env, env->AllocObject( + gCodecInfo.profileLevelClazz)); + + env->SetIntField(srcRef.get(), gCodecInfo.profileField, src.mProfile); + env->SetIntField(srcRef.get(), gCodecInfo.levelField, src.mLevel); + + env->SetObjectArrayElement(profileLevelArray.get(), i, srcRef.get()); + } + + ScopedLocalRef colorFormatsArray( + env, env->NewIntArray(colorFormats.size())); + for (size_t i = 0; i < colorFormats.size(); ++i) { + jint val = colorFormats.itemAt(i); + env->SetIntArrayRegion(colorFormatsArray.get(), i, 1, &val); + } + + return env->NewObject( + gCodecInfo.capsClazz, gCodecInfo.capsCtorId, + profileLevelArray.get(), colorFormatsArray.get(), isEncoder, flags, + defaultFormatRef.get(), detailsRef.get()); +} + +status_t JMediaCodec::getCodecInfo(JNIEnv *env, jobject *codecInfoObject) const { + sp codecInfo; + + status_t err = mCodec->getCodecInfo(&codecInfo); + + if (err != OK) { + return err; + } + + ScopedLocalRef nameObject(env, + env->NewStringUTF(codecInfo->getCodecName())); + + bool isEncoder = codecInfo->isEncoder(); + + Vector mimes; + codecInfo->getSupportedMimes(&mimes); + + ScopedLocalRef capsArrayObj(env, + env->NewObjectArray(mimes.size(), gCodecInfo.capsClazz, NULL)); + + for (size_t i = 0; i < mimes.size(); i++) { + const sp caps = + codecInfo->getCapabilitiesFor(mimes[i].c_str()); + + ScopedLocalRef capsObj(env, getCodecCapabilitiesObject( + env, mimes[i].c_str(), isEncoder, caps)); + + env->SetObjectArrayElement(capsArrayObj.get(), i, capsObj.get()); + } + + ScopedLocalRef codecInfoClazz(env, + env->FindClass("android/media/MediaCodecInfo")); + CHECK(codecInfoClazz.get() != NULL); + + jmethodID codecInfoCtorID = env->GetMethodID(codecInfoClazz.get(), "", + "(Ljava/lang/String;Z[Landroid/media/MediaCodecInfo$CodecCapabilities;)V"); + + *codecInfoObject = env->NewObject(codecInfoClazz.get(), codecInfoCtorID, + nameObject.get(), isEncoder, capsArrayObj.get()); + + return OK; +} + status_t JMediaCodec::getMetrics(JNIEnv *, MediaAnalyticsItem * &reply) const { status_t status = mCodec->getMetrics(reply); @@ -1665,6 +1770,29 @@ static jobject android_media_MediaCodec_getName( return NULL; } +static jobject android_media_MediaCodec_getOwnCodecInfo( + JNIEnv *env, jobject thiz) { + ALOGV("android_media_MediaCodec_getOwnCodecInfo"); + + sp codec = getMediaCodec(env, thiz); + + if (codec == NULL) { + throwExceptionAsNecessary(env, INVALID_OPERATION); + return NULL; + } + + jobject codecInfoObj; + status_t err = codec->getCodecInfo(env, &codecInfoObj); + + if (err == OK) { + return codecInfoObj; + } + + throwExceptionAsNecessary(env, err); + + return NULL; +} + static jobject android_media_MediaCodec_native_getMetrics(JNIEnv *env, jobject thiz) { @@ -1877,6 +2005,28 @@ static void android_media_MediaCodec_native_init(JNIEnv *env) { field = env->GetFieldID(clazz.get(), "mPersistentObject", "J"); CHECK(field != NULL); gPersistentSurfaceClassInfo.mPersistentObject = field; + + clazz.reset(env->FindClass("android/media/MediaCodecInfo$CodecCapabilities")); + CHECK(clazz.get() != NULL); + gCodecInfo.capsClazz = (jclass)env->NewGlobalRef(clazz.get()); + + method = env->GetMethodID(clazz.get(), "", + "([Landroid/media/MediaCodecInfo$CodecProfileLevel;[IZI" + "Ljava/util/Map;Ljava/util/Map;)V"); + CHECK(method != NULL); + gCodecInfo.capsCtorId = method; + + clazz.reset(env->FindClass("android/media/MediaCodecInfo$CodecProfileLevel")); + CHECK(clazz.get() != NULL); + gCodecInfo.profileLevelClazz = (jclass)env->NewGlobalRef(clazz.get()); + + field = env->GetFieldID(clazz.get(), "profile", "I"); + CHECK(field != NULL); + gCodecInfo.profileField = field; + + field = env->GetFieldID(clazz.get(), "level", "I"); + CHECK(field != NULL); + gCodecInfo.levelField = field; } static void android_media_MediaCodec_native_setup( @@ -2002,6 +2152,9 @@ static const JNINativeMethod gMethods[] = { { "getName", "()Ljava/lang/String;", (void *)android_media_MediaCodec_getName }, + { "getOwnCodecInfo", "()Landroid/media/MediaCodecInfo;", + (void *)android_media_MediaCodec_getOwnCodecInfo }, + { "native_getMetrics", "()Landroid/os/PersistableBundle;", (void *)android_media_MediaCodec_native_getMetrics}, diff --git a/media/jni/android_media_MediaCodec.h b/media/jni/android_media_MediaCodec.h index 2ec8703adc831..985f55a89254c 100644 --- a/media/jni/android_media_MediaCodec.h +++ b/media/jni/android_media_MediaCodec.h @@ -120,6 +120,8 @@ struct JMediaCodec : public AHandler { status_t getName(JNIEnv *env, jstring *name) const; + status_t getCodecInfo(JNIEnv *env, jobject *codecInfo) const; + status_t getMetrics(JNIEnv *env, MediaAnalyticsItem * &reply) const; status_t setParameters(const sp ¶ms);