以下内容源于朱有鹏嵌入式课程的学习与整理,如有侵权请告知删除。
前言
本文将详细介绍博文第二季3:sample_venc.c的整体分析中提及的“初始化MPP系统”。
MPP系统的初始化包括以下步骤:
-
配置VB:HI_MPI_VB_SetConf函数
-
初始化VB:HI_MPI_VB_Init函数
-
配置系统:HI_MPI_SYS_SetConf函数
-
初始化系统:HI_MPI_SYS_Init函数
上述函数均位于SAMPLE_COMM_SYS_Init函数中,该个函数之前的代码,都是在填充VB有关的变量。上述函数都是以库的形式提供,没有具体的源代码代码。
一、分析参数的含义
函数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;//baseline,main,highVB_CONF_S stVbConf;//与VB配置有关SAMPLE_VI_CONFIG_S stViConfig = {0};//与VI配置有关VPSS_GRP VpssGrp;VPSS_CHN VpssChn;VPSS_GRP_ATTR_S stVpssGrpAttr;//VPSS组的属性VPSS_CHN_ATTR_S stVpssChnAttr;//vpss信道的属性VPSS_CHN_MODE_S stVpssChnMode;//vpss信道工作模式的属性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、PAYLOAD_TYPE_E 枚举类型
这个枚举类型表示图像的传输格式。
/* We just coyp this value of payload type from RTP/RTSP definition */
typedef enum
{PT_PCMU = 0,PT_1016 = 1,/*……*//* add by hisilicon */PT_AMR = 1001,PT_MJPEG = 1002,PT_AMRWB = 1003,PT_BUTT
} PAYLOAD_TYPE_E;
2、PIC_SIZE_E 枚举类型
这个枚举类型表示图像的分辨率。
typedef enum hiPIC_SIZE_E
{PIC_QCIF = 0,PIC_CIF,PIC_2CIF,PIC_HD1,PIC_D1,PIC_960H,PIC_QVGA, /* 320 * 240 *///………………………………………………QVGAPIC_VGA, /* 640 * 480 *///………………………………………………VGAPIC_XGA, /* 1024 * 768 */PIC_SXGA, /* 1400 * 1050 */PIC_UXGA, /* 1600 * 1200 */PIC_QXGA, /* 2048 * 1536 */PIC_WVGA, /* 854 * 480 */PIC_WSXGA, /* 1680 * 1050 */PIC_WUXGA, /* 1920 * 1200 */PIC_WQXGA, /* 2560 * 1600 */PIC_HD720, /* 1280 * 720 *///……………………………………………HD720PIC_HD1080, /* 1920 * 1080 *///…………………………………………HD1080PIC_2304x1296, /* 3M:2304 * 1296 */PIC_2592x1520, /* 4M:2592 * 1520 */PIC_5M, /* 2592 * 1944 */PIC_UHD4K, /* 3840 * 2160 */PIC_12M, /* 4000 * 3000 */PIC_BUTT
} PIC_SIZE_E;
3、VB_CONF_S 结构体
这个结构体用于描述“视频缓冲池”的配置。具体见第二季2:视频缓存池的简介_天糊土的博客。
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;
4、 SAMPLE_VI_CONFIG_S 结构体
这个结构体用于描述VI模块的配置。
typedef struct sample_vi_config_s
{SAMPLE_VI_MODE_E enViMode;//sensor的种类VIDEO_NORM_E enNorm;//视频信号制式 /*DC: VIDEO_ENCODING_MODE_AUTO */ ROTATE_E enRotate;//图像旋转SAMPLE_VI_CHN_SET_E enViChnSet; //表示flip或者mirrorWDR_MODE_E enWDRMode;//宽动态有关
}SAMPLE_VI_CONFIG_S;
(1)其中sample_vi_mode_e用来描述摄像头传感器的种类,有着不同的分辨率和帧率。
typedef enum sample_vi_mode_e
{ APTINA_AR0130_DC_720P_30FPS = 0,APTINA_9M034_DC_720P_30FPS,APTINA_AR0230_HISPI_1080P_30FPS,SONY_IMX222_DC_1080P_30FPS,SONY_IMX222_DC_720P_30FPS,PANASONIC_MN34222_MIPI_1080P_30FPS,OMNIVISION_OV9712_DC_720P_30FPS,OMNIVISION_OV9732_DC_720P_30FPS,OMNIVISION_OV9750_MIPI_720P_30FPS,OMNIVISION_OV9752_MIPI_720P_30FPS,OMNIVISION_OV2718_MIPI_1080P_25FPS,SAMPLE_VI_MODE_1_D1,SAMPLE_VI_MODE_BT1120_720P,SAMPLE_VI_MODE_BT1120_1080P,
}SAMPLE_VI_MODE_E;
(2)其中VIDEO_NORM_E用来表示视频信号制式,比如PAL、NTSC等,具体介绍见博文PAL与NTSC制式的详解。简单来讲,像是每秒显示多少帧,每帧总的像素数量,每个像素的显示时间、显示顺序都是包含在制式所要求的格式里面的。
typedef enum hiVIDEO_NORM_E
{VIDEO_ENCODING_MODE_PAL = 0,VIDEO_ENCODING_MODE_NTSC,VIDEO_ENCODING_MODE_AUTO,VIDEO_ENCODING_MODE_BUTT
} VIDEO_NORM_E;
(3)其中ROTATE_E表示将图像旋转多少度。
typedef enum hiROTATE_E
{ROTATE_NONE = 0,ROTATE_90 = 1,ROTATE_180 = 2,ROTATE_270 = 3,ROTATE_BUTT
} ROTATE_E;
(4)其中SAMPLE_VI_CHN_SET_E表示将图像沿着水平轴翻转(flip),或者沿着纵轴翻转(mirror)。比如参见博客第4季6:图像sensor的寄存器操作所列出的图像。
typedef enum sample_vi_chn_set_e
{VI_CHN_SET_NORMAL = 0, /* mirror, flip close */VI_CHN_SET_MIRROR, /* open MIRROR */VI_CHN_SET_FLIP, /* open filp */VI_CHN_SET_FLIP_MIRROR /* mirror, flip */
}SAMPLE_VI_CHN_SET_E;
(5)其中WDR_MODE_E用来描述宽动态有关的内容。具体介绍见宽动态 (WDR)介绍和理解。
typedef enum hiWDR_MODE_E
{WDR_MODE_NONE = 0,WDR_MODE_BUILT_IN,WDR_MODE_2To1_LINE,WDR_MODE_2To1_FRAME,WDR_MODE_2To1_FRAME_FULL_RATE,WDR_MODE_3To1_LINE,WDR_MODE_3To1_FRAME,WDR_MODE_3To1_FRAME_FULL_RATE,WDR_MODE_4To1_LINE,WDR_MODE_4To1_FRAME,WDR_MODE_4To1_FRAME_FULL_RATE,WDR_MODE_BUTT,
} WDR_MODE_E;
5、VPSS_GRP_ATTR_S 结构体
这个结构体用来描述VPSS组的属性。
/*Define attributes of vpss GROUP*/
typedef struct hiVPSS_GRP_ATTR_S
{/*statistic attributes*/HI_U32 u32MaxW; /*MAX width of the group*/ HI_U32 u32MaxH; /*MAX height of the group*/PIXEL_FORMAT_E enPixFmt; /*Pixel format*/HI_BOOL bIeEn; /*Image enhance enable*/HI_BOOL bDciEn; /*Dynamic contrast Improve enable*/HI_BOOL bNrEn; /*Noise reduce enable*/HI_BOOL bHistEn; /*Hist enable*/VPSS_DIE_MODE_E enDieMode; /*De-interlace enable*/
}VPSS_GRP_ATTR_S;
6、VPSS_CHN_ATTR_S 结构体
这个结构体用来描述VPSS信道的属性。
/*Define attributes of vpss channel*/
typedef struct hiVPSS_CHN_ATTR_S
{HI_BOOL bSpEn; /*Sharpen enable*/ HI_BOOL bBorderEn; /*Frame enable*/HI_BOOL bMirror; /*mirror enable*/HI_BOOL bFlip; /*flip enable*/HI_S32 s32SrcFrameRate; /* source frame rate */HI_S32 s32DstFrameRate; /* dest frame rate */ BORDER_S stBorder;
}VPSS_CHN_ATTR_S;
7、VPSS_CHN_MODE_S 结构体
这个结构体用来描述VPSS信道工作模式的属性。
/*Define attributes of vpss channel's work mode*/
typedef struct hiVPSS_CHN_MODE_S
{VPSS_CHN_MODE_E enChnMode; /*Vpss channel's work mode*/HI_U32 u32Width; /*Width of target image*/HI_U32 u32Height; /*Height of target image*/HI_BOOL bDouble; /*Field-frame transfer,only valid for VPSS_PRE0_CHN*/PIXEL_FORMAT_E enPixelFormat;/*Pixel format of target image*/COMPRESS_MODE_E enCompressMode; /*Compression mode of the output*/
}VPSS_CHN_MODE_S;
8、SAMPLE_RC_E 枚举类型
这个枚举类型用来描述码率控制的类型。
typedef enum sample_rc_e
{SAMPLE_RC_CBR = 0,//固定比特率SAMPLE_RC_VBR,//可变比特率SAMPLE_RC_FIXQP//???
}SAMPLE_RC_E;
9、SIZE_S 结构体
这个结构体用来描述图像的分辨率(长宽像素),这里是具体的分辨率信息,有别于PIC_SIZE_E 这种指代形式的。
typedef struct hiSIZE_S
{HI_U32 u32Width;HI_U32 u32Height;
} SIZE_S;
二、分析MPP系统的初始化
MPP系统的初始化包括以下两个步骤,其函数调用关系如下。
step1:
SAMPLE_COMM_VI_GetSizeBySensor//根据sensor类型获取图像分辨率(枚举类型)
SAMPLE_COMM_SYS_CalcPicVbBlkSize//计算VB中缓冲块的大小SAMPLE_COMM_SYS_GetPicSize//计算图像的真实分辨率(根据上面的枚举类型返回w与h)CEILING_2_POWER//宏函数VB_PIC_HEADER_SIZE//宏函数,计算出图像头的大小step2:
SAMPLE_COMM_SYS_Init//初始化MPP系统HI_MPI_SYS_Exit//去除MPP的初始化HI_MPI_VB_Exit//去除VB的初始化HI_MPI_VB_SetConf//设置VB属性HI_MPI_VB_Init//初始化VBHI_MPI_SYS_SetConf//设置系统属性HI_MPI_SYS_Init//初始化系统
1、step1的分析
step1的代码与分析如下,主要是填充stVbConf这个变量。该变量表示公共缓冲池,一帧图像大小小于缓冲池里的一个缓冲块的容积。
/******************************************step 1: init sys variable ******************************************/memset(&stVbConf,0,sizeof(VB_CONF_S));//该函数通过sensor的类型来获取图像的分辨率。SAMPLE_COMM_VI_GetSizeBySensor(&enSize[0]); //函数返回时enSize[0]= PIC_HD720if (PIC_HD1080 == enSize[0]){enSize[1] = PIC_VGA;s32ChnNum = 2;}else if (PIC_HD720 == enSize[0])//这个成立{enSize[1] = PIC_VGA; enSize[2] = PIC_QVGA;s32ChnNum = 3;//三路码流内容一样,但分辨率不一样//三路码流对应的图像分辨率为HD720、VGA、QVGA}else{printf("not support this sensor\n");return HI_FAILURE;}
#ifdef hi3518ev201s32ChnNum = 1;
#endifprintf("s32ChnNum = %d\n",s32ChnNum);stVbConf.u32MaxPoolCnt = 128;//定义缓冲池的最大数目是128个//每个码流对应着一个公共缓冲池,每个缓冲池中有很多块。//下面代码在计算,三个码流所对应的缓冲池中的块的大小。/*video buffer*/if(s32ChnNum >= 1){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;//值为4,即每个缓冲池有4个缓冲块}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){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;}
(1)SAMPLE_COMM_VI_GetSizeBySensor函数如下,其参数是输出型参数,通过判断传感器类型从而获取图像分辨率(只是一个枚举变量,对应着某个具体的分辨率),
/******************************************************************************
* funciton : Get enSize by diffrent sensor
******************************************************************************/
HI_S32 SAMPLE_COMM_VI_GetSizeBySensor(PIC_SIZE_E *penSize)
{HI_S32 s32Ret = HI_SUCCESS;SAMPLE_VI_MODE_E enMode = SENSOR_TYPE;//sensor的类型定义在Makefile.param文件中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;//上述sensor类型的分辨率都是这个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;}
(2)SAMPLE_COMM_SYS_CalcPicVbBlkSize函数如下,它计算缓冲池中每个块的大小。其中参数1是图像制式(上层函数传过来的是NTSC),参数2是图像分辨率(枚举变量),参数3是像素格式(上层函数传过来的是SAMPLE_PIXEL_FORMAT,即YUV_SEMIPLANAR_420),参数4是对齐宽度(上层函数传过来的是SAMPLE_SYS_ALIGN_WIDTH,即64)。
/******************************************************************************
* function : calculate VB Block size of picture.
******************************************************************************/
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);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(stSize.u32Height,u32AlignWidth) * \((PIXEL_FORMAT_YUV_SEMIPLANAR_422 == enPixFmt)?2:1.5));VB_PIC_HEADER_SIZE(stSize.u32Width, stSize.u32Height, enPixFmt, u32HeaderSize);u32VbSize += u32HeaderSize;return u32VbSize;
}
1)SAMPLE_COMM_SYS_GetPicSize函数代码如下,参数1是图像制式,参数2是图像分辨率(枚举变量),而参数3是输出型参数,它表示图像的真实分辨率,即一帧图像的大小(w*h)。
/******************************************************************************
* function : get picture size(w*h), according Norm and enPicSize
******************************************************************************/
HI_S32 SAMPLE_COMM_SYS_GetPicSize(VIDEO_NORM_E enNorm, PIC_SIZE_E enPicSize, SIZE_S *pstSize)
{switch (enPicSize){//省略部分代码 case PIC_2CIF:pstSize->u32Width = 360;pstSize->u32Height = (VIDEO_ENCODING_MODE_PAL==enNorm)?576:480;break;case PIC_QVGA: /* 320 * 240 */pstSize->u32Width = 320;pstSize->u32Height = 240;break;case PIC_VGA: /* 640 * 480 */pstSize->u32Width = 640;pstSize->u32Height = 480;break;//省略部分代码case PIC_HD720: /* 1280 * 720 */pstSize->u32Width = 1280;pstSize->u32Height = 720;break;case PIC_HD1080: /* 1920 * 1080 */pstSize->u32Width = 1920;pstSize->u32Height = 1080;break;case PIC_5M: /* 2592 * 1944 */pstSize->u32Width = 2592;pstSize->u32Height = 1944;break;default:return HI_FAILURE;}return HI_SUCCESS;
}
2)CEILING_2_POWER这个宏的定义如下,它计算出不小于x的、且是a整数倍的最小值。
#define CEILING_2_POWER(x,a) ( ((x) + ((a) - 1) ) & ( ~((a) - 1) ) )
参考博客:CEILING_2_POWER、对齐操作函数、视频帧内存size计算。
另外,(PIXEL_FORMAT_YUV_SEMIPLANAR_422 == enPixFmt)?2:1.5),是什么意思呢?
我们知道YUV422,对于2*2的4个像素点,有4个Y、2个U、2个V,一共8个字节,那么每个像素占用2个字节。而对于YUV420,对于2*2的4个像素点,有4个Y,1个U,1个V,一共6个字节,那么每个像素占1.5个字节。上面的三目运算符就是这个意思。
这里之所以只有YUV422和YUV420,是因为海思方案里只允许使用这两种色彩空间。rawRGB转化为RGB后,应该还是要转为YUV422或者420的(这里使用420)。
3)VB_PIC_HEADER_SIZE这个宏计算出图像头的大小。根据代码,一帧图像对应着缓冲池里的一个缓冲块,但缓冲块的容量肯定比一帧图像要大,因为之前的CEILING_2_POWER计算方式得到的就比一帧图像要大,而且这里还加上一个图像头。
2、step2的分析
step2的代码如下,主要是将step1中填充的stVbConf变量作为参数,调用SAMPLE_COMM_SYS_Init函数来初始化mpp系统。
/******************************************step 2: mpp system init. ******************************************/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;}
其中的SAMPLE_COMM_SYS_Init函数的调用图谱如下。注意到其所调用的函数都是以HI开头的,这些函数我们看不到具体的源码,不过要明白它们的用法与含义。
SAMPLE_COMM_SYS_InitHI_MPI_VB_SetConf //配置VBHI_MPI_VB_Init //初始化VBHI_MPI_SYS_SetConf //配置系统HI_MPI_SYS_Init //初始化系统
/******************************************************************************
* 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);if (HI_SUCCESS != s32Ret){SAMPLE_PRT("HI_MPI_VB_SetConf failed!\n");return HI_FAILURE;}s32Ret = HI_MPI_VB_Init();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();if (HI_SUCCESS != s32Ret){SAMPLE_PRT("HI_MPI_SYS_Init failed!\n");return HI_FAILURE;}return HI_SUCCESS;
}