diff options
| -rw-r--r-- | media/java/android/media/MediaCodecInfo.java | 1099 | ||||
| -rw-r--r-- | media/jni/android_media_CodecCapabilities.cpp | 610 | ||||
| -rw-r--r-- | media/jni/android_media_CodecCapabilities.h | 47 | ||||
| -rw-r--r-- | media/jni/android_media_MediaCodec.cpp | 39 | ||||
| -rw-r--r-- | media/jni/android_media_MediaCodecList.cpp | 154 |
5 files changed, 504 insertions, 1445 deletions
diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java index 69b26fd9e073..06d428212c34 100644 --- a/media/java/android/media/MediaCodecInfo.java +++ b/media/java/android/media/MediaCodecInfo.java @@ -244,7 +244,12 @@ public final class MediaCodecInfo { * {@link MediaCodecInfo#getCapabilitiesForType getCapabilitiesForType()}, passing a MIME type. */ public static final class CodecCapabilities { - private static final String TAG = "CodecCapabilities"; + public CodecCapabilities() { + } + + // CLASSIFICATION + private String mMime; + private int mMaxSupportedInstances; // LEGACY FIELDS @@ -622,6 +627,12 @@ public final class MediaCodecInfo { */ public int[] colorFormats; // NOTE this array is modifiable by user + // FEATURES + + private int mFlagsSupported; + private int mFlagsRequired; + private int mFlagsVerified; + /** * <b>video decoder only</b>: codec supports seamless resolution changes. */ @@ -811,680 +822,122 @@ public final class MediaCodecInfo { @FlaggedApi(FLAG_NULL_OUTPUT_SURFACE) public static final String FEATURE_DetachedSurface = "detached-surface"; - /** package private */ interface CodecCapsIntf { - public CodecCapsIntf dup(); - - public boolean isFeatureSupported(String name); - - public boolean isFeatureRequired(String name); - - public boolean isFormatSupported(MediaFormat format); - - public MediaFormat getDefaultFormat(); - - public String getMimeType(); - - public int getMaxSupportedInstances(); - - public AudioCapabilities getAudioCapabilities(); - - public VideoCapabilities getVideoCapabilities(); - - public EncoderCapabilities getEncoderCapabilities(); - - public boolean isRegular(); - - public CodecProfileLevel[] getProfileLevels(); - - public int[] getColorFormats(); + /** + * Query codec feature capabilities. + * <p> + * These features are supported to be used by the codec. These + * include optional features that can be turned on, as well as + * features that are always on. + */ + public final boolean isFeatureSupported(String name) { + return checkFeature(name, mFlagsSupported); } - /* package private */ static final class CodecCapsLegacyImpl implements CodecCapsIntf { - // errors while reading profile levels - accessed from sister capabilities - int mError; - - private CodecProfileLevel[] mProfileLevels; - private int[] mColorFormats; - - // CLASSIFICATION - private String mMime; - private int mMaxSupportedInstances; - - // FEATURES - private int mFlagsSupported; - private int mFlagsRequired; - private int mFlagsVerified; - - // NEW-STYLE CAPABILITIES - private AudioCapabilities mAudioCaps; - private VideoCapabilities mVideoCaps; - private EncoderCapabilities mEncoderCaps; - private MediaFormat mDefaultFormat; - - private MediaFormat mCapabilitiesInfo; - - public CodecProfileLevel[] getProfileLevels() { - return mProfileLevels; - } - - public int[] getColorFormats() { - return mColorFormats; - } - - public CodecCapsLegacyImpl() {} - - public CodecCapsLegacyImpl dup() { - CodecCapsLegacyImpl caps = new CodecCapsLegacyImpl(); - - caps.mProfileLevels = Arrays.copyOf(mProfileLevels, mProfileLevels.length); - caps.mColorFormats = Arrays.copyOf(mColorFormats, mColorFormats.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; - } - - public final boolean isFeatureSupported(String name) { - return checkFeature(name, mFlagsSupported); - } - - public final boolean isFeatureRequired(String name) { - return checkFeature(name, mFlagsRequired); - } - - // Flags are used for feature list creation so separate this into a private - // static class to delay reading the flags only when constructing the list. - private static class FeatureList { - private static Feature[] getDecoderFeatures() { - ArrayList<Feature> features = new ArrayList(); - features.add(new Feature(FEATURE_AdaptivePlayback, (1 << 0), true)); - features.add(new Feature(FEATURE_SecurePlayback, (1 << 1), false)); - features.add(new Feature(FEATURE_TunneledPlayback, (1 << 2), false)); - features.add(new Feature(FEATURE_PartialFrame, (1 << 3), false)); - features.add(new Feature(FEATURE_FrameParsing, (1 << 4), false)); - features.add(new Feature(FEATURE_MultipleFrames, (1 << 5), false)); - features.add(new Feature(FEATURE_DynamicTimestamp, (1 << 6), false)); - features.add(new Feature(FEATURE_LowLatency, (1 << 7), true)); - if (GetFlag(() -> android.media.codec.Flags.dynamicColorAspects())) { - features.add(new Feature(FEATURE_DynamicColorAspects, (1 << 8), true)); - } - if (GetFlag(() -> android.media.codec.Flags.nullOutputSurface())) { - features.add(new Feature(FEATURE_DetachedSurface, (1 << 9), true)); - } - - // feature to exclude codec from REGULAR codec list - features.add(new Feature(FEATURE_SpecialCodec, (1 << 30), false, true)); - - return features.toArray(new Feature[0]); - }; - - private static Feature[] decoderFeatures = getDecoderFeatures(); - - private static Feature[] getEncoderFeatures() { - ArrayList<Feature> features = new ArrayList(); - - features.add(new Feature(FEATURE_IntraRefresh, (1 << 0), false)); - features.add(new Feature(FEATURE_MultipleFrames, (1 << 1), false)); - features.add(new Feature(FEATURE_DynamicTimestamp, (1 << 2), false)); - features.add(new Feature(FEATURE_QpBounds, (1 << 3), false)); - features.add(new Feature(FEATURE_EncodingStatistics, (1 << 4), false)); - features.add(new Feature(FEATURE_HdrEditing, (1 << 5), false)); - if (GetFlag(() -> android.media.codec.Flags.hlgEditing())) { - features.add(new Feature(FEATURE_HlgEditing, (1 << 6), true)); - } - if (GetFlag(() -> android.media.codec.Flags.regionOfInterest())) { - features.add(new Feature(FEATURE_Roi, (1 << 7), true)); - } - - // feature to exclude codec from REGULAR codec list - features.add(new Feature(FEATURE_SpecialCodec, (1 << 30), false, true)); - - return features.toArray(new Feature[0]); - }; - - private static Feature[] encoderFeatures = getEncoderFeatures(); - - public static Feature[] getFeatures(boolean isEncoder) { - if (isEncoder) { - return encoderFeatures; - } else { - return decoderFeatures; - } - } - } - - /** @hide */ - public String[] validFeatures() { - Feature[] features = getValidFeatures(); - String[] res = new String[features.length]; - for (int i = 0; i < res.length; i++) { - if (!features[i].mInternal) { - res[i] = features[i].mName; - } - } - return res; - } - - private Feature[] getValidFeatures() { - return FeatureList.getFeatures(isEncoder()); - } - - private boolean checkFeature(String name, int flags) { - for (Feature feat: getValidFeatures()) { - if (feat.mName.equals(name)) { - return (flags & feat.mValue) != 0; - } - } - return false; - } - - public boolean isRegular() { - // regular codecs only require default features - for (Feature feat: getValidFeatures()) { - if (!feat.mDefault && isFeatureRequired(feat.mName)) { - return false; - } - } - return true; - } - - public final boolean isFormatSupported(MediaFormat format) { - final Map<String, Object> map = format.getMap(); - final String mime = (String) map.get(MediaFormat.KEY_MIME); - - // mime must match if present - if (mime != null && !mMime.equalsIgnoreCase(mime)) { - return false; - } - - // check feature support - for (Feature feat: getValidFeatures()) { - if (feat.mInternal) { - continue; - } - - Integer yesNo = (Integer) map.get(MediaFormat.KEY_FEATURE_ + feat.mName); - if (yesNo == null) { - continue; - } - if ((yesNo == 1 && !isFeatureSupported(feat.mName)) - || (yesNo == 0 && isFeatureRequired(feat.mName))) { - return false; - } - } - - Integer profile = (Integer) map.get(MediaFormat.KEY_PROFILE); - Integer level = (Integer) map.get(MediaFormat.KEY_LEVEL); - - if (profile != null) { - if (!supportsProfileLevel(profile, level)) { - return false; - } - - // If we recognize this profile, check that this format is supported by the - // highest level supported by the codec for that profile. (Ignore specified - // level beyond the above profile/level check as level is only used as a - // guidance. E.g. AVC Level 1 CIF format is supported if codec supports - // level 1.1 even though max size for Level 1 is QCIF. However, MPEG2 Simple - // Profile 1080p format is not supported even if codec supports Main Profile - // Level High, as Simple Profile does not support 1080p. - CodecCapsLegacyImpl levelCaps = null; - int maxLevel = 0; - for (CodecProfileLevel pl : mProfileLevels) { - if (pl.profile == profile && pl.level > maxLevel) { - // H.263 levels are not completely ordered: - // Level45 support only implies Level10 support - if (!mMime.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_H263) - || pl.level != CodecProfileLevel.H263Level45 - || maxLevel == CodecProfileLevel.H263Level10) { - maxLevel = pl.level; - } - } - } - levelCaps = createFromProfileLevel(mMime, profile, maxLevel); - // We must remove the profile from this format otherwise - // levelCaps.isFormatSupported will get into this same condition and loop - // forever. Furthermore, since levelCaps does not contain features and bitrate - // specific keys, keep only keys relevant for a level check. - Map<String, Object> levelCriticalFormatMap = new HashMap<>(map); - final Set<String> criticalKeys = isVideo() - ? VideoCapabilities.VideoCapsLegacyImpl.VIDEO_LEVEL_CRITICAL_FORMAT_KEYS - : isAudio() - ? AudioCapabilities.AudioCapsLegacyImpl.AUDIO_LEVEL_CRITICAL_FORMAT_KEYS - : null; - - // critical keys will always contain KEY_MIME, but should also contain others - // to be meaningful - if (criticalKeys != null && criticalKeys.size() > 1 && levelCaps != null) { - levelCriticalFormatMap.keySet().retainAll(criticalKeys); - - MediaFormat levelCriticalFormat = new MediaFormat(levelCriticalFormatMap); - if (!levelCaps.isFormatSupported(levelCriticalFormat)) { - return false; - } - } - } - if (mAudioCaps != null && !mAudioCaps.supportsFormat(format)) { - return false; - } - if (mVideoCaps != null && !mVideoCaps.supportsFormat(format)) { - return false; - } - if (mEncoderCaps != null && !mEncoderCaps.supportsFormat(format)) { - return false; - } - return true; - } - - private static boolean supportsBitrate( - Range<Integer> bitrateRange, MediaFormat format) { - Map<String, Object> map = format.getMap(); + /** + * Query codec feature requirements. + * <p> + * These features are required to be used by the codec, and as such, + * they are always turned on. + */ + public final boolean isFeatureRequired(String name) { + return checkFeature(name, mFlagsRequired); + } - // consider max bitrate over average bitrate for support - Integer maxBitrate = (Integer)map.get(MediaFormat.KEY_MAX_BIT_RATE); - Integer bitrate = (Integer)map.get(MediaFormat.KEY_BIT_RATE); - if (bitrate == null) { - bitrate = maxBitrate; - } else if (maxBitrate != null) { - bitrate = Math.max(bitrate, maxBitrate); + // Flags are used for feature list creation so separate this into a private + // static class to delay reading the flags only when constructing the list. + private static class FeatureList { + private static Feature[] getDecoderFeatures() { + ArrayList<Feature> features = new ArrayList(); + features.add(new Feature(FEATURE_AdaptivePlayback, (1 << 0), true)); + features.add(new Feature(FEATURE_SecurePlayback, (1 << 1), false)); + features.add(new Feature(FEATURE_TunneledPlayback, (1 << 2), false)); + features.add(new Feature(FEATURE_PartialFrame, (1 << 3), false)); + features.add(new Feature(FEATURE_FrameParsing, (1 << 4), false)); + features.add(new Feature(FEATURE_MultipleFrames, (1 << 5), false)); + features.add(new Feature(FEATURE_DynamicTimestamp, (1 << 6), false)); + features.add(new Feature(FEATURE_LowLatency, (1 << 7), true)); + if (GetFlag(() -> android.media.codec.Flags.dynamicColorAspects())) { + features.add(new Feature(FEATURE_DynamicColorAspects, (1 << 8), true)); } - - if (bitrate != null && bitrate > 0) { - return bitrateRange.contains(bitrate); + if (GetFlag(() -> android.media.codec.Flags.nullOutputSurface())) { + features.add(new Feature(FEATURE_DetachedSurface, (1 << 9), true)); } - return true; - } - - private boolean supportsProfileLevel(int profile, Integer level) { - for (CodecProfileLevel pl: mProfileLevels) { - if (pl.profile != profile) { - continue; - } - - // No specific level requested - if (level == null) { - return true; - } - - // AAC doesn't use levels - if (mMime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_AAC)) { - return true; - } + // feature to exclude codec from REGULAR codec list + features.add(new Feature(FEATURE_SpecialCodec, (1 << 30), false, true)); - // DTS doesn't use levels - if (mMime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_DTS) - || mMime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_DTS_HD) - || mMime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_DTS_UHD)) { - return true; - } - - // H.263 levels are not completely ordered: - // Level45 support only implies Level10 support - if (mMime.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_H263)) { - if (pl.level != level && pl.level == CodecProfileLevel.H263Level45 - && level > CodecProfileLevel.H263Level10) { - continue; - } - } + return features.toArray(new Feature[0]); + }; - // MPEG4 levels are not completely ordered: - // Level1 support only implies Level0 (and not Level0b) support - if (mMime.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_MPEG4)) { - if (pl.level != level && pl.level == CodecProfileLevel.MPEG4Level1 - && level > CodecProfileLevel.MPEG4Level0) { - continue; - } - } + private static Feature[] decoderFeatures = getDecoderFeatures(); - // HEVC levels incorporate both tiers and levels. Verify tier support. - if (mMime.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_HEVC)) { - boolean supportsHighTier = - (pl.level & CodecProfileLevel.HEVCHighTierLevels) != 0; - boolean checkingHighTier - = (level & CodecProfileLevel.HEVCHighTierLevels) != 0; - // high tier levels are only supported by other high tier levels - if (checkingHighTier && !supportsHighTier) { - continue; - } - } + private static Feature[] getEncoderFeatures() { + ArrayList<Feature> features = new ArrayList(); - if (pl.level >= level) { - // if we recognize the listed profile/level, we must also recognize the - // profile/level arguments. - if (createFromProfileLevel(mMime, profile, pl.level) != null) { - return createFromProfileLevel(mMime, profile, level) != null; - } - return true; - } + features.add(new Feature(FEATURE_IntraRefresh, (1 << 0), false)); + features.add(new Feature(FEATURE_MultipleFrames, (1 << 1), false)); + features.add(new Feature(FEATURE_DynamicTimestamp, (1 << 2), false)); + features.add(new Feature(FEATURE_QpBounds, (1 << 3), false)); + features.add(new Feature(FEATURE_EncodingStatistics, (1 << 4), false)); + features.add(new Feature(FEATURE_HdrEditing, (1 << 5), false)); + if (GetFlag(() -> android.media.codec.Flags.hlgEditing())) { + features.add(new Feature(FEATURE_HlgEditing, (1 << 6), true)); } - return false; - } - - public MediaFormat getDefaultFormat() { - return mDefaultFormat; - } - - public String getMimeType() { - return mMime; - } - - public int getMaxSupportedInstances() { - return mMaxSupportedInstances; - } - - private boolean isAudio() { - return mAudioCaps != null; - } - - public AudioCapabilities getAudioCapabilities() { - return mAudioCaps; - } - - private boolean isEncoder() { - return mEncoderCaps != null; - } - - public EncoderCapabilities getEncoderCapabilities() { - return mEncoderCaps; - } - - private boolean isVideo() { - return mVideoCaps != null; - } - - public VideoCapabilities getVideoCapabilities() { - return mVideoCaps; - } - - public static CodecCapsLegacyImpl createFromProfileLevel( - String mime, int profile, int level) { - CodecProfileLevel pl = new CodecProfileLevel(); - pl.profile = profile; - pl.level = level; - MediaFormat defaultFormat = new MediaFormat(); - defaultFormat.setString(MediaFormat.KEY_MIME, mime); - - CodecCapsLegacyImpl ret = new CodecCapsLegacyImpl( - new CodecProfileLevel[] { pl }, new int[0], true /* encoder */, - defaultFormat, new MediaFormat() /* info */); - if (ret.mError != 0) { - return null; + if (GetFlag(() -> android.media.codec.Flags.regionOfInterest())) { + features.add(new Feature(FEATURE_Roi, (1 << 7), true)); } - return ret; - } - - /* package private */ CodecCapsLegacyImpl( - CodecProfileLevel[] profLevs, int[] colFmts, - boolean encoder, - Map<String, Object>defaultFormatMap, - Map<String, Object>capabilitiesMap) { - this(profLevs, colFmts, encoder, - new MediaFormat(defaultFormatMap), - new MediaFormat(capabilitiesMap)); - } - /* package private */ CodecCapsLegacyImpl( - CodecProfileLevel[] profLevs, int[] colFmts, boolean encoder, - MediaFormat defaultFormat, MediaFormat info) { - final Map<String, Object> map = info.getMap(); - mColorFormats = colFmts; - mFlagsVerified = 0; // TODO: remove as it is unused - mDefaultFormat = defaultFormat; - mCapabilitiesInfo = info; - mMime = mDefaultFormat.getString(MediaFormat.KEY_MIME); - - /* VP9 introduced profiles around 2016, so some VP9 codecs may not advertise any - supported profiles. Determine the level for them using the info they provide. */ - if (profLevs.length == 0 - && mMime.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_VP9)) { - CodecProfileLevel profLev = new CodecProfileLevel(); - profLev.profile = CodecProfileLevel.VP9Profile0; - profLev.level = VideoCapabilities.VideoCapsLegacyImpl.equivalentVP9Level(info); - profLevs = new CodecProfileLevel[] { profLev }; - } - mProfileLevels = profLevs; - - if (mMime.toLowerCase().startsWith("audio/")) { - mAudioCaps = AudioCapabilities.create(info, this); - mAudioCaps.getDefaultFormat(mDefaultFormat); - } else if (mMime.toLowerCase().startsWith("video/") - || mMime.equalsIgnoreCase(MediaFormat.MIMETYPE_IMAGE_ANDROID_HEIC)) { - mVideoCaps = VideoCapabilities.create(info, this); - } - if (encoder) { - mEncoderCaps = EncoderCapabilities.create(info, this); - mEncoderCaps.getDefaultFormat(mDefaultFormat); - } + // feature to exclude codec from REGULAR codec list + features.add(new Feature(FEATURE_SpecialCodec, (1 << 30), false, true)); - final Map<String, Object> global = MediaCodecList.getGlobalSettings(); - mMaxSupportedInstances = Utils.parseIntSafely( - global.get("max-concurrent-instances"), DEFAULT_MAX_SUPPORTED_INSTANCES); + return features.toArray(new Feature[0]); + }; - int maxInstances = Utils.parseIntSafely( - map.get("max-concurrent-instances"), mMaxSupportedInstances); - mMaxSupportedInstances = - Range.create(1, MAX_SUPPORTED_INSTANCES_LIMIT).clamp(maxInstances); + private static Feature[] encoderFeatures = getEncoderFeatures(); - for (Feature feat: getValidFeatures()) { - String key = MediaFormat.KEY_FEATURE_ + feat.mName; - Integer yesNo = (Integer)map.get(key); - if (yesNo == null) { - continue; - } - if (yesNo > 0) { - mFlagsRequired |= feat.mValue; - } - mFlagsSupported |= feat.mValue; - if (!feat.mInternal) { - mDefaultFormat.setInteger(key, 1); - } - // TODO restrict features by mFlagsVerified once all codecs reliably verify them + public static Feature[] getFeatures(boolean isEncoder) { + if (isEncoder) { + return encoderFeatures; + } else { + return decoderFeatures; } } } - /* package private */ static final class CodecCapsNativeImpl implements CodecCapsIntf { - private long mNativeContext; // accessed by native methods - - private CodecProfileLevel[] mProfileLevels; - private int[] mColorFormats; - - private MediaFormat mDefaultFormat; - private AudioCapabilities mAudioCaps; - private VideoCapabilities mVideoCaps; - private EncoderCapabilities mEncoderCaps; - - public static CodecCapsNativeImpl createFromProfileLevel( - String mime, int profile, int level) { - return native_createFromProfileLevel(mime, profile, level); - } - - /** - * Constructor used by JNI. - * - * The Java CodecCapabilities object keeps these subobjects to avoid recontructing. - */ - /* package private */ CodecCapsNativeImpl(CodecProfileLevel[] profLevs, int[] colFmts, - MediaFormat defaultFormat, AudioCapabilities audioCaps, - VideoCapabilities videoCaps, EncoderCapabilities encoderCaps) { - mProfileLevels = profLevs; - mColorFormats = colFmts; - mDefaultFormat = defaultFormat; - mAudioCaps = audioCaps; - mVideoCaps = videoCaps; - mEncoderCaps = encoderCaps; - } - - public CodecCapsNativeImpl dup() { - CodecCapsNativeImpl impl = native_dup(); - return impl; - } - - @Override - protected void finalize() { - native_finalize(); - } - - public CodecProfileLevel[] getProfileLevels() { - return mProfileLevels; - } - - public int[] getColorFormats() { - return mColorFormats; - } - - public final boolean isFeatureSupported(String name) { - return native_isFeatureSupported(name); - } - - public final boolean isFeatureRequired(String name) { - return native_isFeatureRequired(name); - } - - public boolean isRegular() { - return native_isRegular(); - } - - public final boolean isFormatSupported(MediaFormat format) { - if (format == null) { - throw new NullPointerException(); - } - - Map<String, Object> formatMap = format.getMap(); - String[] keys = new String[formatMap.size()]; - Object[] values = new Object[formatMap.size()]; - - int i = 0; - for (Map.Entry<String, Object> entry: formatMap.entrySet()) { - keys[i] = entry.getKey(); - values[i] = entry.getValue(); - ++i; + /** @hide */ + public String[] validFeatures() { + Feature[] features = getValidFeatures(); + String[] res = new String[features.length]; + for (int i = 0; i < res.length; i++) { + if (!features[i].mInternal) { + res[i] = features[i].mName; } - - return native_isFormatSupported(keys, values); - } - - public MediaFormat getDefaultFormat() { - return mDefaultFormat; - } - - public String getMimeType() { - return native_getMimeType(); - } - - public int getMaxSupportedInstances() { - return native_getMaxSupportedInstances(); - } - - public AudioCapabilities getAudioCapabilities() { - return mAudioCaps; - } - - public EncoderCapabilities getEncoderCapabilities() { - return mEncoderCaps; - } - - public VideoCapabilities getVideoCapabilities() { - return mVideoCaps; - } - - private static native void native_init(); - private static native CodecCapsNativeImpl native_createFromProfileLevel( - String mime, int profile, int level); - private native CodecCapsNativeImpl native_dup(); - private native void native_finalize(); - private native int native_getMaxSupportedInstances(); - private native String native_getMimeType(); - private native final boolean native_isFeatureRequired(String name); - private native final boolean native_isFeatureSupported(String name); - private native final boolean native_isFormatSupported(@Nullable String[] keys, - @Nullable Object[] values); - private native boolean native_isRegular(); - - static { - System.loadLibrary("media_jni"); - native_init(); } + return res; } - private CodecCapsIntf mImpl; - - /** - * Retrieve the codec capabilities for a certain {@code mime type}, {@code - * profile} and {@code level}. If the type, or profile-level combination - * is not understood by the framework, it returns null. - * <p class=note> In {@link android.os.Build.VERSION_CODES#M}, calling this - * method without calling any method of the {@link MediaCodecList} class beforehand - * results in a {@link NullPointerException}.</p> - */ - public static CodecCapabilities createFromProfileLevel( - String mime, int profile, int level) { - CodecCapsIntf impl; - if (GetFlag(() -> android.media.codec.Flags.nativeCapabilites())) { - impl = CodecCapsNativeImpl.createFromProfileLevel(mime, profile, level); - } else { - impl = CodecCapsLegacyImpl.createFromProfileLevel(mime, profile, level); - } - return new CodecCapabilities(impl); + private Feature[] getValidFeatures() { + return FeatureList.getFeatures(isEncoder()); } - public CodecCapabilities() { - mImpl = new CodecCapsLegacyImpl(); - } - - /** package private */ CodecCapabilities(CodecCapsIntf impl) { - mImpl = impl; - profileLevels = mImpl.getProfileLevels(); - colorFormats = mImpl.getColorFormats(); - } - - /** @hide */ - public CodecCapabilities dup() { - 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.mImpl = mImpl.dup(); - - return caps; - } - - /** - * Query codec feature capabilities. - * <p> - * These features are supported to be used by the codec. These - * include optional features that can be turned on, as well as - * features that are always on. - */ - public final boolean isFeatureSupported(String name) { - return mImpl.isFeatureSupported(name); - } - - /** - * Query codec feature requirements. - * <p> - * These features are required to be used by the codec, and as such, - * they are always turned on. - */ - public final boolean isFeatureRequired(String name) { - return mImpl.isFeatureRequired(name); + private boolean checkFeature(String name, int flags) { + for (Feature feat: getValidFeatures()) { + if (feat.mName.equals(name)) { + return (flags & feat.mValue) != 0; + } + } + return false; } /** @hide */ public boolean isRegular() { - return mImpl.isRegular(); + // regular codecs only require default features + for (Feature feat: getValidFeatures()) { + if (!feat.mDefault && isFeatureRequired(feat.mName)) { + return false; + } + } + return true; } /** @@ -1593,22 +1046,201 @@ public final class MediaCodecInfo { * and feature requests. */ public final boolean isFormatSupported(MediaFormat format) { - return mImpl.isFormatSupported(format); + final Map<String, Object> map = format.getMap(); + final String mime = (String)map.get(MediaFormat.KEY_MIME); + + // mime must match if present + if (mime != null && !mMime.equalsIgnoreCase(mime)) { + return false; + } + + // check feature support + for (Feature feat: getValidFeatures()) { + if (feat.mInternal) { + continue; + } + + Integer yesNo = (Integer)map.get(MediaFormat.KEY_FEATURE_ + feat.mName); + if (yesNo == null) { + continue; + } + if ((yesNo == 1 && !isFeatureSupported(feat.mName)) || + (yesNo == 0 && isFeatureRequired(feat.mName))) { + return false; + } + } + + Integer profile = (Integer)map.get(MediaFormat.KEY_PROFILE); + Integer level = (Integer)map.get(MediaFormat.KEY_LEVEL); + + if (profile != null) { + if (!supportsProfileLevel(profile, level)) { + return false; + } + + // If we recognize this profile, check that this format is supported by the + // highest level supported by the codec for that profile. (Ignore specified + // level beyond the above profile/level check as level is only used as a + // guidance. E.g. AVC Level 1 CIF format is supported if codec supports level 1.1 + // even though max size for Level 1 is QCIF. However, MPEG2 Simple Profile + // 1080p format is not supported even if codec supports Main Profile Level High, + // as Simple Profile does not support 1080p. + CodecCapabilities levelCaps = null; + int maxLevel = 0; + for (CodecProfileLevel pl : profileLevels) { + if (pl.profile == profile && pl.level > maxLevel) { + // H.263 levels are not completely ordered: + // Level45 support only implies Level10 support + if (!mMime.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_H263) + || pl.level != CodecProfileLevel.H263Level45 + || maxLevel == CodecProfileLevel.H263Level10) { + maxLevel = pl.level; + } + } + } + levelCaps = createFromProfileLevel(mMime, profile, maxLevel); + // We must remove the profile from this format otherwise levelCaps.isFormatSupported + // will get into this same condition and loop forever. Furthermore, since levelCaps + // does not contain features and bitrate specific keys, keep only keys relevant for + // a level check. + Map<String, Object> levelCriticalFormatMap = new HashMap<>(map); + final Set<String> criticalKeys = isVideo() + ? VideoCapabilities.VideoCapsLegacyImpl.VIDEO_LEVEL_CRITICAL_FORMAT_KEYS + : isAudio() + ? AudioCapabilities.AudioCapsLegacyImpl.AUDIO_LEVEL_CRITICAL_FORMAT_KEYS + : null; + + // critical keys will always contain KEY_MIME, but should also contain others to be + // meaningful + if (criticalKeys != null && criticalKeys.size() > 1 && levelCaps != null) { + levelCriticalFormatMap.keySet().retainAll(criticalKeys); + + MediaFormat levelCriticalFormat = new MediaFormat(levelCriticalFormatMap); + if (!levelCaps.isFormatSupported(levelCriticalFormat)) { + return false; + } + } + } + if (mAudioCaps != null && !mAudioCaps.supportsFormat(format)) { + return false; + } + if (mVideoCaps != null && !mVideoCaps.supportsFormat(format)) { + return false; + } + if (mEncoderCaps != null && !mEncoderCaps.supportsFormat(format)) { + return false; + } + return true; + } + + private static boolean supportsBitrate( + Range<Integer> bitrateRange, MediaFormat format) { + Map<String, Object> map = format.getMap(); + + // consider max bitrate over average bitrate for support + Integer maxBitrate = (Integer)map.get(MediaFormat.KEY_MAX_BIT_RATE); + Integer bitrate = (Integer)map.get(MediaFormat.KEY_BIT_RATE); + if (bitrate == null) { + bitrate = maxBitrate; + } else if (maxBitrate != null) { + bitrate = Math.max(bitrate, maxBitrate); + } + + if (bitrate != null && bitrate > 0) { + return bitrateRange.contains(bitrate); + } + + return true; + } + + private boolean supportsProfileLevel(int profile, Integer level) { + for (CodecProfileLevel pl: profileLevels) { + if (pl.profile != profile) { + continue; + } + + // No specific level requested + if (level == null) { + return true; + } + + // AAC doesn't use levels + if (mMime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_AAC)) { + return true; + } + + // DTS doesn't use levels + if (mMime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_DTS) + || mMime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_DTS_HD) + || mMime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_DTS_UHD)) { + return true; + } + + // H.263 levels are not completely ordered: + // Level45 support only implies Level10 support + if (mMime.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_H263)) { + if (pl.level != level && pl.level == CodecProfileLevel.H263Level45 + && level > CodecProfileLevel.H263Level10) { + continue; + } + } + + // MPEG4 levels are not completely ordered: + // Level1 support only implies Level0 (and not Level0b) support + if (mMime.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_MPEG4)) { + if (pl.level != level && pl.level == CodecProfileLevel.MPEG4Level1 + && level > CodecProfileLevel.MPEG4Level0) { + continue; + } + } + + // HEVC levels incorporate both tiers and levels. Verify tier support. + if (mMime.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_HEVC)) { + boolean supportsHighTier = + (pl.level & CodecProfileLevel.HEVCHighTierLevels) != 0; + boolean checkingHighTier = (level & CodecProfileLevel.HEVCHighTierLevels) != 0; + // high tier levels are only supported by other high tier levels + if (checkingHighTier && !supportsHighTier) { + continue; + } + } + + if (pl.level >= level) { + // if we recognize the listed profile/level, we must also recognize the + // profile/level arguments. + if (createFromProfileLevel(mMime, profile, pl.level) != null) { + return createFromProfileLevel(mMime, profile, level) != null; + } + return true; + } + } + return false; } + // errors while reading profile levels - accessed from sister capabilities + int mError; + + private static final String TAG = "CodecCapabilities"; + + // NEW-STYLE CAPABILITIES + private AudioCapabilities mAudioCaps; + private VideoCapabilities mVideoCaps; + private EncoderCapabilities mEncoderCaps; + private MediaFormat mDefaultFormat; + /** * Returns a MediaFormat object with default values for configurations that have * defaults. */ public MediaFormat getDefaultFormat() { - return mImpl.getDefaultFormat(); + return mDefaultFormat; } /** * Returns the mime type for which this codec-capability object was created. */ public String getMimeType() { - return mImpl.getMimeType(); + return mMime; } /** @@ -1620,28 +1252,157 @@ public final class MediaCodecInfo { * resources at time of use. */ public int getMaxSupportedInstances() { - return mImpl.getMaxSupportedInstances(); + return mMaxSupportedInstances; + } + + private boolean isAudio() { + return mAudioCaps != null; } /** * Returns the audio capabilities or {@code null} if this is not an audio codec. */ public AudioCapabilities getAudioCapabilities() { - return mImpl.getAudioCapabilities(); + return mAudioCaps; + } + + private boolean isEncoder() { + return mEncoderCaps != null; } /** * Returns the encoding capabilities or {@code null} if this is not an encoder. */ public EncoderCapabilities getEncoderCapabilities() { - return mImpl.getEncoderCapabilities(); + return mEncoderCaps; + } + + private boolean isVideo() { + return mVideoCaps != null; } /** * Returns the video capabilities or {@code null} if this is not a video codec. */ public VideoCapabilities getVideoCapabilities() { - return mImpl.getVideoCapabilities(); + return mVideoCaps; + } + + /** @hide */ + public CodecCapabilities dup() { + 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; + } + + /** + * Retrieve the codec capabilities for a certain {@code mime type}, {@code + * profile} and {@code level}. If the type, or profile-level combination + * is not understood by the framework, it returns null. + * <p class=note> In {@link android.os.Build.VERSION_CODES#M}, calling this + * method without calling any method of the {@link MediaCodecList} class beforehand + * results in a {@link NullPointerException}.</p> + */ + public static CodecCapabilities createFromProfileLevel( + String mime, int profile, int level) { + CodecProfileLevel pl = new CodecProfileLevel(); + pl.profile = profile; + pl.level = level; + MediaFormat defaultFormat = new MediaFormat(); + defaultFormat.setString(MediaFormat.KEY_MIME, mime); + + CodecCapabilities ret = new CodecCapabilities( + new CodecProfileLevel[] { pl }, new int[0], true /* encoder */, + defaultFormat, new MediaFormat() /* info */); + if (ret.mError != 0) { + return null; + } + return ret; + } + + /* package private */ CodecCapabilities( + CodecProfileLevel[] profLevs, int[] colFmts, + boolean encoder, + Map<String, Object>defaultFormatMap, + Map<String, Object>capabilitiesMap) { + this(profLevs, colFmts, encoder, + new MediaFormat(defaultFormatMap), + new MediaFormat(capabilitiesMap)); + } + + private MediaFormat mCapabilitiesInfo; + + /* package private */ CodecCapabilities( + CodecProfileLevel[] profLevs, int[] colFmts, boolean encoder, + MediaFormat defaultFormat, MediaFormat info) { + final Map<String, Object> map = info.getMap(); + colorFormats = colFmts; + mFlagsVerified = 0; // TODO: remove as it is unused + mDefaultFormat = defaultFormat; + mCapabilitiesInfo = info; + mMime = mDefaultFormat.getString(MediaFormat.KEY_MIME); + + /* VP9 introduced profiles around 2016, so some VP9 codecs may not advertise any + supported profiles. Determine the level for them using the info they provide. */ + if (profLevs.length == 0 && mMime.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_VP9)) { + CodecProfileLevel profLev = new CodecProfileLevel(); + profLev.profile = CodecProfileLevel.VP9Profile0; + profLev.level = VideoCapabilities.VideoCapsLegacyImpl.equivalentVP9Level(info); + profLevs = new CodecProfileLevel[] { profLev }; + } + profileLevels = profLevs; + + if (mMime.toLowerCase().startsWith("audio/")) { + mAudioCaps = AudioCapabilities.create(info, this); + mAudioCaps.getDefaultFormat(mDefaultFormat); + } else if (mMime.toLowerCase().startsWith("video/") + || mMime.equalsIgnoreCase(MediaFormat.MIMETYPE_IMAGE_ANDROID_HEIC)) { + mVideoCaps = VideoCapabilities.create(info, this); + } + if (encoder) { + mEncoderCaps = EncoderCapabilities.create(info, this); + mEncoderCaps.getDefaultFormat(mDefaultFormat); + } + + final Map<String, Object> global = MediaCodecList.getGlobalSettings(); + mMaxSupportedInstances = Utils.parseIntSafely( + global.get("max-concurrent-instances"), DEFAULT_MAX_SUPPORTED_INSTANCES); + + int maxInstances = Utils.parseIntSafely( + map.get("max-concurrent-instances"), mMaxSupportedInstances); + mMaxSupportedInstances = + Range.create(1, MAX_SUPPORTED_INSTANCES_LIMIT).clamp(maxInstances); + + for (Feature feat: getValidFeatures()) { + String key = MediaFormat.KEY_FEATURE_ + feat.mName; + Integer yesNo = (Integer)map.get(key); + if (yesNo == null) { + continue; + } + if (yesNo > 0) { + mFlagsRequired |= feat.mValue; + } + mFlagsSupported |= feat.mValue; + if (!feat.mInternal) { + mDefaultFormat.setInteger(key, 1); + } + // TODO restrict features by mFlagsVerified once all codecs reliably verify them + } } } @@ -1672,7 +1433,7 @@ public final class MediaCodecInfo { } /* package private */ static final class AudioCapsLegacyImpl implements AudioCapsIntf { - private CodecCapabilities.CodecCapsLegacyImpl mParent; + private CodecCapabilities mParent; private Range<Integer> mBitrateRange; private int[] mSampleRates; @@ -1724,7 +1485,7 @@ public final class MediaCodecInfo { private AudioCapsLegacyImpl() { } public static AudioCapsLegacyImpl create( - MediaFormat info, CodecCapabilities.CodecCapsLegacyImpl parent) { + MediaFormat info, CodecCapabilities parent) { if (GetFlag(() -> android.media.codec.Flags.nativeCapabilites())) { Log.d(TAG, "Legacy implementation is called while native flag is on."); } @@ -1734,7 +1495,7 @@ public final class MediaCodecInfo { return caps; } - private void init(MediaFormat info, CodecCapabilities.CodecCapsLegacyImpl parent) { + private void init(MediaFormat info, CodecCapabilities parent) { mParent = parent; initWithPlatformLimits(); applyLevelLimits(); @@ -1815,7 +1576,7 @@ public final class MediaCodecInfo { int[] sampleRates = null; Range<Integer> sampleRateRange = null, bitRates = null; int maxChannels = MAX_INPUT_CHANNEL_COUNT; - CodecProfileLevel[] profileLevels = mParent.getProfileLevels(); + CodecProfileLevel[] profileLevels = mParent.profileLevels; String mime = mParent.getMimeType(); if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_MPEG)) { @@ -2050,7 +1811,7 @@ public final class MediaCodecInfo { return false; } - if (!CodecCapabilities.CodecCapsLegacyImpl.supportsBitrate(mBitrateRange, format)) { + if (!CodecCapabilities.supportsBitrate(mBitrateRange, format)) { return false; } @@ -2142,7 +1903,7 @@ public final class MediaCodecInfo { /** @hide */ public static AudioCapabilities create( - MediaFormat info, CodecCapabilities.CodecCapsLegacyImpl parent) { + MediaFormat info, CodecCapabilities parent) { AudioCapsLegacyImpl impl = AudioCapsLegacyImpl.create(info, parent); AudioCapabilities caps = new AudioCapabilities(impl); return caps; @@ -2731,7 +2492,7 @@ public final class MediaCodecInfo { MediaFormat.KEY_BIT_RATE, MediaFormat.KEY_MIME); - private CodecCapabilities.CodecCapsLegacyImpl mParent; + private CodecCapabilities mParent; private Range<Integer> mBitrateRange; private Range<Integer> mHeightRange; @@ -2759,7 +2520,7 @@ public final class MediaCodecInfo { @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) public static VideoCapsLegacyImpl create( - MediaFormat info, CodecCapabilities.CodecCapsLegacyImpl parent) { + MediaFormat info, CodecCapabilities parent) { if (GetFlag(() -> android.media.codec.Flags.nativeCapabilites())) { Log.d(TAG, "Legacy implementation is called while native flag is on."); } @@ -2769,7 +2530,7 @@ public final class MediaCodecInfo { return caps; } - private void init(MediaFormat info, CodecCapabilities.CodecCapsLegacyImpl parent) { + private void init(MediaFormat info, CodecCapabilities parent) { mParent = parent; initWithPlatformLimits(); applyLevelLimits(); @@ -3004,7 +2765,7 @@ public final class MediaCodecInfo { return false; } - if (!CodecCapabilities.CodecCapsLegacyImpl.supportsBitrate(mBitrateRange, format)) { + if (!CodecCapabilities.supportsBitrate(mBitrateRange, format)) { return false; } @@ -3519,7 +3280,7 @@ public final class MediaCodecInfo { int maxDPBBlocks = 0; int errors = ERROR_NONE_SUPPORTED; - CodecProfileLevel[] profileLevels = mParent.getProfileLevels(); + CodecProfileLevel[] profileLevels = mParent.profileLevels; String mime = mParent.getMimeType(); if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_AVC)) { @@ -4326,7 +4087,7 @@ public final class MediaCodecInfo { /** @hide */ public static VideoCapabilities create( - MediaFormat info, CodecCapabilities.CodecCapsLegacyImpl parent) { + MediaFormat info, CodecCapabilities parent) { VideoCapsLegacyImpl impl = VideoCapsLegacyImpl.create(info, parent); VideoCapabilities caps = new VideoCapabilities(impl); return caps; @@ -4575,7 +4336,7 @@ public final class MediaCodecInfo { } /* package private */ static final class EncoderCapsLegacyImpl implements EncoderCapsIntf { - private CodecCapabilities.CodecCapsLegacyImpl mParent; + private CodecCapabilities mParent; private Range<Integer> mQualityRange; private Range<Integer> mComplexityRange; @@ -4618,7 +4379,7 @@ public final class MediaCodecInfo { /** @hide */ public static EncoderCapsLegacyImpl create( - MediaFormat info, CodecCapabilities.CodecCapsLegacyImpl parent) { + MediaFormat info, CodecCapabilities parent) { if (GetFlag(() -> android.media.codec.Flags.nativeCapabilites())) { Log.d(TAG, "Legacy implementation is called while native flag is on."); } @@ -4628,7 +4389,7 @@ public final class MediaCodecInfo { return caps; } - private void init(MediaFormat info, CodecCapabilities.CodecCapsLegacyImpl parent) { + private void init(MediaFormat info, CodecCapabilities parent) { // no support for complexity or quality yet mParent = parent; mComplexityRange = Range.create(0, 0); @@ -4698,7 +4459,7 @@ public final class MediaCodecInfo { ok = mQualityRange.contains(quality); } if (ok && profile != null) { - for (CodecProfileLevel pl: mParent.getProfileLevels()) { + for (CodecProfileLevel pl: mParent.profileLevels) { if (pl.profile == profile) { profile = null; break; @@ -4823,7 +4584,7 @@ public final class MediaCodecInfo { /** @hide */ public static EncoderCapabilities create( - MediaFormat info, CodecCapabilities.CodecCapsLegacyImpl parent) { + MediaFormat info, CodecCapabilities parent) { EncoderCapsLegacyImpl impl = EncoderCapsLegacyImpl.create(info, parent); EncoderCapabilities caps = new EncoderCapabilities(impl); return caps; diff --git a/media/jni/android_media_CodecCapabilities.cpp b/media/jni/android_media_CodecCapabilities.cpp index df0c826d8d87..29695d60eeb2 100644 --- a/media/jni/android_media_CodecCapabilities.cpp +++ b/media/jni/android_media_CodecCapabilities.cpp @@ -17,20 +17,14 @@ //#define LOG_NDEBUG 0 #define LOG_TAG "MediaCodec-JNI" -#include "android_media_CodecCapabilities.h" -#include "android_media_Streams.h" #include "android_runtime/AndroidRuntime.h" #include "jni.h" #include <media/AudioCapabilities.h> -#include <media/CodecCapabilities.h> #include <media/EncoderCapabilities.h> #include <media/VideoCapabilities.h> #include <media/stagefright/foundation/ADebug.h> -#include <media/stagefright/foundation/AMessage.h> #include <nativehelper/JNIHelp.h> -#include <nativehelper/ScopedLocalRef.h> -#include <utils/Log.h> namespace android { @@ -38,59 +32,9 @@ struct fields_t { jfieldID audioCapsContext; jfieldID videoCapsContext; jfieldID encoderCapsContext; - jfieldID codecCapsContext; }; static fields_t fields; -// JCodecCapabilities - -JCodecCapabilities::JCodecCapabilities(std::shared_ptr<CodecCapabilities> codecCaps) - : mCodecCaps(codecCaps) {} - -std::shared_ptr<CodecCapabilities> JCodecCapabilities::getCodecCaps() const { - return mCodecCaps; -} - -int32_t JCodecCapabilities::getMaxSupportedInstances() const { - return mCodecCaps->getMaxSupportedInstances(); -} - -std::string JCodecCapabilities::getMediaType() const { - return mCodecCaps->getMediaType(); -} - -bool JCodecCapabilities::isFeatureRequired(const std::string& name) const { - return mCodecCaps->isFeatureRequired(name); -} - -bool JCodecCapabilities::isFeatureSupported(const std::string& name) const { - return mCodecCaps->isFeatureSupported(name); -} - -bool JCodecCapabilities::isFormatSupported(const sp<AMessage> &format) const { - return mCodecCaps->isFormatSupported(format); -} - -bool JCodecCapabilities::isRegular() const { - return mCodecCaps->isRegular(); -} - -// Setter - -static sp<JCodecCapabilities> setCodecCapabilities(JNIEnv *env, jobject thiz, - const sp<JCodecCapabilities>& jCodecCaps) { - sp<JCodecCapabilities> old - = (JCodecCapabilities*)env->GetLongField(thiz, fields.codecCapsContext); - if (jCodecCaps != NULL) { - jCodecCaps->incStrong(thiz); - } - if (old != NULL) { - old->decStrong(thiz); - } - env->SetLongField(thiz, fields.codecCapsContext, (jlong)jCodecCaps.get()); - return old; -} - // Getters static AudioCapabilities* getAudioCapabilities(JNIEnv *env, jobject thiz) { @@ -111,12 +55,6 @@ static EncoderCapabilities* getEncoderCapabilities(JNIEnv *env, jobject thiz) { return p; } -static sp<JCodecCapabilities> getCodecCapabilities(JNIEnv *env, jobject thiz) { - JCodecCapabilities* const p = (JCodecCapabilities*)env->GetLongField( - thiz, fields.codecCapsContext); - return sp<JCodecCapabilities>(p); -} - // Utils static jobject convertToJavaIntRange(JNIEnv *env, const Range<int32_t>& range) { @@ -138,125 +76,8 @@ static jobject convertToJavaDoubleRange(JNIEnv *env, const Range<double>& range) return jRange; } -static jobjectArray convertToJavaIntRangeArray(JNIEnv *env, - const std::vector<Range<int32_t>>& ranges) { - jclass rangeClazz = env->FindClass("android/util/Range"); - CHECK(rangeClazz != NULL); - jobjectArray jRanges = env->NewObjectArray(ranges.size(), rangeClazz, NULL); - for (int i = 0; i < ranges.size(); i++) { - Range<int32_t> range = ranges.at(i); - jobject jRange = convertToJavaIntRange(env, range); - env->SetObjectArrayElement(jRanges, i, jRange); - env->DeleteLocalRef(jRange); - jRange = NULL; - } - return jRanges; -} - // Converters between Java objects and native instances -// The Java AudioCapabilities object keep bitrateRange, sampleRates, sampleRateRanges -// and inputChannelRanges in it to prevent reconstruction when called the getters functions. -static jobject convertToJavaAudioCapabilities( - JNIEnv *env, std::shared_ptr<AudioCapabilities> audioCaps) { - if (audioCaps == nullptr) { - return NULL; - } - - // construct Java bitrateRange - const Range<int32_t>& bitrateRange = audioCaps->getBitrateRange(); - jobject jBitrateRange = convertToJavaIntRange(env, bitrateRange); - - // construct Java sampleRates array - const std::vector<int32_t>& sampleRates = audioCaps->getSupportedSampleRates(); - jintArray jSampleRates = env->NewIntArray(sampleRates.size()); - for (size_t i = 0; i < sampleRates.size(); ++i) { - jint val = sampleRates.at(i); - env->SetIntArrayRegion(jSampleRates, i, 1, &val); - } - - // construct Java sampleRateRanges - const std::vector<Range<int32_t>>& sampleRateRanges = audioCaps->getSupportedSampleRateRanges(); - jobjectArray jSampleRateRanges = convertToJavaIntRangeArray(env, sampleRateRanges); - - // construct Java inputChannelRanges - const std::vector<Range<int32_t>>& inputChannelRanges = audioCaps->getInputChannelCountRanges(); - jobjectArray jInputChannelRanges = convertToJavaIntRangeArray(env, inputChannelRanges); - - // construct Java AudioCapsNativeImpl - jclass audioCapsImplClazz - = env->FindClass("android/media/MediaCodecInfo$AudioCapabilities$AudioCapsNativeImpl"); - CHECK(audioCapsImplClazz != NULL); - jmethodID audioCapsImplConstructID = env->GetMethodID(audioCapsImplClazz, "<init>", - "(Landroid/util/Range;" - "[I" - "[Landroid/util/Range;" - "[Landroid/util/Range;)V"); - jobject jAudioCapsImpl = env->NewObject(audioCapsImplClazz, audioCapsImplConstructID, - jBitrateRange, jSampleRates, jSampleRateRanges, jInputChannelRanges); - // The native AudioCapabilities won't be destructed until process ends. - env->SetLongField(jAudioCapsImpl, fields.audioCapsContext, (jlong)audioCaps.get()); - - // construct Java AudioCapabilities - jclass audioCapsClazz - = env->FindClass("android/media/MediaCodecInfo$AudioCapabilities"); - CHECK(audioCapsClazz != NULL); - jmethodID audioCapsConstructID = env->GetMethodID(audioCapsClazz, "<init>", - "(Landroid/media/MediaCodecInfo$AudioCapabilities$AudioCapsIntf;)V"); - jobject jAudioCaps = env->NewObject(audioCapsClazz, audioCapsConstructID, jAudioCapsImpl); - - env->DeleteLocalRef(jBitrateRange); - jBitrateRange = NULL; - - env->DeleteLocalRef(jSampleRates); - jSampleRates = NULL; - - env->DeleteLocalRef(jSampleRateRanges); - jSampleRateRanges = NULL; - - env->DeleteLocalRef(jInputChannelRanges); - jInputChannelRanges = NULL; - - env->DeleteLocalRef(jAudioCapsImpl); - jAudioCapsImpl = NULL; - - return jAudioCaps; -} - -// convert native PerformancePoints to Java objects -static jobject convertToJavaPerformancePoints(JNIEnv *env, - const std::vector<VideoCapabilities::PerformancePoint>& performancePoints) { - jclass performancePointClazz = env->FindClass( - "android/media/MediaCodecInfo$VideoCapabilities$PerformancePoint"); - CHECK(performancePointClazz != NULL); - jmethodID performancePointConstructID = env->GetMethodID(performancePointClazz, "<init>", - "(IIIJII)V"); - - jobjectArray jPerformancePoints = env->NewObjectArray(performancePoints.size(), - performancePointClazz, NULL); - int i = 0; - for (auto it = performancePoints.begin(); it != performancePoints.end(); ++it, ++i) { - jobject jPerformancePoint = env->NewObject(performancePointClazz, - performancePointConstructID, it->getWidth(), - it->getHeight(), it->getMaxFrameRate(), - it->getMaxMacroBlockRate(), it->getBlockSize().getWidth(), - it->getBlockSize().getHeight()); - - env->SetObjectArrayElement(jPerformancePoints, i, jPerformancePoint); - - env->DeleteLocalRef(jPerformancePoint); - } - - jclass helperClazz = env->FindClass("android/media/MediaCodecInfo$GenericHelper"); - CHECK(helperClazz != NULL); - jmethodID asListID = env->GetStaticMethodID(helperClazz, "constructPerformancePointList", - "([Landroid/media/MediaCodecInfo$VideoCapabilities$PerformancePoint;)Ljava/util/List;"); - CHECK(asListID != NULL); - jobject jList = env->CallStaticObjectMethod(helperClazz, asListID, jPerformancePoints); - - return jList; -} - static VideoCapabilities::PerformancePoint convertToNativePerformancePoint( JNIEnv *env, jobject pp) { if (pp == NULL) { @@ -296,260 +117,6 @@ static VideoCapabilities::PerformancePoint convertToNativePerformancePoint( width, height, maxFrameRate, maxMacroBlockRate); } -static jobject convertToJavaVideoCapabilities(JNIEnv *env, - std::shared_ptr<VideoCapabilities> videoCaps) { - if (videoCaps == nullptr) { - return NULL; - } - - // get Java bitrateRange - const Range<int32_t>& bitrateRange = videoCaps->getBitrateRange(); - jobject jBitrateRange = convertToJavaIntRange(env, bitrateRange); - - // get Java widthRange - const Range<int32_t>& widthRange = videoCaps->getSupportedWidths(); - jobject jWidthRange = convertToJavaIntRange(env, widthRange); - - // get Java heightRange - const Range<int32_t>& heightRange = videoCaps->getSupportedHeights(); - jobject jHeightRange = convertToJavaIntRange(env, heightRange); - - // get Java frameRateRange - const Range<int32_t>& frameRateRange = videoCaps->getSupportedFrameRates(); - jobject jFrameRateRange = convertToJavaIntRange(env, frameRateRange); - - // get Java performancePoints - const std::vector<VideoCapabilities::PerformancePoint>& performancePoints - = videoCaps->getSupportedPerformancePoints(); - jobject jPerformancePoints = convertToJavaPerformancePoints(env, performancePoints); - - // get width alignment - int32_t widthAlignment = videoCaps->getWidthAlignment(); - - // get height alignment - int32_t heightAlignment = videoCaps->getHeightAlignment(); - - // get Java VideoCapsNativeImpl - jclass videoCapsImplClazz = env->FindClass( - "android/media/MediaCodecInfo$VideoCapabilities$VideoCapsNativeImpl"); - CHECK(videoCapsImplClazz != NULL); - jmethodID videoCapsImplConstructID = env->GetMethodID(videoCapsImplClazz, "<init>", - "(Landroid/util/Range;" - "Landroid/util/Range;" - "Landroid/util/Range;" - "Landroid/util/Range;" - "Ljava/util/List;II)V"); - jobject jVideoCapsImpl = env->NewObject(videoCapsImplClazz, videoCapsImplConstructID, - jBitrateRange, jWidthRange, jHeightRange, jFrameRateRange, jPerformancePoints, - widthAlignment, heightAlignment); - // The native VideoCapabilities won't be destructed until process ends. - env->SetLongField(jVideoCapsImpl, fields.videoCapsContext, (jlong)videoCaps.get()); - - // get Java VideoCapabilities - jclass videoCapsClazz - = env->FindClass("android/media/MediaCodecInfo$VideoCapabilities"); - CHECK(videoCapsClazz != NULL); - jmethodID videoCapsConstructID = env->GetMethodID(videoCapsClazz, "<init>", - "(Landroid/media/MediaCodecInfo$VideoCapabilities$VideoCapsIntf;)V"); - jobject jVideoCaps = env->NewObject(videoCapsClazz, videoCapsConstructID, jVideoCapsImpl); - - env->DeleteLocalRef(jBitrateRange); - jBitrateRange = NULL; - - env->DeleteLocalRef(jWidthRange); - jWidthRange = NULL; - - env->DeleteLocalRef(jHeightRange); - jHeightRange = NULL; - - env->DeleteLocalRef(jFrameRateRange); - jFrameRateRange = NULL; - - env->DeleteLocalRef(jPerformancePoints); - jPerformancePoints = NULL; - - env->DeleteLocalRef(jVideoCapsImpl); - jVideoCapsImpl = NULL; - - return jVideoCaps; -} - -static jobject convertToJavaEncoderCapabilities(JNIEnv *env, - std::shared_ptr<EncoderCapabilities> encoderCaps) { - if (encoderCaps == nullptr) { - return NULL; - } - - // get quality range - const Range<int>& qualityRange = encoderCaps->getQualityRange(); - jobject jQualityRange = convertToJavaIntRange(env, qualityRange); - - // get complexity range - const Range<int>& complexityRange = encoderCaps->getComplexityRange(); - jobject jComplexityRange = convertToJavaIntRange(env, complexityRange); - - // construct java EncoderCapsNativeImpl - jclass encoderCapsImplClazz = env->FindClass( - "android/media/MediaCodecInfo$EncoderCapabilities$EncoderCapsNativeImpl"); - CHECK(encoderCapsImplClazz != NULL); - jmethodID encoderCapsImplConstructID = env->GetMethodID(encoderCapsImplClazz, "<init>", - "(Landroid/util/Range;Landroid/util/Range;)V"); - jobject jEncoderCapsImpl = env->NewObject(encoderCapsImplClazz, encoderCapsImplConstructID, - jQualityRange, jComplexityRange); - // The native EncoderCapabilities won't be destructed until process ends. - env->SetLongField(jEncoderCapsImpl, fields.encoderCapsContext, (jlong)encoderCaps.get()); - - // construct java EncoderCapabilities object - jclass encoderCapsClazz - = env->FindClass("android/media/MediaCodecInfo$EncoderCapabilities"); - CHECK(encoderCapsClazz != NULL); - jmethodID encoderCapsConstructID = env->GetMethodID(encoderCapsClazz, "<init>", - "(Landroid/media/MediaCodecInfo$EncoderCapabilities$EncoderCapsIntf;)V"); - jobject jEncoderCaps = env->NewObject(encoderCapsClazz, encoderCapsConstructID, - jEncoderCapsImpl); - - env->DeleteLocalRef(jQualityRange); - jQualityRange = NULL; - - env->DeleteLocalRef(jComplexityRange); - jComplexityRange = NULL; - - env->DeleteLocalRef(jEncoderCapsImpl); - jEncoderCapsImpl = NULL; - - return jEncoderCaps; -} - -// Java CodecCapsNativeImpl keeps the defaultFormat, profileLevels, colorFormats, audioCapabilities, -// videoCapabilities and encoderCapabilities in it to prevent reconsturction when called by getter. -static jobject convertToJavaCodecCapsNativeImpl( - JNIEnv *env, std::shared_ptr<CodecCapabilities> codecCaps) { - if (codecCaps == nullptr) { - jniThrowException(env, "java/lang/IllegalArgumentException", NULL); - return NULL; - } - - // Construct defaultFormat - sp<AMessage> defaultFormat = codecCaps->getDefaultFormat(); - - jobject formatMap = NULL; - if (ConvertMessageToMap(env, defaultFormat, &formatMap)) { - return NULL; - } - - ScopedLocalRef<jclass> mediaFormatClass{env, env->FindClass("android/media/MediaFormat")}; - ScopedLocalRef<jobject> jDefaultFormat{env, env->NewObject( - mediaFormatClass.get(), - env->GetMethodID(mediaFormatClass.get(), "<init>", "(Ljava/util/Map;)V"), - formatMap)}; - - env->DeleteLocalRef(formatMap); - formatMap = NULL; - - // Construct Java ProfileLevelArray - std::vector<ProfileLevel> profileLevels = codecCaps->getProfileLevels(); - - jclass profileLevelClazz = - env->FindClass("android/media/MediaCodecInfo$CodecProfileLevel"); - CHECK(profileLevelClazz != NULL); - - jobjectArray profileLevelArray = - env->NewObjectArray(profileLevels.size(), profileLevelClazz, NULL); - - jfieldID profileField = - env->GetFieldID(profileLevelClazz, "profile", "I"); - jfieldID levelField = - env->GetFieldID(profileLevelClazz, "level", "I"); - - for (size_t i = 0; i < profileLevels.size(); ++i) { - const ProfileLevel &src = profileLevels.at(i); - - jobject profileLevelObj = env->AllocObject(profileLevelClazz); - - env->SetIntField(profileLevelObj, profileField, src.mProfile); - env->SetIntField(profileLevelObj, levelField, src.mLevel); - - env->SetObjectArrayElement(profileLevelArray, i, profileLevelObj); - - env->DeleteLocalRef(profileLevelObj); - profileLevelObj = NULL; - } - - // Construct ColorFormatArray - std::vector<uint32_t> colorFormats = codecCaps->getColorFormats(); - - jintArray colorFormatsArray = env->NewIntArray(colorFormats.size()); - env->SetIntArrayRegion(colorFormatsArray, 0, colorFormats.size(), - reinterpret_cast<jint*>(colorFormats.data())); - - // Construct and set AudioCapabilities - std::shared_ptr<AudioCapabilities> audioCaps = codecCaps->getAudioCapabilities(); - jobject jAudioCaps = convertToJavaAudioCapabilities(env, audioCaps); - - // Set VideoCapabilities - std::shared_ptr<VideoCapabilities> videoCaps = codecCaps->getVideoCapabilities(); - jobject jVideoCaps = convertToJavaVideoCapabilities(env, videoCaps); - - // Set EncoderCapabilities - std::shared_ptr<EncoderCapabilities> encoderCaps = codecCaps->getEncoderCapabilities(); - jobject jEncoderCaps = convertToJavaEncoderCapabilities(env, encoderCaps); - - // Construct CodecCapsNativeImpl - jclass codecCapsImplClazz = - env->FindClass("android/media/MediaCodecInfo$CodecCapabilities$CodecCapsNativeImpl"); - CHECK(codecCapsImplClazz != NULL); - jmethodID codecCapsImplConstructID = env->GetMethodID(codecCapsImplClazz, "<init>", - "([Landroid/media/MediaCodecInfo$CodecProfileLevel;[I" - "Landroid/media/MediaFormat;" - "Landroid/media/MediaCodecInfo$AudioCapabilities;" - "Landroid/media/MediaCodecInfo$VideoCapabilities;" - "Landroid/media/MediaCodecInfo$EncoderCapabilities;)V"); - jobject javaCodecCapsImpl = env->NewObject(codecCapsImplClazz, codecCapsImplConstructID, - profileLevelArray, colorFormatsArray, jDefaultFormat.get(), - jAudioCaps, jVideoCaps, jEncoderCaps); - - // Construct JCodecCapabilities and hold the codecCaps in it - sp<JCodecCapabilities> jCodecCaps = sp<JCodecCapabilities>::make(codecCaps); - setCodecCapabilities(env, javaCodecCapsImpl, jCodecCaps); - - env->DeleteLocalRef(profileLevelArray); - profileLevelArray = NULL; - - env->DeleteLocalRef(colorFormatsArray); - colorFormatsArray = NULL; - - env->DeleteLocalRef(jAudioCaps); - jAudioCaps = NULL; - - env->DeleteLocalRef(jVideoCaps); - jVideoCaps = NULL; - - env->DeleteLocalRef(jEncoderCaps); - jEncoderCaps = NULL; - - return javaCodecCapsImpl; -} - -jobject convertToJavaCodecCapabiliites( - JNIEnv *env, std::shared_ptr<CodecCapabilities> codecCaps) { - if (codecCaps == nullptr) { - jniThrowException(env, "java/lang/IllegalArgumentException", NULL); - return NULL; - } - - jobject javaCodecCapsImpl = convertToJavaCodecCapsNativeImpl(env, codecCaps); - - // Construct CodecCapabilities - jclass codecCapsClazz = env->FindClass("android/media/MediaCodecInfo$CodecCapabilities"); - CHECK(codecCapsClazz != NULL); - - jmethodID codecCapsConstructID = env->GetMethodID(codecCapsClazz, "<init>", - "(Landroid/media/MediaCodecInfo$CodecCapabilities$CodecCapsIntf;)V"); - jobject javaCodecCaps = env->NewObject(codecCapsClazz, codecCapsConstructID, javaCodecCapsImpl); - - return javaCodecCaps; -} - } // namespace android // ---------------------------------------------------------------------------- @@ -775,167 +342,6 @@ static jboolean android_media_EncoderCapabilities_isBitrateModeSupported(JNIEnv return res; } -// CodecCapabilities - -static void android_media_CodecCapabilities_native_init(JNIEnv *env, jobject /* thiz */) { - jclass codecCapsClazz - = env->FindClass("android/media/MediaCodecInfo$CodecCapabilities$CodecCapsNativeImpl"); - if (codecCapsClazz == NULL) { - return; - } - - fields.codecCapsContext = env->GetFieldID(codecCapsClazz, "mNativeContext", "J"); - if (fields.codecCapsContext == NULL) { - return; - } - - env->DeleteLocalRef(codecCapsClazz); -} - -static jobject android_media_CodecCapabilities_createFromProfileLevel(JNIEnv *env, - jobject /* thiz */, jstring mediaType, jint profile, jint level) { - if (mediaType == NULL) { - jniThrowException(env, "java/lang/IllegalArgumentException", NULL); - return NULL; - } - - const char *mediaTypeStr = env->GetStringUTFChars(mediaType, nullptr); - if (mediaTypeStr == nullptr) { - return NULL; - } - - std::shared_ptr<CodecCapabilities> codecCaps = CodecCapabilities::CreateFromProfileLevel( - mediaTypeStr, profile, level); - - jobject javaCodecCapsImpl = convertToJavaCodecCapsNativeImpl(env, codecCaps); - - env->ReleaseStringUTFChars(mediaType, mediaTypeStr); - - return javaCodecCapsImpl; -} - -static jobject android_media_CodecCapabilities_native_dup(JNIEnv *env, jobject thiz) { - sp<JCodecCapabilities> jCodecCaps = getCodecCapabilities(env, thiz); - - // As the CodecCaps objects are ready ony, it is ok to use the default copy constructor. - // The duplicate CodecCaps will share the same subobjects with the existing one. - // The lifetime of subobjects are managed by the shared pointer and sp. - std::shared_ptr<CodecCapabilities> duplicate - = std::make_shared<CodecCapabilities>(*(jCodecCaps->getCodecCaps())); - - jobject javaCodecCapsImpl = convertToJavaCodecCapsNativeImpl(env, duplicate); - - return javaCodecCapsImpl; -} - -static void android_media_CodecCapabilities_native_finalize(JNIEnv *env, jobject thiz) { - ALOGV("native_finalize"); - setCodecCapabilities(env, thiz, NULL); -} - -static jint android_media_CodecCapabilities_getMaxSupportedInstances(JNIEnv *env, jobject thiz) { - sp<JCodecCapabilities> codecCaps = getCodecCapabilities(env, thiz); - if (codecCaps == nullptr) { - jniThrowException(env, "java/lang/IllegalStateException", NULL); - return 0; - } - - int maxSupportedInstances = codecCaps->getMaxSupportedInstances(); - return maxSupportedInstances; -} - -static jstring android_media_CodecCapabilities_getMimeType(JNIEnv *env, jobject thiz) { - sp<JCodecCapabilities> codecCaps = getCodecCapabilities(env, thiz); - if (codecCaps == nullptr) { - jniThrowException(env, "java/lang/IllegalStateException", NULL); - return NULL; - } - - std::string mediaType = codecCaps->getMediaType(); - return env->NewStringUTF(mediaType.c_str()); -} - -static jboolean android_media_CodecCapabilities_isFeatureRequired( - JNIEnv *env, jobject thiz, jstring name) { - sp<JCodecCapabilities> codecCaps = getCodecCapabilities(env, thiz); - if (codecCaps == nullptr) { - jniThrowException(env, "java/lang/IllegalStateException", NULL); - return false; - } - - if (name == NULL) { - jniThrowException(env, "java/lang/IllegalArgumentException", NULL); - return -ENOENT; - } - - const char *nameStr = env->GetStringUTFChars(name, NULL); - if (nameStr == NULL) { - // Out of memory exception already pending. - return -ENOENT; - } - - bool isFeatureRequired = codecCaps->isFeatureRequired(nameStr); - - env->ReleaseStringUTFChars(name, nameStr); - - return isFeatureRequired; -} - -static jboolean android_media_CodecCapabilities_isFeatureSupported( - JNIEnv *env, jobject thiz, jstring name) { - sp<JCodecCapabilities> codecCaps = getCodecCapabilities(env, thiz); - if (codecCaps == nullptr) { - jniThrowException(env, "java/lang/IllegalStateException", NULL); - return false; - } - - if (name == NULL) { - jniThrowException(env, "java/lang/IllegalArgumentException", NULL); - return -ENOENT; - } - - const char *nameStr = env->GetStringUTFChars(name, NULL); - if (nameStr == NULL) { - // Out of memory exception already pending. - return -ENOENT; - } - - bool isFeatureSupported = codecCaps->isFeatureSupported(nameStr); - - env->ReleaseStringUTFChars(name, nameStr); - - return isFeatureSupported; -} - -static jboolean android_media_CodecCapabilities_isFormatSupported(JNIEnv *env, jobject thiz, - jobjectArray keys, jobjectArray values) { - sp<JCodecCapabilities> codecCaps = getCodecCapabilities(env, thiz); - if (codecCaps == nullptr) { - jniThrowException(env, "java/lang/IllegalStateException", NULL); - return false; - } - - sp<AMessage> format; - status_t err = ConvertKeyValueArraysToMessage(env, keys, values, &format); - if (err != OK) { - jniThrowException(env, "java/lang/IllegalArgumentException", NULL); - return -ENOENT;; - } - - return codecCaps->isFormatSupported(format); -} - -static jboolean android_media_CodecCapabilities_isRegular(JNIEnv *env, jobject thiz) { - sp<JCodecCapabilities> codecCaps = getCodecCapabilities(env, thiz); - if (codecCaps == nullptr) { - jniThrowException(env, "java/lang/IllegalStateException", NULL); - return false; - } - - bool res = codecCaps->isRegular(); - return res; -} - // ---------------------------------------------------------------------------- static const JNINativeMethod gAudioCapsMethods[] = { @@ -966,19 +372,6 @@ static const JNINativeMethod gEncoderCapsMethods[] = { {"native_isBitrateModeSupported", "(I)Z", (void *)android_media_EncoderCapabilities_isBitrateModeSupported} }; -static const JNINativeMethod gCodecCapsMethods[] = { - { "native_init", "()V", (void *)android_media_CodecCapabilities_native_init }, - { "native_createFromProfileLevel", "(Ljava/lang/String;II)Landroid/media/MediaCodecInfo$CodecCapabilities$CodecCapsNativeImpl;", (void *)android_media_CodecCapabilities_createFromProfileLevel }, - { "native_dup", "()Landroid/media/MediaCodecInfo$CodecCapabilities$CodecCapsNativeImpl;", (void *)android_media_CodecCapabilities_native_dup }, - { "native_finalize", "()V", (void *)android_media_CodecCapabilities_native_finalize }, - { "native_getMaxSupportedInstances", "()I", (void *)android_media_CodecCapabilities_getMaxSupportedInstances }, - { "native_getMimeType", "()Ljava/lang/String;", (void *)android_media_CodecCapabilities_getMimeType }, - { "native_isFeatureRequired", "(Ljava/lang/String;)Z", (void *)android_media_CodecCapabilities_isFeatureRequired }, - { "native_isFeatureSupported", "(Ljava/lang/String;)Z", (void *)android_media_CodecCapabilities_isFeatureSupported }, - { "native_isFormatSupported", "([Ljava/lang/String;[Ljava/lang/Object;)Z", (void *)android_media_CodecCapabilities_isFormatSupported }, - { "native_isRegular", "()Z", (void *)android_media_CodecCapabilities_isRegular }, -}; - int register_android_media_CodecCapabilities(JNIEnv *env) { int result = AndroidRuntime::registerNativeMethods(env, "android/media/MediaCodecInfo$AudioCapabilities$AudioCapsNativeImpl", @@ -1008,8 +401,5 @@ int register_android_media_CodecCapabilities(JNIEnv *env) { return result; } - result = AndroidRuntime::registerNativeMethods(env, - "android/media/MediaCodecInfo$CodecCapabilities$CodecCapsNativeImpl", - gCodecCapsMethods, NELEM(gCodecCapsMethods)); return result; }
\ No newline at end of file diff --git a/media/jni/android_media_CodecCapabilities.h b/media/jni/android_media_CodecCapabilities.h deleted file mode 100644 index 5cca0b503740..000000000000 --- a/media/jni/android_media_CodecCapabilities.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2024, The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef _ANDROID_MEDIA_CODECCAPABILITIES_H_ -#define _ANDROID_MEDIA_CODECCAPABILITIES_H_ - -#include "jni.h" - -#include <media/CodecCapabilities.h> - -namespace android { - -struct JCodecCapabilities : public RefBase { - JCodecCapabilities(std::shared_ptr<CodecCapabilities> codecCaps); - - std::shared_ptr<CodecCapabilities> getCodecCaps() const; - - int32_t getMaxSupportedInstances() const; - std::string getMediaType() const; - bool isFeatureRequired(const std::string& name) const; - bool isFeatureSupported(const std::string& name) const; - bool isFormatSupported(const sp<AMessage> &format) const; - bool isRegular() const; - -private: - std::shared_ptr<CodecCapabilities> mCodecCaps; -}; - -jobject convertToJavaCodecCapabiliites( - JNIEnv *env, std::shared_ptr<CodecCapabilities> codecCaps); - -} - -#endif // _ANDROID_MEDIA_CODECCAPABILITIES_H_
\ No newline at end of file diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp index 695361e60622..fc184fe5c872 100644 --- a/media/jni/android_media_MediaCodec.cpp +++ b/media/jni/android_media_MediaCodec.cpp @@ -16,14 +16,12 @@ //#define LOG_NDEBUG 0 #define LOG_TAG "MediaCodec-JNI" -#include <android_media_codec.h> #include <utils/Log.h> #include <type_traits> #include "android_media_MediaCodec.h" -#include "android_media_CodecCapabilities.h" #include "android_media_MediaCodecLinearBlock.h" #include "android_media_MediaCrypto.h" #include "android_media_MediaDescrambler.h" @@ -140,8 +138,6 @@ static struct { static struct { jclass capsClazz; jmethodID capsCtorId; - jclass cpasImplClazz; - jmethodID capsImplCtorId; jclass profileLevelClazz; jfieldID profileField; jfieldID levelField; @@ -1000,12 +996,10 @@ static jobject getCodecCapabilitiesObject( env->SetIntArrayRegion(colorFormatsArray.get(), i, 1, &val); } - jobject javaCodecCapsImpl = env->NewObject( - gCodecInfo.cpasImplClazz, gCodecInfo.capsImplCtorId, + return env->NewObject( + gCodecInfo.capsClazz, gCodecInfo.capsCtorId, profileLevelArray.get(), colorFormatsArray.get(), isEncoder, defaultFormatRef.get(), detailsRef.get()); - - return env->NewObject(gCodecInfo.capsClazz, gCodecInfo.capsCtorId, javaCodecCapsImpl); } status_t JMediaCodec::getCodecInfo(JNIEnv *env, jobject *codecInfoObject) const { @@ -1033,18 +1027,11 @@ status_t JMediaCodec::getCodecInfo(JNIEnv *env, jobject *codecInfoObject) const env->NewObjectArray(mediaTypes.size(), gCodecInfo.capsClazz, NULL)); for (size_t i = 0; i < mediaTypes.size(); i++) { - jobject jCodecCaps = NULL; - if (android::media::codec::provider_->native_capabilites()) { - const std::shared_ptr<CodecCapabilities> codecCaps - = codecInfo->getCodecCapsFor(mediaTypes[i].c_str()); - jCodecCaps = convertToJavaCodecCapabiliites(env, codecCaps); - } else { - const sp<MediaCodecInfo::Capabilities> caps = - codecInfo->getCapabilitiesFor(mediaTypes[i].c_str()); - jCodecCaps = getCodecCapabilitiesObject( - env, mediaTypes[i].c_str(), isEncoder, caps); - } - ScopedLocalRef<jobject> capsObj(env, jCodecCaps); + const sp<MediaCodecInfo::Capabilities> caps = + codecInfo->getCapabilitiesFor(mediaTypes[i].c_str()); + + ScopedLocalRef<jobject> capsObj(env, getCodecCapabilitiesObject( + env, mediaTypes[i].c_str(), isEncoder, caps)); env->SetObjectArrayElement(capsArrayObj.get(), i, capsObj.get()); } @@ -3890,20 +3877,10 @@ static void android_media_MediaCodec_native_init(JNIEnv *env, jclass) { gCodecInfo.capsClazz = (jclass)env->NewGlobalRef(clazz.get()); method = env->GetMethodID(clazz.get(), "<init>", - "(Landroid/media/MediaCodecInfo$CodecCapabilities$CodecCapsIntf;)V"); - CHECK(method != NULL); - gCodecInfo.capsCtorId = method; - - clazz.reset(env->FindClass( - "android/media/MediaCodecInfo$CodecCapabilities$CodecCapsLegacyImpl")); - CHECK(clazz.get() != NULL); - gCodecInfo.cpasImplClazz = (jclass)env->NewGlobalRef(clazz.get()); - - method = env->GetMethodID(clazz.get(), "<init>", "([Landroid/media/MediaCodecInfo$CodecProfileLevel;[IZ" "Ljava/util/Map;Ljava/util/Map;)V"); CHECK(method != NULL); - gCodecInfo.capsImplCtorId = method; + gCodecInfo.capsCtorId = method; clazz.reset(env->FindClass("android/media/MediaCodecInfo$CodecProfileLevel")); CHECK(clazz.get() != NULL); diff --git a/media/jni/android_media_MediaCodecList.cpp b/media/jni/android_media_MediaCodecList.cpp index 3522b35539ab..07866ac34e4c 100644 --- a/media/jni/android_media_MediaCodecList.cpp +++ b/media/jni/android_media_MediaCodecList.cpp @@ -16,9 +16,6 @@ //#define LOG_NDEBUG 0 #define LOG_TAG "MediaCodec-JNI" - -#include <android_media_codec.h> - #include <utils/Log.h> #include <media/stagefright/foundation/ADebug.h> @@ -35,7 +32,6 @@ #include "android_runtime/AndroidRuntime.h" #include "jni.h" #include <nativehelper/JNIHelp.h> -#include "android_media_CodecCapabilities.h" #include "android_media_Streams.h" using namespace android; @@ -249,113 +245,95 @@ static jobject android_media_MediaCodecList_getCodecCapabilities( return NULL; } - jobject caps; - if (android::media::codec::provider_->native_capabilites()) { - std::shared_ptr<CodecCapabilities> codecCaps = info.info->getCodecCapsFor(typeStr); - caps = android::convertToJavaCodecCapabiliites(env, codecCaps); - } else { - Vector<MediaCodecInfo::ProfileLevel> profileLevels; - Vector<uint32_t> colorFormats; - - sp<AMessage> defaultFormat = new AMessage(); - defaultFormat->setString("mime", typeStr); - - // TODO query default-format also from codec/codec list - const sp<MediaCodecInfo::Capabilities> &capabilities = - info.info->getCapabilitiesFor(typeStr); - env->ReleaseStringUTFChars(type, typeStr); - typeStr = NULL; - if (capabilities == NULL) { - jniThrowException(env, "java/lang/IllegalArgumentException", NULL); - return NULL; - } - - capabilities->getSupportedColorFormats(&colorFormats); - capabilities->getSupportedProfileLevels(&profileLevels); - sp<AMessage> details = capabilities->getDetails(); - bool isEncoder = info.info->isEncoder(); - - jobject defaultFormatObj = NULL; - if (ConvertMessageToMap(env, defaultFormat, &defaultFormatObj)) { - return NULL; - } + Vector<MediaCodecInfo::ProfileLevel> profileLevels; + Vector<uint32_t> colorFormats; - jobject infoObj = NULL; - if (ConvertMessageToMap(env, details, &infoObj)) { - env->DeleteLocalRef(defaultFormatObj); - return NULL; - } + sp<AMessage> defaultFormat = new AMessage(); + defaultFormat->setString("mime", typeStr); - jclass capsImplClazz = env->FindClass( - "android/media/MediaCodecInfo$CodecCapabilities$CodecCapsLegacyImpl"); - CHECK(capsImplClazz != NULL); + // TODO query default-format also from codec/codec list + const sp<MediaCodecInfo::Capabilities> &capabilities = + info.info->getCapabilitiesFor(typeStr); + env->ReleaseStringUTFChars(type, typeStr); + typeStr = NULL; + if (capabilities == NULL) { + jniThrowException(env, "java/lang/IllegalArgumentException", NULL); + return NULL; + } - jclass profileLevelClazz = - env->FindClass("android/media/MediaCodecInfo$CodecProfileLevel"); - CHECK(profileLevelClazz != NULL); + capabilities->getSupportedColorFormats(&colorFormats); + capabilities->getSupportedProfileLevels(&profileLevels); + sp<AMessage> details = capabilities->getDetails(); + bool isEncoder = info.info->isEncoder(); - jobjectArray profileLevelArray = - env->NewObjectArray(profileLevels.size(), profileLevelClazz, NULL); + jobject defaultFormatObj = NULL; + if (ConvertMessageToMap(env, defaultFormat, &defaultFormatObj)) { + return NULL; + } - jfieldID profileField = - env->GetFieldID(profileLevelClazz, "profile", "I"); + jobject infoObj = NULL; + if (ConvertMessageToMap(env, details, &infoObj)) { + env->DeleteLocalRef(defaultFormatObj); + return NULL; + } - jfieldID levelField = - env->GetFieldID(profileLevelClazz, "level", "I"); + jclass capsClazz = + env->FindClass("android/media/MediaCodecInfo$CodecCapabilities"); + CHECK(capsClazz != NULL); - for (size_t i = 0; i < profileLevels.size(); ++i) { - const MediaCodecInfo::ProfileLevel &src = profileLevels.itemAt(i); + jclass profileLevelClazz = + env->FindClass("android/media/MediaCodecInfo$CodecProfileLevel"); + CHECK(profileLevelClazz != NULL); - jobject profileLevelObj = env->AllocObject(profileLevelClazz); + jobjectArray profileLevelArray = + env->NewObjectArray(profileLevels.size(), profileLevelClazz, NULL); - env->SetIntField(profileLevelObj, profileField, src.mProfile); - env->SetIntField(profileLevelObj, levelField, src.mLevel); + jfieldID profileField = + env->GetFieldID(profileLevelClazz, "profile", "I"); - env->SetObjectArrayElement(profileLevelArray, i, profileLevelObj); + jfieldID levelField = + env->GetFieldID(profileLevelClazz, "level", "I"); - env->DeleteLocalRef(profileLevelObj); - profileLevelObj = NULL; - } + for (size_t i = 0; i < profileLevels.size(); ++i) { + const MediaCodecInfo::ProfileLevel &src = profileLevels.itemAt(i); - jintArray colorFormatsArray = env->NewIntArray(colorFormats.size()); + jobject profileLevelObj = env->AllocObject(profileLevelClazz); - for (size_t i = 0; i < colorFormats.size(); ++i) { - jint val = colorFormats.itemAt(i); - env->SetIntArrayRegion(colorFormatsArray, i, 1, &val); - } + env->SetIntField(profileLevelObj, profileField, src.mProfile); + env->SetIntField(profileLevelObj, levelField, src.mLevel); - jmethodID capsImplConstructID = env->GetMethodID(capsImplClazz, "<init>", - "([Landroid/media/MediaCodecInfo$CodecProfileLevel;[IZ" - "Ljava/util/Map;Ljava/util/Map;)V"); + env->SetObjectArrayElement(profileLevelArray, i, profileLevelObj); - jobject capsImpl = env->NewObject(capsImplClazz, capsImplConstructID, - profileLevelArray, colorFormatsArray, isEncoder, - defaultFormatObj, infoObj); + env->DeleteLocalRef(profileLevelObj); + profileLevelObj = NULL; + } - jclass capsClazz = env->FindClass( - "android/media/MediaCodecInfo$CodecCapabilities"); - CHECK(capsClazz != NULL); + jintArray colorFormatsArray = env->NewIntArray(colorFormats.size()); - jmethodID capsConstructID = env->GetMethodID(capsClazz, "<init>", - "(Landroid/media/MediaCodecInfo$CodecCapabilities$CodecCapsIntf;)V"); + for (size_t i = 0; i < colorFormats.size(); ++i) { + jint val = colorFormats.itemAt(i); + env->SetIntArrayRegion(colorFormatsArray, i, 1, &val); + } - caps = env->NewObject(capsClazz, capsConstructID, capsImpl); + jmethodID capsConstructID = env->GetMethodID(capsClazz, "<init>", + "([Landroid/media/MediaCodecInfo$CodecProfileLevel;[IZ" + "Ljava/util/Map;Ljava/util/Map;)V"); - env->DeleteLocalRef(profileLevelArray); - profileLevelArray = NULL; + jobject caps = env->NewObject(capsClazz, capsConstructID, + profileLevelArray, colorFormatsArray, isEncoder, + defaultFormatObj, infoObj); - env->DeleteLocalRef(colorFormatsArray); - colorFormatsArray = NULL; + env->DeleteLocalRef(profileLevelArray); + profileLevelArray = NULL; - env->DeleteLocalRef(defaultFormatObj); - defaultFormatObj = NULL; + env->DeleteLocalRef(colorFormatsArray); + colorFormatsArray = NULL; - env->DeleteLocalRef(infoObj); - infoObj = NULL; + env->DeleteLocalRef(defaultFormatObj); + defaultFormatObj = NULL; - env->DeleteLocalRef(capsImpl); - capsImpl = NULL; - } + env->DeleteLocalRef(infoObj); + infoObj = NULL; return caps; } |