Home | History | Annotate | Download | only in cts
      1 /*
      2  * Copyright (C) 2012 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.media.cts;
     18 
     19 import static android.media.MediaCodecInfo.CodecCapabilities.FEATURE_SecurePlayback;
     20 import static android.media.MediaCodecInfo.CodecCapabilities.FEATURE_TunneledPlayback;
     21 
     22 import com.android.compatibility.common.util.PropertyUtil;
     23 
     24 import android.content.pm.PackageManager;
     25 import android.media.MediaCodec;
     26 import android.media.MediaCodecInfo;
     27 import android.media.MediaCodecInfo.AudioCapabilities;
     28 import android.media.MediaCodecInfo.CodecCapabilities;
     29 import android.media.MediaCodecInfo.EncoderCapabilities;
     30 import android.media.MediaCodecInfo.VideoCapabilities;
     31 import android.media.MediaCodecList;
     32 import android.media.MediaFormat;
     33 import android.platform.test.annotations.RequiresDevice;
     34 import android.test.AndroidTestCase;
     35 import android.util.Log;
     36 import android.util.Pair;
     37 import android.util.Size;
     38 
     39 import androidx.test.filters.SmallTest;
     40 
     41 import java.io.File;
     42 import java.io.IOException;
     43 import java.util.ArrayList;
     44 import java.util.Arrays;
     45 import java.util.HashSet;
     46 import java.util.List;
     47 import java.util.Set;
     48 
     49 @SmallTest
     50 @RequiresDevice
     51 public class MediaCodecListTest extends AndroidTestCase {
     52 
     53     private static final String TAG = "MediaCodecListTest";
     54     private static final String MEDIA_CODEC_XML_FILE = "/etc/media_codecs.xml";
     55     private static final String VENDOR_MEDIA_CODEC_XML_FILE = "/vendor/etc/media_codecs.xml";
     56     private static final String ODM_MEDIA_CODEC_XML_FILE = "/odm/etc/media_codecs.xml";
     57     private final MediaCodecList mRegularCodecs =
     58             new MediaCodecList(MediaCodecList.REGULAR_CODECS);
     59     private final MediaCodecList mAllCodecs =
     60             new MediaCodecList(MediaCodecList.ALL_CODECS);
     61     private final MediaCodecInfo[] mRegularInfos =
     62             mRegularCodecs.getCodecInfos();
     63     private final MediaCodecInfo[] mAllInfos =
     64             mAllCodecs.getCodecInfos();
     65 
     66     class CodecType {
     67         CodecType(String type, boolean isEncoder, MediaFormat sampleFormat) {
     68             mMimeTypeName = type;
     69             mIsEncoder = isEncoder;
     70             mSampleFormat = sampleFormat;
     71         }
     72 
     73         boolean equals(CodecType codecType) {
     74             return (mMimeTypeName.compareTo(codecType.mMimeTypeName) == 0) &&
     75                     mIsEncoder == codecType.mIsEncoder;
     76         }
     77 
     78         boolean canBeFound() {
     79             return codecCanBeFound(mIsEncoder, mSampleFormat);
     80         }
     81 
     82         @Override
     83         public String toString() {
     84             return mMimeTypeName + (mIsEncoder ? " encoder" : " decoder") + " for " + mSampleFormat;
     85         }
     86 
     87         private String mMimeTypeName;
     88         private boolean mIsEncoder;
     89         private MediaFormat mSampleFormat;
     90     };
     91 
     92     class AudioCodec extends CodecType {
     93         AudioCodec(String mime, boolean isEncoder, int sampleRate) {
     94             super(mime, isEncoder, MediaFormat.createAudioFormat(
     95                     mime, sampleRate, 1 /* channelCount */));
     96         }
     97     }
     98 
     99     class VideoCodec extends CodecType {
    100         VideoCodec(String mime, boolean isEncoder) {
    101             // implicit assumption that QVGA video is always valid
    102             super(mime, isEncoder, MediaFormat.createVideoFormat(
    103                     mime, 176 /* width */, 144 /* height */));
    104         }
    105     }
    106 
    107     public static boolean hasExpandedCodecInfo() {
    108         return PropertyUtil.isVendorApiLevelNewerThan(29);
    109     }
    110 
    111     public static void testMediaCodecXmlFileExist() {
    112         File file = new File(MEDIA_CODEC_XML_FILE);
    113         File vendorFile = new File(VENDOR_MEDIA_CODEC_XML_FILE);
    114         File odmFile = new File(ODM_MEDIA_CODEC_XML_FILE);
    115         assertTrue("media_codecs.xml does not exist in /odm/etc, /vendor/etc or /etc.",
    116                 file.exists() || vendorFile.exists() || odmFile.exists());
    117     }
    118 
    119     private MediaCodecInfo[] getLegacyInfos() {
    120         Log.d(TAG, "getLegacyInfos");
    121 
    122         int codecCount = MediaCodecList.getCodecCount();
    123         MediaCodecInfo[] res = new MediaCodecInfo[codecCount];
    124 
    125         for (int i = 0; i < codecCount; ++i) {
    126             res[i] = MediaCodecList.getCodecInfoAt(i);
    127         }
    128         return res;
    129     }
    130 
    131     public void assertEqualsOrSuperset(Set big, Set tiny, boolean superset) {
    132         if (!superset) {
    133             assertEquals(big, tiny);
    134         } else {
    135             assertTrue(big.containsAll(tiny));
    136         }
    137     }
    138 
    139     private static <T> Set<T> asSet(T[] array) {
    140         Set<T> s = new HashSet<T>();
    141         for (T el : array) {
    142             s.add(el);
    143         }
    144         return s;
    145     }
    146 
    147     private static Set<Integer> asSet(int[] array) {
    148         Set<Integer> s = new HashSet<Integer>();
    149         for (int el : array) {
    150             s.add(el);
    151         }
    152         return s;
    153     }
    154 
    155     public void assertEqualsOrSuperset(
    156             CodecCapabilities big, CodecCapabilities tiny, boolean superset) {
    157         // ordering of enumerations may differ
    158         assertEqualsOrSuperset(asSet(big.colorFormats), asSet(tiny.colorFormats), superset);
    159         assertEqualsOrSuperset(asSet(big.profileLevels), asSet(tiny.profileLevels), superset);
    160         AudioCapabilities bigAudCaps = big.getAudioCapabilities();
    161         VideoCapabilities bigVidCaps = big.getVideoCapabilities();
    162         EncoderCapabilities bigEncCaps = big.getEncoderCapabilities();
    163         AudioCapabilities tinyAudCaps = tiny.getAudioCapabilities();
    164         VideoCapabilities tinyVidCaps = tiny.getVideoCapabilities();
    165         EncoderCapabilities tinyEncCaps = tiny.getEncoderCapabilities();
    166         assertEquals(bigAudCaps != null, tinyAudCaps != null);
    167         assertEquals(bigAudCaps != null, tinyAudCaps != null);
    168         assertEquals(bigAudCaps != null, tinyAudCaps != null);
    169     }
    170 
    171     public void assertEqualsOrSuperset(
    172             MediaCodecInfo big, MediaCodecInfo tiny, boolean superset) {
    173         assertEquals(big.getName(), tiny.getName());
    174         assertEquals(big.isEncoder(), tiny.isEncoder());
    175         assertEqualsOrSuperset(
    176                 asSet(big.getSupportedTypes()), asSet(tiny.getSupportedTypes()), superset);
    177         for (String type : big.getSupportedTypes()) {
    178             assertEqualsOrSuperset(
    179                     big.getCapabilitiesForType(type),
    180                     tiny.getCapabilitiesForType(type),
    181                     superset);
    182         }
    183     }
    184 
    185     public void assertSuperset(MediaCodecInfo big, MediaCodecInfo tiny) {
    186         assertEqualsOrSuperset(big, tiny, true /* superset */);
    187     }
    188 
    189     public void assertEquals(MediaCodecInfo big, MediaCodecInfo tiny) {
    190         assertEqualsOrSuperset(big, tiny, false /* superset */);
    191     }
    192 
    193     // Each component advertised by MediaCodecList should at least be
    194     // instantiable.
    195     private void testComponentInstantiation(MediaCodecInfo[] infos) throws IOException {
    196         for (MediaCodecInfo info : infos) {
    197             Log.d(TAG, "codec: " + info.getName());
    198             Log.d(TAG, "  isEncoder = " + info.isEncoder());
    199 
    200             MediaCodec codec = MediaCodec.createByCodecName(info.getName());
    201 
    202             assertEquals(codec.getName(), info.getName());
    203             assertEquals(codec.getCanonicalName(), info.getCanonicalName());
    204             assertEquals(codec.getCodecInfo(), info);
    205 
    206             codec.release();
    207             codec = null;
    208         }
    209     }
    210 
    211     public void testRegularComponentInstantiation() throws IOException {
    212         Log.d(TAG, "testRegularComponentInstantiation");
    213         testComponentInstantiation(mRegularInfos);
    214     }
    215 
    216     public void testAllComponentInstantiation() throws IOException {
    217         Log.d(TAG, "testAllComponentInstantiation");
    218         testComponentInstantiation(mAllInfos);
    219     }
    220 
    221     public void testLegacyComponentInstantiation() throws IOException {
    222         Log.d(TAG, "testLegacyComponentInstantiation");
    223         testComponentInstantiation(getLegacyInfos());
    224     }
    225 
    226     // For each type advertised by any of the components we should be able
    227     // to get capabilities.
    228     private void testGetCapabilities(MediaCodecInfo[] infos) {
    229         for (MediaCodecInfo info : infos) {
    230             Log.d(TAG, "codec: " + info.getName());
    231             Log.d(TAG, "  isEncoder = " + info.isEncoder());
    232 
    233             String[] types = info.getSupportedTypes();
    234             for (int j = 0; j < types.length; ++j) {
    235                 Log.d(TAG, "calling getCapabilitiesForType " + types[j]);
    236                 CodecCapabilities cap = info.getCapabilitiesForType(types[j]);
    237             }
    238         }
    239     }
    240 
    241     public void testGetRegularCapabilities() {
    242         Log.d(TAG, "testGetRegularCapabilities");
    243         testGetCapabilities(mRegularInfos);
    244     }
    245 
    246     public void testGetAllCapabilities() {
    247         Log.d(TAG, "testGetAllCapabilities");
    248         testGetCapabilities(mAllInfos);
    249     }
    250 
    251     public void testGetLegacyCapabilities() {
    252         Log.d(TAG, "testGetLegacyCapabilities");
    253         testGetCapabilities(getLegacyInfos());
    254     }
    255 
    256     public void testLegacyMediaCodecListIsSameAsRegular() {
    257         // regular codecs should be equivalent to legacy codecs, including
    258         // codec ordering
    259         MediaCodecInfo[] legacyInfos = getLegacyInfos();
    260         assertEquals(legacyInfos.length, mRegularInfos.length);
    261         for (int i = 0; i < legacyInfos.length; ++i) {
    262             assertEquals(legacyInfos[i], mRegularInfos[i]);
    263         }
    264     }
    265 
    266     public void testRegularMediaCodecListIsASubsetOfAll() {
    267         Log.d(TAG, "testRegularMediaCodecListIsASubsetOfAll");
    268         // regular codecs should be a subsequence of all codecs, including
    269         // codec ordering
    270         int ix = 0;
    271         for (MediaCodecInfo info : mAllInfos) {
    272             if (ix == mRegularInfos.length) {
    273                 break;
    274             }
    275             if (!mRegularInfos[ix].getName().equals(info.getName())) {
    276                 Log.d(TAG, "skipping non-regular codec " + info.getName());
    277                 continue;
    278             }
    279             Log.d(TAG, "checking codec " + info.getName());
    280             assertSuperset(info, mRegularInfos[ix]);
    281             ++ix;
    282         }
    283         assertEquals(
    284                 "some regular codecs are not listed in all codecs", ix, mRegularInfos.length);
    285     }
    286 
    287     public void testRequiredMediaCodecList() {
    288         List<CodecType> requiredList = getRequiredCodecTypes();
    289         List<CodecType> supportedList = getSupportedCodecTypes();
    290         assertTrue(areRequiredCodecTypesSupported(requiredList, supportedList));
    291         for (CodecType type : requiredList) {
    292             assertTrue("cannot find " + type, type.canBeFound());
    293         }
    294     }
    295 
    296     private boolean hasCamera() {
    297         PackageManager pm = getContext().getPackageManager();
    298         return pm.hasSystemFeature(pm.FEATURE_CAMERA_FRONT) ||
    299                 pm.hasSystemFeature(pm.FEATURE_CAMERA);
    300     }
    301 
    302     private boolean hasMicrophone() {
    303         PackageManager pm = getContext().getPackageManager();
    304         return pm.hasSystemFeature(pm.FEATURE_MICROPHONE);
    305     }
    306 
    307     private boolean isWatch() {
    308         PackageManager pm = getContext().getPackageManager();
    309         return pm.hasSystemFeature(pm.FEATURE_WATCH);
    310     }
    311 
    312     private boolean isHandheld() {
    313         // handheld nature is not exposed to package manager, for now
    314         // we check for touchscreen and NOT watch and NOT tv
    315         PackageManager pm = getContext().getPackageManager();
    316         return pm.hasSystemFeature(pm.FEATURE_TOUCHSCREEN)
    317                 && !pm.hasSystemFeature(pm.FEATURE_WATCH)
    318                 && !pm.hasSystemFeature(pm.FEATURE_TELEVISION)
    319                 && !pm.hasSystemFeature(pm.FEATURE_AUTOMOTIVE);
    320     }
    321 
    322     // Find whether the given codec can be found using MediaCodecList.find methods.
    323     private boolean codecCanBeFound(boolean isEncoder, MediaFormat format) {
    324         String codecName = isEncoder
    325                 ? mRegularCodecs.findEncoderForFormat(format)
    326                 : mRegularCodecs.findDecoderForFormat(format);
    327         return codecName != null;
    328     }
    329 
    330     /*
    331      * Find whether all required media codec types are supported
    332      */
    333     private boolean areRequiredCodecTypesSupported(
    334         List<CodecType> requiredList, List<CodecType> supportedList) {
    335         for (CodecType requiredCodec: requiredList) {
    336             boolean isSupported = false;
    337             for (CodecType supportedCodec: supportedList) {
    338                 if (requiredCodec.equals(supportedCodec)) {
    339                     isSupported = true;
    340                 }
    341             }
    342             if (!isSupported) {
    343                 String codec = requiredCodec.mMimeTypeName
    344                                 + ", " + (requiredCodec.mIsEncoder? "encoder": "decoder");
    345                 Log.e(TAG, "Media codec (" + codec + ") is not supported");
    346                 return false;
    347             }
    348         }
    349         return true;
    350     }
    351 
    352     /*
    353      * Find all the media codec types are supported.
    354      */
    355     private List<CodecType> getSupportedCodecTypes() {
    356         List<CodecType> supportedList = new ArrayList<CodecType>();
    357         for (MediaCodecInfo info : mRegularInfos) {
    358             String[] types = info.getSupportedTypes();
    359             assertTrue("Unexpected number of supported types", types.length > 0);
    360             boolean isEncoder = info.isEncoder();
    361             for (int j = 0; j < types.length; ++j) {
    362                 supportedList.add(new CodecType(types[j], isEncoder, null /* sampleFormat */));
    363             }
    364         }
    365         return supportedList;
    366     }
    367 
    368     /*
    369      * This list should be kept in sync with the CCD document
    370      * See http://developer.android.com/guide/appendix/media-formats.html
    371      */
    372     private List<CodecType> getRequiredCodecTypes() {
    373         List<CodecType> list = new ArrayList<CodecType>(16);
    374 
    375         // Mandatory audio decoders
    376 
    377         list.add(new AudioCodec(MediaFormat.MIMETYPE_AUDIO_FLAC, false, 48000));
    378         list.add(new AudioCodec(MediaFormat.MIMETYPE_AUDIO_MPEG, false, 8000));  // mp3
    379         list.add(new AudioCodec(MediaFormat.MIMETYPE_AUDIO_MPEG, false, 48000)); // mp3
    380         list.add(new AudioCodec(MediaFormat.MIMETYPE_AUDIO_VORBIS, false, 8000));
    381         list.add(new AudioCodec(MediaFormat.MIMETYPE_AUDIO_VORBIS, false, 48000));
    382         list.add(new AudioCodec(MediaFormat.MIMETYPE_AUDIO_AAC, false, 8000));
    383         list.add(new AudioCodec(MediaFormat.MIMETYPE_AUDIO_AAC, false, 48000));
    384         list.add(new AudioCodec(MediaFormat.MIMETYPE_AUDIO_RAW, false, 8000));
    385         list.add(new AudioCodec(MediaFormat.MIMETYPE_AUDIO_RAW, false, 44100));
    386         list.add(new AudioCodec(MediaFormat.MIMETYPE_AUDIO_OPUS, false, 48000));
    387 
    388         // Mandatory audio encoders (for non-watch devices with camera)
    389 
    390         if (hasMicrophone() && !isWatch()) {
    391             list.add(new AudioCodec(MediaFormat.MIMETYPE_AUDIO_AAC, true, 8000));
    392             list.add(new AudioCodec(MediaFormat.MIMETYPE_AUDIO_AAC, true, 48000));
    393             // flac encoder is not required
    394             // list.add(new AudioCodec(MediaFormat.MIMETYPE_AUDIO_FLAC, true));  // encoder
    395         }
    396 
    397         // Mandatory audio encoders for handheld devices
    398         if (isHandheld()) {
    399             list.add(new AudioCodec(MediaFormat.MIMETYPE_AUDIO_AMR_NB, false, 8000));  // decoder
    400             list.add(new AudioCodec(MediaFormat.MIMETYPE_AUDIO_AMR_NB, true,  8000));  // encoder
    401             list.add(new AudioCodec(MediaFormat.MIMETYPE_AUDIO_AMR_WB, false, 16000)); // decoder
    402             list.add(new AudioCodec(MediaFormat.MIMETYPE_AUDIO_AMR_WB, true,  16000)); // encoder
    403         }
    404 
    405         // Mandatory video codecs (for non-watch devices)
    406 
    407         if (!isWatch()) {
    408             list.add(new VideoCodec(MediaFormat.MIMETYPE_VIDEO_AVC, false));   // avc decoder
    409             list.add(new VideoCodec(MediaFormat.MIMETYPE_VIDEO_AVC, true));    // avc encoder
    410             list.add(new VideoCodec(MediaFormat.MIMETYPE_VIDEO_VP8, false));   // vp8 decoder
    411             list.add(new VideoCodec(MediaFormat.MIMETYPE_VIDEO_VP8, true));    // vp8 encoder
    412             list.add(new VideoCodec(MediaFormat.MIMETYPE_VIDEO_VP9, false));   // vp9 decoder
    413             list.add(new VideoCodec(MediaFormat.MIMETYPE_VIDEO_HEVC, false));  // hevc decoder
    414             list.add(new VideoCodec(MediaFormat.MIMETYPE_VIDEO_MPEG4, false)); // m4v decoder
    415             list.add(new VideoCodec(MediaFormat.MIMETYPE_VIDEO_H263, false));  // h263 decoder
    416             if (hasCamera()) {
    417                 list.add(new VideoCodec(MediaFormat.MIMETYPE_VIDEO_H263, true)); // h263 encoder
    418             }
    419         }
    420 
    421         return list;
    422     }
    423 
    424     public void testFindDecoderWithAacProfile() throws Exception {
    425         Log.d(TAG, "testFindDecoderWithAacProfile");
    426         MediaFormat format = MediaFormat.createAudioFormat(
    427                 MediaFormat.MIMETYPE_AUDIO_AAC, 8000, 1);
    428         List<Integer> profiles = new ArrayList<>();
    429         profiles.add(MediaCodecInfo.CodecProfileLevel.AACObjectLC);
    430         profiles.add(MediaCodecInfo.CodecProfileLevel.AACObjectHE);
    431         profiles.add(MediaCodecInfo.CodecProfileLevel.AACObjectHE_PS);
    432         // The API is added at 5.0, so the profile below must be supported.
    433         profiles.add(MediaCodecInfo.CodecProfileLevel.AACObjectELD);
    434         for (int profile : profiles) {
    435             format.setInteger(MediaFormat.KEY_AAC_PROFILE, profile);
    436             String codecName = mRegularCodecs.findDecoderForFormat(format);
    437             assertNotNull("Profile " + profile + " must be supported.", codecName);
    438         }
    439     }
    440 
    441     public void testFindEncoderWithAacProfile() throws Exception {
    442         Log.d(TAG, "testFindEncoderWithAacProfile");
    443         MediaFormat format = MediaFormat.createAudioFormat(
    444                 MediaFormat.MIMETYPE_AUDIO_AAC, 8000, 1);
    445         List<Integer> profiles = new ArrayList<>();
    446         if (hasMicrophone() && !isWatch()) {
    447             profiles.add(MediaCodecInfo.CodecProfileLevel.AACObjectLC);
    448             // The API is added at 5.0, so the profiles below must be supported.
    449             profiles.add(MediaCodecInfo.CodecProfileLevel.AACObjectHE);
    450             profiles.add(MediaCodecInfo.CodecProfileLevel.AACObjectELD);
    451         }
    452         for (int profile : profiles) {
    453             format.setInteger(MediaFormat.KEY_AAC_PROFILE, profile);
    454             String codecName = mRegularCodecs.findEncoderForFormat(format);
    455             assertNotNull("Profile " + profile + " must be supported.", codecName);
    456         }
    457     }
    458 
    459     public void testAudioCodecChannels() {
    460         for (MediaCodecInfo info : mAllInfos) {
    461             String[] types = info.getSupportedTypes();
    462             for (int j = 0; j < types.length; ++j) {
    463                 CodecCapabilities cap = info.getCapabilitiesForType(types[j]);
    464                 AudioCapabilities audioCap = cap.getAudioCapabilities();
    465                 if (audioCap == null) {
    466                     assertFalse("no audio capabilities for audio media type " + types[j] + " of "
    467                                     + info.getName(),
    468                                 types[j].toLowerCase().startsWith("audio/"));
    469                     continue;
    470                 }
    471                 int n = audioCap.getMaxInputChannelCount();
    472                 Log.d(TAG, info.getName() + ": " + n);
    473                 assertTrue(info.getName() + " max input channel not positive: " + n, n > 0);
    474             }
    475         }
    476     }
    477 
    478     private void testCanonicalCodecIsNotAnAlias(String canonicalName) {
    479         // canonical name must point to a non-alias
    480         for (MediaCodecInfo canonical : mAllInfos) {
    481             if (canonical.getName().equals(canonicalName)) {
    482                 assertFalse(canonical.isAlias());
    483                 return;
    484             }
    485         }
    486         fail("could not find info to canonical name '" + canonicalName + "'");
    487     }
    488 
    489     private String getCustomPartOfComponentName(MediaCodecInfo info) {
    490         String name = info.getName();
    491         if (name.startsWith("OMX.") || name.startsWith("c2.")) {
    492             // strip off OMX.<vendor_name>.
    493             return name.replaceFirst("^OMX\\.([^.]+)\\.", "");
    494         }
    495         return name;
    496     }
    497 
    498     private void testKindInCodecNamesIsMeaningful(MediaCodecInfo info) {
    499         String name = getCustomPartOfComponentName(info);
    500         // codec names containing 'encoder' or 'enc' must be encoders, 'decoder' or 'dec' must
    501         // be decoders
    502         if (name.matches("(?i)\\b(encoder|enc)\\b")) {
    503             assertTrue(info.isEncoder());
    504         }
    505         if (name.matches("(?i)\\b(decoder|dec)\\b")) {
    506             assertFalse(info.isEncoder());
    507         }
    508     }
    509 
    510     private void testMediaTypeInCodecNamesIsMeaningful(MediaCodecInfo info) {
    511         // Codec names containing media type names must support that media type
    512         String name = getCustomPartOfComponentName(info);
    513 
    514         Set<String> supportedTypes = new HashSet<String>(Arrays.asList(info.getSupportedTypes()));
    515 
    516         // video types
    517         if (name.matches("(?i)\\b(mp(eg)?2)\\b")) {
    518             // this may refer to audio mpeg1-layer2 or video mpeg2
    519             assertTrue(supportedTypes.contains(MediaFormat.MIMETYPE_VIDEO_MPEG2)
    520                         || supportedTypes.contains(MediaFormat.MIMETYPE_AUDIO_MPEG + "-L2"));
    521         }
    522         if (name.matches("(?i)\\b(h\\.?263)\\b")) {
    523             assertTrue(supportedTypes.contains(MediaFormat.MIMETYPE_VIDEO_H263));
    524         }
    525         if (name.matches("(?i)\\b(mp(eg)?4)\\b")) {
    526             assertTrue(supportedTypes.contains(MediaFormat.MIMETYPE_VIDEO_MPEG4));
    527         }
    528         if (name.matches("(?i)\\b(h\\.?264|avc)\\b")) {
    529             assertTrue(supportedTypes.contains(MediaFormat.MIMETYPE_VIDEO_AVC));
    530         }
    531         if (name.matches("(?i)\\b(vp8)\\b")) {
    532             assertTrue(supportedTypes.contains(MediaFormat.MIMETYPE_VIDEO_VP8));
    533         }
    534         if (name.matches("(?i)\\b(h\\.?265|hevc)\\b")) {
    535             assertTrue(supportedTypes.contains(MediaFormat.MIMETYPE_VIDEO_HEVC));
    536         }
    537         if (name.matches("(?i)\\b(vp9)\\b")) {
    538             assertTrue(supportedTypes.contains(MediaFormat.MIMETYPE_VIDEO_VP9));
    539         }
    540         if (name.matches("(?i)\\b(av0?1)\\b")) {
    541             assertTrue(supportedTypes.contains(MediaFormat.MIMETYPE_VIDEO_AV1));
    542         }
    543 
    544         // audio types
    545         if (name.matches("(?i)\\b(mp(eg)?3)\\b")) {
    546             assertTrue(supportedTypes.contains(MediaFormat.MIMETYPE_AUDIO_MPEG));
    547         }
    548         if (name.matches("(?i)\\b(x?aac)\\b")) {
    549             assertTrue(supportedTypes.contains(MediaFormat.MIMETYPE_AUDIO_AAC));
    550         }
    551         if (name.matches("(?i)\\b(pcm)\\b")) {
    552             assertTrue(supportedTypes.contains(MediaFormat.MIMETYPE_AUDIO_RAW));
    553         }
    554         if (name.matches("(?i)\\b(raw)\\b")) {
    555             assertTrue(supportedTypes.contains(MediaFormat.MIMETYPE_AUDIO_RAW)
    556                         || supportedTypes.contains(MediaFormat.MIMETYPE_VIDEO_RAW));
    557         }
    558         if (name.matches("(?i)\\b(amr)\\b")) {
    559             if (name.matches("(?i)\\b(nb)\\b")) {
    560                 assertTrue(supportedTypes.contains(MediaFormat.MIMETYPE_AUDIO_AMR_NB));
    561             } else if (name.matches("(?i)\\b(wb)\\b")) {
    562                 assertTrue(supportedTypes.contains(MediaFormat.MIMETYPE_AUDIO_AMR_WB));
    563             } else {
    564                 assertTrue(
    565                     supportedTypes.contains(MediaFormat.MIMETYPE_AUDIO_AMR_NB)
    566                             || supportedTypes.contains(MediaFormat.MIMETYPE_AUDIO_AMR_WB));
    567             }
    568         }
    569         if (name.matches("(?i)\\b(opus)\\b")) {
    570             assertTrue(supportedTypes.contains(MediaFormat.MIMETYPE_AUDIO_OPUS));
    571         }
    572         if (name.matches("(?i)\\b(vorbis)\\b")) {
    573             assertTrue(supportedTypes.contains(MediaFormat.MIMETYPE_AUDIO_VORBIS));
    574         }
    575         if (name.matches("(?i)\\b(flac)\\b")) {
    576             assertTrue(supportedTypes.contains(MediaFormat.MIMETYPE_AUDIO_FLAC));
    577         }
    578         if (name.matches("(?i)\\b(ac3)\\b")) {
    579             assertTrue(supportedTypes.contains(MediaFormat.MIMETYPE_AUDIO_AC3));
    580         }
    581         if (name.matches("(?i)\\b(ac4)\\b")) {
    582             assertTrue(supportedTypes.contains(MediaFormat.MIMETYPE_AUDIO_AC4));
    583         }
    584         if (name.matches("(?i)\\b(eac3)\\b")) {
    585             assertTrue(supportedTypes.contains(MediaFormat.MIMETYPE_AUDIO_EAC3)
    586                         || supportedTypes.contains(MediaFormat.MIMETYPE_AUDIO_EAC3_JOC));
    587         }
    588     }
    589 
    590     public void testCodecCharacterizations() {
    591         for (MediaCodecInfo info : mAllInfos) {
    592             Log.d(TAG, "codec: " + info.getName() + " canonical: " + info.getCanonicalName());
    593 
    594             // AOSP codecs must not be marked as vendor or hardware accelerated
    595             if (info.getName().startsWith("OMX.google.")) {
    596                 assertFalse(info.isVendor());
    597                 assertFalse(info.isHardwareAccelerated());
    598             }
    599 
    600             // Codec 2.0 based AOSP codecs must run in a software-only process
    601             if (info.getName().startsWith("c2.android.")) {
    602                 assertTrue(info.isSoftwareOnly());
    603                 assertFalse(info.isVendor());
    604                 assertFalse(info.isHardwareAccelerated());
    605             }
    606 
    607             // validate aliases
    608             if (info.isAlias()) {
    609                 assertFalse(info.getName().equals(info.getCanonicalName()));
    610                 testCanonicalCodecIsNotAnAlias(info.getCanonicalName());
    611             } else {
    612                 // validate codec names: (Canonical) codec names must be meaningful.
    613                 // We only test this on canonical infos as we allow aliases to support
    614                 // existing codec names that do not fit this.
    615                 assertEquals(info.getName(), info.getCanonicalName());
    616                 testKindInCodecNamesIsMeaningful(info);
    617                 testMediaTypeInCodecNamesIsMeaningful(info);
    618             }
    619 
    620             // hardware accelerated codecs cannot be software only
    621             assertFalse(info.isHardwareAccelerated() && info.isSoftwareOnly());
    622         }
    623     }
    624 
    625     public void testVideoPerformancePointsSanity() {
    626         MediaFormat hd25Format =
    627             MediaFormat.createVideoFormat(MediaFormat.MIMETYPE_VIDEO_AVC, 1280, 720);
    628         hd25Format.setFloat(MediaFormat.KEY_FRAME_RATE, 25.f);
    629 
    630         MediaFormat portraitHd240Format =
    631             MediaFormat.createVideoFormat(MediaFormat.MIMETYPE_VIDEO_AVC, 720, 1280);
    632         portraitHd240Format.setInteger(MediaFormat.KEY_FRAME_RATE, 240);
    633 
    634         /* common-sense checks */
    635         assertTrue(VideoCapabilities.PerformancePoint.HD_30.covers(hd25Format));
    636         assertTrue(VideoCapabilities.PerformancePoint.HD_25.covers(hd25Format));
    637         assertFalse(VideoCapabilities.PerformancePoint.HD_24.covers(hd25Format));
    638         assertTrue(VideoCapabilities.PerformancePoint.FHD_30.covers(hd25Format));
    639         assertTrue(VideoCapabilities.PerformancePoint.FHD_25.covers(hd25Format));
    640         assertFalse(VideoCapabilities.PerformancePoint.FHD_24.covers(hd25Format));
    641 
    642         assertTrue(VideoCapabilities.PerformancePoint.HD_240.covers(portraitHd240Format));
    643         assertFalse(VideoCapabilities.PerformancePoint.HD_200.covers(portraitHd240Format));
    644         assertTrue(VideoCapabilities.PerformancePoint.FHD_240.covers(portraitHd240Format));
    645         assertFalse(VideoCapabilities.PerformancePoint.FHD_200.covers(portraitHd240Format));
    646 
    647         /* test macroblock size and conversion support */
    648         VideoCapabilities.PerformancePoint bigBlockFHD30_120 =
    649             new VideoCapabilities.PerformancePoint(1920, 1080, 30, 120, new Size(128, 64));
    650         assertEquals(120, bigBlockFHD30_120.getMaxFrameRate());
    651         assertEquals(8160, bigBlockFHD30_120.getMaxMacroBlocks());
    652         assertEquals(244800, bigBlockFHD30_120.getMaxMacroBlockRate());
    653 
    654         VideoCapabilities.PerformancePoint bigRotBlockFHD30_120 =
    655             new VideoCapabilities.PerformancePoint(1920, 1080, 30, 120, new Size(64, 128));
    656         assertEquals(120, bigRotBlockFHD30_120.getMaxFrameRate());
    657         assertEquals(8640, bigRotBlockFHD30_120.getMaxMacroBlocks());
    658         assertEquals(259200, bigRotBlockFHD30_120.getMaxMacroBlockRate());
    659 
    660         /* test conversion logic */
    661         {
    662             /* 900*900@25-50 */
    663             VideoCapabilities.PerformancePoint unusual =
    664                 new VideoCapabilities.PerformancePoint(900, 900, 25, 50, new Size(1, 1));
    665             assertEquals(50, unusual.getMaxFrameRate());
    666             assertEquals(3249, unusual.getMaxMacroBlocks());
    667             assertEquals(81225, unusual.getMaxMacroBlockRate());
    668 
    669             /* becomes 960*1024@25-50 */
    670             VideoCapabilities.PerformancePoint converted1 =
    671                 new VideoCapabilities.PerformancePoint(unusual, new Size(128, 64));
    672             assertEquals(50, converted1.getMaxFrameRate());
    673             assertEquals(3840, converted1.getMaxMacroBlocks());
    674             assertEquals(96000, converted1.getMaxMacroBlockRate());
    675 
    676             /* becomes 1024*960@25-50 */
    677             VideoCapabilities.PerformancePoint converted2 =
    678                 new VideoCapabilities.PerformancePoint(unusual, new Size(64, 128));
    679             assertEquals(50, converted2.getMaxFrameRate());
    680             assertEquals(3840, converted2.getMaxMacroBlocks());
    681             assertEquals(96000, converted2.getMaxMacroBlockRate());
    682 
    683             /* becomes 1024*1024@25-50 */
    684             VideoCapabilities.PerformancePoint converted3 =
    685                 new VideoCapabilities.PerformancePoint(converted1, new Size(64, 128));
    686             assertEquals(50, converted3.getMaxFrameRate());
    687             assertEquals(4096, converted3.getMaxMacroBlocks());
    688             assertEquals(102400, converted3.getMaxMacroBlockRate());
    689 
    690             assertEquals(converted1, converted2);
    691             assertEquals(converted2, converted1);
    692             assertEquals(converted1, converted3);
    693             assertEquals(converted3, converted1);
    694             assertTrue(converted1.covers(converted2));
    695             assertTrue(converted2.covers(converted1));
    696             assertTrue(converted2.covers(converted3));
    697             assertTrue(converted3.covers(converted2));
    698         }
    699 
    700         // big macroblock size does not impact standard performance points as the dimensions are set
    701         VideoCapabilities.PerformancePoint bigBlockFHD30 =
    702             new VideoCapabilities.PerformancePoint(1920, 1080, 30, 30, new Size(128, 64));
    703 
    704         assertTrue(bigBlockFHD30.covers(VideoCapabilities.PerformancePoint.FHD_30));
    705         assertTrue(VideoCapabilities.PerformancePoint.FHD_30.covers(bigBlockFHD30));
    706         assertTrue(bigBlockFHD30.equals(VideoCapabilities.PerformancePoint.FHD_30));
    707         assertTrue(VideoCapabilities.PerformancePoint.FHD_30.equals(bigBlockFHD30));
    708 
    709         // but it impacts the case where dimensions differ
    710         assertFalse(bigBlockFHD30.covers(new VideoCapabilities.PerformancePoint(1080, 1920, 30)));
    711         assertFalse(bigBlockFHD30.covers(new VideoCapabilities.PerformancePoint(1936, 1072, 30)));
    712         assertFalse(bigBlockFHD30.covers(new VideoCapabilities.PerformancePoint(1280, 720, 63)));
    713         assertTrue(bigBlockFHD30_120.covers(new VideoCapabilities.PerformancePoint(1280, 720, 63)));
    714         assertFalse(bigBlockFHD30_120.covers(new VideoCapabilities.PerformancePoint(1280, 720, 64)));
    715         assertTrue(VideoCapabilities.PerformancePoint.FHD_30.covers(
    716                 new VideoCapabilities.PerformancePoint(1080, 1920, 30)));
    717         assertTrue(VideoCapabilities.PerformancePoint.FHD_30.covers(
    718                 new VideoCapabilities.PerformancePoint(1936, 1072, 30)));
    719         assertTrue(new VideoCapabilities.PerformancePoint(1920, 1080, 30, 120, new Size(1, 1))
    720                    .covers(new VideoCapabilities.PerformancePoint(1280, 720, 68)));
    721     }
    722 
    723     private void verifyPerformancePoints(
    724             MediaCodecInfo info, String mediaType,
    725             List<VideoCapabilities.PerformancePoint> points) {
    726         List<VideoCapabilities.PerformancePoint> standardPoints = Arrays.asList(
    727                 VideoCapabilities.PerformancePoint.UHD_240,
    728                 VideoCapabilities.PerformancePoint.UHD_200,
    729                 VideoCapabilities.PerformancePoint.UHD_120,
    730                 VideoCapabilities.PerformancePoint.UHD_100,
    731                 VideoCapabilities.PerformancePoint.UHD_60,
    732                 VideoCapabilities.PerformancePoint.UHD_50,
    733                 VideoCapabilities.PerformancePoint.UHD_30,
    734                 VideoCapabilities.PerformancePoint.UHD_25,
    735                 VideoCapabilities.PerformancePoint.UHD_24,
    736                 VideoCapabilities.PerformancePoint.FHD_240,
    737                 VideoCapabilities.PerformancePoint.FHD_200,
    738                 VideoCapabilities.PerformancePoint.FHD_120,
    739                 VideoCapabilities.PerformancePoint.FHD_100,
    740                 VideoCapabilities.PerformancePoint.FHD_60,
    741                 VideoCapabilities.PerformancePoint.FHD_50,
    742                 VideoCapabilities.PerformancePoint.FHD_30,
    743                 VideoCapabilities.PerformancePoint.FHD_25,
    744                 VideoCapabilities.PerformancePoint.FHD_24,
    745                 VideoCapabilities.PerformancePoint.HD_240,
    746                 VideoCapabilities.PerformancePoint.HD_200,
    747                 VideoCapabilities.PerformancePoint.HD_120,
    748                 VideoCapabilities.PerformancePoint.HD_100,
    749                 VideoCapabilities.PerformancePoint.HD_60,
    750                 VideoCapabilities.PerformancePoint.HD_50,
    751                 VideoCapabilities.PerformancePoint.HD_30,
    752                 VideoCapabilities.PerformancePoint.HD_25,
    753                 VideoCapabilities.PerformancePoint.HD_24,
    754                 VideoCapabilities.PerformancePoint.SD_60,
    755                 VideoCapabilities.PerformancePoint.SD_50,
    756                 VideoCapabilities.PerformancePoint.SD_48,
    757                 VideoCapabilities.PerformancePoint.SD_30,
    758                 VideoCapabilities.PerformancePoint.SD_25,
    759                 VideoCapabilities.PerformancePoint.SD_24);
    760 
    761         // Components must list all supported standard performance points unless those performance
    762         // points are covered by other listed standard performance points.
    763         for (VideoCapabilities.PerformancePoint pp : points) {
    764             if (standardPoints.contains(pp)) {
    765                 // standard points must not be covered by other listed standard points
    766                 for (VideoCapabilities.PerformancePoint pp2 : points) {
    767                     if (!standardPoints.contains(pp2)) {
    768                         continue;
    769                     }
    770                     // using object equality to determine otherness
    771                     assertFalse("standard " + pp2 + " for " + info.getCanonicalName()
    772                             + " for media type " + mediaType + " covers standard " + pp,
    773                             pp2 != pp && pp2.covers(pp));
    774                 }
    775             } else {
    776                 // non-standard points must list all covered standard point not covered by another
    777                 // listed standard point
    778                 for (VideoCapabilities.PerformancePoint spp : standardPoints) {
    779                     if (pp.covers(spp)) {
    780                         // Must be either listed or covered by another standard. Since a point
    781                         // covers itself, it is sufficient to check that it is covered by a listed
    782                         // standard point.
    783                         boolean covered = false;
    784                         for (VideoCapabilities.PerformancePoint pp2 : points) {
    785                             // using object equality to determine otherness
    786                             if (standardPoints.contains(pp2) && pp2.covers(spp)) {
    787                                 covered = true;
    788                                 break;
    789                             }
    790                         }
    791                         assertTrue(pp + " for " + info.getCanonicalName() + " for media type "
    792                                 + mediaType + " covers standard " + spp
    793                                 + " that is not covered by a listed standard point",
    794                                 covered);
    795                     }
    796                 }
    797                 // non-standard points should not be covered by any other performance point
    798                 for (VideoCapabilities.PerformancePoint pp2 : points) {
    799                     // using object equality to determine otherness
    800                     assertFalse(pp2 + " for " + info.getCanonicalName()
    801                             + " for media type " + mediaType + " covers " + pp,
    802                             pp2 != pp && pp2.covers(pp));
    803                 }
    804             }
    805         }
    806     }
    807 
    808     public void testAllHardwareAcceleratedVideoCodecsPublishPerformancePoints() {
    809         List<String> mandatoryTypes = Arrays.asList(
    810                 MediaFormat.MIMETYPE_VIDEO_AVC,
    811                 MediaFormat.MIMETYPE_VIDEO_VP8,
    812                 MediaFormat.MIMETYPE_VIDEO_DOLBY_VISION,
    813                 MediaFormat.MIMETYPE_VIDEO_HEVC,
    814                 MediaFormat.MIMETYPE_VIDEO_VP9,
    815                 MediaFormat.MIMETYPE_VIDEO_AV1);
    816 
    817         String[] featuresToConfig = new String[] {
    818             FEATURE_SecurePlayback,
    819             FEATURE_TunneledPlayback,
    820         };
    821 
    822         Set<Pair<String, Integer>> describedTypes = new HashSet<>(); // mediaType - featureIndex
    823         Set<Pair<String, Integer>> supportedTypes = new HashSet<>(); // mediaType - featureIndex
    824 
    825         // Once any hardware codec performance is described, we assume that all hardware codecs
    826         // must be described, even if we cannot confirm expanded codec info support.
    827         boolean hasPerformancePoints = hasExpandedCodecInfo();
    828         if (!hasPerformancePoints) {
    829             for (MediaCodecInfo info : mAllInfos) {
    830                 String[] types = info.getSupportedTypes();
    831                 for (int j = 0; j < types.length; ++j) {
    832                     String mediaType = types[j];
    833                     CodecCapabilities cap = info.getCapabilitiesForType(mediaType);
    834                     VideoCapabilities videoCap = cap.getVideoCapabilities();
    835                     if (videoCap != null
    836                             && videoCap.getSupportedPerformancePoints() != null) {
    837                         hasPerformancePoints = true;
    838                         break;
    839                     }
    840                 }
    841                 if (hasPerformancePoints) {
    842                     break;
    843                 }
    844             }
    845         }
    846 
    847         for (MediaCodecInfo info : mAllInfos) {
    848             String[] types = info.getSupportedTypes();
    849             for (int j = 0; j < types.length; ++j) {
    850                 String mediaType = types[j];
    851                 CodecCapabilities cap = info.getCapabilitiesForType(mediaType);
    852                 MediaFormat defaultFormat = cap.getDefaultFormat();
    853                 VideoCapabilities videoCap = cap.getVideoCapabilities();
    854 
    855                 Log.d(TAG, "codec: " + info.getName() + " canonical: " + info.getCanonicalName()
    856                         + " type: " + mediaType);
    857 
    858                 if (videoCap == null) {
    859                     assertFalse("no video capabilities for video media type " + mediaType + " of "
    860                                     + info.getName(),
    861                                 mediaType.toLowerCase().startsWith("video/"));
    862                     continue;
    863                 }
    864 
    865                 List<VideoCapabilities.PerformancePoint> pps =
    866                     videoCap.getSupportedPerformancePoints();
    867 
    868                 // see which feature combinations are supported by this codec
    869                 // we do this by counting in binary up to a number of bits
    870                 List<Integer> supportedFeatureConfigs = new ArrayList<Integer>();
    871                 for (int cfg_ix = 1 << featuresToConfig.length; --cfg_ix >= 0; ) {
    872                     boolean supported = true;
    873                     for (int f_ix = 0; supported && f_ix < featuresToConfig.length; ++f_ix) {
    874                         if (((cfg_ix >> f_ix) & 1) != 0) {
    875                             // feature is to be enabled
    876                             supported = supported && cap.isFeatureSupported(featuresToConfig[f_ix]);
    877                         } else {
    878                             // feature is not to be enabled
    879                             supported = supported && !cap.isFeatureRequired(featuresToConfig[f_ix]);
    880                         }
    881                     }
    882                     if (supported) {
    883                         supportedFeatureConfigs.add(cfg_ix);
    884                     }
    885                 }
    886 
    887                 Log.d(TAG, "codec supports configs " + Arrays.toString(
    888                         supportedFeatureConfigs.stream().mapToInt(Integer::intValue).toArray()));
    889                 boolean isMandatory = mandatoryTypes.contains(mediaType);
    890                 if (info.isHardwareAccelerated()) {
    891                     for (Integer cfg_ix : supportedFeatureConfigs) {
    892                         Pair<String, Integer> type = Pair.create(mediaType, cfg_ix);
    893                         if (hasPerformancePoints && isMandatory) {
    894                             supportedTypes.add(type);
    895                         }
    896                         if (pps != null && pps.size() > 0) {
    897                             describedTypes.add(type);
    898                         }
    899                     }
    900                 }
    901 
    902                 if (pps == null) {
    903                     if (hasExpandedCodecInfo()) {
    904                         // Hardware-accelerated video components must publish performance points,
    905                         // even if it is an empty list.
    906                         assertFalse("HW-accelerated codec '" + info.getName()
    907                                 + "' must publish performance points", info.isHardwareAccelerated());
    908                     }
    909 
    910                     continue;
    911                 }
    912 
    913                 // At least one hardware accelerated codec for each media type (including secure
    914                 // codecs) must publish valid performance points for AVC/VP8/VP9/HEVC/AV1.
    915                 if (pps.size() == 0) {
    916                     if (isMandatory) {
    917                         Log.d(TAG, "empty performance points list published by HW accelerated" +
    918                                    "component " + info.getName() + " for " + types[j]);
    919                     }
    920                 } else {
    921                     for (VideoCapabilities.PerformancePoint p : pps) {
    922                         Log.d(TAG, "got performance point " + p);
    923                     }
    924                     verifyPerformancePoints(info, types[j], pps);
    925                 }
    926             }
    927         }
    928 
    929         for (Pair<String, Integer> type : supportedTypes) {
    930             assertTrue("codecs for media type " + type.first + " in configuration " + type.second
    931                     + " do not have substantial performance point data",
    932                     describedTypes.contains(type));
    933         }
    934     }
    935 }
    936