系统架构及缓存池
首先要看一下《HiMPP 媒体处理软件开发参考.pdf》的第一章和第二章,对于基本的框架要有概念
概述
海思提供的媒体处理软件平台(Media Process Platform,简称 MPP),可支持应用软件快速开发。该平台对应用软件屏蔽了芯片相关的复杂的底层处理,并对应用软件直接提供
MPI(MPP Programe Interface)接口完成相应功能。该平台支持应用软件快速开发以下功能:
- 输入视频捕获
- H.264/MJPEG/JPEG/MPEG4 编码
- H264/H.265/VC1/MPEG4/MPEG2/AVS 解码
- 视频输出显示
- 视频图像前处理(包括去噪、增强、锐化、Deinterlace)
- 编码码流叠加 OSD
- 视频侦测分析
- 智能分析、音频捕获及输出
- 音频编解码
系统架构
- 硬件层:硬件层由 Hi35xx 芯片加上必要的外围器件构成。外围器件包括 Flash、DDR(Double Data-Rate)、视频 Sensor 或 AD、音频 AD 等。
- 操作系统层:基于 linux OS 系统
- 媒体处理平台:基于操作系统层,控制芯片完成相应的媒体处理功能。它对应用层屏蔽了硬件处理细节,并为应用层提供 API 接口完成相应功能
- 其他驱动:除媒体处理平台外,海思为 Hi35xx 芯片的其他相关硬件处理单元提供了相应的驱动, 包括 GMAC、SDIO、I2C、USB、SSP 等驱动
- 应用层:基于海思媒体处理平台及其他驱动,由用户开发的应用软件系统
MPP架构
- VI 模块:捕获视频图像,可对其做剪切、缩放、镜像等处理,并输出多路不同分辨率的图像数据。
- VDEC解码模块:对编码后的视频码流进行解码,并将解析后的图像数据送 VPSS 进行图像处理或直接送 VO 显示。可对 H.264/H.265/VC1/MPEG4/MPEG2/AVS 格式的视频码流进行解码。
- VPSS 模块:接收 VI 和解码模块发送过来的图像,可对图像进行去噪、图像增强、锐化等处理,并实现同源输出多路不同分辨率的图像数据用于编码、预览或抓拍。
- VENC编码模块:接收 VI 捕获并经 VPSS 处理后输出的图像数据,可叠加用户通过 Region模块设置的 OSD 图像,然后按不同协议进行编码并输出相应码流。
- VDA 模块:接收 VI 的输出图像,并进行移动侦测和遮挡侦测,最后输出侦测分析结果。
- VO 模块:接收 VPSS 处理后的输出图像,可进行播放控制等处理,最后按用户配置的输出协议输出给外围视频设备。
- AI 模块:捕获音频数据,然后 AENC 模块支持按多种音频协议对其进行编码,最后输出音频码流。
用户从网络或外围存储设备获取的音频码流可直接送给 ADEC 模块,ADEC 支持解码多种不同的音频格式码流,解码后数据送给 AO 模块即可播放声音。
应用程序启动 MPP 业务前,必须完成 MPP 系统初始化工作。同理,应用程序退出MPP 业务后,也要完成 MPP 系统去初始化工作,释放资源
视频缓存池
视频缓存池主要向媒体业务提供大块物理内存管理功能,负责内存的分配和回收,充分发挥内存缓存池的作用,让物理内存资源在各个媒体处理模块中合理使用。
一组大小相同、物理地址连续的缓存块组成一个视频缓存池。必须在系统初始化之前配置公共视频缓存池。根据业务的不同,公共缓存池的数量、缓存块的大小和数量不同
所有的视频输入通道都可以从公共视频缓存池中获取视频缓存块用于保存采集的图像,如图中所示从公共视频缓存池 B 中获取视频缓存块 Bm。缓存块 Bm 经 VI 发送给 VPSS,输入缓存块 Bm 经过 VPSS 处理之后被释放回公共视频缓存池。假设VPSS 通道的工作模式是 USER,则 VPSS 通道 0 从公共视频缓存池 B 中获取缓存块 Bi 作为输出图像缓存 buffer 发送给 VENC,VPSS 通道 1 从公共视频缓存池 B 中获取缓存块 Bk 作为输出图像缓存 buffer 发送给 VO,Bi 经 VENC 编码完之后释放回公共视频缓存池,Bk 经 VO 显示完之后释放回公共视频缓存池
系统的去初始化
参考代码及makfile
这一部分需要用的库和头文件,在每个API中均有说明,hi_comm_sys.h、hi_comm_vb.h、mpi_sys.h、mpi_vb.h、libmpi.a,需要在代码中引入头文件,在makefile中引入库文件libmpi.a,并指定头文件的位置
除此之外,我提供的马克file中的其它静态库和其它链接库也是编译所必须的,当然你可以去找sample的makefile改一下拿过来用
glx_hdmi.c
#include <hi_comm_sys.h>
#include <hi_comm_vb.h>
#include <mpi_sys.h>
#include <mpi_vb.h>
HI_VOID hi_mpp_sys_deinit(HI_VOID)
{
HI_U32 i = 0;
HI_MPI_SYS_Exit();
for(i=0; i<VB_MAX_USER; i++)
{
HI_MPI_VB_ExitModCommPool(i);
}
for (i=0; i<VB_MAX_POOLS; i++)
{
HI_MPI_VB_DestroyPool(i);
}
HI_MPI_VB_Exit();
}
int main(int argc, char * argv [ ])
{
hi_mpp_sys_deinit();
return 0;
}
makefile
CC := arm-hisiv500-linux-gcc
SRC := glx_hdmi.c
OBJ := glx_hdmi.bin
CFLAGS := -Wall -g
INC_FLAGS := -I../include
LIBA := ../lib/libmpi.a ../lib/libjpeg.a ../lib/libVoiceEngine.a ../lib/libupvqe.a ../lib/libdnvqe.a ../lib/libhdmi.a
LIBS :=
LIB := -lpthread -ldl -lm
all:
$(CC) $(SRC) -o $(OBJ) \
$(CFLAGS) \
$(INC_FLAGS) \
$(LIBA) \
$(LIBS) \
$(LIB)
clean:
rm -f *.bin *.o
第一步对系统去初始化,以防止因上一次错误退出,部分模块没有还原到初始状态而出现问题,可以理解为将整个mpp重置
step1
去初始化 MPP 系统。除了音频的编解码通道外,所有的音频输入输出、视频输入输出、视频编码、视频叠加区域、视频侦测分析通道等都会被销毁或者禁用。
HI_S32 HI_MPI_SYS_Exit(HI_VOID);
step2
退出模块公共视频缓存池
//注意参数是一个枚举变量,所以此处应该对枚举的每一个值,都执行此函数,也就是需要一个for循环
HI_S32 HI_MPI_VB_ExitModCommPool(VB_UID_E enVbUid);
step3
销毁视频缓存池
//注意参数是一个枚举变量,所以此处应该对枚举的每一个值,都执行此函数,也就是需要一个for循环
HI_S32 HI_MPI_VB_DestroyPool(VB_POOL Pool);
step4
去初始化 MPP 视频缓存池
HI_S32 HI_MPI_VB_Exit (HI_VOID);
系统的初始化
进行工作之前,得对内存池和mpp做一个基本的初始化,具体步骤可以参考sample中的sample_vio.c
参考代码
HI_VOID hi_mpp_sys_init(HI_VOID)
{
VB_CONF_S stVbConf = {
0};
MPP_SYS_CONF_S stSysConf = {
0};
stVbConf.u32MaxPoolCnt = 64;
stVbConf.astCommPool[0].u32BlkSize = 1920*1080*2;
stVbConf.astCommPool[0].u32BlkCnt = 20;
HI_MPI_VB_SetConf(&stVbConf);
HI_MPI_VB_Init();
stSysConf.u32AlignWidth = 64;
HI_MPI_SYS_SetConf(&stSysConf);
HI_MPI_SYS_Init();
}
step1
设置 MPP 视频缓存池属性
HI_S32 HI_MPI_VB_SetConf (const VB_CONF_S *pstVbConf);
注意到传进来的是结构体指针,所以需要提前声明一个结构体变量,并往里面填充初始值
//定义视频缓存池属性结构体
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];