第四章: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 打开某个摄像头。
整个过程就像这样:
- Provider 启动,注册到 ServiceManager
- Provider 内部初始化 CameraDevice 列表
- Camera Service 查询 Provider,获取 ID 列表
- Camera Service 根据 ID 获取 Device 接口
- 后续操作(打开、预览、拍照)都在 Device 层面完成
嗯,这个流程我调试过无数次。最常出问题的地方是第 1 步和第 4 步。要么 Provider 没注册成功,要么 Device 接口返回了空指针。
调试建议:在 Provider 的每个接口入口加日志,记录调用时间和参数。这样出问题时能快速定位是哪个环节出了问题。
好了,这一章的内容就到这里。下一章我们会深入 CameraDevice 的实现,看看真正的硬件操作是怎么做的。
记住,Provider 只是「门面」,真正的功夫在 Device 里。