5、多路Camera HAL实现:多Camera设备管理、Session配置、并发Stream的挑战
好,咱们今天聊点硬核的。多路Camera HAL,说白了就是让系统同时管好几个摄像头,还要让它们各自干活不打架。这活儿听起来简单,做起来坑不少。我当年第一次接触这个需求时,心想不就是多开几个设备嘛,结果被并发Stream的同步问题折腾得够呛。
5.1 多Camera设备管理:从单兵到集团军
单摄像头时代,HAL层只需要管好一个设备节点。多路摄像头一来,你得同时管理多个物理设备。每个设备都有自己的属性、能力和状态机。
我个人习惯把设备管理分成三层:
- 物理设备层:直接操作V4L2或MIPI CSI接口
- 逻辑设备层:封装成Android CameraDevice,提供统一接口
- 会话管理层:协调多个设备间的资源分配
举个例子,一个典型的车载环视系统,可能有4个甚至6个摄像头同时工作。每个摄像头都有自己的分辨率、帧率和HDR模式。HAL层得把这些信息注册到CameraProvider中。
核心要点:多设备管理的难点不在于打开设备,而在于设备间的资源隔离。我曾经遇到一个bug,前置摄像头关闭时把后置摄像头的ISP配置也给重置了——就是因为共享资源没做好隔离。
5.2 Session配置:一次成功的“多方会谈”
Session配置,说白了就是告诉HAL层:我要用哪些摄像头,每个摄像头要输出什么格式的Stream。这就像组织一场多方会议,你得先确认每个参会者的设备都正常,然后统一会议参数。
在Android Camera HAL3中,Session配置通过ICameraDeviceSession::configureStreams实现。多路场景下,这个调用会变得复杂。
// 伪代码示例:多路Session配置
status_t configureMultiStreams(
const std::vector<CameraId>& cameraIds,
const StreamConfigurationMap& configs) {
// 1. 校验所有设备是否可用
for (auto& id : cameraIds) {
if (!isDeviceAvailable(id)) {
return INVALID_OPERATION;
}
}
// 2. 检查资源是否足够(带宽、ISP、内存)
ResourceRequirement totalReq = calculateTotalRequirement(configs);
if (!checkResourceAvailability(totalReq)) {
return NO_MEMORY;
}
// 3. 逐个配置Stream
for (size_t i = 0; i < cameraIds.size(); i++) {
status_t ret = configureSingleStream(
cameraIds[i], configs[i]);
if (ret != OK) {
// 回滚已配置的设备
rollbackConfiguredStreams(cameraIds, i);
return ret;
}
}
// 4. 同步所有设备的启动时间
synchronizeDeviceStart(cameraIds);
return OK;
}
你想想看,这里最怕什么?最怕配置到一半失败了。比如第3个摄像头配置成功了,第4个失败了,你得把前面3个都回滚。我早期实现时没做回滚,结果系统状态就乱了,摄像头一直报错。
注意:Session配置必须是原子操作。要么全部成功,要么全部回滚。否则系统会进入一个半配置状态,后续所有操作都可能失败。
5.3 并发Stream的挑战:数据洪流下的调度艺术
多路摄像头同时输出Stream,数据量是成倍增长的。一个1080p@30fps的YUV流,带宽大约在1.5Gbps左右。4个这样的流同时跑,就是6Gbps。这还没算上HDR多帧合成的额外开销。
并发Stream的核心挑战有三个:
- 带宽争抢:多个Stream共享MIPI CSI总线,需要合理分配带宽
- 内存压力:每个Stream都需要自己的Buffer队列,内存消耗巨大
- 时序同步:多路Stream的帧到达时间不一致,需要同步机制
我遇到过最头疼的问题,是两路摄像头同时请求高分辨率HDR流时,ISP直接罢工了。后来查原因,是ISP内部的处理管线被占满,新的请求无法排队。
我的经验:解决并发Stream问题,建议从两个维度入手。一是硬件层面,确认ISP和总线带宽的极限值;二是软件层面,实现Stream优先级和降级机制。当资源不足时,自动降低非关键Stream的分辨率或帧率。
5.4 多路Camera HAL的架构设计
说了这么多,咱们来看看一个典型的多路Camera HAL架构长什么样。下面这张图是我自己项目中用过的方案,经过多次迭代,还算稳定。
这个架构里,最核心的就是中间的多Camera设备管理器。它负责统一管理所有物理设备,处理Session配置,协调并发Stream。我建议把资源分配和同步逻辑都集中在这里,不要分散到各个设备中。
5.5 实战中的避坑指南
最后,分享几个我踩过的坑,希望能帮你少走弯路。
坑1:Buffer死锁
多路Stream同时请求Buffer时,如果Buffer池设计不合理,很容易出现死锁。我曾经遇到过A路Stream占用了所有Buffer,B路Stream一直等不到Buffer释放,整个系统卡死。
解决方案:为每个Stream分配独立的Buffer池,并设置超时机制。当某个Stream长时间未释放Buffer时,强制回收。
坑2:帧同步偏移
多路摄像头同时启动,但它们的帧同步信号(VSYNC)可能不是完全对齐的。时间长了,帧偏移会越来越大。这在环视拼接场景下特别明显,画面会出现撕裂。
解决方案:硬件层面使用同一个PLL生成VSYNC,软件层面实现帧时间戳对齐算法。
坑3:HDR多帧合成的时序冲突
HDR模式需要连续采集多帧(比如长曝光+短曝光),如果此时另一路摄像头也请求HDR,ISP的处理管线可能冲突。我遇到过两路HDR同时启动,结果ISP内部状态机错乱,输出全是黑图。
解决方案:在Session配置阶段就检查HDR资源占用情况,如果ISP不支持并发HDR,就拒绝其中一个请求,或者降级为单帧模式。
嗯,多路Camera HAL的实现,说到底就是资源管理和时序协调的艺术。你把这些基础打牢了,后面做功能安全、做HDR合成,都会顺手很多。
公众号:蓝海资料掘金营,微信deep3321