ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

高通Camera HAL3调试实战:camxoverridesettings配置与日志dump全攻略

高通Camera HAL3调试实战:camxoverridesettings配置与日志dump全攻略 高通Camera HAL3的调试说白了就是一场和黑屏、花屏、亮度异常、对焦失败的拉锯战。我这些年经手过的案子十有八九都卡在日志不够清楚、图像数据拿不到手这两件事上。后来把camxoverridesettings.txt这套配置吃透了才算是有了趁手的工具——改一行配置、重启一下相机进程日志立刻哗哗地出dump图也能按需抓取整个调试效率完全不一样。这篇内容我打算把这些年踩过的坑和验证过的配置全部摊开讲清楚。无论你是刚接触高通平台Camera的新手还是已经和HAL3打过几轮交道的老手只要手头有debug build的机器按这篇文章的路子走基本都能顺藤摸瓜定位到问题。1. HAL3日志为什么难抓camxoverridesettings的设计逻辑很多人第一次接触camxoverridesettings.txt下意识会把它当成类似sysprops的开关文件。实际上它的作用远不止“开个日志”——它是高通CamXCamera eXtension框架在运行期读取的配置入口决定了整个HAL3 pipeline在什么粒度、什么级别输出调试信息以及在什么条件下转储图像buffer。1.1 默认日志策略的局限高通Camera HAL3的默认日志策略非常保守。默认情况下CamX的各个模块SENSOR、CSL、ISP、Chi等只输出error级别以上的信息而且日志组的mask默认值经过了裁剪很多Perf相关的关键节点根本不会打印。这意味着出问题时用adb logcat -s CamX抓到的往往只是一两句ERROR根本看不出是sensor配置不对、ISP参数溢出还是Chi node的request queue卡住了。我自己遇到的一个典型case某项目在切换分辨率时偶发画面卡住默认日志下只能看到CHIUSECASE ERROR: request timeout完全无从下手。后来打开CamXLogGroupUsecase和CamXLogGroupChi的verbose日志才看清楚是P2Ppreview-to-preview路径上的一个buffer delay计数溢出了。1.2 camxoverridesettings的读取机制这个文件在设备上有几条固定的搜索路径按优先级排列/vendor/etc/camera/camxoverridesettings.txt/system/etc/camera/camxoverridesettings.txt/data/vendor/camera/camxoverridesettings.txt系统会按顺序查找找到第一个存在的文件就读取并停止搜索。这里有个很实用的技巧调试时把配置文件放到/data/vendor/camera/目录下可以避免刷vendor镜像。而且改动文件后不需要整机重启大部分配置项只要重启camera provider进程就能生效。文件本身的格式沿用了Android property风格每一行是一个keyvalue对用#注释。解析发生在CamX每次创建pipeline之前所以修改后强制停止并重启相机应用即可。注意不是所有配置项都支持热加载个别和sensor mode关联的配置需要把camera provider彻底杀掉重启保险起见我用完都会执行一遍adb shell pkill -f cameraserver或者stop/start vendor.camera相关的service。提示改文件前一定先备份一份原版。camxoverridesettings.txt里很多配置项名字极其相似比如EnableDumpBuffers和DumpBuffersNumFrames拼错一个字符不会报错但调试行为会完全不对。2. 日志抓取配置从入门到能定位问题的三级设置2.1 基础配置项日志组mask与日志级别CamX把日志按功能模块分成了group每个group对应一个bit配置项是# 打开所有日志组的verbose级别 CamXLogGroupAllEnableMask0xFFFFFFFF CamXLogGroupAllEnableMask0xFFFFFFFF CamXLogCategoryMask0xFFFFFFFF第一行把所有groups全部打开第二行把所有日志类别performance、error、warning、info、verbose全部打开。这样设置之后adb logcat -s CamX的输出会爆炸式增长一秒钟上千行是常有的事。这会带来什么后果呢日志太多导致串口阻塞logcat环形缓冲区瞬间被冲掉日志还没同步出来就被覆盖了。所以我的做法是分两步走先用全开mask确认问题模块是否存在打印然后立刻收窄只保留两个组。比如怀疑sensor配置问题就只开CamXLogGroupAllEnableMask0x0 CamXLogGroupSensors0xFFFFFFFF CamXLogGroupCSL0xFFFFFFFF这里0x0是清空所有分组再单独打开指定group。每个group mask的值在高通CamX头文件里都有定义常见的有Group名典型用途建议maskCamXLogGroupSensorssensor上电、模式切换、曝光计算0xFFFFFFFFCamXLogGroupISPISP tune、bayer处理、3A统计0xFFFFFFFFCamXLogGroupChiCHI override、node调用链0xFFFFFFFFCamXLogGroupUsecaserequest生命周期、pipeline状态0xFFFFFFFFCamXLogGroupCSLcamera service层buffer交互0xFFFFFFFF2.2 enablePerfLogs和sensor时序日志如果你处理的是性能问题比如预览启动慢、连拍卡顿那必须额外打开Perf日志。CamX里面有一个单独的配置项EnablePerfLogsTRUE EnablePerfLogsTRUE打开之后会输出各node的实际执行时长以及pipeline里每一段的时延分布。这个日志对定位“哪一段拖慢整体帧率”特别有用。我曾遇到预览启动延时高达1.2s的case打开PerfLogs后直接看到ISP node耗时占了700ms最后发现是sensor PLL配置在了低速档与pipeline的需求不匹配。另外sensor上电时序这类问题仅靠常规日志组也看不全需要同时打开Sensors组的verbose级别让CSL层把每次写sensor寄存器的时间都打出来。排查开机进入预览黑屏问题我一般都会同时抓adb logcat -b all -c # 清空缓冲区后立刻操作相机 adb logcat -b all -v threadtime | tee camx_log.txt adb shell dumpsys media.camera dumpsys_camera.txt注意dumpsys media.camera的输出里包含了底层打开的camera id、当前sensor mode、pipeline状态还有最近几帧的request记录——这些信息和camx日志对照着看能快速判断是HAL层的问题还是Framework层配置的session参数不匹配。2.3 抓不住问题时的终极手段提前分配环形缓冲默认的adb logcat使用的是logd的缓冲区camera大量打印时1MB左右的buffer几秒钟就填满了。这里分享一个我自己摸索出来的技巧先把logd的main buffer调大再启动相机。adb logcat -G 32M adb shell setprop log.tag.CamX VERBOSE adb shell setprop log.tag.CamX.CHIO VERBOSElogcat -G调整的是logd环形缓冲区总大小调成32M甚至64M能给抓取争取到时间。这样即使手忙脚乱地从logcat里慢慢筛也不容易丢前端的关键信息。设置prop的方式尤其适合验证“偶发问题”——先把prop设置好挂在后台复现一次问题后把日志拉出来根本不用盯着终端等输出。3. dump图像数据让HAL3把要看的帧“吐”出来日志能解决的是“哪条路径出问题”但相机调试里大量问题是要看图像内容的——黑帧是不是全0、花屏是不是行错位、偏色是不是ISP增益不对。这时候就要靠dump功能把原始buffer导出到文件再用PC上的图像工具逐帧分析。3.1 打开buffer dump开关CamX的dump功能核心配置是EnableDumpBuffersTRUE DumpBuffersNumFrames5EnableDumpBuffers是总开关DumpBuffersNumFrames表示每个stream要dump几帧。我通常设为3到5帧因为实际使用中第1帧往往是pipeline刚起流的脏数据第2帧之后才稳定。如果你只想dump某个特定节点处理的buffer比如ISP输出或者Chi node输出可以用EnableDumpBuffersTRUE DumpBuffersNumFrames5注意在部分高通的amss版本上还需要同步打开EnableDumpYuvTRUE EnableDumpRawTRUE这两个开关分别控制YUV和RAW格式的buffer是否输出。只开EnableDumpBuffers但不开这两个某些平台版本上dump出来的目录可能是空的。3.2 控制dump输出位置和命名dump出的文件默认写到/data/vendor/camera/目录下理论上不同stream的buffer会按时间戳stream名归类。实际操作中我强烈建议你先把这台设备的/data/vendor/camera/目录清空再开始复现adb shell rm -rf /data/vendor/camera/* adb shell mkdir -p /data/vendor/camera然后在复现完问题后再把整个目录拉出来adb pull /data/vendor/camera/ dump_files/dump文件的命名通常包含node类型和frame号比如node_isp_0_1952_0809_1024.raw这类格式其中前面是pipeline里的node编号后面是width和height。如果是RAW格式还会多一个bayer_pattern的标识需要根据文件后缀去猜mipi的位深和排列方式。3.3 只dump指定分辨率或指定sensor modedump全量stream的缺点很明显文件巨大拉取慢。如果是1200万像素的RAW图一帧就是20多MBdump 5帧就把磁盘塞掉几GB。所以大多数时候我们需要更精准的方式。这时有两个辅助配置项特别有用ForceSensorMode0 FullRAWCropTRUEForceSensorMode可以强制sensor工作在指定的mode这样dump出的RAW分辨率是固定的不会因为camera app切来切去导致文件尺寸对不上。FullRAWCrop控制的是dump出来的RAW是否裁切到有效区域。做Sensor矫正或者说做lens shading tuning时我一般把它设为TRUE让输出对齐sensor的有效像素区避免读图时还要处理边角空像素。如果需要按stream维度做区分高通HAL3还会读CHI override配置ChiOverrideSettingEnableDumpBufferType0x1这里0x1是bit mask不同的bit对应预览、拍照、视频等不同的stream类型。比如只想dump preview stream就把bit0置为1只想dump snapshot就把bit1置为1。这个配置在不同高通平台如SM8250、SM8450、SM8550上写法略有差异建议先查对应平台CamX版本的chicommon.h确认bit定义。4. dump文件怎么分析和定位问题拿到raw/yuv文件只是第一步接下来要把像素数据“读出来”。这一步很多新手会卡住因为dump出的yuv文件是NV12或NV21格式而不是直接可以打开的jpg。我推荐一套自己的分析流程全程不需要装复杂的图形界面工具。4.1 用Python快速做RAW转可视图像以最常见的RAW10MIPI RAW10为例dump出的文件每个像素点实际只占10bit存储时被打包成4个像素用5个字节的方式。读取时直接按位拆包即可。我自己常用的脚本类似这样import numpy as np import sys from PIL import Image def read_raw10_to_16bit(path, width, height): # RAW10 packed: 每4个像素用5字节存储 pixel_count width * height byte_count (pixel_count * 5 3) // 4 raw np.fromfile(path, dtypenp.uint8, countbyte_count) # 拆包5字节 - 4个10bit像素 raw16 np.zeros(pixel_count, dtypenp.uint16) reshaped raw[:pixel_count // 4 * 5].reshape(-1, 5) b0 reshaped[:, 0].astype(np.uint16) b1 reshaped[:, 1].astype(np.uint16) b2 reshaped[:, 2].astype(np.uint16) b3 reshaped[:, 3].astype(np.uint16) b4 reshaped[:, 4].astype(np.uint16) raw16[0::4] ((b0 0xFF) 2) | ((b4 0x03) 0) raw16[1::4] ((b1 0xFF) 2) | ((b4 0x0C) 2) raw16[2::4] ((b2 0xFF) 2) | ((b4 0x30) 4) raw16[3::4] ((b3 0xFF) 2) | ((b4 0xC0) 6) img raw16.reshape((height, width)) return img if __name__ __main__: path sys.argv[1] width int(sys.argv[2]) height int(sys.argv[3]) img16 read_raw10_to_16bit(path, width, height) # 转成8bit用于直观检查 img8 (img16 2).astype(np.uint8) Image.fromarray(img8).save(path .png)如果dump的是NV12 YUV数据转换更简单import numpy as np from PIL import Image def yuv420_nv12_to_rgb(path, width, height): data np.fromfile(path, dtypenp.uint8) y data[:width*height].reshape(height, width) uv data[width*height:].reshape(height//2, width//2, 2) # 这里省略色彩空间转换细节直接用PIL的fromarray # 建议直接用ffmpeg在PC上一条命令搞定 return y说实话直接写代码解析yuv到rgb挺繁琐的我在实际调试中更常用的是把dump文件拉到PC上用ffmpeg转换# yuv转jpg ffmpeg -f rawvideo -pix_fmt nv12 -s 1920x1080 -i output.yuv output.jpg # bayer raw转png需要指定bayer pattern ffmpeg -f rawvideo -pix_fmt bayer_bggr8 -s 1920x1080 -i output.raw output.pngffmpeg的rawvideo参数对调试场景足够用了一次跑一张图几秒钟出结果。4.2 从dump图和日志的“时间戳”对齐问题dump了图像文件之后一定要和camx日志做时间戳对齐。我遇到不少工程师卡在这一步image dump出来了但不知道它对应的是哪次异常。原因很简单——dump文件名的数字是pipeline的frame count而不是时间戳只有少数平台版本文件里带nanosecond时间戳。我的习惯做法是在dumpsys请求或者复现脚本里先创建一个标志性事件。比如在命令行里发起一次拍照之后紧接着在logcat里输出一行自定义标记adb log -t MY_TAG snapshot trigger at front of scene然后把logcat -v threadtime的显示时间与dump文件的修改时间adb shell stat -c %Y dump文件名对一下。这个精度虽然只有秒级但配合frame count递增关系基本能对应上。更精确的做法是用dumpsys media.camera打印出最近的frame number与dump文件名中的frame号精确对应。4.3 常见奇葩文件格式注意点高通不同版本的CamXdump文件的命名规则和container格式并不完全一致。我遇到过几种坑比较深的某些平台dump出的RAW文件头会多出128字节或者512字节的AIQ RAW header直接按像素宽度去读会整体错位。检查方法是用十六进制编辑器看文件头前几个字节正常RAW10的像素数据从0偏移开始如果文件头有ASCII字符串比如AIQ就需要跳转到偏移位置再开始解析。NV12的UV平面可能是半平面存储也可能分成U和V两个平面读取时要注意文件的字节数是否等于width*height*3/2。差了就不对。dump出来的文件分为“带padding”和“不带padding”两种尤其yuv在各平台stride不一定是width读取时要拿到当时的stride值。可以用dumpsys media.camera | grep -i stride查看当前流的stride配置。这几个坑如果不知道很有可能让调试者怀疑是数据本身有问题查了一圈下来发现是自己的解析工具不对白白浪费时间。5. 配置实战一个完整的调试会话复盘到目前为止讲的都是配置项和工具但真正的调试一定是配合具体案例走的。我拿一个实际处理过的“拍照后画面全绿”的问题把整个操作流程串一遍。5.1 操作流程从复现到定位当时的情况是某项目在暗光环境下拍照得到的照片整体偏绿。初步怀疑是ISP的AWB自动白平衡在暗光下gain偏大导致色偏但客户反馈说同款sensor在别的平台上是正常的因此定位点落在HAL层pipeline配置上。我按下面的顺序做了完整排查清理环境设置dump只抓snapshot流# camxoverridesettings.txt EnableDumpBuffersTRUE DumpBuffersNumFrames3 EnableDumpRawTRUE EnableDumpYuvTRUE ChiOverrideSettingEnableDumpBufferType0x2 CamXLogGroupAllEnableMask0x0 CamXLogGroupISP0xFFFFFFFF CamXLogGroupSensors0xFFFFFFFF保证不会因为其他stream的dump文件干扰分析这里特意把EnableDumpBufferType设为0x2只保留snapshot stream。重启相机进程adb shell pkill -f cameraserver adb shell pkill -f vndcamera打开相机拍照拍照完成后立刻抓日志adb logcat -G 32M adb logcat -b all -v threadtime camx_full.log拉出dump目录adb pull /data/vendor/camera/ ./dump/ adb shell rm -rf /data/vendor/camera/*整个操作流程看起来简单但每一步都有讲究。比如第3步重启进程很多人会图省事直接adb reboot这其实把问题带偏了——重启之后camera provider重新加载所有库日志序号的起点会变化dump的frame count也会重置不好和logcat时间对齐。5.2 本次问题定位的关键证据拉出dump的raw文件后我用ffmpeg直接转png查看。绿色通道明显偏亮而red和blue通道gain不足。把camx日志里的AWB统计打开一看grep -i awb camx_full.log | tail -30能看到在暗光下AWB算法给出的r_gain约为1.8b_gain约为2.1g_gain约为1.0理论上这个增益组合会让色温偏暖而非偏绿。但实际ISP写入的gain却是r1.0b1.0g2.0。问题就出在ISP的gain矩阵在写入时offset错了等于把绿色通道增益强制提高色偏自然就出现了。这个问题的根因其实在tuning manager配置里——某次tuning数据合入时AWB的gamma曲线和色彩校正矩阵CCM配合方式发生了冲突在低照度场景下CCM的主对角元素被改掉。这里的排查如果没有dump图像做交叉验证光看日志很难判断是sensor的问题还是ISP处理的问题。5.3 配置项的坑与排查速查表调试过程中几个配置之间的优先级关系很容易搞混。我整理了一个速查表现象检查项建议做法dump目录为空EnableDumpBuffers是否开启确认配置拼写正确并检查/data/vendor/camera权限dump只有几帧就停止DumpBuffersNumFrames值太小调大到20帧再复现日志只有ERROR没有detailCamXLogGroupAllEnableMask被清空确认GroupMask值和CategoryMask同时打开修改配置后不生效进程未重启执行pkill -f cameraserver强制重启raw文件解析出来图像错位带AIQ header或stride padding用十六进制检查文件头确认宽度为stridedump文件巨大磁盘满全局开启dump所有stream用EnableDumpBufferType限制stream类型6. 几点使用camxoverridesettings的体会把camxoverridesettings.txt用熟之后最大的感受是HAL3调试根本不需要“盲打”。日志组mask给的是定位路径的线索dump文件给的是证据两者配合起来几乎可以闭环解决所有图像质量问题。很多工程师花大量时间在写临时debug代码、编HAL、刷vendor镜像其实大部分场景下用这个文件就能完成同样的事情效率高得多。最后分享一个我个人的小习惯我会把常用配置场景写成几个模板文件放在本地比如debug_log_dump.txt、perf_log_dump.txt、trace_sensor.txt这样新项目拿来只要改一下平台差异化的字段就能直接用。调试完记得把配置恢复默认尤其是EnableDumpBuffersTRUE这种重负载的开关一定不能留在出厂的vendor分区里否则量产机拍照会卡顿、发热、耗电售后那边会被“疑难杂症”堆满。
返回列表