18、Camera 高帧率录制:HighSpeedVideoConfig配置、FPS范围设置、慢动作录制实现、高帧率下的曝光限制
高帧率录制,说白了就是让手机拍出「丝滑慢动作」的核心技术。我最早接触这个功能是在做某款旗舰机的相机调优时,客户要求支持 960fps 的超级慢动作。嗯,那段时间真是踩了不少坑。今天我就把高帧率录制的底层逻辑和配置细节,一次性给你讲透。
18.1 什么是高帧率录制?
常规视频通常是 30fps 或 60fps。高帧率录制指的是 120fps、240fps 甚至 960fps。帧率越高,单位时间内采集的画面越多,回放时就能把时间「拉长」,形成慢动作效果。
你想想看,如果以 240fps 录制,再以 30fps 回放,时间就慢了 8 倍。这就是慢动作录制的核心原理。
核心概念:高帧率录制 ≠ 慢动作录制。高帧率是采集端的行为,慢动作是回放端的行为。但 Android 框架把两者绑定在一起,通过 HighSpeedVideoConfig 统一管理。
18.2 HighSpeedVideoConfig 配置
在 Android Camera2 中,高帧率录制通过 CameraCharacteristics.CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES 和 StreamConfigurationMap.getHighSpeedVideoFpsRanges() 来查询支持的能力。
我个人习惯先检查设备是否支持高帧率:
// 检查高帧率 FPS 范围
val highSpeedFpsRanges = characteristics
.get(CameraCharacteristics.CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES)
?.filter { it.upper >= 120 }
if (highSpeedFpsRanges.isNullOrEmpty()) {
// 不支持高帧率
return
}
// 获取高帧率配置
val configMap = characteristics
.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP)
val highSpeedConfigs = configMap?.getHighSpeedVideoFpsRanges()
这里有个坑:getHighSpeedVideoFpsRanges() 返回的是 Range<Integer> 列表,每个 Range 代表一个支持的 FPS 区间。比如 [120, 120] 表示固定 120fps,[120, 240] 表示可变帧率。
注意:高帧率录制对分辨率有严格限制。通常 1080p 才能跑到 240fps,4K 最多 60fps。我在项目中遇到过某款芯片号称支持 4K 120fps,结果实际跑起来发热严重,最后只能降级到 1080p。
18.3 FPS 范围设置
设置 FPS 范围时,需要用到 CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE。但高帧率模式下,这个参数的行为和普通模式不太一样。
普通模式下,FPS 范围是 [min, max],相机可以在这个区间内动态调整。但高帧率模式下,我建议固定到具体值,比如 [120, 120],否则容易出现帧率抖动。
// 创建高帧率 CaptureRequest
val session = createCaptureSession(/* 高帧率配置 */)
val requestBuilder = session.device.createCaptureRequest(
CameraDevice.TEMPLATE_RECORD
)
// 设置固定 FPS 范围
requestBuilder.set(
CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE,
Range(120, 120) // 固定 120fps
)
// 启用高帧率模式
requestBuilder.set(
CaptureRequest.CONTROL_MODE,
CaptureRequest.CONTROL_MODE_AUTO
)
为什么会这样?因为高帧率下,每帧的曝光时间被压缩得很短。如果允许 FPS 动态变化,曝光参数会频繁调整,导致画面闪烁。我曾经在调试 240fps 录制时,就因为没固定 FPS 范围,拍出来的视频一明一暗,像在蹦迪。
18.4 慢动作录制实现
慢动作录制有两种实现方式:
- 实时慢动作:录制时就以高帧率采集,回放时直接降速。优点是画质好,缺点是文件体积大。
- 后处理慢动作:以常规帧率录制,后期通过插帧实现慢动作。优点是省空间,缺点是画质有损。
Android 原生支持的是第一种。实现时需要注意:
- 必须使用
SurfaceView或TextureView作为预览载体 - 需要创建专门的
HighSpeedVideoCaptureSession - 录制过程中不能切换分辨率或 FPS
// 创建高帧率录制会话
val highSpeedSession = session as? CameraConstrainedHighSpeedCaptureSession
highSpeedSession?.createHighSpeedRequestList(requestBuilder.build())?.let { list ->
// 提交高帧率请求列表
highSpeedSession.setRepeatingBurst(list, callback, handler)
}
这里有个细节:createHighSpeedRequestList() 返回的是一个 List<CaptureRequest>,而不是单个请求。因为高帧率模式下,相机需要交替使用不同的曝光参数来保证画面稳定。
小技巧:如果你需要实现「按下按钮开始慢动作」的效果,可以在普通录制过程中动态切换到高帧率模式。但切换时会有短暂的黑屏或卡顿,建议在 UI 上做一个过渡动画来掩盖。
18.5 高帧率下的曝光限制
高帧率录制最大的敌人是什么?是光线。
你想想看,120fps 意味着每帧只有 8.3ms 的曝光时间。240fps 更是只有 4.1ms。这么短的时间,传感器能收集到的光线非常有限。结果就是画面偏暗、噪点增多。
具体来说,高帧率下的曝光限制包括:
| 帧率 | 最大曝光时间 | 典型场景 | 常见问题 |
|---|---|---|---|
| 120fps | 8.3ms | 室内运动 | 画面偏暗 |
| 240fps | 4.1ms | 户外运动 | 噪点明显 |
| 480fps | 2.0ms | 强光环境 | 动态范围不足 |
| 960fps | 1.0ms | 极强光 | 几乎无法使用 |
我在项目中遇到过最头疼的问题,就是 960fps 录制时画面几乎全黑。后来发现是曝光时间太短,传感器增益已经拉到最大,但 ISO 上去后噪点又爆炸了。最后只能通过硬件补光来解决。
解决曝光限制的几个思路:
- 提高传感器增益:但会引入噪点,需要配合降噪算法
- 使用大光圈镜头:进光量更大,但景深变浅
- 多帧合成:将多帧短曝光合并成一帧长曝光,但会降低实际帧率
- 硬件补光:比如 LED 补光灯,但功耗和发热是问题
经验之谈:如果你在做高帧率录制,建议在 UI 上显示一个「光线不足」的提示。我曾经在户外阴天测试 240fps,用户反馈视频太暗,后来加了光线检测和提示,体验好了很多。
18.6 高帧率录制的知识体系
下面这张图总结了高帧率录制的核心逻辑和配置流程:
从图中可以看到,高帧率录制是一个从设备能力查询到最终视频输出的完整链路。每一步都有对应的配置和限制,尤其是曝光部分,是决定录制成败的关键。
避坑指南:我曾经在调试某款手机时,发现高帧率录制总是报错。查了半天,原来是预览 Surface 的尺寸不匹配。高帧率模式下,预览尺寸必须和录制尺寸一致,否则会触发 IllegalArgumentException。嗯,这个坑我记了好几年。
最后说一句,高帧率录制对系统资源消耗很大。CPU、GPU、ISP、内存、存储,每一个环节都可能成为瓶颈。如果你在实际开发中遇到性能问题,建议先从帧率稳定性入手,再考虑画质优化。毕竟,稳定的 120fps 比忽高忽低的 240fps 体验好得多。