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、高帧率等复杂场景
内存优化要点:HAL3的零拷贝机制虽然减少了内存分配次数,但Stream配置不当(如未正确设置usage flags或format)会导致BufferQueue无法复用buffer,从而引发隐性内存泄漏。排查时应重点关注Stream的创建与销毁是否成对出现。

2.2 Pipeline模型:从Request到Frame的内存流转

HAL3的核心是Pipeline模型,它将一次拍照或预览分解为多个可并行处理的阶段。每个阶段都涉及buffer的申请、填充、传递与释放。理解Pipeline的完整生命周期,是定位内存泄漏的关键。

HAL3 Pipeline 内存流转模型 1. Capture Request 应用下发请求 2. Stream配置 解析stream_t参数 3. Buffer分配 allocateBuffers() 4. ISP处理 硬件填充数据 5. Buffer传递 processCaptureResult() 6. Consumer消费 Surface/ImageReader 7. Buffer回收 releaseBuffer() Buffer复用循环 内存泄漏高发节点: • 阶段3→阶段4:Buffer分配后未正确映射到ISP硬件,导致buffer泄漏 • 阶段5→阶段6:Buffer传递后Consumer未及时释放引用,阻塞BufferQueue

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生命周期如下:

  1. Stream创建:应用通过configureStreams()传入stream_t数组,HAL根据配置分配初始buffer池。
  2. Buffer注册:HAL调用registerBuffers()将buffer句柄注册到硬件模块(如ISP、GPU)。
  3. Buffer流转:在Pipeline中,buffer通过processCaptureRequest()和processCaptureResult()在HAL与Framework间传递。
  4. Stream销毁:应用调用deleteStream(),HAL释放所有关联buffer并注销硬件注册。
常见内存泄漏场景:当应用异常退出或Stream配置变更时,如果HAL没有正确实现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。
内存优化切入点与数据结构关联 Camera HAL内存优化 引用计数管理 buffer_handle_t生命周期 usage flags优化 stream_t配置 Buffer池大小 max_buffers设置 核心原则:每个buffer_handle_t必须成对出现(register/unregister),每个stream_t必须正确配置usage flags, max_buffers应根据实际帧率和延迟要求动态调整,避免内存浪费或Pipeline饥饿。