这是我参与8月更文挑战的第14天,活动详情查看:8月更文挑战
Android自定义Camera2相机
自定义Camera2相机
开发流程
4.打开相机设备
val cameraStateCallback = CameraStateCallback()
cameraManager.openCamera(cameraId, cameraStateCallback, mainHandler)
......
private inner class CameraStateCallback : CameraDevice.StateCallback() {
@MainThread
override fun onOpened(camera: CameraDevice) {
cameraDeviceFuture!!.set(camera)
cameraCharacteristicsFuture!!.set(getCameraCharacteristics(camera.id))
}
@MainThread
override fun onClosed(camera: CameraDevice) {
}
@MainThread
override fun onDisconnected(camera: CameraDevice) {
cameraDeviceFuture!!.set(camera)
closeCamera()
}
@MainThread
override fun onError(camera: CameraDevice, error: Int) {
cameraDeviceFuture!!.set(camera)
closeCamera()
}
}
cameraManager.openCamera(@NonNull String cameraId,@NonNull final CameraDevice.StateCallback callback, @Nullable Handler handler)
的三个参数:
- cameraId:摄像头的唯一标识
- callback:设备连接状态变化的回调
- handler:回调执行的Handler对象,传入null则使用当前的主线程Handler
其中CameraStateCallback回调:
- onOpened:表示相机打开成功,可以真正开始使用相机,创建Capture会话
- onDisconnected:当相机断开连接时回调该方法,需要进行释放相机的操作
- onError:当相机打开失败时,需要进行释放相机的操作
- onClosed:调用Camera.close()后的回调方法
5.创建Capture Session
val sessionStateCallback = SessionStateCallback()
......
val cameraDevice = cameraDeviceFuture?.get()
cameraDevice?.createCaptureSession(outputs, sessionStateCallback, mainHandler)
......
private inner class SessionStateCallback : CameraCaptureSession.StateCallback() {
@MainThread
override fun onConfigureFailed(session: CameraCaptureSession) {
captureSessionFuture!!.set(session)
}
@MainThread
override fun onConfigured(session: CameraCaptureSession) {
captureSessionFuture!!.set(session)
}
@MainThread
override fun onClosed(session: CameraCaptureSession) {
}
}
这段的代码核心方法是mCameraDevice.createCaptureSession()
创建Capture会话,它接受了三个参数:
- outputs:用于接受图像数据的surface集合,这里传入的是一个preview的surface
- callback:用于监听 Session 状态的CameraCaptureSession.StateCallback对象
- handler:用于执行CameraCaptureSession.StateCallback的Handler对象,传入null则使用当前的主线程Handler
6.创建CaptureRequest
CaptureRequest是向CameraCaptureSession提交Capture请求时的信息载体,其内部包括了本次Capture的参数配置和接收图像数据的Surface
if (cameraDevice != null) {
previewImageRequestBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW)
captureImageRequestBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE)
}
......
val cameraDevice = cameraDeviceFuture?.get()
val captureSession = captureSessionFuture?.get()
val previewImageRequestBuilder = previewImageRequestBuilder!!
val captureImageRequestBuilder = captureImageRequestBuilder!!
if (cameraDevice != null && captureSession != null) {
val previewSurface = previewSurface!!
val previewDataSurface = previewDataSurface
previewImageRequestBuilder.addTarget(previewSurface)
// Avoid missing preview frame while capturing image.
captureImageRequestBuilder.addTarget(previewSurface)
if (previewDataSurface != null) {
previewImageRequestBuilder.addTarget(previewDataSurface)
// Avoid missing preview data while capturing image.
captureImageRequestBuilder.addTarget(previewDataSurface)
}
val previewRequest = previewImageRequestBuilder.build()
captureSession.setRepeatingRequest(previewRequest, RepeatingCaptureStateCallback(), mainHandler)
}
......
private inner class RepeatingCaptureStateCallback : CameraCaptureSession.CaptureCallback() {
@MainThread
override fun onCaptureStarted(session: CameraCaptureSession, request: CaptureRequest, timestamp: Long, frameNumber: Long) {
super.onCaptureStarted(session, request, timestamp, frameNumber)
}
@MainThread
override fun onCaptureCompleted(session: CameraCaptureSession, request: CaptureRequest, result: TotalCaptureResult) {
super.onCaptureCompleted(session, request, result)
}
}
除了模式的配置,CaptureRequest还可以配置很多其他信息,例如图像格式、图像分辨率、传感器控制、闪光灯控制、3A(自动对焦-AF、自动曝光-AE和自动白平衡-AWB)控制等。在createCaptureSession的回调中可以进行设置,最后通过build()
方法生成CaptureRequest对象。
7.预览
Camera2中,通过连续重复的Capture实现预览功能,每次Capture会把预览画面显示到对应的Surface上。连续重复的Capture操作通过
captureSession.setRepeatingRequest(previewRequest, RepeatingCaptureStateCallback(), mainHandler)
实现,该方法有三个参数:
- request:CaptureRequest对象
- listener:监听Capture 状态的回调
- handler:用于执行CameraCaptureSession.CaptureCallback的Handler对象,传入null则使用当前的主线程Handler
停止预览使用mCaptureSession.stopRepeating()
方法。
8.拍照
设置上面的request,session后,就可以真正的开始拍照操作
val captureImageRequest = captureImageRequestBuilder.build()
captureSession.capture(captureImageRequest, CaptureImageStateCallback(), mainHandler)
......
private inner class CaptureImageStateCallback : CameraCaptureSession.CaptureCallback() {
@MainThread
override fun onCaptureStarted(session: CameraCaptureSession, request: CaptureRequest, timestamp: Long, frameNumber: Long) {
super.onCaptureStarted(session, request, timestamp, frameNumber)
// Play the shutter click sound.
cameraHandler?.post { mediaActionSound.play(MediaActionSound.SHUTTER_CLICK) }
}
@MainThread
override fun onCaptureCompleted(session: CameraCaptureSession, request: CaptureRequest, result: TotalCaptureResult) {
super.onCaptureCompleted(session, request, result)
captureResults.put(result)
}
}
captureSession.capture()
方法也有三个参数,和mCaptureSession.setRepeatingRequest
一样:
- request:CaptureRequest对象
- listener:监听Capture 状态的回调
- handler:用于执行CameraCaptureSession.CaptureCallback的Handler对象,传入null则使用当前的主线程Handler
9.关闭相机
和其他硬件资源的使用一样,当我们不再需要使用相机时记得调用 CameraDevice.close() 方法及时关闭相机回收资源。关闭相机的操作至关重要,因为如果你一直占用相机资源,其他基于相机开发的功能都会无法正常使用,严重情况下直接导致其他相机相关的 APP 无法正常使用,当相机被完全关闭的时候会通过 CameraStateCallback.onCllosed() 方法通知你相机已经被关闭。那么在什么时候关闭相机最合适呢?个人的建议是在 onPause() 的时候就一定要关闭相机,因为在这个时候相机页面已经不是用户关注的焦点,大部分情况下已经可以关闭相机了。
cameraDevice?.close()
previewDataImageReader?.close()
jpegImageReader?.close()
先后对CaptureSession,CameraDevice,ImageReader进行close操作,释放资源。
从 Camera1迁移到Camera2的建议
如果你的项目正在使用Camera1,并且打算从Camera1迁移到Camera2的话,希望以下几个建议可以对你有所帮助:
- Camera1严格区分了预览和拍照两个流程,而Camera2则把这两个流程都抽象成了Capture行为,所以建议你不要带着过多的Camera1思维使用Camera2,避免因为思维上的束缚而无法充分利用Camera2灵活的 API;
- 如同Camera1一样,Camera2的一些 API调用也会耗时,所以建议你使用独立的线程执行所有的相机操作,尽量避免直接在主线程调用Camera2的API,HandlerThread 是一个不错的选择;
- 可以认为Camera1是Camera2的一个子集,也就是说Camera1能做的事情Camera2一定能做,反过来则不一定行得通;
- 如果你的应用程序需要同时兼容Camera1 和Camera2,个人建议分开维护,因为Camera1蹩脚的API设计很可能让Camera2灵活的API无法得到充分的发挥,另外将两个设计上完全不兼容的东西搅和在一起带来的痛苦可能远大于其带来便利性,多写一些冗余的代码也许还更开心;
- 官方说Camera2的性能会更好,但在较早期的一些机器上运行Camera2的性能并没有比Camera1好多少;
- 当设备的 Supported Hardware Level 低于FULL的时候,建议还是使用Camera1,因为FULL级别以下的 Camera2 能提供的功能几乎和Camera1一样,所以倒不如选择更加稳定的Camera1。