4. Camera HAL3.4 与 HAL3.6/3.8 的差异:Pipeline Depth、Partial Result、Repeating Request 等关键特性的版本兼容处理
在跨厂商平台适配 Camera HAL 时,版本差异是导致兼容性问题的首要根源。Android 从 HAL3.4 演进到 HAL3.6/3.8,引入了多项关键特性变更,其中 Pipeline Depth、Partial Result 和 Repeating Request 是影响最深的三个维度。本章将深入剖析这些差异,并给出实战级别的兼容处理策略。
4.1 版本演进全景:从同步到异步的架构跃迁
HAL3.4 作为早期版本,其设计哲学是“请求-响应”的严格同步:应用层下发一个 capture request,HAL 必须返回一个完整的 capture result。这种模型在单帧场景下工作良好,但在高帧率、多路流(如同时预览 + 拍照 + 视频)场景下,CPU 和 ISP 的利用率极低。
HAL3.6/3.8 引入了 异步流水线模型,允许 HAL 在未完成所有处理前,先返回部分结果(Partial Result),同时通过 Pipeline Depth 告知框架当前流水线中最多可以有多少个请求正在处理。这一变化使得多帧并发成为可能,但也带来了版本兼容的适配难点。
4.2 Pipeline Depth:并发能力的“水位线”
Pipeline Depth 是 HAL 通过 android.request.pipelineDepth 静态元数据上报给框架的整数值,表示 HAL 内部流水线最多能同时处理的请求数量。这个值直接影响框架的请求调度策略。
| 版本 | 典型 Pipeline Depth | 框架行为 | 性能影响 |
|---|---|---|---|
| HAL3.4 | 1(或未定义) | 串行下发,等待前一个结果返回后才发下一个请求 | 低帧率,CPU/ISP 空闲等待 |
| HAL3.6 | 2 ~ 4 | 允许流水线并发,框架可提前下发多个请求 | 中等帧率,流水线利用率提升 |
| HAL3.8 | 4 ~ 8 | 深度流水线,支持多帧叠加处理 | 高帧率,ISP 全速运转 |
4.2.1 兼容处理策略
- 策略一:静态上报 + 动态调整 —— 在
camera_metadata中静态上报一个保守值(如 2),然后在运行时根据实际负载动态调整内部队列深度。 - 策略二:平台特性探测 —— 在 HAL 初始化阶段,通过读取设备树或硬件寄存器获取 ISP 流水线级数,据此计算 Pipeline Depth。
- 策略三:框架侧回退兼容 —— 如果 HAL 上报的 Pipeline Depth 为 0 或 1,框架应自动启用“请求排队”模式,避免因并发导致 HAL 内部 buffer 溢出。
// 示例:Pipeline Depth 计算逻辑(C++)
int32_t calculatePipelineDepth(const CameraHardwareConfig& config) {
int isp_stages = config.getIspPipelineStages(); // ISP 流水线级数
int buffer_slots = config.getInternalBufferSlots(); // 内部 buffer 槽位数
int depth = std::min(isp_stages, buffer_slots);
// HAL3.4 兼容:至少保证 1
depth = std::max(depth, 1);
// HAL3.6+ 优化:不超过框架允许的最大值
const int kMaxPipelineDepth = 8;
depth = std::min(depth, kMaxPipelineDepth);
return depth;
}
4.3 Partial Result:流式交付的“中间件”
Partial Result 是 HAL3.6 引入的核心机制,允许 HAL 在完整结果生成之前,先返回部分已处理完成的 metadata 或 buffer。例如,3A 统计信息可以在图像数据之前返回,从而让框架提前进行白平衡或曝光调整。
4.3.1 版本差异对比
| 特性 | HAL3.4 | HAL3.6/3.8 |
|---|---|---|
| 结果交付方式 | 一次性返回完整结果 | 支持多次 Partial + 一次 Final |
| Partial Result 数量 | 不支持 | 0 ~ N 次(由 HAL 决定) |
| 框架等待策略 | 等待所有 metadata 就绪 | 可提前处理部分 metadata |
| Buffer 交付 | 与 metadata 绑定 | 可分离交付 |
4.4 Repeating Request:会话管理的“心跳”
Repeating Request 是 HAL3 中用于持续流(如预览、视频录制)的请求模式。应用层通过 submitRepeatingRequest() 下发一个模板请求,HAL 需要持续重复执行该请求,直到被新的 Repeating Request 替换或显式停止。
4.4.1 版本行为差异
- HAL3.4:Repeating Request 与 Single Request 共享同一个请求队列,HAL 需要自行区分。框架不保证 Repeating Request 的连续性,HAL 可能收到重复的请求 ID。
- HAL3.6:引入
ANDROID_REQUEST_ID和ANDROID_REQUEST_FRAME_COUNT元数据,框架为每个 Repeating Request 分配递增的 frame number,HAL 可据此追踪帧序列。 - HAL3.8:进一步规范了 Repeating Request 的“替换语义”:当新的 Repeating Request 下发时,HAL 必须立即停止当前重复流,切换到新请求,不能出现“新旧请求混合执行”的情况。
// 示例:Repeating Request 处理逻辑(伪代码)
void processRepeatingRequest(const CaptureRequest& request) {
if (isRepeatingRequest(request)) {
// HAL3.6+:使用 frame_count 追踪序列
uint32_t frame_count = request.getMetadata(ANDROID_REQUEST_FRAME_COUNT);
// HAL3.8:检查是否有新的替换请求
if (hasNewRepeatingRequest()) {
flushCurrentPipeline(); // 立即刷新流水线
switchToNewRequest(); // 切换到新请求
} else {
// 继续执行当前重复流
executeRepeatingFrame(frame_count);
}
} else {
// Single Request 处理
executeSingleFrame(request);
}
}
4.5 版本兼容处理清单
| 兼容项 | HAL3.4 行为 | HAL3.6/3.8 行为 | 适配方案 |
|---|---|---|---|
| Pipeline Depth | 不强制上报 | 必须上报 ≥ 2 | 根据 ISP 级数 + buffer 槽位计算 |
| Partial Result | 不支持 | 可选实现 | 先实现 Final-only,再逐步拆分 |
| Repeating Request | 共享队列 | 独立帧序列 | 使用 frame_count 追踪,实现替换语义 |
| Result 回调 | process_capture_result | process_capture_result + partial_result | 增加 partial result 回调分支 |
| 错误处理 | 直接返回错误 | 支持 error_result 回调 | 实现 error 回调,区分帧错误和设备错误 |
4.6 兼容性测试验证点
- Pipeline Depth 验证:使用 CTS 的
android.hardware.camera2.cts.CameraExtensionCharacteristicsTest检查上报值是否在合理范围内。 - Partial Result 验证:通过
CameraMetadataTest检查 Partial Result 中是否包含ANDROID_REQUEST_PARTIAL_RESULT_COUNT标签。 - Repeating Request 验证:使用
CameraPreviewTest连续切换预览流,检查是否出现帧跳跃或卡顿。 - 跨版本回退验证:在 Android 10 (HAL3.4) 和 Android 12 (HAL3.8) 上分别运行同一套 HAL 二进制,确保不崩溃。
partial_result_count 设置为 0,导致框架认为 HAL 不支持 Partial Result,从而退化为 HAL3.4 模式。正确的做法是:如果不实现 Partial Result,应将 partial_result_count 设置为 1(表示只有 Final Result),而不是 0。