4. Stream与Buffer关系:Stream配置参数、Buffer请求与返回流程、Stream与Buffer的生命周期

在Camera HAL框架中,Stream(流)与Buffer(缓冲区)是数据管道的核心抽象。Stream定义了图像的生产规格(分辨率、格式、帧率、用途),而Buffer承载了实际的像素数据。理解两者之间的绑定关系、配置参数、请求流转以及生命周期管理,是掌握Buffer队列调度与帧同步优化的基础。

核心概念速记: Stream是“管道规格说明书”,Buffer是“管道中流动的数据包”。一个Stream对应一个Buffer队列,HAL通过配置Stream参数来创建队列,通过请求与返回Buffer来驱动数据流动。

4.1 Stream配置参数详解

Stream的配置参数在Camera3StreamConfiguration结构体中定义,是HAL设备configure_streams()调用的核心输入。这些参数决定了Buffer队列的容量、格式、方向和使用场景。

参数名称 类型 说明 典型值/范围
stream_type uint32_t 流方向:输入(给ISP)、输出(给应用)、双向 CAMERA3_STREAM_OUTPUT (0)
CAMERA3_STREAM_INPUT (1)
CAMERA3_STREAM_BIDIRECTIONAL (2)
width uint32_t 图像宽度(像素) 640, 1280, 1920, 3840, 4096
height uint32_t 图像高度(像素) 480, 720, 1080, 2160, 3072
format int32_t 像素格式(HAL定义) HAL_PIXEL_FORMAT_YCbCr_420_888
HAL_PIXEL_FORMAT_RAW10
HAL_PIXEL_FORMAT_BLOB (JPEG)
data_space android_dataspace_t 色彩空间与动态范围 HAL_DATASPACE_SRGB
HAL_DATASPACE_BT2020_HLG
rotation int32_t 输出旋转角度(仅输出流) 0, 90, 180, 270
usage uint64_t Buffer使用标志(Gralloc) GRALLOC_USAGE_SW_READ_OFTEN
GRALLOC_USAGE_HW_TEXTURE
max_buffers uint32_t HAL请求的最大Buffer数(由HAL填充) 通常 3~8,取决于流水线深度
配置要点:
  • stream_type 决定了Buffer的所有权流向:输出流由HAL填充,输入流由框架填充。
  • format 与 data_space 必须匹配,否则HAL可能拒绝配置。
  • max_buffers 由HAL在configure_streams()返回时设置,框架据此分配Buffer池。
  • 多个Stream可以共享同一个物理Camera,但分辨率/格式组合受ISP带宽限制。

4.2 Buffer请求与返回流程

Buffer的请求与返回构成了Camera HAL的帧循环。框架通过process_capture_request()向HAL提交请求,HAL通过process_capture_result()回调返回填充好的Buffer。整个流程遵循生产者-消费者模型。

Buffer请求与返回流程(帧循环) Framework Camera HAL 1. 填充CaptureRequest process_capture_request() 2. 硬件处理/ISP 3. 填充CaptureResult process_capture_result() 4. 消费Buffer 循环:下一个请求 关键约束: • 框架必须等待HAL返回上一个请求的Buffer,才能复用该Buffer提交新请求(Buffer级流水线)。 • HAL可以在一个result中返回多个Stream的Buffer,但每个Buffer只能属于一个Stream。
Buffer所有权转移:
  • 请求阶段: 框架将Buffer的handle(句柄)传递给HAL,但所有权仍属框架。
  • 处理阶段: HAL拥有Buffer的临时独占访问权,可读写像素数据。
  • 返回阶段: HAL通过result回调将Buffer归还框架,框架恢复完全控制权。
  • 关键规则: HAL不得在返回后继续访问Buffer,否则会导致数据竞争或崩溃。

4.3 Stream与Buffer的生命周期

Stream和Buffer的生命周期由配置、激活、流转、销毁四个阶段构成。理解生命周期有助于避免内存泄漏、死锁和帧率抖动。

4.3.1 Stream生命周期

