zoukankan      html  css  js  c++  java
  • sample_venc解析

    概述

    从main函数开始,根据传入参数选择对VI VPSS等模块的操作,等。
    一般选择进入 SAMPLE_VENC_1080P_CLASSIC
    在 SAMPLE_VENC_1080P_CLASSIC 中进行如下操作
    step0:定义了一些视频编解码过程中会用到的变量
    setp1:init sys variable video buffer(初始化系统变量 MPP系统缓存池定义)
    step2:mpp system init(MPP系统初始化)
    step3:start vi dev & chn to capture(开启VI,并捕获视频通道数据)
    step4:start vpss and vi bind vpss(开启VPSS,并绑定视频通道等)
    step5:start stream venc(开启venc流视频)
    step6:stream venc process – get stream, then save it to file (开启venc处理,获取视频流,并保存到文件中)
    然后getchar等待程序结束

    main函数

    主要是根据argv[]来决定具体视频处理的具体码率大小,通道数等,具体见代码。
    SAMPLE_VENC_1080P_CLASSIC

    step0:定义了一堆变量
    #还有一些等下来补充
    
    PAYLOAD_TYPE_E enPayLoad[3]= {PT_H264, PT_H264,PT_H264}; #存放三路视频的编码格式,这里仅仅是默认填充,具体还要在后面根据参数填充
    PIC_SIZE_E enSize[3] = {PIC_HD1080, PIC_VGA,PIC_QVGA}; #存放的是视频大小,也是默认填充,具体大小后面填充
    HI_U32 u32Profile = 0;
    
    VB_CONF_S stVbConf;	#视频缓冲区管理块
    SAMPLE_VI_CONFIG_S stViConfig = {0};	#VI模块配置结构体
    
    VPSS_GRP VpssGrp;
    VPSS_CHN VpssChn;
    VPSS_GRP_ATTR_S stVpssGrpAttr;
    VPSS_CHN_ATTR_S stVpssChnAttr;
    VPSS_CHN_MODE_S stVpssChnMode;
    
    VENC_CHN VencChn;
    SAMPLE_RC_E enRcMode= SAMPLE_RC_CBR;
    
    HI_S32 s32ChnNum=0;
    
    HI_S32 s32Ret = HI_SUCCESS;
    HI_U32 u32BlkSize;
    SIZE_S stSize;
    char c;
    
    
    setp1:init sys variable video buffer(初始化系统变量 MPP系统缓存池定义)
    
    /**************************************************************************************************************************
    step  1: init sys variable 
    **************************************************************************************************************************/
    memset(&stVbConf,0,sizeof(VB_CONF_S));	#清空stVbConf视频缓存池控制块数据
    
    SAMPLE_COMM_VI_GetSizeBySensor(&enSize[0]); #根据VI输入的sensor来填充enSize[0](存放输出视频流的大小)
    if (PIC_HD1080 == enSize[0])	#再根据enSize[0]来填充enSize[1]
    {
    	enSize[1] = PIC_VGA;
    	s32ChnNum = 2;
    }
    else if (PIC_HD720 == enSize[0])
    {
    	enSize[1] = PIC_VGA;			
    	enSize[2] = PIC_QVGA;
    	s32ChnNum = 3;
    }
    else
    {
    	printf("not support this sensor
    ");
    	return HI_FAILURE;
    }
    #ifdef hi3518ev201
    	s32ChnNum = 1;
    #endif
    printf("s32ChnNum = %d
    ",s32ChnNum);
    stVbConf.u32MaxPoolCnt = 128;	#这是设置允许的最大缓存池数量,实际缓存池数量肯定比这个少
    /*****************************************video buffer*****************************************/
    if(s32ChnNum >= 1)	#根据s32ChnNum通道数来选择填充stVbConf元素,
    {
    	u32BlkSize = SAMPLE_COMM_SYS_CalcPicVbBlkSize(gs_enNorm,	enSize[0], SAMPLE_PIXEL_FORMAT, SAMPLE_SYS_ALIGN_WIDTH);
    	stVbConf.astCommPool[0].u32BlkSize = u32BlkSize;
    	stVbConf.astCommPool[0].u32BlkCnt = g_u32BlkCnt;
    }
    if(s32ChnNum >= 2)
    {
    	u32BlkSize = SAMPLE_COMM_SYS_CalcPicVbBlkSize(gs_enNorm,	enSize[1], SAMPLE_PIXEL_FORMAT, SAMPLE_SYS_ALIGN_WIDTH);
    	stVbConf.astCommPool[1].u32BlkSize = u32BlkSize;
    	stVbConf.astCommPool[1].u32BlkCnt =g_u32BlkCnt;
    }
    if(s32ChnNum >= 3)
    {
    	u32BlkSize = SAMPLE_COMM_SYS_CalcPicVbBlkSize(gs_enNorm,	enSize[2], SAMPLE_PIXEL_FORMAT, SAMPLE_SYS_ALIGN_WIDTH);
    	stVbConf.astCommPool[2].u32BlkSize = u32BlkSize;
    	stVbConf.astCommPool[2].u32BlkCnt = g_u32BlkCnt;
    }
    
    step2:mpp system init(MPP系统初始化)
    将 stVbConf 传入 SAMPLE_COMM_SYS_Init ,根据 stVbConf 具体实际初始化缓存池
    
    s32Ret = SAMPLE_COMM_SYS_Init(&stVbConf);
    if (HI_SUCCESS != s32Ret)
    {
        SAMPLE_PRT("system init failed with %d!
    ", s32Ret);
        goto END_VENC_1080P_CLASSIC_0;
    }
    

    其中 SAMPLE_COMM_SYS_Init

    /******************************************************************************

    • function : vb init & MPI system init
      ******************************************************************************/
      HI_S32 SAMPLE_COMM_SYS_Init(VB_CONF_S *pstVbConf)
      {
      MPP_SYS_CONF_S stSysConf = {0};
      HI_S32 s32Ret = HI_FAILURE;

      HI_MPI_SYS_Exit(); #EXIT主要防止系统之前已经初始化过了…
      HI_MPI_VB_Exit();

      s32Ret = HI_MPI_VB_SetConf(pstVbConf); #下面这一系列函数都是海思以KO的形式提供的模块库
      s32Ret = HI_MPI_VB_Init();
      stSysConf.u32AlignWidth = SAMPLE_SYS_ALIGN_WIDTH;
      s32Ret = HI_MPI_SYS_SetConf(&stSysConf);
      s32Ret = HI_MPI_SYS_Init();
      return HI_SUCCESS;
      }

      step3:start vi dev & chn to capture(开启VI,并捕获视频通道数据)
      填充 stViConfig 结构体,将 stViConfig 传入 SAMPLE_COMM_VI_StartVi 开启VI模块

      stViConfig.enViMode = SENSOR_TYPE; #sensor模块的类型
      stViConfig.enRotate = ROTATE_NONE; #图像旋转是否
      stViConfig.enNorm = VIDEO_ENCODING_MODE_AUTO; #视频自动编解码
      stViConfig.enViChnSet = VI_CHN_SET_NORMAL; #VI通道自动设置
      stViConfig.enWDRMode = WDR_MODE_NONE; #不设置宽动态模式
      s32Ret = SAMPLE_COMM_VI_StartVi(&stViConfig); #根据以上具体设置初始化传感器模块
      if (HI_SUCCESS != s32Ret)
      {
      SAMPLE_PRT(“start vi failed! ”);
      goto END_VENC_1080P_CLASSIC_1;
      }

      start vpss and vi bind vpss(开启VPSS,并绑定视频通道等)

      /******************************************
      step 4: start vpss and vi bind vpss
      ******************************************/
      s32Ret = SAMPLE_COMM_SYS_GetPicSize(gs_enNorm, enSize[0], &stSize); #根据 gs_enNorm 、enSize[0] 的值来填充 stSize 结构体
      if (HI_SUCCESS != s32Ret)
      {
      SAMPLE_PRT(“SAMPLE_COMM_SYS_GetPicSize failed! ”);
      goto END_VENC_1080P_CLASSIC_1;
      }
      if(s32ChnNum >= 1)
      { #填充 stVpssGrpAttr 结构体
      VpssGrp = 0;
      stVpssGrpAttr.u32MaxW = stSize.u32Width;
      stVpssGrpAttr.u32MaxH = stSize.u32Height;
      stVpssGrpAttr.bIeEn = HI_FALSE;
      stVpssGrpAttr.bNrEn = HI_TRUE;
      stVpssGrpAttr.bHistEn = HI_FALSE;
      stVpssGrpAttr.bDciEn = HI_FALSE;
      stVpssGrpAttr.enDieMode = VPSS_DIE_MODE_NODIE;
      stVpssGrpAttr.enPixFmt = PIXEL_FORMAT_YUV_SEMIPLANAR_420;

        s32Ret = SAMPLE_COMM_VPSS_StartGroup(VpssGrp, &stVpssGrpAttr);		#开启VPSS GRP。
        if (HI_SUCCESS != s32Ret)
        {
        	SAMPLE_PRT("Start Vpss failed!
      ");
        	goto END_VENC_1080P_CLASSIC_2;
        }
      
        s32Ret = SAMPLE_COMM_VI_BindVpss(stViConfig.enViMode);		#绑定VPSS通道
        if (HI_SUCCESS != s32Ret)
        {
        	SAMPLE_PRT("Vi bind Vpss failed!
      ");
        	goto END_VENC_1080P_CLASSIC_3;
        }
      
        VpssChn = 0;
        stVpssChnMode.enChnMode      = VPSS_CHN_MODE_USER;	#填充VPSS通道模式
        stVpssChnMode.bDouble        = HI_FALSE;
        stVpssChnMode.enPixelFormat  = PIXEL_FORMAT_YUV_SEMIPLANAR_420;
        stVpssChnMode.u32Width       = stSize.u32Width;
        stVpssChnMode.u32Height      = stSize.u32Height;
        stVpssChnMode.enCompressMode = COMPRESS_MODE_SEG;
        memset(&stVpssChnAttr, 0, sizeof(stVpssChnAttr));
        stVpssChnAttr.s32SrcFrameRate = -1;
        stVpssChnAttr.s32DstFrameRate = -1;
        s32Ret = SAMPLE_COMM_VPSS_EnableChn(VpssGrp, VpssChn, &stVpssChnAttr, &stVpssChnMode, HI_NULL);		#根据stVpssChnMode 来使能CHN
        if (HI_SUCCESS != s32Ret)
        {
        	SAMPLE_PRT("Enable vpss chn failed!
      ");
        	goto END_VENC_1080P_CLASSIC_4;
        }
      

      }

      step5:start stream venc(开启venc流视频)

    /******************************************
    step 5: start stream venc
    ***************************************/
    /
     HD1080P **/
    printf(" c) cbr. ");
    printf(" v) vbr. ");
    printf(" f) fixQp ");
    printf(“please input choose rc mode! ”);
    c = (char)getchar();
    switch©
    { #根据输入的字符决定具体哪种模式
    case ‘c’:
    enRcMode = SAMPLE_RC_CBR;
    break;
    case ‘v’:
    enRcMode = SAMPLE_RC_VBR;
    break;
    case ‘f’:
    enRcMode = SAMPLE_RC_FIXQP;
    break;
    default:
    printf(“rc mode! is invaild! ”);
    goto END_VENC_1080P_CLASSIC_4;
    }

    /*** enSize[0] **/
    if(s32ChnNum >= 1)
    {
    	VpssGrp = 0;
    	VpssChn = 0;
    	VencChn = 0;
    	s32Ret = SAMPLE_COMM_VENC_Start(VencChn, enPayLoad[0],	#开启venc视频流
    	gs_enNorm, enSize[0], enRcMode,u32Profile);
    	if (HI_SUCCESS != s32Ret)
    	{
    		SAMPLE_PRT("Start Venc failed!
    ");
    		goto END_VENC_1080P_CLASSIC_5;
    	}
    	
    	s32Ret = SAMPLE_COMM_VENC_BindVpss(VencChn, VpssGrp, VpssChn);	#绑定 VencChn 与 VpssChn 
    	if (HI_SUCCESS != s32Ret)
    	{
    		SAMPLE_PRT("Start Venc failed!
    ");
    		goto END_VENC_1080P_CLASSIC_5;
    	}
    }
    
    step6:stream venc process – get stream, then save it to file (开启venc处理,获取视频流,并保存到文件中)
    

    /******************************************
    step 6: stream venc process – get stream, then save it to file.
    ******************************************/
    s32Ret = SAMPLE_COMM_VENC_StartGetStream(s32ChnNum); #获取venc视频流,函数源码其实是开启了一个线程来获取视频并保存视频流
    if (HI_SUCCESS != s32Ret)
    {
    SAMPLE_PRT(“Start Venc failed! ”);
    goto END_VENC_1080P_CLASSIC_5;
    }

    printf("please press twice ENTER to exit this sample
    ");
    getchar();
    getchar();
    
    
    
    step7:exit process
    
    /******************************************
         step 7: exit process
        ******************************************/
        SAMPLE_COMM_VENC_StopGetStream();	#键盘按下两次退出程序后释放相关资源,包括关闭线程等操作,然后继续退出程序
    

    VI部分

    频输入( VI)模块实现的功能:通过 ITU-R BT656/601/1120 接口或 Digital Camera接口、 MIPI Rx(含 MIPI 接口、 LVDS 接口和 HISPI 接口)接收视频数据。当工作在离线模式时,将接收到的数据存入到指定的内存区域;当工作在在线模式时, VI 会将数据直接送给 VPSS。在此过程中, VI 可以对接收到的原始视频图像数据进行裁剪( Crop)等处理,并实现一路原始视频图像输入,输出一路视频图像功能。
    主要包括1.与sensor接口对应的部分、2.ISP部分、3.VI dev和channel 部分

    stViConfig.enViMode   = SENSOR_TYPE;
    	stViConfig.enRotate   = ROTATE_NONE;
    	stViConfig.enNorm     = VIDEO_ENCODING_MODE_AUTO;
    	stViConfig.enViChnSet = VI_CHN_SET_NORMAL;
    	stViConfig.enWDRMode  = WDR_MODE_NONE;
    	s32Ret = SAMPLE_COMM_VI_StartVi(&stViConfig);
    

    SAMPLE_COMM_VI_StartVi 函数中由逻辑可得 走的是s32Ret = SAMPLE_COMM_VI_StartIspAndVi(pstViConfig);

    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
    ", __FUNCTION__);
            return HI_FAILURE;
        }
        enViMode = pstViConfig->enViMode;
        if(!IsSensorInput(enViMode)) {
            s32Ret = SAMPLE_COMM_VI_StartBT656(pstViConfig);
        }
        else{
            s32Ret = SAMPLE_COMM_VI_StartIspAndVi(pstViConfig);
        }
        return s32Ret; 
    }
    

    SAMPLE_COMM_VI_StartIspAndVi函数中封装了很多细节

    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
    ", __FUNCTION__);
    		return HI_FAILURE;
    	}
    	enViMode = pstViConfig->enViMode;
    	
    	/******************************************
    	step 1: mipi configure	mipi设置与sensor接口相关的部分
    	******************************************/
    	s32Ret = SAMPLE_COMM_VI_StartMIPI(pstViConfig);		# init mipi
    	if (HI_SUCCESS != s32Ret)
    	{
    		SAMPLE_PRT("%s: MIPI init failed!
    ", __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);		#其中包括了很多细节如3A初始化,ISP具体初始化等
    	if (HI_SUCCESS != s32Ret)
    	{
    		SAMPLE_PRT("%s: Sensor init failed!
    ", __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();		#主要是开了一个线程来跑Test_ISP_Run 
    	if (HI_SUCCESS != s32Ret)
    	{
    		SAMPLE_PRT("%s: ISP init failed!
    ", __FUNCTION__);
    		/* disable videv */
    		return HI_FAILURE;
    	}
    	
    	/******************************************************
    	step 4 : config & start vicap dev	#配置并开启vi 捕获设备 u32DevNum = 1
    	******************************************************/
    	for (i = 0; i < u32DevNum; i++)
    	{
    		ViDev = i;
    		s32Ret = SAMPLE_COMM_VI_StartDev(ViDev, enViMode);		#开启设备,可以理解为3518e中带一个镜头就是一个设备
    		if (HI_SUCCESS != s32Ret)
    		{
    			SAMPLE_PRT("%s: start vi dev[%d] failed!
    ", __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);	#star vi chn 开启vi CHN,有很多个虚拟channel
    		if (HI_SUCCESS != s32Ret)
    		{
    			SAMPLE_COMM_ISP_Stop();
    			return HI_FAILURE;
    		}
    	}
    	
    	return s32Ret;
    }
    

    VPSS部分

    VPSS( Video Process Sub-System)支持对一幅输入图像进行统一预处理,如去噪、去隔行等,然后再对各通道分别进行缩放、锐化等处理,最后输出多种不同分辨率的图像。
    VPSS 单元支持的具体图像处理功能包括 FRC( Frame Rate Control)、 Crop、 NR( Noise Reduce)、 LDC( Lens Distortion Correction)、 Rotate、 Cover/Overlay、 Scale、Mirror/Flip、 FishEye 等。

    /******************************************
         step 4: start vpss and vi bind vpss
        ******************************************/
    	s32Ret = SAMPLE_COMM_SYS_GetPicSize(gs_enNorm, enSize[0], &stSize);	#根据 enSize[0] 来填充 stSize 结构体
    	if (HI_SUCCESS != s32Ret)
    	{
    		SAMPLE_PRT("SAMPLE_COMM_SYS_GetPicSize failed!
    ");
    		goto END_VENC_1080P_CLASSIC_1;
    	}
    	if(s32ChnNum >= 1)
    	{
    		VpssGrp = 0;		#仅仅在第一个通道的时候初始化 VpssGrp 的相关一些属性
    		stVpssGrpAttr.u32MaxW = stSize.u32Width;
    		stVpssGrpAttr.u32MaxH = stSize.u32Height;
    		stVpssGrpAttr.bIeEn = HI_FALSE;
    		stVpssGrpAttr.bNrEn = HI_TRUE;
    		stVpssGrpAttr.bHistEn = HI_FALSE;
    		stVpssGrpAttr.bDciEn = HI_FALSE;
    		stVpssGrpAttr.enDieMode = VPSS_DIE_MODE_NODIE;
    		stVpssGrpAttr.enPixFmt = PIXEL_FORMAT_YUV_SEMIPLANAR_420;
    		
    		s32Ret = SAMPLE_COMM_VPSS_StartGroup(VpssGrp, &stVpssGrpAttr);		#也仅仅在第一个通道的时候写入VpssGrp 的 stVpssGrpAttr
    		if (HI_SUCCESS != s32Ret)
    		{
    			SAMPLE_PRT("Start Vpss failed!
    ");
    			goto END_VENC_1080P_CLASSIC_2;
    		}
    		
    		s32Ret = SAMPLE_COMM_VI_BindVpss(stViConfig.enViMode);		#也仅仅在第一个通道的时候绑定VI与VPSS
    		if (HI_SUCCESS != s32Ret)
    		{
    			SAMPLE_PRT("Vi bind Vpss failed!
    ");
    			goto END_VENC_1080P_CLASSIC_3;
    		}
    		
    		VpssChn = 0;		#填充 VpssChn  的 stVpssChnMode 
    		stVpssChnMode.enChnMode      = VPSS_CHN_MODE_USER;
    		stVpssChnMode.bDouble        = HI_FALSE;
    		stVpssChnMode.enPixelFormat  = PIXEL_FORMAT_YUV_SEMIPLANAR_420;
    		stVpssChnMode.u32Width       = stSize.u32Width;
    		stVpssChnMode.u32Height      = stSize.u32Height;
    		stVpssChnMode.enCompressMode = COMPRESS_MODE_SEG;
    		memset(&stVpssChnAttr, 0, sizeof(stVpssChnAttr));
    		stVpssChnAttr.s32SrcFrameRate = -1;
    		stVpssChnAttr.s32DstFrameRate = -1;
    		s32Ret = SAMPLE_COMM_VPSS_EnableChn(VpssGrp, VpssChn, &stVpssChnAttr, &stVpssChnMode, HI_NULL);
    		#使能 VpssChn ,写入stVpssChnAttr 与 stVpssChnMode
    		if (HI_SUCCESS != s32Ret)
    		{
    			SAMPLE_PRT("Enable vpss chn failed!
    ");
    			goto END_VENC_1080P_CLASSIC_4;
    		}
    	}
    
    	if(s32ChnNum >= 2)
    	{
    		s32Ret = SAMPLE_COMM_SYS_GetPicSize(gs_enNorm, enSize[1], &stSize);	#获取第二通道图像大小填充stSize
    		if (HI_SUCCESS != s32Ret)
    		{
    			SAMPLE_PRT("SAMPLE_COMM_SYS_GetPicSize failed!
    ");
    			goto END_VENC_1080P_CLASSIC_4;
    		}
    		VpssChn = 1;
    		stVpssChnMode.enChnMode       = VPSS_CHN_MODE_USER;
    		stVpssChnMode.bDouble         = HI_FALSE;
    		stVpssChnMode.enPixelFormat   = PIXEL_FORMAT_YUV_SEMIPLANAR_420;
    		stVpssChnMode.u32Width        = stSize.u32Width;
    		stVpssChnMode.u32Height       = stSize.u32Height;
    		stVpssChnMode.enCompressMode  = COMPRESS_MODE_SEG;
    		stVpssChnAttr.s32SrcFrameRate = -1;
    		stVpssChnAttr.s32DstFrameRate = -1;
    		s32Ret = SAMPLE_COMM_VPSS_EnableChn(VpssGrp, VpssChn, &stVpssChnAttr, &stVpssChnMode, HI_NULL);
    		if (HI_SUCCESS != s32Ret)
    		{
    			SAMPLE_PRT("Enable vpss chn failed!
    ");
    			goto END_VENC_1080P_CLASSIC_4;
    		}
    	}
    

    VENC部分

    VENC 模块,即视频编码模块。本模块支持多路实时编码,且每路编码独立,编码协议和编码 profile 可以不同。本模块支持视频编码同时,调度 Region 模块对编码图像内容进行叠加和遮挡。
    VENC 模块的输入源包括三类:

    用户态读取图像文件向编码模块发送数据
    视频输入( VIU)模块采集的图像经视频处理子系统( VPSS)发送到编码模块(离线模式)
    视频输入( VIU)模块采集的图像直接发送到编码模块(在线模式)
    

    VENC 模块由编码通道子模块( VENC)和编码协议子模块( H.264/H.265/JPEG/MJPEG)组成。通道支持接收 YUV 格式图像输入,支持格式为 Semi-planar YUV 4:2:0 或 Semi-planar YUV 4:2:2,其中 H.264/H.265 只支持 Semi-planar YUV 4:2:0, JPEG/MJPEG 支持 Semiplanar YUV 4:2:0 或 Semi-planar YUV 4:2:2。另外, Hi3518EV200 能够支持单分量输入
    (只存在 Y 分量)。通道模块接收外部原始图像数据,而不关心图像数据是来自哪个外部模块。
    REGION 模块支持对图像内容的遮挡和叠加。

    码率控制:码率控制器实现对编码码率进行控制。
    从信息学的角度分析,图像的压缩比越低,压缩图像的质量越高;图像压缩比例越高,压缩图像的质量越低。对于场景变化的真实场景,图像质量稳定,编码码率会波动;编码码率稳定,图像质量会波动。以 H.264 编码为例,通常图像 Qp 越低,图像的质量越好,码率越高;图像 Qp 越高,图像质量越差,码率越低。主要分为:

    CBR:CBR( Constant Bit Rate)固定比特率。即在码率统计时间内保证编码码率平稳
    VBR:VBR( Variable Bit Rate)可变比特率,即允许在码率统计时间内编码码率波动,从而保证编码图像质量平稳
    FIXQP:Fix Qp 固定 Qp 值。保证图像质量的一定
    
     /******************************************
         step 5: start stream venc
        ******************************************/
        /*** HD1080P **/
    	printf("	 c) cbr.
    ");
    	printf("	 v) vbr.
    ");
    	printf("	 f) fixQp
    ");
    	printf("please input choose rc mode!
    ");
    	c = (char)getchar();
    	switch(c)		#根据输入的字符来决定是采用哪种码率控制
    	{
    		case 'c':
    			enRcMode = SAMPLE_RC_CBR;
    			break;
    		case 'v':
    			enRcMode = SAMPLE_RC_VBR;
    			break;
    		case 'f':
    			enRcMode = SAMPLE_RC_FIXQP;
    			break;
    		default:
    			printf("rc mode! is invaild!
    ");
    			goto END_VENC_1080P_CLASSIC_4;
    	}
    	
    	/*** enSize[0] **/
    	if(s32ChnNum >= 1)
    	{
    		VpssGrp = 0;
    		VpssChn = 0;
    		VencChn = 0;
    		s32Ret = SAMPLE_COMM_VENC_Start(VencChn, enPayLoad[0], gs_enNorm, enSize[0], enRcMode,u32Profile);
    		/*SAMPLE_COMM_VENC_Start 较为复杂 
    		*step1:根据图像格式获取图像长宽大小
    		*step2:根据图像编码格式和码率控制方式来填充stH264Attr 和 stH264Cbr 等结构体
    		*step3:HI_MPI_VENC_CreateChn 创建 VENC 的 CHN
    		*step4:HI_MPI_VENC_StartRecvPic 开启接收视频流
    		*/
    		if (HI_SUCCESS != s32Ret)
    		{
    			SAMPLE_PRT("Start Venc failed!
    ");
    			goto END_VENC_1080P_CLASSIC_5;
    		}
    		
    		s32Ret = SAMPLE_COMM_VENC_BindVpss(VencChn, VpssGrp, VpssChn);	#绑定VENC VPSSGRP VPSSCHN
    		if (HI_SUCCESS != s32Ret)
    		{
    			SAMPLE_PRT("Start Venc failed!
    ");
    			goto END_VENC_1080P_CLASSIC_5;
    		}
    	}
    

    视频流保存

    /******************************************
         step 6: stream venc process -- get stream, then save it to file. 
        ******************************************/
        s32Ret = SAMPLE_COMM_VENC_StartGetStream(s32ChnNum);
        /*
        *封装了很多细节,最终调用了一个线程创建函数`pthread_create(&gs_VencPid, 0, SAMPLE_COMM_VENC_GetVencStreamProc, (HI_VOID*)&gs_stPara);`
        *很多细节实现都在 `SAMPLE_COMM_VENC_GetVencStreamProc `中
        */
        if (HI_SUCCESS != s32Ret)
        {
            SAMPLE_PRT("Start Venc failed!
    ");
            goto END_VENC_1080P_CLASSIC_5;
        }
    
        printf("please press twice ENTER to exit this sample
    ");
        getchar();
        getchar();
    
    /******************************************
     step 7: exit process
    ******************************************/
    SAMPLE_COMM_VENC_StopGetStream();
    

    SAMPLE_COMM_VENC_GetVencStreamProc

    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_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_STAT_S stStat;
    VENC_STREAM_S stStream;
    HI_S32 s32Ret;
    VENC_CHN VencChn;
    PAYLOAD_TYPE_E enPayLoadType[VENC_MAX_CHN_NUM];

    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
    ");
    	return NULL;
    }
    for (i = 0; i < s32ChnTotal; i++)
    {
    	/* decide the stream file name, and open file to save stream */
    	VencChn = i;
    	s32Ret = HI_MPI_VENC_GetChnAttr(VencChn, &stVencChnAttr);	#读取 VencChn 的 Attr
    	if(s32Ret != HI_SUCCESS)
    	{
    		SAMPLE_PRT("HI_MPI_VENC_GetChnAttr chn[%d] failed with %#x!
    ", 		VencChn, s32Ret);
    		return NULL;
    	}
    	enPayLoadType[i] = stVencChnAttr.stVeAttr.enType;
    	
    	s32Ret = SAMPLE_COMM_VENC_GetFilePostfix(enPayLoadType[i], szFilePostfix);		#根据 enPayLoadType[i]决定文件后缀名
    	if(s32Ret != HI_SUCCESS)
    	{
    		SAMPLE_PRT("SAMPLE_COMM_VENC_GetFilePostfix [%d] failed with %#x!
    ", stVencChnAttr.stVeAttr.enType, s32Ret);
    		return NULL;
    	}
    	sprintf(aszFileName[i], "stream_chn%d%s", i, szFilePostfix);		#获得文件名
    	pFile[i] = fopen(aszFileName[i], "wb");
    	if (!pFile[i])
    	{
    		SAMPLE_PRT("open file[%s] failed!
    ", 
    		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!
    ", 
    		VencFd[i]);
    		return NULL;
    	}
    	if (maxfd <= VencFd[i])
    	{
    		maxfd = VencFd[i];
    	}
    }
    
    /******************************************
     step 2:  Start to get streams of each channel.
    ******************************************/
    while (HI_TRUE == pstPara->bThreadStart)	# 正常情况下会一直录制视频,条件一直成立,除非在父进程中修改了 pstPara->bThreadStart
    {
    	FD_ZERO(&read_fds);
    	for (i = 0; i < s32ChnTotal; i++)
    	{
    		FD_SET(VencFd[i], &read_fds);
    	}
    
    	TimeoutVal.tv_sec  = 2;
    	TimeoutVal.tv_usec = 0;
    	s32Ret = select(maxfd + 1, &read_fds, NULL, NULL, &TimeoutVal);	#设置超时等待时间,并监视的文件描述符的变化
    	if (s32Ret < 0)
    	{
    		SAMPLE_PRT("select failed!
    ");
    		break;
    	}
    	else if (s32Ret == 0)
    	{
    		SAMPLE_PRT("get venc stream time out, exit thread
    ");
    		continue;
    	}
    	else
    	{		#文件描述符正常变化
    		for (i = 0; i < s32ChnTotal; i++)	#遍历三个视频流CHN,如果产生了变化就去做处理,软件上是完全无序的
    		{
    			if (FD_ISSET(VencFd[i], &read_fds))
    			{
    				/*******************************************************
    				step 2.1 : query how many packs in one-frame stream.
    				*******************************************************/
    				memset(&stStream, 0, sizeof(stStream));
    				s32Ret = HI_MPI_VENC_Query(i, &stStat);	#某一通道视频流就绪后查询此通道状态,将其保存到 stStat 中
    				if (HI_SUCCESS != s32Ret)
    				{
    					SAMPLE_PRT("HI_MPI_VENC_Query chn[%d] failed with %#x!
    ", i, s32Ret);
    					break;
    				}
    				
    				/*******************************************************
    				step 2.2 :suggest to check both u32CurPacks and u32LeftStreamFrames at the same time,for example:
    				if(0 == stStat.u32CurPacks || 0 == stStat.u32LeftStreamFrames)
    				{
    					SAMPLE_PRT("NOTE: Current  frame is NULL!
    ");
    					continue;
    				}
    				*******************************************************/
    				if(0 == stStat.u32CurPacks)
    				{
    					SAMPLE_PRT("NOTE: Current  frame is NULL!
    ");
    					continue;
    				}
    				/*******************************************************
    				step 2.3 : malloc corresponding number of pack nodes.
    				*******************************************************/
    				stStream.pstPack = (VENC_PACK_S*)malloc(sizeof(VENC_PACK_S) * stStat.u32CurPacks);	#分配存储视频流的内存
    				if (NULL == stStream.pstPack)
    				{
    					SAMPLE_PRT("malloc stream pack failed!
    ");
    					break;
    				}
    				
    				/*******************************************************
    				step 2.4 : call mpi to get one-frame stream
    				*******************************************************/
    				stStream.u32PackCount = stStat.u32CurPacks;
    				s32Ret = HI_MPI_VENC_GetStream(i, &stStream, HI_TRUE);	#以非阻塞的方式来获取视频流
    				if (HI_SUCCESS != s32Ret)
    				{
    					free(stStream.pstPack);
    					stStream.pstPack = NULL;
    					SAMPLE_PRT("HI_MPI_VENC_GetStream failed with %#x!
    ", 					s32Ret);
    					break;
    				}
    				
    				/*******************************************************
    				step 2.5 : save frame to file
    				*******************************************************/
    				s32Ret = SAMPLE_COMM_VENC_SaveStream(enPayLoadType[i], pFile[i], &stStream);	#将视频保存到flash中,根据视频格式选择不同方式,且保存是无缓存的,读完内存立即更新flash,实时更新。
    				if (HI_SUCCESS != s32Ret)
    				{
    					free(stStream.pstPack);
    					stStream.pstPack = NULL;
    					SAMPLE_PRT("save stream failed!
    ");
    					break;
    				}
    				/*******************************************************
    				step 2.6 : release stream
    				*******************************************************/
    				s32Ret = HI_MPI_VENC_ReleaseStream(i, &stStream);	#Release释放视频流,不然会一直占用,与Get成对出现
    				if (HI_SUCCESS != s32Ret)
    				{
    					free(stStream.pstPack);
    					stStream.pstPack = NULL;
    					break;
    				}
    				/*******************************************************
    				step 2.7 : free pack nodes
    				*******************************************************/
    				free(stStream.pstPack);
    				stStream.pstPack = NULL;
    			}
    		}
    	}
    }
    
    /*******************************************************
    * step 3 : close save-file
    *******************************************************/
    for (i = 0; i < s32ChnTotal; i++)
    {
        fclose(pFile[i]);		
    }
    
    return NULL;
    

    }

    退出视频录制

    getchar将本进程阻塞,在连续两次回车后可以继续运行

    · printf(“please press twice ENTER to exit this sample ”);
    getchar();
    getchar();

    /******************************************
    	step 7: exit process
    	******************************************/
    	SAMPLE_COMM_VENC_StopGetStream();		#释放视频录制的一些资源,最主要的是修改`gs_stPara.bThreadStart = HI_FALSE;`,将视频保存线程杀死,然后回收。
    
     
  • 相关阅读:
    Apache Kafka:下一代分布式消息系统
    深入理解Java之线程池
    JAVA中线程同步的方法(7种)汇总
    String、StringBuffer与StringBuilder之间区别
    Java中是否可以继承String类,为什么
    JAVA4种线程池的使用
    一分钟教你知道乐观锁和悲观锁的区别
    java常见面试题及答案 11-20(JVM)
    springmvc中的页面解析器ViewResolver不起作用,变量输出字符串的解决方案
    SpringMVC默认欢迎页面的问题
  • 原文地址:https://www.cnblogs.com/sunnyside/p/12882553.html
Copyright © 2011-2022 走看看