以下是自己在使用hi3559av100过程的一些记录和心得理解,做笔记在此,有不正确的地方,欢迎大家提出来。

Hi3559AV100这货挺强的,应该算得上是国货精品了啊。看下图:

基于海思3559AV100的视频库开发(一)-LMLPHP
 

基于海思3559AV100的视频库开发(一)-LMLPHP

基于海思3559AV100的视频库开发(一)-LMLPHP

基于海思3559AV100的视频库开发(一)-LMLPHP

这只是各个处理器核,这货的最适应场景是编解码,支持H264/H265/JPEG,最大8K广播级录制,6轴防抖,最多8sensor输入,影视级RAW输出。12nm工艺。说到这,也不知道短期内28nm以下的产品如何发展,特大嘴下台,拜登上来,不知道有啥动静。回到正题,还继续3559.我使用到的功能模块主要有VIVDECVENCVPSSVO/WBC

基于海思3559AV100的视频库开发(一)-LMLPHP

基于海思3559AV100的视频库开发(一)-LMLPHP

海思MPP内部处理流程图(去掉音频处理部分)

基于海思3559AV100的视频库开发(一)-LMLPHP


谈谈自己的学习海思SDK的方法

   我以前从来没有接触过视频处理,一点点基础都没有,除了知道线性代数里矩阵相乘就是图像在象限里移动,反转等以外,就只仅仅有vlc看片的经验。我也是一边请教别人,一边自己搞,我的学习过程大概如下面所说:

1 仔细阅读海思的mpp开发参考手册,除了概念以外,还要仔细看各个参数与函数的详细描述,尤其是一些加粗的注意事项;

2 结合SDK里的sample,这些例子把海思编解码硬件基本都用到了,除了怎么使用硬件外,还有特别留意各个硬件模块的调用步骤;

3 尝试修改sample的代码,看效果,如果你能遇见一些问题,再想办法处理了,应该进步就很大了;

3 我是先大概浏览mpp开发参考手册,主要是对从概念和结构上理解海思平台,再阅读sample代码,第一遍看代码,先看的顶层的main里的函数流程,结合mpp手册理解不同的业务对视频流的处理过程;

4 接下来该仔细看sample了,各个硬件模块初始化的结构体都有哪些参数,从mpp手册找到对应结构体,看具体描述的意思,然后在sample做修改,理解各参数对应的具体表现,加深对视频领域各个概念的理解;

5 把理解的模块代码抠出来添加到自己的工程;
6 当然如果有高手能给予一定的指导。那你将会少走很多很多弯路;

 

各硬件模块的理解

1 系统控制

   这一块主要2个问题,第一个是缓存的申请和使用,这里其实我自己搞得也不是特别清楚,互要是参考sample里的实例,另外hi_buffer.h这个文件里有缓存申请时用到的函数,根据这些函数填充输入即可。另外一个问题就是bindsrcdes设置,在海思平台上,你bindsrcdes,视频流由可以src流向des了。如果你设置了src模块的属性,视频流就会按照你的属性设置参数执行,des模块从src模块得到的就是你设置的src属性执行完以后的视频流。至于具体的哪些srcbind到哪些des上,查看mpp参考手册,很详细。

2 VI模块

   输入模块,这个模块挺复杂的,只是我输入端使用的不是sensor,由LT6911uxchdmimipi6911可以转2mipi,接3559mipi0mipi1,我们只使用一路输入。输入注意配置lanelane其实就是一对差分线,1P1NMIPI设备用的是模式7,4dev,分别是dev0,dev2,dev4dev6,我们只使用dev0,分配lane0-lane3。这里需要注意,在使能或设置mipi设备时候,需要输入一个devno这样一个参数,按照模式7的话,这个devno分别是0246。3559的lane分配情况:

基于海思3559AV100的视频库开发(一)-LMLPHP

基于海思3559AV100的视频库开发(一)-LMLPHP

模式7lane分配,如上图中绿色,如果你外接8sensor的话,模式设置就是0x0B模式,lane分配如上图红色描述。然后有一个HI_MIPI_SET_DEV_ATTR设置属性,结构体是combo_dev_attr_t,这个结构体是这样子的:

基于海思3559AV100的视频库开发(一)-LMLPHP

可见使用的lane0-3,按照我们上面图中绿色内容分配的。然后对LT6911reset,通过I2C控制6911启动或停止。VI模块就这么多吧。

 

3 VPSS模块

   视频处理子系统。这个模块功能很多,我们使用比较单一,大部分都没有用到。这里有2个概念必须理解,那就是groupchannel。每个group都有若干channel,分为物理channel和扩展channel。每个groupchannel和其它groupchannel没有任何关系,所以不同group可以有相同编号的channelgroup是输入的概念,一个group必须也只能bind一个输入视频源。Channel是输出的概念。每个group的多个channel可以bind到不同输出。如一个1080p视频源bindvpss的某一个group,然后channel0输出到venc0,编码1080p-H264-30pchannel1输出到venc1编码720p-H265-25p。注意如果设置多个输出,通道必须设置user模式,auto模式只能bind一个输出。另外在vpss还可以设置osdVpss不设置帧率控制,输入和输出都不管,即输入多少帧率,输出就多少帧率,帧率改变通过其它方式来处理。

