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