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

Commit bdb899d7 authored by Igor Murashkin's avatar Igor Murashkin Committed by Android (Google) Code Review
Browse files

Merge changes I5c1008cf,I911616c9

* changes:
  camera2: (legacy) Fixes for CaptureRequestTest#testAeModeAndLock
  camera2: (Legacy) Implement ae compensation step ranges and ae/awb locks
parents c5ea3df1 49a1d7b7
Loading
Loading
Loading
Loading
+23 −2
Original line number Diff line number Diff line
@@ -84,10 +84,11 @@ public class LegacyMetadataMapper {
     * TODO: Remove these constants and strip out any code that previously relied on them
     * being set to true.
     */
    static final boolean LIE_ABOUT_AE_STATE = true;
    static final boolean LIE_ABOUT_AE_STATE = false;
    static final boolean LIE_ABOUT_AE_MAX_REGIONS = false;
    static final boolean LIE_ABOUT_AF = true;
    static final boolean LIE_ABOUT_AF_MAX_REGIONS = true;
    static final boolean LIE_ABOUT_AWB_STATE = false;
    static final boolean LIE_ABOUT_AWB = true;

    /**
@@ -330,16 +331,17 @@ public class LegacyMetadataMapper {
            List<String> flashModes = p.getSupportedFlashModes();

            String[] flashModeStrings = new String[] {
                    Camera.Parameters.FLASH_MODE_OFF,
                    Camera.Parameters.FLASH_MODE_AUTO,
                    Camera.Parameters.FLASH_MODE_ON,
                    Camera.Parameters.FLASH_MODE_RED_EYE,
                    // Map these manually
                    Camera.Parameters.FLASH_MODE_TORCH,
                    Camera.Parameters.FLASH_MODE_OFF,
            };
            int[] flashModeInts = new int[] {
                    CONTROL_AE_MODE_ON,
                    CONTROL_AE_MODE_ON_AUTO_FLASH,
                    CONTROL_AE_MODE_ON_ALWAYS_FLASH,
                    CONTROL_AE_MODE_ON_AUTO_FLASH_REDEYE
            };
            int[] aeAvail = ArrayUtils.convertStringListToIntArray(
@@ -355,6 +357,25 @@ public class LegacyMetadataMapper {
            // Note that AE_MODE_OFF is never available.
            m.set(CONTROL_AE_AVAILABLE_MODES, aeAvail);
        }

        /*
         * control.aeCompensationRanges
         */
        {
            int min = p.getMinExposureCompensation();
            int max = p.getMaxExposureCompensation();

            m.set(CONTROL_AE_COMPENSATION_RANGE, Range.create(min, max));
        }

        /*
         * control.aeCompensationStep
         */
        {
            float step = p.getExposureCompensationStep();

            m.set(CONTROL_AE_COMPENSATION_STEP, ParamsUtils.createRational(step));
        }
    }

    private static void mapControlAwb(CameraMetadataNative m, Camera.Parameters p) {
+72 −2
Original line number Diff line number Diff line
@@ -30,6 +30,7 @@ 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.CaptureRequest.*;
@@ -153,13 +154,56 @@ public class LegacyRequestMapper {
         * control
         */

        // control.aeExposureCompensation
        {
            Range<Integer> compensationRange =
                    characteristics.get(CameraCharacteristics.CONTROL_AE_COMPENSATION_RANGE);
            int compensation = getOrDefault(request,
                    CONTROL_AE_EXPOSURE_COMPENSATION,
                    /*defaultValue*/0);

            if (!compensationRange.inRange(compensation)) {
                Log.w(TAG,
                        "convertRequestMetadata - control.aeExposureCompensation " +
                        "is out of range, ignoring value");
                compensation = 0;
            }

            params.setExposureCompensation(compensation);
        }

        // control.aeLock
        {
            Boolean aeLock = getIfSupported(request, CONTROL_AE_LOCK, /*defaultValue*/false,
                    params.isAutoExposureLockSupported(),
                    /*allowedValue*/false);

            if (aeLock != null) {
                params.setAutoExposureLock(aeLock);
            }

            if (VERBOSE) {
                Log.v(TAG, "convertRequestToMetadata - control.aeLock set to " + aeLock);
            }

            // TODO: Don't add control.aeLock to availableRequestKeys if it's not supported
        }

        // control.aeMode, flash.mode
        mapAeAndFlashMode(request, /*out*/params);

        // control.awbLock
        Boolean awbLock = request.get(CONTROL_AWB_LOCK);
        params.setAutoWhiteBalanceLock(awbLock == null ? false : awbLock);
        {
            Boolean awbLock = getIfSupported(request, CONTROL_AWB_LOCK, /*defaultValue*/false,
                    params.isAutoWhiteBalanceLockSupported(),
                    /*allowedValue*/false);

            if (awbLock != null) {
                params.setAutoWhiteBalanceLock(awbLock);
            }

         // TODO: Don't add control.awbLock to availableRequestKeys if it's not supported
        }
    }

    private static List<Camera.Area> convertMeteringRegionsToLegacy(
@@ -275,6 +319,7 @@ public class LegacyRequestMapper {
        return legacyFps;
    }

    /** Return the value set by the key, or the {@code defaultValue} if no value was set. */
    private static <T> T getOrDefault(CaptureRequest r, CaptureRequest.Key<T> key, T defaultValue) {
        checkNotNull(r, "r must not be null");
        checkNotNull(key, "key must not be null");
@@ -287,4 +332,29 @@ public class LegacyRequestMapper {
            return value;
        }
    }

    /**
     * Return {@code null} if the value is not supported, otherwise return the retrieved key's
     * value from the request (or the default value if it wasn't set).
     *
     * <p>If the fetched value in the request is equivalent to {@code allowedValue},
     * then omit the warning (e.g. turning off AF lock on a camera
     * that always has the AF lock turned off is a silent no-op), but still return {@code null}.</p>
     *
     * <p>Logs a warning to logcat if the key is not supported by api1 camera device.</p.
     */
    private static <T> T getIfSupported(
            CaptureRequest r, CaptureRequest.Key<T> key, T defaultValue, boolean isSupported,
            T allowedValue) {
        T val = getOrDefault(r, key, defaultValue);

        if (!isSupported) {
            if (!Objects.equals(val, allowedValue)) {
                Log.w(TAG, key.getName() + " is not supported; ignoring requested value " + val);
            }
            return null;
        }

        return val;
    }
}
+38 −3
Original line number Diff line number Diff line
@@ -28,7 +28,9 @@ 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.Rational;
import android.util.Size;

import java.util.ArrayList;
@@ -78,19 +80,22 @@ public class LegacyResultMapper {
        /*
         * control.ae*
         */
        mapAe(result, activeArraySize, zoomData, /*out*/params);
        mapAe(result, request, activeArraySize, zoomData, /*out*/params);

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

        // control.awbState
        if (LegacyMetadataMapper.LIE_ABOUT_AWB) {
        if (LegacyMetadataMapper.LIE_ABOUT_AWB_STATE) {
            // 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
        }

        if (LegacyMetadataMapper.LIE_ABOUT_AWB) {
            result.set(CaptureResult.CONTROL_AWB_MODE,
                    request.get(CaptureRequest.CONTROL_AWB_MODE));
        }
@@ -117,7 +122,7 @@ public class LegacyResultMapper {
    }

    private static void mapAe(CameraMetadataNative m,
            Rect activeArray, ZoomData zoomData, /*out*/Parameters p) {
            CaptureRequest request, Rect activeArray, ZoomData zoomData, /*out*/Parameters p) {
        // control.aeAntiBandingMode
        {
            int antiBandingMode = LegacyMetadataMapper.convertAntiBandingModeOrDefault(
@@ -125,6 +130,29 @@ public class LegacyResultMapper {
            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 (VERBOSE) {
                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
        mapAeAndFlashMode(m, p);

@@ -160,6 +188,13 @@ public class LegacyResultMapper {

            m.set(CONTROL_AF_REGIONS, meteringRectArray);
        }

        // control.awbLock
        {
            boolean lock = p.isAutoWhiteBalanceLockSupported() ?
                    p.getAutoWhiteBalanceLock() : false;
            m.set(CONTROL_AWB_LOCK, lock);
        }
    }

    private static MeteringRectangle[] getMeteringRectangles(Rect activeArray, ZoomData zoomData,
+53 −0
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ package android.hardware.camera2.utils;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.graphics.RectF;
import android.util.Rational;
import android.util.Size;

import static com.android.internal.util.Preconditions.*;
@@ -28,6 +29,9 @@ import static com.android.internal.util.Preconditions.*;
 */
public class ParamsUtils {

    /** Arbitrary denominator used to estimate floats as rationals */
    private static final int RATIONAL_DENOMINATOR = 1000000; // 1million

    /**
     * Create a {@link Rect} from a {@code Size} by creating a new rectangle with
     * left, top = {@code (0, 0)} and right, bottom = {@code (width, height)}
@@ -103,6 +107,55 @@ public class ParamsUtils {
        return new Size(rect.width(), rect.height());
    }

    /**
     * Create a {@link Rational} value by approximating the float value as a rational.
     *
     * <p>Floating points too large to be represented as an integer will be converted to
     * to {@link Integer#MAX_VALUE}; floating points too small to be represented as an integer
     * will be converted to {@link Integer#MIN_VALUE}.</p>
     *
     * @param value a floating point value
     * @return the rational representation of the float
     */
    public static Rational createRational(float value) {
        if (Float.isNaN(value)) {
            return Rational.NaN;
        } else if (value == Float.POSITIVE_INFINITY) {
            return Rational.POSITIVE_INFINITY;
        } else if (value == Float.NEGATIVE_INFINITY) {
            return Rational.NEGATIVE_INFINITY;
        } else if (value == 0.0f) {
            return Rational.ZERO;
        }

        // normal finite value: approximate it

        /*
         * Start out trying to approximate with denominator = 1million,
         * but if the numerator doesn't fit into an Int then keep making the denominator
         * smaller until it does.
         */
        int den = RATIONAL_DENOMINATOR;
        float numF;
        do {
            numF = value * den;

            if ((numF > Integer.MIN_VALUE && numF < Integer.MAX_VALUE) || (den == 1)) {
                break;
            }

            den /= 10;
        } while (true);

        /*
         *  By float -> int narrowing conversion in JLS 5.1.3, this will automatically become
         *  MIN_VALUE or MAX_VALUE if numF is too small/large to be represented by an integer
         */
        int num = (int) numF;

        return new Rational(num, den);
     }

    /**
     * Convert an integral rectangle ({@code source}) to a floating point rectangle
     * ({@code destination}) in-place.