博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
嵌入式 视频编码(H264)hi3518 sample流程及API
阅读量:3920 次
发布时间:2019-05-23

本文共 40567 字,大约阅读时间需要 135 分钟。

stViConfig.enViMode

=(SENSOR_TYPE)
=APTINA_AR0130_DC_720P_30FPS

摄像头获取的模拟信号通过经芯片处理(我们使用的是CX25825),将模拟信号转成数字信号,产生标准的ITU 656 YUV格式的数字信号以帧为单位送到编码卡上的DSP和内存中。分别供视频实时预览、移动侦测处理以及编码等使用。其中编码的作用是将编码卡内存中的YUV数据送到H264编码器中,进过H.264编码产生压缩好的码流,送到主机内存中,供录像或网络传输使用。编码模块完成各个协议编码,协调 MD、VPP 相关模块的管理、同步和控制,配合软件调度和硬件共同完成视频编码相关功能。

一、重要概念

主次码流

主次码流是指硬件逻辑单元启动一次同时产生的 2 路码流,即 1 路主码流和 1 路次码流。主码流和次码流可以为不同的编码协议,但其宽高比例都必须满足 1:1、1:2 或 1:4,次码流不能单独存在(必须和 1 路主码流在同一个通道组中) 。

双码流

双码流是指硬件逻辑单元启动 2 次分时产生的 2 个码流,即 2 路主码流。双码流可以为不同的编码协议,双码流之间的大小比例没有约束关系。

通道组

通道组是指芯片能够同时处理的编码通道的集合,相当于一个容器。一个通道组最多可同时包含 1 路主码流(H.264/MJPEG) 、1路次码流(H.264/MJPEG) ,或者仅包含 1 路 JPEG抓拍(即 JPEG抓拍时,不允许包含任何其他通道) ,或者 1 路MPEG4 编码通道。

12 系统初始化(SYS INT):------------------------1 初始化并配置VB视频缓冲池;   {
memset(&stVbConf,0,sizeof(VB_CONF_S)); SAMPLE_COMM_VI_GetSizeBySensor(&enSize[0]);}2 初始化系统 SAMPLE_COMM_SYS_Init{
2个去初始化 SYS_Exit VB_Exit VB和SYS各自的的 配置+Init: ①VB_SetConf ②VB_Init ①SYS_SetConf ②SYS_Init}3 配置视频捕获(VI+ISP):------------------------ start vi dev & chn to capture--------------【SAMPLE_COMM_VI_StartVi】→→【SAMPLE_COMM_VI_StartIspAndVi(pstViConfig);】 配置MIPI SAMPLE_COMM_VI_StartMIPI 初始化ISP SAMPLE_COMM_ISP_Init(pstViConfig->enWDRMode); 运行ISP线程 SAMPLE_COMM_ISP_Run(); 配置开启VI 设备捕获 SAMPLE_COMM_VI_StartDev(ViDev, enViMode); 配置开启VI 通道123捕获 SAMPLE_COMM_VI_StartChn HI_MPI_VI_SetChnAttr(ViChn, &stChnAttr);【设置 源、目标帧率 -1 -1 不设置】4 配置视频处理子系统(VPSS):------------------------ start vpss and vi bind vpss绑定: ①创建启用启用VPSS GROUP=0 ②vi-chn和vpss-grp绑定 ③各个通道012: 填充VPSS通道模式stVpssChnMode ↓ 设置HI_MPI_VPSS_SetChnAttr(VpssGrp, VpssChn, VPSS_CHN_ATTR_S); 【物理通道额外需要HI_MPI_VPSS_SetChnMode】 ↓ 使能通道HI_MPI_VPSS_EnableChn(VpssGrp, VpssChn);5 创建配置编码通道(VENC)----------------------------{
码率控制模式3选一 CBR VBR FIXQP 创建 开启编码通道 SAMPLE_COMM_VENC_Start 绑定VPSS_group_chn到VENC_chn SAMPLE_COMM_VENC_BindVpss(VencChn, VpssGrp, VpssChn);→→HI_MPI_SYS_Bind} 6 获取并处理编码码流---------------------SAMPLE_COMM_VENC_StartGetStream(s32ChnNum);{
1获取编码通道参数VENC_CHN_ATTR_S 依据视频类型决定文件后缀 打开并得到存储用的文件描述符*pFile[i] 获取编码设备文件句柄 VencFd[012] = HI_MPI_VENC_GetFd(012); 2 while (HI_TRUE == pstPara->bThreadStart) {
清零read_fds + 放入VencFd[012] + select IO复用 对编码设备文件句柄VencFd[012]进行IO多路复用 找到就绪的文件句柄 {
1 查询编码通道状态。 HI_MPI_VENC_Query(i, &stStat); 编码码流buf中的数据 帧数 包数 余下待接收的帧数 //2核对 核对当前帧的包数和编码buff中余下的帧数→若有0则? 3 申请帧码流包结构体空间 malloc(sizeof(VENC_PACK_S) * stStat.u32CurPacks); 申请一包*包数的空间 4 【获取编码码流】 HI_MPI_VENC_GetStream(i, &stStream, HI_TRUE); →→→→→→→stStream 5 保存,处理码流 SAMPLE_COMM_VENC_SaveH264 保存至文件 67 释放编码码流 HI_MPI_VENC_ReleaseStream } } 3 close save-file}【VENC_CHN_STAT_S stStat编码通道的状态结构体。---码流 buffer 剩余的 byte 数。码流 buffer 剩余的帧数。当前帧的码流包个数。剩余待接收的帧数,】【每次从编码缓冲池中提取的】VENC_STREAM_S stStream 帧码流类型结构体{
帧码流包指针。 包数 码流序列号。 码流特征信息} 】fifo实现借鉴:填充:【sample中链表是将pstStream中的每一包作为一个链表单元→→那么fifo也模仿 每一个包的内容的【指针和长度】做成一个数组元素12 】发送:当读写指针指向同一位置表示没有东西号发送 否则调用VENC_SENT发送 然后清理并移动读指针 printf("please press twice ENTER to exit this sample\n");//结束编码的契机 getchar(); getchar();7 退出 将前一步中循环检测的一个变量设置为false--即venc停止-------------------------------------------------------------------//int main(入口在sample_venc.c) 根据传参case '0'在switch中选择一条分支→SAMPLE_VENC_1080P_CLASSIC //HI3518E内部的ISP单元是隶属于VI模块的。//VI模块就包含3大部分:第一部分是和Sensor对接的部分,第二部分就是ISP,第三部分就是VI dev和channel
PAYLOAD_TYPE_E enPayLoad[3]= {
PT_H264, PT_H264,PT_H264};//视频数据类型PIC_SIZE_E enSize[3] = {
PIC_HD1080, PIC_VGA,PIC_QVGA};;//默认的三路码流,后面根据实际sensor可能会改动HI_U32 u32Profile = 0;VB_CONF_S stVbConf;//缓冲池结构体SAMPLE_VI_CONFIG_S stViConfig = {
0};VPSS_GRP VpssGrp;VPSS_CHN VpssChn;VPSS_GRP_ATTR_S stVpssGrpAttr;//定义 VPSS GROUP 属性VPSS_CHN_ATTR_S stVpssChnAttr;//定义 VPSS Chn 静态属性VPSS_CHN_MODE_S stVpssChnMode;//定义 VPSS Chn 模式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;

step 1: init sys variable

定义视频缓存池属性结构体:

typedef struct hiVB_CONF_S{
HI_U32 u32MaxPoolCnt; //刺痛中缓冲池最大数量 如128? 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;
计算缓冲池单帧大小:enPicSize+制式→长宽→向上对齐 *系数  +头(←宏)SAMPLE_COMM_SYS_GetPicSize(enNorm, enPicSize, &stSize);{
switch (enPicSize) {
case PIC_HD720: /* 1280 * 720 */ pstSize->u32Width = 1280; pstSize->u32Height = 720; break; case PIC_HD1080: /* 1920 * 1080 */ pstSize->u32Width = 1920; pstSize->u32Height = 1080; break; case PIC_QCIF: pstSize->u32Width = 176; pstSize->u32Height = (VIDEO_ENCODING_MODE_PAL==enNorm)?144:120; break; ……}u32VbSize = (CEILING_2_POWER(stSize.u32Width, u32AlignWidth) * \ CEILING_2_POWER(stSize.u32Height,u32AlignWidth) * \ ((PIXEL_FORMAT_YUV_SEMIPLANAR_422 == enPixFmt)?2:1.5));//宽高:向上对齐后成为u32AlignWidth的倍数 系数←像素格式如YUV420 VB_PIC_HEADER_SIZE(stSize.u32Width, stSize.u32Height, enPixFmt, u32HeaderSize);//宏:计算帧的头大小=如420:size = (VB_HEADER_STRIDE * (Height) * 3) >> 1; u32VbSize += u32HeaderSize;【后面会填充通道012通道的VB】

在这里插入图片描述

是否压缩由VI通道参数决定

#define VB_PIC_BLK_SIZE(Width, Height, Type, size)\    do{\        unsigned int u32AlignWidth;\        unsigned int u32AlignHeight;\        unsigned int u32HeadSize;\        u32AlignWidth = VB_ALIGN(Width, 16);\        u32AlignHeight= VB_ALIGN(Height, 2);\        u32HeadSize = VB_HEADER_STRIDE * u32AlignHeight;        /* compress header stride 16 */\        if (PIXEL_FORMAT_YUV_SEMIPLANAR_422 == Type)\        {
\ size = (u32AlignWidth * u32AlignHeight + u32HeadSize) * 2;\ }\ else if (PIXEL_FORMAT_YUV_400 == Type)\ {
\ size = (u32AlignWidth * u32AlignHeight + u32HeadSize);\ }\ else\ {
\ size = ((u32AlignWidth * u32AlignHeight + u32HeadSize) * 3) >> 1;\ }\ }while(0)

