多平台与多厂商调试:Camera HAL兼容性工程案例 - 第2节
2.1 HAL1 与 HAL3 架构对比
Camera HAL 架构是 Android 相机子系统的核心抽象层,向上对接 Framework,向下控制 Sensor、ISP 等硬件。从 Android 4.0 到 8.0 及以后,HAL 接口经历了从 HAL1 到 HAL3 的重大演进。理解两者的本质区别,是进行多平台兼容性调试的基础。
核心观点:HAL1 是“黑盒”模型,Framework 只能发起简单的预览/拍照请求;HAL3 是“白盒”Pipeline 模型,Framework 可以精细控制每一帧的曝光、增益、输出流等参数。
2.1.1 架构差异总览
| 对比维度 | HAL1 | HAL3 |
|---|---|---|
| 引入版本 | Android 4.0 (API 9) | Android 5.0 (API 21) |
| 控制模型 | 同步/阻塞式调用 | 异步请求-响应 Pipeline |
| 输出流 | 单路输出(预览或拍照) | 多路并发输出(预览+拍照+视频+分析) |
| 元数据 | 无标准元数据通道 | 基于 CameraMetadata 的完整 3A 控制 |
| Buffer 管理 | 由 HAL 内部管理 | Framework 通过 Stream 和 BufferQueue 管理 |
| 厂商适配难度 | 较低,适合功能机 | 较高,需要实现完整 Pipeline |
工程提示:在调试老平台(如 MT6580、展讯 SC9832)时,经常遇到 HAL1 残留代码。如果 Framework 层强制启用 HAL3,会导致
open() 失败或预览黑屏。建议通过 system_property 或 camera.hal1.packagelist 做降级兼容。
2.1.2 调用流程对比
下图展示了 HAL1 与 HAL3 在典型预览场景下的调用序列差异。HAL1 的 preview() 是阻塞循环,而 HAL3 通过 processCaptureRequest() 异步提交请求。
兼容性陷阱:部分厂商(如某些 Rockchip 平台)的 HAL1 实现中,
preview() 内部会死等 VSYNC,如果 Sensor 驱动异常,整个 Camera 进程会卡死。迁移到 HAL3 后,这种阻塞被异步请求替代,但需要厂商实现正确的 flush() 逻辑。
2.2 Pipeline 模型详解
HAL3 的核心是 Pipeline 模型。每一帧从 Sensor 曝光到最终输出,经过 ISP、缩放、格式转换等多个 Stage。Framework 通过 CaptureRequest 描述“想要什么”,HAL 通过 CaptureResult 返回“实际发生了什么”。
2.2.1 逻辑 Pipeline 结构
一个典型的 HAL3 Pipeline 包含以下 Stage,每个 Stage 都可以独立配置参数:
- Sensor Stage:控制曝光时间、增益、帧率。
- ISP Stage:处理黑电平校正、去马赛克、降噪、色调映射。
- Scaler Stage:将原始分辨率缩放到目标输出尺寸。
- Format Stage:将 Bayer/Raw 数据转换为 YUV、NV12、JPEG 等格式。
- Output Stage:将 Buffer 通过
Stream返回给 Framework。
调试技巧:当遇到“预览帧率不足”问题时,不要只怀疑 Sensor。使用
adb shell dumpsys media.camera 查看每个 Stage 的耗时。常见瓶颈在 ISP 降噪强度过高或 Scaler 输出分辨率过大。
2.3 Stream 配置与 Camera Device Session
Stream 是 HAL3 中连接 Framework 和 HAL 的数据通道。一个 CameraDevice 可以同时创建多个 Stream,分别用于预览、拍照、视频录制、YUV 分析等。Stream 的配置必须在 CameraCaptureSession 创建时一次性确定。
2.3.1 Stream 配置矩阵
每个 Stream 需要指定 Format、Resolution、Usage 和 Consumer。以下是一个典型的多流配置示例:
| Stream ID | Format | Resolution | Usage | Consumer |
|---|---|---|---|---|
| 0 | NV12 (YUV420) | 1920x1080 | 预览 | SurfaceView |
| 1 | JPEG | 4000x3000 | 拍照 | ImageReader |
| 2 | NV12 (YUV420) | 640x480 | 视频编码 | MediaCodec |
| 3 | YV12 | 320x240 | 人脸检测 | ImageReader |
兼容性陷阱:某些低端 ISP(如 OV8856 搭配的 GC ISP)不支持同时输出 4000x3000 JPEG 和 1920x1080 NV12。此时 HAL 必须返回
ERROR_STREAM_CONFIGURE_FAIL,Framework 需要降级分辨率或关闭部分 Stream。
2.3.2 Session 生命周期
CameraCaptureSession 是 Stream 配置的运行时容器。其生命周期包含以下关键阶段:
- 创建(createCaptureSession):Framework 传入 Stream 列表,HAL 验证硬件能力并分配内部资源。
- 配置(configureStreams):HAL 内部将 Stream 映射到物理 Pipeline Stage,设置 ISP 寄存器。
- 请求循环(setRepeatingRequest / capture):Framework 持续提交请求,HAL 按序处理并回调结果。
- 切换(switchToOffline):部分平台支持将 Session 切换到离线模式,用于高速连拍。
- 关闭(close):释放所有 Stream Buffer,停止 Sensor 时钟。
2.3.3 多流并发调试要点
在多平台调试中,Stream 配置失败是最常见的兼容性问题之一。以下是几个关键检查点:
- 分辨率对齐:检查 Sensor 输出分辨率是否支持所有 Stream 的宽高比。例如,Sensor 原生 4:3,但 Stream 要求 16:9,HAL 需要做裁剪或黑边填充。
- 格式转换链:确认 ISP 是否支持从 Bayer 到 NV12 和 JPEG 的同时转换。部分硬件只有一条 ISP 通道,无法并行处理两种格式。
- Buffer 数量:每个 Stream 需要至少 3~4 个 Buffer 才能维持流畅的 Pipeline。如果 HAL 分配不足,会导致
dequeueBuffer超时。 - Consumer 阻塞:如果 SurfaceView 或 ImageReader 消费速度慢,会反压到 HAL Pipeline,造成帧率下降。使用
adb shell dumpsys gfxinfo查看 Buffer 排队情况。
实战案例:在某展锐 T610 平台上,同时开启预览 (1920x1080 NV12) 和拍照 (4160x3120 JPEG) 时,JPEG 编码器占用了 ISP 带宽,导致预览帧率从 30fps 掉到 12fps。解决方案:在 HAL 中为预览 Stream 开启独立的 ISP 降噪通道,并将 JPEG 编码卸载到硬件协处理器。