前言:
分析完vio的例程后瞬间信心满满,自信的去找大佬请求分配任务了,“简简单单”几个步骤,照着葫芦画瓢,调调参数,对应位置具体分析具体修改,美好光明的“大路”清晰明朗啊!
年轻人,too young too naive啊要真是这么简单,用芯片大炮打蚊子吗?
可是看起来就是很简单啊,属性挨个配置,一绑定,巴拉巴拉
那如果不绑定呢?需要自己加算法修改呢?怎么获取图像,怎么输出?假如要做个人脸识别呢?
有没有感觉到越简单的流程好像越不自由,很多都没法自己掌控,想自己随需求修改的更多,自然就没那么容易了
捂着被打肿的脸痛定思痛,一定要好好拆解一下具体步骤
一个sample不够那就再看个复杂一点点的!
sample_venc
有了之前的经验,整体流程心里还是有了点x数,就不用每次都跳来跳去挨个查看了,直接理一下代码框架吧
main 主函数 SAMPLE_VENC_4K60 视频采集函数 SAMPLE_COMM_SYS_GetPicSize 获取w.h参数 SAMPLE_COMM_VI_GetSensorInfo SAMPLE_VENC_CheckSensor SAMPLE_COMM_VI_GetSizeBySensor 得到sensor的相关参数,得到mmp系统所需的一些参数 SAMPLE_COMM_SYS_GetPicSize 得到图片的尺寸 SAMPLE_VENC_VI_Init SAMPLE_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_GetFrameRateBySensor HI_MPI_ISP_GetCtrlParam 获取 ISP 控制参数。 HI_MPI_ISP_SetCtrlParam 设置 ISP 控制参数。 SAMPLE_COMM_VI_StartVi SAMPLE_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 通道ch SAMPLE_COMM_VI_CreateIsp 参考HiISP开发参考 SAMPLE_VENC_VPSS_Init SAMPLE_COMM_VI_GetSizeBySensor 得到sensor的相关参数,得到mmp系统所需的一些参数 SAMPLE_COMM_SYS_GetPicSize 得到图片的尺寸 SAMPLE_COMM_VPSS_Start 创建vpss group 配置通道属性 启用通道 启用vpss group SAMPLE_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_Start SAMPLE_COMM_VENC_Creat 创建venc channel根据enType选择编码格式 HI_MPI_VENC_StartRecvFrame 开启编码通道接收输入图像。允许指定接收帧数,超出指定的帧数后自动停止接收图像 SAMPLE_COMM_VPSS_Bind_VENC HI_MPI_SYS_Bind 数据源到数据接收者绑定接口。 /*stream save process*/ SAMPLE_COMM_VENC_StartGetStream 创建线程用以开启编码流 SAMPLE_COMM_VENC_GetVencStreamProc step 1: 检查并准备保存文件和venc fd /* 确定视频流文件名,并打开文件以保存视频流*/ /* 设置 Venc Fd. */ step 2: 开始获取每个通道的视频流 step 2.1 : 查询每个帧流中有多少包。 step 2.2 :建议同时检查u32CurPacks和u32LeftStreamFramese step 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_Thread HI_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 stream step 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();