近期通过我对众多的博客的查看,以及之前项目上的一些总结进行了大概的思路的概括。
基础知识:
Android系统提供API和Intent来支持自定义相机拍照和快速拍照,以下是有关的类:
- Camera
该类提供基础API来使用设备上的相机,且该类可以为你的应用提供拍照和录像相关的API。
- SurfaceView
该类用于显示相机的预览数据。
- MediaRecorder
该类提供相机录像相关的API。
- Intent
使用MediaStore.ACTION_IMAGE_CAPTURE 和MediaStore.ACTION_VIDEO_CAPTURE Intent action可以快速拍照或者录像。
创建一个相机应用
创建一个独立自定义的相机app基本遵循如下步骤:
- 检测和访问相机 - 首先代码检测该设备相机是否存在,如果存在才能请求访问设备相机.
- 创建一个预览来显示相机图像 - 在你的布局中使用SurfaceView控件,然后在代码中继承SurfaceHolder.Callback接口并且实现接口中的方法来显示来自相机的图像信息。
- 设置相机基本参数 - 根据需求设置相机预览尺寸,图片大小,预览方向,图片方向等。
- 设置拍照录像监听 - 当用户按下按钮时调用Camera#takePicture或者MediaRecorder#start()来进行拍照或录像。
- 文件保存 - 当拍照结束或者录像视频结束时,需要开启一个后台线程去保存图片或者视频文件。
- 释放相机资源 - Camera硬件是一个共享资源,所以你必须小心的编写你的应用代码来管理相机资源。一般在Activity的生命周期的onResume中开机相机,在onPause中释放相机。
注意: 当你不在使用相机资源时,记得调用Camera#release方法来释放相机资源,否则其他应用甚至你自己的应用再次请求访问设备相机时会失败,并且crash。
首先需要了解camera2的几个比较重要的类:
- CameraManager: 管理手机上的所有摄像头设备,它的作用主要是获取摄像头列表和打开指定的摄像头;
- CameraDevice: 具体的摄像头设备,它有一系列参数(预览尺寸、拍照尺寸等),可以通过CameraManager的 getCameraCharacteristics()方法获取。它的作用主要是创建CameraCaptureSession和CaptureRequest;
- CameraCaptureSession: 相机捕获会话,用于处理拍照和预览的工作(很重要);
- CaptureRequest: 捕获请求,定义输出缓冲区以及显示界面(TextureView或SurfaceView)等;
打开相机 - ---》创建预览会话----》 拍照 保存 -----》释放相机和多线程
这是一个总体的一个大体思路流程,这个大体的一个思路流程可以在里面进行代码细节的处理
一)、打开相机
用CameraManager的openCamera(String cameraId, CameraDevice.StateCallback callback, Handler handler)方法打开指定摄像头。该方法的第一个参数代表要打开的摄像头ID;第二个参数用于监听摄像头的状态;第三个参数代表执行callback的Handler,如果程序希望直接在当前线程中执行callback,则可将handler参数设为null。(对设备硬件的一个整体的获取,去启动硬件的设备,可以理解为激活)
二)、创建预览会话
1、当摄像头被打开之后会回调接口 mStateCallback.onOpened,程序即可获取CameraDevice —— 即根据摄像头ID获取了指定摄像头设备,然后调用CameraDevice的createCaptureSession(List outputs, CameraCaptureSession. StateCallback callback,Handler handler)方法来创建CameraCaptureSession。该方法的第一个参数是一个List集合,封装了所有需要从该摄像头获取图片的Surface,第二个参数用于监听CameraCaptureSession的创建过程;第三个参数代表执行callback的Handler,如果程序希望直接在当前线程中执行callback,则可将handler参数设为null。
2、不管预览还是拍照,程序都调用CameraDevice的createCaptureRequest(int templateType)方法创建CaptureRequest.Builder,该方法支持TEMPLATE_PREVIEW(预览)、TEMPLATE_RECORD(拍摄视频)、TEMPLATE_STILL_CAPTURE(拍照)等参数。
3、通过以上步骤所调用方法返回的CaptureRequest.Builder设置拍照的各种参数,比如对焦模式、曝光模式等。
4、调用CaptureRequest.Builder的build()方法即可得到CaptureRequest对象,接下来程序可通过CameraCaptureSession的setRepeatingRequest()方法开始预览,或调用capture()方法拍照。
注:相机的预览与拍照流程我们了解了。
五)、拍照、保存
1、通过createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE) 创建一个拍照请求的Builder对象
2、然后设置各种参数。注意,captureRequestBuilder.set(CaptureRequest.JPEG_ORIENTATION, mCameraSensorOrientation)用来设置保存照片的旋转方向。如果不设置的话,保存的照片不是"自然方向"
3、拍照的结果是在 OnImageAvailableListener 对象中得到的。首先通过 CacquireNextImage() 方法获取到一个Image对象,然后通过 image.planes[0].buffer 得到 byteBuffer,将这个 byteBuffer 转换成 byteArray 。这个 byteArray 就是拍照所得到的图像数据。然后就可以把这个 byteArray 保存成图片到手机存储中
六)、 释放相机及线程
1、变量mCameraCaptureSession、mCameraDevice、mImageReader的close操作和“=null”;
2、handlerThread.quitSafely();
整个拍摄流程如下:
1、创建一个用于从 Pipeline 获取图片的 CaptureRequest。
2、修改 CaptureRequest 的闪光灯配置,让闪光灯在拍照过程中亮起来。
3、创建两个不同尺寸的 Surface 用于接收图片数据,并且将它们添加到 CaptureRequest 中。
4、发送配置好的 CaptureRequest 到 Pipeline 中等待它返回拍照结果。
5、一个新的 CaptureRequest 会被放入一个被称作 Pending Request Queue 的队列中等待被执行,
6、当 In-Flight Capture Queue 队列空闲的时候就会从 Pending Request Queue 获取若干个待处理的 CaptureRequest,
7、并且根据每一个 CaptureRequest 的配置进行 Capture 操作。
8、最后我们从不同尺寸的 Surface 中获取图片数据并且还会得到一个包含了很多与本次拍照相关的信息的 CaptureResult,流程结束。