14、Camera子系统:Camera HAL3架构、ISP处理、预览与拍照流程
Camera子系统,说白了就是Android设备上那块“眼睛”的驱动层。我做了这么多年嵌入式开发,Camera HAL3的改动可以说是Android系统里最折腾人的部分之一。从HAL1到HAL3,不光是接口变了,整个设计思路都翻了个个儿。
今天咱们就聊聊Camera HAL3的架构、ISP怎么处理图像,以及预览和拍照的完整流程。嗯,这里头坑不少,我一个个给你拆开讲。
14.1 Camera HAL3架构概览
HAL3的核心思想,是把Camera当成一个“流水线”。你想想看,每一帧图像从传感器出来,经过ISP处理,再送到应用层,这中间每一步都可以独立控制。HAL3就是把这个流水线暴露给了上层。
我个人习惯把HAL3架构分成三层:
- 应用层:Camera2 API,开发者直接调用的接口
- 框架层:CameraService,负责协调多个Client的请求
- HAL层:Camera HAL3实现,直接跟硬件打交道
这里有个关键点:HAL3引入了Capture Request和Capture Result的概念。每次拍照或预览,应用层下发一个Request,HAL层处理完后返回一个Result。这跟HAL1那种“打开-预览-拍照-关闭”的线性流程完全不同。
核心变化:HAL3支持同时处理多个Request,而且每个Request可以指定不同的参数(比如分辨率、帧率、对焦区域)。这在HAL1时代是想都不敢想的。
我在项目中遇到过一个问题:某个平台升级到HAL3后,预览帧率反而下降了。查了半天,发现是Request队列没处理好,导致ISP一直在切换参数。后来加了个缓冲区,问题就解决了。嗯,细节决定成败。
14.2 ISP处理流程
ISP(Image Signal Processor)是Camera的灵魂。传感器出来的原始数据是Bayer格式的,说白了就是每个像素只记录一种颜色(红、绿或蓝)。ISP要把它变成我们能看的RGB图像。
ISP的处理管线大致如下:
- 黑电平校正:去掉传感器本身的暗电流噪声
- 去马赛克:把Bayer格式插值成RGB
- 白平衡:调整色温,让白色看起来是白的
- 色彩校正:把传感器色彩空间映射到标准色彩空间
- Gamma校正:调整亮度曲线,适应人眼感知
- 降噪:去掉各种噪声(亮度噪声、色度噪声)
- 锐化:增强边缘细节
你可能会问:“这些步骤能不能跳过?” 嗯,有些可以,但大部分不行。比如黑电平校正如果跳过了,暗部区域会偏色。我调试过一款低端传感器,黑电平校正参数没配好,拍出来的夜景全是紫色的。折腾了两天才找到原因。
我的经验:ISP参数调试是个细活。每个传感器都有自己的特性,同一套参数换到另一个传感器上可能完全不能用。建议先用厂商提供的参考参数,再逐步微调。
下面这张图展示了ISP处理的核心流程:
14.3 预览流程
预览流程,说白了就是让Camera持续输出帧数据到Surface上。在HAL3里,这个过程是这样的:
- 应用层创建CaptureSession,并配置输出Surface(比如TextureView或SurfaceView)
- 应用层下发Repeating Request,告诉HAL“我要持续预览”
- HAL层收到Request后,驱动Sensor开始采集数据
- ISP处理完一帧后,把数据送到指定的Surface
- 应用层从Surface拿到数据,渲染到屏幕上
这里有个容易踩的坑:Repeating Request的模板选择。HAL3定义了多种模板,比如TEMPLATE_PREVIEW、TEMPLATE_STILL_CAPTURE、TEMPLATE_VIDEO_RECORD等。每个模板的默认参数不一样。
注意:预览时一定要用TEMPLATE_PREVIEW模板。我曾经见过有人用TEMPLATE_STILL_CAPTURE做预览,结果帧率只有5fps,因为那个模板默认开启了全尺寸输出和最高画质处理。预览根本不需要那么高的质量,够用就行。
预览流程的代码示例(简化版):
// 创建CameraCaptureSession
cameraDevice.createCaptureSession(
Arrays.asList(previewSurface),
new CameraCaptureSession.StateCallback() {
@Override
public void onConfigured(CameraCaptureSession session) {
// 下发Repeating Request
CaptureRequest.Builder builder =
cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
builder.addTarget(previewSurface);
session.setRepeatingRequest(builder.build(), null, null);
}
}, null);
嗯,代码看着简单,但实际调试时问题不少。比如Surface的格式必须匹配,否则HAL会报错。我遇到过Surface配置成RGB_565,但HAL只支持NV21的情况,折腾了半天才找到原因。
14.4 拍照流程
拍照跟预览最大的区别是:拍照需要单次触发,而且通常需要更高分辨率和更高质量的处理。
拍照流程如下:
- 应用层下发一个单次Capture Request,参数通常用TEMPLATE_STILL_CAPTURE
- HAL层收到Request后,可能会先做一次预对焦(如果开启了自动对焦)
- Sensor采集一帧数据,ISP做完整处理(包括降噪、锐化等)
- 处理完的数据送到指定的输出Surface(比如ImageReader)
- 应用层从ImageReader拿到JPEG或YUV数据,保存到文件
这里有个细节:拍照时通常会同时输出两路数据——一路是预览用的低分辨率流,一路是拍照用的高分辨率流。HAL3支持多路输出,但要注意性能开销。
我的建议:拍照时不要停止预览流。很多开发者习惯先停止预览再拍照,拍完再重新预览。其实完全没必要,HAL3支持同时处理多个Request。保持预览流不断,用户体验会好很多。
拍照流程的代码示例:
// 创建ImageReader,用于接收拍照数据
ImageReader reader = ImageReader.newInstance(
width, height, ImageFormat.JPEG, 1);
reader.setOnImageAvailableListener(reader -> {
Image image = reader.acquireLatestImage();
// 保存JPEG数据
saveImage(image);
image.close();
}, null);
// 下发拍照Request
CaptureRequest.Builder builder =
cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
builder.addTarget(reader.getSurface());
// 同时保持预览流
builder.addTarget(previewSurface);
session.capture(builder.build(), null, null);
你可能会问:“为什么ImageReader的maxImages设成1?” 嗯,这是为了避免内存占用过高。拍照数据通常很大,一张JPEG可能几MB,如果缓存太多帧,内存会爆。设成1就够了,拍完一帧马上取走。
14.5 预览与拍照的协同
在实际应用中,预览和拍照是同时进行的。HAL3通过Stream Configuration来管理多路输出。配置Stream时,需要指定每个Stream的格式、分辨率和用途。
常见的配置组合:
| Stream类型 | 分辨率 | 格式 | 用途 |
|---|---|---|---|
| 预览流 | 1920x1080 | NV21 | 实时显示 |
| 拍照流 | 4000x3000 | JPEG | 保存照片 |
| 分析流 | 640x480 | YUV_420_888 | 人脸检测等 |
配置Stream时有个限制:所有Stream的帧率必须一致。你不能让预览流跑30fps,拍照流跑15fps。HAL3会取所有Stream的最大帧率作为统一帧率。
避坑指南:我曾经在一个项目里配置了三个Stream(预览、拍照、分析),结果发现预览帧率从30fps掉到了15fps。查了半天,发现是分析流的分辨率设得太高,ISP处理不过来。后来把分析流降到320x240,问题解决了。记住:Stream越多,性能开销越大。
下面这张图展示了预览和拍照的协同流程:
14.6 常见问题与调试技巧
做Camera HAL3开发,有几个问题几乎每个人都会遇到:
- 预览黑屏:通常是Surface配置不对,或者HAL没收到Request。先检查logcat里有没有“configureStreams”相关的错误。
- 拍照卡顿:可能是JPEG编码太慢。试试降低JPEG质量,或者用YUV格式输出,让应用层自己编码。
- 帧率不稳定:检查ISP的处理时间。如果一帧处理时间超过33ms,帧率就上不了30fps。
- 颜色偏色:白平衡参数没调好。建议先用AWB(自动白平衡),稳定后再切手动。
重要提醒:调试Camera HAL3时,一定要看logcat的“CameraService”和“CameraHal”标签。很多错误信息会打印在这里。我曾经花了一整天查一个预览黑屏的问题,最后发现是HAL层没实现configureStreams接口。logcat里清清楚楚地打印了“not implemented”,只是我没注意看。
好了,Camera HAL3的内容就聊到这儿。记住:HAL3的核心是Request/Result模型,ISP处理是图像质量的关键,预览和拍照可以并行进行。把这些搞明白了,Camera开发就成功了一大半。