1 /* 2 * Copyright (C) 2010 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 org.junit.Assert.assertEquals; 20 import static org.junit.Assert.assertFalse; 21 import static org.junit.Assert.assertNotNull; 22 import static org.junit.Assert.assertNull; 23 import static org.junit.Assert.assertTrue; 24 import static org.junit.Assert.fail; 25 import static org.junit.Assume.assumeTrue; 26 27 import android.content.Context; 28 import android.content.pm.PackageManager; 29 import android.hardware.Camera; 30 import android.hardware.Camera.Size; 31 import android.hardware.cts.helpers.CameraUtils; 32 import android.media.CamcorderProfile; 33 import android.media.EncoderProfiles; 34 import android.media.MediaCodecInfo; 35 import android.media.MediaCodecInfo.CodecProfileLevel; 36 import android.media.MediaCodecList; 37 import android.media.MediaFormat; 38 import android.media.MediaRecorder; 39 import android.util.Log; 40 41 import androidx.test.InstrumentationRegistry; 42 import androidx.test.ext.junit.runners.AndroidJUnit4; 43 44 import com.android.compatibility.common.util.FrameworkSpecificTest; 45 import com.android.compatibility.common.util.NonMainlineTest; 46 47 import org.junit.Test; 48 import org.junit.runner.RunWith; 49 50 import java.util.Arrays; 51 import java.util.List; 52 53 @FrameworkSpecificTest 54 @NonMainlineTest 55 @RunWith(AndroidJUnit4.class) 56 public class CamcorderProfileTest { 57 58 private static final String TAG = "CamcorderProfileTest"; 59 private static final int MIN_HIGH_SPEED_FPS = 100; 60 private static final Integer[] ALL_SUPPORTED_QUALITIES = { 61 CamcorderProfile.QUALITY_LOW, 62 CamcorderProfile.QUALITY_HIGH, 63 CamcorderProfile.QUALITY_QCIF, 64 CamcorderProfile.QUALITY_CIF, 65 CamcorderProfile.QUALITY_480P, 66 CamcorderProfile.QUALITY_720P, 67 CamcorderProfile.QUALITY_1080P, 68 CamcorderProfile.QUALITY_QVGA, 69 CamcorderProfile.QUALITY_2160P, 70 CamcorderProfile.QUALITY_VGA, 71 CamcorderProfile.QUALITY_4KDCI, 72 CamcorderProfile.QUALITY_QHD, 73 CamcorderProfile.QUALITY_2K, 74 CamcorderProfile.QUALITY_8KUHD, 75 76 CamcorderProfile.QUALITY_TIME_LAPSE_LOW, 77 CamcorderProfile.QUALITY_TIME_LAPSE_HIGH, 78 CamcorderProfile.QUALITY_TIME_LAPSE_QCIF, 79 CamcorderProfile.QUALITY_TIME_LAPSE_CIF, 80 CamcorderProfile.QUALITY_TIME_LAPSE_480P, 81 CamcorderProfile.QUALITY_TIME_LAPSE_720P, 82 CamcorderProfile.QUALITY_TIME_LAPSE_1080P, 83 CamcorderProfile.QUALITY_TIME_LAPSE_QVGA, 84 CamcorderProfile.QUALITY_TIME_LAPSE_2160P, 85 CamcorderProfile.QUALITY_TIME_LAPSE_VGA, 86 CamcorderProfile.QUALITY_TIME_LAPSE_4KDCI, 87 CamcorderProfile.QUALITY_TIME_LAPSE_QHD, 88 CamcorderProfile.QUALITY_TIME_LAPSE_2K, 89 CamcorderProfile.QUALITY_TIME_LAPSE_8KUHD, 90 91 CamcorderProfile.QUALITY_HIGH_SPEED_LOW, 92 CamcorderProfile.QUALITY_HIGH_SPEED_HIGH, 93 CamcorderProfile.QUALITY_HIGH_SPEED_480P, 94 CamcorderProfile.QUALITY_HIGH_SPEED_720P, 95 CamcorderProfile.QUALITY_HIGH_SPEED_1080P, 96 CamcorderProfile.QUALITY_HIGH_SPEED_2160P, 97 CamcorderProfile.QUALITY_HIGH_SPEED_CIF, 98 CamcorderProfile.QUALITY_HIGH_SPEED_VGA, 99 CamcorderProfile.QUALITY_HIGH_SPEED_4KDCI, 100 }; 101 private static final int LAST_QUALITY = CamcorderProfile.QUALITY_8KUHD; 102 private static final int LAST_TIMELAPSE_QUALITY = CamcorderProfile.QUALITY_TIME_LAPSE_8KUHD; 103 private static final int LAST_HIGH_SPEED_QUALITY = CamcorderProfile.QUALITY_HIGH_SPEED_4KDCI; 104 private static final Integer[] UNKNOWN_QUALITIES = { 105 LAST_QUALITY + 1, // Unknown normal profile quality 106 LAST_TIMELAPSE_QUALITY + 1, // Unknown timelapse profile quality 107 LAST_HIGH_SPEED_QUALITY + 1 // Unknown high speed timelapse profile quality 108 }; 109 110 // Uses get without id if cameraId == -1 and get with id otherwise. getWithOptionalId(int quality, int cameraId)111 private CamcorderProfile getWithOptionalId(int quality, int cameraId) { 112 if (cameraId == -1) { 113 return CamcorderProfile.get(quality); 114 } else { 115 return CamcorderProfile.get(cameraId, quality); 116 } 117 } 118 checkProfile(CamcorderProfile profile, List<Size> videoSizes)119 private void checkProfile(CamcorderProfile profile, List<Size> videoSizes) { 120 Log.v(TAG, String.format("profile: duration=%d, quality=%d, " + 121 "fileFormat=%d, videoCodec=%d, videoBitRate=%d, videoFrameRate=%d, " + 122 "videoFrameWidth=%d, videoFrameHeight=%d, audioCodec=%d, " + 123 "audioBitRate=%d, audioSampleRate=%d, audioChannels=%d", 124 profile.duration, 125 profile.quality, 126 profile.fileFormat, 127 profile.videoCodec, 128 profile.videoBitRate, 129 profile.videoFrameRate, 130 profile.videoFrameWidth, 131 profile.videoFrameHeight, 132 profile.audioCodec, 133 profile.audioBitRate, 134 profile.audioSampleRate, 135 profile.audioChannels)); 136 assertTrue(profile.duration > 0); 137 assertTrue(Arrays.asList(ALL_SUPPORTED_QUALITIES).contains(profile.quality)); 138 assertTrue(profile.videoBitRate > 0); 139 assertTrue(profile.videoFrameRate > 0); 140 assertTrue(profile.videoFrameWidth > 0); 141 assertTrue(profile.videoFrameHeight > 0); 142 assertTrue(profile.audioBitRate > 0); 143 assertTrue(profile.audioSampleRate > 0); 144 assertTrue(profile.audioChannels > 0); 145 assertTrue(isSizeSupported(profile.videoFrameWidth, 146 profile.videoFrameHeight, 147 videoSizes)); 148 } 149 checkAllProfiles(EncoderProfiles allProfiles, CamcorderProfile profile, List<Size> videoSizes)150 private void checkAllProfiles(EncoderProfiles allProfiles, CamcorderProfile profile, 151 List<Size> videoSizes) { 152 Log.v(TAG, String.format("profile: duration=%d, quality=%d, " + 153 "fileFormat=%d, videoCodec=%d, videoBitRate=%d, videoFrameRate=%d, " + 154 "videoFrameWidth=%d, videoFrameHeight=%d, audioCodec=%d, " + 155 "audioBitRate=%d, audioSampleRate=%d, audioChannels=%d", 156 profile.duration, 157 profile.quality, 158 profile.fileFormat, 159 profile.videoCodec, 160 profile.videoBitRate, 161 profile.videoFrameRate, 162 profile.videoFrameWidth, 163 profile.videoFrameHeight, 164 profile.audioCodec, 165 profile.audioBitRate, 166 profile.audioSampleRate, 167 profile.audioChannels)); 168 // generic fields must match the corresponding CamcorderProfile 169 assertEquals(profile.duration, allProfiles.getDefaultDurationSeconds()); 170 assertEquals(profile.fileFormat, allProfiles.getRecommendedFileFormat()); 171 boolean first = true; 172 for (EncoderProfiles.VideoProfile videoProfile : allProfiles.getVideoProfiles()) { 173 if (first) { 174 // the first profile must be the default profile which must match 175 // the corresponding CamcorderProfile 176 assertEquals(profile.videoCodec, videoProfile.getCodec()); 177 assertEquals(profile.videoBitRate, videoProfile.getBitrate()); 178 assertEquals(profile.videoFrameRate, videoProfile.getFrameRate()); 179 first = false; 180 181 // The first video profile must be a basic profile: YUV 4:2:0 8-bit SDR. 182 // This is to ensure backward compatibility with the corresponding 183 // CamcorderProfile, which is always a basic profile. 184 assertEquals(EncoderProfiles.VideoProfile.YUV_420, 185 videoProfile.getChromaSubsampling()); 186 assertEquals(EncoderProfiles.VideoProfile.HDR_NONE, videoProfile.getHdrFormat()); 187 assertEquals(8, videoProfile.getBitDepth()); 188 } 189 // all profiles must be the same size 190 assertEquals(profile.videoFrameWidth, videoProfile.getWidth()); 191 assertEquals(profile.videoFrameHeight, videoProfile.getHeight()); 192 assertTrue(videoProfile.getMediaType() != null); 193 switch (videoProfile.getCodec()) { 194 // don't validate profile for regular codecs as vendors may use vendor specific profile 195 case MediaRecorder.VideoEncoder.H263: 196 assertEquals(MediaFormat.MIMETYPE_VIDEO_H263, videoProfile.getMediaType()); 197 break; 198 case MediaRecorder.VideoEncoder.H264: 199 assertEquals(MediaFormat.MIMETYPE_VIDEO_AVC, videoProfile.getMediaType()); 200 break; 201 case MediaRecorder.VideoEncoder.MPEG_4_SP: 202 assertEquals(MediaFormat.MIMETYPE_VIDEO_MPEG4, videoProfile.getMediaType()); 203 break; 204 case MediaRecorder.VideoEncoder.VP8: 205 assertEquals(MediaFormat.MIMETYPE_VIDEO_VP8, videoProfile.getMediaType()); 206 break; 207 case MediaRecorder.VideoEncoder.HEVC: 208 assertEquals(MediaFormat.MIMETYPE_VIDEO_HEVC, videoProfile.getMediaType()); 209 break; 210 case MediaRecorder.VideoEncoder.VP9: 211 assertEquals(MediaFormat.MIMETYPE_VIDEO_VP9, videoProfile.getMediaType()); 212 break; 213 case MediaRecorder.VideoEncoder.DOLBY_VISION: 214 assertEquals(MediaFormat.MIMETYPE_VIDEO_DOLBY_VISION, videoProfile.getMediaType()); 215 break; 216 case MediaRecorder.VideoEncoder.AV1: 217 assertEquals(MediaFormat.MIMETYPE_VIDEO_AV1, videoProfile.getMediaType()); 218 break; 219 } 220 // Cannot validate profile as vendors may use vendor specific profile. Just read it. 221 int codecProfile = videoProfile.getProfile(); 222 } 223 // there must have been at least one video profile 224 assertFalse("no video profiles in getAll()", first); 225 first = true; 226 for (EncoderProfiles.AudioProfile audioProfile : allProfiles.getAudioProfiles()) { 227 if (first) { 228 // the first profile must be the default profile which must match 229 // the corresponding CamcorderProfile 230 assertEquals(profile.audioCodec, audioProfile.getCodec()); 231 assertEquals(profile.audioBitRate, audioProfile.getBitrate()); 232 assertEquals(profile.audioSampleRate, audioProfile.getSampleRate()); 233 assertEquals(profile.audioChannels, audioProfile.getChannels()); 234 first = false; 235 } 236 assertTrue(audioProfile.getMediaType() != null); 237 switch (audioProfile.getCodec()) { 238 // don't validate profile for regular codecs as vendors may use vendor specific profile 239 case MediaRecorder.AudioEncoder.AMR_NB: 240 assertEquals(MediaFormat.MIMETYPE_AUDIO_AMR_NB, audioProfile.getMediaType()); 241 break; 242 case MediaRecorder.AudioEncoder.AMR_WB: 243 assertEquals(MediaFormat.MIMETYPE_AUDIO_AMR_WB, audioProfile.getMediaType()); 244 break; 245 case MediaRecorder.AudioEncoder.AAC: 246 assertEquals(MediaFormat.MIMETYPE_AUDIO_AAC, audioProfile.getMediaType()); 247 break; 248 case MediaRecorder.AudioEncoder.HE_AAC: 249 assertEquals(MediaFormat.MIMETYPE_AUDIO_AAC, audioProfile.getMediaType()); 250 assertEquals(MediaCodecInfo.CodecProfileLevel.AACObjectHE, 251 audioProfile.getProfile()); 252 break; 253 case MediaRecorder.AudioEncoder.AAC_ELD: 254 assertEquals(MediaFormat.MIMETYPE_AUDIO_AAC, audioProfile.getMediaType()); 255 assertEquals(MediaCodecInfo.CodecProfileLevel.AACObjectELD, 256 audioProfile.getProfile()); 257 break; 258 case MediaRecorder.AudioEncoder.VORBIS: 259 assertEquals(MediaFormat.MIMETYPE_AUDIO_VORBIS, audioProfile.getMediaType()); 260 break; 261 case MediaRecorder.AudioEncoder.OPUS: 262 assertEquals(MediaFormat.MIMETYPE_AUDIO_OPUS, audioProfile.getMediaType()); 263 break; 264 default: 265 // there may be some extended profiles we don't know about and that's OK 266 break; 267 } 268 } 269 } 270 assertProfileEquals(CamcorderProfile expectedProfile, CamcorderProfile actualProfile)271 private void assertProfileEquals(CamcorderProfile expectedProfile, 272 CamcorderProfile actualProfile) { 273 assertEquals(expectedProfile.duration, actualProfile.duration); 274 assertEquals(expectedProfile.fileFormat, actualProfile.fileFormat); 275 assertEquals(expectedProfile.videoCodec, actualProfile.videoCodec); 276 assertEquals(expectedProfile.videoBitRate, actualProfile.videoBitRate); 277 assertEquals(expectedProfile.videoFrameRate, actualProfile.videoFrameRate); 278 assertEquals(expectedProfile.videoFrameWidth, actualProfile.videoFrameWidth); 279 assertEquals(expectedProfile.videoFrameHeight, actualProfile.videoFrameHeight); 280 assertEquals(expectedProfile.audioCodec, actualProfile.audioCodec); 281 assertEquals(expectedProfile.audioBitRate, actualProfile.audioBitRate); 282 assertEquals(expectedProfile.audioSampleRate, actualProfile.audioSampleRate); 283 assertEquals(expectedProfile.audioChannels, actualProfile.audioChannels); 284 } 285 checkSpecificProfileDimensions(CamcorderProfile profile, int quality)286 private void checkSpecificProfileDimensions(CamcorderProfile profile, int quality) { 287 Log.v(TAG, String.format("specific profile: quality=%d, width = %d, height = %d", 288 profile.quality, profile.videoFrameWidth, profile.videoFrameHeight)); 289 290 switch (quality) { 291 case CamcorderProfile.QUALITY_QCIF: 292 case CamcorderProfile.QUALITY_TIME_LAPSE_QCIF: 293 assertEquals(176, profile.videoFrameWidth); 294 assertEquals(144, profile.videoFrameHeight); 295 break; 296 297 case CamcorderProfile.QUALITY_CIF: 298 case CamcorderProfile.QUALITY_TIME_LAPSE_CIF: 299 assertEquals(352, profile.videoFrameWidth); 300 assertEquals(288, profile.videoFrameHeight); 301 break; 302 303 case CamcorderProfile.QUALITY_480P: 304 case CamcorderProfile.QUALITY_TIME_LAPSE_480P: 305 assertTrue(720 == profile.videoFrameWidth || // SMPTE 293M/ITU-R Rec. 601 306 640 == profile.videoFrameWidth || // ATSC/NTSC (square sampling) 307 704 == profile.videoFrameWidth); // ATSC/NTSC (non-square sampling) 308 assertEquals(480, profile.videoFrameHeight); 309 break; 310 311 case CamcorderProfile.QUALITY_720P: 312 case CamcorderProfile.QUALITY_TIME_LAPSE_720P: 313 assertEquals(1280, profile.videoFrameWidth); 314 assertEquals(720, profile.videoFrameHeight); 315 break; 316 317 case CamcorderProfile.QUALITY_1080P: 318 case CamcorderProfile.QUALITY_TIME_LAPSE_1080P: 319 // 1080p could be either 1920x1088 or 1920x1080. 320 assertEquals(1920, profile.videoFrameWidth); 321 assertTrue(1088 == profile.videoFrameHeight || 322 1080 == profile.videoFrameHeight); 323 break; 324 case CamcorderProfile.QUALITY_2K: 325 case CamcorderProfile.QUALITY_TIME_LAPSE_2K: 326 // 2K could be either 2048x1088 or 2048x1080 327 assertEquals(2048, profile.videoFrameWidth); 328 assertTrue(1088 == profile.videoFrameHeight || 329 1080 == profile.videoFrameHeight); 330 break; 331 case CamcorderProfile.QUALITY_QHD: 332 case CamcorderProfile.QUALITY_TIME_LAPSE_QHD: 333 assertEquals(2560, profile.videoFrameWidth); 334 assertEquals(1440, profile.videoFrameHeight); 335 break; 336 case CamcorderProfile.QUALITY_2160P: 337 case CamcorderProfile.QUALITY_TIME_LAPSE_2160P: 338 assertEquals(3840, profile.videoFrameWidth); 339 assertEquals(2160, profile.videoFrameHeight); 340 break; 341 case CamcorderProfile.QUALITY_HIGH_SPEED_480P: 342 assertTrue(720 == profile.videoFrameWidth || // SMPTE 293M/ITU-R Rec. 601 343 640 == profile.videoFrameWidth || // ATSC/NTSC (square sampling) 344 704 == profile.videoFrameWidth); // ATSC/NTSC (non-square sampling) 345 assertEquals(480, profile.videoFrameHeight); 346 assertTrue(profile.videoFrameRate >= MIN_HIGH_SPEED_FPS); 347 break; 348 case CamcorderProfile.QUALITY_HIGH_SPEED_720P: 349 assertEquals(1280, profile.videoFrameWidth); 350 assertEquals(720, profile.videoFrameHeight); 351 assertTrue(profile.videoFrameRate >= MIN_HIGH_SPEED_FPS); 352 break; 353 case CamcorderProfile.QUALITY_HIGH_SPEED_1080P: 354 // 1080p could be either 1920x1088 or 1920x1080. 355 assertEquals(1920, profile.videoFrameWidth); 356 assertTrue(1088 == profile.videoFrameHeight || 357 1080 == profile.videoFrameHeight); 358 assertTrue(profile.videoFrameRate >= MIN_HIGH_SPEED_FPS); 359 break; 360 } 361 } 362 363 // Checks if the existing specific profiles have the correct dimensions. 364 // Also checks that the mimimum quality specific profile matches the low profile and 365 // similarly that the maximum quality specific profile matches the high profile. checkSpecificProfiles( int cameraId, CamcorderProfile low, CamcorderProfile high, int[] specificQualities, List<Size> videoSizes)366 private void checkSpecificProfiles( 367 int cameraId, 368 CamcorderProfile low, 369 CamcorderProfile high, 370 int[] specificQualities, 371 List<Size> videoSizes) { 372 373 // For high speed levels, low and high quality are optional,skip the test if 374 // they are missing. 375 if (low == null && high == null) { 376 // No profile should be available if low and high are unavailable. 377 for (int quality : specificQualities) { 378 assertFalse(CamcorderProfile.hasProfile(cameraId, quality)); 379 } 380 return; 381 } 382 383 CamcorderProfile minProfile = null; 384 CamcorderProfile maxProfile = null; 385 386 for (int i = 0; i < specificQualities.length; i++) { 387 int quality = specificQualities[i]; 388 if ((cameraId != -1 && CamcorderProfile.hasProfile(cameraId, quality)) || 389 (cameraId == -1 && CamcorderProfile.hasProfile(quality))) { 390 CamcorderProfile profile = getWithOptionalId(quality, cameraId); 391 checkSpecificProfileDimensions(profile, quality); 392 393 assertTrue(isSizeSupported(profile.videoFrameWidth, 394 profile.videoFrameHeight, 395 videoSizes)); 396 397 if (minProfile == null) { 398 minProfile = profile; 399 } 400 maxProfile = profile; 401 } 402 } 403 404 assertNotNull(minProfile); 405 assertNotNull(maxProfile); 406 407 Log.v(TAG, String.format("min profile: quality=%d, width = %d, height = %d", 408 minProfile.quality, minProfile.videoFrameWidth, minProfile.videoFrameHeight)); 409 Log.v(TAG, String.format("max profile: quality=%d, width = %d, height = %d", 410 maxProfile.quality, maxProfile.videoFrameWidth, maxProfile.videoFrameHeight)); 411 412 assertProfileEquals(low, minProfile); 413 assertProfileEquals(high, maxProfile); 414 415 } 416 checkGet(int cameraId)417 private void checkGet(int cameraId) { 418 Log.v(TAG, (cameraId == -1) 419 ? "Checking get without id" 420 : "Checking get with id = " + cameraId); 421 422 Camera camera = null; 423 if (cameraId == -1) { 424 camera = Camera.open(); 425 assumeTrue("Device does not have a back-facing camera", camera != null); 426 } else { 427 camera = Camera.open(cameraId); 428 assertNotNull("failed to open CameraId " + cameraId, camera); 429 } 430 431 final List<Size> videoSizes = CameraUtils.getSupportedVideoSizes(camera); 432 433 camera.release(); 434 camera = null; 435 436 /** 437 * Check all possible supported profiles: get profile should work, and the profile 438 * should be sane. Note that, timelapse and high speed video sizes may not be listed 439 * as supported video sizes from camera, skip the size check. 440 */ 441 for (Integer quality : ALL_SUPPORTED_QUALITIES) { 442 if (CamcorderProfile.hasProfile(cameraId, quality) || isProfileMandatory(quality)) { 443 List<Size> videoSizesToCheck = null; 444 if (quality >= CamcorderProfile.QUALITY_LOW && 445 quality <= CamcorderProfile.QUALITY_2K) { 446 videoSizesToCheck = videoSizes; 447 } 448 CamcorderProfile profile = getWithOptionalId(quality, cameraId); 449 checkProfile(profile, videoSizesToCheck); 450 if (cameraId >= 0) { 451 EncoderProfiles allProfiles = 452 CamcorderProfile.getAll(String.valueOf(cameraId), quality); 453 checkAllProfiles(allProfiles, profile, videoSizesToCheck); 454 } 455 } else { 456 assertNull(CamcorderProfile.getAll(String.valueOf(cameraId), quality)); 457 } 458 } 459 460 /** 461 * Check unknown profiles: hasProfile() should return false. 462 */ 463 for (Integer quality : UNKNOWN_QUALITIES) { 464 assertFalse("Unknown profile quality " + quality + " shouldn't be supported by camera " 465 + cameraId, CamcorderProfile.hasProfile(cameraId, quality)); 466 } 467 468 // High speed low and high profile are optional, 469 // but they should be both present or missing. 470 CamcorderProfile lowHighSpeedProfile = null; 471 CamcorderProfile highHighSpeedProfile = null; 472 if (CamcorderProfile.hasProfile(cameraId, CamcorderProfile.QUALITY_HIGH_SPEED_LOW)) { 473 lowHighSpeedProfile = 474 getWithOptionalId(CamcorderProfile.QUALITY_HIGH_SPEED_LOW, cameraId); 475 } 476 if (CamcorderProfile.hasProfile(cameraId, CamcorderProfile.QUALITY_HIGH_SPEED_HIGH)) { 477 highHighSpeedProfile = 478 getWithOptionalId(CamcorderProfile.QUALITY_HIGH_SPEED_HIGH, cameraId); 479 } 480 if (lowHighSpeedProfile != null) { 481 assertNotNull("high speed high quality profile should be supported if low" + 482 " is supported ", 483 highHighSpeedProfile); 484 checkProfile(lowHighSpeedProfile, null); 485 checkProfile(highHighSpeedProfile, null); 486 } else { 487 assertNull("high speed high quality profile shouldn't be supported if " + 488 "low is unsupported ", highHighSpeedProfile); 489 } 490 491 int[] specificProfileQualities = {CamcorderProfile.QUALITY_QCIF, 492 CamcorderProfile.QUALITY_QVGA, 493 CamcorderProfile.QUALITY_CIF, 494 CamcorderProfile.QUALITY_480P, 495 CamcorderProfile.QUALITY_720P, 496 CamcorderProfile.QUALITY_1080P, 497 CamcorderProfile.QUALITY_2K, 498 CamcorderProfile.QUALITY_QHD, 499 CamcorderProfile.QUALITY_2160P}; 500 501 int[] specificTimeLapseProfileQualities = {CamcorderProfile.QUALITY_TIME_LAPSE_QCIF, 502 CamcorderProfile.QUALITY_TIME_LAPSE_QVGA, 503 CamcorderProfile.QUALITY_TIME_LAPSE_CIF, 504 CamcorderProfile.QUALITY_TIME_LAPSE_480P, 505 CamcorderProfile.QUALITY_TIME_LAPSE_720P, 506 CamcorderProfile.QUALITY_TIME_LAPSE_1080P, 507 CamcorderProfile.QUALITY_TIME_LAPSE_2K, 508 CamcorderProfile.QUALITY_TIME_LAPSE_QHD, 509 CamcorderProfile.QUALITY_TIME_LAPSE_2160P}; 510 511 int[] specificHighSpeedProfileQualities = {CamcorderProfile.QUALITY_HIGH_SPEED_480P, 512 CamcorderProfile.QUALITY_HIGH_SPEED_720P, 513 CamcorderProfile.QUALITY_HIGH_SPEED_1080P, 514 CamcorderProfile.QUALITY_HIGH_SPEED_2160P}; 515 516 CamcorderProfile lowProfile = 517 getWithOptionalId(CamcorderProfile.QUALITY_LOW, cameraId); 518 CamcorderProfile highProfile = 519 getWithOptionalId(CamcorderProfile.QUALITY_HIGH, cameraId); 520 CamcorderProfile lowTimeLapseProfile = 521 getWithOptionalId(CamcorderProfile.QUALITY_TIME_LAPSE_LOW, cameraId); 522 CamcorderProfile highTimeLapseProfile = 523 getWithOptionalId(CamcorderProfile.QUALITY_TIME_LAPSE_HIGH, cameraId); 524 checkSpecificProfiles(cameraId, lowProfile, highProfile, 525 specificProfileQualities, videoSizes); 526 checkSpecificProfiles(cameraId, lowTimeLapseProfile, highTimeLapseProfile, 527 specificTimeLapseProfileQualities, null); 528 checkSpecificProfiles(cameraId, lowHighSpeedProfile, highHighSpeedProfile, 529 specificHighSpeedProfileQualities, null); 530 } 531 532 @Test testGetFirstBackCamera()533 public void testGetFirstBackCamera() { 534 /* 535 * Device may not have rear camera for checkGet(-1). 536 * Checking PackageManager.FEATURE_CAMERA is included or not to decide the flow. 537 * Continue if the feature is included. 538 * Otherwise, exit test. 539 */ 540 Context context = InstrumentationRegistry.getContext(); 541 assertNotNull("did not find context", context); 542 PackageManager pm = context.getPackageManager(); 543 assertNotNull("did not find package manager", pm); 544 if (!pm.hasSystemFeature(PackageManager.FEATURE_CAMERA)) { 545 return; 546 } 547 checkGet(-1); 548 } 549 550 @Test testGetWithId()551 public void testGetWithId() { 552 int nCamera = Camera.getNumberOfCameras(); 553 Context context = InstrumentationRegistry.getContext(); 554 assertNotNull("did not find context", context); 555 for (int cameraId = 0; cameraId < nCamera; cameraId++) { 556 boolean isExternal = false; 557 try { 558 isExternal = CameraUtils.isExternal(context, cameraId); 559 } catch (Exception e) { 560 Log.e(TAG, "Unable to query external camera: " + e); 561 } 562 563 if (!isExternal) { 564 checkGet(cameraId); 565 } 566 } 567 } 568 569 MediaCodecList mCodecList = new MediaCodecList(MediaCodecList.ALL_CODECS); 570 checkSupportedEncoder( String mediaType, int width, int height, int frameRate, int profile)571 private void checkSupportedEncoder( 572 String mediaType, int width, int height, int frameRate, int profile) { 573 MediaFormat format = MediaFormat.createVideoFormat(mediaType, width, height); 574 format.setInteger(MediaFormat.KEY_FRAME_RATE, frameRate); 575 format.setInteger(MediaFormat.KEY_PROFILE, profile); 576 format.setInteger(MediaFormat.KEY_LEVEL, 0 /* unknown */); 577 for (MediaCodecInfo info : mCodecList.getCodecInfos()) { 578 if (!info.isEncoder() || !info.isHardwareAccelerated()) { 579 continue; 580 } 581 MediaCodecInfo.CodecCapabilities caps = info.getCapabilitiesForType(mediaType); 582 if (caps == null) { 583 continue; 584 } 585 if (caps.isFormatSupported(format)) { 586 return; 587 } 588 } 589 fail( 590 "No supported encoder found: " 591 + width 592 + "x" 593 + height 594 + "@" 595 + frameRate 596 + " for " 597 + mediaType 598 + " at profile " 599 + profile); 600 } 601 checkHdrProfile( int cameraId, String mediaType, int camHdrFormat, List<Integer> mediaHdrProfiles)602 private boolean checkHdrProfile( 603 int cameraId, String mediaType, int camHdrFormat, List<Integer> mediaHdrProfiles) { 604 boolean hasSupportedProfiles = false; 605 for (Integer quality : ALL_SUPPORTED_QUALITIES) { 606 if (!CamcorderProfile.hasProfile(cameraId, quality)) { 607 continue; 608 } 609 CamcorderProfile profile = getWithOptionalId(quality, cameraId); 610 if (profile == null) { 611 continue; 612 } 613 EncoderProfiles allProfiles = 614 CamcorderProfile.getAll(String.valueOf(cameraId), quality); 615 for (EncoderProfiles.VideoProfile videoProfile : allProfiles.getVideoProfiles()) { 616 Log.i( 617 TAG, 618 "Video encoder profile: cameraId=" 619 + cameraId 620 + " mediaType=" 621 + videoProfile.getMediaType() 622 + " hdrFormat=" 623 + videoProfile.getHdrFormat() 624 + " profile=" 625 + videoProfile.getProfile() 626 + " " 627 + videoProfile.getWidth() 628 + "x" 629 + videoProfile.getHeight() 630 + "@" 631 + videoProfile.getFrameRate()); 632 if (!videoProfile.getMediaType().equals(mediaType) 633 || videoProfile.getHdrFormat() != camHdrFormat) { 634 continue; 635 } 636 assertTrue( 637 "Unexpected video profile: " 638 + videoProfile.getProfile() 639 + " expected to be one in " 640 + mediaHdrProfiles, 641 mediaHdrProfiles.contains(videoProfile.getProfile())); 642 checkSupportedEncoder( 643 mediaType, 644 videoProfile.getWidth(), 645 videoProfile.getHeight(), 646 videoProfile.getFrameRate(), 647 videoProfile.getProfile()); 648 hasSupportedProfiles = true; 649 } 650 } 651 return hasSupportedProfiles; 652 } 653 checkAllHdrProfile( String mediaType, int camHdrFormat, List<Integer> mediaHdrProfiles)654 private void checkAllHdrProfile( 655 String mediaType, int camHdrFormat, List<Integer> mediaHdrProfiles) { 656 int nCamera = Camera.getNumberOfCameras(); 657 Context context = InstrumentationRegistry.getContext(); 658 assertNotNull("did not find context", context); 659 boolean hasSupportedProfiles = false; 660 for (int cameraId = 0; cameraId < nCamera; cameraId++) { 661 boolean isExternal = false; 662 try { 663 isExternal = CameraUtils.isExternal(context, cameraId); 664 } catch (Exception e) { 665 Log.e(TAG, "Unable to query external camera: " + e); 666 } 667 668 if (!isExternal) { 669 if (checkHdrProfile(cameraId, mediaType, camHdrFormat, mediaHdrProfiles)) { 670 hasSupportedProfiles = true; 671 } 672 } 673 } 674 assumeTrue( 675 "No profile detected for mediaType=" 676 + mediaType 677 + " hdrFormat=" 678 + camHdrFormat, 679 hasSupportedProfiles); 680 } 681 682 @Test testHevcHlgEncoderSupport()683 public void testHevcHlgEncoderSupport() { 684 checkAllHdrProfile( 685 MediaFormat.MIMETYPE_VIDEO_HEVC, 686 EncoderProfiles.VideoProfile.HDR_HLG, 687 List.of(CodecProfileLevel.HEVCProfileMain10)); 688 } 689 690 @Test testHevcHdr10EncoderSupport()691 public void testHevcHdr10EncoderSupport() { 692 checkAllHdrProfile( 693 MediaFormat.MIMETYPE_VIDEO_HEVC, 694 EncoderProfiles.VideoProfile.HDR_HDR10, 695 List.of(CodecProfileLevel.HEVCProfileMain10HDR10)); 696 } 697 698 @Test testHevcHdr10PlusEncoderSupport()699 public void testHevcHdr10PlusEncoderSupport() { 700 checkAllHdrProfile( 701 MediaFormat.MIMETYPE_VIDEO_HEVC, 702 EncoderProfiles.VideoProfile.HDR_HDR10PLUS, 703 List.of(CodecProfileLevel.HEVCProfileMain10HDR10Plus)); 704 } 705 706 @Test testVp9HlgEncoderSupport()707 public void testVp9HlgEncoderSupport() { 708 checkAllHdrProfile( 709 MediaFormat.MIMETYPE_VIDEO_VP9, 710 EncoderProfiles.VideoProfile.HDR_HLG, 711 List.of(CodecProfileLevel.VP9Profile2, CodecProfileLevel.VP9Profile3)); 712 } 713 714 @Test testVp9Hdr10EncoderSupport()715 public void testVp9Hdr10EncoderSupport() { 716 checkAllHdrProfile( 717 MediaFormat.MIMETYPE_VIDEO_VP9, 718 EncoderProfiles.VideoProfile.HDR_HDR10, 719 List.of(CodecProfileLevel.VP9Profile2HDR, CodecProfileLevel.VP9Profile3HDR)); 720 } 721 722 @Test testVp9Hdr10PlusEncoderSupport()723 public void testVp9Hdr10PlusEncoderSupport() { 724 checkAllHdrProfile( 725 MediaFormat.MIMETYPE_VIDEO_VP9, 726 EncoderProfiles.VideoProfile.HDR_HDR10PLUS, 727 List.of(CodecProfileLevel.VP9Profile2HDR10Plus, 728 CodecProfileLevel.VP9Profile3HDR10Plus)); 729 } 730 731 @Test testAv1HlgEncoderSupport()732 public void testAv1HlgEncoderSupport() { 733 checkAllHdrProfile( 734 MediaFormat.MIMETYPE_VIDEO_AV1, 735 EncoderProfiles.VideoProfile.HDR_HLG, 736 List.of(CodecProfileLevel.AV1ProfileMain10)); 737 } 738 739 @Test testAv1Hdr10EncoderSupport()740 public void testAv1Hdr10EncoderSupport() { 741 checkAllHdrProfile( 742 MediaFormat.MIMETYPE_VIDEO_AV1, 743 EncoderProfiles.VideoProfile.HDR_HDR10, 744 List.of(CodecProfileLevel.AV1ProfileMain10HDR10)); 745 } 746 747 @Test testAv1Hdr10PlusEncoderSupport()748 public void testAv1Hdr10PlusEncoderSupport() { 749 checkAllHdrProfile( 750 MediaFormat.MIMETYPE_VIDEO_AV1, 751 EncoderProfiles.VideoProfile.HDR_HDR10PLUS, 752 List.of(CodecProfileLevel.AV1ProfileMain10HDR10Plus)); 753 } 754 isSizeSupported(int width, int height, List<Size> sizes)755 private boolean isSizeSupported(int width, int height, List<Size> sizes) { 756 if (sizes == null) return true; 757 758 for (Size size: sizes) { 759 if (size.width == width && size.height == height) { 760 return true; 761 } 762 } 763 Log.e(TAG, "Size (" + width + "x" + height + ") is not supported"); 764 return false; 765 } 766 isProfileMandatory(int quality)767 private boolean isProfileMandatory(int quality) { 768 return (quality == CamcorderProfile.QUALITY_LOW) || 769 (quality == CamcorderProfile.QUALITY_HIGH) || 770 (quality == CamcorderProfile.QUALITY_TIME_LAPSE_LOW) || 771 (quality == CamcorderProfile.QUALITY_TIME_LAPSE_HIGH); 772 } 773 } 774