Hi3518 编码 代码总结——MPP初始化

海思媒体(mmp)处理平台架构
海思媒体处理平台的主要内部处理流程如图 1-2 所示,主要分为视频输入(VI)、视频处理(VPSS)、视频编码(VENC)、视频解码(VDEC)、视频输出(VO)、视频侦测分析(VDA)、音频输入(AI)、音频输出(AO)、音频编码(AENC)、音频解码(ADEC)、区域管理(REGION)等模块。主要的处理流程介绍如下:
Hi3518 编码 代码总结——MPP初始化

VI 模块捕获视频图像,可对其做剪切、缩放等处理,并输出多路不同分辨率的图像数据。解码模块对编码后的视频码流进行解码,并将解析后的图像数据送 VPSS 进行图像处理或直接送 VO 显示。可对 H264/MPEG4/MPEG2 格式的视频码流进行解码。

VPSS 模块接收 VI 和解码模块发送过来的图像,可对图像进行去噪、图像增强、锐化等处理,并实现同源输出多路不同分辨率的图像数据用于编码、预览或抓拍。

编码模块接收 VI 捕获并经 VPSS 处理后输出的图像数据,可叠加用户通过 Region模块设置的 OSD 图像,然后按不同协议进行编码并输出相应码流。

VDA 模块接收 VI 的输出图像,并进行移动侦测和遮挡侦测,最后输出侦测分析结果。

VO 模块接收 VPSS 处理后的输出图像,可进行播放控制等处理,最后按用户配置的输出协议输出给外围视频设备。

AI 模块捕获音频数据,然后 AENC 模块支持按多种音频协议对其进行编码,最后输出音频码流。

用户从网络或外围存储设备获取的音频码流可直接送给 ADEC 模块, ADEC 支持解码多种不同的音频格式码流,解码后数据送给 AO 模块即可播放声音。

进入主函数
如果输入0

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_VENC_1080P_CLASSIC,他会输出三路不同格式的编码裸流,详细格式注释已经标明

主函数跳转进入SAMPLE_VENC_1080P_CLASSIC函数首先会初始化分辨率和编码格式结构体数组
还会定义一个公共视频缓存池

