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或G或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将一个颜色拆解为3个纯色的亮度组合,YUV将一个颜色分解为一个亮度和2个色度的组合。
(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))
(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也属于YUV422的一种,它是一种Plane模式,即平面模式,并不是将YUV数据交错存储,而是先存放所有的Y分量,然后存储所有的U(Cb)分量,最后存储所有的V(Cr)分量,其每一个像素点的YUV值提取方法也是遵循YUV422格式的最基本提取方法,即两个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、Cb00,其他依次类推
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×480字节是Y分量值, (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)公共视频缓存池的公共2字,可以理解为全局变量,也就是各个模块都能访问的一段内存
(5)看似视频缓存块在各个模块之间流转,实际上并没有内存复制,而是指针在传递
(6)视频缓存池的内存由MPP来维护,我们在系统启动时就把整个SDRAM分成了2部分:系统部分(由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
{
HI_U32 u32MaxPoolCnt; 整个系统中可容纳的缓存池个数 256
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;
/* 最大的视频缓存池个数 /
#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 system 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
{
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
} PIC_SIZE_E;
3.VB_CONF_S:定义视频缓存池属性结构体
typedef struct hiVB_CONF_S
{
/* max count of pools, (0,VB_MAX_POOLS] 整个系统中可容纳的缓存池个数*/
HI_U32 u32MaxPoolCnt;
公共缓存池属性结构体,成员包括公共缓存池中每个缓存块的
大小(以 byte 为单位)和缓存块的个数及此缓存池在所在的
MMZ 的名字。
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;
4.SAMPLE_VI_CONFIG_S: 设置VI模块属性的结构体
typedef struct sample_vi_config_s
{
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])
{
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) )
{
pstcomboDevAttr = &MIPI_CMOS3V3_ATTR;
}
combo_dev_attr_t MIPI_CMOS3V3_ATTR =
{
/* input mode */
.input_mode = INPUT_MODE_CMOS_33V,
{}
};
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 模式主要用于通道一绑多,即一个通道绑定多个输出
源;在 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
{
VENC_ATTR_S stVeAttr; 编码器属性
VENC_RC_ATTR_S stRcAttr; 码率控制器属性
}VENC_CHN_ATTR_S;
VENC_ATTR_S 定义编码器属性结构体
typedef struct hiVENC_ATTR_S
{
PAYLOAD_TYPE_E enType;
union
{
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 定义编码通道的状态结构体
{
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
{
VENC_PACK_S *pstPack; 帧码流包结构。
HI_U32 u32PackCount; 一帧码流的所有包的个数
HI_U32 u32Seq; 码流序列号。
按帧获取帧序号;按包获取包序号
union 码流特征信息
{
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).释放系统,释放公共缓冲池