Hi3518EV200进行H264编码时对VENC的基本设置
〇、文档说明
本文性质为个人学习笔记。
由于项目需要,基于Hi3518EV200平台进行H264编码,在此过程中涉及到对VENC的控制和理解,记录以备忘。
另外,如果您在学习hi3518编码h264的过程中有疑惑,希望我的博文可以帮到您:
1.Hi3518EV200实现H264视频采集的源码及流程详解(不依赖SAMPLE库)
2.从零开始进行Hi3518_SDK安装、环境搭建和Linux内核编译,并生成可供烧写的uImage
期待如今的我和未来的你,一起进步!
一、VENC(Video Encoder,视频编码模块)
VENC的相关内容,无论是网上还是手册中都已经介绍的非常详细了,但是对于一个学习的人而言,应当考虑的是如何将这些东西转化为自己的理解,而理解的最好过程就是用自己的语言讲出来。
VENC的学习,先从了解它的作用开始。下文描述、举例、现象等均在Hi3518EV200平台进行,MPP[Hi3518EV200_MPP_V1.0.4.0 B050 Release]。
视频流从sensor到输出是这样的:
VI(sensor-ISP-ViDev0-Chn[0]/Ext_Chn[1-16])–VPSS(Group-Chn[(在线)0/(离线)0-31]/Ext_Chn[4-11])–VENC(Chn[0-15],Payload[H264/H265/JPEG],RC[CBR/VBR/AVBR/FIXQP])–输出
上面这个流程涵盖了几乎所有基本配置内容,其中通道数的限制都是基于手册查找。VENC在整个编码过程中可以干的事情有:关于通道有效荷载的编码协议选择、编码码率控制。我们所有针对VENC的设置都是围绕这两方面进行。
二、VENC设置步骤
VENC部分需要完成的设置一共有三项:
1.1选择要编码的编码方式,照着该编码方式配置相应结构体;
1.2按照所选择的编码方式的不同,配置与其对应的码流控制参数结构体;
2.按照上述的配置创建VENC通道;
3.启动通道的图像接收并绑定前级模块。
VENC_CHN VencChn=0; //选择VENC通道号——0
PAYLOAD_TYPE_E enPayLoad=PT_H264; //选择编码方式——H264,更换编码要同时更换后面的设置参数
VENC_CHN_ATTR_S stVencChnAttr; //VENC通道属性参数结构体
VENC_ATTR_H264_S stH264Attr; //H264属性参数结构体
VENC_ATTR_H264_CBR_S stH264Cbr; //H264-CBR码率控制参数结构体
SIZE_S stPicSize; //图像尺寸信息变量
stPicSize.u32Width=1920; //设置图像宽度
stPicSize.u32Height=1080; //设置图像高度
HI_U32 u32Profile=0; //0: baseline; 1:MP; 2:HP;3:Svc_t
//--1--创建VENC通道
stVencChnAttr.stVeAttr.enType=enPayLoad;
if(enPayLoad==PT_H264)//在编码为H264的时候执行,换编码方式在此处
{
stH264Attr.u32MaxPicWidth=stPicSize.u32Width; //设置最大图像宽度
stH264Attr.u32MaxPicHeight=stPicSize.u32Height;//设置最大图像高度
stH264Attr.u32PicWidth=stPicSize.u32Width; //设置图像宽度
stH264Attr.u32PicHeight=stPicSize.u32Height; //设置图像高度
stH264Attr.u32BufSize=stPicSize.u32Width * stPicSize.u32Height;//流缓冲器大小
stH264Attr.u32Profile=u32Profile; /*0: baseline; 1:MP; 2:HP; 3:svc_t */
stH264Attr.bByFrame=HI_TRUE; //获取流模式是切片模式还是帧模式?
stH264Attr.u32BFrameNum=0; //0: 不支持B帧; >=1:B帧的数量
stH264Attr.u32RefNum=1; //0: 默认; >=0:参考帧数量
memcpy(&stVencChnAttr.stVeAttr.stAttrH264e, &stH264Attr, sizeof(VENC_ATTR_H264_S));
//选择编码码率控制方式——CBR
{
stVencChnAttr.stRcAttr.enRcMode=VENC_RC_MODE_H264CBR;
stH264Cbr.u32Gop =30;//图像组:I帧间间隔
stH264Cbr.u32StatTime =1; //码率统计时间
stH264Cbr.u32SrcFrmRate =30;//输入(VI)帧率
stH264Cbr.fr32DstFrmRate =30;//输出帧率
//下面的定义需要根据传感器分辨率修改
stH264Cbr.u32BitRate=1024*2;
stH264Cbr.u32FluctuateLevel=0;//平均比特率
memcpy(&stVencChnAttr.stRcAttr.stAttrH264Cbr, &stH264Cbr, sizeof(VENC_ATTR_H264_CBR_S));
}
}
s32Ret=HI_MPI_VENC_CreateChn(VencChn, &stVencChnAttr);//创建VENC通道
//--2--启动接收VENC图像
s32Ret=HI_MPI_VENC_StartRecvPic(VencChn);
//--3--绑定VPSS和VENC
MPP_CHN_S stSrcChn; //源通道设备参数结构体
MPP_CHN_S stDestChn; //目的通道设备参数结构体
stSrcChn.enModId=HI_ID_VPSS;
stSrcChn.s32DevId=0; //选择VPSS组号——0
stSrcChn.s32ChnId=0; //选择VPSS通道号——0
stDestChn.enModId=HI_ID_VENC;
stDestChn.s32DevId=0;
stDestChn.s32ChnId=0;//选择VENC通道号——0
s32Ret=HI_MPI_SYS_Bind(&stSrcChn, &stDestChn);//绑定源通道和目的通道
三、相关设置项说明
1.VENC的图像缩放
参考《HiMPP IPC V2.0 媒体处理软件开发参考》(下文称“手册”)521页,通道接收到图像之后,比较图像尺寸和编码通道尺寸:如果输入图像比编码通道尺寸大,VENC将按照编码通道尺寸大小,调用VGS对源图像进行缩小,然后对缩小之后的图像进行编码。如果输入图像比编码通道尺寸小,VENC丢弃源图像。VENC不支持放大输入图像编码。如果输入图像与编码通道尺寸相当,VENC直接接受源图像,进行编码。
也就是说,VENC模块只支持原比例编码或者缩小,但不支持放大,如果设置的编码宽高大于输入图像的宽高,则会产生错误。
VENC输出的图像宽高在stH264Attr.u32PicWidth和stH264Attr.u32PicHeight两个变量进行设置。
对于编码器宽高取值的限制在手册535页:
编码器属性最大宽高,通道宽高必须满足如下约束:
− MaxPicWidth∈[MIN_WIDTH, MAX_WIDTH]
− MaxPicHeight∈[MIN_HEIGHT, MAX_HEIGHT]
− PicWidth∈[MIN_WIDTH, MAX_WIDTH]
− PicHeight∈[MIN_HEIGHT, MAX_HEIGHT]
− 最大宽高,通道宽高必须是MIN_ALIGN的整数倍。
其中MIN_WIDTH,MAX_WIDTH,MIN_HEIGHT,MAX_HEIGHT,MIN_ALIGN分别表示编码通道支持的最小宽度,最大宽度,最小高度,最大高度,最小对齐单元(像素)。
另外,在手册774也中对宽高的属性也做了说明:
编码器属性中除通道宽高(u32PicWidth和u32PicHeight)外都是静态属性,一旦创建编码通道成功,静态属性不支持被修改,除非该通道被销毁,重新创建。
这说明在通道建立成功后,可以在程序运行过程中动态地修改编码宽、高。
2.VENC帧率控制
VENC作为编码的模块,自然有能力决定编码产生的帧率大小,对帧率的设置在码率参数结构体中进行:
stH264Cbr.u32SrcFrmRate表示从前级模块(VI或VPSS)传来的视频图像的帧率,一般我们在设置VPSS模块的时候不开启它的帧率控制,例如芯片SAMPLE例程库中进行的那样,此时从VPSS模块传来的图像帧率就是VI模块输出的图像帧率。
VI模块的帧率设置分为两部分,一是sensor向ISP注册时传入的帧率参数,这个帧率是sensor支持的帧率,是不一定能任意修改的;二是VI物理通道设置的帧率控制,这个在SAMPLE库中也没有进行设置。所以我们不妨也按照这种方法进行,即对帧率的修改放在VENC模块上。
此时传给VENC模块的源帧率就是sensor支持得到的最大值,假设是30fps,我们只需要设置目的帧率stH264Cbr.fr32DstFrmRate变量即可完成帧率控制。
但是实际的情况要是都这么简单就好了。
按照手册951页的说法,我给翻译翻译,就是说VENC的帧率控制分为两种情况,分别为“增帧”或者“减帧/不控制”。
在增帧模式下,要设置的是u32SrcFrmRate成员,假设前级传来的帧率是30fps,我想要60fps,那就应该设置u32SrcFrmRate=60,fr32DstFrmRate=60;
在减帧或者不控制的情况下,源帧率要设置成产生时间参考的实际帧率,此时要区分在线模式或者离线模式,关于离线模式和在线模式的说明可以参考手册32页。这里如果我们在VI/VPSS前级模块没有进行任何帧率控制,那么u32SrcFrmRate就是VI的帧率(sensor帧率)。目的帧率fr32DstFrmRate的设置分为两种,一是目标帧率为整数,此时fr32DstFrmRate设置为整数即可,例如25帧(每秒25帧);二是目标帧率为分数,例如每两秒25帧(每秒25/2帧),则fr32DstFrmRate=(2<<16)+25。
3.GOP设置
声明: 本节参考了https://blog.csdn.net/xiaoyida11/article/details/52852398
GOP设置由u32Gop成员确定,其目的是定义视频编码帧中的I帧间间隔,如果设置编码码流帧配置模式为多包模式(参考手册527页),那么可以发现,I帧会分为4个小包,其中第四包为数据包。下图为设置GOP为30时的编码流情况:
u32Gop成员即决定了I帧和I帧之间间隔包计数。顺便提一嘴,B帧的编码有前文程序中的stH264Attr.u32BFrameNum变量确定,参考例程,已经给关了。
如果希望I帧出现在每秒编码帧的首包,则应该将u32Gop设置为目的帧率。下图为设置u32Gop=fr32DstFrmRate=10的编码输出情况。
暂时从项目中总结的就是这么多,以上。
四、阅读注意
1.在本记录中为了简便起见,删除了所有对函数返回值s32Ret的判断,在实际程序中应该对相应执行失败的函数做适当处理。
2.本文仅为个人学习记录,如直接套用或者言语不明造成经济损失等后果作者不承担责任,如需转载,请注明原作者及出处。
————2020-1-2 @燕卫博————