第四章:Camera Provider 实现

好,咱们进入正题。

上一章我们把 HAL 的接口定义讲清楚了。这一章,我们要真正动手,把 Camera Provider 跑起来。

说白了,Camera Provider 就是 Android 系统和底层相机硬件之间的「中间人」。系统要打开相机,得先找到 Provider,然后通过它拿到具体的 Camera Device。这个流程,我当年第一次接触时也觉得挺绕,但拆开来看其实就三件事:

  • 创建一个 Provider 服务
  • 把 Camera 设备注册进去
  • 实现那几个核心接口:getCameraIdList、getCameraDeviceInterface

4.1 创建 CameraProvider 服务

先说说服务怎么创建。

Android 的 Camera HAL 从 Treble 架构开始,就变成了独立的服务进程。这意味着你的 Provider 得是一个 Binder 服务,注册到 ServiceManager 里。

我个人习惯用 HIDL 或 AIDL 来定义服务接口。不过现在主流已经是 AIDL 了,咱们就用 AIDL 来写。

核心思路:实现一个 ICameraProvider 接口,然后通过 ServiceManager 注册。

代码大概长这样:

// CameraProvider.cpp
class CameraProvider : public ICameraProvider {
public:
    CameraProvider() {
        // 初始化设备列表
        mCameraIds = {"0", "1"};
        // 这里可以加载实际的 HAL 设备
        ALOGI("CameraProvider created");
    }

    // 注册到 ServiceManager
    static void instantiate() {
        defaultServiceManager()->addService(
            String16("media.camera.provider"), 
            new CameraProvider()
        );
    }

private:
    std::vector<std::string> mCameraIds;
};

嗯,这里要注意一点:注册的服务名必须和系统期望的一致。我见过有人因为拼写错误,导致系统死活找不到 Provider,折腾了一整天。

小技巧:可以用 lshal 命令查看当前注册的 HAL 服务,确认你的 Provider 是否成功注册。

4.2 注册 Camera 设备

Provider 创建好了,接下来要把具体的 Camera 设备注册进去。

每个 Camera 设备对应一个 CameraDevice 对象。Provider 内部维护一个设备列表,系统通过 getCameraDeviceInterface 来获取。

我在项目中遇到过一种情况:同一个物理摄像头,可能对应多个逻辑摄像头(比如多摄融合)。这时候注册逻辑就要复杂一些。

// 注册设备
void CameraProvider::registerCameraDevice(
        const std::string& cameraId,
        std::shared_ptr<ICameraDevice> device) {
    
    std::lock_guard<std::mutex> lock(mLock);
    
    // 检查是否已存在
    if (mDevices.find(cameraId) != mDevices.end()) {
        ALOGW("Camera %s already registered, replacing", cameraId.c_str());
    }
    
    mDevices[cameraId] = device;
    ALOGI("Camera device %s registered", cameraId.c_str());
}

你想想看,为什么要有这个注册过程?

因为 Camera Provider 本身不直接操作硬件。它更像一个「调度中心」,真正的硬件操作在 CameraDevice 里。Provider 只负责把正确的 Device 交给系统。

注意:设备注册的顺序会影响 getCameraIdList 返回的列表顺序。有些上层应用依赖这个顺序,比如默认使用第一个摄像头。我建议你按照物理位置或功能优先级来排序。

4.3 实现 getCameraIdList

这个接口很简单,就是返回当前可用的摄像头 ID 列表。

但简单归简单,坑也不少。我曾经遇到过一个 case:某个摄像头在初始化时失败了,但 getCameraIdList 还是把它列出来了。结果上层一打开就崩溃。

Return<void> CameraProvider::getCameraIdList(
        getCameraIdList_cb _hidl_cb) {
    
    std::vector<std::string> availableIds;
    
    {
        std::lock_guard<std::mutex> lock(mLock);
        for (const auto& [id, device] : mDevices) {
            // 只返回初始化成功的设备
            if (device->isInitialized()) {
                availableIds.push_back(id);
            }
        }
    }
    
    // 回调返回结果
    _hidl_cb(Status::OK, availableIds);
    return Void();
}

这里有个细节:回调函数 _hidl_cb 是 HIDL 框架自动生成的。你只需要把状态码和列表传进去就行。

我个人建议在返回前做一次设备状态检查。别把「半死不活」的设备暴露给上层。

4.4 实现 getCameraDeviceInterface

这个接口是 Provider 的核心。系统通过它来获取具体的 Camera 设备。

说白了,就是根据 cameraId 找到对应的 CameraDevice 对象,然后返回它的接口。

Return<void> CameraProvider::getCameraDeviceInterface(
        const std::string& cameraId,
        getCameraDeviceInterface_cb _hidl_cb) {
    
    std::shared_ptr<ICameraDevice> device;
    
    {
        std::lock_guard<std::mutex> lock(mLock);
        auto it = mDevices.find(cameraId);
        if (it == mDevices.end()) {
            ALOGE("Camera device %s not found", cameraId.c_str());
            _hidl_cb(Status::ILLEGAL_ARGUMENT, nullptr);
            return Void();
        }
        device = it->second;
    }
    
    // 检查设备是否可用
    if (!device->isAvailable()) {
        ALOGW("Camera device %s is not available", cameraId.c_str());
        _hidl_cb(Status::INTERNAL_ERROR, nullptr);
        return Void();
    }
    
    _hidl_cb(Status::OK, device);
    return Void();
}

这里要注意线程安全。Provider 可能被多个客户端同时访问,所以加锁是必须的。

另外,我建议在返回设备之前,做一次「可用性检查」。有些设备可能正在被其他进程使用,或者处于错误状态。提前拒绝比让上层调用时崩溃要好得多。

总结一下:

  • Provider 是服务端,负责管理设备列表
  • getCameraIdList 返回可用设备 ID
  • getCameraDeviceInterface 返回具体设备接口
  • 别忘了加锁和状态检查

4.5 完整流程串讲

好,我们把整个流程串起来看看。

当 Android 系统启动时,Camera Service 会去 ServiceManager 查找 Camera Provider。找到之后,调用 getCameraIdList 获取摄像头列表。然后根据应用的需求,调用 getCameraDeviceInterface 打开某个摄像头。

整个过程就像这样:

  1. Provider 启动,注册到 ServiceManager
  2. Provider 内部初始化 CameraDevice 列表
  3. Camera Service 查询 Provider,获取 ID 列表
  4. Camera Service 根据 ID 获取 Device 接口
  5. 后续操作(打开、预览、拍照)都在 Device 层面完成

嗯,这个流程我调试过无数次。最常出问题的地方是第 1 步和第 4 步。要么 Provider 没注册成功,要么 Device 接口返回了空指针。

调试建议:在 Provider 的每个接口入口加日志,记录调用时间和参数。这样出问题时能快速定位是哪个环节出了问题。

好了,这一章的内容就到这里。下一章我们会深入 CameraDevice 的实现,看看真正的硬件操作是怎么做的。

记住,Provider 只是「门面」,真正的功夫在 Device 里。