一、启动 VENC 编码
step 1: init sys variable
根据《HiMPP IPC V2.0 媒体处理软件开发参考》里面说的软件架构来讲,这一步主要是
为了创建视频缓冲池的 conf 配置
配置变量为
VB_CONF_S stVbConf;
结构体定义如下
typedef struct hiVB_CONF_S
{
HI_U32 u32MaxPoolCnt;
struct hiVB_CPOOL_S
{
//缓冲池最大数量
HI_U32 u32BlkSize;
HI_U32 u32BlkCnt;
HI_CHAR acMmzName[MAX_MMZ_NAME_LEN];
//每个缓冲池的大小
//每个缓冲池能分为几块
}astCommPool[VB_MAX_COMM_POOLS];
//未用到
} VB_CONF_S;
缓冲池最大数量和每个缓冲池能分几块问题不大,sample 例程给的默认值是 128 和 20
每个缓冲池的大小需要调用函数 SAMPLE_COMM_SYS_CalcPicVbBlkSize 通过返回值获取
函数原型如下
HI_U32 SAMPLE_COMM_SYS_CalcPicVbBlkSize(
VIDEO_NORM_E enNorm,
PIC_SIZE_E enPicSize,
//电视广播制式,有 PLA 或者 NTSC 等等
//长宽尺寸,不过这里是用宏定义的,想要自定义尺寸,需
要在 hiPIC_SIZE_E 枚举里面添加自定的宏定义,然后再 SAMPLE_COMM_SYS_GetPicSize 函数
中返回具体的长宽
PIXEL_FORMAT_E enPixFmt, //像素格式,比如 YUV422,YUV420 等等
HI_U32 u32AlignWidth
//系统中使用图像的字节对齐数,在初始化 MPP 系统的
时候传入
)
所以第一步就是为了填充定义的 stVbConf 变量
step 2: mpp system init.
这一步主要是做两个工作
1.初始化视频缓冲池处理模块(VB)
2.初始化 MPP 系统
调用的接口是 SAMPLE_COMM_SYS_Init,传入上一步中我们填充的配置 stVbConf 即可
step 3: start vi dev & chn to capture
这一步就是调用函数 SAMPLE_COMM_VI_StartVi 来设置 VI 和 chn 并且开始捕获图像
这个函数需要传入一个 SAMPLE_VI_CONFIG_S* pstViConfig 变量,结构体的原型如下
typedef struct sample_vi_config_s
{
SAMPLE_VI_MODE_E enViMode;
//输入的格式,BT656/BT1120 或者是
Digital Camera 等等
VIDEO_NORM_E enNorm;
ROTATE_E enRotate;
SAMPLE_VI_CHN_SET_E enViChnSet;
WDR_MODE_E enWDRMode;
} SAMPLE_VI_CONFIG_S;
//电视广播制式,有 PLA 或者 NTSC 等等
//旋转角度,
//通道设置,镜像或者翻转
//宽动态范围模式
所以这一步就是填充定义的 pstViConfig 变量,然后调用 SAMPLE_COMM_VI_StartVi 开始
捕获输入
step 4: start vpss and vi bind vpss
根据《HiMPP IPC V2.0 媒体处理软件开发参考》里面说的软件架构来讲,数据接收者需
要绑定数据源
绑定后,数据源的数据将直接送给数据接收者,为了编码我们需要将 VPSS 绑定 VI,然
后将 VENC 绑定 VPSS
这里的工作就是两个
1.创建一个 VPSS_GROUP(各 Group 分时复用)
2.将 VPSS 绑定到 VI
3.启动 VPSS(上一步我们已经启动了 VI)
4.1.创建一个 VPSS_GROUP(各 Group 分时复用)
创建一个 VPSS,需要调用 SAMPLE_COMM_VPSS_StartGroup 函数,函数原型如下
HI_S32 SAMPLE_COMM_VPSS_StartGroup(
VPSS_GRP VpssGrp,
VPSS_GRP_ATTR_S* pstVpssGrpAttr
//VPSS 属性变量
//VPSS_GRP 编号,范围是 0 - VPSS_MAX_GRP_NUM 之间
)
想要创建一个 VPSS,就需要我们构建一个 VPSS_GRP_ATTR_S 属性结构体,这个结构
体的定义如下
typedef struct hiVPSS_GRP_ATTR_S
{
/*statistic attributes*/
HI_U32 u32MaxW;
/*MAX width of the group*/
不可更改
//最大宽度,创建时设定,
HI_U32 u32MaxH;
/*MAX height of the group*/
//最大高度,创建时设定,
不可更改
PIXEL_FORMAT_E enPixFmt; /*Pixel format*/
//像素格式,比如 YUV422,
YUV420 等等
HI_BOOL bIeEn;
/*Image enhance enable*/
//保留,必须设置为
HI_FALSE
HI_BOOL bDciEn;
HI_BOOL bNrEn;
HI_BOOL bHistEn;
/*Dynamic contrast Improve enable*/
/*Noise reduce enable*/
/*Hist enable*/
//保留,必须设置为 HI_FALSE
//去噪
//保留,必须设置为
HI_FALSE
VPSS_DIE_MODE_E enDieMode; /*De-interlace enable*/
// 保 留 , 必 须 设 置 为
VPSS_DIE_MODE_NODIE
}VPSS_GRP_ATTR_S;
4.2.将 VPSS 绑定到 VI
将 VPSS 绑定到 VI,需要调用 SAMPLE_COMM_VI_BindVpss 函数,函数原型如下
HI_S32 SAMPLE_COMM_VI_BindVpss(
SAMPLE_VI_MODE_E enViMode
//输入的格式,BT656/BT1120 或者是
Digital Camera 等等
)
这里我们调用的时候,enVimode 可以使用第三步创建的结构体 pstViConfig 变量中的
enViMode 即可
4.3.启动 VPSS(上一步我们已经启动了 VI)
这里需要先说一下 VPSS 组通道的概念,手册上是这样说的
VPSS 组的通道。通道分为 2 种:物理通道和扩展通道。 VPSS 硬件提供多个
物理通道,每个通道具有缩放、裁剪等功能。扩展通道具备缩放功能,它通过绑定物理通道,
将物理通道输出作为自己的输入,把图像缩放成用户设置的目标分辨率输出。
所以,我们要启动 VPSS 就需要绑定物理通道,然后启动这个物理通道
需要调用的函数是 SAMPLE_COMM_VPSS_EnableChn,其原型为
HI_S32 SAMPLE_COMM_VPSS_EnableChn(
VPSS_GRP VpssGrp,
VPSS_CHN VpssChn,
VPSS_CHN_ATTR_S* pstVpssChnAttr,
VPSS_CHN_MODE_S* pstVpssChnMode,
VPSS_EXT_CHN_ATTR_S* pstVpssExtChnAttr
//VPSS_GRP 编号,范围是 0 - VPSS_MAX_GRP_NUM 之间
//VPSS_CHN 编号,范围是 0 - VPSS_MAX_CHN_NUM 之间
//VPSS 物理通道属性
//VPSS_CHN 工作模式结构
//VPSS 扩展通道属性
)
可以看到,这里我们需要定义三个结构体,VPSS_CHN_ATTR_S 结构体定义如下
typedef struct hiVPSS_CHN_ATTR_S
{
HI_BOOL bSpEn;
HI_BOOL bBorderEn;
HI_BOOL bMirror;
//保留,只能为 HI_FALSE
//保留,只能为 HI_FALSE
//镜像开关(第三步也有一个开关,那里是 VI 的通道,这里
是 VPSS 的通道)
HI_BOOL bFlip;
里是 VPSS 的通道)
//翻转开关(第三步也有一个开关,那里是 VI 的通道,这
HI_S32
s32SrcFrameRate;
//通道帧率控制:源帧率[-1,60],源帧率与目标帧率都
为-1,则不进行帧率控制
HI_S32
s32DstFrameRate;
//通道帧率控制: 目标帧率[-1,60],源帧率与目标帧率都
为-1,则不进行帧率控制
BORDER_S
stBorder;
//边框属性,可以定义各个边的宽度以及整个框的颜色
}VPSS_CHN_ATTR_S;
VPSS_CHN_MODE_S 结构体定义如下
typedef struct hiVPSS_CHN_MODE_S
{
VPSS_CHN_MODE_E
enChnMode;
//VPSS_CHN 工作模式,USER 模式主要用于同一
通道图像进行多路编码的场景
HI_U32 u32Width;
HI_U32 u32Height;
HI_BOOL bDouble;
PIXEL_FORMAT_E
COMPRESS_MODE_E enCompressMode;
//目标图像宽
//目标图像高
//保留
enPixelFormat; //目标图像像素格式,比如 YUV422,YUV420 等等
//目标图像压缩格式
}VPSS_CHN_MODE_S;
做完这三步,VPSS 就应该开始正常工作了,
step 5: start stream venc
截止到上一步,VI 和 VPSS 我们都已经启动了,所以接下来我们就需要绑定 VENC 和 VPSS,
然后启动 VENC 进行编码
1.绑定 VENC 到 VPSS
绑定 VENC 到 VPSS 需要调用 SAMPLE_COMM_VENC_BindVpss 函数,函数原型如下
HI_S32 SAMPLE_COMM_VENC_BindVpss(
VENC_CHN VeChn,
VPSS_GRP VpssGrp,
VPSS_CHN VpssChn
)
//VENC 通道编号
//VPSS_GROUP 通道编号
//VPSS 通道编号
这三个参数我们之前都已经用到过了,很简单
2.启动 VNCE 编码
启动 VNCE 编码需要调用 SAMPLE_COMM_VENC_Start 函数,函数原型如下
HI_S32 SAMPLE_COMM_VENC_Start(
VENC_CHN VencChn,
PAYLOAD_TYPE_E enType,
VIDEO_NORM_E enNorm,
PIC_SIZE_E enSize,
SAMPLE_RC_E enRcMode,
HI_U32 u32Profile
//VENC 通道编号
//编码类型,即进行哪一种编码
//电视广播制式,有 PLA 或者 NTSC 等等
//长宽尺寸
//码率控制属性
//帧/包模式获取码流,HI_TRUE:按帧获取,
HI_FALSE:按包获取
)
到这一步,从 VI 到 VENC 就已经全部配置完成了
step 6: stream venc process -- get stream, then save it to
file.
这个例程中我们只是进行了 VENC 编码,但是编码后的码流截止到目前我们没有获取到
只有获取到编码后的码流,我们才能进行下一步操作
海思在这里启动了一个新线程,用来获取编码后的码流
为了启动新线程我们在这里只需要调用 SAMPLE_COMM_VENC_StartGetStream 函数即可,
函数源码如下
HI_S32 SAMPLE_COMM_VENC_StartGetStream(HI_S32 s32Cnt)
{
gs_stPara.bThreadStart = HI_TRUE;
gs_stPara.s32Cnt = s32Cnt;
return pthread_create(&gs_VencPid, 0, SAMPLE_COMM_VENC_GetVencStreamProc,
(HI_VOID*)&gs_stPara);
}
可以看到,获取编码后码流的流程在 SAMPLE_COMM_VENC_GetVencStreamProc 线程中
进行了处理
截止到这一步,我们就相当于做完了初始化的工作,后面对编码后码流的操作需要去新
线程中处理
二、获取 VENC 编码流
step 1:venc-fd
第一步我们需要获取编码通道对应的设备文件句柄,有了这个句柄我们才能进行后续的
操作
获取句柄需要调用 HI_MPI_VENC_GetFd 函数,函数原型如下
HI_S32 HI_MPI_VENC_GetFd(
VENC_CHN VeChn
//venv 通道,这里例程的处理是遍历 venc 所有的通道,
然后都存放在数组里
);
step 2: Start to get streams of each channel.
开始编码之后我们并不知道什么时候哪个通道会编码完成,所以这里我们需要使用
select 函数
监听所有通道的句柄,一旦某个通道的句柄可读了,就处理该通道
/*******************************************************
step 2.1 : query how many packs in one-frame stream.
*******************************************************/
虽然该通道可读了,但是我们还需要查询编码通道状态来获取已编码帧个数和未编
码帧个数
查询编码通道状态调用 HI_MPI_VENC_Query 函数,该函数原型如下
HI_S32 HI_MPI_VENC_Query(
VENC_CHN VeChn,
VENC_CHN_STAT_S *pstStat
//VENC_CHN 编号
//编码通道的状态指针
);
编码通道的状态我们通过 pstStat 参数来获取,结构体体的定义如下
typedef struct hiVENC_CHN_STAT_S
{
HI_U32 u32LeftPics;
HI_U32 u32LeftStreamBytes;
//待编码的图像数
//码流 buffer 剩余的 byte
数
HI_U32 u32LeftStreamFrames;
HI_U32 u32CurPacks;
HI_U32 u32LeftRecvPics;
用户设置 HI_MPI_VENC_StartRecvPicEx 后有效
HI_U32 u32LeftEncPics;
用户设置 HI_MPI_VENC_StartRecvPicEx 后有效
}VENC_CHN_STAT_S;
//码流 buffer 剩余的帧数
//当前帧的码流包个数
//剩余待接收的帧数,在
//剩余待编码的帧数,在
/*******************************************************
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;
******************************************************/
这一步如注释所说,要确定当前帧已经编码完成或者当前帧不为空
/*******************************************************
step 2.3 : malloc corresponding number of pack nodes.
*******************************************************/
分配空间,准备获取编码流,