海思提供的媒体处理软件平台(Media Process Platform,简称 MPP),可支持应用软件快速开发。
通俗点就是海思提供的这套ko、lib、api,对内存的管理。对内部进行视频编码的dsp的封装。
这一套体系就是mpp
海思媒体处理平台的主要内部处理流程如图 1-2 所示,主要分为
视频输入( VI)
视频处理( VPSS)
视频编码(VENC)
视频解码( VDEC)
视频输出(VO)
视频侦测分析(VDA)
音频输入(AI)
音频输出(AO)
音频编码( AENC)
音频解码( ADEC)
区域管理( REGION)等模块。
主要的处理流程介绍如下:
VI 模块捕获视频图像,可对其做剪切、缩放等处理,并输出多路不同分辨率的图像数据。
解码模块对编码后的视频码流进行解码,并将解析后的图像数据送 VPSS 进行图像处理或直接送 VO 显示。可对 H264/MPEG4/MPEG2 格式的视频码流进行解码。
VPSS 模块接收 VI 和解码模块发送过来的图像,可对图像进行去噪、图像增强、锐化等处理,并实现同源输出多路不同分辨率的图像数据用于编码、预览或抓拍。
编码模块接收 VI 捕获并经 VPSS 处理后输出的图像数据,可叠加用户通过 Region模块设置的 OSD 图像,然后按不同协议进行编码并输出相应码流。
VDA 模块接收 VI 的输出图像,并进行移动侦测和遮挡侦测,最后输出侦测分析结果。
VO 模块接收 VPSS 处理后的输出图像,可进行播放控制等处理,最后按用户配置的输出协议输出给外围视频设备。
AI 模块捕获音频数据,然后 AENC 模块支持按多种音频协议对其进行编码,最后输出音频码流。
用户从网络或外围存储设备获取的音频码流可直接送给 ADEC 模块, ADEC 支持解码多种不同的音频格式码流,解码后数据送给 AO 模块即可播放声音。
视频缓存池主要向媒体业务提供大块物理内存管理功能,负责内存的分配和回收,充分发挥内存缓存池的作用,让物理内存资源在各个媒体处理模块中合理使用。
一组大小相同、物理地址连续的缓存块组成一个视频缓存池。
视频输入通道需要使用公共视频缓存池。所有的视频输入通道都可以从公共视频缓存池中获取视频缓存块用于保存采集的图像(如图 2-1 中所示从公共视频缓存池 A 中获取视频缓存块 Bm)。由于视频输入通道不提供创建和销毁公共视频缓存池功能,因此,在系统初始化之前,必须为视频输入通道配置公共视频缓存池。根据业务的不同,公共缓存池的数量、缓存块的大小和数量不同。 图 2-1 中所示缓存块的生存期是指经过 VPSS 通道传给后续模块的情形( 图 2-1 实线径)。如果该缓存块完全没有经过 VPSS 通道传给其他模块,则将在 VPSS 模块处理后被放回公共缓存池( 图 2-1 虚线路径)。
其中
typedef struct hiVB_CONF_S { HI_U32 u32MaxPoolCnt; /* max count of pools, (0,VB_MAX_POOLS] */ struct hiVB_CPOOL_S { HI_U32 u32BlkSize; HI_U32 u32BlkCnt; HI_CHAR acMmzName[MAX_MMZ_NAME_LEN]; }astCommPool[VB_MAX_COMM_POOLS]; } VB_CONF_S;
这个结构体是一些公共缓冲池的属性
由于板载DDR是64M,所以视频缓冲池的大小是有限的
结构体中的结构体就是每个缓冲池中块的大小
海思SDK中给的相关函数是
VB_CONF_S
HI_MPI_VB_SetConf
HI_MPI_VB_Init
需要按照海思文档中说明的顺序进行操作
从main函数开始
int main(int argc, char *argv[]) { HI_S32 s32Ret; if ( (argc < 2) || (1 != strlen(argv[1])))//如果传参用法不对 { SAMPLE_VENC_Usage(argv[0]);//打印用法函数 return HI_FAILURE; } signal(SIGINT, SAMPLE_VENC_HandleSig); signal(SIGTERM, SAMPLE_VENC_HandleSig); switch (*argv[1]) { //H264 编码 1080分辨率 30帧/秒 case '0':/* H.264@1080p@30fps+H.265@1080p@30fps+H.264@D1@30fps */ s32Ret = SAMPLE_VENC_1080P_CLASSIC(); break; case '1':/* 1*1080p mjpeg encode + 1*1080p jpeg */ s32Ret = SAMPLE_VENC_1080P_MJPEG_JPEG(); break; case '2':/* low delay */ s32Ret = SAMPLE_VENC_LOW_DELAY(); break; case '3':/* roibg framerate */ s32Ret = SAMPLE_VENC_ROIBG_CLASSIC(); break; case '4':/* Thumbnail of 1*1080p jpeg */ s32Ret = SAMPLE_VENC_1080P_JPEG_Thumb(); break; #ifndef hi3518ev201 case '5':/* H.264 Svc-t */ s32Ret = SAMPLE_VENC_SVC_H264(); break; #endif default: printf("the index is invaild!\n"); SAMPLE_VENC_Usage(argv[0]); return HI_FAILURE; } if (HI_SUCCESS == s32Ret) printf("program exit normally!\n"); else printf("program exit abnormally!\n"); exit(s32Ret); }
为了保证兼容性,海思给的sample中有很多版本需要的内容
我们所用的主要是input为0时候,即SAMPLE_VENC_1080P_CLASSIC
函数
/****************************************************************************** * function : H.264@1080p@30fps+H.264@VGA@30fps ******************************************************************************/ HI_S32 SAMPLE_VENC_1080P_CLASSIC(HI_VOID) { PAYLOAD_TYPE_E enPayLoad[3]= {PT_H264, PT_H264,PT_H264}; PIC_SIZE_E enSize[3] = {PIC_HD1080, PIC_VGA,PIC_QVGA}; HI_U32 u32Profile = 0; VB_CONF_S stVbConf;//视频缓存池//VB_CONF_S视频缓存池结构体 SAMPLE_VI_CONFIG_S stViConfig = {0}; VPSS_GRP VpssGrp; VPSS_CHN VpssChn; VPSS_GRP_ATTR_S stVpssGrpAttr; VPSS_CHN_ATTR_S stVpssChnAttr; VPSS_CHN_MODE_S stVpssChnMode; VENC_CHN VencChn;//编码通道 SAMPLE_RC_E enRcMode= SAMPLE_RC_CBR; HI_S32 s32ChnNum=0; HI_S32 s32Ret = HI_SUCCESS; HI_U32 u32BlkSize; SIZE_S stSize; char c;
首先看上面的变量定义部分
最开始定义了一个PAYLOAD_TYPE_E
的数组,对应三个通道,每一个都是H264
格式
图像的尺寸对应三个通道,分别是1080、VGA、QVGA
有一点值得一提的是,枚举类型的结尾是BUTT
,由于枚举是从1开始按顺序每个值增加的,所以最后写一个BUTT,可以通过BUTT进行判断
在初始化分辨率和编码格式结构体数组
还会定义一个公共视频缓存池
定义完变量后,第一步就是初始化sys部分的变量(mpp系统中的变量)
系统初始化,为之前申请的变量分配内存
图像的采集,启动VI部分的dev(设备)和chn(通道)
VI之后就会传给VPSS,传给VPSS之前先要把它启动起来
vi bind vpss:VI和VPSS是两个独立的模块.通过bind这操作,MPP系统提供的一个API(bind api),
通过调用bind api就可以把VI和VPSS这两个模块绑定起来。绑定起来之后有什么作用呢?
VI这边当它采集到一帧图像丢到一个VB(VIDEO BUFFER视频缓冲池)里面的缓存块里面之后,这个缓存块会自动传送到VPSS里面去。
VPSS处理完之后就会到VENC里面去。这一步就开始启动VENC单元,如果你要添加水印信息就在这一步去添加。
H.264编码也在这一步去研究。编码完之后呢就得到H.264的一个码流了。
我们得到H.264码流之后怎么处理呢?
-(1)可以把这个码流打包成一个MP4存储到硬盘里面去,这就是录像。
-(2)也可以分包,分成一个一个视频包通过RTSP传出去。
-(3)也可以像sample一样直接作为一个裸流直接丢到流文件里面去。
那么这种流文件必须通过像VLC这种能够解析裸流文件的播放器来观看。
所以说编码模块只负责输出一段H.264的裸流,这个裸流要怎么办是你的事。
如果不想录了之后就按两次回车跑到SAMPLE_COMM_VENC_StopGetStream();里面去
然后进行一些列的释放内存、解除绑定的操作