AndroidCamera2Settings.java revision d0185cc2f5786571565f01b26e1143ce0099bdc8
1/* 2 * Copyright (C) 2014 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 17package com.android.ex.camera2.portability; 18 19import static android.hardware.camera2.CaptureRequest.*; 20 21import android.graphics.Rect; 22import android.hardware.camera2.CameraAccessException; 23import android.hardware.camera2.CameraDevice; 24import android.hardware.camera2.params.MeteringRectangle; 25import android.location.Location; 26import android.util.Range; 27 28import com.android.ex.camera2.portability.CameraCapabilities.FlashMode; 29import com.android.ex.camera2.portability.CameraCapabilities.FocusMode; 30import com.android.ex.camera2.portability.CameraCapabilities.SceneMode; 31import com.android.ex.camera2.portability.CameraCapabilities.WhiteBalance; 32import com.android.ex.camera2.portability.debug.Log; 33import com.android.ex.camera2.utils.Camera2RequestSettingsSet; 34 35import java.util.List; 36import java.util.Objects; 37 38/** 39 * The subclass of {@link CameraSettings} for Android Camera 2 API. 40 */ 41public class AndroidCamera2Settings extends CameraSettings { 42 private static final Log.Tag TAG = new Log.Tag("AndCam2Set"); 43 44 private final Builder mTemplateSettings; 45 private final Rect mActiveArray; 46 private final Camera2RequestSettingsSet mRequestSettings; 47 48 /** 49 * Create a settings representation that answers queries of unspecified 50 * options in the same way as the provided template would. 51 * 52 * <p>The default settings provided by the given template are only ever used 53 * for reporting back to the client app (i.e. when it queries an option 54 * it didn't explicitly set first). {@link Camera2RequestSettingsSet}s 55 * generated by an instance of this class will have any settings not 56 * modified using one of that instance's mutators forced to default, so that 57 * their effective values when submitting a capture request will be those of 58 * the template that is provided to the camera framework at that time.</p> 59 * 60 * @param camera Device from which to draw default settings. 61 * @param template Specific template to use for the defaults. 62 * @param activeArray Boundary coordinates of the sensor's active array. 63 * @param preview Dimensions of preview streams. 64 * @param photo Dimensions of captured images. 65 * 66 * @throws CameraAccessException Upon internal framework/driver failure. 67 */ 68 public AndroidCamera2Settings(CameraDevice camera, int template, Rect activeArray, 69 Size preview, Size photo) throws CameraAccessException { 70 mTemplateSettings = camera.createCaptureRequest(template); 71 mActiveArray = activeArray; 72 mRequestSettings = new Camera2RequestSettingsSet(); 73 74 Range<Integer> previewFpsRange = mTemplateSettings.get(CONTROL_AE_TARGET_FPS_RANGE); 75 if (previewFpsRange != null) { 76 setPreviewFpsRange(previewFpsRange.getLower(), previewFpsRange.getUpper()); 77 } 78 setPreviewSize(preview); 79 // TODO: mCurrentPreviewFormat 80 setPhotoSize(photo); 81 mJpegCompressQuality = queryTemplateDefaultOrMakeOneUp(JPEG_QUALITY, (byte) 0); 82 // TODO: mCurrentPhotoFormat 83 // TODO: mCurrentZoomRatio 84 mCurrentZoomRatio = 1.0f; 85 // TODO: mCurrentZoomIndex 86 mExposureCompensationIndex = 87 queryTemplateDefaultOrMakeOneUp(CONTROL_AE_EXPOSURE_COMPENSATION, 0); 88 89 mCurrentFlashMode = flashModeFromRequest(); 90 Integer currentFocusMode = mTemplateSettings.get(CONTROL_AF_MODE); 91 if (currentFocusMode != null) { 92 mCurrentFocusMode = AndroidCamera2Capabilities.focusModeFromInt(currentFocusMode); 93 } 94 Integer currentSceneMode = mTemplateSettings.get(CONTROL_SCENE_MODE); 95 if (currentSceneMode != null) { 96 mCurrentSceneMode = AndroidCamera2Capabilities.sceneModeFromInt(currentSceneMode); 97 } 98 Integer whiteBalance = mTemplateSettings.get(CONTROL_AWB_MODE); 99 if (whiteBalance != null) { 100 mWhiteBalance = AndroidCamera2Capabilities.whiteBalanceFromInt(whiteBalance); 101 } 102 103 mVideoStabilizationEnabled = queryTemplateDefaultOrMakeOneUp( 104 CONTROL_VIDEO_STABILIZATION_MODE, CONTROL_VIDEO_STABILIZATION_MODE_OFF) == 105 CONTROL_VIDEO_STABILIZATION_MODE_ON; 106 mAutoExposureLocked = queryTemplateDefaultOrMakeOneUp(CONTROL_AE_LOCK, false); 107 mAutoWhiteBalanceLocked = queryTemplateDefaultOrMakeOneUp(CONTROL_AWB_LOCK, false); 108 // TODO: mRecordingHintEnabled 109 // TODO: mGpsData 110 android.util.Size exifThumbnailSize = mTemplateSettings.get(JPEG_THUMBNAIL_SIZE); 111 if (exifThumbnailSize != null) { 112 mExifThumbnailSize = 113 new Size(exifThumbnailSize.getWidth(), exifThumbnailSize.getHeight()); 114 } 115 } 116 117 public AndroidCamera2Settings(AndroidCamera2Settings other) { 118 super(other); 119 mTemplateSettings = other.mTemplateSettings; 120 mActiveArray = other.mActiveArray; 121 mRequestSettings = new Camera2RequestSettingsSet(other.mRequestSettings); 122 } 123 124 @Override 125 public CameraSettings copy() { 126 return new AndroidCamera2Settings(this); 127 } 128 129 private <T> T queryTemplateDefaultOrMakeOneUp(Key<T> key, T defaultDefault) { 130 T val = mTemplateSettings.get(key); 131 if (val != null) { 132 return val; 133 } else { 134 // Spoof the default so matchesTemplateDefault excludes this key from generated sets. 135 // This approach beats a simple sentinel because it provides basic boolean support. 136 mTemplateSettings.set(key, defaultDefault); 137 return defaultDefault; 138 } 139 } 140 141 private FlashMode flashModeFromRequest() { 142 Integer autoExposure = mTemplateSettings.get(CONTROL_AE_MODE); 143 if (autoExposure != null) { 144 switch (autoExposure) { 145 case CONTROL_AE_MODE_ON: 146 return FlashMode.OFF; 147 case CONTROL_AE_MODE_ON_AUTO_FLASH: 148 return FlashMode.AUTO; 149 case CONTROL_AE_MODE_ON_ALWAYS_FLASH: { 150 if (mTemplateSettings.get(FLASH_MODE) == FLASH_MODE_TORCH) { 151 return FlashMode.TORCH; 152 } else { 153 return FlashMode.ON; 154 } 155 } 156 case CONTROL_AE_MODE_ON_AUTO_FLASH_REDEYE: 157 return FlashMode.RED_EYE; 158 } 159 } 160 return null; 161 } 162 163 private boolean matchesTemplateDefault(Key<?> setting) { 164 if (setting == CONTROL_AE_REGIONS) { 165 return mMeteringAreas.size() == 0; 166 } else if (setting == CONTROL_AF_REGIONS) { 167 return mFocusAreas.size() == 0; 168 } else if (setting == CONTROL_AE_TARGET_FPS_RANGE) { 169 Range defaultFpsRange = mTemplateSettings.get(CONTROL_AE_TARGET_FPS_RANGE); 170 return (mPreviewFpsRangeMin == 0 && mPreviewFpsRangeMax == 0) || 171 (defaultFpsRange != null && mPreviewFpsRangeMin == defaultFpsRange.getLower() && 172 mPreviewFpsRangeMax == defaultFpsRange.getUpper()); 173 } else if (setting == JPEG_QUALITY) { 174 return Objects.equals(mJpegCompressQuality, 175 mTemplateSettings.get(JPEG_QUALITY)); 176 } else if (setting == CONTROL_AE_EXPOSURE_COMPENSATION) { 177 return Objects.equals(mExposureCompensationIndex, 178 mTemplateSettings.get(CONTROL_AE_EXPOSURE_COMPENSATION)); 179 } else if (setting == CONTROL_VIDEO_STABILIZATION_MODE) { 180 Integer videoStabilization = mTemplateSettings.get(CONTROL_VIDEO_STABILIZATION_MODE); 181 return (videoStabilization != null && 182 (mVideoStabilizationEnabled && videoStabilization == 183 CONTROL_VIDEO_STABILIZATION_MODE_ON) || 184 (!mVideoStabilizationEnabled && videoStabilization == 185 CONTROL_VIDEO_STABILIZATION_MODE_OFF)); 186 } else if (setting == CONTROL_AE_LOCK) { 187 return Objects.equals(mAutoExposureLocked, mTemplateSettings.get(CONTROL_AE_LOCK)); 188 } else if (setting == CONTROL_AWB_LOCK) { 189 return Objects.equals(mAutoWhiteBalanceLocked, mTemplateSettings.get(CONTROL_AWB_LOCK)); 190 } else if (setting == JPEG_THUMBNAIL_SIZE) { 191 android.util.Size defaultThumbnailSize = mTemplateSettings.get(JPEG_THUMBNAIL_SIZE); 192 return (mExifThumbnailSize.width() == 0 && mExifThumbnailSize.height() == 0) || 193 (defaultThumbnailSize != null && 194 mExifThumbnailSize.width() == defaultThumbnailSize.getWidth() && 195 mExifThumbnailSize.height() == defaultThumbnailSize.getHeight()); 196 } 197 Log.w(TAG, "Settings implementation checked default of unhandled option key"); 198 // Since this class isn't equipped to handle it, claim it matches the default to prevent 199 // updateRequestSettingOrForceToDefault from going with the user-provided preference 200 return true; 201 } 202 203 private <T> void updateRequestSettingOrForceToDefault(Key<T> setting, T possibleChoice) { 204 mRequestSettings.set(setting, matchesTemplateDefault(setting) ? null : possibleChoice); 205 } 206 207 public Camera2RequestSettingsSet getRequestSettings() { 208 updateRequestSettingOrForceToDefault(CONTROL_AE_REGIONS, 209 legacyAreasToMeteringRectangles(mMeteringAreas)); 210 updateRequestSettingOrForceToDefault(CONTROL_AF_REGIONS, 211 legacyAreasToMeteringRectangles(mFocusAreas)); 212 updateRequestSettingOrForceToDefault(CONTROL_AE_TARGET_FPS_RANGE, 213 new Range(mPreviewFpsRangeMin, mPreviewFpsRangeMax)); 214 // TODO: mCurrentPreviewFormat 215 updateRequestSettingOrForceToDefault(JPEG_QUALITY, mJpegCompressQuality); 216 // TODO: mCurrentPhotoFormat 217 // TODO: mCurrentZoomRatio 218 // TODO: mCurrentZoomIndex 219 updateRequestSettingOrForceToDefault(CONTROL_AE_EXPOSURE_COMPENSATION, 220 mExposureCompensationIndex); 221 updateRequestFlashMode(); 222 updateRequestFocusMode(); 223 updateRequestSceneMode(); 224 updateRequestWhiteBalance(); 225 updateRequestSettingOrForceToDefault(CONTROL_VIDEO_STABILIZATION_MODE, 226 mVideoStabilizationEnabled ? 227 CONTROL_VIDEO_STABILIZATION_MODE_ON : CONTROL_VIDEO_STABILIZATION_MODE_OFF); 228 // OIS shouldn't be on if software video stabilization is. 229 mRequestSettings.set(LENS_OPTICAL_STABILIZATION_MODE, 230 mVideoStabilizationEnabled ? LENS_OPTICAL_STABILIZATION_MODE_OFF : 231 null); 232 updateRequestSettingOrForceToDefault(CONTROL_AE_LOCK, mAutoExposureLocked); 233 updateRequestSettingOrForceToDefault(CONTROL_AWB_LOCK, mAutoWhiteBalanceLocked); 234 // TODO: mRecordingHintEnabled 235 updateRequestGpsData(); 236 updateRequestSettingOrForceToDefault(JPEG_THUMBNAIL_SIZE, 237 new android.util.Size( 238 mExifThumbnailSize.width(), mExifThumbnailSize.height())); 239 240 return mRequestSettings; 241 } 242 243 private MeteringRectangle[] legacyAreasToMeteringRectangles( 244 List<android.hardware.Camera.Area> reference) { 245 MeteringRectangle[] transformed = null; 246 if (reference.size() > 0) { 247 248 transformed = new MeteringRectangle[reference.size()]; 249 for (int index = 0; index < reference.size(); ++index) { 250 android.hardware.Camera.Area source = reference.get(index); 251 Rect rectangle = source.rect; 252 253 // Old API coordinates were [-1000,1000]; new ones are [0,ACTIVE_ARRAY_SIZE). 254 double oldLeft = (rectangle.left + 1000) / 2000.0; 255 double oldTop = (rectangle.top + 1000) / 2000.0; 256 double oldRight = (rectangle.right + 1000) / 2000.0; 257 double oldBottom = (rectangle.bottom + 1000) / 2000.0; 258 int left = toIntConstrained( mActiveArray.width() * oldLeft + mActiveArray.left, 259 0, mActiveArray.width() - 1); 260 int top = toIntConstrained( mActiveArray.height() * oldTop + mActiveArray.top, 261 0, mActiveArray.height() - 1); 262 int right = toIntConstrained( mActiveArray.width() * oldRight + mActiveArray.left, 263 0, mActiveArray.width() - 1); 264 int bottom = toIntConstrained( mActiveArray.height() * oldBottom + mActiveArray.top, 265 0, mActiveArray.height() - 1); 266 transformed[index] = new MeteringRectangle(left, top, right - left, bottom - top, 267 source.weight); 268 } 269 } 270 return transformed; 271 } 272 273 private int toIntConstrained(double original, int min, int max) { 274 original = Math.max(original, min); 275 original = Math.min(original, max); 276 return (int) original; 277 } 278 279 private void updateRequestFlashMode() { 280 Integer aeMode = null; 281 Integer flashMode = null; 282 if (mCurrentFlashMode != null) { 283 switch (mCurrentFlashMode) { 284 case AUTO: { 285 aeMode = CONTROL_AE_MODE_ON_AUTO_FLASH; 286 break; 287 } 288 case OFF: { 289 aeMode = CONTROL_AE_MODE_ON; 290 flashMode = FLASH_MODE_OFF; 291 break; 292 } 293 case ON: { 294 aeMode = CONTROL_AE_MODE_ON_ALWAYS_FLASH; 295 flashMode = FLASH_MODE_SINGLE; 296 break; 297 } 298 case TORCH: { 299 flashMode = FLASH_MODE_TORCH; 300 break; 301 } 302 case RED_EYE: { 303 aeMode = CONTROL_AE_MODE_ON_AUTO_FLASH_REDEYE; 304 break; 305 } 306 default: { 307 Log.w(TAG, "Unable to convert to API 2 flash mode: " + mCurrentFlashMode); 308 break; 309 } 310 } 311 } 312 mRequestSettings.set(CONTROL_AE_MODE, aeMode); 313 mRequestSettings.set(FLASH_MODE, flashMode); 314 } 315 316 private void updateRequestFocusMode() { 317 Integer mode = null; 318 if (mCurrentFocusMode != null) { 319 switch (mCurrentFocusMode) { 320 case AUTO: { 321 mode = CONTROL_AF_MODE_AUTO; 322 break; 323 } 324 case CONTINUOUS_PICTURE: { 325 mode = CONTROL_AF_MODE_CONTINUOUS_PICTURE; 326 break; 327 } 328 case CONTINUOUS_VIDEO: { 329 mode = CONTROL_AF_MODE_CONTINUOUS_VIDEO; 330 break; 331 } 332 case EXTENDED_DOF: { 333 mode = CONTROL_AF_MODE_EDOF; 334 break; 335 } 336 case FIXED: { 337 mode = CONTROL_AF_MODE_OFF; 338 break; 339 } 340 // TODO: We cannot support INFINITY 341 case MACRO: { 342 mode = CONTROL_AF_MODE_MACRO; 343 break; 344 } 345 default: { 346 Log.w(TAG, "Unable to convert to API 2 focus mode: " + mCurrentFocusMode); 347 break; 348 } 349 } 350 } 351 mRequestSettings.set(CONTROL_AF_MODE, mode); 352 } 353 354 private void updateRequestSceneMode() { 355 Integer mode = null; 356 if (mCurrentSceneMode != null) { 357 switch (mCurrentSceneMode) { 358 case AUTO: { 359 mode = CONTROL_SCENE_MODE_DISABLED; 360 break; 361 } 362 case ACTION: { 363 mode = CONTROL_SCENE_MODE_ACTION; 364 break; 365 } 366 case BARCODE: { 367 mode = CONTROL_SCENE_MODE_BARCODE; 368 break; 369 } 370 case BEACH: { 371 mode = CONTROL_SCENE_MODE_BEACH; 372 break; 373 } 374 case CANDLELIGHT: { 375 mode = CONTROL_SCENE_MODE_CANDLELIGHT; 376 break; 377 } 378 case FIREWORKS: { 379 mode = CONTROL_SCENE_MODE_FIREWORKS; 380 break; 381 } 382 // TODO: We cannot support HDR 383 case LANDSCAPE: { 384 mode = CONTROL_SCENE_MODE_LANDSCAPE; 385 break; 386 } 387 case NIGHT: { 388 mode = CONTROL_SCENE_MODE_NIGHT; 389 break; 390 } 391 // TODO: We cannot support NIGHT_PORTRAIT 392 case PARTY: { 393 mode = CONTROL_SCENE_MODE_PARTY; 394 break; 395 } 396 case PORTRAIT: { 397 mode = CONTROL_SCENE_MODE_PORTRAIT; 398 break; 399 } 400 case SNOW: { 401 mode = CONTROL_SCENE_MODE_SNOW; 402 break; 403 } 404 case SPORTS: { 405 mode = CONTROL_SCENE_MODE_SPORTS; 406 break; 407 } 408 case STEADYPHOTO: { 409 mode = CONTROL_SCENE_MODE_STEADYPHOTO; 410 break; 411 } 412 case SUNSET: { 413 mode = CONTROL_SCENE_MODE_SUNSET; 414 break; 415 } 416 case THEATRE: { 417 mode = CONTROL_SCENE_MODE_THEATRE; 418 break; 419 } 420 default: { 421 Log.w(TAG, "Unable to convert to API 2 scene mode: " + mCurrentSceneMode); 422 break; 423 } 424 } 425 } 426 mRequestSettings.set(CONTROL_SCENE_MODE, mode); 427 } 428 429 private void updateRequestWhiteBalance() { 430 Integer mode = null; 431 if (mWhiteBalance != null) { 432 switch (mWhiteBalance) { 433 case AUTO: { 434 mode = CONTROL_AWB_MODE_AUTO; 435 break; 436 } 437 case CLOUDY_DAYLIGHT: { 438 mode = CONTROL_AWB_MODE_CLOUDY_DAYLIGHT; 439 break; 440 } 441 case DAYLIGHT: { 442 mode = CONTROL_AWB_MODE_DAYLIGHT; 443 break; 444 } 445 case FLUORESCENT: { 446 mode = CONTROL_AWB_MODE_FLUORESCENT; 447 break; 448 } 449 case INCANDESCENT: { 450 mode = CONTROL_AWB_MODE_INCANDESCENT; 451 break; 452 } 453 case SHADE: { 454 mode = CONTROL_AWB_MODE_SHADE; 455 break; 456 } 457 case TWILIGHT: { 458 mode = CONTROL_AWB_MODE_TWILIGHT; 459 break; 460 } 461 case WARM_FLUORESCENT: { 462 mode = CONTROL_AWB_MODE_WARM_FLUORESCENT; 463 break; 464 } 465 default: { 466 Log.w(TAG, "Unable to convert to API 2 white balance: " + mWhiteBalance); 467 break; 468 } 469 } 470 } 471 mRequestSettings.set(CONTROL_AWB_MODE, mode); 472 } 473 474 private void updateRequestGpsData() { 475 if (mGpsData == null || mGpsData.processingMethod == null) { 476 // It's a hack since we always use GPS time stamp but does 477 // not use other fields sometimes. Setting processing 478 // method to null means the other fields should not be used. 479 mRequestSettings.set(JPEG_GPS_LOCATION, null); 480 } else { 481 Location location = new Location(mGpsData.processingMethod); 482 location.setTime(mGpsData.timeStamp); 483 location.setAltitude(mGpsData.altitude); 484 location.setLatitude(mGpsData.latitude); 485 location.setLongitude(mGpsData.longitude); 486 mRequestSettings.set(JPEG_GPS_LOCATION, location); 487 } 488 } 489} 490