1 /* 2 * Copyright (C) 2022 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.mediav2.cts; 18 19 import static android.media.MediaCodecInfo.CodecCapabilities.COLOR_Format32bitABGR2101010; 20 import static android.media.MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface; 21 import static android.media.MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Flexible; 22 import static android.media.MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420PackedPlanar; 23 import static android.media.MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420PackedSemiPlanar; 24 import static android.media.MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Planar; 25 import static android.media.MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar; 26 import static android.media.MediaCodecInfo.CodecCapabilities.COLOR_FormatYUVP010; 27 import static android.media.MediaCodecInfo.CodecCapabilities.FEATURE_DynamicColorAspects; 28 import static android.media.MediaCodecInfo.CodecCapabilities.FEATURE_HdrEditing; 29 import static android.media.MediaCodecInfo.CodecCapabilities.FEATURE_HlgEditing; 30 import static android.media.codec.Flags.FLAG_DYNAMIC_COLOR_ASPECTS; 31 import static android.media.codec.Flags.FLAG_IN_PROCESS_SW_AUDIO_CODEC; 32 import static android.mediav2.common.cts.CodecTestBase.BOARD_FIRST_SDK_IS_AT_LEAST_202404; 33 import static android.mediav2.common.cts.CodecTestBase.BOARD_SDK_IS_AT_LEAST_T; 34 import static android.mediav2.common.cts.CodecTestBase.FIRST_SDK_IS_AT_LEAST_T; 35 import static android.mediav2.common.cts.CodecTestBase.IS_AT_LEAST_T; 36 import static android.mediav2.common.cts.CodecTestBase.IS_AT_LEAST_V; 37 import static android.mediav2.common.cts.CodecTestBase.IS_HDR_CAPTURE_SUPPORTED; 38 import static android.mediav2.common.cts.CodecTestBase.PROFILE_HDR10_MAP; 39 import static android.mediav2.common.cts.CodecTestBase.PROFILE_HDR10_PLUS_MAP; 40 import static android.mediav2.common.cts.CodecTestBase.VNDK_IS_AT_LEAST_T; 41 import static android.mediav2.common.cts.CodecTestBase.canDisplaySupportHDRContent; 42 import static android.mediav2.common.cts.CodecTestBase.codecFilter; 43 import static android.mediav2.common.cts.CodecTestBase.codecPrefix; 44 import static android.mediav2.common.cts.CodecTestBase.isFeatureSupported; 45 import static android.mediav2.common.cts.CodecTestBase.isVendorCodec; 46 import static android.mediav2.common.cts.CodecTestBase.selectCodecs; 47 48 import static org.junit.Assert.assertEquals; 49 import static org.junit.Assert.assertFalse; 50 import static org.junit.Assert.assertTrue; 51 52 import android.media.MediaCodecInfo; 53 import android.media.MediaCodecInfo.CodecProfileLevel; 54 import android.media.MediaCodecList; 55 import android.media.MediaFormat; 56 import android.mediav2.common.cts.CodecTestBase; 57 import android.os.Build; 58 import android.platform.test.annotations.RequiresFlagsEnabled; 59 import android.platform.test.flag.junit.CheckFlagsRule; 60 import android.platform.test.flag.junit.DeviceFlagsValueProvider; 61 import android.util.Range; 62 63 import androidx.test.filters.SdkSuppress; 64 import androidx.test.filters.SmallTest; 65 66 import com.android.compatibility.common.util.ApiTest; 67 import com.android.compatibility.common.util.CddTest; 68 import com.android.compatibility.common.util.FrameworkSpecificTest; 69 import com.android.compatibility.common.util.MediaUtils; 70 import com.android.compatibility.common.util.NonMainlineTest; 71 import com.android.compatibility.common.util.VsrTest; 72 73 import org.junit.Assume; 74 import org.junit.Rule; 75 import org.junit.Test; 76 import org.junit.runner.RunWith; 77 import org.junit.runners.Parameterized; 78 79 import java.util.ArrayList; 80 import java.util.Collection; 81 import java.util.List; 82 import java.util.stream.IntStream; 83 84 /** 85 * Check if information advertised by components are in accordance with its peripherals. The 86 * scope of this test is to only check if the information advertised is ok. Their functionality 87 * however is not verified here. 88 */ 89 @SmallTest 90 @RunWith(Parameterized.class) 91 public class CodecInfoTest { 92 private static final String LOG_TAG = CodecInfoTest.class.getSimpleName(); 93 94 public String mMediaType; 95 public String mCodecName; 96 public MediaCodecInfo mCodecInfo; 97 98 @Rule 99 public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); 100 CodecInfoTest(String mediaType, String codecName, MediaCodecInfo codecInfo)101 public CodecInfoTest(String mediaType, String codecName, MediaCodecInfo codecInfo) { 102 mMediaType = mediaType; 103 mCodecName = codecName; 104 mCodecInfo = codecInfo; 105 } 106 107 @Parameterized.Parameters(name = "{index}_{0}_{1}") input()108 public static Collection<Object[]> input() { 109 final List<Object[]> argsList = new ArrayList<>(); 110 MediaCodecList codecList = new MediaCodecList(MediaCodecList.REGULAR_CODECS); 111 for (MediaCodecInfo codecInfo : codecList.getCodecInfos()) { 112 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && codecInfo.isAlias()) { 113 continue; 114 } 115 String codecName = codecInfo.getName(); 116 if (codecPrefix != null && !codecName.startsWith(codecPrefix) 117 || (codecFilter != null && !codecFilter.matcher(codecName).matches())) { 118 continue; 119 } 120 String[] types = codecInfo.getSupportedTypes(); 121 for (String type : types) { 122 argsList.add(new Object[]{type, codecName, codecInfo}); 123 } 124 } 125 return argsList; 126 } 127 128 /** 129 * For all the available decoders on the device, the test checks if their decoding 130 * capabilities are in sync with the device's display capabilities. Precisely, if a video 131 * decoder advertises support for a HDR profile then the device should be capable of 132 * displaying the same with out any tone mapping. Else, the decoder should not advertise such 133 * support. 134 */ 135 @Test 136 // TODO (b/228237404) Remove the following once there is a reliable way to query HDR 137 // display capabilities at native level, till then limit the test to vendor codecs 138 @NonMainlineTest 139 @FrameworkSpecificTest 140 @CddTest(requirements = "5.1.7/C-2-1") 141 @ApiTest(apis = "android.media.MediaCodecInfo.CodecCapabilities#profileLevels") testHDRDisplayCapabilities()142 public void testHDRDisplayCapabilities() { 143 Assume.assumeTrue("Test needs Android 13", IS_AT_LEAST_T); 144 Assume.assumeTrue("Test needs VNDK Android 13", VNDK_IS_AT_LEAST_T); 145 Assume.assumeTrue("Test needs First SDK Android 13", FIRST_SDK_IS_AT_LEAST_T); 146 Assume.assumeTrue("Test is applicable for video codecs", mMediaType.startsWith("video/")); 147 // TODO (b/228237404) Remove the following once there is a reliable way to query HDR 148 // display capabilities at native level, till then limit the test to vendor codecs 149 Assume.assumeTrue("Test is restricted to vendor codecs", isVendorCodec(mCodecName)); 150 151 int[] Hdr10Profiles = PROFILE_HDR10_MAP.get(mMediaType); 152 int[] Hdr10PlusProfiles = PROFILE_HDR10_PLUS_MAP.get(mMediaType); 153 Assume.assumeTrue("Test is applicable for codecs with HDR10/HDR10+ profiles", 154 Hdr10Profiles != null || Hdr10PlusProfiles != null); 155 156 MediaCodecInfo.CodecCapabilities caps = mCodecInfo.getCapabilitiesForType(mMediaType); 157 158 for (CodecProfileLevel pl : caps.profileLevels) { 159 boolean isHdr10Profile = Hdr10Profiles != null && 160 IntStream.of(Hdr10Profiles).anyMatch(x -> x == pl.profile); 161 boolean isHdr10PlusProfile = Hdr10PlusProfiles != null && 162 IntStream.of(Hdr10PlusProfiles).anyMatch(x -> x == pl.profile); 163 // TODO (b/228237404) Once there is a way to query support for HDR10/HDR10+ display at 164 // native level, separate the following to independent checks for HDR10 and HDR10+ 165 if (isHdr10Profile || isHdr10PlusProfile) { 166 assertTrue(mCodecInfo.getName() + " Advertises support for HDR10/HDR10+ profile " + 167 pl.profile + " without any HDR display", canDisplaySupportHDRContent()); 168 } 169 } 170 } 171 172 /** 173 * Checks if the video codecs available on the device advertise support for mandatory color 174 * formats. The test only checks if the decoder/encoder is advertising the required color 175 * format. It doesn't verify if it actually supports by decoding/encoding. 176 */ 177 @CddTest(requirements = {"5.1.7/C-1-2", "5.1.7/C-1-3", "5.1.7/C-4-1", "5.12/C-6-5", 178 "5.12/C-7-1", "5.12/C-7-3"}) 179 @VsrTest(requirements = {"VSR-4.4-011"}) 180 @Test testColorFormatSupport()181 public void testColorFormatSupport() { 182 Assume.assumeTrue("Test is applicable for video codecs", mMediaType.startsWith("video/")); 183 MediaCodecInfo.CodecCapabilities caps = mCodecInfo.getCapabilitiesForType(mMediaType); 184 assertFalse(mCodecInfo.getName() + " does not support COLOR_FormatYUV420Flexible", 185 IntStream.of(caps.colorFormats) 186 .noneMatch(x -> x == COLOR_FormatYUV420Flexible)); 187 188 assertFalse(mCodecInfo.getName() 189 + " does not support at least one of planar or semi planar yuv 420 888", 190 IntStream.of(caps.colorFormats) 191 .noneMatch(x -> x == COLOR_FormatYUV420PackedPlanar) 192 && IntStream.of(caps.colorFormats) 193 .noneMatch(x -> x == COLOR_FormatYUV420Planar) 194 && IntStream.of(caps.colorFormats) 195 .noneMatch(x -> x == COLOR_FormatYUV420PackedSemiPlanar) 196 && IntStream.of(caps.colorFormats) 197 .noneMatch(x -> x == COLOR_FormatYUV420SemiPlanar)); 198 199 boolean canHandleHdr = CodecTestBase.doesCodecSupportHDRProfile(mCodecName, mMediaType); 200 if (mCodecInfo.isEncoder()) { 201 if (IS_HDR_CAPTURE_SUPPORTED && canHandleHdr) { 202 assertFalse(mCodecInfo.getName() 203 + " supports HDR profile but does not support COLOR_FormatYUVP010", 204 IntStream.of(caps.colorFormats).noneMatch(x -> x == COLOR_FormatYUVP010)); 205 } 206 207 // Encoders that support FEATURE_HdrEditing / FEATURE_HlgEditing, must support 208 // ABGR2101010 color format and at least one HDR profile 209 boolean hdrEditingSupported = caps.isFeatureSupported(FEATURE_HdrEditing); 210 boolean hlgEditingSupported = (IS_AT_LEAST_V && android.media.codec.Flags.hlgEditing()) 211 ? caps.isFeatureSupported(FEATURE_HlgEditing) : false; 212 if (hdrEditingSupported || hlgEditingSupported) { 213 boolean abgr2101010Supported = IntStream.of(caps.colorFormats) 214 .anyMatch(x -> x == COLOR_Format32bitABGR2101010); 215 assertTrue(mCodecName + " supports feature HdrEditing/HlgEditing, but does not" 216 + " support COLOR_FormatABGR2101010 color formats.", abgr2101010Supported); 217 assertTrue(mCodecName + " supports feature HdrEditing/HlgEditing, but does not" 218 + " support any HDR profiles.", canHandleHdr); 219 } 220 } else { 221 if (((FIRST_SDK_IS_AT_LEAST_T && VNDK_IS_AT_LEAST_T && BOARD_SDK_IS_AT_LEAST_T) 222 || BOARD_FIRST_SDK_IS_AT_LEAST_202404) && canDisplaySupportHDRContent() 223 && canHandleHdr) { 224 if (MediaUtils.isTv()) { 225 // Some TV devices support HDR10 display with VO instead of GPU. In this 226 // case, skip checking P010 on TV devices. 227 Assume.assumeFalse(mCodecInfo.getName() 228 + " supports HDR profile but does not support " 229 + "COLOR_FormatYUVP010. Skip checking on TV device", 230 IntStream.of(caps.colorFormats) 231 .noneMatch(x -> x == COLOR_FormatYUVP010)); 232 } else { 233 assertFalse(mCodecInfo.getName() 234 + " supports HDR profile but does not support " 235 + "COLOR_FormatYUVP010", 236 IntStream.of(caps.colorFormats) 237 .noneMatch(x -> x == COLOR_FormatYUVP010)); 238 } 239 } 240 } 241 242 // COLOR_FormatSurface support is an existing requirement, but we did not 243 // test for it before T. We can not retroactively apply the higher standard to 244 // devices that are already certified, so only test on devices luanching with T or later. 245 if (FIRST_SDK_IS_AT_LEAST_T && VNDK_IS_AT_LEAST_T) { 246 assertFalse(mCodecInfo.getName() + " does not support COLOR_FormatSurface", 247 IntStream.of(caps.colorFormats) 248 .noneMatch(x -> x == COLOR_FormatSurface)); 249 } 250 } 251 252 /** 253 * For all the available encoders on the device, the test checks if their encoding 254 * capabilities are in sync with the device's decoding capabilities. 255 */ 256 @CddTest(requirements = "5/C-0-3") 257 @Test testDecoderAvailability()258 public void testDecoderAvailability() { 259 Assume.assumeTrue("Test is applicable only for encoders", mCodecInfo.isEncoder()); 260 Assume.assumeTrue("Test is applicable for video/audio codecs", 261 mMediaType.startsWith("video/") || mMediaType.startsWith("audio/")); 262 if (selectCodecs(mMediaType, null, null, true).size() > 0) { 263 assertTrue("Device advertises support for encoding " + mMediaType + 264 ", but not decoding it", 265 selectCodecs(mMediaType, null, null, false).size() > 0); 266 } 267 } 268 269 /** 270 * For all the available regulard codecs on the device, the test checks 271 * none of them are marked as suitable only for trusted content. 272 */ 273 @Test 274 @RequiresFlagsEnabled(FLAG_IN_PROCESS_SW_AUDIO_CODEC) 275 @SdkSuppress(minSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM, 276 codeName = "VanillaIceCream") 277 @ApiTest(apis = "android.media.MediaCodecInfo#getSecurityModel") testSecurityModel()278 public void testSecurityModel() { 279 assertTrue("Codecs in REGULAR_CODECS list should have security model " 280 + "SANDBOXED or MEMORY_SAFE", 281 List.of(MediaCodecInfo.SECURITY_MODEL_SANDBOXED, 282 MediaCodecInfo.SECURITY_MODEL_MEMORY_SAFE).contains( 283 mCodecInfo.getSecurityModel())); 284 } 285 286 /** 287 * All decoders for compression technologies that were introduced after 2002 must support 288 * dynamic color aspects feature on CHIPSETs that set ro.board.first_api_level to V or higher. 289 */ 290 @RequiresFlagsEnabled(FLAG_DYNAMIC_COLOR_ASPECTS) 291 @SdkSuppress(minSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM, 292 codeName = "VanillaIceCream") 293 @VsrTest(requirements = {"VSR-4.2-005.001"}) 294 @Test testDynamicColorAspectSupport()295 public void testDynamicColorAspectSupport() { 296 Assume.assumeTrue("Test is applicable for video codecs", mMediaType.startsWith("video/")); 297 Assume.assumeFalse("Test is applicable only for decoders", mCodecInfo.isEncoder()); 298 Assume.assumeTrue("Skipping, Only intended for coding technologies introduced after 2002.", 299 !mMediaType.equals(MediaFormat.MIMETYPE_VIDEO_MPEG4) 300 && !mMediaType.equals(MediaFormat.MIMETYPE_VIDEO_H263) 301 && !mMediaType.equals(MediaFormat.MIMETYPE_VIDEO_MPEG2)); 302 Assume.assumeTrue("Skipping, Only intended for devices with board first_api_level >= V", 303 BOARD_FIRST_SDK_IS_AT_LEAST_202404); 304 assertTrue(mCodecName + " does not support FEATURE_DynamicColorAspects.", 305 isFeatureSupported(mCodecName, mMediaType, FEATURE_DynamicColorAspects)); 306 } 307 308 /** 309 * Components advertising support for compression technologies that were introduced after 2002 310 * must support a given resolution in both portrait and landscape mode. 311 */ 312 @VsrTest(requirements = {"VSR-4.2-004.002"}) 313 @Test testResolutionSupport()314 public void testResolutionSupport() { 315 Assume.assumeTrue("Test is applicable for video codecs", mMediaType.startsWith("video/")); 316 Assume.assumeTrue("Skipping, Only intended for coding technologies introduced after 2002.", 317 !mMediaType.equals(MediaFormat.MIMETYPE_VIDEO_MPEG4) 318 && !mMediaType.equals(MediaFormat.MIMETYPE_VIDEO_H263) 319 && !mMediaType.equals(MediaFormat.MIMETYPE_VIDEO_MPEG2)); 320 Assume.assumeTrue("Skipping, Only intended for devices with SDK >= 202404", 321 BOARD_FIRST_SDK_IS_AT_LEAST_202404); 322 if (!isFeatureSupported(mCodecName, mMediaType, "can-swap-width-height")) { 323 MediaCodecInfo.VideoCapabilities vCaps = 324 mCodecInfo.getCapabilitiesForType(mMediaType).getVideoCapabilities(); 325 Range<Integer> widths = vCaps.getSupportedWidths(); 326 Range<Integer> heights = vCaps.getSupportedHeights(); 327 assertEquals(mCodecName + " does not support identical size ranges. Width range " 328 + widths + " height range " + heights, widths, heights); 329 } 330 } 331 332 /** 333 * Components advertising support for compression technologies that were introduced after 2002 334 * must support 1x1 alignment for vp8, av1 and 2x2 for avc, hevc and vp9. 335 */ 336 @VsrTest(requirements = {"VSR-4.2-004.001"}) 337 @Test testAlignmentSupport()338 public void testAlignmentSupport() { 339 Assume.assumeTrue("Test is applicable for video codecs", mMediaType.startsWith("video/")); 340 Assume.assumeTrue("Skipping, Only intended for coding technologies introduced after 2002.", 341 !mMediaType.equals(MediaFormat.MIMETYPE_VIDEO_MPEG4) 342 && !mMediaType.equals(MediaFormat.MIMETYPE_VIDEO_H263) 343 && !mMediaType.equals(MediaFormat.MIMETYPE_VIDEO_MPEG2)); 344 Assume.assumeTrue("Skipping, Only intended for devices with SDK >= 202404", 345 BOARD_FIRST_SDK_IS_AT_LEAST_202404); 346 MediaCodecInfo.VideoCapabilities vCaps = 347 mCodecInfo.getCapabilitiesForType(mMediaType).getVideoCapabilities(); 348 int widthAlignment = vCaps.getWidthAlignment(); 349 int heightAlignment = vCaps.getHeightAlignment(); 350 switch (mMediaType) { 351 case MediaFormat.MIMETYPE_VIDEO_AVC: 352 case MediaFormat.MIMETYPE_VIDEO_HEVC: 353 case MediaFormat.MIMETYPE_VIDEO_VP9: 354 assertTrue(mCodecName + ", width alignment = " + widthAlignment 355 + " should be <= 2 ", widthAlignment <= 2); 356 assertTrue(mCodecName + ", height alignment = " + heightAlignment 357 + " should be <= 2 ", heightAlignment <= 2); 358 break; 359 case MediaFormat.MIMETYPE_VIDEO_VP8: 360 case MediaFormat.MIMETYPE_VIDEO_AV1: 361 assertEquals(mCodecName + ", width alignment = " + widthAlignment 362 + " should be equal to 1 ", 1, widthAlignment); 363 assertEquals(mCodecName + ", height alignment = " + heightAlignment 364 + " should be equal to 1 ", 1, heightAlignment); 365 break; 366 } 367 } 368 } 369