Home | History | Annotate | Download | only in devcamera
      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