概述

从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 hi3518ev201s32ChnNum = 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 来使能CHNif (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 mipiif (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,有很多个虚拟channelif (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 的 stVpssGrpAttrif (HI_SUCCESS != s32Ret){SAMPLE_PRT("Start Vpss failed!\n");goto END_VENC_1080P_CLASSIC_2;}s32Ret = SAMPLE_COMM_VI_BindVpss(stViConfig.enViMode);        #也仅仅在第一个通道的时候绑定VI与VPSSif (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 与 stVpssChnModeif (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);    #获取第二通道图像大小填充stSizeif (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 VPSSCHNif (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 的 Attrif(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;`,将视频保存线程杀死,然后回收。

sample_venc解析相关推荐

  1. 海思项目学习记录 -2、解析mmp的sample

    1.分析mmp的sample工程 注意:建立SI工程的时候注意要把外面的那个include文件夹加载进来 sample_venc的大体分析 (1)从main入手,main的传参分析 (2)几个基本概念 ...

  2. golang通过RSA算法生成token,go从配置文件中注入密钥文件,go从文件中读取密钥文件,go RSA算法下token生成与解析;go java token共用

    RSA算法 token生成与解析 本文演示两种方式,一种是把密钥文件放在配置文件中,一种是把密钥文件本身放入项目或者容器中. 下面两种的区别在于私钥公钥的初始化, init方法,需要哪种取哪种. 通过 ...

  3. List元素互换,List元素转换下标,Java Collections.swap()方法实例解析

    Java Collections.swap()方法解析 jdk源码: public static void swap(List<?> list, int i, int j) {// ins ...

  4. 条形码?二维码?生成、解析都在这里!

    二维码生成与解析 一.生成二维码 二.解析二维码 三.生成一维码 四.全部的代码 五.pom依赖 直接上代码: 一.生成二维码 public class demo {private static fi ...

  5. Go 学习笔记(82)— Go 第三方库之 viper(解析配置文件、热更新配置文件)

    1. viper 特点 viper 是一个完整的 Go应用程序的配置解决方案,它被设计为在应用程序中工作,并能处理所有类型的配置需求和格式.支持特性功能如下: 设置默认值 读取 JSON.TOML.Y ...

  6. Go 学习笔记(77)— Go 第三方库之 cronexpr(解析 crontab 表达式,定时任务)

    cronexpr 支持的比 Linux 自身的 crontab 更详细,可以精确到秒级别. ​ 1. 实现方式 cronexpr 表达式从前到后的顺序如下所示: 字段类型 是否为必须字段 允许的值 允 ...

  7. mybatis配置文件解析

    mybatis配置文件解析 mybatis核心配置文件`mybatis-config.xml文件. mybatis的配置文件包含了会深深影响mybatis行为的设置和属性信息. 能配置的内容: con ...

  8. 谷歌BERT预训练源码解析(二):模型构建

    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.csdn.net/weixin_39470744/arti ...

  9. Python 标准库之 xml.etree.ElementTree xml解析

    Python 标准库之 xml.etree.ElementTree Python中有多种xml处理API,常用的有xml.dom.*模块.xml.sax.*模块.xml.parser.expat模块和 ...

  10. 谷歌BERT预训练源码解析(三):训练过程

    目录 前言 源码解析 主函数 自定义模型 遮蔽词预测 下一句预测 规范化数据集 前言 本部分介绍BERT训练过程,BERT模型训练过程是在自己的TPU上进行的,这部分我没做过研究所以不做深入探讨.BE ...

最新文章

  1. Chapter 1 First Sight——14
  2. javascript 閉包
  3. 皮一皮:低调的凡尔赛...
  4. 11.Verilog中如何避免Latch
  5. SAP MPS、MRP名词解释
  6. 选32位 64位 oracle,32位PLSQL配置为64位的Oracle和64位系统
  7. oracle索引机制
  8. SQL基础【三、Select】
  9. 《算法竞赛入门经典》习题4-2 正方形 (Squares,ACM,ICPC World Finals 1990,UVa201)——仅提供大体方法
  10. my batis的理解
  11. mysql简单聚合函数根据条件单表查询
  12. linux删除jpeg动态库,linux如何不用的删除动态库
  13. spark 性能优化
  14. 《中国人工智能学会通讯》——1.20 聊天机器人研究存在的挑战
  15. .net framework 3.5win10无法安装,一招解决win10无法安装.NET Framework 3.5
  16. 计算机毕设 SpringBoot+Vue车辆租赁管理系统 网上汽车租赁系统 汽车租赁管理系统 汽车分时租赁系统Java Vue MySQL数据库 远程调试 代码讲解
  17. 小红书6.18种草拔草投放攻略
  18. 谷歌邮箱的注册以及GEE的注册使用教程
  19. Leetcode报错runtime error
  20. 黑客主要学习python的什么_黑客最常用的黑客语言——Python!

热门文章

  1. cad插件苹果系统_CAD看图软件mac版|CAD迷你看图 for Mac下载 v4.0.0 官方版_最火软件站...
  2. PPT高级教程及技巧 .
  3. 蓝桥杯题目 计算后续日期
  4. funcode黄金矿工(提高篇)
  5. 【Axure图标库】单线形图标元件库 细线矢量图标1000+
  6. 从零开始实现 AlphaGo(一)
  7. 多功能照片图片处理器小程序源码/流量主系列小程序源码
  8. windows网络编程-几种模式
  9. nodejs调用google翻译api
  10. 2345等浏览器主页劫持的解决办法