17、Camera子系统:Camera HAL3、CameraService、Preview与Capture流程
Camera 子系统,说复杂也复杂,说简单也简单。你想想看,从用户按下快门到最终拿到一张照片,中间经过了多少层?我当年刚接触 Android Camera 时,也被这层层叠叠的架构搞得头晕。但摸清楚 HAL3、CameraService 和 Preview/Capture 流程后,你会发现——嗯,其实就那么回事。
17.1 Camera 整体架构概览
先看一张图,把 Camera 子系统的骨架搭起来。我个人习惯把 Camera 架构分成三层:
- 应用层:你写的 Camera App,调用 Camera2 API。
- 框架层:CameraService 是核心,管理所有 Camera 设备。
- 硬件抽象层:Camera HAL3 接口,直接跟硬件打交道。
这张图我建议你记在脑子里。每次遇到 Camera 问题,先定位是哪个层的问题,能省不少时间。
17.2 Camera HAL3 接口详解
HAL3 是 Android 从 5.0 开始引入的,取代了老旧的 HAL1。为什么?因为 HAL1 太死板了,只能做简单的 Preview 和 Capture,没法满足现代拍照需求。
HAL3 的核心思想是 Request/Result 模型。说白了就是:
- 上层下发一个拍照请求(Request)
- HAL 处理完后返回结果(Result)
- 每个 Request 可以指定不同的参数:曝光、对焦、白平衡等等
17.2.1 关键接口方法
| 方法名 | 作用 | 我踩过的坑 |
|---|---|---|
initialize() |
初始化 HAL,传入回调接口 | 回调没注册好,后面所有流程都卡住 |
configureStreams() |
配置数据流(Preview、Capture、ZSL等) | 流配置顺序错了,HAL 直接 crash |
processCaptureRequest() |
下发拍照请求 | Request 的 buffer 没填对,黑屏 |
flush() |
清空所有待处理的 Request | 多线程调用 flush 容易死锁 |
processCaptureRequest() 是异步的。你下发 Request 后,HAL 通过回调返回 Result。千万别同步等待,否则性能直接崩掉。
17.3 CameraService 的角色
CameraService 是系统服务,运行在 cameraserver 进程中。它负责:
- 设备管理:打开/关闭 Camera 设备
- 权限检查:没有权限?直接拒绝
- 流配置协调:多个 App 同时用 Camera?Service 来仲裁
- Binder 通信:把 App 的请求转给 HAL
我记得有一次,客户反馈说打开 Camera 特别慢。查了半天,发现是 CameraService 在初始化时做了太多同步操作。后来改成懒加载,速度提升了一倍。
17.3.1 服务启动流程
// 伪代码,展示 CameraService 启动核心逻辑
void CameraService::onFirstRef() {
// 1. 枚举所有 Camera HAL
mModule = hw_get_module(CAMERA_HARDWARE_MODULE_ID, ...);
// 2. 获取 HAL 设备数量
int numCameras = mModule->get_number_of_cameras();
// 3. 为每个 Camera 创建设备实例
for (int i = 0; i < numCameras; i++) {
sp<CameraDevice> device = new CameraDevice(i);
mDevices.push_back(device);
}
// 4. 注册服务到 ServiceManager
publish();
}
adb shell dumpsys media.camera 查看当前所有 Camera 设备的状态。这个命令我几乎天天用。
17.4 Preview 流程深度剖析
Preview 流程,说白了就是让用户看到取景画面。流程是这样的:
- App 调用
createCaptureSession()创建会话 - CameraService 调用 HAL 的
configureStreams()配置 Preview 流 - App 下发重复的 Preview Request
- HAL 不断返回 Preview 帧数据
- 数据通过 Surface 送到屏幕上
你想想看,Preview 要求的是低延迟。如果一帧延迟超过 33ms(30fps),画面就会卡顿。我曾经在一个项目里,Preview 延迟到了 50ms,用户说「这相机怎么像幻灯片一样」。
17.4.1 Preview 的关键参数
| 参数 | 推荐值 | 说明 |
|---|---|---|
| 分辨率 | 1920x1080 | 太高了带宽不够,太低了画质差 |
| 格式 | YUV_420_888 | 兼容性好,处理方便 |
| 帧率 | 30fps | 够用,功耗也平衡 |
| Buffer 数量 | 3~4 个 | 太少容易丢帧,太多延迟高 |
17.5 Capture 流程深度剖析
Capture 流程比 Preview 复杂得多。因为拍照需要更高的画质,还要处理 3A(自动对焦、自动曝光、自动白平衡)。
核心流程:
- App 下发单次 Capture Request
- HAL 执行 3A 算法,调整参数
- Sensor 曝光,ISP 处理 RAW 数据
- 输出最终图像(JPEG 或 YUV)
- 通过回调返回给 App
17.5.1 Capture Request 示例
// 构建一个拍照请求
CameraCaptureRequest request;
request.stream = &captureStream;
request.settings = {
.aeMode = AE_MODE_ON,
.aeTargetFpsRange = {15, 30},
.awbMode = AWB_MODE_AUTO,
.afMode = AF_MODE_AUTO,
.sensorSensitivity = 400, // ISO
.sensorFrameDuration = 33333333 // 30fps 对应的纳秒数
};
request.buffers = {
{.stream = &jpegStream, .buffer = jpegBuffer}
};
// 下发请求
status_t ret = halDevice->processCaptureRequest(&request, 1, nullptr);
sensorFrameDuration 参数很关键。设得太短,曝光不足;设得太长,拍照延迟高。我一般建议根据场景动态调整——白天用短曝光,晚上用长曝光。
17.6 Preview 与 Capture 的协同
实际场景中,Preview 和 Capture 是同时进行的。用户看着 Preview 画面,然后按下快门拍照。这时候需要处理两个流:
- Preview 流:持续输出低分辨率帧,用于显示
- Capture 流:单次输出高分辨率帧,用于拍照
HAL3 支持多流并发。你可以在 configureStreams() 中同时配置 Preview 流和 Capture 流。HAL 内部会做 Buffer 复用和调度。
我记得有一次,客户要求「拍照时 Preview 不能卡顿」。这其实挺难的,因为拍照瞬间 ISP 要全力处理高分辨率数据,Preview 流可能被饿死。解决方案是给 Preview 流设置更高的优先级,或者用独立的 ISP pipeline。
17.7 常见问题与调试技巧
做 Camera 开发,你一定会遇到这些问题:
| 问题 | 可能原因 | 我的排查方法 |
|---|---|---|
| Preview 黑屏 | Buffer 没填对,或 Surface 未注册 | 检查 dumpsys 中的 BufferQueue 状态 |
| 拍照卡顿 | 3A 算法耗时太长 | 看 logcat 中 HAL 的 3A 日志 |
| 画面偏色 | AWB 参数不对 | 手动设置 AWB 模式,排除硬件问题 |
| 闪退 | HAL 接口调用顺序错误 | 对照 HAL3 规范检查调用顺序 |
adb logcat -s CameraService 只看 CameraService 的日志。配合 adb shell dumpsys media.camera,大部分问题都能定位。
嗯,Camera 子系统就讲到这里。HAL3 的 Request/Result 模型、CameraService 的设备管理、Preview 和 Capture 的流程,这些都是你必须要掌握的。下次遇到 Camera 问题,先画个架构图,定位到具体层,再深入排查——这是我多年来的习惯,分享给你。
公众号:蓝海资料掘金营,微信deep3321