hi3516 mpp


原文链接: hi3516 mpp

命名规则:

变量定义 以变量类型为前缀 
    VB_CONFIG_S             stVbConf;    // 结构体变量 st
    SAMPLE_VI_CONFIG_S      stViConfig;
    VPSS_CHN_ATTR_S         stVpssChnAttr[VPSS_MAX_PHY_CHN_NUM] = {0};   // 
    HI_BOOL                 abChnEnable[VPSS_MAX_PHY_CHN_NUM] = {0};   // 数组变量 a 

https://blog.csdn.net/taotongning/article/details/82427955

1.
/* We just coyp this value of payload type from RTP/RTSP definition */
typedef enum
{
    PT_PCMU          = 0,
    .......          ....
    PT_H265          = 265,
    PT_MAX           = 266,
    /* add by hisilicon */
    PT_AMR           = 1001,
    PT_MJPEG         = 1002,
    PT_AMRWB         = 1003,
    PT_BUTT
} PAYLOAD_TYPE_E;

#海思源码中,枚举类型都是以大写字母加下划线构成,并以E结尾,结构体是以S结尾
#这里面PT就是PAYLOAD_TYPE,PAYLOAD简单理解为载体或者载荷,可以这么理解,假设你在传输一个视频,你传输的这个视频是个什么类型的
#这就是这个视频的PAYLOAD_TYPE,你把RTSP视频传输看成是用饺子皮把饺子馅包起来运走,这个PAYLOAD_TYPE就是问你这个饺子是什么馅的。
#通过RTSP不仅能传H.264类型的,还可以传其他类型的,都列在枚举里面
#RTP/RTSP本来就是用来传输音视频的一些东西,音视频的编码种类特别多,那你传输的是怎样一种编码,你要用PAYLOAD_TYPE告诉别人
#PT_BUTT是结尾标识符,不能用
===========================================================================================================
2.
PAYLOAD_TYPE_E enPayLoad[3]= {PT_H264, PT_H264,PT_H264};
#这个变量enPayLoad以en打头就告诉你这个是enum类型的
#从这个数组有三个变量来看,要编3路视频
#三路都是H.264的,这里只是一个初始化,后面再去填充
===========================================================================================================
3.
VPSS_GRP_ATTR_S stVpssGrpAttr; 
#以st打头的就是结构体变量,前的VPSS_GRP_ATTR_S就是一个结构体,全部大写,下划线分开,S结尾
===========================================================================================================
4.
typedef enum hiPIC_SIZE_E
{
    PIC_QCIF = 0,  //176*144
    PIC_CIF,       //352*288
    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;
#图片分辨率的
=============================================================================================================
5.
VB_CONF_S;定义了模型,点菜
HI_MPI_VB_SetConf; 把模型告诉了VB,点好菜交给服务员
HI_MPI_VB_Init;VB真正去执行分配了,服务员把点好的菜单交给后厨开始做菜
#顺序不能搞错
#可以有多个缓冲池,每个缓冲池又分多个缓存块
=============================================================================================================
6. 
step 6: stream venc process -- get stream, then save it to file. 
#可以把这个码流打包成一个MP4存储到你的硬盘里面去,这就是录像。
#也可以分包,分成一个一个的视频包通过RTSP传出去。
#也可以作为一个裸流直接丢到流文件里面。这个裸流文件必须通过像VLC这些可以解析流文件的播放器才可以观看。
#裸流是开发过程中的半成品,没有文件头,就得花时间去检测一下这幅图是多大,正常情况不会给别人提供裸流的
#正常的视频去分辨率都是直接从文件中提取的
=============================================================================================================
7.
step  1: init sys variable   指的是初始化MMP这个系统的变量 
=============================================================================================================
8. 
typedef struct hiVB_CONF_S
{
    HI_U32 u32MaxPoolCnt;     /* max count of pools, (0,VB_MAX_POOLS]  */    
    struct hiVB_CPOOL_S
    {
        HI_U32 u32BlkSize;    /*what the size of each block. */
        HI_U32 u32BlkCnt;     /*How many blocks of each pool*/
        HI_CHAR acMmzName[MAX_MMZ_NAME_LEN];
    }astCommPool[VB_MAX_COMM_POOLS];
} VB_CONF_S;
#u32BlkSize一帧图像的大小决定了一个缓冲块的大小
#u32BlkCnt 缓冲块的数量按道理来说越多越好,要适量分配,避免浪费。内存大你就多给点避免不够用的情况出现,内存不多就要合理安排
#step  1: init sys variable这一步是根据我们的实际来核算各路缓存池的BlkSize,BlkCnt,不是随便给的,
#在这一步整个MPP系统还没有启动

=============================================================================================================
9.
SAMPLE_COMM_VI_GetSizeBySensor(&enSize[0]);   
#填充变量,命令表明函数的位置在sample/common目录下,意思是通过sensor来算得到的图像大小
#我们这里的SENSOR是720p的,这个函数出来后就变成720p了
#刚开始初始化时PIC_SIZE_E enSize[3] = {PIC_HD1080, PIC_VGA,PIC_QVGA};给的是这三路码流,经过我们初始化后的到一路720p了
=============================================================================================================
10.
SAMPLE_VI_MODE_E enMode = SENSOR_TYPE;
#SENSOR_TYPE是在sample目录下面Makefile.param配置的

################# select sensor type for your sample ####################

#SENSOR_TYPE ?= APTINA_9M034_DC_720P_30FPS

SENSOR_TYPE ?= SONY_IMX222_DC_1080P_30FPS
#SENSOR_TYPE ?= SONY_IMX222_DC_720P_30FPS

#SENSOR_TYPE ?= APTINA_AR0130_DC_720P_30FPS

#SENSOR_TYPE ?= PANASONIC_MN34222_MIPI_1080P_30FPS

#SENSOR_TYPE ?= APTINA_AR0230_HISPI_1080P_30FPS

#SENSOR_TYPE ?= OMNIVISION_OV9712_DC_720P_30FPS
#SENSOR_TYPE ?= OMNIVISION_OV9732_DC_720P_30FPS
#SENSOR_TYPE ?= OMNIVISION_OV9750_MIPI_720P_30FPS
#SENSOR_TYPE ?= OMNIVISION_OV9752_MIPI_720P_30FPS

#SENSOR_TYPE ?= OMNIVISION_OV2718_MIPI_1080P_25FPS

##########################################################################
===========================================================================================================
11.
 if (PIC_HD1080 == enSize[0])
    {
        enSize[1] = PIC_VGA;
        s32ChnNum = 2;
    }
    else if (PIC_HD720 == enSize[0])
    {
        enSize[1] = PIC_VGA;            
        enSize[2] = PIC_QVGA;
        s32ChnNum = 3;
    }
    else
    {
        printf("not support this sensor\n");
        return HI_FAILURE;
    }
#最后我们得到的是720p,三路码流,enSize[0]=PIC_HD720 enSize[1] = PIC_VGA enSize[2] = PIC_QVGA
#PIC_HD720是主码流,PIC_VGA,PIC_QVGA是子码流,子码流的意思是通过对主码流裁剪,缩放等操作后得到的。
#子码流一般是通过手机来观看,手机屏幕毕竟小,没必要HD720,而且还流畅,节省带宽。
============================================================================================================
12.
stVbConf.u32MaxPoolCnt = 128;
#视频缓存池的个数是我们自己根据资源估算设置的,128是一个经验值
#不是实际设置了128个,而是设置了一个上限
============================================================================================================
13.
    /*video buffer*/
    if(s32ChnNum >= 1)
    {
        u32BlkSize = SAMPLE_COMM_SYS_CalcPicVbBlkSize(gs_enNorm,\
                    enSize[0], SAMPLE_PIXEL_FORMAT, SAMPLE_SYS_ALIGN_WIDTH);
        stVbConf.astCommPool[0].u32BlkSize = u32BlkSize;
        stVbConf.astCommPool[0].u32BlkCnt = g_u32BlkCnt;
    }   \\返回一帧图像所要的内存大小即u32VbSize,这个值就是block的大小-----第一路码流的公共缓存池
    if(s32ChnNum >= 2)
    {
        u32BlkSize = SAMPLE_COMM_SYS_CalcPicVbBlkSize(gs_enNorm,\
                    enSize[1], SAMPLE_PIXEL_FORMAT, SAMPLE_SYS_ALIGN_WIDTH);
        stVbConf.astCommPool[1].u32BlkSize = u32BlkSize;
        stVbConf.astCommPool[1].u32BlkCnt =g_u32BlkCnt;
    }   \\---第二路码流的公共缓存池
    if(s32ChnNum >= 3)
    {
        u32BlkSize = SAMPLE_COMM_SYS_CalcPicVbBlkSize(gs_enNorm,\
                enSize[2], SAMPLE_PIXEL_FORMAT, SAMPLE_SYS_ALIGN_WIDTH);
        stVbConf.astCommPool[2].u32BlkSize = u32BlkSize;
        stVbConf.astCommPool[2].u32BlkCnt = g_u32BlkCnt;
    }   \\---第三路码流的公共缓存池
#各路缓存池的BlkSize,BlkCnt是不一样的
#一个缓冲池其实就对应一路码流,
#为什么要这么分呢,是因为不同的码流分辨率不一样,耗费的内存的块block也不一样,这是为了避免浪费内存空间
#enSize[0]目前为PIC_HD720,
#SAMPLE_PIXEL_FORMAT为像素格式,比如RGB888,RGB565就不一样,RGB888 24b占三个字节,RGB565 16b占两个字节
#SAMPLE_SYS_ALIGN_WIDTH对齐
=============================================================================================================
14.
((PIXEL_FORMAT_YUV_SEMIPLANAR_422 == enPixFmt)?2:1.5));
#这里指的是平均一个像素占几个字节
#YUV422 4+2+2=8字节,4个像素一共占了8个字节,那一个像素平均占多少个字节8/4=2字节
#YUV420 4+2=6字节,4个像素一共占了6个字节,那一个像素平均占多少个字节6/4=1.5字节
=============================================================================================================
15.
u32VbSize += u32HeaderSize;
#最后还要加上头信息
=============================================================================================================
16. 
    VB_PIC_HEADER_SIZE(stSize.u32Width, stSize.u32Height, enPixFmt, u32HeaderSize);
    u32VbSize += u32HeaderSize;

    return u32VbSize;
