第二章:Android Camera 系统架构:从 App 到 Kernel 的完整数据流

大家好,我是老张。这一章咱们聊聊 Camera 系统的整体架构。说实话,很多新人一上来就盯着 HAL 层代码看,结果越看越懵。为什么?因为你不知道数据从哪来、到哪去。今天我就把这条链路彻底讲清楚。

2.1 一条数据流的完整旅程

想象一下,你打开手机相机,按下快门。这一瞬间发生了什么?

数据从 App 出发,一路向下,穿过 Framework、HAL、Kernel,最后到达 Sensor。拍完的照片再原路返回。整个过程大概经过 5 个层级:

  1. App 层:调用 Camera2 API,比如 capture()
  2. Framework 层:CameraService 接管请求,管理权限和策略
  3. HAL 层:实现具体的硬件控制逻辑
  4. Kernel 层:V4L2 驱动,直接操作 Sensor 和 ISP
  5. 硬件层:Sensor 曝光、ISP 处理、输出图像

我个人习惯把这条链路叫做「五层递进模型」。每一层都有明确的职责,不能越界。

核心要点:App 只管发请求,HAL 只管实现功能,Kernel 只管操作硬件。谁也别替谁干活。

2.2 CameraService:真正的调度中心

很多人以为 CameraService 只是个传话筒。其实不是。它干的事情比你想象的多得多。

CameraService 运行在 cameraserver 进程中,是系统级服务。它的主要职责包括:

  • 管理 Camera 设备的打开和关闭
  • 处理来自多个 App 的并发请求
  • 执行权限检查(比如哪个 App 能用 Camera)
  • 协调 Flash、对焦、3A 等策略

我记得在某个项目里,遇到过两个 App 同时打开 Camera 导致崩溃的问题。查了半天,发现是 CameraService 的并发锁没处理好。嗯,这种坑踩过一次就记住了。

小技巧:调试 CameraService 时,可以看 dumpsys media.camera 的输出。里面会列出所有打开的 Camera 设备、当前请求状态、甚至每个 Session 的配置信息。

2.3 HAL 层:承上启下的关键

HAL 层是 Camera 系统的灵魂。它定义了 Framework 和硬件之间的接口。在 Android 8.0 之前,HAL 是直接以 .so 库的形式加载到 cameraserver 进程里的。这种方式有个问题——HAL 崩溃会导致整个系统服务挂掉。

从 Android 8.0 开始,Treble 架构引入了 HAL 的进程隔离。HAL 运行在独立的进程中,通过 HIDL(后来是 AIDL)与 Framework 通信。说白了,就是给 HAL 加了个「安全屋」。

HAL 层需要实现的核心接口包括:

  • open():打开 Camera 设备
  • configureStreams():配置数据流
  • processCaptureRequest():处理拍照请求
  • getCaptureResult():返回拍照结果

你想想看,这些接口其实就对应了 App 端的操作流程。App 调用 createCaptureSession(),最终会走到 HAL 的 configureStreams()。这就是我说的「五层递进」的体现。

2.4 V4L2:Kernel 层的通用接口

V4L2(Video for Linux 2)是 Linux 内核中处理视频设备的通用框架。在 Camera 领域,它负责管理 Sensor 的初始化、帧缓冲区的分配、以及图像数据的传输。

V4L2 的核心概念就三个:

概念 说明
Video Device 对应 /dev/video* 节点,每个 Camera 设备一个
Buffer 存放图像数据的内存块,通过 mmap 映射到用户空间
Queue 管理 Buffer 的进出,分为 INPUT_QUEUEOUTPUT_QUEUE

HAL 层通过 ioctl 系统调用与 V4L2 驱动交互。比如:

// 打开设备
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;
fmt.fmt.pix.width = 1920;
fmt.fmt.pix.height = 1080;
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_NV12;
ioctl(fd, VIDIOC_S_FMT, &fmt);

// 请求缓冲区
struct v4l2_requestbuffers req;
req.count = 4;
req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
req.memory = V4L2_MEMORY_MMAP;
ioctl(fd, VIDIOC_REQBUFS, &req);

这段代码看着简单,但实际项目中坑很多。我曾经在某个平台上,因为 VIDIOC_S_FMT 的调用顺序不对,导致 Sensor 输出格式和 ISP 期望格式不匹配,画面全是绿的。查了两天才找到原因。

注意:V4L2 的 Buffer 管理是典型的「生产者-消费者」模型。HAL 从驱动取走 Buffer,处理完再还回去。如果 Buffer 泄漏,系统会卡死。我曾经见过一个项目,因为忘记调用 VIDIOC_QBUF,导致 30 秒后 Camera 完全无响应。

2.5 Treble 架构对 HAL 的影响

Treble 架构是 Android 8.0 引入的重大变革。它的核心目标是把 Framework 和 Vendor 实现解耦。具体到 Camera HAL,影响主要体现在三个方面:

  1. 进程隔离:HAL 运行在 camerahalserver 进程中,不再和 cameraserver 共享地址空间
  2. 接口标准化:使用 HIDL(后来是 AIDL)定义接口,不再允许直接调用 .so 中的函数
  3. 版本管理:每个 HAL 接口都有明确的版本号,比如 android.hardware.camera.provider@2.4

说白了,Treble 让 Camera HAL 变成了一个「黑盒子」。Framework 只通过标准接口调用,不关心底层实现。这对我们做 HAL 开发的人来说,既是好事也是坏事。

好事是:接口更清晰了,不会因为 Framework 升级导致 HAL 需要大改。坏事是:调试变复杂了,因为多了一层进程间通信。我记得在某个 Treble 项目上,为了定位一个 HIDL 调用超时的问题,我不得不同时看 cameraserver 和 camerahalserver 的日志,来回切换,非常痛苦。

关键变化:Treble 之后,HAL 开发者需要同时关注两件事——一是实现标准的 HIDL/AIDL 接口,二是保证底层硬件驱动的稳定性。两者缺一不可。

2.6 数据流的完整时序

最后,我用一个典型的拍照流程来总结整个数据流:

  1. App 调用 CameraCaptureSession.capture()
  2. CameraService 收到请求,检查权限和状态
  3. CameraService 通过 HIDL 调用 HAL 的 processCaptureRequest()
  4. HAL 将请求转换为 V4L2 的 VIDIOC_S_CTRLVIDIOC_DQBUF 操作
  5. Kernel 驱动控制 Sensor 曝光,ISP 处理图像
  6. 图像数据通过 DMA 写入 Buffer
  7. HAL 从 V4L2 拿到 Buffer,返回给 CameraService
  8. CameraService 将结果回调给 App

整个过程,从 App 到 Kernel 再回到 App,通常需要 30-100 毫秒。如果哪个环节慢了,用户就会感觉到「快门延迟」。我优化过的最极端的一个项目,把整个链路从 120ms 压到了 45ms,靠的就是减少 Buffer 拷贝和优化 ISP 参数。

好了,这一章的内容就到这里。下一章我会深入讲解 HAL 层的具体实现,包括如何配置 Stream、如何处理 Request。到时候咱们再细聊。