海思3519 VIO Sample例程讲解

海思VIO Sample例程讲解

海思SDK解压出来后,Sample包含各个功能模块的历程,本篇讲解VIO Sample历程。
进入VIO模块可以看到,VIO的main函数文件,先从main函数执行程序。
在这里插入图片描述
进入文件后首先看下VIO实现的功能,可以看到VIO示例的功能还是挺多的,并且支持HDMI与BT1120这两种输出接口,在SAMPLE_VIO_VoInterface_Usage函数,可以看到,下面一个一个功能进行说明。

1、Sample_vio_main函数

/******************************************************************************
* function : show usage
***************************************************************************/
HI_VOID SAMPLE_VIO_VoInterface_Usage(HI_VOID)
{
    printf("intf:\n");
    printf("\t 0) vo HDMI output.\n");
    printf("\t 1) vo BT1120 output, default.\n");
}

void SAMPLE_VIO_Usage(char *sPrgNm)
{
    printf("Usage : %s <index> <intf>\n", sPrgNm);
    printf("index:\n");
    printf("\t 0)VI(Online) - VPSS(Online) - VO.\n");
    printf("\t 1)VI(Offline)- VPSS(Offline) - VO. LDC+DIS+SPREAD.\n");
    printf("\t 2)VI(Offline)- VPSS(Online) - VO.  Double pipe.\n");
    printf("\t 3)VI(Online)- VPSS(Offline) - VO.  Double chn.\n");
    printf("\t 4)Resolute Ratio Switch.\n");
    printf("\t 5)GDC - VPSS LowDelay.\n");
    printf("\t 6)Double WDR Pipe.\n");
    printf("\t 7)FPN Calibrate & Correction.\n");
    printf("\t 8)WDR Switch.\n");
    printf("\t 9)90/180/270 Rotate.\n");
    printf("\t 10)Mipi Demux Yuv.\n");
    printf("\t 11)UserPic.\n");

    printf("intf:\n");
    printf("\t 0) vo HDMI output, default.\n");
    printf("\t 1) vo BT1120 output.\n");
    return;
}

首先判断传给执行程序传入的参数个数是否正确,不正确则打印使用说明。

/******************************************************************************
* function    : main()
* Description : main
***************************************************************************/
#ifdef __HuaweiLite__
int app_main(int argc, char *argv[])
#else
int main(int argc, char *argv[])
#endif
{
    HI_S32 s32Ret = HI_FAILURE;
    HI_S32 s32Index;
    HI_U32 u32VoIntfType = 0;

    if (argc < 2)
    {
        SAMPLE_VIO_Usage(argv[0]);
        return HI_FAILURE;
    }


设置相应信号处理函数,SIGINT由Interrupt Key产生,通常是CTRL+C或者Delete,发送给所有ForeGround Group的进程。SIGTERM请求终止进程,Kill命令缺省发送。

#ifdef __HuaweiLite__
#else
    signal(SIGINT, SAMPLE_VIO_HandleSig);
    signal(SIGTERM, SAMPLE_VIO_HandleSig);
#endif

    if ((argc > 2) && (1 == atoi(argv[2])))
    {
        u32VoIntfType = 1;
    }

    SAMPLE_VIO_MsgInit();

当捕获到异常信号后触发的自定义函数如下,主要是对信号的重新定义以及系统退出资源的释放。

void SAMPLE_VIO_HandleSig(HI_S32 signo)
{
    signal(SIGINT, SIG_IGN);
    signal(SIGTERM, SIG_IGN);

    if (SIGINT == signo || SIGTERM == signo)
    {
        SAMPLE_COMM_VENC_StopGetStream();
        SAMPLE_COMM_All_ISP_Stop();
        SAMPLE_COMM_VO_HdmiStop();
        SAMPLE_COMM_SYS_Exit();
        printf("\033[0;31mprogram termination abnormally!\033[0;39m\n");
    }
    exit(-1);
}

SAMPLE_VIO_MsgInit()与SAMPLE_VIO_MsgExit()分别为程序初始化与退出的桩函数,用户可以进行添加内容。

HI_VOID SAMPLE_VIO_MsgInit(HI_VOID)
{
}

HI_VOID SAMPLE_VIO_MsgExit(HI_VOID)
{
}

接着就是选择相应的功能,下面依次解析相关功能。

 s32Index = atoi(argv[1]);
    switch (s32Index)
    {
        case 0:
            s32Ret = SAMPLE_VIO_ViOnlineVpssOnlineRoute(u32VoIntfType);
            break;

        case 1:
            s32Ret = SAMPLE_VIO_ViOnlineVpssOfflineRoute(u32VoIntfType);
            break;

        case 2:
            s32Ret = SAMPLE_VIO_ViDoublePipeRoute(u32VoIntfType);
            break;

        case 3:
            s32Ret = SAMPLE_VIO_ViDoubleChnRoute(u32VoIntfType);
            break;

        case 4:
            s32Ret = SAMPLE_VIO_ViResoSwitch(u32VoIntfType);
            break;

        case 5:
            s32Ret = SAMPLE_VIO_ViVpssLowDelay(u32VoIntfType);
            break;

        case 6:
            s32Ret = SAMPLE_VIO_ViDoubleWdrPipe(u32VoIntfType);
            break;

        case 7:
            s32Ret = SAMPLE_VIO_ViFPN(u32VoIntfType);
            break;

        case 8:
            s32Ret = SAMPLE_VIO_ViWdrSwitch(u32VoIntfType);
            break;

        case 9:
            s32Ret = SAMPLE_VIO_ViRotate(u32VoIntfType);
            break;

        case 10:
            s32Ret = SAMPLE_VIO_ViDeMuxYuv(u32VoIntfType);
            break;

        case 11:
            s32Ret = SAMPLE_VIO_ViSetUsrPic(u32VoIntfType);
            break;

        default:
            SAMPLE_PRT("the index %d is invaild!\n",s32Index);
            SAMPLE_VIO_Usage(argv[0]);
            SAMPLE_VIO_MsgExit();
            return HI_FAILURE;
    }

2、SAMPLE_VIO_ViOnlineVpssOnlineRoute

在解析源码前你可能需要阅读一下《HiMPP V4.0 媒体处理软件开发参考》文档,大部分的内容文档中都已经描述的很清楚,当然涉及到一些专业术语就要我们自己去查了。

前面一些都是关于VI、VPSS、VO以及VENC的配置,具体参数的信息可以在《HiMPP V4.0 媒体处理软件开发参考》查阅,这里是VI配置为单个Sensor,VI的配置为设备号0,使用PIPE与通道0,然后配置视频动态范围、线性模式、像素格式为YUV NV21、视频格式、不压缩等等,采用H265编码器对VPSS输出的视频流进行编码保存。

HI_S32 SAMPLE_VIO_ViOnlineVpssOnlineRoute(HI_U32 u32VoIntfType)
{
    HI_S32             s32Ret;

    HI_S32             s32ViCnt       = 1;//VI配置为单个sensor
    VI_DEV             ViDev          = 0;//VI配置为设备号0
    VI_PIPE            ViPipe         = 0;//VI配置为物理PIPE号0
    VI_CHN             ViChn          = 0;//VI配置通道号0
    HI_S32             s32WorkSnsId   = 0;
    SAMPLE_VI_CONFIG_S stViConfig;

    SIZE_S             stSize;
    VB_CONFIG_S        stVbConf;          //视频缓存池属性
    PIC_SIZE_E         enPicSize;
    HI_U32             u32BlkSize;

    VO_CHN             VoChn          = 0;//VO配置视频输出通道号0
    SAMPLE_VO_CONFIG_S stVoConfig;

    WDR_MODE_E         enWDRMode      = WDR_MODE_NONE;//WDR 工作模式,分为帧模式、行模式、非WDR
    DYNAMIC_RANGE_E    enDynamicRange = DYNAMIC_RANGE_SDR8;//动态范围,8bit数据的标准动态范围
    PIXEL_FORMAT_E     enPixFormat    = PIXEL_FORMAT_YVU_SEMIPLANAR_420;//像素格式
    VIDEO_FORMAT_E     enVideoFormat  = VIDEO_FORMAT_LINEAR;//视频格式,现行存储
    COMPRESS_MODE_E    enCompressMode = COMPRESS_MODE_NONE;//视频压缩数据格式,非压缩

    VPSS_GRP           VpssGrp        = 0; //VPSS GROUP 号0
    VPSS_GRP_ATTR_S    stVpssGrpAttr;
    VPSS_CHN           VpssChn        = VPSS_CHN0; //VPSS 通道号
    HI_BOOL            abChnEnable[VPSS_MAX_PHY_CHN_NUM] = {0};
    VPSS_CHN_ATTR_S    astVpssChnAttr[VPSS_MAX_PHY_CHN_NUM]; //VPSS物理通道属性

    VENC_CHN           VencChn[1]  = {0};   //编码通道号
    PAYLOAD_TYPE_E     enType      = PT_H265;//音视频净荷类型,解码通道类型为PT_256
    SAMPLE_RC_E        enRcMode    = SAMPLE_RC_CBR;
    HI_U32             u32Profile  = 0;
    VENC_GOP_ATTR_S    stGopAttr;     //编码器GOP属性
    
    /*config vi*/
    SAMPLE_COMM_VI_GetSensorInfo(&stViConfig);

因为很多sample需要调用同样的配置,所以海思将一些通用的接口都放在SAMPLE_COMM_XXX接口中,这里Sensor配置是使用I2C接口,可以在下面的注释部分有说明,Hi3519AV100的VI设备最多支持5个,不同的芯片型号支持的不一样,可以具体阅读《HiMPP V4.0 媒体处理软件开发参考》。

HI_VOID SAMPLE_COMM_VI_GetSensorInfo(SAMPLE_VI_CONFIG_S* pstViConfig)
{
    HI_S32 i;

    for (i = 0; i < VI_MAX_DEV_NUM; i++)
    {
        pstViConfig->astViInfo[i].stSnsInfo.s32SnsId = i;
        pstViConfig->astViInfo[i].stSnsInfo.s32BusId = i;
        pstViConfig->astViInfo[i].stSnsInfo.MipiDev  = i;
        hi_memset(&pstViConfig->astViInfo[i].stSnapInfo, sizeof(SAMPLE_SNAP_INFO_S), 0, sizeof(SAMPLE_SNAP_INFO_S));
        pstViConfig->astViInfo[i].stPipeInfo.bMultiPipe = HI_FALSE;
        pstViConfig->astViInfo[i].stPipeInfo.bVcNumCfged = HI_FALSE;
    }

    /*single sensor using I2C,  s32BusId must be set to 1
      single sensor using spi,  s32BusId must be set to 0
      if add sensor using spi into SAMPLE_SNS_TYPE_E, please add code branch with sensor type*/
    pstViConfig->astViInfo[0].stSnsInfo.s32BusId = 1;

    pstViConfig->astViInfo[0].stSnsInfo.enSnsType = SENSOR0_TYPE;
    pstViConfig->astViInfo[1].stSnsInfo.enSnsType = SENSOR1_TYPE;
    pstViConfig->astViInfo[2].stSnsInfo.enSnsType = SENSOR2_TYPE;
    pstViConfig->astViInfo[3].stSnsInfo.enSnsType = SENSOR3_TYPE;
    pstViConfig->astViInfo[4].stSnsInfo.enSnsType = SENSOR4_TYPE;
}
typedef struct hiSAMPLE_VI_CONFIG_S
{
    SAMPLE_VI_INFO_S    astViInfo[VI_MAX_DEV_NUM];
    HI_S32              as32WorkingViId[VI_MAX_DEV_NUM];
    HI_S32              s32WorkingViNum;
} SAMPLE_VI_CONFIG_S;
typedef struct hiSAMPLE_VI_INFO_S
{
    SAMPLE_SENSOR_INFO_S    stSnsInfo;
    SAMPLE_DEV_INFO_S       stDevInfo;
    SAMPLE_PIPE_INFO_S      stPipeInfo;
    SAMPLE_CHN_INFO_S       stChnInfo;
    SAMPLE_SNAP_INFO_S      stSnapInfo;
} SAMPLE_VI_INFO_S;
typedef struct hiSAMPLE_SENSOR_INFO_S
{
    SAMPLE_SNS_TYPE_E   enSnsType;
    HI_S32              s32SnsId;
    HI_S32              s32BusId;
    combo_dev_t           MipiDev;
} SAMPLE_SENSOR_INFO_S;
typedef enum hiSAMPLE_SNS_TYPE_E
{
    SONY_IMX290_MIPI_2M_30FPS_12BIT,
    SONY_IMX290_MIPI_2M_30FPS_12BIT_WDR2TO1,
    SONY_IMX290_SLAVE_MIPI_2M_60FPS_10BIT,
    SONY_IMX334_MIPI_8M_30FPS_12BIT,
    SONY_IMX334_MIPI_8M_30FPS_12BIT_WDR2TO1,
    SONY_IMX377_MIPI_8M_30FPS_10BIT,
    NVP6324_MIPI_2M_30FPS_8BIT,
    SAMPLE_SNS_TYPE_BUTT,
} SAMPLE_SNS_TYPE_E;

可以看到结构体是一层套着一层,还是比较复杂的。
因为VPSS的工作模式设置为在线模式,可以简单的认为VPSS在线模式由VI_PROC将数据流直接发送给VPSS进行处理。

 /*config vi*/
    SAMPLE_COMM_VI_GetSensorInfo(&stViConfig);
    stViConfig.s32WorkingViNum                                   = s32ViCnt;
    stViConfig.as32WorkingViId[0]                                = 0;
    stViConfig.astViInfo[s32WorkSnsId].stSnsInfo.MipiDev         = ViDev;
    stViConfig.astViInfo[s32WorkSnsId].stDevInfo.ViDev           = ViDev;
    stViConfig.astViInfo[s32WorkSnsId].stDevInfo.enWDRMode       = enWDRMode;
    stViConfig.astViInfo[s32WorkSnsId].stPipeInfo.enMastPipeMode = VI_ONLINE_VPSS_ONLINE;//VI PIPE 和 VPSS 组的工作模式
    stViConfig.astViInfo[s32WorkSnsId].stPipeInfo.aPipe[0]       = ViPipe;
    stViConfig.astViInfo[s32WorkSnsId].stPipeInfo.aPipe[1]       = -1;
    stViConfig.astViInfo[s32WorkSnsId].stPipeInfo.aPipe[2]       = -1;
    stViConfig.astViInfo[s32WorkSnsId].stPipeInfo.aPipe[3]       = -1;
    stViConfig.astViInfo[s32WorkSnsId].stChnInfo.ViChn           = ViChn;
    stViConfig.astViInfo[s32WorkSnsId].stChnInfo.enPixFormat     = enPixFormat;
    stViConfig.astViInfo[s32WorkSnsId].stChnInfo.enDynamicRange  = enDynamicRange;
    stViConfig.astViInfo[s32WorkSnsId].stChnInfo.enVideoFormat   = enVideoFormat;
    stViConfig.astViInfo[s32WorkSnsId].stChnInfo.enCompressMode  = enCompressMode;

根据不同Sensor类型的图片大小分配缓存,这里配置VI写出RAW的缓存块为4块,YUV缓存块10块,可以在下面的最后几行赋值看到10和4,数据结构u32BlkCnt可以在MIPP媒体软件开发参考里面查到

   /*get picture size*/
    s32Ret = SAMPLE_COMM_VI_GetSizeBySensor(stViConfig.astViInfo[s32WorkSnsId].stSnsInfo.enSnsType, &enPicSize);
    if (HI_SUCCESS != s32Ret)
    {
        SAMPLE_PRT("get picture size by sensor failed!\n");
        return s32Ret;
    }

    s32Ret = SAMPLE_COMM_SYS_GetPicSize(enPicSize, &stSize);
    if (HI_SUCCESS != s32Ret)
    {
        SAMPLE_PRT("get picture size failed!\n");
        return s32Ret;
    }

    /*config vb*/
    hi_memset(&stVbConf, sizeof(VB_CONFIG_S), 0, sizeof(VB_CONFIG_S));
    stVbConf.u32MaxPoolCnt              = 2;

    u32BlkSize = COMMON_GetPicBufferSize(stSize.u32Width, stSize.u32Height, SAMPLE_PIXEL_FORMAT, DATA_BITWIDTH_8, COMPRESS_MODE_SEG, DEFAULT_ALIGN);
    stVbConf.astCommPool[0].u64BlkSize  = u32BlkSize;
    stVbConf.astCommPool[0].u32BlkCnt   = 10;

    u32BlkSize = VI_GetRawBufferSize(stSize.u32Width, stSize.u32Height, PIXEL_FORMAT_RGB_BAYER_16BPP, COMPRESS_MODE_NONE, DEFAULT_ALIGN);
    stVbConf.astCommPool[1].u64BlkSize  = u32BlkSize;
    stVbConf.astCommPool[1].u32BlkCnt   = 4;

在启动各个模块之前必须需要MPP系统进行初始化。

s32Ret = SAMPLE_COMM_SYS_Init(&stVbConf);
if (HI_SUCCESS != s32Ret)
{
    SAMPLE_PRT("system init failed with %d!\n", s32Ret);
    return s32Ret;
}

为了防止其它进程已经使用MPP需要对其进行去初始化,然后根据之前VB的配置进行设置对系统进行初始化。

/******************************************************************************
* function : vb init & MPI system init
******************************************************************************/
HI_S32 SAMPLE_COMM_SYS_Init(VB_CONFIG_S* pstVbConfig)
{
    HI_S32 s32Ret = HI_FAILURE;

    HI_MPI_SYS_Exit();
    HI_MPI_VB_Exit();

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

    s32Ret = HI_MPI_VB_SetConfig(pstVbConfig);

    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;
    }

    s32Ret = HI_MPI_SYS_Init();

    if (HI_SUCCESS != s32Ret)
    {
        SAMPLE_PRT("HI_MPI_SYS_Init failed!\n");
        HI_MPI_VB_Exit();
        return HI_FAILURE;
    }

    return HI_SUCCESS;
}

启动VI模块需要先启动MIPI,接口根据hi_mipi驱动进行控制,根据VI的配置进行VI模块的参数设置以及创建,还需要启动ISP,ISP这块又涉及到许多内容,包括调试图像、可以使用自定义3A库开发等等。

   /*start vi*/
    s32Ret = SAMPLE_COMM_VI_StartVi(&stViConfig);
    if (HI_SUCCESS != s32Ret)
    {
        SAMPLE_PRT("start vi failed.s32Ret:0x%x !\n", s32Ret);
        goto EXIT;
    }

SAMPLE_COMM_VI_StartVi函数,ISP内容见最后几行代码。

HI_S32 SAMPLE_COMM_VI_StartVi(SAMPLE_VI_CONFIG_S* pstViConfig)
{
    HI_S32 s32Ret = HI_SUCCESS;

    if (!pstViConfig)
    {
        SAMPLE_PRT("%s: null ptr\n", __FUNCTION__);
        return HI_FAILURE;
    }

    s32Ret = SAMPLE_COMM_VI_StartMIPI(pstViConfig);
    if (s32Ret != HI_SUCCESS)
    {
        SAMPLE_PRT("SAMPLE_COMM_VI_StartMIPI failed!\n");
        return HI_FAILURE;
    }

    s32Ret = SAMPLE_COMM_VI_SetParam(pstViConfig);
    if (s32Ret != HI_SUCCESS)
    {
        SAMPLE_PRT("SAMPLE_COMM_VI_SetParam failed!\n");
        return HI_FAILURE;
    }

    s32Ret = SAMPLE_COMM_VI_CreateVi(pstViConfig);
    if (s32Ret != HI_SUCCESS)
    {
        SAMPLE_PRT("SAMPLE_COMM_VI_CreateVi failed!\n");
        return HI_FAILURE;
    }

    s32Ret = SAMPLE_COMM_VI_CreateIsp(pstViConfig);
    if (s32Ret != HI_SUCCESS)
    {
        SAMPLE_COMM_VI_DestroyVi(pstViConfig);
        SAMPLE_PRT("SAMPLE_COMM_VI_CreateIsp failed!\n");
        return HI_FAILURE;
    }

    return s32Ret;
}

根据VPSS的配置创建group并启动,设置并使能VPSS,这里因为配置为VI与VPSS在线模式所以不需要进行VI与VPSS的绑定。

/*config vpss*/
hi_memset(&stVpssGrpAttr, sizeof(VPSS_GRP_ATTR_S), 0, sizeof(VPSS_GRP_ATTR_S));
stVpssGrpAttr.stFrameRate.s32SrcFrameRate    = -1;
stVpssGrpAttr.stFrameRate.s32DstFrameRate    = -1;
stVpssGrpAttr.enDynamicRange                 = DYNAMIC_RANGE_SDR8;
stVpssGrpAttr.enPixelFormat                  = enPixFormat;
stVpssGrpAttr.u32MaxW                        = stSize.u32Width;
stVpssGrpAttr.u32MaxH                        = stSize.u32Height;
stVpssGrpAttr.bNrEn                          = HI_TRUE;
stVpssGrpAttr.stNrAttr.enCompressMode        = COMPRESS_MODE_FRAME;
stVpssGrpAttr.stNrAttr.enNrMotionMode        = NR_MOTION_MODE_NORMAL;

astVpssChnAttr[VpssChn].u32Width                    = stSize.u32Width;
astVpssChnAttr[VpssChn].u32Height                   = stSize.u32Height;
astVpssChnAttr[VpssChn].enChnMode                   = VPSS_CHN_MODE_USER;
astVpssChnAttr[VpssChn].enCompressMode              = enCompressMode;
astVpssChnAttr[VpssChn].enDynamicRange              = enDynamicRange;
astVpssChnAttr[VpssChn].enVideoFormat               = enVideoFormat;
astVpssChnAttr[VpssChn].enPixelFormat               = enPixFormat;
astVpssChnAttr[VpssChn].stFrameRate.s32SrcFrameRate = 30;
astVpssChnAttr[VpssChn].stFrameRate.s32DstFrameRate = 30;
astVpssChnAttr[VpssChn].u32Depth                    = 0;
astVpssChnAttr[VpssChn].bMirror                     = HI_FALSE;
astVpssChnAttr[VpssChn].bFlip                       = HI_FALSE;
astVpssChnAttr[VpssChn].stAspectRatio.enMode        = ASPECT_RATIO_NONE;

    /*start vpss*/
    abChnEnable[0] = HI_TRUE;
    s32Ret = SAMPLE_COMM_VPSS_Start(VpssGrp, abChnEnable, &stVpssGrpAttr, astVpssChnAttr);
    if (HI_SUCCESS != s32Ret)
    {
        SAMPLE_PRT("start vpss group failed. s32Ret: 0x%x !\n", s32Ret);
        goto EXIT1;
    }

配置编码器并启动,这里接口可以根据配置切换为不同编码器,并绑定VPSS与VENC,自动将VPSS处理完的数据发送到VENC中进行编码。

/*config venc */
stGopAttr.enGopMode  = VENC_GOPMODE_SMARTP;
stGopAttr.stSmartP.s32BgQpDelta  = 7;
stGopAttr.stSmartP.s32ViQpDelta  = 2;
stGopAttr.stSmartP.u32BgInterval = 1200;
s32Ret = SAMPLE_COMM_VENC_Start(VencChn[0], enType, enPicSize, enRcMode, u32Profile, &stGopAttr);
if (HI_SUCCESS != s32Ret)
{
    SAMPLE_PRT("start venc failed. s32Ret: 0x%x !\n", s32Ret);
    goto EXIT2;
}

s32Ret = SAMPLE_COMM_VPSS_Bind_VENC(VpssGrp, VpssChn, VencChn[0]);
if (HI_SUCCESS != s32Ret)
{
    SAMPLE_PRT("Venc bind Vpss failed. s32Ret: 0x%x !n", s32Ret);
    goto EXIT3;
}

最后配置VO并启动。

/*config vo*/
SAMPLE_COMM_VO_GetDefConfig(&stVoConfig);
stVoConfig.enDstDynamicRange = enDynamicRange;
if (1 == u32VoIntfType)
{
    stVoConfig.enVoIntfType = VO_INTF_BT1120;
}
else
{
    stVoConfig.enVoIntfType = VO_INTF_HDMI;
}
stVoConfig.enPicSize = enPicSize;

/*start vo*/
s32Ret = SAMPLE_COMM_VO_StartVO(&stVoConfig);
if (HI_SUCCESS != s32Ret)
{
    SAMPLE_PRT("start vo failed. s32Ret: 0x%x !\n", s32Ret);
    goto EXIT4;
}

可以修改VO的输出接口时序为你需要的分辨率。

/*
* Name : SAMPLE_COMM_VO_GetDefConfig
* Desc : An instance of SAMPLE_VO_CONFIG_S, which allows you to use vo immediately.
*/
HI_S32 SAMPLE_COMM_VO_GetDefConfig(SAMPLE_VO_CONFIG_S *pstVoConfig)
{
    RECT_S  stDefDispRect  = {0, 0, 1920, 1080};
    SIZE_S  stDefImageSize = {1920, 1080};
    if(NULL == pstVoConfig)
    {
        SAMPLE_PRT("Error:argument can not be NULL\n");
        return HI_FAILURE;
    }

    pstVoConfig->VoDev             = SAMPLE_VO_DEV_UHD;
#ifdef HI_FPGA
    pstVoConfig->enVoIntfType = VO_INTF_BT1120;
#else
    pstVoConfig->enVoIntfType = VO_INTF_HDMI;
#endif
    pstVoConfig->enIntfSync        = VO_OUTPUT_1080P30;
    pstVoConfig->u32BgColor        = COLOR_RGB_BLUE;
    pstVoConfig->enPixFormat       = PIXEL_FORMAT_YVU_SEMIPLANAR_420;
    pstVoConfig->stDispRect        = stDefDispRect;
    pstVoConfig->stImageSize       = stDefImageSize;
    pstVoConfig->enVoPartMode      = VO_PART_MODE_SINGLE;
    pstVoConfig->u32DisBufLen      = 3;
    pstVoConfig->enDstDynamicRange = DYNAMIC_RANGE_SDR8;
    pstVoConfig->enVoMode          = VO_MODE_1MUX;

    return HI_SUCCESS;
}

SAMPLE_COMM_VO_StartVO函数中最后需要启动相应的输出接口,HDMI由系统接口实现,MipiTx海思有提供相应的驱动hi_mipi_tx。
小注:另一篇Hi3516的文章,在SAMPLE_COMM_VO_StartVO还是里面配置输出时序。

/******************************
* Start hdmi device.
* Note : do this after vo device started.
********************************/
if(VO_INTF_HDMI == enVoIntfType)
{
    SAMPLE_COMM_VO_HdmiStartByDyRg(enIntfSync, enDstDyRg);
}

/******************************
* Start mipi_tx device.
* Note : do this after vo device started.
********************************/
if(VO_INTF_MIPI == enVoIntfType)
{
    SAMPLE_COMM_VO_StartMipiTx(enIntfSync);
}

return HI_SUCCESS;

绑定VPSS与VO自动将VPSS处理完的数据进行输出。

/*vpss bind vo*/
s32Ret = SAMPLE_COMM_VPSS_Bind_VO(VpssGrp, VpssChn, stVoConfig.VoDev, VoChn);
if (HI_SUCCESS != s32Ret)
{
    SAMPLE_PRT("vo bind vpss failed. s32Ret: 0x%x !\n", s32Ret);
    goto EXIT5;
}

创建线程,通过Venc的fd判断是否有数据流编码完成进行保存到文件中。下面的两段代码是在解释线程和fd的

s32Ret = SAMPLE_COMM_VENC_StartGetStream(VencChn, sizeof(VencChn)/sizeof(VENC_CHN));
if (HI_SUCCESS != s32Ret)
{
    SAMPLE_PRT("Get venc stream failed!\n");
    goto EXIT6;
}

PAUSE();

线程创建pthread_create,线程函数pthread_create的SAMPLE_COMM_VENC_GetVencStreamProc里面有关于fd和保存文件的函数,暂时还没有搞懂怎么用。

/******************************************************************************
* funciton : start get venc stream process thread
******************************************************************************/
HI_S32 SAMPLE_COMM_VENC_StartGetStream(VENC_CHN VeChn[],HI_S32 s32Cnt)
{
    HI_U32 i;

    gs_stPara.bThreadStart = HI_TRUE;
    gs_stPara.s32Cnt = s32Cnt;
    for(i=0; i<s32Cnt; i++)
    {
        gs_stPara.VeChn[i] = VeChn[i];
    }
    return pthread_create(&gs_VencPid, 0, SAMPLE_COMM_VENC_GetVencStreamProc, (HI_VOID*)&gs_stPara);
}

SAMPLE_COMM_VENC_GetVencStreamProc函数,其中step 1: check & prepare save-file & venc-fd

/******************************************************************************
* funciton : get stream from each channels and save them
******************************************************************************/
HI_VOID* SAMPLE_COMM_VENC_GetVencStreamProc(HI_VOID* p)
{
    HI_S32 i;
    HI_S32 s32ChnTotal;
    VENC_CHN_ATTR_S stVencChnAttr;
    SAMPLE_VENC_GETSTREAM_PARA_S* pstPara;
    HI_S32 maxfd = 0;
    struct timeval TimeoutVal;
    fd_set read_fds;
    HI_U32 u32PictureCnt[VENC_MAX_CHN_NUM]={0};
    HI_S32 VencFd[VENC_MAX_CHN_NUM];
    HI_CHAR aszFileName[VENC_MAX_CHN_NUM][64];
    FILE* pFile[VENC_MAX_CHN_NUM];
    char szFilePostfix[10];
    VENC_CHN_STATUS_S stStat;
    VENC_STREAM_S stStream;
    HI_S32 s32Ret;
    VENC_CHN VencChn;
    PAYLOAD_TYPE_E enPayLoadType[VENC_MAX_CHN_NUM];
    VENC_STREAM_BUF_INFO_S stStreamBufInfo[VENC_MAX_CHN_NUM];

    prctl(PR_SET_NAME, "GetVencStream", 0,0,0);

    pstPara = (SAMPLE_VENC_GETSTREAM_PARA_S*)p;
    s32ChnTotal = pstPara->s32Cnt;
    /******************************************
     step 1:  check & prepare save-file & venc-fd
    ******************************************/
    if (s32ChnTotal >= VENC_MAX_CHN_NUM)
    {
        SAMPLE_PRT("input count invaild\n");
        return NULL;
    }
    for (i = 0; i < s32ChnTotal; i++)
    {
        /* decide the stream file name, and open file to save stream */
        VencChn = pstPara->VeChn[i];
        s32Ret = HI_MPI_VENC_GetChnAttr(VencChn, &stVencChnAttr);
        if (s32Ret != HI_SUCCESS)
        {
            SAMPLE_PRT("HI_MPI_VENC_GetChnAttr chn[%d] failed with %#x!\n", \
                       VencChn, s32Ret);
            return NULL;
        }
        enPayLoadType[i] = stVencChnAttr.stVencAttr.enType;

        s32Ret = SAMPLE_COMM_VENC_GetFilePostfix(enPayLoadType[i], szFilePostfix);
        if (s32Ret != HI_SUCCESS)
        {
            SAMPLE_PRT("SAMPLE_COMM_VENC_GetFilePostfix [%d] failed with %#x!\n", \
                       stVencChnAttr.stVencAttr.enType, s32Ret);
            return NULL;
        }
        if(PT_JPEG != enPayLoadType[i])
        {
            snprintf(aszFileName[i],32, "stream_chn%d%s", i, szFilePostfix);

            pFile[i] = fopen(aszFileName[i], "wb");
            if (!pFile[i])
            {
                SAMPLE_PRT("open file[%s] failed!\n",
                           aszFileName[i]);
                return NULL;
            }
        }
        /* Set Venc Fd. */
        VencFd[i] = HI_MPI_VENC_GetFd(i);
        if (VencFd[i] < 0)
        {
            SAMPLE_PRT("HI_MPI_VENC_GetFd failed with %#x!\n",
                       VencFd[i]);
            return NULL;
        }
        if (maxfd <= VencFd[i])
        {
            maxfd = VencFd[i];
        }

不同异常进行资源释放的处理。

   SAMPLE_COMM_VENC_StopGetStream();

EXIT6:
    SAMPLE_COMM_VPSS_UnBind_VO(VpssGrp, VpssChn, stVoConfig.VoDev, VoChn);
EXIT5:
    SAMPLE_COMM_VO_StopVO(&stVoConfig);
EXIT4:
    SAMPLE_COMM_VPSS_UnBind_VENC(VpssGrp, VpssChn, VencChn[0]);
EXIT3:
    SAMPLE_COMM_VENC_Stop(VencChn[0]);
EXIT2:
    SAMPLE_COMM_VPSS_Stop(VpssGrp, abChnEnable);
EXIT1:
    SAMPLE_COMM_VI_StopVi(&stViConfig);
EXIT:
    SAMPLE_COMM_SYS_Exit();
    return s32Ret;
}

3、SAMPLE_VIO_ViOnlineVpssOfflineRoute

该函数实现VI在线VPSS离线功能,基本配置实现与上述相同,只有几处不同的地方。

需要配置模式为VI在线VPSS离线

/*config vi*/
SAMPLE_COMM_VI_GetSensorInfo(&stViConfig);

stViConfig.s32WorkingViNum                                   = s32ViCnt;
stViConfig.as32WorkingViId[0]                                = 0;
stViConfig.astViInfo[s32WorkSnsId].stSnsInfo.MipiDev         = ViDev;
stViConfig.astViInfo[s32WorkSnsId].stDevInfo.ViDev           = ViDev;
stViConfig.astViInfo[s32WorkSnsId].stDevInfo.enWDRMode       = enWDRMode;
stViConfig.astViInfo[s32WorkSnsId].stPipeInfo.enMastPipeMode = VI_OFFLINE_VPSS_OFFLINE;
stViConfig.astViInfo[s32WorkSnsId].stPipeInfo.aPipe[0]       = ViPipe;
stViConfig.astViInfo[s32WorkSnsId].stPipeInfo.aPipe[1]       = -1;
stViConfig.astViInfo[s32WorkSnsId].stPipeInfo.aPipe[2]       = -1;
stViConfig.astViInfo[s32WorkSnsId].stPipeInfo.aPipe[3]       = -1;
stViConfig.astViInfo[s32WorkSnsId].stChnInfo.ViChn           = ViChn;
stViConfig.astViInfo[s32WorkSnsId].stChnInfo.enPixFormat     = enPixFormat;
stViConfig.astViInfo[s32WorkSnsId].stChnInfo.enDynamicRange  = enDynamicRange;
stViConfig.astViInfo[s32WorkSnsId].stChnInfo.enVideoFormat   = enVideoFormat;
stViConfig.astViInfo[s32WorkSnsId].stChnInfo.enCompressMode  = enCompressMode;

需要绑定VI与VPSS实现将VI捕获的内容自动发送到VPSS中处理。

/*start vpss*/
abChnEnable[0] = HI_TRUE;
s32Ret = SAMPLE_COMM_VPSS_Start(VpssGrp, abChnEnable, &stVpssGrpAttr, astVpssChnAttr);
if (HI_SUCCESS != s32Ret)
{
    SAMPLE_PRT("start vpss group failed. s32Ret: 0x%x !\n", s32Ret);
    goto EXIT1;
}

/*vi bind vpss*/
s32Ret = SAMPLE_COMM_VI_Bind_VPSS(ViPipe, ViChn, VpssGrp);
if (HI_SUCCESS != s32Ret)
{
    SAMPLE_PRT("vi bind vpss failed. s32Ret: 0x%x !\n", s32Ret);
    goto EXIT2;
}

VPSS绑定VO,

    /*vpss bind vo*/
    s32Ret = SAMPLE_COMM_VPSS_Bind_VO(VpssGrp, VpssChn, stVoConfig.VoDev, VoChn);
    if (HI_SUCCESS != s32Ret)
    {
        SAMPLE_PRT("vo bind vpss failed. s32Ret: 0x%x !\n", s32Ret);
        goto EXIT6;
    }
    
    

按回车设置VI镜头畸变校正。

    s32Ret = SAMPLE_COMM_VENC_StartGetStream(VencChn, sizeof(VencChn)/sizeof(VENC_CHN));
    if (HI_SUCCESS != s32Ret)
    {
        SAMPLE_PRT("Get venc stream failed!\n");
        goto EXIT7;
    }

    printf("Press Enter key to Enable LDC!\n");
    getchar();

    stLDCAttr.bEnable = HI_TRUE;
    stLDCAttr.stAttr.bAspect = 0;
    stLDCAttr.stAttr.s32XRatio = 100;
    stLDCAttr.stAttr.s32YRatio = 100;
    stLDCAttr.stAttr.s32XYRatio = 100;
    stLDCAttr.stAttr.s32CenterXOffset = 0;
    stLDCAttr.stAttr.s32CenterYOffset = 0;
    stLDCAttr.stAttr.s32DistortionRatio = 500;

    s32Ret = HI_MPI_VI_SetChnLDCAttr(ViPipe, ViChn, &stLDCAttr);
    if (HI_SUCCESS != s32Ret)
    {
        SAMPLE_PRT("HI_MPI_VI_SetChnLDCAttr failed witfh %d\n", s32Ret);
        goto EXIT8;
    }

按回车启动防抖动设置。

printf("Press Enter key to Enable DIS!\n");
getchar();

stDISConfig.enMode              = DIS_MODE_6_DOF_GME;
stDISConfig.enMotionLevel       = DIS_MOTION_LEVEL_NORMAL;
stDISConfig.u32CropRatio        = 80;
stDISConfig.u32BufNum           = 5;
stDISConfig.enPdtType           = DIS_PDT_TYPE_IPC;
stDISConfig.u32GyroOutputRange  = 0;
stDISConfig.u32FrameRate        = 30;
stDISConfig.bScale              = HI_TRUE;
stDISConfig.bCameraSteady       = HI_FALSE;

s32Ret = HI_MPI_VI_SetChnDISConfig(ViPipe, ViChn, &stDISConfig);
if (HI_SUCCESS != s32Ret)
{
    SAMPLE_PRT("HI_MPI_VI_SetChnDISConfig failed.s32Ret:0x%x !\n", s32Ret);
    goto EXIT5;
}

stDISAttr.bEnable               = HI_TRUE;
stDISAttr.u32MovingSubjectLevel = 0;
stDISAttr.s32RollingShutterCoef = 0;
stDISAttr.u32ViewAngle          = 1000;
stDISAttr.bStillCrop            = HI_FALSE;
stDISAttr.u32HorizontalLimit    = 512;
stDISAttr.u32VerticalLimit      = 512;

s32Ret = HI_MPI_VI_SetChnDISAttr(ViPipe, ViChn, &stDISAttr);
if (HI_SUCCESS != s32Ret)
{
    SAMPLE_PRT("HI_MPI_VI_SetChnDISAttr failed.s32Ret:0x%x !\n", s32Ret);
    goto EXIT5;
}

按回车开启VI通道展宽功能,展宽图像操作是为了使用户便于观察记录信息显示的细节情况而设立的,其功能与压缩图像相反,默认是最宽状态。

printf("Press Enter key to Enable Spread!\n");
getchar();

stSpreadAttr.bEnable        = HI_TRUE;
stSpreadAttr.u32SpreadCoef  = 16;
stSpreadAttr.stDestSize.u32Width = 3840;
stSpreadAttr.stDestSize.u32Height = 2160;

s32Ret = HI_MPI_VI_SetChnSpreadAttr(ViPipe, ViChn, &stSpreadAttr);
if (HI_SUCCESS != s32Ret)
{
    SAMPLE_PRT("HI_MPI_VI_SetChnSpreadAttr failed witfh %d\n", s32Ret);
    goto EXIT5;
}

PAUSE();

4、SAMPLE_VIO_ViDoublePipeRoute

改函数配置2个PIPE,并且配置2个VPSS。

HI_S32 SAMPLE_VIO_ViDoublePipeRoute(HI_U32 u32VoIntfType)
{
    HI_S32             s32Ret;

    HI_S32             s32ViCnt       = 1;
    VI_DEV             ViDev          = 0;
    VI_PIPE            ViPipe[2]      = {0, 1};
    VI_CHN             ViChn          = 0;
    HI_S32             s32WorkSnsId   = 0;
    SAMPLE_VI_CONFIG_S stViConfig;

    SIZE_S             stSize;
    VB_CONFIG_S        stVbConf;
    PIC_SIZE_E         enPicSize;
    HI_U32             u32BlkSize;

    VO_CHN             VoChn          = 0;
    SAMPLE_VO_CONFIG_S stVoConfig;

    WDR_MODE_E         enWDRMode      = WDR_MODE_NONE;
    DYNAMIC_RANGE_E    enDynamicRange = DYNAMIC_RANGE_SDR8;
    PIXEL_FORMAT_E     enPixFormat    = PIXEL_FORMAT_YVU_SEMIPLANAR_420;
    VIDEO_FORMAT_E     enVideoFormat  = VIDEO_FORMAT_LINEAR;
    COMPRESS_MODE_E    enCompressMode = COMPRESS_MODE_NONE;

    VPSS_GRP           VpssGrp[2]     = {0, 1};
    VPSS_GRP_ATTR_S    stVpssGrpAttr;
    VPSS_CHN           VpssChn        = VPSS_CHN0;
    HI_BOOL            abChnEnable[VPSS_MAX_PHY_CHN_NUM] = {0};
    VPSS_CHN_ATTR_S    astVpssChnAttr[VPSS_MAX_PHY_CHN_NUM];

    VENC_CHN           VencChn[1]  = {0};
    PAYLOAD_TYPE_E     enType      = PT_H265;
    SAMPLE_RC_E        enRcMode    = SAMPLE_RC_CBR;
    HI_U32             u32Profile  = 0;
    VENC_GOP_ATTR_S    stGopAttr;

由于第0个PIPE配置为VI离线VPSS在线所以其他PIPE也必须配置为VI离线VPSS在线,在SAMPLE_COMM_VI_SetParam函数中可以看到该逻辑。

 /*config vi*/
    SAMPLE_COMM_VI_GetSensorInfo(&stViConfig);

    stViConfig.s32WorkingViNum                                   = s32ViCnt;
    stViConfig.as32WorkingViId[0]                                = 0;
    stViConfig.astViInfo[s32WorkSnsId].stSnsInfo.MipiDev         = ViDev;
    stViConfig.astViInfo[s32WorkSnsId].stDevInfo.ViDev           = ViDev;
    stViConfig.astViInfo[s32WorkSnsId].stDevInfo.enWDRMode       = enWDRMode;
    stViConfig.astViInfo[s32WorkSnsId].stPipeInfo.enMastPipeMode = VI_OFFLINE_VPSS_ONLINE;
    stViConfig.astViInfo[s32WorkSnsId].stPipeInfo.aPipe[0]       = ViPipe[0];
    stViConfig.astViInfo[s32WorkSnsId].stPipeInfo.aPipe[1]       = ViPipe[1];
    stViConfig.astViInfo[s32WorkSnsId].stPipeInfo.aPipe[2]       = -1;
    stViConfig.astViInfo[s32WorkSnsId].stPipeInfo.aPipe[3]       = -1;
    stViConfig.astViInfo[s32WorkSnsId].stChnInfo.ViChn           = ViChn;
    stViConfig.astViInfo[s32WorkSnsId].stChnInfo.enPixFormat     = enPixFormat;
    stViConfig.astViInfo[s32WorkSnsId].stChnInfo.enDynamicRange  = enDynamicRange;
    stViConfig.astViInfo[s32WorkSnsId].stChnInfo.enVideoFormat   = enVideoFormat;
    stViConfig.astViInfo[s32WorkSnsId].stChnInfo.enCompressMode  = enCompressMode;

    stViConfig.astViInfo[s32WorkSnsId].stPipeInfo.bMultiPipe     = HI_TRUE;

因为VPSS是在线模式所以不用绑定VI与VPSS,并且将VPSSGRP1绑定到编码器,VPSSGRP0绑定到VO输出

/*start vpss*/
abChnEnable[0] = HI_TRUE;
s32Ret = SAMPLE_COMM_VPSS_Start(VpssGrp[0], abChnEnable, &stVpssGrpAttr, astVpssChnAttr);
if (HI_SUCCESS != s32Ret)
{
    SAMPLE_PRT("start vpss group failed. s32Ret: 0x%x !\n", s32Ret);
    goto EXIT1;
}

s32Ret = SAMPLE_COMM_VPSS_Start(VpssGrp[1], abChnEnable, &stVpssGrpAttr, astVpssChnAttr);
if (HI_SUCCESS != s32Ret)
{
    SAMPLE_PRT("start vpss group failed. s32Ret: 0x%x !\n", s32Ret);
    goto EXIT2;
}

/*config venc */
stGopAttr.enGopMode  = VENC_GOPMODE_SMARTP;
stGopAttr.stSmartP.s32BgQpDelta  = 7;
stGopAttr.stSmartP.s32ViQpDelta  = 2;
stGopAttr.stSmartP.u32BgInterval = 1200;
s32Ret = SAMPLE_COMM_VENC_Start(VencChn[0], enType, enPicSize, enRcMode, u32Profile, &stGopAttr);
if (HI_SUCCESS != s32Ret)
{
    SAMPLE_PRT("start venc failed. s32Ret: 0x%x !\n", s32Ret);
    goto EXIT3;
}

s32Ret = SAMPLE_COMM_VPSS_Bind_VENC(VpssGrp[1], VpssChn, VencChn[0]);
if (HI_SUCCESS != s32Ret)
{
    SAMPLE_PRT("Venc bind Vpss failed. s32Ret: 0x%x !n", s32Ret);
    goto EXIT4;
}
/*vpss bind vo*/
s32Ret = SAMPLE_COMM_VPSS_Bind_VO(VpssGrp[0], VpssChn, stVoConfig.VoDev, VoChn);
if (HI_SUCCESS != s32Ret)
{
    SAMPLE_PRT("vo bind vpss failed. s32Ret: 0x%x !\n", s32Ret);
    goto EXIT6;
}

s32Ret = SAMPLE_COMM_VENC_StartGetStream(VencChn, sizeof(VencChn)/sizeof(VENC_CHN));
if (HI_SUCCESS != s32Ret)
{
    SAMPLE_PRT("Get venc stream failed!\n");
    goto EXIT7;
}

PAUSE();

5、SAMPLE_VIO_ViDoubleChnRoute

该函数配置双通道。

HI_S32 SAMPLE_VIO_ViDoubleChnRoute(HI_U32 u32VoIntfType)
{
    HI_S32             s32Ret;

    HI_S32             s32ViCnt       = 1;
    VI_DEV             ViDev          = 0;
    VI_PIPE            ViPipe         = 0;
    VI_CHN             ViChn[2]       = {0, 1};
    HI_S32             s32WorkSnsId   = 0;
    SAMPLE_VI_CONFIG_S stViConfig;
    VI_CHN_ATTR_S      stChnAttr;
    VI_EARLY_INTERRUPT_S stEarlyInterrupt;

将VPSS设置为离线模式,并且将VI通道0与VPSS绑定。

/*config vi*/
SAMPLE_COMM_VI_GetSensorInfo(&stViConfig);

stViConfig.s32WorkingViNum                                   = s32ViCnt;
stViConfig.as32WorkingViId[0]                                = 0;
stViConfig.astViInfo[s32WorkSnsId].stSnsInfo.MipiDev         = ViDev;
stViConfig.astViInfo[s32WorkSnsId].stDevInfo.ViDev           = ViDev;
stViConfig.astViInfo[s32WorkSnsId].stDevInfo.enWDRMode       = enWDRMode;
stViConfig.astViInfo[s32WorkSnsId].stPipeInfo.enMastPipeMode = VI_ONLINE_VPSS_OFFLINE;
stViConfig.astViInfo[s32WorkSnsId].stPipeInfo.aPipe[0]       = ViPipe;
stViConfig.astViInfo[s32WorkSnsId].stPipeInfo.aPipe[1]       = -1;
stViConfig.astViInfo[s32WorkSnsId].stPipeInfo.aPipe[2]       = -1;
stViConfig.astViInfo[s32WorkSnsId].stPipeInfo.aPipe[3]       = -1;
stViConfig.astViInfo[s32WorkSnsId].stChnInfo.ViChn           = ViChn[0];
stViConfig.astViInfo[s32WorkSnsId].stChnInfo.enPixFormat     = enPixFormat;
stViConfig.astViInfo[s32WorkSnsId].stChnInfo.enDynamicRange  = enDynamicRange;
stViConfig.astViInfo[s32WorkSnsId].stChnInfo.enVideoFormat   = enVideoFormat;
stViConfig.astViInfo[s32WorkSnsId].stChnInfo.enCompressMode  = enCompressMode;
/*vi bind vpss*/
s32Ret = SAMPLE_COMM_VI_Bind_VPSS(ViPipe, ViChn[0], VpssGrp);
if (HI_SUCCESS != s32Ret)
{
    SAMPLE_PRT("vi bind vpss failed. s32Ret: 0x%x !\n", s32Ret);
    goto EXIT2;
}

VI通道1配置提前上报中断,并且将VI通道1与VO进行绑定直接输出图像

/*start vi chn1*/
s32Ret = HI_MPI_VI_GetChnAttr(ViPipe, ViChn[0], &stChnAttr);
if (HI_SUCCESS != s32Ret)
{
    SAMPLE_PRT("HI_MPI_VI_GetChnAttr failed. s32Ret: 0x%x !\n", s32Ret);
    goto EXIT3;
}

stChnAttr.stSize.u32Width = stSize.u32Width / 2;
stChnAttr.stSize.u32Height = stSize.u32Height / 2;

s32Ret = HI_MPI_VI_SetChnAttr(ViPipe, ViChn[1], &stChnAttr);
if (HI_SUCCESS != s32Ret)
{
    SAMPLE_PRT("HI_MPI_VI_GetChnAttr failed. s32Ret: 0x%x !\n", s32Ret);
    goto EXIT3;
}

stEarlyInterrupt.bEnable = HI_TRUE;
stEarlyInterrupt.u32LineCnt = stChnAttr.stSize.u32Height / 2;
s32Ret = HI_MPI_VI_SetChnEarlyInterrupt(ViPipe, ViChn[1], &stEarlyInterrupt);
if (HI_SUCCESS != s32Ret)
{
    SAMPLE_PRT("HI_MPI_VI_SetChnEarlyInterrupt failed. s32Ret: 0x%x !\n", s32Ret);
    goto EXIT3;
}

s32Ret = HI_MPI_VI_EnableChn(ViPipe, ViChn[1]);
if (HI_SUCCESS != s32Ret)
{
    SAMPLE_PRT("HI_MPI_VI_EnableChn failed. s32Ret: 0x%x !\n", s32Ret);
    goto EXIT3;
}
/*vpss bind vo*/
s32Ret = SAMPLE_COMM_VI_Bind_VO(ViPipe, ViChn[1], stVoConfig.VoDev, VoChn);
if (HI_SUCCESS != s32Ret)
{
    SAMPLE_PRT("vo bind vpss failed. s32Ret: 0x%x !\n", s32Ret);
    goto EXIT7;
}

s32Ret = SAMPLE_COMM_VENC_StartGetStream(VencChn, sizeof(VencChn)/sizeof(VENC_CHN));
if (HI_SUCCESS != s32Ret)
{
    SAMPLE_PRT("Get venc stream failed!\n");
    goto EXIT8;
}

PAUSE();

6、SAMPLE_VIO_ViWdrSwitch

该函数只是在最后添加切换到2帧合成行WDR模式,需要设置两个PIPE。

printf("switch to wdr mode========\n");
getchar();

SAMPLE_COMM_VPSS_Stop(VpssGrp, abChnEnable);
SAMPLE_COMM_VI_SwitchMode_StopVI(&stViConfig);

if (SONY_IMX290_MIPI_2M_30FPS_12BIT == stViConfig.astViInfo[s32WorkSnsId].stSnsInfo.enSnsType)
{
    stViConfig.astViInfo[s32WorkSnsId].stSnsInfo.enSnsType = SONY_IMX290_MIPI_2M_30FPS_12BIT_WDR2TO1;
}
else if (SONY_IMX334_MIPI_8M_30FPS_12BIT == stViConfig.astViInfo[s32WorkSnsId].stSnsInfo.enSnsType)
{
    stViConfig.astViInfo[s32WorkSnsId].stSnsInfo.enSnsType = SONY_IMX334_MIPI_8M_30FPS_12BIT_WDR2TO1;
}

stViConfig.astViInfo[s32WorkSnsId].stDevInfo.enWDRMode = WDR_MODE_2To1_LINE;
stViConfig.astViInfo[s32WorkSnsId].stPipeInfo.aPipe[0]       = ViPipe;
stViConfig.astViInfo[s32WorkSnsId].stPipeInfo.aPipe[1]       = 1;

SAMPLE_COMM_VI_SwitchMode(&stViConfig);

s32Ret = SAMPLE_COMM_VPSS_Start(VpssGrp, abChnEnable, &stVpssGrpAttr, astVpssChnAttr);
if (HI_SUCCESS != s32Ret)
{
    SAMPLE_PRT("start vpss group failed. s32Ret: 0x%x !\n", s32Ret);
    goto EXIT8;
}

再重新切换回线性模式,只需要一个PIPE。

printf("switch to linear mode========\n");
getchar();

SAMPLE_COMM_VPSS_Stop(VpssGrp, abChnEnable);
SAMPLE_COMM_VI_SwitchMode_StopVI(&stViConfig);

stViConfig.astViInfo[s32WorkSnsId].stSnsInfo.enSnsType = SENSOR0_TYPE;
stViConfig.astViInfo[s32WorkSnsId].stDevInfo.enWDRMode = WDR_MODE_NONE;
stViConfig.astViInfo[s32WorkSnsId].stPipeInfo.aPipe[0]       = ViPipe;
stViConfig.astViInfo[s32WorkSnsId].stPipeInfo.aPipe[1]       = -1;

SAMPLE_COMM_VI_SwitchMode(&stViConfig);
s32Ret = SAMPLE_COMM_VPSS_Start(VpssGrp, abChnEnable, &stVpssGrpAttr, astVpssChnAttr);
if (HI_SUCCESS != s32Ret)
{
    SAMPLE_PRT("start vpss group failed. s32Ret: 0x%x !\n", s32Ret);
    goto EXIT8;
}

PAUSE();

7、SAMPLE_VIO_ViVpssLowDelay

该函数只是增加设置VI镜头畸变校正并按回车设置低延时,低延时指图像写出指定的行数到 DDR 后,VI上报一个中断,把图像发给后端模块处理,可以减少延时,且硬件会有机制保证图像是先写后读,不会出现读图像错误。

stLDCAttr.bEnable = HI_TRUE;
stLDCAttr.stAttr.bAspect = 0;
stLDCAttr.stAttr.s32XRatio = 100;
stLDCAttr.stAttr.s32YRatio = 100;
stLDCAttr.stAttr.s32XYRatio = 100;
stLDCAttr.stAttr.s32CenterXOffset = 0;
stLDCAttr.stAttr.s32CenterYOffset = 0;
stLDCAttr.stAttr.s32DistortionRatio = 500;
s32Ret = HI_MPI_VI_SetChnLDCAttr(ViPipe, ViChn, &stLDCAttr);
if (HI_SUCCESS != s32Ret)
{
    SAMPLE_PRT("HI_MPI_VI_SetChnLDCAttr failed witfh %d\n", s32Ret);
    goto EXIT8;
}

printf("Press Enter key to Enable LowDelay!\n");
getchar();

stLowDelayInfo.bEnable = HI_TRUE;
stLowDelayInfo.u32LineCnt = stSize.u32Height / 2;
HI_MPI_VI_SetChnLowDelayAttr(ViPipe, ViChn, &stLowDelayInfo);
if (HI_SUCCESS != s32Ret)
{
    SAMPLE_PRT("HI_MPI_VI_SetChnLowDelayAttr failed witfh %d\n", s32Ret);
    goto EXIT8;
}

PAUSE();

8、小结:

剩下的几个功能大体流程都相识,只是增加调用了几个关于像低延时、旋转这样的接口使用示例,虽然海思的sample例程比较粗糙,但上手的话还是可以基于sample进行开发,思路还是在sample了解与《HiMPP V4.0 媒体处理软件开发参考》查找相关说明。

注:感谢博主工藤_新一的整理,本文参考解析sample例程学习官方API:解析VIO Sample例程,如有侵权,请及时联系小编,小编会及时删除的。

  • 3
    点赞
  • 49
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ltqshs

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值