HAL层基础:Camera HAL架构解析

做图像质量调优,绕不开HAL层。说白了,HAL就是操作系统和硬件之间的翻译官。你上层App发一个「拍照」指令,经过Framework层层传递,最后到HAL这里,它得告诉硬件:「嘿,该干活了」。

我个人习惯把HAL层比作一个黑盒的接口层。你不需要知道硬件怎么实现的,但你必须知道它提供了什么能力。今天我们就从Android HAL3开始聊起。

Android HAL3架构

Android的Camera HAL经历了HAL1到HAL3的演进。HAL1太老了,现在主流都是HAL3。为什么?因为HAL3支持更精细的控制。

HAL3的核心设计理念是「管道模型」。你想想看,数据从传感器出来,经过ISP处理,最后送到应用层,这不就是一条流水线吗?

HAL3架构中,主要有这几个角色:

  • CameraDevice:代表一个物理摄像头设备
  • CameraSession:管理数据流的会话
  • CaptureRequest:每一次捕获的请求描述
  • CaptureResult:捕获完成后的结果反馈

我在项目中遇到过一个问题:某个平台HAL3实现不完整,导致预览和拍照的帧率不一致。查了半天,发现是CaptureRequest里的控制参数没传对。嗯,这里要注意,HAL3的接口定义很严格,少一个参数都可能出问题。

核心要点:HAL3的架构设计让应用层可以同时发送多个请求,并且每个请求可以独立配置参数。这就是为什么你可以一边预览一边拍照,还能同时做视频录制。

HAL层数据流

数据流是HAL层最直观的部分。我们分三种场景来看:Preview、Video、Snapshot。

Preview数据流

预览是最基础的需求。流程大概是这样的:

  1. App创建CameraCaptureSession
  2. App提交重复的CaptureRequest(设置TEMPLATE_PREVIEW)
  3. HAL层从Sensor获取帧数据
  4. 经过ISP处理后,送到Surface上显示

这里有个坑:预览的帧率一般要求30fps,但有些低端平台只能跑到24fps。我曾经调试过一个项目,预览画面明显卡顿,最后发现是Sensor的输出分辨率设置太高了。降低到1080p后,帧率就稳了。

Video数据流

视频录制和预览其实共享同一条数据流。区别在于:

  • 预览走的是显示Surface
  • 录制走的是编码器Surface

HAL3支持同时输出多个Stream。你可以把预览和录制配置成两个不同的Stream,一个给显示,一个给编码器。这样互不干扰。

经验之谈:视频录制时,我建议把预览的帧率和录制的帧率保持一致。否则会出现预览流畅但录出来的视频卡顿的情况。别问我怎么知道的,都是泪。

Snapshot数据流

拍照是最高质量的需求。Snapshot的流程和预览不同:

  • App提交单次CaptureRequest(设置TEMPLATE_STILL_CAPTURE)
  • HAL层使用最高质量参数处理
  • 输出全分辨率JPEG或YUV

为什么拍照要单独走一个请求?因为拍照需要更长的曝光时间、更复杂的后处理。你想想看,预览要30fps,每帧只有33ms的处理时间。但拍照可以花几百毫秒甚至几秒来优化画质。

我曾经调试过一个bug:拍照后照片偏暗。排查发现是HAL层在Snapshot模式下没有正确应用AE补偿。说白了,就是预览和拍照的曝光参数没同步。

Buffer管理机制

Buffer管理是HAL层的核心难点。数据在HAL层流转,本质上就是Buffer在流转。

Android使用Gralloc来管理图形Buffer。HAL3引入了BufferQueue机制,生产者(HAL)把Buffer填好数据,消费者(App/显示/编码器)消费Buffer。

Buffer管理的关键点:

概念 说明
Buffer分配 由Gralloc分配,支持多种格式(NV12、NV21、YV12等)
Buffer流转 通过dequeue_buffer和queue_buffer操作
Buffer复用 消费完成后,Buffer回到池中等待下次使用
内存优化 避免频繁分配释放,使用Buffer池

注意:Buffer泄漏是HAL层最常见的bug之一。如果Buffer没有正确归还到池中,很快就会出现「out of memory」错误。我见过一个项目,连续拍照10张后系统就崩了,最后发现是JPEG编码完成后没有release Buffer。

Buffer管理的代码示例(简化版):

// HAL层处理Buffer的典型流程
void processCaptureRequest(capture_request_t *request) {
    // 1. 从Buffer池中获取一个空Buffer
    buffer_handle_t buffer = gralloc_alloc(width, height, format);
    
    // 2. 填充图像数据
    fill_image_data(buffer, sensor_data);
    
    // 3. 将Buffer交给消费者
    buffer_queue.queue_buffer(buffer);
    
    // 4. 消费者处理完成后,Buffer自动回池
    // 注意:这里不能手动释放,否则会double free
}

Metadata传递机制

Metadata是HAL3的灵魂。它负责传递控制参数和状态信息。

Metadata的结构是key-value对。Android定义了一套标准的Metadata key,比如:

  • ANDROID_SENSOR_EXPOSURE_TIME:曝光时间
  • ANDROID_SENSOR_SENSITIVITY:ISO感光度
  • ANDROID_LENS_FOCUS_DISTANCE:对焦距离
  • ANDROID_CONTROL_AE_MODE:自动曝光模式

Metadata的传递流程:

  1. App在CaptureRequest中设置控制参数
  2. HAL层解析Request中的Metadata
  3. HAL层配置硬件参数
  4. 捕获完成后,HAL层填充Result Metadata
  5. App通过CaptureResult获取实际参数

这里有个容易忽略的点:Request中的参数是「期望值」,Result中的参数是「实际值」。比如你设置曝光时间为10ms,但硬件可能只能做到10.5ms。Result里会告诉你实际用了多少。

避坑指南:我曾经调试过一个3A(AE/AWB/AF)不收敛的问题。查了两天,最后发现是Metadata里的AE补偿值没有正确传递到HAL层。App设置了补偿值,但HAL层根本没读到。所以,调试时一定要打印出完整的Metadata内容,看看参数到底传没传过去。

Metadata的序列化格式通常是二进制的,但调试时可以转成可读的JSON格式。我习惯在HAL层加一个dump函数,把每次Request和Result的Metadata都打印出来。这样问题定位快很多。

知识体系总览

下面这张图展示了HAL层核心知识的关系:

Camera HAL3 核心知识体系 应用层 (App/Framework) HAL3 接口层 (Camera HAL3 API) 数据流管理 Preview/Video/Snapshot Buffer管理 Gralloc/BufferQueue Metadata传递 Request/Result 硬件层 (Sensor/ISP/GPU) 数据流从上到下传递,Metadata双向交互

这张图把HAL3的核心模块串起来了。从上到下是数据流的方向,Metadata则在各个模块之间双向传递。Buffer管理贯穿整个流程,是性能的关键。

好了,HAL层的基础就聊到这里。记住一句话:HAL3的核心是「请求-响应」模型,理解了这个,后面的调优就好办了。

可可精品资料库