ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

1.1 海思3518 H264编码

2019-07-04 12:30:16  阅读:594  来源: 互联网

标签:3518 s32Ret H264 MPI SAMPLE HI VENC VPSS 海思


海思的SDK里其实有H264编码的sample,但因为要匹配很多东西,代码有点复杂,让初学都感到有点混乱。我根据sample自己修改了一下代码,从最简单的情况(确定摄像头类型,只选一种尺寸的图片,只用一个通道)来说明海思HI3518是怎么编码为H264的。

先把源代码下载下来,再分析程序。

下载链接:https://download.csdn.net/download/zhanshenrui/10324766

首先从main函数开始。

//./myvenc 
int main(int argc, char *argv[])//main()
{
	HI_S32 s32Ret;
	signal(SIGINT, SAMPLE_VENC_HandleSig);//ctrl+c,delete
	signal(SIGTERM, SAMPLE_VENC_HandleSig);//shell命令kill缺省产生这个信号.
	s32Ret = H264_Venc();
<span class="hljs-keyword">if</span>(s32Ret==HI_SUCCESS)
	<span class="hljs-built_in">printf</span>(<span class="hljs-string">"normally\n"</span>);
<span class="hljs-keyword">else</span>
	<span class="hljs-built_in">printf</span>(<span class="hljs-string">"unnormally\n"</span>);
<span class="hljs-keyword">return</span> <span class="hljs-number">-1</span>;

}
main函数里先是用signal定义了两个信号来中断程序的运行,在板子上输入./myvenc 执行程序,按ctrl+c来中断程序,然后再往下就是H264编码函数H264_Venc()。

在H264_Venc()函数里先去初始化MPI系统,然后根据摄像头设置参数。我这里是AR030摄像头,其拍摄图片大小为720P,编码为H264,为了简单只输出一路。在初始化MPI系统时要计算视频缓存池VB_CONF_S的大小,然后填充VB_CONF_S结构体。填充完VB_CONF_S结构体后再用HI_MPI_VB_SetConf设置,最后初始化视频视频缓存池HI_MPI_VB_Init。这里注意的是基本上每个函数调用后都会判断返回值并作出错处理,后面很多函数也是这样处理的,这样利于程序排错。

//为了简单点,编码类型就选择PT_H264,图片大小就选择PIC_HD720,通道也只用1路s32ChnNum=1
PAYLOAD_TYPE_E enPayLoad=PT_H264;//264编码
PIC_SIZE_E enSize=PIC_HD720;//摄像头拍摄图片的大小,这里只用720P
HI_S32 s32ChnNum=1;//支持一路摄像
HI_S32 s32Ret=HI_FAILURE;
<span class="hljs-comment">/******************************************
mpp system init. 
******************************************/</span>
HI_MPI_SYS_Exit();
HI_MPI_VB_Exit();

VB_CONF_S stVbConf;<span class="hljs-comment">//缓存池参数结构体</span>
HI_U32 u32BlkSize;<span class="hljs-comment">//一张图片占多少字节</span>
<span class="hljs-built_in">memset</span>(&amp;stVbConf,<span class="hljs-number">0</span>,<span class="hljs-keyword">sizeof</span>(VB_CONF_S));
<span class="hljs-comment">//根据制式,图片大小,图片格式及对齐方式确定图片缓存大小</span>
<span class="hljs-comment">//这里用NTSC,720P,YUV420,64字节对齐</span>
u32BlkSize=SAMPLE_COMM_SYS_CalcPicVbBlkSize(gs_enNorm,enSize, PIXEL_FORMAT_YUV_SEMIPLANAR_420, SAMPLE_SYS_ALIGN_WIDTH);
<span class="hljs-built_in">printf</span>(<span class="hljs-string">"u32BlkSize=%d\n"</span>,u32BlkSize);
stVbConf.u32MaxPoolCnt = <span class="hljs-number">128</span>;<span class="hljs-comment">//用默认值,3518默认是128</span>
stVbConf.astCommPool[<span class="hljs-number">0</span>].u32BlkSize = u32BlkSize;
stVbConf.astCommPool[<span class="hljs-number">0</span>].u32BlkCnt = g_u32BlkCnt;
s32Ret = HI_MPI_VB_SetConf(&amp;stVbConf);<span class="hljs-comment">//设置 MPP 视频缓存池属性</span>
<span class="hljs-keyword">if</span> (HI_SUCCESS != s32Ret)
{
	SAMPLE_PRT(<span class="hljs-string">"HI_MPI_VB_SetConf failed!\n"</span>);
	<span class="hljs-keyword">return</span> HI_FAILURE;
}
s32Ret = HI_MPI_VB_Init();<span class="hljs-comment">//初始化 MPP 视频缓存池</span>
<span class="hljs-keyword">if</span> (HI_SUCCESS != s32Ret)
{
	SAMPLE_PRT(<span class="hljs-string">"HI_MPI_VB_Init failed!\n"</span>);
	<span class="hljs-keyword">return</span> HI_FAILURE;
}</code></pre><span></span>视频缓存池初始化后就是初始化MPI系统。海思模块化了各个功能,我们想实现哪一种产品就按这种产品的框架来实现各个模块功能,而实现模块化功能也很简单,基本套路就是定义一个结构体,给结构体赋值,然后设置就可以了,像前面缓存池模块就是先定一个VB_CONF_S结构体,然后对结体体赋值,然后调用函数设置进去,最后初始化。同理,初始化MPI系统也要先定义一个结构体再赋值然后设置最后初始化,这种思路在linux里用得很多。<p></p><p><span style="line-height:28px;"><span style="color:rgb(79,79,79);line-height:28px;text-align:justify;"></span></span></p><pre><code class="language-cpp hljs"><span class="hljs-comment">//定义结构体,对结构体赋值,然后设置,最后初始化</span>
MPP_SYS_CONF_S stSysConf = {<span class="hljs-number">0</span>};
stSysConf.u32AlignWidth = SAMPLE_SYS_ALIGN_WIDTH;
s32Ret = HI_MPI_SYS_SetConf(&amp;stSysConf);<span class="hljs-comment">//配置系统控制参数</span>
<span class="hljs-keyword">if</span> (HI_SUCCESS != s32Ret)
{
	SAMPLE_PRT(<span class="hljs-string">"HI_MPI_SYS_SetConf failed\n"</span>);
	<span class="hljs-keyword">return</span> HI_FAILURE;
}
s32Ret = HI_MPI_SYS_Init();<span class="hljs-comment">//初始化 MPP 系统</span>
<span class="hljs-keyword">if</span> (HI_SUCCESS != s32Ret)
{
	SAMPLE_PRT(<span class="hljs-string">"HI_MPI_SYS_Init failed!\n"</span>);
	<span class="hljs-keyword">return</span> HI_FAILURE;
}</code></pre>系统初始化完后还要打开硬件,摄像头用的是MIPI,应用层只需要用open打开MIPI,用ioctl设置<p></p><p><span style="color:rgb(79,79,79);line-height:28px;text-align:justify;"><span style="color:rgb(79,79,79);line-height:28px;text-align:justify;"></span></span></p><pre><code class="language-cpp hljs">HI_S32 fdmipi;
<span class="hljs-keyword">combo_dev_attr_t</span> *pstcomboDevAttr = <span class="hljs-literal">NULL</span>;
fdmipi = open(<span class="hljs-string">"/dev/hi_mipi"</span>, O_RDWR);
<span class="hljs-keyword">if</span> (fdmipi &lt; <span class="hljs-number">0</span>)
{
	<span class="hljs-built_in">printf</span>(<span class="hljs-string">"warning: open hi_mipi dev failed\n"</span>);
	<span class="hljs-keyword">return</span> <span class="hljs-number">-1</span>;
}
pstcomboDevAttr = &amp;MIPI_CMOS3V3_ATTR;<span class="hljs-comment">//AR0130模组属性</span>
<span class="hljs-keyword">if</span> (ioctl(fdmipi, HI_MIPI_SET_DEV_ATTR, pstcomboDevAttr))
{
	<span class="hljs-built_in">printf</span>(<span class="hljs-string">"set mipi attr failed\n"</span>);
	close(fdmipi);
<span class="hljs-keyword">return</span> HI_FAILURE;
}
close(fdmipi);</code></pre>根据海思SDK里的文档介绍,H264编码过程应该是:系统初始化--VI--VPSS-VENC.系统初始化前面已完成,现在要做的就是实现输入模块VI.VI模块有通道,所以还要设置通道参数。具体解释和代码如下,不再做过多解释:<p></p><p><span style="color:rgb(79,79,79);line-height:28px;text-align:justify;"><span style="color:rgb(79,79,79);line-height:28px;text-align:justify;"></span></span></p><pre><code class="language-cpp hljs">HI_S32 i,ViChn=<span class="hljs-number">0</span>;<span class="hljs-comment">//只有一路输出,所以ViChn=0</span>
<span class="hljs-comment">//设置输入配置参数</span>
SAMPLE_VI_CONFIG_S stViConfig = {<span class="hljs-number">0</span>};
stViConfig.enViMode   = APTINA_AR0130_DC_720P_30FPS;<span class="hljs-comment">//摄像头是AR0130模组</span>
stViConfig.enRotate   = ROTATE_NONE;<span class="hljs-comment">//不翻转</span>
stViConfig.enNorm     = VIDEO_ENCODING_MODE_AUTO;<span class="hljs-comment">//编码模式自动</span>
stViConfig.enViChnSet = VI_CHN_SET_NORMAL;<span class="hljs-comment">//普通</span>
stViConfig.enWDRMode  = WDR_MODE_NONE;<span class="hljs-comment">//不设置宽动态</span>

