Android Camera性能Debug

一.相机性能场景

本质上,相机性能主要从以下三点入手:
1.代码架构合理
比如尝试并行化处理耗时较久的操作,用户体验合理(比如拍照完可立即进入图库查看,虽然显示一张模糊的图正在处理,过一会才处理完显示变清楚,但用户体验要比等待算法处理完才可进图库要好很多),动态创建资源等,有些算法库耗时,不正常耗时即算法库的处理逻辑有问题也需要库代码侧去改善.

2.CPU调度调优
若cpu高负载运行必然使得CPU温顿升高,另外还有Sensor温度随处理频繁而升高,这就会触发Thermal降频机制或者其他防止手机继续升温的控制,便会给CPU降频,而CPU大核一降频,相机拍照时间变久,预览丢帧,录制最终的视频帧率不足等性能问题就来了;而一个好的CPU调度策略,会让CPU保持大小核均匀负载,没有其他进程抢占,绑核现象,使CPU负载均匀严密,一定程度上不仅可以提高处理效率还可以维持一定的温度稳定.及CPU不被降频,调度合理最优.

3.内存管理高效
相机占用的内存较大,如果机器内存较小,且当前内存紧张,由内存不足引起的相机视频丢帧,预览卡顿等性能问题便出现了.比如连拍功能,或者高帧率防抖录制视频,对内存依赖都很大,因此便需要在内存优化上下功夫,如提前分配更大的buffer以解决一开始启动时内存突然消耗,或者降低一些算法的Buffer数量,做到合理水平,或者可以在启动相机或者进入某个高消耗内存的模式时调用系统接口去清理下后台内存.
内存如果足够紧张,最终不仅是性能有问题,相机稳定性也会收到影响,比如lmk杀了前台进程导致相机闪退(醉了),或者其他影响用户体验的问题,比如从三方调用相机,结果导致返回应用时应用重新启动了,对,就是内存不租被干掉了,看样子,杀不杀后台进程还得看系统的智慧的判断才行,比如这里用户要返回的场景就会导致用户体验极差.
不仅CPU用到的内存,GPU的内存使用也很重要,比如GPU如果内存不足导致分配GPU内存变慢同样会导致预览卡顿等性能问题.因此GPU内存把控也十分重要.
————————————————

二.相机性能debug工具及方案建议

1.dumpsys media.camera

adb shell dumpsys media.camera > camera_dumpsys.txt
有些很有用的信息:

2.systrace

参考:
手把手教你使用Systrace(一)
Android Systrace 基础知识

建议用report_html.py生成的perf.html,会有更直观的统计信息

y 轴表示调用栈,每一层都是一个函数。调用栈越深,火焰就越高,顶部就是正在执行的函数,下方都是它的父函数。
x 轴表示抽样数,如果一个函数在 x 轴占据的宽度越宽,就表示它被抽到的次数多,即执行的时间长。注意,x 轴不代表时间,而是所有的调用栈合并后,按字母顺序排列的。

火焰图就是看顶层的哪个函数占据的宽度最大。只要有”平顶”(plateaus),就表示该函数可能存在性能问题。
颜色没有特殊含义,因为火焰图表示的是 CPU 的繁忙程度,所以一般选择暖色调。

3.SimplePerf,火焰图

Simpleperf是谷歌将perf工具port到Android上的性能分析工具,它的命令行界面支持与linux-tools perf大致相同的选项,但是它还支持许多Android特有的改进。具有三个主要的功能:stat,record 和 report。

使用方法:

抓取方法

在pc端:
快速方式:
python /home/sun/work/code/j6a1-curtana/system/extras/simpleperf/scripts/run_simpleperf_on_device.py kmem record --call-graph fp -f 4000 --duration 10 -o /data/local/tmp/perf.data

完整方式:
python /home/sun/work/code/j6a1-curtana/system/extras/simpleperf/scripts/run_simpleperf_on_device.py kmem record --call-graph fp -f 4000 --duration 10 --symfs ~/work/image/curtana_in_global_symbols_20.3.13.root_10.0_96fc05f913_gdcdaf60/out/target/product/curtana/symbols/ -o /data/local/tmp/perf.data
python /home/sun/work/code/j6a1-curtana/system/extras/simpleperf/scripts/run_simpleperf_on_device.py kmem record -p pid -e kmem:kmalloc,kmem:kmem_cache_alloc --call-graph fp -f 4000 --duration 10 -o /data/local/tmp/perf.data
adb pull /data/local/tmp/perf.data ./