Stream生命周期状态机 未配置 已配置 活跃 已销毁 configure_streams() 首次请求 flush() / 关闭 异常路径:未配置直接销毁
生命周期陷阱:
  • 重复配置: 在Stream活跃时再次调用configure_streams(),HAL必须等待所有未完成的Buffer返回,否则可能导致Buffer泄漏。
  • Stream删除: 框架在关闭Camera时删除Stream,HAL必须释放所有关联的硬件资源(如ISP通道、DMA通道)。
  • Buffer残留: 如果HAL在Stream销毁后仍持有Buffer引用,Gralloc会报错,严重时导致系统服务重启。

4.3.2 Buffer生命周期

Buffer的生命周期比Stream更短,且高度依赖引用计数。每个Buffer在Stream的Buffer队列中经历以下状态:

状态 所有者 描述 触发动作
空闲 框架 Buffer在队列中等待被请求 Buffer分配完成 / 返回后释放
已请求 HAL(临时) 框架通过request将Buffer交给HAL process_capture_request()
已填充 HAL HAL完成硬件处理,等待返回 ISP中断 / 硬件完成
已返回 框架 HAL通过result回调归还Buffer process_capture_result()
已释放 Gralloc Buffer从Stream队列中移除,内存回收 Stream销毁 / 显式释放

4.3.3 Stream与Buffer的绑定关系

一个Stream在配置时确定其Buffer队列的容量(max_buffers),但Buffer本身是按需分配的。框架在首次请求时通过allocate_buffers()(或隐式分配)为Stream创建Buffer池。绑定关系如下:

  • 一对一: 每个Buffer在任意时刻只属于一个Stream。Buffer的stream_id字段标识其归属。
  • 多对一: 一个Stream可以拥有多个Buffer(队列深度),但所有Buffer共享相同的配置参数(分辨率、格式)。
  • 动态绑定: 框架可以在Stream活跃期间动态增加Buffer(通过signal_stream_flush()),但HAL必须支持这种动态调整。
最佳实践:
  • Buffer复用: 优先复用Buffer而非频繁分配/释放,减少Gralloc开销。
  • 队列深度: 根据ISP流水线深度设置max_buffers,通常为“流水线阶段数 + 2”。
  • 生命周期对齐: 确保Buffer的释放发生在Stream销毁之前,避免悬空指针。
  • 调试手段: 在HAL中记录每个Buffer的frame_number和stream_id,便于追踪生命周期异常。

4.4 典型场景:多Stream下的Buffer调度

在实际应用中(如同时预览+拍照+录像),多个Stream共享同一个Sensor输出。HAL需要将Sensor的原始数据分发给不同的Stream,每个Stream拥有独立的Buffer队列。

多Stream Buffer调度架构 Sensor / ISP Stream分发器 Stream 0: 预览 1920x1080 YUV Stream 1: 拍照 4096x3072 JPEG Stream 2: 录像 3840x2160 H.264 Buffer队列 (max=4) Buffer队列 (max=3) Buffer队列 (max=6)
多Stream调度策略:
  • 同步分发: 同一帧数据可以同时填充多个Stream的Buffer(如预览和录像共享YUV数据)。
  • 异步分发: 拍照Stream可能使用独立的Sensor输出(如全分辨率RAW),与预览流异步。
  • Buffer饥饿: 如果某个Stream的Buffer队列为空(所有Buffer都在HAL中),框架会阻塞对该Stream的请求,直到有Buffer返回。
  • 优先级: 预览流通常要求低延迟,因此HAL应优先返回预览Buffer,拍照流可以容忍稍长延迟。

4.5 总结:Stream与Buffer关系要点

维度 Stream Buffer
定义 图像数据的管道规格 承载像素数据的内存块
配置时机 configure_streams() 时确定 首次请求时分配,可动态调整
生命周期 从配置到销毁,贯穿Camera会话 从分配到释放,随帧循环流转
所有权 框架拥有Stream对象,HAL拥有硬件资源 框架拥有Buffer句柄,HAL临时借用
数量关系 一个Camera可以有多个Stream 一个Stream可以有多个Buffer(队列)
错误处理 配置失败则整个Camera无法启动 Buffer丢失只影响单帧,可重试

掌握Stream与Buffer的关系,是理解后续Buffer队列管理(第5章)和帧同步优化(第6章)的前提。在实际HAL开发中,配置参数的准确性和生命周期的一致性是避免帧率抖动、内存泄漏和系统崩溃的关键。