LegacyResultMapper.java revision 3fe9eba9044c0b20ed349a4b9094bf1fa7942cdf
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 android.hardware.camera2.legacy; 18 19import android.graphics.Rect; 20import android.hardware.Camera; 21import android.hardware.Camera.Parameters; 22import android.hardware.camera2.CameraCharacteristics; 23import android.hardware.camera2.CameraMetadata; 24import android.hardware.camera2.CaptureRequest; 25import android.hardware.camera2.CaptureResult; 26import android.hardware.camera2.impl.CameraMetadataNative; 27import android.hardware.camera2.legacy.ParameterUtils.WeightedRectangle; 28import android.hardware.camera2.legacy.ParameterUtils.ZoomData; 29import android.hardware.camera2.params.MeteringRectangle; 30import android.hardware.camera2.utils.ListUtils; 31import android.hardware.camera2.utils.ParamsUtils; 32import android.util.Log; 33import android.util.Size; 34 35import java.util.ArrayList; 36import java.util.List; 37 38import static com.android.internal.util.Preconditions.*; 39import static android.hardware.camera2.CaptureResult.*; 40 41/** 42 * Provide legacy-specific implementations of camera2 CaptureResult for legacy devices. 43 */ 44@SuppressWarnings("deprecation") 45public class LegacyResultMapper { 46 private static final String TAG = "LegacyResultMapper"; 47 private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE); 48 49 private LegacyRequest mCachedRequest = null; 50 private CameraMetadataNative mCachedResult = null; 51 52 /** 53 * Generate capture result metadata from the legacy camera request. 54 * 55 * <p>This method caches and reuses the result from the previous call to this method if 56 * the {@code parameters} of the subsequent {@link LegacyRequest} passed to this method 57 * have not changed.</p> 58 * 59 * @param legacyRequest a non-{@code null} legacy request containing the latest parameters 60 * @param timestamp the timestamp to use for this result in nanoseconds. 61 * 62 * @return {@link CameraMetadataNative} object containing result metadata. 63 */ 64 public CameraMetadataNative cachedConvertResultMetadata( 65 LegacyRequest legacyRequest, long timestamp) { 66 CameraMetadataNative result; 67 boolean cached; 68 69 /* 70 * Attempt to look up the result from the cache if the parameters haven't changed 71 */ 72 if (mCachedRequest != null && legacyRequest.parameters.same(mCachedRequest.parameters)) { 73 result = new CameraMetadataNative(mCachedResult); 74 cached = true; 75 } else { 76 result = convertResultMetadata(legacyRequest, timestamp); 77 cached = false; 78 79 // Always cache a *copy* of the metadata result, 80 // since api2's client side takes ownership of it after it receives a result 81 mCachedRequest = legacyRequest; 82 mCachedResult = new CameraMetadataNative(result); 83 } 84 85 /* 86 * Unconditionally set fields that change in every single frame 87 */ 88 { 89 // sensor.timestamp 90 result.set(SENSOR_TIMESTAMP, timestamp); 91 } 92 93 if (VERBOSE) { 94 Log.v(TAG, "cachedConvertResultMetadata - cached? " + cached + 95 " timestamp = " + timestamp); 96 97 Log.v(TAG, "----- beginning of result dump ------"); 98 result.dumpToLog(); 99 Log.v(TAG, "----- end of result dump ------"); 100 } 101 102 return result; 103 } 104 105 /** 106 * Generate capture result metadata from the legacy camera request. 107 * 108 * @param legacyRequest a non-{@code null} legacy request containing the latest parameters 109 * @param timestamp the timestamp to use for this result in nanoseconds. 110 * 111 * @return a {@link CameraMetadataNative} object containing result metadata. 112 */ 113 private static CameraMetadataNative convertResultMetadata(LegacyRequest legacyRequest, 114 long timestamp) { 115 CameraCharacteristics characteristics = legacyRequest.characteristics; 116 CaptureRequest request = legacyRequest.captureRequest; 117 Size previewSize = legacyRequest.previewSize; 118 Camera.Parameters params = legacyRequest.parameters; 119 120 CameraMetadataNative result = new CameraMetadataNative(); 121 122 Rect activeArraySize = characteristics.get( 123 CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE); 124 ZoomData zoomData = ParameterUtils.convertScalerCropRegion(activeArraySize, 125 request.get(CaptureRequest.SCALER_CROP_REGION), previewSize, params); 126 127 /* 128 * control 129 */ 130 131 /* 132 * control.ae* 133 */ 134 mapAe(result, characteristics, request, activeArraySize, zoomData, /*out*/params); 135 136 // control.afMode 137 result.set(CaptureResult.CONTROL_AF_MODE, convertLegacyAfMode(params.getFocusMode())); 138 139 // control.awbLock 140 result.set(CaptureResult.CONTROL_AWB_LOCK, params.getAutoWhiteBalanceLock()); 141 142 // control.awbState 143 if (LegacyMetadataMapper.LIE_ABOUT_AWB_STATE) { 144 // Lie to pass CTS temporarily. 145 // TODO: CTS needs to be updated not to query this value 146 // for LIMITED devices unless its guaranteed to be available. 147 result.set(CaptureResult.CONTROL_AWB_STATE, 148 CameraMetadata.CONTROL_AWB_STATE_CONVERGED); 149 // TODO: Read the awb mode from parameters instead 150 } 151 152 if (LegacyMetadataMapper.LIE_ABOUT_AWB) { 153 result.set(CaptureResult.CONTROL_AWB_MODE, 154 request.get(CaptureRequest.CONTROL_AWB_MODE)); 155 } 156 157 158 /* 159 * control.mode 160 */ 161 { 162 int controlMode = ParamsUtils.getOrDefault(request, CaptureRequest.CONTROL_MODE, 163 CONTROL_MODE_AUTO); 164 if (controlMode == CaptureResult.CONTROL_MODE_USE_SCENE_MODE) { 165 result.set(CONTROL_MODE, CONTROL_MODE_USE_SCENE_MODE); 166 } else { 167 result.set(CONTROL_MODE, CONTROL_MODE_AUTO); 168 } 169 } 170 171 /* 172 * control.sceneMode 173 */ 174 { 175 String legacySceneMode = params.getSceneMode(); 176 int mode = LegacyMetadataMapper.convertSceneModeFromLegacy(legacySceneMode); 177 if (mode != LegacyMetadataMapper.UNKNOWN_MODE) { 178 result.set(CaptureResult.CONTROL_SCENE_MODE, mode); 179 } else { 180 Log.w(TAG, "Unknown scene mode " + legacySceneMode + 181 " returned by camera HAL, setting to disabled."); 182 result.set(CaptureResult.CONTROL_SCENE_MODE, CONTROL_SCENE_MODE_DISABLED); 183 } 184 } 185 186 187 /* 188 * control.effectMode 189 */ 190 { 191 String legacyEffectMode = params.getColorEffect(); 192 int mode = LegacyMetadataMapper.convertEffectModeFromLegacy(legacyEffectMode); 193 if (mode != LegacyMetadataMapper.UNKNOWN_MODE) { 194 result.set(CaptureResult.CONTROL_EFFECT_MODE, mode); 195 } else { 196 Log.w(TAG, "Unknown effect mode " + legacyEffectMode + 197 " returned by camera HAL, setting to off."); 198 result.set(CaptureResult.CONTROL_EFFECT_MODE, CONTROL_EFFECT_MODE_OFF); 199 } 200 } 201 202 /* 203 * flash 204 */ 205 { 206 // TODO 207 } 208 209 /* 210 * lens 211 */ 212 // lens.focusDistance 213 { 214 if (Parameters.FOCUS_MODE_INFINITY.equals(params.getFocusMode())) { 215 result.set(CaptureResult.LENS_FOCUS_DISTANCE, 0.0f); 216 } 217 } 218 219 // lens.focalLength 220 result.set(CaptureResult.LENS_FOCAL_LENGTH, params.getFocalLength()); 221 222 /* 223 * request 224 */ 225 // request.pipelineDepth 226 result.set(REQUEST_PIPELINE_DEPTH, 227 characteristics.get(CameraCharacteristics.REQUEST_PIPELINE_MAX_DEPTH)); 228 229 /* 230 * scaler 231 */ 232 mapScaler(result, zoomData, /*out*/params); 233 234 /* 235 * sensor 236 */ 237 238 // TODO: Remaining result metadata tags conversions. 239 return result; 240 } 241 242 private static void mapAe(CameraMetadataNative m, 243 CameraCharacteristics characteristics, 244 CaptureRequest request, Rect activeArray, ZoomData zoomData, /*out*/Parameters p) { 245 // control.aeAntiBandingMode 246 { 247 int antiBandingMode = LegacyMetadataMapper.convertAntiBandingModeOrDefault( 248 p.getAntibanding()); 249 m.set(CONTROL_AE_ANTIBANDING_MODE, antiBandingMode); 250 } 251 252 // control.aeExposureCompensation 253 { 254 m.set(CONTROL_AE_EXPOSURE_COMPENSATION, p.getExposureCompensation()); 255 } 256 257 // control.aeLock 258 { 259 boolean lock = p.isAutoExposureLockSupported() ? p.getAutoExposureLock() : false; 260 m.set(CONTROL_AE_LOCK, lock); 261 if (VERBOSE) { 262 Log.v(TAG, 263 "mapAe - android.control.aeLock = " + lock + 264 ", supported = " + p.isAutoExposureLockSupported()); 265 } 266 267 Boolean requestLock = request.get(CaptureRequest.CONTROL_AE_LOCK); 268 if (requestLock != null && requestLock != lock) { 269 Log.w(TAG, 270 "mapAe - android.control.aeLock was requested to " + requestLock + 271 " but resulted in " + lock); 272 } 273 } 274 275 // control.aeMode, flash.mode, flash.state 276 mapAeAndFlashMode(m, characteristics, p); 277 278 // control.aeState 279 if (LegacyMetadataMapper.LIE_ABOUT_AE_STATE) { 280 // Lie to pass CTS temporarily. 281 // TODO: Implement precapture trigger, after which we can report CONVERGED ourselves 282 m.set(CONTROL_AE_STATE, CONTROL_AE_STATE_CONVERGED); 283 } 284 285 // control.aeRegions 286 { 287 if (VERBOSE) { 288 String meteringAreas = p.get("metering-areas"); 289 Log.v(TAG, "mapAe - parameter dump; metering-areas: " + meteringAreas); 290 } 291 292 MeteringRectangle[] meteringRectArray = getMeteringRectangles(activeArray, 293 zoomData, p.getMeteringAreas(), "AE"); 294 295 m.set(CONTROL_AE_REGIONS, meteringRectArray); 296 } 297 298 // control.afRegions 299 { 300 if (VERBOSE) { 301 String focusAreas = p.get("focus-areas"); 302 Log.v(TAG, "mapAe - parameter dump; focus-areas: " + focusAreas); 303 } 304 305 MeteringRectangle[] meteringRectArray = getMeteringRectangles(activeArray, 306 zoomData, p.getFocusAreas(), "AF"); 307 308 m.set(CONTROL_AF_REGIONS, meteringRectArray); 309 } 310 311 // control.awbLock 312 { 313 boolean lock = p.isAutoWhiteBalanceLockSupported() ? 314 p.getAutoWhiteBalanceLock() : false; 315 m.set(CONTROL_AWB_LOCK, lock); 316 } 317 } 318 319 private static MeteringRectangle[] getMeteringRectangles(Rect activeArray, ZoomData zoomData, 320 List<Camera.Area> meteringAreaList, String regionName) { 321 List<MeteringRectangle> meteringRectList = new ArrayList<>(); 322 if (meteringAreaList != null) { 323 for (Camera.Area area : meteringAreaList) { 324 WeightedRectangle rect = 325 ParameterUtils.convertCameraAreaToActiveArrayRectangle( 326 activeArray, zoomData, area); 327 328 meteringRectList.add(rect.toMetering()); 329 } 330 } 331 332 if (VERBOSE) { 333 Log.v(TAG, 334 "Metering rectangles for " + regionName + ": " 335 + ListUtils.listToString(meteringRectList)); 336 } 337 338 return meteringRectList.toArray(new MeteringRectangle[0]); 339 } 340 341 /** Map results for control.aeMode, flash.mode, flash.state */ 342 private static void mapAeAndFlashMode(CameraMetadataNative m, 343 CameraCharacteristics characteristics, Parameters p) { 344 // Default: AE mode on but flash never fires 345 int flashMode = FLASH_MODE_OFF; 346 // If there is no flash on this camera, the state is always unavailable 347 // , otherwise it's only known for TORCH/SINGLE modes 348 Integer flashState = characteristics.get(CameraCharacteristics.FLASH_INFO_AVAILABLE) 349 ? null : FLASH_STATE_UNAVAILABLE; 350 int aeMode = CONTROL_AE_MODE_ON; 351 352 String flashModeSetting = p.getFlashMode(); 353 354 if (flashModeSetting != null) { 355 switch (flashModeSetting) { 356 case Parameters.FLASH_MODE_OFF: 357 break; // ok, using default 358 case Parameters.FLASH_MODE_AUTO: 359 aeMode = CONTROL_AE_MODE_ON_AUTO_FLASH; 360 break; 361 case Parameters.FLASH_MODE_ON: 362 // flashMode = SINGLE + aeMode = ON is indistinguishable from ON_ALWAYS_FLASH 363 flashMode = FLASH_MODE_SINGLE; 364 aeMode = CONTROL_AE_MODE_ON_ALWAYS_FLASH; 365 flashState = FLASH_STATE_FIRED; 366 break; 367 case Parameters.FLASH_MODE_RED_EYE: 368 aeMode = CONTROL_AE_MODE_ON_AUTO_FLASH_REDEYE; 369 break; 370 case Parameters.FLASH_MODE_TORCH: 371 flashMode = FLASH_MODE_TORCH; 372 flashState = FLASH_STATE_FIRED; 373 break; 374 default: 375 Log.w(TAG, 376 "mapAeAndFlashMode - Ignoring unknown flash mode " + p.getFlashMode()); 377 } 378 } 379 380 // flash.state 381 m.set(FLASH_STATE, flashState); 382 // flash.mode 383 m.set(FLASH_MODE, flashMode); 384 // control.aeMode 385 m.set(CONTROL_AE_MODE, aeMode); 386 } 387 388 private static int convertLegacyAfMode(String mode) { 389 if (mode == null) { 390 Log.w(TAG, "convertLegacyAfMode - no AF mode, default to OFF"); 391 return CONTROL_AF_MODE_OFF; 392 } 393 394 switch (mode) { 395 case Parameters.FOCUS_MODE_AUTO: 396 return CONTROL_AF_MODE_AUTO; 397 case Parameters.FOCUS_MODE_CONTINUOUS_PICTURE: 398 return CONTROL_AF_MODE_CONTINUOUS_PICTURE; 399 case Parameters.FOCUS_MODE_CONTINUOUS_VIDEO: 400 return CONTROL_AF_MODE_CONTINUOUS_VIDEO; 401 case Parameters.FOCUS_MODE_EDOF: 402 return CONTROL_AF_MODE_EDOF; 403 case Parameters.FOCUS_MODE_MACRO: 404 return CONTROL_AF_MODE_MACRO; 405 case Parameters.FOCUS_MODE_FIXED: 406 return CONTROL_AF_MODE_OFF; 407 case Parameters.FOCUS_MODE_INFINITY: 408 return CONTROL_AF_MODE_OFF; 409 default: 410 Log.w(TAG, "convertLegacyAfMode - unknown mode " + mode + " , ignoring"); 411 return CONTROL_AF_MODE_OFF; 412 } 413 } 414 415 /** Map results for scaler.* */ 416 private static void mapScaler(CameraMetadataNative m, 417 ZoomData zoomData, 418 /*out*/Parameters p) { 419 /* 420 * scaler.cropRegion 421 */ 422 { 423 m.set(SCALER_CROP_REGION, zoomData.reportedCrop); 424 } 425 } 426} 427