public class FaceCameraHelper {
private static final String TAG = "FaceCameraHelper";
public static final int SUPPORT_DEFAULT_WIDTH = 1920;
public static final int SUPPORT_DEFAULT_HEIGHT = 1080;
private Camera mCamera;
private int mCameraId;
private SurfaceHolder mSurfaceHolder;
private SurfaceView mSurfaceView;
private Activity mContext;
private Point mPreviewViewSize;
private Camera.Size mSize;
private CameraListener mCameraListener;
private int mRotateOrientation;
private FaceCameraHelper(Builder builder) {
mCameraId = builder.cameraId;
mSurfaceView = builder.surfaceView;
mContext = builder.activity;
mPreviewViewSize = builder.previewViewSize;
mCameraListener = builder.cameraListener;
init();
}
public interface CameraListener {
void previewData(byte[] data, Camera camera);
}
public void takePicture(Camera.PictureCallback pictureCallback) {
if (mCamera != null && pictureCallback != null) {
mCamera.takePicture(null, null, pictureCallback);
}
}
public void startPreview() {
if (mCamera != null) {
mCamera.startPreview();
}
}
public void stopPreview() {
if (mCamera != null) {
mCamera.stopPreview();
}
}
public static class Builder {
//当前使用前/后置摄像头 默认前置
private static final int CURRENT_FACING = Camera.CameraInfo.CAMERA_FACING_FRONT;
private int cameraId = CURRENT_FACING;
private Activity activity;
private SurfaceView surfaceView;
private Point previewViewSize;
private CameraListener cameraListener;
public Builder setCameraListener(CameraListener cameraListener) {
this.cameraListener = cameraListener;
return this;
}
public Builder setPreviewViewSize(Point previewViewSize) {
this.previewViewSize = previewViewSize;
return this;
}
/**
* 摄像头
*
* @param cameraId 0:后置 1:前置
* @return 当前对象
*/
public Builder setCameraId(int cameraId) {
this.cameraId = cameraId;
return this;
}
public Builder setActivity(Activity activity) {
this.activity = activity;
return this;
}
public Builder setSurfaceView(SurfaceView surfaceView) {
this.surfaceView = surfaceView;
return this;
}
public FaceCameraHelper build() {
return new FaceCameraHelper(this);
}
}
private void init() {
Log.d(TAG, "init: ");
mSurfaceHolder = mSurfaceView.getHolder();
mSurfaceHolder.addCallback(new SurfaceHolder.Callback() {
@Override
public void surfaceCreated(SurfaceHolder holder) {
Log.d(TAG, "surfaceCreated: ");
// if (mCamera != null) {
// try {
// mCamera.setPreviewDisplay(holder);
// } catch (IOException e) {
// e.printStackTrace();
// }
// }
openCamera();
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
Log.d(TAG, "surfaceChanged: ");
// if (mSurfaceHolder.getSurface() == null) {
// // preview surface does not exist
// Log.e(TAG, "mHolder.getSurface() == null");
// return;
// }
// try {
// mCamera.stopPreview();
// } catch (Exception e) {
// // ignore: tried to stop a non-existent preview
// Log.e(TAG, "Error stopping camera preview: " + e.getMessage());
// }
// try {
// mCamera.setPreviewDisplay(mSurfaceHolder);
// mCamera.startPreview();
// } catch (Exception e) {
// // ignore: tried to stop a non-existent preview
// Log.d(TAG, "Error starting camera preview: " + e.getMessage());
// }
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
Log.d(TAG, "surfaceDestroyed: ");
closeCamera();
}
});
}
public synchronized void openCamera() {
Log.d(TAG, "openCamera: ");
if (mCamera == null) {
mCamera = Camera.open(mCameraId);
}
mRotateOrientation = getRotateResult();
mCamera.setDisplayOrientation(mRotateOrientation);
Camera.Parameters parameters = mCamera.getParameters();
mSize = parameters.getPreviewSize();
List<Camera.Size> supportedPreviewSizes = parameters.getSupportedPreviewSizes();
if (supportedPreviewSizes != null && supportedPreviewSizes.size() > 0) {
mSize = getBestSupportedSize(supportedPreviewSizes, mPreviewViewSize);
}
parameters.setPreviewSize(mSize.width, mSize.height);
parameters.setPictureSize(mSize.width, mSize.height);//需要设置这个参数,不然拍照时有些手机(vivo x21)手机输出的图片非常模糊
//对焦模式设置
List<String> supportedFocusModes = parameters.getSupportedFocusModes();
if (supportedFocusModes != null && supportedFocusModes.size() > 0) {
if (supportedFocusModes.contains(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE)) {
parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
} else if (supportedFocusModes.contains(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO)) {
parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO);
} else if (supportedFocusModes.contains(Camera.Parameters.FOCUS_MODE_AUTO)) {
parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);
}
}
try {
mCamera.setParameters(parameters);
} catch (Exception e) {
e.printStackTrace();
resolveSamePhoneException(parameters);
}
try {
mCamera.setPreviewDisplay(mSurfaceHolder);
} catch (IOException e) {
e.printStackTrace();
}
mCamera.setPreviewCallback(mPreviewCallback);
mCamera.startPreview();
}
//个别机型(小米6x)在SupportPreviewSizes里汇报了支持某种预览尺寸,但实际是不支持的,设置进去就会抛出RuntimeException.
private void resolveSamePhoneException(Camera.Parameters parameters) {
try {
//遇到上面所说的情况,只能设置一个最小的预览尺寸
parameters.setPreviewSize(SUPPORT_DEFAULT_WIDTH, SUPPORT_DEFAULT_HEIGHT);
mCamera.setParameters(parameters);
} catch (Exception e1) {
//到这里还有问题,就是拍照尺寸的锅了,同样只能设置一个最小的拍照尺寸
e1.printStackTrace();
try {
parameters.setPictureSize(SUPPORT_DEFAULT_WIDTH, SUPPORT_DEFAULT_HEIGHT);
mCamera.setParameters(parameters);
} catch (Exception ignored) {
}
}
}
public int getRotateOrientation() {
return mRotateOrientation;
}
private Camera.PreviewCallback mPreviewCallback = new Camera.PreviewCallback() {
@Override
public void onPreviewFrame(byte[] data, Camera camera) {
if (mCameraListener != null) {
mCameraListener.previewData(data, camera);
}
}
};
public synchronized void closeCamera() {
Log.d(TAG, "closeCamera: ");
if (mCamera != null) {
mCamera.setPreviewCallback(null);
mCamera.stopPreview();
mCamera.release();
mCamera = null;
}
}
private Camera.Size getBestSupportedSize(List<Camera.Size> sizes, Point previewViewSize) {
if (sizes == null || sizes.size() == 0) {
return mCamera.getParameters().getPreviewSize();
}
Camera.Size[] tempSizes = sizes.toArray(new Camera.Size[0]);
Arrays.sort(tempSizes, new Comparator<Camera.Size>() {
@Override
public int compare(Camera.Size o1, Camera.Size o2) {
if (o1.width > o2.width) {
return -1;
} else if (o1.width == o2.width) {
return o1.height > o2.height ? -1 : 1;
} else {
return 1;
}
}
});
sizes = Arrays.asList(tempSizes);
Camera.Size bestSize = sizes.get(0);
float previewViewRatio;
if (previewViewSize != null) {
previewViewRatio = (float) previewViewSize.x / (float) previewViewSize.y;
} else {
previewViewRatio = (float) bestSize.width / (float) bestSize.height;
}
if (previewViewRatio > 1) {
previewViewRatio = 1 / previewViewRatio;
}
boolean isNormalRotate = (0 % 180 == 0);
for (Camera.Size s : sizes) {
if (mPreviewViewSize != null && mPreviewViewSize.x == s.width && mPreviewViewSize.y == s.height) {
return s;
}
if (isNormalRotate) {
if (Math.abs((s.height / (float) s.width) - previewViewRatio) < Math.abs(bestSize.height / (float) bestSize.width - previewViewRatio)) {
bestSize = s;
}
} else {
if (Math.abs((s.width / (float) s.height) - previewViewRatio) < Math.abs(bestSize.width / (float) bestSize.height - previewViewRatio)) {
bestSize = s;
}
}
}
return bestSize;
}
private int getRotateResult() {
Camera.CameraInfo info = new Camera.CameraInfo();
Camera.getCameraInfo(mCameraId, info);
int rotation = mContext.getWindowManager().getDefaultDisplay().getRotation();
int degrees = 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;
}
int result;
if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
result = (info.orientation + degrees) % 360;
result = (360 - result) % 360;
} else {
result = (info.orientation - degrees + 360) % 360;
}
return result;
}
}
使用:
FaceCameraHelper mFaceCameraHelper = new FaceCameraHelper.Builder()
.setCameraId(1)//1:表示前置摄像头
.setPreviewViewSize(new Point(mSurfaceView.getMeasuredWidth(), mSurfaceView.getMeasuredHeight()))
.setActivity(this)
.setSurfaceView(mSurfaceView)
.setCameraListener(null)//数据回调
.build();
本文介绍了一个用于控制摄像头预览及拍照的Java类FaceCameraHelper。该类支持前后置摄像头切换,提供预览尺寸配置,并实现了相机开启、关闭、预览开始与停止等功能。此外,还介绍了如何设置最佳支持尺寸和旋转角度。
3583

被折叠的 条评论
为什么被折叠?



