LegacyResultMapper.java revision 1dc1326eaedd11ffd8f85927b8f0195f4f7598d3
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.CaptureResult;
25import android.hardware.camera2.impl.CameraMetadataNative;
26import android.hardware.camera2.legacy.ParameterUtils.WeightedRectangle;
27import android.hardware.camera2.legacy.ParameterUtils.ZoomData;
28import android.hardware.camera2.params.MeteringRectangle;
29import android.hardware.camera2.utils.ListUtils;
30import android.hardware.camera2.utils.ParamsUtils;
31import android.location.Location;
32import android.util.Log;
33import android.util.Size;
34
35import java.util.ArrayList;
36import java.util.List;
37
38import static com.android.internal.util.Preconditions.*;
39import static android.hardware.camera2.CaptureResult.*;
40
41/**
42 * Provide legacy-specific implementations of camera2 CaptureResult for legacy devices.
43 */
44@SuppressWarnings("deprecation")
45public class LegacyResultMapper {
46    private static final String TAG = "LegacyResultMapper";
47    private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
48
49    private LegacyRequest mCachedRequest = null;
50    private CameraMetadataNative mCachedResult = null;
51
52    /**
53     * Generate capture result metadata from the legacy camera request.
54     *
55     * <p>This method caches and reuses the result from the previous call to this method if
56     * the {@code parameters} of the subsequent {@link LegacyRequest} passed to this method
57     * have not changed.</p>
58     *
59     * @param legacyRequest a non-{@code null} legacy request containing the latest parameters
60     * @param timestamp the timestamp to use for this result in nanoseconds.
61     *
62     * @return {@link CameraMetadataNative} object containing result metadata.
63     */
64    public CameraMetadataNative cachedConvertResultMetadata(
65            LegacyRequest legacyRequest, long timestamp) {
66        CameraMetadataNative result;
67        boolean cached;
68
69        /*
70         * Attempt to look up the result from the cache if the parameters haven't changed
71         */
72        if (mCachedRequest != null && legacyRequest.parameters.same(mCachedRequest.parameters)) {
73            result = new CameraMetadataNative(mCachedResult);
74            cached = true;
75        } else {
76            result = convertResultMetadata(legacyRequest, timestamp);
77            cached = false;
78
79            // Always cache a *copy* of the metadata result,
80            // since api2's client side takes ownership of it after it receives a result
81            mCachedRequest = legacyRequest;
82            mCachedResult = new CameraMetadataNative(result);
83        }
84
85        /*
86         * Unconditionally set fields that change in every single frame
87         */
88        {
89            // sensor.timestamp
90            result.set(SENSOR_TIMESTAMP, timestamp);
91        }
92
93        if (VERBOSE) {
94            Log.v(TAG, "cachedConvertResultMetadata - cached? " + cached +
95                    " timestamp = " + timestamp);
96
97            Log.v(TAG, "----- beginning of result dump ------");
98            result.dumpToLog();
99            Log.v(TAG, "----- end of result dump ------");
100        }
101
102        return result;
103    }
104
105    /**
106     * Generate capture result metadata from the legacy camera request.
107     *
108     * @param legacyRequest a non-{@code null} legacy request containing the latest parameters
109     * @param timestamp the timestamp to use for this result in nanoseconds.
110     *
111     * @return a {@link CameraMetadataNative} object containing result metadata.
112     */
113    private static CameraMetadataNative convertResultMetadata(LegacyRequest legacyRequest,
114                                                      long timestamp) {
115        CameraCharacteristics characteristics = legacyRequest.characteristics;
116        CaptureRequest request = legacyRequest.captureRequest;
117        Size previewSize = legacyRequest.previewSize;
118        Camera.Parameters params = legacyRequest.parameters;
119
120        CameraMetadataNative result = new CameraMetadataNative();
121
122        Rect activeArraySize = characteristics.get(
123                CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE);
124        ZoomData zoomData = ParameterUtils.convertScalerCropRegion(activeArraySize,
125                request.get(CaptureRequest.SCALER_CROP_REGION), previewSize, params);
126
127        /*
128         * control
129         */
130
131        /*
132         * control.ae*
133         */
134        mapAe(result, characteristics, request, activeArraySize, zoomData, /*out*/params);
135
136        /*
137         * control.af*
138         */
139        mapAf(result, activeArraySize, zoomData, /*out*/params);
140
141        /*
142         * control.awb*
143         */
144        mapAwb(result, /*out*/params);
145
146        /*
147         * control.captureIntent
148         */
149        {
150            int captureIntent = ParamsUtils.getOrDefault(request,
151                    CaptureRequest.CONTROL_CAPTURE_INTENT,
152                    /*defaultValue*/CaptureRequest.CONTROL_CAPTURE_INTENT_PREVIEW);
153
154            captureIntent = LegacyRequestMapper.filterSupportedCaptureIntent(captureIntent);
155
156            result.set(CONTROL_CAPTURE_INTENT, captureIntent);
157        }
158
159        /*
160         * control.mode
161         */
162        {
163            int controlMode = ParamsUtils.getOrDefault(request, CaptureRequest.CONTROL_MODE,
164                    CONTROL_MODE_AUTO);
165            if (controlMode == CaptureResult.CONTROL_MODE_USE_SCENE_MODE) {
166                result.set(CONTROL_MODE, CONTROL_MODE_USE_SCENE_MODE);
167            } else {
168                result.set(CONTROL_MODE, CONTROL_MODE_AUTO);
169            }
170        }
171
172        /*
173         * control.sceneMode
174         */
175        {
176            String legacySceneMode = params.getSceneMode();
177            int mode = LegacyMetadataMapper.convertSceneModeFromLegacy(legacySceneMode);
178            if (mode != LegacyMetadataMapper.UNKNOWN_MODE) {
179                result.set(CaptureResult.CONTROL_SCENE_MODE, mode);
180                // In case of SCENE_MODE == FACE_PRIORITY, LegacyFaceDetectMapper will override
181                // the result to say SCENE_MODE == FACE_PRIORITY.
182            }  else {
183                Log.w(TAG, "Unknown scene mode " + legacySceneMode +
184                        " returned by camera HAL, setting to disabled.");
185                result.set(CaptureResult.CONTROL_SCENE_MODE, CONTROL_SCENE_MODE_DISABLED);
186            }
187        }
188
189        /*
190         * control.effectMode
191         */
192        {
193            String legacyEffectMode = params.getColorEffect();
194            int mode = LegacyMetadataMapper.convertEffectModeFromLegacy(legacyEffectMode);
195            if (mode != LegacyMetadataMapper.UNKNOWN_MODE) {
196                result.set(CaptureResult.CONTROL_EFFECT_MODE, mode);
197            } else {
198                Log.w(TAG, "Unknown effect mode " + legacyEffectMode +
199                        " returned by camera HAL, setting to off.");
200                result.set(CaptureResult.CONTROL_EFFECT_MODE, CONTROL_EFFECT_MODE_OFF);
201            }
202        }
203
204        // control.videoStabilizationMode
205        {
206            int stabMode =
207                    (params.isVideoStabilizationSupported() && params.getVideoStabilization()) ?
208                        CONTROL_VIDEO_STABILIZATION_MODE_ON :
209                        CONTROL_VIDEO_STABILIZATION_MODE_OFF;
210            result.set(CONTROL_VIDEO_STABILIZATION_MODE, stabMode);
211        }
212
213        /*
214         * flash
215         */
216        {
217            // flash.mode, flash.state mapped in mapAeAndFlashMode
218        }
219
220        /*
221         * lens
222         */
223        // lens.focusDistance
224        {
225            if (Parameters.FOCUS_MODE_INFINITY.equals(params.getFocusMode())) {
226                result.set(CaptureResult.LENS_FOCUS_DISTANCE, 0.0f);
227            }
228        }
229
230        // lens.focalLength
231        result.set(CaptureResult.LENS_FOCAL_LENGTH, params.getFocalLength());
232
233        /*
234         * request
235         */
236        // request.pipelineDepth
237        result.set(REQUEST_PIPELINE_DEPTH,
238                characteristics.get(CameraCharacteristics.REQUEST_PIPELINE_MAX_DEPTH));
239
240        /*
241         * scaler
242         */
243        mapScaler(result, zoomData, /*out*/params);
244
245        /*
246         * sensor
247         */
248        // sensor.timestamp varies every frame; mapping is done in #cachedConvertResultMetadata
249        {
250            // Unconditionally no test patterns
251            result.set(SENSOR_TEST_PATTERN_MODE, SENSOR_TEST_PATTERN_MODE_OFF);
252        }
253
254        /*
255         * jpeg
256         */
257        // jpeg.gpsLocation
258        result.set(JPEG_GPS_LOCATION, request.get(CaptureRequest.JPEG_GPS_LOCATION));
259
260        // jpeg.orientation
261        result.set(JPEG_ORIENTATION, request.get(CaptureRequest.JPEG_ORIENTATION));
262
263        // jpeg.quality
264        result.set(JPEG_QUALITY, (byte) params.getJpegQuality());
265
266        // jpeg.thumbnailQuality
267        result.set(JPEG_THUMBNAIL_QUALITY, (byte) params.getJpegThumbnailQuality());
268
269        // jpeg.thumbnailSize
270        Camera.Size s = params.getJpegThumbnailSize();
271        if (s != null) {
272            result.set(JPEG_THUMBNAIL_SIZE, ParameterUtils.convertSize(s));
273        } else {
274            Log.w(TAG, "Null thumbnail size received from parameters.");
275        }
276
277        // TODO: Remaining result metadata tags conversions.
278        return result;
279    }
280
281    private static void mapAe(CameraMetadataNative m,
282            CameraCharacteristics characteristics,
283            CaptureRequest request, Rect activeArray, ZoomData zoomData, /*out*/Parameters p) {
284        // control.aeAntiBandingMode
285        {
286            int antiBandingMode = LegacyMetadataMapper.convertAntiBandingModeOrDefault(
287                    p.getAntibanding());
288            m.set(CONTROL_AE_ANTIBANDING_MODE, antiBandingMode);
289        }
290
291        // control.aeExposureCompensation
292        {
293            m.set(CONTROL_AE_EXPOSURE_COMPENSATION, p.getExposureCompensation());
294        }
295
296        // control.aeLock
297        {
298            boolean lock = p.isAutoExposureLockSupported() ? p.getAutoExposureLock() : false;
299            m.set(CONTROL_AE_LOCK, lock);
300            if (VERBOSE) {
301                Log.v(TAG,
302                        "mapAe - android.control.aeLock = " + lock +
303                        ", supported = " + p.isAutoExposureLockSupported());
304            }
305
306            Boolean requestLock = request.get(CaptureRequest.CONTROL_AE_LOCK);
307            if (requestLock != null && requestLock != lock) {
308                Log.w(TAG,
309                        "mapAe - android.control.aeLock was requested to " + requestLock +
310                        " but resulted in " + lock);
311            }
312        }
313
314        // control.aeMode, flash.mode, flash.state
315        mapAeAndFlashMode(m, characteristics, p);
316
317        // control.aeState
318        if (LegacyMetadataMapper.LIE_ABOUT_AE_STATE) {
319            // Lie to pass CTS temporarily.
320            // TODO: Implement precapture trigger, after which we can report CONVERGED ourselves
321            m.set(CONTROL_AE_STATE, CONTROL_AE_STATE_CONVERGED);
322        }
323
324        // control.aeRegions
325        {
326            if (VERBOSE) {
327                String meteringAreas = p.get("metering-areas");
328                Log.v(TAG, "mapAe - parameter dump; metering-areas: " + meteringAreas);
329            }
330
331            MeteringRectangle[] meteringRectArray = getMeteringRectangles(activeArray,
332                    zoomData, p.getMeteringAreas(), "AE");
333
334            m.set(CONTROL_AE_REGIONS, meteringRectArray);
335        }
336
337    }
338
339    private static void mapAf(CameraMetadataNative m,
340            Rect activeArray, ZoomData zoomData, Camera.Parameters p) {
341        // control.afMode
342        m.set(CaptureResult.CONTROL_AF_MODE, convertLegacyAfMode(p.getFocusMode()));
343
344        // control.afRegions
345        {
346            if (VERBOSE) {
347                String focusAreas = p.get("focus-areas");
348                Log.v(TAG, "mapAe - parameter dump; focus-areas: " + focusAreas);
349            }
350
351            MeteringRectangle[] meteringRectArray = getMeteringRectangles(activeArray,
352                    zoomData, p.getFocusAreas(), "AF");
353
354            m.set(CONTROL_AF_REGIONS, meteringRectArray);
355        }
356    }
357
358    private static void mapAwb(CameraMetadataNative m, Camera.Parameters p) {
359        // control.awbLock
360        {
361            boolean lock = p.isAutoWhiteBalanceLockSupported() ?
362                    p.getAutoWhiteBalanceLock() : false;
363            m.set(CONTROL_AWB_LOCK, lock);
364        }
365
366        // control.awbMode
367        {
368            int awbMode = convertLegacyAwbMode(p.getWhiteBalance());
369            m.set(CONTROL_AWB_MODE, awbMode);
370        }
371    }
372
373    private static MeteringRectangle[] getMeteringRectangles(Rect activeArray, ZoomData zoomData,
374            List<Camera.Area> meteringAreaList, String regionName) {
375        List<MeteringRectangle> meteringRectList = new ArrayList<>();
376        if (meteringAreaList != null) {
377            for (Camera.Area area : meteringAreaList) {
378                WeightedRectangle rect =
379                        ParameterUtils.convertCameraAreaToActiveArrayRectangle(
380                                activeArray, zoomData, area);
381
382                meteringRectList.add(rect.toMetering());
383            }
384        }
385
386        if (VERBOSE) {
387            Log.v(TAG,
388                    "Metering rectangles for " + regionName + ": "
389                     + ListUtils.listToString(meteringRectList));
390        }
391
392        return meteringRectList.toArray(new MeteringRectangle[0]);
393    }
394
395    /** Map results for control.aeMode, flash.mode, flash.state */
396    private static void mapAeAndFlashMode(CameraMetadataNative m,
397            CameraCharacteristics characteristics, Parameters p) {
398        // Default: AE mode on but flash never fires
399        int flashMode = FLASH_MODE_OFF;
400        // If there is no flash on this camera, the state is always unavailable
401        // , otherwise it's only known for TORCH/SINGLE modes
402        Integer flashState = characteristics.get(CameraCharacteristics.FLASH_INFO_AVAILABLE)
403                ? null : FLASH_STATE_UNAVAILABLE;
404        int aeMode = CONTROL_AE_MODE_ON;
405
406        String flashModeSetting = p.getFlashMode();
407
408        if (flashModeSetting != null) {
409            switch (flashModeSetting) {
410                case Parameters.FLASH_MODE_OFF:
411                    break; // ok, using default
412                case Parameters.FLASH_MODE_AUTO:
413                    aeMode = CONTROL_AE_MODE_ON_AUTO_FLASH;
414                    break;
415                case Parameters.FLASH_MODE_ON:
416                    // flashMode = SINGLE + aeMode = ON is indistinguishable from ON_ALWAYS_FLASH
417                    flashMode = FLASH_MODE_SINGLE;
418                    aeMode = CONTROL_AE_MODE_ON_ALWAYS_FLASH;
419                    flashState = FLASH_STATE_FIRED;
420                    break;
421                case Parameters.FLASH_MODE_RED_EYE:
422                    aeMode = CONTROL_AE_MODE_ON_AUTO_FLASH_REDEYE;
423                    break;
424                case Parameters.FLASH_MODE_TORCH:
425                    flashMode = FLASH_MODE_TORCH;
426                    flashState = FLASH_STATE_FIRED;
427                    break;
428                default:
429                    Log.w(TAG,
430                            "mapAeAndFlashMode - Ignoring unknown flash mode " + p.getFlashMode());
431            }
432        }
433
434        // flash.state
435        m.set(FLASH_STATE, flashState);
436        // flash.mode
437        m.set(FLASH_MODE, flashMode);
438        // control.aeMode
439        m.set(CONTROL_AE_MODE, aeMode);
440    }
441
442    private static int convertLegacyAfMode(String mode) {
443        if (mode == null) {
444            Log.w(TAG, "convertLegacyAfMode - no AF mode, default to OFF");
445            return CONTROL_AF_MODE_OFF;
446        }
447
448        switch (mode) {
449            case Parameters.FOCUS_MODE_AUTO:
450                return CONTROL_AF_MODE_AUTO;
451            case Parameters.FOCUS_MODE_CONTINUOUS_PICTURE:
452                return CONTROL_AF_MODE_CONTINUOUS_PICTURE;
453            case Parameters.FOCUS_MODE_CONTINUOUS_VIDEO:
454                return CONTROL_AF_MODE_CONTINUOUS_VIDEO;
455            case Parameters.FOCUS_MODE_EDOF:
456                return CONTROL_AF_MODE_EDOF;
457            case Parameters.FOCUS_MODE_MACRO:
458                return CONTROL_AF_MODE_MACRO;
459            case Parameters.FOCUS_MODE_FIXED:
460                return CONTROL_AF_MODE_OFF;
461            case Parameters.FOCUS_MODE_INFINITY:
462                return CONTROL_AF_MODE_OFF;
463            default:
464                Log.w(TAG, "convertLegacyAfMode - unknown mode " + mode + " , ignoring");
465                return CONTROL_AF_MODE_OFF;
466        }
467    }
468
469    private static int convertLegacyAwbMode(String mode) {
470        if (mode == null) {
471            // OK: camera1 api may not support changing WB modes; assume AUTO
472            return CONTROL_AWB_MODE_AUTO;
473        }
474
475        switch (mode) {
476            case Camera.Parameters.WHITE_BALANCE_AUTO:
477                return CONTROL_AWB_MODE_AUTO;
478            case Camera.Parameters.WHITE_BALANCE_INCANDESCENT:
479                return CONTROL_AWB_MODE_INCANDESCENT;
480            case Camera.Parameters.WHITE_BALANCE_FLUORESCENT:
481                return CONTROL_AWB_MODE_FLUORESCENT;
482            case Camera.Parameters.WHITE_BALANCE_WARM_FLUORESCENT:
483                return CONTROL_AWB_MODE_WARM_FLUORESCENT;
484            case Camera.Parameters.WHITE_BALANCE_DAYLIGHT:
485                return CONTROL_AWB_MODE_DAYLIGHT;
486            case Camera.Parameters.WHITE_BALANCE_CLOUDY_DAYLIGHT:
487                return CONTROL_AWB_MODE_CLOUDY_DAYLIGHT;
488            case Camera.Parameters.WHITE_BALANCE_TWILIGHT:
489                return CONTROL_AWB_MODE_TWILIGHT;
490            case Camera.Parameters.WHITE_BALANCE_SHADE:
491                return CONTROL_AWB_MODE_SHADE;
492            default:
493                Log.w(TAG, "convertAwbMode - unrecognized WB mode " + mode);
494                return CONTROL_AWB_MODE_AUTO;
495        }
496    }
497
498    /** Map results for scaler.* */
499    private static void mapScaler(CameraMetadataNative m,
500            ZoomData zoomData,
501            /*out*/Parameters p) {
502        /*
503         * scaler.cropRegion
504         */
505        {
506            m.set(SCALER_CROP_REGION, zoomData.reportedCrop);
507        }
508    }
509}
510