Vpss的设置大概流程就是顺序

点击(此处)折叠或打开

  1. HI_MPI_VPSS_CreateGrp

  2. HI_MPI_VPSS_EnableChn

  3. HI_MPI_VPSS_StartGrp
假如
我们设置2个group,每个group2channel输出:

点击(此处)折叠或打开

  1. HI_MPI_VPSS_CreateGrp(groupid0, VPSS_GRP_ATTR_S*);--->
  2. HI_MPI_VPSS_CreateGrp(groupid1, VPSS_GRP_ATTR_S*);--->
  3. HI_MPI_VPSS_EnableChn(groupid0, channelid0);--->
  4. HI_MPI_VPSS_EnableChn(groupid0, channelid1);--->
  5. HI_MPI_VPSS_EnableChn(groupid1, channelid0);--->
  6. HI_MPI_VPSS_EnableChn(groupid1, channelid1);--->
  7. HI_MPI_VPSS_StartGrp(groupid0);--->
  8. HI_MPI_VPSS_StartGrp(groupid1);

VPSS就到这了。

 

4 VENC模块

   编码模块。3559支持多路实时编码,每路独立。当然所有编码通道的编码负载之和要在3559最大编码范围内。我无聊想看看极限时,编码2路4KP60的时候,偶尔会出现闪烁,按道理说这距离3559的编码最大范围还很远。平时我们的需求都是1080和720,并且路数不是很多,所以没有深挖极限。支持H264/H265/jpeg。输入源可以是下面中的一项:

基于海思3559AV100的视频库开发(一)-LMLPHP


通道接收到图像之后,比较图像尺寸和编码通道尺寸:

1 如果输入图像比编码通道尺寸大,VENC 将按照编码通道尺寸大小,调用 VGS

源图像进行缩小,然后对缩小之后的图像进行编码。

2 如果输入图像比编码通道尺寸小,VENC 丢弃源图像。VENC 不支持放大输入图像编码。

3 如果输入图像与编码通道尺寸相当,VENC 直接接受源图像,进行编码。

   也就是说如果你的输入视频源比编码通道设置的视频尺寸小的话,是没有输出的。码率和
gop设置,帧存计算一类,这一块内容量比较大,我没有细究,大部分参考sample默认
就可以。关于帧率控制我是先启动默认值,等编码器启动以后,再修改根据输入和输出修改帧率。

 

5 VDEC模块

   解码模块。支持H264/H265/JPEG3559在最大解码范围内最多支持128路解码。我使用6rtsp+2usb,最大输入设置的尺寸都是1080p。我没有那么多1080p的输入源,所以没做测试,但是41080p是肯定没有问题的。解码器输入按frame模式输入,输出按显示序输出。我的输入都没有B帧,所以其实解码序和显示序相同。

    5.1 pts的设置。3559描述如下:

在模式 VIDEO_MODE_FRAME 下发送码流时,解码输出的图像时间戳 PTS 为发送码流接口(HI_MPI_VDEC_SendStream)中用户送入的 PTS,解码器不会更改此数值

   所以你解码时候得到一帧数据必须合适设置pts,否则你的视频会变慢或变快或者根本就不能正常播放。Usb使用V4l2来获取数据,pts直接使用uvc得到帧的pts(这块是别人写的,我没有自己琢磨)rtsp部分使用ffmpeg获取rtsp的视频frame,然后把frame送到vdec解码。rtsp网络流封装的tbn90k,而ffmpeg的时间基是100000,所以必须把所有的视频输入的时间基统一为ffmpeg的时间基,再处理。各种视频容器封装的pts都不一样,大家自己查一查,ffmpeg里时间基转换就那么几个函数。除了转换时间基以外还需要处理下得到的第一帧的pts,比如由于给vdec提供码流的硬件刚开始可能第一帧的pts很大,而后来又正常了,那么正常的pts会比第一帧的小很多。而每一帧的pts都是相对于第一帧的偏差。这样pts可能会出现负数,所以第一帧或前几帧的pts需要特殊处理。

    5.2 dec帧存申请

3559 mpp开发手册这样描述,一共有3种分配方式,我使用这一种:

基于海思3559AV100的视频库开发(一)-LMLPHP

代码中是这样的:

