LegacyRequestMapper.java revision df6242e374b81e802a38cb891477f05d3e4b3cbc
1/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.hardware.camera2.legacy;
18
19import android.graphics.Rect;
20import android.hardware.Camera;
21import android.hardware.Camera.Parameters;
22import android.hardware.camera2.CameraCharacteristics;
23import android.hardware.camera2.CaptureRequest;
24import android.hardware.camera2.params.MeteringRectangle;
25import android.hardware.camera2.utils.ListUtils;
26import android.util.Log;
27import android.util.Range;
28import android.util.Size;
29
30import java.util.ArrayList;
31import java.util.Arrays;
32import java.util.List;
33
34import static com.android.internal.util.Preconditions.*;
35import static android.hardware.camera2.CaptureRequest.*;
36
37/**
38 * Provide legacy-specific implementations of camera2 CaptureRequest for legacy devices.
39 */
40public class LegacyRequestMapper {
41    private static final String TAG = "LegacyRequestMapper";
42    private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
43
44    /** The default normalized camera area spans the entire size of the preview viewport */
45    private static final Camera.Area CAMERA_AREA_DEFAULT =
46            new Camera.Area(
47                    new Rect(/*left*/-1000, /*top*/-1000, /*right*/1000, /*bottom*/1000),
48                    /*weight*/1);
49
50    /**
51     * Set the legacy parameters using the {@link LegacyRequest legacy request}.
52     *
53     * <p>The legacy request's parameters are changed as a side effect of calling this
54     * method.</p>
55     *
56     * @param legacyRequest a non-{@code null} legacy request
57     */
58    public static void convertRequestMetadata(LegacyRequest legacyRequest) {
59        CameraCharacteristics characteristics = legacyRequest.characteristics;
60        CaptureRequest request = legacyRequest.captureRequest;
61        Size previewSize = legacyRequest.previewSize;
62        Camera.Parameters params = legacyRequest.parameters;
63
64        /*
65         * scaler.cropRegion
66         */
67        {
68            Rect activeArraySize = characteristics.get(
69                    CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE);
70            Rect activeArraySizeOnly = new Rect(
71                    /*left*/0, /*top*/0,
72                    activeArraySize.width(), activeArraySize.height());
73
74            Rect userCropRegion = request.get(SCALER_CROP_REGION);
75
76            if (userCropRegion == null) {
77                userCropRegion = activeArraySizeOnly;
78            }
79
80            if (VERBOSE) {
81                Log.v(TAG, "convertRequestToMetadata - user crop region was " + userCropRegion);
82            }
83
84            Rect reportedCropRegion = new Rect();
85            Rect previewCropRegion = new Rect();
86            int zoomIndex = ParameterUtils.getClosestAvailableZoomCrop(params, activeArraySizeOnly,
87                    previewSize, userCropRegion,
88                    /*out*/reportedCropRegion, /*out*/previewCropRegion);
89
90            if (VERBOSE) {
91                Log.v(TAG, "convertRequestToMetadata - zoom calculated to: " +
92                        "zoomIndex = " + zoomIndex +
93                        ", reported crop region = " + reportedCropRegion +
94                        ", preview crop region = " + previewCropRegion);
95            }
96            if (params.isZoomSupported()) {
97                params.setZoom(zoomIndex);
98            } else if (VERBOSE) {
99                Log.v(TAG, "convertRequestToMetadata - zoom is not supported");
100            }
101        }
102
103
104        /*
105         * control.ae*
106         */
107        // control.aeAntibandingMode
108        {
109        String legacyMode;
110            Integer antiBandingMode = request.get(CONTROL_AE_ANTIBANDING_MODE);
111            if (antiBandingMode != null) {
112                legacyMode = convertAeAntiBandingModeToLegacy(antiBandingMode);
113            } else {
114                legacyMode = ListUtils.listSelectFirstFrom(params.getSupportedAntibanding(),
115                        new String[] {
116                            Parameters.ANTIBANDING_AUTO,
117                            Parameters.ANTIBANDING_OFF,
118                            Parameters.ANTIBANDING_50HZ,
119                            Parameters.ANTIBANDING_60HZ,
120                        });
121            }
122
123            if (legacyMode != null) {
124                params.setAntibanding(legacyMode);
125            }
126        }
127
128        /*
129         * control.aeRegions
130         * -- ORDER OF EXECUTION MATTERS:
131         * -- This must be done after the crop region (zoom) was already set in the parameters
132         */
133        {
134            MeteringRectangle[] aeRegions = request.get(CONTROL_AE_REGIONS);
135            int maxNumMeteringAreas = params.getMaxNumMeteringAreas();
136            if (aeRegions !=  null && maxNumMeteringAreas > 0) {
137                // Add all non-zero weight regions to the list
138                List<MeteringRectangle> meteringRectangleList = new ArrayList<>();
139                for (MeteringRectangle rect : aeRegions) {
140                    if (rect.getMeteringWeight() != MeteringRectangle.METERING_WEIGHT_DONT_CARE) {
141                        meteringRectangleList.add(rect);
142                    }
143                }
144
145                // Ignore any regions beyond our maximum supported count
146                int countMeteringAreas =
147                        Math.min(maxNumMeteringAreas, meteringRectangleList.size());
148                List<Camera.Area> meteringAreaList = new ArrayList<>(countMeteringAreas);
149                Rect activeArray = characteristics.get(
150                        CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE);
151
152                for (int i = 0; i < countMeteringAreas; ++i) {
153                    MeteringRectangle rect = meteringRectangleList.get(i);
154
155                    Camera.Area area = convertMeteringRectangleToLegacy(activeArray, rect);
156                    meteringAreaList.add(area);
157                }
158
159                params.setMeteringAreas(meteringAreaList);
160
161                if (maxNumMeteringAreas < meteringRectangleList.size()) {
162                    Log.w(TAG,
163                            "convertRequestToMetadata - Too many requested AE regions, "
164                                    + "ignoring all beyond the first " + maxNumMeteringAreas);
165                }
166            } else {
167                if (maxNumMeteringAreas > 0) {
168                    params.setMeteringAreas(Arrays.asList(CAMERA_AREA_DEFAULT));
169                } else {
170                    params.setMeteringAreas(null);
171                }
172            }
173        }
174
175        // control.aeTargetFpsRange
176        Range<Integer> aeFpsRange = request.get(CONTROL_AE_TARGET_FPS_RANGE);
177        if (aeFpsRange != null) {
178            int[] legacyFps = convertAeFpsRangeToLegacy(aeFpsRange);
179            params.setPreviewFpsRange(legacyFps[Parameters.PREVIEW_FPS_MIN_INDEX],
180                    legacyFps[Parameters.PREVIEW_FPS_MAX_INDEX]);
181        }
182
183        /*
184         * control
185         */
186
187        // control.aeMode, flash.mode
188        mapAeAndFlashMode(request, /*out*/params);
189
190        // control.awbLock
191        Boolean awbLock = request.get(CONTROL_AWB_LOCK);
192        params.setAutoWhiteBalanceLock(awbLock == null ? false : awbLock);
193
194    }
195
196    private static void mapAeAndFlashMode(CaptureRequest r, /*out*/Parameters p) {
197        int flashMode = getOrDefault(r, FLASH_MODE, FLASH_MODE_OFF);
198        int aeMode = getOrDefault(r, CONTROL_AE_MODE, CONTROL_AE_MODE_ON);
199
200        List<String> supportedFlashModes = p.getSupportedFlashModes();
201
202        /*
203         * Map all of the control.aeMode* enums, but ignore AE_MODE_OFF since we never support it
204         */
205
206        // Ignore flash.mode controls unless aeMode == ON
207        if (aeMode == CONTROL_AE_MODE_ON) {
208            // Flash is OFF by default
209            p.setFlashMode(Parameters.FLASH_MODE_OFF);
210
211            if (flashMode == FLASH_MODE_TORCH &&
212                    ListUtils.listContains(supportedFlashModes, Parameters.FLASH_MODE_TORCH)) {
213                p.setFlashMode(Parameters.FLASH_MODE_TORCH);
214            } else if (flashMode == FLASH_MODE_SINGLE &&
215                    ListUtils.listContains(supportedFlashModes, Parameters.FLASH_MODE_ON)) {
216                p.setFlashMode(Parameters.FLASH_MODE_ON);
217            }
218        } else if (aeMode == CONTROL_AE_MODE_ON_ALWAYS_FLASH &&
219                ListUtils.listContains(supportedFlashModes, Parameters.FLASH_MODE_ON)) {
220            p.setFlashMode(Parameters.FLASH_MODE_ON);
221        } else if (aeMode == CONTROL_AE_MODE_ON_AUTO_FLASH &&
222                ListUtils.listContains(supportedFlashModes, Parameters.FLASH_MODE_AUTO)) {
223            p.setFlashMode(Parameters.FLASH_MODE_AUTO);
224        } else if (aeMode == CONTROL_AE_MODE_ON_AUTO_FLASH_REDEYE &&
225                ListUtils.listContains(supportedFlashModes, Parameters.FLASH_MODE_RED_EYE)) {
226            p.setFlashMode(Parameters.FLASH_MODE_RED_EYE);
227        } else {
228            // Default to aeMode == ON, flash = OFF
229            p.setFlashMode(Parameters.FLASH_MODE_OFF);
230        }
231    }
232
233    /**
234     * Returns null if the anti-banding mode enum is not supported.
235     */
236    private static String convertAeAntiBandingModeToLegacy(int mode) {
237        switch (mode) {
238            case CONTROL_AE_ANTIBANDING_MODE_OFF: {
239                return Parameters.ANTIBANDING_OFF;
240            }
241            case CONTROL_AE_ANTIBANDING_MODE_50HZ: {
242                return Parameters.ANTIBANDING_50HZ;
243            }
244            case CONTROL_AE_ANTIBANDING_MODE_60HZ: {
245                return Parameters.ANTIBANDING_60HZ;
246            }
247            case CONTROL_AE_ANTIBANDING_MODE_AUTO: {
248                return Parameters.ANTIBANDING_AUTO;
249            }
250            default: {
251                return null;
252            }
253        }
254    }
255
256    private static int[] convertAeFpsRangeToLegacy(Range<Integer> fpsRange) {
257        int[] legacyFps = new int[2];
258        legacyFps[Parameters.PREVIEW_FPS_MIN_INDEX] = fpsRange.getLower();
259        legacyFps[Parameters.PREVIEW_FPS_MAX_INDEX] = fpsRange.getUpper();
260        return legacyFps;
261    }
262
263    private static Camera.Area convertMeteringRectangleToLegacy(
264            Rect activeArray, MeteringRectangle meteringRect) {
265        // TODO: use matrix transform magic here
266
267        Rect rect = new Rect();
268
269        // TODO: Take the cropRegion (zooming) into account here
270
271        // TODO: crop to be within [-1000, 1000] range for both X and Y if the values end up too big
272        //return new Camera.Area(rect, meteringRect.getMeteringWeight());
273
274        Log.w(TAG, "convertMeteringRectangleToLegacy - TODO: support metering rects");
275        return CAMERA_AREA_DEFAULT;
276    }
277
278    private static <T> T getOrDefault(CaptureRequest r, CaptureRequest.Key<T> key, T defaultValue) {
279        checkNotNull(r, "r must not be null");
280        checkNotNull(key, "key must not be null");
281        checkNotNull(defaultValue, "defaultValue must not be null");
282
283        T value = r.get(key);
284        if (value == null) {
285            return defaultValue;
286        } else {
287            return value;
288        }
289    }
290}
291