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