之前用过camera1做过多路摄像头预览,但是在监听camera usb这功能出现了问题,因为热拔插原因,拔出camera usb时camera id可能会出现错乱问题,而camera1获取id方法只能通过for循环递增方法获取;而camera2可通过CameraManager.getCameraIdList()获取到所有的cameraId.而且camera1能实现的功能camera2也可以.所有最终决定用camera2开发.
我这里主要给配置功能的关键代码,剩余的只要对camera2了解的,基本都能实现; ,如果遇到什么困难的,可以在评论区问我,我都会帮忙解决.
对camera2实现单个摄像头预览拍照的过程有了解的话,实现起来的话还是比较简单的.如果不是清楚的,建议先自己用camera2实现单摄像头的预览拍照.
public class Camera2Information {
private String camera2Id;
private CameraDevice cameraDevice;//相机设备
private CaptureRequest.Builder previewBuilder;//捕获请求(捕获请求模式:预览,拍照等)
private CameraCaptureSession cameraCaptureSession;//捕获会话的管理(开启或停止预览)
private ImageReader imageReader;//预览,拍照数据回调
private int previewFormat;//预览格式
private Size previewSize;//预览尺寸
private Size pictureSize;//图片尺寸
private Range<Integer> previewFps;//FPS
private String previewOrientation;//预览方向
private HandlerThread handlerThread;
private Handler handler;
.....
}
private List<Camera2Information> camera2InfoList;
private String[] CameraIdArray = null;
public void initData() {
camera2InfoList = new ArrayList<>();
CameraIdArray = Camera2Utils.getCameraId(this);
for (String cameraId : CameraIdArray) {
Camera2Information cameraInfo = new Camera2Information();
cameraInfo.setCamera2Id(cameraId);
camera2InfoList.add(cameraInfo);
}
}
......
cameraCaseAdapter = new CameraCaseAdapter(this, camera2InfoList);
//surfaceTexture的状态监听
private TextureView.SurfaceTextureListener mSTListener = new TextureView.SurfaceTextureListener() {
@Override
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
int position = (int) itemView.getTag();
//默认全部打开所有摄像头
mOpenBtn.setText(R.string.camera_case_btn_close);
mOpenBtn.setActivated(false);
openCamera(mCameraManager, camera2InfoList.get(position));
}
@Override
public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
}
@Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
return false;
}
@Override
public void onSurfaceTextureUpdated(SurfaceTexture surface) {
}
};
private void openCamera(CameraManager mCameraManager, Camera2Information info) {
try {
if (mCameraManager != null) {
String camera2Id = info.getCamera2Id();
//创建HandlerThread,用"CAMERA2"标记
mThreadHandler = new HandlerThread("CAMERA2");
mThreadHandler.start();
mHandler = new Handler(mThreadHandler.getLooper());
info.setHandler(mHandler);
info.setHandlerThread(mThreadHandler);
//获取摄像头相关属性
getCameraDefaultAttr(info);
if (mContext.checkSelfPermission(Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
LogUtils.e("TAG", "权限不足!");
return;
}
//打开相机设备
mCameraManager.openCamera(camera2Id, new CameraDeviceStateCallback(info), info.getHandler());
}
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
4.在 getCameraDefaultAttr(info);设置默认相机参数:预览格式,预览尺寸,预览方向,FPS;
(1). 预览格式:camera2的官方文档说明了:适合预览的格式为YUV_420_888,而ImageFormat.JPEG格式渲染JPEG数据量过大,预览时会导致掉帧.不适合作为预览格式,适合拍照.
/**
* 选择合适的预览格式:camera2合适的预览格式为YUV_420_888
* 如果摄像头不支持,应选非ImageFormat.JPEG的格式
* 当只有ImageFormat.JPEG格式,选择该预览格式
*
* @param mContext
* @param camera2Id
* @param defaultFormat
* @return
*/
public static int getBestPreviewFormat(Context mContext, String camera2Id, int defaultFormat) {
CameraManager mCameraManager = (CameraManager) mContext.getSystemService(CAMERA_SERVICE);
CameraCharacteristics characteristics = null;
StreamConfigurationMap map = null;
try {
if (mCameraManager != null) {
characteristics = mCameraManager.getCameraCharacteristics(camera2Id);
map = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
}
} catch (CameraAccessException e) {
e.printStackTrace();
}
int bestPreviewFormat = 0;
if (map != null) {
int[] mPreviewFormat = map.getOutputFormats();
//是否支持defaultFormat
for (int format : mPreviewFormat) {
if (format == defaultFormat) {
bestPreviewFormat = format;
return bestPreviewFormat;
}
}
//不支持defaultFormat时,选ImageFormat.YUV_420_888格式
for (int format : mPreviewFormat) {
if (format == ImageFormat.YUV_420_888) {
bestPreviewFormat = format;
return bestPreviewFormat;
}
}
//不支持ImageFormat.YUV_420_888时,选非ImageFormat.JPEG的格式
for (int format : mPreviewFormat) {
if (format != ImageFormat.JPEG) {
bestPreviewFormat = format;
return bestPreviewFormat;
}
}
//只支持ImageFormat.JPEG时
for (int format : mPreviewFormat) {
bestPreviewFormat = format;
return bestPreviewFormat;
}
}
return bestPreviewFormat;
}
//使用
mImageReader = ImageReader.newInstance(info.getPictureSize().getWidth(),
info.getPictureSize().getHeight(),
info.getPreviewFormat(), maxImages);
(2). 预览尺寸: 为了不让预览画面变形,预览尺寸比要等于且接近surface的宽高比;而camera2默认屏幕方向为横屏,所以surface的宽高和PreviewSizes刚好相反.
/**
* 设置最合适的预览尺寸
* 默认竖屏,surface的宽高和PreviewSizes刚好相反
* 尺寸比等于或者接近surface比才不会变形.
*
* @param surfaceWidth
* @param surfaceHeight
* @return
*/
public static Size getBestPreviewSize(Context mContext, String camera2Id, int surfaceWidth, int surfaceHeight) {
CameraManager mCameraManager = (CameraManager) mContext.getSystemService(CAMERA_SERVICE);
CameraCharacteristics characteristics = null;
StreamConfigurationMap map = null;
try {
if (mCameraManager != null) {
characteristics = mCameraManager.getCameraCharacteristics(camera2Id);
map = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
}
} catch (CameraAccessException e) {
e.printStackTrace();
}
int diffs = Integer.MAX_VALUE;
Size bestPreviewSize = null;
List<Size> sizeList = new ArrayList<>();
boolean flag = false;
if (map != null) {
Size[] sizeArray = map.getOutputSizes(SurfaceTexture.class);
if (sizeArray.length == 0) {
return null;
}
for (Size size : sizeArray) {
//保存比例相等的值,相机默认为横屏,size宽高和屏幕相反
if ((size.getWidth() * surfaceWidth / surfaceHeight) == size.getHeight()) {
sizeList.add(size);
flag = true;
}
}
if (!flag) {
sizeList = new ArrayList<>(Arrays.asList(sizeArray));
}
//在比例相等的值集合中选差值最小
for (Size s : sizeList) {
int newDiffs = Math.abs(s.getHeight() - surfaceWidth) + Math.abs(s.getWidth() - surfaceHeight);
if (newDiffs <= diffs) {
bestPreviewSize = s;
diffs = newDiffs;
}
}
}
return bestPreviewSize;
}
//使用
texture.setDefaultBufferSize(info.getPreviewSize().getWidth(), info.getPreviewSize().getHeight());
(3). 预览方向:因为camera2没有像camera1直接设置预览方向的方法,只能通过旋转TextureView,我这里只适配了前置和后置摄像头的预览方向,第三个摄像头可能需要手动旋转.注意:mCaptureBuilder.set(CaptureRequest.JPEG_ORIENTATION, angle);该方法是旋转拍照后图片的方向,而且只有当预览格式为ImageFormat.JPEG时才会生效,官方建议预览用YUV_420_888,拍照用ImageFormat.JPEG格式.
public static int getPreviewOrientation(Context mContext, String camera2Id) {
CameraManager mCameraManager = (CameraManager) mContext.getSystemService(CAMERA_SERVICE);
CameraCharacteristics characteristics = null;
try {
if (mCameraManager != null) {
characteristics = mCameraManager.getCameraCharacteristics(camera2Id);
}
} catch (CameraAccessException e) {
e.printStackTrace();
}
int result = 0;
if (characteristics != null) {
Integer mCameraOrientation = characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION);
WindowManager wm = ((WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE));
if (wm != null) {
int rotation = wm.getDefaultDisplay().getRotation();
if (characteristics.get(CameraCharacteristics.LENS_FACING) == CameraMetadata.LENS_FACING_FRONT) {
result = (mCameraOrientation + ORIENTATIONS.get(rotation)) % 360;
result = (360 - result) % 360;
} else {
result = (mCameraOrientation - ORIENTATIONS.get(rotation) + 360) % 360;
}
}
}
return result;
}
//使用
if (info.getPreviewOrientation() == null) {
int angle = Camera2Utils.getPreviewOrientation(mContext, info.getCamera2Id());
info.setPreviewOrientation(String.valueOf(angle));
}
mPreviewTv.setRotation((float) Integer.parseInt(info.getPreviewOrientation()));
(4). FPS:一般来说,预览适合的FPS为30,人眼看起来才不会卡顿.
/**
* int [min,max]
* 设置最合适的FPS:默认 defaultFps,没有则接近 defaultFps,优先选> defaultFps
*
* @param mContext
* @param camera2Id
* @param defaultFps
* @return
*/
public static Range<Integer> getBestPreviewFps(Context mContext, String camera2Id, int defaultFps) {
CameraManager mCameraManager = (CameraManager) mContext.getSystemService(CAMERA_SERVICE);
CameraCharacteristics characteristics = null;
try {
if (mCameraManager != null) {
characteristics = mCameraManager.getCameraCharacteristics(camera2Id);
}
} catch (CameraAccessException e) {
e.printStackTrace();
}
Range<Integer>[] FpsRanges = null;
if (characteristics != null) {
FpsRanges = characteristics.get(CameraCharacteristics.CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES);
}
int minDiffs = Integer.MAX_VALUE;
int maxDiffs = Integer.MAX_VALUE;
Range<Integer> bestFps = null;
if (FpsRanges != null) {
for (int i = 0; i < FpsRanges.length; i++) {
Range<Integer> fps = FpsRanges[i];
if (defaultFps == fps.getLower() && defaultFps == fps.getUpper()) {
bestFps = fps;
return bestFps;
}
//min
if (fps.getLower() >= defaultFps) {
int value = Math.abs(fps.getLower() - defaultFps);
if (value < minDiffs) {
bestFps = fps;
minDiffs = value;
}
continue;
}
//max
if (fps.getUpper() <= defaultFps) {
int value = Math.abs(fps.getUpper() - defaultFps);
if (value <= maxDiffs) {
bestFps = fps;
maxDiffs = value;
}
}
}
}
return bestFps;
}
//使用
mPreviewBuilder.set(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE, info.getPreviewFps());
先写到这样吧,后面在补充拍照相关的设置:图片大小,图片方向,镜像翻转,部分格式拍照;以及camera usb的拔插监听.