海思3531DV100视频输入模块(VI)
VI 可以对接收到的原始视频图像数据进行裁剪(Crop)、水平垂直缩小(Down Scaling)和水平垂直翻转(Mirror、Flip)等处理,并实现一路或多路原始视频图像输入,输出一路或多路视频图像功能。
视频输入设备支持标准 BT.656、标准 BT.1120、自定义时序等若干种时序输入,负责对时序进行解析。
Hi3531DV100 芯片有 5 个 BT.1120 接口,除第 5 个 BT.1120 接口外,每个 BT.1120 接口依次对应两个 VI 设备,即第 1 个 BT.1120 口对应 VI 的 Dev0 和 Dev1,第 2 个BT.1120 口对应 VI 的 Dev2 和 Dev3,第 3 个 BT.1120 口对应 VI 的 Dev4 和 Dev5,第 4个 BT.1120 口对应 VI 的 Dev6 和 Dev7。第 5 个 BT.1120 口是专用于级联的,对应DEV8,共8+1个输入设备。
前 8 个 VI 设备均支持 1/2/4 路 D1(D1:480i:720×480,几乎相当于我们所说的4CIF(720×576);)、960H 复合模式输入(BT.656 协议),以及 2 路720P 复合模式。当前 8 个设备配置为支持 720P/1080P 高清输入(BT.1120 协议),此时,同一个 BT1120 接口的另一个 Dev 不可用,即 DEV0、DEV1 中只能一个可用;DEV2、DEV3 中只能用一个,依此类推。DEV 的选择与 VI 的时钟必须一致,以DEV0 和 DEV1 为例,如果时钟选择为 VI0_CLK,则 DEV 选择为 DEV0,如果时钟选择为 VI1_CLK,则 DEV 选择为 DEV1,其他 DEV 依次类推。
绑定关系
Hi3531DV100 VI 有 9 个设备和 33 个物理通道,除级联设备外每个设备默认与对应的 4个通道绑定,其钟一个设备绑定关系如图 所示
级联设备绑定关系
通过cat /proc/umap/vi可以查看vi状态,如下图。
图中显示了配置的vi设备及通道,板子使用了24通道,为vi设备6的通道0,同时显示了接受的数据帧数。
下图说明了具体的vi设备配置属性,使用的接口及复用情况等。
下图说明了具体的vi绑定关系,中断次数,获取 VideoBuffer 失败次数,丢失中断次数,图像帧场标志:frm:逐行图像,intl:隔行图像,图片一行跨度。
通过cat /proc/umap/sys可以查看整个系统绑定状态,如下图。
这幅图说明了我绑定的资源为vi设备6绑定了vpss的group6的1号通道,vpss再绑定vo(视频输出)。
下面的代码块配置说明为了简洁,已经把if安全判断删除
次代码为配置视频输入的主要代码,为打开vi(视频输入)设备,vpss(视频处理)设备,将vi的数据绑定vpss,绑定后,vi采集的数据将自动传输给绑定的vpss
{
s32Ret = SAMPLE_COMM_VI_Start(enViMode, enNorm);
/******************************************
step 4: start vpss and vi bind vpss(视频处理)
******************************************/
s32Ret = SAMPLE_COMM_SYS_GetPicSize(enNorm, PIC_HD1080, &stSize);
memset(&stGrpAttr,0,sizeof(VPSS_GRP_ATTR_S));
stGrpAttr.u32MaxW = stSize.u32Width;
stGrpAttr.u32MaxH = stSize.u32Height;
stGrpAttr.bNrEn = HI_TRUE;
stGrpAttr.enDieMode = VPSS_DIE_MODE_NODIE;
stGrpAttr.enPixFmt = SAMPLE_PIXEL_FORMAT;
s32Ret = SAMPLE_COMM_VPSS_Start(s32VpssGrpCnt, &stSize, VPSS_MAX_CHN_NUM, &stGrpAttr);
s32Ret = SAMPLE_COMM_VI_BindVpss(enViMode);
}
按照视频输入场景,依次初始化VI设备
HI_S32 SAMPLE_COMM_VI_Start(SAMPLE_VI_MODE_E enViMode, VIDEO_NORM_E enNorm)
{
VI_DEV ViDev;
VI_CHN ViChn;
HI_S32 i;
HI_S32 s32Ret;
SAMPLE_VI_PARAM_S stViParam;
SIZE_S stTargetSize;
RECT_S stCapRect;
VI_CHN_BIND_ATTR_S stChnBindAttr;
/*** 通过视频输入场景,获得输入设备参数 ***/
s32Ret = SAMPLE_COMM_VI_Mode2Param(enViMode, &stViParam);
//手动设置输入的视频宽高
s32Ret = SAMPLE_COMM_VI_Mode2Size(enViMode, enNorm, &stCapRect, &stTargetSize);
for(i=0; i<stViParam.s32ViDevCnt; i++)
{
ViDev = i * stViParam.s32ViDevInterval;
s32Ret = SAMPLE_COMM_VI_StartDev(ViDev, enViMode);//根据视频输入场景,初始化输入设备
}
/*** Start VI Chn ***/
for(i=0; i<stViParam.s32ViChnCnt; i++)
{
ViChn = i * stViParam.s32ViChnInterval;
if (SAMPLE_VI_MODE_16_1080P == enViMode|| SAMPLE_VI_MODE_16_720P == enViMode)
{
/* When in the 16x1080p mode, bind chn 2,6,10,14 to way1 is needed */
if (ViChn%4 != 0)
{
s32Ret = HI_MPI_VI_GetChnBind(ViChn, &stChnBindAttr);//获取 VI 通道绑定关系
if (HI_ERR_VI_FAILED_NOTBIND == s32Ret)
{
stChnBindAttr.ViDev = ViChn/4;
stChnBindAttr.ViWay = 1;
s32Ret = HI_MPI_VI_BindChn(ViChn, &stChnBindAttr);
}
}
}
s32Ret = SAMPLE_COMM_VI_StartChn(ViChn, &stCapRect, &stTargetSize, enViMode, VI_CHN_SET_NORMAL);
}
return HI_SUCCESS;
}
HI_S32 SAMPLE_COMM_VI_Mode2Param(SAMPLE_VI_MODE_E enViMode, SAMPLE_VI_PARAM_S pstViParam)
{
switch (enViMode)
{
case SAMPLE_VI_MODE_32_D1:
case SAMPLE_VI_MODE_32_960H:
case SAMPLE_VI_MODE_32_1280H:
case SAMPLE_VI_MODE_32_HALF720P:
/ use chn 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16*/
pstViParam->s32ViDevCnt = 4;
pstViParam->s32ViDevInterval = 1;//设备号间隔
pstViParam->s32ViChnCnt = 16;
pstViParam->s32ViChnInterval = 1;//通道号间隔
break;
case SAMPLE_VI_MODE_16_720P:
case SAMPLE_VI_MODE_16_1080P:
/* use chn 0,2,4,6,8,10,12,14,16,18,20,22,24,28,30,32 /
pstViParam->s32ViDevCnt = 8;
pstViParam->s32ViDevInterval = 1;
pstViParam->s32ViChnCnt = 16;
pstViParam->s32ViChnInterval = 2;
break;
case SAMPLE_VI_MODE_8_720P:
case SAMPLE_VI_MODE_8_1080P:
/ use chn 0,4,8,12,16,20,24,28 */
pstViParam->s32ViDevCnt = 8;
pstViParam->s32ViDevInterval = 1;
pstViParam->s32ViChnCnt = 8;
pstViParam->s32ViChnInterval = 4;
break;
default:
SAMPLE_PRT(“ViMode invaild!\n”);
return HI_FAILURE;
}
return HI_SUCCESS;
}
//设置输入视频宽高
HI_S32 SAMPLE_COMM_VI_Mode2Size(SAMPLE_VI_MODE_E enViMode, VIDEO_NORM_E enNorm, RECT_S *pstCapRect, SIZE_S *pstDestSize)
{
pstCapRect->s32X = 0;
pstCapRect->s32Y = 0;
switch (enViMode)
{
case SAMPLE_VI_MODE_32_D1:
pstDestSize->u32Width = 720; //图像目标宽度(由捕获图像缩放得到)
pstDestSize->u32Height = (VIDEO_ENCODING_MODE_PALenNorm)?576:480;
pstCapRect->u32Width = 576; //捕获视频输入区域起始位置
pstCapRect->u32Height = (VIDEO_ENCODING_MODE_PALenNorm)?576:480;
break;
case SAMPLE_VI_MODE_32_960H:
pstDestSize->u32Width = 960;
pstDestSize->u32Height = (VIDEO_ENCODING_MODE_PALenNorm)?576:480;
pstCapRect->u32Width = 960;
pstCapRect->u32Height = (VIDEO_ENCODING_MODE_PALenNorm)?576:480;
break;
case SAMPLE_VI_MODE_8_720P:
case SAMPLE_VI_MODE_16_720P:
pstDestSize->u32Width = 1280;
pstDestSize->u32Height = 720;
pstCapRect->u32Width = 1280;
pstCapRect->u32Height = 720;
break;
case SAMPLE_VI_MODE_8_1080P:
case SAMPLE_VI_MODE_16_1080P:
pstDestSize->u32Width = 1920;
pstDestSize->u32Height = 1080;
pstCapRect->u32Width = 1920;
pstCapRect->u32Height = 1080;
break;
case SAMPLE_VI_MODE_4_4Kx2K:
pstDestSize->u32Width = 3840;
pstDestSize->u32Height = 2160;
pstCapRect->u32Width = 3840;
pstCapRect->u32Height = 2160;
break;
default:
SAMPLE_PRT("vi mode invaild!\n");
return HI_FAILURE;
}
return HI_SUCCESS;
}
//打开对应的视频输入设备
HI_S32 SAMPLE_COMM_VI_StartDev(VI_DEV ViDev, SAMPLE_VI_MODE_E enViMode)
{
HI_S32 s32Ret;
VI_DEV_ATTR_S stViDevAttr;
memset(&stViDevAttr,0,sizeof(stViDevAttr));
//主要设置视频输入 接口模式;1、2、4 路复合工作模式; 分量掩码配置;时钟边沿模式;入数据顺序 (仅支持 yuv 格式)。
switch (enViMode)
{
case SAMPLE_VI_MODE_32_D1:
memcpy(&stViDevAttr,&DEV_ATTR_BT656D1_4MUX,sizeof(stViDevAttr));
SAMPLE_COMM_VI_SetMask(ViDev,&stViDevAttr);
break;
case SAMPLE_VI_MODE_32_960H:
memcpy(&stViDevAttr,&DEV_ATTR_BT656D1_4MUX,sizeof(stViDevAttr));
SAMPLE_COMM_VI_SetMask(ViDev,&stViDevAttr);
break;
case SAMPLE_VI_MODE_8_720P:
case SAMPLE_VI_MODE_8_1080P:
memcpy(&stViDevAttr,&DEV_ATTR_TP2823_720P_1MUX_BASE,sizeof(stViDevAttr));
SAMPLE_COMM_VI_SetMask(ViDev,&stViDevAttr);
break;
case SAMPLE_VI_MODE_16_720P:
memcpy(&stViDevAttr,&DEV_ATTR_TP2823_720P_2MUX_BASE,sizeof(stViDevAttr));
SAMPLE_COMM_VI_SetMask(ViDev,&stViDevAttr);
break;
case SAMPLE_VI_MODE_16_1080P:
memcpy(&stViDevAttr,&DEV_ATTR_7441_BT1120_STANDARD_BASE,sizeof(stViDevAttr));
SAMPLE_COMM_VI_SetMask(ViDev,&stViDevAttr);
break;
default:
SAMPLE_PRT(“vi input type[%d] is invalid!\n”, enViMode);
return HI_FAILURE;
}
s32Ret = HI_MPI_VI_SetDevAttr(ViDev, &stViDevAttr);//设置 VI 设备属性
s32Ret = HI_MPI_VI_EnableDev(ViDev);//启用 VI 设备
return HI_SUCCESS;
}
/初始化vpss,vpss支持4个通道,具体属性在下节说明/
HI_S32 SAMPLE_COMM_VPSS_Start(HI_S32 s32GrpCnt, SIZE_S *pstSize, HI_S32 s32ChnCnt,VPSS_GRP_ATTR_S *pstVpssGrpAttr)
{
VPSS_GRP VpssGrp;
VPSS_CHN VpssChn;
VPSS_GRP_ATTR_S stGrpAttr = {0};
VPSS_CHN_ATTR_S stChnAttr = {0};
VPSS_GRP_PARAM_S stVpssParam = {0};
HI_S32 s32Ret;
HI_S32 i, j;
/*** Set Vpss Grp Attr ***/
if(NULL == pstVpssGrpAttr)
{
stGrpAttr.u32MaxW = pstSize->u32Width;
stGrpAttr.u32MaxH = pstSize->u32Height;
stGrpAttr.enPixFmt = SAMPLE_PIXEL_FORMAT;
stGrpAttr.bIeEn = HI_FALSE;
stGrpAttr.bNrEn = HI_TRUE;
stGrpAttr.bDciEn = HI_FALSE;
stGrpAttr.bHistEn = HI_FALSE;
stGrpAttr.bEsEn = HI_FALSE;
stGrpAttr.enDieMode = VPSS_DIE_MODE_NODIE;
}
else
{
memcpy(&stGrpAttr,pstVpssGrpAttr,sizeof(VPSS_GRP_ATTR_S));
}
for(i=0; i<s32GrpCnt; i++)
{
VpssGrp = i;
/*** create vpss group ***/
s32Ret = HI_MPI_VPSS_CreateGrp(VpssGrp, &stGrpAttr);
/*** set vpss param ***/
s32Ret = HI_MPI_VPSS_GetGrpParam(VpssGrp, &stVpssParam);
stVpssParam.u32IeStrength = 0;
s32Ret = HI_MPI_VPSS_SetGrpParam(VpssGrp, &stVpssParam);
/*** enable vpss chn, with frame ***/
for(j=0; j<s32ChnCnt; j++)
{
VpssChn = j;
/* Set Vpss Chn attr */
stChnAttr.bSpEn = HI_FALSE;
stChnAttr.bUVInvert = HI_FALSE;
stChnAttr.bBorderEn = HI_TRUE;
stChnAttr.stBorder.u32Color = 0xffffff;
stChnAttr.stBorder.u32LeftWidth = 2;
stChnAttr.stBorder.u32RightWidth = 2;
stChnAttr.stBorder.u32TopWidth = 2;
stChnAttr.stBorder.u32BottomWidth = 2;
s32Ret = HI_MPI_VPSS_SetChnAttr(VpssGrp, VpssChn, &stChnAttr);
s32Ret = HI_MPI_VPSS_EnableChn(VpssGrp, VpssChn);
}
/*** start vpss group ***/
s32Ret = HI_MPI_VPSS_StartGrp(VpssGrp);
}
return HI_SUCCESS;
}
//将vi的设备绑定对应vpss。至此,vi的通道数据就可以到达vpss。
HI_S32 SAMPLE_COMM_VI_BindVpss(SAMPLE_VI_MODE_E enViMode)
{
HI_S32 j, s32Ret;
VPSS_GRP VpssGrp;
MPP_CHN_S stSrcChn;
MPP_CHN_S stDestChn;
SAMPLE_VI_PARAM_S stViParam;
VI_CHN ViChn;
s32Ret = SAMPLE_COMM_VI_Mode2Param(enViMode, &stViParam);
VpssGrp = 0;
for (j=0; j<stViParam.s32ViChnCnt; j++)
{
ViChn = j * stViParam.s32ViChnInterval;
stSrcChn.enModId = HI_ID_VIU;
stSrcChn.s32DevId = 0;
stSrcChn.s32ChnId = ViChn;
stDestChn.enModId = HI_ID_VPSS;
stDestChn.s32DevId = VpssGrp;
stDestChn.s32ChnId = 0;
s32Ret = HI_MPI_SYS_Bind(&stSrcChn, &stDestChn);
VpssGrp ++;
}
return HI_SUCCESS;
}