网站开发需要那些技能,2024新闻热点事件,价格网app下载,安阳贴吧黄一以下内容源于朱有鹏嵌入式课程的学习与整理#xff0c;如有侵权请告知删除。
前言
本文将详细介绍博文第二季3#xff1a;sample_venc.c的整体分析提及的“ 保存编码得到的码流 ”。
即把编码得到的三路码流#xff08;三路码流都是H264格式的#xff0c;只是分辨率不同…以下内容源于朱有鹏嵌入式课程的学习与整理如有侵权请告知删除。
前言
本文将详细介绍博文第二季3sample_venc.c的整体分析提及的“ 保存编码得到的码流 ”。
即把编码得到的三路码流三路码流都是H264格式的只是分辨率不同保存为裸流文件这里保持为stream_ch0.h264、stream_ch1.h264、stream_ch2.h264文件。
我们首先列出该部分称之为StoF的函数调用关系图谱然后分析其具体代码细节。 一、StoF的函数调用关系
StoF部分的函数调用关系如下所示
SAMPLE_COMM_VENC_StartGetStreamSAMPLE_COMM_VENC_GetVencStreamProc//是一个线程函数//step1HI_MPI_VENC_GetChnAttrSAMPLE_COMM_VENC_GetFilePostfix//添加文件后缀(比如.h264)pFile[i] fopen(aszFileName[i], wb)HI_MPI_VENC_GetFd//获取编码通道对应的设备文件句柄//step2step2_1 HI_MPI_VENC_Query //查询编码通道状态step2_2 step2_3 malloc//申请码流空间step2_4 HI_MPI_VENC_GetStream//获取一帧图像的编码码流step2_5 SAMPLE_COMM_VENC_SaveStream//保存码流到文件中若RTSP发送则修改这里SAMPLE_COMM_VENC_SaveH264fwritestep2_6 HI_MPI_VENC_ReleaseStream//释放编码码流step2_7 free//释放buff//step3close //关闭文件
由此可知该部分包括以下几个步骤
获取编码通道对应的设备文件句柄查询编码通道的状态申请码流空间获取一帧图像的编码码流保存码流到文件中释放编码码流
下面我们将详细介绍这几个步骤的代码细节。 二、StoF的代码详解
1、StoF代码整体分析
StoF的代码如下主要是SAMPLE_COMM_VENC_StartGetStream其参数s32ChnNum在step1时被初始化为3。 /******************************************step 6: stream venc process -- get stream, then save it to file. ******************************************/s32Ret SAMPLE_COMM_VENC_StartGetStream(s32ChnNum);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();
SAMPLE_COMM_VENC_GetVencStreamProc函数内容与分析如下该函数获取每个编码通道的码流并保存为裸流文件。
/******************************************************************************
* funciton : get stream from each channels and save them
******************************************************************************/
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;//这个表示有多少个编码通道这里等于3/******************************************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)//即for(i0;i3;i){/* decide the stream file name, and open file to save stream */VencChn i;//将分别为 012//获取venc模块通道i的属性这个属性在SAMPLE_COMM_VENC_Start函数中设置s32Ret HI_MPI_VENC_GetChnAttr(VencChn, stVencChnAttr);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;//获取图像的传输格式//根据图像的传输格式来决定文件名的后缀保存在szFilePostfix这个字符数组里s32Ret SAMPLE_COMM_VENC_GetFilePostfix(enPayLoadType[i], szFilePostfix);if(s32Ret ! HI_SUCCESS){SAMPLE_PRT(SAMPLE_COMM_VENC_GetFilePostfix [%d] failed with %#x!\n, \stVencChnAttr.stVeAttr.enType, s32Ret);return NULL;}//合成裸流文件的文件名“stream_chn0.h264”“stream_chn1.h264”“stream_chn2.h264”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);//获取编码通道对应的设备文件句柄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)//上层传过来的这个成立{//select函数相关的内容见相关博客这里先略过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){if (FD_ISSET(VencFd[i], read_fds))//这里表示啥意思{/*******************************************************step 2.1 : query how many packs in one-frame stream.*******************************************************/memset(stStream, 0, sizeof(stStream));//查看编码通道的状态i是编码通道号stState是输出型参数通道状态信息在其中s32Ret HI_MPI_VENC_Query(i, 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.*******************************************************///这个结构体指针指向所申请的由一帧的所有数据包组成的空间//其中VENC_PACK_S是数据包结构体//stStat.u32CurPacks是一帧图像包含的数据包数量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;//该函数获取编码的码流参数1表示编码通道号//参数2表示码流结构体指针参数3表示码流超时时间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);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);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;
}
下面我们将重点分析这两个内容
1 HI_MPI_VENC_GetStream(i, stStream, HI_TRUE)
2SAMPLE_COMM_VENC_SaveStream(enPayLoadType[i], pFile[i], stStream)
2、SAMPLE_COMM_VENC_SaveStream函数分析
该函数有3个参数参数1表示图像传输格式参数2表示码流要保存到哪个文件参数3表示码流的结构体指针。该函数内部通过判断参数1来选择不同的保存方式案例里参数1是PT_H264于是下一步是SAMPLE_COMM_VENC_SaveH264这个函数。
SAMPLE_COMM_VENC_SaveH264函数包括2个参数参数1表示码流要保存到哪个文件参数2表示码流的结构体指针。该函数的内容与分析如下所示。
/******************************************************************************
* funciton : save H264 stream
******************************************************************************/
HI_S32 SAMPLE_COMM_VENC_SaveH264(FILE* fpH264File, VENC_STREAM_S *pstStream)
{HI_S32 i;for (i 0; i pstStream-u32PackCount; i){ /*每个数据包的首地址 每个数据包的有效数据相对于首地址的偏移量*/fwrite(pstStream-pstPack[i].pu8Addr pstStream-pstPack[i].u32Offset,/*每个数据包的长度 - 每个数据包的有效数据相对于首地址的偏移量 有效数据长度*/pstStream-pstPack[i].u32Len-pstStream-pstPack[i].u32Offset, 1,//写入一次fpH264File);//写入哪个文件fflush(fpH264File);//表示立即将输出缓冲区的数据写入该文件中}return HI_SUCCESS;
}
关于fwrite、fflush函数的用法见博客fwrite函数与fflush函数_天糊土的博客-CSDN博客。
3、HI_MPI_VENC_GetStream函数分析
该函数的具体介绍见文档《HiMPP IPC V2.0 媒体处理软件开发参考.pdf_1111》的第539页。这里仅摘抄与整理部分内容。
函数原型
HI_S32 HI_MPI_VENC_GetStream(VENC_CHN VeChn, VENC_STREAM_S *pstStream, HI_S32 s32MilliSec);
调用实例
s32Ret HI_MPI_VENC_GetStream(i, stStream, HI_TRUE);
其中参数1表示编码通道的编号参数2是输出型参数表示码流的结构体指针参数3表示获取码流的超时时间。
1支持设置超时方式
取-1表示阻塞即如果缓冲无数据则会等待有数据时才返回获取成功取0表示非阻塞即如果缓冲无数据则立即返回失败取大于0的数字表示超时时间即如果缓冲无数据则会等待用户设定的超时时间若在设定的时间内有数据则返回获取成功否则返回超时失败。
2支持按帧或者按包的方式获取码流
如果按包的方式获取码流对于 H.264/265 编码协议每次获取的是一个 NAL 单元。
3帧码流结构体 VENC_STREAM_S的详解
//“ 帧码流 ” 表示一帧图像对应的码流
typedef struct hiVENC_STREAM_S
{VENC_PACK_S *pstPack; //指向帧码流包的指针HI_U32 u32PackCount; //帧码流的所有包的个数HI_U32 u32Seq; //码流序列号按帧获取则表示帧序号按包获取则表示包序号 union{ //码流特征信息VENC_STREAM_INFO_H264_S stH264Info; /*the stream info of h264*/VENC_STREAM_INFO_JPEG_S stJpegInfo; /*the stream info of jpeg*/VENC_STREAM_INFO_MPEG4_S stMpeg4Info; /*the stream info of mpeg4*/VENC_STREAM_INFO_H265_S stH265Info; /*the stream info of h265*/};
}VENC_STREAM_S;
A、帧码流包结构体 VENC_PACK_S
typedef struct hiVENC_PACK_S
{HI_U32 u32PhyAddr; //每个码流包的物理地址 /*the physics address of stream*/HI_U8 *pu8Addr; //每个码流包首地址 /*the virtual address of stream*/HI_U32 u32Len; //每个码流包长度 /*the length of stream*/HI_U64 u64PTS; //时间戳,单位us /*PTS*/HI_BOOL bFrameEnd; //帧结束标识 /*frame end*///码流类型支持 H.264/JPEG/MPEG-4 协议类型的数据包。VENC_DATA_TYPE_U DataType; /*the type of stream*/HI_U32 u32Offset;//每个码流包中有效数据与码流包首地址 pu8Addr 的偏移。HI_U32 u32DataNum;//当前码流包类型由 DataType 指定数据中包含其他类型码流包的个数。VENC_PACK_INFO_S stPackInfo[8];//当前码流包数据中包含其他类型码流包数据信息
}VENC_PACK_S;/*
(1)关于帧结束标志
HI_TRUE表示该码流包是该帧的最后一个包HI_FALSE表示该码流包不是该帧的最后一个包。*/
VENC_PACK_S类型的指针变量pstPack它指向一组 VENC_PACK_S 的内存空间该空间由调用者分配。如果是按包获取则此空间不小于 sizeofVENC_PACK_S的大小如果按帧获取则此空间不小于 N × sizeofVENC_PACK_S的大小其中 N 代表当前帧之中的包的个数可以在 select 之后通过查询接口获得。
B、码流包个数 u32PackCount
此值指定 pstPack 中 VENC_PACK_S 的个数。按包获取时u32PackCount 必须不小于 1按帧获取时u32PackCount 必须不小于当前帧的包个数。在函数调用成功后u32PackCount 返回实际填充 pstPack 的包的个数。
C、序列号 u32Seq
按帧获取时是帧序列号按包获取时为包序列号。
D、码流特征信息
数据类型为联合体包含了不同编码协议对应的码流特征信息stH264Info/ stJpegInfo/stMpeg4Info/stH265Info码流特征信息的输出用于支持用户的上层应用。
我们看一下H.264码流特征信息结构体VENC_STREAM_INFO_H264_S。
typedef struct hiVENC_STREAM_INFO_H264_S
{ //编码当前帧的字节BYTE数HI_U32 u32PicBytesNum; /* the coded picture stream byte number */
//编码当前帧中采用跳跃SKIP编码模式的宏块数HI_U32 u32PSkipMbNum; /* the skip macroblock num */
//编码当前帧中采用 IPCM 编码模式的宏块数 HI_U32 u32IpcmMbNum; /* the ipcm macroblock num */
//编码当前帧中采用 Inter16x8 预测模式的宏块数HI_U32 u32Inter16x8MbNum; /* the inter16x8 macroblock num */HI_U32 u32Inter16x16MbNum; /* the inter16x16 macroblock num */HI_U32 u32Inter8x16MbNum; /* the inter8x16 macroblock num */HI_U32 u32Inter8x8MbNum; /* the inter8x8 macroblock num */HI_U32 u32Intra16MbNum; /* the intra16x16 macroblock num */HI_U32 u32Intra8MbNum; /* the intra8x8 macroblock num */HI_U32 u32Intra4MbNum; /* the inter4x4 macroblock num */
//编码当前帧属于何种跳帧参考模式下的参考帧H264E_REFSLICE_TYPE_E enRefSliceType; /*the reference type of H264E slice*/
//高级跳帧参考下的编码帧类型H264E_REF_TYPE_E enRefType; /*Type of encoded frames in advanced frame skipping reference mode*/
//通道属性或参数(包含 RC 参数)被设置的次数HI_U32 u32UpdateAttrCnt; /*Number of times that channel attributes or parameters (including RC parameters) are set*/
//编码当前帧的 startqp 值HI_U32 u32StartQp; /*StartQP Value*/
}VENC_STREAM_INFO_H264_S;
4码流包结构图
见第二季7创建配置编码通道step5VENC部分第一节的第5点内容描述。