VI_DEV_ATTR_S  stViDevAttr;
VI_DEV ViDev=<span class="hljs-number">0</span>;<span class="hljs-comment">//只有一个摄像头设备,所以设备序号为0</span>
ISP_DEV s32IspDev=<span class="hljs-number">0</span>;<span class="hljs-comment">//同样,ISP序号也为0</span>
<span class="hljs-built_in">memset</span>(&amp;stViDevAttr,<span class="hljs-number">0</span>,<span class="hljs-keyword">sizeof</span>(stViDevAttr));
<span class="hljs-built_in">memcpy</span>(&amp;stViDevAttr,&amp;DEV_ATTR_9M034_DC_720P_BASE,<span class="hljs-keyword">sizeof</span>(stViDevAttr));
<span class="hljs-comment">/******************************************
step 1: mipi configure
******************************************/</span>
<span class="hljs-comment">/*
s32Ret = SAMPLE_COMM_VI_StartMIPI(&amp;stViConfig);
if (HI_SUCCESS != s32Ret)
{
	SAMPLE_PRT("%s: MIPI init failed!\n", __FUNCTION__);
	return HI_FAILURE;
}  
*/</span>
<span class="hljs-comment">/******************************************
step 2: configure sensor and ISP (include WDR mode).
note: you can jump over this step, if you do not use Hi3516A interal isp. 
//虽然说了不用,但还要需要,因为后面HI_MPI_ISP_GetWDRMode函数会调用ISP_CHECK_MEM_INIT
//检测内存,所以还是在这里使用
******************************************/</span>
s32Ret = SAMPLE_COMM_ISP_Init(stViConfig.enWDRMode);
<span class="hljs-keyword">if</span> (HI_SUCCESS != s32Ret)
{
	SAMPLE_PRT(<span class="hljs-string">"%s: Sensor init failed!\n"</span>, __FUNCTION__);
	<span class="hljs-keyword">return</span> HI_FAILURE;
}
<span class="hljs-comment">/******************************************
step 3: run isp thread 
note: you can jump over this step, if you do not use Hi3516A interal isp.
******************************************/</span>
s32Ret = SAMPLE_COMM_ISP_Run();
<span class="hljs-keyword">if</span> (HI_SUCCESS != s32Ret)
{
	SAMPLE_PRT(<span class="hljs-string">"%s: ISP init failed!\n"</span>, __FUNCTION__);
	<span class="hljs-comment">/* disable videv */</span>
	<span class="hljs-keyword">return</span> HI_FAILURE;
}