或者在手机端:
快速方式:
simpleperf kmem record --call-graph fp -f 4000 --duration 10 -o /data/local/tmp/perf_2.data
完整方式:
simpleperf kmem record -p pid -e kmem:kmalloc,kmem:kmem_cache_alloc --call-graph fp -f 4000 --duration 10 -o /data/local/tmp/perf_2.data

解析生成火焰图:

python /home/sun/work/code/j6a1-curtana/system/extras/simpleperf/scripts/report_html.py -i ./perf.data -o ./perf.html
或者
python /home/sun/work/code/j6a1-curtana/system/extras/simpleperf/scripts/report_sample.py --symfs ~/work/image/curtana_in_global_symbols_20.3.13.root_10.0_96fc05f913_gdcdaf60/out/target/product/curtana/symbols/  -i perf.data > out.perf

git clone https://github.com/brendangregg/FlameGraph.git
FlameGraph/stackcollapse-perf.pl out.perf > out.folded
FlameGraph/flamegraph.pl out.folded > graph.svg
解析完成后的perf.html或者graph.svg直接用谷歌浏览器打开,如下:

在这里插入图片描述

建议用report_html.py生成的perf.html,会有更直观的统计信息

y 轴表示调用栈,每一层都是一个函数。调用栈越深,火焰就越高,顶部就是正在执行的函数,下方都是它的父函数。
x 轴表示抽样数,如果一个函数在 x 轴占据的宽度越宽,就表示它被抽到的次数多,即执行的时间长。注意,x 轴不代表时间,而是所有的调用栈合并后,按字母顺序排列的。

火焰图就是看顶层的哪个函数占据的宽度最大。只要有”平顶”(plateaus),就表示该函数可能存在性能问题。
颜色没有特殊含义,因为火焰图表示的是 CPU 的繁忙程度,所以一般选择暖色调。

原文链接:https://blog.csdn.net/TaylorPotter/article/details/105474945

三、MTK Camera驱动工程师工作调试过程

Device:

1、 修改imgsensor相关(ProjectConfig.mk文件)

device/mediateksample/{platform}/ProjectConfig.mk

此文件用于将相关模块加入编译。

2、 在头文件中添加sensor id和 sensor name宏定义,sensor id在该sensor的规格书(datasheet)中搜索寄存器地址。(kd_imgsensor.h)

device/mediatek/common/kernel-headers/kd_imgsensor.h

Kernel:

1、 将sensor原厂或是模组厂提供的sensor 驱动放置到如下文件夹(具体路径需要到out路径下看平台走的是哪个路径,此路径不唯一)

kernel-4.19/drivers/misc/mediatek/imgsensor/src/common/v1_1

2、 将模组信息添加到头文件

kernel-4.19/drivers/misc/mediatek/imgsensor/inc/kd_imgsensor.h

3、 在defconfig中添加宏定义让驱动文件编译

kernel-4.19/arch/arm64/configs/xxx_debug_defconfig

kernel-4.19/arch/arm64/configs/xxx_defconfig

4、 在SensorList中添加sensor id ,sensor name与sensor driver init函数

kernel-4.19/drivers/misc/mediatek/imgsensor/src/common/v1_1/imgsensor_sensor_list.h 添加函数声明

kernel-4.19/drivers/misc/mediatek/imgsensor/src/common/v1_1/imgsensor_sensor_list.c 添加数组成员

5、 上电时序配置

kernel-4.19/drivers/misc/mediatek/imgsensor/src/common/v1_1/imgsensor_pCLKwr_seq.c

6、 供电方式配置

需要注意配置的引脚是PMIC供电还是GPIO控制,如果供电由PMIC控制,则需要在对应位置修改为IMGSENSOR_HW_ID_REGULATOR,如果是GPIO配置,则应该为IMGSENSOR_HW_ID_GPIO

Kernel-4.19/drivers/misc/mediatek/imgsensor/src/mt6853/camera_hw_mt6833/imgsensor_cfg_table.c

VCAMD:就是DVDD数字供电,主要给ISP供电,所以有些没有ISP的模组没有这个引脚。

VCAMA:就是AVDD模拟供电,主要给感光区和ADC部分供电。

VCAMIO:就是VDDIO数字IO供电,主要给I2C部分供电。

VCAMAF:对焦马达供电。

7、 dtsi相关配置

