sample的整体架构 1.sample整体结构: sample有很多例程,所以有很多例程main函数,common它是一个通用的主题函数,我们分析它sample_venc 2.基本结构为:venc中的main调用venc再次调用中间的功能函数common再次调用中间的功能函数mpp中的API,再调用HI3518E内部硬件单元 3.先理解几个基本概念: H.264 H.265 MJPEG 视频编码标准 1080P、720P、VGA(640480) D1(720576) 视频分辨率(清晰度) fps(frame per second) 帧率
计算机中的图像素格式 RGB: 1.RGB表示颜色的方式 (1)RGB有RGB565和RGB888,ARGB等多种子分类 (2)RGB本质:将色度分解为R、G、B然后记录三个部分的亮度数据 (3)RGB优点:数字表达方便,广泛应用于数字彩色显示器、计算机编程等领域。 (4)RGB缺点:与传统灰度图兼容性差,表达颜色效率低
2.rawRGB图像采集过程 (1)图像采集过程:成像物体中的光反射->镜头汇聚->Sensor光电转换->ADC为rawRGB (2)sensor每个像素只收集特定颜色的光强,因此sensor每个像素只能是R或RG或B (3)rawRGB和RGB它们都用来描述图像。图像采集时RGB是由rawRGB计算而来的 (4)由于图像颜色本身有一定的连贯性,人眼不理想,图像采集和再显示给人三个要素:分辨率、pitch、观看距离 (5)如果是视频,质量要加帧率framerate (6)图像表达、压缩、修复等相关技术发生在rawRGB进来后的各个环节 (7).RGB就是由rawRGB转换后,做了一个平均操作,这个像素缺失的两个颜色值被平均补充到周围。
YUV (1)YUV是一种色彩空间,Y表示亮度,U和V表示色度。只有Y是黑白图像,加上UV是彩色图像。YUV的一个好处就是让彩色系统和传统黑白系统很好的兼容。 (2)YUV和RGB相似之处在于:都是用来表达颜色的数学方法;区别在于:对颜色的描述思路和方法不同。RGB将一种颜色分解成三种纯色的亮度组合,YUV将一种颜色分解为亮度和两种颜色的组合。 (3)RGB和YUV数学方法可以相互转换,是典型的浮点操作过程。 RGB和YUV是可以互相转换,是一个浮点数运算过程。 R = Y 1.4075 * (V-128); G = Y - 0.3455 * (U-128) - 0.7169*(V-128); B = Y 1.779 * (U-128);
Y = 0.299*R 0.587*G 0.114*B; U = (B-Y)/1.772; V = (R-Y)/1.402; (U~(-128-127)) 1 2 3 (4)YUV和YCbCr几乎可以看作一个概念 (5).YUV分为packed和planar两种: 前者将YUV重量存储在同一数组中;后者分别存储三个数组YUV三个分量,就像是一个三维平面一样。 见此博客,感谢分享:https://blog.csdn.net/zmjames2000/article/details/88523010: 以下是大佬的博客摘录:如有侵权,请联系作者删除。 1.现在的YUV计算机领域通常用于表示YCbCr编码文件。所以你可以粗略地看YUV为YCbCr。 2.熟悉我们RGB类似,YUV它也是一种颜色编码方法,主要用于电视系统和模拟视频领域它将亮度信息(Y)与色彩信息(UV)分离,没有UV信息可以显示完整的图像,只是黑白的,解决了彩色电视和黑白电视的兼容性。并且,YUV不像RGB这需要三个独立的视频信号同时传输,因此使用YUV传输占用的频宽很少 3.YUV码流的存储格式实际上与采样方式密切相关,主流采样方式有三种,YUV4:4:4,YUV4:2:2,YUV4:2:0: 记住以下段落,然后提取每个像素YUV将使用重量。
YUV 4:4:4采样,每个Y对应一组UV分量。 YUV 4:2:2采样,每两个Y共用一组UV分量。 YUV 4:2:0采样,每四个Y共用一组UV分量。 4.存储格式: (1)YUVY 存储格式 (属于YUV422存储格式 ),YUYV为YUV422采样的存储格式之一,相邻的两个Y共用相邻的两个Cb、Cr,对像素点进行分析Y’00、Y’01 而言,其Cb、Cr的值均为 Cb00、Cr00,其他像素点YUV依次类推取值 (2).UYVY 存储格式 (属于YUV422存储格式 ),UYVY格式也是YUV422采样的存储格式之一只是与YUYV不同的是UV的排列顺序不一样而已,还原其每个像素点的YUV值的方法和上面一样 (3).YUV422P存储格式 (属于YUV422存储格式 ),YUV422P也属于YUV一种422,是一种Plane模式,即平面模式,不是YUV数据交错存储,但首先存储所有Y重量,然后存储所有Y重量U(Cb)重量,最后存储所有V(Cr)分量,每一个像素点YUV还遵循了提取值的方法YUV最基本的422格式提取方法,即两个Y共用一个UV。比如像素点Y’00、Y’01 而言,其Cb、Cr的值均为 Cb00、Cr00。
YV12,YU12格式(属于YUV420):YU12和YV12属于YUV420格式也是一种Plane模式,将Y、U、V分别打包,依次存放。每一个像素点YUV遵循数据提取YUV420格式的提取方法,即4个Y分量共用一组UV。,Y’00、Y’01、Y’10、Y’11共用Cr00、Cb其他的依次类推 NV12、NV21(属于YUV420):NV12和NV21属于YUV420格式,是一种two-plane模式,即Y和UV分为两个Plane,但是UV(CbCr)交错存储,而不是三种plane。其提取方法与上一种相似,即Y’00、Y’01、Y’10、Y’11共用Cr00、Cb00. YUV文件大小计算 以720×488大小图象YUV420 planar例如,其存储格式为: 共大小为(720×480×3>>1)字节
分为三部分:Y,U和V
Y分量: (720×480)个字节 U(Cb)分量:(720×480>>2)个字节 V(Cr)分量:(720×480>>2)个字节
三部分内部优先存储,三部分之间存储Y,U,V 顺序存储。 即 0--720×Y分量值为480字节, (YUYV 720×480--720×480×5/4字节是U分量 720×480×5/4 --720×480×3/2字节为V分量。 关于YUV到目前为止,如有相关问题,请参考博客: https://blog.csdn.net/zmjames2000/article/details/88523010 https://blog.csdn.net/taotongning/article/details/84882431
海思MPP学习功能模块和视频缓存池 1.PAYLOAD_TYPE_E,在传输视频时,我们称之为视频类型PAYLOAD_TYPE,手册解释了定义音视频净荷类型枚举。枚举的最后一类PT_BUTT不能使用。 PAYLOAD_TYPE_E enPayLoad[3]= {PT_H264, PT_H264,PT_H264}; 这是一个枚举数组,数组数为3 2.PIC_SIZE_E,我在码流的分辨率mpp它的定义在手册中没有找到,它的定义在网上找到了 3.VB_CONF_S:视频缓存池属性结构体。 视频缓存池: 什么是视频缓冲池? (1)视频的本质是多帧图片,图片的本质是RGB或rawRGB数据占用连续内存 (2)视频切割、缩放、修正等各种操作,本质上是计算内存中的数据 (3)视频缓存池(VB, video buffer)它是一个大的、合理划分和管理的内存,用于临时存储视频数据和操作场地 (4)公共视频缓存池的公共二字可以理解为全局变量,即每个模块都可以访问的内存 (5)视频缓存块似乎在每个模块之间流通,但实际上没有内存复制,而是指针在传输 (6)视频缓存池内存由视频缓存池内存MPP维护,当系统启动时,我们将整个系统启动SDRAM分为两部分:系统部分(由系统部分)linux kernel维护管理)和mpp部分(由mpp维护管理系统) (7)缓存池需要几个,每个都包含几个缓存块,用户程序可以设置每个缓存块的参数,然后调用MPP的相应API来向MPP申请分配。 4.SAMPLE_COMM_SYS_Init 这里进行mpp模块的初始化 HI_MPI_VB_SetConf: 设置视频缓冲池的属性,传输pstVbConf,该指针指向的内容在以后赋值。 HI_MPI_VB_Init:使用设置的属性来分配内存和其他初始工作 顺序:先HI_MPI_VB_SetConf()再HI_MPI_VB_Init()相当于先点菜,厨师再做饭 5.typedef struct hiVB_CONF_S {undefined HI_U32 u32MaxPoolCnt; 整个系统中可容纳的缓存池数量 256 Struct hiVB_CPOOL_S {undefined HI_U32 u32BlkSize; HI_U32 u32BlkCnt; HI_CHAR acMmzName[MAX_MMZ_NAME_LEN]; 公共缓冲池的名称 }astCommPool[VB_MAX_COMM_POOLS]; } VB_CONF_S;
/* 最大的视频缓存池数量 / #define VB_MAX_POOLS 256 / 最大的公共视频缓存池数量 / #define VB_MAX_COMM_POOLS 16 / MMZ名字长度 / #define MAX_MMZ_NAME_LEN 16 / VB辅助信息JPEG DCF MASK */ #define VB_SUPPLEMENT_JPEG_MASK 0x1 6. step 1: init sys variable //mpp系统变量的初始化是填充这些变量 step 2: mpp sysem init. 视频缓冲出相关的初始化 step 3: start vi dev & chn to capture:图像的采集,启动VI部分的dev(设备)和chn(通道) step 4: start vpss and vi bind vpss:VI之后就会传给VPSS,传给VPSS之前先要把vpss启动起来, vi bind vpss:VI和VPSS是两个独立的模块.通过bind这操作,MPP系统提供的一个API(bind api), 通过调用bind api就可以把VI和VPSS这两个模块绑定起来。绑定起来之后有什么作用呢? VI这边当它采集到一帧图像丢到一个VB(VIDEO BUFFER视频缓冲池)里面的缓存块里面之后,这个 缓存块会自动传送到VPSS里面去。 setp 5:set 一些信息 step 6: start stream venc:VPSS处理完之后就会到VENC里面去。这一步就开始启动VENC单元,如果你要添加水印信息就在这一步去,添加。H.264编码也在这一步去研究。编码完之后呢就得到H.264的一个码流了。 step 7: stream venc process – get stream, then save it to file. 我们得到H.264码流之后怎么处理呢? (1)可以把这个码流打包成一个MP4存储到硬盘里面去,这就是录像。 (2)也可以分包,分成一个一个视频包通过RTSP传出去。 (3)也可以像sample一样直接作为一个裸流直接丢到流文件里面去。 那么这种流文件必须通过像VLC这种能够解析裸流文件的播放器来观看。 所以说编码模块只负责输出一段H.264的裸流,这个裸流要怎么办是你的事。 step 8: 如果你不想录像了,连续按两次enter就可以了 exit process
程序详细分析: 1.SAMPLE_COMM_VI_GetSizeBySensor ,然后PIC_HD720 == enSize[0] enSize[0] = PIC_HD720 enSize[1] = PIC_VGA; enSize[2] = PIC_QVGA; s32ChnNum = 3; 有3路码流,同一个sensor输出3路不同分辨率的码流 一路主码流(原始的没有裁剪的,全功能版本),另外两路(在主码流的基础上经过裁剪缩放等操作简化而来,阉割版本) 为子码流,客户用手机看的话就用子码流看就行了,没必要看主码流,还可以省点网络带宽,还比较流畅。 2.SAMPLE_COMM_SYS_CalcPicVbBlkSize 传入分辨率,得到公共缓冲出的size,这里三个通道,我们是用了三个公共缓冲池,不同的码流分辨率不一样,对应的blocksize的大小也就不一样, 函数里面需要注意的: //SAMPLE_PRT(“w:%d, u32AlignWidth:%d\n”, CEILING_2_POWER(stSize.u32Width,u32AlignWidth), u32AlignWidth); u32VbSize = (CEILING_2_POWER(stSize.u32Width, u32AlignWidth) * CEILING_2_POWER(stSize.u32Height, u32AlignWidth) * ((PIXEL_FORMAT_YUV_SEMIPLANAR_422 == enPixFmt) ? 2 : 1.5)); //YUV422 4个像素占4+2+2=8个字节,那么每个像素平均占8/4=2个字节 u32VbSize=宽高每个像素的字节数 //YUV420 4个像素占4+1+1=6个字节,那么每个像素平均占6/4=1.5个字节 3.SAMPLE_COMM_SYS_Init : HI_MPI_SYS_Exit(); //注意顺序,释放的时候是SYS在前,VB在后的 HI_MPI_VB_Exit(); //怕你之前的VB和SYS没处理干净,保险起见,打扫卫生的 4.//只要把VB(缓冲池搞好了),SYS里面都是自动化的,海思封装的特别好,我们要操心的是VB的参数pstVbconf
常用Sensor的接口有三种:MIPI LVDS DC SAMPLE_COMM_VI_StartVi 调用:sensor_register_callback我们搜索不到,sensor_register_callback()在哪里定义的,其实它在sensor驱动里面定义的,Z:\IPC-HI3518EV200\Hi3518E_SDK_V1.0.3.0\mpp\component\isp\sensor\ar0130\ar0130_cmos.c WDR :宽动态范围,很亮和很暗都可以看得很清楚的一种技术。硬件本身要支持才行。 动态范围:在图像里面能看到的最亮的和最暗的一个比例。 ISP:image signal process图像信号处理,3518E内部的一个硬件单元,这个硬件单元就是用来做ISP运算的, 这个模块在MPP里面被封装成了API,SAMPLE_COMM_ISP_Init()函数就是启动ISP这个硬件单元,把这个线程运行起来,ISP主要是用来处理图像,比如典型的3A,如果你自己添加了一个专用的ISP芯片,那么你可以不用 3518E内部的这个ISP模块(不启动它)。 4.HI3518E内部ISP单元是隶属于VI模块的,VI模块就包含3大部分: 第一部分是和sensor对接的部分;第二部分就是ISP,第三部分就是VI dev和channel dev就是用来采集图像的一个硬件单元,一个dev可能有好几个通道(分支),可以分成好几条道路(分支),然后 来进行不同的裁剪,并且每一个通道和后面进行绑定,dev就是一个整体管道,我这个管道有一个入口,从sensor这 边进去的,但是有3个甚至10出口(通道),每一个出口连接了后端的一个流程, Hi3519v101可以接两路sensor自然就有两个dev和两个channel VPSS部分: VPSS(Video Process Sub-System) 对一幅图像进行统一处理,不是针对某一块,去噪、去隔行(把隔行扫描的转成逐行扫描的,之前有的sensor是隔行扫描的,现在的sensor一般都是逐行扫描的了) 然后再对各通道分别进行缩放(我们出来的图像有1080p,有720p,VGA的等就是因为进行了缩放,是在VPSS这里来处理的) 最后输出多种不同分辨率的图像(从这里可以看出一进多出的,VI是一路进来,VPSS是多路输出的)。 FRC(Frame Rate Control) Crop(裁剪) NR(Noise Reduce) LDC(Lens Distortion Correction) Rotate Cover/Overlay Scale(缩放) Mirror/Flip FishEye(鱼眼)
海思媒体处理平台架构 1.视频输入 VI 2.视频处理 VPSS 3.视频编码 VENC 4.视频解码 VDEC 5.视频输出 VO 6.视频侦测分析 VDA 7.音频输入 AI 8.音频输出 AO 9.音频编码 AENC 10.音频解码 ADEC 11.区域管理 REGION
sample源代码分析: hisi-sample_venc
1.PAYLOAD_TYPE_E:定义音视频净荷类型枚举,其实就是:音视频的类型 PAYLOAD_TYPE_E 这个枚举太复杂了,数据手册89面,
2.PIC_SIZE_E:图片分辨率的,也就是定义的视频的每一帧的分辨率 typedef enum hiPIC_SIZE_E {undefined PIC_QCIF = 0, //176144 PIC_CIF, //352288 PIC_2CIF, PIC_HD1, PIC_D1, //704*576 PIC_960H,
PIC_QVGA, /* 320 * 240 */ PIC_VGA, /* 640 * 480 */ PIC_XGA, /* 1024 * 768 */ PIC_SXGA, /* 1400 * 1050 */ PIC_UXGA, /* 1600 * 1200 */ PIC_QXGA, /* 2048 * 1536 */
PIC_WVGA, /* 854 * 480 */ PIC_WSXGA, /* 1680 * 1050 */ PIC_WUXGA, /* 1920 * 1200 */ PIC_WQXGA, /* 2560 * 1600 */
PIC_HD720, /* 1280 * 720 */ PIC_HD1080, /* 1920 * 1080 */ PIC_2304x1296, /* 3M:2304 * 1296 */ PIC_2592x1520, /* 4M:2592 * 1520 */ PIC_5M, /* 2592 * 1944 */ PIC_UHD4K, /* 3840 * 2160 */ PIC_12M, /* 4000 * 3000 */
PIC_BUTT 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 } PIC_SIZE_E;
3.VB_CONF_S:定义视频缓存池属性结构体 typedef struct hiVB_CONF_S {undefined /* max count of pools, (0,VB_MAX_POOLS] 整个系统中可容纳的缓存池个数*/ HI_U32 u32MaxPoolCnt;
公共缓存池属性结构体,成员包括公共缓存池中每个缓存块的 1 大小(以 byte 为单位)和缓存块的个数及此缓存池在所在的 MMZ 的名字。 Struct hiVB_CPOOL_S {undefined HI_U32 u32BlkSize; HI_U32 u32BlkCnt; HI_CHAR acMmzName[MAX_MMZ_NAME_LEN]; }astCommPool[VB_MAX_COMM_POOLS]; } VB_CONF_S;
4.SAMPLE_VI_CONFIG_S: 设置VI模块属性的结构体 typedef struct sample_vi_config_s {undefined SAMPLE_VI_MODE_E enViMode; VIDEO_NORM_E enNorm; /*DC: VIDEO_ENCODING_MODE_AUTO / ROTATE_E enRotate; SAMPLE_VI_CHN_SET_E enViChnSet; WDR_MODE_E enWDRMode; }SAMPLE_VI_CONFIG_S; 下面对此结构体里面的每一个元素进行说明: (1).SAMPLE_VI_MODE_E:相机的型号和分辨率等信息 typedef enum sample_vi_mode_e { APTINA_AR0130_DC_720P_30FPS = 0, APTINA_9M034_DC_720P_30FPS, APTINA_AR0230_HISPI_1080P_30FPS, SONY_IMX222_DC_1080P_30FPS, SONY_IMX222_DC_720P_30FPS, PANASONIC_MN34222_MIPI_1080P_30FPS, OMNIVISION_OV9712_DC_720P_30FPS, OMNIVISION_OV9732_DC_720P_30FPS, OMNIVISION_OV9750_MIPI_720P_30FPS, OMNIVISION_OV9752_MIPI_720P_30FPS, OMNIVISION_OV2718_MIPI_1080P_25FPS, SAMPLE_VI_MODE_1_D1, SAMPLE_VI_MODE_BT1120_720P, SAMPLE_VI_MODE_BT1120_1080P, }SAMPLE_VI_MODE_E; (2).VIDEO_NORM_E: 定义视频输入制式类型 typedef enum hiVIDEO_NORM_E { VIDEO_ENCODING_MODE_PAL=0, VIDEO_ENCODING_MODE_NTSC, VIDEO_ENCODING_MODE_AUTO, VIDEO_ENCODING_MODE_BUTT, } VIDEO_NORM_E (3).ROTATE_E: 旋转枚举 typedef enum hiROTATE_E { ROTATE_NONE = 0, ROTATE_90 = 1, ROTATE_180 = 2, ROTATE_270 = 3, ROTATE_BUTT } ROTATE_E; (4).SAMPLE_VI_CHN_SET_E: 图像镜像,翻转在这里设置, 水平翻转就是关于y对称, 垂直翻转就是关于x对称的操作 typedef enum sample_vi_chn_set_e { VI_CHN_SET_NORMAL = 0, / mirror, flip close / VI_CHN_SET_MIRROR, / open MIRROR / VI_CHN_SET_FLIP, / open filp / VI_CHN_SET_FLIP_MIRROR / mirror, flip */ }SAMPLE_VI_CHN_SET_E; (5).WDR_MODE_E: WDR 模式枚举 宽动态,这种技术需要sensor硬件支持。动态范围:在一幅图像中,能看到最亮与最暗的比例
source code: step 1: init sys variable 1.memset(&stVbConf,0,sizeof(VB_CONF_S)); 视频缓冲池属性结构体清0 2.SAMPLE_COMM_VI_GetSizeBySensor(&enSize[0]); 执行后: enSize[0] = PIC_HD720 3.程序会执行到: else if (PIC_HD720 == enSize[0]) {undefined enSize[1] = PIC_VGA; enSize[2] = PIC_QVGA; s32ChnNum = 3; } 执行完了之后的结果就是:enSize[0]=PIC_HD720 enSize[1] = PIC_VGA enSize[2] = PIC_QVGA s32ChnNum = 3 4.printf(“s32ChnNum = %d\n”,s32ChnNum); 这里应该打印3 5.stVbConf.u32MaxPoolCnt = 128; 系统中视频缓冲出个数给的128,海思推荐 6.if(s32ChnNum >= 1) 会执行:SAMPLE_COMM_SYS_CalcPicVbBlkSize 调用:SAMPLE_COMM_SYS_GetPicSize, 得到stSize.width = 1280 stSize.heght = 720 CEILING_2_POWER: 这是一个宏,返回:参数2的倍数,且该倍数是最小的大于参数1的 解析: CEILING_2_POWER(1280, 64) * CEILING_2_POWER(720, 64) * 1.5 =1344(1280???) * 738 * 1.5 = 1487808 bytes VB_PIC_HEADER_SIZE: 宏,得到头像压缩头的大小 u32VbSize += u32HeaderSize; 一帧头像的带下+头像压缩头的大小 所以这里返回的公共缓冲池的大小就是:一帧头像的带下+头像压缩头的大小 u32BlkSize = 一帧头像的带下+头像压缩头的大小 u32BlkCnt = 4 7.if(s32ChnNum >= 2) 和 if(s32ChnNum >= 3)按照同样的道理去分析
step 2: mpp system init 1.SAMPLE_COMM_SYS_Init 2.HI_MPI_SYS_Exit 去初始化(注销) MPP 系统。包括音频输入输出、视频输入输出、视频编码、视频叠加区域、 视频侦测分析通道等都会被销毁或者禁用 3.去初始化(注销) MPP 视频缓存池 4.HI_MPI_VB_SetConf:设置 MPP 视频缓存池属性, 之前有对其参数进行赋值 5.HI_MPI_VB_Init:初始化 MPP 视频缓存池 6.stSysConf.u32AlignWidth = SAMPLE_SYS_ALIGN_WIDTH; 整个系统中使用图像的 stride 字节对齐数。这里赋值为64 7.HI_MPI_SYS_SetConf:配置系统控制参数 8.HI_MPI_SYS_Init:初始化 MPP 系统。包括音频输入输出、视频输入输出、视频编码、视频叠加区域、视 频侦测分析等都会被初始化 9.注意:初始化的时候,先初始化VB模块,再初始化系统。 注销的时候,先注销系统,再VB模块
step 3: start vi dev & chn to capture
stViConfig.enViMode = SENSOR_TYPE; sensor的类型 stViConfig.enRotate = ROTATE_NONE; 图像出来要旋转的话在这里设置,这里设置的不旋转
//图像制式的标准有PAL和NTSC两种,对于这种数字接口的sensor来说,不重要。 stViConfig.enNorm = VIDEO_ENCODING_MODE_AUTO; //图像镜像,翻转在这里设置 stViConfig.enViChnSet = VI_CHN_SET_NORMAL; //宽动态,这种技术需要sensor硬件支持。动态范围:在一幅图像中,能看到最亮与最暗的比例 stViConfig.enWDRMode = WDR_MODE_NONE; 2.SAMPLE_COMM_VI_StartVi IsSensorInput(enViMode):返回TRUE 所以执行:SAMPLE_COMM_VI_StartIspAndVi 3.SAMPLE_COMM_VI_StartIspAndVi SAMPLE_COMM_VI_StartMIPI SAMPLE_COMM_VI_SetMipiAttr 4.SAMPLE_COMM_VI_SetMipiAttr: sensor驱动装载完后会生成/dev/hi_mipi这样的一个设备文件,打开,打开后准备好相应的参数,不同的sensor填充的参数是不一样的 fd = open("/dev/hi_mipi", O_RDWR); 我们匹配下面的:APTINA_AR0130_DC_720P_30FPS if ( (pstViConfig->enViMode == APTINA_9M034_DC_720P_30FPS) || (pstViConfig->enViMode == APTINA_AR0130_DC_720P_30FPS) || (pstViConfig->enViMode == SONY_IMX222_DC_1080P_30FPS) || (pstViConfig->enViMode == SONY_IMX222_DC_720P_30FPS) || (pstViConfig->enViMode == OMNIVISION_OV9712_DC_720P_30FPS) || (pstViConfig->enViMode == OMNIVISION_OV9732_DC_720P_30FPS) ) {undefined pstcomboDevAttr = &MIPI_CMOS3V3_ATTR; } combo_dev_attr_t MIPI_CMOS3V3_ATTR = {undefined /* input mode */ .input_mode = INPUT_MODE_CMOS_33V, {undefined
} }; 5.调用: if (ioctl(fd, HI_MIPI_SET_DEV_ATTR, pstcomboDevAttr)) 写到摄像头驱动中去 HI_MIPI_SET_DEV_ATTR 是3518E给sensor做属性设置的命令,传参的标准都是海思定义好的一个结构体, 但是这个结构体在不用的sensor里面是不一样的, ioctl是驱动对应用开放的接口 SAMPLE_COMM_VI_SetMipiAttr功能就是在应用层对sensor做一个初始化 6.SAMPLE_COMM_ISP_Init: #ISP image signal process 是一种技术,就是做一下数学运算,3518e内部的一个硬件单元 #这个ISP硬件单元就是专门用来做ISP的,这个模块在MPP里面被封装成了API #SAMPLE_COMM_ISP_Init功能就是把这个ISP的线程运行起来 #ISP里面处理比如3A #也可以专门加一个ISP芯片,不用3518e里面这个ISP模块单元,不启动就行,默认就是关闭的 (1).sensor_register_callback() IQ调试相关, 在:E:\hisi-ipc\官方资源\Hi3518E V200R001C01SPC030\01.software\board\Hi3518E_SDK_V1.0.3.0\package\mpp\component\isp\sensor\ar0130/ar0130_cmos.c (2).HI_MPI_AE_Register 自动曝光相关 (3).HI_MPI_AWB_Register 自动白平衡 (4).HI_MPI_AF_Register 自动对焦 注册一下3A单元 (5).HI_MPI_ISP_MemInit 给ISP单元分配内存,传这个参数IspDev就可以了,内部自动会去分配内存 (6).HI_MPI_ISP_SetWDRMode 设置宽动态 (7).stPubAttr.enBayer = BAYER_GRBG; RGB原始信号的排列序列,查sensor的datasheet (8).HI_MPI_ISP_SetPubAttr 设置isp相关的属性 (9).HI_MPI_ISP_Init ISP初始化以后ISP就已经准备好了 (10).#3518E内部ISP单元是隶属于VI模块的 #VI模块就包括3大部分,第一部分是与sensor对接的部分,第二部分是就是ISP,第三部分就是VI dev和channel部分。 #VI dev是采集图像的硬件单元,channel是与后端连接的多个出口,也是一个硬件单元。每一个出口就是一个channel。 11.SAMPLE_COMM_ISP_Run Test_ISP_Run HI_MPI_ISP_Run 12.SAMPLE_COMM_VI_StartDev HI_MPI_VI_SetDevAttr HI_MPI_ISP_GetWDRMode: 获取WDR模式HI_MPI_ISP_GetWDRMode HI_MPI_VI_EnableDev 使能dev
SAMPLE_COMM_VI_StartDev 和 SAMPLE_COMM_VI_StartChn函数都只执行了一次
step 4: start vpss and vi bind vpss VPSS(Video Process Sub-System), #支持对一幅图像进行统一预处理,如去噪,去隔行等。 #什么是去隔行,就是把隔行转成逐行,因为以前的技术有隔行扫描,不过现在的大多数的sensor都是逐行扫描的了。 #然后再对各通道分别进行缩放,锐化等处理,最后输出多种不同分辨率的图像。 #从这句话可以看出,VI到VPSS是单通道输入,多通道输出是从VPSS这里出去的。扩展通道是从VPSS这里出来的。 #具体的功能包括FRC(Frame Rate Control)、Crop、NR(Noise Reduce)、LDC(Lens Distortion Correction)#Rotate、Cover/Overlay、Scale、Mirror/Flip、FishEye #通过调用SYS模块的绑定接口这句话的意思,SYS模块就是MPP,绑定接口就是s32Ret = HI_MPI_SYS_Bind(&stSrcChn, &stDestChn); #stSrcChn源头通道是VI,目的通道stDestChn是VPSS的 #group是VPSS硬件在软件上的映射,如果只有一个group那就是VPSS硬件的1:1的映射,不用复用了 #group里面分了好多个channel; #VI里面的channel和group里面的channel是不同的东西 #是VI这里的channel去绑定VPSS的gruop的 1.SAMPLE_COMM_SYS_GetPicSize 2.SAMPLE_COMM_VPSS_StartGroup HI_MPI_VPSS_CreateGrp 创建一个 VPSS GROUP, 在线模式,第一个值是0 HI_MPI_VPSS_GetNRParam 获取 VPSS 3DNR 参数 HI_MPI_VPSS_SetNRParam 设置 VPSS 3DNR 参数 HI_MPI_VPSS_StartGrp 启用 VPSS GROUP 3.SAMPLE_COMM_VI_BindVpss SAMPLE_COMM_VI_Mode2Param 得到的stViParam的每一个值都是1 绑定了VI的dev0的channel0,作为输入源, VpssGrp0作为目标 进行绑定,所以把dev0的channel0和VpssGrp0进行了绑定 4.COMPRESS_MODE_SEG 段压缩的视频格式,按照 256 bytes 为一段进行压缩。 VPSS_CHN_MODE_USER 用户设置模式 5.VPSS_CHN_ATTR_S 定义 VPSS 物理通道的属性 stVpssChnAttr.s32SrcFrameRate = -1; 源帧率 stVpssChnAttr.s32DstFrameRate = -1; 目标帧率 6.VPSS_CHN_MODE_S 定义 VPSS CHN 工作模式结构 stVpssChnMode.enChnMode = VPSS_CHN_MODE_USER; VpssChnal的工作模式为用户设置模式 stVpssChnMode.enPixelFormat = PIXEL_FORMAT_YUV_SEMIPLANAR_420; 目标图像像素格式 stVpssChnMode.enCompressMode = COMPRESS_MODE_SEG; 目标图像压缩格式 7.VPSS_GRP_ATTR_S 定义 VPSS GROUP 静态属性 8. HI_MPI_VPSS_EnableChn 启用Vpss通道, HI_MPI_VPSS_SetChnAttr 设置VPSS通道属性
设置 VPSS 通道工作模式。 USER 模式主要用于通道一绑多,即一个通道绑定多个输出 1 源;在 USER 模式下, VPSS 后端可以不用绑定输出源,而通过获取图像接口获取图 像。 AUTO 模式主要用于一般场景,对后端自适应 HI_MPI_VPSS_SetChnMode
9.SAMPLE_COMM_VI_Mode2Param 把 stViParam参数中的所有的成员都设置为1了 10.HI_MPI_SYS_Bind 这里把VI的第0通道和VPSS进行了绑定 11. HI_MPI_VPSS_SetChnAttr 设置VPSS通道属性 HI_MPI_VPSS_SetExtChnAttr 设置 VPSS扩展通道属性,扩展通道的主要应用是进行二次缩放和帧率控制 HI_MPI_VPSS_SetChnMode 设置 VPSS 通道工作模式 HI_MPI_VPSS_EnableChn 启用VPSS通道 12.注意: 第227行的SAMPLE_COMM_VI_BindVpss函数进行的是VI的chan0和VPSS的绑定 13.先绑定了VI 的chan0和VPSS之后,然后对CPSS的chan进行属性和模式的设置 14.通过SYS模块绑定,VPSS前面可以和VI模块绑定,后面可以和VO/VENC/IVE等模块进行绑定,其中 前者是VPSS的输入源,后者是VPSS的接受者。每一个GROUP仅可与一个输入源绑定,GROUP的 物理通道有两种工作模式,AUTO和USER两种,两种模式之间可以动态切换,默认的工作模式的AUTO ,此模式下各个通道仅可与一个接收者绑定,若想使用 USER 模式,则需调用 MPI 接口进行设置, 同时指定所需图像的大小和格式,此模式下各通道可与多个接收者绑定。需要特别注 意的是, USER 模式主要用于对同一通道图像进行多路编码的场景,此模式下播放控 制不生效,因此预览和回放场景下不建议使用 USER 模式。HI3518EV200仅支持 USER 工作模式 15.接着使能了:VPSS的通道0 通道1 通道2
step 5: start stream venc 在海思3531有CBR FIXQP 和VBR这三种编码方式,CBR是恒定比特率控制,码率好像是控制不了的,而VBR是可控比特率,码率是可以调节的主要是在SAMPLE_COMM_VENC_Start这个函数里,的stH264Vbr.u32MinQp,stH264Vbr.u32MaxQp。这里stH264Vbr.u32MinQp的值越小,码流越大,图像越清晰,保存下来的文件也越大,它的最小值是0,最大值是50。而FIXQP这种我也不是很清楚
cbr:CBR(Constant Bit Rate)是以恒定比特率方式进行编码,有Motion发生时,由于码率恒定,只能通过增大QP来减少码字大小,图像质量变差,当场景静止时,图像质量又变好,因此图像质量不稳定。这种算法优先考虑码率(带宽)。 这个算法也算是码率控制最难的算法了,因为无法确定何时有motion发生,假设在码率统计窗口的最后一帧发生motion,就会导致该帧size变大,从而导致统计的码率大于预设的码率,也就是说每秒统计一次码率是不合理的,应该是统计一段时间内的平均码率,这样会更合理一些
vbr: VBR(Variable Bit Rate)动态比特率,其码率可以随着图像的复杂程度的不同而变化,因此其编码效率比较高,Motion发生时,马赛克很少。码率控制算法根据图像内容确定使用的比特率,图像内容比较简单则分配较少的码率(似乎码字更合适),图像内容复杂则分配较多的码字,这样既保证了质量,又兼顾带宽限制。这种算法优先考虑图像质量
CVBR(Constrained VariableBit Rate),这样翻译成中文就比较难听了,它是VBR的一种改进方法。但是Constrained又体现在什么地方呢?这种算法对应的Maximum bitRate恒定或者Average BitRate恒定。这种方法的兼顾了以上两种方法的优点:在图像内容静止时,节省带宽,有Motion发生时,利用前期节省的带宽来尽可能的提高图像质量,达到同时兼顾带宽和图像质量的目的。这种方法通常会让用户输入最大码率和最小码率,静止时,码率稳定在最小码率,运动时,码率大于最小码率,但是又不超过最大码率。比较理想的模型如下:
ABR (Average Bit Rate) 在一定的时间范围内达到设定的码率,但是局部码率峰值可以超过设定的码率,平均码率恒定。
fixQp: 网上并没有查到相关的信息
1.SAMPLE_COMM_VENC_Start 我们图像的类型对应:PT_H264 VENC_CHN_ATTR_S: 定义编码通道属性结构体 typedef struct hiVENC_CHN_ATTR_S {undefined VENC_ATTR_S stVeAttr; 编码器属性 VENC_RC_ATTR_S stRcAttr; 码率控制器属性 }VENC_CHN_ATTR_S;
VENC_ATTR_S 定义编码器属性结构体 typedef struct hiVENC_ATTR_S {undefined PAYLOAD_TYPE_E enType; union {undefined VENC_ATTR_H264_S stAttrH264e; VENC_ATTR_MJPEG_S stAttrMjpeg; VENC_ATTR_JPEG_S stAttrJpeg; VENC_ATTR_MPEG4_S stAttrMpeg4; VENC_ATTR_H265_S stAttrH265e; }; }VENC_ATTR_S;
VENC_ATTR_H264_CBR_S 定义 H.264 编码通道 CBR 属性结构 所以例子中:使用了H264编码,设置了H264相关的一些参数,设置了VI的输入码率和编码器输出的码率,设置的是25或者30。设置了H264 cbr相关的一些参数。 2.HI_MPI_VENC_CreateChn 创建了编码器通道 3.HI_MPI_VENC_StartRecvPic 开启编码通道接收输入图像。 4.SAMPLE_COMM_VENC_BindVpss 绑定VPSS的通道和编码器的通道 5.绑定VPSS的通道和编码器的通道 通道2 和 通道3 的继续绑定
step 6: stream venc process – get stream, then save it to file. 1.SAMPLE_COMM_VENC_StartGetStream SAMPLE_COMM_VENC_GetVencStreamProc 2.SAMPLE_COMM_VENC_GetVencStreamProc函数中: for (i = 0; i < s32ChnTotal; i++) 三个编码通道 文件名一般是:stream_chn%d%s stream_chn0.h264 stream_chn1.h264 stream_chn2.h264 然后打开文件 HI_MPI_VENC_GetFd 获取编码通道对应的设备文件句柄 HI_MPI_VENC_Query 查询编码通道状态
typedef struct hiVENC_CHN_STAT_S 定义编码通道的状态结构体 {undefined 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 定义帧码流类型结构体 typedef struct hiVENC_STREAM_S {undefined VENC_PACK_S *pstPack; 帧码流包结构。 HI_U32 u32PackCount; 一帧码流的所有包的个数 HI_U32 u32Seq; 码流序列号。 按帧获取帧序号;按包获取包序号 union 码流特征信息 {undefined VENC_STREAM_INFO_H264_S stH264Info; VENC_STREAM_INFO_JPEG_S stJpegInfo; VENC_STREAM_INFO_MPEG4_S stMpeg4Info; VENC_STREAM_INFO_H265_S stH265Info; }; }VENC_STREAM_S
HI_MPI_VENC_GetStream: 获取编码的码流 3.SAMPLE_COMM_VENC_SaveStream 把码流的相关信息写到了文件中去 4.HI_MPI_VENC_ReleaseStream 释放码流缓存 5.关闭文件
step 7: exit process 1.按两次enter,执行到 SAMPLE_COMM_VENC_StopGetStream 2.SAMPLE_COMM_VENC_StopGetStream 函数中把线程的运行条件设置为:false 3.pthread_join 回收线程
倒影式处理部分: 1.END_VENC_1080P_CLASSIC_5: (1).解绑VENC的通道和VPSS的通道 (2).停止VENC编码部分 (3).解绑VI和VPSS部分 2.END_VENC_1080P_CLASSIC_4 (1).关闭VPSS的通道 3.END_VENC_1080P_CLASSIC_3 (1).解绑VI和VPSS部分 4.END_VENC_1080P_CLASSIC_2 (1).关闭VPSS的GROUP 5.END_VENC_1080P_CLASSIC_1 (1).关闭VI部分 6.END_VENC_1080P_CLASSIC_0 (1).释放系统,释放公共缓冲池 ———————————————— 版权声明:本文为CSDN博主「Mark wyz」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/qq_33472414/article/details/100087220