2. Camera HAL 层架构:Android Camera HAL3 详解与 UVC 交互

Camera HAL (Hardware Abstraction Layer) 是 Android 相机框架中连接 Framework 与底层硬件驱动的关键中间层。在 UVC 设备接入场景下,HAL 层需要将标准的 UVC 流数据转换为 Android Camera HAL3 接口所定义的 pipeline 格式。本章深入剖析 HAL3 架构、与 UVC 驱动的交互接口以及核心的 Buffer 管理流程。

2.1 Android Camera HAL3 架构详解

HAL3 接口(Camera3Module)采用 pipeline 设计理念,将相机处理流程抽象为多个可配置的阶段。相比 HAL1 的固定流模式,HAL3 允许更精细的控制和更高的性能。

核心设计原则: 每一帧图像从 Sensor 到输出都经过一个由多个 camera3_stream 组成的 pipeline,HAL 负责管理这些 stream 的配置、请求和返回。

2.1.1 HAL3 关键组件

组件 角色 与 UVC 的关联
CameraModule HAL 入口,负责初始化、枚举设备 识别 UVC 设备 VID/PID,创建对应 CameraDevice
CameraDevice 单个相机设备实例,管理 pipeline 封装 UVC 设备句柄,维护流状态
camera3_stream 定义输出流的格式、分辨率、使用场景 映射 UVC 的 format descriptor 到 Android 格式
camera3_capture_request 包含输出 buffer 列表和 metadata 触发 UVC 驱动进行 isochronous 传输
camera3_callback_ops HAL 向 Framework 返回数据/事件的接口 将 UVC 帧数据填充到 buffer 后回调

2.1.2 HAL3 Pipeline 状态机

HAL3 设备在生命周期内经历以下状态转换,UVC 设备的插拔事件会触发状态迁移:

  1. Uninitialized → 设备枚举后进入 Opened 状态。
  2. Opened → 配置 stream 后进入 Configured 状态。
  3. Configured → 开始处理 capture request 时进入 Streaming 状态。
  4. Streaming → 收到 flush 或错误时回到 Configured 或 Opened。
HAL3 Pipeline 状态机(UVC 场景) Uninitialized Opened Configured Streaming 枚举 UVC 设备 配置 stream 提交 capture request flush / 错误 设备断开 注意 UVC 设备断开 需在 Streaming 状态处理异常

2.2 HAL 层与 UVC 驱动的交互接口

HAL 层通过 V4L2 (Video for Linux 2) 框架与 UVC 驱动通信。UVC 驱动注册为 V4L2 设备节点(/dev/videoX),HAL 层通过标准的 V4L2 ioctl 和 mmap 接口完成控制与数据交换。

2.2.1 关键交互接口清单

接口类型 V4L2 调用 HAL 层用途
设备打开/关闭 open() / close() 获取 UVC 设备句柄
查询能力 VIDIOC_QUERYCAP 确认设备支持 V4L2_CAP_VIDEO_CAPTURE
枚举格式 VIDIOC_ENUM_FMT 获取 UVC 支持的像素格式(YUYV、MJPEG 等)
设置格式 VIDIOC_S_FMT 配置分辨率、帧率、像素格式
请求 buffer VIDIOC_REQBUFS 分配 V4L2 buffer 池
入队 buffer VIDIOC_QBUF 将空闲 buffer 放入驱动队列
出队 buffer VIDIOC_DQBUF 获取已填充数据的 buffer
开始/停止流 VIDIOC_STREAMON / VIDIOC_STREAMOFF 控制 UVC isochronous 传输启停

2.2.2 交互流程示例代码

// HAL 层初始化 UVC 设备
int uvc_fd = open("/dev/video0", O_RDWR);
struct v4l2_capability cap;
ioctl(uvc_fd, VIDIOC_QUERYCAP, &cap);

// 设置格式:YUYV 640x480
struct v4l2_format fmt = {0};
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
fmt.fmt.pix.width = 640;
fmt.fmt.pix.height = 480;
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
fmt.fmt.pix.field = V4L2_FIELD_NONE;
ioctl(uvc_fd, VIDIOC_S_FMT, &fmt);

// 请求 4 个 buffer
struct v4l2_requestbuffers req = {0};
req.count = 4;
req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
req.memory = V4L2_MEMORY_MMAP;
ioctl(uvc_fd, VIDIOC_REQBUFS, &req);