kernel-4.14/arch/arm64/boot/dts/mediatek/cust_mt6833_camera.dtsi

可以参考其他项目的设备树是如何配置的。

cam0_rst0:后主摄,rst引脚,输出低电平;对应的是pinctrl-1,引用camera_pins_cam0_rst_0这个节点

cam0_rst1:后主摄,rst引脚,输出高电平;对应的是pinctrl-2,引用camera_pins_cam0_rst_1这个节点上下一一对应

以此类推,clk和RST为GPIO供电,需要在&pio中添加子节点定义在GPIO list中搜索R_CAM1_CLK1,R_CAM1_RST1找到对应的GPIO号

RST这路电需要配置的gpio口为GPIO56,camera_pins_cam0_rst_0和camera_pins_cam0_rst_1这两个节点中配置的GPIO口为56, CLK这路电需要配置的gpio口为GPIO50

8、 DWS配置 配置修改dws文件需要使用dtc工具,在代码中如下位置:

vendor/mediatek/proprietary/tools/dct,其中包含了linux版本与windows版本工具。dws文件存放位置如下:

kernel-4.19/drivers/misc/mediatek/dws/,工具中可以配置gpio引脚信息状态,i2c总线上挂载的设备等功能

Vendor:

1、 在SensorList中添加sensor id ,sensor name与sensor driver init函数 vendor/mediatek/proprietary/custom/common/hal/imgsensor_src/sensorlist.cpp

2、 根据原理图配置MIPI PORT和MLCK

vendor/mediatek/proprietary/custom/mt6853/hal/imgsensor_src/cfg_setting_imgsensor.cpp

3、 新增metadata文件和效果参数文件

vendor/mediatek/proprietary/custom/mt6739/hal/imgsensor/ver1

vendor/mediatek/proprietary/custom/common/hal/imgsensor_metadata/sensor/xxx_mipi_raw/

vendor/mediatek/proprietary/custom/mt6739/hal/imgsensor_metadata/xxx_mipi_raw/

资料作用:

Datasheet:用于获取slave address,上下电时序和要求。在配置sensor ID和供电方式时用到。

Gpio map:用于查找各引脚对应的gpio端口号。在配置dtsi的gpio端口号时用到。

硬件原理图:用于查看硬件的信息。用于供电方式供电配置中查看引脚是gpio供电还是pmic供电,在dws配置中查看I2C通道,查看csi和mclk的通道。

模组规格图:可以查看I2C地址,电压配置信息。用于上电时序中三路电压的配置。
附录总结:

替换文件名字指令:

find 13870* |xargs -i echo mv “{}” “{}” | sed ‘s/13870/5648/2g’ | sh

替换文件内容指令:

#替换单个文件下的内容,比如将文件中的"alidata"替换为"data".
 
sed -i "s/alidata/data/g" test.txt
 
#替换某个目录下所有文件中的内容,比如将root目录下所有文件中的"wwwroot"替换为"www".
 
sed -i "s/wwwroot/www/g" 'grep -rl wwwroot /root'

读取sensor id方法:

法一、adb shell后su切换超级权限使用cat /proc/drivers/camera_info查看

法二、使用手机里mtk自带log工具抓开机log,搜索关键字进行查找。或使用adb shell cat /proc/kmsg >./log.log抓log查看。
重要文件

kernel-4.19\drivers\misc\mediatek\imgsensor:

inc->kd_imgsensor.h —–定义sensor id 和sensor name

src->common->v1(此路径不绝对)->imgsensor_hw.c—–配置camera的供电

src->{project}->camera_hw->imgsensor_cfg_table.c—–配置供电方式以及上电时序

src->common->v1(此路径不绝对)->imgsensor.c—–camera驱动模块的加载,platform总线的注册

原文链接:https://blog.csdn.net/qq_58703058/article/details/132994554

总结:

相机驱动工程师的驱动配置工作包括:

  1. 配置Device,在相关目录下修改imgsensor相关,在头文件中添加sensor id和 sensor name宏定义,sensor id在该sensor的规格书(datasheet)中搜索寄存器地址。

  2. 配置Kernel,添加模组厂提供的sensor 驱动,宏定义,sensor id ,sensor name与sensor driver init函数,上下电时序配置,设备树配置

  3. 配置Vendor,sensor id ,sensor name与sensor driver init函数,根据原理图配置MIPI PORT和MLCK, 新增metadata文件和效果参数文件,修改摄像头参数

四、 Camera 模块驱动的注册、匹配与加载过程

1.注册驱动

通过 Platform_driver_register(&g_stCAMERA_HW_Driver)把 Camera 模块驱动注册到Platform 总线上。

Platform_driver 这个结构体包含 Probe()、Remove()等函数来完成驱动的填充。

image-20240226211910889

2. 设备的注册

对 platform_device 的定义通常在 BSP 的板级文件

image-20240226215131063

image-20240226215155881

3.总线匹配

既 然 是 驱 动 Platform_device 那 对 应 的 设 备 必 然 是 挂 载 Platform 总 线 上 的Platform_device,Platform 总线是 Linux 系统提供的一种机制,不同于 I2C、I2S 等总线,它 是一种虚拟的总线。

Linux 系统为 Platform 总线定义了一个 bus_type 的实例 Platform_bus_type:(Kernel\drivers\base\platform.c)

image-20240226215605222

Platform 总线通过 platform_match 这个成员函数来确定 platform_device 与 platform_driver 如何进行匹配

image-20240226215652466

五、 Camera 驱动工作流程

image-20240226221052683

从上图可以清晰的了解到 Camera 的一个工作流程主要分为这么七步:

\1. 打开 Camera Power LDO,让 Camera 有能量保证。
\2. 打开 IIC,设置 PDN 引脚,使 Camera 退出出 Standby 模式,按照要求让 Reset 脚做一个复位动作。
\3. 读一下 sensor 的版本 ID,这样可以让你确认是否连接上你想要的 sensor。
\4. 对 Sensor 进行初始化下载最基本的参数让 Sensor 工作起来,可能包括软复位。
\5. 下载 preview 的参数,为预览动作准备。
\6. 下载 Capture 的参数,为拍照动作准备。
\7. 设置 PDN 引脚,使 Sensor 进入 Standby 模式,或者关掉 LDO 等动作,退出 Camera

我们都知道,Linux 内核是通过模块的机制来加载设备驱动的,那么接下来我们就从设备模块加载的角度来看下 Camera 工作流程的驱动代码是如何工作的。

在-alps\mediatek\custom\common\kernel\imgsensor\src\kd_sensorlist.c 中可以看到:

module_init(CAMERA_HW_i2C_init);
module_exit(CAMERA_HW_i2C_exit);

在这里 Linux 内核加载和卸载 Camera 模块。

static struct platform_driver g_stCAMERA_HW_Driver = {
    .probe = CAMERA_HW_probe,
    .remove = CAMERA_HW_remove,
    .suspend = CAMERA_HW_suspend,
    .resume = CAMERA_HW_resume,
    .driver = {
    .name = "image_sensor",
    .owner = THIS_MODULE,
}
};

Camera 模块初始化开始向总线注册驱动,在 Platform_driver 的成员函数.probe()中,通过 i2c_add_driver(&CAMERA_HW_i2c_driver)向 I2C 申请,而 CAMERA_HW_i2c_driver 这个结构体里填充的是将 Camera 作为一个字符设备在 I2C 上进行注册:

image-20240226222830162

在 RegisterCAMERA_HWCharDrv()中cdev_init(g_pCAMERA_HW_CharDrv, &g_stCAMERA_HW_fops);对设备进行初始化,并将
g_stCAMERA_HW_fops 这个文件操作函数作为上层对 Camera 设备操作的接口留给上层进行调用:

image-20240226223035213

其中成员函数 open()只是初始化一个原子变量留给系统调用。ioctl()才是整个 Camera驱动的入口:

image-20240226223109267

CAMERA_HW_Ioctl()是上层文件操作系统操作底层硬件的方法,它先对 Camera 需要的Buffer 做一个初始化,然后建立对 Cameraopen、getinfo 等操作的接口:

六、Camera 驱动添加、调试流程

1、 修改系统配置文件 ProjectConfig.mk

-alps\mediatek\config$project$\ProjectConfig.mk

image-20240226223559943

2、检查、配置供电文件:

-alps\mediatek\custom$project$\Kernel\Camera\Camera\kd_camera_hw.c
Camera 供电流程(以3M 前摄MT9V114+5M 后摄OV5647 为例):

3、添加Camera 驱动(以ov5647 为例):

  • 版权声明: 本博客所有文章除特别声明外,著作权归作者所有。转载请注明出处!
  • Copyrights © 2022-2024 lk
  • 访问人数: | 浏览次数:

请我喝杯咖啡吧~

支付宝
微信