Android Camera2 会话创建逻辑

Android Camera2 session creation logic

我是 Camera2 框架的新手,正在尝试了解捕获会话的创建逻辑。

我需要一个简单的东西——预览和录制视频。我还想在开始录制视频时设置正确的方向提示。但是我遇到了一个 chicken/egg 问题。

这是我的逻辑:

为了开始录制,我是这样做的:

    val recordRequest = session.device.createCaptureRequest(CameraDevice.TEMPLATE_RECORD).apply {
        // Add the preview and recording surface targets
        addTarget(viewFinder.holder.surface)
        addTarget(recorder.surface)
    }.build()

    session.setRepeatingRequest(recordRequest, null, cameraHandler)

    recorder.setOrientationHint(it) // NOT allowed after getSurface()!
    recorder.prepare()              // NOT allowed after getSurface()!
    recorder.start()

但是,我在上面添加目标时已经调用了 recorder.surface(或 getSurface())。可以认为我可以准备然后添加目标,但是 documentation for addTarget() 说,表面 添加的表面必须是最近包含的表面之一调用 CameraDevice#createCaptureSession


这导致了一个有趣的问题。每当我打开应用程序时,我都需要创建捕获会话以开始预览相机图像。但是,在创建时 createCaptureSession() needs to include all the surfaces that will come in future capture requests. Which means that I also need to include the recording surface, even if I simply open camera without recording yet. How do I get this Surface for recording? Well, the documentation 说我可以从 MediaRecorder 获取它,或者我可以从 MediaCodec 获取它。我想从 MediaRecorder 获取它,因为我想使用 CamcorderProfiles。然而,正如我在上面的代码中所展示的,一旦我在会话创建时从记录器获取表面 - 我无法在那里进行任何更改开始录制,如设置方向提示

官方 Camera2Video sample app does a trick - it uses createPersistentInputSurface 然而在他们的示例中,相机是固定的,这允许他们为其分配足够的内存并在整个应用程序生命周期中使用该表面。

如何解决?我误解了这里的概念吗?当我开始录制时,我如何在稍后创建记录器,但当我打开相机进行预览时,仍然有之前创建的表面?

使用持久输入表面是正确的方法。知道录制方向后创建一个新的 MediaRecorder,并使用持久输入表面设置其表面。

这正是 Camera2Video 示例所做的,as well:

        // React to user touching the capture button
        capture_button.setOnTouchListener { view, event ->
            when (event.action) {

                MotionEvent.ACTION_DOWN -> lifecycleScope.launch(Dispatchers.IO) {

                    // Prevents screen rotation during the video recording
                    requireActivity().requestedOrientation =
                            ActivityInfo.SCREEN_ORIENTATION_LOCKED

                    // Start recording repeating requests, which will stop the ongoing preview
                    //  repeating requests without having to explicitly call `session.stopRepeating`
                    session.setRepeatingRequest(recordRequest, null, cameraHandler)

                    // Finalizes recorder setup and starts recording
                    recorder.apply {
                        // Sets output orientation based on current sensor value at start time
                        relativeOrientation.value?.let { setOrientationHint(it) }
                        prepare()
                        start()
                    }

并且 recorder 是使用之前创建的永久表面创建的:

    /** Saves the video recording */
    private val recorder: MediaRecorder by lazy { createRecorder(recorderSurface) 
    }

你说摄像头是固定的,是指应用方向是固定的,还是样本不支持切换front/back摄像头? None 其中对于持久性表面尤其重要;如果需要,您可以在切换摄像头或更改方向时创建一个新摄像头。