
简介高通相机Camx架构的全套源码为Android平台上的相机软件开发与图像处理研究提供了完整参考。资源面向相机驱动工程师、图像算法工程师和基于骁龙平台的设备制造商覆盖核心框架、图像处理节点如IFE节点、IPE节点、BPS节点、传感器控制以及硬件抽象层交互逻辑可帮助理解自动曝光、自动对焦、白平衡、HDR、降噪等算法是如何在真实工程中落地的。压缩包内共763个文件以358个头文件与321个C源文件为主体辅以构建脚本、说明文档以及少量XML与动态库文件整体体积仅1.68MB结构清晰便于按模块查找和二次开发。该资源已有529人学习下载适合希望深入高通相机管线源码的开发者。通过研读其中的传感器节点、图像处理节点等关键模块可以掌握从硬件控制到完整图像处理链路的实现思路为定制相机功能、优化成像效果提供直接参考。1. 高通Camera Camx是什么从源码仓库到成像流水线做高通平台的相机bring up绕不开Camx这套东西。新平台回来第一步往往就是打开log看到一行camx is not supported或者no camera are attached然后开始翻vendor/qcom/proprietary/camx下的代码。Camx全称Camera eXtension是高通从SDM845之后主推的相机HAL架构取代了老旧的MM-Camera。它分两层底层Camx HAL负责和ISP、传感器打交道上层CHI-CDKCamera HAL Interface - Camera Driver Kit做算法集成和feature定制。源码仓库通常跟着BSP一起发CAF上也能同步到。这套代码决定了你最终能否把一颗sensor点亮、把美颜水印叠上去、把出帧延时压下来。这篇文章就按我实际调过的路径从架构、同步、编译一直讲到踩坑和验证给准备在这套源码上动手的人一条靠谱的路线。2. Camx架构拆解从HAL到ISP的调用链与关键模块2.1 CHI/Camx分层为什么高通把相机HAL拆成两层先看整体分层。Camx不是单一半身而是Camx HAL CHI-CDK两个仓库协同工作。Camx是核心HAL实现包含V4L2对接、ISP pipeline管理、node节点调度、request队列等CHI-CDK是上层可配的扩展层包含feature graph、usecase、extension模块以及外置算法厂商高通ais、虹软、商汤等的override接口。这种拆分的目的只有一个把稳定不变的硬件抽象和天天变的算法策略分开。Camx层尽量保持稳定改动多在CHI-CDK层完成。你拿到一套camx仓库全套源码实际上会看到camx目录和chi-cdk目录并存编译产物也是一个hal模块和一个chi-cdk模块。工程上最常见的做法是sensor特性、AE/AWB参数、降噪算法、美颜策略全部放CHI-CDK侧而Camx侧只处理通用流程比如request submission、pipeline切换、stream on/off。这种分层带来的直接好处是多项目共用同一份Camx核心只替换CHI-CDK配置。但也埋了坑你在CHI-CDK里加一个override函数Camx侧如果没有对应接口一样编不过反过来Camx层动了node的连接关系CHI-CDK的graph定义就得同步改。2.2 Camx核心数据结构Pipeline、Node、Request手上如果有一份camx源码优先看这几个文件camxhal3.cpp、camxdevice.cpp、camxpipeline.cpp、camxnode.cpp、camxrequest.cpp。它们对应了HAL3标准接口、设备管理、pipeline生命周期、node调度和request流转。一个典型的Camx pipeline由一串node组成每个node承担一个功能切片。常见node包括IFEBayer(RAW域处理)、IPELite(降噪/色彩)、IPE(输出域处理)、JPEG node、Sensor node、Stats node等。它们通过node link连成拓扑类似一个有向图。运行时一次拍照请求会生成一个requestrequest里会携带3A结果、buffer句柄、tuning mode等信息然后Camx把request分发到pipeline的各个node去执行。关键要理解preview/picture pipeline分离和batching调度这两个底层机制。Camx默认有batching参数把一个request批次内的多个帧合并提交给ISP减少CPU和硬件的交互次数同时preview和capture分开走不同的pipeline避免高分辨率拍照阻塞低分辨率预览的出帧。2.3 从Open Camera到出帧一次拍照请求走的路实际调试时最常问的问题是相机打不开到底卡在哪一环节。我一般用这条调用链去定位CameraService调用HAL3接口openCamera正常会走到CamxDevice的CreateDevice。CamxDevice创建各种session每个session对应一组stream和pipeline。上层下发processCaptureRequestCamxDevice把request转成CamxRequest塞进对应Session的queue。Sensor node开始读取曝光参数IFEBayer node和stats node采集数据3A算法更新最后IPE node输出YUV/JPEG填回hal3 buffer。如果中间任何一环的拓扑配置和实际硬件能力不匹配比如sensor的raw格式配错、node link少了条边、tuning mode名对不上request就会卡死在camx内部上层表现为打开相机超时或者第一帧黑屏。这时你打开camx log能看到Failed to submit request或者Node X not ready。3. camx仓库全套源码怎么落到本地repo同步与编译环境3.1 获取源码从CAF同步camx与chi-cdk拿到高通平台BSP后camx源码一般会在vendor/qcom/proprietary/camx和vendor/qcom/proprietary/chi-cdk两个目录。如果是通过CAF同步常见初始化命令是repo init -u https://git.codelinaro.org/clo/la/platform/manifest.git -b release -m LA.QSSI.12.0.rx.xml repo sync -c -j16说明这里-b release重点是匹配你的平台release tag可以从BSP release notes里查到具体分支比如kalama、pineapple对应的分支不同。-c只同步当前分支能大幅减少下载量-j16是并行任务数按机器CPU核数调。如果只需要camx相关源码可以只同步camx和chi-cdk两个git项目。拿到后第一件事不是编译而是检查目录完整性camx源码通常包含core、hwl、oem、utils、tools等子目录chi-cdk则包含chi、override、topology等目录。中间缺任何一块编译都会报找不到头文件。3.2 编译camx模块使用Soong构建单模块Camx从Android 11之后基本用SoongAndroid.bp构建不再走旧款Android.mk。所以编译前要确认你使用的Android版本和编译环境。最小验证编译命令source build/envsetup.sh lunch device-userdebug cd vendor/qcom/proprietary/camx mm -j$(nproc) 21 | tee camx_build.log说明mm会在当前目录编译该模块及其依赖如果没有单独初始化过camx的product变量可能要加ALLOW_MISSING_DEPENDENCIEStrue。编译日志最重要的一行是最后的Install: out/target/product/ /vendor/lib64/hw/camera.qcom.so看到这条才算成功。如果编译报依赖缺失先检查你的BSP是否带完整的MM-Camera和ais相关仓库。Camx经常依赖高通的amera_conf、scve、ais等预编译库这些仓库不在camx目录里。同步时最好把vendor/qcom/proprietary下整个common主题都拉下来否则会反复报找不到头文件。3.3 把camx放到设备上手动push与权限校验编译产物出来后最常见的验证方式是把新HAL push到设备vendor分区。注意fastboot和adb两个方向都可能存在分区校验特别是Android 12以上有动态分区和avb校验。常规路径adb root adb remount adb push camera.qcom.so /vendor/lib64/hw/ adb push camera.qcom.so /vendor/lib/hw/ adb push libchi-cdk.so /vendor/lib64/ adb sync adb reboot说明camx的hal库名通常是camera.qcom.so但如果你的平台有camera.kalama.so之类的依赖也要一起同步。push后先检查权限是否变成rw-r--r--如果push时提示Read-only file system说明没有remount成功或者adb root之后仍然被avb锁住需要刷入userdebug版本的vbmeta或者用adb disable-verity。还有一类玄学问题是push之后相机能打开但一拍照就闪退多半是vendor/bin下面的hwservicemanager缓存了旧的HAL。解决办法是重启hwservicemanager或直接reboot不要只kill camera provider进程。4. camx源码实战改一个小feature并验证4.1 最小改动在CHI-CDK增加一个tuning override拿到源码后做一个最小的改动可以验证你改的是不是活代码。我常用的做法是在CHI-CDK的override里加一个自定义tuning模式比如给夜景模式强制改一下降噪强度。先找到chi-cdk/override/chiOverridesettings.cpp在某个usecase的PopulateChiMetadata函数里加一段if (pRequest-pUsecaseRequest-usecaseType ChiUsecase::Night) { ChiMetadata *pMetadata pRequest-pUsecaseRequest-pChiMetadata; pMetadata-SetMetadata(ANDROID_NOISE_REDUCTION_STRENGTH, 5); }说明ChiMetadata::SetMetadata可以在request的metadata里写一个自定义值供后面camx的tuning模块读取。这里关键在于理解override的调用时机它是在Camx把request下发到node之前执行所以改动生效于整个pipeline前。ANDROID_NOISE_REDUCTION_STRENGTH是实际存在的metadata tag可以替换成厂商自定义tag。编译chi-cdk模块后同样mm单独出chi-cdk的sopush到vendor/lib64通常能立即通过日志验证新增log有没有打印。这是判断源码版本和当前设备是否匹配的快速手段——很多工程机刷的bin和手里的源码根本对不上通过加log再对比logcat最直接。4.2 用camx debug日志定位问题Camx的日志体系是运行时可配的。最常用的日志控制方式是通过camxoverridesettings.txt放/vendor/etc或者/data/vendor/camera。比如LogConfig2 LogTagCamxNode LogLevel3说明LogTag可以指定你想要跟踪的模块CamxNode会打印所有node的状态切换LogLevel3对应verbose级别一般定位启动超时先开到3。改完重启camera provideradb shell pkill camera-provider再复测。你本地编译camx时还可以打开CAMX_LOG_LEVEL编译宏在Android.bp里加一句cflags: [-DCAMX_LOG_LEVEL3],这样编出来的camx会强制开启verbose日志不用依赖配置文件。缺点是日志量巨大连拍时每秒几百条要通过logcat的tag过滤成camx*再抓取。4.3 使用camx自带的dump工具导出错误现场遇到crash或者死机画面camx仓库里的几个工具很关键camxhal3dump、dump pipeline topology和节点自带的DumpState。其中最简单的是在camxnode.cpp里找到DumpState函数手动加一段代码把当前frame的buffer信息导出if (IsFrameError(pRequest)) { DumpDebugInfo(m_pNodeName, pRequest-GetFrameNumber()); }说明实际操作中会发现单靠logcat很难看出死锁现场因为camx内部线程多而且request queue的依赖关系在log里是散的。我一般通过kill -3触发camx的dump把整个pipeline的node状态和buffer引用打印到logcat再结合addr2line反解crash地址。比如adb shell kill -3 $(pidof camera-provider) adb logcat -d | grep -A100 CamxPipeline::DumpState pipelinedump.txt这时图上会画出node间的link关系以及每个node当前缓存了几帧。如果某个node一直处于WaitForDependency状态瓶颈就在那个node的上游。5. camx仓库踩坑编译、联调、稳定性三板斧5.1 编译报错找不到camx/inc/camxdefs.h现象同步camx源码后直接用mm编译报错fatal error: camx/inc/camxdefs.h: No such file or directory。原因camx的头文件路径依赖全局的include路径这个路径通常由vendor/qcom/proprietary/common/Android.bp注入。只同步camx和chi-cdk会缺common模块导致include路径不完整。解决回到repo manifest把vendor/qcom/proprietary/common和vendor/qcom/proprietary/ais一起同步重新mm。如果不想全量同步也可以手动在camx的Android.bp里加一条include_dirs: [vendor/qcom/proprietary/camx/inc]但不推荐因为后续还会依赖更多公共路径。5.2 相机打开闪退logcat报camx: request failed: no camera are attached现象设备启动正常但打开相机App瞬间黑屏闪退logcat里能看到No Camera are attached。原因这个报错不是没插摄像头而是Camx没有识别到任何sensor。常见原因有三种sensor驱动没加载、camxsensor配置里的传感器名不匹配、以及sensor device tree的i2c addr不对。解决先确认adb shell ls /dev/video*有没有对应的video节点再用adb shell cat /sys/bus/i2c/devices/*/name查sensor名字去chi-cdk/topology/sensor目录找同名配置XML。如果XML里sensor名和驱动名对不上把XML里sensorName改成驱动输出或者反向改驱动的name字段。这类问题在多平台共用一个camx源码树时尤其容易发生因为不同项目会通过编译宏或overlay配置选sensor。5.3 预览一帧后卡死camx log停在Streamon: waiting for sensor pipe现象预览出第一帧后不再刷新logcat里CamxSensorPipeline反复打印waiting for sensor done。原因这是sensor的stream on序列没有完成。最常见的是sensor standby/gpio控制时序不对或者sensor的s_stream_on callback里sleep时间超过camx预期。也有一种情况是你在batching配置里设置的最大批次数太小导致sensor帧率跟不上设定值。解决先打开sensor node的verbose日志重点看sensor的stream on/off返回码。如果返回码是-110说明I2C通信超时检查sensor供电和reset gpio时序。如果返回码是0但还卡把/vendor/etc/camera/camxoverridesettings.txt里BatchingCount调回0试试0表示由硬件自动决定批次数能规避一批sensor驱动对burst mode支持不完整的问题。5.4 拍照crash在CamxNode::ReleaseBuffer附近现象正常预览一拍照片就crashbacktrace最后帧停在CamxNode::ReleaseBuffer或者CamxBufferManager::ReleaseBuffer。原因这种问题绝大多数和buffer的producer/consumer生命周期不同步有关。在HAL3下camera provider的buffer queue由CamxBufferManager管理拍照模式会同时存在多个streampreview同时开如果某个node在request完成前提前release了buffer另一个node再访问就会use-after-free。解决优先查是不是你在override层自己动了buffer句柄。很多人在CHI-CDK里往metadata里塞了中间buffer的handle但没有增加buffer引用计数导致Camx把buf释放后你的override在后续frame里还在引用。正确做法是引用chiBufferHandle前先调CamxBufferManager::Acquire增加refcount用完再release。其次可以把CamxOverridesettings.txt里DisableBufferSharing1打开粗暴但能快速确认是不是share buffer导致。5.5 改了camx源码后push进去行为没有任何变化现象在camx或chi-cdk里加上log编译push后log不出现行为还是老样子。原因大概率是你改的模块没有被真正加载或者代码改动藏在某个条件宏里没被编进去。常见三个坑第一camera HAL有多个variant可能加载的是camera.kalama.so而你在camera.qcom.so的Android.bp里改push错库第二camx有precompiled版本设备上跑的是/vendor/lib64/libcamx.so而不是你在源码里改的那个hal模块第三manifest里选用的fwk版本和你改的usecase代码不在同一条编译路径。解决先确认设备加载了哪个soadb shell ls -l /vendor/lib64/hw/ | grep camera再对比你push的文件hash然后用adb shell strings so | grep 你加的log关键字确认目标库里确实包含了你的改动。如果strings查不到说明编译时这个文件没被重新编检查mm的产物路径和$OUT环境是否对应。6. 把camx用起来调试入口、性能分析与常用命令到了能改能编能跑这一步真正生产环境里还缺一套调试习惯。我最后分享几个能直接复用的入口。6.1 用属性开关快速切换camx行为不重编译就能调一些行为用adb shell setprop配合camx在运行时读取的override配置。比如adb shell setprop persist.vendor.camera.profiling 1 adb shell setprop persist.vendor.camera.3a.debug 1 adb shell setprop persist.vendor.camera.sensor.debug 1这些属性在Camx初始化时读取会改动日志级别和3A调试输出。进阶的是camxoverridesettings.txt放在/data/vendor/camera下修改后只需重启camera provider不需要重启整机这对线上稳定性测试很有用。6.2 性能分析量出帧延时和出帧率做camera稳定性问题分析最终都要落在帧率和延时两个数字上。Camx里最直接的方式是看log中每帧的ts时间戳adb logcat -d | grep CamxRequest::ProcessRequest | grep -o frameNum[0-9]*.*配合perfetto的camera trace可以精确看到sensor、IFE、IPE每段的耗时。我自己的习惯是保存一版camx_profiling.log连续预览5分钟再统计出帧间隔是否稳定在33ms/16ms附近。如果间隔抖动大优先去看tuning里的AEC收敛速度和你GMSL/FPD-Link转接板上的帧同步这两个位置是最常出问题的。最后一句话送给准备动camx仓库源码的朋友先别急着改功能把日志能力和模块加载路径摸清楚这比多看十篇架构文章更能帮你避开莫名其妙的黑匣子。希望帮到你。本文还有配套的精品资源点击获取