s32Ret=HI_MPI_VI_SetDevAttr(ViDev, &amp;stViDevAttr);<span class="hljs-comment">//只有一个设备,所以设备号为0</span>
<span class="hljs-keyword">if</span> (s32Ret != HI_SUCCESS)
{
	SAMPLE_PRT(<span class="hljs-string">"HI_MPI_VI_SetDevAttr failed with %#x!\n"</span>, s32Ret);
	<span class="hljs-keyword">return</span> HI_FAILURE;
}
ISP_WDR_MODE_S stWdrMode;
s32Ret = HI_MPI_ISP_GetWDRMode(s32IspDev, &amp;stWdrMode);
<span class="hljs-keyword">if</span> (s32Ret != HI_SUCCESS)
{
	SAMPLE_PRT(<span class="hljs-string">"HI_MPI_ISP_GetWDRMode failed with %#x!\n"</span>, s32Ret);
	<span class="hljs-keyword">return</span> HI_FAILURE;
}
VI_WDR_ATTR_S stWdrAttr;
stWdrAttr.enWDRMode = stWdrMode.enWDRMode;
stWdrAttr.bCompress = HI_FALSE;
s32Ret = HI_MPI_VI_SetWDRAttr(ViDev, &amp;stWdrAttr);
<span class="hljs-keyword">if</span> (s32Ret)
{
	SAMPLE_PRT(<span class="hljs-string">"HI_MPI_VI_SetWDRAttr failed with %#x!\n"</span>, s32Ret);
	<span class="hljs-keyword">return</span> HI_FAILURE;
}
s32Ret = HI_MPI_VI_EnableDev(ViDev);
<span class="hljs-keyword">if</span> (s32Ret != HI_SUCCESS)
{
	SAMPLE_PRT(<span class="hljs-string">"HI_MPI_VI_EnableDev failed with %#x!\n"</span>, s32Ret);
	<span class="hljs-keyword">return</span> HI_FAILURE;
}


RECT_S stCapRect;
SIZE_S stTargetSize;
stCapRect.s32X = <span class="hljs-number">0</span>;
   stCapRect.s32Y = <span class="hljs-number">0</span>;
stCapRect.u32Width = <span class="hljs-number">1280</span>;
   stCapRect.u32Height = <span class="hljs-number">720</span>;<span class="hljs-comment">//AR030输出1280*720图像</span>
   stTargetSize.u32Width = stCapRect.u32Width;
   stTargetSize.u32Height = stCapRect.u32Height;
<span class="hljs-comment">//设置通道属性</span>
VI_CHN_ATTR_S stChnAttr;
<span class="hljs-built_in">memcpy</span>(&amp;stChnAttr.stCapRect, &amp;stCapRect, <span class="hljs-keyword">sizeof</span>(RECT_S));
stChnAttr.enCapSel = VI_CAPSEL_BOTH;
stChnAttr.stDestSize.u32Width = stTargetSize.u32Width;
stChnAttr.stDestSize.u32Height = stTargetSize.u32Height;
stChnAttr.enPixFormat = PIXEL_FORMAT_YUV_SEMIPLANAR_420; 
stChnAttr.bMirror = HI_FALSE;
stChnAttr.bFlip = HI_FALSE;
stChnAttr.s32SrcFrameRate = <span class="hljs-number">-1</span>;
stChnAttr.s32DstFrameRate = <span class="hljs-number">-1</span>;
stChnAttr.enCompressMode = COMPRESS_MODE_NONE;
s32Ret = HI_MPI_VI_SetChnAttr(ViChn, &amp;stChnAttr);<span class="hljs-comment">//设置通道属性</span>
<span class="hljs-keyword">if</span> (s32Ret != HI_SUCCESS)
{
	SAMPLE_PRT(<span class="hljs-string">"in HI_MPI_VI_SetChnAttr failed with %#x!\n"</span>, s32Ret);
	SAMPLE_COMM_ISP_Stop();
	<span class="hljs-keyword">return</span> HI_FAILURE;
}
s32Ret = HI_MPI_VI_EnableChn(ViChn);<span class="hljs-comment">//使能通道</span>
<span class="hljs-keyword">if</span> (s32Ret != HI_SUCCESS)
{
	SAMPLE_PRT(<span class="hljs-string">"HI_MPI_VI_EnableChn failed with %#x!\n"</span>, s32Ret);
	SAMPLE_COMM_ISP_Stop();
	<span class="hljs-keyword">return</span> HI_FAILURE;
}</code></pre>VI模块设置完以后就要设置VPSS模块,VPSS模块有组,通道概念,所以设置VPSS模块也要设置VPSS组、VPSS通道。<p></p><p><span style="color:rgb(79,79,79);line-height:28px;text-align:justify;"><span style="color:rgb(79,79,79);line-height:28px;text-align:justify;">设置VPSS组过程如下:定义结构体,给结构体赋值,设置(创建的同时就设置了),启动</span></span></p><p><span style="color:rgb(79,79,79);line-height:28px;text-align:justify;"><span style="color:rgb(79,79,79);line-height:28px;text-align:justify;"></span></span></p><pre><code class="language-cpp hljs">SIZE_S stSize;
stSize.u32Width  = <span class="hljs-number">1280</span>;
stSize.u32Height = <span class="hljs-number">720</span>;

VPSS_GRP_ATTR_S stVpssGrpAttr;
VPSS_NR_PARAM_U unNrParam = {{<span class="hljs-number">0</span>}};
VPSS_GRP VpssGrp=<span class="hljs-number">0</span>;<span class="hljs-comment">//只有一个设备,序号为0</span>
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 = HI_MPI_VPSS_CreateGrp(VpssGrp, &amp;stVpssGrpAttr);<span class="hljs-comment">//创建组的同时将组的属性设置进去</span>
<span class="hljs-keyword">if</span> (s32Ret != HI_SUCCESS)
{
	SAMPLE_PRT(<span class="hljs-string">"HI_MPI_VPSS_CreateGrp failed with %#x!\n"</span>, s32Ret);
	<span class="hljs-keyword">return</span> HI_FAILURE;
}
s32Ret = HI_MPI_VPSS_GetNRParam(VpssGrp, &amp;unNrParam);
<span class="hljs-keyword">if</span> (s32Ret != HI_SUCCESS)
{
	SAMPLE_PRT(<span class="hljs-string">"HI_MPI_VPSS_GetNRParam failed with %#x!\n"</span>, s32Ret);
	<span class="hljs-keyword">return</span> HI_FAILURE;
}
s32Ret = HI_MPI_VPSS_SetNRParam(VpssGrp, &amp;unNrParam);
<span class="hljs-keyword">if</span> (s32Ret != HI_SUCCESS)
{
	SAMPLE_PRT(<span class="hljs-string">"HI_MPI_VPSS_SetNRParam failed with %#x!\n"</span>, s32Ret);
	<span class="hljs-keyword">return</span> HI_FAILURE;
}
s32Ret = HI_MPI_VPSS_StartGrp(VpssGrp);<span class="hljs-comment">//启动VPSS组</span>
<span class="hljs-keyword">if</span> (s32Ret != HI_SUCCESS)
{
	SAMPLE_PRT(<span class="hljs-string">"HI_MPI_VPSS_StartGrp failed with %#x\n"</span>, s32Ret);
	<span class="hljs-keyword">return</span> HI_FAILURE;
}</code></pre>VPSS还要与VI绑定,以便VI的数据直接输入VPSS,不需要我们手动从VI复制数据到VPSS,绑定时将绑定源设置为VI的哪<br style="color:rgb(79,79,79);line-height:28px;text-align:justify;"><span style="color:rgb(79,79,79);line-height:28px;text-align:justify;">一个通道,目的源设置为VPSS的一个通道</span><pre><code class="language-cpp hljs">SAMPLE_VI_PARAM_S stViParam;
stViParam.s32ViDevCnt      = <span class="hljs-number">1</span>;
stViParam.s32ViDevInterval = <span class="hljs-number">1</span>;
stViParam.s32ViChnCnt      = <span class="hljs-number">1</span>;
stViParam.s32ViChnInterval = <span class="hljs-number">1</span>;
MPP_CHN_S stSrcChn;
	MPP_CHN_S stDestChn;
