2、预览流程总览:从App到HAL的预览请求链路全景图
好,咱们正式开始聊预览流程。
说实话,我刚开始接触Camera HAL层时,最头疼的就是搞不清一条预览请求到底是怎么从App一路跑到HAL的。你想想看,用户点一下「预览」按钮,背后要经过多少层?Framework、Service、Provider、HAL……每一层都有自己的职责,稍不留神就掉坑里了。
这一节,我带你从高空俯瞰整条链路。先看全景,再拆细节。
2.1 整条链路长什么样?
一条预览请求,从App到HAL,大致经过这么几个节点:
- App层:调用
CameraManager.openCamera()打开相机,然后调用CameraDevice.createCaptureSession()创建会话,最后调用CameraCaptureSession.setRepeatingRequest()发起预览请求。 - Framework层:App的请求被封装成
CaptureRequest,通过Binder跨进程发送到CameraService。 - CameraService:这是承上启下的关键节点。它把请求交给
CameraProvider,同时管理着底层的CameraDevice状态。 - CameraProvider:HAL的入口。它把Framework的请求翻译成HAL能理解的格式,然后调用HAL接口。
- HAL层:最终执行者。HAL收到请求后,配置ISP、Sensor,开始输出帧数据。
嗯,说白了就是一条「App → Framework → Service → Provider → HAL」的链条。每一层都在做一件事:把上一层的请求「翻译」成下一层能听懂的语言。
我个人习惯:调试预览问题时,先确认请求到底卡在了哪一层。比如App层报错,那多半是权限或配置问题;如果HAL层没收到请求,那就要查Binder通信或Service的状态了。
2.2 关键数据结构:CaptureRequest 的旅行
预览请求的核心,其实就是 CaptureRequest 这个对象。它从App一路传到HAL,沿途被不断「拆包」和「重组」。
我画个简化的流程图给你看:
App (CaptureRequest)
↓ Binder
CameraService (CaptureRequest)
↓ 内部转换
CameraProvider (CaptureRequest)
↓ HAL接口调用
HAL (camera3_capture_request_t)
注意看,到了HAL层,数据结构变成了 camera3_capture_request_t。这是HAL定义的C结构体,和Framework的Java对象完全不同。
为什么会这样?因为跨进程传输需要序列化,而HAL层为了性能,直接用C结构体操作内存。我在项目中遇到过一个问题:App设置了AE补偿,但HAL收到的值始终是0。查了半天,发现是Provider层在转换时漏掉了这个字段。嗯,这种坑很常见。
2.3 预览请求的「三要素」
一个预览请求,说白了就三样东西:
| 要素 | 说明 | 常见坑点 |
|---|---|---|
| 输出目标(Surface) | 预览画面要显示到哪里?通常是SurfaceView或TextureView | Surface格式不匹配,比如HAL要求NV12,App给了YV12 |
| 控制参数(Settings) | AE、AWB、AF等参数,决定画面效果 | 参数未初始化,导致HAL使用默认值,画面偏色 |
| 模板类型(Template) | 告诉HAL这是预览、拍照还是录像 | 模板类型传错,HAL可能用错ISP配置 |
你想想看,这三样东西只要有一个不对,预览就可能黑屏、卡顿或者画面异常。我曾经调试过一个案子:预览画面一直闪烁,最后发现是Surface的BufferQueue深度设置太小,导致丢帧。嗯,这种问题在HAL层很难定位,因为错误信息往往只出现在Framework层。
2.4 预览请求的「生命周期」
一条预览请求从发出到结束,大致经历这几个阶段:
- 请求提交:App调用
setRepeatingRequest(),请求进入Framework的请求队列。 - 请求分发:CameraService把请求分发给CameraProvider,Provider再调用HAL的
process_capture_request()。 - 硬件执行:HAL配置Sensor和ISP,开始曝光、读出、处理,最终输出一帧图像。
- 结果回调:HAL通过
process_capture_result()把结果返回给Framework,Framework再回调给App。
这里有个关键点:预览是「重复请求」,所以第1步到第4步会不断循环。HAL每处理完一帧,就会收到下一个请求。如果HAL处理速度跟不上Sensor的帧率,就会出现丢帧。
避坑指南:我曾经遇到一个项目,预览帧率只有15fps,但Sensor明明支持30fps。查到最后,发现是HAL的 process_capture_request() 里做了太多同步操作,导致处理时间过长。解决办法是把一些初始化操作移到 configure_streams() 里,提前准备好。
2.5 预览流程的「关键节点」
整条链路中,有几个节点特别容易出问题,我列出来给你参考:
- Binder通信:App和Service之间的跨进程调用,如果Binder缓冲区满了,请求会阻塞。我建议在App层用异步方式提交请求,避免卡UI线程。
- Stream配置:创建CaptureSession时,App会指定输出Stream的格式和尺寸。如果HAL不支持这个配置,就会报错。嗯,这个坑我踩过好几次,后来学乖了,先查
CameraCharacteristics确认支持的配置。 - HAL的process_capture_request:这是HAL层的核心函数。它必须尽快返回,不能阻塞。如果HAL需要做耗时操作,比如3A计算,应该另开线程处理。
- 结果回调:HAL返回结果时,必须保证帧序号和请求序号一致。否则Framework会认为丢帧,触发异常处理。
2.6 小结:记住这张全景图
好了,预览流程的全景图就讲到这里。你记住一句话就行:
预览请求从App出发,经过Framework、Service、Provider,最终到达HAL。每一层都在做「翻译」和「调度」的工作。
下一节,我会带你逐行分析 setRepeatingRequest() 的代码,看看Framework层到底是怎么处理这个请求的。到时候你会发现,很多细节其实藏在Binder通信和请求队列里。
嗯,今天就到这儿。有问题随时问我。