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