stSrcChn.enModId  = HI_ID_VIU;
stSrcChn.s32DevId = <span class="hljs-number">0</span>;
stSrcChn.s32ChnId = ViChn;

stDestChn.enModId  = HI_ID_VPSS;
stDestChn.s32DevId = VpssGrp;
stDestChn.s32ChnId = <span class="hljs-number">0</span>;
s32Ret = HI_MPI_SYS_Bind(&amp;stSrcChn, &amp;stDestChn);<span class="hljs-comment">//将VI的0通道与VPSS的组的0通道绑定起来</span>
<span class="hljs-keyword">if</span> (s32Ret != HI_SUCCESS)
{
	SAMPLE_PRT(<span class="hljs-string">"failed with %#x!\n"</span>, s32Ret);
	<span class="hljs-keyword">return</span> HI_FAILURE;
}</code></pre>前面设置了VPSS组,将VI通道与VPSS组里的0通道绑定起来,但还未设置0通道,下面设置0通道.还是老套路,定义结构体,给结构体赋值,将结构体设置进去,开启通道。<br><pre><code class="language-cpp hljs"><span class="hljs-comment">//设置VPSS通道,只有一个通道,所以VpssChn=0</span>
VPSS_CHN VpssChn=<span class="hljs-number">0</span>;
VPSS_CHN_ATTR_S stVpssChnAttr;
VPSS_CHN_MODE_S 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;
<span class="hljs-built_in">memset</span>(&amp;stVpssChnAttr, <span class="hljs-number">0</span>, <span class="hljs-keyword">sizeof</span>(stVpssChnAttr));
stVpssChnAttr.s32SrcFrameRate = <span class="hljs-number">-1</span>;
stVpssChnAttr.s32DstFrameRate = <span class="hljs-number">-1</span>;
s32Ret = HI_MPI_VPSS_SetChnAttr(VpssGrp, VpssChn, &amp;stVpssChnAttr);
<span class="hljs-keyword">if</span> (s32Ret != HI_SUCCESS)
{
	SAMPLE_PRT(<span class="hljs-string">"HI_MPI_VPSS_SetChnAttr failed with %#x\n"</span>, s32Ret);
	<span class="hljs-keyword">return</span> HI_FAILURE;
}
s32Ret = HI_MPI_VPSS_SetChnMode(VpssGrp, VpssChn, &amp;stVpssChnMode);
<span class="hljs-keyword">if</span> (s32Ret != HI_SUCCESS)
{
	SAMPLE_PRT(<span class="hljs-string">"%s failed with %#x\n"</span>, __FUNCTION__, s32Ret);
	<span class="hljs-keyword">return</span> HI_FAILURE;
}  
s32Ret = HI_MPI_VPSS_EnableChn(VpssGrp, VpssChn);
<span class="hljs-keyword">if</span> (s32Ret != HI_SUCCESS)
{
	SAMPLE_PRT(<span class="hljs-string">"HI_MPI_VPSS_EnableChn failed with %#x\n"</span>, s32Ret);
	<span class="hljs-keyword">return</span> HI_FAILURE;
}</code></pre>上面将VPSS设置完成,按照框架,下一个应该设置VENC模块,跟VPSS一样,老套路设置,然后绑定。<p></p><p><span style="color:rgb(79,79,79);line-height:28px;text-align:justify;"><span style="color:rgb(79,79,79);line-height:28px;text-align:justify;"></span></span></p><pre><code class="language-cpp hljs"><span class="hljs-comment">//定义结构体,给结构体赋值,创建通道同时将结构体设置进去</span>
SAMPLE_RC_E enRcMode= SAMPLE_RC_CBR;
VENC_CHN VencChn=<span class="hljs-number">0</span>;
VENC_CHN_ATTR_S stVencChnAttr;
VENC_ATTR_H264_S stH264Attr;
VENC_ATTR_H264_CBR_S    stH264Cbr;
stVencChnAttr.stVeAttr.enType =enPayLoad;
stH264Attr.u32MaxPicWidth =stSize.u32Width;
stH264Attr.u32MaxPicHeight = stSize.u32Height;
stH264Attr.u32PicWidth = stSize.u32Width;<span class="hljs-comment">/*the picture width*/</span>
stH264Attr.u32PicHeight = stSize.u32Height;<span class="hljs-comment">/*the picture height*/</span>
stH264Attr.u32BufSize  = stSize.u32Width * stSize.u32Height;<span class="hljs-comment">/*stream buffer size*/</span>
stH264Attr.u32Profile  = <span class="hljs-number">0</span>;<span class="hljs-comment">/*0: baseline; 1:MP; 2:HP;  3:svc_t */</span>
stH264Attr.bByFrame = HI_TRUE;<span class="hljs-comment">/*get stream mode is slice mode or frame mode?*/</span>
stH264Attr.u32BFrameNum = <span class="hljs-number">0</span>;<span class="hljs-comment">/* 0: not support B frame; &gt;=1: number of B frames */</span>
stH264Attr.u32RefNum = <span class="hljs-number">1</span>;<span class="hljs-comment">/* 0: default; number of refrence frame*/</span>
<span class="hljs-built_in">memcpy</span>(&amp;stVencChnAttr.stVeAttr.stAttrH264e, &amp;stH264Attr, <span class="hljs-keyword">sizeof</span>(VENC_ATTR_H264_S));

