Check CodecProfileLevels for audio decoders

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=208796064
This commit is contained in:
andrewlewis 2018-08-15 03:25:40 -07:00 committed by Oliver Woodman
parent e7ef75342c
commit 80f5b7e7b2
4 changed files with 95 additions and 31 deletions

View File

@ -287,13 +287,8 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
}
// Check capabilities for the first decoder in the list, which takes priority.
MediaCodecInfo decoderInfo = decoderInfos.get(0);
// Note: We assume support for unknown sampleRate and channelCount.
boolean decoderCapable = Util.SDK_INT < 21
|| ((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;
boolean isFormatSupported = decoderInfo.isFormatSupported(format);
int formatSupport = isFormatSupported ? FORMAT_HANDLED : FORMAT_EXCEEDS_CAPABILITIES;
return ADAPTIVE_NOT_SEAMLESS | tunnelingSupport | formatSupport;
}

View File

@ -88,6 +88,8 @@ public final class MediaCodecInfo {
/** Whether this instance describes a passthrough codec. */
public final boolean passthrough;
private final boolean isVideo;
/**
* Creates an instance representing an audio passthrough decoder.
*
@ -157,6 +159,7 @@ public final class MediaCodecInfo {
adaptive = !forceDisableAdaptive && capabilities != null && isAdaptive(capabilities);
tunneling = capabilities != null && isTunneling(capabilities);
secure = forceSecure || (capabilities != null && isSecure(capabilities));
isVideo = MimeTypes.isVideo(mimeType);
}
@Override
@ -187,6 +190,41 @@ public final class MediaCodecInfo {
: 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
* decide, returns true.

View File

@ -74,6 +74,9 @@ public final class MediaCodecUtil {
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_HVC1 = "hvc1";
// MP4A AAC.
private static final SparseIntArray MP4A_AUDIO_OBJECT_TYPE_TO_PROFILE;
private static final String CODEC_ID_MP4A = "mp4a";
// Lazily initialized.
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
* recognized, or null otherwise
*/
public static Pair<Integer, Integer> getCodecProfileAndLevel(String codec) {
public static @Nullable Pair<Integer, Integer> getCodecProfileAndLevel(String codec) {
if (codec == null) {
return null;
}
@ -211,6 +214,8 @@ public final class MediaCodecUtil {
case CODEC_ID_AVC1:
case CODEC_ID_AVC2:
return getAvcProfileAndLevel(codec, parts);
case CODEC_ID_MP4A:
return getAacCodecProfileAndLevel(codec, parts);
default:
return null;
}
@ -445,8 +450,8 @@ public final class MediaCodecUtil {
return new Pair<>(profile, level);
}
private static Pair<Integer, Integer> getAvcProfileAndLevel(String codec, String[] codecsParts) {
if (codecsParts.length < 2) {
private static Pair<Integer, Integer> getAvcProfileAndLevel(String codec, String[] parts) {
if (parts.length < 2) {
// The codec has fewer parts than required by the AVC codec string format.
Log.w(TAG, "Ignoring malformed AVC codec string: " + codec);
return null;
@ -454,14 +459,14 @@ public final class MediaCodecUtil {
Integer profileInteger;
Integer levelInteger;
try {
if (codecsParts[1].length() == 6) {
if (parts[1].length() == 6) {
// Format: avc1.xxccyy, where xx is profile and yy level, both hexadecimal.
profileInteger = Integer.parseInt(codecsParts[1].substring(0, 2), 16);
levelInteger = Integer.parseInt(codecsParts[1].substring(4), 16);
} else if (codecsParts.length >= 3) {
profileInteger = Integer.parseInt(parts[1].substring(0, 2), 16);
levelInteger = Integer.parseInt(parts[1].substring(4), 16);
} else if (parts.length >= 3) {
// Format: avc1.xx.[y]yy where xx is profile and [y]yy level, both decimal.
profileInteger = Integer.parseInt(codecsParts[1]);
levelInteger = Integer.parseInt(codecsParts[2]);
profileInteger = Integer.parseInt(parts[1]);
levelInteger = Integer.parseInt(parts[2]);
} else {
// We don't recognize the format.
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 {
/**
@ -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("H183", CodecProfileLevel.HEVCHighTierLevel61);
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);
}
}

View File

@ -265,23 +265,10 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
}
// Check capabilities for the first decoder in the list, which takes priority.
MediaCodecInfo decoderInfo = decoderInfos.get(0);
boolean decoderCapable = decoderInfo.isCodecSupported(format.codecs);
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 + "]");
}
}
}
boolean isFormatSupported = decoderInfo.isFormatSupported(format);
int adaptiveSupport = decoderInfo.adaptive ? ADAPTIVE_SEAMLESS : ADAPTIVE_NOT_SEAMLESS;
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;
}