step 2: mpp system init.

SAMPLE_COMM_SYS_Init(VB_CONF_S *pstVbConf){
MPP_SYS_CONF_S stSysConf = {
0};//MPP系统控制属性结构体--只有一个对齐参数 HI_MPI_SYS_Exit();//去初始化 MPP 系统。 HI_MPI_VB_Exit();//去初始化 MPP 视频缓存池。 HI_MPI_VB_SetConf(pstVbConf);//设置 MPP 视频缓存池属性 HI_MPI_VB_Init();//初始化 MPP 视频缓存池 stSysConf.u32AlignWidth = SAMPLE_SYS_ALIGN_WIDTH;//填充对齐参数 HI_MPI_SYS_SetConf(&stSysConf);//配置系统控制参数(只有一个对齐参数) HI_MPI_SYS_Init();//HI_MPI_SYS_Init //前面几个函数如果返回有问题→函数回HI_FAILURE return HI_SUCCESS;}

MPP 系统控制属性结构体(其实只有一个对齐参数)

typedef struct hiMPP_SYS_CONF_S{
// stride of picture buffer must be aligned with this value. //这个对齐参数不能是1,必须是16的倍数 范围[1,1024] HI_U32 u32AlignWidth;//对齐参数}MPP_SYS_CONF_S;

step 3: start vi dev & chn to capture

设置VI配置结构体(samplede的)+开启VI(sensor写入+运行ISP)

stViConfig.enViMode   = SENSOR_TYPE;//配置文件中定义的宏 摄像头信号AR0130=APTINA_AR0130_DC_720P_30FPSstViConfig.enRotate   = ROTATE_NONE;//枚举-不旋转stViConfig.enNorm     = VIDEO_ENCODING_MODE_AUTO;//视频制式:PAL、NTSC、AUTO暂时不支持stViConfig.enViChnSet = VI_CHN_SET_NORMAL;//枚举-既不镜像也不翻转stViConfig.enWDRMode  = WDR_MODE_NONE;//WDR宽动态 NONE不支持SAMPLE_COMM_VI_StartVi(SAMPLE_VI_CONFIG_S* pstViConfig)enViMode类型→不同来源sensor或者硬盘(这里选择sensor来源)				↓							↓SAMPLE_COMM_VI_StartIspAndVi(SAMPLE_VI_CONFIG_S* pstViConfig){
step 1: 配置mipi SAMPLE_COMM_VI_StartMIPI(pstViConfig); step 2: 配置sensor and ISP (include WDR mode) SAMPLE_COMM_ISP_Init {
sensor_register_callback //* 1. 注册一个sensor回调 HI_MPI_AE_Register //* 2. 注册isp内部的ae单元-自动曝光 HI_MPI_AWB_Register //* 3. 自动白平衡 HI_MPI_AF_Register //* 4. 自动对焦 HI_MPI_ISP_MemInit //* 5. 给isp单元分配必要的内存 HI_MPI_ISP_SetWDRMode //* 6. 设置宽动态相关属性 HI_MPI_ISP_SetPubAttr // 7. isp set pub attributes //根据SENSOR_TYPEswitch确定结构体的内容(颜色排序BAYER_RGGB、GRGB、BGGR 帧率 起始xy坐标 长宽)再写到IPS内部单元 HI_MPI_ISP_Init //* 8. ISP初始化 } step 3: run isp thread SAMPLE_COMM_ISP_Run pthread_create(&gs_IspPid, &attr, (void* (*)(void*))Test_ISP_Run, NULL) Test_ISP_Run HI_MPI_ISP_Run step 4 : config & start vicap dev SAMPLE_COMM_VI_StartDev //根据sensor型号填充stViDevAttr结构体(mamcpy结构体+手动赋值0,0,长宽即分辨率) HI_MPI_VI_SetDevAttr(ViDev, &stViDevAttr);//将stViDevAttr的值设置进去 这里吧参数结构体写进去(前面写好参数结构体) HI_MPI_ISP_GetWDRMode HI_MPI_VI_SetWDRAttr HI_MPI_VI_EnableDev//启动Dev Step 5: config & start vicap chn (max 1) SAMPLE_COMM_VI_StartChn HI_MPI_VI_SetChnAttr HI_MPI_VI_SetRotate HI_MPI_VI_EnableChn//打开 //step45 都是设置一些参数再启动 只有一个dev一个chn }

1.定义VENC编码通道属性结构体:

载荷为H264时使用的编码通道属性结构体:

typedef struct hiVENC_ATTR_H264_S{
HI_U32 u32MaxPicWidth;/*编码图像最大高度 */ HI_U32 u32MaxPicHeight; /*编码图像最大高度 */ //必须是 MIN_ALIGN 的整数倍静态属性。 HI_U32 u32PicWidth;/*编码图像宽度 必须是 MIN_ALIGN 的整数倍*/ HI_U32 u32PicHeight;/*编码图像高度 必须是 MIN_ALIGN 的整数倍*/ HI_U32 u32BufSize;/*码流buffer大小*///推荐值:一幅最大编码图像大小。推荐值为 u32MaxPicWidth × u32MaxPicHeight ×1.5 byte HI_U32 u32Profile; /*编码的等级 */ //0: baseline; 1:MP; 2:HP; 3: SVC-T [0,3]; HI_BOOL bByFrame; /*帧/包模式获取码流*/--------------------- //HI_TRUE:按帧获取。 HI_FALSE:按包获取 HI_U32 u32BFrameNum; /* 0:不支持B帧; >=1:B帧数量 */ HI_U32 u32RefNum;/*编码支持参考帧的个数*/ //0: default; number of refrence frame 取值范围: [1, 2]。*/ }VENC_ATTR_H264_S;

sample中特有的一个结构体:VI配置参数结构体:

typedef struct sample_vi_config_s{
SAMPLE_VI_MODE_E enViMode;//vi模式 如APTINA_AR0130_DC_720P_30FPS 但是实际用了SENSOR_TYPE VIDEO_NORM_E enNorm; //制式 sample用了VIDEO_ENCODING_MODE_AUTO 但是手册说自动识别制式目前不支持? ROTATE_E enRotate;//枚举 旋转 0° 90° 180° 270° SAMPLE_VI_CHN_SET_E enViChnSet; //镜像(水平翻转 上下翻转 2*2=4) WDR_MODE_E enWDRMode;//宽动态模式}SAMPLE_VI_CONFIG_S;

而手册上的VI参数结构体:

typedef struct hiVI_CHN_ATTR_S{
RECT_S stCapRect;//捕获区域起始坐标(相对于设备图像的大小)与宽高。 SIZE_S stDestSize;//目标图像大小。 VI_CAPSEL_E enCapSel;//帧场选择 PIXEL_FORMAT_E enPixFormat;//像素存储格式 COMPRESS_MODE_E enCompressMode;//是否压缩,支持非压缩与 256byte 段式压缩。---VI通道和VI拓展通道都有的成员 HI_BOOL bMirror;//是否水平翻转。 HI_BOOL bFlip;//是否垂直翻转 HI_S32 s32SrcFrameRate;//原始帧率,不能小于-1;建议设置为与对接 camera 一致的值。 HI_S32 s32DstFrameRate;//目标帧率,必须小于或等于原始帧率,同时不能小于-1 //原始帧率与目标帧率要么同时为-1,要么都不为-1,两者都为-1 时表示不进行帧率控制。} VI_CHN_ATTR_S;

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

step 4: start vpss and vi bind vpss

图像预处理,如去噪、去隔行等,再对各通道进行缩放、锐化等处理。

最后输出多种不同分辨率的图像

设置VPSS_GRP_ATTR_S stVpssGrpAttr 设置group的长宽 像素格式+5个其他的SAMPLE_COMM_VPSS_StartGroup(①group号:VpssGrp=0, ②group参数:VPSS_GRP_ATTR_S ){
HI_MPI_VPSS_CreateGrp(VpssGrp, pstVpssGrpAttr);//创建一个 VPSS GROUP。 软降方面的实例化 硬件早就有了 //HI_MPI_VPSS_GetNRParam(VpssGrp, &unNrParam);//获取 VPSS 3DNR 参数 //HI_MPI_VPSS_SetNRParam(VpssGrp, &unNrParam);//设置 VPSS 3DNR 参数 HI_MPI_VPSS_StartGrp(VpssGrp);//启用 VPSS GROUP。}SAMPLE_COMM_VI_BindVpss(stViConfig.enViMode);//VI的chn和VPSS的grp绑定{
//SAMPLE_COMM_VI_Mode2Param(enViMode, &stViParam);//设了4个1: 设备号 设备号递增量 通道号 通道号递增量(设备这两个1应该没必要 通道号的话可能会有1个物理通道Chn0+多个拓展通道的情况-有必要) //VI硬件单元只有一个VI设备,即 Dev0, 仅包含 1 个VI视频物理通道,即Chn0,但支持最多16个扩展通道。 //for (j=0; j
宽u32Width和高u32Height HD720宽1280*高720 }if(s32ChnNum :1 2 3){
SAMPLE_COMM_SYS_GetPicSize(VIDEO_NORM_E enNorm, PIC_SIZE_E enPicSize, SIZE_S *pstSize) {
switch (enPicSize和制式enNorm)→pstSize->宽u32Width和高u32Height HD720宽1280*高720 } VpssChn = 0 1 2; 填充VPSS_CHN_MODE_S通道工作模式:模式+预留+目标宽高+目标像素格式+目标压缩格式 stVpssChnMode.enChnMode = VPSS_CHN_MODE_USER;//只能工作在uesr模式下 不能工作在auto模式 stVpssChnMode.bDouble = HI_FALSE; stVpssChnMode.enPixelFormat = PIXEL_FORMAT_YUV_SEMIPLANAR_420;//像素格式 stVpssChnMode.u32Width = stSize.u32Width;//←SAMPLE_COMM_SYS_GetPicSize stVpssChnMode.u32Height = stSize.u32Height;//←SAMPLE_COMM_SYS_GetPicSize stVpssChnMode.enCompressMode = COMPRESS_MODE_SEG;//即段压缩的视频格式,按照 256 bytes 为一段进行压缩。 memset(&stVpssChnAttr, 0, sizeof(stVpssChnAttr)); stVpssChnAttr.s32SrcFrameRate = -1;//目标和源的帧率均为-1表示不控制 不变化 stVpssChnAttr.s32DstFrameRate = -1; 使能VPSS通道SAMPLE_COMM_VPSS_EnableChn(VpssGrp, VpssChn, &stVpssChnAttr, &stVpssChnMode, HI_NULL); {
①通道号

MPP手册中的绑定函数

MPP 提供系统绑定接口(HI_MPI_SYS_Bind),即通过数据接收者绑定数据源来建立两者之间的关联关系(只允许数据接收者绑定数据源)。绑定后,数据源生成的数据将自动发送给接收者。

HI_MPI_SYS_Bind数据源到数据接收者绑定接口。HI_S32 HI_MPI_SYS_Bind(MPP_CHN_S *pstSrcChn, MPP_CHN_S *pstDestChn);VI、VDEC 作为数据源:通道为发送者					设备号置为 0(SDK不检查输入的设备号)VPSS接收:设备(GROUP)为接收者 		通道号=0(SDK 不检查输入的通道号)VENC接收: 通道号接收			设备号=0(SDK 不检查输入的通道号)其他情况均需指定设备号和通道号。typedef struct hiMPP_CHN_S//模块设备通道结构体{
MOD_ID_E enModId;//模块ID号 HI_S32 s32DevId;//设备号 HI_S32 s32ChnId;//通道号} MPP_CHN_S;typedef enum hiMOD_ID_E//模块ID 枚举类型{
HI_ID_CMPI = 0, HI_ID_VB = 1, HI_ID_SYS = 2, HI_ID_RGN = 3, HI_ID_CHNL = 4, HI_ID_VDEC = 5,HI_ID_GROUP = 6,HI_ID_VPSS = 7, HI_ID_VENC = 8,HI_ID_VIU = 16,//VI模块 ....} MOD_ID_E;

VPSS CHN 工作模式结构VPSS_CHN_MODE_S

//4个物理通道要设置,拓展通道不用设置

typedef struct hiVPSS_CHN_MODE_S{
VPSS_CHN_MODE_E enChnMode;//VPSS CHN 工作模式 //2种:VPSS_CHN_MODE_USER 用户设置模式 VPSS_CHN_MODE_AUTO 自动模式,通道的默认工作模式 HI_U32 u32Width;//目标图像宽 HI_U32 u32Height;//目标图像高。 HI_BOOL bDouble;//保留。 PIXEL_FORMAT_E enPixelFormat;//目标图像像素格式。 COMPRESS_MODE_E enCompressMode;//目标图像压缩格式 枚举:非压缩 段压缩(256 128byte) 行压缩 帧压缩}VPSS_CHN_MODE_S;

VPSS 物理group的属性

typedef struct hiVPSS_GRP_ATTR_S//VPSS group静态属性【创建 Group 时设定,不可更改,除非关了group】{
HI_U32 u32MaxW; //最大图像宽度 范围:[VPSS_MIN_IMAGE_WIDTH,VPSS_MAX_IMAGE_WIDTH] HI_U32 u32MaxH; //最大图像高度 范围: [VPSS_MIN_IMAGE_HEIGHT,VPSS_MAX_IMAGE_HEIGHT] PIXEL_FORMAT_E enPixFmt;//像素格式 HI_BOOL bIeEn; //保留。必须设置为HI_FALSE HI_BOOL bDciEn; //保留。必须设置为 HI_FALSE HI_BOOL bNrEn; //Nr 使能开关 HI_BOOL bHistEn; //保留。必须设置为 HI_FALSE。 VPSS_DIE_MODE_E enDieMode; //保留。必须设置为VPSS_DIE_MODE_NODIE。}VPSS_GRP_ATTR_S;相关接口: VPSS_DIE_MODE_E HI_MPI_VPSS_CreateGrp HI_MPI_VPSS_SetGrpAttr HI_MPI_VPSS_GetGrpAttr

VPSS 物理chn的属性

typedef struct hiVPSS_CHN_ATTR_S{
HI_BOOL bSpEn;//Sp 使能开关,只能为 HI_FALSE。 HI_BOOL bBorderEn;//边框使能开关,只能为 HI_FALSE。 HI_BOOL bMirror;//Mirror 使能开关。 HI_BOOL bFlip;//Flip 使能开关。 HI_S32 s32SrcFrameRate;//通道帧率控制:源帧率。取值范围: (0, 240],以及-1 HI_S32 s32DstFrameRate;//通道帧率控制:目标帧率。取值范围: [-1, 240]。 BORDER_S stBorder;//边框属性。}VPSS_CHN_ATTR_S;源帧率与目标帧率都为-1,则不进行帧率控制。

step 5: start stream venc

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[012] **/ if(s32ChnNum >= 1 2 3) {
VpssGrp = 0; VpssChn = 0 1 2; VencChn = 0 1 2; SAMPLE_COMM_VENC_Start(VencChn, 载荷类型, 制式, 图像大小, 码流控制模式,画质级别Profile) {
获取图像的宽高SAMPLE_COMM_SYS_GetPicSize(enNorm, enSize, &stPicSize);//图像大小+制式→→宽高 step 1:填充编码通道属性结构体+创建编码通道 载荷类型H264→→编码器属性VENC_CHN_ATTR_S 之 协议类型+该协议类型的编码器属性 码流控制模式→→编码器属性VENC_CHN_ATTR_S 之 码流控制器属性VENC_RC_ATTR_S 中的控制模式+具体协议编码通道的码流模式属性 //图片大小→具体协议编码通道的码流模式属性的比特率 如stH264Cbr.u32BitRate = 1024*2; HI_MPI_VENC_CreateChn(编码通道号VencChn, 编码通道属性指针VENC_CHN_ATTR_S);//创建编码通道 step 2:开启编码通道(接收输入图像) HI_MPI_VENC_StartRecvPic(VencChn);//开启编码通道接收输入图像 } SAMPLE_COMM_VENC_BindVpss(VeChn,VpssGrp,VpssChn) {
模块ID 设备号 通道号 stSrcChn HI_ID_VPSS; VpssGrp; VpssChn; stDestChn HI_ID_VENC; 0; VeChn; HI_MPI_SYS_Bind(&stSrcChn, &stDestChn);//数据源到数据接收者绑定 } }end if(s32ChnNum >= 1 2 3)

编码通道属性结构体:

typedef struct hiVENC_CHN_ATTR_S//定义编码通道属性结构体。{
① VENC_ATTR_S stVeAttr;//编码器属性。 ② VENC_RC_ATTR_S stRcAttr;//码率控制器属性。}VENC_CHN_ATTR_S; ↓ ↓ ↓①编码器属性:typedef struct hiVENC_ATTR_S{
PAYLOAD_TYPE_E enType;//编码协议类型 union {
VENC_ATTR_H264_S stAttrH264e;//某种协议的编码器属性。 {
HI_U32 u32MaxPicWidth;//编码图像最大高度 HI_U32 u32MaxPicHeight; //编码图像最大高度 HI_U32 u32PicWidth;//编码图像宽度 HI_U32 u32PicHeight;//编码图像高度 //上面2组宽高都必须是 MIN_ALIGN 的整数倍 HI_U32 u32BufSize;//码流buffer大小 推荐值:一幅最大编码图像大小。 //推荐值为 u32MaxPicWidth × u32MaxPicHeight ×1.5 byte HI_U32 u32Profile; //编码的等级 0: baseline; 1:MP; 2:HP; 3: SVC-T [0,3]; ------------HI_BOOL bByFrame; //帧or包模式获取码流 HI_TRUE按帧获取 HI_FALSE按包获取 取值范围: {HI_TRUE=0, HI_FALSE=1}。 HI_U32 u32BFrameNum; //0:不支持B帧; >=1:B帧数量 HI_U32 u32RefNum;//*编码支持参考帧的个数*/ //0: default; number of refrence frame 取值范围: [1, 2]。*/ } } VENC_ATTR_MJPEG_S stAttrMjpeg; VENC_ATTR_JPEG_S stAttrJpeg; VENC_ATTR_MPEG4_S stAttrMpeg4; VENC_ATTR_H265_S stAttrH265e; };}VENC_ATTR_S;②码率控制器属性:typedef struct hiVENC_RC_ATTR_S//码流控制器属性{
VENC_RC_MODE_E enRcMode;//编码通道码率控制器模式。 解释就在下面 union {
VENC_ATTR_H264_CBR_S stAttrH264Cbr;//H.264协议编码通道 Cbr模式属性 VENC_ATTR_H264_VBR_S stAttrH264Vbr;//H.264协议编码通道 Vbr模式属性 VENC_ATTR_H264_FIXQP_S stAttrH264FixQp; VENC_ATTR_H264_ABR_S stAttrH264Abr; VENC_ATTR_MPEG4_CBR_S stAttrMpeg4Cbr; VENC_ATTR_MPEG4_FIXQP_S stAttrMpeg4FixQp; VENC_ATTR_MPEG4_VBR_S stAttrMpeg4Vbr; VENC_ATTR_MJPEG_CBR_S stAttrMjpegeCbr; VENC_ATTR_MJPEG_FIXQP_S stAttrMjpegeFixQp; VENC_ATTR_MJPEG_VBR_S stAttrMjpegeVbr; VENC_ATTR_H265_CBR_S stAttrH265Cbr; VENC_ATTR_H265_VBR_S stAttrH265Vbr; VENC_ATTR_H265_FIXQP_S stAttrH265FixQp; }; HI_VOID* pRcAttr ;}VENC_RC_ATTR_S;typedef enum hiVENC_RC_MODE_E //编码通道码率控制器模式。{
VENC_RC_MODE_H264CBR = 1, VENC_RC_MODE_H264VBR, VENC_RC_MODE_H264ABR, VENC_RC_MODE_H264FIXQP, VENC_RC_MODE_MJPEGCBR, VENC_RC_MODE_MJPEGVBR, VENC_RC_MODE_MJPEGABR, VENC_RC_MODE_MJPEGFIXQP, VENC_RC_MODE_MPEG4CBR, VENC_RC_MODE_MPEG4VBR, VENC_RC_MODE_MPEG4ABR, VENC_RC_MODE_MPEG4FIXQP, VENC_RC_MODE_H265CBR, VENC_RC_MODE_H265VBR, VENC_RC_MODE_H265FIXQP, VENC_RC_MODE_BUTT, //4种协议:H264 MPJEG MPEG4 H265 //3种码流控制方式:CBR Fixqp VBR ABR 手册上ARB模式均没有提供}VENC_RC_MODE_E;相关API:HI_MPI_VENC_CreateChn 创建编码通道

step 6: stream venc process – get stream, then save it to file.

HI_S32 SAMPLE_COMM_VENC_StartGetStream_Svc_t(实际chn数量HI_S32 s32Cnt){
gs_stPara.bThreadStart = HI_TRUE;//全局变量结构体 VENC的{编码状态 VENC_ChnNum} //HI_TRUE正常编码 HI_FALSE停止编码 gs_stPara.s32Cnt = s32Cnt; return pthread_create(&gs_VencPid, 0, SAMPLE_COMM_VENC_GetVencStreamProc_Svc_t, (HI_VOID*)&gs_stPara);}printf("please press twice ENTER to exit this sample\n");getchar();getchar();

实际负责处理的线程:

HI_VOID* SAMPLE_COMM_VENC_GetVencStreamProc(HI_VOID *p→→结构体指向线程状态和s32ChnNum)//这里传参指针位void *类型 函数中使用前先转换(SAMPLE_VENC_GETSTREAM_PARA_S*)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]; 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;//传参使用的是void* 这里重新变回结构体 s32ChnTotal = pstPara->s32Cnt;step 1: fopen创建&打开获得fd指针pFile[i] + 获取编码通道设备文件句柄Venc[i] for (i = 0; i < s32ChnTotal; i++)//0 1 2 3...
<通道数 { 载荷类型→后缀名→完整文件名→创建并打开这个文件(后面会保存至这个fd) vencchn="i;" hi_mpi_venc_getchnattr(vencchn, &stvencchnattr); enpayloadtype[i]="stVencChnAttr.stVeAttr.enType;//编码通道参数.编码器参数.编码协议类型" 根据载荷类型enpayloadtype确定得到的文件后缀 .h264 sprintf(aszfilename[i], "stream_chn%d%s", i, szfilepostfix); 将后缀名输出到文件名中 pfile[i]="fopen(aszFileName[i]," "wb"); 源文件不存在在→wb会创建出文件 * set venc fd. vencfd[i]="HI_MPI_VENC_GetFd(i);//获取编码通道对应的设备文件句柄" 并获取各个通道设备文件句柄的最大值→maxfd } #if ortp_enable ***rtp init**** prtpsession="rtpInit(" local_host_ip ,8080); #endifstep 2: start to get streams of each channel. while (hi_true="=" pstpara->
bThreadStart)//整个获取码流线程循环是以HI_TRUE为条件的 即处于获取状态 而没有被step7终止 {
//清空文件描述符集合+填充各个chn的fd 设置超时等待结构体TimeoutVal 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);//建议用户使用 select 方式获取码流,且按照如下的流程://(1)调用HI_MPI_VENC_Query 函数查询编码通道状态; 查询失败→break;//(2)确保 u32CurPacks 和u32LeftStreamFrames 同时大于 0;不满足→break;//(3)调用 malloc 分配 u32CurPacks 个包信息结构体//(4)调用 HI_MPI_VENC_GetStream 获取编码码流;//(5)调用HI_MPI_VENC_ReleaseStream 释放码流缓存。//(6)释放帧码流包结构体内存 帧码流结构体中的指针→NULL //s32Ret < 0失败→break //s32Ret == 0 超时→continue→重新while循环 //s32Ret > 0就绪的fd数量 保留↓ for (i = 0; (i < s32ChnTotal), &read_fds)); i++) 还要满足(FD_ISSET(VencFd[i]//有效序号且还序号的fd就绪 {
step 2.1 : query how many packs in one-frame stream. memset(&stStream, 0, sizeof(stStream));//清空码流结构体 HI_MPI_VENC_Query(i, &stStat);//查询编码通道状态→VENC_CHN_STAT_S stStat; step 2.2 :suggest to check 当前帧的包数u32CurPacks and 码流buff中余下的帧数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); //帧码流结构体中的指针--指向--申请的帧码流包结构体//VPSS_CHN_ATTR_S-编码器属性VENC_ATTR_S-某种协议的编码器属性VENC_ATTR_H264_S-bByFrame:{HI_TRUE按帧获取, HI_FALSE按包获取} step 2.4 : call mpi to get one-frame stream stStream.u32PackCount = stStat.u32CurPacks; HI_MPI_VENC_GetStream(编码通道号i, 码流结构体指针&stStream, 阻塞方式-等待时间HI_TRUE);//获取编码的码流/*HI_MPI_VENC_GetStream 应当和HI_MPI_VENC_ReleaseStream配对起来使用,用户获取码流后必须及时释放已经获取的码流缓存,否则可能会导致码流 buffer 满,影响编码器编码,并且用户必须按先获取先释放的顺序释放已经获取的码流缓存。(如果用户长时间不获取码流,码流缓冲区就会满。→不再编码)(若只拿而不释放还是会码流buffer满→不再编码)在编码通道复位以后,所有未释放的码流包均无效,不能再使用或者释放这部分无效的码流缓存。*/ step 2.5 : save frame to file //高级跳帧模式下不同chn有着不同的保存倾向 ↓↓↓ SAMPLE_COMM_VENC_SaveStream(enPayLoadType[i], pFile[i], &stStream); //先载荷→分支选择SAMPLE_COMM_VENC_SaveH264 {
HI_S32 i; for (i = 0; i < pstStream->u32PackCount; i++) {
#if ORTP_ENABLE rtpSend(pRtpSession,pstStream->pstPack[i].pu8Addr, pstStream->pstPack[i].u32Len); #else fwrite(pstStream->pstPack[i].pu8Addr+pstStream->pstPack[i].u32Offset, pstStream->pstPack[i].u32Len-pstStream->pstPack[i].u32Offset, 1, fpH264File);//size_t fwrite(代谢如数据const void *ptr, 元素大小size_t size, 元素个数size_t nmemb, 向这个位置写入FILE *stream) fflush(fpH264File);//fflush(stdin)刷新标准输入缓冲区,把输入缓冲区里的东西丢弃;//fflush(stdout)刷新标准输出缓冲区,把输出缓冲区里的东西强制打印到标准输出设备上。//? #endif } return HI_SUCCESS; } step 2.6 : release stream HI_MPI_VENC_ReleaseStream(i, &stStream);//释放码流缓存 step 2.7 : free pack nodes释放之前申请的帧码流包结构体 并使指针变成空指针 free(stStream.pstPack);//之前malloc申请的包数*码流包结构体,有码流结构体的第一个指针指向 stStream.pstPack = NULL; }? for (i = 0; (i < s32ChnTotal)&&(FD_ISSET(VencFd[i], &read_fds)); ? }? end while (HI_TRUE == pstPara->bThreadStart) ?step 3 : close save-file for (i = 0; i < s32ChnTotal; i++){
fclose(pFile[i]);} return NULL;}
一个特殊的结构体:{
venc状态+venc通道数}typedef struct sample_venc_getstream_s{
HI_BOOL bThreadStart; step7会改状态关闭编码:HI_TRUE正常/HI_FALSE 即停止编码 HI_S32 s32Cnt; //venc编码通道数,后面会用到}SAMPLE_VENC_GETSTREAM_PARA_S;

MPP 结构体

编码通道状态结构体:

相关APIHI_MPI_VENC_Query(i, &stStat);//查询编码通道状态

typedef struct hiVENC_CHN_STAT_S{
HI_U32 u32LeftPics;//待编码的图像数 HI_U32 u32LeftStreamBytes;//码流buffer 剩余的 byte 数 HI_U32 u32LeftStreamFrames;//码流buffer 剩余的帧数。 HI_U32 u32CurPacks;//当前帧的码流包个数。 HI_U32 u32LeftRecvPics;//剩余待接收的帧数 //在用户设置HI_MPI_VENC_StartRecvPicEx 后有效。 HI_U32 u32LeftEncPics;//剩余待编码的帧数, //在用户设置HI_MPI_VENC_StartRecvPicEx 后有效。}VENC_CHN_STAT_S;

码流结构体VENC_STREAM_S

相关API:

(使用前清零,并且要填充包数stStream.u32PackCount = stStat.u32CurPacks;)
HI_MPI_VENC_GetStream(编码通道号i, 码流结构体指针&stStream, 超时时间HI_TRUE);//获取编码的码流

typedef struct hiVENC_STREAM_S{
①VENC_PACK_S *pstPack;//帧码流包信息指针→→→帧码流包结构体 VENC_PACK_S //按包获取则该空间不小于 sizeof(VENC_PACK_S) //按帧获取则该空间不小于 N(当前帧的包数) × sizeof(VENC_PACK_S) ②HI_U32 u32PackCount;//一帧码流的所有包的个数 //在输入时,此值指定 pstPack 中 VENC_PACK_S 的个数。 //按包获取时,u32PackCount 必须不小于 1; //→获取后为1,地址为pstPack[0].pu8Addr //按帧获取时, u32PackCount 必须不小于当前帧的包个数。 //在函数调用成功后, u32PackCount 返回实际填充 pstPack 的包的个数。 ③HI_U32 u32Seq;//码流序列号。 按帧获取帧序号;按包获取包序号。 ④union {
VENC_STREAM_INFO_H264_S stH264Info;//码流特征信息 {
HI_U32 u32PicBytesNum; HI_U32 u32PSkipMbNum; HI_U32 u32IpcmMbNum; HI_U32 u32Inter16x8MbNum; HI_U32 u32Inter16x16MbNum; HI_U32 u32Inter8x16MbNum; HI_U32 u32Inter8x8MbNum; HI_U32 u32Intra16MbNum; HI_U32 u32Intra8MbNum; HI_U32 u32Intra4MbNum; H264E_REFSLICE_TYPE_E enRefSliceType;//编码当前帧属于何种跳帧参考模式下的参考帧 H264E_REF_TYPE_E enRefType;//高级跳帧参考下的编码帧类型 HI_U32 u32UpdateAttrCnt; HI_U32 u32StartQp; } VENC_STREAM_INFO_JPEG_S stJpegInfo;//码流特征信息 VENC_STREAM_INFO_MPEG4_S stMpeg4Info;//码流特征信息 VENC_STREAM_INFO_H265_S stH265Info;//码流特征信息 }; //包含了不同编码协议对应的码流特征信息stH264Info/ stJpegInfo/ stMpeg4Info/stH265Info,码流特征信息的输出用于支持用户的上层应用。}VENC_STREAM_SHI_MPI_VENC_GetStream支持按包或按帧方式获取码流。如果按包获取,则:− 对于 H.264 H.265 编码协议,每次获取的是一个 NAL 单元。− 对于 JPEG 编码协议(包括 JPEG 抓拍和 MJPEG),每次获取的是一个 ECS 或图像参数码流包。

HI_MPI_VENC_GetStream获取码流分

①按帧获取(每次拿到一帧,包数取决于帧的大小)
②按包获取(每次拿到一包)

MPP手册6.2.11 编码码流帧配置模式

在这里插入图片描述
在这里插入图片描述

帧码流包结构体(存放单个码流包的信息)

typedef struct hiVENC_PACK_S{
HI_U32 u32PhyAddr;//码流包首地址 HI_U8 *pu8Addr;//码流包物理地址 HI_U32 u32Len;//码流包长度 HI_U64 u64PTS;//时间戳。单位: us。 HI_BOOL bFrameEnd;//帧结束标识 HI_TRUE最后一包 HI_FALSE非最后包 VENC_DATA_TYPE_U DataType;//码流类型,支持H.264/JPEG/MPEG-4协议类型的数据包 HI_U32 u32Offset;//码流包中有效数据与码流包首地址 pu8Addr 的偏移 HI_U32 u32DataNum;//当前码流包(当前包的类型由 DataType 指定)数据中包含其他类型码流包的个数 VENC_PACK_INFO_S stPackInfo[8];//当前码流包数据中包含其他类型码流包数据信息。}VENC_PACK_S;

包含联合体 VENC_DATA_TYPE_U

step 7: exit process

HI_S32 SAMPLE_COMM_VENC_StopGetStream(){
if (HI_TRUE == gs_stPara.bThreadStart) {
gs_stPara.bThreadStart = HI_FALSE; pthread_join(gs_VencPid, 0); } return HI_SUCCESS;}

7.定义码流结果联合体

H264E_NALU_TYPE_E

JPEGE_PACK_TYPE_E
MPEG4E_PACK_TYPE_E
H265E_NALU_TYPE_E

typedef union hiVENC_DATA_TYPE_U{
H264E_NALU_TYPE_E enH264EType;//H.264 码流包类型。 {
H264E_NALU_PSLICE = 1,//PSLICE 类型 H264E_NALU_ISLICE = 5,//ISLICE 类型 H264E_NALU_SEI = 6,//SEI 类型。 H264E_NALU_SPS = 7,//SPS 类型 H264E_NALU_PPS = 8,//PPS 类型 H264E_NALU_IPSLICE = 9, H264E_NALU_BUTT } JPEGE_PACK_TYPE_E enJPEGEType;//JPEG 码流包类型。 {
JPEGE_PACK_ECS = 5, //ECS类型 JPEGE_PACK_APP = 6,//APP 类型 JPEGE_PACK_VDO = 7,//VDO 类型 JPEGE_PACK_PIC = 8,//PIC 类型 JPEGE_PACK_BUTT } MPEG4E_PACK_TYPE_E enMPEG4EType;//MPEG-4 码流包类型。 {
MPEG4E_PACK_VOP_P = 1,//P 帧的视频对象平面类型。 MPEG4E_PACK_VOP_I = 5,//I 帧视频数据包类型。 MPEG4E_PACK_VOS = 6,//视频对象分列类型。 MPEG4E_PACK_VO = 7,//视频对象类型。 MPEG4E_PACK_VOL = 8,//视频对象层类型 MPEG4E_PACK_GVOP = 9 //视频对象平面类型组 } H265E_NALU_TYPE_E enH265EType;//H.265 码流包类型。 {
H265E_NALU_PSLICE = 1,//PSLICE 类型 H265E_NALU_ISLICE = 19,//ISLICE 类型 H265E_NALU_VPS = 32, H265E_NALU_SPS = 33, H265E_NALU_PPS = 34, H265E_NALU_SEI = 39, H265E_NALU_BUTT }}VENC_DATA_TYPE_U;

定义 H.264编码的 NALU大小设置结构体

typedef structhiVENC_ATTR_H264_NALU_S

{
HI_BOOL bNaluSplitEnable; //是否打开 NALU划分,HI_TURE:打开。
HI_U32 u32NaluSize; //NALU划分使能的情况下指定 NALU的大小,以字节为单位,在关闭使能的情况下,此参数无效。 必须满足 128 <=u32NaluSize <= 图象大小(包括色度)。
} VENC_ATTR_H264_NALU_S;

三、API 参考

视频编码功能实际包含 VENC(视频编码)和 GROUP(通道组管理)两个重要的部分,主要提供视频编码通道组的创建和销毁、通道组 GROUP 与视频输入通道的绑定和解绑定、视频编码通道的创建和销毁、注册和反注册到通道组、开启和停止接收图像、设置和获取编码通道属性、获取和释放码流、设置和获取数字水印属性、启用和禁用数字水印、视频编码通道属性的设置和查询等功能。

1.创建/销毁编码通道组

HI_S32 HI_MPI_VENC_CreateGroup(VENC_GRP VeGroup);
HI_S32 HI_MPI_VENC_DestroyGroup(VENC_GRP VeGroup);
A.本文档中含有通道组号的接口的通道组号的取值范围为[0, VENC_MAX_GRP_NUM),否则返回 HI_ERR_VENC_INVALID_CHNID。
B.编码通道组是指芯片能够同时处理的编码通道的集合,一个通道组最多可同时包含 1 路主码流(H.264/MJPEG)和一路次码流(H.264/MJPEG) ,或者包含 1 路JPEG抓拍,或者仅包含 1 路 MPEG4通道。
C.如果指定的通道组已经存在,则返回错误码 HI_ERR_VENC_EXIST。
D.销毁通道组时,必须保证通道组为空,即没有任何通道在通道组中注册,否则会返回错误码 HI_ERR_VENC_NOT_PERM。
E.销毁并不存在的通道组,返回错误码 HI_ERR_VENC_UNEXIST。

3.创建/销毁编码通道

HI_S32 HI_MPI_VENC_CreateChn(VENC_CHN VeChn, const VENC_CHN_ATTR_S *pstAttr, const VENC_WM_ATTR_S *pstWm);
HI_S32 HI_MPI_VENC_DestroyChn(VENC_CHN VeChn);
A.Hi3520 支持对主次码流进行编码。在创建编码通道的时候必须指定该通道是主码流还是次码流。
B.在创建编码通道的时候,编码通道属性除需要输入各个协议的特有的编码属性之外,一般还需要输入主次码流(MPEG4 编码协议无此属性) 、编码协议、编码的帧场模式、输入图像的帧场模式、获取码流的方式(按帧还是按包获取码流) 、编码图像大小属性,这些属性受表 6-1约束,并且这些属性都为静态属性,不允许动态设置。
C.若输入图像大于大码流的宽高,但相差 16 像素以内(含 16像素) ,则大码流编码图像通过输入图像做切边得到。
D.若输入图像小于大码流的宽高,会丢弃这些图像,而不会对其放大进行编码。该出错信息会在 log 中显示。
E.推荐的大码流编码宽高为:2048×1536(3M 像素) 、1280×1024(1.3M 像素) 、1920×1080(1080P) 、1280×720(720P) 、704×576、704×480、352×288、352×240。
F.对于 H.264主码流,编码图像大小不为 D1 时,其编码方式推荐使用帧编码。
G.当参数 pstWm为空时,表示该编码通道不需要使用水印,否则认为需要使用数字水印。如果创建成功,数字水印默认使能。目前只有 H.264 编码的大码流可以设置数字水印,其他的情况设置数字水印时均返回错误码HI_ERR_VENC_NOT_SUPPORT。
H.销毁并不存在的通道,返回错误码 HI_ERR_VENC_UNEXIST。
I.销毁前必须保证通道已经从通道组反注册,否则返回错误码HI_ERR_VENC_NOT_PERM。

4.注册/反注册编码通道到通道组

HI_S32 HI_MPI_VENC_RegisterChn(VENC_GRP VeGroup,VENC_CHN VeChn);
HI_S32 HI_MPI_VENC_UnRegisterChn(VENC_CHN VeChn);
A.注册并不存在的通道,返回错误码 HI_ERR_VENC_UNEXIST。
B.注册通道到不存在的通道组,返回错误码 HI_ERR_VENC_UNEXIST。
C.同一个编码通道只能注册到一个通道组,如果该通道已经注册到某个通道组,则返回 HI_ERR_VENC_NOT_PERM。
D.主次码流注册的时候需要判定以下约束关系:
− 主码流要先于次码流注册,否则返回 HI_ERR_VENC_NOT_PERM。
− 如果编码通道已经注册,则在反注册前不能再进行注册,否则返回HI_ERR_VENC_NOT_PERM。
E.MD通道注册必须在编码通道注册成功之后进行,否则返回HI_ERR_VENC_NOT_PERM。
F.同组的主次码流若为 1:1的关系,则编码方式必须同为帧编码,否则返回HI_ERR_VENC_NOT_PERM。
G.同组的主次码流宽高必须符合如下约束:D/s – d = R(主次码流宽或高分别为 D和 d,s 为1、2 或者4,R 为0 到 16) 。
H.如果通道未注册,则返回错误码 HI_ERR_VENC_NOT_PERM。
I.如果编码通道未停止接收图像编码(HI_MPI_VENC_StopRecvPic 可停止接收) ,则返回错误码 HI_ERR_VENC_NOT_PERM。
J.反注册后会将编码通道复位,如果用户还在使用未及时释放的码流 buffer,将不能保证此 buffer 数据的正确性。用户可以使用 HI_MPI_VENC_Query接口来查询状态,确认自己所有的操作都完成之后再反注册通道。

5.开启/停止编码通道接收输入图像

HI_S32 HI_MPI_VENC_StartRecvPic(VENC_CHN VeChn);
HI_S32 HI_MPI_VENC_StopRecvPic(VENC_CHN VeChn);
A.如果通道未创建,则返回 HI_ERR_VENC_UNEXIST。
B.如果通道没有注册到通道组,则返回 HI_ERR_VENC_NOT_PERM。
C.此接口不判断当前是否已经开启接收,直接将状态设置为开启接收。
D.此接口用于开启编码通道接收图像来编码,请注意它和绑定通道组的区别。
E.开始接收输入是针对通道的,只有开启接收之后编码器才开始接收图像编码。
F.此接口并不判断当前是否停止接收,直接将状态设置为停止接收。
G.此接口用于编码通道停止接收图像来编码,在编码通道反注册前必须停止接收图像。
H.调用此接口仅停止接收原始数据编码,码流 buffer并不会被清除。

7.查询编码通道状态

HI_S32 HI_MPI_VENC_Query(VENC_CHN VeChn, VENC_CHN_STAT_S *pstStat);
A.如果通道未创建,则返回 HI_ERR_VENC_UNEXIST。
B.此接口用于查询此函数调用时刻的编码器状态,pstStat 包含三个主要的信息:
− 在编码通道状态结构体中,u32LeftPics表示待编码的帧个数。
在反注册通道前,可以通过查询是否还有图像待编码来决定反注册时机,防反注册时将可能需要编码的帧清理出去。
− 在编码通道状态结构体中,u32LeftStreamBytes表示码流 buffer 中剩余的 by数目。
在反注册通道前,可以通过查询是否还有码流没有被处理来决定反注册时机防止反注册时将可能需要的码流清理出去。
− 在编码通道状态结构体中,u32CurPacks 表示当前帧的码流包个数。
在按包获取时当前帧可能不是一个完整帧(被取走一部分) ,按帧获取时表示当前一个完整帧的包个数(如果没有一帧数据则为 0) 。用户在需要按帧获取码流时,需要查询一个完整帧的包个数,在这种情况下,通常可以在 select 成功之后执行 query操作,此时 u32CurPacks是当前完整帧中包的个数。

//int main(入口在sample_venc.c) 根据传参case '0'在switch中选择一条分支→SAMPLE_VENC_1080P_CLASSIC   //HI3518E内部的ISP单元是隶属于VI模块的。//VI模块就包含3大部分:第一部分是和Sensor对接的部分,第二部分就是ISP,第三部分就是VI dev和channeltypedef struct hiVB_CONF_S{
HI_U32 u32MaxPoolCnt; /* max count of pools, (0,VB_MAX_POOLS] */ struct hiVB_CPOOL_S {
HI_U32 u32BlkSize; HI_U32 u32BlkCnt; HI_CHAR acMmzName[MAX_MMZ_NAME_LEN]; }astCommPool[VB_MAX_COMM_POOLS]; 最大缓冲池数量VB_MAX_COMM_POOLS} VB_CONF_S;/******************************************************************************* function : H.264@1080p@30fps+H.264@VGA@30fps(前面main函数选择分支'0' 即case '0':/* H.264@1080p@30fps+H.265@1080p@30fps+H.264@D1@30fps */)******************************************************************************/SAMPLE_VENC_1080P_CLASSIC(HI_VOID){
//PAYLOAD_TYPE_E enPayLoad[3]= {PT_H264, PT_H264,PT_H264};//视频数据类型 //PIC_SIZE_E enSize[3] = {PIC_HD1080, PIC_VGA,PIC_QVGA};//默认的三路码流,后面根据实际sensor可能会改动 //VB_CONF_S stVbConf;//缓冲池结构体 /****************************************** step 1: init sys variable //填充缓冲池结构体参数(缓冲块大小+数量) ******************************************/ memset(&stVbConf,0,sizeof(VB_CONF_S));//缓冲池结构体置0 SAMPLE_COMM_VI_GetSizeBySensor(&enSize[0]);//根据sensor型号确定输出型参数enSize[0]赋值(输出图像的清晰度) //进一步决定ensize[012]和输出通道数 计算s32ChnNum个缓冲池信息:SAMPLE_COMM_SYS_CalcPicVbBlkSize//一帧内存=头(宏计算)+像素空间内存(长宽向对齐参数向上对齐) ①为每一个通道计算缓冲块大小(每一帧所需的内存)stVbConf.astCommPool[012].u32BlkSize u32BlkSize = SAMPLE_COMM_SYS_CalcPicVbBlkSize(gs_enNorm,enSize[0], SAMPLE_PIXEL_FORMAT, SAMPLE_SYS_ALIGN_WIDTH); //(①视频制式 ②图像格式PIC_HD720P ③像素格式PIXEL_FORMAT_YUV_SEMIPLANAR_420,RGB888 ④对齐排布 ) {
s32Ret = SAMPLE_COMM_SYS_GetPicSize(enNorm, enPicSize, &stSize);//视频制式+图像格式👉👉得到长宽stSize(长+宽) u32VbSize = (CEILING_2_POWER(stSize.u32Width, u32AlignWidth) * //图片宽和对齐传参的整数倍 取较大者 (确保能够对齐) 即长宽向Align的倍数向上对齐----计算一帧像素个数=长*宽*系数 CEILING_2_POWER(stSize.u32Height,u32AlignWidth) * ((PIXEL_FORMAT_YUV_SEMIPLANAR_422 == enPixFmt)?2:1.5)); VB_PIC_HEADER_SIZE(stSize.u32Width, stSize.u32Height, enPixFmt, u32HeaderSize);//用宏计算大小头信息u32HeaderSize(像素格式enPixFmt决定计算方式 宽高用于计算) u32VbSize += u32HeaderSize;//图片像素空间+头信息空间=图片整体所需空间u32Vbsize 即一帧图像所需的内存 return u32VbSize; } ②赋值缓冲块数量stVbConf.astCommPool[2].u32BlkCnt=4(宏定义分支选择4) /****************************************** step 2: mpp system init. (按照指定顺序调用API进行MPP系统的初始化) ******************************************/ SAMPLE_COMM_SYS_Init(&stVbConf); {
HI_MPI_SYS_Exit();//保险措施,打扫卫生 HI_MPI_VB_Exit();//保险措施,打扫卫生 HI_MPI_VB_SetConf//BV(先)和SYS(后)有特定顺序 HI_MPI_VB_Init HI_MPI_SYS_SetConf HI_MPI_SYS_Init } /****************************************** step 3: start vi dev & chn to capture //设置VI配置结构体+开启VI(sensor写入+运行ISP) ******************************************/ stViConfig.enViMode = SENSOR_TYPE;//配置文件中定义的宏 摄像头信号AR0130 stViConfig.enRotate = ROTATE_NONE;//不旋转(枚举) stViConfig.enNorm = VIDEO_ENCODING_MODE_AUTO; stViConfig.enViChnSet = VI_CHN_SET_NORMAL;//既不镜像也不翻转 stViConfig.enWDRMode = WDR_MODE_NONE;//WDR宽动态 NONE不支持 SAMPLE_COMM_VI_StartVi(&stViConfig); IsSensorInput//根据传参结构体中的ViMode确定是不是通过sensor传入信号→确定下面分支2选1→SAMPLE_COMM_VI_StartBT656传统和SAMPLE_COMM_VI_StartIspAndVi常规sensor来源 ↓↓2选1 选择了普通摄像头输入↓↓ SAMPLE_COMM_VI_StartIspAndVi(&stViConfig); {
------------ step 1: mipi configure-------(将mipi宏+摄像头信号决定的结构体写入sensor驱动文件)------------------------------------------------------------------------------------------------- SAMPLE_COMM_VI_StartMIPI(step 1: mipi configure) //sensor与HI3518E芯片的接口有:MIPI,LVDS,DC SAMPLE_COMM_VI_SetMipiAttr(&stViConfig);//给sensor填充必要的参数-sensor初始化 fd = open("/dev/hi_mipi", O_RDWR);//打开sensor驱动文件/dev/hi_mipi 填充相关参数 //根据SENSOR_TYPE决定用哪一个结构体数据→pstcomboDevAttr ioctl(fd, HI_MIPI_SET_DEV_ATTR进行属性设置的命令传参, 摄像头型号决定的结构体)//ioctl驱动给应用开放的一个接口 ---------------step 2: configure sensor and ISP (include WDR mode).note: you can jump over this step, if you do not use Hi3516A interal isp. ------------- SAMPLE_COMM_ISP_Init //启动ISP图像处理 image signal process 将一些参数设置进去 sensor_register_callback /* 1. sensor register callback *///函数在sensor驱动里面//注册一个sensor回调 HI_MPI_AE_Register /* 2. register hisi ae lib *///注册isp内部的ae单元-自动曝光 HI_MPI_AWB_Register /* 3. register hisi awb lib *///自动白平衡 HI_MPI_AF_Register /* 4. register hisi af lib *///自动对焦 HI_MPI_ISP_MemInit /* 5. isp mem init *///给isp单元分配必要的内存 HI_MPI_ISP_SetWDRMode /* 6. isp set WDR mode *///设置宽动态相关属性 HI_MPI_ISP_SetPubAttr /* 7. isp set pub attributes *///根据SENSOR_TYPEswitch确定结构体的内容(颜色排序BAYER_RGGB、GRGB、BGGR 帧率 起始xy坐标 长宽)再写到IPS内部单元 HI_MPI_ISP_Init /* 8. isp init *///ISP初始化 -------------step 3: run isp thread note: you can jump over this step, if you do not use Hi3516A interal isp.-----ISP隶属于VI模块---------------------------- SAMPLE_COMM_ISP_Run pthread_create(&gs_IspPid, &attr, (void* (*)(void*))Test_ISP_Run, NULL) Test_ISP_Run HI_MPI_ISP_Run -------------------step 4 : config & start vicap dev---------------------------------------------------------------------------------------- SAMPLE_COMM_VI_StartDev //根据sensor型号填充stViDevAttr结构体(mamcpy结构体+手动赋值0,0,长宽即分辨率) HI_MPI_VI_SetDevAttr(ViDev, &stViDevAttr);//将stViDevAttr的值设置进去 这里吧参数结构体写进去(前面写好参数结构体) HI_MPI_ISP_GetWDRMode HI_MPI_VI_SetWDRAttr HI_MPI_VI_EnableDev//启动Dev -----------------Step 5: config & start vicap chn (max 1) -------------------------------------------------- SAMPLE_COMM_VI_StartChn( Step 5: config & start vicap chn (max 1) ) HI_MPI_VI_SetChnAttr HI_MPI_VI_SetRotate HI_MPI_VI_EnableChn//打开 } ------step45 都是设置一些参数再启动 只有一个dev一个chn------------------------------------------------------- /****************************************** step 4: start vpss and vi bind vpss处理图像VPSS(Vedio Pross Sub-System)and vi bind vpss //图像预处理,如去噪、去隔行等,再对各通道进行缩放、锐化等处理。最后输出多种不同分辨率的图像 ******************************************/ SAMPLE_COMM_SYS_GetPicSize//根据ensize得到具体的长宽stSize →用于设置VPSS的group以及他下面的chn012 设置VpssGrp = 0;以及stVpssGrpAttr内的参数:最大的长宽u32MaxW/H以及一些功能是否要开启进行参数设定以及最终输出格式YGB420 //VpssGrp = 0; //设置(填充)VPSS的数据结构体stVpssGrpAttr,比如长宽 输出格式YUV420 SAMPLE_COMM_VPSS_StartGroup(VpssGrp通道号, &stVpssGrpAttr);//开启VPSSgrp 传参grp编号及该grp的参数stVpssGrpAttr HI_MPI_VPSS_CreateGrp//硬件单元早就存在了 这里要创建group软件单元(是软件对硬件的封装,类似一个数据结构/类) 应该是进行实例化,绑定什么的 HI_MPI_VPSS_GetNRParam//这里得到后再重新写进去,没有干什么 HI_MPI_VPSS_StartGrp //下面group012分别执行一次 SAMPLE_COMM_VI_BindVpss {
定义一个结构体stViParam 后面进行填充及使用里面的数据(→↓填充4个数字) SAMPLE_COMM_VI_Mode2Param//设定grp有1个 其间隔数字编号为1 chn有stViParam.s32ViChnCnt=1个 其间隔数字编号为1-------------设定VI有一个chn 一个grp for(总计stViParam.s32ViChnCnt个vichn进行绑定) {
vichn的编号=0(此处唯一的一个计算出=0) 图像来源stSrcChn的模块编号enModID=VI(stSrcChn.enModId = HI_ID_VIU;)//Hi3518E内部每一个单元全部编号,以后用单元号来表明用的是哪一个单元 方便 stSrcChn.s32DevId = 0;用的是VI里面的DEV 0 stSrcChn.s32ChnId = ViChn;用的是VI里面的chn=0 图像去向stDestChn.enModId = HI_ID_VPSS;目标是VPSS模块 stDestChn.s32DevId = VpssGrp;对应VPSS中的grp stDestChn.s32ChnId = 0;//vpss的grp和VI绑定 而不是vpss的chn和vi绑定 对应grp=0里面的chn=0 HI_MPI_SYS_Bind(&stSrcChn, &stDestChn);将来源和去向进行绑定 在线模式的绑定 //是VOI的chn和VPSS的grp绑定 VPssGrp++//下一次循环 但是sample中vichn=1👉只进行一次循环 } }//将VI里面的chn012和VPSS的grp0绑定起来→故VPSS的grp0里面的chn012都能拿到来源 //即视频数据能够从VI的chn0留到VPSS的grp0中来了 后面进行是grp0下面的chn012进行设置(chn0直接放到grp0创建那里面去了) {
SAMPLE_COMM_SYS_GetPicSize VpssChn = 0; stVpssChnMode.enChnMode = VPSS_CHN_MODE_USER;//只能工作在uesr模式下 不能工作在auto模式 stVpssChnMode.bDouble = HI_FALSE; stVpssChnMode.enPixelFormat = PIXEL_FORMAT_YUV_SEMIPLANAR_420; stVpssChnMode.u32Width = stSize.u32Width;//长宽每次都要重新获取图像大小(第前5行) stVpssChnMode.u32Height = stSize.u32Height; stVpssChnMode.enCompressMode = COMPRESS_MODE_SEG;以??为单位进行压缩 memset(&stVpssChnAttr, 0, sizeof(stVpssChnAttr)); stVpssChnAttr.s32SrcFrameRate = -1;源目标帧率控制均为-1=不控制 stVpssChnAttr.s32DstFrameRate = -1; SAMPLE_COMM_VPSS_EnableChn(VpssGrp, VpssChn, &stVpssChnAttr, &stVpssChnMode, HI_NULL); {
if通道号
<最大物理通道数量4→使用物理通道(第下1行函数) 否则使用拓展通道(第下2行函数) hi_mpi_vpss_setchnattr hi_mpi_vpss_setextchnattr 设置 vpss 扩展通道属性,扩展通道的主要应用是进行二次缩放和帧率控制。 hi_mpi_vpss_setchnmode 通道工作模式。 user 模式主要用于通道一绑多,即一个通道绑定多个输出 源;在 模式下, 后端可以不用绑定输出源,而通过获取图像接口获取图 像。 auto 模式主要用于一般场景,对后端自适应。 hi_mpi_vpss_enablechn 启用 通道。 } }chn012设置完成 通道0不缩放 通道123缩小 通道4-11二次缩放(0-3物理通道 4-11拓展通道) ****************************************** step 5: start stream venc printf("\t c) cbr.\n");三选一中控制 v) vbr.\n"); f) fixqp\n"); printf("please input choose rc mode!\n"); chn012分别处理一趟 { vpssgrp="0;" vpsschn="0/1/2;" vencchn="0/1/2;" printf scan选择c v f 三选一rc模式 分3路进行编码ensize[012] sample_comm_venc_start(vencchn, enpayload[0](h.264),gs_ennorm, ensize[0](分辨率), enrcmode,u32profile); sample_comm_sys_getpicsize --------------------------------step 1: create venc channel switch 编码方式+前第12-10行三选一→填充数据 hi_mpi_venc_createchn 创建编码通道。 ----------------------------step 2: start recv pictures hi_mpi_venc_startrecvpic 开启编码通道接收输入图像。 sample_comm_venc_bindvpss(vencchn, vpssgrp, vpsschn); hi_mpi_sys_bind 数据源到数据接收者绑定接口。 6: process -- get stream, then save it to file. sample_comm_venc_startgetstream------开启线程sample_comm_venc_getvencstreamproc------------------funciton : from each channels and them gs_stpara.bthreadstart="HI_TRUE;下第2行开启的线程函数的标签数据" true则不停循环 false则停止循环 gs_stpara.s32cnt="s32Cnt;" return pthread_create(&gs_vencpid, 0, sample_comm_venc_getvencstreamproc, (hi_void*)&gs_stpara); ↓↓↓ sample_comm_venc_getvencstreamproc ←← 开了线程 -----------------------step check & prepare save-file venc-fd-------------------------------得到venc[012]的attr并创建文件,获得fd for (i="0;" i < s32chntotal传参="3;" i++) 遍历所有通道 hi_mpi_venc_getchnattr(vencchn, &stvencchnattr); 获取venc的编码通道chn属性。 enpayloadtype[i]="stVencChnAttr.stVeAttr.enType;//得到venc[i]的视频类型,H.264" sample_comm_venc_getfilepostfix(enpayloadtype[i], szfilepostfix); 根据文件类型entype获取文件名字后缀 如.h264 sprintf(aszfilename[i], "stream_chn%d%s", i, 打印完整文件名 pfile[i]="fopen(aszFileName[i]," "wb"); 以w写&b二进制方式打开文件 pfile[i+s32cnt]="fopen(aszFileName[i+s32Cnt]," 打开一个文件,下一行的fd文件的内容写入这个只读文件(即输出文件) vencfd[i]="HI_MPI_VENC_GetFd(i);//venc编码生成的流文件存放在mpp内部,外部没法下手,通过GetFd接口获取文件描述符v" 获取编码通道对应的设备文件句柄 即生成的文件(位于mpp通道内部)的接口→用于写成生成文件 #if ortp_enable 添加ortp库需要额外添加的代码 ***rtp init**** prtpsession="rtpInit(" local_host_ip ,8080); 见ortp库 src ortp.c文件 if (prtpsession="=NULL)" printf( "error rtpinit" ); exit(-1); 0; #endif --------------step streams of channel.)-------------------------------------- 【线程主while循环 当全局变量pstpara->
bThreadStart赋值false时终止( step 7: exit process中实现这个子线程终止)】 while循环获得strean并写入文件/保存到链表 //stStream.pstPack(内存指针 单包大小*包数)👉写入文件or放入fifo {
FD_ZERO&FD_SET先将012三个文件描述符放入文件描述符集合中 selsct超时2s阻塞//如果有文件*pfd可写→返回正数 超时0 错误<0 for (i = 0; i < s32ChnTotal; i++) {
if (FD_ISSET(VencFd[i], &read_fds))//查询(确定)到底是哪一个pfd可写了 {
//step 2.1 : query how many packs in one-frame stream.查询编码通道状态~~~~~~~~~~~~~~~~~~~~~~~~~~~ memset(&stStream, 0, sizeof(stStream)); s32Ret = HI_MPI_VENC_Query(i, &stStat);//查询编码通道状态→stStat //step 2.2 :suggest to check both u32CurPacks and u32LeftStreamFrames at the same time,for example:~~~~~~~~~~~~~~~~~~~~~~~~~~ // step 2.3 : malloc corresponding number of pack nodes.~~~~~~~~~~~~~~~~~~~~~~~~~~~ stStream.pstPack = (VENC_PACK_S*)malloc(sizeof(VENC_PACK_S) * stStat.u32CurPacks);//调用 malloc 分配 u32CurPacks 个包信息结构体 //step 2.4 : call mpi to get one-frame stream~~~~~~~~~~~~~~~~~~~~~~~~~~ stStream.u32PackCount = stStat.u32CurPacks; s32Ret = HI_MPI_VENC_GetStream(i, &stStream, HI_TRUE);//获取编码的码流&stStream 第三个参数码流超时时间设定为HI_TRUE? //step 2.5 : save frame to file~~~~~~~~~~~~~~~~~~~~~~~~~ SAMPLE_COMM_VENC_SaveStream(enPayLoadType[i], pFile[i], &stStream);//根据entype决定调用哪一个保存函数 SAMPLE_COMM_VENC_SaveH264(pFd, pstStream);//将编码码流&stStream保存至文件指针pFd {
for (i = 0; i < pstStream->u32PackCount; i++) {
#if ORTP_ENABLE rtpSend(pRtpSession,pstStream->pstPack[i].pu8Addr, pstStream->pstPack[i].u32Len);//ORTP库引入后直接调用rtpsnd函数发送出去 #else fwrite(pstStream->pstPack[i].pu8Addr+pstStream->pstPack[i].u32Offset,//原本海思sample是要将编码码流存放到pFD中去的 pstStream->pstPack[i].u32Len-pstStream->pstPack[i].u32Offset, 1, fpH264File); fflush(fpH264File); #endif } } //step 2.6 : release stream~~~~~~~~~~~~~~~~~~~~~~~~~释放编码码流&stStream HI_MPI_VENC_ReleaseStream(i, &stStream); //step 2.7 : free pack nodes~~~~~~~~~~~~~~~~~~~~~~ free(stStream.pstPack); stStream.pstPack = NULL; } } } --------------* step 3 : close save-file--------------------------------------关闭所有输出文件pfd for (i = 0; i < s32ChnTotal; i++) fclose(pFile[i]); } /****************************************** step 7: exit process ******************************************/ SAMPLE_COMM_VENC_StopGetStream {
if (HI_TRUE == gs_stPara.bThreadStart) {
gs_stPara.bThreadStart = HI_FALSE; pthread_join(gs_VencPid, 0); } return HI_SUCCESS;} } 海思的sensor驱动大部分工作在应用层,即应用层驱动(非ko形式存在),这样一来在对sensor改动时,就不需要重新编译内核了。 缺点是,不稳定,效率会比较低。纯应用层驱动是没办法操作硬件的,所以需要调用底层i2c驱动。 sensor_register_callback这个函数就在sensor驱动里面,这里会调用到kernel内的i2c驱动对sensor进行硬件操作。 这个函数就是对整个sensor初始化,内部封装了操作sensor所需要的函数,(ISP、AE(自动曝光)、AWB(白平衡))

转载地址:http://ayhrn.baihongyu.com/

你可能感兴趣的文章
.NET5都来了,你还不知道怎么部署到linux?最全部署方案,总有一款适合你
查看>>
我画着图,FluentAPI 她自己就生成了
查看>>
BenchmarkDotNet v0.12x新增功能
查看>>
使用 .NET 5 体验大数据和机器学习
查看>>
C# 中的数字分隔符 _
查看>>
使用 docker 构建分布式调用链跟踪框架skywalking
查看>>
Github Actions 中 Service Container 的使用
查看>>
别在.NET死忠粉面前黑.NET5,它未来可期!
查看>>
Winform 进度条弹窗和任务控制
查看>>
部署Dotnet Core应用到Kubernetes(二)
查看>>
持续交付二:为什么需要多个环境
查看>>
FreeSql接入CAP的实践
查看>>
浅析 EF Core 5 中的 DbContextFactory
查看>>
听说容器正在吃掉整个软件世界?
查看>>
真实经历:整整一年了,他是这样从程序员转型做产品经理的
查看>>
netcore一键部署到linux服务器以服务方式后台运行
查看>>
还在犹豫是否迁移.NET5?这几个项目已经上线了!
查看>>
被 C# 的 ThreadStatic 标记的静态变量,都存放在哪里了?
查看>>
ASP.NET Core使用HostingStartup增强启动操作
查看>>
结合控制台程序和K8S的CronJob完成定时任务
查看>>