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