You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
300 lines
11 KiB
300 lines
11 KiB
/*
|
|
* Copyright (C) 2016 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
package com.android.devcamera;
|
|
|
|
import android.graphics.ImageFormat;
|
|
import android.graphics.Rect;
|
|
import android.hardware.camera2.CameraCharacteristics;
|
|
import android.hardware.camera2.CameraManager;
|
|
import android.hardware.camera2.CameraMetadata;
|
|
import android.hardware.camera2.params.StreamConfigurationMap;
|
|
import android.os.Build;
|
|
import android.util.Log;
|
|
import android.util.Size;
|
|
import android.util.SizeF;
|
|
|
|
/**
|
|
* Caches (static) information about the first/main camera.
|
|
* Convenience functions represent data from CameraCharacteristics.
|
|
*/
|
|
|
|
public class CameraInfoCache {
|
|
private static final String TAG = "DevCamera_CAMINFO";
|
|
|
|
public static final boolean IS_NEXUS_6 = "shamu".equalsIgnoreCase(Build.DEVICE);
|
|
|
|
public int[] noiseModes;
|
|
public int[] edgeModes;
|
|
|
|
private CameraCharacteristics mCameraCharacteristics;
|
|
private String mCameraId;
|
|
private Size mLargestYuvSize;
|
|
private Size mLargestJpegSize;
|
|
private Size mRawSize;
|
|
private Rect mActiveArea;
|
|
private Integer mSensorOrientation;
|
|
private Integer mRawFormat;
|
|
private int mBestFaceMode;
|
|
private int mHardwareLevel;
|
|
private Size mDepthCloudSize = null;
|
|
|
|
/**
|
|
* Constructor.
|
|
*/
|
|
public CameraInfoCache(CameraManager cameraMgr, boolean useFrontCamera) {
|
|
String[] cameralist;
|
|
try {
|
|
cameralist = cameraMgr.getCameraIdList();
|
|
for (String id : cameralist) {
|
|
mCameraCharacteristics = cameraMgr.getCameraCharacteristics(id);
|
|
Integer facing = mCameraCharacteristics.get(CameraCharacteristics.LENS_FACING);
|
|
if (facing == (useFrontCamera ? CameraMetadata.LENS_FACING_FRONT : CameraMetadata.LENS_FACING_BACK)) {
|
|
mCameraId = id;
|
|
break;
|
|
}
|
|
}
|
|
} catch (Exception e) {
|
|
Log.e(TAG, "ERROR: Could not get camera ID list / no camera information is available: " + e);
|
|
return;
|
|
}
|
|
// Should have mCameraId as this point.
|
|
if (mCameraId == null) {
|
|
Log.e(TAG, "ERROR: Could not find a suitable rear or front camera.");
|
|
return;
|
|
}
|
|
|
|
// Store YUV_420_888, JPEG, Raw info
|
|
StreamConfigurationMap map = mCameraCharacteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
|
|
int[] formats = map.getOutputFormats();
|
|
long lowestStall = Long.MAX_VALUE;
|
|
for (int i = 0; i < formats.length; i++) {
|
|
if (formats[i] == ImageFormat.YUV_420_888) {
|
|
mLargestYuvSize = returnLargestSize(map.getOutputSizes(formats[i]));
|
|
}
|
|
if (formats[i] == ImageFormat.JPEG) {
|
|
mLargestJpegSize = returnLargestSize(map.getOutputSizes(formats[i]));
|
|
}
|
|
if (formats[i] == ImageFormat.RAW10 || formats[i] == ImageFormat.RAW_SENSOR) { // TODO: Add RAW12
|
|
Size size = returnLargestSize(map.getOutputSizes(formats[i]));
|
|
long stall = map.getOutputStallDuration(formats[i], size);
|
|
if (stall < lowestStall) {
|
|
mRawFormat = formats[i];
|
|
mRawSize = size;
|
|
lowestStall = stall;
|
|
}
|
|
}
|
|
if (formats[i] == ImageFormat.DEPTH_POINT_CLOUD) {
|
|
Size size = returnLargestSize(map.getOutputSizes(formats[i]));
|
|
mDepthCloudSize = size;
|
|
}
|
|
}
|
|
|
|
mActiveArea = mCameraCharacteristics.get(CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE);
|
|
|
|
// Compute best face mode.
|
|
int[] faceModes = mCameraCharacteristics.get(CameraCharacteristics.STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES);
|
|
for (int i=0; i<faceModes.length; i++) {
|
|
if (faceModes[i] > mBestFaceMode) {
|
|
mBestFaceMode = faceModes[i];
|
|
}
|
|
}
|
|
edgeModes = mCameraCharacteristics.get(CameraCharacteristics.EDGE_AVAILABLE_EDGE_MODES);
|
|
noiseModes = mCameraCharacteristics.get(CameraCharacteristics.NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES);
|
|
|
|
// Misc stuff.
|
|
mHardwareLevel = mCameraCharacteristics.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
|
|
|
|
mSensorOrientation = mCameraCharacteristics.get(CameraCharacteristics.SENSOR_ORIENTATION);
|
|
}
|
|
|
|
boolean supportedModesContains(int[] modes, int mode) {
|
|
for (int m : modes) {
|
|
if (m == mode) return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public int sensorOrientation() {
|
|
return mSensorOrientation;
|
|
}
|
|
|
|
public boolean isCamera2FullModeAvailable() {
|
|
return isHardwareLevelAtLeast(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL);
|
|
}
|
|
|
|
public boolean isHardwareLevelAtLeast(int level) {
|
|
// Special-case LEGACY since it has numerical value 2
|
|
if (level == CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY) {
|
|
// All devices are at least LEGACY
|
|
return true;
|
|
}
|
|
if (mHardwareLevel == CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY) {
|
|
// Since level isn't LEGACY
|
|
return false;
|
|
}
|
|
// All other levels can be compared numerically
|
|
return mHardwareLevel >= level;
|
|
}
|
|
|
|
public boolean isCapabilitySupported(int capability) {
|
|
int[] caps = mCameraCharacteristics.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
|
|
for (int c: caps) {
|
|
if (c == capability) return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public float getDiopterLow() {
|
|
return 0f; // Infinity
|
|
}
|
|
|
|
public float getDiopterHi() {
|
|
Float minFocusDistance =
|
|
mCameraCharacteristics.get(CameraCharacteristics.LENS_INFO_MINIMUM_FOCUS_DISTANCE);
|
|
// LEGACY devices don't report this, but they won't report focus distance anyway, so just
|
|
// default to zero
|
|
return (minFocusDistance == null) ? 0.0f : minFocusDistance;
|
|
}
|
|
|
|
/**
|
|
* Calculate camera device horizontal and vertical fields of view.
|
|
*
|
|
* @return horizontal and vertical field of view, in degrees.
|
|
*/
|
|
public float[] getFieldOfView() {
|
|
float[] availableFocalLengths =
|
|
mCameraCharacteristics.get(CameraCharacteristics.LENS_INFO_AVAILABLE_FOCAL_LENGTHS);
|
|
float focalLength = 4.5f; // mm, default from Nexus 6P
|
|
if (availableFocalLengths == null || availableFocalLengths.length == 0) {
|
|
Log.e(TAG, "No focal length reported by camera device, assuming default " +
|
|
focalLength);
|
|
} else {
|
|
focalLength = availableFocalLengths[0];
|
|
}
|
|
SizeF physicalSize =
|
|
mCameraCharacteristics.get(CameraCharacteristics.SENSOR_INFO_PHYSICAL_SIZE);
|
|
if (physicalSize == null) {
|
|
physicalSize = new SizeF(6.32f, 4.69f); // mm, default from Nexus 6P
|
|
Log.e(TAG, "No physical sensor dimensions reported by camera device, assuming default "
|
|
+ physicalSize);
|
|
}
|
|
|
|
// Only active array is actually visible, so calculate fraction of physicalSize that it takes up
|
|
Size pixelArraySize = mCameraCharacteristics.get(CameraCharacteristics.SENSOR_INFO_PIXEL_ARRAY_SIZE);
|
|
Rect activeArraySize = mCameraCharacteristics.get(CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE);
|
|
|
|
float activeWidthFraction = activeArraySize.width() / (float) pixelArraySize.getWidth();
|
|
float activeHeightFraction = activeArraySize.height() / (float) pixelArraySize.getHeight();
|
|
|
|
// Simple rectilinear lens field of view formula:
|
|
// angle of view = 2 * arctan ( active size / (2 * focal length) )
|
|
float[] fieldOfView = new float[2];
|
|
fieldOfView[0] = (float) Math.toDegrees(
|
|
2 * Math.atan(physicalSize.getWidth() * activeWidthFraction / 2 / focalLength));
|
|
fieldOfView[1] = (float) Math.toDegrees(
|
|
2 * Math.atan(physicalSize.getHeight() * activeHeightFraction / 2 / focalLength));
|
|
|
|
return fieldOfView;
|
|
}
|
|
/**
|
|
* Private utility function.
|
|
*/
|
|
private Size returnLargestSize(Size[] sizes) {
|
|
Size largestSize = null;
|
|
int area = 0;
|
|
for (int j = 0; j < sizes.length; j++) {
|
|
if (sizes[j].getHeight() * sizes[j].getWidth() > area) {
|
|
area = sizes[j].getHeight() * sizes[j].getWidth();
|
|
largestSize = sizes[j];
|
|
}
|
|
}
|
|
return largestSize;
|
|
}
|
|
|
|
public int bestFaceDetectionMode() {
|
|
return mBestFaceMode;
|
|
}
|
|
|
|
public int faceOffsetX() {
|
|
return (mActiveArea.width() - mLargestYuvSize.getWidth()) / 2;
|
|
}
|
|
|
|
public int faceOffsetY() {
|
|
return (mActiveArea.height() - mLargestYuvSize.getHeight()) / 2;
|
|
}
|
|
|
|
public int activeAreaWidth() {
|
|
return mActiveArea.width();
|
|
}
|
|
|
|
public int activeAreaHeight() {
|
|
return mActiveArea.height();
|
|
}
|
|
|
|
public Rect getActiveAreaRect() {
|
|
return mActiveArea;
|
|
}
|
|
|
|
public String getCameraId() {
|
|
return mCameraId;
|
|
}
|
|
|
|
public Size getPreviewSize() {
|
|
float aspect = mLargestYuvSize.getWidth() / mLargestYuvSize.getHeight();
|
|
aspect = aspect > 1f ? aspect : 1f / aspect;
|
|
if (aspect > 1.6) {
|
|
return new Size(1920, 1080); // TODO: Check available resolutions.
|
|
}
|
|
if (isHardwareLevelAtLeast(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_3)) {
|
|
// Bigger preview size for more advanced devices
|
|
return new Size(1440, 1080);
|
|
}
|
|
return new Size(1280, 960); // TODO: Check available resolutions.
|
|
}
|
|
|
|
public Size getJpegStreamSize() {
|
|
return mLargestJpegSize;
|
|
}
|
|
|
|
public Size getYuvStream1Size() {
|
|
return mLargestYuvSize;
|
|
}
|
|
|
|
public Size getYuvStream2Size() {
|
|
return new Size(320, 240);
|
|
}
|
|
|
|
public boolean rawAvailable() {
|
|
return mRawSize != null;
|
|
}
|
|
public boolean isYuvReprocessingAvailable() {
|
|
return isCapabilitySupported(
|
|
CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_YUV_REPROCESSING);
|
|
}
|
|
|
|
public Integer getRawFormat() {
|
|
return mRawFormat;
|
|
}
|
|
|
|
public Size getRawStreamSize() {
|
|
return mRawSize;
|
|
}
|
|
|
|
public Size getDepthCloudSize() {
|
|
return mDepthCloudSize;
|
|
}
|
|
}
|