2. HAL3 Pipeline 详解

Camera HAL3 的核心是一个高度流水线化的请求-响应架构。理解其内部 Pipeline 的处理流程、从应用层到 HAL 层的请求流转以及同步与异步机制,是进行性能优化和 CTS 问题定位的基础。本章将深入剖析这一架构的每个关键环节。

2.1 HAL3 Pipeline 整体架构与处理流程

HAL3 的 Pipeline 设计旨在最大化吞吐量和降低延迟。它将一次拍照或预览过程分解为多个阶段,允许硬件在软件准备下一帧的同时处理当前帧。整个流程从应用层发起请求开始,经过框架层封装,最终由 HAL 层驱动硬件完成捕获和结果返回。

核心设计理念: 基于 CaptureRequest 和 CaptureResult 的异步流水线模型。应用提交请求后不阻塞,通过回调或轮询获取结果。

下图展示了 HAL3 Pipeline 从应用层到 HAL 层的完整数据流与状态转换。

HAL3 Pipeline 完整处理流程 HAL3 Pipeline 请求-响应流转图 应用层 (App) 框架层 (Framework) HAL 层 (Camera HAL) 硬件层 (Sensor/ISP) ① 请求构建 ② 请求入队 & 排序 ③ 参数解析 & 验证 ④ 硬件捕获 (Sensor/ISP) ⑤ 结果构建 & 回调 关键说明: • 请求是异步的,应用提交后立即返回。 • Pipeline 支持多请求并发处理。 • 结果通过回调机制异步返回应用。 • 硬件捕获阶段是性能瓶颈点。 性能指标: • Pipeline 深度:通常为 3-6 帧 • 请求到结果延迟:< 33ms (30fps) • 吞吐量:取决于 Sensor 和 ISP 能力

2.2 从应用层到 HAL 层的请求流转

一次完整的请求流转涉及多个层次的交互。应用层通过 Android 的 Camera2 API 发起请求,该请求经过框架层封装后,最终由 HAL 层处理。以下是详细的流转步骤:

  1. 应用层发起请求: 应用通过 CameraCaptureSession.capture() 或 setRepeatingRequest() 提交 CaptureRequest。
  2. 框架层封装: Android Framework 将 CaptureRequest 转换为 HAL 层可识别的 camera3_capture_request_t 结构体,并分配帧编号 (Frame Number)。
  3. HAL 层接收: HAL 的 process_capture_request() 函数被调用,传入封装后的请求结构体。
  4. 硬件处理: HAL 驱动 Sensor 曝光、ISP 处理,生成原始图像数据或经过处理的 YUV/JPEG 数据。
  5. 结果返回: HAL 通过 process_capture_result() 回调将 camera3_capture_result_t 返回给框架层,框架层再将其转换为 CaptureResult 回调给应用。
性能优化要点: 在应用层,应尽量复用 CaptureRequest 对象,避免频繁创建和销毁。在 HAL 层,应减少 process_capture_request() 中的内存分配操作,使用预分配的内存池。

2.3 同步与异步机制详解

HAL3 架构中,同步与异步机制的正确实现是保证系统稳定性和性能的关键。错误的同步设计会导致死锁、帧率下降或 CTS 测试失败。

2.3.1 异步请求处理

HAL3 要求 process_capture_request() 必须是非阻塞的。HAL 在接收到请求后,应立即返回,并在后台线程中处理硬件操作。框架层通过 camera3_stream_buffer_t 中的 acquire_fence 和 release_fence 文件描述符来实现 Buffer 的同步。

2.3.2 Buffer 同步机制

Buffer 同步是 HAL3 中最复杂的同步场景。它使用 Sync Fence 机制来协调不同硬件模块(如 Sensor、ISP、Video Encoder)对同一块 Buffer 的访问。

