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持续写入编码后的数据包。 - 切换摄像头时:
- 先停止当前摄像头的重复捕获请求,关闭当前
CaptureSession和CameraDevice。 - 根据新摄像头的传感器方向和设备屏幕方向,重新计算画面旋转角度,更新后续
CaptureRequest的方向参数(CaptureRequest.JPEG_ORIENTATION)。 - 打开新摄像头,创建新的
CameraCaptureSession,绑定同一个编码器input Surface(不重启编码器)。 - 提交新的
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
相关产品推荐
相关产品推荐

