3. ZSL Pipeline 架构:环形缓冲区、流分离与关键时序

零延迟拍照(ZSL)的核心在于 Pipeline 架构。传统拍照模式下,用户按下快门后 Sensor 才开始曝光,导致明显的快门时滞。ZSL 通过维持一个持续运转的帧流与请求流,配合环形缓冲区,实现“按下即得”的体验。本章深入剖析 ZSL Pipeline 的三个核心设计:环形缓冲区、流分离机制以及关键时序节点。

3.1 ZSL 环形缓冲区设计

环形缓冲区(Ring Buffer)是 ZSL 的“时间机器”。它预分配固定数量的帧缓冲区,在 Sensor 持续输出帧流时,最新的帧不断覆盖最旧的帧。当用户按下快门时,系统直接从缓冲区中取出最近一帧或最佳一帧,无需等待 Sensor 重新曝光。

设计要点: 缓冲区大小直接影响延迟与内存开销。过小容易丢帧,过大会增加内存压力与检索延迟。

3.1.1 缓冲区结构与参数

参数 典型值 说明
缓冲区深度(N) 4 ~ 8 帧 决定可回溯的时间窗口,通常覆盖 100~200ms
帧分辨率 全分辨率 / 预览分辨率 ZSL 通常使用全分辨率帧,避免后期缩放损失细节
元数据槽 与帧槽 1:1 绑定 存储每帧的曝光参数、AE/AWB 状态、时间戳
写入指针(WritePtr) 循环递增 指向当前 Sensor 输出帧的写入位置
读取指针(ReadPtr) 由快门事件触发 指向被选为“拍照帧”的缓冲区位置

3.1.2 环形缓冲区工作流程

  1. 预填充阶段: 打开 Camera 后,Sensor 开始输出帧流,WritePtr 循环写入缓冲区,直到缓冲区填满。
  2. 稳态阶段: 缓冲区满后,新帧覆盖最旧帧,WritePtr 持续循环。
  3. 拍照触发: 用户按下快门,HAL 根据时间戳或 AE 状态选择最佳帧(通常是最近一帧或曝光最稳定的一帧)。
  4. 帧提取: 从 ReadPtr 位置取出帧数据与元数据,送入 JPEG/HEIC 编码管线。
  5. 缓冲区复位: 拍照完成后,缓冲区继续循环,WritePtr 不受影响。
性能提示: 缓冲区深度建议根据 Sensor 帧率与预期延迟计算。例如 30fps 下,4 帧缓冲区可覆盖约 133ms,足以应对大部分场景。
ZSL 环形缓冲区结构 槽 0 帧数据 + 元数据 时间戳: T0 槽 1 帧数据 + 元数据 时间戳: T1 槽 2 帧数据 + 元数据 时间戳: T2 槽 3 帧数据 + 元数据 时间戳: T3 WritePtr ReadPtr 新帧持续覆盖旧帧,ReadPtr 在拍照瞬间锁定最佳帧

3.2 帧流与请求流分离

ZSL 架构中,帧流(Frame Stream) 与 请求流(Request Stream) 是两条独立的异步管道。这种分离设计是降低延迟的关键。

  • 帧流: 由 Sensor 硬件驱动,以固定帧率(如 30fps)持续输出原始帧数据,写入环形缓冲区。帧流不受上层应用状态影响,始终保持稳定。
  • 请求流: 由应用层(Camera2 / CameraX)发起,包含拍照、预览、对焦等请求。请求流不直接控制 Sensor 曝光,而是从帧流中“选取”或“标记”帧。

3.2.1 分离架构的优势

维度 传统耦合架构 ZSL 流分离架构
延迟 请求到达后 Sensor 才开始曝光,延迟约 100~300ms 请求直接取已曝光帧,延迟 < 10ms
帧率稳定性 受请求负载影响,帧率波动大 帧流独立运行,帧率恒定
资源开销 每次请求重新配置 Sensor Sensor 持续工作,无重复配置开销
并发能力 单线程阻塞式处理 帧流与请求流可并行处理

3.2.2 流分离的实现模型

在 Camera HAL 层,帧流与请求流通过 Stream 对象 进行抽象。帧流对应一个 ImageReader 或 Surface,请求流对应 CaptureRequest 队列。HAL 内部维护一个 帧选择器(Frame Selector),负责将请求与帧流中的帧进行匹配。

// 伪代码:帧流与请求流分离的核心逻辑
class ZslPipeline {
    RingBuffer<Frame> frameBuffer;   // 帧流缓冲区
    Queue<CaptureRequest> requestQueue; // 请求流队列

    void onFrameAvailable(Frame frame) {
        frameBuffer.write(frame);  // 帧流持续写入
    }

    void onCaptureRequest(CaptureRequest request) {
        requestQueue.enqueue(request);
        // 请求流不阻塞帧流
    }

    void processRequests() {
        while (!requestQueue.isEmpty()) {
            CaptureRequest req = requestQueue.dequeue();
            Frame bestFrame = frameBuffer.selectBestFrame(req);
            // 将最佳帧送入编码管线
            encodeFrame(bestFrame, req);
        }
    }
}
注意: 帧选择器的策略直接影响拍照质量。常见策略包括:选择时间戳最接近请求的帧、选择 AE 状态最稳定的帧、或选择曝光参数与请求最匹配的帧。
帧流与请求流分离架构 Sensor 硬件 30fps 持续输出 帧流 (Frame Stream) 环形缓冲区写入 帧选择器 匹配最佳帧 应用层请求 CaptureRequest 请求流 (Request Stream) 请求队列 编码管线 JPEG / HEIC 帧流与请求流异步并行,互不阻塞

3.3 关键时序节点分析

ZSL 的“零延迟”并非绝对零延迟,而是将延迟从“按下快门后”转移到“按下快门前的持续运行中”。理解关键时序节点有助于定位性能瓶颈。

3.3.1 时序节点定义

节点 符号 典型耗时 说明
Sensor 曝光时间 Texp 10~33ms 取决于帧率与 AE 策略,30fps 下约 33ms
帧传输延迟 Ttx 5~15ms Sensor 数据通过 CSI 接口传输到 ISP 的时间
ISP 处理时间 Tisp 8~20ms 包括去马赛克、降噪、色调映射等
缓冲区写入时间 Twrite < 1ms 帧数据从 ISP 拷贝到环形缓冲区
帧选择时间 Tselect < 0.5ms 根据元数据匹配最佳帧
编码时间 Tencode 50~200ms JPEG/HEIC 压缩,与分辨率强相关

3.3.2 时序流水线分析

在 ZSL 模式下,关键路径是:用户按下快门 → 帧选择 → 编码输出。Sensor 曝光与 ISP 处理在按下快门之前已经完成。因此,ZSL 的拍照延迟主要由 Tselect + Tencode 决定,而 Texp + Ttx + Tisp 被完全隐藏。

优化焦点: 编码时间 Tencode 是 ZSL 延迟的主要贡献者。优化方向包括:使用硬件编码器、降低 JPEG 质量、或采用 HEIC 格式。
ZSL 关键时序流水线(按下快门前后) t0 t1 t2 t3 传统: 曝光 + ISP 传统: 编码 ZSL: 预曝光 + ISP(按下快门之前完成) 选择 编码 快门按下 ZSL 将曝光与 ISP 处理前置,快门后仅需帧选择 + 编码 延迟从传统模式的 200~400ms 降低到 50~200ms

3.4 架构设计总结