#返回u32VbSize就知道考虑了所有的的余量之后一帧图像要多大内存
=============================================================================================================
17.
    /******************************************
     step 2: mpp system init. 
    ******************************************/
    s32Ret = SAMPLE_COMM_SYS_Init(&stVbConf);
    if (HI_SUCCESS != s32Ret)
    {
        SAMPLE_PRT("system init failed with %d!\n", s32Ret);
        goto END_VENC_1080P_CLASSIC_0;
    }
#s32Ret = SAMPLE_COMM_SYS_Init(&stVbConf);把参数&stVbConf传进来,stVbConf是一个结构体    
#函数内部用了两个API
#这两个API分别为 s32Ret = HI_MPI_VB_SetConf(pstVbConf); s32Ret = HI_MPI_VB_Init();
=============================================================================================================
18.
/******************************************************************************
* function : vb init & MPI system init
******************************************************************************/
HI_S32 SAMPLE_COMM_SYS_Init(VB_CONF_S *pstVbConf)
{
    MPP_SYS_CONF_S stSysConf = {0};
    HI_S32 s32Ret = HI_FAILURE;

    HI_MPI_SYS_Exit();   
    HI_MPI_VB_Exit();    

    if (NULL == pstVbConf)
    {
        SAMPLE_PRT("input parameter is null, it is invaild!\n");
        return HI_FAILURE;
    }

    s32Ret = HI_MPI_VB_SetConf(pstVbConf);
    if (HI_SUCCESS != s32Ret)
    {
        SAMPLE_PRT("HI_MPI_VB_SetConf failed!\n");
        return HI_FAILURE;
    }

    s32Ret = HI_MPI_VB_Init();

#HI_MPI_SYS_Exit(); 和HI_MPI_VB_Exit(); 打扫场地用的,注意调用顺序,释放是SYS在前,VB在后
#建立的时候是先VB后SYS
#其实VB的参数要操点心,pstVbConf自己去算好,余下的都是模式化,
================================================================================================================
19.
//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));
#CEILING_2_POWER(stSize.u32Width, u32AlignWidth) 这个宏的意思是stSize.u32Width向u32AlignWidth对齐
#怎么对齐,stSize.u32Width向上取整,u32AlignWidth是64,stSize.u32Width为1280,往上数,一直数到够第一个够64整除的数,意思就是往上留点余量
================================================================================================================
20.
PIXEL_FORMAT_YUV_SEMIPLANAR_420
#是怎么定成YUV420的
#其实是写APP的人用宏定义直接定义出来的,他怎么知道把它定成YUV420?
#define VB_PIC_HEADER_SIZE(Width, Height, Type, size)\
    do{\
        if (PIXEL_FORMAT_YUV_SEMIPLANAR_422 == Type || PIXEL_FORMAT_RGB_BAYER == Type )\
        {\
            size = VB_HEADER_STRIDE * (Height) * 2;\
        }\
        else if(PIXEL_FORMAT_YUV_SEMIPLANAR_420 == Type)\
        {\
            size = (VB_HEADER_STRIDE * (Height) * 3) >> 1;\
        }\
        else if(PIXEL_FORMAT_YUV_400 == Type)\
        {\
            size = VB_HEADER_STRIDE * (Height);\
        }\
    }while(0)
#PIXEL_FORMAT_RGB_BAYER指的是rawRGB,后面转成RGA,再到YUV,为什么不直接用RGB呢?
#是因为YUV这种方式表达颜色更加科学,所以最后不管你sensor这边出来是什么格式,一律转成YUV格式
#你想变成YUV422还是YUV420都可以,自己来定
#很明显YUV422的颜色分量会多一些,将来出来的色彩的还原度会高一些,但有比较浪费内存,所以自己来权衡颜色分量和内存占用情况
#行业内YUV420用得比较多
=================================================================================================================
21.
    /******************************************
     step 3: start vi dev & chn to capture     
    ******************************************/
    stViConfig.enViMode   = SENSOR_TYPE;
    stViConfig.enRotate   = ROTATE_NONE;
    stViConfig.enNorm     = VIDEO_ENCODING_MODE_AUTO;
    stViConfig.enViChnSet = VI_CHN_SET_NORMAL;
    stViConfig.enWDRMode  = WDR_MODE_NONE;
    s32Ret = SAMPLE_COMM_VI_StartVi(&stViConfig);
    if (HI_SUCCESS != s32Ret)
    {
        SAMPLE_PRT("start vi failed!\n");
        goto END_VENC_1080P_CLASSIC_1;
    }
#MPP里面的函数我们追不进去
#HI_MPI_VI_SetDevAttr  MPI代表MPP interface
#常用senso接口,MIPI,LVDS,DC(并口)
#stViConfig.enRotate   = ROTATE_NONE;图像出来要旋转的话在这里设置    
#stViConfig.enNorm     = VIDEO_ENCODING_MODE_AUTO;图像制式的标准有PAL和NTSC两种,对于这种数字接口的sensor来说,不重要。
#stViConfig.enViChnSet = VI_CHN_SET_NORMAL; 图像镜像,翻转在这里设置
#stViConfig.enWDRMode  = WDR_MODE_NONE;宽动态,这种技术需要sensor硬件支持。动态范围:在一幅图像中,能看到最亮与最暗的比例
#动态范围的模式有如下几种
typedef enum hiWDR_MODE_E
{
    WDR_MODE_NONE = 0,
    WDR_MODE_BUILT_IN,

    WDR_MODE_2To1_LINE,
    WDR_MODE_2To1_FRAME,
    WDR_MODE_2To1_FRAME_FULL_RATE,

    WDR_MODE_3To1_LINE,
    WDR_MODE_3To1_FRAME,
    WDR_MODE_3To1_FRAME_FULL_RATE,

    WDR_MODE_4To1_LINE,
    WDR_MODE_4To1_FRAME,
    WDR_MODE_4To1_FRAME_FULL_RATE,

    WDR_MODE_BUTT,
} WDR_MODE_E;
#sensor运行需要驱动
HI_S32 SAMPLE_COMM_VI_SetMipiAttr(SAMPLE_VI_CONFIG_S* pstViConfig)
{
    HI_S32 fd;
    combo_dev_attr_t *pstcomboDevAttr = NULL;

    /* mipi reset unrest */
    fd = open("/dev/hi_mipi", O_RDWR);
    if (fd < 0)
    {
        printf("warning: open hi_mipi dev failed\n");
        return -1;
    }
    printf("=============SAMPLE_COMM_VI_SetMipiAttr enWDRMode: %d\n", pstViConfig->enWDRMode);

    if ( pstViConfig->enViMode == APTINA_AR0230_HISPI_1080P_30FPS )
    {
        pstcomboDevAttr = &HISPI_4lane_SENSOR_AR0230_12BIT_ATTR;
    }

    if ( pstViConfig->enViMode == PANASONIC_MN34222_MIPI_1080P_30FPS )
    {
        pstcomboDevAttr = &MIPI_2lane_SENSOR_MN34222_12BIT_NOWDR_ATTR;
    }

    if ( (pstViConfig->enViMode == OMNIVISION_OV9752_MIPI_720P_30FPS)
        || (pstViConfig->enViMode == OMNIVISION_OV9750_MIPI_720P_30FPS) )
    {
        pstcomboDevAttr = &MIPI_2lane_SENSOR_OV9752_12BIT_NOWDR_ATTR;
    }

    if ( pstViConfig->enViMode ==  OMNIVISION_OV2718_MIPI_1080P_25FPS )
    {
        pstcomboDevAttr = &MIPI_4lane_SENSOR_OV2718_12BIT_NOWDR_ATTR;
    }

    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;
    }

    if (NULL == pstcomboDevAttr)
    {
        printf("Func %s() Line[%d], unsupported enViMode: %d\n", __FUNCTION__, __LINE__, pstViConfig->enViMode);
        close(fd);
        return HI_FAILURE;   
    }

    if (ioctl(fd, HI_MIPI_SET_DEV_ATTR, pstcomboDevAttr))
    {
        printf("set mipi attr failed\n");
        close(fd);
        return HI_FAILURE;
    }
    close(fd);
    return HI_SUCCESS;
#sensor驱动装载完后会生成/dev/hi_mipi这样的一个设备文件,打开,打开后准备好相应的参数,不同的sensor填充的参数是不一样的
#填充完以后通过一个ioctl(fd, HI_MIPI_SET_DEV_ATTR, pstcomboDevAttr),HI_MIPI_SET_DEV_ATTR是3518E给sensor做属性设置的命令,传参的标准都是海思定义好的一个结构体,
#但是这个结构体在不用的sensor里面是不一样的,
#ioctl是驱动对应用开放的接口
#SAMPLE_COMM_VI_SetMipiAttr功能就是在应用层对sensor做一个初始化
combo_dev_attr_t HISPI_4lane_SENSOR_AR0230_12BIT_ATTR = 
{
    /* input mode */
    .input_mode = INPUT_MODE_HISPI,
    {
        .lvds_attr = 
        {
            .img_size = {1920, 1080},
            HI_WDR_MODE_NONE,
            LVDS_SYNC_MODE_SOL,
            RAW_DATA_12BIT,
            LVDS_ENDIAN_LITTLE,
            LVDS_ENDIAN_LITTLE,
            .lane_id = {0, 1, 2, 3, -1, -1, -1, -1},
            .sync_code = 
            { 
                {{0x003, 0x007, 0x001, 0x005}, 
                {0x003, 0x007, 0x001, 0x005}, 
                {0x003, 0x007, 0x001, 0x005}, 
                {0x003, 0x007, 0x001, 0x005}},
                
                {{0x003, 0x007, 0x001, 0x005}, 
                {0x003, 0x007, 0x001, 0x005}, 
                {0x003, 0x007, 0x001, 0x005}, 
                {0x003, 0x007, 0x001, 0x005}},

                {{0x003, 0x007, 0x001, 0x005}, 
                {0x003, 0x007, 0x001, 0x005}, 
                {0x003, 0x007, 0x001, 0x005}, 
                {0x003, 0x007, 0x001, 0x005}},
                
                {{0x003, 0x007, 0x001, 0x005}, 
                {0x003, 0x007, 0x001, 0x005}, 
                {0x003, 0x007, 0x001, 0x005}, 
                {0x003, 0x007, 0x001, 0x005}},
                
                {{0x003, 0x007, 0x001, 0x005}, 
                {0x003, 0x007, 0x001, 0x005}, 
                {0x003, 0x007, 0x001, 0x005}, 
                {0x003, 0x007, 0x001, 0x005}},
                    
                {{0x003, 0x007, 0x001, 0x005}, 
                {0x003, 0x007, 0x001, 0x005}, 
                {0x003, 0x007, 0x001, 0x005}, 
                {0x003, 0x007, 0x001, 0x005}},

                {{0x003, 0x007, 0x001, 0x005}, 
                {0x003, 0x007, 0x001, 0x005}, 
                {0x003, 0x007, 0x001, 0x005}, 
                {0x003, 0x007, 0x001, 0x005}},
                
                {{0x003, 0x007, 0x001, 0x005}, 
                {0x003, 0x007, 0x001, 0x005}, 
                {0x003, 0x007, 0x001, 0x005}, 
                {0x003, 0x007, 0x001, 0x005}} 
            }
        }
    }
};
===============================================================================================================================
22.
/******************************************
     step 2: configure sensor and ISP (include WDR mode).
     note: you can jump over this step, if you do not use Hi3516A interal isp. 
    ******************************************/
    s32Ret = SAMPLE_COMM_ISP_Init(pstViConfig->enWDRMode);
    if (HI_SUCCESS != s32Ret)
    {
        SAMPLE_PRT("%s: Sensor init failed!\n", __FUNCTION__);
        return HI_FAILURE;
    }
#ISP image signal process 是一种技术,就是做一下数学运算,3518e内部的一个硬件单元
#这个ISP硬件单元就是专门用来做ISP的,这个模块在MPP里面被封装成了API
#SAMPLE_COMM_ISP_Init功能就是把这个ISP的线程运行起来
#ISP里面处理比如3A
#也可以专门加一个ISP芯片,不用3518e里面这个ISP模块单元,不启动就行,默认就是关闭的
/******************************************************************************
* funciton : ISP init
******************************************************************************/
HI_S32 SAMPLE_COMM_ISP_Init(WDR_MODE_E  enWDRMode)
{
    ISP_DEV IspDev = 0;
    HI_S32 s32Ret;
    ISP_PUB_ATTR_S stPubAttr;
    ALG_LIB_S stLib;
    
#if 0   
    /* 0. set cmos iniparser file path */
    s32Ret = sensor_set_inifile_path("configs/");
    if (s32Ret != HI_SUCCESS)
    {
        printf("%s: set cmos iniparser file path failed with %#x!\n", \
               __FUNCTION__, s32Ret);
        return s32Ret;
    }
#endif

    /* 1. sensor register callback */
    s32Ret = sensor_register_callback();
    if (s32Ret != HI_SUCCESS)
    {
        printf("%s: sensor_register_callback failed with %#x!\n", \
               __FUNCTION__, s32Ret);
        return s32Ret;
    }

    /* 2. register hisi ae lib */
    stLib.s32Id = 0;
    strcpy(stLib.acLibName, HI_AE_LIB_NAME);
    s32Ret = HI_MPI_AE_Register(IspDev, &stLib);
    if (s32Ret != HI_SUCCESS)
    {
        printf("%s: HI_MPI_AE_Register failed!\n", __FUNCTION__);
        return s32Ret;
    }

    /* 3. register hisi awb lib */
    stLib.s32Id = 0;
    strcpy(stLib.acLibName, HI_AWB_LIB_NAME);
    s32Ret = HI_MPI_AWB_Register(IspDev, &stLib);
    if (s32Ret != HI_SUCCESS)
    {
        printf("%s: HI_MPI_AWB_Register failed!\n", __FUNCTION__);
        return s32Ret;
    }

    /* 4. register hisi af lib */
    stLib.s32Id = 0;
    strcpy(stLib.acLibName, HI_AF_LIB_NAME);
    s32Ret = HI_MPI_AF_Register(IspDev, &stLib);
    if (s32Ret != HI_SUCCESS)
    {
        printf("%s: HI_MPI_AF_Register failed!\n", __FUNCTION__);
        return s32Ret;
    }

    /* 5. isp mem init */
    s32Ret = HI_MPI_ISP_MemInit(IspDev);
    if (s32Ret != HI_SUCCESS)
    {
        printf("%s: HI_MPI_ISP_Init failed!\n", __FUNCTION__);
        return s32Ret;
    }

    /* 6. isp set WDR mode */
    ISP_WDR_MODE_S stWdrMode;
    stWdrMode.enWDRMode  = enWDRMode;
    s32Ret = HI_MPI_ISP_SetWDRMode(0, &stWdrMode);    
    if (HI_SUCCESS != s32Ret)
    {
        printf("start ISP WDR failed!\n");
        return s32Ret;
    }

    /* 7. isp set pub attributes */
    /* note : different sensor, different ISP_PUB_ATTR_S define.
              if the sensor you used is different, you can change
              ISP_PUB_ATTR_S definition */
              
    switch(SENSOR_TYPE)
    {
        case APTINA_9M034_DC_720P_30FPS:
        case APTINA_AR0130_DC_720P_30FPS:
            stPubAttr.enBayer               = BAYER_GRBG;
            stPubAttr.f32FrameRate          = 30;
            stPubAttr.stWndRect.s32X        = 0;
            stPubAttr.stWndRect.s32Y        = 0;
            stPubAttr.stWndRect.u32Width    = 1280;
            stPubAttr.stWndRect.u32Height   = 720;
            break;

        case SONY_IMX222_DC_1080P_30FPS:
            stPubAttr.enBayer               = BAYER_RGGB;
            stPubAttr.f32FrameRate          = 30;
            stPubAttr.stWndRect.s32X        = 200;
            stPubAttr.stWndRect.s32Y        = 20;
            stPubAttr.stWndRect.u32Width    = 1920;
            stPubAttr.stWndRect.u32Height   = 1080;
            break;

        case SONY_IMX222_DC_720P_30FPS:
            stPubAttr.enBayer               = BAYER_RGGB;
            stPubAttr.f32FrameRate          = 30;
            stPubAttr.stWndRect.s32X        = 200;
            stPubAttr.stWndRect.s32Y        = 20;
            stPubAttr.stWndRect.u32Width    = 1280;
            stPubAttr.stWndRect.u32Height   = 720;
            break;

        case APTINA_AR0230_HISPI_1080P_30FPS:
            stPubAttr.enBayer               = BAYER_GRBG;
            stPubAttr.f32FrameRate          = 30;
            stPubAttr.stWndRect.s32X        = 0;
            stPubAttr.stWndRect.s32Y        = 0;
            stPubAttr.stWndRect.u32Width    = 1920;
            stPubAttr.stWndRect.u32Height   = 1080;
            break;

        case PANASONIC_MN34222_MIPI_1080P_30FPS:
            stPubAttr.enBayer               = BAYER_GRBG;
            stPubAttr.f32FrameRate          = 30;
            stPubAttr.stWndRect.s32X        = 0;
            stPubAttr.stWndRect.s32Y        = 0;
            stPubAttr.stWndRect.u32Width    = 1920;
            stPubAttr.stWndRect.u32Height   = 1080;
            break;

        case OMNIVISION_OV9712_DC_720P_30FPS:
            stPubAttr.enBayer               = BAYER_BGGR;
            stPubAttr.f32FrameRate          = 30;
            stPubAttr.stWndRect.s32X        = 0;
            stPubAttr.stWndRect.s32Y        = 0;
            stPubAttr.stWndRect.u32Width    = 1280;
            stPubAttr.stWndRect.u32Height   = 720;
            break;
            
        case OMNIVISION_OV9732_DC_720P_30FPS:
            stPubAttr.enBayer               = BAYER_BGGR;
            stPubAttr.f32FrameRate          = 30;
            stPubAttr.stWndRect.s32X        = 0;
            stPubAttr.stWndRect.s32Y        = 0;
            stPubAttr.stWndRect.u32Width    = 1280;
            stPubAttr.stWndRect.u32Height   = 720;
            break;

        case OMNIVISION_OV9750_MIPI_720P_30FPS:
        case OMNIVISION_OV9752_MIPI_720P_30FPS:
            stPubAttr.enBayer               = BAYER_BGGR;
            stPubAttr.f32FrameRate          = 30;
            stPubAttr.stWndRect.s32X        = 0;
            stPubAttr.stWndRect.s32Y        = 0;
            stPubAttr.stWndRect.u32Width    = 1280;
            stPubAttr.stWndRect.u32Height   = 720;
            break;

        case OMNIVISION_OV2718_MIPI_1080P_25FPS:
            stPubAttr.enBayer               = BAYER_BGGR;
            stPubAttr.f32FrameRate          = 25;
            stPubAttr.stWndRect.s32X        = 0;
            stPubAttr.stWndRect.s32Y        = 0;
            stPubAttr.stWndRect.u32Width    = 1920;
            stPubAttr.stWndRect.u32Height   = 1080;
            break;
            
        default:
            stPubAttr.enBayer      = BAYER_GRBG;
            stPubAttr.f32FrameRate          = 30;
            stPubAttr.stWndRect.s32X        = 0;
            stPubAttr.stWndRect.s32Y        = 0;
            stPubAttr.stWndRect.u32Width    = 1920;
            stPubAttr.stWndRect.u32Height   = 1080;
            break;
    }

    s32Ret = HI_MPI_ISP_SetPubAttr(IspDev, &stPubAttr);
    if (s32Ret != HI_SUCCESS)
    {
        printf("%s: HI_MPI_ISP_SetPubAttr failed with %#x!\n", __FUNCTION__, s32Ret);
        return s32Ret;
    }

    /* 8. isp init */
    s32Ret = HI_MPI_ISP_Init(IspDev);
    if (s32Ret != HI_SUCCESS)
    {
        printf("%s: HI_MPI_ISP_Init failed!\n", __FUNCTION__);
        return s32Ret;
    }

    gbIspInited = HI_TRUE;

    return HI_SUCCESS;
}
#sensor_register_callback() IQ调试相关
#这个函数在sensor驱动里面Z:\Hi3518E_V200R001C01SPC030\Hi3518E V200R001C01SPC030\
#01.software\board\Hi3518E_SDK_V1.0.3.0\package\mpp\component\isp\sensor\ar0130目录的ar0130_cmos.c里面

/* 2. register hisi ae lib */
    stLib.s32Id = 0;
    strcpy(stLib.acLibName, HI_AE_LIB_NAME);
    s32Ret = HI_MPI_AE_Register(IspDev, &stLib);
    if (s32Ret != HI_SUCCESS)
    {
        printf("%s: HI_MPI_AE_Register failed!\n", __FUNCTION__);
        return s32Ret;
    }
#自动曝光

    /* 3. register hisi awb lib */
    stLib.s32Id = 0;
    strcpy(stLib.acLibName, HI_AWB_LIB_NAME);
    s32Ret = HI_MPI_AWB_Register(IspDev, &stLib);
    if (s32Ret != HI_SUCCESS)
    {
        printf("%s: HI_MPI_AWB_Register failed!\n", __FUNCTION__);
        return s32Ret;
    }
#自动白平衡

    /* 4. register hisi af lib */
    stLib.s32Id = 0;
    strcpy(stLib.acLibName, HI_AF_LIB_NAME);
    s32Ret = HI_MPI_AF_Register(IspDev, &stLib);
    if (s32Ret != HI_SUCCESS)
    {
        printf("%s: HI_MPI_AF_Register failed!\n", __FUNCTION__);
        return s32Ret;
    }
#自动对焦
#注册一下3A单元
    /* 5. isp mem init */
    s32Ret = HI_MPI_ISP_MemInit(IspDev);
    if (s32Ret != HI_SUCCESS)
    {
        printf("%s: HI_MPI_ISP_Init failed!\n", __FUNCTION__);
        return s32Ret;
    }
#给ISP单元分配内存,传这个参数IspDev就可以了,内部自动会去分配内存
    /* 6. isp set WDR mode */
    ISP_WDR_MODE_S stWdrMode;
    stWdrMode.enWDRMode  = enWDRMode;
    s32Ret = HI_MPI_ISP_SetWDRMode(0, &stWdrMode);    
    if (HI_SUCCESS != s32Ret)
    {
        printf("start ISP WDR failed!\n");
        return s32Ret;
    }
#设置宽动态
    switch(SENSOR_TYPE)
    {
        case APTINA_9M034_DC_720P_30FPS:
        case APTINA_AR0130_DC_720P_30FPS:
            stPubAttr.enBayer               = BAYER_GRBG;
            stPubAttr.f32FrameRate          = 30;
            stPubAttr.stWndRect.s32X        = 0;
            stPubAttr.stWndRect.s32Y        = 0;
            stPubAttr.stWndRect.u32Width    = 1280;
            stPubAttr.stWndRect.u32Height   = 720;
#stPubAttr.enBayer               = BAYER_GRBG;  RGB原始信号的排列序列,查sensor的datasheet
#           stPubAttr.stWndRect.s32X        = 0;
#           stPubAttr.stWndRect.s32Y        = 0;   图像区域的起始点(0,0)
#ISP初始化以后ISP就已经准备好了
/* 8. isp init */
    s32Ret = HI_MPI_ISP_Init(IspDev);
    if (s32Ret != HI_SUCCESS)
    {
        printf("%s: HI_MPI_ISP_Init failed!\n", __FUNCTION__);
        return s32Ret;
    }
#3518E内部ISP单元是隶属于VI模块的    
#VI模块就包括3大部分,第一部分是与sensor对接的部分,第二部分是就是ISP,第三部分就是VI dev和channel部分。
#VI dev是采集图像的硬件单元,channel是与后端连接的多个出口,也是一个硬件单元。每一个出口就是一个channel。
#填充完后就设置进去HI_MPI_VI_SetDevAttr
    s32Ret = HI_MPI_VI_SetDevAttr(ViDev, &stViDevAttr);
    if (s32Ret != HI_SUCCESS)
    {
        SAMPLE_PRT("HI_MPI_VI_SetDevAttr failed with %#x!\n", s32Ret);
        return HI_FAILURE;
    }
#获取WDR模式HI_MPI_ISP_GetWDRMode
if ( (SAMPLE_VI_MODE_BT1120_1080P != enViMode)
        &&(SAMPLE_VI_MODE_BT1120_720P != enViMode) )
    {
        s32Ret = HI_MPI_ISP_GetWDRMode(s32IspDev, &stWdrMode);
        if (s32Ret != HI_SUCCESS)
        {
            SAMPLE_PRT("HI_MPI_ISP_GetWDRMode failed with %#x!\n", s32Ret);
            return HI_FAILURE;
        }
#使能dev
s32Ret = HI_MPI_VI_EnableDev(ViDev);
    if (s32Ret != HI_SUCCESS)
    {
        SAMPLE_PRT("HI_MPI_VI_EnableDev failed with %#x!\n", s32Ret);
        return HI_FAILURE;
    }
#dev循环了一次,因为u32DevNum=1
    /******************************************************
     step 4 : config & start vicap dev
    ******************************************************/
    for (i = 0; i < u32DevNum; i++)
    {
        ViDev = i;
        s32Ret = SAMPLE_COMM_VI_StartDev(ViDev, enViMode);
        if (HI_SUCCESS != s32Ret)
        {
            SAMPLE_PRT("%s: start vi dev[%d] failed!\n", __FUNCTION__, i);
            return HI_FAILURE;
        }
    }

    
    
### VPSS(Video Process Sub-System)
#支持对一幅图像进行统一预处理,如去噪,去隔行等。
#什么是去隔行,就是把隔行转成逐行,因为以前的技术有隔行扫描,不过现在的大多数的sensor都是逐行扫描的了。
#然后再对各通道分别进行缩放,锐化等处理,最后输出多种不同分辨率的图像。
#从这句话可以看出,VI到VPSS是当通道输入,多通道输出是从VPSSz这里出去的。扩展通道是从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的

    /*** enSize[0] **/
    if(s32ChnNum >= 1)
    {
        VpssGrp = 0;
        VpssChn = 0;
        VencChn = 0;
        s32Ret = SAMPLE_COMM_VENC_Start(VencChn, enPayLoad[0],\
                                       gs_enNorm, enSize[0], enRcMode,u32Profile);
        if (HI_SUCCESS != s32Ret)
        {
            SAMPLE_PRT("Start Venc failed!\n");
            goto END_VENC_1080P_CLASSIC_5;
        }

        s32Ret = SAMPLE_COMM_VENC_BindVpss(VencChn, VpssGrp, VpssChn);
        if (HI_SUCCESS != s32Ret)
        {
            SAMPLE_PRT("Start Venc failed!\n");
            goto END_VENC_1080P_CLASSIC_5;
        }
    }
#Profile指的是H.264编码的清晰度,分为baseline mainprofile hightprofile

/******************************************
     step 1:  Create Venc Channel
    ******************************************/
    stVencChnAttr.stVeAttr.enType = enType;
    switch(enType)
    {
        case PT_H264:
        {
            stH264Attr.u32MaxPicWidth = stPicSize.u32Width;
            stH264Attr.u32MaxPicHeight = stPicSize.u32Height;
            stH264Attr.u32PicWidth = stPicSize.u32Width;/*the picture width*/
            stH264Attr.u32PicHeight = stPicSize.u32Height;/*the picture height*/
            stH264Attr.u32BufSize  = stPicSize.u32Width * stPicSize.u32Height;/*stream buffer size*/
            stH264Attr.u32Profile  = u32Profile;/*0: baseline; 1:MP; 2:HP;  3:svc_t */
            stH264Attr.bByFrame = HI_TRUE;/*get stream mode is slice mode or frame mode?*/
            stH264Attr.u32BFrameNum = 0;/* 0: not support B frame; >=1: number of B frames */
            stH264Attr.u32RefNum = 1;/* 0: default; number of refrence frame*/
            memcpy(&stVencChnAttr.stVeAttr.stAttrH264e, &stH264Attr, sizeof(VENC_ATTR_H264_S));
#stH264Attr.u32MaxPicWidth,stH264Attr.u32MaxPicHeight编码通道的最大宽和高,如果给到通道里面的图像比这个大就会被压缩,如果小就丢弃
#所有图像的宽高stH264Attr.u32PicWidth,stH264Attr.u32PicHeight就应该和最大宽和高设置一样大,这样是最好的,不用处理直接编码
#stH264Attr.u32BufSize过程中的一个buffer,以像素为单位
#svc_t是h.264的一个补充标准
================================================================================================================

pstPara = (SAMPLE_VENC_GETSTREAM_PARA_S*)p;
s32ChnTotal = pstPara->s32Cnt;
#s32Cnt告诉你有几路,我们目前有三路

    /******************************************
     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++)
    {
        /* decide the stream file name, and open file to save stream */
        VencChn = i;
        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;

        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;
        }
        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;
        }
#去get流就是跟VENC这边对接的,
#获取编码输出通道的一些信息HI_MPI_VENC_GetChnAttr
# enPayLoadType[i] = stVencChnAttr.stVeAttr.enType;这个拿出来就是H.264的类型
#SAMPLE_COMM_VENC_GetFilePostfix获取编码后文件名的后缀。比如.h264
#aszFileName[i]编码后文件名
#最后这个流就是写到一个文件里面去了pFile[i] = fopen(aszFileName[i], "wb");一个裸流,裸流就是没有文件头,没有文件格式
#一般的播放器是播放不了,VLC是面向开发的,支持裸流播放。wb为二级制的形式写入。

VencFd[i] = HI_MPI_VENC_GetFd(i);
#取流文件的访问接口,毕竟编码后的文件是封装在里面的,你不能直接去取的
#什么是流,流就是连续的,你去读只能读一段

stStream.pstPack = (VENC_PACK_S*)malloc(sizeof(VENC_PACK_S) * stStat.u32CurPacks);
#通过malloc申请一段内存

s32Ret = SAMPLE_COMM_VENC_SaveStream(enPayLoadType[i], pFile[i], &stStream);
#分配了一个payload,我们这是当然是h.264

/******************************************************************************
* 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写到flash里面去了,确保断电不丢失,另一方面是内存有限

/*******************************************************
                     step 2.6 : release stream
                    *******************************************************/
                    s32Ret = HI_MPI_VENC_ReleaseStream(i, &stStream);
                    if (HI_SUCCESS != s32Ret)
                    {
                        free(stStream.pstPack);
                        stStream.pstPack = NULL;
                        break;
                    }
#写完了还要调用HI_MPI_VENC_ReleaseStream,海思内部的设计架构,不用考虑

    /*******************************************************
    * step 3 : close save-file
    *******************************************************/
    for (i = 0; i < s32ChnTotal; i++)
    {
        fclose(pFile[i]);
    }

    return NULL;
#我们不想录像后,就关闭文件

HI_VOID* SAMPLE_COMM_VENC_GetVencStreamProc(HI_VOID *p)
#这个函数是我们获取视频并且写成文件的一个主线程,这个线程一死,整个录像过程就掉了

 /******************************************
     step 6: stream venc process -- get stream, then save it to file. 
     1.可以把这个码流打包成一个MP4存储到你的硬盘里面去,这就是录像。
     2.也可以分包,分成一个一个的视频包通过RTSP传出去。
     3.也可以作为一个裸流直接丢到流文件里面。
     
    ******************************************/
    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();
#这个主线程在这里可以结束,两次回车就会结束,录像停止

    /******************************************
     step 7: exit process 
     刚好是个一个逆过程,要把场地收拾干净,
     就像一个栈,先进后出
    ******************************************/
    SAMPLE_COMM_VENC_StopGetStream();
#主线程结束后,跳到这里,
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;
}
#gs_stPara.bThreadStart = HI_FALSE; 就把变量设置为FALSE,弄死
#回收线程 pthread_join(gs_VencPid, 0); 收尸
#整个程序结束

END_VENC_1080P_CLASSIC_5:
    
    VpssGrp = 0;
    switch(s32ChnNum)
    {
        case 3:
            VpssChn = 2;   
            VencChn = 2;
            SAMPLE_COMM_VENC_UnBindVpss(VencChn, VpssGrp, VpssChn);
            SAMPLE_COMM_VENC_Stop(VencChn);
        case 2:
            VpssChn = 1;   
            VencChn = 1;
            SAMPLE_COMM_VENC_UnBindVpss(VencChn, VpssGrp, VpssChn);
            SAMPLE_COMM_VENC_Stop(VencChn);
        case 1:
            VpssChn = 0;  
            VencChn = 0;
            SAMPLE_COMM_VENC_UnBindVpss(VencChn, VpssGrp, VpssChn);
            SAMPLE_COMM_VENC_Stop(VencChn);
            break;
            
    }
    SAMPLE_COMM_VI_UnBindVpss(stViConfig.enViMode);
    
END_VENC_1080P_CLASSIC_4:    //vpss stop

    VpssGrp = 0;
    switch(s32ChnNum)
    {
        case 3:
            VpssChn = 2;
            SAMPLE_COMM_VPSS_DisableChn(VpssGrp, VpssChn);
        case 2:
            VpssChn = 1;
            SAMPLE_COMM_VPSS_DisableChn(VpssGrp, VpssChn);
        case 1:
            VpssChn = 0;
            SAMPLE_COMM_VPSS_DisableChn(VpssGrp, VpssChn);
        break;
    
    }

END_VENC_1080P_CLASSIC_3:    //vpss stop       
    SAMPLE_COMM_VI_UnBindVpss(stViConfig.enViMode);
END_VENC_1080P_CLASSIC_2:    //vpss stop   
    SAMPLE_COMM_VPSS_StopGroup(VpssGrp);
END_VENC_1080P_CLASSIC_1:    //vi stop
    SAMPLE_COMM_VI_StopVi(&stViConfig);
END_VENC_1080P_CLASSIC_0:    //system exit
    SAMPLE_COMM_SYS_Exit();
    
    return s32Ret;    
}
#各种不同级别恢复原位的过程,比如VPSS死了,你得把你初始化的MPP初始化给恢复原位
#注意一个倒影的问题,前面是123,后面是321的一个过程


==============================================================================================
1. ORTP
#open RTP (RTP的一个开源实现)
#视频在网络上的传输主要有两种:
#(1).基于下载:http or ftp 要播放的话,先从服务器上下载到本地,比如视频网站播放视频,
#     下载的速度可以赶得上播放的速度那就是实时的,网速慢就在那里缓冲,网速快缓冲比播放提前。
#     基于下载的这种模式一般是为了保证视频的质量。
#(2).基于实时:RTP/RTSP/RTCP 主要用于视频监控的相关领域。还有直播
#     这种应用一般都是为了保证时间上的同步的应用场景。如果网速不够,牺牲的是画面质量。网速快的时候
#     看到的是清晰实时的画面,网速慢的时候看到的是模糊实时的画面。
#RTP(Real-time Transport Protocol)  可以用来传输语音、视频流等
#RTSP(Real Time Streaming Protocol)专门用来传输视频流的
#RTCP(RTP Control Protocol)用来控制用的,传输方与接收方的一个协调,RTCP是RTP的一个补充,因为RTP只传输,不能控制。
#两种传输方式是没有好坏之分的,关键是你看的应用场景。
#
==============================================================================================
2. h.264的编码原理
#图像的冗余信息:空间冗余,时间冗余,
#视频编码的关键点:压缩比、算法的复杂度、还原度;求得一个平衡,压缩分为硬压缩,软压缩;3518E就是用了
#一个硬件单元DSP来压缩,属于硬压缩。
#H.264的2大组成部分:VCL和NAL  VCL关心的是视频的压缩,NAL关心的是这些被压缩后的视频流如何被网络传输到对方解码
#h.264编码相关的一些概念
#(1) 宏块 MB(macroblock) 表示的是一幅图像的一小块区域, 压缩都是以宏块为单位(不是以像素为单位),因为一个宏块里面的像素是有相似性的
#(2) 片 slice 帧的一部分
#(3) 帧 frame 有时候帧只有一个slice,有时候又有多个slice
#像素组成宏块,宏块组成片,片组成帧,多个帧加起来组成一个序列,多个序列组成了一个码流
#(4) I帧(非参考帧,只和自己有关,可以理解为图像的第一帧,之前没有参考,做不了时间冗余,只能做帧内压缩,及空间压缩)
#(5) B帧(参考帧,相当于图像后面的帧,做了空间和时间冗余,压缩的时候前后帧都做了参考,可以这么理解,第二帧与第一帧非常相似,
#不用记录内容,记录差异就行,这样所占用的空间就小了,还原的时候前后帧都要参考,再把差异修正了就行,谁像就多参考些,
#因为在编码的时候前面后面的帧都已经出来了)
#(6) P帧(参考帧,只参考了前一帧,算法复杂度没那么高)
#I帧必须得有
#帧率 fps
================================================================================================
3. NAL单元
#NAL关系的是VCL的输出的纯视频流如何被表达和封包以利于网络传输
#NAL部分出来的就是H.264的码流,这部分码流包括纯视频流和封包信息,封包的作用是利于网络传输和解码
#SODB :String Of Date Bits VCL的输出的纯视频流
#RBSP: Raw Byte Sequence Payload   在SODB基础上加上了封包(头尾信息)
#NALU: Network Abstraction Layer Units  h.264里面就是一个一个的NALU
#关系:SODB + RBSP trailing bits(头尾信息) = RBSP
#NAL header(1 byte) + RBSP = NALU
#做编码器的人关心的是VCL部分,做视频传输和解码播放的人关心的是NAL部分
#雷神作品:SpecialVH264.exe
#国外工具:Elecard StreamEye Tools
#二级制工具:winhex
#网络抓包工具:wireshark
#播放器:vlc
#海思平台编码出来的h.264码流都是一个个序列:包含1sps+1pps+1sei+1I帧+若干p帧
#相关概念
#序列 sequence ,每个sequence都有一个I帧,本sequence的I帧坏了顶多是丢弃本sequence,不会影响其他sequence.
#一秒钟一个sequence,每一秒钟的第一个帧都是I帧,往下就有 帧率-1个P帧,每秒钟的sequence数等于帧率。
#分隔符00 00 00 01在h.264的码流里面是有特殊含义的,表示有一个新的开始,分隔符不是有效数据,相当于房子的墙
#00 00 00 01后的第一个数据是SPS,长度为14个字节,向后数14个字节后,又遇到分隔符00 00 00 01,分隔符后面是PPS
#PPS长度为4个字节,然后是分隔符00 00 00 01,接下来是SEI,长度为5个字节,然后是分隔符00 00 00 01,接下来是IDR_SLICE(I帧),
#然后是分隔符00 00 00 01,接下来就是P帧,依次类推。
#如果码流数据有00 00 00 ,那么要转变成 00 00 03 00 避免和分隔符00 00 00 01冲突
================================================================================================
4. h.264中的profile和level
#profile是对视频压缩等级或档次的描述,profile越高,就说明采用了越高级的压缩特性,越高级的压缩算法。压缩结果就越好,压缩算法的实现对硬件要求就比较高。
#level是对视频本身特性的描述(码率、分辨率、fps)。Level越高,视频的码率、分辨率、fps越高。
#在同一个profile里面,level是可以不一样的,比如大家都用的是最基础的profile(Base line Profile),最后得到的码率、分辨率、帧率也可以不一样。
#level指的是图像本身的一些参数,profile指的是图像压缩算法的一些参数。
#h.264 profile分为三个档次,分别为baseline profile(低配,硬件性能要求低)、main profile(主流)、high profile(高配,但是硬件性能要求比较高)。
#1280*720@30f对应的level是3.1
#程序中可以配置profile的
================================================================================================
5. sequence
#一段h.264码流其实就是由多个sequence组成的
#一个sequence持续一秒
#每个sequence均有固定结构:1sps+1pps+1sei+1I帧+若干P帧
#p帧的个数等于fps-1
#sps和pps和sei描述该sequence的图像信息,这些信息有两个功能:网络传输和解码
#I帧是关键,丢了I帧整个sequence就废了,每个sequence有且只有1个I帧(这只针对海思平台)
#I帧越大则P帧可以越小,反之I帧越小则I帧会越大
#I比较大说明I帧包括的信息比较详细,说明I帧的压缩比例没那么高
#I帧越详细,P帧就越好参考,p帧就会越小。反之,I帧越小,说明I帧压缩的越狠,I帧本身就
#不是很详细,那么I帧就得内容多一些,否则你I帧也小,P帧也小,图像肯定就不清晰。
#I帧的大小取决于图像本身内容(图像比较丰富,可压缩的空间就比较小,反之,图像比较单一,可压缩的空间就比较大,I帧压缩与时间没有关系)和压缩算法
#的空间压缩部分
#P帧的大小取决于图像的变化剧烈程度,如果这一帧的相对于上一帧变化非常的小,那么这帧P帧就可以很小,反之,如果这一帧的相对于上一帧变化非常的大,
#那么这帧P帧就很大。
#视频码率就是数据传输时单位时间传送的数据位数,一般我们用单位是kbps。
#CBR和VBR下P帧的大小策略会不同,CBR时P帧大小基本恒定,VBR时变化比较剧烈。
#CRR下就是牺牲图像的清晰度,图像变化剧烈,清晰度就会下降,图像稳定,清晰度就会上升。
#VBR的情况下是保存图像的清晰度,如果图像变化剧烈,码率就会极大增加,网速吃紧。
=================================================================================================
6. RTSP
--------------------- 
作者:taotongning 
来源:CSDN 
原文:https://blog.csdn.net/taotongning/article/details/82427955

`