1. Camera HAL架构概览:Android Camera整体架构、HAL3 vs HAL1区别、HAL接口定义

大家好,我是你们的Camera驱动讲师。今天咱们聊聊Camera HAL架构。说实话,这个主题我讲了不下几十遍了,但每次都有新感悟。Android Camera系统,说白了就是一条从摄像头硬件到上层应用的完整链路。而HAL层,就是这条链路上最关键的“翻译官”。

1.1 Android Camera整体架构

先看整体架构。Android Camera系统分四层:

  • 应用层:你手机里的相机App,调用Camera API
  • Framework层:Android系统提供的CameraService,管理摄像头资源
  • HAL层:硬件抽象层,连接Framework和硬件驱动
  • Kernel层:V4L2驱动、Sensor驱动,真正操作硬件

我个人习惯把HAL层比作“中间人”。上层App说“我要拍照”,Framework翻译成HAL接口调用,HAL再告诉驱动“该干活了”。反过来,驱动采集到图像数据,通过HAL回调一层层往上送。

关键点:HAL层是Android Camera性能瓶颈的重灾区。我见过太多项目,上层调优做得飞起,结果HAL回调卡成PPT。嗯,这里要注意,HAL层的设计直接决定了相机启动速度、拍照延迟、预览流畅度。

1.2 HAL3 vs HAL1:到底差在哪?

很多刚入行的朋友问我:“HAL1和HAL3到底选哪个?”我的回答很直接:能用HAL3就别用HAL1。为什么?

对比项 HAL1(传统模式) HAL3(Camera3模式)
设计理念 固定流水线,操作简单 灵活管线,可定制
数据流 单路输出,预览/拍照互斥 多路输出,可同时预览+拍照+录像
控制粒度 粗粒度,只能开关功能 细粒度,可控制每一帧的参数
性能 延迟高,吞吐量低 延迟低,吞吐量高
兼容性 老设备支持好 新设备标配

我在项目中遇到过一件事:某款手机用HAL1做双摄虚化,结果预览和拍照切换时总有黑屏。后来改成HAL3,用多路输出同时处理预览帧和拍照帧,问题迎刃而解。说白了,HAL3就是为现代相机需求设计的——你要同时做人脸检测、美颜、HDR、录像,HAL1那套单线程流水线根本扛不住。

避坑指南:我曾经在移植HAL3时踩过一个坑——metadata配置不全。HAL3要求每个请求都必须携带完整的metadata,少一个tag就可能crash。所以,如果你从HAL1迁移到HAL3,记得把metadata的“户口本”查清楚。

1.3 HAL接口定义:到底长什么样?

HAL接口定义在hardware/libhardware/include/hardware/camera.hcamera3.h里。咱们直接看代码:

// HAL1的接口定义(简化版)
typedef struct camera_device {
    int (*open)(const struct hw_module_t* module, const char* id,
                struct hw_device_t** device);
    int (*close)(struct hw_device_t* device);
    int (*set_preview_window)(struct camera_device* dev,
                              struct preview_stream_ops* window);
    int (*start_preview)(struct camera_device* dev);
    int (*stop_preview)(struct camera_device* dev);
    int (*take_picture)(struct camera_device* dev);
    // ... 其他接口
} camera_device_t;
// HAL3的接口定义(核心部分)
typedef struct camera3_device {
    int (*initialize)(const struct camera3_device* dev,
                      const camera3_callback_ops_t* callback_ops);
    int (*configure_streams)(const struct camera3_device* dev,
                             camera3_stream_configuration_t* config);
    int (*process_capture_request)(const struct camera3_device* dev,
                                   camera3_capture_request_t* request);
    void (*dump)(const struct camera3_device* dev, int fd);
    // ... 其他接口
} camera3_device_t;

看到区别了吗?HAL1的接口是“功能导向”的——你要预览就调start_preview,要拍照就调take_picture。而HAL3是“请求导向”的——你只需要提交一个capture_request,里面包含你要的帧类型、参数、输出buffer,剩下的HAL自己调度。

你想想看,这种设计带来的好处是什么?灵活性。在HAL3里,你可以同时提交多个请求,每个请求可以指定不同的输出流。比如:请求A输出预览帧到SurfaceView,请求B输出全分辨率帧到ImageReader,请求C输出YUV帧给算法模块。这在HAL1里几乎不可能实现。

注意:HAL3的回调机制是异步的。你提交请求后,HAL通过process_capture_result回调返回结果。这个回调可能在任何线程触发,所以你的代码必须线程安全。我曾经见过一个项目,因为回调里直接操作UI线程,导致死锁——嗯,血的教训。

1.4 回调机制:HAL的灵魂

说到回调,这是HAL3最核心的设计。回调函数定义在camera3_callback_ops_t结构体中:

typedef struct camera3_callback_ops {
    void (*process_capture_result)(const struct camera3_callback_ops* ops,
                                   const camera3_capture_result_t* result);
    void (*notify)(const struct camera3_callback_ops* ops,
                   const camera3_notify_msg_t* msg);
} camera3_callback_ops_t;

process_capture_result负责返回图像数据和metadata,notify负责返回事件通知(比如快门事件、错误事件)。这两个回调是HAL和Framework之间的“高速公路”,所有数据都通过它们传输。

我个人习惯在实现HAL时,把回调函数设计成“轻量级”的——回调里只做数据拷贝和状态标记,不做耗时操作。为什么?因为回调是在HAL的线程里执行的,如果你在回调里做图像处理,会阻塞后续的请求处理,导致帧率下降。

总结一下:HAL3的回调机制,说白了就是“你提交请求,我异步返回结果”。这种设计让Camera系统可以同时处理多个请求,实现高吞吐量。但代价是代码复杂度上升,你需要处理好同步、线程安全、内存管理等问题。

好了,这一章就到这里。下一章咱们深入HAL3的初始化流程,看看initialize函数里到底发生了什么。到时候我会分享一个我在调试某款ISP芯片时遇到的“初始化顺序”问题——保证让你有收获。