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