AndroidCamera2Settings.java revision bc9c0e2cb071b085ecc5646751fb59f5145d51b1
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 88 mCurrentFlashMode = flashModeFromRequest(); 89 Integer currentFocusMode = mTemplateSettings.get(CONTROL_AF_MODE); 90 if (currentFocusMode != null) { 91 mCurrentFocusMode = AndroidCamera2Capabilities.focusModeFromInt(currentFocusMode); 92 } 93 Integer currentSceneMode = mTemplateSettings.get(CONTROL_SCENE_MODE); 94 if (currentSceneMode != null) { 95 mCurrentSceneMode = AndroidCamera2Capabilities.sceneModeFromInt(currentSceneMode); 96 } 97 Integer whiteBalance = mTemplateSettings.get(CONTROL_AWB_MODE); 98 if (whiteBalance != null) { 99 mWhiteBalance = AndroidCamera2Capabilities.whiteBalanceFromInt(whiteBalance); 100 } 101 102 mVideoStabilizationEnabled = queryTemplateDefaultOrMakeOneUp( 103 CONTROL_VIDEO_STABILIZATION_MODE, CONTROL_VIDEO_STABILIZATION_MODE_OFF) == 104 CONTROL_VIDEO_STABILIZATION_MODE_ON; 105 mAutoExposureLocked = queryTemplateDefaultOrMakeOneUp(CONTROL_AE_LOCK, false); 106 mAutoWhiteBalanceLocked = queryTemplateDefaultOrMakeOneUp(CONTROL_AWB_LOCK, false); 107 // TODO: mRecordingHintEnabled 108 // TODO: mGpsData 109 android.util.Size exifThumbnailSize = mTemplateSettings.get(JPEG_THUMBNAIL_SIZE); 110 if (exifThumbnailSize != null) { 111 mExifThumbnailSize = 112 new Size(exifThumbnailSize.getWidth(), exifThumbnailSize.getHeight()); 113 } 114 } 115 116 public AndroidCamera2Settings(AndroidCamera2Settings other) { 117 super(other); 118 mTemplateSettings = other.mTemplateSettings; 119 mActiveArray = other.mActiveArray; 120 mRequestSettings = new Camera2RequestSettingsSet(other.mRequestSettings); 121 } 122 123 @Override 124 public CameraSettings copy() { 125 return new AndroidCamera2Settings(this); 126 } 127 128 private <T> T queryTemplateDefaultOrMakeOneUp(Key<T> key, T defaultDefault) { 129 T val = mTemplateSettings.get(key); 130 if (val != null) { 131 return val; 132 } else { 133 // Spoof the default so matchesTemplateDefault excludes this key from generated sets. 134 // This approach beats a simple sentinel because it provides basic boolean support. 135 mTemplateSettings.set(key, defaultDefault); 136 return defaultDefault; 137 } 138 } 139 140 private FlashMode flashModeFromRequest() { 141 Integer autoExposure = mTemplateSettings.get(CONTROL_AE_MODE); 142 if (autoExposure != null) { 143 switch (autoExposure) { 144 case CONTROL_AE_MODE_ON: 145 return FlashMode.OFF; 146 case CONTROL_AE_MODE_ON_AUTO_FLASH: 147 return FlashMode.AUTO; 148 case CONTROL_AE_MODE_ON_ALWAYS_FLASH: { 149 if (mTemplateSettings.get(FLASH_MODE) == FLASH_MODE_TORCH) { 150 return FlashMode.TORCH; 151 } else { 152 return FlashMode.ON; 153 } 154 } 155 case CONTROL_AE_MODE_ON_AUTO_FLASH_REDEYE: 156 return FlashMode.RED_EYE; 157 } 158 } 159 return null; 160 } 161 162 private boolean matchesTemplateDefault(Key<?> setting) { 163 if (setting == CONTROL_AE_REGIONS) { 164 return mMeteringAreas.size() == 0; 165 } else if (setting == CONTROL_AF_REGIONS) { 166 return mFocusAreas.size() == 0; 167 } else if (setting == CONTROL_AE_TARGET_FPS_RANGE) { 168 Range defaultFpsRange = mTemplateSettings.get(CONTROL_AE_TARGET_FPS_RANGE); 169 return (mPreviewFpsRangeMin == 0 && mPreviewFpsRangeMax == 0) || 170 (defaultFpsRange != null && mPreviewFpsRangeMin == defaultFpsRange.getLower() && 171 mPreviewFpsRangeMax == defaultFpsRange.getUpper()); 172 } else if (setting == JPEG_QUALITY) { 173 return Objects.equals(mJpegCompressQuality, 174 mTemplateSettings.get(JPEG_QUALITY)); 175 } else if (setting == CONTROL_AE_EXPOSURE_COMPENSATION) { 176 return Objects.equals(mExposureCompensationIndex, 177 mTemplateSettings.get(CONTROL_AE_EXPOSURE_COMPENSATION)); 178 } else if (setting == CONTROL_VIDEO_STABILIZATION_MODE) { 179 Integer videoStabilization = mTemplateSettings.get(CONTROL_VIDEO_STABILIZATION_MODE); 180 return (videoStabilization != null && 181 (mVideoStabilizationEnabled && videoStabilization == 182 CONTROL_VIDEO_STABILIZATION_MODE_ON) || 183 (!mVideoStabilizationEnabled && videoStabilization == 184 CONTROL_VIDEO_STABILIZATION_MODE_OFF)); 185 } else if (setting == CONTROL_AE_LOCK) { 186 return Objects.equals(mAutoExposureLocked, mTemplateSettings.get(CONTROL_AE_LOCK)); 187 } else if (setting == CONTROL_AWB_LOCK) { 188 return Objects.equals(mAutoWhiteBalanceLocked, mTemplateSettings.get(CONTROL_AWB_LOCK)); 189 } else if (setting == JPEG_THUMBNAIL_SIZE) { 190 android.util.Size defaultThumbnailSize = mTemplateSettings.get(JPEG_THUMBNAIL_SIZE); 191 return (mExifThumbnailSize.width() == 0 && mExifThumbnailSize.height() == 0) || 192 (defaultThumbnailSize != null && 193 mExifThumbnailSize.width() == defaultThumbnailSize.getWidth() && 194 mExifThumbnailSize.height() == defaultThumbnailSize.getHeight()); 195 } 196 Log.w(TAG, "Settings implementation checked default of unhandled option key"); 197 // Since this class isn't equipped to handle it, claim it matches the default to prevent 198 // updateRequestSettingOrForceToDefault from going with the user-provided preference 199 return true; 200 } 201 202 private <T> void updateRequestSettingOrForceToDefault(Key<T> setting, T possibleChoice) { 203 mRequestSettings.set(setting, matchesTemplateDefault(setting) ? null : possibleChoice); 204 } 205 206 public Camera2RequestSettingsSet getRequestSettings() { 207 updateRequestSettingOrForceToDefault(CONTROL_AE_REGIONS, 208 legacyAreasToMeteringRectangles(mMeteringAreas)); 209 updateRequestSettingOrForceToDefault(CONTROL_AF_REGIONS, 210 legacyAreasToMeteringRectangles(mFocusAreas)); 211 updateRequestSettingOrForceToDefault(CONTROL_AE_TARGET_FPS_RANGE, 212 new Range(mPreviewFpsRangeMin, mPreviewFpsRangeMax)); 213 // TODO: mCurrentPreviewFormat 214 updateRequestSettingOrForceToDefault(JPEG_QUALITY, mJpegCompressQuality); 215 // TODO: mCurrentPhotoFormat 216 // TODO: mCurrentZoomRatio 217 // TODO: mCurrentZoomIndex 218 updateRequestSettingOrForceToDefault(CONTROL_AE_EXPOSURE_COMPENSATION, 219 mExposureCompensationIndex); 220 updateRequestFlashMode(); 221 updateRequestFocusMode(); 222 updateRequestSceneMode(); 223 updateRequestWhiteBalance(); 224 updateRequestSettingOrForceToDefault(CONTROL_VIDEO_STABILIZATION_MODE, 225 mVideoStabilizationEnabled ? 226 CONTROL_VIDEO_STABILIZATION_MODE_ON : CONTROL_VIDEO_STABILIZATION_MODE_OFF); 227 // OIS shouldn't be on if software video stabilization is. 228 mRequestSettings.set(LENS_OPTICAL_STABILIZATION_MODE, 229 mVideoStabilizationEnabled ? LENS_OPTICAL_STABILIZATION_MODE_OFF : 230 null); 231 updateRequestSettingOrForceToDefault(CONTROL_AE_LOCK, mAutoExposureLocked); 232 updateRequestSettingOrForceToDefault(CONTROL_AWB_LOCK, mAutoWhiteBalanceLocked); 233 // TODO: mRecordingHintEnabled 234 // TODO: mGpsData 235 updateRequestSettingOrForceToDefault(JPEG_THUMBNAIL_SIZE, 236 new android.util.Size( 237 mExifThumbnailSize.width(), mExifThumbnailSize.height())); 238 239 return mRequestSettings; 240 } 241 242 private MeteringRectangle[] legacyAreasToMeteringRectangles( 243 List<android.hardware.Camera.Area> reference) { 244 MeteringRectangle[] transformed = null; 245 if (reference.size() > 0) { 246 247 transformed = new MeteringRectangle[reference.size()]; 248 for (int index = 0; index < reference.size(); ++index) { 249 android.hardware.Camera.Area source = reference.get(index); 250 Rect rectangle = source.rect; 251 252 // Old API coordinates were [-1000,1000]; new ones are [0,ACTIVE_ARRAY_SIZE). 253 double oldLeft = (rectangle.left + 1000) / 2000.0; 254 double oldTop = (rectangle.top + 1000) / 2000.0; 255 double oldRight = (rectangle.right + 1000) / 2000.0; 256 double oldBottom = (rectangle.bottom + 1000) / 2000.0; 257 int left = toIntConstrained( mActiveArray.width() * oldLeft + mActiveArray.left, 258 0, mActiveArray.width() - 1); 259 int top = toIntConstrained( mActiveArray.height() * oldTop + mActiveArray.top, 260 0, mActiveArray.height() - 1); 261 int right = toIntConstrained( mActiveArray.width() * oldRight + mActiveArray.left, 262 0, mActiveArray.width() - 1); 263 int bottom = toIntConstrained( mActiveArray.height() * oldBottom + mActiveArray.top, 264 0, mActiveArray.height() - 1); 265 transformed[index] = new MeteringRectangle(left, top, right - left, bottom - top, 266 source.weight); 267 } 268 } 269 return transformed; 270 } 271 272 private int toIntConstrained(double original, int min, int max) { 273 original = Math.max(original, min); 274 original = Math.min(original, max); 275 return (int) original; 276 } 277 278 private void updateRequestFlashMode() { 279 Integer aeMode = null; 280 Integer flashMode = null; 281 if (mCurrentFlashMode != null) { 282 switch (mCurrentFlashMode) { 283 case AUTO: { 284 aeMode = CONTROL_AE_MODE_ON_AUTO_FLASH; 285 break; 286 } 287 case OFF: { 288 aeMode = CONTROL_AE_MODE_ON; 289 flashMode = FLASH_MODE_OFF; 290 break; 291 } 292 case ON: { 293 aeMode = CONTROL_AE_MODE_ON_ALWAYS_FLASH; 294 flashMode = FLASH_MODE_SINGLE; 295 break; 296 } 297 case TORCH: { 298 flashMode = FLASH_MODE_TORCH; 299 break; 300 } 301 case RED_EYE: { 302 aeMode = CONTROL_AE_MODE_ON_AUTO_FLASH_REDEYE; 303 break; 304 } 305 default: { 306 Log.w(TAG, "Unable to convert to API 2 flash mode: " + mCurrentFlashMode); 307 break; 308 } 309 } 310 } 311 mRequestSettings.set(CONTROL_AE_MODE, aeMode); 312 mRequestSettings.set(FLASH_MODE, flashMode); 313 } 314 315 private void updateRequestFocusMode() { 316 Integer mode = null; 317 if (mCurrentFocusMode != null) { 318 switch (mCurrentFocusMode) { 319 case AUTO: { 320 mode = CONTROL_AF_MODE_AUTO; 321 break; 322 } 323 case CONTINUOUS_PICTURE: { 324 mode = CONTROL_AF_MODE_CONTINUOUS_PICTURE; 325 break; 326 } 327 case CONTINUOUS_VIDEO: { 328 mode = CONTROL_AF_MODE_CONTINUOUS_VIDEO; 329 break; 330 } 331 case EXTENDED_DOF: { 332 mode = CONTROL_AF_MODE_EDOF; 333 break; 334 } 335 case FIXED: { 336 mode = CONTROL_AF_MODE_OFF; 337 break; 338 } 339 // TODO: We cannot support INFINITY 340 case MACRO: { 341 mode = CONTROL_AF_MODE_MACRO; 342 break; 343 } 344 default: { 345 Log.w(TAG, "Unable to convert to API 2 focus mode: " + mCurrentFocusMode); 346 break; 347 } 348 } 349 } 350 mRequestSettings.set(CONTROL_AF_MODE, mode); 351 } 352 353 private void updateRequestSceneMode() { 354 Integer mode = null; 355 if (mCurrentSceneMode != null) { 356 switch (mCurrentSceneMode) { 357 case AUTO: { 358 mode = CONTROL_SCENE_MODE_DISABLED; 359 break; 360 } 361 case ACTION: { 362 mode = CONTROL_SCENE_MODE_ACTION; 363 break; 364 } 365 case BARCODE: { 366 mode = CONTROL_SCENE_MODE_BARCODE; 367 break; 368 } 369 case BEACH: { 370 mode = CONTROL_SCENE_MODE_BEACH; 371 break; 372 } 373 case CANDLELIGHT: { 374 mode = CONTROL_SCENE_MODE_CANDLELIGHT; 375 break; 376 } 377 case FIREWORKS: { 378 mode = CONTROL_SCENE_MODE_FIREWORKS; 379 break; 380 } 381 // TODO: We cannot support HDR 382 case LANDSCAPE: { 383 mode = CONTROL_SCENE_MODE_LANDSCAPE; 384 break; 385 } 386 case NIGHT: { 387 mode = CONTROL_SCENE_MODE_NIGHT; 388 break; 389 } 390 // TODO: We cannot support NIGHT_PORTRAIT 391 case PARTY: { 392 mode = CONTROL_SCENE_MODE_PARTY; 393 break; 394 } 395 case PORTRAIT: { 396 mode = CONTROL_SCENE_MODE_PORTRAIT; 397 break; 398 } 399 case SNOW: { 400 mode = CONTROL_SCENE_MODE_SNOW; 401 break; 402 } 403 case SPORTS: { 404 mode = CONTROL_SCENE_MODE_SPORTS; 405 break; 406 } 407 case STEADYPHOTO: { 408 mode = CONTROL_SCENE_MODE_STEADYPHOTO; 409 break; 410 } 411 case SUNSET: { 412 mode = CONTROL_SCENE_MODE_SUNSET; 413 break; 414 } 415 case THEATRE: { 416 mode = CONTROL_SCENE_MODE_THEATRE; 417 break; 418 } 419 default: { 420 Log.w(TAG, "Unable to convert to API 2 scene mode: " + mCurrentSceneMode); 421 break; 422 } 423 } 424 } 425 mRequestSettings.set(CONTROL_SCENE_MODE, mode); 426 } 427 428 private void updateRequestWhiteBalance() { 429 Integer mode = null; 430 if (mWhiteBalance != null) { 431 switch (mWhiteBalance) { 432 case AUTO: { 433 mode = CONTROL_AWB_MODE_AUTO; 434 break; 435 } 436 case CLOUDY_DAYLIGHT: { 437 mode = CONTROL_AWB_MODE_CLOUDY_DAYLIGHT; 438 break; 439 } 440 case DAYLIGHT: { 441 mode = CONTROL_AWB_MODE_DAYLIGHT; 442 break; 443 } 444 case FLUORESCENT: { 445 mode = CONTROL_AWB_MODE_FLUORESCENT; 446 break; 447 } 448 case INCANDESCENT: { 449 mode = CONTROL_AWB_MODE_INCANDESCENT; 450 break; 451 } 452 case SHADE: { 453 mode = CONTROL_AWB_MODE_SHADE; 454 break; 455 } 456 case TWILIGHT: { 457 mode = CONTROL_AWB_MODE_TWILIGHT; 458 break; 459 } 460 case WARM_FLUORESCENT: { 461 mode = CONTROL_AWB_MODE_WARM_FLUORESCENT; 462 break; 463 } 464 default: { 465 Log.w(TAG, "Unable to convert to API 2 white balance: " + mWhiteBalance); 466 break; 467 } 468 } 469 } 470 mRequestSettings.set(CONTROL_AWB_MODE, mode); 471 } 472} 473