4. Stream复用原理:BufferQueue与Surface组合、多个Stream共享同一个BufferQueue的机制

在Camera HAL3框架中,Stream复用是降低内存占用、减少CPU/GPU拷贝、提升整体吞吐量的核心技术。其核心思想是:多个Stream(如预览、录像、ZSL)在满足分辨率、格式、使用场景约束的前提下,共享同一个BufferQueue,从而避免为每个Stream独立分配和搬运Buffer。

本章深入剖析BufferQueue与Surface的组合关系,以及多个Stream共享同一个BufferQueue的底层机制、配置约束和性能收益。

4.1 BufferQueue与Surface:生产者-消费者模型

Android图形系统中,BufferQueue是核心的Buffer管理组件,Surface是BufferQueue的生产者端接口。在Camera HAL3中,每个Stream对应一个或多个Surface,这些Surface最终绑定到同一个BufferQueue上。

组件 角色 说明
BufferQueue Buffer池与调度器 管理Buffer的分配、排队、回收;支持多个生产者和消费者
Surface 生产者接口 Camera HAL通过Surface向BufferQueue提交填充好的Buffer
Consumer 消费者接口 如SurfaceFlinger、MediaCodec、GPU,从BufferQueue取出Buffer消费
BufferSlot Buffer槽位 BufferQueue内部维护的固定数量Buffer槽,每个槽可容纳一个GraphicBuffer
关键概念: 一个BufferQueue可以绑定多个Surface(生产者),但所有Surface共享同一组BufferSlot。这意味着Camera HAL通过不同Surface提交的Buffer,实际上来自同一个Buffer池。
性能提示: BufferQueue的深度(maxBufferCount)直接影响延迟和内存。Camera场景通常设置为4~8个Buffer,过少会导致生产者阻塞,过多则增加内存压力。

4.2 多个Stream共享同一个BufferQueue的机制

当多个Stream(如预览Stream和录像Stream)满足复用条件时,Camera Framework会在底层将它们映射到同一个BufferQueue上。其核心流程如下:

  1. Stream配置阶段: Camera HAL收到configure_streams调用,Framework根据Stream参数(分辨率、格式、usage flag)判断是否可复用。
  2. BufferQueue分配: 若判定可复用,Framework只为这组Stream分配一个BufferQueue,并创建多个Surface指向该BufferQueue。
  3. Buffer提交: HAL通过不同Surface提交Buffer时,BufferQueue内部记录每个Buffer对应的Surface ID。
  4. 消费者分发: 消费者(如预览SurfaceFlinger、录像MediaCodec)从BufferQueue取出Buffer时,根据Surface ID或Buffer的metadata决定是否消费。
多个Stream共享同一个BufferQueue的机制 Camera HAL (生产者) 通过多个Surface 提交Buffer BufferQueue BufferSlot[0..N] 共享Buffer池 maxBufferCount=4~8 Surface ID标记 消费者 SurfaceFlinger MediaCodec GPU Surface A/B/C Buffer分发 复用条件 1. 分辨率相同(宽×高一致) 2. 格式兼容(如均为NV12或均为YUV420) 3. usage flag无冲突(如CPU_READ与HAL_PRODUCER可共存) 性能收益 ✓ 内存节省:N个Stream共享Buffer池,而非N个独立池 ✓ 拷贝消除:HAL只需填充一次Buffer,多个消费者直接复用 ✓ 延迟降低:减少Buffer分配/释放/搬运的CPU开销

4.3 Stream复用配置约束与判定规则

并非所有Stream都能复用。Framework在configure_streams阶段执行严格的判定。以下表格总结了复用条件:

判定维度 复用条件 不可复用示例
分辨率 宽×高完全一致 预览1920×1080 vs 录像1280×720 → 不可复用
像素格式 格式兼容(如均为NV12或均为YUV420_888) NV12 vs JPEG → 不可复用
usage flag 无冲突(如CPU_READ与HAL_PRODUCER可共存) CPU_READ_OFTEN vs GPU_RENDER → 可能冲突
数据空间 相同的数据空间(如均为V0或均为V1) V0 vs V1 → 不可复用
Stream类型 均为输出Stream(INPUT类型不可与OUTPUT复用) OUTPUT vs INPUT → 不可复用
常见陷阱: 即使分辨率相同,若一个Stream要求CPU_READ_OFTEN(频繁CPU读取),另一个要求HAL_PRODUCER(仅HAL写入),虽然usage flag可共存,但CPU读取会触发Cache刷新,影响HAL写入性能。此时复用虽然技术上可行,但性能可能反而下降。

4.4 共享BufferQueue的Buffer生命周期

当多个Stream共享同一个BufferQueue时,Buffer的生命周期管理变得复杂。以下是一个典型的Buffer流转过程:

  1. dequeueBuffer: HAL从BufferQueue申请一个空闲Buffer,获得BufferSlot索引。
  2. queueBuffer: HAL填充数据后,将Buffer入队,并标记对应的Surface ID(如预览Surface)。
  3. acquireBuffer: 消费者(如SurfaceFlinger)根据Surface ID或Buffer的acquire fence,取出Buffer进行显示。
  4. releaseBuffer: 消费者消费完成后,将Buffer释放回BufferQueue,等待下一次dequeue。
共享BufferQueue的Buffer生命周期 1. dequeueBuffer HAL申请空闲Buffer 获得Slot索引 2. queueBuffer HAL填充数据 标记Surface ID 3. acquireBuffer 消费者取出Buffer 根据Surface ID消费 4. release Buffer回池 等待下次dequeue 循环复用 关键点:Buffer复用 vs 数据复用 Buffer复用:多个Stream共享同一个BufferSlot,内存只分配一次 数据复用:HAL只填充一次数据,多个消费者通过acquire fence同步读取

4.5 实际场景:预览+录像Stream复用

以最常见的“预览+录像”场景为例,分析Stream复用的配置和收益:

Stream 分辨率 格式 usage flag 是否复用
预览Stream 1920×1080 NV12 HAL_PRODUCER | CPU_READ 是(共享BufferQueue)
录像Stream 1920×1080 NV12 HAL_PRODUCER | VIDEO_ENCODER 是(共享BufferQueue)
性能对比:
  • 不复用: 预览和录像各分配4个Buffer,每个Buffer约6MB(1920×1080×3/2),总内存=2×4×6=48MB;HAL需要填充两次Buffer,CPU拷贝开销约2ms/帧。
  • 复用: 共享4个Buffer,总内存=4×6=24MB(节省50%);HAL只填充一次Buffer,CPU拷贝开销约1ms/帧(节省50%)。

4.6 性能优化建议

  • 优先复用同分辨率Stream: 在Stream配置阶段,尽量将预览、录像、ZSL等Stream的分辨率对齐,以触发BufferQueue共享。
  • 避免usage flag冲突: CPU_READ_OFTEN与GPU_RENDER同时存在时,建议拆分Stream或使用Buffer sharing扩展(如Gralloc4的Buffer Sharing API)。
  • 控制BufferQueue深度: 根据帧率和延迟要求,合理设置maxBufferCount。30fps场景建议4个Buffer,60fps场景建议6~8个Buffer。
  • 使用acquire fence同步: 多个消费者共享Buffer时,务必使用fence机制确保数据完整性,避免出现画面撕裂或数据损坏。
进阶技巧: 在HAL3中,可以通过camera3_stream_t的max_buffers字段向Framework建议BufferQueue深度。但Framework可能根据整体系统负载调整该值,HAL应做好动态适配。