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 */ 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 123 boolean supportedModesContains(int[] modes, int mode) { 124 for (int m : modes) { 125 if (m == mode) return true; 126 } 127 return false; 128 } 129 130 public int sensorOrientation() { 131 return mSensorOrientation; 132 } 133 134 public boolean isCamera2FullModeAvailable() { 135 return isHardwareLevelAtLeast(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL); 136 } 137 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 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 160 public float getDiopterLow() { 161 return 0f; // Infinity 162 } 163 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 */ 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 */ 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 227 public int bestFaceDetectionMode() { 228 return mBestFaceMode; 229 } 230 231 public int faceOffsetX() { 232 return (mActiveArea.width() - mLargestYuvSize.getWidth()) / 2; 233 } 234 235 public int faceOffsetY() { 236 return (mActiveArea.height() - mLargestYuvSize.getHeight()) / 2; 237 } 238 239 public int activeAreaWidth() { 240 return mActiveArea.width(); 241 } 242 243 public int activeAreaHeight() { 244 return mActiveArea.height(); 245 } 246 247 public Rect getActiveAreaRect() { 248 return mActiveArea; 249 } 250 251 public String getCameraId() { 252 return mCameraId; 253 } 254 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 268 public Size getJpegStreamSize() { 269 return mLargestJpegSize; 270 } 271 272 public Size getYuvStream1Size() { 273 return mLargestYuvSize; 274 } 275 276 public Size getYuvStream2Size() { 277 return new Size(320, 240); 278 } 279 280 public boolean rawAvailable() { 281 return mRawSize != null; 282 } 283 public boolean isYuvReprocessingAvailable() { 284 return isCapabilitySupported( 285 CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_YUV_REPROCESSING); 286 } 287 288 public Integer getRawFormat() { 289 return mRawFormat; 290 } 291 292 public Size getRawStreamSize() { 293 return mRawSize; 294 } 295 296 public Size getDepthCloudSize() { 297 return mDepthCloudSize; 298 } 299 } 300