stVencChnAttr.stRcAttr.enRcMode = VENC_RC_MODE_H264CBR;
stH264Cbr.u32Gop            = (VIDEO_ENCODING_MODE_PAL== gs_enNorm)?<span class="hljs-number">25</span>:<span class="hljs-number">30</span>;
stH264Cbr.u32StatTime       = <span class="hljs-number">1</span>; <span class="hljs-comment">/* stream rate statics time(s) */</span>
stH264Cbr.u32SrcFrmRate      = (VIDEO_ENCODING_MODE_PAL== gs_enNorm)?<span class="hljs-number">25</span>:<span class="hljs-number">30</span>;<span class="hljs-comment">/* input (vi) frame rate */</span>
stH264Cbr.fr32DstFrmRate = (VIDEO_ENCODING_MODE_PAL== gs_enNorm)?<span class="hljs-number">25</span>:<span class="hljs-number">30</span>;<span class="hljs-comment">/* target frame rate */</span>
stH264Cbr.u32BitRate = <span class="hljs-number">1024</span>*<span class="hljs-number">2</span>;
stH264Cbr.u32FluctuateLevel = <span class="hljs-number">0</span>; <span class="hljs-comment">/* average bit rate */</span>
<span class="hljs-built_in">memcpy</span>(&amp;stVencChnAttr.stRcAttr.stAttrH264Cbr, &amp;stH264Cbr, <span class="hljs-keyword">sizeof</span>(VENC_ATTR_H264_CBR_S));
s32Ret = HI_MPI_VENC_CreateChn(VencChn, &amp;stVencChnAttr);<span class="hljs-comment">//创建通道同时将结构体设置进去</span>
<span class="hljs-keyword">if</span> (HI_SUCCESS != s32Ret)
{
	SAMPLE_PRT(<span class="hljs-string">"HI_MPI_VENC_CreateChn [%d] faild with %#x!\n"</span>,VencChn, s32Ret);
	<span class="hljs-keyword">return</span> s32Ret;
}</code></pre>设置完以后,开启接收图片并将VPSS通道与VENC通道绑定起来。VPSS通道作为源通道,VENC通道作为目的通道<p></p><p><span style="color:rgb(79,79,79);line-height:28px;text-align:justify;"><span style="color:rgb(79,79,79);line-height:28px;text-align:justify;"></span></span></p><pre><code class="language-cpp hljs"><span class="hljs-comment">//将VPSS通道作为源通道与VENC通道作为目的通道绑定起来</span>
stSrcChn.enModId = HI_ID_VPSS;
stSrcChn.s32DevId = VpssGrp;
stSrcChn.s32ChnId = VpssChn;
stDestChn.enModId = HI_ID_VENC;
stDestChn.s32DevId = <span class="hljs-number">0</span>;
stDestChn.s32ChnId = VencChn;
s32Ret=HI_MPI_SYS_Bind(&amp;stSrcChn, &amp;stDestChn);
<span class="hljs-keyword">if</span> (s32Ret != HI_SUCCESS)
{
	SAMPLE_PRT(<span class="hljs-string">"failed with %#x!\n"</span>, s32Ret);
	<span class="hljs-keyword">return</span> HI_FAILURE;
}</code></pre>这样,整个系统我们就设置好了,下一步要操作地是开始接收H264码流并将码流保存起来。这里用到多线程,用pthread_create创建个线程函数,在线程函数里用select多路IO复用来获取码流并保存。<pre><code class="language-cpp hljs">gs_stPara.bThreadStart = HI_TRUE;
gs_stPara.s32Cnt = s32ChnNum;
pthread_create(&amp;gs_VencPid, <span class="hljs-number">0</span>, VENC_GetVencStreamProc, (HI_VOID*)&amp;gs_stPara);

<span class="hljs-built_in">printf</span>(<span class="hljs-string">"please press twice ENTER to exit this sample\n"</span>);</code></pre>gs_stPara是自己定义的一个结构体,里面的成员变量bThreadStart为真时表示不断读取保存H264码流,当接连有两次按下键盘时或有ctrl+c按下时,这个成员变量会为假,从而会停止VENC_GetVencStreamProc线程函数。<span style="color:rgb(79,79,79);line-height:28px;text-align:justify;">VENC_GetVencStreamProc线程函数可以说是获取H264码流的最关键的函数,我们稍会再说,先按正常流程说下后面代码的作用。后面的代码如下:</span><p></p><p><span style="color:rgb(79,79,79);line-height:28px;text-align:justify;"><span style="color:rgb(79,79,79);line-height:28px;text-align:justify;"></span></span></p><pre><code class="language-cpp hljs">getchar();
getchar();
<span class="hljs-keyword">if</span> (HI_TRUE == gs_stPara.bThreadStart)
{
	gs_stPara.bThreadStart = HI_FALSE;
	pthread_join(gs_VencPid, <span class="hljs-number">0</span>);
}</code></pre>getchar()函数的执行模式是阻塞式的,当需要接收字符流的时候,当前线程(main函数所在线程)就会被挂起,其后的所有代码均要等待用户输入回车表示输入完毕后,线程才会被调度执行余下的代码,即进入if让gs_stPara.bThreadStart 为假,同时调用pthread_join(gs_VencPid, 0)函数,main函数又会进入阻塞模式,等待线程函数<span style="color:rgb(79,79,79);line-height:28px;text-align:justify;">VENC_GetVencStreamProc结束。<span style="color:rgb(79,79,79);line-height:28px;text-align:justify;">线程函数</span><span style="color:rgb(79,79,79);line-height:28px;">VENC_GetVencStreamProc结束后,CPU又进入main函数执行<span style="color:rgb(79,79,79);line-height:28px;text-align:justify;">pthread_join(gs_VencPid, 0)后的代码,可以猜测后面的代码就是收尾工作,应该与前面的步骤动作相反,前面绑定通道,后面就要解绑,前面创建开启通道,后面就要停止销毁通道,前面初始化, 后面就要去初始化,前面按顺序设置MPI_SYS,VI,VPSS,VENC,后面就要反过来按顺序设置VENC,VPSS,VI,MPI_SYS,看代码的确如此:</span></span></span><p></p><p><span style="color:rgb(79,79,79);line-height:28px;text-align:justify;"><span style="color:rgb(79,79,79);line-height:28px;text-align:justify;"><span style="color:rgb(79,79,79);line-height:28px;text-align:justify;"><span style="color:rgb(79,79,79);line-height:28px;"></span></span></span></span></p><pre><code class="language-cpp hljs">stSrcChn.enModId = HI_ID_VPSS;
stSrcChn.s32DevId = VpssGrp;
stSrcChn.s32ChnId = VpssChn;
stDestChn.enModId = HI_ID_VENC;
stDestChn.s32DevId = <span class="hljs-number">0</span>;
stDestChn.s32ChnId = VencChn;
s32Ret = HI_MPI_SYS_UnBind(&amp;stSrcChn, &amp;stDestChn);<span class="hljs-comment">//先解绑VPSS和VENC的绑定,后面再解绑VPSS和VI的绑定</span>
<span class="hljs-keyword">if</span> (s32Ret != HI_SUCCESS)
{
	SAMPLE_PRT(<span class="hljs-string">"failed with %#x!\n"</span>, s32Ret);
	<span class="hljs-keyword">return</span> HI_FAILURE;
}
s32Ret = HI_MPI_VENC_StopRecvPic(VencChn);<span class="hljs-comment">//前面开启了接收图片,这里就要停止</span>
<span class="hljs-keyword">if</span> (HI_SUCCESS != s32Ret)
{
	SAMPLE_PRT(<span class="hljs-string">"HI_MPI_VENC_StopRecvPic vechn[%d] failed with %#x!\n"</span>,VencChn, s32Ret);
	<span class="hljs-keyword">return</span> HI_FAILURE;
}