同步对象 描述 CTS 验证要点
acquire_fence HAL 在读取 Buffer 前必须等待的 Fence。框架层通过此 Fence 通知 HAL 数据已就绪。 HAL 必须在访问 Buffer 数据前等待此 Fence;未等待直接访问会导致数据竞争。
release_fence HAL 在完成 Buffer 写入后发出的 Fence。框架层通过此 Fence 得知 HAL 已释放 Buffer 所有权。 HAL 必须在完成写入后立即 Signal 此 Fence;延迟 Signal 会导致框架层 Buffer 饥饿。
intermediate_buffer 用于内部处理的临时 Buffer,不直接返回给框架层。 HAL 必须正确管理其生命周期,避免内存泄漏。
常见陷阱: 许多 CTS 失败案例源于 HAL 未正确处理 Fence。例如,在 process_capture_result() 中返回了尚未 Signal release_fence 的 Buffer,或者忽略了 acquire_fence 直接读取数据。这些都会导致框架层检测到超时或数据错误。

2.3.3 同步与异步流程对比

为了更清晰地展示同步与异步在 HAL3 中的不同表现,下图对比了两种模式下的请求处理路径。

同步 vs 异步请求处理对比 同步 vs 异步请求处理对比 同步模式 (不推荐) 应用提交请求 HAL 阻塞等待硬件完成 应用阻塞等待结果返回 处理完成,返回结果 异步模式 (HAL3 标准) 应用提交请求 (非阻塞) HAL 立即返回,后台处理 应用继续执行其他任务 回调通知应用结果就绪 异步模式是 HAL3 的强制要求,同步模式会导致 CTS 测试失败和严重的性能问题。

2.4 Pipeline 深度与性能权衡

Pipeline 深度(即同时处理的请求数量)是影响延迟和吞吐量的关键参数。HAL 通过 camera3_device_ops_t 中的 get_metadata() 上报 ANDROID_REQUEST_PIPELINE_MAX_DEPTH 来声明其最大深度。

Pipeline 深度 优势 劣势 典型场景
低 (1-2) 延迟低,内存占用少 吞吐量受限,无法充分利用硬件 单帧拍照、高精度需求
中 (3-4) 平衡延迟与吞吐量 需要更复杂的 Buffer 管理 30fps 视频预览、连拍
高 (5-6) 高吞吐量,适合高速连拍 延迟增加,内存压力大 60fps 或 120fps 高速视频
优化建议: 不要盲目追求高 Pipeline 深度。应根据硬件能力和实际场景动态调整。例如,在低光环境下,可以降低 Pipeline 深度以减少噪声累积;在高速连拍时,适当增加深度以提升帧率。

2.5 关键代码路径示例

以下代码展示了 HAL 层 process_capture_request 函数中处理请求和 Fence 的标准流程。

// HAL 层 process_capture_request 实现片段
int32_t my_hal_process_capture_request(
        const struct camera3_device *device,
        camera3_capture_request_t *request) {

    // 1. 验证请求参数
    if (request->num_output_buffers == 0) {
        ALOGE("No output buffers provided");
        return -EINVAL;
    }

    // 2. 等待所有输入 Buffer 的 acquire_fence
    for (size_t i = 0; i < request->num_output_buffers; ++i) {
        camera3_stream_buffer_t *buf = &request->output_buffers[i];
        if (buf->acquire_fence >= 0) {
            // 阻塞等待 Fence 信号
            sync_wait(buf->acquire_fence, 1000); // 超时 1 秒
            close(buf->acquire_fence);
            buf->acquire_fence = -1;
        }
    }

    // 3. 将请求加入内部工作队列,立即返回
    //    实际硬件操作在后台线程中执行
    enqueue_request_to_hardware(request);

    // 4. 返回 0 表示成功接收请求
    return 0;
}

// 后台线程处理完成后,调用 process_capture_result 回调
void my_hal_on_capture_done(camera3_capture_result_t *result) {
    // 设置 release_fence
    for (size_t i = 0; i < result->num_output_buffers; ++i) {
        camera3_stream_buffer_t *buf = &result->output_buffers[i];
        // 创建并设置 release_fence,通知框架层 Buffer 已可用
        buf->release_fence = create_release_fence();
    }

    // 调用框架层回调
    result->result = build_capture_result_metadata();
    g_camera3_callbacks->process_capture_result(g_callback_ops, result);
}
调试技巧: 在开发阶段,可以通过打印每个请求的帧编号和 Fence 状态来追踪 Pipeline 的流转情况。使用 adb shell dumpsys media.camera 可以查看框架层维护的请求队列状态。