4. Stream复用策略设计:基于分辨率、格式、Usage与动态复用
在Camera HAL层开发中,Stream复用(Stream Reuse)是提升内存效率、降低延迟、减少带宽消耗的核心技术。复用策略的核心在于:在满足下游消费者(Preview、Video、Snapshot、Analysis)需求的前提下,尽可能共享Buffer或Stream管道,避免重复分配和拷贝。本章将系统阐述四种主流复用策略:基于分辨率、基于格式、基于Usage以及动态复用策略。
4.1 基于分辨率的复用策略
分辨率复用是最直观、最常用的策略。当多个Consumer请求的分辨率相同或存在整数倍缩放关系时,可以共享同一个Stream的输出Buffer,或通过Scale/Downscale共享数据。
4.1.1 策略模型
- 完全匹配复用: 多个Consumer请求完全相同的分辨率(如Preview和Video都请求1920x1080),直接共享同一个Stream ID和Buffer队列。
- 整数倍缩放复用: 高分辨率Stream(如4K)通过硬件Downscaler输出低分辨率(如1080p),供多个Consumer使用。
- 裁剪复用: 从大分辨率Stream中裁剪出感兴趣区域(ROI),供不同Consumer使用。
4.2 基于格式的复用策略
格式复用关注的是Pixel Format的兼容性。当不同Consumer请求的格式在内存布局上兼容时,可以共享Buffer,仅通过不同的解析方式(Interpretation)来满足各自需求。
4.2.1 格式兼容性矩阵
| Consumer A | Consumer B | 兼容性 | 复用方式 |
|---|---|---|---|
| NV12 | NV12 | 完全兼容 | 直接共享Buffer |
| NV12 | NV21 | 部分兼容 | UV分量交换(需CPU/GPU处理) |
| NV12 | YV12 | 不兼容 | 无法复用,需格式转换 |
| NV12 | Y8(Grayscale) | 部分兼容 | 仅使用Y平面,忽略UV |
| NV12 | P010(10-bit) | 不兼容 | 位深不同,需转换 |
4.2.2 典型场景:NV12与Y8复用
// 格式复用示例:NV12 Buffer同时用于Preview(NV12)和Face Detection(Y8)
struct BufferReuseConfig {
int stream_id;
uint32_t width;
uint32_t height;
uint32_t format; // HAL_PIXEL_FORMAT_YCbCr_420_SP
uint32_t usage; // GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_SW_READ_OFTEN
// 复用描述
struct ReusePlane {
int consumer_id;
uint32_t plane_index; // 0: Y plane, 1: UV plane
uint32_t offset;
uint32_t stride;
} planes[2];
};
// 初始化:NV12 Buffer,Y plane供Face Detection使用
struct BufferReuseConfig config = {
.stream_id = 1,
.width = 1920,
.height = 1080,
.format = HAL_PIXEL_FORMAT_YCbCr_420_SP,
.usage = GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_SW_READ_OFTEN,
.planes = {
{ .consumer_id = 0, .plane_index = 0, .offset = 0, .stride = 1920 }, // Y plane
{ .consumer_id = 1, .plane_index = 1, .offset = 1920*1080, .stride = 1920 } // UV plane
}
};
4.3 基于Usage的复用策略
Usage复用关注的是Buffer的访问模式(CPU读/写、GPU纹理、HW Video Encoder、Display等)。当多个Consumer的Usage位掩码兼容时,可以共享同一块物理内存,避免重复分配。
4.3.1 Usage兼容性分析
- HW_TEXTURE + HW_COMPOSER: 高度兼容,GPU和Display都可以从同一Buffer读取。
- HW_VIDEO_ENCODER + SW_READ_OFTEN: 部分兼容,Encoder需要连续物理内存,CPU读可能触发Cache同步。
- SW_WRITE_OFTEN + HW_TEXTURE: 不推荐,CPU写后GPU读需要显式Cache Flush,性能开销大。
- HW_CAMERA_WRITE + HW_VIDEO_ENCODER: 最佳复用组合,Camera直接写入Encoder可访问的内存区域,零拷贝。
4.4 动态复用策略
动态复用策略是前三种策略的融合与运行时优化。它根据当前系统负载、Consumer活跃状态、Buffer分配压力等因素,在运行时动态调整复用关系。
4.4.1 动态复用决策引擎
| 输入参数 | 来源 | 影响决策 |
|---|---|---|
| 活跃Consumer列表 | Camera Framework | 决定哪些Stream需要复用 |
| 当前Buffer压力 | Gralloc Allocator | 压力高时倾向复用,低时倾向独立分配 |
| 帧率需求 | Consumer配置 | 高帧率Consumer优先独立Stream |
| 平台带宽余量 | ISP/VPU状态 | 带宽不足时强制复用减少输出 |
| Cache同步开销 | 运行时统计 | 开销超过阈值时切换为独立Buffer |
4.4.2 动态复用状态机
// 动态复用状态机伪代码
enum ReuseState {
REUSE_INDEPENDENT, // 独立分配,无复用
REUSE_SHARED_BUFFER, // 共享Buffer
REUSE_SHARED_STREAM // 共享整个Stream管道
};
struct DynamicReuseManager {
// 当前复用状态
ReuseState current_state;
// 决策阈值
float bandwidth_threshold; // 带宽利用率阈值,超过则强制复用
float cache_sync_cost; // 当前Cache同步开销(us)
float cache_sync_limit; // Cache同步开销上限(us)
// 动态决策函数
ReuseState evaluate_reuse(const ConsumerGroup &group) {
// 1. 检查带宽压力
if (get_bandwidth_usage() > bandwidth_threshold) {
return REUSE_SHARED_STREAM; // 带宽不足,强制共享Stream
}
// 2. 检查Cache同步开销
if (cache_sync_cost > cache_sync_limit) {
return REUSE_INDEPENDENT; // Cache同步太贵,独立分配
}
// 3. 检查Consumer兼容性
if (group.has_compatible_resolution() &&
group.has_compatible_format() &&
group.has_compatible_usage()) {
return REUSE_SHARED_BUFFER; // 完全兼容,共享Buffer
}
// 4. 默认:独立分配
return REUSE_INDEPENDENT;
}
// 状态切换回调
void on_state_change(ReuseState old_state, ReuseState new_state) {
// 重新配置ISP输出通道
// 更新Gralloc Buffer分配策略
// 通知Consumer重新配置
}
};
- 在Camera Open阶段进行静态分析,建立初始复用关系。
- 在Stream配置阶段(ConfigureStreams)进行动态评估,调整复用策略。
- 在运行阶段(ProcessCaptureRequest)监控性能计数器,触发策略切换。
- 策略切换时需保证Buffer Pipeline的完整性,避免出现“黑帧”或“花屏”。
4.5 复用策略对比与选型指南
| 策略类型 | 复用粒度 | 实现复杂度 | 内存节省 | 带宽节省 | 适用场景 |
|---|---|---|---|---|---|
| 基于分辨率 | Stream/Buffer | 低 | 中(30%~50%) | 高(40%~60%) | Preview + Video + Snapshot |
| 基于格式 | Buffer Plane | 中 | 低(10%~20%) | 中(20%~30%) | Face Detection + Preview |
| 基于Usage | Buffer | 中 | 中(20%~40%) | 高(30%~50%) | Camera + Encoder + Display |
| 动态复用 | 全链路 | 高 | 高(40%~70%) | 高(50%~80%) | 多Camera、高帧率、资源受限平台 |
- 不要盲目复用:当Consumer对延迟敏感时(如Video Encoder),独立Stream可减少排队等待。
- 注意Buffer生命周期:复用Buffer时,必须确保所有Consumer都完成读取后才能归还Buffer池。
- 平台差异:不同SoC的ISP输出通道数、Downscaler能力、Gralloc实现差异很大,复用策略需针对性调优。
- 测试覆盖:复用策略必须在所有Camera使用场景(拍照、录像、预览、第三方App)下充分测试。
4.6 本章小结
Stream复用策略是Camera HAL层性能优化的核心手段。基于分辨率的复用最易实现且收益显著;基于格式的复用适用于特定场景(如Y plane共享);基于Usage的复用需要深入理解Gralloc和硬件访问模式;动态复用策略则是在前三种基础上的智能化升级,能够根据运行时状态自适应调整。在实际项目中,建议优先实现分辨率复用,然后根据性能瓶颈逐步引入格式和Usage复用,最后在资源紧张的高端平台上部署动态复用引擎。