<span class="hljs-comment">/******************************************
Distroy Venc Channel
******************************************/</span>
s32Ret = HI_MPI_VENC_DestroyChn(VencChn);
<span class="hljs-comment">//前面创建了VENC通道,这里就要销毁VENC通道,而且最先销毁,后面再销毁VI通道</span>
<span class="hljs-keyword">if</span> (HI_SUCCESS != s32Ret)
{
	SAMPLE_PRT(<span class="hljs-string">"HI_MPI_VENC_DestroyChn vechn[%d] failed with %#x!\n"</span>,VencChn, s32Ret);
	<span class="hljs-keyword">return</span> HI_FAILURE;
}
stSrcChn.enModId = HI_ID_VIU;
stSrcChn.s32DevId = ViDev;
stSrcChn.s32ChnId = ViChn;
stDestChn.enModId = HI_ID_VPSS;
stDestChn.s32DevId = VpssGrp;
stDestChn.s32ChnId = <span class="hljs-number">0</span>;
s32Ret = HI_MPI_SYS_UnBind(&amp;stSrcChn, &amp;stDestChn);
<span class="hljs-comment">//后面再解绑VPSS和VI的绑定,在解绑VPSS和VENC的绑定之后,顺序与绑定顺序相反</span>
<span class="hljs-keyword">if</span> (s32Ret != HI_SUCCESS)
{
	SAMPLE_PRT(<span class="hljs-string">"failed with %#x!\n"</span>, s32Ret);
	<span class="hljs-keyword">return</span> HI_FAILURE;
}
s32Ret = HI_MPI_VPSS_DisableChn(VpssGrp, VpssChn);<span class="hljs-comment">//失能VPSS组和VPSS通道</span>
<span class="hljs-keyword">if</span> (s32Ret != HI_SUCCESS)
{
	SAMPLE_PRT(<span class="hljs-string">"%s failed with %#x\n"</span>, __FUNCTION__, s32Ret);
	<span class="hljs-keyword">return</span> HI_FAILURE;
}
s32Ret = HI_MPI_VPSS_StopGrp(VpssGrp);<span class="hljs-comment">//停止VPSS组</span>
<span class="hljs-keyword">if</span> (s32Ret != HI_SUCCESS)
{
	SAMPLE_PRT(<span class="hljs-string">"%s failed with %#x\n"</span>, __FUNCTION__, s32Ret);
	<span class="hljs-keyword">return</span> HI_FAILURE;
}
s32Ret = HI_MPI_VPSS_DestroyGrp(VpssGrp);<span class="hljs-comment">//销毁组</span>
<span class="hljs-keyword">if</span> (s32Ret != HI_SUCCESS)
{
	SAMPLE_PRT(<span class="hljs-string">"%s failed with %#x\n"</span>, __FUNCTION__, s32Ret);
	<span class="hljs-keyword">return</span> HI_FAILURE;
}
s32Ret = HI_MPI_VI_DisableChn(ViChn);<span class="hljs-comment">//失能通道</span>
<span class="hljs-keyword">if</span> (HI_SUCCESS != s32Ret)
{
	SAMPLE_PRT(<span class="hljs-string">"HI_MPI_VI_DisableChn failed with %#x\n"</span>,s32Ret);
	<span class="hljs-keyword">return</span> HI_FAILURE;
}
s32Ret = HI_MPI_VI_DisableDev(ViDev);<span class="hljs-comment">//失能VI设备即摄像头</span>
<span class="hljs-keyword">if</span> (HI_SUCCESS != s32Ret)
{
	SAMPLE_PRT(<span class="hljs-string">"HI_MPI_VI_DisableDev failed with %#x\n"</span>, s32Ret);
	<span class="hljs-keyword">return</span> HI_FAILURE;
}
SAMPLE_COMM_ISP_Stop();<span class="hljs-comment">//停止ISP</span>
HI_MPI_SYS_Exit();<span class="hljs-comment">//去初始化</span>
HI_MPI_VB_Exit();
<span class="hljs-comment">//由以上几个步骤可以看出,设置与销毁顺序相反,而且是成对出现</span>
<span class="hljs-keyword">return</span> HI_SUCCESS;</code></pre>至此,H264_Venc函数分析完,现在我们来重点分析线程函数VENC_GetVencStreamProc是怎么获取码流并保存到一个文件里的。<p></p><p><span style="color:rgb(79,79,79);line-height:28px;text-align:justify;"><span style="color:rgb(79,79,79);line-height:28px;text-align:justify;"></span></span></p><pre><code class="language-cpp hljs"><span class="hljs-function">HI_VOID* <span class="hljs-title">VENC_GetVencStreamProc</span><span class="hljs-params">(HI_VOID *p)</span>

