3. Camera HAL与CamX/CHI框架
在Android相机软件栈中,Camera HAL是连接上层Camera Framework与底层Sensor/ISP驱动的中间层。从Android 8.0(Treble)开始,HAL3接口被标准化为android.hardware.camera.provider@2.4及其后续版本,通过HIDL/AIDL与CameraService通信。高通平台在HAL3之下引入了CamX(Camera eXtended)与CHI(Camera Hardware Interface)两层结构:CamX负责通用HAL3协议实现、请求调度、节点图管理与资源仲裁;CHI则提供厂商与OEM可覆写的扩展层,用于定制Usecase、Feature、Pipeline与Node。理解这三者的分工与交互,是排查拍照链路问题的前提。
HAL3接口:请求—结果—通知三元模型
HAL3的核心是异步的“请求—结果”模型。上层通过process_capture_request()下发CaptureRequest,其中包含frame_number、settings(3A、曝光、尺寸等元数据)以及输出stream_buffer列表。HAL必须在合理时间内返回process_capture_result(),携带部分或全部结果元数据与已填充的buffer;同时通过notify()上报SHUTTER、ERROR、REQUEST_RESULT等事件。关键约束是:结果可以分多次返回,但frame_number必须与请求一致;SHUTTER通知的timestamp必须与结果中的SENSOR_TIMESTAMP一致,否则上层会判定时间戳错乱。
在CamX中,HAL3接口由camxhal3entry.cpp中的HAL3Module实现,它把HAL3调用翻译为内部的CaptureRequest对象,再交给HAL3Device与HAL3Usecase处理。因此,当出现“request下发后无result”或“result frame_number错位”时,排查顺序通常是:HAL3 entry日志 → Usecase选择 → Pipeline/Node执行 → 结果回填。
CamX节点图:Pipeline、Node与Port
CamX把一次拍照或预览抽象为一张有向图:Pipeline是图的容器,Node是处理单元,Port是节点间的输入输出端点,Link连接上游OutputPort与下游InputPort。每个Node有独立的NodeProcessRequest,按依赖顺序执行。典型Snapshot Pipeline包含:Sensor Node → IFE(ISP前端)Node → IPE(ISP后端)Node → JPEG Node → Sink。Preview Pipeline则可能走IFE → IPE → Sink,跳过JPEG。
节点图的价值在于:它把“谁先谁后、谁依赖谁”显式化。例如IFE输出TARGET_BUFFER给IPE,IPE再输出给JPEG;若JPEG Node的输入Port未收到buffer,它不会执行,最终表现为“拍照卡住”。因此,调试时常用camxhal3log或CamX Log查看每个Node的ProcessRequest是否被调用、Port是否Ready。
CHI覆写机制:XML配置与动态库
CHI的覆写不是改CamX源码,而是通过两层机制:XML配置与动态库覆写。XML文件(如g_chiusecase.xml、g_chipipeline.xml)定义Usecase、Pipeline、Node、Port、Link以及Feature的拓扑与参数。CamX启动时解析这些XML,构建内部对象树。若OEM需要修改某个Node的行为,可在CHI中实现同名Node并注册到CHINode工厂,CamX在创建Node时优先使用CHI注册的实现。
典型覆写场景包括:自定义3A算法(替换StatsNode或AFNode)、增加私有Metadata、修改JPEG编码参数、插入自定义后处理Node。覆写时需注意:CHI Node必须实现CamX定义的Node接口,且Port的BufferRequirement要与上下游匹配,否则会出现“Port未就绪”或“buffer格式不匹配”的错误。
Usecase与Feature选择流程
Usecase是“场景级”的抽象,例如UsecasePreview、UsecaseSnapshot、UsecaseVideo。当HAL3收到请求时,HAL3Device根据stream_config与CaptureRequest中的ANDROID_CONTROL_CAPTURE_INTENT、ANDROID_CONTROL_MODE等元数据,选择匹配的Usecase。选择逻辑通常由UsecaseSelector完成,它遍历已注册的Usecase,按优先级与条件匹配。
Feature是Usecase内部的“功能开关”,例如HDR、夜景、人像虚化、多帧降噪。Feature通过FeaturePool管理,每个Feature可注册到特定Pipeline,并在SelectFeature阶段根据请求元数据决定是否激活。例如,当ANDROID_CONTROL_AE_MODE为ON_AUTO_FLASH且ANDROID_FLASH_MODE为SINGLE时,可能激活FlashFeature;当ANDROID_NOISE_REDUCTION_MODE为HIGH_QUALITY时,激活MFNRFeature。
实际调试中,若发现“夜景模式未生效”,应检查:请求中是否携带了触发夜景的Metadata;Usecase是否选择了支持夜景的Pipeline;FeaturePool中NightFeature是否被SelectFeature激活;以及该Feature依赖的Node是否在Pipeline中存在且Port连接正确。这四个环节任一断裂,都会导致Feature静默失效。
调试提示:CamX提供camxhal3log与CHI Log两级日志。开启persist.camera.camx.log可输出Usecase选择、Feature激活、Node执行与Port状态。排查时先确认Usecase是否匹配,再确认Feature是否激活,最后看Node是否执行,可快速定位问题层级。
关键配置示例
以下是一个简化的CHI XML片段,用于定义一个Snapshot Pipeline中的JPEG Node覆写:
<CHINode name="JPEGNode">
<InputPort name="InputImage" />
<OutputPort name="OutputImage" />
<Property name="Quality" value="95" />
<Property name="Format" value="NV12" />
</CHINode>
<CHIPipeline name="SnapshotPipeline">
<Node ref="SensorNode" />
<Node ref="IFENode" />
<Node ref="IPENode" />
<Node ref="JPEGNode" />
<Link src="IFENode.Output" dst="IPENode.Input" />
<Link src="IPENode.Output" dst="JPEGNode.InputImage" />
</CHIPipeline>
该配置中,JPEGNode的Quality与Format被覆写为95与NV12;SnapshotPipeline显式声明了节点顺序与连接关系。若实际运行中JPEG输出异常,可先检查IPENode.Output的buffer格式是否为NV12,再检查JPEGNode是否收到InputImage。
小结与排查路径
HAL3负责协议,CamX负责通用实现与节点图调度,CHI负责定制覆写。三者关系可概括为:HAL3请求进入CamX,CamX根据Usecase选择Pipeline,Pipeline按节点图执行,CHI在节点、Feature、Usecase层面提供覆写点。排查问题时,建议按“HAL3请求是否下发 → Usecase是否匹配 → Feature是否激活 → Node是否执行 → Port是否就绪 → 结果是否回填”的顺序逐层验证,避免在错误层级反复修改配置。