4、CameraDevice 初始化:open() 流程、Session 创建、以及 Pipeline 的建立

好,咱们今天来啃一块硬骨头——CameraDevice 的初始化流程。说白了,就是上层 App 调用 open() 之后,底层到底发生了什么?Session 是怎么创建的?Pipeline 又是怎么搭起来的?

我记得刚接手 Camera HAL 时,被这一套流程绕得晕头转向。后来跟了几次 log,才慢慢理清脉络。今天我把这些经验掰开揉碎讲给你听。

4.1 open() 调用:从 Framework 到 HAL 的第一次握手

App 调用 CameraManager.openCamera(),这只是一个开始。真正的重头戏在 Framework 层。

流程大致是这样的:

  1. App 通过 Binder 调用到 CameraService
  2. CameraService 调用 CameraProviderManager,找到对应的 HAL 实现。
  3. 最终调用到 HAL 层的 camera_device_t::open()

嗯,这里要注意:HAL 层的 open() 不是简单的打开设备。它要做三件事:

  • 初始化硬件资源(比如 sensor、ISP)
  • 分配 camera3_device_t 结构体
  • 注册回调函数(process_capture_requestflush 等)

核心要点: open() 返回的 camera3_device_t 是后续所有操作的基石。如果这里失败了,后面什么都做不了。

我在项目中遇到过一个问题:某款 sensor 在 open() 时初始化时间过长,导致 App 超时崩溃。后来我们加了一个异步初始化策略,才解决这个问题。

4.2 Session 创建:谁在管理你的拍照请求?

Session 这个概念,说白了就是「一次完整的相机使用周期」。从打开相机到关闭相机,就是一个 Session。

但这里有个容易混淆的地方:Framework 层的 Session 和 HAL 层的 Session 不是一回事

Framework 层的 Session 由 CameraCaptureSession 管理。它负责:

  • 接收 App 的拍照请求
  • 把请求拆分成多个 capture request
  • 把结果返回给 App

HAL 层的 Session 则更底层。它对应的是 camera3_stream_configuration_t。这个结构体定义了:

  • 有多少个 stream(预览流、拍照流、视频流等)
  • 每个 stream 的格式、分辨率、使用场景
  • stream 之间的同步关系

避坑指南: 我曾经因为 stream 配置顺序不对,导致预览和拍照的 buffer 互相覆盖。后来发现 HAL 要求 stream 按优先级排列——预览流在前,拍照流在后。这个细节在文档里只提了一句,坑了不少人。

4.3 Pipeline 的建立:数据流动的骨架

Pipeline 是什么?你可以把它想象成一条流水线。sensor 采集到的原始数据,经过 ISP 处理、算法优化、格式转换,最终变成 App 能用的 YUV 或 JPEG。

建立 Pipeline 的关键步骤:

  1. 配置 stream:告诉 HAL 你需要哪些数据流。
  2. 分配 buffer:每个 stream 都需要对应的 buffer 来存放数据。
  3. 建立通路:把 sensor、ISP、encoder 等模块连接起来。
  4. 启动 pipeline:开始处理数据。

你想想看,如果 pipeline 没搭好,数据流就会卡住。轻则掉帧,重则死机。

我记得有一次调试 4K 视频录制,发现 pipeline 总是卡在 ISP 阶段。查了半天,原来是 buffer 队列深度不够。ISP 处理速度跟不上 sensor 的输出速度,导致 pipeline 阻塞。

注意: Pipeline 的建立顺序很重要。必须先配置 stream,再分配 buffer,最后启动 pipeline。顺序搞反了,HAL 会直接返回错误。

4.4 代码实战:从 open() 到 pipeline 建立

光说不练假把式。咱们来看一段简化版的代码,展示 open() 到 pipeline 建立的核心逻辑。

// HAL 层的 open() 实现
int camera_device_open(const struct hw_module_t* module,
                       const char* id,
                       struct hw_device_t** device) {
    camera3_device_t* camera_dev = malloc(sizeof(camera3_device_t));
    // 初始化硬件
    sensor_init();
    isp_init();
    // 注册回调
    camera_dev->ops = &camera_device_ops;
    *device = (struct hw_device_t*)camera_dev;
    return 0;
}

// Session 创建:配置 stream
int configure_streams(camera3_device_t* device,
                      camera3_stream_configuration_t* config) {
    // 解析 stream 配置
    for (int i = 0; i < config->num_streams; i++) {
        camera3_stream_t* stream = config->streams[i];
        // 为每个 stream 分配 buffer
        allocate_buffers(stream);
    }
    // 建立 pipeline
    build_pipeline(config);
    return 0;
}

// Pipeline 建立
void build_pipeline(camera3_stream_configuration_t* config) {
    // 连接 sensor -> ISP -> encoder
    connect_sensor_to_isp();
    connect_isp_to_encoder();
    // 启动 pipeline
    start_pipeline();
}

这段代码虽然简化了,但核心逻辑都在。你仔细看 configure_streams() 这个函数——它其实就是 Session 创建和 Pipeline 建立的交汇点。

4.5 常见问题与调试技巧

做 Camera HAL 开发,这几个问题你早晚会遇到:

问题 原因 解决方法
open() 超时 硬件初始化耗时过长 异步初始化,或优化硬件驱动
Session 创建失败 stream 配置不合法 检查 stream 格式、分辨率、数量
Pipeline 卡死 buffer 队列阻塞 增加 buffer 深度,或优化处理速度
数据流错乱 stream 优先级配置错误 按文档要求重新排列 stream 顺序

我的调试习惯: 遇到 pipeline 问题,先看 logcat 中 HAL 层的输出。重点关注 configure_streamsprocess_capture_request 的调用时序。如果这两个函数调用正常,问题大概率在 buffer 管理上。

嗯,今天就先讲到这里。CameraDevice 初始化这块内容,说白了就是「打开设备 -> 配置数据流 -> 建立处理流水线」这三步。但每一步都有很多细节,需要你在实际项目中慢慢体会。

下一章咱们聊聊 capture request 的处理流程,那才是真正考验 HAL 功底的地方。