4. 系统架构设计:Camera HAL 整体架构图、HAL 与 Framework 的交互流程、HAL 与 Kernel 的接口、模块划分(Stream、Device、Callback)
系统架构设计是 Camera HAL 工程落地的核心蓝图。本章从整体架构图出发,逐层拆解 HAL 与 Android Framework 的交互流程、与 Kernel 的接口定义,以及内部三大核心模块(Stream、Device、Callback)的职责划分。通过本章学习,你将掌握如何从零构建一个可维护、可扩展的 Camera HAL 系统。
4.1 Camera HAL 整体架构图
Camera HAL 位于 Android 相机系统的中间层,向上对接 Framework 的 CameraService,向下通过 V4L2 或自定义驱动接口与 Kernel 交互。整体架构采用分层解耦设计,确保各模块独立演进。
架构设计原则:
- 分层隔离:Framework 与 HAL 通过 HIDL / AIDL 接口通信,HAL 与 Kernel 通过标准字符设备或 V4L2 交互。
- 模块化:Stream、Device、Callback 三大模块各司其职,降低耦合度。
- 异步驱动:所有数据流和事件回调均采用异步机制,避免阻塞上层调用。
4.2 HAL 与 Framework 的交互流程
HAL 与 Framework 的交互遵循 Android 定义的 HIDL/AIDL 接口规范。整个流程从应用打开相机开始,到帧数据返回结束,是一个典型的异步请求-响应模型。
4.2.1 核心交互接口
| 接口名称 | 方向 | 功能描述 | 关键参数 |
|---|---|---|---|
| openCamera | Framework → HAL | 打开指定 Camera ID 的设备 | cameraId, callback |
| configureStreams | Framework → HAL | 配置输出流(分辨率、格式、用途) | stream_configuration |
| processCaptureRequest | Framework → HAL | 提交单次或批量捕获请求 | capture_request, buffer_handles |
| processCaptureResult | HAL → Framework | 返回捕获结果(Metadata + Buffer) | capture_result, buffer |
| notify | HAL → Framework | 上报错误、快门、3A 事件 | notify_msg |
| flush | Framework → HAL | 清空所有待处理请求 | 无 |
4.2.2 交互流程时序
工程经验:在 configureStreams 阶段,HAL 需要根据 Framework 传入的 stream 配置(分辨率、格式、usage 标志)分配内部 buffer 池。如果平台支持 JPEG 编码 offload,建议在 HAL 层直接集成硬件编码器,避免数据在 Framework 和 HAL 之间多次拷贝。
4.3 HAL 与 Kernel 的接口
HAL 与 Kernel 的接口是 Camera 数据流的底层通道。主流方案基于 V4L2(Video for Linux 2)框架,部分平台使用自定义字符设备或 ION 内存管理接口。
4.3.1 接口类型对比
| 接口类型 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|
| V4L2 | 标准 Linux Camera 驱动 | 生态成熟、工具链丰富(v4l2-ctl) | 对 ISP 私有特性支持有限 |
| 自定义字符设备 | 专用 ISP / 多传感器融合 | 灵活、可深度定制 | 驱动开发工作量大、兼容性差 |
| ION / DMA-BUF | 跨硬件模块共享内存 | 零拷贝、高效 | 依赖平台内存分配器 |
4.3.2 V4L2 关键 ioctl 调用
/* 打开设备 */
int fd = open("/dev/video0", O_RDWR);
/* 查询设备能力 */
struct v4l2_capability cap;
ioctl(fd, VIDIOC_QUERYCAP, &cap);
/* 设置格式 */
struct v4l2_format fmt;
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
fmt.fmt.pix_mp.width = 1920;
fmt.fmt.pix_mp.height = 1080;
fmt.fmt.pix_mp.pixelformat = V4L2_PIX_FMT_NV12;
fmt.fmt.pix_mp.num_planes = 2;
ioctl(fd, VIDIOC_S_FMT, &fmt);
/* 申请 buffer */
struct v4l2_requestbuffers req;
req.count = 4;
req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
req.memory = V4L2_MEMORY_DMABUF;
ioctl(fd, VIDIOC_REQBUFS, &req);
/* 入队 buffer */
struct v4l2_buffer buf;
struct v4l2_plane planes[2];
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
buf.memory = V4L2_MEMORY_DMABUF;
buf.index = 0;
buf.m.planes = planes;
buf.length = 2;
ioctl(fd, VIDIOC_QBUF, &buf);
/* 启动流 */
int type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
ioctl(fd, VIDIOC_STREAMON, &type);
/* 出队已填满的 buffer */
ioctl(fd, VIDIOC_DQBUF, &buf);
/* 停止流 */
ioctl(fd, VIDIOC_STREAMOFF, &type);
/* 关闭设备 */
close(fd);
避坑指南:
- V4L2 的 QBUF/DQBUF 必须在 STREAMON 之后才能正常工作,顺序错误会导致 ioctl 返回 EINVAL。
- 多平面格式(M Plane)需要正确设置 planes 数组长度和每个 plane 的 bytesperline/sizeimage。
- 使用 DMA-BUF 时,必须确保 HAL 层和驱动层使用相同的 ION heap 类型,否则会导致内存映射失败。
4.4 模块划分:Stream、Device、Callback
Camera HAL 内部按职责划分为三个核心模块:Device 模块负责设备生命周期管理,Stream 模块负责数据流配置和 buffer 管理,Callback 模块负责异步事件上报。三者协同工作,构成完整的 HAL 内部架构。
4.4.1 模块职责矩阵
| 模块 | 核心职责 | 关键数据结构 | 线程模型 |
|---|---|---|---|
| Device | Camera 设备打开/关闭、初始化、3A 控制、Metadata 管理 | CameraDevice, CameraMetadata, SensorInfo | 主线程 + 3A 线程 |
| Stream | Stream 配置、Buffer 申请/入队/出队、格式转换、帧数据处理 | CameraStream, StreamBuffer, BufferPool | Capture 线程 + 处理线程池 |
| Callback | 结果回调、事件通知、错误上报、状态同步 | CaptureResult, NotifyMsg, CallbackQueue | 独立 Callback 线程 |
4.4.2 模块间协作关系
4.4.3 模块接口定义示例
// Device 模块核心接口
class CameraDevice {
public:
virtual int openCamera(int cameraId, const CameraDeviceCallback& callback) = 0;
virtual int closeCamera() = 0;
virtual int initialize(const CameraMetadata& sessionParams) = 0;
virtual int set3AState(ControlMode mode, AeMode ae, AwbMode awb) = 0;
virtual CameraMetadata getDefaultRequestSettings(int templateType) = 0;
};
// Stream 模块核心接口
class CameraStream {
public:
virtual int configureStreams(const StreamConfiguration& config) = 0;
virtual int processCaptureRequest(const CaptureRequest& request) = 0;
virtual int flush() = 0;
virtual BufferPool* getBufferPool() = 0;
private:
virtual int enqueueBufferToKernel(int streamId, buffer_handle_t buffer) = 0;
virtual int dequeueBufferFromKernel(int streamId, buffer_handle_t* buffer) = 0;
};
// Callback 模块核心接口
class CameraCallback {
public:
virtual void processCaptureResult(const CaptureResult& result) = 0;
virtual void notify(const NotifyMsg& msg) = 0;
virtual void setCallbackThread(Thread* thread) = 0;
};
架构决策要点:
- Device 模块应保持无状态设计,所有 3A 参数通过 Metadata 传递,避免内部缓存导致状态不一致。
- Stream 模块的 Buffer 池建议使用环形队列(Ring Buffer)管理,减少内存分配和释放的开销。
- Callback 模块必须使用独立线程,且回调函数内部不能执行耗时操作,否则会阻塞 Framework 的消息循环。
- 三个模块之间通过接口依赖注入,方便单元测试和模块替换。