LegacyRequestMapper.java revision 396532ffb80f70c336b3564e5bac4c09d3be07ff
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;
33import java.util.Objects;
34
35import static com.android.internal.util.Preconditions.*;
36import static android.hardware.camera2.CaptureRequest.*;
37
38/**
39 * Provide legacy-specific implementations of camera2 CaptureRequest for legacy devices.
40 */
41public class LegacyRequestMapper {
42    private static final String TAG = "LegacyRequestMapper";
43    private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
44
45    /**
46     * Set the legacy parameters using the {@link LegacyRequest legacy request}.
47     *
48     * <p>The legacy request's parameters are changed as a side effect of calling this
49     * method.</p>
50     *
51     * @param legacyRequest a non-{@code null} legacy request
52     */
53    public static void convertRequestMetadata(LegacyRequest legacyRequest) {
54        CameraCharacteristics characteristics = legacyRequest.characteristics;
55        CaptureRequest request = legacyRequest.captureRequest;
56        Size previewSize = legacyRequest.previewSize;
57        Camera.Parameters params = legacyRequest.parameters;
58
59        Rect activeArray = characteristics.get(CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE);
60
61        /*
62         * scaler.cropRegion
63         */
64        ParameterUtils.ZoomData zoomData;
65        {
66            zoomData = ParameterUtils.convertScalerCropRegion(activeArray,
67                    request.get(SCALER_CROP_REGION),
68                    previewSize,
69                    params);
70
71            if (params.isZoomSupported()) {
72                params.setZoom(zoomData.zoomIndex);
73            } else if (VERBOSE) {
74                Log.v(TAG, "convertRequestToMetadata - zoom is not supported");
75            }
76        }
77
78
79        /*
80         * control.ae*
81         */
82        // control.aeAntibandingMode
83        {
84        String legacyMode;
85            Integer antiBandingMode = request.get(CONTROL_AE_ANTIBANDING_MODE);
86            if (antiBandingMode != null) {
87                legacyMode = convertAeAntiBandingModeToLegacy(antiBandingMode);
88            } else {
89                legacyMode = ListUtils.listSelectFirstFrom(params.getSupportedAntibanding(),
90                        new String[] {
91                            Parameters.ANTIBANDING_AUTO,
92                            Parameters.ANTIBANDING_OFF,
93                            Parameters.ANTIBANDING_50HZ,
94                            Parameters.ANTIBANDING_60HZ,
95                        });
96            }
97
98            if (legacyMode != null) {
99                params.setAntibanding(legacyMode);
100            }
101        }
102
103        /*
104         * control.aeRegions, afRegions
105         */
106        {
107            // aeRegions
108            {
109                MeteringRectangle[] aeRegions = request.get(CONTROL_AE_REGIONS);
110                int maxNumMeteringAreas = params.getMaxNumMeteringAreas();
111                List<Camera.Area> meteringAreaList = convertMeteringRegionsToLegacy(
112                        activeArray, zoomData, aeRegions, maxNumMeteringAreas,
113                        /*regionName*/"AE");
114
115                params.setMeteringAreas(meteringAreaList);
116            }
117
118            // afRegions
119            {
120                MeteringRectangle[] afRegions = request.get(CONTROL_AF_REGIONS);
121                int maxNumFocusAreas = params.getMaxNumFocusAreas();
122                List<Camera.Area> focusAreaList = convertMeteringRegionsToLegacy(
123                        activeArray, zoomData, afRegions, maxNumFocusAreas,
124                        /*regionName*/"AF");
125
126                params.setFocusAreas(focusAreaList);
127            }
128        }
129
130        // control.aeTargetFpsRange
131        Range<Integer> aeFpsRange = request.get(CONTROL_AE_TARGET_FPS_RANGE);
132        if (aeFpsRange != null) {
133            int[] legacyFps = convertAeFpsRangeToLegacy(aeFpsRange);
134
135            // TODO - Should we enforce that all HAL1 devices must include (30, 30) FPS range?
136            boolean supported = false;
137            for(int[] range : params.getSupportedPreviewFpsRange()) {
138                if (legacyFps[0] == range[0] && legacyFps[1] == range[1]) {
139                    supported = true;
140                    break;
141                }
142            }
143            if (supported) {
144                params.setPreviewFpsRange(legacyFps[Camera.Parameters.PREVIEW_FPS_MIN_INDEX],
145                        legacyFps[Camera.Parameters.PREVIEW_FPS_MAX_INDEX]);
146                params.setRecordingHint(false);
147            } else {
148                Log.w(TAG, "Unsupported FPS range set [" + legacyFps[0] + "," + legacyFps[1] + "]");
149                params.setRecordingHint(true);
150            }
151        }
152
153        /*
154         * control
155         */
156
157        // control.aeExposureCompensation
158        {
159            Range<Integer> compensationRange =
160                    characteristics.get(CameraCharacteristics.CONTROL_AE_COMPENSATION_RANGE);
161            int compensation = getOrDefault(request,
162                    CONTROL_AE_EXPOSURE_COMPENSATION,
163                    /*defaultValue*/0);
164
165            if (!compensationRange.inRange(compensation)) {
166                Log.w(TAG,
167                        "convertRequestMetadata - control.aeExposureCompensation " +
168                        "is out of range, ignoring value");
169                compensation = 0;
170            }
171
172            params.setExposureCompensation(compensation);
173        }
174
175        // control.aeLock
176        {
177            Boolean aeLock = getIfSupported(request, CONTROL_AE_LOCK, /*defaultValue*/false,
178                    params.isAutoExposureLockSupported(),
179                    /*allowedValue*/false);
180
181            if (aeLock != null) {
182                params.setAutoExposureLock(aeLock);
183            }
184
185            if (VERBOSE) {
186                Log.v(TAG, "convertRequestToMetadata - control.aeLock set to " + aeLock);
187            }
188
189            // TODO: Don't add control.aeLock to availableRequestKeys if it's not supported
190        }
191
192        // control.aeMode, flash.mode
193        mapAeAndFlashMode(request, /*out*/params);
194
195        // control.awbLock
196        {
197            Boolean awbLock = getIfSupported(request, CONTROL_AWB_LOCK, /*defaultValue*/false,
198                    params.isAutoWhiteBalanceLockSupported(),
199                    /*allowedValue*/false);
200
201            if (awbLock != null) {
202                params.setAutoWhiteBalanceLock(awbLock);
203            }
204
205         // TODO: Don't add control.awbLock to availableRequestKeys if it's not supported
206        }
207    }
208
209    private static List<Camera.Area> convertMeteringRegionsToLegacy(
210            Rect activeArray, ParameterUtils.ZoomData zoomData,
211            MeteringRectangle[] meteringRegions, int maxNumMeteringAreas, String regionName) {
212        if (meteringRegions == null || maxNumMeteringAreas <= 0) {
213            if (maxNumMeteringAreas > 0) {
214                return Arrays.asList(ParameterUtils.CAMERA_AREA_DEFAULT);
215            } else {
216                return null;
217            }
218        }
219
220        // Add all non-zero weight regions to the list
221        List<MeteringRectangle> meteringRectangleList = new ArrayList<>();
222        for (MeteringRectangle rect : meteringRegions) {
223            if (rect.getMeteringWeight() != MeteringRectangle.METERING_WEIGHT_DONT_CARE) {
224                meteringRectangleList.add(rect);
225            }
226        }
227
228        // Ignore any regions beyond our maximum supported count
229        int countMeteringAreas =
230                Math.min(maxNumMeteringAreas, meteringRectangleList.size());
231        List<Camera.Area> meteringAreaList = new ArrayList<>(countMeteringAreas);
232
233        for (int i = 0; i < countMeteringAreas; ++i) {
234            MeteringRectangle rect = meteringRectangleList.get(i);
235
236            ParameterUtils.MeteringData meteringData =
237                    ParameterUtils.convertMeteringRectangleToLegacy(activeArray, rect, zoomData);
238            meteringAreaList.add(meteringData.meteringArea);
239        }
240
241        if (maxNumMeteringAreas < meteringRectangleList.size()) {
242            Log.w(TAG,
243                    "convertMeteringRegionsToLegacy - Too many requested " + regionName +
244                            " regions, ignoring all beyond the first " + maxNumMeteringAreas);
245        }
246
247        if (VERBOSE) {
248            Log.v(TAG, "convertMeteringRegionsToLegacy - " + regionName + " areas = "
249                    + ParameterUtils.stringFromAreaList(meteringAreaList));
250        }
251
252        return meteringAreaList;
253    }
254
255    private static void mapAeAndFlashMode(CaptureRequest r, /*out*/Parameters p) {
256        int flashMode = getOrDefault(r, FLASH_MODE, FLASH_MODE_OFF);
257        int aeMode = getOrDefault(r, CONTROL_AE_MODE, CONTROL_AE_MODE_ON);
258
259        List<String> supportedFlashModes = p.getSupportedFlashModes();
260
261        String flashModeSetting = null;
262
263        // Flash is OFF by default, on cameras that support flash
264        if (ListUtils.listContains(supportedFlashModes, Parameters.FLASH_MODE_OFF)) {
265            flashModeSetting = Parameters.FLASH_MODE_OFF;
266        }
267
268        /*
269         * Map all of the control.aeMode* enums, but ignore AE_MODE_OFF since we never support it
270         */
271
272        // Ignore flash.mode controls unless aeMode == ON
273        if (aeMode == CONTROL_AE_MODE_ON) {
274            if (flashMode == FLASH_MODE_TORCH) {
275                    if (ListUtils.listContains(supportedFlashModes, Parameters.FLASH_MODE_TORCH)) {
276                        flashModeSetting = Parameters.FLASH_MODE_TORCH;
277                    } else {
278                        Log.w(TAG, "mapAeAndFlashMode - Ignore flash.mode == TORCH;" +
279                                "camera does not support it");
280                    }
281            } else if (flashMode == FLASH_MODE_SINGLE) {
282                if (ListUtils.listContains(supportedFlashModes, Parameters.FLASH_MODE_ON)) {
283                    flashModeSetting = Parameters.FLASH_MODE_ON;
284                } else {
285                    Log.w(TAG, "mapAeAndFlashMode - Ignore flash.mode == SINGLE;" +
286                            "camera does not support it");
287                }
288            } else {
289                // Use the default FLASH_MODE_OFF
290            }
291        } else if (aeMode == CONTROL_AE_MODE_ON_ALWAYS_FLASH) {
292                if (ListUtils.listContains(supportedFlashModes, Parameters.FLASH_MODE_ON)) {
293                    flashModeSetting = Parameters.FLASH_MODE_ON;
294                } else {
295                    Log.w(TAG, "mapAeAndFlashMode - Ignore control.aeMode == ON_ALWAYS_FLASH;" +
296                            "camera does not support it");
297                }
298        } else if (aeMode == CONTROL_AE_MODE_ON_AUTO_FLASH) {
299            if (ListUtils.listContains(supportedFlashModes, Parameters.FLASH_MODE_AUTO)) {
300                flashModeSetting = Parameters.FLASH_MODE_AUTO;
301            } else {
302                Log.w(TAG, "mapAeAndFlashMode - Ignore control.aeMode == ON_AUTO_FLASH;" +
303                        "camera does not support it");
304            }
305        } else if (aeMode == CONTROL_AE_MODE_ON_AUTO_FLASH_REDEYE) {
306                if (ListUtils.listContains(supportedFlashModes, Parameters.FLASH_MODE_RED_EYE)) {
307                    flashModeSetting = Parameters.FLASH_MODE_RED_EYE;
308                } else {
309                    Log.w(TAG, "mapAeAndFlashMode - Ignore control.aeMode == ON_AUTO_FLASH_REDEYE;"
310                            + "camera does not support it");
311                }
312        } else {
313            // Default to aeMode == ON, flash = OFF
314        }
315
316        if (flashModeSetting != null) {
317            p.setFlashMode(flashModeSetting);
318        }
319
320        if (VERBOSE) {
321                Log.v(TAG,
322                        "mapAeAndFlashMode - set flash.mode (api1) to " + flashModeSetting
323                        + ", requested (api2) " + flashMode
324                        + ", supported (api1) " + ListUtils.listToString(supportedFlashModes));
325        }
326    }
327
328    /**
329     * Returns null if the anti-banding mode enum is not supported.
330     */
331    private static String convertAeAntiBandingModeToLegacy(int mode) {
332        switch (mode) {
333            case CONTROL_AE_ANTIBANDING_MODE_OFF: {
334                return Parameters.ANTIBANDING_OFF;
335            }
336            case CONTROL_AE_ANTIBANDING_MODE_50HZ: {
337                return Parameters.ANTIBANDING_50HZ;
338            }
339            case CONTROL_AE_ANTIBANDING_MODE_60HZ: {
340                return Parameters.ANTIBANDING_60HZ;
341            }
342            case CONTROL_AE_ANTIBANDING_MODE_AUTO: {
343                return Parameters.ANTIBANDING_AUTO;
344            }
345            default: {
346                return null;
347            }
348        }
349    }
350
351    private static int[] convertAeFpsRangeToLegacy(Range<Integer> fpsRange) {
352        int[] legacyFps = new int[2];
353        legacyFps[Parameters.PREVIEW_FPS_MIN_INDEX] = fpsRange.getLower();
354        legacyFps[Parameters.PREVIEW_FPS_MAX_INDEX] = fpsRange.getUpper();
355        return legacyFps;
356    }
357
358    /** Return the value set by the key, or the {@code defaultValue} if no value was set. */
359    private static <T> T getOrDefault(CaptureRequest r, CaptureRequest.Key<T> key, T defaultValue) {
360        checkNotNull(r, "r must not be null");
361        checkNotNull(key, "key must not be null");
362        checkNotNull(defaultValue, "defaultValue must not be null");
363
364        T value = r.get(key);
365        if (value == null) {
366            return defaultValue;
367        } else {
368            return value;
369        }
370    }
371
372    /**
373     * Return {@code null} if the value is not supported, otherwise return the retrieved key's
374     * value from the request (or the default value if it wasn't set).
375     *
376     * <p>If the fetched value in the request is equivalent to {@code allowedValue},
377     * then omit the warning (e.g. turning off AF lock on a camera
378     * that always has the AF lock turned off is a silent no-op), but still return {@code null}.</p>
379     *
380     * <p>Logs a warning to logcat if the key is not supported by api1 camera device.</p.
381     */
382    private static <T> T getIfSupported(
383            CaptureRequest r, CaptureRequest.Key<T> key, T defaultValue, boolean isSupported,
384            T allowedValue) {
385        T val = getOrDefault(r, key, defaultValue);
386
387        if (!isSupported) {
388            if (!Objects.equals(val, allowedValue)) {
389                Log.w(TAG, key.getName() + " is not supported; ignoring requested value " + val);
390            }
391            return null;
392        }
393
394        return val;
395    }
396}
397