| /* |
| * Copyright (C) 2014 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package android.hardware.camera2.legacy; |
| |
| import android.graphics.Rect; |
| import android.hardware.Camera; |
| import android.hardware.Camera.Parameters; |
| import android.hardware.camera2.CameraCharacteristics; |
| import android.hardware.camera2.CaptureRequest; |
| import android.hardware.camera2.CaptureResult; |
| import android.hardware.camera2.impl.CameraMetadataNative; |
| import android.hardware.camera2.legacy.ParameterUtils.WeightedRectangle; |
| import android.hardware.camera2.legacy.ParameterUtils.ZoomData; |
| import android.hardware.camera2.params.MeteringRectangle; |
| import android.hardware.camera2.utils.ListUtils; |
| import android.hardware.camera2.utils.ParamsUtils; |
| import android.util.Log; |
| import android.util.Size; |
| |
| import java.util.ArrayList; |
| import java.util.List; |
| |
| import static android.hardware.camera2.CaptureResult.*; |
| |
| /** |
| * Provide legacy-specific implementations of camera2 CaptureResult for legacy devices. |
| */ |
| @SuppressWarnings("deprecation") |
| public class LegacyResultMapper { |
| private static final String TAG = "LegacyResultMapper"; |
| private static final boolean DEBUG = false; |
| |
| private LegacyRequest mCachedRequest = null; |
| private CameraMetadataNative mCachedResult = null; |
| |
| /** |
| * Generate capture result metadata from the legacy camera request. |
| * |
| * <p>This method caches and reuses the result from the previous call to this method if |
| * the {@code parameters} of the subsequent {@link LegacyRequest} passed to this method |
| * have not changed.</p> |
| * |
| * @param legacyRequest a non-{@code null} legacy request containing the latest parameters |
| * @param timestamp the timestamp to use for this result in nanoseconds. |
| * |
| * @return {@link CameraMetadataNative} object containing result metadata. |
| */ |
| public CameraMetadataNative cachedConvertResultMetadata( |
| LegacyRequest legacyRequest, long timestamp) { |
| CameraMetadataNative result; |
| boolean cached; |
| |
| /* |
| * Attempt to look up the result from the cache if the parameters haven't changed |
| */ |
| if (mCachedRequest != null && |
| legacyRequest.parameters.same(mCachedRequest.parameters) && |
| legacyRequest.captureRequest.equals(mCachedRequest.captureRequest)) { |
| result = new CameraMetadataNative(mCachedResult); |
| cached = true; |
| } else { |
| result = convertResultMetadata(legacyRequest); |
| cached = false; |
| |
| // Always cache a *copy* of the metadata result, |
| // since api2's client side takes ownership of it after it receives a result |
| mCachedRequest = legacyRequest; |
| mCachedResult = new CameraMetadataNative(result); |
| } |
| |
| /* |
| * Unconditionally set fields that change in every single frame |
| */ |
| { |
| // sensor.timestamp |
| result.set(SENSOR_TIMESTAMP, timestamp); |
| } |
| |
| if (DEBUG) { |
| Log.v(TAG, "cachedConvertResultMetadata - cached? " + cached + |
| " timestamp = " + timestamp); |
| |
| Log.v(TAG, "----- beginning of result dump ------"); |
| result.dumpToLog(); |
| Log.v(TAG, "----- end of result dump ------"); |
| } |
| |
| return result; |
| } |
| |
| /** |
| * Generate capture result metadata from the legacy camera request. |
| * |
| * @param legacyRequest a non-{@code null} legacy request containing the latest parameters |
| * @return a {@link CameraMetadataNative} object containing result metadata. |
| */ |
| private static CameraMetadataNative convertResultMetadata(LegacyRequest legacyRequest) { |
| CameraCharacteristics characteristics = legacyRequest.characteristics; |
| CaptureRequest request = legacyRequest.captureRequest; |
| Size previewSize = legacyRequest.previewSize; |
| Camera.Parameters params = legacyRequest.parameters; |
| |
| CameraMetadataNative result = new CameraMetadataNative(); |
| |
| Rect activeArraySize = characteristics.get( |
| CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE); |
| ZoomData zoomData = ParameterUtils.convertScalerCropRegion(activeArraySize, |
| request.get(CaptureRequest.SCALER_CROP_REGION), previewSize, params); |
| |
| /* |
| * colorCorrection |
| */ |
| // colorCorrection.aberrationMode |
| { |
| result.set(COLOR_CORRECTION_ABERRATION_MODE, |
| request.get(CaptureRequest.COLOR_CORRECTION_ABERRATION_MODE)); |
| } |
| |
| /* |
| * control |
| */ |
| |
| /* |
| * control.ae* |
| */ |
| mapAe(result, characteristics, request, activeArraySize, zoomData, /*out*/params); |
| |
| /* |
| * control.af* |
| */ |
| mapAf(result, activeArraySize, zoomData, /*out*/params); |
| |
| /* |
| * control.awb* |
| */ |
| mapAwb(result, /*out*/params); |
| |
| /* |
| * control.captureIntent |
| */ |
| { |
| int captureIntent = ParamsUtils.getOrDefault(request, |
| CaptureRequest.CONTROL_CAPTURE_INTENT, |
| /*defaultValue*/CaptureRequest.CONTROL_CAPTURE_INTENT_PREVIEW); |
| |
| captureIntent = LegacyRequestMapper.filterSupportedCaptureIntent(captureIntent); |
| |
| result.set(CONTROL_CAPTURE_INTENT, captureIntent); |
| } |
| |
| /* |
| * control.mode |
| */ |
| { |
| int controlMode = ParamsUtils.getOrDefault(request, CaptureRequest.CONTROL_MODE, |
| CONTROL_MODE_AUTO); |
| if (controlMode == CaptureResult.CONTROL_MODE_USE_SCENE_MODE) { |
| result.set(CONTROL_MODE, CONTROL_MODE_USE_SCENE_MODE); |
| } else { |
| result.set(CONTROL_MODE, CONTROL_MODE_AUTO); |
| } |
| } |
| |
| /* |
| * control.sceneMode |
| */ |
| { |
| String legacySceneMode = params.getSceneMode(); |
| int mode = LegacyMetadataMapper.convertSceneModeFromLegacy(legacySceneMode); |
| if (mode != LegacyMetadataMapper.UNKNOWN_MODE) { |
| result.set(CaptureResult.CONTROL_SCENE_MODE, mode); |
| // In case of SCENE_MODE == FACE_PRIORITY, LegacyFaceDetectMapper will override |
| // the result to say SCENE_MODE == FACE_PRIORITY. |
| } else { |
| Log.w(TAG, "Unknown scene mode " + legacySceneMode + |
| " returned by camera HAL, setting to disabled."); |
| result.set(CaptureResult.CONTROL_SCENE_MODE, CONTROL_SCENE_MODE_DISABLED); |
| } |
| } |
| |
| /* |
| * control.effectMode |
| */ |
| { |
| String legacyEffectMode = params.getColorEffect(); |
| int mode = LegacyMetadataMapper.convertEffectModeFromLegacy(legacyEffectMode); |
| if (mode != LegacyMetadataMapper.UNKNOWN_MODE) { |
| result.set(CaptureResult.CONTROL_EFFECT_MODE, mode); |
| } else { |
| Log.w(TAG, "Unknown effect mode " + legacyEffectMode + |
| " returned by camera HAL, setting to off."); |
| result.set(CaptureResult.CONTROL_EFFECT_MODE, CONTROL_EFFECT_MODE_OFF); |
| } |
| } |
| |
| // control.videoStabilizationMode |
| { |
| int stabMode = |
| (params.isVideoStabilizationSupported() && params.getVideoStabilization()) ? |
| CONTROL_VIDEO_STABILIZATION_MODE_ON : |
| CONTROL_VIDEO_STABILIZATION_MODE_OFF; |
| result.set(CONTROL_VIDEO_STABILIZATION_MODE, stabMode); |
| } |
| |
| /* |
| * flash |
| */ |
| { |
| // flash.mode, flash.state mapped in mapAeAndFlashMode |
| } |
| |
| /* |
| * lens |
| */ |
| // lens.focusDistance |
| { |
| if (Parameters.FOCUS_MODE_INFINITY.equals(params.getFocusMode())) { |
| result.set(CaptureResult.LENS_FOCUS_DISTANCE, 0.0f); |
| } |
| } |
| |
| // lens.focalLength |
| result.set(CaptureResult.LENS_FOCAL_LENGTH, params.getFocalLength()); |
| |
| /* |
| * request |
| */ |
| // request.pipelineDepth |
| result.set(REQUEST_PIPELINE_DEPTH, |
| characteristics.get(CameraCharacteristics.REQUEST_PIPELINE_MAX_DEPTH)); |
| |
| /* |
| * scaler |
| */ |
| mapScaler(result, zoomData, /*out*/params); |
| |
| /* |
| * sensor |
| */ |
| // sensor.timestamp varies every frame; mapping is done in #cachedConvertResultMetadata |
| { |
| // Unconditionally no test patterns |
| result.set(SENSOR_TEST_PATTERN_MODE, SENSOR_TEST_PATTERN_MODE_OFF); |
| } |
| |
| /* |
| * jpeg |
| */ |
| // jpeg.gpsLocation |
| result.set(JPEG_GPS_LOCATION, request.get(CaptureRequest.JPEG_GPS_LOCATION)); |
| |
| // jpeg.orientation |
| result.set(JPEG_ORIENTATION, request.get(CaptureRequest.JPEG_ORIENTATION)); |
| |
| // jpeg.quality |
| result.set(JPEG_QUALITY, (byte) params.getJpegQuality()); |
| |
| // jpeg.thumbnailQuality |
| result.set(JPEG_THUMBNAIL_QUALITY, (byte) params.getJpegThumbnailQuality()); |
| |
| // jpeg.thumbnailSize |
| Camera.Size s = params.getJpegThumbnailSize(); |
| if (s != null) { |
| result.set(JPEG_THUMBNAIL_SIZE, ParameterUtils.convertSize(s)); |
| } else { |
| Log.w(TAG, "Null thumbnail size received from parameters."); |
| } |
| |
| /* |
| * noiseReduction.* |
| */ |
| // noiseReduction.mode |
| result.set(NOISE_REDUCTION_MODE, request.get(CaptureRequest.NOISE_REDUCTION_MODE)); |
| |
| return result; |
| } |
| |
| private static void mapAe(CameraMetadataNative m, |
| CameraCharacteristics characteristics, |
| CaptureRequest request, Rect activeArray, ZoomData zoomData, /*out*/Parameters p) { |
| // control.aeAntiBandingMode |
| { |
| int antiBandingMode = LegacyMetadataMapper.convertAntiBandingModeOrDefault( |
| p.getAntibanding()); |
| m.set(CONTROL_AE_ANTIBANDING_MODE, antiBandingMode); |
| } |
| |
| // control.aeExposureCompensation |
| { |
| m.set(CONTROL_AE_EXPOSURE_COMPENSATION, p.getExposureCompensation()); |
| } |
| |
| // control.aeLock |
| { |
| boolean lock = p.isAutoExposureLockSupported() ? p.getAutoExposureLock() : false; |
| m.set(CONTROL_AE_LOCK, lock); |
| if (DEBUG) { |
| Log.v(TAG, |
| "mapAe - android.control.aeLock = " + lock + |
| ", supported = " + p.isAutoExposureLockSupported()); |
| } |
| |
| Boolean requestLock = request.get(CaptureRequest.CONTROL_AE_LOCK); |
| if (requestLock != null && requestLock != lock) { |
| Log.w(TAG, |
| "mapAe - android.control.aeLock was requested to " + requestLock + |
| " but resulted in " + lock); |
| } |
| } |
| |
| // control.aeMode, flash.mode, flash.state |
| mapAeAndFlashMode(m, characteristics, p); |
| |
| // control.aeState |
| if (LegacyMetadataMapper.LIE_ABOUT_AE_STATE) { |
| // Lie to pass CTS temporarily. |
| // TODO: Implement precapture trigger, after which we can report CONVERGED ourselves |
| m.set(CONTROL_AE_STATE, CONTROL_AE_STATE_CONVERGED); |
| } |
| |
| // control.aeRegions |
| if (p.getMaxNumMeteringAreas() > 0) { |
| if (DEBUG) { |
| String meteringAreas = p.get("metering-areas"); |
| Log.v(TAG, "mapAe - parameter dump; metering-areas: " + meteringAreas); |
| } |
| |
| MeteringRectangle[] meteringRectArray = getMeteringRectangles(activeArray, |
| zoomData, p.getMeteringAreas(), "AE"); |
| |
| m.set(CONTROL_AE_REGIONS, meteringRectArray); |
| } |
| |
| } |
| |
| private static void mapAf(CameraMetadataNative m, |
| Rect activeArray, ZoomData zoomData, Camera.Parameters p) { |
| // control.afMode |
| m.set(CaptureResult.CONTROL_AF_MODE, convertLegacyAfMode(p.getFocusMode())); |
| |
| // control.afRegions |
| if (p.getMaxNumFocusAreas() > 0) { |
| if (DEBUG) { |
| String focusAreas = p.get("focus-areas"); |
| Log.v(TAG, "mapAe - parameter dump; focus-areas: " + focusAreas); |
| } |
| |
| MeteringRectangle[] meteringRectArray = getMeteringRectangles(activeArray, |
| zoomData, p.getFocusAreas(), "AF"); |
| |
| m.set(CONTROL_AF_REGIONS, meteringRectArray); |
| } |
| } |
| |
| private static void mapAwb(CameraMetadataNative m, Camera.Parameters p) { |
| // control.awbLock |
| { |
| boolean lock = p.isAutoWhiteBalanceLockSupported() ? |
| p.getAutoWhiteBalanceLock() : false; |
| m.set(CONTROL_AWB_LOCK, lock); |
| } |
| |
| // control.awbMode |
| { |
| int awbMode = convertLegacyAwbMode(p.getWhiteBalance()); |
| m.set(CONTROL_AWB_MODE, awbMode); |
| } |
| } |
| |
| private static MeteringRectangle[] getMeteringRectangles(Rect activeArray, ZoomData zoomData, |
| List<Camera.Area> meteringAreaList, String regionName) { |
| List<MeteringRectangle> meteringRectList = new ArrayList<>(); |
| if (meteringAreaList != null) { |
| for (Camera.Area area : meteringAreaList) { |
| WeightedRectangle rect = |
| ParameterUtils.convertCameraAreaToActiveArrayRectangle( |
| activeArray, zoomData, area); |
| |
| meteringRectList.add(rect.toMetering()); |
| } |
| } |
| |
| if (DEBUG) { |
| Log.v(TAG, |
| "Metering rectangles for " + regionName + ": " |
| + ListUtils.listToString(meteringRectList)); |
| } |
| |
| return meteringRectList.toArray(new MeteringRectangle[0]); |
| } |
| |
| /** Map results for control.aeMode, flash.mode, flash.state */ |
| private static void mapAeAndFlashMode(CameraMetadataNative m, |
| CameraCharacteristics characteristics, Parameters p) { |
| // Default: AE mode on but flash never fires |
| int flashMode = FLASH_MODE_OFF; |
| // If there is no flash on this camera, the state is always unavailable |
| // , otherwise it's only known for TORCH/SINGLE modes |
| Integer flashState = characteristics.get(CameraCharacteristics.FLASH_INFO_AVAILABLE) |
| ? null : FLASH_STATE_UNAVAILABLE; |
| int aeMode = CONTROL_AE_MODE_ON; |
| |
| String flashModeSetting = p.getFlashMode(); |
| |
| if (flashModeSetting != null) { |
| switch (flashModeSetting) { |
| case Parameters.FLASH_MODE_OFF: |
| break; // ok, using default |
| case Parameters.FLASH_MODE_AUTO: |
| aeMode = CONTROL_AE_MODE_ON_AUTO_FLASH; |
| break; |
| case Parameters.FLASH_MODE_ON: |
| // flashMode = SINGLE + aeMode = ON is indistinguishable from ON_ALWAYS_FLASH |
| flashMode = FLASH_MODE_SINGLE; |
| aeMode = CONTROL_AE_MODE_ON_ALWAYS_FLASH; |
| flashState = FLASH_STATE_FIRED; |
| break; |
| case Parameters.FLASH_MODE_RED_EYE: |
| aeMode = CONTROL_AE_MODE_ON_AUTO_FLASH_REDEYE; |
| break; |
| case Parameters.FLASH_MODE_TORCH: |
| flashMode = FLASH_MODE_TORCH; |
| flashState = FLASH_STATE_FIRED; |
| break; |
| default: |
| Log.w(TAG, |
| "mapAeAndFlashMode - Ignoring unknown flash mode " + p.getFlashMode()); |
| } |
| } |
| |
| // flash.state |
| m.set(FLASH_STATE, flashState); |
| // flash.mode |
| m.set(FLASH_MODE, flashMode); |
| // control.aeMode |
| m.set(CONTROL_AE_MODE, aeMode); |
| } |
| |
| private static int convertLegacyAfMode(String mode) { |
| if (mode == null) { |
| Log.w(TAG, "convertLegacyAfMode - no AF mode, default to OFF"); |
| return CONTROL_AF_MODE_OFF; |
| } |
| |
| switch (mode) { |
| case Parameters.FOCUS_MODE_AUTO: |
| return CONTROL_AF_MODE_AUTO; |
| case Parameters.FOCUS_MODE_CONTINUOUS_PICTURE: |
| return CONTROL_AF_MODE_CONTINUOUS_PICTURE; |
| case Parameters.FOCUS_MODE_CONTINUOUS_VIDEO: |
| return CONTROL_AF_MODE_CONTINUOUS_VIDEO; |
| case Parameters.FOCUS_MODE_EDOF: |
| return CONTROL_AF_MODE_EDOF; |
| case Parameters.FOCUS_MODE_MACRO: |
| return CONTROL_AF_MODE_MACRO; |
| case Parameters.FOCUS_MODE_FIXED: |
| return CONTROL_AF_MODE_OFF; |
| case Parameters.FOCUS_MODE_INFINITY: |
| return CONTROL_AF_MODE_OFF; |
| default: |
| Log.w(TAG, "convertLegacyAfMode - unknown mode " + mode + " , ignoring"); |
| return CONTROL_AF_MODE_OFF; |
| } |
| } |
| |
| private static int convertLegacyAwbMode(String mode) { |
| if (mode == null) { |
| // OK: camera1 api may not support changing WB modes; assume AUTO |
| return CONTROL_AWB_MODE_AUTO; |
| } |
| |
| switch (mode) { |
| case Camera.Parameters.WHITE_BALANCE_AUTO: |
| return CONTROL_AWB_MODE_AUTO; |
| case Camera.Parameters.WHITE_BALANCE_INCANDESCENT: |
| return CONTROL_AWB_MODE_INCANDESCENT; |
| case Camera.Parameters.WHITE_BALANCE_FLUORESCENT: |
| return CONTROL_AWB_MODE_FLUORESCENT; |
| case Camera.Parameters.WHITE_BALANCE_WARM_FLUORESCENT: |
| return CONTROL_AWB_MODE_WARM_FLUORESCENT; |
| case Camera.Parameters.WHITE_BALANCE_DAYLIGHT: |
| return CONTROL_AWB_MODE_DAYLIGHT; |
| case Camera.Parameters.WHITE_BALANCE_CLOUDY_DAYLIGHT: |
| return CONTROL_AWB_MODE_CLOUDY_DAYLIGHT; |
| case Camera.Parameters.WHITE_BALANCE_TWILIGHT: |
| return CONTROL_AWB_MODE_TWILIGHT; |
| case Camera.Parameters.WHITE_BALANCE_SHADE: |
| return CONTROL_AWB_MODE_SHADE; |
| default: |
| Log.w(TAG, "convertAwbMode - unrecognized WB mode " + mode); |
| return CONTROL_AWB_MODE_AUTO; |
| } |
| } |
| |
| /** Map results for scaler.* */ |
| private static void mapScaler(CameraMetadataNative m, |
| ZoomData zoomData, |
| /*out*/Parameters p) { |
| /* |
| * scaler.cropRegion |
| */ |
| { |
| m.set(SCALER_CROP_REGION, zoomData.reportedCrop); |
| } |
| } |
| } |