LegacyMetadataMapper.java revision 91b9aabc9fa0c058ecc4a8b3f486540c28fe1cc0
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.ImageFormat;
20import android.hardware.Camera;
21import android.hardware.Camera.CameraInfo;
22import android.hardware.Camera.Size;
23import android.hardware.camera2.CameraCharacteristics;
24import android.hardware.camera2.CaptureRequest;
25import android.hardware.camera2.CaptureResult;
26import android.hardware.camera2.impl.CameraMetadataNative;
27import android.hardware.camera2.params.StreamConfiguration;
28import android.hardware.camera2.params.StreamConfigurationDuration;
29import android.util.Log;
30import android.util.Range;
31
32import java.util.ArrayList;
33import java.util.Arrays;
34import java.util.List;
35
36import static com.android.internal.util.Preconditions.*;
37import static android.hardware.camera2.CameraCharacteristics.*;
38
39/**
40 * Provide legacy-specific implementations of camera2 metadata for legacy devices, such as the
41 * camera characteristics.
42 */
43public class LegacyMetadataMapper {
44    private static final String TAG = "LegacyMetadataMapper";
45    private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
46
47    // from graphics.h
48    private static final int HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED = 0x22;
49    private static final int HAL_PIXEL_FORMAT_BLOB = 0x21;
50
51    private static final long APPROXIMATE_CAPTURE_DELAY_MS = 200; // ms
52    private static final long APPROXIMATE_SENSOR_AREA = (1 << 23); // 8mp
53    private static final long APPROXIMATE_JPEG_ENCODE_TIME = 600; // ms
54    private static final long NS_PER_MS = 1000000;
55
56    /**
57     * Create characteristics for a legacy device by mapping the {@code parameters}
58     * and {@code info}
59     *
60     * @param parameters A string parseable by {@link Camera.Parameters#unflatten}
61     * @param info Camera info with camera facing direction and angle of orientation
62     * @return static camera characteristics for a camera device
63     *
64     * @throws NullPointerException if any of the args were {@code null}
65     */
66    public static CameraCharacteristics createCharacteristics(String parameters,
67            android.hardware.CameraInfo info) {
68        checkNotNull(parameters, "parameters must not be null");
69        checkNotNull(info, "info must not be null");
70        checkNotNull(info.info, "info.info must not be null");
71
72        CameraMetadataNative m = new CameraMetadataNative();
73
74        mapCameraInfo(m, info.info);
75
76        Camera.Parameters params = Camera.getEmptyParameters();
77        params.unflatten(parameters);
78        mapCameraParameters(m, params);
79
80        if (VERBOSE) {
81            Log.v(TAG, "createCharacteristics metadata:");
82            Log.v(TAG, "--------------------------------------------------- (start)");
83            m.dumpToLog();
84            Log.v(TAG, "--------------------------------------------------- (end)");
85        }
86
87        return new CameraCharacteristics(m);
88    }
89
90    private static void mapCameraInfo(CameraMetadataNative m, CameraInfo i) {
91        m.set(LENS_FACING, i.facing == CameraInfo.CAMERA_FACING_BACK ?
92                LENS_FACING_BACK : LENS_FACING_FRONT);
93        m.set(SENSOR_ORIENTATION, i.orientation);
94    }
95
96    private static void mapCameraParameters(CameraMetadataNative m, Camera.Parameters p) {
97        mapStreamConfigs(m, p);
98        mapAeConfig(m, p);
99        mapCapabilities(m, p);
100
101        // TODO: map other fields
102    }
103
104    private static void mapStreamConfigs(CameraMetadataNative m, Camera.Parameters p) {
105
106        ArrayList<StreamConfiguration> availableStreamConfigs = new ArrayList<>();
107        /*
108         * Implementation-defined (preview, recording, etc) -> use camera1 preview sizes
109         * YUV_420_888 cpu callbacks -> use camera1 preview sizes
110         * Other preview callbacks (CPU) -> use camera1 preview sizes
111         * JPEG still capture -> use camera1 still capture sizes
112         *
113         * Use platform-internal format constants here, since StreamConfigurationMap does the
114         * remapping to public format constants.
115         */
116        List<Size> previewSizes = p.getSupportedPreviewSizes();
117        appendStreamConfig(availableStreamConfigs,
118                HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED, previewSizes);
119        appendStreamConfig(availableStreamConfigs,
120                ImageFormat.YUV_420_888, previewSizes);
121        for (int format : p.getSupportedPreviewFormats()) {
122            if (ImageFormat.isPublicFormat(format)) {
123                appendStreamConfig(availableStreamConfigs, format, previewSizes);
124            } else {
125                /*
126                 *  Do not add any formats unknown to us
127                 * (since it would fail runtime checks in StreamConfigurationMap)
128                 */
129                Log.w(TAG,
130                        String.format("mapStreamConfigs - Skipping non-public format %x", format));
131            }
132        }
133
134        List<Camera.Size> jpegSizes = p.getSupportedPictureSizes();
135        appendStreamConfig(availableStreamConfigs,
136                HAL_PIXEL_FORMAT_BLOB, p.getSupportedPictureSizes());
137        m.set(SCALER_AVAILABLE_STREAM_CONFIGURATIONS,
138                availableStreamConfigs.toArray(new StreamConfiguration[0]));
139
140        // No frame durations available
141        m.set(SCALER_AVAILABLE_MIN_FRAME_DURATIONS, new StreamConfigurationDuration[0]);
142
143        StreamConfigurationDuration[] jpegStalls =
144                new StreamConfigurationDuration[jpegSizes.size()];
145        int i = 0;
146        long longestStallDuration = -1;
147        for (Camera.Size s : jpegSizes) {
148            long stallDuration =  calculateJpegStallDuration(s);
149            jpegStalls[i++] = new StreamConfigurationDuration(HAL_PIXEL_FORMAT_BLOB, s.width,
150                    s.height, stallDuration);
151            if (longestStallDuration < stallDuration) {
152                longestStallDuration = stallDuration;
153            }
154        }
155        // Set stall durations for jpeg, other formats use default stall duration
156        m.set(SCALER_AVAILABLE_STALL_DURATIONS, jpegStalls);
157
158        m.set(SENSOR_INFO_MAX_FRAME_DURATION, longestStallDuration);
159    }
160
161    @SuppressWarnings({"unchecked"})
162    private static void mapAeConfig(CameraMetadataNative m, Camera.Parameters p) {
163
164        List<int[]> fpsRanges = p.getSupportedPreviewFpsRange();
165        if (fpsRanges == null) {
166            throw new AssertionError("Supported FPS ranges cannot be null.");
167        }
168        int rangesSize = fpsRanges.size();
169        if (rangesSize <= 0) {
170            throw new AssertionError("At least one FPS range must be supported.");
171        }
172        Range<Integer>[] ranges = new Range[rangesSize];
173        int i = 0;
174        for (int[] r : fpsRanges) {
175            ranges[i++] = Range.create(r[Camera.Parameters.PREVIEW_FPS_MIN_INDEX],
176                    r[Camera.Parameters.PREVIEW_FPS_MAX_INDEX]);
177        }
178        m.set(CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES, ranges);
179
180        List<String> antiBandingModes = p.getSupportedAntibanding();
181        int antiBandingModesSize = antiBandingModes.size();
182        if (antiBandingModesSize > 0) {
183            int[] modes = new int[antiBandingModesSize];
184            int j = 0;
185            for (String mode : antiBandingModes) {
186                int convertedMode = convertAntiBandingMode(mode);
187                if (convertedMode == -1) {
188                    Log.w(TAG, "Antibanding mode " + ((mode == null) ? "NULL" : mode) +
189                            " not supported, skipping...");
190                } else {
191                    modes[j++] = convertedMode;
192                }
193            }
194            m.set(CONTROL_AE_AVAILABLE_ANTIBANDING_MODES, Arrays.copyOf(modes, j));
195        }
196    }
197
198    private static void mapCapabilities(CameraMetadataNative m, Camera.Parameters p) {
199        int[] capabilities = { REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE };
200        m.set(REQUEST_AVAILABLE_CAPABILITIES, capabilities);
201    }
202
203    private static void appendStreamConfig(
204            ArrayList<StreamConfiguration> configs, int format, List<Camera.Size> sizes) {
205        for (Camera.Size size : sizes) {
206            StreamConfiguration config =
207                    new StreamConfiguration(format, size.width, size.height, /*input*/false);
208            configs.add(config);
209        }
210    }
211
212    /**
213     * Returns -1 if the anti-banding mode string is null, or not supported.
214     */
215    private static int convertAntiBandingMode(final String mode) {
216        if (mode == null) {
217            return -1;
218        }
219        switch(mode) {
220            case Camera.Parameters.ANTIBANDING_OFF: {
221                return CONTROL_AE_ANTIBANDING_MODE_OFF;
222            }
223            case Camera.Parameters.ANTIBANDING_50HZ: {
224                return CONTROL_AE_ANTIBANDING_MODE_50HZ;
225            }
226            case Camera.Parameters.ANTIBANDING_60HZ: {
227                return CONTROL_AE_ANTIBANDING_MODE_60HZ;
228            }
229            case Camera.Parameters.ANTIBANDING_AUTO: {
230                return CONTROL_AE_ANTIBANDING_MODE_AUTO;
231            }
232            default: {
233                return -1;
234            }
235        }
236    }
237
238    /**
239     * Returns null if the anti-banding mode enum is not supported.
240     */
241    private static String convertAntiBandingModeToLegacy(int mode) {
242        switch(mode) {
243            case CONTROL_AE_ANTIBANDING_MODE_OFF: {
244                return Camera.Parameters.ANTIBANDING_OFF;
245            }
246            case CONTROL_AE_ANTIBANDING_MODE_50HZ: {
247                return Camera.Parameters.ANTIBANDING_50HZ;
248            }
249            case CONTROL_AE_ANTIBANDING_MODE_60HZ: {
250                return Camera.Parameters.ANTIBANDING_60HZ;
251            }
252            case CONTROL_AE_ANTIBANDING_MODE_AUTO: {
253                return Camera.Parameters.ANTIBANDING_AUTO;
254            }
255            default: {
256                return null;
257            }
258        }
259    }
260
261
262    private static int[] convertAeFpsRangeToLegacy(Range<Integer> fpsRange) {
263        int[] legacyFps = new int[2];
264        legacyFps[Camera.Parameters.PREVIEW_FPS_MIN_INDEX] = fpsRange.getLower();
265        legacyFps[Camera.Parameters.PREVIEW_FPS_MAX_INDEX] = fpsRange.getUpper();
266        return legacyFps;
267    }
268
269    /**
270     * Return the stall duration for a given output jpeg size in nanoseconds.
271     *
272     * <p>An 8mp image is chosen to have a stall duration of 0.8 seconds.</p>
273     */
274    private static long calculateJpegStallDuration(Camera.Size size) {
275        long baseDuration = APPROXIMATE_CAPTURE_DELAY_MS * NS_PER_MS; // 200ms for capture
276        long area = size.width * (long) size.height;
277        long stallPerArea = APPROXIMATE_JPEG_ENCODE_TIME * NS_PER_MS /
278                APPROXIMATE_SENSOR_AREA; // 600ms stall for 8mp
279        return baseDuration + area * stallPerArea;
280    }
281
282    /**
283     * Generate capture result metadata from legacy camera parameters.
284     *
285     * @param params a {@link Camera.Parameters} object to generate metadata from.
286     * @param request the {@link CaptureRequest} used for this result.
287     * @param timestamp the timestamp to use for this result in nanoseconds.
288     * @return a {@link CameraMetadataNative} object containing result metadata.
289     */
290    public static CameraMetadataNative convertResultMetadata(Camera.Parameters params,
291                                                      CaptureRequest request,
292                                                      long timestamp) {
293        CameraMetadataNative result = new CameraMetadataNative();
294        result.set(CaptureResult.LENS_FOCAL_LENGTH, params.getFocalLength());
295        result.set(CaptureResult.SENSOR_TIMESTAMP, timestamp);
296
297        // TODO: Remaining result metadata tags conversions.
298        return result;
299    }
300
301    /**
302     * Set the legacy parameters using the request metadata.
303     *
304     * @param request a {@link CaptureRequest} object to generate parameters from.
305     * @param params the a {@link Camera.Parameters} to set parameters in.
306     */
307    public static void convertRequestMetadata(CaptureRequest request,
308            /*out*/Camera.Parameters params) {
309        Integer antiBandingMode = request.get(CaptureRequest.CONTROL_AE_ANTIBANDING_MODE);
310        if (antiBandingMode != null) {
311            String legacyMode = convertAntiBandingModeToLegacy(antiBandingMode);
312            if (legacyMode != null) params.setAntibanding(legacyMode);
313        }
314
315        Range<Integer> aeFpsRange = request.get(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE);
316        if (aeFpsRange != null) {
317            int[] legacyFps = convertAeFpsRangeToLegacy(aeFpsRange);
318            params.setPreviewFpsRange(legacyFps[Camera.Parameters.PREVIEW_FPS_MIN_INDEX],
319                    legacyFps[Camera.Parameters.PREVIEW_FPS_MAX_INDEX]);
320        }
321    }
322}
323