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