{
SAMPLE_VENC_GETSTREAM_PARA_S *pstPara;
HI_S32 s32ChnTotal;
VENC_CHN VencChn;
HI_S32 s32Ret;
PAYLOAD_TYPE_E enPayLoadType[VENC_MAX_CHN_NUM];
HI_CHAR aszFileName[VENC_MAX_CHN_NUM][64];
VENC_CHN_ATTR_S stVencChnAttr;
struct timeval TimeoutVal;
fd_set read_fds;
HI_S32 VencFd[VENC_MAX_CHN_NUM],maxfd;
VENC_STREAM_S stStream;
VENC_CHN_STAT_S stStat;
FILE *pFile[VENC_MAX_CHN_NUM];
HI_S32 i;

pstPara = (SAMPLE_VENC_GETSTREAM_PARA_S*)p;
s32ChnTotal = pstPara-&gt;s32Cnt;<span class="hljs-comment">//pstPara-&gt;s32Cnt是由参数传进来的,为1</span>
<span class="hljs-keyword">for</span> (i = <span class="hljs-number">0</span>; i &lt; s32ChnTotal; i++)
{
	VencChn = i;
	s32Ret = HI_MPI_VENC_GetChnAttr(VencChn, &amp;stVencChnAttr);
	<span class="hljs-comment">//这里是为了取得是什么编码类型,以便确定保存文件的后缀名</span>
	<span class="hljs-comment">//比如这里是H264编码,所以保存文件的后缀后就是.h264</span>
	<span class="hljs-keyword">if</span>(s32Ret != HI_SUCCESS)
	{
		SAMPLE_PRT(<span class="hljs-string">"HI_MPI_VENC_GetChnAttr chn[%d] failed with %#x!\n"</span>, \
		VencChn, s32Ret);
		<span class="hljs-keyword">return</span> <span class="hljs-literal">NULL</span>;
	}
	enPayLoadType[i] = stVencChnAttr.stVeAttr.enType;
	gettime(filename);<span class="hljs-comment">//获取时间如20171101085639为文件名</span>
	<span class="hljs-built_in">sprintf</span>(aszFileName[i], <span class="hljs-string">"%s_%d%s"</span>,filename, i, <span class="hljs-string">".h264"</span>);<span class="hljs-comment">//20171101085639.h264</span>
	
	pFile[i] = fopen(aszFileName[i], <span class="hljs-string">"wb"</span>);
	<span class="hljs-keyword">if</span> (!pFile[i])
	{
		SAMPLE_PRT(<span class="hljs-string">"open file[%s] failed!\n"</span>, aszFileName[i]);
		<span class="hljs-keyword">return</span> <span class="hljs-literal">NULL</span>;
	}
	VencFd[i] = HI_MPI_VENC_GetFd(i);<span class="hljs-comment">//获取文件句柄,以便后面能用select来IO复用</span>
	<span class="hljs-keyword">if</span> (VencFd[i] &lt; <span class="hljs-number">0</span>)
	{
		SAMPLE_PRT(<span class="hljs-string">"HI_MPI_VENC_GetFd failed with %#x!\n"</span>, VencFd[i]);
		<span class="hljs-keyword">return</span> <span class="hljs-literal">NULL</span>;
	}
	<span class="hljs-keyword">if</span> (maxfd &lt;= VencFd[i])
	{
		maxfd = VencFd[i];
	}
}

<span class="hljs-keyword">while</span> (HI_TRUE == pstPara-&gt;bThreadStart)
<span class="hljs-comment">//当main函数所在的线程接收到两个键盘字符或ctrl+c时,pstPara-&gt;bThreadStart会为假,跳出while循环</span>
<span class="hljs-comment">//然后往下执行关闭前面打开的文件,执行完这个VENC_GetVencStreamProc线程函数,线程结束</span>
{
<span class="hljs-comment">/*IO复用4步骤
	1.清空文件集合FD_ZERO(&amp;read_fds);
	2.将文件加入文件集合FD_SET(VencFd[i], &amp;read_fds);
	3.设置超时时间并用select(maxfd + 1, &amp;read_fds, NULL, NULL, &amp;TimeoutVal)来等待文件状态有变化唤醒线程
	或超时唤醒
	4.FD_ISSET查询文件状态是否有变化,有变化则处理
*/</span>
	FD_ZERO(&amp;read_fds);
	<span class="hljs-keyword">for</span> (i = <span class="hljs-number">0</span>; i &lt; s32ChnTotal; i++)
	{
		FD_SET(VencFd[i], &amp;read_fds);
	}
	TimeoutVal.tv_sec  = <span class="hljs-number">2</span>;
	TimeoutVal.tv_usec = <span class="hljs-number">0</span>;
	s32Ret = select(maxfd + <span class="hljs-number">1</span>, &amp;read_fds, <span class="hljs-literal">NULL</span>, <span class="hljs-literal">NULL</span>, &amp;TimeoutVal);
	<span class="hljs-keyword">if</span> (s32Ret &lt; <span class="hljs-number">0</span>)
	{
		SAMPLE_PRT(<span class="hljs-string">"select failed!\n"</span>);
		<span class="hljs-keyword">break</span>;
	}
	<span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (s32Ret == <span class="hljs-number">0</span>)
	{
		SAMPLE_PRT(<span class="hljs-string">"get venc stream time out, exit thread\n"</span>);
		<span class="hljs-keyword">continue</span>;
	}
	<span class="hljs-keyword">else</span>
	{
		<span class="hljs-keyword">for</span> (i = <span class="hljs-number">0</span>; i &lt; s32ChnTotal; i++)
		{
			<span class="hljs-keyword">if</span> (FD_ISSET(VencFd[i], &amp;read_fds))
			{
				<span class="hljs-built_in">memset</span>(&amp;stStream, <span class="hljs-number">0</span>, <span class="hljs-keyword">sizeof</span>(stStream));
				s32Ret = HI_MPI_VENC_Query(i, &amp;stStat);<span class="hljs-comment">//查询是否有码流,并将码流信息填充到stStat结构体中</span>
				<span class="hljs-keyword">if</span> (HI_SUCCESS != s32Ret)
				{
					SAMPLE_PRT(<span class="hljs-string">"HI_MPI_VENC_Query chn[%d] failed with %#x!\n"</span>, i, s32Ret);
					<span class="hljs-keyword">break</span>;
				}
				<span class="hljs-keyword">if</span>(<span class="hljs-number">0</span> == stStat.u32CurPacks)
				{
					SAMPLE_PRT(<span class="hljs-string">"NOTE: Current  frame is NULL!\n"</span>);
					<span class="hljs-keyword">continue</span>;
				}
				stStream.pstPack = (VENC_PACK_S*)<span class="hljs-built_in">malloc</span>(<span class="hljs-keyword">sizeof</span>(VENC_PACK_S) * stStat.u32CurPacks);
				<span class="hljs-comment">//分配内存以便保存码流包数据</span>
				<span class="hljs-keyword">if</span> (<span class="hljs-literal">NULL</span> == stStream.pstPack)
				{
					SAMPLE_PRT(<span class="hljs-string">"malloc stream pack failed!\n"</span>);
					<span class="hljs-keyword">break</span>;
				}

				stStream.u32PackCount = stStat.u32CurPacks;
				<span class="hljs-comment">//printf("stStream.u32PackCount=%d\n",stStream.u32PackCount);</span>
				s32Ret = HI_MPI_VENC_GetStream(i, &amp;stStream, HI_TRUE);<span class="hljs-comment">//获取码流数据并保存到stStream结构体中</span>
				<span class="hljs-keyword">if</span> (HI_SUCCESS != s32Ret)
				{
					<span class="hljs-built_in">free</span>(stStream.pstPack);<span class="hljs-comment">//获取失败则要释放前面分配的内存,否则会造成内存溢出</span>
					stStream.pstPack = <span class="hljs-literal">NULL</span>;
					SAMPLE_PRT(<span class="hljs-string">"HI_MPI_VENC_GetStream failed with %#x!\n"</span>, s32Ret);
				<span class="hljs-keyword">break</span>;
				}
				HI_S32 u32PackIndex;
				<span class="hljs-keyword">for</span> (u32PackIndex= <span class="hljs-number">0</span>;u32PackIndex &lt; stStream.u32PackCount; u32PackIndex++)
				{
					fwrite(	stStream.pstPack[u32PackIndex].pu8Addr+stStream.pstPack[u32PackIndex].u32Offset,\
							stStream.pstPack[u32PackIndex].u32Len-  stStream.pstPack[u32PackIndex].u32Offset, \
							<span class="hljs-number">1</span>, pFile[i]);
					fflush(pFile[i]);
					<span class="hljs-meta">#<span class="hljs-meta-keyword">if</span> 1</span>
					<span class="hljs-built_in">printf</span>(<span class="hljs-string">"stStream.u32PackCount=%d,stStream.pstPack[%d].pu8Addr=0x%08x,\
						stStream.pstPack[%d].u32Offset=%d,stStream.pstPack[%d].u32Len=%d\n"</span>,\
						stStream.u32PackCount,u32PackIndex,stStream.pstPack[u32PackIndex].pu8Addr,\
						u32PackIndex,stStream.pstPack[u32PackIndex].u32Offset,u32PackIndex,stStream.pstPack[u32PackIndex].u32Len);
					<span class="hljs-comment">//添加打印信息,查看保存码流内容的内存是怎么样的</span>
					<span class="hljs-meta">#<span class="hljs-meta-keyword">endif</span></span>
				}

				s32Ret = HI_MPI_VENC_ReleaseStream(i, &amp;stStream);<span class="hljs-comment">//保存后要释放码流</span>
				<span class="hljs-keyword">if</span> (HI_SUCCESS != s32Ret)
				{
					<span class="hljs-built_in">free</span>(stStream.pstPack);<span class="hljs-comment">//获取失败则要释放前面分配的内存,否则会造成内存溢出</span>
					stStream.pstPack = <span class="hljs-literal">NULL</span>;
					<span class="hljs-keyword">break</span>;
				}

				<span class="hljs-built_in">free</span>(stStream.pstPack);<span class="hljs-comment">//释放码流后,也要释放分配的内存,避免内存溢出</span>
				stStream.pstPack = <span class="hljs-literal">NULL</span>;

			}
		}
	}

}

<span class="hljs-keyword">for</span> (i = <span class="hljs-number">0</span>; i &lt; s32ChnTotal; i++)
{
	fclose(pFile[i]);
}
<span class="hljs-keyword">return</span> <span class="hljs-literal">NULL</span>;

}线程函数里先通过HI_MPI_VENC_GetFd取得文件句柄,然后在while循环里不断清空读文件集合FD_ZERO(&read_fds);将文件句柄加入读文件集合FD_SET(VencFd[i], &read_fds);用select来休眠线程,有数据或超时又唤醒线程select(maxfd + 1, &read_fds, NULL, NULL, &TimeoutVal);用FD_ISSET(VencFd[i], &read_fds)来查询是哪个文件句柄有数据以便处理,然后用HI_MPI_VENC_Query查询码流统计信息以便分配内存,HI_MPI_VENC_GetStream来取得码流包数据,fwrite将码流包数据保存到文件中,码流包处理后要HI_MPI_VENC_ReleaseStream释放掉码流,释放掉申请的内存。

