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 是影响最深的三个维度。本章将深入剖析这些差异,并给出实战级别的兼容处理策略。

核心观点: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 告知框架当前流水线中最多可以有多少个请求正在处理。这一变化使得多帧并发成为可能,但也带来了版本兼容的适配难点。

适配提示:如果你的平台原本基于 HAL3.4 开发,直接升级到 HAL3.6/3.8 时,最常见的崩溃原因是 Pipeline Depth 上报为 0 或 1,导致框架认为 HAL 不支持并发,从而退化为同步模式,引发性能下降甚至 ANR。
HAL3.4 vs HAL3.6/3.8 架构对比 HAL3.4 同步模型 Request N Result N 必须等待 Result N 返回 才能下发 Request N+1 Request N+1 Result N+1 HAL3.6/3.8 异步流水线 Req N Req N+1 Req N+2 Pipeline Depth = 3 Partial N Final N Partial N+1

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 全速运转
常见陷阱:部分厂商在 HAL3.6 适配时,将 Pipeline Depth 硬编码为 1,导致框架认为 HAL 不支持并发,退化为 HAL3.4 的同步模式。此时即使 HAL 内部实现了流水线,框架也不会提前下发请求,造成性能浪费。正确的做法是根据 ISP 硬件流水线级数 + 内部 buffer 队列深度来动态计算该值。

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 统计信息可以在图像数据之前返回,从而让框架提前进行白平衡或曝光调整。

Partial Result 流式交付流程 Capture Request Partial: 3A Stats Framework Partial: Metadata Final: Image Buffer Partial Result 可多次返回,Final Result 只返回一次

4.3.1 版本差异对比

特性 HAL3.4 HAL3.6/3.8
结果交付方式 一次性返回完整结果 支持多次 Partial + 一次 Final
Partial Result 数量 不支持 0 ~ N 次(由 HAL 决定)
框架等待策略 等待所有 metadata 就绪 可提前处理部分 metadata
Buffer 交付 与 metadata 绑定 可分离交付
适配建议:在 HAL3.6 适配初期,建议先实现“零 Partial”模式(即只返回一次 Final Result),确保基本功能正常。待稳定后,再逐步将 3A 统计信息、传感器时间戳等低延迟数据拆分为 Partial Result,以优化预览启动速度和 AE/AWB 收敛时间。

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 回调,区分帧错误和设备错误
实战总结:版本兼容的核心不是“一刀切”地实现所有新特性,而是通过特性探测 + 条件编译 + 运行时适配,让同一套 HAL 代码能够在不同 Android 版本上正确运行。建议在 HAL 初始化阶段读取框架版本号,动态注册对应的回调函数和特性集。

4.6 兼容性测试验证点

  1. Pipeline Depth 验证:使用 CTS 的 android.hardware.camera2.cts.CameraExtensionCharacteristicsTest 检查上报值是否在合理范围内。
  2. Partial Result 验证:通过 CameraMetadataTest 检查 Partial Result 中是否包含 ANDROID_REQUEST_PARTIAL_RESULT_COUNT 标签。
  3. Repeating Request 验证:使用 CameraPreviewTest 连续切换预览流,检查是否出现帧跳跃或卡顿。
  4. 跨版本回退验证:在 Android 10 (HAL3.4) 和 Android 12 (HAL3.8) 上分别运行同一套 HAL 二进制,确保不崩溃。
特别注意:部分厂商在 HAL3.6 适配时,错误地将 Partial Result 的 partial_result_count 设置为 0,导致框架认为 HAL 不支持 Partial Result,从而退化为 HAL3.4 模式。正确的做法是:如果不实现 Partial Result,应将 partial_result_count 设置为 1(表示只有 Final Result),而不是 0。