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