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.cts; 18 19 20 import android.content.pm.PackageManager; 21 import android.hardware.Camera; 22 import android.hardware.Camera.Parameters; 23 import android.hardware.Camera.Size; 24 import android.media.CamcorderProfile; 25 import android.test.AndroidTestCase; 26 import android.util.Log; 27 28 import java.util.Arrays; 29 import java.util.List; 30 31 @NonMediaMainlineTest 32 public class CamcorderProfileTest extends AndroidTestCase { 33 34 private static final String TAG = "CamcorderProfileTest"; 35 private static final int MIN_HIGH_SPEED_FPS = 100; 36 private static final Integer[] ALL_SUPPORTED_QUALITIES = { 37 CamcorderProfile.QUALITY_LOW, 38 CamcorderProfile.QUALITY_HIGH, 39 CamcorderProfile.QUALITY_QCIF, 40 CamcorderProfile.QUALITY_CIF, 41 CamcorderProfile.QUALITY_480P, 42 CamcorderProfile.QUALITY_720P, 43 CamcorderProfile.QUALITY_1080P, 44 CamcorderProfile.QUALITY_QVGA, 45 CamcorderProfile.QUALITY_2160P, 46 CamcorderProfile.QUALITY_TIME_LAPSE_LOW, 47 CamcorderProfile.QUALITY_TIME_LAPSE_HIGH, 48 CamcorderProfile.QUALITY_TIME_LAPSE_QCIF, 49 CamcorderProfile.QUALITY_TIME_LAPSE_CIF, 50 CamcorderProfile.QUALITY_TIME_LAPSE_480P, 51 CamcorderProfile.QUALITY_TIME_LAPSE_720P, 52 CamcorderProfile.QUALITY_TIME_LAPSE_1080P, 53 CamcorderProfile.QUALITY_TIME_LAPSE_QVGA, 54 CamcorderProfile.QUALITY_TIME_LAPSE_2160P, 55 CamcorderProfile.QUALITY_HIGH_SPEED_LOW, 56 CamcorderProfile.QUALITY_HIGH_SPEED_HIGH, 57 CamcorderProfile.QUALITY_HIGH_SPEED_480P, 58 CamcorderProfile.QUALITY_HIGH_SPEED_720P, 59 CamcorderProfile.QUALITY_HIGH_SPEED_1080P, 60 CamcorderProfile.QUALITY_HIGH_SPEED_2160P 61 }; 62 private static final int LAST_QUALITY = CamcorderProfile.QUALITY_2K; 63 private static final int LAST_TIMELAPSE_QUALITY = CamcorderProfile.QUALITY_TIME_LAPSE_2K; 64 private static final int LAST_HIGH_SPEED_QUALITY = CamcorderProfile.QUALITY_HIGH_SPEED_4KDCI; 65 private static final Integer[] UNKNOWN_QUALITIES = { 66 LAST_QUALITY + 1, // Unknown normal profile quality 67 LAST_TIMELAPSE_QUALITY + 1, // Unknown timelapse profile quality 68 LAST_HIGH_SPEED_QUALITY + 1 // Unknown high speed timelapse profile quality 69 }; 70 71 // Uses get without id if cameraId == -1 and get with id otherwise. getWithOptionalId(int quality, int cameraId)72 private CamcorderProfile getWithOptionalId(int quality, int cameraId) { 73 if (cameraId == -1) { 74 return CamcorderProfile.get(quality); 75 } else { 76 return CamcorderProfile.get(cameraId, quality); 77 } 78 } 79 checkProfile(CamcorderProfile profile, List<Size> videoSizes)80 private void checkProfile(CamcorderProfile profile, List<Size> videoSizes) { 81 Log.v(TAG, String.format("profile: duration=%d, quality=%d, " + 82 "fileFormat=%d, videoCodec=%d, videoBitRate=%d, videoFrameRate=%d, " + 83 "videoFrameWidth=%d, videoFrameHeight=%d, audioCodec=%d, " + 84 "audioBitRate=%d, audioSampleRate=%d, audioChannels=%d", 85 profile.duration, 86 profile.quality, 87 profile.fileFormat, 88 profile.videoCodec, 89 profile.videoBitRate, 90 profile.videoFrameRate, 91 profile.videoFrameWidth, 92 profile.videoFrameHeight, 93 profile.audioCodec, 94 profile.audioBitRate, 95 profile.audioSampleRate, 96 profile.audioChannels)); 97 assertTrue(profile.duration > 0); 98 assertTrue(Arrays.asList(ALL_SUPPORTED_QUALITIES).contains(profile.quality)); 99 assertTrue(profile.videoBitRate > 0); 100 assertTrue(profile.videoFrameRate > 0); 101 assertTrue(profile.videoFrameWidth > 0); 102 assertTrue(profile.videoFrameHeight > 0); 103 assertTrue(profile.audioBitRate > 0); 104 assertTrue(profile.audioSampleRate > 0); 105 assertTrue(profile.audioChannels > 0); 106 assertTrue(isSizeSupported(profile.videoFrameWidth, 107 profile.videoFrameHeight, 108 videoSizes)); 109 } 110 assertProfileEquals(CamcorderProfile expectedProfile, CamcorderProfile actualProfile)111 private void assertProfileEquals(CamcorderProfile expectedProfile, 112 CamcorderProfile actualProfile) { 113 assertEquals(expectedProfile.duration, actualProfile.duration); 114 assertEquals(expectedProfile.fileFormat, actualProfile.fileFormat); 115 assertEquals(expectedProfile.videoCodec, actualProfile.videoCodec); 116 assertEquals(expectedProfile.videoBitRate, actualProfile.videoBitRate); 117 assertEquals(expectedProfile.videoFrameRate, actualProfile.videoFrameRate); 118 assertEquals(expectedProfile.videoFrameWidth, actualProfile.videoFrameWidth); 119 assertEquals(expectedProfile.videoFrameHeight, actualProfile.videoFrameHeight); 120 assertEquals(expectedProfile.audioCodec, actualProfile.audioCodec); 121 assertEquals(expectedProfile.audioBitRate, actualProfile.audioBitRate); 122 assertEquals(expectedProfile.audioSampleRate, actualProfile.audioSampleRate); 123 assertEquals(expectedProfile.audioChannels, actualProfile.audioChannels); 124 } 125 checkSpecificProfileDimensions(CamcorderProfile profile, int quality)126 private void checkSpecificProfileDimensions(CamcorderProfile profile, int quality) { 127 Log.v(TAG, String.format("specific profile: quality=%d, width = %d, height = %d", 128 profile.quality, profile.videoFrameWidth, profile.videoFrameHeight)); 129 130 switch (quality) { 131 case CamcorderProfile.QUALITY_QCIF: 132 case CamcorderProfile.QUALITY_TIME_LAPSE_QCIF: 133 assertEquals(176, profile.videoFrameWidth); 134 assertEquals(144, profile.videoFrameHeight); 135 break; 136 137 case CamcorderProfile.QUALITY_CIF: 138 case CamcorderProfile.QUALITY_TIME_LAPSE_CIF: 139 assertEquals(352, profile.videoFrameWidth); 140 assertEquals(288, profile.videoFrameHeight); 141 break; 142 143 case CamcorderProfile.QUALITY_480P: 144 case CamcorderProfile.QUALITY_TIME_LAPSE_480P: 145 assertTrue(720 == profile.videoFrameWidth || // SMPTE 293M/ITU-R Rec. 601 146 640 == profile.videoFrameWidth || // ATSC/NTSC (square sampling) 147 704 == profile.videoFrameWidth); // ATSC/NTSC (non-square sampling) 148 assertEquals(480, profile.videoFrameHeight); 149 break; 150 151 case CamcorderProfile.QUALITY_720P: 152 case CamcorderProfile.QUALITY_TIME_LAPSE_720P: 153 assertEquals(1280, profile.videoFrameWidth); 154 assertEquals(720, profile.videoFrameHeight); 155 break; 156 157 case CamcorderProfile.QUALITY_1080P: 158 case CamcorderProfile.QUALITY_TIME_LAPSE_1080P: 159 // 1080p could be either 1920x1088 or 1920x1080. 160 assertEquals(1920, profile.videoFrameWidth); 161 assertTrue(1088 == profile.videoFrameHeight || 162 1080 == profile.videoFrameHeight); 163 break; 164 case CamcorderProfile.QUALITY_2160P: 165 case CamcorderProfile.QUALITY_TIME_LAPSE_2160P: 166 assertEquals(3840, profile.videoFrameWidth); 167 assertEquals(2160, profile.videoFrameHeight); 168 break; 169 case CamcorderProfile.QUALITY_HIGH_SPEED_480P: 170 assertTrue(720 == profile.videoFrameWidth || // SMPTE 293M/ITU-R Rec. 601 171 640 == profile.videoFrameWidth || // ATSC/NTSC (square sampling) 172 704 == profile.videoFrameWidth); // ATSC/NTSC (non-square sampling) 173 assertEquals(480, profile.videoFrameHeight); 174 assertTrue(profile.videoFrameRate >= MIN_HIGH_SPEED_FPS); 175 break; 176 case CamcorderProfile.QUALITY_HIGH_SPEED_720P: 177 assertEquals(1280, profile.videoFrameWidth); 178 assertEquals(720, profile.videoFrameHeight); 179 assertTrue(profile.videoFrameRate >= MIN_HIGH_SPEED_FPS); 180 break; 181 case CamcorderProfile.QUALITY_HIGH_SPEED_1080P: 182 // 1080p could be either 1920x1088 or 1920x1080. 183 assertEquals(1920, profile.videoFrameWidth); 184 assertTrue(1088 == profile.videoFrameHeight || 185 1080 == profile.videoFrameHeight); 186 assertTrue(profile.videoFrameRate >= MIN_HIGH_SPEED_FPS); 187 break; 188 } 189 } 190 191 // Checks if the existing specific profiles have the correct dimensions. 192 // Also checks that the mimimum quality specific profile matches the low profile and 193 // similarly that the maximum quality specific profile matches the high profile. checkSpecificProfiles( int cameraId, CamcorderProfile low, CamcorderProfile high, int[] specificQualities, List<Size> videoSizes)194 private void checkSpecificProfiles( 195 int cameraId, 196 CamcorderProfile low, 197 CamcorderProfile high, 198 int[] specificQualities, 199 List<Size> videoSizes) { 200 201 // For high speed levels, low and high quality are optional,skip the test if 202 // they are missing. 203 if (low == null && high == null) { 204 // No profile should be available if low and high are unavailable. 205 for (int quality : specificQualities) { 206 assertFalse(CamcorderProfile.hasProfile(cameraId, quality)); 207 } 208 return; 209 } 210 211 CamcorderProfile minProfile = null; 212 CamcorderProfile maxProfile = null; 213 214 for (int i = 0; i < specificQualities.length; i++) { 215 int quality = specificQualities[i]; 216 if ((cameraId != -1 && CamcorderProfile.hasProfile(cameraId, quality)) || 217 (cameraId == -1 && CamcorderProfile.hasProfile(quality))) { 218 CamcorderProfile profile = getWithOptionalId(quality, cameraId); 219 checkSpecificProfileDimensions(profile, quality); 220 221 assertTrue(isSizeSupported(profile.videoFrameWidth, 222 profile.videoFrameHeight, 223 videoSizes)); 224 225 if (minProfile == null) { 226 minProfile = profile; 227 } 228 maxProfile = profile; 229 } 230 } 231 232 assertNotNull(minProfile); 233 assertNotNull(maxProfile); 234 235 Log.v(TAG, String.format("min profile: quality=%d, width = %d, height = %d", 236 minProfile.quality, minProfile.videoFrameWidth, minProfile.videoFrameHeight)); 237 Log.v(TAG, String.format("max profile: quality=%d, width = %d, height = %d", 238 maxProfile.quality, maxProfile.videoFrameWidth, maxProfile.videoFrameHeight)); 239 240 assertProfileEquals(low, minProfile); 241 assertProfileEquals(high, maxProfile); 242 243 } 244 checkGet(int cameraId)245 private void checkGet(int cameraId) { 246 Log.v(TAG, (cameraId == -1) 247 ? "Checking get without id" 248 : "Checking get with id = " + cameraId); 249 250 final List<Size> videoSizes = getSupportedVideoSizes(cameraId); 251 252 /** 253 * Check all possible supported profiles: get profile should work, and the profile 254 * should be sane. Note that, timelapse and high speed video sizes may not be listed 255 * as supported video sizes from camera, skip the size check. 256 */ 257 for (Integer quality : ALL_SUPPORTED_QUALITIES) { 258 if (CamcorderProfile.hasProfile(cameraId, quality) || isProfileMandatory(quality)) { 259 List<Size> videoSizesToCheck = null; 260 if (quality >= CamcorderProfile.QUALITY_LOW && 261 quality <= CamcorderProfile.QUALITY_2160P) { 262 videoSizesToCheck = videoSizes; 263 } 264 CamcorderProfile profile = getWithOptionalId(quality, cameraId); 265 checkProfile(profile, videoSizesToCheck); 266 } 267 } 268 269 /** 270 * Check unknown profiles: hasProfile() should return false. 271 */ 272 for (Integer quality : UNKNOWN_QUALITIES) { 273 assertFalse("Unknown profile quality " + quality + " shouldn't be supported by camera " 274 + cameraId, CamcorderProfile.hasProfile(cameraId, quality)); 275 } 276 277 // High speed low and high profile are optional, 278 // but they should be both present or missing. 279 CamcorderProfile lowHighSpeedProfile = null; 280 CamcorderProfile highHighSpeedProfile = null; 281 if (CamcorderProfile.hasProfile(cameraId, CamcorderProfile.QUALITY_HIGH_SPEED_LOW)) { 282 lowHighSpeedProfile = 283 getWithOptionalId(CamcorderProfile.QUALITY_HIGH_SPEED_LOW, cameraId); 284 } 285 if (CamcorderProfile.hasProfile(cameraId, CamcorderProfile.QUALITY_HIGH_SPEED_HIGH)) { 286 highHighSpeedProfile = 287 getWithOptionalId(CamcorderProfile.QUALITY_HIGH_SPEED_HIGH, cameraId); 288 } 289 if (lowHighSpeedProfile != null) { 290 assertNotNull("high speed high quality profile should be supported if low" + 291 " is supported ", 292 highHighSpeedProfile); 293 checkProfile(lowHighSpeedProfile, null); 294 checkProfile(highHighSpeedProfile, null); 295 } else { 296 assertNull("high speed high quality profile shouldn't be supported if " + 297 "low is unsupported ", highHighSpeedProfile); 298 } 299 300 int[] specificProfileQualities = {CamcorderProfile.QUALITY_QCIF, 301 CamcorderProfile.QUALITY_QVGA, 302 CamcorderProfile.QUALITY_CIF, 303 CamcorderProfile.QUALITY_480P, 304 CamcorderProfile.QUALITY_720P, 305 CamcorderProfile.QUALITY_1080P, 306 CamcorderProfile.QUALITY_2160P}; 307 308 int[] specificTimeLapseProfileQualities = {CamcorderProfile.QUALITY_TIME_LAPSE_QCIF, 309 CamcorderProfile.QUALITY_TIME_LAPSE_QVGA, 310 CamcorderProfile.QUALITY_TIME_LAPSE_CIF, 311 CamcorderProfile.QUALITY_TIME_LAPSE_480P, 312 CamcorderProfile.QUALITY_TIME_LAPSE_720P, 313 CamcorderProfile.QUALITY_TIME_LAPSE_1080P, 314 CamcorderProfile.QUALITY_TIME_LAPSE_2160P}; 315 316 int[] specificHighSpeedProfileQualities = {CamcorderProfile.QUALITY_HIGH_SPEED_480P, 317 CamcorderProfile.QUALITY_HIGH_SPEED_720P, 318 CamcorderProfile.QUALITY_HIGH_SPEED_1080P, 319 CamcorderProfile.QUALITY_HIGH_SPEED_2160P}; 320 321 CamcorderProfile lowProfile = 322 getWithOptionalId(CamcorderProfile.QUALITY_LOW, cameraId); 323 CamcorderProfile highProfile = 324 getWithOptionalId(CamcorderProfile.QUALITY_HIGH, cameraId); 325 CamcorderProfile lowTimeLapseProfile = 326 getWithOptionalId(CamcorderProfile.QUALITY_TIME_LAPSE_LOW, cameraId); 327 CamcorderProfile highTimeLapseProfile = 328 getWithOptionalId(CamcorderProfile.QUALITY_TIME_LAPSE_HIGH, cameraId); 329 checkSpecificProfiles(cameraId, lowProfile, highProfile, 330 specificProfileQualities, videoSizes); 331 checkSpecificProfiles(cameraId, lowTimeLapseProfile, highTimeLapseProfile, 332 specificTimeLapseProfileQualities, null); 333 checkSpecificProfiles(cameraId, lowHighSpeedProfile, highHighSpeedProfile, 334 specificHighSpeedProfileQualities, null); 335 } 336 testGet()337 public void testGet() { 338 /* 339 * Device may not have rear camera for checkGet(-1). 340 * Checking PackageManager.FEATURE_CAMERA is included or not to decide the flow. 341 * Continue if the feature is included. 342 * Otherwise, exit test. 343 */ 344 PackageManager pm = mContext.getPackageManager(); 345 if (!pm.hasSystemFeature(PackageManager.FEATURE_CAMERA)) { 346 return; 347 } 348 checkGet(-1); 349 } 350 testGetWithId()351 public void testGetWithId() { 352 int nCamera = Camera.getNumberOfCameras(); 353 for (int cameraId = 0; cameraId < nCamera; cameraId++) { 354 checkGet(cameraId); 355 } 356 } 357 getSupportedVideoSizes(int cameraId)358 private List<Size> getSupportedVideoSizes(int cameraId) { 359 Camera camera = (cameraId == -1)? Camera.open(): Camera.open(cameraId); 360 Parameters parameters = camera.getParameters(); 361 List<Size> videoSizes = parameters.getSupportedVideoSizes(); 362 if (videoSizes == null) { 363 videoSizes = parameters.getSupportedPreviewSizes(); 364 assertNotNull(videoSizes); 365 } 366 camera.release(); 367 return videoSizes; 368 } 369 isSizeSupported(int width, int height, List<Size> sizes)370 private boolean isSizeSupported(int width, int height, List<Size> sizes) { 371 if (sizes == null) return true; 372 373 for (Size size: sizes) { 374 if (size.width == width && size.height == height) { 375 return true; 376 } 377 } 378 Log.e(TAG, "Size (" + width + "x" + height + ") is not supported"); 379 return false; 380 } 381 isProfileMandatory(int quality)382 private boolean isProfileMandatory(int quality) { 383 return (quality == CamcorderProfile.QUALITY_LOW) || 384 (quality == CamcorderProfile.QUALITY_HIGH) || 385 (quality == CamcorderProfile.QUALITY_TIME_LAPSE_LOW) || 386 (quality == CamcorderProfile.QUALITY_TIME_LAPSE_HIGH); 387 } 388 } 389