Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit df6242e3 authored by Igor Murashkin's avatar Igor Murashkin
Browse files

camera2: Add crop region support and some other AE tags to legacy

Change-Id: If7a87b210a57ea13d6329bb363b1c7ecdadc52e8
parent df0697e3
Loading
Loading
Loading
Loading
+40 −0
Original line number Diff line number Diff line
@@ -1841,6 +1841,27 @@ public class Camera {
        return camera.new Parameters();
    }

    /**
     * Returns a copied {@link Parameters}; for shim use only.
     *
     * @param parameters a non-{@code null} parameters
     * @return a Parameter object, with all the parameters copied from {@code parameters}.
     *
     * @throws NullPointerException if {@code parameters} was {@code null}
     * @hide
     */
    public static Parameters getParametersCopy(Camera.Parameters parameters) {
        if (parameters == null) {
            throw new NullPointerException("parameters must not be null");
        }

        Camera camera = parameters.getOuter();
        Parameters p = camera.new Parameters();
        p.copyFrom(parameters);

        return p;
    }

    /**
     * Image size (width and height dimensions).
     */
@@ -2331,6 +2352,25 @@ public class Camera {
            mMap = new LinkedHashMap<String, String>(/*initialCapacity*/64);
        }

        /**
         * Overwrite existing parameters with a copy of the ones from {@code other}.
         *
         * <b>For use by the legacy shim only.</b>
         *
         * @hide
         */
        public void copyFrom(Parameters other) {
            if (other == null) {
                throw new NullPointerException("other must not be null");
            }

            mMap.putAll(other.mMap);
        }

        private Camera getOuter() {
            return Camera.this;
        }

        /**
         * Writes the current Parameters to the log.
         * @hide
+2 −1
Original line number Diff line number Diff line
@@ -199,7 +199,8 @@ public class CameraDeviceUserShim implements ICameraDeviceUser {

        CameraCharacteristics characteristics =
                LegacyMetadataMapper.createCharacteristics(legacyCamera.getParameters(), info);
        LegacyCameraDevice device = new LegacyCameraDevice(cameraId, legacyCamera, callbacks);
        LegacyCameraDevice device = new LegacyCameraDevice(
                cameraId, legacyCamera, characteristics, callbacks);
        return new CameraDeviceUserShim(cameraId, device, characteristics, init);
    }

+5 −2
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package android.hardware.camera2.legacy;

import android.graphics.ImageFormat;
import android.hardware.Camera;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.impl.CaptureResultExtras;
import android.hardware.camera2.ICameraDeviceCallbacks;
@@ -202,9 +203,11 @@ public class LegacyCameraDevice implements AutoCloseable {
     *
     * @param cameraId the id of the camera.
     * @param camera an open {@link Camera} device.
     * @param characteristics the static camera characteristics for this camera device
     * @param callbacks {@link ICameraDeviceCallbacks} callbacks to call for Camera2 API operations.
     */
    public LegacyCameraDevice(int cameraId, Camera camera, ICameraDeviceCallbacks callbacks) {
    public LegacyCameraDevice(int cameraId, Camera camera, CameraCharacteristics characteristics,
            ICameraDeviceCallbacks callbacks) {
        mCameraId = cameraId;
        mDeviceCallbacks = callbacks;
        TAG = String.format("CameraDevice-%d-LE", mCameraId);
@@ -215,7 +218,7 @@ public class LegacyCameraDevice implements AutoCloseable {
        mCallbackHandler = new Handler(mCallbackHandlerThread.getLooper());
        mDeviceState.setCameraDeviceCallbacks(mCallbackHandler, mStateListener);
        mRequestThreadManager =
                new RequestThreadManager(cameraId, camera, mDeviceState);
                new RequestThreadManager(cameraId, camera, characteristics, mDeviceState);
        mRequestThreadManager.start();
    }

+136 −250
Original line number Diff line number Diff line
@@ -17,28 +17,31 @@
package android.hardware.camera2.legacy;

import android.graphics.ImageFormat;
import android.graphics.Rect;
import android.hardware.Camera;
import android.hardware.Camera.CameraInfo;
import android.hardware.Camera.Parameters;
import android.hardware.Camera.Size;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CameraMetadata;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.CaptureResult;
import android.hardware.camera2.impl.CameraMetadataNative;
import android.hardware.camera2.params.StreamConfiguration;
import android.hardware.camera2.params.StreamConfigurationDuration;
import android.hardware.camera2.utils.ArrayUtils;
import android.hardware.camera2.utils.ListUtils;
import android.hardware.camera2.utils.ParamsUtils;
import android.util.Log;
import android.util.Range;
import android.util.Size;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;

import static com.android.internal.util.Preconditions.*;
import static android.hardware.camera2.CameraCharacteristics.*;
import static android.hardware.camera2.legacy.ParameterUtils.*;

/**
 * Provide legacy-specific implementations of camera2 metadata for legacy devices, such as the
@@ -81,10 +84,9 @@ public class LegacyMetadataMapper {
     * TODO: Remove these constants and strip out any code that previously relied on them
     * being set to true.
     */
    private static final boolean LIE_ABOUT_FLASH = true;
    private static final boolean LIE_ABOUT_AE = true;
    private static final boolean LIE_ABOUT_AF = true;
    private static final boolean LIE_ABOUT_AWB = true;
    static final boolean LIE_ABOUT_AE_STATE = true;
    static final boolean LIE_ABOUT_AF = true;
    static final boolean LIE_ABOUT_AWB = true;

    /**
     * Create characteristics for a legacy device by mapping the {@code parameters}
@@ -155,7 +157,12 @@ public class LegacyMetadataMapper {
         * info.supportedHardwareLevel
         */
        m.set(INFO_SUPPORTED_HARDWARE_LEVEL, INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED);
        mapStreamConfigs(m, p);

        /*
         * scaler.availableStream*, scaler.available*Durations, sensor.info.maxFrameDuration
         */
        mapScalerStreamConfigs(m, p);

        /*
         * control.ae*
         */
@@ -184,13 +191,23 @@ public class LegacyMetadataMapper {
        mapRequest(m, p);
        // TODO: map other fields

        /*
         * scaler.*
         */
        mapScaler(m, p);

        /*
         * sensor.*
         */
        mapSensor(m, p);

        /*
         * sync.*
         */
        mapSync(m, p);
    }

    private static void mapStreamConfigs(CameraMetadataNative m, Camera.Parameters p) {
    private static void mapScalerStreamConfigs(CameraMetadataNative m, Camera.Parameters p) {

        ArrayList<StreamConfiguration> availableStreamConfigs = new ArrayList<>();
        /*
@@ -202,7 +219,7 @@ public class LegacyMetadataMapper {
         * Use platform-internal format constants here, since StreamConfigurationMap does the
         * remapping to public format constants.
         */
        List<Size> previewSizes = p.getSupportedPreviewSizes();
        List<Camera.Size> previewSizes = p.getSupportedPreviewSizes();
        appendStreamConfig(availableStreamConfigs,
                HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED, previewSizes);
        appendStreamConfig(availableStreamConfigs,
@@ -261,9 +278,31 @@ public class LegacyMetadataMapper {

    @SuppressWarnings({"unchecked"})
    private static void mapControlAe(CameraMetadataNative m, Camera.Parameters p) {
        /*
         * control.aeAvailableAntiBandingModes
         */
        List<String> antiBandingModes = p.getSupportedAntibanding();
        if (antiBandingModes != null && antiBandingModes.size() > 0) { // antibanding is optional
            int[] modes = new int[antiBandingModes.size()];
            int j = 0;
            for (String mode : antiBandingModes) {
                int convertedMode = convertAntiBandingMode(mode);
                if (convertedMode == -1) {
                    Log.w(TAG, "Antibanding mode " + ((mode == null) ? "NULL" : mode) +
                            " not supported, skipping...");
                } else {
                    modes[j++] = convertedMode;
                }
            }
            m.set(CONTROL_AE_AVAILABLE_ANTIBANDING_MODES, Arrays.copyOf(modes, j));
        } else {
            m.set(CONTROL_AE_AVAILABLE_ANTIBANDING_MODES, new int[0]);
        }

        /*
         * control.aeAvailableTargetFpsRanges
         */
        {
            List<int[]> fpsRanges = p.getSupportedPreviewFpsRange();
            if (fpsRanges == null) {
                throw new AssertionError("Supported FPS ranges cannot be null.");
@@ -279,31 +318,12 @@ public class LegacyMetadataMapper {
                        r[Camera.Parameters.PREVIEW_FPS_MAX_INDEX]);
            }
            m.set(CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES, ranges);

        /*
         * control.aeAvailableAntiBandingModes
         */

        List<String> antiBandingModes = p.getSupportedAntibanding();
        int antiBandingModesSize = antiBandingModes.size();
        if (antiBandingModesSize > 0) {
            int[] modes = new int[antiBandingModesSize];
            int j = 0;
            for (String mode : antiBandingModes) {
                int convertedMode = convertAntiBandingMode(mode);
                if (convertedMode == -1) {
                    Log.w(TAG, "Antibanding mode " + ((mode == null) ? "NULL" : mode) +
                            " not supported, skipping...");
                } else {
                    modes[j++] = convertedMode;
                }
            }
            m.set(CONTROL_AE_AVAILABLE_ANTIBANDING_MODES, Arrays.copyOf(modes, j));
        }

        /*
         * control.aeAvailableModes
         */
        {
            List<String> flashModes = p.getSupportedFlashModes();

            String[] flashModeStrings = new String[] {
@@ -319,7 +339,8 @@ public class LegacyMetadataMapper {
                    CONTROL_AE_MODE_ON_AUTO_FLASH,
                    CONTROL_AE_MODE_ON_AUTO_FLASH_REDEYE
            };
        int[] aeAvail = convertStringListToIntArray(flashModes, flashModeStrings, flashModeInts);
            int[] aeAvail = ArrayUtils.convertStringListToIntArray(
                    flashModes, flashModeStrings, flashModeInts);

            // No flash control -> AE is always on
            if (aeAvail == null || aeAvail.length == 0) {
@@ -328,16 +349,10 @@ public class LegacyMetadataMapper {
                };
            }

        if (LIE_ABOUT_FLASH) {
            // TODO: Remove this branch
            Log.w(TAG, "mapControlAe - lying; saying we only support CONTROL_AE_MODE_ON");
            aeAvail = new int[] {
                    CONTROL_AE_MODE_ON
            };
        }

            // Note that AE_MODE_OFF is never available.
            m.set(CONTROL_AE_AVAILABLE_MODES, aeAvail);
        }
    }

    private static void mapControlAwb(CameraMetadataNative m, Camera.Parameters p) {
        if (!LIE_ABOUT_AWB) {
@@ -373,18 +388,11 @@ public class LegacyMetadataMapper {
    private static void mapFlash(CameraMetadataNative m, Camera.Parameters p) {
        boolean flashAvailable = false;
        List<String> supportedFlashModes = p.getSupportedFlashModes();

        if (supportedFlashModes != null) {
            // If only 'OFF' is available, we don't really have flash support
            if (!(supportedFlashModes.contains(Camera.Parameters.FLASH_MODE_OFF) &&
                    supportedFlashModes.size() == 1)) {
                flashAvailable = true;
            }
        }

        if (LIE_ABOUT_FLASH && flashAvailable) {
            // TODO: remove this branch
            Log.w(TAG, "mapFlash - lying; saying we never support flash");
            flashAvailable = false;
            flashAvailable = !ListUtils.listElementsEqualTo(
                    supportedFlashModes, Camera.Parameters.FLASH_MODE_OFF);
        }

        /*
@@ -425,6 +433,35 @@ public class LegacyMetadataMapper {
                (byte)(REQUEST_PIPELINE_MAX_DEPTH_HAL1 + REQUEST_PIPELINE_MAX_DEPTH_OURS));
    }

    private static void mapScaler(CameraMetadataNative m, Parameters p) {
        /*
         * scaler.availableMaxDigitalZoom
         */
        m.set(SCALER_AVAILABLE_MAX_DIGITAL_ZOOM, ParameterUtils.getMaxZoomRatio(p));

        /*
         * scaler.croppingType = CENTER_ONLY
         */
        m.set(SCALER_CROPPING_TYPE, SCALER_CROPPING_TYPE_CENTER_ONLY);
    }

    private static void mapSensor(CameraMetadataNative m, Parameters p) {
        // Use the largest jpeg size (by area) for both active array and pixel array
        Size largestJpegSize = getLargestSupportedJpegSizeByArea(p);
        /*
         * sensor.info.activeArraySize
         */
        {
            Rect activeArrayRect = ParamsUtils.createRect(largestJpegSize);
            m.set(SENSOR_INFO_ACTIVE_ARRAY_SIZE, activeArrayRect);
        }

        /*
         * sensor.info.pixelArraySize
         */
        m.set(SENSOR_INFO_PIXEL_ARRAY_SIZE, largestJpegSize);
    }

    private static void mapSync(CameraMetadataNative m, Parameters p) {
        /*
         * sync.maxLatency
@@ -442,12 +479,17 @@ public class LegacyMetadataMapper {
    }

    /**
     * Returns -1 if the anti-banding mode string is null, or not supported.
     * Convert the ae antibanding mode from api1 into api2.
     *
     * @param mode the api1 mode, {@code null} is allowed and will return {@code -1}.
     *
     * @return The api2 value, or {@code -1} by default if conversion failed
     */
    private static int convertAntiBandingMode(final String mode) {
    private static int convertAntiBandingMode(String mode) {
        if (mode == null) {
            return -1;
        }

        switch (mode) {
            case Camera.Parameters.ANTIBANDING_OFF: {
                return CONTROL_AE_ANTIBANDING_MODE_OFF;
@@ -462,34 +504,27 @@ public class LegacyMetadataMapper {
                return CONTROL_AE_ANTIBANDING_MODE_AUTO;
            }
            default: {
                Log.w(TAG, "convertAntiBandingMode - Unknown antibanding mode " + mode);
                return -1;
            }
        }
    }

    /**
     * Returns null if the anti-banding mode enum is not supported.
     * Convert the ae antibanding mode from api1 into api2.
     *
     * @param mode the api1 mode, {@code null} is allowed and will return {@code MODE_OFF}.
     *
     * @return The api2 value, or {@code MODE_OFF} by default if conversion failed
     */
    private static String convertAntiBandingModeToLegacy(int mode) {
        switch(mode) {
            case CONTROL_AE_ANTIBANDING_MODE_OFF: {
                return Camera.Parameters.ANTIBANDING_OFF;
            }
            case CONTROL_AE_ANTIBANDING_MODE_50HZ: {
                return Camera.Parameters.ANTIBANDING_50HZ;
            }
            case CONTROL_AE_ANTIBANDING_MODE_60HZ: {
                return Camera.Parameters.ANTIBANDING_60HZ;
            }
            case CONTROL_AE_ANTIBANDING_MODE_AUTO: {
                return Camera.Parameters.ANTIBANDING_AUTO;
            }
            default: {
                return null;
            }
        }
    static int convertAntiBandingModeOrDefault(String mode) {
        int antiBandingMode = convertAntiBandingMode(mode);
        if (antiBandingMode == -1) {
            return CONTROL_AE_ANTIBANDING_MODE_OFF;
        }

        return antiBandingMode;
    }

    private static int[] convertAeFpsRangeToLegacy(Range<Integer> fpsRange) {
        int[] legacyFps = new int[2];
@@ -512,164 +547,15 @@ public class LegacyMetadataMapper {
    }

    /**
     * Generate capture result metadata from legacy camera parameters.
     * Set the legacy parameters using the {@link LegacyRequest legacy request}.
     *
     * @param params a {@link Camera.Parameters} object to generate metadata from.
     * @param request the {@link CaptureRequest} used for this result.
     * @param timestamp the timestamp to use for this result in nanoseconds.
     * @return a {@link CameraMetadataNative} object containing result metadata.
     */
    public static CameraMetadataNative convertResultMetadata(Camera.Parameters params,
                                                      CaptureRequest request,
                                                      long timestamp) {
        CameraMetadataNative result = new CameraMetadataNative();

        /*
         * control
         */
        // control.afState
        if (LIE_ABOUT_AF) {
            // TODO: Implement autofocus state machine
            result.set(CaptureResult.CONTROL_AF_MODE, request.get(CaptureRequest.CONTROL_AF_MODE));
        }

        // control.aeState
        if (LIE_ABOUT_AE) {
            // Lie to pass CTS temporarily.
            // TODO: Implement precapture trigger, after which we can report CONVERGED ourselves
            result.set(CaptureResult.CONTROL_AE_STATE,
                    CONTROL_AE_STATE_CONVERGED);

            result.set(CaptureResult.CONTROL_AE_MODE,
                    request.get(CaptureRequest.CONTROL_AE_MODE));
        }

        // control.awbLock
        result.set(CaptureResult.CONTROL_AWB_LOCK, params.getAutoWhiteBalanceLock());

        // control.awbState
        if (LIE_ABOUT_AWB) {
            // Lie to pass CTS temporarily.
            // TODO: CTS needs to be updated not to query this value
            // for LIMITED devices unless its guaranteed to be available.
            result.set(CaptureResult.CONTROL_AWB_STATE,
                    CameraMetadata.CONTROL_AWB_STATE_CONVERGED);
            // TODO: Read the awb mode from parameters instead
            result.set(CaptureResult.CONTROL_AWB_MODE,
                    request.get(CaptureRequest.CONTROL_AWB_MODE));
        }

        /*
         * lens
         */
        // lens.focalLength
        result.set(CaptureResult.LENS_FOCAL_LENGTH, params.getFocalLength());

        /*
         * sensor
         */
        // sensor.timestamp
        result.set(CaptureResult.SENSOR_TIMESTAMP, timestamp);

        // TODO: Remaining result metadata tags conversions.
        return result;
    }

    /**
     * Set the legacy parameters using the request metadata.
     * <p>The legacy request's parameters are changed as a side effect of calling this
     * method.</p>
     *
     * @param request a {@link CaptureRequest} object to generate parameters from.
     * @param params the a {@link Camera.Parameters} to set parameters in.
     * @param request a non-{@code null} legacy request
     */
    public static void convertRequestMetadata(CaptureRequest request,
            /*out*/Camera.Parameters params) {

        /*
         * control.ae*
         */
        // control.aeAntibandingMode
        Integer antiBandingMode = request.get(CaptureRequest.CONTROL_AE_ANTIBANDING_MODE);
        if (antiBandingMode != null) {
            String legacyMode = convertAntiBandingModeToLegacy(antiBandingMode);
            if (legacyMode != null) params.setAntibanding(legacyMode);
        }

        // control.aeTargetFpsRange
        Range<Integer> aeFpsRange = request.get(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE);
        if (aeFpsRange != null) {
            int[] legacyFps = convertAeFpsRangeToLegacy(aeFpsRange);
            params.setPreviewFpsRange(legacyFps[Camera.Parameters.PREVIEW_FPS_MIN_INDEX],
                    legacyFps[Camera.Parameters.PREVIEW_FPS_MAX_INDEX]);
        }

        /*
         * control
         */
        // control.awbLock
        Boolean awbLock = request.get(CaptureRequest.CONTROL_AWB_LOCK);
        params.setAutoWhiteBalanceLock(awbLock == null ? false : awbLock);
    }

    /**
     * Create an int[] from the List<> by using {@code convertFrom} and {@code convertTo}
     * as a one-to-one map (via the index).
     *
     * <p>Strings not appearing in {@code convertFrom} are ignored (with a warning);
     * strings appearing in {@code convertFrom} but not {@code convertTo} are silently
     * dropped.</p>
     *
     * @param list Source list of strings
     * @param convertFrom Conversion list of strings
     * @param convertTo Conversion list of ints
     * @return An array of ints where the values correspond to the ones in {@code convertTo}
     *         or {@code null} if {@code list} was {@code null}
     */
    private static int[] convertStringListToIntArray(
            List<String> list, String[] convertFrom, int[] convertTo) {
        if (list == null) {
            return null;
        }

        List<Integer> convertedList = new ArrayList<>(list.size());

        for (String str : list) {
            int strIndex = getArrayIndex(convertFrom, str);

            // Guard against bad API1 values
            if (strIndex < 0) {
                Log.w(TAG, "Ignoring invalid parameter " + str);
                continue;
            }

            // Ignore values we can't map into (intentional)
            if (strIndex < convertTo.length) {
                convertedList.add(convertTo[strIndex]);
            }
        }

        int[] returnArray = new int[convertedList.size()];
        for (int i = 0; i < returnArray.length; ++i) {
            returnArray[i] = convertedList.get(i);
        }

        return returnArray;
    }

    /** Return the index of {@code needle} in the {@code array}, or else {@code -1} */
    private static <T> int getArrayIndex(T[] array, T needle) {
        if (needle == null) {
            return -1;
        }

        int index = 0;
        for (T elem : array) {
            if (Objects.equals(elem, needle)) {
                return index;
            }
            index++;
        }

        return -1;
    public static void convertRequestMetadata(LegacyRequest request) {
        LegacyRequestMapper.convertRequestMetadata(request);
    }

    /**
+67 −0
Original line number Diff line number Diff line
/*
 * 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.hardware.Camera;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CaptureRequest;
import android.util.Size;

import static com.android.internal.util.Preconditions.*;

/**
 * Hold important data necessary to build the camera1 parameters up from a capture request.
 */
public class LegacyRequest {
    /** Immutable characteristics for the camera corresponding to this request */
    public final CameraCharacteristics characteristics;
    /** Immutable capture request, as requested by the user */
    public final CaptureRequest captureRequest;
    /** Immutable api1 preview buffer size at the time of the request */
    public final Size previewSize;
    /** <em>Mutable</em> camera parameters */
    public final Camera.Parameters parameters;

    /**
     * Create a new legacy request; the parameters are copied.
     *
     * @param characteristics immutable static camera characteristics for this camera
     * @param captureRequest immutable user-defined capture request
     * @param previewSize immutable internal preview size used for {@link Camera#setPreviewSurface}
     * @param parameters the initial camera1 parameter state; (copied) can be mutated
     */
    public LegacyRequest(CameraCharacteristics characteristics, CaptureRequest captureRequest,
            Size previewSize, Camera.Parameters parameters) {
        this.characteristics = checkNotNull(characteristics, "characteristics must not be null");
        this.captureRequest = checkNotNull(captureRequest, "captureRequest must not be null");
        this.previewSize = checkNotNull(previewSize, "previewSize must not be null");
        checkNotNull(parameters, "parameters must not be null");

        this.parameters = Camera.getParametersCopy(parameters);
    }

    /**
     * Update the current parameters in-place to be a copy of the new parameters.
     *
     * @param parameters non-{@code null} parameters for api1 camera
     */
    public void setParameters(Camera.Parameters parameters) {
        checkNotNull(parameters, "parameters must not be null");

        this.parameters.copyFrom(parameters);
    }
}
Loading