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\n");
	return HI_FAILURE;
}
#ifdef hi3518ev201
	s32ChnNum = 1;
#endif
printf("s32ChnNum = %d\n",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!\n", 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!\n”);
    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!\n”);
    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!\n");
      	goto END_VENC_1080P_CLASSIC_2;
      }
    
      s32Ret = SAMPLE_COMM_VI_BindVpss(stViConfig.enViMode);		#綁定VPSS通道
      if (HI_SUCCESS != s32Ret)
      {
      	SAMPLE_PRT("Vi bind Vpss failed!\n");
      	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!\n");
      	goto END_VENC_1080P_CLASSIC_4;
      }
    

    }

    step5:start stream venc(開啟venc流視頻)

/******************************************
step 5: start stream venc
***************************************/
/
 HD1080P **/
printf("\t c) cbr.\n");
printf("\t v) vbr.\n");
printf("\t f) fixQp\n");
printf(“please input choose rc mode!\n”);
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!\n”);
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!\n");
		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!\n");
		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!\n”);
goto END_VENC_1080P_CLASSIC_5;
}

printf("please press twice ENTER to exit this sample\n");
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\n", __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\n", __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!\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);		#其中包括了很多細節如3A初始化,ISP具體初始化等
	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();		#主要是開了一個線程來跑Test_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	#配置並開啟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!\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);	#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!\n");
		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!\n");
			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!\n");
			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!\n");
			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!\n");
			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!\n");
			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("\t c) cbr.\n");
	printf("\t v) vbr.\n");
	printf("\t f) fixQp\n");
	printf("please input choose rc mode!\n");
	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!\n");
			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!\n");
			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!\n");
			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!\n");
        goto END_VENC_1080P_CLASSIC_5;
    }

    printf("please press twice ENTER to exit this sample\n");
    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\n");
	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!\n", 		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!\n", 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!\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];
	}
}

/******************************************
 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!\n");
		break;
	}
	else if (s32Ret == 0)
	{
		SAMPLE_PRT("get venc stream time out, exit thread\n");
		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!\n", 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!\n");
					continue;
				}
				*******************************************************/
				if(0 == stStat.u32CurPacks)
				{
					SAMPLE_PRT("NOTE: Current  frame is NULL!\n");
					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!\n");
					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!\n", 					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!\n");
					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\n”);
getchar();
getchar();

/******************************************
	step 7: exit process
	******************************************/
	SAMPLE_COMM_VENC_StopGetStream();		#釋放視頻錄制的一些資源,最主要的是修改`gs_stPara.bThreadStart = HI_FALSE;`,將視頻保存線程殺死,然后回收。
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM