概述
文章目录
- 1.整体流程设计
- 2.模块分析
- 2.1 ipcm
- 2.2 VB初始化
- 2.3 vpss dump frame
- 2.4 YUV420SP转RGB
- 2.4.1 利用IVE转换出MMZ内存图像直接送给模型运算
- 2.5 datafifo使用
- 2.5 VO显示屏配置
1.整体流程设计
2.模块分析
2.1 ipcm
2.1.1 ipcm初始化
参考sample_msg_client 和 sample_msg_server的 Media_Msg_Init(void)函数
服务重名导致添加服务失败
开始的时候直接把sample_vio_server放在liteos上运行,运行报错
[IPCMSG]:Fun:HI_IPCMSG_AddService,Line:135,Service HiMPP_MSG already exist.
HI_IPCMSG_AddService fail
提示就是这个HiMPP_MSG已经运行了,然后就去代码里面搜索这个字符串,确实./ndk/code/mediaserver/msg/msg_server.c这个服务已经创建了同样的服务,所以只能修改咯,把服务端和客户端的 服务名+端口号都修改
2.1.2 收发消息
客户端:
HI_S32 start_server_video(){
HI_S32 s32Ret=HI_FAILURE;
HI_IPCMSG_MESSAGE_S *pReq = NULL;
HI_IPCMSG_MESSAGE_S *pResp = NULL;
pReq = HI_IPCMSG_CreateMessage(IPCM_MOD_START_CAMERA, 0, NULL, 0);
HI_IPCMSG_SendSync(g_MCmsgId, pReq, &pResp, SEND_MSG_TIMEOUT);//阻塞型,等待回复消息
s32Ret = pResp->s32RetVal;//取出回复的值
// printf("jjcc s32Ret=%dn",s32Ret);
HI_IPCMSG_DestroyMessage(pReq);
HI_IPCMSG_DestroyMessage(pResp);
return s32Ret;
}
服务端:
HI_S32 MSG_Start_Camera(HI_S32 s32MsgId, HI_IPCMSG_MESSAGE_S *pstMsg)
{
printf("jjcc MSG_Start_Cameran");
HI_S32 s32Ret;
HI_IPCMSG_MESSAGE_S *respMsg;
s32Ret = SAMPLE_VIO_StartViOnlineVpssOnlineRoute();
printf("jjcc MSG_Start_Camera ret=%#xn",s32Ret);
respMsg = HI_IPCMSG_CreateRespMessage(pstMsg, s32Ret , NULL, 0);//把执行结果s32Ret 放到回复消息中
HI_IPCMSG_SendAsync(s32MsgId, respMsg, NULL);
HI_IPCMSG_DestroyMessage(respMsg);
return HI_SUCCESS;
}
2.2 VB初始化
HI_S32 SAMPLE_VIO_StartViOnlineVpssOnlineRoute(HI_VOID)
{
。。。
/*config vb*/
memset_s(&stVbConf, sizeof(VB_CONFIG_S), 0, sizeof(VB_CONFIG_S));
stVbConf.u32MaxPoolCnt = 2;
u32BlkSize = COMMON_GetPicBufferSize(stSize.u32Width, stSize.u32Height, SAMPLE_PIXEL_FORMAT, DATA_BITWIDTH_8, COMPRESS_MODE_SEG, DEFAULT_ALIGN);
stVbConf.astCommPool[0].u64BlkSize = u32BlkSize;
stVbConf.astCommPool[0].u32BlkCnt = 10;
u32BlkSize = VI_GetRawBufferSize(stSize.u32Width, stSize.u32Height, PIXEL_FORMAT_RGB_BAYER_16BPP, COMPRESS_MODE_NONE, DEFAULT_ALIGN);
stVbConf.astCommPool[1].u64BlkSize = u32BlkSize;
stVbConf.astCommPool[1].u32BlkCnt = 4;
s32Ret = SAMPLE_COMM_SYS_Init(&stVbConf);
。。。
SAMPLE_COMM_SYS_Init里面就进行了VB的初始化,这里出现了报错:
[SAMPLE_COMM_SYS_Init]-381: HI_MPI_VB_Init failed!
分析步骤:
1.看log,可以看到是HI_MPI_VB_Init这个接口返回失败,一般HI_MPI接口失败的ret值,文档都会有说明,可以打印出来,查一下什么原因。我这边当时没有保存下来,现在不知道返回值是多少了。
2.查询mpp log。在linux端的命令是 cat /dev/logmpp,但是现在是运行在liteos,所以运行cat_logmpp
这边可以看到是VB申请MMZ失败。
Huawei LiteOS # cat_logmpp
<3>[(null)] mmz malloc failed!
<0>[ vb] [Func]:create_pool [Line]:252 [Info]:[size = 3136512, cnt = 10]vb mmz alloc:(null) failed!
3.查看MMZ,MMZ分为linux和liteos两端,独立的,内存分配文件是SDK根目录的.config
CONFIG_MEM_TOTAL_SIZE="512"
CONFIG_MEM_IPCM_BASE="0x80000000"
CONFIG_MEM_PARAM_BASE="0x80100000"
CONFIG_MEM_PARAM_SIZE="0x00100000"
CONFIG_MEM_HUAWEILITE_SYS_BASE="0x80200000"
CONFIG_MEM_HUAWEILITE_SYS_SIZE="0x01d00000"
CONFIG_MEM_RES_BASE="0x81f00000"
CONFIG_MEM_RES_SIZE="0x00100000"
CONFIG_MEM_LINUX_SYS_BASE="0x82000000"
CONFIG_MEM_LINUX_SYS_SIZE="0x06000000"
CONFIG_MEM_HUAWEILITE_MMZ_BASE="0x88000000"
CONFIG_MEM_HUAWEILITE_MMZ_SIZE="0x10000000"
CONFIG_MEM_LINUX_MMZ_BASE="0x98000000"
CONFIG_MEM_LINUX_MMZ_ANONYMOUS_SIZE="0x03000000"
CONFIG_MEM_LINUX_MMZ_HIGO_SIZE="0x05000000"
可以看到CONFIG_MEM_HUAWEILITE_MMZ_SIZE=0x10000000=256M,CONFIG_MEM_LINUX_MMZ_=0x03000000+0x05000000=128M.
这么大还不够?
那我们看看板端实际MMZ大小。
linux端查看MMZ命令如下,可以看到是128M,符合配置。
/ # cat /proc/media-mem
+---ZONE: PHYS(0x9B000000, 0x9FFFFFFF), GFP=1, nBYTES=81920KB, NAME="higo_mmz"
+---ZONE: PHYS(0x98000000, 0x9AFFFFFF), GFP=0, nBYTES=49152KB, NAME="anonymous"
|-MMB: phys(0x98000000, 0x9801FFFF), kvirt=0x00000000, flags=0x00000000, length=128KB, name="TDE_MEMPOOL_MMB"
---MMZ_USE_INFO:
total size=131072KB(128MB),used=128KB(0MB + 128KB),remain=130944KB(127MB + 896KB),zone_number=2,block_number=1
liteos端查看MMZ命令如下,可以看到只有16M!!!
Huawei LiteOS # cat /proc/umap/media-mem
Huawei LiteOS # +---ZONE: PHYS(0x88000000, 0x88FFFFFF), GFP=0, nBYTES=16384KB, NAME="anonymous"
|-MMB: phys(0x88000000, 0x88007FFF), kvirt=0x88000000, flags=0x00000001, length=32KB, name="sys_scale_coef"
|-MMB: phys(0x88008000, 0x88023FFF), kvirt=0x88008000, flags=0x00000001, length=112KB, name="gdc_node_buf"
|-MMB: phys(0x88024000, 0x88024FFF), kvirt=0x88024000, flags=0x00000001, length=4KB, name="GDC int_pole_co"
|-MMB: phys(0x88025000, 0x88031FFF), kvirt=0x88025000, flags=0x00000001, length=52KB, name="VGS_NodeBuf"
---MMZ_USE_INFO:
total size=16384KB(16MB),used=200KB(0MB + 200KB),remain=16184KB(15MB + 824KB),zone_number=1,block_number=4
我这边改了几次.config,也make clean;make all全编译,还是16M。后面报问题给海思,他们给了个命令,
make osdrv_clean;make osdrv;make drv_clean;make drv;cd reference;make samplecam_clean;make samplecam
这个命令编译出来正常,256M。可能是我环境的问题,大家如果遇到这种问题,也可以试试这个命令。
4.上面分析到是vb申请MMZ内存失败,MMZ太小的时候,由于一时没法调整大MMZ,所以分析如何减小申请的MMZ内存。
由2.2最开始的SAMPLE_VIO_StartViOnlineVpssOnlineRoute函数,可以看到申请的内存就是astCommPool[0]和astCommPool[1],由于vio_app例程能在我的板子上跑起来1280720,也是基于16M的MMZ,那为什么sample_vio这边跑1280720就不行呢?
对比sample_vio和vio_app 720P时候申请的内存
vio_app 申请 13996805+1228805=7M
sample_vio 申请 139968010+18432004=20M
sample_vio超过了16M,所以失败。为什么都是720P,sample_vio申请的空间会比vio_app大?
可以看到第一个size 1399680是一样的,只需要把个数改为5就能减小。
第二个size很大,需要把数据宽度PIXEL_FORMAT_RGB_BAYER_16BPP改为PIXEL_FORMAT_RGB_BAYER_8BPP,或者改为PIXEL_FORMAT_YVU_PLANAR_420,就会变小,申请的内存就够。
2.3 vpss dump frame
由于我们需要获取一帧数据用于人脸识别,VI-VPSS-VO整个流程里面,我们选择了VPSS这个阶段获取,因为这里能进行一些图像处理,例如放大缩小,所以就用到了HI_MPI_VPSS_GetChnFrame这个函数。
调试阶段,我们需要验证获取的帧是否正常,那么我们就在liteos端需要dump下一帧,并且保存为文件,然后电脑端用工具点开查看显示是否正常。
这里我找到了一个sdk自带的tool,amp/a7_liteos/mpp/tools/vpss_chn_dump.c,里面就有SAMPLE_MISC_VpssDump函数,负责从vpss dump一帧图像,保存yuv文件。把这个文件拷贝到我们要用到地方,然后调用这个SAMPLE_MISC_VpssDump接口。
其中会有些问题:
1.保存文件的路径 /sharefs/XXX 改为 /app/sharefs/XXX
2.需要先启动linux和liteos共享目录 /sharefs,开机后linux端运行:sharefs & ,这样共享目录就建立了。
成功获取了yuv文件之后,我们在电脑端可以用工具7yuv工具打开看。
怎么会是偏绿色,首先是怀疑摄像头驱动问题,排查了一轮没查出原因,后面配好了LCD显示屏,VO输出到显示器上,画面颜色是正常的。所以VI-VPSS-VO整个流程是没问题的,所以是我dump这边出的问题。
首先怀疑是不是数据个数读取存储出错了,用到的函数是sample_yuv_8bit_dump,对照文档YUV420SP的存储格式
sample_yuv_8bit_dump的逻辑过了一遍,也没问题。问题陷入了困境。
最后是怎么解决问题的呢?看2.4
2.4 YUV420SP转RGB
既然YUV显示偏绿色,那我dump一帧后转为RGB,再存图片看看是不是还是偏绿色咯,另外我肯定要做这个转化的,因为mtcnn模型支持的输入是RGB的。然后就了解到IVE模块支持这个颜色空间转化,HI_MPI_IVE_CSC这个接口
HI_VOID YUV2RGB(VIDEO_FRAME_S *stVFrame,cv::Mat& rgb_img )
{
HI_S32 s32Ret = HI_FAILURE;
//YUV 2 RGB
IVE_DST_IMAGE_S stDstData; //转换后
IVE_SRC_IMAGE_S stSrcData; //转换前
IVE_HANDLE hIveHandle;
HI_BOOL bInstant = HI_TRUE;
HI_BOOL bFinish = HI_FALSE;
HI_BOOL bBlock = HI_TRUE;
IVE_CSC_CTRL_S stCscCtrl;
stCscCtrl.enMode = IVE_CSC_MODE_VIDEO_BT601_YUV2RGB;
HI_U32 u32Size = 0;
/* 颜色空间转换 */
stSrcData.enType = IVE_IMAGE_TYPE_YUV420SP; //广义图像的物理地址数组
stSrcData.au64PhyAddr[0] = stVFrame->u64PhyAddr[0]; //0
stSrcData.au64PhyAddr[1] = stVFrame->u64PhyAddr[1]; //
stSrcData.au64VirAddr[0] = stVFrame->u64VirAddr[0]; //0
stSrcData.au64VirAddr[1] = stVFrame->u64VirAddr[1]; //
stSrcData.au32Stride[0] = stVFrame->u32Stride[0]; //
stSrcData.au32Stride[1] = stVFrame->u32Stride[1]; //
stSrcData.u32Width = stVFrame->u32Width;//
stSrcData.u32Height = stVFrame->u32Height; //
stDstData.enType = IVE_IMAGE_TYPE_U8C3_PACKAGE;
stDstData.u32Width = stSrcData.u32Width; //高宽同stSrcData
stDstData.u32Height = stSrcData.u32Height;
stDstData.au32Stride[0] = stVFrame->u32Stride[0]; //
stDstData.au32Stride[1] = stVFrame->u32Stride[1];
stDstData.au32Stride[2] = stVFrame->u32Stride[2];
u32Size = stDstData.au32Stride[0] * stDstData.u32Height * 3;
s32Ret = HI_MPI_SYS_MmzAlloc_Cached(&stDstData.au64PhyAddr[0], (HI_VOID**)&stDstData.au64VirAddr[0], "User", HI_NULL, u32Size); //
if(HI_SUCCESS != s32Ret)
{
HI_MPI_SYS_MmzFree(stSrcData.au64PhyAddr[0], (HI_VOID**)stSrcData.au64VirAddr[0]);
return;
}
memset((HI_VOID**)stDstData.au64VirAddr[0], 0, u32Size);
s32Ret = HI_MPI_SYS_MmzFlushCache(stDstData.au64PhyAddr[0], (HI_VOID**)stDstData.au64VirAddr[0], u32Size);
if(HI_SUCCESS != s32Ret)
{
printf("HI_MPI_SYS_MmzFlushCache FAILED,s32Ret=%#xn",s32Ret);
HI_MPI_SYS_MmzFree(stDstData.au64PhyAddr[0], (HI_VOID**)stDstData.au64VirAddr[0]);
return;
}
/* 颜色空间转换 */
s32Ret = HI_MPI_IVE_CSC(&hIveHandle,&stSrcData,&stDstData,&stCscCtrl,bInstant);
if(HI_SUCCESS != s32Ret)
{
printf("HI_MPI_IVE_CSC FAILED,s32Ret=%#xn",s32Ret);
HI_MPI_SYS_MmzFree(stDstData.au64PhyAddr[0], (HI_VOID**)stDstData.au64VirAddr[0]);
return;
}
s32Ret = HI_MPI_IVE_Query(hIveHandle,&bFinish,bBlock);
while(HI_ERR_IVE_QUERY_TIMEOUT == s32Ret)
{
usleep(100);
s32Ret = HI_MPI_IVE_Query(hIveHandle,&bFinish,bBlock);
}
//=====================================================================
rgb_img.create(stVFrame->u32Height, stVFrame->u32Width, CV_8UC3);
memcpy(rgb_img.data, (unsigned char*)stDstData.au64VirAddr[0], u32Size);
HI_MPI_SYS_MmzFree(stDstData.au64PhyAddr[0], (HI_VOID**)stDstData.au64VirAddr[0]);
}
之前我不明白为什么au64PhyAddr明明是定义了[3] 有三个的,为什么只赋值了0,1。这个可以看2.2上面YUV420SP的存储结构,Y存了一组,UV合在一起存在一组。另外由于是4:2,所以总的数据长度是Y长度的3/2。
好了这个接口在liteos上运行起来又出问题了:
HI_MPI_IVE_CSC失败,返回 0xa01d8010,对照文档查询就是:系统没有初始化或者没有加载相应的模块。
再在liteos初始化的文件里面搜索IVE初始化相关的代码。
找到了,ive模块初始化接口被SVP_BIND_LITEOS控制了,这个宏就是说SVP部署在liteos端还是linux端,默认是部署在linux端的,我们识别算法也是跑在linux端的,所以,,,,liteos端没有IVE,,,所以只能把这个转化的动作放在linux端来做了。
后面打通了datafifo之后,把YUV数据传到了linux,转为了RGB之后,颜色正常!!!,再看转RGB之前的YUV,颜色也正常!!!dump的函数和liteos端一模一样,可是出来的图片颜色不一样,好吧,不关我代码的事情了。
2.4.1 利用IVE转换出MMZ内存图像直接送给模型运算
检测模型有forward(cv::Mat &mat_img)接口,Mat图片需要转化为mmz内存传给模型Blob。需要增加forward(MMZIMG *mmz_img)接口,加速检测
问题:
mmz图像检测失败。摄像头传过来的YUV图像经过IVE转化为BGR图像后,一份拷贝给Mat,一份直接把mmz指针传给mmz_img。进入forward后,分别打印mmz_img.VirAddr数据和mat_img.data数据是一样的,可是只有后者能检测到人脸,前者检测不到。
定位问题:
forward传入的参数数据是一样的,打印实际模型输入Blob不一样,那就是img——Blob出现了异常。
原因:
Mat2Blob用的不是mat.data赋值,而是mat.atcv::Vec3b(h, w)[c]方式。
由下图Mat像素存储分布看,mat.data取值是顺序逐个逐个取,也就是BGRBGRBGR(rgb_package格式)。而后者取值方式是按通道取值,是BBBGGGRRR(rgb_planar格式)。所以mmz_img图像检测失败的原因就是数据顺序不对。
解决方法:mmz_img进行像素顺序调整后送入模型forward,问题解决。
然而,利用指针逐个调整mmz_img像素顺序,耗时较大。
继续分析优化,阅读IVE文档,发现如下说明:
看来IVE是支持这两种格式的,是否YUV2RGB就能直接转换出rgb_planar呢?答案是可以的。
只要配置:
stCscCtrl.enMode=IVE_CSC_MODE_PIC_BT601_YUV2RGB
stSrcData.enType=IVE_IMAGE_TYPE_YUV420SP
stDstData.enType=IVE_IMAGE_TYPE_U8C3_PLANAR
另外要注意:
如果使用IVE_IMAGE_TYPE_U8C3_PACKAGE,则stDstData只需要配置1组地址,因为package格式输出是BGRBGRBGR,只需要一个起始指针就行了
stDstData.au64PhyAddr[0]
stDstData.au64VirAddr[0]
如果使用IVE_IMAGE_TYPE_U8C3_PLANAR,则stDstData只需要配置3组地址,因为planar格式输出是分开3通道,分别存B,G,R,需要3通道地址。
stDstData.au64PhyAddr[0],stDstData.au64PhyAddr[1],stDstData.au64PhyAddr[2]
stDstData.au64VirAddr[0],stDstData.au64VirAddr[1],stDstData.au64VirAddr[2]
否则会出现HI_MPI_IVE_CSC 0xa01d8003 参数非法的报错。
2.5 datafifo使用
2.4最后提到,需要把从VPSS获取的一帧图像,传到linux端处理,这就要用到datafifo了。
先去看官方文档:《HiSysLink API 开发参考.pdf》
了解完原理之后就看看SDK的demo代码:
amp/a7_liteos/mpp/sample/common/media_msg/client/sample_msg_venc.c
amp/a7_liteos/mpp/sample/common/media_msg/server/sample_msg_venc.c
里面就有客户端和服务端的datafifo启动,写数据,读数据逻辑。
之前出现过个问题,运行一段时间之后,客户端会报错:
==========================getOneFrame==========================
jjccmmz_userdev:ioctl_mmb_alloc: getOneFrame
jjcc pu64PhyAddr=2291351552,g_DatafifoHandle=2291351552,readLen=328
[Func]:HI_MPI_SYmmz_userdev:get_mmbinfo_safe: S_MmzAlloc_Cached [Line]:936 [Info]:System alloc mmz memory failed!
HI_MPI_SYS_MmzAlloc_Cached FAILED,s32Ret=0xffffffff
[Func]:HI_MPI_SYS_MmzFree [Line]:969 [Info]:System unmap mmz memory failed!
HI_MPI_SYS_MmzFree,s32Ret=0xffffffff
又是MMZ内存失败,通过linux端查看mmz命令:cat /proc/media-mem 发现,MMZ剩余空间逐渐变小,原来是出现了内存泄漏,后面查到是调用了HI_MPI_SYS_MmzAlloc_Cached结束后没有调用HI_MPI_SYS_MmzFree释放空间,修复了就好了。
2.5 VO显示屏配置
1.对于vio_app例程来说
屏幕初始化流程:https://www.cnblogs.com/linhaostudy/p/11077703.html
屏幕驱动路径:amp/a7_liteos/drv/extdrv/screen/st7789/libhi_ssp_st7789.a
加载屏幕驱动:reference/samplecam/modules/init/amp/liteos/Makefile
else ifeq ($(CONFIG_SCREEN_ST7789),y)
VSS_LIB += -lhi_ssp_st7789 -lhalscreen_st7789_server
.config配置屏幕类型
CONFIG_SCREEN_ST7789=y
app初始化屏幕:
reference/samplecam/app/vio/vio_main.c
VIO_LcdPreview
HI_HAL_SCREEN_Register(HI_HAL_SCREEN_IDX_0, &stHALSCREENObj)
stHALSCREENObj来自于reference/hal/screen/st7789/hal_screen_st7789.c
HI_HAL_SCREEN_OBJ_S stHALSCREENObj
HAL_SCREEN_ST7789_GetAttr
2.对于sample_viol例程
/*config vo*/
SAMPLE_COMM_VO_GetDefConfig(&stVoConfig);
stVoConfig.enDstDynamicRange = enDynamicRange;
stVoConfig.enVoIntfType = VO_INTF_LCD_6BIT;
stVoConfig.enIntfSync = VO_OUTPUT_240x320_50;
stVoConfig.enPicSize = enPicSize;
//HI_MPI_VO_SetChnRotation(stVoConfig.VoDev,0,ROTATION_90);//旋转
/*start vo*/
s32Ret = SAMPLE_COMM_VO_StartVO(&stVoConfig);
/*vpss bind vo*/
s32Ret = SAMPLE_COMM_VPSS_Bind_VO(VpssGrp, VpssChn, stVoConfig.VoDev, VoChn);
最后
以上就是清爽白昼为你收集整理的HI3559V200获取IMX458摄像头数据_(3)实例分析+问题解决1.整体流程设计2.模块分析的全部内容,希望文章能够帮你解决HI3559V200获取IMX458摄像头数据_(3)实例分析+问题解决1.整体流程设计2.模块分析所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复