You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android Camera2 API录制视频时前后摄像头切换实现方案问询

可以用MediaCodec + MediaMuxer实现录制中摄像头切换(基于Camera2 API)

完全可以通过MediaCodec(视频编码)+ MediaMuxer(封装视频文件)的组合实现录制过程中前后摄像头无缝切换,且满足你对高层API、无分段合并的需求,以下是具体实现思路和关键细节:

核心实现思路

1. 提前统一兼容配置

  • 用CameraManager获取前后摄像头的CameraCharacteristics,提前筛选出两者共同支持的分辨率、帧率、像素格式(比如优先选两者都支持的1080P/30fps,格式用ImageFormat.YUV_420_888),以此作为MediaCodec的输入配置,避免切换时重启编码器。
  • 初始化MediaCodec时,设置成编码模式(MediaCodec.CONFIGURE_FLAG_ENCODE),并配置兼容的MediaFormat(包含分辨率、帧率、比特率等通用参数)。
  • 初始化MediaMuxer,指定输出文件路径,等待编码器输出格式就绪后添加视频轨道。

2. 摄像头切换的无缝衔接逻辑

  • 录制启动阶段:打开第一个摄像头,创建CameraCaptureSession,将编码器的input Surface和预览Surface绑定,提交TEMPLATE_RECORD类型的CaptureRequest,开始向编码器推送帧数据,MediaMuxer持续写入编码后的数据包。
  • 切换摄像头时:
    1. 先停止当前摄像头的重复捕获请求,关闭当前CaptureSession和CameraDevice。
    2. 根据新摄像头的传感器方向和设备屏幕方向,重新计算画面旋转角度,更新后续CaptureRequest的方向参数(CaptureRequest.JPEG_ORIENTATION)。
    3. 打开新摄像头,创建新的CameraCaptureSession,绑定同一个编码器input Surface(不重启编码器)。
    4. 提交新的CaptureRequest,继续向编码器推送帧数据,MediaMuxer会持续写入数据,全程生成单个完整视频文件。

3. 解决方向配置问题

  • 放弃依赖MediaRecorder的orientation配置,改为通过Camera2的CaptureRequest直接控制摄像头输出方向,或者在MediaCodec的MediaFormat中设置KEY_ROTATION参数。
  • 切换摄像头时,根据新摄像头的SensorOrientation和当前屏幕旋转角度,实时计算正确的旋转值,写入新的CaptureRequest即可,无需修改编码器配置。

关键代码片段示例

切换摄像头的核心逻辑

private var currentCameraDevice: CameraDevice? = null
private var currentCaptureSession: CameraCaptureSession? = null
private val handler = Handler(Looper.getMainLooper())
private lateinit var codecInputSurface: Surface

// 切换摄像头方法
private fun switchCamera(targetCameraId: String) {
    // 清理当前摄像头资源
    currentCaptureSession?.apply {
        stopRepeating()
        abortCaptures()
        close()
    }
    currentCameraDevice?.close()

    // 获取目标摄像头特性
    val targetCameraChars = cameraManager.getCameraCharacteristics(targetCameraId)
    val commonSize = getCommonSupportedSize(targetCameraChars, prevCameraChars) // 提前计算的通用分辨率

    // 打开新摄像头并创建捕获会话
    cameraManager.openCamera(targetCameraId, object : CameraDevice.StateCallback() {
        override fun onOpened(camera: CameraDevice) {
            currentCameraDevice = camera
            val surfaces = listOf(previewSurface, codecInputSurface)
            camera.createCaptureSession(surfaces, object : CameraCaptureSession.StateCallback() {
                override fun onConfigured(session: CameraCaptureSession) {
                    currentCaptureSession = session
                    // 配置捕获请求,设置方向和分辨率
                    val captureRequest = camera.createCaptureRequest(CameraDevice.TEMPLATE_RECORD).apply {
                        addTarget(codecInputSurface)
                        addTarget(previewSurface)
                        set(CaptureRequest.SCALER_CROP_REGION, getCropRegion(targetCameraChars, commonSize))
                        set(CaptureRequest.JPEG_ORIENTATION, calculateOrientation(targetCameraChars))
                    }
                    // 启动重复捕获,继续推送帧到编码器
                    session.setRepeatingRequest(captureRequest, null, handler)
                }

                override fun onConfigureFailed(session: CameraCaptureSession) {
                    // 处理配置失败逻辑
                }
            }, handler)
        }

        override fun onDisconnected(camera: CameraDevice) {
            camera.close()
        }

        override fun onError(camera: CameraDevice, error: Int) {
            camera.close()
        }
    }, handler)
}

// 计算摄像头输出方向
private fun calculateOrientation(chars: CameraCharacteristics): Int {
    val sensorOrientation = chars.get(CameraCharacteristics.SENSOR_ORIENTATION)!!
    val displayRotation = windowManager.defaultDisplay.rotation
    return when (displayRotation) {
        Surface.ROTATION_0 -> sensorOrientation
        Surface.ROTATION_90 -> (sensorOrientation + 270) % 360
        Surface.ROTATION_180 -> (sensorOrientation + 180) % 360
        Surface.ROTATION_270 -> (sensorOrientation + 90) % 360
        else -> sensorOrientation
    }
}

注意事项

  • 编码器全程保持运行状态,不要在切换摄像头时重启,确保数据流连续,避免生成分段视频。
  • 必须确保前后摄像头的输出帧格式与编码器输入格式完全匹配,提前通过StreamConfigurationMap验证兼容性。
  • 处理摄像头切换时的资源同步,确保旧会话完全关闭后再启动新会话,避免CameraDevice资源冲突。

内容的提问来源于stack exchange,提问作者nil

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 21:55:10