一沙一世界 一树一菩提
分类: 嵌入式
2020-12-16 01:22:58
Hi3559AV100这货挺强的,看下图:
这只是各个处理器核,这货的最适应场景是编解码,支持H264/H265/JPEG,最大8K广播级录制,6轴防抖,最多8路sensor输入,影视级RAW输出。12nm工艺。说到这,也不知道短期内28nm以下的产品如何发展,特大嘴下台,拜登上来,不知道有啥动静。回到正题,还继续3559.我使用到的功能模块主要有VI、VDEC、VENC、VPSS、VO/WBC。
海思MPP内部处理流程图(去掉音频处理部分):
一 谈谈自己的学习海思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这个文件里有缓存申请时用到的函数,根据这些函数填充输入即可。另外一个问题就是bind的src和des设置,在海思平台上,你bind了src和des,视频流由可以src流向des了。如果你设置了src模块的属性,视频流就会按照你的属性设置参数执行,des模块从src模块得到的就是你设置的src属性执行完以后的视频流。至于具体的哪些src能bind到哪些des上,查看mpp参考手册,很详细。
2 VI模块
输入模块,这个模块挺复杂的,只是我输入端使用的不是sensor,由LT6911uxc转hdmi为mipi,6911可以转2路mipi,接3559的mipi0和mipi1,我们只使用一路输入。输入注意配置lane,lane其实就是一对差分线,1P1N。MIPI设备用的是模式7,即4个dev,分别是dev0,dev2,dev4和dev6,我们只使用dev0,分配lane0-lane3。这里需要注意,在使能或设置mipi设备时候,需要输入一个devno这样一个参数,按照模式7的话,这个devno分别是0、2、4、6。3559的lane分配情况:
模式7的lane分配,如上图中绿色,如果你外接8个sensor的话,模式设置就是0x0B模式,lane分配如上图红色描述。然后有一个HI_MIPI_SET_DEV_ATTR设置属性,结构体是combo_dev_attr_t,这个结构体是这样子的:
可见使用的lane是0-3,按照我们上面图中绿色内容分配的。然后对LT6911进reset,通过I2C控制6911启动或停止。VI模块就这么多吧。
3 VPSS模块
视频处理子系统。这个模块功能很多,我们使用比较单一,大部分都没有用到。这里有2个概念必须理解,那就是group和channel。每个group都有若干channel,分为物理channel和扩展channel。每个group的channel和其它group的channel没有任何关系,所以不同group可以有相同编号的channel。group是输入的概念,一个group必须也只能bind一个输入视频源。Channel是输出的概念。每个group的多个channel可以bind到不同输出。如一个1080p视频源bind到vpss的某一个group,然后channel0输出到venc0,编码1080p-H264-30p,channel1输出到venc1编码720p-H265-25p。注意如果设置多个输出,通道必须设置user模式,auto模式只能bind一个输出。另外在vpss还可以设置osd。Vpss不设置帧率控制,输入和输出都不管,即输入多少帧率,输出就多少帧率,帧率改变通过其它方式来处理。
Vpss的设置大概流程就是顺序:
点击(此处)折叠或打开
点击(此处)折叠或打开
VPSS就到这了。
4 VENC模块
编码模块。3559支持多路实时编码,每路独立。当然所有编码通道的编码负载之和要在3559最大编码范围内。我无聊想看看极限时,编码2路4KP60的时候,偶尔会出现闪烁,按道理说这距离3559的编码最大范围还很远。平时我们的需求都是1080和720,并且路数不是很多,所以没有深挖极限。支持H264/H265/jpeg。输入源可以是下面中的一项:
通道接收到图像之后,比较图像尺寸和编码通道尺寸:
1 如果输入图像比编码通道尺寸大,VENC 将按照编码通道尺寸大小,调用 VGS 对
源图像进行缩小,然后对缩小之后的图像进行编码。
2 如果输入图像比编码通道尺寸小,VENC 丢弃源图像。VENC 不支持放大输入图像编码。
3 如果输入图像与编码通道尺寸相当,VENC 直接接受源图像,进行编码。
也就是说如果你的输入视频源比编码通道设置的视频尺寸小的话,是没有输出的。码率和gop设置,帧存计算一类,这一块内容量比较大,我没有细究,大部分参考sample默认就可以。关于帧率控制我是先启动默认值,等编码器启动以后,再修改根据输入和输出修改帧率。
5 VDEC模块
解码模块。支持H264/H265/JPEG,3559在最大解码范围内最多支持128路解码。我使用6路rtsp+2路usb,最大输入设置的尺寸都是1080p。我没有那么多1080p的输入源,所以没做测试,但是4路1080p是肯定没有问题的。解码器输入按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网络流封装的tbn是90k,而ffmpeg的时间基是100000,所以必须把所有的视频输入的时间基统一为ffmpeg的时间基,再处理。各种视频容器封装的pts都不一样,大家自己查一查,ffmpeg里时间基转换就那么几个函数。除了转换时间基以外还需要处理下得到的第一帧的pts,比如由于给vdec提供码流的硬件刚开始可能第一帧的pts很大,而后来又正常了,那么正常的pts会比第一帧的小很多。而每一帧的pts都是相对于第一帧的偏差。这样pts可能会出现负数,所以第一帧或前几帧的pts需要特殊处理。
5.2 dec帧存申请
3559 mpp开发手册这样描述,一共有3种分配方式,我使用这一种:
代码中是这样的:
点击(此处)折叠或打开
具体的各个buffersize多大,怎么计算,hi_buffer.h中海思已经做好了。你只要传入参数即可。下图是3559启动以后,通过proc查看的结果,可以发现大小和帧存个数致。至于参数怎么确定,mpp开发手册都有,sample例子中也有。
6 VO模块
输出模块,主要有几个概念需要搞明白:视频设备,视频层,视频通道。在我的项目中,需要多个dec解码后的数据融合成一屏,然后进入venc编码,输出推流到网络或本地存储。这中间须用到VO/WBC设备。也就是说需要把vdec解码后的视频源缩小,然后分别输出到VO某一个视频层的不同通道,通道设置不同的位置,如果VO直接接hdmi的话,在显示器上就看到显示结果了,不同通道的视频内容显示在不同的位置,并且位置和大小可以动态改变。3559AV100有2个视频设备,DHD0和DHD1,最大支持输出分别为4K60p和1080P60,DHD0上的视频层有VHD0和VHD2,DHD1上的视频层有VHD1,固定分配,不能修改。对于通道,mpp手册这样描述:
SDK 将通道归属于视频层管理,一个视频层上可显示多个视频,每一个视频显示区域称为一个通道,视频被限制通道内,通道被限制在视频层内。对于一个视频层,其上面的通道都是独立的。同时,不同的视频层上的通道也是独立的。对于通道的排号上面不存在跨层的连续。
通道的缩放,你设置的通道位置和大小,会马上生效,可动态修改。通道的优先级,优先级数值大的通道显示内容覆盖优先级小的通道的显示内容,相同的优先级,后设置的通道覆盖前面设置的通道内容,当然前提是各通道设置的位置和显示范围有重叠。如果没有重叠的话,各通道会分别在视频层的不同位置。没有重叠显示就是分屏,有重叠就是画中画。
回写设备,3559上称之为WD,代码里用wbc,我用wbc,主要就是把视屏设备或视频层的数据再次进行编码或其它处理。我的项目中,就是把所有vdec的输出源bind到VO同一视频层的不同通道,然后把启动WBC,让WBC的视频流经vpss,最后vpss输出bind到venc的不同通道进行不同的编码。其实整个系统就是一个NVR设备。只是输入可以支持HDMI、USB、rtsp。这里我说下venc的帧率控制,我的数据流图如下:
Hdmi帧率假如30,usb1帧率25,ipcamera(rtsp)帧率20,解码器不管帧率,即解码后不改变输入视频源的帧率。hdmi和usb、rtsp解码后都bind到VO视频设备0的视屏层0的不同通道。设置VO帧率为60,这样不管前面的帧率如何,从VO出来的都是60,然后VO/WBC接vpss的group0,vpss不管帧率。即vpss输入和输出帧率相同。vpss不同的channel接不同的venc。venc的帧率控制需要设置源帧率和目标帧率,目标帧率就是我们要设置的帧率,但是源帧率是VO/WBC的输出帧率,尽管从数据流上看venc的上一级是vpss。VO就这么多吧。
7 proc模块
Proc模块是一个调试利器,那是相当的有用,你设置的各个模块属性,启动系统以后,在proc/umap目录下可以检查任何一个模块的工作情况。大家自己看吧。
8 关于ffmpeg超时设置
关于ffmpeg链接rtsp或者其它的协议如rtmp设置超时的问题,如果不设置超时参数的话,read_frame或者write_frame会由于网络情况出现卡死的状态。如果是rtsp,就设置stimeout,参数单位是us,其它网络连接设置io操作设置rw_timeout,参数单位也是us。
以上都是自己的一些过程和理解,有误的地方大家提出来。