海思3559 sample解析:venc
前言:
分析完vio的例程后瞬间信心满满,自信的去找大佬请求分配任务了,“简简单单”几个步骤,照着葫芦画瓢,调调参数,对应位置具体分析具体修改,美好光明的“大路”清晰明朗啊!

年轻人,too young too naive啊要真是这么简单,用芯片大炮打蚊子吗?
可是看起来就是很简单啊,属性挨个配置,一绑定,巴拉巴拉
那如果不绑定呢?需要自己加算法修改呢?怎么获取图像,怎么输出?假如要做个人脸识别呢?

有没有感觉到越简单的流程好像越不自由,很多都没法自己掌控,想自己随需求修改的更多,自然就没那么容易了
捂着被打肿的脸痛定思痛,一定要好好拆解一下具体步骤
一个sample不够那就再看个复杂一点点的!
sample_venc
有了之前的经验,整体流程心里还是有了点x数,就不用每次都跳来跳去挨个查看了,直接理一下代码框架吧
main 主函数SAMPLE_VENC_4K60 视频采集函数SAMPLE_COMM_SYS_GetPicSize 获取w.h参数SAMPLE_COMM_VI_GetSensorInfoSAMPLE_VENC_CheckSensorSAMPLE_COMM_VI_GetSizeBySensor 得到sensor的相关参数,得到mmp系统所需的一些参数SAMPLE_COMM_SYS_GetPicSize 得到图片的尺寸SAMPLE_VENC_VI_InitSAMPLE_VENC_SYS_Init 编码系统初始化SAMPLE_COMM_VI_GetSizeBySensor 得到sensor的相关参数,得到mmp系统所需的一些参数SAMPLE_COMM_SYS_GetPicSize 得到图片的尺寸COMMON_GetPicBufferSize 得到缓存块的大小SAMPLE_COMM_SYS_Init MMP系统初始化HI_MPI_SYS_Exit();去初始化 MPP 系统。HI_MPI_VB_Exit();去初始化 MPP 视频缓存池HI_MPI_VB_SetConfig设置 MPP 视频缓存池属性HI_MPI_VB_Init 初始化 MPP 视频缓存池HI_MPI_SYS_Init 初始化 MPP 系统。SAMPLE_COMM_VI_GetFrameRateBySensorHI_MPI_ISP_GetCtrlParam 获取 ISP 控制参数。HI_MPI_ISP_SetCtrlParam 设置 ISP 控制参数。SAMPLE_COMM_VI_StartViSAMPLE_COMM_VI_StartMIPI 初始化mipi : 设置mipi的模式。.使能mipi时钟。mipi复位。使能sensor时钟。sensor复位。设置mipi的属性。mipi解复位。.sensor解复位SAMPLE_COMM_VI_SetParam获取 VI,VPSS 的工作模式。设置 VI,VPSS 工作模式SAMPLE_COMM_VI_CreateVi设置 VI 设备与物理 PIPE 的绑定关系。 创建vi pipe。 设置 VI 通道ch属性,启用 VI 通道chSAMPLE_COMM_VI_CreateIsp 参考HiISP开发参考SAMPLE_VENC_VPSS_InitSAMPLE_COMM_VI_GetSizeBySensor 得到sensor的相关参数,得到mmp系统所需的一些参数SAMPLE_COMM_SYS_GetPicSize 得到图片的尺寸SAMPLE_COMM_VPSS_Start 创建vpss group 配置通道属性 启用通道 启用vpss groupSAMPLE_COMM_VI_Bind_VPSS 包含数据源3个参数接收者3个参数,分别是vi和vpss,调用数据源到数据接收者绑定接口HI_MPI_SYS_Bind/ start stream venc /SAMPLE_VENC_GetRcMode 获取rc模式(码率控制)SAMPLE_VENC_GetGopMode gop模式选择SAMPLE_COMM_VENC_GetGopAttr 根据gop模式配置相关属性/***encode h.265 **/SAMPLE_COMM_VENC_Start 启动编码 stream模式SAMPLE_COMM_VENC_Creat 创建venc channel根据enType选择编码格式HI_MPI_VENC_StartRecvFrame 开启编码通道接收输入图像。允许指定接收帧数,超出指定的帧数后自动停止接收图像SAMPLE_COMM_VPSS_Bind_VENC 数据源到数据接收者绑定接口。HI_MPI_SYS_Bind 数据源到数据接收者绑定接口。/***encode h.264 **/SAMPLE_COMM_VENC_StartSAMPLE_COMM_VENC_Creat 创建venc channel根据enType选择编码格式HI_MPI_VENC_StartRecvFrame 开启编码通道接收输入图像。允许指定接收帧数,超出指定的帧数后自动停止接收图像SAMPLE_COMM_VPSS_Bind_VENCHI_MPI_SYS_Bind 数据源到数据接收者绑定接口。/*stream save process*/SAMPLE_COMM_VENC_StartGetStream 创建线程用以开启编码流SAMPLE_COMM_VENC_GetVencStreamProcstep 1: 检查并准备保存文件和venc fd/* 确定视频流文件名,并打开文件以保存视频流*//* 设置 Venc Fd. */step 2: 开始获取每个通道的视频流step 2.1 : 查询每个帧流中有多少包。step 2.2 :建议同时检查u32CurPacks和u32LeftStreamFramesestep 2.3 : malloc对应的包节点数。step 2.4 : 调用mpi获取一个帧流step 2.5 : 将框架保存到文件step 2.6 : 释放视频流step 2.7 : 释放包节点step 3 :关闭保存文件SAMPLE_COMM_VENC_StopGetStream pthread_join线程
看起啦又清晰了不少,那么接下来就以此为基础,开始我们自己的平台搭建吧!
部分补充注释
PIC_SIZE_E enSize[2] = {PIC_3840x2160, PIC_1080P};
编码通道s32ChnNum=2;//一个是4k120一个是1080p30
VENC_CHN VencChn[2] = {0,1};
HI_U32 u32Profile[2] = {0,1};
PAYLOAD_TYPE_E enPayLoad[2] = {PT_H265, PT_H264}; //两种编码方式
一、获取编码的具体分辨率
二、获取sensor的信息
三、检查sensor
主要的工作内容是获取sensor的宽高,检查需要编码的视频的宽高是否大于sensor能够捕获的最大宽高。如果大于则说明编码分辨率设置错误。
四、VI的初始化
1.venc初始化
获取sensor的分辨率,并计算出需要的vb块大小(这里计算了两个,一个是sensor的分辨率,一个是1920 *1080的分辨率),并且缓存池的数量是2(stVbConf.u32MaxPoolCnt = 2;)
设置 MPP 视频缓存池属性,初始化VB,初始化sys:
HI_MPI_SYS_Exit();
HI_MPI_VB_Exit();
HI_MPI_VB_SetConfig(pstVbConfig);
HI_MPI_VB_Init();
HI_MPI_SYS_Init();
2.根据sensor的类型获取帧率u32FrameRate。
3.获取ISP的控制参数。
打开isp_dev设备节点,获取控制参数:ioctl(g_as32IspFd[ViPipe], ISP_GET_CTRL_PARAM, pstIspCtrlParam);
stIspCtrlParam.u32StatIntvl = u32FrameRate/30;
ioctl(g_as32IspFd[ViPipe], ISP_SET_CTRL_PARAM, pstIspCtrlParam);
4.start mipi
.设置mipi的模式
fd = open("/dev/hi_mipi", O_RDWR);ioctl(fd, HI_MIPI_SET_HS_MODE, LANE_DIVIDE_MODE_7);close(fd);
.使能mipi时钟
fd = open("/dev/hi_mipi", O_RDWR);ioctl(fd, HI_MIPI_ENABLE_MIPI_CLOCK, &devno);close(fd);
.reset mipi
fd = open("/dev/hi_mipi", O_RDWR);s32Ret = ioctl(fd, HI_MIPI_RESET_MIPI, &devno);close(fd);
.使能sensor时钟
fd = open("/dev/hi_mipi", O_RDWR);ioctl(fd, HI_MIPI_ENABLE_SENSOR_CLOCK, &SnsDev);close(fd);
.reset sensor
fd = open("/dev/hi_mipi", O_RDWR);s32Ret = ioctl(fd, HI_MIPI_RESET_SENSOR, &SnsDev);close(fd);
.设置mipi的属性
fd = open("/dev/hi_mipi", O_RDWR);ioctl(fd, HI_MIPI_SET_DEV_ATTR, &stcomboDevAttr);//raw图位宽,close(fd);
.mipi解复位
fd = open("/dev/hi_mipi", O_RDWR);ioctl(fd, HI_MIPI_UNRESET_MIPI, &devno);close(fd);
.sensor解复位
open("/dev/hi_mipi", O_RDWR);ioctl(fd, HI_MIPI_UNRESET_SENSOR, &SnsDev);close(fd);
5.set vi vpss的工作模式【这个模式具体细节可参考hi说明文档】
HI_MPI_SYS_SetVIVPSSMode(&stVIVPSSMode);//定义 VI 各个 PIPE 和 VPSS 各个组的工作模式。
typedef enum hiVI_VPSS_MODE_E{VI_OFFLINE_VPSS_OFFLINE = 0,VI_OFFLINE_VPSS_ONLINE,VI_ONLINE_VPSS_OFFLINE,VI_ONLINE_VPSS_ONLINE,VI_PARALLEL_VPSS_OFFLINE,VI_PARALLEL_VPSS_PARALLEL,VI_VPSS_MODE_BUTT} VI_VPSS_MODE_E;
6.创建vi和ISP
.创建VI
HI_MPI_VI_SetDevAttr(ViDev, &stViDevAttr);
HI_MPI_VI_EnableDev(ViDev);
HI_MPI_VI_SetDevBindPipe(pstViInfo->stDevInfo.ViDev, &stDevBindPipe);//vi设备和物理pipe的绑定关系
HI_MPI_VI_CreatePipe(ViPipe, &stPipeAttr);
HI_MPI_VI_SetChnAttr(ViPipe, ViChn, &stChnAttr);
HI_MPI_VI_EnableChn(ViPipe, ViChn);// vi在线VPSS在线模式、VI离线VPSS在线模式, VI并行VPSS 并行模式下,启动VI通道不生效,直接返回成功。
.创建ISP
以下的操作,每个输入工作通道都需要处理。
获得回调函数指针:sensor_register_callback
执行注册的回调函数:sensor_register_callback
sensor_register_callback
cmos_init_sensor_exp_function(&stIspRegister.stSnsExp);HI_MPI_ISP_SensorRegCallBack(ViPipe, &stSnsAttrInfo, &stIspRegister);cmos_init_ae_exp_function(&stAeRegister.stSnsExp);HI_MPI_AE_SensorRegCallBack(ViPipe, pstAeLib, &stSnsAttrInfo, &stAeRegister);cmos_init_awb_exp_function(&stAwbRegister.stSnsExp);HI_MPI_AWB_SensorRegCallBack(ViPipe, pstAwbLib, &stSnsAttrInfo, &stAwbRegister);
根据sensor的不同,注册不同的控制总线(iic或者ssp)
HI_MPI_AE_Register(IspDev, &stAeLib);HI_MPI_AWB_Register(IspDev, &stAwbLib)HI_MPI_ISP_MemInit(ViPipe);HI_MPI_ISP_SetPubAttr(ViPipe, &stPubAttr);HI_MPI_ISP_Init(ViPipe);pthread_create(&g_IspPid[*pIspDev], pstAttr, SAMPLE_COMM_ISP_Thread, (HI_VOID*)pIspDev);SAMPLE_COMM_ISP_ThreadHI_MPI_ISP_Run(IspDev);
五、VPSS初始化
1.通过sensor的型号确定分辨率
- VPSS start
HI_MPI_VPSS_CreateGrp(VpssGrp, pstVpssGrpAttr);
同一个goroup下面的4个通道都需要
HI_MPI_VPSS_SetChnAttr(VpssGrp, VpssChn, &pastVpssChnAttr[VpssChn]);
HI_MPI_VPSS_EnableChn(VpssGrp, VpssChn);
HI_MPI_VPSS_StartGrp(VpssGrp);
六、VI bind VPSS
stSrcChn.enModId = HI_ID_VI;stSrcChn.s32DevId = ViPipe;stSrcChn.s32ChnId = ViChn;stDestChn.enModId = HI_ID_VPSS;stDestChn.s32DevId = VpssGrp;stDestChn.s32ChnId = 0;HI_MPI_SYS_Bind(&stSrcChn, &stDestChn);
七、start stream venc
确定用户选择的rc模式和gop模式,并根据这些用户选择确定gop属性。
八、encode h.265 h.264
1.VENC creat
HI_MPI_VENC_CreateChn(VencChn, &stVencChnAttr);//参数中对h.264 h.265 jpeg等编码做了区分。
(1)主码流进行h.265编码
SAMPLE_COMM_VENC_Start(VencChn[0], enPayLoad[0],enSize[0], enRcMode,u32Profile[0],&stGopAttr);
SAMPLE_COMM_VPSS_Bind_VENC(VpssGrp, VpssChn[0],VencChn[0]);
(2)子码流进行h.264编码
SAMPLE_COMM_VENC_Start(VencChn[1], enPayLoad[1], enSize[1], enRcMode,u32Profile[1],&stGopAttr);
SAMPLE_COMM_VPSS_Bind_VENC(VpssGrp, VpssChn[1],VencChn[1]);
2.HI_MPI_VENC_StartRecvFrame(VencChn,&stRecvParam); 获取编码码流到码流空间中
开启编码通道接收输入图像, 允许指定接收帧数,超出指定的帧数后自动停止接收图像,具体流程如下
开启线程
(1) check & prepare save-file & venc-fd
(2) Start to get streams of each channel.
select(maxfd + 1, &read_fds, NULL, NULL, &TimeoutVal)
step 2.1 : query how many packs in one-frame stream.HI_MPI_VENC_QueryStatus(i, &stStat);step 2.2 :suggest to check both u32CurPacks and u32LeftStreamFrames at the same time,step 2.3 : malloc corresponding number of pack nodes.step 2.4 : call mpi to get one-frame streamstep 2.5 : save frame to file
snprintf(aszFileName[i],32, “stream_chn%d_%d%s”, i, u32PictureCnt[i],szFilePostfix);
pFile[i] = fopen(aszFileName[i], “wb”);
#ifndef __HuaweiLite__
直接写文件
s32Ret = SAMPLE_COMM_VENC_SaveStream(pFile[i], &stStream);for (i = 0; i < pstStream->u32PackCount; i++){fwrite(pstStream->pstPack[i].pu8Addr + pstStream->pstPack[i].u32Offset,pstStream->pstPack[i].u32Len - pstStream->pstPack[i].u32Offset, 1, pFd);fflush(pFd);}#else
直接传递物理地址
s32Ret = SAMPLE_COMM_VENC_SaveStream_PhyAddr(pFile[i], &stStreamBufInfo[i], &stStream);#endif
stSrcChn.enModId = HI_ID_VPSS;stSrcChn.s32DevId = VpssGrp;stSrcChn.s32ChnId = VpssChn;stDestChn.enModId = HI_ID_VENC;stDestChn.s32DevId = 0;stDestChn.s32ChnId = VencChn;HI_MPI_SYS_Bind(&stSrcChn, &stDestChn);// h.265 h.264 VpssChn和VencChn的值分别是0和1. VpssGrp = 0
九、stream save process
pthread_create(&gs_VencPid,0,SAMPLE_COMM_VENC_GetVencStreamProc, (HI_VOID*)&gs_stPara);SAMPLE_COMM_VENC_GetVencStreamProc// get stream from each channels and save them
十、exit process
1.杀死上面创建的获取数据流的线程
pthread_join(gs_VencPid, 0);
2.VPSS 和VENC解绑定VENC停止。要分为H264 H265,参数要求同上面绑定操作一样。
3.下面两步操作需要分为H264 H265, VencChn取值分别为0,1
HI_MPI_VENC_StopRecvFrame(VencChn);//stop recv picture
HI_MPI_VENC_DestroyChn(VencChn);// Distroy Venc Channel
4.VI和VPSS解绑定
stSrcChn.enModId = HI_ID_VI;stSrcChn.s32DevId = ViPipe;stSrcChn.s32ChnId = ViChn;stDestChn.enModId = HI_ID_VPSS;stDestChn.s32DevId = VpssGrp;stDestChn.s32ChnId = 0;HI_MPI_SYS_UnBind(&stSrcChn, &stDestChn)
5.VPSS停止
HI_MPI_VPSS_StopGrp(VpssGrp);
HI_MPI_VPSS_DisableChn(VpssGrp, VpssChn);
HI_MPI_VPSS_DestroyGrp(VpssGrp);
6.VI stop
SAMPLE_COMM_VI_DestroyIsp(pstViConfig);
SAMPLE_COMM_VI_DestroyVi(pstViConfig);
SAMPLE_COMM_VI_StopMIPI(pstViConfig);
7.sysexit
HI_MPI_SYS_Exit();
HI_MPI_VB_Exit();
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
