10、CameraService 架构:Camera HAL3 模型、Camera 设备会话、预览与拍照流程
各位同学,今天我们来啃一块硬骨头——CameraService 的架构。说实话,Camera 子系统在 Android 里一直是最复杂、坑最多的模块之一。我当年刚接手相机项目时,光是把 HAL3 的 pipeline 理清楚就花了两周。但只要你把今天这几个核心概念吃透,后面写相机应用或者做相机定制,就会顺手很多。
10.1 Camera HAL3 模型:从“黑盒”到“管道”
先聊聊 HAL3 模型。Android 相机 HAL 经历过两次大迭代:HAL1 和 HAL3。HAL1 是个典型的“黑盒”模型——你调用 open(),它给你返回一个 preview 流,内部怎么处理你完全不知道。说白了,HAL1 就是一把大锤子,你只能砸,不能精细控制。
HAL3 就不一样了。它引入了 pipeline 模型,把相机处理流程拆成了一个个可配置的 stage。每个 stage 可以独立设置参数,比如曝光、增益、白平衡。你想想看,这就像从“点外卖”变成了“自己下厨”——你可以控制每一道工序。
HAL3 的架构可以用下面这张图来理解。我习惯把它画成三层:App 层、Framework 层、HAL 层。App 通过 Camera2 API 下发请求,Framework 里的 CameraService 负责调度,HAL 层真正干活。
嗯,这里要注意:HAL3 的 pipeline 是 异步 的。你下发一个 request,HAL 处理完后通过回调通知你。这个设计让相机可以连续处理多帧,但也带来了同步的复杂性。我在项目中遇到过一个问题:连续下发 30 帧 request,结果 HAL 返回的顺序乱了,导致预览画面抖动。后来发现是 buffer 复用没做好。
10.2 Camera 设备会话:Session 的生命周期
接下来是设备会话。在 Camera2 API 里,你要先创建一个 CameraCaptureSession。这个 session 说白了就是你和相机硬件之间的一条“专线”。
创建 session 的流程大致如下:
// 1. 获取 CameraDevice
CameraManager manager = (CameraManager) getSystemService(CAMERA_SERVICE);
String cameraId = manager.getCameraIdList()[0];
manager.openCamera(cameraId, stateCallback, handler);
// 2. 在 onOpened 回调中创建 session
@Override
public void onOpened(CameraDevice camera) {
List<Surface> outputs = new ArrayList<>();
outputs.add(surfaceView.getHolder().getSurface()); // 预览输出
outputs.add(imageReader.getSurface()); // 拍照输出
camera.createCaptureSession(outputs, sessionCallback, handler);
}
// 3. 在 onConfigured 回调中开始预览
@Override
public void onConfigured(CameraCaptureSession session) {
CaptureRequest.Builder builder = camera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
builder.addTarget(surfaceView.getHolder().getSurface());
session.setRepeatingRequest(builder.build(), captureCallback, handler);
}
session 有三种状态:Active(正在处理请求)、Idle(空闲)、Closed(已关闭)。你可以在 onCaptureSequenceCompleted 回调里判断是否所有请求都完成了。
| 状态 | 触发条件 | 常见问题 |
|---|---|---|
| Active | 有未完成的 capture 或 repeating request | 连续拍照时 session 可能卡在 Active |
| Idle | 所有请求都已完成,没有新的 request | 切换场景时最好先等 Idle |
| Closed | 调用了 session.close() | 关闭后不能再提交 request |
10.3 预览流程:从 Sensor 到屏幕
预览流程,说白了就是相机 sensor 不断采集数据,经过 ISP 处理,最后显示到屏幕上。这个过程是 循环 的——每一帧都走一遍同样的 pipeline。
具体步骤:
- App 下发 RepeatingRequest:通过
session.setRepeatingRequest()告诉 HAL 持续输出预览帧。 - HAL 处理 Sensor 数据:HAL 驱动 sensor 曝光、读出 raw 数据,然后做 ISP 处理(去噪、颜色校正、缩放)。
- HAL 填充 Output Buffer:处理完的数据写入 Surface 对应的 buffer 里。
- SurfaceFlinger 合成显示:Surface 的 buffer 被 SurfaceFlinger 取走,合成到屏幕上。
这里有个关键点:预览帧率。Android 默认预览帧率是 30fps,但如果你在 HAL 里做了太多处理(比如人脸检测),帧率可能掉到 15fps。我建议你在 HAL 的 process_capture_request 里加个计时器,监控每帧的处理时间。
// HAL 层的伪代码
int process_capture_request(const camera3_capture_request_t *request) {
uint64_t start = get_timestamp_ns();
// 1. 获取 sensor 数据
buffer_handle_t input_buffer = request->input_buffers[0];
// 2. ISP 处理
isp_process(input_buffer, request->settings);
// 3. 填充输出 buffer
for (int i = 0; i < request->num_output_buffers; i++) {
fill_output_buffer(request->output_buffers[i]);
}
uint64_t elapsed = get_timestamp_ns() - start;
if (elapsed > 33 * 1000000) { // 超过 33ms 说明掉帧了
ALOGW("Preview frame took %lld ms, dropping frame rate", elapsed / 1000000);
}
return 0;
}
10.4 拍照流程:从 Request 到 JPEG
拍照和预览最大的区别是:预览是 持续 的,拍照是 单次 的。拍照时,App 下发一个 capture() 请求,HAL 处理完一帧后返回结果,然后 pipeline 就停了(除非你继续发 repeating request)。
拍照的完整流程:
- App 下发 CaptureRequest:设置好拍照参数(闪光灯、对焦模式、输出格式等)。
- HAL 执行 3A 收敛:如果设置了 AE/AF/AWB,HAL 会先做自动曝光、自动对焦、自动白平衡。这个过程可能需要几帧。
- HAL 捕获一帧:sensor 曝光、读出数据,ISP 处理成 YUV 或 RAW 格式。
- JPEG 编码:如果输出格式是 JPEG,HAL 或 Framework 会做 JPEG 压缩。
- 回调 CaptureResult:App 在 onCaptureCompleted() 里拿到 JPEG 数据。
嗯,这里有个坑:拍照时如果闪光灯开了,HAL 需要先做预闪(pre-flash)来确定曝光参数。预闪会额外消耗 1-2 帧的时间。我遇到过一个问题:用户连拍时,闪光灯每拍一次都做预闪,导致连拍速度很慢。后来我们改成了只在第一次拍照时做预闪,后续复用参数。
// 拍照的 CaptureRequest 设置示例
CaptureRequest.Builder builder = camera.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
builder.addTarget(imageReader.getSurface());
// 设置闪光灯模式
builder.set(CaptureRequest.FLASH_MODE, CameraMetadata.FLASH_MODE_SINGLE);
// 设置对焦模式
builder.set(CaptureRequest.CONTROL_AF_MODE, CameraMetadata.CONTROL_AF_MODE_AUTO);
// 设置输出格式(通过 ImageReader 的配置)
// ImageReader reader = ImageReader.newInstance(width, height, ImageFormat.JPEG, 1);
// 触发拍照
session.capture(builder.build(), captureCallback, handler);
session.stopRepeating();
session.capture(stillCaptureRequest, new CaptureCallback() {
@Override
public void onCaptureCompleted(CaptureResult result) {
// 拍照完成,恢复预览
session.setRepeatingRequest(previewRequest, null, null);
}
}, handler);
最后,总结一下今天的内容。Camera HAL3 的核心是 pipeline 模型,它把相机处理拆成了可配置的 stage。设备会话是 App 和 HAL 之间的桥梁,管理着请求的生命周期。预览和拍照本质上是同一套 pipeline,只是一个是循环执行,一个是单次执行。
我个人觉得,理解 CameraService 架构的关键在于“异步”和“buffer 管理”这两个点。你只要把这两个点想通了,后面看 CameraService 的源码就会轻松很多。好了,今天的课就到这里,大家回去可以自己写个 demo,试试用 Camera2 API 实现预览和拍照,感受一下 pipeline 的流转。