1/*
2 * Copyright (C) 2016 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 */
16package com.android.devcamera;
17
18import android.graphics.ImageFormat;
19import android.graphics.Rect;
20import android.hardware.camera2.CameraCharacteristics;
21import android.hardware.camera2.CameraManager;
22import android.hardware.camera2.CameraMetadata;
23import android.hardware.camera2.params.StreamConfigurationMap;
24import android.os.Build;
25import android.util.Log;
26import android.util.Size;
27import android.util.SizeF;
28
29/**
30 * Caches (static) information about the first/main camera.
31 * Convenience functions represent data from CameraCharacteristics.
32 */
33
34public class CameraInfoCache {
35    private static final String TAG = "DevCamera_CAMINFO";
36
37    public static final boolean IS_NEXUS_6 = "shamu".equalsIgnoreCase(Build.DEVICE);
38
39    public int[] noiseModes;
40    public int[] edgeModes;
41
42    private CameraCharacteristics mCameraCharacteristics;
43    private String mCameraId;
44    private Size mLargestYuvSize;
45    private Size mLargestJpegSize;
46    private Size mRawSize;
47    private Rect mActiveArea;
48    private Integer mSensorOrientation;
49    private Integer mRawFormat;
50    private int mBestFaceMode;
51    private int mHardwareLevel;
52
53    /**
54     * Constructor.
55     */
56    public CameraInfoCache(CameraManager cameraMgr, boolean useFrontCamera) {
57        String[] cameralist;
58        try {
59            cameralist = cameraMgr.getCameraIdList();
60            for (String id : cameralist) {
61                mCameraCharacteristics = cameraMgr.getCameraCharacteristics(id);
62                Integer facing = mCameraCharacteristics.get(CameraCharacteristics.LENS_FACING);
63                if (facing == (useFrontCamera ? CameraMetadata.LENS_FACING_FRONT : CameraMetadata.LENS_FACING_BACK)) {
64                    mCameraId = id;
65                    break;
66                }
67            }
68        } catch (Exception e) {
69            Log.e(TAG, "ERROR: Could not get camera ID list / no camera information is available: " + e);
70            return;
71        }
72        // Should have mCameraId as this point.
73        if (mCameraId == null) {
74            Log.e(TAG, "ERROR: Could not find a suitable rear or front camera.");
75            return;
76        }
77
78        // Store YUV_420_888, JPEG, Raw info
79        StreamConfigurationMap map = mCameraCharacteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
80        int[] formats = map.getOutputFormats();
81        long lowestStall = Long.MAX_VALUE;
82        for (int i = 0; i < formats.length; i++) {
83            if (formats[i] == ImageFormat.YUV_420_888) {
84                mLargestYuvSize = returnLargestSize(map.getOutputSizes(formats[i]));
85            }
86            if (formats[i] == ImageFormat.JPEG) {
87                mLargestJpegSize = returnLargestSize(map.getOutputSizes(formats[i]));
88            }
89            if (formats[i] == ImageFormat.RAW10 || formats[i] == ImageFormat.RAW_SENSOR) { // TODO: Add RAW12
90                Size size = returnLargestSize(map.getOutputSizes(formats[i]));
91                long stall = map.getOutputStallDuration(formats[i], size);
92                if (stall < lowestStall) {
93                    mRawFormat = formats[i];
94                    mRawSize = size;
95                    lowestStall = stall;
96                }
97            }
98        }
99
100        mActiveArea = mCameraCharacteristics.get(CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE);
101
102        // Compute best face mode.
103        int[] faceModes = mCameraCharacteristics.get(CameraCharacteristics.STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES);
104        for (int i=0; i<faceModes.length; i++) {
105            if (faceModes[i] > mBestFaceMode) {
106                mBestFaceMode = faceModes[i];
107            }
108        }
109        edgeModes = mCameraCharacteristics.get(CameraCharacteristics.EDGE_AVAILABLE_EDGE_MODES);
110        noiseModes = mCameraCharacteristics.get(CameraCharacteristics.NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES);
111
112        // Misc stuff.
113        mHardwareLevel = mCameraCharacteristics.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
114
115        mSensorOrientation = mCameraCharacteristics.get(CameraCharacteristics.SENSOR_ORIENTATION);
116    }
117
118    boolean supportedModesContains(int[] modes, int mode) {
119        for (int m : modes) {
120            if (m == mode) return true;
121        }
122        return false;
123    }
124
125    public int sensorOrientation() {
126        return mSensorOrientation;
127    }
128
129    public boolean isCamera2FullModeAvailable() {
130        return isHardwareLevelAtLeast(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL);
131    }
132
133    public boolean isHardwareLevelAtLeast(int level) {
134        // Special-case LEGACY since it has numerical value 2
135        if (level == CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY) {
136            // All devices are at least LEGACY
137            return true;
138        }
139        if (mHardwareLevel == CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY) {
140            // Since level isn't LEGACY
141            return false;
142        }
143        // All other levels can be compared numerically
144        return mHardwareLevel >= level;
145    }
146
147    public boolean isCapabilitySupported(int capability) {
148        int[] caps = mCameraCharacteristics.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
149        for (int c: caps) {
150            if (c == capability) return true;
151        }
152        return false;
153    }
154
155    public float getDiopterLow() {
156        return 0f; // Infinity
157    }
158
159    public float getDiopterHi() {
160        Float minFocusDistance =
161                mCameraCharacteristics.get(CameraCharacteristics.LENS_INFO_MINIMUM_FOCUS_DISTANCE);
162        // LEGACY devices don't report this, but they won't report focus distance anyway, so just
163        // default to zero
164        return (minFocusDistance == null) ? 0.0f : minFocusDistance;
165    }
166
167    /**
168     * Calculate camera device horizontal and vertical fields of view.
169     *
170     * @return horizontal and vertical field of view, in degrees.
171     */
172    public float[] getFieldOfView() {
173        float[] availableFocalLengths =
174                mCameraCharacteristics.get(CameraCharacteristics.LENS_INFO_AVAILABLE_FOCAL_LENGTHS);
175        float focalLength = 4.5f; // mm, default from Nexus 6P
176        if (availableFocalLengths == null || availableFocalLengths.length == 0) {
177            Log.e(TAG, "No focal length reported by camera device, assuming default " +
178                    focalLength);
179        } else {
180            focalLength = availableFocalLengths[0];
181        }
182        SizeF physicalSize =
183                mCameraCharacteristics.get(CameraCharacteristics.SENSOR_INFO_PHYSICAL_SIZE);
184        if (physicalSize == null) {
185            physicalSize = new SizeF(6.32f, 4.69f); // mm, default from Nexus 6P
186            Log.e(TAG, "No physical sensor dimensions reported by camera device, assuming default "
187                    + physicalSize);
188        }
189
190        // Only active array is actually visible, so calculate fraction of physicalSize that it takes up
191        Size pixelArraySize = mCameraCharacteristics.get(CameraCharacteristics.SENSOR_INFO_PIXEL_ARRAY_SIZE);
192        Rect activeArraySize = mCameraCharacteristics.get(CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE);
193
194        float activeWidthFraction = activeArraySize.width() / (float) pixelArraySize.getWidth();
195        float activeHeightFraction = activeArraySize.height() / (float) pixelArraySize.getHeight();
196
197        // Simple rectilinear lens field of view formula:
198        //   angle of view = 2 * arctan ( active size / (2 * focal length) )
199        float[] fieldOfView = new float[2];
200        fieldOfView[0] = (float) Math.toDegrees(
201                2 * Math.atan(physicalSize.getWidth() * activeWidthFraction / 2 / focalLength));
202        fieldOfView[1] = (float) Math.toDegrees(
203                2 * Math.atan(physicalSize.getHeight() * activeHeightFraction / 2 / focalLength));
204
205        return fieldOfView;
206    }
207    /**
208     * Private utility function.
209     */
210    private Size returnLargestSize(Size[] sizes) {
211        Size largestSize = null;
212        int area = 0;
213        for (int j = 0; j < sizes.length; j++) {
214            if (sizes[j].getHeight() * sizes[j].getWidth() > area) {
215                area = sizes[j].getHeight() * sizes[j].getWidth();
216                largestSize = sizes[j];
217            }
218        }
219        return largestSize;
220    }
221
222    public int bestFaceDetectionMode() {
223        return mBestFaceMode;
224    }
225
226    public int faceOffsetX() {
227        return (mActiveArea.width() - mLargestYuvSize.getWidth()) / 2;
228    }
229
230    public int faceOffsetY() {
231        return (mActiveArea.height() - mLargestYuvSize.getHeight()) / 2;
232    }
233
234    public int activeAreaWidth() {
235        return mActiveArea.width();
236    }
237
238    public int activeAreaHeight() {
239        return mActiveArea.height();
240    }
241
242    public Rect getActiveAreaRect() {
243        return mActiveArea;
244    }
245
246    public String getCameraId() {
247        return mCameraId;
248    }
249
250    public Size getPreviewSize() {
251        float aspect = mLargestYuvSize.getWidth() / mLargestYuvSize.getHeight();
252        aspect = aspect > 1f ? aspect : 1f / aspect;
253        if (aspect > 1.6) {
254            return new Size(1920, 1080); // TODO: Check available resolutions.
255        }
256        if (isHardwareLevelAtLeast(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_3)) {
257            // Bigger preview size for more advanced devices
258            return new Size(1440, 1080);
259        }
260        return new Size(1280, 960); // TODO: Check available resolutions.
261    }
262
263    public Size getJpegStreamSize() {
264        return mLargestJpegSize;
265    }
266
267    public Size getYuvStream1Size() {
268        return mLargestYuvSize;
269    }
270
271    public Size getYuvStream2Size() {
272        return new Size(320, 240);
273    }
274
275    public boolean rawAvailable() {
276        return mRawSize != null;
277    }
278    public boolean isYuvReprocessingAvailable() {
279        return isCapabilitySupported(
280                CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_YUV_REPROCESSING);
281    }
282
283    public Integer getRawFormat() {
284        return mRawFormat;
285    }
286
287    public Size getRawStreamSize() {
288        return mRawSize;
289    }
290
291}
292