summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--media/java/android/media/MediaCodecInfo.java1099
-rw-r--r--media/jni/android_media_CodecCapabilities.cpp610
-rw-r--r--media/jni/android_media_CodecCapabilities.h47
-rw-r--r--media/jni/android_media_MediaCodec.cpp39
-rw-r--r--media/jni/android_media_MediaCodecList.cpp154
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;
}