// 映射 buffer 到用户空间
for (int i = 0; i < 4; i++) {
    struct v4l2_buffer buf = {0};
    buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    buf.memory = V4L2_MEMORY_MMAP;
    buf.index = i;
    ioctl(uvc_fd, VIDIOC_QUERYBUF, &buf);
    buffers[i].start = mmap(NULL, buf.length,
                            PROT_READ | PROT_WRITE,
                            MAP_SHARED, uvc_fd, buf.m.offset);
    buffers[i].length = buf.length;
}

// 入队所有 buffer
for (int i = 0; i < 4; i++) {
    struct v4l2_buffer buf = {0};
    buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    buf.memory = V4L2_MEMORY_MMAP;
    buf.index = i;
    ioctl(uvc_fd, VIDIOC_QBUF, &buf);
}

// 开始流
int type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
ioctl(uvc_fd, VIDIOC_STREAMON, &type);
性能提示: UVC 驱动默认使用 isochronous 传输,HAL 层应确保 VIDIOC_REQBUFS 的 count 不小于 4,以避免驱动侧 buffer 饥饿导致帧丢失。
HAL 层与 UVC 驱动交互架构 Android Framework CameraService Camera HAL3 camera3_device_t UVC 驱动 uvcvideo.ko USB 控制器 xHCI / EHCI HAL3 接口 V4L2 ioctl URB 传输 关键数据流 HAL 通过 V4L2 获取 UVC 帧数据后, 填充到 Android GraphicBuffer 中

2.3 Buffer 管理流程

Buffer 管理是 HAL 层性能优化的核心。在 UVC 场景下,HAL 需要协调 V4L2 buffer 池 和 Android GraphicBuffer 池 之间的数据流转,避免不必要的内存拷贝。

2.3.1 双 Buffer 池模型

Buffer 池 所有者 分配方式 典型数量
V4L2 Buffer 池 UVC 驱动 mmap 映射到 HAL 用户空间 4 ~ 8
GraphicBuffer 池 Android BufferQueue Gralloc 分配,可跨进程共享 3 ~ 6

2.3.2 零拷贝策略

为了降低延迟和 CPU 负载,HAL 层应优先采用 零拷贝 方式:

  • 直接映射: 如果 UVC 驱动支持 V4L2_MEMORY_DMABUF,HAL 可以将 GraphicBuffer 的 dma-buf fd 传递给驱动,让 UVC 直接写入目标 buffer。
  • GPU 共享: 使用 EGL_EXT_image_dma_buf_import 扩展,将 UVC 帧数据直接导入 GPU 纹理,避免 CPU 拷贝。
  • 回退方案: 当硬件不支持时,使用 memcpy 从 V4L2 buffer 拷贝到 GraphicBuffer,但需注意对齐和缓存一致性。

2.3.3 完整 Buffer 流转步骤

  1. HAL 接收 capture request: Framework 下发 camera3_capture_request,包含目标 GraphicBuffer 句柄。
  2. HAL 从 V4L2 池获取帧: 调用 VIDIOC_DQBUF 阻塞等待 UVC 驱动返回已填充的 buffer。
  3. 数据转换/拷贝: 根据格式(MJPEG 解码、颜色空间转换)处理数据,填充到 GraphicBuffer。
  4. 回调 Framework: 调用 process_capture_result 将填充后的 buffer 返回给 Framework。
  5. V4L2 buffer 重新入队: 调用 VIDIOC_QBUF 将空闲 buffer 归还驱动,供下一帧使用。
Buffer 管理流程(UVC 场景) ① 接收 Request Framework 下发 ② DQBUF 从 V4L2 池取帧 ③ 转换/拷贝 MJPEG 解码等 ④ 回调结果 process_capture_result V4L2 Buffer 池 mmap 映射 4 ~ 8 个 buffer 循环队列 GraphicBuffer 池 Gralloc 分配 3 ~ 6 个 buffer BufferQueue 获取 GraphicBuffer DQBUF 取帧 填充数据 返回 GraphicBuffer QBUF 重新入队 零拷贝优化点 使用 DMABUF 直接 将 UVC 数据写入 GraphicBuffer 避免 memcpy

2.3.4 性能关键指标

指标 说明 优化目标
Buffer 周转时间 从 DQBUF 到 QBUF 的时间 < 5 ms (1080p@30fps)
拷贝延迟 V4L2 buffer 到 GraphicBuffer 的拷贝耗时 零拷贝 < 0.5 ms
Buffer 饥饿率 V4L2 池中无可用 buffer 的概率 < 0.1%
帧丢弃率 因 buffer 不足导致的帧丢失比例 < 0.01%
常见陷阱: 在 UVC 设备热插拔时,HAL 层必须及时释放 V4L2 buffer 的 mmap 映射并关闭设备 fd,否则会导致内核内存泄漏。建议在 camera3_device_t::close() 中执行完整的清理流程。