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

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

Merge "camera2: (legacy) Implement AF/AE regions for request and result"

parents 329fa240 7ee78d1e
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -85,7 +85,7 @@ public class LegacyMetadataMapper {
     * being set to true.
     */
    static final boolean LIE_ABOUT_AE_STATE = true;
    static final boolean LIE_ABOUT_AE_MAX_REGIONS = true;
    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 = true;
@@ -201,7 +201,7 @@ public class LegacyMetadataMapper {
        /*
         * info.supportedHardwareLevel
         */
        m.set(INFO_SUPPORTED_HARDWARE_LEVEL, INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED);
        m.set(INFO_SUPPORTED_HARDWARE_LEVEL, INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY);

        /*
         * scaler.availableStream*, scaler.available*Durations, sensor.info.maxFrameDuration
+72 −87
Original line number Diff line number Diff line
@@ -41,12 +41,6 @@ public class LegacyRequestMapper {
    private static final String TAG = "LegacyRequestMapper";
    private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);

    /** The default normalized camera area spans the entire size of the preview viewport */
    private static final Camera.Area CAMERA_AREA_DEFAULT =
            new Camera.Area(
                    new Rect(/*left*/-1000, /*top*/-1000, /*right*/1000, /*bottom*/1000),
                    /*weight*/1);

    /**
     * Set the legacy parameters using the {@link LegacyRequest legacy request}.
     *
@@ -61,40 +55,20 @@ public class LegacyRequestMapper {
        Size previewSize = legacyRequest.previewSize;
        Camera.Parameters params = legacyRequest.parameters;

        Rect activeArray = characteristics.get(CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE);

        /*
         * scaler.cropRegion
         */
        ParameterUtils.ZoomData zoomData;
        {
            Rect activeArraySize = characteristics.get(
                    CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE);
            Rect activeArraySizeOnly = new Rect(
                    /*left*/0, /*top*/0,
                    activeArraySize.width(), activeArraySize.height());

            Rect userCropRegion = request.get(SCALER_CROP_REGION);

            if (userCropRegion == null) {
                userCropRegion = activeArraySizeOnly;
            }
            zoomData = ParameterUtils.convertScalerCropRegion(activeArray,
                    request.get(SCALER_CROP_REGION),
                    previewSize,
                    params);

            if (VERBOSE) {
                Log.v(TAG, "convertRequestToMetadata - user crop region was " + userCropRegion);
            }

            Rect reportedCropRegion = new Rect();
            Rect previewCropRegion = new Rect();
            int zoomIndex = ParameterUtils.getClosestAvailableZoomCrop(params, activeArraySizeOnly,
                    previewSize, userCropRegion,
                    /*out*/reportedCropRegion, /*out*/previewCropRegion);

            if (VERBOSE) {
                Log.v(TAG, "convertRequestToMetadata - zoom calculated to: " +
                        "zoomIndex = " + zoomIndex +
                        ", reported crop region = " + reportedCropRegion +
                        ", preview crop region = " + previewCropRegion);
            }
            if (params.isZoomSupported()) {
                params.setZoom(zoomIndex);
                params.setZoom(zoomData.zoomIndex);
            } else if (VERBOSE) {
                Log.v(TAG, "convertRequestToMetadata - zoom is not supported");
            }
@@ -126,49 +100,29 @@ public class LegacyRequestMapper {
        }

        /*
         * control.aeRegions
         * -- ORDER OF EXECUTION MATTERS:
         * -- This must be done after the crop region (zoom) was already set in the parameters
         * control.aeRegions, afRegions
         */
        {
            // aeRegions
            {
                MeteringRectangle[] aeRegions = request.get(CONTROL_AE_REGIONS);
                int maxNumMeteringAreas = params.getMaxNumMeteringAreas();
            if (aeRegions !=  null && maxNumMeteringAreas > 0) {
                // Add all non-zero weight regions to the list
                List<MeteringRectangle> meteringRectangleList = new ArrayList<>();
                for (MeteringRectangle rect : aeRegions) {
                    if (rect.getMeteringWeight() != MeteringRectangle.METERING_WEIGHT_DONT_CARE) {
                        meteringRectangleList.add(rect);
                    }
                }
                List<Camera.Area> meteringAreaList = convertMeteringRegionsToLegacy(
                        activeArray, zoomData, aeRegions, maxNumMeteringAreas,
                        /*regionName*/"AE");

                // Ignore any regions beyond our maximum supported count
                int countMeteringAreas =
                        Math.min(maxNumMeteringAreas, meteringRectangleList.size());
                List<Camera.Area> meteringAreaList = new ArrayList<>(countMeteringAreas);
                Rect activeArray = characteristics.get(
                        CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE);

                for (int i = 0; i < countMeteringAreas; ++i) {
                    MeteringRectangle rect = meteringRectangleList.get(i);

                    Camera.Area area = convertMeteringRectangleToLegacy(activeArray, rect);
                    meteringAreaList.add(area);
                params.setMeteringAreas(meteringAreaList);
            }

                params.setMeteringAreas(meteringAreaList);
            // afRegions
            {
                MeteringRectangle[] afRegions = request.get(CONTROL_AF_REGIONS);
                int maxNumFocusAreas = params.getMaxNumFocusAreas();
                List<Camera.Area> focusAreaList = convertMeteringRegionsToLegacy(
                        activeArray, zoomData, afRegions, maxNumFocusAreas,
                        /*regionName*/"AF");

                if (maxNumMeteringAreas < meteringRectangleList.size()) {
                    Log.w(TAG,
                            "convertRequestToMetadata - Too many requested AE regions, "
                                    + "ignoring all beyond the first " + maxNumMeteringAreas);
                }
            } else {
                if (maxNumMeteringAreas > 0) {
                    params.setMeteringAreas(Arrays.asList(CAMERA_AREA_DEFAULT));
                } else {
                    params.setMeteringAreas(null);
                }
                params.setFocusAreas(focusAreaList);
            }
        }

@@ -208,6 +162,52 @@ public class LegacyRequestMapper {

    }

    private static List<Camera.Area> convertMeteringRegionsToLegacy(
            Rect activeArray, ParameterUtils.ZoomData zoomData,
            MeteringRectangle[] meteringRegions, int maxNumMeteringAreas, String regionName) {
        if (meteringRegions == null || maxNumMeteringAreas <= 0) {
            if (maxNumMeteringAreas > 0) {
                return Arrays.asList(ParameterUtils.CAMERA_AREA_DEFAULT);
            } else {
                return null;
            }
        }

        // Add all non-zero weight regions to the list
        List<MeteringRectangle> meteringRectangleList = new ArrayList<>();
        for (MeteringRectangle rect : meteringRegions) {
            if (rect.getMeteringWeight() != MeteringRectangle.METERING_WEIGHT_DONT_CARE) {
                meteringRectangleList.add(rect);
            }
        }

        // Ignore any regions beyond our maximum supported count
        int countMeteringAreas =
                Math.min(maxNumMeteringAreas, meteringRectangleList.size());
        List<Camera.Area> meteringAreaList = new ArrayList<>(countMeteringAreas);

        for (int i = 0; i < countMeteringAreas; ++i) {
            MeteringRectangle rect = meteringRectangleList.get(i);

            ParameterUtils.MeteringData meteringData =
                    ParameterUtils.convertMeteringRectangleToLegacy(activeArray, rect, zoomData);
            meteringAreaList.add(meteringData.meteringArea);
        }

        if (maxNumMeteringAreas < meteringRectangleList.size()) {
            Log.w(TAG,
                    "convertMeteringRegionsToLegacy - Too many requested " + regionName +
                            " regions, ignoring all beyond the first " + maxNumMeteringAreas);
        }

        if (VERBOSE) {
            Log.v(TAG, "convertMeteringRegionsToLegacy - " + regionName + " areas = "
                    + ParameterUtils.stringFromAreaList(meteringAreaList));
        }

        return meteringAreaList;
    }

    private static void mapAeAndFlashMode(CaptureRequest r, /*out*/Parameters p) {
        int flashMode = getOrDefault(r, FLASH_MODE, FLASH_MODE_OFF);
        int aeMode = getOrDefault(r, CONTROL_AE_MODE, CONTROL_AE_MODE_ON);
@@ -275,21 +275,6 @@ public class LegacyRequestMapper {
        return legacyFps;
    }

    private static Camera.Area convertMeteringRectangleToLegacy(
            Rect activeArray, MeteringRectangle meteringRect) {
        // TODO: use matrix transform magic here

        Rect rect = new Rect();

        // TODO: Take the cropRegion (zooming) into account here

        // TODO: crop to be within [-1000, 1000] range for both X and Y if the values end up too big
        //return new Camera.Area(rect, meteringRect.getMeteringWeight());

        Log.w(TAG, "convertMeteringRectangleToLegacy - TODO: support metering rects");
        return CAMERA_AREA_DEFAULT;
    }

    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");
+63 −33
Original line number Diff line number Diff line
@@ -24,9 +24,16 @@ 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.legacy.ParameterUtils.WeightedRectangle;
import android.hardware.camera2.legacy.ParameterUtils.ZoomData;
import android.hardware.camera2.params.MeteringRectangle;
import android.hardware.camera2.utils.ListUtils;
import android.util.Log;
import android.util.Size;

import java.util.ArrayList;
import java.util.List;

import static com.android.internal.util.Preconditions.*;
import static android.hardware.camera2.CaptureResult.*;

@@ -54,6 +61,11 @@ public class LegacyResultMapper {

        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);

        /*
         * control
         */
@@ -66,7 +78,7 @@ public class LegacyResultMapper {
        /*
         * control.ae*
         */
        mapAe(result, /*out*/params);
        mapAe(result, activeArraySize, zoomData, /*out*/params);

        // control.awbLock
        result.set(CaptureResult.CONTROL_AWB_LOCK, params.getAutoWhiteBalanceLock());
@@ -92,7 +104,7 @@ public class LegacyResultMapper {
        /*
         * scaler
         */
        mapScaler(result, characteristics, request, previewSize, params);
        mapScaler(result, zoomData, /*out*/params);

        /*
         * sensor
@@ -104,7 +116,8 @@ public class LegacyResultMapper {
        return result;
    }

    private static void mapAe(CameraMetadataNative m, /*out*/Parameters p) {
    private static void mapAe(CameraMetadataNative m,
            Rect activeArray, ZoomData zoomData, /*out*/Parameters p) {
        // control.aeAntiBandingMode
        {
            int antiBandingMode = LegacyMetadataMapper.convertAntiBandingModeOrDefault(
@@ -123,15 +136,52 @@ public class LegacyResultMapper {
        }

        // control.aeRegions
        {
            if (VERBOSE) {
                String meteringAreas = p.get("metering-areas");
                Log.v(TAG, "mapAe - parameter dump; metering-areas: " + meteringAreas);
            }

        /*
         * TODO: Use the *resulting* crop region to calculate intersection with
         * metering region
         *
         * Report the sensor-relative metering region in the result even
         * if that's not actually the real thing (similar to how we do it
         * for zooming)
         */
            MeteringRectangle[] meteringRectArray = getMeteringRectangles(activeArray,
                    zoomData, p.getMeteringAreas(), "AE");

            m.set(CONTROL_AE_REGIONS, meteringRectArray);
        }

        // control.afRegions
        {
            if (VERBOSE) {
                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 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 (VERBOSE) {
            Log.v(TAG,
                    "Metering rectangles for " + regionName + ": "
                     + ListUtils.listToString(meteringRectList));
        }

        return meteringRectList.toArray(new MeteringRectangle[0]);
    }


@@ -169,33 +219,13 @@ public class LegacyResultMapper {

    /** Map results for scaler.* */
    private static void mapScaler(CameraMetadataNative m,
            CameraCharacteristics characteristics,
            CaptureRequest request,
            Size previewSize,
            ZoomData zoomData,
            /*out*/Parameters p) {
        /*
         * scaler.cropRegion
         */
        {
            Rect activeArraySize = characteristics.get(
                    CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE);
            Rect activeArraySizeOnly = new Rect(
                    /*left*/0, /*top*/0,
                    activeArraySize.width(), activeArraySize.height());

            Rect userCropRegion = request.get(CaptureRequest.SCALER_CROP_REGION);

            if (userCropRegion == null) {
                userCropRegion = activeArraySizeOnly;
            }

            Rect reportedCropRegion = new Rect();
            Rect previewCropRegion = new Rect();
            ParameterUtils.getClosestAvailableZoomCrop(p, activeArraySizeOnly,
                    previewSize, userCropRegion,
                    /*out*/reportedCropRegion, /*out*/previewCropRegion);

            m.set(SCALER_CROP_REGION, reportedCropRegion);
            m.set(SCALER_CROP_REGION, zoomData.reportedCrop);
        }
    }
}
+421 −0

File changed.

Preview size limit exceeded, changes collapsed.

+19 −3
Original line number Diff line number Diff line
@@ -100,6 +100,8 @@ public final class MeteringRectangle {
    /**
     * Create a new metering rectangle.
     *
     * <p>The point {@code xy}'s data is copied; the reference is not retained.</p>
     *
     * @param xy a non-{@code null} {@link Point} with both x,y >= 0
     * @param dimensions a non-{@code null} {@link android.util.Size Size} with width, height >= 0
     * @param meteringWeight weight >= 0
@@ -121,6 +123,8 @@ public final class MeteringRectangle {
    /**
     * Create a new metering rectangle.
     *
     * <p>The rectangle data is copied; the reference is not retained.</p>
     *
     * @param rect a non-{@code null} rectangle with all x,y,w,h dimensions >= 0
     * @param meteringWeight weight >= 0
     *
@@ -185,7 +189,7 @@ public final class MeteringRectangle {
    /**
     * Convenience method to create the upper-left (X,Y) coordinate as a {@link Point}.
     *
     * @return {@code (x,y)} point with both x,y >= 0
     * @return a new {@code (x,y)} {@link Point} with both x,y >= 0
     */
    public Point getUpperLeftPoint() {
        return new Point(mX, mY);
@@ -196,7 +200,7 @@ public final class MeteringRectangle {
     *
     * <p>This strips away the X,Y,weight from the rectangle.</p>
     *
     * @return a Size with non-negative width and height
     * @return a new {@link Size} with non-negative width and height
     */
    public Size getSize() {
        return new Size(mWidth, mHeight);
@@ -207,7 +211,7 @@ public final class MeteringRectangle {
     *
     * <p>This strips away the weight from the rectangle.</p>
     *
     * @return a {@link Rect} with non-negative x1, y1, x2, y2
     * @return a new {@link Rect} with non-negative x1, y1, x2, y2
     */
    public Rect getRect() {
        return new Rect(mX, mY, mX + mWidth, mY + mHeight);
@@ -250,4 +254,16 @@ public final class MeteringRectangle {
    public int hashCode() {
        return HashCodeHelpers.hashCode(mX, mY, mWidth, mHeight, mWeight);
    }

    /**
     * Return the metering rectangle as a string representation
     * {@code "(x:%d, y:%d, w:%d, h:%d, wt:%d)"} where each {@code %d} respectively represents
     * the x, y, width, height, and weight points.
     *
     * @return string representation of the metering rectangle
     */
    @Override
    public String toString() {
        return String.format("(x:%d, y:%d, w:%d, h:%d, wt:%d)", mX, mY, mWidth, mHeight, mWeight);
    }
}
Loading