1. 为什么我们需要CamX-CHI?
如果你在Android相机开发领域摸爬滚打超过3年,一定还记得当年被MM-Camera架构支配的恐惧。2019年之前,高通的相机架构就像个黑盒子——想要加个简单的美颜滤镜?得先翻遍几十万行代码,小心翼翼地避开各种私有API,最后可能还要重新编译整个HAL层。
我在2018年开发双摄虚化功能时,曾经为了修改ISP参数连续加班两周。每次改动都要重新烧写固件,测试周期长得让人崩溃。直到CamX-CHI架构出现,这种局面才被彻底改变。
CamX-CHI的精髓在于"分而治之":
- CamX:高通维护的核心引擎,处理传感器驱动、ISP控制等底层硬件操作
- CHI(Camera Hardware Interface):开放给厂商的定制层,就像相机的"应用商店"
实测下来,现在添加一个新功能节点(Node)的开发周期,从原来的2周缩短到3天。去年我们团队在CHI层实现AI场景识别功能,从编码到上线只用了5个工作日——这在MM-Camera时代根本不敢想象。
2. 解剖CamX-CHI的架构设计
2.1 目录结构里的秘密
先看代码仓库里这两个关键目录:
vendor/qcom/proprietary/ ├── camx/ # 高通的核心引擎 │ ├── core/ # HAL3接口实现 │ ├── csl/ # 与kernel驱动的通信层 │ └── hwl/ # 硬件加速节点 └── chi-cdk/ # 厂商定制区 ├── node/ # 你的自定义节点 ├── topology/ # 管线配置 └── bin/ # 平台配置文件我特别喜欢CHI的node目录设计。去年给某手机厂商做夜景模式优化时,就在这里添加了NightSceneNode。你只需要关注图像处理算法本身,框架会自动处理内存管理、线程调度这些脏活累活。
2.2 五大核心组件详解
2.2.1 Usecase:场景指挥官
想象你在指挥交响乐团:
- 演奏莫扎特(普通拍照)和演奏马勒(夜景模式)需要不同的乐器组合
common_usecase.xml就是你的乐谱,比如这段ZSL(零延时拍照)配置:
<Usecase Name="UsecaseZSL"> <Target Type="Display" Format="UBWCTP10" MinWidth="720" MinHeight="1280"/> <Pipeline Name="RealtimeZSL"/> </Usecase>我在调试一加9 Pro的8K录像时,发现个有趣现象:当温度超过42℃时,系统会自动切换到UsecaseVideoLimit,这就是在AdvancedCameraUsecase里实现的降级逻辑。
2.2.2 Feature:特效魔术师
最近给Redmi Note 12 Pro开发的"魔法分身"功能,就是用FeatureMultiShot实现的。关键代码结构:
class FeatureMultiShot : public Feature { void ExecuteProcessRequest() override { // 1. 启动主摄Pipeline // 2. 0.5秒后启动广角Pipeline // 3. 融合两路图像 } }踩过的坑:一定要在Destroy()里释放OpenCL资源,否则连续拍摄20次后会内存泄漏。
2.2.3 Pipeline:流水线工头
理解Pipeline最好的方式就是看topology/下的XML配置。比如人像模式的管线:
<Pipeline Name="PortraitMode"> <Node Name="IFE" Type="SensorInput"/> <Node Name="Bokeh" Type="Software"/> <Link SrcPort="IFE:Output0" DstPort="Bokeh:Input0"/> </Pipeline>实测发现:当Pipeline包含超过7个Node时,建议拆分成两个Session,否则帧率会下降15%以上。
2.2.4 Node:功能积木块
去年开发的SkinSmoothingNode现在成了各大厂商的标配,核心逻辑其实很简单:
void SkinSmoothingNode::ProcessRequest() { // 1. 获取人脸关键点 // 2. 分区磨皮算法 // 3. 输出处理后的纹理 }性能优化秘诀:在SetupRequest()里预分配缓冲区,可以减少30%的内存碎片。
3. 实战:开发一个滤镜Node
3.1 搭建开发环境
首先在chi-cdk/node/下创建新目录:
mkdir MyFilterNode cd MyFilterNode需要这些基础文件:
Android.mk # 构建脚本 MyFilterNode.h # 头文件 MyFilterNode.cpp # 实现记得在Android.mk里添加OpenCV依赖:
LOCAL_SHARED_LIBRARIES += libopencv_core3.2 实现核心逻辑
重点看ProcessRequest()的实现:
void MyFilterNode::ProcessRequest() { // 获取输入图像 CHIBUFFERINFO* input = GetInputBuffer(0); // 应用滤镜(示例:反色效果) cv::Mat img(input->height, input->width, CV_8UC3, input->pVirtualAddr); cv::bitwise_not(img, img); // 提交结果 CHIBUFFERINFO* output = GetOutputBuffer(0); memcpy(output->pVirtualAddr, img.data, img.total() * img.elemSize()); }避坑指南:一定要检查input->format是否等于FORMAT_YUV420NV12,否则会引发绿屏问题。
3.3 集成到Pipeline
在topology/my_filter.xml中添加配置:
<Node Name="MyFilter" Type="Software"> <InputPort Name="Input0" Format="YUV420NV12"/> <OutputPort Name="Output0" Format="YUV420NV12"/> </Node>然后修改你的Usecase配置,在合适的位置插入这个Node。
4. 性能调优实战
4.1 内存管理技巧
CamX-CHI最让人头疼的就是内存问题。分享两个救命技巧:
- 缓冲区复用:在
CreateBufferManager()里设置maxBufferCount=3,可以降低30%内存占用
bufferManager.create( maxBufferCount: 3, bufferStride: width * 1.5, producerFlags: CAMX_BUFFER_PRODUCER );- 延迟加载:对于AI模型这类大资源,在
OnNodeStreamOn()时才加载
4.2 多线程优化
去年优化视频HDR功能时,发现Node的默认线程优先级太低。解决方法:
void MyNode::Initialize() { SetThreadPriority("MyNodeThread", PRIORITY_URGENT_DISPLAY); }注意:不要超过PRIORITY_URGENT_DISPLAY,否则会影响系统稳定性。
4.3 功耗控制
在actuator/目录下的配置文件中,可以设置马达的省电模式:
<Actuator name="main"> <PowerSave enable="true" timeoutMs="500"/> </Actuator>实测这个改动让某款机型的待机功耗降低了8%。