Camera2使用
添加布局
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/cameraFragment"
tools:context=".CameraFragment">
<SurfaceView
android:id="@+id/surfaceView"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<ImageButton
android:id="@+id/capture"
android:layout_gravity="bottom|center"
android:layout_width="80dp"
android:layout_height="80dp"
android:scaleType="fitCenter"
android:background="@drawable/ic_shutter"/>
</FrameLayout>
声明权限
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
整体流程
// 打开摄像头
cameraDevice = openCamera()
// 创建session
captureSession = createCaptureSession()
// 下发预览
captureSession.setRepeatingRequest(previewRequest, null, cameraHandler)
// 点击下发录像
fragmentCameraBinding.capture.setOnTouchListener {
view, event -> run {
when (event.action) {
MotionEvent.ACTION_DOWN -> lifecycleScope.launch(Dispatchers.IO) {
// 下发录像
captureSession.setRepeatingRequest(recordRequest, null, cameraHandler)
// 启动MediaRecorder
recorder.apply {
prepare()
start()
}
recordingStartMillis = System.currentTimeMillis()
Log.d(TAG, "Recording started")
}
MotionEvent.ACTION_UP -> lifecycleScope.launch(Dispatchers.IO) {
Log.d(TAG, "Recording stopped. Output file: $outputFile")
recorder.stop()
}
else -> {}
}
}
Camera2拍照录像的整体流程,一般分为3个步骤:
- 打开摄像头:根据硬件能力,选择对应的
cameraId
,并获得CameraDevice
对象作为Camera管理实例 - 创建CaptureSession:手机与摄像头的数据交互需要通过
CameraDevice实例
创建一个会话(session)
,通过会话中的管道(pipe)
来进行传输 - 下发指令:通过
CaptureSession
实例下发的CaptureRequest
的类型和参数,可以进行预览、拍照和录像等操作
由于Camera2各个步骤都是通过回调来完成,为了流程更直观清晰,这里使用Kotlin的协程(Coroutine)
来执行各个步骤的调用
打开摄像头
-
初始化相机处理的线程
private val cameraThread = HandlerThread("CameraThread").apply { start() } private val cameraHandler = Handler(cameraThread.looper)
-
获取CameraManager并获取cameraId
// camera管理实例 private val cameraManager: CameraManager by lazy { var context = requireContext().applicationContext context.getSystemService(Context.CAMERA_SERVICE) as CameraManager } // 默认获取后置摄像头 private val cameraId: String by lazy { cameraManager.cameraIdList[0] }
-
打开摄像头
@SuppressLint("MissingPermission") private suspend fun openCamera(): CameraDevice = suspendCancellableCoroutine { cont -> cameraManager.openCamera(cameraId, object: CameraDevice.StateCallback() { override fun onOpened(camera: CameraDevice) { cont.resume(camera) } override fun onDisconnected(camera: CameraDevice) { Log.w(TAG, "Camera disconnected.") } override fun onError(camera: CameraDevice, error: Int) { val msg = when(error) { ERROR_CAMERA_DEVICE -> "Fatal (device)" ERROR_CAMERA_DISABLED -> "Device policy" ERROR_CAMERA_IN_USE -> "Camera in use" ERROR_CAMERA_SERVICE -> "Fatal (service)" ERROR_MAX_CAMERAS_IN_USE -> "Maximum cameras in use" else -> "Unknown" } val exception = RuntimeException("Camera error: $error $msg") Log.e(TAG, exception.message, exception) if (cont.isActive) cont.resumeWithException(exception) } }, cameraHandler) }
打开Camera会通过几个回调来返回Camera的状态:
onOpend()
:表示Camera已正常打开,可以执行后面的流程onDisconnected
:表示Camera已断开,一般发生在不同应用间的Camera资源抢占onError
:表示打开Camera过程中出现异常,可以根据不同的异常类型做相应处理
创建Session
private suspend fun createCaptureSession(): CameraCaptureSession =
suspendCoroutine { cont ->
val targets = listOf(fragmentCameraBinding.surfaceView.holder.surface,
recorderSurface) as MutableList<Surface>
cameraDevice.createCaptureSession(targets, object:
CameraCaptureSession.StateCallback() {
override fun onConfigured(session: CameraCaptureSession) {
cont.resume(session)
}
override fun onConfigureFailed(session: CameraCaptureSession) {
val exception = RuntimeException("Camera ${cameraDevice.id} session configuration failed")
Log.e(TAG, exception.message, exception)
cont.resumeWithException(exception)
}
}, cameraHandler)
}
通过openCamera
获得的CameraDevice
实例执行创建Session流程,这里需要添加整个流程中需要使用到的全部Surface
,同样也是通过回调来返回执行结果:
onConfigured
: 表示创建Session成功,可以继续执行后续操作onConfigureFailed
: 表示创建Session失败
下发指令
-
初始化
CaptureRequest
private val previewRequest: CaptureRequest by lazy { // Capture request holds references to target surfaces captureSession.device.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW).apply { // Add the preview surface target addTarget(fragmentCameraBinding.surfaceView.holder.surface) }.build() } private val recordRequest: CaptureRequest by lazy { // Capture request holds references to target surfaces captureSession.device.createCaptureRequest(CameraDevice.TEMPLATE_RECORD).apply { addTarget(fragmentCameraBinding.surfaceView.holder.surface) addTarget(recorderSurface) }.build() }
根据不同模板和
target
分别创建出预览的previewRequest
和录像的recordRequest
,target
也就是单个流程中需要用于承载Camera数据的Surface
,模板类型有预览的CameraDevice.TEMPLATE_PREVIEW
, 拍照的CameraDevice.TEMPLATE_STILL_CAPTURE
和录像的CameraDevice.TEMPLATE_RECORD
等 -
下发指令
// 下发预览 captureSession.setRepeatingRequest(previewRequest, null, cameraHandler) // 下发录像 captureSession.setRepeatingRequest(recordRequest, null, cameraHandler)