Camera2使用

Camera2使用

添加布局


<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和录像的recordRequesttarget也就是单个流程中需要用于承载Camera数据的Surface,模板类型有预览的CameraDevice.TEMPLATE_PREVIEW, 拍照的CameraDevice.TEMPLATE_STILL_CAPTURE和录像的CameraDevice.TEMPLATE_RECORD

  • 下发指令

    // 下发预览
    captureSession.setRepeatingRequest(previewRequest, null, cameraHandler)
    
    // 下发录像
    captureSession.setRepeatingRequest(recordRequest, null, cameraHandler)
    

你可能感兴趣的:(音视频,Android,kotlin,android,Camera2)