/* * 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. * *
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.
* * @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)) { 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 { // Always hardcoded to FAST result.set(COLOR_CORRECTION_ABERRATION_MODE, COLOR_CORRECTION_ABERRATION_MODE_FAST); } /* * 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, NOISE_REDUCTION_MODE_FAST); 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