综合整个程序,在创建线程函数前都是对系统设置,按照海思SDK里的文档步骤设置就行,基本上就是定义结构体,给结构体赋值,然后设置进系统。到后面创建线程后就有两个线程函数了,一个main函数所在线程,一个VENC_GetVencStreamProc函数所在线程。main函数所在线程创建VENC_GetVencStreamProc线程后就调用getchar();函数进入休眠状态,只有当键盘有输入时getchar()函数有返回值时才会唤醒main函数所在线程,所以CPU就切换到VENC_GetVencStreamProc线程,VENC_GetVencStreamProc线程在调用select函数时,如果读文件集合read_fds有要读的内容则会往下执行,如果没有VENC_GetVencStreamProc线程也会休眠,CPU会切换到别的线程,当read_fds有要读的内容内容时或超时VENC_GetVencStreamProc线程会唤醒,往下执行去进行读码流的处理过程。

程序写完后,在sample文件夹里创建个子文件夹如myvenc,然后将这个文件保存到myvenc里,从sample\venc里复制makefile到sample\myvenc里,这个makefile都不要我们改些什么东西,只要位置放对了位置(跟源代码文件在同一个目录里)就直接可以用,然后make clean,make编译,如果没问题的话会生成myvenc可执行文件,将这个可执行文件拷贝到HI3518开发板(可通过SD卡从PC拷贝到开发板,也可挂载根文件系统直接复制过去,方法多样),然后执行./myvenc就会运行程序,过一会就会在myvenc同级目录下生成一个以时间为名的,后缀名为h264的文件,这个文件就是H264码流保存的文件,将这文件从Hi3518里复制到PC机上,用VLC播放器就可以播放出摄像头拍摄的内容。

编译运行程序后生成了h264文件,这个文件及程序运行时打印的输出内容有助于我们分析h264,下一篇我们将分析h264以便后期怎么将h264数据打包到rtp中进行rtsp传送数据。

这个程序比较简单,只是为了让大家了解海思芯片是怎么进行多媒体开发,所以很多功能没有写成函数,感觉内容很多,但避免了sample里复杂的函数调用,有助于将了解各个数据结构及海思SDK各个函数的用法。程序从前往后看,流程一目了然,各个数据结构、函数对应哪个模块很清楚,对入门很有好处。我将代码上传到github,欢迎朋友们下载,一起讨论学习。

最后看一下程序运行效果,用VLC打开H264文件,可以看到摄像头拍摄界面:

在这里插入图片描述

项目源代码及工具:Hi3518编码H264













标签:3518,s32Ret,H264,MPI,SAMPLE,HI,VENC,VPSS,海思
来源: https://blog.csdn.net/weixin_44893022/article/details/94604950

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有