2. Preview链路基础:Preview Pipeline的启动流程、Stream配置、Buffer分配与流转机制
Preview链路是Camera HAL中最常被调用的数据通路,其性能直接决定取景器(Viewfinder)的流畅度与功耗。本章深入剖析Preview Pipeline的启动流程、Stream配置、Buffer分配与流转机制,帮助开发者建立从上层请求到底层硬件调度的完整认知。
2.1 Preview Pipeline启动流程
Preview链路的启动遵循“配置→创建→请求→回调”的固定时序,涉及Camera Service、HAL、Kernel与ISP四层协作。下图展示了完整的启动流程与关键交互节点:
启动延迟优化要点: 在 configureStreams() 阶段预分配 Buffer 池,避免在 processCaptureRequest() 中动态申请内存;ISP 初始化应异步执行,不阻塞 HAL 线程。
2.2 Stream配置详解
Stream配置是Preview链路的基础,决定了图像格式、分辨率、数据方向与Buffer数量。HAL3通过 camera3_stream_t 结构体描述每个Stream,Framework通过 configureStreams() 一次性下发所有Stream配置。
2.2.1 Stream配置参数表
| 参数 | 类型 | 说明 | Preview典型值 |
|---|---|---|---|
| stream_type | camera3_stream_type_t | CAMERA3_STREAM_OUTPUT / INPUT / BIDIRECTIONAL | CAMERA3_STREAM_OUTPUT |
| width | uint32_t | 图像宽度(像素) | 1920 / 1280 / 640 |
| height | uint32_t | 图像高度(像素) | 1080 / 720 / 480 |
| format | int | 像素格式(HAL_PIXEL_FORMAT_*) | HAL_PIXEL_FORMAT_YCbCr_420_888 |
| usage | uint32_t | Gralloc usage flags(GRALLOC_USAGE_*) | GRALLOC_USAGE_SW_READ_OFTEN |
| max_buffers | uint32_t | HAL建议的最大Buffer数 | 4 ~ 6 |
| data_space | android_dataspace_t | 色彩空间(HAL_DATASPACE_*) | HAL_DATASPACE_V0_JFIF |
| rotation | int | 输出旋转角度(0/90/180/270) | 0(由Sensor方向决定) |
2.2.2 Stream配置流程
- Framework调用 configureStreams():传入
camera3_stream_configuration_t,包含所有Stream的数组指针。 - HAL校验Stream组合:检查分辨率、格式、方向是否满足硬件能力;若不支持,返回
-EINVAL。 - HAL设置Stream私有数据:在
camera3_stream_t->priv中保存HAL内部句柄,Framework不修改此字段。 - HAL调整 max_buffers:根据ISP管线深度和内存压力,向Framework建议最优Buffer数量。
- Framework分配Buffer:基于HAL返回的
max_buffers和usage标志,通过Gralloc分配Buffer。
常见配置错误: 多个Stream使用不同
usage 标志可能导致Gralloc分配冲突;Preview Stream的 format 必须与SurfaceView/TextureView的期望格式匹配,否则SurfaceFlinger合成会降级。
2.3 Buffer分配机制
Buffer分配是Preview链路性能的关键瓶颈。HAL3采用“Framework分配、HAL使用”的模式,Buffer通过Gralloc分配,在Framework与HAL之间传递 buffer_handle_t 句柄。
2.3.1 Buffer分配流程
2.3.2 Buffer数量选择策略
| 场景 | 推荐Buffer数 | 原因 |
|---|---|---|
| 低延迟Preview(30fps) | 3 ~ 4 | ISP管线深度2~3帧,预留1个Buffer给Framework消费 |
| 高帧率Preview(60fps) | 4 ~ 6 | 减少Buffer饥饿概率,容忍ISP调度抖动 |
| 多Stream并发(Preview+Video) | 6 ~ 8 | 每个Stream独立Buffer池,避免互相阻塞 |
| 低内存设备 | 2 ~ 3 | 牺牲部分流畅度换取内存占用 |
性能调优建议: 在HAL的
configureStreams() 中,根据当前系统内存水位动态调整 max_buffers。例如,当可用内存 < 200MB 时,将Preview Stream的Buffer数从4降为3,避免触发LMK(Low Memory Killer)。
2.4 Buffer流转机制
Buffer流转是Preview链路的核心循环:Framework将Buffer交给HAL → HAL填充数据 → HAL返回Buffer给Framework → Framework消费后再次提交。这个循环的效率决定了Preview的帧率与延迟。
2.4.1 流转状态机
每个Buffer在生命周期中经历以下状态:
- FREE:Buffer空闲,在Framework的BufferQueue中等待被提交。
- DEQUEUED:Framework从BufferQueue取出Buffer,准备提交给HAL。
- QUEUED:Buffer通过
processCaptureRequest()提交给HAL,HAL开始处理。 - IN_FLIGHT:HAL正在填充数据(ISP处理中),Buffer不可用。
- FILLED:HAL通过
processCaptureResult()返回Buffer,Framework开始消费。 - RELEASED:Framework消费完成,Buffer回到FREE状态。
2.4.2 流转中的关键性能问题
| 问题 | 表现 | 根因 | 优化方案 |
|---|---|---|---|
| Buffer饥饿 | 帧率抖动、丢帧 | Buffer数量不足或Framework消费过慢 | 增加max_buffers;优化SurfaceFlinger合成效率 |
| Buffer泄漏 | 内存持续增长,最终OOM | HAL未在processCaptureResult()中返回Buffer | 添加Buffer超时监控,强制回收超时Buffer |
| Cache抖动 | CPU访问Buffer时延迟突增 | HAL写入后未clean cache,Framework读取时触发cache miss | 使用GRALLOC_USAGE_HW_CAMERA_WRITE避免CPU cache操作 |
| 跨进程拷贝 | CPU占用高、延迟大 | HAL与Framework之间通过binder传递buffer_handle_t时发生隐式拷贝 | 使用dma-buf传递fd,避免memcpy |
2.5 Preview链路性能调优实践
基于上述机制,以下是在实际项目中验证有效的Preview链路调优策略:
调优清单:
- Stream配置阶段: 优先选择原生格式(如NV12/YV12),避免格式转换;分辨率对齐ISP输出能力,避免scaling。
- Buffer分配阶段: 使用
GRALLOC_USAGE_HW_CAMERA_WRITE | GRALLOC_USAGE_HW_TEXTURE减少CPU介入;Buffer数量设为ISP管线深度+2。 - 流转阶段: 在HAL内部维护Buffer状态表,使用原子操作标记IN_FLIGHT状态;processCaptureResult()中优先返回Buffer再填充Metadata。
- 监控手段: 在HAL中记录每次processCaptureRequest()到processCaptureResult()的耗时,通过dumpsys输出;设置单帧超时阈值(如100ms),超时则跳过该帧。