先上效果图:
文章推荐,没看的可以去看下,将会对Camera的认识更加深刻:
1.Camera开发知识储备
2.了解Matrix
3.Camera1基础开发
4.基于Camera实现人脸检测
Camera2
Camera2相对于Camera1引入了管道的概念:
Camera2的架构图:
Camera2拍照流程图,同时也是API调用顺序的图:
流程:
1.创建Camera线程和该线程的handler,绑定并开启线程
2.添加TextureView的监听
3.初始化Sureface,指定cameraId,创建ImageReader并设置监听
4.打开相机
5.创建相机预览会话
6.拍照
多的注解文字我就不写了,直接贴示例代码好了:
/**
* Camera2
*/
public class Camera2Helper {
private static final String TAG = "Camera2Helper";
//默认使用后置摄像头
private int mFacing = CameraCharacteristics.LENS_FACING_BACK;
private int PREVIEW_WIDTH = 720; //预览的宽度
private int PREVIEW_HEIGHT = 1280; //预览的高度
private int PIC_WIDTH = 720; //保存图片的宽度
private int PIC_HEIGHT = 1280; //保存图片的高度
//相机的信息
private CameraCharacteristics mCameraCharacteristics;
//相机ID
private String cameraID;
private HandlerThread handlerThread = new HandlerThread("CameraThread");
private Activity activity;
private TextureView textureView;
private Integer mCameraSensorOrientation;
private StreamConfigurationMap streamConfigurationMap;
private ImageReader mImageReader;
private Handler cameraHandler;
private CameraManager cameraManager;
private CameraDevice cameraDevice;
private CameraCaptureSession cameraCaptureSession;
private boolean canExchangeCamera;
private boolean canTakePic;
public Camera2Helper(Activity activity, TextureView textureView) {
this.activity = activity;
this.textureView = textureView;
init();
}
public void init() {
handlerThread.start();
//将handler绑定到指定线程
cameraHandler = new Handler(handlerThread.getLooper());
textureView.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() {
@Override
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
Log.i(TAG, " onSurfaceTextureAvailable");
//初始化相机资源
initCamera();
}
@Override
public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
Log.i(TAG, " onSurfaceTextureSizeChanged");
}
@Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
Log.i(TAG, " onSurfaceTextureDestroyed");
//释放相机资源
releaseCamera();
return true;
}
@Override
public void onSurfaceTextureUpdated(SurfaceTexture surface) {
Log.i(TAG, " onSurfaceTextureUpdated");
if (cameraDevice == null) {
initCamera();
}
}
});
}
/**
* 初始化相机
*/
public void initCamera() {
try {
cameraManager = (CameraManager) activity.getSystemService(Context.CAMERA_SERVICE);
if (cameraManager != null) {
String[] cameraIdList = cameraManager.getCameraIdList();
if (cameraIdList.length == 0) {
Toast.makeText(activity, "暂无相机可用", Toast.LENGTH_SHORT).show();
return;
}
for (String id : cameraIdList) {
CameraCharacteristics cameraCharacteristics = cameraManager.getCameraCharacteristics(id);
Integer facing = cameraCharacteristics.get(CameraCharacteristics.LENS_FACING);
// TODO: 2020/3/19 如何分析后置摄像头的类型
if (mFacing == facing) {
mCameraCharacteristics = cameraCharacteristics;
if (mFacing == CameraCharacteristics.LENS_FACING_BACK && id.equals("2")) {
cameraID = id;
break;
} else
cameraID = id;
}
Log.i(TAG, "摄像头的ID:" + cameraID);
}
Integer supportLevel = mCameraCharacteristics.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
if (supportLevel != null && supportLevel ==
CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY) {
Toast.makeText(activity, "相机硬件不支持新特性", Toast.LENGTH_SHORT).show();
}
//获取手机的方向
int screenRotation = activity.getWindowManager().getDefaultDisplay().getRotation();
//获取摄像头的方向
mCameraSensorOrientation = mCameraCharacteristics.get(CameraCharacteristics.SENSOR_ORIENTATION);
//获取StreamConfigurationMap,它是管理摄像头支持的所有输出格式和尺寸
streamConfigurationMap = mCameraCharacteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
//获取支持的图片尺寸
Size[] pictureSizes = streamConfigurationMap.getOutputSizes(ImageFormat.JPEG);
//获取支持的预览尺寸
Size[] previewSizes = streamConfigurationMap.getOutputSizes(SurfaceTexture.class);
//是否需要调换宽高
boolean exchange = exchangeWidthAndHeight(screenRotation, mCameraSensorOrientation);
Size size;
if (exchange) {
size = new Size(textureView.getHeight(), textureView.getWidth());
} else {
size = new Size(textureView.getWidth(), textureView.getHeight());
}
Log.i(TAG, " textureView.getWidth() :" + textureView.getWidth()
+ " textureView.getHeight():" + textureView.getHeight());
Size pictureSize = getBestSize(exchange ? PIC_HEIGHT : PIC_WIDTH,
exchange ? PIC_WIDTH : PIC_HEIGHT, size, pictureSizes);
Size previewSize = getBestSize(exchange ? PREVIEW_HEIGHT : PREVIEW_WIDTH,
exchange ? PREVIEW_WIDTH : PREVIEW_HEIGHT, size, previewSizes);
textureView.getSurfaceTexture().setDefaultBufferSize(previewSize.getWidth(),
previewSize.getHeight());
mImageReader = ImageReader.newInstance(pictureSize.getWidth(), pictureSize.getHeight(),
ImageFormat.JPEG, 1);
mImageReader.setOnImageAvailableListener(imageAvailableListener, cameraHandler);
//打开相机
openCamera();
}
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
private ImageReader.OnImageAvailableListener imageAvailableListener =
new ImageReader.OnImageAvailableListener() {
@Override
public void onImageAvailable(ImageReader reader) {
//接收图片的数据
if (reader != null) {
Log.i(TAG, "Image Available! 当前线程:" + Thread.currentThread().getName());
Image image = reader.acquireLatestImage();
// Image image = reader.acquireNextImage();
// new Thread(new ImageSaver(activity, image, mFacing)).start();
ByteBuffer buffer = image.getPlanes()[0].getBuffer();
byte[] data = new byte[buffer.remaining()];
buffer.get(data);
String dirPath = activity.getExternalFilesDir(Environment.DIRECTORY_PICTURES).getPath()
+ "/Picture/";
File dirFile = new File(dirPath);
if (!dirFile.exists())
dirFile.mkdirs();
String picturePath = dirPath + "picture.jpeg";
File picFile = new File(picturePath);
if (picFile.exists())
picFile.delete();
transform(data, image, picFile);
}
}
};
public void transform(byte[] data, Image image, File filePath) {
//给图片变换为视觉角度
if (data != null && data.length > 0) {
Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
Matrix matrix = new Matrix();
if (mFacing == CameraCharacteristics.LENS_FACING_FRONT) {
//如果时前置摄像头,镜像;因为时镜像,所以旋转角度是360-rotation
matrix.postScale(-1, 1);
}
Bitmap bitmap1 = Bitmap.createBitmap(bitmap, 0, 0,
bitmap.getWidth(), bitmap.getHeight(), matrix, true);
saveBmp(bitmap1, filePath);
bitmap1.recycle();
image.close();//这里要关闭image,否则界面会卡
}
}
/**
* 存储图片
*/
private void saveBmp(Bitmap bmp, File picFile) {
try {
FileOutputStream fos = new FileOutputStream(picFile);
bmp.compress(Bitmap.CompressFormat.JPEG, 100, fos);
fos.flush();
fos.close();
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(activity, "图片保存成功", Toast.LENGTH_SHORT).show();
}
});
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
bmp.recycle();
}
}
/**
* 开启相机
*/
private void openCamera() {
if (cameraManager != null) {
if (ContextCompat.checkSelfPermission(activity, Manifest.permission.CAMERA)
== PackageManager.PERMISSION_GRANTED) {
try {
cameraManager.openCamera(cameraID, new CameraDevice.StateCallback() {
@Override
public void onOpened(@NonNull CameraDevice camera) {
Log.i(TAG, " onOpened()");
cameraDevice = camera;
//创建一个相机预览的会话
createCaptureSession(camera);
}
@Override
public void onDisconnected(@NonNull CameraDevice camera) {
Log.i(TAG, " onDisconnected()");
//防止切换相机应用时,当前应用占用了相机导致预览会话创建失败
releaseCamera();
}
@Override
public void onError(@NonNull CameraDevice camera, int error) {
Log.i(TAG, " 相机打开失败:" + error);
}
}, cameraHandler);
} catch (CameraAccessException e) {
e.printStackTrace();
}
} else {
Toast.makeText(activity, "请开启相机权限", Toast.LENGTH_SHORT).show();
}
}
}
/**
* 释放相机资源
*/
public void releaseCamera() {
cameraCaptureSession.close();
cameraCaptureSession = null;
cameraDevice.close();
cameraDevice = null;
mImageReader.close();
mImageReader = null;
canExchangeCamera = false;
}
/**
* 退出线程
*/
public void releaseThread() {
handlerThread.quitSafely();
}
/**
* 创建一个相机预览的会话
*/
private void createCaptureSession(CameraDevice cameraDevice) {
try {
if (cameraDevice != null) {
CaptureRequest.Builder captureRequestBuilder =
cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
Surface surface = new Surface(textureView.getSurfaceTexture());
// 将CaptureRequest的构建器与Surface对象绑定在一起
captureRequestBuilder.addTarget(surface);
// 禁用闪光灯
captureRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE,
CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
// 添加自动对焦
captureRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE,
CaptureRequest.CONTROL_AF_STATE_FOCUSED_LOCKED);
CaptureRequest captureRequest = captureRequestBuilder.build();
//创建会话
cameraDevice.createCaptureSession(Arrays.asList(surface, mImageReader.getSurface()),
new CameraCaptureSession.StateCallback() {
@Override
public void onConfigured(@NonNull CameraCaptureSession session) {
try {
cameraCaptureSession = session;
session.setRepeatingRequest(captureRequest,
captureCallback, cameraHandler);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
@Override
public void onConfigureFailed(@NonNull CameraCaptureSession session) {
}
}, cameraHandler);
}
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
private CameraCaptureSession.CaptureCallback captureCallback =
new CameraCaptureSession.CaptureCallback() {
@Override
public void onCaptureStarted(@NonNull CameraCaptureSession session,
@NonNull CaptureRequest request,
long timestamp, long frameNumber) {
super.onCaptureStarted(session, request, timestamp, frameNumber);
}
@Override
public void onCaptureCompleted(@NonNull CameraCaptureSession session,
@NonNull CaptureRequest request,
@NonNull TotalCaptureResult result) {
super.onCaptureCompleted(session, request, result);
// unlockFocus();//恢复预览
canExchangeCamera = true;
canTakePic = true;
}
@Override
public void onCaptureFailed(@NonNull CameraCaptureSession session,
@NonNull CaptureRequest request,
@NonNull CaptureFailure failure) {
super.onCaptureFailed(session, request, failure);
Toast.makeText(activity, "预览会话创建失败", Toast.LENGTH_SHORT).show();
}
};
/**
* 拍照
*/
public void takePhoto() {
if (cameraDevice == null || !textureView.isAvailable() || !canTakePic) return;
try {
CaptureRequest.Builder captureRequest = cameraDevice.
createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
captureRequest.addTarget(mImageReader.getSurface());
// 自动对焦
captureRequest.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
// 闪光灯
captureRequest.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
//根据摄像头方向对保存的照片进行旋转,使其为"自然方向"
captureRequest.set(CaptureRequest.JPEG_ORIENTATION, mCameraSensorOrientation);
//拍照
CaptureRequest build = captureRequest.build();
cameraCaptureSession.capture(build, captureCallback, cameraHandler);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
/**
* 切换相机
*/
public void exchangeCamera() {
if (cameraDevice == null || !canExchangeCamera || !textureView.isAvailable()) return;
if (mFacing == CameraCharacteristics.LENS_FACING_FRONT)
mFacing = CameraCharacteristics.LENS_FACING_BACK;
else
mFacing = CameraCharacteristics.LENS_FACING_FRONT;
releaseCamera();
initCamera();
}
/**
* 根据手机的方向和相机传感器的方向确定是否需要调换宽高
*
* @param screenRotation 手机方向
* @param mCameraSensorOrientation 相机传感器的方向
*/
private boolean exchangeWidthAndHeight(int screenRotation, int mCameraSensorOrientation) {
boolean exchange = false;
if (screenRotation == Surface.ROTATION_0 || screenRotation == Surface.ROTATION_180) {
if (mCameraSensorOrientation == 90 || mCameraSensorOrientation == 270) {
exchange = true;
}
} else if (screenRotation == Surface.ROTATION_90 || screenRotation == Surface.ROTATION_270) {
if (mCameraSensorOrientation == 0 || mCameraSensorOrientation == 180) {
exchange = true;
}
}
return exchange;
}
/**
* 计算出最佳的尺寸
*
* @param targetWidth targetWidth 图片或预览指定的尺寸
* @param targetHeight targetHeight 图片或预览指定的尺寸
* @param size textureView的尺寸
* @param supportSizes 支持的尺寸
* @return
*/
private Size getBestSize(float targetWidth, float targetHeight, Size size, Size[] supportSizes) {
//宽高大的Size列表
ArrayList<Size> bigList = new ArrayList<>();
//宽高小的Size列表
ArrayList<Size> smallList = new ArrayList<>();
for (Size supportSize : supportSizes) {
//宽<=最大宽度 && 高<=最大高度 && 宽高比 == 目标值宽高比
if ((float) supportSize.getWidth() <= (float) size.getWidth() &&
(float) supportSize.getHeight() <= (float) size.getHeight()
&& targetWidth / targetHeight == (float) supportSize.getWidth()
/ (float) supportSize.getHeight()) {
if (supportSize.getWidth() >= targetWidth && supportSize.getHeight() >= targetHeight) {
bigList.add(supportSize);
} else {
smallList.add(supportSize);
}
}
Log.i(TAG, "系统支持的尺寸: w " + supportSize.getWidth() + " h "
+ supportSize.getHeight() + " 比例 :"
+ supportSize.getWidth() / supportSize.getHeight());
}
// Log.i(TAG, "最大尺寸 :w:" + size.getWidth() + " h: "
// + size.getHeight() + ", 比例 :" + (size.getWidth() / size.getHeight()));
// Log.i(TAG, "目标尺寸 :w:" + targetWidth + " h:" +
// targetHeight + " 比例 :" + (targetWidth / targetHeight));
//选择bigEnough中最小的值 或 notBigEnough中最大的值
if (bigList.size() > 0) {
Size min = Collections.min(bigList, new CompareSizeByArea());
return min;
}
if (smallList.size() > 0) {
Size max = Collections.max(smallList, new CompareSizeByArea());
return max;
} else {
return supportSizes[0];
}
}
private class CompareSizeByArea implements Comparator<Size> {
@Override
public int compare(Size o1, Size o2) {
return Long.signum((long) o1.getWidth() * o1.getHeight()
- (long) o2.getWidth() * o2.getHeight());
}
}
}
在Activity中使用:
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
TextureView textureView = view.findViewById(R.id.textureView);
ImageView ivSetting = view.findViewById(R.id.ivSetting);
ImageButton btnTakePic = view.findViewById(R.id.btnTakePic);
//录制视频的按钮
ImageView btnStart = view.findViewById(R.id.btnStart);
ImageView btnStop = view.findViewById(R.id.btnStop);
//切换相机摄像头的按钮
ImageView ivExchange = view.findViewById(R.id.ivExchange);
camera2Helper = new Camera2Helper(getActivity(), textureView);
ivSetting.setOnClickListener(this);
btnTakePic.setOnClickListener(this);
btnStart.setOnClickListener(this);
btnStop.setOnClickListener(this);
ivExchange.setOnClickListener(this);
}
@Override
public void onResume() {
super.onResume();
}
@Override
public void onPause() {
super.onPause();
camera2Helper.releaseCamera();
}
@Override
public void onDestroy() {
super.onDestroy();
camera2Helper.releaseThread();
}
@Override
public void onClick(View v) {
if (v.getId() == R.id.btnTakePic) {
//拍照
camera2Helper.takePhoto();
} else if (v.getId() == R.id.ivExchange) {
//切换相机
camera2Helper.exchangeCamera();
} else if (v.getId() == R.id.ivSetting) {
//相机设置
} else if (v.getId() == R.id.btnStart) {
//开始录制视频
} else if (v.getId() == R.id.btnStop) {
//停止录制视频
}
}