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 17 package android.hardware.camera2.legacy; 18 19 import android.graphics.Rect; 20 import android.hardware.Camera; 21 import android.hardware.Camera.Parameters; 22 import android.hardware.camera2.CameraCharacteristics; 23 import android.hardware.camera2.CaptureRequest; 24 import android.hardware.camera2.CaptureResult; 25 import android.hardware.camera2.impl.CameraMetadataNative; 26 import android.hardware.camera2.legacy.ParameterUtils.WeightedRectangle; 27 import android.hardware.camera2.legacy.ParameterUtils.ZoomData; 28 import android.hardware.camera2.params.MeteringRectangle; 29 import android.hardware.camera2.utils.ListUtils; 30 import android.hardware.camera2.utils.ParamsUtils; 31 import android.util.Log; 32 import android.util.Size; 33 34 import java.util.ArrayList; 35 import java.util.List; 36 37 import static android.hardware.camera2.CaptureResult.*; 38 39 /** 40 * Provide legacy-specific implementations of camera2 CaptureResult for legacy devices. 41 */ 42 @SuppressWarnings("deprecation") 43 public class LegacyResultMapper { 44 private static final String TAG = "LegacyResultMapper"; 45 private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE); 46 47 private LegacyRequest mCachedRequest = null; 48 private CameraMetadataNative mCachedResult = null; 49 50 /** 51 * Generate capture result metadata from the legacy camera request. 52 * 53 * <p>This method caches and reuses the result from the previous call to this method if 54 * the {@code parameters} of the subsequent {@link LegacyRequest} passed to this method 55 * have not changed.</p> 56 * 57 * @param legacyRequest a non-{@code null} legacy request containing the latest parameters 58 * @param timestamp the timestamp to use for this result in nanoseconds. 59 * 60 * @return {@link CameraMetadataNative} object containing result metadata. 61 */ cachedConvertResultMetadata( LegacyRequest legacyRequest, long timestamp)62 public CameraMetadataNative cachedConvertResultMetadata( 63 LegacyRequest legacyRequest, long timestamp) { 64 CameraMetadataNative result; 65 boolean cached; 66 67 /* 68 * Attempt to look up the result from the cache if the parameters haven't changed 69 */ 70 if (mCachedRequest != null && legacyRequest.parameters.same(mCachedRequest.parameters)) { 71 result = new CameraMetadataNative(mCachedResult); 72 cached = true; 73 } else { 74 result = convertResultMetadata(legacyRequest); 75 cached = false; 76 77 // Always cache a *copy* of the metadata result, 78 // since api2's client side takes ownership of it after it receives a result 79 mCachedRequest = legacyRequest; 80 mCachedResult = new CameraMetadataNative(result); 81 } 82 83 /* 84 * Unconditionally set fields that change in every single frame 85 */ 86 { 87 // sensor.timestamp 88 result.set(SENSOR_TIMESTAMP, timestamp); 89 } 90 91 if (VERBOSE) { 92 Log.v(TAG, "cachedConvertResultMetadata - cached? " + cached + 93 " timestamp = " + timestamp); 94 95 Log.v(TAG, "----- beginning of result dump ------"); 96 result.dumpToLog(); 97 Log.v(TAG, "----- end of result dump ------"); 98 } 99 100 return result; 101 } 102 103 /** 104 * Generate capture result metadata from the legacy camera request. 105 * 106 * @param legacyRequest a non-{@code null} legacy request containing the latest parameters 107 * @return a {@link CameraMetadataNative} object containing result metadata. 108 */ convertResultMetadata(LegacyRequest legacyRequest)109 private static CameraMetadataNative convertResultMetadata(LegacyRequest legacyRequest) { 110 CameraCharacteristics characteristics = legacyRequest.characteristics; 111 CaptureRequest request = legacyRequest.captureRequest; 112 Size previewSize = legacyRequest.previewSize; 113 Camera.Parameters params = legacyRequest.parameters; 114 115 CameraMetadataNative result = new CameraMetadataNative(); 116 117 Rect activeArraySize = characteristics.get( 118 CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE); 119 ZoomData zoomData = ParameterUtils.convertScalerCropRegion(activeArraySize, 120 request.get(CaptureRequest.SCALER_CROP_REGION), previewSize, params); 121 122 /* 123 * colorCorrection 124 */ 125 // colorCorrection.aberrationMode 126 { 127 // Always hardcoded to FAST 128 result.set(COLOR_CORRECTION_ABERRATION_MODE, COLOR_CORRECTION_ABERRATION_MODE_FAST); 129 } 130 131 /* 132 * control 133 */ 134 135 /* 136 * control.ae* 137 */ 138 mapAe(result, characteristics, request, activeArraySize, zoomData, /*out*/params); 139 140 /* 141 * control.af* 142 */ 143 mapAf(result, activeArraySize, zoomData, /*out*/params); 144 145 /* 146 * control.awb* 147 */ 148 mapAwb(result, /*out*/params); 149 150 /* 151 * control.captureIntent 152 */ 153 { 154 int captureIntent = ParamsUtils.getOrDefault(request, 155 CaptureRequest.CONTROL_CAPTURE_INTENT, 156 /*defaultValue*/CaptureRequest.CONTROL_CAPTURE_INTENT_PREVIEW); 157 158 captureIntent = LegacyRequestMapper.filterSupportedCaptureIntent(captureIntent); 159 160 result.set(CONTROL_CAPTURE_INTENT, captureIntent); 161 } 162 163 /* 164 * control.mode 165 */ 166 { 167 int controlMode = ParamsUtils.getOrDefault(request, CaptureRequest.CONTROL_MODE, 168 CONTROL_MODE_AUTO); 169 if (controlMode == CaptureResult.CONTROL_MODE_USE_SCENE_MODE) { 170 result.set(CONTROL_MODE, CONTROL_MODE_USE_SCENE_MODE); 171 } else { 172 result.set(CONTROL_MODE, CONTROL_MODE_AUTO); 173 } 174 } 175 176 /* 177 * control.sceneMode 178 */ 179 { 180 String legacySceneMode = params.getSceneMode(); 181 int mode = LegacyMetadataMapper.convertSceneModeFromLegacy(legacySceneMode); 182 if (mode != LegacyMetadataMapper.UNKNOWN_MODE) { 183 result.set(CaptureResult.CONTROL_SCENE_MODE, mode); 184 // In case of SCENE_MODE == FACE_PRIORITY, LegacyFaceDetectMapper will override 185 // the result to say SCENE_MODE == FACE_PRIORITY. 186 } else { 187 Log.w(TAG, "Unknown scene mode " + legacySceneMode + 188 " returned by camera HAL, setting to disabled."); 189 result.set(CaptureResult.CONTROL_SCENE_MODE, CONTROL_SCENE_MODE_DISABLED); 190 } 191 } 192 193 /* 194 * control.effectMode 195 */ 196 { 197 String legacyEffectMode = params.getColorEffect(); 198 int mode = LegacyMetadataMapper.convertEffectModeFromLegacy(legacyEffectMode); 199 if (mode != LegacyMetadataMapper.UNKNOWN_MODE) { 200 result.set(CaptureResult.CONTROL_EFFECT_MODE, mode); 201 } else { 202 Log.w(TAG, "Unknown effect mode " + legacyEffectMode + 203 " returned by camera HAL, setting to off."); 204 result.set(CaptureResult.CONTROL_EFFECT_MODE, CONTROL_EFFECT_MODE_OFF); 205 } 206 } 207 208 // control.videoStabilizationMode 209 { 210 int stabMode = 211 (params.isVideoStabilizationSupported() && params.getVideoStabilization()) ? 212 CONTROL_VIDEO_STABILIZATION_MODE_ON : 213 CONTROL_VIDEO_STABILIZATION_MODE_OFF; 214 result.set(CONTROL_VIDEO_STABILIZATION_MODE, stabMode); 215 } 216 217 /* 218 * flash 219 */ 220 { 221 // flash.mode, flash.state mapped in mapAeAndFlashMode 222 } 223 224 /* 225 * lens 226 */ 227 // lens.focusDistance 228 { 229 if (Parameters.FOCUS_MODE_INFINITY.equals(params.getFocusMode())) { 230 result.set(CaptureResult.LENS_FOCUS_DISTANCE, 0.0f); 231 } 232 } 233 234 // lens.focalLength 235 result.set(CaptureResult.LENS_FOCAL_LENGTH, params.getFocalLength()); 236 237 /* 238 * request 239 */ 240 // request.pipelineDepth 241 result.set(REQUEST_PIPELINE_DEPTH, 242 characteristics.get(CameraCharacteristics.REQUEST_PIPELINE_MAX_DEPTH)); 243 244 /* 245 * scaler 246 */ 247 mapScaler(result, zoomData, /*out*/params); 248 249 /* 250 * sensor 251 */ 252 // sensor.timestamp varies every frame; mapping is done in #cachedConvertResultMetadata 253 { 254 // Unconditionally no test patterns 255 result.set(SENSOR_TEST_PATTERN_MODE, SENSOR_TEST_PATTERN_MODE_OFF); 256 } 257 258 /* 259 * jpeg 260 */ 261 // jpeg.gpsLocation 262 result.set(JPEG_GPS_LOCATION, request.get(CaptureRequest.JPEG_GPS_LOCATION)); 263 264 // jpeg.orientation 265 result.set(JPEG_ORIENTATION, request.get(CaptureRequest.JPEG_ORIENTATION)); 266 267 // jpeg.quality 268 result.set(JPEG_QUALITY, (byte) params.getJpegQuality()); 269 270 // jpeg.thumbnailQuality 271 result.set(JPEG_THUMBNAIL_QUALITY, (byte) params.getJpegThumbnailQuality()); 272 273 // jpeg.thumbnailSize 274 Camera.Size s = params.getJpegThumbnailSize(); 275 if (s != null) { 276 result.set(JPEG_THUMBNAIL_SIZE, ParameterUtils.convertSize(s)); 277 } else { 278 Log.w(TAG, "Null thumbnail size received from parameters."); 279 } 280 281 /* 282 * noiseReduction.* 283 */ 284 // noiseReduction.mode 285 result.set(NOISE_REDUCTION_MODE, NOISE_REDUCTION_MODE_FAST); 286 287 return result; 288 } 289 mapAe(CameraMetadataNative m, CameraCharacteristics characteristics, CaptureRequest request, Rect activeArray, ZoomData zoomData, Parameters p)290 private static void mapAe(CameraMetadataNative m, 291 CameraCharacteristics characteristics, 292 CaptureRequest request, Rect activeArray, ZoomData zoomData, /*out*/Parameters p) { 293 // control.aeAntiBandingMode 294 { 295 int antiBandingMode = LegacyMetadataMapper.convertAntiBandingModeOrDefault( 296 p.getAntibanding()); 297 m.set(CONTROL_AE_ANTIBANDING_MODE, antiBandingMode); 298 } 299 300 // control.aeExposureCompensation 301 { 302 m.set(CONTROL_AE_EXPOSURE_COMPENSATION, p.getExposureCompensation()); 303 } 304 305 // control.aeLock 306 { 307 boolean lock = p.isAutoExposureLockSupported() ? p.getAutoExposureLock() : false; 308 m.set(CONTROL_AE_LOCK, lock); 309 if (VERBOSE) { 310 Log.v(TAG, 311 "mapAe - android.control.aeLock = " + lock + 312 ", supported = " + p.isAutoExposureLockSupported()); 313 } 314 315 Boolean requestLock = request.get(CaptureRequest.CONTROL_AE_LOCK); 316 if (requestLock != null && requestLock != lock) { 317 Log.w(TAG, 318 "mapAe - android.control.aeLock was requested to " + requestLock + 319 " but resulted in " + lock); 320 } 321 } 322 323 // control.aeMode, flash.mode, flash.state 324 mapAeAndFlashMode(m, characteristics, p); 325 326 // control.aeState 327 if (LegacyMetadataMapper.LIE_ABOUT_AE_STATE) { 328 // Lie to pass CTS temporarily. 329 // TODO: Implement precapture trigger, after which we can report CONVERGED ourselves 330 m.set(CONTROL_AE_STATE, CONTROL_AE_STATE_CONVERGED); 331 } 332 333 // control.aeRegions 334 if (p.getMaxNumMeteringAreas() > 0) { 335 if (VERBOSE) { 336 String meteringAreas = p.get("metering-areas"); 337 Log.v(TAG, "mapAe - parameter dump; metering-areas: " + meteringAreas); 338 } 339 340 MeteringRectangle[] meteringRectArray = getMeteringRectangles(activeArray, 341 zoomData, p.getMeteringAreas(), "AE"); 342 343 m.set(CONTROL_AE_REGIONS, meteringRectArray); 344 } 345 346 } 347 mapAf(CameraMetadataNative m, Rect activeArray, ZoomData zoomData, Camera.Parameters p)348 private static void mapAf(CameraMetadataNative m, 349 Rect activeArray, ZoomData zoomData, Camera.Parameters p) { 350 // control.afMode 351 m.set(CaptureResult.CONTROL_AF_MODE, convertLegacyAfMode(p.getFocusMode())); 352 353 // control.afRegions 354 if (p.getMaxNumFocusAreas() > 0) { 355 if (VERBOSE) { 356 String focusAreas = p.get("focus-areas"); 357 Log.v(TAG, "mapAe - parameter dump; focus-areas: " + focusAreas); 358 } 359 360 MeteringRectangle[] meteringRectArray = getMeteringRectangles(activeArray, 361 zoomData, p.getFocusAreas(), "AF"); 362 363 m.set(CONTROL_AF_REGIONS, meteringRectArray); 364 } 365 } 366 mapAwb(CameraMetadataNative m, Camera.Parameters p)367 private static void mapAwb(CameraMetadataNative m, Camera.Parameters p) { 368 // control.awbLock 369 { 370 boolean lock = p.isAutoWhiteBalanceLockSupported() ? 371 p.getAutoWhiteBalanceLock() : false; 372 m.set(CONTROL_AWB_LOCK, lock); 373 } 374 375 // control.awbMode 376 { 377 int awbMode = convertLegacyAwbMode(p.getWhiteBalance()); 378 m.set(CONTROL_AWB_MODE, awbMode); 379 } 380 } 381 getMeteringRectangles(Rect activeArray, ZoomData zoomData, List<Camera.Area> meteringAreaList, String regionName)382 private static MeteringRectangle[] getMeteringRectangles(Rect activeArray, ZoomData zoomData, 383 List<Camera.Area> meteringAreaList, String regionName) { 384 List<MeteringRectangle> meteringRectList = new ArrayList<>(); 385 if (meteringAreaList != null) { 386 for (Camera.Area area : meteringAreaList) { 387 WeightedRectangle rect = 388 ParameterUtils.convertCameraAreaToActiveArrayRectangle( 389 activeArray, zoomData, area); 390 391 meteringRectList.add(rect.toMetering()); 392 } 393 } 394 395 if (VERBOSE) { 396 Log.v(TAG, 397 "Metering rectangles for " + regionName + ": " 398 + ListUtils.listToString(meteringRectList)); 399 } 400 401 return meteringRectList.toArray(new MeteringRectangle[0]); 402 } 403 404 /** Map results for control.aeMode, flash.mode, flash.state */ mapAeAndFlashMode(CameraMetadataNative m, CameraCharacteristics characteristics, Parameters p)405 private static void mapAeAndFlashMode(CameraMetadataNative m, 406 CameraCharacteristics characteristics, Parameters p) { 407 // Default: AE mode on but flash never fires 408 int flashMode = FLASH_MODE_OFF; 409 // If there is no flash on this camera, the state is always unavailable 410 // , otherwise it's only known for TORCH/SINGLE modes 411 Integer flashState = characteristics.get(CameraCharacteristics.FLASH_INFO_AVAILABLE) 412 ? null : FLASH_STATE_UNAVAILABLE; 413 int aeMode = CONTROL_AE_MODE_ON; 414 415 String flashModeSetting = p.getFlashMode(); 416 417 if (flashModeSetting != null) { 418 switch (flashModeSetting) { 419 case Parameters.FLASH_MODE_OFF: 420 break; // ok, using default 421 case Parameters.FLASH_MODE_AUTO: 422 aeMode = CONTROL_AE_MODE_ON_AUTO_FLASH; 423 break; 424 case Parameters.FLASH_MODE_ON: 425 // flashMode = SINGLE + aeMode = ON is indistinguishable from ON_ALWAYS_FLASH 426 flashMode = FLASH_MODE_SINGLE; 427 aeMode = CONTROL_AE_MODE_ON_ALWAYS_FLASH; 428 flashState = FLASH_STATE_FIRED; 429 break; 430 case Parameters.FLASH_MODE_RED_EYE: 431 aeMode = CONTROL_AE_MODE_ON_AUTO_FLASH_REDEYE; 432 break; 433 case Parameters.FLASH_MODE_TORCH: 434 flashMode = FLASH_MODE_TORCH; 435 flashState = FLASH_STATE_FIRED; 436 break; 437 default: 438 Log.w(TAG, 439 "mapAeAndFlashMode - Ignoring unknown flash mode " + p.getFlashMode()); 440 } 441 } 442 443 // flash.state 444 m.set(FLASH_STATE, flashState); 445 // flash.mode 446 m.set(FLASH_MODE, flashMode); 447 // control.aeMode 448 m.set(CONTROL_AE_MODE, aeMode); 449 } 450 convertLegacyAfMode(String mode)451 private static int convertLegacyAfMode(String mode) { 452 if (mode == null) { 453 Log.w(TAG, "convertLegacyAfMode - no AF mode, default to OFF"); 454 return CONTROL_AF_MODE_OFF; 455 } 456 457 switch (mode) { 458 case Parameters.FOCUS_MODE_AUTO: 459 return CONTROL_AF_MODE_AUTO; 460 case Parameters.FOCUS_MODE_CONTINUOUS_PICTURE: 461 return CONTROL_AF_MODE_CONTINUOUS_PICTURE; 462 case Parameters.FOCUS_MODE_CONTINUOUS_VIDEO: 463 return CONTROL_AF_MODE_CONTINUOUS_VIDEO; 464 case Parameters.FOCUS_MODE_EDOF: 465 return CONTROL_AF_MODE_EDOF; 466 case Parameters.FOCUS_MODE_MACRO: 467 return CONTROL_AF_MODE_MACRO; 468 case Parameters.FOCUS_MODE_FIXED: 469 return CONTROL_AF_MODE_OFF; 470 case Parameters.FOCUS_MODE_INFINITY: 471 return CONTROL_AF_MODE_OFF; 472 default: 473 Log.w(TAG, "convertLegacyAfMode - unknown mode " + mode + " , ignoring"); 474 return CONTROL_AF_MODE_OFF; 475 } 476 } 477 convertLegacyAwbMode(String mode)478 private static int convertLegacyAwbMode(String mode) { 479 if (mode == null) { 480 // OK: camera1 api may not support changing WB modes; assume AUTO 481 return CONTROL_AWB_MODE_AUTO; 482 } 483 484 switch (mode) { 485 case Camera.Parameters.WHITE_BALANCE_AUTO: 486 return CONTROL_AWB_MODE_AUTO; 487 case Camera.Parameters.WHITE_BALANCE_INCANDESCENT: 488 return CONTROL_AWB_MODE_INCANDESCENT; 489 case Camera.Parameters.WHITE_BALANCE_FLUORESCENT: 490 return CONTROL_AWB_MODE_FLUORESCENT; 491 case Camera.Parameters.WHITE_BALANCE_WARM_FLUORESCENT: 492 return CONTROL_AWB_MODE_WARM_FLUORESCENT; 493 case Camera.Parameters.WHITE_BALANCE_DAYLIGHT: 494 return CONTROL_AWB_MODE_DAYLIGHT; 495 case Camera.Parameters.WHITE_BALANCE_CLOUDY_DAYLIGHT: 496 return CONTROL_AWB_MODE_CLOUDY_DAYLIGHT; 497 case Camera.Parameters.WHITE_BALANCE_TWILIGHT: 498 return CONTROL_AWB_MODE_TWILIGHT; 499 case Camera.Parameters.WHITE_BALANCE_SHADE: 500 return CONTROL_AWB_MODE_SHADE; 501 default: 502 Log.w(TAG, "convertAwbMode - unrecognized WB mode " + mode); 503 return CONTROL_AWB_MODE_AUTO; 504 } 505 } 506 507 /** Map results for scaler.* */ mapScaler(CameraMetadataNative m, ZoomData zoomData, Parameters p)508 private static void mapScaler(CameraMetadataNative m, 509 ZoomData zoomData, 510 /*out*/Parameters p) { 511 /* 512 * scaler.cropRegion 513 */ 514 { 515 m.set(SCALER_CROP_REGION, zoomData.reportedCrop); 516 } 517 } 518 } 519