1. 程式人生 > >海思Hi3518EV200 SDK分析筆記

海思Hi3518EV200 SDK分析筆記

===========================================================================================================
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", \
                &nbs