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