Commit a3ded59f by olly Committed by Oliver Woodman

Check codec profile/level for AV1

Add appropriate unit tests.

PiperOrigin-RevId: 258552404
parent 5e4f5254
...@@ -81,10 +81,13 @@ public final class MediaCodecUtil { ...@@ -81,10 +81,13 @@ public final class MediaCodecUtil {
private static final Map<String, Integer> DOLBY_VISION_STRING_TO_LEVEL; 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_DVHE = "dvhe";
private static final String CODEC_ID_DVH1 = "dvh1"; 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. // MP4A AAC.
private static final SparseIntArray MP4A_AUDIO_OBJECT_TYPE_TO_PROFILE; private static final SparseIntArray MP4A_AUDIO_OBJECT_TYPE_TO_PROFILE;
private static final String CODEC_ID_MP4A = "mp4a"; private static final String CODEC_ID_MP4A = "mp4a";
// Lazily initialized. // Lazily initialized.
private static int maxH264DecodableFrameSize = -1; private static int maxH264DecodableFrameSize = -1;
...@@ -239,8 +242,6 @@ public final class MediaCodecUtil { ...@@ -239,8 +242,6 @@ public final class MediaCodecUtil {
if (codec == null) { if (codec == null) {
return 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("\\."); String[] parts = codec.split("\\.");
switch (parts[0]) { switch (parts[0]) {
case CODEC_ID_AVC1: case CODEC_ID_AVC1:
...@@ -254,6 +255,8 @@ public final class MediaCodecUtil { ...@@ -254,6 +255,8 @@ public final class MediaCodecUtil {
case CODEC_ID_DVHE: case CODEC_ID_DVHE:
case CODEC_ID_DVH1: case CODEC_ID_DVH1:
return getDolbyVisionProfileAndLevel(codec, parts); return getDolbyVisionProfileAndLevel(codec, parts);
case CODEC_ID_AV01:
return getAv1ProfileAndLevel(codec, parts);
case CODEC_ID_MP4A: case CODEC_ID_MP4A:
return getAacCodecProfileAndLevel(codec, parts); return getAacCodecProfileAndLevel(codec, parts);
default: default:
...@@ -684,6 +687,48 @@ public final class MediaCodecUtil { ...@@ -684,6 +687,48 @@ public final class MediaCodecUtil {
return new Pair<>(profile, level); 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. * Conversion values taken from ISO 14496-10 Table A-1.
* *
...@@ -1010,6 +1055,32 @@ public final class MediaCodecUtil { ...@@ -1010,6 +1055,32 @@ public final class MediaCodecUtil {
DOLBY_VISION_STRING_TO_LEVEL.put("08", CodecProfileLevel.DolbyVisionLevelUhd48); DOLBY_VISION_STRING_TO_LEVEL.put("08", CodecProfileLevel.DolbyVisionLevelUhd48);
DOLBY_VISION_STRING_TO_LEVEL.put("09", CodecProfileLevel.DolbyVisionLevelUhd60); 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 = new SparseIntArray();
MP4A_AUDIO_OBJECT_TYPE_TO_PROFILE.put(1, CodecProfileLevel.AACObjectMain); 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(2, CodecProfileLevel.AACObjectLC);
......
...@@ -61,6 +61,31 @@ public final class MediaCodecUtilTest { ...@@ -61,6 +61,31 @@ public final class MediaCodecUtilTest {
} }
@Test @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() { public void getCodecProfileAndLevel_rejectsNullCodecString() {
assertThat(MediaCodecUtil.getCodecProfileAndLevel(/* codec= */ null)).isNull(); assertThat(MediaCodecUtil.getCodecProfileAndLevel(/* codec= */ null)).isNull();
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment