以下内容源于朱有鹏嵌入式课程的学习与整理,如有侵权请告知删除。

前言

本文将详细介绍博文第二季3:sample_venc.c的整体分析提及的“配置视频捕获模块”。

分析方法上,我们首先介绍VI模块相关的宽动态、设备、通道等概念,然后绘制VI模块的函数调用关系图谱,接着讲解具体的代码细节。学习效果上,要把控全局,掌握一些新的概念和对应的数据结构,理解关键操作在哪里设置,将来需要修改的时候能找到地方。

一、VI模块的相关概念

1、离线/在线模式

VI和VPSS的协作模式分为以下 2 种:

VI/VPSS 离线模式,是指 VI 进行时序解析后将图像数据写出到 DDR,VPSS 从DDR 中载入 VI 采集的数据进行图像处理,是传统 Hi3518/Hi3520D 等芯片的VI/VPSS 的协作模式。

VI/VPSS 在线模式,是指 VI 进行时序解析后直接在芯片内部将数据传递到 VPSS,中间无 DDR 写出的过程。在线模式可以省一定的带宽和内存,降低端到端的延时。需要注意的是,在线模式时,因为 VI 不写出数据到 DDR,无法进行CoverEx、OverlayEx、 Rotate、 LDC 等操作,需要在 VPSS 各通道写出后再进行Rotate/LDC 等处理,而且有些功能只在离线下能支持,比如 DIS。

这两种模式的切换可以由 load 脚本(即博客第4季3:Hi3518e的sensor接口引脚复用设置中的脚本文件load3518e)中的参数 vi_vpss_online来控制。如下所示,在insert_ko()函数中有:

insert_ko()
{# sys configsys_config;# driver loadinsmod mmz.ko mmz=anonymous,0,$mmz_start,$mmz_size anony=1 || report_errorinsmod hi_media.koinsmod hi3518e_base.koinsmod hi3518e_sys.ko vi_vpss_online=$b_arg_online sensor=$SNS_TYPE//这里#省略部分代码}

我们可以在调用load3518e文件时传入参数offline,如果不设置这个参数,则默认为online。

我们做实验时都是没有设置这个参数的,因此都是online的。

2、VI模块的功能

通过 BT656/601/1120 接口或 DC 接口(即并口)、MIPI Rx(含 MIPI 接口、LVDS 接口和 HISPI 接口)接收视频数据。当工作在离线模式时,将接收到的数据存入到指定的内存区域;当工作在在线模式时,VI会将数据直接送给 VPSS。在此过程中,VI 模块可以对接收到的原始视频图像数据进行裁剪等处理,并实现一路原始视频图像输入,输出一路视频图像功能(VI设备只有一个且它的通道只有一个)。

3、VI模块的组成

VI模块包含三大内容:和sensor对接的部分(采用什么接口等等内容)、ISP、VI设备和通道。

其中ISP是“ image signal process ”的缩写,即图像信号处理。HI3518E内部集成了ISP硬件单元,这个ISP单元在功能上隶属于VI模块。

HI3518E的硬件单元功能框图如下,可知HI3518E芯片只有一个VI设备(即Dev0),它支持上面提到的那些接口的输入。注意,VI设备不是sensor,它是HI3518E内部的硬件单元。

HI3518E的VI通道功能框图如下。这个VI设备只包含一个物理通道(即Chn0)。它支持720@30、1080@30等典型分辨率。这个物理通道(Chn0)和对应的VI设备(Dev0)是固定绑定的, 不能改变它们的绑定关系。HI3518E最多支持16个扩展通道,它们是物理通道的扩展(它们的数据来源于物理通道),主要实现缩放功能。

4、Sensor与SoC之间的接口

Sensor与SoC之间的接口主要包括MIPI、LVDS、DC(即并口),具体介绍见博客第4季2:并口、MIPI、LVDS的简介。

我们的AR0130和OV9712,和HI3518E之间的数据接口就是DC,而非MIPI。

5、宽动态(WDR)

简单地理解,宽动态技术即同一幅图的不同区域,其曝光程度不一样。

具体介绍见宽动态 (WDR)介绍和理解_Mr.TangR的博客。

实现宽动态这个功能需要硬件的支持,有些sensor支持,有些sensor不支持,比如我们的AR0130和OV9712就不支持这个功能。

二、VI模块的函数调用关系

VI模块的函数调用关系如下所示(或者见链接)。

SAMPLE_COMM_VI_StartViIsSensorInput//此函数检测是否sensor输入,因为有的可能是其他输入方式SAMPLE_COMM_VI_StartIspAndVistep1:SAMPLE_COMM_VI_StartMIPI//此函数操作sensor驱动中的ioctl函数,对sensor进行必要的初始化SAMPLE_COMM_VI_SetMipiAttrfd = open("/dev/hi_mipi", O_RDWR);ioctl(fd, HI_MIPI_SET_DEV_ATTR, pstcomboDevAttr)step2:SAMPLE_COMM_ISP_Init//此函数初始化内部的ISP单元sensor_register_callback//…………具体介绍在第4季4篇章。此函数在sensor的驱动里package\mpp\component\isp\sensor\ar0130\ar0130_cmos.c,可以看文档《ISP_3A开发指南.pdf》HI_MPI_AE_Register//此函数注册AE单元,AE即自动曝光HI_MPI_AWB_Register//此函数注册AWB单元,AWB即白平衡HI_MPI_AF_Register//此函数注册自动对焦(AF)单元HI_MPI_ISP_MemInit//此函数给ISP单元分配必要的内存HI_MPI_ISP_SetWDRMode//此函数设置宽动态相关属性HI_MPI_ISP_SetPubAttr//此函数通过传参(函数前填充了参数内容)告知ISP单元sensor的一些属性以便ISPHI_MPI_ISP_Init//此函数初始化ISPstep3:SAMPLE_COMM_ISP_Run//此函数通过创建线程,让ISP运行pthread_create(&gs_IspPid, &attr, (void* (*)(void*))Test_ISP_Run, NULL)Test_ISP_RunHI_MPI_ISP_Runstep4:SAMPLE_COMM_VI_StartDev//此函数打开(采集图像的)设备HI_MPI_VI_SetDevAttr//设置dev的属性HI_MPI_ISP_GetWDRModeHI_MPI_VI_SetWDRAttrHI_MPI_VI_EnableDev//启动dev单元step5:SAMPLE_COMM_VI_StartChn//此函数打开通道HI_MPI_VI_SetChnAttr//设置通道属性HI_MPI_VI_SetRotateHI_MPI_VI_EnableChn//打开通道

由此可知,该模块涉及以下几个步骤:

  • Sensor的初始化操作
  • ISP单元的初始化与运行操作(注册3A、设置宽动态、初始化ISP、运行 ISP等内容)
  • 打开采集图像的设备(设置设备的属性,然后启动设备)
  • 打开通道(设置通道的属性,然后打开通道)

下面我们将详细介绍这几个步骤涉及到的概念与代码细节。

三、VI模块代码详解

1、VI模块的整体代码

其中stViConfig这个变量的数据类型是SAMPLE_VI_CONFIG_S,其成员enViMode表示摄像头sensor的种类,不同sensor有着不同的分辨率和帧率;enRotate表示是否旋转图像;enNorm表示图像制式;enViChnSet表示是否将图像flip或者mirror。具体介绍见博客内容第二季4:MPP模块的初始化。

    /******************************************step 3: start vi dev & chn to capture******************************************/stViConfig.enViMode   = SENSOR_TYPE;//sensor的类型定义在Makefile.param文件中stViConfig.enRotate   = ROTATE_NONE;//图像是否旋转stViConfig.enNorm     = VIDEO_ENCODING_MODE_AUTO;//图像制式stViConfig.enViChnSet = VI_CHN_SET_NORMAL;//是否flip或mirrorstViConfig.enWDRMode  = WDR_MODE_NONE;//设置宽动态相关内容s32Ret = SAMPLE_COMM_VI_StartVi(&stViConfig);if (HI_SUCCESS != s32Ret){SAMPLE_PRT("start vi failed!\n");goto END_VENC_MJPEG_JPEG_1;}

SAMPLE_COMM_VI_StartVi函数位于sample_comm_venc.c文件,代码内容如下。

HI_S32 SAMPLE_COMM_VI_StartVi(SAMPLE_VI_CONFIG_S* pstViConfig)
{HI_S32 s32Ret = HI_SUCCESS;SAMPLE_VI_MODE_E enViMode;  if(!pstViConfig){SAMPLE_PRT("%s: null ptr\n", __FUNCTION__);return HI_FAILURE;}enViMode = pstViConfig->enViMode;if(!IsSensorInput(enViMode)){s32Ret = SAMPLE_COMM_VI_StartBT656(pstViConfig);//从其他渠道,比如电视图像信号}else{s32Ret = SAMPLE_COMM_VI_StartIspAndVi(pstViConfig);//此处是sensor输入的,我们分析这个路线}return s32Ret;
}

其中IsSensorInput函数内部通过根据传参是否为某个具体型号的sensor,来判断图像数据是否为sensor输入,这里不再赘述。SAMPLE_COMM_VI_StartIspAndVi函数是真正开启VI模块的函数,我们将详细分析。

2、函数SAMPLE_COMM_VI_StartIspAndVi的分析

函数SAMPLE_COMM_VI_StartIspAndVi位于sample_comm_venc.c文件中,具体内容如下。

HI_S32 SAMPLE_COMM_VI_StartIspAndVi(SAMPLE_VI_CONFIG_S* pstViConfig)
{HI_S32 i, s32Ret = HI_SUCCESS;VI_DEV ViDev;VI_CHN ViChn;HI_U32 u32DevNum = 1;HI_U32 u32ChnNum = 1;SIZE_S stTargetSize;RECT_S stCapRect;SAMPLE_VI_MODE_E enViMode;if(!pstViConfig){SAMPLE_PRT("%s: null ptr\n", __FUNCTION__);return HI_FAILURE;}enViMode = pstViConfig->enViMode;/******************************************step 1: mipi configure******************************************/s32Ret = SAMPLE_COMM_VI_StartMIPI(pstViConfig);if (HI_SUCCESS != s32Ret){SAMPLE_PRT("%s: MIPI init failed!\n", __FUNCTION__);return HI_FAILURE;}     /******************************************step 2: configure sensor and ISP (include WDR mode).note: you can jump over this step, if you do not use Hi3516A interal isp. ******************************************/s32Ret = SAMPLE_COMM_ISP_Init(pstViConfig->enWDRMode);if (HI_SUCCESS != s32Ret){SAMPLE_PRT("%s: Sensor init failed!\n", __FUNCTION__);return HI_FAILURE;}/******************************************step 3: run isp thread note: you can jump over this step, if you do not use Hi3516A interal isp.******************************************/s32Ret = SAMPLE_COMM_ISP_Run();if (HI_SUCCESS != s32Ret){SAMPLE_PRT("%s: ISP init failed!\n", __FUNCTION__);/* disable videv */return HI_FAILURE;}/******************************************************step 4 : config & start vicap dev******************************************************/for (i = 0; i < u32DevNum; i++){ViDev = i;                    //设备号   设备类型s32Ret = SAMPLE_COMM_VI_StartDev(ViDev, enViMode);if (HI_SUCCESS != s32Ret)     //这里的设备,也就是sensor。{SAMPLE_PRT("%s: start vi dev[%d] failed!\n", __FUNCTION__, i);return HI_FAILURE;}}/******************************************************* Step 5: config & start vicap chn (max 1) ******************************************************/for (i = 0; i < u32ChnNum; i++){ViChn = i;stCapRect.s32X = 0;stCapRect.s32Y = 0;switch (enViMode){case APTINA_9M034_DC_720P_30FPS:case APTINA_AR0130_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:stCapRect.u32Width = 1280;stCapRect.u32Height = 720;break;        case SONY_IMX222_DC_1080P_30FPS:case APTINA_AR0230_HISPI_1080P_30FPS:case PANASONIC_MN34222_MIPI_1080P_30FPS:case OMNIVISION_OV2718_MIPI_1080P_25FPS:stCapRect.u32Width  = 1920;stCapRect.u32Height = 1080;break;default:stCapRect.u32Width  = 1920;stCapRect.u32Height = 1080;break;}stTargetSize.u32Width = stCapRect.u32Width;stTargetSize.u32Height = stCapRect.u32Height;s32Ret = SAMPLE_COMM_VI_StartChn(ViChn, &stCapRect, &stTargetSize, pstViConfig);if (HI_SUCCESS != s32Ret){SAMPLE_COMM_ISP_Stop();return HI_FAILURE;}}return s32Ret;
}

可知该函数将整个过程划分为5个步骤,具体分析如下。

(1)step1:配置MIPI

这一步主要是函数SAMPLE_COMM_VI_StartMIPI(pstViConfig)。其中pstViConfig变量是上层函数传过来的、指向SAMPLE_VI_CONFIG_S类型变量stViConfig的指针。

该函数又调用SAMPLE_COMM_VI_SetMipiAttr(pstViConfig),后者函数内容如下:

HI_S32 SAMPLE_COMM_VI_SetMipiAttr(SAMPLE_VI_CONFIG_S* pstViConfig)
{HI_S32 fd;combo_dev_attr_t *pstcomboDevAttr = NULL;/* mipi reset unrest */fd = open("/dev/hi_mipi", O_RDWR);if (fd < 0){printf("warning: open hi_mipi dev failed\n");return -1;}printf("=============SAMPLE_COMM_VI_SetMipiAttr enWDRMode: %d\n", pstViConfig->enWDRMode);if ( pstViConfig->enViMode == APTINA_AR0230_HISPI_1080P_30FPS ){pstcomboDevAttr = &HISPI_4lane_SENSOR_AR0230_12BIT_ATTR;}if ( pstViConfig->enViMode == PANASONIC_MN34222_MIPI_1080P_30FPS ){pstcomboDevAttr = &MIPI_2lane_SENSOR_MN34222_12BIT_NOWDR_ATTR;}if ( (pstViConfig->enViMode == OMNIVISION_OV9752_MIPI_720P_30FPS)|| (pstViConfig->enViMode == OMNIVISION_OV9750_MIPI_720P_30FPS) ){pstcomboDevAttr = &MIPI_2lane_SENSOR_OV9752_12BIT_NOWDR_ATTR;}if ( pstViConfig->enViMode ==  OMNIVISION_OV2718_MIPI_1080P_25FPS ){pstcomboDevAttr = &MIPI_4lane_SENSOR_OV2718_12BIT_NOWDR_ATTR;}if ( (pstViConfig->enViMode == APTINA_9M034_DC_720P_30FPS)|| (pstViConfig->enViMode == APTINA_AR0130_DC_720P_30FPS)|| (pstViConfig->enViMode == SONY_IMX222_DC_1080P_30FPS)|| (pstViConfig->enViMode == SONY_IMX222_DC_720P_30FPS)|| (pstViConfig->enViMode == OMNIVISION_OV9712_DC_720P_30FPS)|| (pstViConfig->enViMode == OMNIVISION_OV9732_DC_720P_30FPS) ){pstcomboDevAttr = &MIPI_CMOS3V3_ATTR;}if (NULL == pstcomboDevAttr){printf("Func %s() Line[%d], unsupported enViMode: %d\n", __FUNCTION__, __LINE__, pstViConfig->enViMode);close(fd);return HI_FAILURE;   }if (ioctl(fd, HI_MIPI_SET_DEV_ATTR, pstcomboDevAttr)){printf("set mipi attr failed\n");close(fd);return HI_FAILURE;}close(fd);return HI_SUCCESS;
}

函数开头定义了一个combo_dev_attr_t类型的指针变量pstcomboDevAttr,然后根据sensor的型号来给这个指针变量赋值,赋值的内容已经代码写好(这些内容厂商一般都提供的)。

然后打开名叫“/dev/hi_mipi”的设备文件,利用ioctl函数,以fd、pstcomboDevAttr、HI_MIPI_SET_DEV_ATTR作为参数,对MIPI接口进行配置。值得一提的是,我们的sensor和HI3518E之间的数据接口不是MIPI,这里的MIPI应该是对sensor和HI3518E之间的接口的一种统称而已,并不是说接口就是MIPI。这里配置MIPI,其实是对sensor进行一些初始化。

combo_dev_attr_t类型定义如下:

typedef struct
{input_mode_t          input_mode;               /* input mode: MIPI/LVDS/SUBLVDS/HISPI/DC */union{mipi_dev_attr_t     mipi_attr;lvds_dev_attr_t     lvds_attr;};
}combo_dev_attr_t;

(2)step2:初始化ISP

这一步主要是函数SAMPLE_COMM_ISP_Init(pstViConfig->enWDRMode)。

其中pstViConfig变量指向stViConfig变量的指针,这个变量的成员enWDRMode表示宽动态相关的内容,定义如下。

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;

我们来看一下函数SAMPLE_COMM_ISP_Init的内容。根据本文第一节,该函数的调用关系如下:

step2:SAMPLE_COMM_ISP_Init//此函数初始化内部的ISP单元sensor_register_callback//具体介绍见第4季4。//此函数在sensor驱动mpp\component\isp\sensor\ar0130\ar0130_cmos.c里//具体介绍见文档《ISP_3A开发指南.pdf》HI_MPI_AE_Register//此函数注册AE单元,AE即自动曝光HI_MPI_AWB_Register//此函数注册AWB单元,AWB即白平衡HI_MPI_AF_Register//此函数注册自动对焦(AF)单元HI_MPI_ISP_MemInit//此函数给ISP单元分配必要的内存HI_MPI_ISP_SetWDRMode//此函数设置宽动态相关属性HI_MPI_ISP_SetPubAttr//此函数通过传参告知ISP单元sensor的一些属性以便ISPHI_MPI_ISP_Init//此函数初始化ISP

首先定义了ISP_PUB_ATTR_S结构体变量stPubAttr,以及ALG_LIB_S结构体变量stLib。这两个结构体的定义如下:

typedef struct hiISP_PUB_ATTR_S
{RECT_S          stWndRect;      /* RW. */HI_FLOAT        f32FrameRate;   /* RW. */ISP_BAYER_FORMAT_E  enBayer;    /* RW. */
} ISP_PUB_ATTR_S;typedef struct hiALG_LIB_S
{HI_S32  s32Id;HI_CHAR acLibName[20];
} ALG_LIB_S;

然后先填充stLib变量的成员,用来先后注册AE、AWB、AF单元。

接着定义ISP_WDR_MODE_S结构体变量stWdrMode,并填充其成员enWDRMode,然后利用HI_MPI_ISP_SetWDRMode函数来设置宽动态的内容。ISP_WDR_MODE_S结构体的定义如下:

typedef struct hiISP_WDR_MODE_S
{WDR_MODE_E  enWDRMode;
} ISP_WDR_MODE_S;

接着根据sensor型号来填充stPubAttr变量的成员,然后调用HI_MPI_ISP_SetPubAttr函数来设置。

最后调用HI_MPI_ISP_Init函数来初始化ISP。

(3)step3:运行ISP

这一步主要是开启Test_ISP_Run线程来运行ISP。

/******************************************************************************
* funciton : ISP Run
******************************************************************************/
HI_S32 SAMPLE_COMM_ISP_Run()
{pthread_attr_t attr;pthread_attr_init(&attr);pthread_attr_setstacksize(&attr, 4096 * 1024);if (0 != pthread_create(&gs_IspPid, &attr, (void* (*)(void*))Test_ISP_Run, NULL)){printf("%s: create isp running thread failed!\n", __FUNCTION__);pthread_attr_destroy(&attr);return HI_FAILURE;}usleep(1000);pthread_attr_destroy(&attr);
}

Test_ISP_Run线程中调用HI_MPI_ISP_Run(IspDev)函数(参数IspDev值为0),函数内容如下:

/*****************************************************************************Prototype       : HI_MPI_ISP_RunDescription     : isp firmware recurrent task, always run in a single thread.Input           : I_VOID  **Output          : NoneReturn Value    : Process         : Note             : History         1.Date         : 2011/1/13Author       : x00100808Modification : Created function*****************************************************************************/
HI_S32 HI_MPI_ISP_Run(ISP_DEV IspDev)
{HI_S32 s32Ret;HI_U32 u32IntStatus = 0;HI_BOOL bEn;HI_U32 u32WDRmode;ISP_CTX_S *pstIspCtx = HI_NULL;/* 1. check status */ISP_CHECK_DEV(IspDev);ISP_GET_CTX(IspDev, pstIspCtx);ISP_CHECK_POINTER(pstIspCtx);    ISP_CHECK_OPEN(IspDev);ISP_CHECK_SENSOR_REGISTER(IspDev);ISP_CHECK_MEM_INIT(IspDev);ISP_CHECK_ISP_INIT(IspDev);if (HI_TRUE == pstIspCtx->stIspParaRec.bRun){ISP_TRACE(HI_DBG_ERR, "ISP[%d] Run failed!\n", IspDev);return HI_ERR_ISP_ILLEGAL_PARAM;}pthread_mutex_lock(&pstIspCtx->stLock);/* Sometimes HI_MPI_ISP_Run thread is not scheduled to run before calling HI_MPI_ISP_Exit. */if (HI_FALSE == pstIspCtx->stIspParaRec.bRunEn){pthread_mutex_unlock(&pstIspCtx->stLock);return HI_SUCCESS;}/* 2. enable interrupt */bEn = HI_TRUE;if (ioctl(g_as32IspFd[IspDev], ISP_SET_INT_ENABLE, &bEn) < 0){ISP_TRACE(HI_DBG_ERR, "Enable ISP[%d] interrupt failed!\n", IspDev);pthread_mutex_unlock(&pstIspCtx->stLock);return -1;}pstIspCtx->stIspParaRec.bRun = HI_TRUE;pthread_mutex_unlock(&pstIspCtx->stLock);while (1){pthread_mutex_lock(&pstIspCtx->stLock);if (HI_FALSE == pstIspCtx->stIspParaRec.bRunEn){pthread_mutex_unlock(&pstIspCtx->stLock);break;}/*change  resolution  */ISP_SwitchImageMode(IspDev);u32WDRmode = hi_ext_system_sensor_wdr_mode_read();/* swtich linear/WDR mode, width/height, fps  */if (pstIspCtx->u8SnsWDRMode != u32WDRmode){pstIspCtx->u8SnsWDRMode = u32WDRmode;ISP_SwitchWDRMode(IspDev);}{u32IntStatus = 0;/* 3. waked up by the interrupt */s32Ret = ioctl(g_as32IspFd[IspDev], ISP_GET_FRAME_EDGE, &u32IntStatus);if (s32Ret){}else{/* 4.isp firmware calculate, include AE/AWB, etc. */if (ISP_1ST_INT & u32IntStatus){ISP_Run(IspDev);}}}pthread_mutex_unlock(&pstIspCtx->stLock);usleep(10);}/* 8. disable interrupt */bEn = HI_FALSE;if (ioctl(g_as32IspFd[IspDev], ISP_SET_INT_ENABLE, &bEn) < 0){ISP_TRACE(HI_DBG_ERR, "Disable ISP[%d] interrupt failed!\n", IspDev);}return HI_SUCCESS;
}

(4)step4:配置与打开VI设备

这一步涉及函数SAMPLE_COMM_VI_StartDev(ViDev, enViMode),其中ViDev表示设备号(设备号也就是sensor号,这里只有一个,为0),enViMode表示sensor的型号。

我们来看一下SAMPLE_COMM_VI_StartDev函数的内部。

/*****************************************************************************
* function : star vi dev (cfg vi_dev_attr; set_dev_cfg; enable dev)
*****************************************************************************/
HI_S32 SAMPLE_COMM_VI_StartDev(VI_DEV ViDev, SAMPLE_VI_MODE_E enViMode)
{HI_S32 s32Ret;HI_S32 s32IspDev = 0;ISP_WDR_MODE_S stWdrMode;VI_DEV_ATTR_S  stViDevAttr;//重点关注这个设备属性结构体memset(&stViDevAttr,0,sizeof(stViDevAttr));//接下来将填充设备属性结构体的内容switch (enViMode){//省略部分代码case APTINA_AR0130_DC_720P_30FPS:memcpy(&stViDevAttr,&DEV_ATTR_9M034_DC_720P_BASE,sizeof(stViDevAttr));break;//省略部分代码case OMNIVISION_OV9712_DC_720P_30FPS:case OMNIVISION_OV9732_DC_720P_30FPS:memcpy(&stViDevAttr,&DEV_ATTR_OV9732_DC_720P_BASE,sizeof(stViDevAttr));stViDevAttr.stDevRect.s32X = 0;stViDevAttr.stDevRect.s32Y = 0;stViDevAttr.stDevRect.u32Width  = 1280;stViDevAttr.stDevRect.u32Height = 720;break;case OMNIVISION_OV9750_MIPI_720P_30FPS:case OMNIVISION_OV9752_MIPI_720P_30FPS:memcpy(&stViDevAttr,&DEV_ATTR_MIPI_BASE,sizeof(stViDevAttr));stViDevAttr.stDevRect.s32X = 0;stViDevAttr.stDevRect.s32Y = 0;stViDevAttr.stDevRect.u32Width  = 1280;stViDevAttr.stDevRect.u32Height = 720;break;case OMNIVISION_OV2718_MIPI_1080P_25FPS:memcpy(&stViDevAttr,&DEV_ATTR_MIPI_BASE,sizeof(stViDevAttr));stViDevAttr.stDevRect.s32X = 0;stViDevAttr.stDevRect.s32Y = 0;stViDevAttr.stDevRect.u32Width  = 1920;stViDevAttr.stDevRect.u32Height = 1080;break;default:memcpy(&stViDevAttr,&DEV_ATTR_LVDS_BASE,sizeof(stViDevAttr));}s32Ret = HI_MPI_VI_SetDevAttr(ViDev, &stViDevAttr);//设置设备的属性if (s32Ret != HI_SUCCESS){SAMPLE_PRT("HI_MPI_VI_SetDevAttr failed with %#x!\n", s32Ret);return HI_FAILURE;}if ( (SAMPLE_VI_MODE_BT1120_1080P != enViMode)&&(SAMPLE_VI_MODE_BT1120_720P != enViMode) ){s32Ret = HI_MPI_ISP_GetWDRMode(s32IspDev, &stWdrMode);if (s32Ret != HI_SUCCESS){SAMPLE_PRT("HI_MPI_ISP_GetWDRMode failed with %#x!\n", s32Ret);return HI_FAILURE;}VI_WDR_ATTR_S stWdrAttr;stWdrAttr.enWDRMode = stWdrMode.enWDRMode;stWdrAttr.bCompress = HI_FALSE;s32Ret = HI_MPI_VI_SetWDRAttr(ViDev, &stWdrAttr);//设置设备的宽动态if (s32Ret){SAMPLE_PRT("HI_MPI_VI_SetWDRAttr failed with %#x!\n", s32Ret);return HI_FAILURE;}}s32Ret = HI_MPI_VI_EnableDev(ViDev);//启动设备if (s32Ret != HI_SUCCESS){SAMPLE_PRT("HI_MPI_VI_EnableDev failed with %#x!\n", s32Ret);return HI_FAILURE;}return HI_SUCCESS;
}

因为这一步是要配置与打开设备,所以首先定义了一个设备属性结构体变量stViDevAttr,然后根据sensor的型号来填充stViDevAttr的成员。填充时使用 memcpy 函数,它的参数1就是stViDevAttr这个结构体,参数2表示具体型号的sensor的设备属性(具体属性已经写死在代码里),参数2的内容将拷贝到参数1中。完成stViDevAttr这个结构体部分成员的填充后,调用HI_MPI_VI_SetDevAttr函数来设置设备的属性。

其中,设备属性结构体的定义如下。

/* the attributes of a VI device */
typedef struct hiVI_DEV_ATTR_S
{VI_INTF_MODE_E      enIntfMode;         /* Interface mode */VI_WORK_MODE_E      enWorkMode;         /*1-, 2-, or 4-channel multiplexed work mode */HI_U32              au32CompMask[2];    /* Component mask */VI_SCAN_MODE_E      enScanMode;         /* Input scanning mode (progressive or interlaced) */HI_S32              s32AdChnId[4];      /* AD channel ID. Typically, the default value -1 is recommended *//* The below members must be configured in BT.601 mode or DC mode and are invalid in other modes */VI_DATA_YUV_SEQ_E   enDataSeq;          /* Input data sequence (only the YUV format is supported) */VI_SYNC_CFG_S       stSynCfg;           /* Sync timing. This member must be configured in BT.601 mode or DC mode */VI_DATA_PATH_E      enDataPath;         /* ISP enable or bypass */VI_DATA_TYPE_E      enInputDataType;    /* RGB: CSC-709 or CSC-601, PT YUV444 disable; YUV: default yuv CSC coef PT YUV444 enable. */HI_BOOL             bDataRev;           /* Data reverse */RECT_S              stDevRect;          /* Dev capture rect */
} VI_DEV_ATTR_S;

然后定义一个宽动态相关的结构体变量stWdrAttr,接下来也是填充stWdrAttr这个结构体变量的成员,最后使用HI_MPI_VI_SetWDRAttr来设置宽动态的相关内容。

最后,调用HI_MPI_VI_EnableDev函数来启动设备。

(5)step5:配置与打开VI通道

step5首先根据sensor的型号填充结构体变量stCapRect、stTargetSize的成员,这两个结构体变量成员都包括图像分辨率里的w与h,这里即填充w与h。

然后调用SAMPLE_COMM_VI_StartChn(ViChn, &stCapRect, &stTargetSize, pstViConfig)来打开通道。其中参数1的ViChn表示要打开的通道编号(这里只有一个通道,编号为0),参数2和参数3都是输出型参数,参数4是在上一层函数传过来的stViConfig变量,这个变量含有VI模块相关的设置信息(见本文第二部分的描述)。

我们来看一下这个函数的内容。

/*****************************************************************************
* function : star vi chn
*****************************************************************************/
HI_S32 SAMPLE_COMM_VI_StartChn(VI_CHN ViChn, RECT_S *pstCapRect, SIZE_S *pstTarSize, SAMPLE_VI_CONFIG_S* pstViConfig)
{HI_S32 s32Ret;VI_CHN_ATTR_S stChnAttr;ROTATE_E enRotate = ROTATE_NONE;SAMPLE_VI_CHN_SET_E enViChnSet = VI_CHN_SET_NORMAL;if(pstViConfig){enViChnSet = pstViConfig->enViChnSet;enRotate = pstViConfig->enRotate;}/* step  5: config & start vicap dev */memcpy(&stChnAttr.stCapRect, pstCapRect, sizeof(RECT_S));stChnAttr.enCapSel = VI_CAPSEL_BOTH;/* to show scale. this is a sample only, we want to show dist_size = D1 only */stChnAttr.stDestSize.u32Width = pstTarSize->u32Width;stChnAttr.stDestSize.u32Height = pstTarSize->u32Height;stChnAttr.enPixFormat = PIXEL_FORMAT_YUV_SEMIPLANAR_420;   /* sp420 or sp422 */stChnAttr.bMirror = HI_FALSE;stChnAttr.bFlip = HI_FALSE;switch(enViChnSet){case VI_CHN_SET_MIRROR:stChnAttr.bMirror = HI_TRUE;break;case VI_CHN_SET_FLIP:stChnAttr.bFlip = HI_TRUE;break;case VI_CHN_SET_FLIP_MIRROR:stChnAttr.bMirror = HI_TRUE;stChnAttr.bFlip = HI_TRUE;break;default:break;}stChnAttr.s32SrcFrameRate = -1;stChnAttr.s32DstFrameRate = -1;stChnAttr.enCompressMode = COMPRESS_MODE_NONE;s32Ret = HI_MPI_VI_SetChnAttr(ViChn, &stChnAttr);if (s32Ret != HI_SUCCESS){SAMPLE_PRT("failed with %#x!\n", s32Ret);return HI_FAILURE;}if(ROTATE_NONE != enRotate){s32Ret = HI_MPI_VI_SetRotate(ViChn, enRotate);if (s32Ret != HI_SUCCESS){SAMPLE_PRT("HI_MPI_VI_SetRotate failed with %#x!\n", s32Ret);return HI_FAILURE;}}s32Ret = HI_MPI_VI_EnableChn(ViChn);if (s32Ret != HI_SUCCESS){SAMPLE_PRT("failed with %#x!\n", s32Ret);return HI_FAILURE;}return HI_SUCCESS;
}

由此可知,该函数首先定义通道属性结构体变量stChnAttr,然后再填充该变量的成员,接着利用HI_MPI_VI_SetChnAttr函数来设置通道属性,最后利用HI_MPI_VI_EnableChn函数来打开通道。

其中,通道属性结构体的定义如下。

/* the attributes of a VI channel */
typedef struct hiVI_CHN_ATTR_S
{RECT_S          stCapRect;          /* the capture rect (corresponding to the size of the picture captured by a VI device).For primary channels, the stCapRect's u32Width and u32Height are static attributes. That is,the value of them can be changed only after primary and secondary channels are disabled.For secondary channels, stCapRect is an invalid attribute */SIZE_S          stDestSize;         /* Target picture size.For primary channels, stDestSize must be equal to stCapRect's u32Width and u32Height,because primary channel doesn't have scale capability. Additionally, it is a staticattribute, That is, the value of stDestSize can be changed only after primary andsecondary channels are disabled.For secondary channels, stDestSize is a dynamic attribute */VI_CAPSEL_E     enCapSel;           /* Frame/field select. It is used only in interlaced mode.For primary channels, enCapSel is a static attribute */PIXEL_FORMAT_E  enPixFormat;        /* Pixel storage format. Only the formats semi-planar420 and semi-planar422 are supported */COMPRESS_MODE_E enCompressMode;     /* 256B Segment compress or no compress. */HI_BOOL         bMirror;            /* Whether to mirror */HI_BOOL         bFlip;              /* Whether to flip */HI_S32          s32SrcFrameRate;    /* Source frame rate. The value -1 indicates that the frame rate is not controlled */HI_S32          s32DstFrameRate;    /* Target frame rate. The value -1 indicates that the frame rate is not controlled */
} VI_CHN_ATTR_S;

第二季5:配置视频捕获模块(step3:VI模块)相关推荐

  1. 第二季2:视频缓存池的简介

    以下内容源于朱有鹏嵌入式课程的学习与整理,如有侵权请告知删除. 一.视频缓存池的概念 (1)视频的本质是多帧图片,图片的本质是RGB或rawRGB数据,视频要占用一段连续内存. (2)视频的裁剪.缩放 ...

  2. 第二季3:海思MPP模块与视频缓冲池

    以下内容源于朱有鹏课程,如有侵权,请告知删除. 一.MPP功能模块 1.MPP功能模块(MPP,media process platform,媒体处理平台) (1)MPP手册:\01.software ...

  3. 第二季4:初始化MPP系统(step12)

    以下内容源于朱有鹏嵌入式课程的学习与整理,如有侵权请告知删除. 前言 本文将详细介绍博文第二季3:sample_venc.c的整体分析中提及的"初始化MPP系统". MPP系统的初 ...

  4. 第二季:7.怎么查看服务器默认的垃圾收集器是那个?生产上如何配置垃圾收集器的?谈谈你对垃圾收集器的理解?【Java面试题】

    第二季:7.怎么查看服务器默认的垃圾收集器是那个?生产上如何配置垃圾收集器的?谈谈你对垃圾收集器的理解?[Java面试题] 前言 推荐 7.怎么查看服务器默认的垃圾收集器是那个?生产上如何配置垃圾收集 ...

  5. 从全职高手开始的系统_动画全职高手第二季热血回归,腾讯视频的国漫IP全链路开发之道...

    这个国庆档,国产动画在内容市场上的存在感比想象中更加有分量.电影市场上,动画IP电影<姜子牙>完成票房领跑,掀起观影热潮:动画番剧市场上,头部IP动画<全职高手>第二季时隔三年 ...

  6. Python可以这样学(第二季:tkinter案例精选)-董付国-专题视频课程

    Python可以这样学(第二季:tkinter案例精选)-3592人已学习 课程介绍         董付国老师系列教材<Python程序设计基础>(ISBN:9787302410584) ...

  7. HTML5+WebGL打造的无插件纯Web 3D机房(第二季新增视频)

    2019独角兽企业重金招聘Python工程师标准>>> 前情提要 前阵子写了一篇HTML5打造的无插件纯web 3D机房,介绍了如何用html5在网页上创建无插件的精美3d机房场景. ...

  8. 最火的python视频_超火Python400集视频,116-248集思维导图视频介绍(第二季)

    第二季 2.1 文件处理 116_file文件操作_操作系统底层关系_写入文件 117_编码知识_中文乱码问题解决 118_关闭流要点1_try异常管理 119_关闭流要点2_with上下文管理_现场 ...

  9. 视频教程-PHP7入门手册视频版第二季-PHP

    PHP7入门手册视频版第二季 2009年4月创办 淄博日诺网络科技有限公司 法人总经理 2016年负责 中国传媒大学凤凰学院 网站开发 项目负责人 2017年 参与负责 用友软件理财项目开发 郭孟涛 ...

最新文章

  1. SharePoint 2013 开发——SharePoint Designer 2013工作流
  2. 每天一个linux命令(46):vmstat命令
  3. SpringBoot - Spring Boot 应用剖析
  4. 中国无碳复写纸行业竞争现状与运行态势研究报告2022年
  5. python3+opencv+tkinter开发简单的人脸识别小程序
  6. 重新想象 Windows 8 Store Apps (4) - 控件之提示控件: ProgressRing; 范围控件: ProgressBar, Slider...
  7. [CF1442 D] Sum(分治优化dp + 结论)
  8. 从“小白”到“白帽子黑客”的实用指南
  9. shell脚本if中判断大于、小于、等于、不等于的符号
  10. as3 urlloader php交互 jsion,phpQuery获取网页里的js变量,如何获取
  11. 都说比特币无价值,涨得不合理;但你知道比特币最大的用途吗?
  12. 九鼎无双一面面经【凉】
  13. 苹果6设置流量显示无服务器,超实用!15个苹果手机的隐藏功能,不看你手机就白买了!...
  14. 计算机会议论文EI检索,ei检索会议论文算期刊_ei论文检索_ei会议论文算核心吗...
  15. 天翼网关超级密码获取器
  16. 家用小型中央空调发展的现状与趋势
  17. keil5安装及注册许可
  18. [CF1153F]Serval and Bonus Problem(dp/积分+OGF)
  19. WEB漏洞——SQL注入之简要SQL注入
  20. 基于kettle的数据集成平台(三)

热门文章

  1. 小米只能进fastboot和rec救砖
  2. 【PyTorch系列】找不到d2lzh_pytorch包,No module named ‘d2lzh_pytorch’
  3. 双硬盘+双系统引导出问题的另类解决方法
  4. 小程序的退出登陆功能实现代码
  5. mysql 唯一索引出现重复数据_mysql使用唯一索引避免插入重复数据
  6. 【汇编语言 王爽】实验14代码
  7. 内丘计算机学校,内丘学校食堂打卡机
  8. 网上书店管理系统项目【Java数据库编程实战】
  9. NERO刻录教程图解
  10. 【数据库】快速理解脏读、不可重复读、幻读