2. Camera HAL架构概览:HAL3与HAL1的区别、Pipeline模型、关键数据结构
Camera HAL(硬件抽象层)是Android相机系统中连接Framework与底层硬件驱动的关键桥梁。理解HAL架构的演进、核心模型与数据结构,是进行内存管理优化的前提。本章将系统梳理HAL1与HAL3的核心差异、Pipeline处理模型,以及buffer_handle_t、stream_t等关键数据结构的内存布局与生命周期。
2.1 HAL1 vs HAL3:架构演进与内存管理差异
Android从HAL1过渡到HAL3,本质上是将相机控制权从应用层下放到底层硬件,同时将内存管理模型从“同步拷贝”升级为“异步复用”。下表从多个维度对比两者的核心差异:
| 对比维度 | HAL1(传统模型) | HAL3(灵活模型) |
|---|---|---|
| 控制模式 | 应用层通过CameraService直接下发参数,HAL被动执行 | 应用层下发Capture Request,HAL自主调度Pipeline处理 |
| 数据流模型 | 单路同步流,预览/拍照/录像通过不同接口独立处理 | 多路异步流,支持多Stream并发,通过Output/Input Stream复用 |
| 内存管理 | 每次帧处理都需分配/释放buffer,频繁内存拷贝 | 基于BufferQueue的零拷贝机制,buffer在Producer/Consumer间流转 |
| 帧率控制 | 由应用层定时触发,HAL被动响应 | HAL内部Pipeline自主调度,支持高帧率与低延迟 |
| 内存泄漏风险 | 高:buffer分配后若未正确释放,极易泄漏 | 中:依赖BufferQueue引用计数,但Stream配置错误仍会导致泄漏 |
| 适用场景 | 低端设备、功能简单的相机应用 | 中高端设备、多摄像头、HDR、高帧率等复杂场景 |
2.2 Pipeline模型:从Request到Frame的内存流转
HAL3的核心是Pipeline模型,它将一次拍照或预览分解为多个可并行处理的阶段。每个阶段都涉及buffer的申请、填充、传递与释放。理解Pipeline的完整生命周期,是定位内存泄漏的关键。
Pipeline模型的核心优势在于异步解耦:每个阶段都可以独立调度,buffer在阶段间通过引用计数传递,而非拷贝。这意味着内存管理的核心挑战从“如何分配”转变为“如何确保引用计数正确递减”。
2.3 关键数据结构:buffer_handle_t 与 stream_t
在HAL3中,buffer_handle_t和stream_t是内存管理的两个核心数据结构。前者代表物理buffer的句柄,后者描述buffer的配置与用途。它们的正确使用直接决定了内存是否泄漏。
2.3.1 buffer_handle_t:物理buffer的抽象句柄
buffer_handle_t是一个指向native_handle_t的指针,它封装了buffer的共享内存文件描述符(fd)和偏移量。在HAL层,所有buffer操作都通过这个句柄进行。
// native_handle_t 结构定义(简化)
typedef struct native_handle {
int version; // 版本号,固定为 sizeof(native_handle_t)
int numFds; // 文件描述符数量
int numInts; // 整型参数数量
int data[0]; // 变长数组,存储fd和整型值
} native_handle_t;
// buffer_handle_t 类型定义
typedef const native_handle_t* buffer_handle_t;
buffer_handle_t本身不拥有内存,它只是指向共享内存的指针。真正的内存生命周期由Gralloc模块管理。HAL层必须确保在buffer使用完毕后调用freeBuffer()或releaseBuffer(),否则会导致fd泄漏(文件描述符泄漏是Camera HAL中最常见的内存泄漏形式之一)。
2.3.2 stream_t:描述buffer的配置与用途
stream_t结构体定义了buffer的格式、尺寸、用途(usage flags)以及最大buffer数量。它是HAL3配置Pipeline的入口参数。
// stream_t 结构定义(关键字段)
typedef struct camera3_stream {
int stream_type; // CAMERA3_STREAM_OUTPUT / INPUT / BIDIRECTIONAL
uint32_t width; // 图像宽度(像素)
uint32_t height; // 图像高度(像素)
int format; // 像素格式(HAL_PIXEL_FORMAT_*)
uint32_t usage; // 用途标志(GRALLOC_USAGE_*)
uint32_t max_buffers; // 该Stream允许的最大buffer数量
void* priv; // HAL私有数据指针
android_dataspace_t data_space; // 色彩空间
int rotation; // 旋转角度
int reserved[6]; // 保留字段
} camera3_stream_t;
usage字段是内存优化的核心:它告诉Gralloc分配buffer时使用哪种内存类型(如CPU可访问、GPU可访问、硬件ISP专用等)。错误的usage flags会导致buffer分配失败或性能下降。
| usage标志 | 含义 | 内存类型 | 优化建议 |
|---|---|---|---|
GRALLOC_USAGE_SW_READ_OFTEN |
CPU频繁读取 | 系统内存(非连续) | 仅预览/回放场景使用,避免用于编码 |
GRALLOC_USAGE_HW_TEXTURE |
GPU纹理 | GPU内存 | 用于滤镜/特效场景,注意GPU内存有限 |
GRALLOC_USAGE_HW_CAMERA_WRITE |
ISP直接写入 | 连续物理内存(ION) | 预览/拍照首选,减少内存拷贝 |
GRALLOC_USAGE_HW_VIDEO_ENCODER |
视频编码器读取 | 连续物理内存(ION) | 录像场景必须设置,否则编码器无法访问 |
2.4 Stream配置与Buffer生命周期的关联
在HAL3中,Stream的配置直接决定了Buffer的分配策略和生命周期。一个典型的Stream生命周期如下:
- Stream创建:应用通过
configureStreams()传入stream_t数组,HAL根据配置分配初始buffer池。 - Buffer注册:HAL调用
registerBuffers()将buffer句柄注册到硬件模块(如ISP、GPU)。 - Buffer流转:在Pipeline中,buffer通过
processCaptureRequest()和processCaptureResult()在HAL与Framework间传递。 - Stream销毁:应用调用
deleteStream(),HAL释放所有关联buffer并注销硬件注册。
deleteStream()的回调,或者硬件模块的buffer注销操作失败,会导致buffer_handle_t对应的fd无法关闭,从而引发文件描述符泄漏。排查时应使用lsof或/proc/[pid]/fd检查fd数量是否持续增长。
2.5 从数据结构看内存优化切入点
基于上述数据结构分析,Camera HAL内存优化的三个核心切入点为:
- buffer_handle_t的引用计数管理:确保每个
registerBuffers()都有对应的unregisterBuffers(),避免fd泄漏。 - stream_t的usage flags优化:根据实际使用场景选择最合适的内存类型,避免不必要的内存拷贝或分配失败。
- max_buffers的合理设置:过大会浪费内存,过小会导致Pipeline阻塞。通常预览场景设为3~4,录像场景设为4~6。