Check CodecProfileLevels for audio decoders
------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=208796064
This commit is contained in:
parent
e7ef75342c
commit
80f5b7e7b2
@ -287,13 +287,8 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
|
|||||||
}
|
}
|
||||||
// Check capabilities for the first decoder in the list, which takes priority.
|
// Check capabilities for the first decoder in the list, which takes priority.
|
||||||
MediaCodecInfo decoderInfo = decoderInfos.get(0);
|
MediaCodecInfo decoderInfo = decoderInfos.get(0);
|
||||||
// Note: We assume support for unknown sampleRate and channelCount.
|
boolean isFormatSupported = decoderInfo.isFormatSupported(format);
|
||||||
boolean decoderCapable = Util.SDK_INT < 21
|
int formatSupport = isFormatSupported ? FORMAT_HANDLED : FORMAT_EXCEEDS_CAPABILITIES;
|
||||||
|| ((format.sampleRate == Format.NO_VALUE
|
|
||||||
|| decoderInfo.isAudioSampleRateSupportedV21(format.sampleRate))
|
|
||||||
&& (format.channelCount == Format.NO_VALUE
|
|
||||||
|| decoderInfo.isAudioChannelCountSupportedV21(format.channelCount)));
|
|
||||||
int formatSupport = decoderCapable ? FORMAT_HANDLED : FORMAT_EXCEEDS_CAPABILITIES;
|
|
||||||
return ADAPTIVE_NOT_SEAMLESS | tunnelingSupport | formatSupport;
|
return ADAPTIVE_NOT_SEAMLESS | tunnelingSupport | formatSupport;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -88,6 +88,8 @@ public final class MediaCodecInfo {
|
|||||||
/** Whether this instance describes a passthrough codec. */
|
/** Whether this instance describes a passthrough codec. */
|
||||||
public final boolean passthrough;
|
public final boolean passthrough;
|
||||||
|
|
||||||
|
private final boolean isVideo;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an instance representing an audio passthrough decoder.
|
* Creates an instance representing an audio passthrough decoder.
|
||||||
*
|
*
|
||||||
@ -157,6 +159,7 @@ public final class MediaCodecInfo {
|
|||||||
adaptive = !forceDisableAdaptive && capabilities != null && isAdaptive(capabilities);
|
adaptive = !forceDisableAdaptive && capabilities != null && isAdaptive(capabilities);
|
||||||
tunneling = capabilities != null && isTunneling(capabilities);
|
tunneling = capabilities != null && isTunneling(capabilities);
|
||||||
secure = forceSecure || (capabilities != null && isSecure(capabilities));
|
secure = forceSecure || (capabilities != null && isSecure(capabilities));
|
||||||
|
isVideo = MimeTypes.isVideo(mimeType);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -187,6 +190,41 @@ public final class MediaCodecInfo {
|
|||||||
: getMaxSupportedInstancesV23(capabilities);
|
: getMaxSupportedInstancesV23(capabilities);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether the decoder may support decoding the given {@code format}.
|
||||||
|
*
|
||||||
|
* @param format The input media format.
|
||||||
|
* @return Whether the decoder may support decoding the given {@code format}.
|
||||||
|
* @throws MediaCodecUtil.DecoderQueryException Thrown if an error occurs while querying decoders.
|
||||||
|
*/
|
||||||
|
public boolean isFormatSupported(Format format) throws MediaCodecUtil.DecoderQueryException {
|
||||||
|
if (!isCodecSupported(format.codecs)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isVideo) {
|
||||||
|
if (format.width <= 0 || format.height <= 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (Util.SDK_INT >= 21) {
|
||||||
|
return isVideoSizeAndRateSupportedV21(format.width, format.height, format.frameRate);
|
||||||
|
} else {
|
||||||
|
boolean isFormatSupported =
|
||||||
|
format.width * format.height <= MediaCodecUtil.maxH264DecodableFrameSize();
|
||||||
|
if (!isFormatSupported) {
|
||||||
|
logNoSupport("legacyFrameSize, " + format.width + "x" + format.height);
|
||||||
|
}
|
||||||
|
return isFormatSupported;
|
||||||
|
}
|
||||||
|
} else { // Audio
|
||||||
|
return Util.SDK_INT < 21
|
||||||
|
|| ((format.sampleRate == Format.NO_VALUE
|
||||||
|
|| isAudioSampleRateSupportedV21(format.sampleRate))
|
||||||
|
&& (format.channelCount == Format.NO_VALUE
|
||||||
|
|| isAudioChannelCountSupportedV21(format.channelCount)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether the decoder supports the given {@code codec}. If there is insufficient information to
|
* Whether the decoder supports the given {@code codec}. If there is insufficient information to
|
||||||
* decide, returns true.
|
* decide, returns true.
|
||||||
|
@ -74,6 +74,9 @@ public final class MediaCodecUtil {
|
|||||||
private static final Map<String, Integer> HEVC_CODEC_STRING_TO_PROFILE_LEVEL;
|
private static final Map<String, Integer> HEVC_CODEC_STRING_TO_PROFILE_LEVEL;
|
||||||
private static final String CODEC_ID_HEV1 = "hev1";
|
private static final String CODEC_ID_HEV1 = "hev1";
|
||||||
private static final String CODEC_ID_HVC1 = "hvc1";
|
private static final String CODEC_ID_HVC1 = "hvc1";
|
||||||
|
// MP4A AAC.
|
||||||
|
private static final SparseIntArray MP4A_AUDIO_OBJECT_TYPE_TO_PROFILE;
|
||||||
|
private static final String CODEC_ID_MP4A = "mp4a";
|
||||||
|
|
||||||
// Lazily initialized.
|
// Lazily initialized.
|
||||||
private static int maxH264DecodableFrameSize = -1;
|
private static int maxH264DecodableFrameSize = -1;
|
||||||
@ -199,7 +202,7 @@ public final class MediaCodecUtil {
|
|||||||
* @return A pair (profile constant, level constant) if {@code codec} is well-formed and
|
* @return A pair (profile constant, level constant) if {@code codec} is well-formed and
|
||||||
* recognized, or null otherwise
|
* recognized, or null otherwise
|
||||||
*/
|
*/
|
||||||
public static Pair<Integer, Integer> getCodecProfileAndLevel(String codec) {
|
public static @Nullable Pair<Integer, Integer> getCodecProfileAndLevel(String codec) {
|
||||||
if (codec == null) {
|
if (codec == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -211,6 +214,8 @@ public final class MediaCodecUtil {
|
|||||||
case CODEC_ID_AVC1:
|
case CODEC_ID_AVC1:
|
||||||
case CODEC_ID_AVC2:
|
case CODEC_ID_AVC2:
|
||||||
return getAvcProfileAndLevel(codec, parts);
|
return getAvcProfileAndLevel(codec, parts);
|
||||||
|
case CODEC_ID_MP4A:
|
||||||
|
return getAacCodecProfileAndLevel(codec, parts);
|
||||||
default:
|
default:
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -445,8 +450,8 @@ public final class MediaCodecUtil {
|
|||||||
return new Pair<>(profile, level);
|
return new Pair<>(profile, level);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Pair<Integer, Integer> getAvcProfileAndLevel(String codec, String[] codecsParts) {
|
private static Pair<Integer, Integer> getAvcProfileAndLevel(String codec, String[] parts) {
|
||||||
if (codecsParts.length < 2) {
|
if (parts.length < 2) {
|
||||||
// The codec has fewer parts than required by the AVC codec string format.
|
// The codec has fewer parts than required by the AVC codec string format.
|
||||||
Log.w(TAG, "Ignoring malformed AVC codec string: " + codec);
|
Log.w(TAG, "Ignoring malformed AVC codec string: " + codec);
|
||||||
return null;
|
return null;
|
||||||
@ -454,14 +459,14 @@ public final class MediaCodecUtil {
|
|||||||
Integer profileInteger;
|
Integer profileInteger;
|
||||||
Integer levelInteger;
|
Integer levelInteger;
|
||||||
try {
|
try {
|
||||||
if (codecsParts[1].length() == 6) {
|
if (parts[1].length() == 6) {
|
||||||
// Format: avc1.xxccyy, where xx is profile and yy level, both hexadecimal.
|
// Format: avc1.xxccyy, where xx is profile and yy level, both hexadecimal.
|
||||||
profileInteger = Integer.parseInt(codecsParts[1].substring(0, 2), 16);
|
profileInteger = Integer.parseInt(parts[1].substring(0, 2), 16);
|
||||||
levelInteger = Integer.parseInt(codecsParts[1].substring(4), 16);
|
levelInteger = Integer.parseInt(parts[1].substring(4), 16);
|
||||||
} else if (codecsParts.length >= 3) {
|
} else if (parts.length >= 3) {
|
||||||
// Format: avc1.xx.[y]yy where xx is profile and [y]yy level, both decimal.
|
// Format: avc1.xx.[y]yy where xx is profile and [y]yy level, both decimal.
|
||||||
profileInteger = Integer.parseInt(codecsParts[1]);
|
profileInteger = Integer.parseInt(parts[1]);
|
||||||
levelInteger = Integer.parseInt(codecsParts[2]);
|
levelInteger = Integer.parseInt(parts[2]);
|
||||||
} else {
|
} else {
|
||||||
// We don't recognize the format.
|
// We don't recognize the format.
|
||||||
Log.w(TAG, "Ignoring malformed AVC codec string: " + codec);
|
Log.w(TAG, "Ignoring malformed AVC codec string: " + codec);
|
||||||
@ -514,6 +519,31 @@ public final class MediaCodecUtil {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static @Nullable Pair<Integer, Integer> getAacCodecProfileAndLevel(
|
||||||
|
String codec, String[] parts) {
|
||||||
|
if (parts.length != 3) {
|
||||||
|
Log.w(TAG, "Ignoring malformed MP4A codec string: " + codec);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
// Get the object type indication, which is a hexadecimal value (see RFC 6381/ISO 14496-1).
|
||||||
|
int objectTypeIndication = Integer.parseInt(parts[1], 16);
|
||||||
|
String mimeType = MimeTypes.getMimeTypeFromMp4ObjectType(objectTypeIndication);
|
||||||
|
if (MimeTypes.AUDIO_AAC.equals(mimeType)) {
|
||||||
|
// For MPEG-4 audio this is followed by an audio object type indication as a decimal number.
|
||||||
|
int audioObjectTypeIndication = Integer.parseInt(parts[2]);
|
||||||
|
int profile = MP4A_AUDIO_OBJECT_TYPE_TO_PROFILE.get(audioObjectTypeIndication, -1);
|
||||||
|
if (profile != -1) {
|
||||||
|
// Level is set to zero in AAC decoder CodecProfileLevels.
|
||||||
|
return new Pair<>(profile, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
Log.w(TAG, "Ignoring malformed MP4A codec string: " + codec);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
private interface MediaCodecListCompat {
|
private interface MediaCodecListCompat {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -725,6 +755,20 @@ public final class MediaCodecUtil {
|
|||||||
HEVC_CODEC_STRING_TO_PROFILE_LEVEL.put("H180", CodecProfileLevel.HEVCHighTierLevel6);
|
HEVC_CODEC_STRING_TO_PROFILE_LEVEL.put("H180", CodecProfileLevel.HEVCHighTierLevel6);
|
||||||
HEVC_CODEC_STRING_TO_PROFILE_LEVEL.put("H183", CodecProfileLevel.HEVCHighTierLevel61);
|
HEVC_CODEC_STRING_TO_PROFILE_LEVEL.put("H183", CodecProfileLevel.HEVCHighTierLevel61);
|
||||||
HEVC_CODEC_STRING_TO_PROFILE_LEVEL.put("H186", CodecProfileLevel.HEVCHighTierLevel62);
|
HEVC_CODEC_STRING_TO_PROFILE_LEVEL.put("H186", CodecProfileLevel.HEVCHighTierLevel62);
|
||||||
|
|
||||||
|
MP4A_AUDIO_OBJECT_TYPE_TO_PROFILE = new SparseIntArray();
|
||||||
|
MP4A_AUDIO_OBJECT_TYPE_TO_PROFILE.put(1, CodecProfileLevel.AACObjectMain);
|
||||||
|
MP4A_AUDIO_OBJECT_TYPE_TO_PROFILE.put(2, CodecProfileLevel.AACObjectLC);
|
||||||
|
MP4A_AUDIO_OBJECT_TYPE_TO_PROFILE.put(3, CodecProfileLevel.AACObjectSSR);
|
||||||
|
MP4A_AUDIO_OBJECT_TYPE_TO_PROFILE.put(4, CodecProfileLevel.AACObjectLTP);
|
||||||
|
MP4A_AUDIO_OBJECT_TYPE_TO_PROFILE.put(5, CodecProfileLevel.AACObjectHE);
|
||||||
|
MP4A_AUDIO_OBJECT_TYPE_TO_PROFILE.put(6, CodecProfileLevel.AACObjectScalable);
|
||||||
|
MP4A_AUDIO_OBJECT_TYPE_TO_PROFILE.put(17, CodecProfileLevel.AACObjectERLC);
|
||||||
|
MP4A_AUDIO_OBJECT_TYPE_TO_PROFILE.put(20, CodecProfileLevel.AACObjectERScalable);
|
||||||
|
MP4A_AUDIO_OBJECT_TYPE_TO_PROFILE.put(23, CodecProfileLevel.AACObjectLD);
|
||||||
|
MP4A_AUDIO_OBJECT_TYPE_TO_PROFILE.put(29, CodecProfileLevel.AACObjectHE_PS);
|
||||||
|
MP4A_AUDIO_OBJECT_TYPE_TO_PROFILE.put(39, CodecProfileLevel.AACObjectELD);
|
||||||
|
MP4A_AUDIO_OBJECT_TYPE_TO_PROFILE.put(42, CodecProfileLevel.AACObjectXHE);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -265,23 +265,10 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
|||||||
}
|
}
|
||||||
// Check capabilities for the first decoder in the list, which takes priority.
|
// Check capabilities for the first decoder in the list, which takes priority.
|
||||||
MediaCodecInfo decoderInfo = decoderInfos.get(0);
|
MediaCodecInfo decoderInfo = decoderInfos.get(0);
|
||||||
boolean decoderCapable = decoderInfo.isCodecSupported(format.codecs);
|
boolean isFormatSupported = decoderInfo.isFormatSupported(format);
|
||||||
if (decoderCapable && format.width > 0 && format.height > 0) {
|
|
||||||
if (Util.SDK_INT >= 21) {
|
|
||||||
decoderCapable = decoderInfo.isVideoSizeAndRateSupportedV21(format.width, format.height,
|
|
||||||
format.frameRate);
|
|
||||||
} else {
|
|
||||||
decoderCapable = format.width * format.height <= MediaCodecUtil.maxH264DecodableFrameSize();
|
|
||||||
if (!decoderCapable) {
|
|
||||||
Log.d(TAG, "FalseCheck [legacyFrameSize, " + format.width + "x" + format.height + "] ["
|
|
||||||
+ Util.DEVICE_DEBUG_INFO + "]");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int adaptiveSupport = decoderInfo.adaptive ? ADAPTIVE_SEAMLESS : ADAPTIVE_NOT_SEAMLESS;
|
int adaptiveSupport = decoderInfo.adaptive ? ADAPTIVE_SEAMLESS : ADAPTIVE_NOT_SEAMLESS;
|
||||||
int tunnelingSupport = decoderInfo.tunneling ? TUNNELING_SUPPORTED : TUNNELING_NOT_SUPPORTED;
|
int tunnelingSupport = decoderInfo.tunneling ? TUNNELING_SUPPORTED : TUNNELING_NOT_SUPPORTED;
|
||||||
int formatSupport = decoderCapable ? FORMAT_HANDLED : FORMAT_EXCEEDS_CAPABILITIES;
|
int formatSupport = isFormatSupported ? FORMAT_HANDLED : FORMAT_EXCEEDS_CAPABILITIES;
|
||||||
return adaptiveSupport | tunnelingSupport | formatSupport;
|
return adaptiveSupport | tunnelingSupport | formatSupport;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user