Home | History | Annotate | Download | only in cts
      1 /*
      2  * Copyright (C) 2013 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 package android.media.cts;
     17 
     18 import android.content.pm.PackageManager;
     19 import android.media.MediaCodec;
     20 import android.media.MediaCodecInfo;
     21 import android.media.MediaCodecInfo.AudioCapabilities;
     22 import android.media.MediaCodecInfo.CodecCapabilities;
     23 import android.media.MediaCodecInfo.CodecProfileLevel;
     24 import android.media.MediaCodecInfo.VideoCapabilities;
     25 import static android.media.MediaCodecInfo.CodecProfileLevel.*;
     26 import android.media.MediaCodecList;
     27 import android.media.MediaFormat;
     28 import static android.media.MediaFormat.MIMETYPE_VIDEO_AVC;
     29 import static android.media.MediaFormat.MIMETYPE_VIDEO_H263;
     30 import static android.media.MediaFormat.MIMETYPE_VIDEO_HEVC;
     31 import static android.media.MediaFormat.MIMETYPE_VIDEO_MPEG4;
     32 import static android.media.MediaFormat.MIMETYPE_VIDEO_VP8;
     33 import static android.media.MediaFormat.MIMETYPE_VIDEO_VP9;
     34 import android.media.MediaPlayer;
     35 import android.os.Build;
     36 import android.platform.test.annotations.AppModeFull;
     37 import android.util.Log;
     38 
     39 import com.android.compatibility.common.util.ApiLevelUtil;
     40 import com.android.compatibility.common.util.DynamicConfigDeviceSide;
     41 import com.android.compatibility.common.util.MediaUtils;
     42 
     43 import java.io.IOException;
     44 import java.util.HashSet;
     45 import java.util.Set;
     46 import java.util.Arrays;
     47 import java.util.Vector;
     48 
     49 /**
     50  * Basic sanity test of data returned by MediaCodeCapabilities.
     51  */
     52 @AppModeFull(reason = "Dynamic config disabled.")
     53 public class MediaCodecCapabilitiesTest extends MediaPlayerTestBase {
     54 
     55     private static final String TAG = "MediaCodecCapabilitiesTest";
     56     private static final int PLAY_TIME_MS = 30000;
     57     private static final int TIMEOUT_US = 1000000;  // 1 sec
     58     private static final int IFRAME_INTERVAL = 10;          // 10 seconds between I-frames
     59 
     60     private final MediaCodecList mAllCodecs =
     61             new MediaCodecList(MediaCodecList.ALL_CODECS);
     62     private final MediaCodecInfo[] mAllInfos =
     63             mAllCodecs.getCodecInfos();
     64 
     65     private static final String AVC_BASELINE_12_KEY =
     66             "media_codec_capabilities_test_avc_baseline12";
     67     private static final String AVC_BASELINE_30_KEY =
     68             "media_codec_capabilities_test_avc_baseline30";
     69     private static final String AVC_HIGH_31_KEY = "media_codec_capabilities_test_avc_high31";
     70     private static final String AVC_HIGH_40_KEY = "media_codec_capabilities_test_avc_high40";
     71     private static final String MODULE_NAME = "CtsMediaTestCases";
     72     private DynamicConfigDeviceSide dynamicConfig;
     73 
     74     @Override
     75     protected void setUp() throws Exception {
     76         super.setUp();
     77         dynamicConfig = new DynamicConfigDeviceSide(MODULE_NAME);
     78     }
     79 
     80     // Android device implementations with H.264 encoders, MUST support Baseline Profile Level 3.
     81     // SHOULD support Main Profile/ Level 4, if supported the device must also support Main
     82     // Profile/Level 4 decoding.
     83     public void testH264EncoderProfileAndLevel() throws Exception {
     84         if (!MediaUtils.checkEncoder(MIMETYPE_VIDEO_AVC)) {
     85             return; // skip
     86         }
     87 
     88         assertTrue(
     89                 "H.264 must support Baseline Profile Level 3",
     90                 hasEncoder(MIMETYPE_VIDEO_AVC, AVCProfileBaseline, AVCLevel3));
     91 
     92         if (hasEncoder(MIMETYPE_VIDEO_AVC, AVCProfileMain, AVCLevel4)) {
     93             assertTrue(
     94                     "H.264 decoder must support Main Profile Level 4 if it can encode it",
     95                     hasDecoder(MIMETYPE_VIDEO_AVC, AVCProfileMain, AVCLevel4));
     96         }
     97     }
     98 
     99     // Android device implementations with H.264 decoders, MUST support Baseline Profile Level 3.
    100     // Android Television Devices MUST support High Profile Level 4.2.
    101     public void testH264DecoderProfileAndLevel() throws Exception {
    102         if (!MediaUtils.checkDecoder(MIMETYPE_VIDEO_AVC)) {
    103             return; // skip
    104         }
    105 
    106         assertTrue(
    107                 "H.264 must support Baseline Profile Level 3",
    108                 hasDecoder(MIMETYPE_VIDEO_AVC, AVCProfileBaseline, AVCLevel3));
    109 
    110         if (isTv()) {
    111             assertTrue(
    112                     "H.264 must support High Profile Level 4.2 on TV",
    113                     checkDecoder(MIMETYPE_VIDEO_AVC, AVCProfileHigh, AVCLevel42));
    114         }
    115     }
    116 
    117     // Android device implementations with H.263 encoders, MUST support Level 45.
    118     public void testH263EncoderProfileAndLevel() throws Exception {
    119         if (!MediaUtils.checkEncoder(MIMETYPE_VIDEO_H263)) {
    120             return; // skip
    121         }
    122 
    123         assertTrue(
    124                 "H.263 must support Level 45",
    125                 hasEncoder(MIMETYPE_VIDEO_H263, MPEG4ProfileSimple, H263Level45));
    126     }
    127 
    128     // Android device implementations with H.263 decoders, MUST support Level 30.
    129     public void testH263DecoderProfileAndLevel() throws Exception {
    130         if (!MediaUtils.checkDecoder(MIMETYPE_VIDEO_H263)) {
    131             return; // skip
    132         }
    133 
    134         assertTrue(
    135                 "H.263 must support Level 30",
    136                 hasDecoder(MIMETYPE_VIDEO_H263, MPEG4ProfileSimple, H263Level30));
    137     }
    138 
    139     // Android device implementations with MPEG-4 decoders, MUST support Simple Profile Level 3.
    140     public void testMpeg4DecoderProfileAndLevel() throws Exception {
    141         if (!MediaUtils.checkDecoder(MIMETYPE_VIDEO_MPEG4)) {
    142             return; // skip
    143         }
    144 
    145         assertTrue(
    146                 "MPEG-4 must support Simple Profile Level 3",
    147                 hasDecoder(MIMETYPE_VIDEO_MPEG4, MPEG4ProfileSimple, MPEG4Level3));
    148     }
    149 
    150     // Android device implementations, when supporting H.265 codec MUST support the Main Profile
    151     // Level 3 Main tier.
    152     // Android Television Devices MUST support the Main Profile Level 4.1 Main tier.
    153     // When the UHD video decoding profile is supported, it MUST support Main10 Level 5 Main
    154     // Tier profile.
    155     public void testH265DecoderProfileAndLevel() throws Exception {
    156         if (!MediaUtils.checkDecoder(MIMETYPE_VIDEO_HEVC)) {
    157             return; // skip
    158         }
    159 
    160         assertTrue(
    161                 "H.265 must support Main Profile Main Tier Level 3",
    162                 hasDecoder(MIMETYPE_VIDEO_HEVC, HEVCProfileMain, HEVCMainTierLevel3));
    163 
    164         if (isTv()) {
    165             assertTrue(
    166                     "H.265 must support Main Profile Main Tier Level 4.1 on TV",
    167                     hasDecoder(MIMETYPE_VIDEO_HEVC, HEVCProfileMain, HEVCMainTierLevel41));
    168         }
    169 
    170         if (isTv() && MediaUtils.canDecodeVideo(MIMETYPE_VIDEO_HEVC, 3840, 2160, 30)) {
    171             assertTrue(
    172                     "H.265 must support Main10 Profile Main Tier Level 5 if UHD is supported",
    173                     hasDecoder(MIMETYPE_VIDEO_HEVC, HEVCProfileMain10, HEVCMainTierLevel5));
    174         }
    175     }
    176 
    177     public void testAvcBaseline1() throws Exception {
    178         if (!checkDecoder(MIMETYPE_VIDEO_AVC, AVCProfileBaseline, AVCLevel1)) {
    179             return; // skip
    180         }
    181 
    182         // TODO: add a test stream
    183         MediaUtils.skipTest(TAG, "no test stream");
    184     }
    185 
    186     public void testAvcBaseline12() throws Exception {
    187         if (!checkDecoder(MIMETYPE_VIDEO_AVC, AVCProfileBaseline, AVCLevel12)) {
    188             return; // skip
    189         }
    190 
    191         if (checkDecodeWithDefaultPlayer(MIMETYPE_VIDEO_AVC, AVCProfileBaseline, AVCLevel12)) {
    192             String urlString = dynamicConfig.getValue(AVC_BASELINE_12_KEY);
    193             playVideoWithRetries(urlString, 256, 144, PLAY_TIME_MS);
    194         }
    195     }
    196 
    197     public void testAvcBaseline30() throws Exception {
    198         if (!checkDecoder(MIMETYPE_VIDEO_AVC, AVCProfileBaseline, AVCLevel3)) {
    199             return; // skip
    200         }
    201 
    202         if (checkDecodeWithDefaultPlayer(MIMETYPE_VIDEO_AVC, AVCProfileBaseline, AVCLevel3)) {
    203             String urlString = dynamicConfig.getValue(AVC_BASELINE_30_KEY);
    204             playVideoWithRetries(urlString, 640, 360, PLAY_TIME_MS);
    205         }
    206     }
    207 
    208     public void testAvcHigh31() throws Exception {
    209         if (!checkDecoder(MIMETYPE_VIDEO_AVC, AVCProfileHigh, AVCLevel31)) {
    210             return; // skip
    211         }
    212 
    213         if (checkDecodeWithDefaultPlayer(MIMETYPE_VIDEO_AVC, AVCProfileHigh, AVCLevel31)) {
    214             String urlString = dynamicConfig.getValue(AVC_HIGH_31_KEY);
    215             playVideoWithRetries(urlString, 1280, 720, PLAY_TIME_MS);
    216         }
    217     }
    218 
    219     public void testAvcHigh40() throws Exception {
    220         if (!checkDecoder(MIMETYPE_VIDEO_AVC, AVCProfileHigh, AVCLevel4)) {
    221             return; // skip
    222         }
    223         if (ApiLevelUtil.isBefore(18)) {
    224             MediaUtils.skipTest(TAG, "fragmented mp4 not supported");
    225             return;
    226         }
    227 
    228         if (checkDecodeWithDefaultPlayer(MIMETYPE_VIDEO_AVC, AVCProfileHigh, AVCLevel4)) {
    229             String urlString = dynamicConfig.getValue(AVC_HIGH_40_KEY);
    230             playVideoWithRetries(urlString, 1920, 1080, PLAY_TIME_MS);
    231         }
    232     }
    233 
    234     public void testHevcMain1() throws Exception {
    235         if (!checkDecoder(MIMETYPE_VIDEO_HEVC, HEVCProfileMain, HEVCMainTierLevel1)) {
    236             return; // skip
    237         }
    238 
    239         // TODO: add a test stream
    240         MediaUtils.skipTest(TAG, "no test stream");
    241     }
    242 
    243     public void testHevcMain2() throws Exception {
    244         if (!checkDecoder(MIMETYPE_VIDEO_HEVC, HEVCProfileMain, HEVCMainTierLevel2)) {
    245             return; // skip
    246         }
    247 
    248         // TODO: add a test stream
    249         MediaUtils.skipTest(TAG, "no test stream");
    250     }
    251 
    252     public void testHevcMain21() throws Exception {
    253         if (!checkDecoder(MIMETYPE_VIDEO_HEVC, HEVCProfileMain, HEVCMainTierLevel21)) {
    254             return; // skip
    255         }
    256 
    257         // TODO: add a test stream
    258         MediaUtils.skipTest(TAG, "no test stream");
    259     }
    260 
    261     public void testHevcMain3() throws Exception {
    262         if (!checkDecoder(MIMETYPE_VIDEO_HEVC, HEVCProfileMain, HEVCMainTierLevel3)) {
    263             return; // skip
    264         }
    265 
    266         // TODO: add a test stream
    267         MediaUtils.skipTest(TAG, "no test stream");
    268     }
    269 
    270     public void testHevcMain31() throws Exception {
    271         if (!checkDecoder(MIMETYPE_VIDEO_HEVC, HEVCProfileMain, HEVCMainTierLevel31)) {
    272             return; // skip
    273         }
    274 
    275         // TODO: add a test stream
    276         MediaUtils.skipTest(TAG, "no test stream");
    277     }
    278 
    279     public void testHevcMain4() throws Exception {
    280         if (!checkDecoder(MIMETYPE_VIDEO_HEVC, HEVCProfileMain, HEVCMainTierLevel4)) {
    281             return; // skip
    282         }
    283 
    284         // TODO: add a test stream
    285         MediaUtils.skipTest(TAG, "no test stream");
    286     }
    287 
    288     public void testHevcMain41() throws Exception {
    289         if (!checkDecoder(MIMETYPE_VIDEO_HEVC, HEVCProfileMain, HEVCMainTierLevel41)) {
    290             return; // skip
    291         }
    292 
    293         // TODO: add a test stream
    294         MediaUtils.skipTest(TAG, "no test stream");
    295     }
    296 
    297     public void testHevcMain5() throws Exception {
    298         if (!checkDecoder(MIMETYPE_VIDEO_HEVC, HEVCProfileMain, HEVCMainTierLevel5)) {
    299             return; // skip
    300         }
    301 
    302         // TODO: add a test stream
    303         MediaUtils.skipTest(TAG, "no test stream");
    304     }
    305 
    306     public void testHevcMain51() throws Exception {
    307         if (!checkDecoder(MIMETYPE_VIDEO_HEVC, HEVCProfileMain, HEVCMainTierLevel51)) {
    308             return; // skip
    309         }
    310 
    311         // TODO: add a test stream
    312         MediaUtils.skipTest(TAG, "no test stream");
    313     }
    314 
    315     private boolean checkDecoder(String mime, int profile, int level) {
    316         if (!hasDecoder(mime, profile, level)) {
    317             MediaUtils.skipTest(TAG, "no " + mime + " decoder for profile "
    318                     + profile + " and level " + level);
    319             return false;
    320         }
    321         return true;
    322     }
    323 
    324     private boolean hasDecoder(String mime, int profile, int level) {
    325         return supports(mime, false /* isEncoder */, profile, level, false /* defaultOnly */);
    326     }
    327 
    328     private boolean hasEncoder(String mime, int profile, int level) {
    329         return supports(mime, true /* isEncoder */, profile, level, false /* defaultOnly */);
    330     }
    331 
    332     // Checks whether the default AOSP player can play back a specific profile and level for a
    333     // given media type. If it cannot, it automatically logs that the test is skipped.
    334     private boolean checkDecodeWithDefaultPlayer(String mime, int profile, int level) {
    335         if (!supports(mime, false /* isEncoder */, profile, level, true /* defaultOnly */)) {
    336             MediaUtils.skipTest(TAG, "default player cannot test codec");
    337             return false;
    338         }
    339         return true;
    340     }
    341 
    342     private boolean supports(
    343             String mime, boolean isEncoder, int profile, int level,
    344             boolean defaultOnly) {
    345         MediaCodecList mcl = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
    346         for (MediaCodecInfo info : mcl.getCodecInfos()) {
    347             if (isEncoder != info.isEncoder()) {
    348                 continue;
    349             }
    350             try {
    351                 CodecCapabilities caps = info.getCapabilitiesForType(mime);
    352                 for (CodecProfileLevel pl : caps.profileLevels) {
    353                     if (pl.profile != profile) {
    354                         continue;
    355                     }
    356 
    357                     // H.263 levels are not completely ordered:
    358                     // Level45 support only implies Level10 support
    359                     if (mime.equalsIgnoreCase(MIMETYPE_VIDEO_H263)) {
    360                         if (pl.level != level && pl.level == H263Level45 && level > H263Level10) {
    361                             continue;
    362                         }
    363                     }
    364                     if (pl.level >= level) {
    365                         return true;
    366                     }
    367                 }
    368                 // the default AOSP player picks the first codec for a specific mime type, so
    369                 // we can stop after the first one found
    370                 if (defaultOnly) {
    371                     return false;
    372                 }
    373             } catch (IllegalArgumentException e) {
    374             }
    375         }
    376         return false;
    377     }
    378 
    379     private boolean isVideoMime(String mime) {
    380         return mime.toLowerCase().startsWith("video/");
    381     }
    382 
    383     private Set<String> requiredAdaptiveFormats() {
    384         Set<String> adaptiveFormats = new HashSet<String>();
    385         adaptiveFormats.add(MediaFormat.MIMETYPE_VIDEO_AVC);
    386         adaptiveFormats.add(MediaFormat.MIMETYPE_VIDEO_HEVC);
    387         adaptiveFormats.add(MediaFormat.MIMETYPE_VIDEO_VP8);
    388         adaptiveFormats.add(MediaFormat.MIMETYPE_VIDEO_VP9);
    389         return adaptiveFormats;
    390     }
    391 
    392     public void testHaveAdaptiveVideoDecoderForAllSupportedFormats() {
    393         Set<String> supportedFormats = new HashSet<String>();
    394         boolean skipped = true;
    395 
    396         // gather all supported video formats
    397         for (MediaCodecInfo info : mAllInfos) {
    398             if (info.isEncoder()) {
    399                 continue;
    400             }
    401             for (String mime : info.getSupportedTypes()) {
    402                 if (isVideoMime(mime)) {
    403                     supportedFormats.add(mime);
    404                 }
    405             }
    406         }
    407 
    408         // limit to CDD-required formats for now
    409         supportedFormats.retainAll(requiredAdaptiveFormats());
    410 
    411         // check if there is an adaptive decoder for each
    412         for (String mime : supportedFormats) {
    413             skipped = false;
    414             // implicit assumption that QCIF video is always valid.
    415             MediaFormat format = MediaFormat.createVideoFormat(mime, 176, 144);
    416             format.setFeatureEnabled(CodecCapabilities.FEATURE_AdaptivePlayback, true);
    417             String codec = mAllCodecs.findDecoderForFormat(format);
    418             assertTrue(
    419                     "could not find adaptive decoder for " + mime, codec != null);
    420         }
    421         if (skipped) {
    422             MediaUtils.skipTest("no video decoders that are required to be adaptive found");
    423         }
    424     }
    425 
    426     public void testAllVideoDecodersAreAdaptive() {
    427         Set<String> adaptiveFormats = requiredAdaptiveFormats();
    428         boolean skipped = true;
    429         for (MediaCodecInfo info : mAllInfos) {
    430             if (info.isEncoder()) {
    431                 continue;
    432             }
    433             for (String mime : info.getSupportedTypes()) {
    434                 if (!isVideoMime(mime)
    435                         // limit to CDD-required formats for now
    436                         || !adaptiveFormats.contains(mime)) {
    437                     continue;
    438                 }
    439                 skipped = false;
    440                 CodecCapabilities caps = info.getCapabilitiesForType(mime);
    441                 assertTrue(
    442                     info.getName() + " is not adaptive for " + mime,
    443                     caps.isFeatureSupported(CodecCapabilities.FEATURE_AdaptivePlayback));
    444             }
    445         }
    446         if (skipped) {
    447             MediaUtils.skipTest("no video decoders that are required to be adaptive found");
    448         }
    449     }
    450 
    451     private MediaFormat createReasonableVideoFormat(
    452             CodecCapabilities caps, String mime, boolean encoder, int width, int height) {
    453         VideoCapabilities vidCaps = caps.getVideoCapabilities();
    454         MediaFormat format = MediaFormat.createVideoFormat(mime, width, height);
    455         if (encoder) {
    456             // bitrate
    457             int maxWidth = vidCaps.getSupportedWidths().getUpper();
    458             int maxHeight = vidCaps.getSupportedHeightsFor(width).getUpper();
    459             int maxRate = vidCaps.getSupportedFrameRatesFor(width, height).getUpper().intValue();
    460             int bitrate = vidCaps.getBitrateRange().clamp(
    461                     (int)(vidCaps.getBitrateRange().getUpper()
    462                             / Math.sqrt((double)maxWidth * maxHeight / width / height)));
    463             Log.i(TAG, "reasonable bitrate for " + width + "x" + height + "@" + maxRate
    464                     + " " + mime + " = " + bitrate);
    465             format.setInteger(format.KEY_BIT_RATE, bitrate);
    466             format.setInteger(format.KEY_FRAME_RATE, maxRate);
    467             format.setInteger(format.KEY_I_FRAME_INTERVAL, IFRAME_INTERVAL);
    468         }
    469         return format;
    470     }
    471 
    472     public void testSecureCodecsAdvertiseSecurePlayback() throws IOException {
    473         boolean skipped = true;
    474         for (MediaCodecInfo info : mAllInfos) {
    475             boolean isEncoder = info.isEncoder();
    476             if (isEncoder || !info.getName().endsWith(".secure")) {
    477                 continue;
    478             }
    479             for (String mime : info.getSupportedTypes()) {
    480                 if (!isVideoMime(mime)) {
    481                     continue;
    482                 }
    483                 skipped = false;
    484                 CodecCapabilities caps = info.getCapabilitiesForType(mime);
    485                 assertTrue(
    486                         info.getName() + " does not advertise secure playback",
    487                         caps.isFeatureSupported(CodecCapabilities.FEATURE_SecurePlayback));
    488             }
    489         }
    490         if (skipped) {
    491             MediaUtils.skipTest("no video decoders found ending in .secure");
    492         }
    493     }
    494 
    495     private MediaFormat createVideoFormatForBitrateMode(String mime, int width, int height,
    496             int bitrateMode, CodecCapabilities caps) {
    497         MediaCodecInfo.EncoderCapabilities encoderCaps = caps.getEncoderCapabilities();
    498         if (!encoderCaps.isBitrateModeSupported(bitrateMode)) {
    499             return null;
    500         }
    501 
    502         VideoCapabilities vidCaps = caps.getVideoCapabilities();
    503         MediaFormat format = MediaFormat.createVideoFormat(mime, width, height);
    504 
    505         // bitrate
    506         int maxWidth = vidCaps.getSupportedWidths().getUpper();
    507         int maxHeight = vidCaps.getSupportedHeightsFor(width).getUpper();
    508         int maxRate = vidCaps.getSupportedFrameRatesFor(width, height).getUpper().intValue();
    509         format.setInteger(MediaFormat.KEY_BITRATE_MODE, bitrateMode);
    510         if (bitrateMode == MediaCodecInfo.EncoderCapabilities.BITRATE_MODE_CQ) {
    511             int quality = encoderCaps.getQualityRange().getLower();
    512             Log.i(TAG, "reasonable quality for " + width + "x" + height + "@" + maxRate
    513                     + " " + mime + " = " + quality);
    514             format.setInteger(MediaFormat.KEY_QUALITY, quality);
    515         } else {
    516             int bitrate = vidCaps.getBitrateRange().clamp(
    517                     (int)(vidCaps.getBitrateRange().getUpper()
    518                             / Math.sqrt((double)maxWidth * maxHeight / width / height)));
    519             Log.i(TAG, "reasonable bitrate for " + width + "x" + height + "@" + maxRate
    520                     + " " + mime + " = " + bitrate + " mode " + bitrateMode);
    521             format.setInteger(MediaFormat.KEY_BIT_RATE, bitrate);
    522         }
    523         format.setInteger(MediaFormat.KEY_FRAME_RATE, maxRate);
    524         format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, IFRAME_INTERVAL);
    525         format.setInteger(MediaFormat.KEY_COLOR_FORMAT,
    526                 CodecCapabilities.COLOR_FormatYUV420Flexible);
    527 
    528         return format;
    529     }
    530 
    531     public void testAllAdvertisedVideoEncoderBitrateModes() throws IOException {
    532         boolean skipped = true;
    533         final int[] modes = {
    534                 MediaCodecInfo.EncoderCapabilities.BITRATE_MODE_CQ,
    535                 MediaCodecInfo.EncoderCapabilities.BITRATE_MODE_VBR,
    536                 MediaCodecInfo.EncoderCapabilities.BITRATE_MODE_CBR
    537         };
    538         for (MediaCodecInfo info : mAllInfos) {
    539             if (!info.isEncoder()) {
    540                 continue;
    541             }
    542 
    543             for (String mime: info.getSupportedTypes()) {
    544                 boolean isVideo = isVideoMime(mime);
    545                 if (!isVideo) {
    546                     continue;
    547                 }
    548                 skipped = false;
    549 
    550                 int numSupportedModes = 0;
    551                 for (int mode : modes) {
    552                     MediaFormat format = createVideoFormatForBitrateMode(
    553                             mime, 176, 144, mode, info.getCapabilitiesForType(mime));
    554                     if (format == null) {
    555                         continue;
    556                     }
    557                     MediaCodec codec = null;
    558                     try {
    559                         codec = MediaCodec.createByCodecName(info.getName());
    560                         codec.configure(format, null /* surface */, null /* crypto */,
    561                                 MediaCodec.CONFIGURE_FLAG_ENCODE);
    562                     } finally {
    563                         if (codec != null) {
    564                             codec.release();
    565                         }
    566                     }
    567                     numSupportedModes++;
    568                 }
    569                 assertTrue(info.getName() + " has no supported bitrate mode",
    570                         numSupportedModes > 0);
    571             }
    572         }
    573         if (skipped) {
    574             MediaUtils.skipTest("no video encoders found");
    575         }
    576     }
    577 
    578     public void testAllNonTunneledVideoCodecsSupportFlexibleYUV() throws IOException {
    579         boolean skipped = true;
    580         for (MediaCodecInfo info : mAllInfos) {
    581             boolean isEncoder = info.isEncoder();
    582             for (String mime: info.getSupportedTypes()) {
    583                 if (!isVideoMime(mime)) {
    584                     continue;
    585                 }
    586                 CodecCapabilities caps = info.getCapabilitiesForType(mime);
    587                 if (caps.isFeatureRequired(CodecCapabilities.FEATURE_TunneledPlayback)
    588                         || caps.isFeatureRequired(CodecCapabilities.FEATURE_SecurePlayback)) {
    589                     continue;
    590                 }
    591                 skipped = false;
    592                 boolean found = false;
    593                 for (int c : caps.colorFormats) {
    594                     if (c == caps.COLOR_FormatYUV420Flexible) {
    595                         found = true;
    596                         break;
    597                     }
    598                 }
    599                 assertTrue(
    600                     info.getName() + " does not advertise COLOR_FormatYUV420Flexible",
    601                     found);
    602 
    603                 MediaCodec codec = null;
    604                 MediaFormat format = null;
    605                 try {
    606                     codec = MediaCodec.createByCodecName(info.getName());
    607                     // implicit assumption that QCIF video is always valid.
    608                     format = createReasonableVideoFormat(caps, mime, isEncoder, 176, 144);
    609                     format.setInteger(
    610                             MediaFormat.KEY_COLOR_FORMAT,
    611                             caps.COLOR_FormatYUV420Flexible);
    612 
    613                     codec.configure(format, null /* surface */, null /* crypto */,
    614                             isEncoder ? codec.CONFIGURE_FLAG_ENCODE : 0);
    615                     MediaFormat configuredFormat =
    616                             isEncoder ? codec.getInputFormat() : codec.getOutputFormat();
    617                     Log.d(TAG, "color format is " + configuredFormat.getInteger(
    618                             MediaFormat.KEY_COLOR_FORMAT));
    619                     if (isEncoder) {
    620                         codec.start();
    621                         int ix = codec.dequeueInputBuffer(TIMEOUT_US);
    622                         assertNotNull(
    623                                 info.getName() + " encoder has non-flexYUV input buffer #" + ix,
    624                                 codec.getInputImage(ix));
    625                     } else {
    626                         // TODO: test these on various decoders (need test streams)
    627                     }
    628                 } finally {
    629                     if (codec != null) {
    630                         codec.release();
    631                     }
    632                 }
    633             }
    634         }
    635         if (skipped) {
    636             MediaUtils.skipTest("no non-tunneled/non-secure video decoders found");
    637         }
    638     }
    639 
    640     private static MediaFormat createMinFormat(String mime, CodecCapabilities caps) {
    641         MediaFormat format;
    642         if (caps.getVideoCapabilities() != null) {
    643             VideoCapabilities vcaps = caps.getVideoCapabilities();
    644             int minWidth = vcaps.getSupportedWidths().getLower();
    645             int minHeight = vcaps.getSupportedHeightsFor(minWidth).getLower();
    646             int minBitrate = vcaps.getBitrateRange().getLower();
    647             int minFrameRate = Math.max(vcaps.getSupportedFrameRatesFor(minWidth, minHeight)
    648                     .getLower().intValue(), 1);
    649             format = MediaFormat.createVideoFormat(mime, minWidth, minHeight);
    650             format.setInteger(MediaFormat.KEY_COLOR_FORMAT, caps.colorFormats[0]);
    651             format.setInteger(MediaFormat.KEY_BIT_RATE, minBitrate);
    652             format.setInteger(MediaFormat.KEY_FRAME_RATE, minFrameRate);
    653             format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, IFRAME_INTERVAL);
    654         } else {
    655             AudioCapabilities acaps = caps.getAudioCapabilities();
    656             int minSampleRate = acaps.getSupportedSampleRateRanges()[0].getLower();
    657             int minChannelCount = 1;
    658             int minBitrate = acaps.getBitrateRange().getLower();
    659             format = MediaFormat.createAudioFormat(mime, minSampleRate, minChannelCount);
    660             format.setInteger(MediaFormat.KEY_BIT_RATE, minBitrate);
    661         }
    662 
    663         return format;
    664     }
    665 
    666     private static int getActualMax(
    667             boolean isEncoder, String name, String mime, CodecCapabilities caps, int max) {
    668         int flag = isEncoder ? MediaCodec.CONFIGURE_FLAG_ENCODE : 0;
    669         MediaFormat format = createMinFormat(mime, caps);
    670         Log.d(TAG, "Test format " + format);
    671         Vector<MediaCodec> codecs = new Vector<MediaCodec>();
    672         MediaCodec codec = null;
    673         for (int i = 0; i < max; ++i) {
    674             try {
    675                 Log.d(TAG, "Create codec " + name + " #" + i);
    676                 codec = MediaCodec.createByCodecName(name);
    677                 codec.configure(format, null, null, flag);
    678                 codec.start();
    679                 codecs.add(codec);
    680                 codec = null;
    681             } catch (IllegalArgumentException e) {
    682                 fail("Got unexpected IllegalArgumentException " + e.getMessage());
    683             } catch (IOException e) {
    684                 fail("Got unexpected IOException " + e.getMessage());
    685             } catch (MediaCodec.CodecException e) {
    686                 // ERROR_INSUFFICIENT_RESOURCE is expected as the test keep creating codecs.
    687                 // But other exception should be treated as failure.
    688                 if (e.getErrorCode() == MediaCodec.CodecException.ERROR_INSUFFICIENT_RESOURCE) {
    689                     Log.d(TAG, "Got CodecException with ERROR_INSUFFICIENT_RESOURCE.");
    690                     break;
    691                 } else {
    692                     fail("Unexpected CodecException " + e.getDiagnosticInfo());
    693                 }
    694             } finally {
    695                 if (codec != null) {
    696                     Log.d(TAG, "release codec");
    697                     codec.release();
    698                     codec = null;
    699                 }
    700             }
    701         }
    702         int actualMax = codecs.size();
    703         for (int i = 0; i < codecs.size(); ++i) {
    704             Log.d(TAG, "release codec #" + i);
    705             codecs.get(i).release();
    706         }
    707         codecs.clear();
    708         return actualMax;
    709     }
    710 
    711     private boolean knownTypes(String type) {
    712         return (type.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_AAC  ) ||
    713             type.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_AC3      ) ||
    714             type.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_AMR_NB   ) ||
    715             type.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_AMR_WB   ) ||
    716             type.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_EAC3     ) ||
    717             type.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_FLAC     ) ||
    718             type.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_G711_ALAW) ||
    719             type.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_G711_MLAW) ||
    720             type.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_MPEG     ) ||
    721             type.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_MSGSM    ) ||
    722             type.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_OPUS     ) ||
    723             type.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_RAW      ) ||
    724             type.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_VORBIS   ) ||
    725             type.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_AVC      ) ||
    726             type.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_H263     ) ||
    727             type.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_HEVC     ) ||
    728             type.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_MPEG2    ) ||
    729             type.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_MPEG4    ) ||
    730             type.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_VP8      ) ||
    731             type.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_VP9      ));
    732     }
    733 
    734     public void testGetMaxSupportedInstances() {
    735         final int MAX_INSTANCES = 32;
    736         StringBuilder xmlOverrides = new StringBuilder();
    737         MediaCodecList allCodecs = new MediaCodecList(MediaCodecList.ALL_CODECS);
    738         for (MediaCodecInfo info : allCodecs.getCodecInfos()) {
    739             Log.d(TAG, "codec: " + info.getName());
    740             Log.d(TAG, "  isEncoder = " + info.isEncoder());
    741 
    742             String[] types = info.getSupportedTypes();
    743             for (int j = 0; j < types.length; ++j) {
    744                 if (!knownTypes(types[j])) {
    745                     Log.d(TAG, "skipping unknown type " + types[j]);
    746                     continue;
    747                 }
    748                 Log.d(TAG, "calling getCapabilitiesForType " + types[j]);
    749                 CodecCapabilities caps = info.getCapabilitiesForType(types[j]);
    750                 int max = caps.getMaxSupportedInstances();
    751                 Log.d(TAG, "getMaxSupportedInstances returns " + max);
    752                 assertTrue(max > 0);
    753 
    754                 int actualMax = getActualMax(
    755                         info.isEncoder(), info.getName(), types[j], caps, MAX_INSTANCES);
    756                 Log.d(TAG, "actualMax " + actualMax + " vs reported max " + max);
    757                 if (actualMax < (int)(max * 0.9) || actualMax > (int) Math.ceil(max * 1.1)) {
    758                     String codec = "<MediaCodec name=\"" + info.getName() +
    759                             "\" type=\"" + types[j] + "\" >";
    760                     String limit = "    <Limit name=\"concurrent-instances\" max=\"" +
    761                             actualMax + "\" />";
    762                     xmlOverrides.append(codec);
    763                     xmlOverrides.append("\n");
    764                     xmlOverrides.append(limit);
    765                     xmlOverrides.append("\n");
    766                     xmlOverrides.append("</MediaCodec>\n");
    767                 }
    768             }
    769         }
    770 
    771         if (xmlOverrides.length() > 0) {
    772             String failMessage = "In order to pass the test, please publish following " +
    773                     "codecs' concurrent instances limit in /etc/media_codecs.xml: \n";
    774            fail(failMessage + xmlOverrides.toString());
    775         }
    776     }
    777 }
    778