点击(此处)折叠或打开

  1. if(H265 == vdecType || H264 == vdecType)
  2. {
  3.     chnAttr->enMode = VIDEO_MODE_FRAME;
  4.     chnAttr->u32PicWidth = 1920;
  5.     chnAttr->u32PicHeight = 1080;
  6.     chnAttr->u32FrameBufCnt = 6;
  7. }
  8. else if (MJPEG == vdecType)
  9. {
  10.     chnAttr->enMode = VIDEO_MODE_FRAME;
  11.     chnAttr->u32PicWidth = 1920;
  12.     chnAttr->u32PicHeight = 1080;
  13.     chnAttr->u32FrameBufCnt = 3;
  14. }
  15. chnAttr->u32StreamBufSize = chnAttr->u32PicWidth * chnAttr->u32PicHeight;
  16. chnAttr->u32FrameBufSize = VDEC_GetPicBufferSize(chnAttr->enType, chnAttr->u32PicWidth,
  17. chnAttr->u32PicHeight,PIXEL_FORMAT_YVU_SEMIPLANAR_420, enBitWidth, 0);
  18. if (H265 == vdecType || H264 == vdecType)
  19. {
  20.     chnAttr->stVdecVideoAttr.u32RefFrameNum = 3;
  21.     chnAttr->stVdecVideoAttr.bTemporalMvpEnable = 1;
  22.     chnAttr->stVdecVideoAttr.u32TmvBufSize = VDEC_GetTmvBufferSize(chnAttr->enType, chnAttr->u32PicWidth, chnAttr->u32PicHeight);
  23. }

具体的各个buffersize多大,怎么计算,hi_buffer.h中海思已经做好了。你只要传入参数即可。下图是3559启动以后,通过proc查看的结果,可以发现大小和帧存个数致。至于参数怎么确定,mpp开发手册都有,sample例子中也有。

基于海思3559AV100的视频库开发(一)-LMLPHP

 

6 VO模块

   输出模块,主要有几个概念需要搞明白:视频设备,视频层,视频通道。在我的项目中,需要多个dec解码后的数据融合成一屏,然后进入venc编码,输出推流到网络或本地存储。这中间须用到VO/WBC设备。也就是说需要把vdec解码后的视频源缩小,然后分别输出到VO某一个视频层的不同通道,通道设置不同的位置,如果VO直接接hdmi的话,在显示器上就看到显示结果了,不同通道的视频内容显示在不同的位置,并且位置和大小可以动态改变。3559AV1002个视频设备,DHD0DHD1,最大支持输出分别为4K60p1080P60,DHD0上的视频层有VHD0VHD2,DHD1上的视频层有VHD1,固定分配,不能修改。对于通道,mpp手册这样描述:

SDK 将通道归属于视频层管理,一个视频层上可显示多个视频,每一个视频显示区域称为一个通道,视频被限制通道内,通道被限制在视频层内。对于一个视频层,其上面的通道都是独立的。同时,不同的视频层上的通道也是独立的。对于通道的排号上面不存在跨层的连续

通道的缩放,你设置的通道位置和大小,会马上生效,可动态修改。通道的优先级,优先级数值大的通道显示内容覆盖优先级小的通道的显示内容,相同的优先级,后设置的通道覆盖前面设置的通道内容,当然前提是各通道设置的位置和显示范围有重叠。如果没有重叠的话,各通道会分别在视频层的不同位置。没有重叠显示就是分屏,有重叠就是画中画。

   回写设备,3559上称之为WD,代码里用wbc,我用wbc,主要就是把视屏设备或视频层的数据再次进行编码或其它处理。我的项目中,就是把所有vdec的输出源bindVO同一视频层的不同通道,然后把启动WBC,让WBC的视频流经vpss,最后vpss输出bindvenc的不同通道进行不同的编码。其实整个系统就是一个NVR设备。只是输入可以支持HDMIUSBrtsp。这里我说下venc的帧率控制,我的数据流图如下:

基于海思3559AV100的视频库开发(一)-LMLPHP

Hdmi帧率假如30,usb1帧率25,ipcamera(rtsp)帧率20,解码器不管帧率,即解码后不改变输入视频源的帧率。hdmiusbrtsp解码后都bindVO视频设备0的视屏层0的不同通道。设置VO帧率为60,这样不管前面的帧率如何,从VO出来的都是60,然后VO/WBCvpssgroup0,vpss不管帧率。即vpss输入和输出帧率相同。vpss不同的channel接不同的vencvenc的帧率控制需要设置源帧率和目标帧率,目标帧率就是我们要设置的帧率,但是源帧率是VO/WBC的输出帧率,尽管从数据流上看venc的上一级是vpssVO就这么多吧。

 

7 proc模块

   Proc模块是一个调试利器,那是相当的有用,你设置的各个模块属性,启动系统以后,在proc/umap目录下可以检查任何一个模块的工作情况。大家自己看吧。

 

以上都是自己的一些过程和理解,有误的地方大家提出来。

 

 

 



12-16 23:51