package com.eastmoney.android.kaihu.fragment;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.ImageFormat;
import android.hardware.Camera;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.WindowManager;
import com.sensetime.senseid.sdk.liveness.interactive.common.type.Size;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@SuppressWarnings({ "WeakerAccess", "unused", "deprecation" })
public class SenseCamera {
public static final int CAMERA_FACING_BACK = Camera.CameraInfo.CAMERA_FACING_BACK;
public static final int CAMERA_FACING_FRONT = Camera.CameraInfo.CAMERA_FACING_FRONT;
public static final int PREVIEW_SIZE_WIDTH = 640;
public static final int PREVIEW_SIZE_HEIGHT = 480;
private static final int DUMMY_TEXTURE_NAME = 100;
private static final float ASPECT_RATIO_TOLERANCE = 0.01f;
private final Object mCameraLock = new Object();
private Context mContext;
private Camera mCamera;
private int mFacing = CAMERA_FACING_BACK;
private int mRotation;
// Default preview size, Reference https://stackoverflow.com/a/21676309.
private Size mPreviewSize = new Size(PREVIEW_SIZE_WIDTH, PREVIEW_SIZE_HEIGHT);
private float mRequestedFps = 24.0f;
private int mRequestedPreviewWidth = PREVIEW_SIZE_WIDTH;
private int mRequestedPreviewHeight = PREVIEW_SIZE_HEIGHT;
private Map<byte[], ByteBuffer> mBytesToByteBuffer = new HashMap<>();
private Camera.PreviewCallback mPreviewCallback;
private SenseCamera() {
}
private static int getIdForRequestedCamera(final int facing) {
final Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
for (int i = 0; i < Camera.getNumberOfCameras(); ++i) {
Camera.getCameraInfo(i, cameraInfo);
if (cameraInfo.facing == facing) {
return i;
}
}
return -1;
}
private static SizePair selectSizePair(final Camera camera, final int desiredWidth, final int desiredHeight) {
final List<SizePair> validPreviewSizes = generateValidPreviewSizeList(camera);
SizePair selectedPair = null;
int minDiff = Integer.MAX_VALUE;
for (SizePair sizePair : validPreviewSizes) {
final Size size = sizePair.previewSize();
int diff = Math.abs(size.getWidth() - desiredWidth) + Math.abs(size.getHeight() - desiredHeight);
if (diff < minDiff) {
selectedPair = sizePair;
minDiff = diff;
}
}
return selectedPair;
}
private static List<SizePair> generateValidPreviewSizeList(final Camera camera) {
final Camera.Parameters parameters = camera.getParameters();
final List<Camera.Size> supportedPreviewSizes = parameters.getSupportedPreviewSizes();
final List<Camera.Size> supportedPictureSizes = parameters.getSupportedPictureSizes();
final List<SizePair> validPreviewSizes = new ArrayList<>();
for (final Camera.Size previewSize : supportedPreviewSizes) {
final float previewAspectRatio = (float) previewSize.width / (float) previewSize.height;
for (Camera.Size pictureSize : supportedPictureSizes) {
float pictureAspectRatio = (float) pictureSize.width / (float) pictureSize.height;
if (Math.abs(previewAspectRatio - pictureAspectRatio) < ASPECT_RATIO_TOLERANCE) {
validPreviewSizes.add(new SizePair(previewSize, pictureSize));
break;
}
}
}
if (validPreviewSizes.isEmpty()) {
for (Camera.Size previewSize : supportedPreviewSizes) {
validPreviewSizes.add(new SizePair(previewSize, null));
}
}
return validPreviewSizes;
}
/**
* release.
*/
public void release() {
synchronized (this.mCameraLock) {
this.stop();
}
}
/**
* start.
*/
@SuppressLint("MissingPermission")
public SenseCamera start(final SurfaceHolder surfaceHolder) throws IOException, RuntimeException {
synchronized (this.mCameraLock) {
if (this.mCamera != null) {
return this;
}
this.mCamera = createCamera();
this.mCamera.setPreviewDisplay(surfaceHolder);
this.mCamera.startPreview();
}
return this;
}
/**
* stop.
*/
public void stop() {
synchronized (this.mCameraLock) {
this.mBytesToByteBuffer.clear();
if (this.mCamera != null) {
this.mCamera.stopPreview();
this.mCamera.setPreviewCallbackWithBuffer(null);
try {
this.mCamera.setPreviewDisplay(null);
} catch (Exception e) {
e.printStackTrace();
}
this.mCamera.release();
this.mCamera = null;
}
}
}
/**
* get preview size.
*/
public Size getPreviewSize() {
return this.mPreviewSize;
}
/**
* get camera facing.
*/
public int getCameraFacing() {
return this.mFacing;
}
private Camera createCamera() throws RuntimeException {
final int requestedCameraId = SenseCamera.getIdForRequestedCamera(this.mFacing);
if (requestedCameraId == -1) {
throw new IllegalStateException("Could not find requested camera.");
}
final Camera camera = Camera.open(requestedCameraId);
if (camera == null) {
throw new IllegalStateException("Unknown camera error");
}
final SizePair sizePair =
SenseCamera.selectSizePair(camera, this.mRequestedPreviewWidth, this.mRequestedPreviewHeight);
if (sizePair == null) {
throw new IllegalStateException("Could not find suitable preview size.");
}
final Size pictureSize = sizePair.pictureSize();
this.mPreviewSize = sizePair.previewSize();
final int[] previewFpsRange = selectPreviewFpsRange(camera, this.mRequestedFps);
if (previewFpsRange == null) {
throw new IllegalStateException("Could not find suitable preview frames per second range.");
}
final Camera.Parameters parameters = camera.getParameters();
if (pictureSize != null) {
parameters.setPictureSize(pictureSize.getWidth(), pictureSize.getHeight());
}
parameters.setPreviewSize(this.mPreviewSize.getWidth(), this.mPreviewSize.getHeight());
parameters.setPreviewFpsRange(previewFpsRange[Camera.Parameters.PREVIEW_FPS_MIN_INDEX],
previewFpsRange[Camera.Parameters.PREVIEW_FPS_MAX_INDEX]);
parameters.setPreviewFormat(ImageFormat.NV21);
this.setRotation(camera, parameters, requestedCameraId);
if (parameters.getSupportedFocusModes().contains(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO)) {
parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO);
}
camera.setParameters(parameters);
camera.setPreviewCallbackWithBuffer(new Camera.PreviewCallback() {
@Override
public void onPreviewFrame(byte[] data, Camera camera) {
camera.addCallbackBuffer(SenseCamera.this.mBytesToByteBuffer.get(data).array());
if (SenseCamera.this.mPreviewCallback != null) {
SenseCamera.this.mPreviewCallback.onPreviewFrame(
SenseCamera.this.mBytesToByteBuffer.get(data).array(), camera);
}
}
});
camera.addCallbackBuffer(this.createPreviewBuffer(this.mPreviewSize));
return camera;
}
/**
* set preview Callback.
*
* @param callback callback.
*/
public void setOnPreviewFrameCallback(final Camera.PreviewCallback callback) {
this.mPreviewCallback = callback;
}
private int[] selectPreviewFpsRange(final Camera camera, final float desiredPreviewFps) {
final int desiredPreviewFpsScaled = (int) (desiredPreviewFps * 1000.0f);
int[] selectedFpsRange = null;
int minDiff = Integer.MAX_VALUE;
List<int[]> previewFpsRangeList = camera.getParameters().getSupportedPreviewFpsRange();
for (int[] range : previewFpsRangeList) {
int deltaMin = desiredPreviewFpsScaled - range[Camera.Parameters.PREVIEW_FPS_MIN_INDEX];
int deltaMax = desiredPreviewFpsScaled - range[Camera.Parameters.PREVIEW_FPS_MAX_INDEX];
int diff = Math.abs(deltaMin) + Math.abs(deltaMax);
if (diff < minDiff) {
selectedFpsRange = range;
minDiff = diff;
}
}
return selectedFpsRange;
}
private void setRotation(final Camera camera, final Camera.Parameters parameters, final int cameraId) {
final WindowManager windowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
int degrees = 0;
int rotation = windowManager != null ? windowManager.getDefaultDisplay().getRotation() : Surface.ROTATION_0;
switch (rotation) {
case Surface.ROTATION_0:
degrees = 0;
break;
case Surface.ROTATION_90:
degrees = 90;
break;
case Surface.ROTATION_180:
degrees = 180;
break;
case Surface.ROTATION_270:
degrees = 270;
break;
default:
break;
}
final Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
Camera.getCameraInfo(cameraId, cameraInfo);
int angle;
int displayAngle;
if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
angle = (cameraInfo.orientation + degrees) % 360;
displayAngle = (360 - angle) % 360; // compensate for it being mirrored
} else { // back-facing
angle = (cameraInfo.orientation - degrees + 360) % 360;
displayAngle = angle;
}
this.mRotation = angle / 90;
camera.setDisplayOrientation(displayAngle);
parameters.setRotation(angle);
}
/**
* get Rotation Degrees.
*
* @return Degrees.
*/
public int getRotationDegrees() {
return this.mRotation * 90;
}
private byte[] createPreviewBuffer(Size previewSize) {
final int bitsPerPixel = ImageFormat.getBitsPerPixel(ImageFormat.NV21);
final long sizeInBits = previewSize.getHeight() * previewSize.getWidth() * bitsPerPixel;
final int bufferSize = (int) Math.ceil(sizeInBits / 8.0d) + 1;
final byte[] byteArray = new byte[bufferSize];
final ByteBuffer buffer = ByteBuffer.wrap(byteArray);
if (!buffer.hasArray() || (buffer.array() != byteArray)) {
throw new IllegalStateException("Failed to create valid buffer for camera.");
}
this.mBytesToByteBuffer.put(byteArray, buffer);
return byteArray;
}
public static class Builder {
private final SenseCamera mSenseCamera = new SenseCamera();
/**
* Camera Builder.
*/
public Builder(final Context context) {
if (context == null) {
throw new IllegalArgumentException("No context supplied.");
}
this.mSenseCamera.mContext = context;
}
/**
* set requested fps.
*/
public Builder setRequestedFps(final float fps) {
if (fps <= 0) {
throw new IllegalArgumentException("Invalid fps: " + fps);
}
this.mSenseCamera.mRequestedFps = fps;
return this;
}
/**
* set requested preview size.
*/
public Builder setRequestedPreviewSize(final int width, final int height) {
final int max = 1000000;
if ((width <= 0) || (width > max) || (height <= 0) || (height > max)) {
throw new IllegalArgumentException("Invalid preview size: " + width + "x" + height);
}
this.mSenseCamera.mRequestedPreviewWidth = width;
this.mSenseCamera.mRequestedPreviewHeight = height;
return this;
}
/**
* set camera facing.
*/
public Builder setFacing(final int facing) {
if ((facing != CAMERA_FACING_BACK) && (facing != CAMERA_FACING_FRONT)) {
throw new IllegalArgumentException("Invalid camera: " + facing);
}
this.mSenseCamera.mFacing = facing;
return this;
}
/**
* build camera.
*/
public SenseCamera build() {
return this.mSenseCamera;
}
}
private static class SizePair {
private Size mPreview;
private Size mPicture;
public SizePair(final Camera.Size previewSize, final Camera.Size pictureSize) {
mPreview = new Size(previewSize.width, previewSize.height);
if (pictureSize != null) {
mPicture = new Size(pictureSize.width, pictureSize.height);
}
}
public Size previewSize() {
return this.mPreview;
}
@SuppressWarnings("unused")
public Size pictureSize() {
return this.mPicture;
}
}
}
网友评论