HI_S32 SAMPLE_VENC_1080P_CLASSIC(HI_VOID)
{
    PAYLOAD_TYPE_E enPayLoad[3]= {PT_H264, PT_H264,PT_H264};//en枚举编码类型 //存放各种编码格式//定义三路码流的编码类型
    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;//s结构体类型
    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;

什么是视频缓冲池
(1)视频的本质是多帧图片,图片的本质是RGB或rawRGB数据,要占用一段连续内存
(2)视频的裁剪、缩放、修正处理等各种操作,本质上就是对内存中的数据进行运算
(3)视频缓存池(VB, video buffer)就是一段很大,又被合理划分和管理的内存,用来做视频数据的暂存和运算场地
(4)公共视频缓存池的公共2字,可以理解为全局变量,也就是各个模块都能访问的一段内存
(5)看似视频缓存块在各个模块之间流转,实际上并没有内存复制,而是指针在传递
(6)视频缓存池的内存由MPP来维护,我们在系统启动时就把整个SDRAM分成了2部分:系统部分(由linux kernel来维护管理)和mpp部分(由mpp系统来维护管理) 通常分给MPP32M
(7)缓存池需要几个,每个中包含几个缓存块,每个缓存块多大,都是可以由用户程序设置好参数,然后调用MPP的相应API来向MPP申请分配的。//通常块大小对应一帧图片大小

接下一步
首先会调用SAMPLE_COMM_VI_GetSizeBySensor
结合sensor类型返回对应分辨率
AR0130对应的是720p

HI_S32 SAMPLE_COMM_VI_GetSizeBySensor(PIC_SIZE_E *penSize)
{
    HI_S32 s32Ret = HI_SUCCESS;
    SAMPLE_VI_MODE_E enMode = SENSOR_TYPE;//sensor类型,makefile中定义的

    if (!penSize)//参数检查
    {
        return HI_FAILURE;
    }
    
    switch (enMode)
    {
        case APTINA_AR0130_DC_720P_30FPS:
        case APTINA_9M034_DC_720P_30FPS:
        case SONY_IMX222_DC_720P_30FPS:
        case OMNIVISION_OV9712_DC_720P_30FPS:
        case OMNIVISION_OV9732_DC_720P_30FPS:
        case OMNIVISION_OV9750_MIPI_720P_30FPS:
        case OMNIVISION_OV9752_MIPI_720P_30FPS:
            *penSize = PIC_HD720;//720p
            break;
        case APTINA_AR0230_HISPI_1080P_30FPS:
        case SONY_IMX222_DC_1080P_30FPS:
        case PANASONIC_MN34222_MIPI_1080P_30FPS:
        case OMNIVISION_OV2718_MIPI_1080P_25FPS:
            *penSize = PIC_HD1080;
            break;

        default:
            printf("not support this sensor\n");
            break;
    }

    return s32Ret;
}

后面通过对比分辨率输出三路码流
分别是PIC_VGA,PIC_QVGA,和PIC_HD720


    /******************************************
     step  1: init sys variable 初始化sys变量,sys就是mpp系统,这部分主要是填充结构体变量
    ******************************************/
    memset(&stVbConf,0,sizeof(VB_CONF_S));//清除视频缓存池
    
	SAMPLE_COMM_VI_GetSizeBySensor(&enSize[0]);//通过sensor计算get到的图像有多大//函数执行完毕后有enSize[0]变成720p
    if (PIC_HD1080 == enSize[0])
    {
        enSize[1] = PIC_VGA;
		s32ChnNum = 2;
    }
    else if (PIC_HD720 == enSize[0])//我们走的是这里720p
    {
        enSize[1] = PIC_VGA;//640*480			
		enSize[2] = PIC_QVGA;//320*240
		s32ChnNum = 3;//三路通道
    }//输出三路码流,内容一样,分辨率大小不同
    else
    {
        printf("not support this sensor\n");
        return HI_FAILURE;
    }

得到要输出的码流后下面将进行mpp初始化
首先填入参数,他会根据刚才得到的通道数量,分别填入对应的缓存池数据

#ifdef hi3518ev201
	s32ChnNum = 1;
#endif
	printf("s32ChnNum = %d\n",s32ChnNum);

    stVbConf.u32MaxPoolCnt = 128;//最大视频缓存池数量

    /*video buffer*/
	if(s32ChnNum >= 1)//根据通道数量计算mpp结构体参数,填充视频缓存池结构体
    {
    	//u32BlkSize为一帧图像所需要的大小,也就是块大小
	    u32BlkSize = SAMPLE_COMM_SYS_CalcPicVbBlkSize(gs_enNorm,\//第一个参数为扫描方式
	                enSize[0], SAMPLE_PIXEL_FORMAT, SAMPLE_SYS_ALIGN_WIDTH);//计算块大小,//传入分辨率格式,像素格式,对齐排布,用于像素取整
	    stVbConf.astCommPool[0].u32BlkSize = u32BlkSize;//设置块大小
	    stVbConf.astCommPool[0].u32BlkCnt = g_u32BlkCnt;//设置块数量
	}
	if(s32ChnNum >= 2)
    {
	    u32BlkSize = SAMPLE_COMM_SYS_CalcPicVbBlkSize(gs_enNorm,\
	                enSize[1], SAMPLE_PIXEL_FORMAT, SAMPLE_SYS_ALIGN_WIDTH);
	    stVbConf.astCommPool[1].u32BlkSize = u32BlkSize;
	    stVbConf.astCommPool[1].u32BlkCnt =g_u32BlkCnt;
	}
	if(s32ChnNum >= 3)//3个通道对应三个视频缓存池,因为不同通道也就是不同分辨率,他们对应的码流不同
    {
		u32BlkSize = SAMPLE_COMM_SYS_CalcPicVbBlkSize(gs_enNorm,\
                enSize[2], SAMPLE_PIXEL_FORMAT, SAMPLE_SYS_ALIGN_WIDTH);
		stVbConf.astCommPool[2].u32BlkSize = u32BlkSize;
		stVbConf.astCommPool[2].u32BlkCnt = g_u32BlkCnt;
    }

首先会调用SAMPLE_COMM_SYS_CalcPicVbBlkSize,它会得到缓存块大小,然后设置好块数量,这里设置为4

HI_U32 SAMPLE_COMM_SYS_CalcPicVbBlkSize(VIDEO_NORM_E enNorm, PIC_SIZE_E enPicSize, PIXEL_FORMAT_E enPixFmt, HI_U32 u32AlignWidth)
{
    HI_S32 s32Ret = HI_FAILURE;
    SIZE_S stSize;
    HI_U32 u32VbSize;
    HI_U32 u32HeaderSize;

    s32Ret = SAMPLE_COMM_SYS_GetPicSize(enNorm, enPicSize, &stSize);//计算宽高数据//把宽高结果保存到stSize
    if (HI_SUCCESS != s32Ret)
    {
        SAMPLE_PRT("get picture size[%d] failed!\n", enPicSize);
            return HI_FAILURE;
    }

    if (PIXEL_FORMAT_YUV_SEMIPLANAR_422 != enPixFmt && PIXEL_FORMAT_YUV_SEMIPLANAR_420 != enPixFmt)//检查分辨率格式是否正常
    {
        SAMPLE_PRT("pixel format[%d] input failed!\n", enPixFmt);
            return HI_FAILURE;
    }

    if (16!=u32AlignWidth && 32!=u32AlignWidth && 64!=u32AlignWidth)//检查对其格式是否正常
    {
        SAMPLE_PRT("system align width[%d] input failed!\n",\
               u32AlignWidth);
            return HI_FAILURE;
    }
    //SAMPLE_PRT("w:%d, u32AlignWidth:%d\n", CEILING_2_POWER(stSize.u32Width,u32AlignWidth), u32AlignWidth);
    u32VbSize = (CEILING_2_POWER(stSize.u32Width, u32AlignWidth) * \//计算图片大小  宽*高*字节数//CEILING_2_POWER向上对齐假设输入15向上对齐是2的四次方  输出4
            CEILING_2_POWER(stSize.u32Height,u32AlignWidth) * \
           ((PIXEL_FORMAT_YUV_SEMIPLANAR_422 == enPixFmt)?2:1.5));//422对应一个像素占2字节   420对应一个像素1.5字节                   6/4=1.5  

    VB_PIC_HEADER_SIZE(stSize.u32Width, stSize.u32Height, enPixFmt, u32HeaderSize);//留一些余量,计算头信息大小
    u32VbSize += u32HeaderSize;//将图片大小和头信息大小相加

    return u32VbSize;
}

结构体填充完毕后
开始完成mpp初始化


    /******************************************
     step 2: mpp system init. //调用填充的变量来完成mpp初始化
    ******************************************/
    s32Ret = SAMPLE_COMM_SYS_Init(&stVbConf);//将刚才已经设置好的结构体传入函数,初始化视频缓存池
    if (HI_SUCCESS != s32Ret)
    {
        SAMPLE_PRT("system init failed with %d!\n", s32Ret);
        goto END_VENC_1080P_CLASSIC_0;
    }

它会调用

/******************************************************************************
* function : vb init & MPI system init
******************************************************************************/
HI_S32 SAMPLE_COMM_SYS_Init(VB_CONF_S *pstVbConf)
{
    MPP_SYS_CONF_S stSysConf = {0};
    HI_S32 s32Ret = HI_FAILURE;

    HI_MPI_SYS_Exit();//保险措施。释放没有释放掉的东西
    HI_MPI_VB_Exit();

    if (NULL == pstVbConf)
    {
        SAMPLE_PRT("input parameter is null, it is invaild!\n");
        return HI_FAILURE;
    }

    s32Ret = HI_MPI_VB_SetConf(pstVbConf);//将设置好的结构体传入mpp,设置 MPP 视频缓存池属性
    if (HI_SUCCESS != s32Ret)
    {
        SAMPLE_PRT("HI_MPI_VB_SetConf failed!\n");
        return HI_FAILURE;
    }

    s32Ret = HI_MPI_VB_Init();//初始化 MPP 视频缓存池
    if (HI_SUCCESS != s32Ret)
    {
        SAMPLE_PRT("HI_MPI_VB_Init failed!\n");
        return HI_FAILURE;
    }

    stSysConf.u32AlignWidth = SAMPLE_SYS_ALIGN_WIDTH;
    s32Ret = HI_MPI_SYS_SetConf(&stSysConf);//配置系统控制参数
    if (HI_SUCCESS != s32Ret)
    {
        SAMPLE_PRT("HI_MPI_SYS_SetConf failed\n");
        return HI_FAILURE;
    }

    s32Ret = HI_MPI_SYS_Init();//初始化 MPP 系统
    if (HI_SUCCESS != s32Ret)
    {
        SAMPLE_PRT("HI_MPI_SYS_Init failed!\n");
        return HI_FAILURE;
    }

    return HI_SUCCESS;
}

每个get 后面都跟着init
注意顺序不能错

上一篇:python random 库之 sample 函数


下一篇:偶数求和