Check codec profile/level for AV1

Add appropriate unit tests.

PiperOrigin-RevId: 258552404
This commit is contained in:
olly 2019-07-17 13:59:14 +01:00 committed by Oliver Woodman
parent 5e4f52541d
commit a3ded59f28
2 changed files with 99 additions and 3 deletions

View file

@ -81,10 +81,13 @@ public final class MediaCodecUtil {
private static final Map<String, Integer> DOLBY_VISION_STRING_TO_LEVEL;
private static final String CODEC_ID_DVHE = "dvhe";
private static final String CODEC_ID_DVH1 = "dvh1";
// AV1.
private static final SparseIntArray AV1_LEVEL_NUMBER_TO_CONST;
private static final String CODEC_ID_AV01 = "av01";
// 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;
@ -239,8 +242,6 @@ public final class MediaCodecUtil {
if (codec == null) {
return null;
}
// TODO: Check codec profile/level for AV1 once targeting Android Q and [Internal: b/128552878]
// has been fixed.
String[] parts = codec.split("\\.");
switch (parts[0]) {
case CODEC_ID_AVC1:
@ -254,6 +255,8 @@ public final class MediaCodecUtil {
case CODEC_ID_DVHE:
case CODEC_ID_DVH1:
return getDolbyVisionProfileAndLevel(codec, parts);
case CODEC_ID_AV01:
return getAv1ProfileAndLevel(codec, parts);
case CODEC_ID_MP4A:
return getAacCodecProfileAndLevel(codec, parts);
default:
@ -684,6 +687,48 @@ public final class MediaCodecUtil {
return new Pair<>(profile, level);
}
private static Pair<Integer, Integer> getAv1ProfileAndLevel(String codec, String[] parts) {
if (parts.length < 4) {
Log.w(TAG, "Ignoring malformed AV1 codec string: " + codec);
return null;
}
int profileInteger;
int levelInteger;
int bitDepthInteger;
try {
profileInteger = Integer.parseInt(parts[1]);
levelInteger = Integer.parseInt(parts[2].substring(0, 2));
bitDepthInteger = Integer.parseInt(parts[3]);
} catch (NumberFormatException e) {
Log.w(TAG, "Ignoring malformed AV1 codec string: " + codec);
return null;
}
// TODO: Recognize HDR profiles. Currently, the profile is assumed to be either Main8 or Main10.
// See [Internal: b/124435216].
if (profileInteger != 0) {
Log.w(TAG, "Unknown AV1 profile: " + profileInteger);
return null;
}
if (bitDepthInteger != 8 && bitDepthInteger != 10) {
Log.w(TAG, "Unknown AV1 bit depth: " + bitDepthInteger);
return null;
}
int profile;
if (bitDepthInteger == 8) {
profile = CodecProfileLevel.AV1ProfileMain8;
} else {
profile = CodecProfileLevel.AV1ProfileMain10;
}
int level = AV1_LEVEL_NUMBER_TO_CONST.get(levelInteger, -1);
if (level == -1) {
Log.w(TAG, "Unknown AV1 level: " + levelInteger);
return null;
}
return new Pair<>(profile, level);
}
/**
* Conversion values taken from ISO 14496-10 Table A-1.
*
@ -1010,6 +1055,32 @@ public final class MediaCodecUtil {
DOLBY_VISION_STRING_TO_LEVEL.put("08", CodecProfileLevel.DolbyVisionLevelUhd48);
DOLBY_VISION_STRING_TO_LEVEL.put("09", CodecProfileLevel.DolbyVisionLevelUhd60);
AV1_LEVEL_NUMBER_TO_CONST = new SparseIntArray();
AV1_LEVEL_NUMBER_TO_CONST.put(0, CodecProfileLevel.AV1Level2);
AV1_LEVEL_NUMBER_TO_CONST.put(1, CodecProfileLevel.AV1Level21);
AV1_LEVEL_NUMBER_TO_CONST.put(2, CodecProfileLevel.AV1Level22);
AV1_LEVEL_NUMBER_TO_CONST.put(3, CodecProfileLevel.AV1Level23);
AV1_LEVEL_NUMBER_TO_CONST.put(4, CodecProfileLevel.AV1Level3);
AV1_LEVEL_NUMBER_TO_CONST.put(5, CodecProfileLevel.AV1Level31);
AV1_LEVEL_NUMBER_TO_CONST.put(6, CodecProfileLevel.AV1Level32);
AV1_LEVEL_NUMBER_TO_CONST.put(7, CodecProfileLevel.AV1Level33);
AV1_LEVEL_NUMBER_TO_CONST.put(8, CodecProfileLevel.AV1Level4);
AV1_LEVEL_NUMBER_TO_CONST.put(9, CodecProfileLevel.AV1Level41);
AV1_LEVEL_NUMBER_TO_CONST.put(10, CodecProfileLevel.AV1Level42);
AV1_LEVEL_NUMBER_TO_CONST.put(11, CodecProfileLevel.AV1Level43);
AV1_LEVEL_NUMBER_TO_CONST.put(12, CodecProfileLevel.AV1Level5);
AV1_LEVEL_NUMBER_TO_CONST.put(13, CodecProfileLevel.AV1Level51);
AV1_LEVEL_NUMBER_TO_CONST.put(14, CodecProfileLevel.AV1Level52);
AV1_LEVEL_NUMBER_TO_CONST.put(15, CodecProfileLevel.AV1Level53);
AV1_LEVEL_NUMBER_TO_CONST.put(16, CodecProfileLevel.AV1Level6);
AV1_LEVEL_NUMBER_TO_CONST.put(17, CodecProfileLevel.AV1Level61);
AV1_LEVEL_NUMBER_TO_CONST.put(18, CodecProfileLevel.AV1Level62);
AV1_LEVEL_NUMBER_TO_CONST.put(19, CodecProfileLevel.AV1Level63);
AV1_LEVEL_NUMBER_TO_CONST.put(20, CodecProfileLevel.AV1Level7);
AV1_LEVEL_NUMBER_TO_CONST.put(21, CodecProfileLevel.AV1Level71);
AV1_LEVEL_NUMBER_TO_CONST.put(22, CodecProfileLevel.AV1Level72);
AV1_LEVEL_NUMBER_TO_CONST.put(23, CodecProfileLevel.AV1Level73);
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);

View file

@ -60,6 +60,31 @@ public final class MediaCodecUtilTest {
MediaCodecInfo.CodecProfileLevel.DolbyVisionLevelFhd60);
}
@Test
public void getCodecProfileAndLevel_handlesAv1ProfileMain8CodecString() {
assertCodecProfileAndLevelForCodecsString(
"av01.0.10M.08",
MediaCodecInfo.CodecProfileLevel.AV1ProfileMain8,
MediaCodecInfo.CodecProfileLevel.AV1Level42);
}
@Test
public void getCodecProfileAndLevel_handlesAv1ProfileMain10CodecString() {
assertCodecProfileAndLevelForCodecsString(
"av01.0.20M.10",
MediaCodecInfo.CodecProfileLevel.AV1ProfileMain10,
MediaCodecInfo.CodecProfileLevel.AV1Level7);
}
@Test
public void getCodecProfileAndLevel_handlesFullAv1CodecString() {
// Example from https://aomediacodec.github.io/av1-isobmff/#codecsparam.
assertCodecProfileAndLevelForCodecsString(
"av01.0.04M.10.0.112.09.16.09.0",
MediaCodecInfo.CodecProfileLevel.AV1ProfileMain10,
MediaCodecInfo.CodecProfileLevel.AV1Level3);
}
@Test
public void getCodecProfileAndLevel_rejectsNullCodecString() {
assertThat(MediaCodecUtil.getCodecProfileAndLevel(/* codec= */ null)).isNull();