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