Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a0b72a6c authored by Chong Zhang's avatar Chong Zhang
Browse files

fix slow get caps -- DO NOT MERGE

When get caps is called on an MediaCodec object, try to
use the codec info for that MediaCodec first as it should
be available during codec construction. Only when that
somehow failed go and initialize the full MediaCodecList,
as building the entire list could be very slow.

And when cloning the CodecCapabilities from MediaCodecInfo,
copy the fields directly and the fields (other than the ones
modifiable by client) are immutable after construction.

bug: 74073607

Test:
CTS media post submit:
cts-tradefed run cts-dev --module CtsMediaTestCases --compatibility:module-arg CtsMediaTestCases:include-annotation:android.platform.test.annotations.RequiresDevice

Change-Id: I4ae5f2431da8528e1eca093c5cfb98abcb4a5bbf
(cherry picked from commit 90d7304a)
parent dbeb6bc3
Loading
Loading
Loading
Loading
+20 −2
Original line number Diff line number Diff line
@@ -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,8 +3471,24 @@ 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);
+25 −18
Original line number Diff line number Diff line
@@ -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<String, Object> 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) {
+153 −0
Original line number Diff line number Diff line
@@ -29,6 +29,7 @@
#include "android_util_Binder.h"
#include "jni.h"
#include <nativehelper/JNIHelp.h>
#include <nativehelper/ScopedLocalRef.h>

#include <android/hardware/cas/native/1.0/IDescrambler.h>

@@ -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<MediaCodecInfo::Capabilities> &capabilities) {
    Vector<MediaCodecInfo::ProfileLevel> profileLevels;
    Vector<uint32_t> colorFormats;

    sp<AMessage> defaultFormat = new AMessage();
    defaultFormat->setString("mime", mime);

    capabilities->getSupportedColorFormats(&colorFormats);
    capabilities->getSupportedProfileLevels(&profileLevels);
    uint32_t flags = capabilities->getFlags();
    sp<AMessage> details = capabilities->getDetails();

    jobject defaultFormatObj = NULL;
    if (ConvertMessageToMap(env, defaultFormat, &defaultFormatObj)) {
        return NULL;
    }
    ScopedLocalRef<jobject> defaultFormatRef(env, defaultFormatObj);

    jobject detailsObj = NULL;
    if (ConvertMessageToMap(env, details, &detailsObj)) {
        return NULL;
    }
    ScopedLocalRef<jobject> detailsRef(env, detailsObj);

    ScopedLocalRef<jobjectArray> 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<jobject> 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<jintArray> 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<MediaCodecInfo> codecInfo;

    status_t err = mCodec->getCodecInfo(&codecInfo);

    if (err != OK) {
        return err;
    }

    ScopedLocalRef<jstring> nameObject(env,
            env->NewStringUTF(codecInfo->getCodecName()));

    bool isEncoder = codecInfo->isEncoder();

    Vector<AString> mimes;
    codecInfo->getSupportedMimes(&mimes);

    ScopedLocalRef<jobjectArray> capsArrayObj(env,
        env->NewObjectArray(mimes.size(), gCodecInfo.capsClazz, NULL));

    for (size_t i = 0; i < mimes.size(); i++) {
        const sp<MediaCodecInfo::Capabilities> caps =
                codecInfo->getCapabilitiesFor(mimes[i].c_str());

        ScopedLocalRef<jobject> capsObj(env, getCodecCapabilitiesObject(
                env, mimes[i].c_str(), isEncoder, caps));

        env->SetObjectArrayElement(capsArrayObj.get(), i, capsObj.get());
    }

    ScopedLocalRef<jclass> codecInfoClazz(env,
            env->FindClass("android/media/MediaCodecInfo"));
    CHECK(codecInfoClazz.get() != NULL);

    jmethodID codecInfoCtorID = env->GetMethodID(codecInfoClazz.get(), "<init>",
            "(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<JMediaCodec> 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(), "<init>",
            "([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},

+2 −0
Original line number Diff line number Diff line
@@ -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<AMessage> &params);