LegacyResultMapper.java revision 3fe9eba9044c0b20ed349a4b9094bf1fa7942cdf
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.CameraMetadata;
24import android.hardware.camera2.CaptureRequest;
25import android.hardware.camera2.CaptureResult;
26import android.hardware.camera2.impl.CameraMetadataNative;
27import android.hardware.camera2.legacy.ParameterUtils.WeightedRectangle;
28import android.hardware.camera2.legacy.ParameterUtils.ZoomData;
29import android.hardware.camera2.params.MeteringRectangle;
30import android.hardware.camera2.utils.ListUtils;
31import android.hardware.camera2.utils.ParamsUtils;
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        // control.afMode
137        result.set(CaptureResult.CONTROL_AF_MODE, convertLegacyAfMode(params.getFocusMode()));
138
139        // control.awbLock
140        result.set(CaptureResult.CONTROL_AWB_LOCK, params.getAutoWhiteBalanceLock());
141
142        // control.awbState
143        if (LegacyMetadataMapper.LIE_ABOUT_AWB_STATE) {
144            // Lie to pass CTS temporarily.
145            // TODO: CTS needs to be updated not to query this value
146            // for LIMITED devices unless its guaranteed to be available.
147            result.set(CaptureResult.CONTROL_AWB_STATE,
148                    CameraMetadata.CONTROL_AWB_STATE_CONVERGED);
149            // TODO: Read the awb mode from parameters instead
150        }
151
152        if (LegacyMetadataMapper.LIE_ABOUT_AWB) {
153            result.set(CaptureResult.CONTROL_AWB_MODE,
154                    request.get(CaptureRequest.CONTROL_AWB_MODE));
155        }
156
157
158        /*
159         * control.mode
160         */
161        {
162            int controlMode = ParamsUtils.getOrDefault(request, CaptureRequest.CONTROL_MODE,
163                    CONTROL_MODE_AUTO);
164            if (controlMode == CaptureResult.CONTROL_MODE_USE_SCENE_MODE) {
165                result.set(CONTROL_MODE, CONTROL_MODE_USE_SCENE_MODE);
166            } else {
167                result.set(CONTROL_MODE, CONTROL_MODE_AUTO);
168            }
169        }
170
171        /*
172         * control.sceneMode
173         */
174        {
175            String legacySceneMode = params.getSceneMode();
176            int mode = LegacyMetadataMapper.convertSceneModeFromLegacy(legacySceneMode);
177            if (mode != LegacyMetadataMapper.UNKNOWN_MODE) {
178                result.set(CaptureResult.CONTROL_SCENE_MODE, mode);
179            }  else {
180                Log.w(TAG, "Unknown scene mode " + legacySceneMode +
181                        " returned by camera HAL, setting to disabled.");
182                result.set(CaptureResult.CONTROL_SCENE_MODE, CONTROL_SCENE_MODE_DISABLED);
183            }
184        }
185
186
187        /*
188         * control.effectMode
189         */
190        {
191            String legacyEffectMode = params.getColorEffect();
192            int mode = LegacyMetadataMapper.convertEffectModeFromLegacy(legacyEffectMode);
193            if (mode != LegacyMetadataMapper.UNKNOWN_MODE) {
194                result.set(CaptureResult.CONTROL_EFFECT_MODE, mode);
195            } else {
196                Log.w(TAG, "Unknown effect mode " + legacyEffectMode +
197                        " returned by camera HAL, setting to off.");
198                result.set(CaptureResult.CONTROL_EFFECT_MODE, CONTROL_EFFECT_MODE_OFF);
199            }
200        }
201
202        /*
203         * flash
204         */
205        {
206            // TODO
207        }
208
209        /*
210         * lens
211         */
212        // lens.focusDistance
213        {
214            if (Parameters.FOCUS_MODE_INFINITY.equals(params.getFocusMode())) {
215                result.set(CaptureResult.LENS_FOCUS_DISTANCE, 0.0f);
216            }
217        }
218
219        // lens.focalLength
220        result.set(CaptureResult.LENS_FOCAL_LENGTH, params.getFocalLength());
221
222        /*
223         * request
224         */
225        // request.pipelineDepth
226        result.set(REQUEST_PIPELINE_DEPTH,
227                characteristics.get(CameraCharacteristics.REQUEST_PIPELINE_MAX_DEPTH));
228
229        /*
230         * scaler
231         */
232        mapScaler(result, zoomData, /*out*/params);
233
234        /*
235         * sensor
236         */
237
238        // TODO: Remaining result metadata tags conversions.
239        return result;
240    }
241
242    private static void mapAe(CameraMetadataNative m,
243            CameraCharacteristics characteristics,
244            CaptureRequest request, Rect activeArray, ZoomData zoomData, /*out*/Parameters p) {
245        // control.aeAntiBandingMode
246        {
247            int antiBandingMode = LegacyMetadataMapper.convertAntiBandingModeOrDefault(
248                    p.getAntibanding());
249            m.set(CONTROL_AE_ANTIBANDING_MODE, antiBandingMode);
250        }
251
252        // control.aeExposureCompensation
253        {
254            m.set(CONTROL_AE_EXPOSURE_COMPENSATION, p.getExposureCompensation());
255        }
256
257        // control.aeLock
258        {
259            boolean lock = p.isAutoExposureLockSupported() ? p.getAutoExposureLock() : false;
260            m.set(CONTROL_AE_LOCK, lock);
261            if (VERBOSE) {
262                Log.v(TAG,
263                        "mapAe - android.control.aeLock = " + lock +
264                        ", supported = " + p.isAutoExposureLockSupported());
265            }
266
267            Boolean requestLock = request.get(CaptureRequest.CONTROL_AE_LOCK);
268            if (requestLock != null && requestLock != lock) {
269                Log.w(TAG,
270                        "mapAe - android.control.aeLock was requested to " + requestLock +
271                        " but resulted in " + lock);
272            }
273        }
274
275        // control.aeMode, flash.mode, flash.state
276        mapAeAndFlashMode(m, characteristics, p);
277
278        // control.aeState
279        if (LegacyMetadataMapper.LIE_ABOUT_AE_STATE) {
280            // Lie to pass CTS temporarily.
281            // TODO: Implement precapture trigger, after which we can report CONVERGED ourselves
282            m.set(CONTROL_AE_STATE, CONTROL_AE_STATE_CONVERGED);
283        }
284
285        // control.aeRegions
286        {
287            if (VERBOSE) {
288                String meteringAreas = p.get("metering-areas");
289                Log.v(TAG, "mapAe - parameter dump; metering-areas: " + meteringAreas);
290            }
291
292            MeteringRectangle[] meteringRectArray = getMeteringRectangles(activeArray,
293                    zoomData, p.getMeteringAreas(), "AE");
294
295            m.set(CONTROL_AE_REGIONS, meteringRectArray);
296        }
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        // control.awbLock
312        {
313            boolean lock = p.isAutoWhiteBalanceLockSupported() ?
314                    p.getAutoWhiteBalanceLock() : false;
315            m.set(CONTROL_AWB_LOCK, lock);
316        }
317    }
318
319    private static MeteringRectangle[] getMeteringRectangles(Rect activeArray, ZoomData zoomData,
320            List<Camera.Area> meteringAreaList, String regionName) {
321        List<MeteringRectangle> meteringRectList = new ArrayList<>();
322        if (meteringAreaList != null) {
323            for (Camera.Area area : meteringAreaList) {
324                WeightedRectangle rect =
325                        ParameterUtils.convertCameraAreaToActiveArrayRectangle(
326                                activeArray, zoomData, area);
327
328                meteringRectList.add(rect.toMetering());
329            }
330        }
331
332        if (VERBOSE) {
333            Log.v(TAG,
334                    "Metering rectangles for " + regionName + ": "
335                     + ListUtils.listToString(meteringRectList));
336        }
337
338        return meteringRectList.toArray(new MeteringRectangle[0]);
339    }
340
341    /** Map results for control.aeMode, flash.mode, flash.state */
342    private static void mapAeAndFlashMode(CameraMetadataNative m,
343            CameraCharacteristics characteristics, Parameters p) {
344        // Default: AE mode on but flash never fires
345        int flashMode = FLASH_MODE_OFF;
346        // If there is no flash on this camera, the state is always unavailable
347        // , otherwise it's only known for TORCH/SINGLE modes
348        Integer flashState = characteristics.get(CameraCharacteristics.FLASH_INFO_AVAILABLE)
349                ? null : FLASH_STATE_UNAVAILABLE;
350        int aeMode = CONTROL_AE_MODE_ON;
351
352        String flashModeSetting = p.getFlashMode();
353
354        if (flashModeSetting != null) {
355            switch (flashModeSetting) {
356                case Parameters.FLASH_MODE_OFF:
357                    break; // ok, using default
358                case Parameters.FLASH_MODE_AUTO:
359                    aeMode = CONTROL_AE_MODE_ON_AUTO_FLASH;
360                    break;
361                case Parameters.FLASH_MODE_ON:
362                    // flashMode = SINGLE + aeMode = ON is indistinguishable from ON_ALWAYS_FLASH
363                    flashMode = FLASH_MODE_SINGLE;
364                    aeMode = CONTROL_AE_MODE_ON_ALWAYS_FLASH;
365                    flashState = FLASH_STATE_FIRED;
366                    break;
367                case Parameters.FLASH_MODE_RED_EYE:
368                    aeMode = CONTROL_AE_MODE_ON_AUTO_FLASH_REDEYE;
369                    break;
370                case Parameters.FLASH_MODE_TORCH:
371                    flashMode = FLASH_MODE_TORCH;
372                    flashState = FLASH_STATE_FIRED;
373                    break;
374                default:
375                    Log.w(TAG,
376                            "mapAeAndFlashMode - Ignoring unknown flash mode " + p.getFlashMode());
377            }
378        }
379
380        // flash.state
381        m.set(FLASH_STATE, flashState);
382        // flash.mode
383        m.set(FLASH_MODE, flashMode);
384        // control.aeMode
385        m.set(CONTROL_AE_MODE, aeMode);
386    }
387
388    private static int convertLegacyAfMode(String mode) {
389        if (mode == null) {
390            Log.w(TAG, "convertLegacyAfMode - no AF mode, default to OFF");
391            return CONTROL_AF_MODE_OFF;
392        }
393
394        switch (mode) {
395            case Parameters.FOCUS_MODE_AUTO:
396                return CONTROL_AF_MODE_AUTO;
397            case Parameters.FOCUS_MODE_CONTINUOUS_PICTURE:
398                return CONTROL_AF_MODE_CONTINUOUS_PICTURE;
399            case Parameters.FOCUS_MODE_CONTINUOUS_VIDEO:
400                return CONTROL_AF_MODE_CONTINUOUS_VIDEO;
401            case Parameters.FOCUS_MODE_EDOF:
402                return CONTROL_AF_MODE_EDOF;
403            case Parameters.FOCUS_MODE_MACRO:
404                return CONTROL_AF_MODE_MACRO;
405            case Parameters.FOCUS_MODE_FIXED:
406                return CONTROL_AF_MODE_OFF;
407            case Parameters.FOCUS_MODE_INFINITY:
408                return CONTROL_AF_MODE_OFF;
409            default:
410                Log.w(TAG, "convertLegacyAfMode - unknown mode " + mode + " , ignoring");
411                return CONTROL_AF_MODE_OFF;
412        }
413    }
414
415    /** Map results for scaler.* */
416    private static void mapScaler(CameraMetadataNative m,
417            ZoomData zoomData,
418            /*out*/Parameters p) {
419        /*
420         * scaler.cropRegion
421         */
422        {
423            m.set(SCALER_CROP_REGION, zoomData.reportedCrop);
424        }
425    }
426}
427