LegacyRequestMapper.java revision 8c4486c14134e81999c8e732fcee3bd7e89ffb69
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.hardware.camera2.utils.ParamsUtils;
27import android.util.Log;
28import android.util.Range;
29import android.util.Size;
30
31import java.util.ArrayList;
32import java.util.Arrays;
33import java.util.List;
34import java.util.Objects;
35
36import static com.android.internal.util.Preconditions.*;
37import static android.hardware.camera2.CaptureRequest.*;
38
39/**
40 * Provide legacy-specific implementations of camera2 CaptureRequest for legacy devices.
41 */
42@SuppressWarnings("deprecation")
43public class LegacyRequestMapper {
44    private static final String TAG = "LegacyRequestMapper";
45    private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
46
47    /**
48     * Set the legacy parameters using the {@link LegacyRequest legacy request}.
49     *
50     * <p>The legacy request's parameters are changed as a side effect of calling this
51     * method.</p>
52     *
53     * @param legacyRequest a non-{@code null} legacy request
54     */
55    public static void convertRequestMetadata(LegacyRequest legacyRequest) {
56        CameraCharacteristics characteristics = legacyRequest.characteristics;
57        CaptureRequest request = legacyRequest.captureRequest;
58        Size previewSize = legacyRequest.previewSize;
59        Camera.Parameters params = legacyRequest.parameters;
60
61        Rect activeArray = characteristics.get(CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE);
62
63        /*
64         * scaler.cropRegion
65         */
66        ParameterUtils.ZoomData zoomData;
67        {
68            zoomData = ParameterUtils.convertScalerCropRegion(activeArray,
69                    request.get(SCALER_CROP_REGION),
70                    previewSize,
71                    params);
72
73            if (params.isZoomSupported()) {
74                params.setZoom(zoomData.zoomIndex);
75            } else if (VERBOSE) {
76                Log.v(TAG, "convertRequestToMetadata - zoom is not supported");
77            }
78        }
79
80
81        /*
82         * control.ae*
83         */
84        // control.aeAntibandingMode
85        {
86        String legacyMode;
87            Integer antiBandingMode = request.get(CONTROL_AE_ANTIBANDING_MODE);
88            if (antiBandingMode != null) {
89                legacyMode = convertAeAntiBandingModeToLegacy(antiBandingMode);
90            } else {
91                legacyMode = ListUtils.listSelectFirstFrom(params.getSupportedAntibanding(),
92                        new String[] {
93                            Parameters.ANTIBANDING_AUTO,
94                            Parameters.ANTIBANDING_OFF,
95                            Parameters.ANTIBANDING_50HZ,
96                            Parameters.ANTIBANDING_60HZ,
97                        });
98            }
99
100            if (legacyMode != null) {
101                params.setAntibanding(legacyMode);
102            }
103        }
104
105        /*
106         * control.aeRegions, afRegions
107         */
108        {
109            // aeRegions
110            {
111                // Use aeRegions if available, fall back to using awbRegions if present
112                MeteringRectangle[] aeRegions = request.get(CONTROL_AE_REGIONS);
113                if (request.get(CONTROL_AWB_REGIONS) != null) {
114                    Log.w(TAG, "convertRequestMetadata - control.awbRegions setting is not " +
115                            "supported, ignoring value");
116                }
117                int maxNumMeteringAreas = params.getMaxNumMeteringAreas();
118                List<Camera.Area> meteringAreaList = convertMeteringRegionsToLegacy(
119                        activeArray, zoomData, aeRegions, maxNumMeteringAreas,
120                        /*regionName*/"AE");
121
122                params.setMeteringAreas(meteringAreaList);
123            }
124
125            // afRegions
126            {
127                MeteringRectangle[] afRegions = request.get(CONTROL_AF_REGIONS);
128                int maxNumFocusAreas = params.getMaxNumFocusAreas();
129                List<Camera.Area> focusAreaList = convertMeteringRegionsToLegacy(
130                        activeArray, zoomData, afRegions, maxNumFocusAreas,
131                        /*regionName*/"AF");
132
133                params.setFocusAreas(focusAreaList);
134            }
135        }
136
137        // control.aeTargetFpsRange
138        Range<Integer> aeFpsRange = request.get(CONTROL_AE_TARGET_FPS_RANGE);
139        if (aeFpsRange != null) {
140            int[] legacyFps = convertAeFpsRangeToLegacy(aeFpsRange);
141
142            // TODO - Should we enforce that all HAL1 devices must include (30, 30) FPS range?
143            boolean supported = false;
144            for(int[] range : params.getSupportedPreviewFpsRange()) {
145                if (legacyFps[0] == range[0] && legacyFps[1] == range[1]) {
146                    supported = true;
147                    break;
148                }
149            }
150            if (supported) {
151                params.setPreviewFpsRange(legacyFps[Camera.Parameters.PREVIEW_FPS_MIN_INDEX],
152                        legacyFps[Camera.Parameters.PREVIEW_FPS_MAX_INDEX]);
153            } else {
154                Log.w(TAG, "Unsupported FPS range set [" + legacyFps[0] + "," + legacyFps[1] + "]");
155            }
156        }
157
158        /*
159         * control
160         */
161
162        // control.aeExposureCompensation
163        {
164            Range<Integer> compensationRange =
165                    characteristics.get(CameraCharacteristics.CONTROL_AE_COMPENSATION_RANGE);
166            int compensation = ParamsUtils.getOrDefault(request,
167                    CONTROL_AE_EXPOSURE_COMPENSATION,
168                    /*defaultValue*/0);
169
170            if (!compensationRange.contains(compensation)) {
171                Log.w(TAG,
172                        "convertRequestMetadata - control.aeExposureCompensation " +
173                        "is out of range, ignoring value");
174                compensation = 0;
175            }
176
177            params.setExposureCompensation(compensation);
178        }
179
180        // control.aeLock
181        {
182            Boolean aeLock = getIfSupported(request, CONTROL_AE_LOCK, /*defaultValue*/false,
183                    params.isAutoExposureLockSupported(),
184                    /*allowedValue*/false);
185
186            if (aeLock != null) {
187                params.setAutoExposureLock(aeLock);
188            }
189
190            if (VERBOSE) {
191                Log.v(TAG, "convertRequestToMetadata - control.aeLock set to " + aeLock);
192            }
193
194            // TODO: Don't add control.aeLock to availableRequestKeys if it's not supported
195        }
196
197        // control.aeMode, flash.mode
198        mapAeAndFlashMode(request, /*out*/params);
199
200        // control.afMode
201        {
202            int afMode = ParamsUtils.getOrDefault(request, CONTROL_AF_MODE,
203                    /*defaultValue*/CONTROL_AF_MODE_OFF);
204            String focusMode = LegacyMetadataMapper.convertAfModeToLegacy(afMode,
205                    params.getSupportedFocusModes());
206
207            if (focusMode != null) {
208                params.setFocusMode(focusMode);
209            }
210
211            if (VERBOSE) {
212                Log.v(TAG, "convertRequestToMetadata - control.afMode "
213                        + afMode + " mapped to " + focusMode);
214            }
215        }
216
217        // control.awbMode
218        {
219            Integer awbMode = getIfSupported(request, CONTROL_AWB_MODE,
220                    /*defaultValue*/CONTROL_AWB_MODE_AUTO,
221                    params.getSupportedWhiteBalance() != null,
222                    /*allowedValue*/CONTROL_AWB_MODE_AUTO);
223
224            String whiteBalanceMode = null;
225            if (awbMode != null) { // null iff AWB is not supported by camera1 api
226                whiteBalanceMode = convertAwbModeToLegacy(awbMode);
227                params.setWhiteBalance(whiteBalanceMode);
228            }
229
230            if (VERBOSE) {
231                Log.v(TAG, "convertRequestToMetadata - control.awbMode "
232                        + awbMode + " mapped to " + whiteBalanceMode);
233            }
234        }
235
236        // control.awbLock
237        {
238            Boolean awbLock = getIfSupported(request, CONTROL_AWB_LOCK, /*defaultValue*/false,
239                    params.isAutoWhiteBalanceLockSupported(),
240                    /*allowedValue*/false);
241
242            if (awbLock != null) {
243                params.setAutoWhiteBalanceLock(awbLock);
244            }
245
246         // TODO: Don't add control.awbLock to availableRequestKeys if it's not supported
247        }
248
249        // control.videoStabilizationMode
250        {
251            Integer stabMode = getIfSupported(request, CONTROL_VIDEO_STABILIZATION_MODE,
252                    /*defaultValue*/CONTROL_VIDEO_STABILIZATION_MODE_OFF,
253                    params.isVideoStabilizationSupported(),
254                    /*allowedValue*/CONTROL_VIDEO_STABILIZATION_MODE_OFF);
255
256            if (stabMode != null) {
257                params.setVideoStabilization(stabMode == CONTROL_VIDEO_STABILIZATION_MODE_ON);
258            }
259        }
260
261        // lens.focusDistance
262        {
263            boolean infinityFocusSupported =
264                    ListUtils.listContains(params.getSupportedFocusModes(),
265                            Parameters.FOCUS_MODE_INFINITY);
266            Float focusDistance = getIfSupported(request, LENS_FOCUS_DISTANCE,
267                    /*defaultValue*/0f, infinityFocusSupported, /*allowedValue*/0f);
268
269            if (focusDistance == null || focusDistance != 0f) {
270                Log.w(TAG,
271                        "convertRequestToMetadata - Ignoring android.lens.focusDistance "
272                                + infinityFocusSupported + ", only 0.0f is supported");
273            }
274        }
275
276        // control.sceneMode, control.mode
277        {
278            // TODO: Map FACE_PRIORITY scene mode to face detection.
279
280            if (params.getSupportedSceneModes() != null) {
281                int controlMode = ParamsUtils.getOrDefault(request, CONTROL_MODE,
282                    /*defaultValue*/CONTROL_MODE_AUTO);
283                String modeToSet;
284                switch (controlMode) {
285                    case CONTROL_MODE_USE_SCENE_MODE: {
286                        int sceneMode = ParamsUtils.getOrDefault(request, CONTROL_SCENE_MODE,
287                                /*defaultValue*/CONTROL_SCENE_MODE_DISABLED);
288                        String legacySceneMode = LegacyMetadataMapper.
289                                convertSceneModeToLegacy(sceneMode);
290                        if (legacySceneMode != null) {
291                            modeToSet = legacySceneMode;
292                        } else {
293                            modeToSet = Parameters.SCENE_MODE_AUTO;
294                            Log.w(TAG, "Skipping unknown requested scene mode: " + sceneMode);
295                        }
296                        break;
297                    }
298                    case CONTROL_MODE_AUTO: {
299                        modeToSet = Parameters.SCENE_MODE_AUTO;
300                        break;
301                    }
302                    default: {
303                        Log.w(TAG, "Control mode " + controlMode +
304                                " is unsupported, defaulting to AUTO");
305                        modeToSet = Parameters.SCENE_MODE_AUTO;
306                    }
307                }
308                params.setSceneMode(modeToSet);
309            }
310        }
311
312        // control.effectMode
313        {
314            if (params.getSupportedColorEffects() != null) {
315                int effectMode = ParamsUtils.getOrDefault(request, CONTROL_EFFECT_MODE,
316                    /*defaultValue*/CONTROL_EFFECT_MODE_OFF);
317                String legacyEffectMode = LegacyMetadataMapper.convertEffectModeToLegacy(effectMode);
318                if (legacyEffectMode != null) {
319                    params.setColorEffect(legacyEffectMode);
320                } else {
321                    params.setColorEffect(Parameters.EFFECT_NONE);
322                    Log.w(TAG, "Skipping unknown requested effect mode: " + effectMode);
323                }
324            }
325        }
326
327        /*
328         * sensor
329         */
330
331        // sensor.testPattern
332        {
333            int testPatternMode = ParamsUtils.getOrDefault(request, SENSOR_TEST_PATTERN_MODE,
334                    /*defaultValue*/SENSOR_TEST_PATTERN_MODE_OFF);
335            if (testPatternMode != SENSOR_TEST_PATTERN_MODE_OFF) {
336                Log.w(TAG, "convertRequestToMetadata - ignoring sensor.testPatternMode "
337                        + testPatternMode + "; only OFF is supported");
338            }
339        }
340    }
341
342    private static List<Camera.Area> convertMeteringRegionsToLegacy(
343            Rect activeArray, ParameterUtils.ZoomData zoomData,
344            MeteringRectangle[] meteringRegions, int maxNumMeteringAreas, String regionName) {
345        if (meteringRegions == null || maxNumMeteringAreas <= 0) {
346            if (maxNumMeteringAreas > 0) {
347                return Arrays.asList(ParameterUtils.CAMERA_AREA_DEFAULT);
348            } else {
349                return null;
350            }
351        }
352
353        // Add all non-zero weight regions to the list
354        List<MeteringRectangle> meteringRectangleList = new ArrayList<>();
355        for (MeteringRectangle rect : meteringRegions) {
356            if (rect.getMeteringWeight() != MeteringRectangle.METERING_WEIGHT_DONT_CARE) {
357                meteringRectangleList.add(rect);
358            }
359        }
360
361        // Ignore any regions beyond our maximum supported count
362        int countMeteringAreas =
363                Math.min(maxNumMeteringAreas, meteringRectangleList.size());
364        List<Camera.Area> meteringAreaList = new ArrayList<>(countMeteringAreas);
365
366        for (int i = 0; i < countMeteringAreas; ++i) {
367            MeteringRectangle rect = meteringRectangleList.get(i);
368
369            ParameterUtils.MeteringData meteringData =
370                    ParameterUtils.convertMeteringRectangleToLegacy(activeArray, rect, zoomData);
371            meteringAreaList.add(meteringData.meteringArea);
372        }
373
374        if (maxNumMeteringAreas < meteringRectangleList.size()) {
375            Log.w(TAG,
376                    "convertMeteringRegionsToLegacy - Too many requested " + regionName +
377                            " regions, ignoring all beyond the first " + maxNumMeteringAreas);
378        }
379
380        if (VERBOSE) {
381            Log.v(TAG, "convertMeteringRegionsToLegacy - " + regionName + " areas = "
382                    + ParameterUtils.stringFromAreaList(meteringAreaList));
383        }
384
385        return meteringAreaList;
386    }
387
388    private static void mapAeAndFlashMode(CaptureRequest r, /*out*/Parameters p) {
389        int flashMode = ParamsUtils.getOrDefault(r, FLASH_MODE, FLASH_MODE_OFF);
390        int aeMode = ParamsUtils.getOrDefault(r, CONTROL_AE_MODE, CONTROL_AE_MODE_ON);
391
392        List<String> supportedFlashModes = p.getSupportedFlashModes();
393
394        String flashModeSetting = null;
395
396        // Flash is OFF by default, on cameras that support flash
397        if (ListUtils.listContains(supportedFlashModes, Parameters.FLASH_MODE_OFF)) {
398            flashModeSetting = Parameters.FLASH_MODE_OFF;
399        }
400
401        /*
402         * Map all of the control.aeMode* enums, but ignore AE_MODE_OFF since we never support it
403         */
404
405        // Ignore flash.mode controls unless aeMode == ON
406        if (aeMode == CONTROL_AE_MODE_ON) {
407            if (flashMode == FLASH_MODE_TORCH) {
408                    if (ListUtils.listContains(supportedFlashModes, Parameters.FLASH_MODE_TORCH)) {
409                        flashModeSetting = Parameters.FLASH_MODE_TORCH;
410                    } else {
411                        Log.w(TAG, "mapAeAndFlashMode - Ignore flash.mode == TORCH;" +
412                                "camera does not support it");
413                    }
414            } else if (flashMode == FLASH_MODE_SINGLE) {
415                if (ListUtils.listContains(supportedFlashModes, Parameters.FLASH_MODE_ON)) {
416                    flashModeSetting = Parameters.FLASH_MODE_ON;
417                } else {
418                    Log.w(TAG, "mapAeAndFlashMode - Ignore flash.mode == SINGLE;" +
419                            "camera does not support it");
420                }
421            } else {
422                // Use the default FLASH_MODE_OFF
423            }
424        } else if (aeMode == CONTROL_AE_MODE_ON_ALWAYS_FLASH) {
425                if (ListUtils.listContains(supportedFlashModes, Parameters.FLASH_MODE_ON)) {
426                    flashModeSetting = Parameters.FLASH_MODE_ON;
427                } else {
428                    Log.w(TAG, "mapAeAndFlashMode - Ignore control.aeMode == ON_ALWAYS_FLASH;" +
429                            "camera does not support it");
430                }
431        } else if (aeMode == CONTROL_AE_MODE_ON_AUTO_FLASH) {
432            if (ListUtils.listContains(supportedFlashModes, Parameters.FLASH_MODE_AUTO)) {
433                flashModeSetting = Parameters.FLASH_MODE_AUTO;
434            } else {
435                Log.w(TAG, "mapAeAndFlashMode - Ignore control.aeMode == ON_AUTO_FLASH;" +
436                        "camera does not support it");
437            }
438        } else if (aeMode == CONTROL_AE_MODE_ON_AUTO_FLASH_REDEYE) {
439                if (ListUtils.listContains(supportedFlashModes, Parameters.FLASH_MODE_RED_EYE)) {
440                    flashModeSetting = Parameters.FLASH_MODE_RED_EYE;
441                } else {
442                    Log.w(TAG, "mapAeAndFlashMode - Ignore control.aeMode == ON_AUTO_FLASH_REDEYE;"
443                            + "camera does not support it");
444                }
445        } else {
446            // Default to aeMode == ON, flash = OFF
447        }
448
449        if (flashModeSetting != null) {
450            p.setFlashMode(flashModeSetting);
451        }
452
453        if (VERBOSE) {
454                Log.v(TAG,
455                        "mapAeAndFlashMode - set flash.mode (api1) to " + flashModeSetting
456                        + ", requested (api2) " + flashMode
457                        + ", supported (api1) " + ListUtils.listToString(supportedFlashModes));
458        }
459    }
460
461    /**
462     * Returns null if the anti-banding mode enum is not supported.
463     */
464    private static String convertAeAntiBandingModeToLegacy(int mode) {
465        switch (mode) {
466            case CONTROL_AE_ANTIBANDING_MODE_OFF: {
467                return Parameters.ANTIBANDING_OFF;
468            }
469            case CONTROL_AE_ANTIBANDING_MODE_50HZ: {
470                return Parameters.ANTIBANDING_50HZ;
471            }
472            case CONTROL_AE_ANTIBANDING_MODE_60HZ: {
473                return Parameters.ANTIBANDING_60HZ;
474            }
475            case CONTROL_AE_ANTIBANDING_MODE_AUTO: {
476                return Parameters.ANTIBANDING_AUTO;
477            }
478            default: {
479                return null;
480            }
481        }
482    }
483
484    private static int[] convertAeFpsRangeToLegacy(Range<Integer> fpsRange) {
485        int[] legacyFps = new int[2];
486        legacyFps[Parameters.PREVIEW_FPS_MIN_INDEX] = fpsRange.getLower();
487        legacyFps[Parameters.PREVIEW_FPS_MAX_INDEX] = fpsRange.getUpper();
488        return legacyFps;
489    }
490
491    private static String convertAwbModeToLegacy(int mode) {
492        switch (mode) {
493            case CONTROL_AWB_MODE_AUTO:
494                return Camera.Parameters.WHITE_BALANCE_AUTO;
495            case CONTROL_AWB_MODE_INCANDESCENT:
496                return Camera.Parameters.WHITE_BALANCE_INCANDESCENT;
497            case CONTROL_AWB_MODE_FLUORESCENT:
498                return Camera.Parameters.WHITE_BALANCE_FLUORESCENT;
499            case CONTROL_AWB_MODE_WARM_FLUORESCENT:
500                return Camera.Parameters.WHITE_BALANCE_WARM_FLUORESCENT;
501            case CONTROL_AWB_MODE_DAYLIGHT:
502                return Camera.Parameters.WHITE_BALANCE_DAYLIGHT;
503            case CONTROL_AWB_MODE_CLOUDY_DAYLIGHT:
504                return Camera.Parameters.WHITE_BALANCE_CLOUDY_DAYLIGHT;
505            case CONTROL_AWB_MODE_TWILIGHT:
506                return Camera.Parameters.WHITE_BALANCE_TWILIGHT;
507            default:
508                Log.w(TAG, "convertAwbModeToLegacy - unrecognized control.awbMode" + mode);
509                return Camera.Parameters.WHITE_BALANCE_AUTO;
510        }
511    }
512
513
514    /**
515     * Return {@code null} if the value is not supported, otherwise return the retrieved key's
516     * value from the request (or the default value if it wasn't set).
517     *
518     * <p>If the fetched value in the request is equivalent to {@code allowedValue},
519     * then omit the warning (e.g. turning off AF lock on a camera
520     * that always has the AF lock turned off is a silent no-op), but still return {@code null}.</p>
521     *
522     * <p>Logs a warning to logcat if the key is not supported by api1 camera device.</p.
523     */
524    private static <T> T getIfSupported(
525            CaptureRequest r, CaptureRequest.Key<T> key, T defaultValue, boolean isSupported,
526            T allowedValue) {
527        T val = ParamsUtils.getOrDefault(r, key, defaultValue);
528
529        if (!isSupported) {
530            if (!Objects.equals(val, allowedValue)) {
531                Log.w(TAG, key.getName() + " is not supported; ignoring requested value " + val);
532            }
533            return null;
534        }
535
536        return val;
537    }
538}
539