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 设备的插拔事件会触发状态迁移:
- Uninitialized → 设备枚举后进入 Opened 状态。
- Opened → 配置 stream 后进入 Configured 状态。
- Configured → 开始处理 capture request 时进入 Streaming 状态。
- Streaming → 收到 flush 或错误时回到 Configured 或 Opened。
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 饥饿导致帧丢失。
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 流转步骤
- HAL 接收 capture request: Framework 下发
camera3_capture_request,包含目标 GraphicBuffer 句柄。 - HAL 从 V4L2 池获取帧: 调用
VIDIOC_DQBUF阻塞等待 UVC 驱动返回已填充的 buffer。 - 数据转换/拷贝: 根据格式(MJPEG 解码、颜色空间转换)处理数据,填充到 GraphicBuffer。
- 回调 Framework: 调用
process_capture_result将填充后的 buffer 返回给 Framework。 - V4L2 buffer 重新入队: 调用
VIDIOC_QBUF将空闲 buffer 归还驱动,供下一帧使用。
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() 中执行完整的清理流程。