/**********************************************************************************
 *
 * Copyright (c) 2019-2020 Beijing AXera Technology Co., Ltd. All Rights Reserved.
 *
 * This source file is the property of Beijing AXera Technology Co., Ltd. and
 * may not be copied or distributed in any isomorphic form without the prior
 * written consent of Beijing AXera Technology Co., Ltd.
 *
 **********************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <errno.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/prctl.h>
#include <getopt.h>

#include "ax_venc_api.h"
#include "ax_ivps_api.h"
#include "ax_isp_api.h"
#include "ax_sys_dma_api.h"

#include "common_sys.h"
#include "common_vin.h"
#include "common_cam.h"
#include "common_venc.h"
#include "sample_vin_ivps_joint_venc_rtsp.h"
#include "AXRtspWrapper.h"
#include "sample_run_joint_api.h"
#include "sample_log.h"
#include "inifile_api.h"

#define SAMPLE_IVPS_GROUP_NUM (2)
// #define SAMPLE_IVPS_CHN_NUM   (1)
#define SAMPLE_VENC_CHN_NUM (2)
#define SAMPLE_REGION_COUNT (2)
#define SAMPLE_RECT_BOX_COUNT (8)
#define SAMPLE_POSE_LINE_COUNT (8)
#define SAMPLE_IVPS_CHN_TOTAL (3)

typedef struct _IVPS_GET_THREAD_PARAM
{
    AX_U8 nIvpsIndex;
    AX_U8 nIvpsGrp;
    AX_U8 nIvpsChn;
} IVPS_GET_THREAD_PARAM_T;

static const AX_U8 s_sample_ivps_chn_num[SAMPLE_IVPS_GROUP_NUM] = {2, 1};
static IVPS_GET_THREAD_PARAM_T s_sample_ivps_thread_param[SAMPLE_IVPS_CHN_TOTAL] = {{0, 0, 0}, {1, 0, 1}, {2, 1, 0}};

#define RED 0xFF0000
#define PINK 0xFFC0CB
#define GREEN 0x00FF00
#define BLUE 0x0000FF
#define PURPLE 0xA020F0
#define ORANGE 0xFFA500
#define YELLOW 0xFFFF00

#ifndef ALIGN_UP
#define ALIGN_UP(x, align) ((((x) + ((align)-1)) / (align)) * (align))
#endif

#ifndef ALIGN_DOWN
#define ALIGN_DOWN(x, align) (((x) / (align)) * (align))
#endif

typedef AX_S32 IVPS_GRP;
typedef AX_S32 AX_IVPS_FILTER;

typedef struct _stVencGetStreamParam
{
    AX_S32 VeChn;
    AX_BOOL bThreadStart;
    AX_PAYLOAD_TYPE_E ePayloadType;
} VENC_GETSTREAM_PARAM_T;

typedef struct _stRCInfo
{
    SAMPLE_VENC_RC_E eRCType;
    AX_U32 nMinQp;
    AX_U32 nMaxQp;
    AX_U32 nMinIQp;
    AX_U32 nMaxIQp;
    AX_S32 nIntraQpDelta;
} RC_INFO_T;

typedef struct _stVideoConfig
{
    AX_PAYLOAD_TYPE_E ePayloadType;
    AX_U32 nGOP;
    AX_U32 nSrcFrameRate;
    AX_U32 nDstFrameRate;
    AX_U32 nStride;
    AX_S32 nInWidth;
    AX_S32 nInHeight;
    AX_S32 nOutWidth;
    AX_S32 nOutHeight;
    AX_S32 nOffsetCropX;
    AX_S32 nOffsetCropY;
    AX_S32 nOffsetCropW;
    AX_S32 nOffsetCropH;
    AX_IMG_FORMAT_E eImgFormat;
    RC_INFO_T stRCInfo;
    AX_S32 nBitrate;
} VIDEO_CONFIG_T;

typedef enum
{
    SYS_CASE_NONE = -1,
    SYS_CASE_SINGLE_OS04A10 = 0,
    SYS_CASE_SINGLE_IMX334 = 1,
    SYS_CASE_SINGLE_GC4653 = 2,
    SYS_CASE_SINGLE_OS08A20 = 3,
    SYS_CASE_SINGLE_OS04A10_ONLINE = 4,
    SYS_CASE_BUTT
} COMMON_SYS_CASE_E;

typedef enum
{
    VENC_CASE_NONE = -1,
    VENC_CASE_H264 = 0,
    VENC_CASE_H265 = 1,
    VENC_CASE_BUTT
} COMMON_VENC_CASE_E;

typedef struct _IVPS_REGION_PARAM
{
    IVPS_RGN_HANDLE hChnRgn;
    IVPS_GRP nGroup;
    AX_IVPS_FILTER nFilter;
    AX_BOOL bExit;
} IVPS_REGION_PARAM_T, *IVPS_REGION_PARAM_PTR;

IVPS_REGION_PARAM_T g_arrRgnThreadParam[SAMPLE_REGION_COUNT];

typedef struct
{
    IVPS_GRP nGroup;
    AX_IVPS_FILTER nFilter;
    AX_U32 nChnWidth;
    AX_U32 nChnHeight;
    AX_U32 nRgnNum;
    AX_IVPS_RGN_LAYER_E eLayer;
} RGN_GROUP_CFG_T;

typedef struct _sample_RECT_S
{
    float fX, fY, fW, fH;
} SAMPLE_RECT_S;


typedef struct _sample_POINT_S
{
    float fX;
    float fY;
    float score;
} SAMPLE_POINT_S;

typedef struct _sample_PTS_S
{
    SAMPLE_POINT_S point[15]
} SAMPLE_PTS_S;


typedef struct _sample_ALGO_RESULT_S
{
    AX_U8 nSize;
    SAMPLE_RECT_S rect[SAMPLE_RECT_BOX_COUNT];
    SAMPLE_PTS_S pts[SAMPLE_RECT_BOX_COUNT];
} SAMPLE_ALGO_RESULT_S;

static SAMPLE_ALGO_RESULT_S g_sample_algo_result;
static pthread_mutex_t g_result_mutex;

static AX_S32 s_sample_framerate = 25;
static AX_S32 s_sample_ldc_mode = AX_FALSE;
static AX_S32 s_sample_ldc_dRatio = 0;
static AX_S32 s_sample_ldc_xRatio = 0;
static AX_S32 s_sample_ldc_yRatio = 0;
static AX_IVPS_DEWARP_TYPE_E s_sample_gdc_dewarp = AX_IVPS_DEWARP_BUTT;
static AX_S64 s_sample_gdc_perspective_matrix[9] = {0};
static const AX_CHAR *s_sample_strInputYuvFile = NULL;

static COMMON_SYS_POOL_CFG_T gtSysCommPoolSingleOs04a10Sdr[] = {
    {2688, 1520, 2688, AX_FORMAT_BAYER_RAW_10BPP, 4}, /*vin raw10 use */
    {2688, 1520, 2688, AX_FORMAT_BAYER_RAW_16BPP, 4}, /*vin raw16 use */
    {2688, 1520, 2688, AX_YUV420_SEMIPLANAR, 4},      /*vin nv21/nv21 use */
    {1920, 1080, 1920, AX_YUV420_SEMIPLANAR, 4},
    {1280, 720, 1280, AX_YUV420_SEMIPLANAR, 3},
};
static COMMON_SYS_POOL_CFG_T gtSysCommPoolSingleOs04a10OnlineSdr[] = {
    {2688, 1520, 2688, AX_FORMAT_BAYER_RAW_10BPP, 3}, /*vin raw10 use */
    {2688, 1520, 2688, AX_FORMAT_BAYER_RAW_16BPP, 5}, /*vin raw16 use */
    {2688, 1520, 2688, AX_YUV420_SEMIPLANAR, 6},      /*vin nv21/nv21 use */
    {1920, 1080, 1920, AX_YUV420_SEMIPLANAR, 3},
    {1280, 720, 1280, AX_YUV420_SEMIPLANAR, 3},
};

static COMMON_SYS_POOL_CFG_T gtSysCommPoolSingleOs04a10Hdr[] = {
    {2688, 1520, 2688, AX_FORMAT_BAYER_RAW_10BPP, 4}, /*vin raw10 use */
    {2688, 1520, 2688, AX_FORMAT_BAYER_RAW_16BPP, 4}, /*vin raw16 use */
    {2688, 1520, 2688, AX_YUV420_SEMIPLANAR, 6},      /*vin nv21/nv21 use */
    {1920, 1080, 1920, AX_YUV420_SEMIPLANAR, 4},
    {720, 576, 720, AX_YUV420_SEMIPLANAR, 3},
};

static COMMON_SYS_POOL_CFG_T gtSysCommPoolSingleOs04a10OnlineHdr[] = {
    {2688, 1520, 2688, AX_FORMAT_BAYER_RAW_10BPP, 4}, /*vin raw10 use */
    {2688, 1520, 2688, AX_FORMAT_BAYER_RAW_16BPP, 4}, /*vin raw16 use */
    {2688, 1520, 2688, AX_YUV420_SEMIPLANAR, 6},      /*vin nv21/nv21 use */
    {1920, 1080, 1920, AX_YUV420_SEMIPLANAR, 4},
    {720, 576, 720, AX_YUV420_SEMIPLANAR, 3},
};

static COMMON_SYS_POOL_CFG_T gtSysCommPoolSingleImx334Sdr[] = {

    {3840, 2160, 3840, AX_FORMAT_BAYER_RAW_12BPP, 8}, /*vin raw10 use */
    {3840, 2160, 3840, AX_FORMAT_BAYER_RAW_16BPP, 4}, /*vin raw16 use */
    {3840, 2160, 3840, AX_YUV420_SEMIPLANAR, 6},      /*vin nv21/nv21 use */
    {1920, 1080, 1920, AX_YUV420_SEMIPLANAR, 4},
    {960, 540, 960, AX_YUV420_SEMIPLANAR, 3},

};

static COMMON_SYS_POOL_CFG_T gtSysCommPoolSingleImx334Hdr[] = {

    {3840, 2160, 3840, AX_FORMAT_BAYER_RAW_10BPP, 8}, /*vin raw10 use */
    {3840, 2160, 3840, AX_FORMAT_BAYER_RAW_16BPP, 4}, /*vin raw16 use */
    {3840, 2160, 3840, AX_YUV420_SEMIPLANAR, 6},      /*vin nv21/nv21 use */
    {1920, 1080, 1920, AX_YUV420_SEMIPLANAR, 4},
    {960, 540, 960, AX_YUV420_SEMIPLANAR, 3},

};

static COMMON_SYS_POOL_CFG_T gtSysCommPoolSingleGc4653[] = {

    {2560, 1440, 2560, AX_FORMAT_BAYER_RAW_10BPP, 4}, /*vin raw10 use */
    {2560, 1440, 2560, AX_FORMAT_BAYER_RAW_16BPP, 4}, /*vin raw16 use */
    {2560, 1440, 2560, AX_YUV420_SEMIPLANAR, 4},      /*vin nv21/nv21 use */
    {1280, 720, 1280, AX_YUV420_SEMIPLANAR, 4},
    {640, 360, 640, AX_YUV420_SEMIPLANAR, 3},

};

static COMMON_SYS_POOL_CFG_T gtSysCommPoolSingleOs08a20Sdr[] = {

    {3840, 2160, 3840, AX_FORMAT_BAYER_RAW_12BPP, 4}, /*vin raw10 use */
    {3840, 2160, 3840, AX_FORMAT_BAYER_RAW_16BPP, 4}, /*vin raw16 use */
    {3840, 2160, 3840, AX_YUV420_SEMIPLANAR, 6},      /*vin nv21/nv21 use */
    {1920, 1080, 1920, AX_YUV420_SEMIPLANAR, 4},
    {960, 540, 960, AX_YUV420_SEMIPLANAR, 3},

};

static COMMON_SYS_POOL_CFG_T gtSysCommPoolSingleOs08a20Hdr[] = {

    {3840, 2160, 3840, AX_FORMAT_BAYER_RAW_10BPP, 8}, /*vin raw10 use */
    {3840, 2160, 3840, AX_FORMAT_BAYER_RAW_16BPP, 4}, /*vin raw16 use */
    {3840, 2160, 3840, AX_YUV420_SEMIPLANAR, 6},      /*vin nv21/nv21 use */
    {1920, 1080, 1920, AX_YUV420_SEMIPLANAR, 4},
    {960, 540, 960, AX_YUV420_SEMIPLANAR, 3},

};

static CAMERA_T gCams[MAX_CAMERAS] = {0};

static volatile AX_S32 gLoopExit = 0;
static AX_S32 g_isp_force_loop_exit = 0;

const static AX_S32 gVencChnMapping[SAMPLE_VENC_CHN_NUM] = {0, 2};
static VENC_GETSTREAM_PARAM_T gGetStreamPara[SAMPLE_VENC_CHN_NUM];
static pthread_t gGetStreamPid[SAMPLE_VENC_CHN_NUM];
static AX_VOID *gJointHandle = NULL;


AX_S32 s32ModelType = 1;

AX_U32 CalcImgSize(AX_U32 nStride, AX_U32 nW, AX_U32 nH, AX_IMG_FORMAT_E eType, AX_U32 nAlign)
{
    AX_U32 nBpp = 0;
    if (nW == 0 || nH == 0)
    {
        ALOGE("%s: Invalid width %d or height %d!", __func__, nW, nH);
        return 0;
    }

    if (0 == nStride)
    {
        nStride = (0 == nAlign) ? nW : ALIGN_UP(nW, nAlign);
    }
    else
    {
        if (nAlign > 0)
        {
            if (nStride % nAlign)
            {
                ALOGE("%s: stride: %u not %u aligned.!", __func__, nStride, nAlign);
                return 0;
            }
        }
    }

    switch (eType)
    {
    case AX_YUV420_PLANAR:
    case AX_YUV420_SEMIPLANAR:
    case AX_YUV420_SEMIPLANAR_VU:
        nBpp = 12;
        break;
    case AX_YUV422_INTERLEAVED_YUYV:
    case AX_YUV422_INTERLEAVED_UYVY:
        nBpp = 16;
        break;
    case AX_YUV444_PACKED:
    case AX_FORMAT_RGB888:
        nBpp = 24;
        break;
    case AX_FORMAT_RGBA8888:
    case AX_FORMAT_ARGB8888:
        nBpp = 32;
        break;
    default:
        nBpp = 0;
        break;
    }

    return nStride * nH * nBpp / 8;
}

AX_S32 BufPoolBlockAddrGet(AX_POOL PoolId, AX_U32 BlkSize, AX_U64 *nPhyAddr, AX_VOID **pVirAddr, AX_BLK *BlkId)
{
    *BlkId = AX_POOL_GetBlock(PoolId, BlkSize, NULL);

    *nPhyAddr = AX_POOL_Handle2PhysAddr(*BlkId);

    if (!(*nPhyAddr))
    {
        ALOGE("AX_POOL_Handle2PhysAddr fail!");
        return -1;
    }

    ALOGI("AX_POOL_Handle2PhysAddr success!(Blockid:0x%X --> PhyAddr=0x%llx)", *BlkId, *nPhyAddr);

    *pVirAddr = AX_POOL_GetBlockVirAddr(*BlkId);

    if (!(*pVirAddr))
    {
        ALOGE("AX_POOL_GetBlockVirAddr fail!");
        return -1;
    }

    ALOGI("AX_POOL_GetBlockVirAddr success!blockVirAddr=0x%p", *pVirAddr);
    return 0;
}

/* venc get stream task */
static void *VencGetStreamProc(void *arg)
{
    AX_S32 s32Ret = -1;
    AX_VENC_RECV_PIC_PARAM_S stRecvParam;
    VENC_GETSTREAM_PARAM_T *pstPara;
    pstPara = (VENC_GETSTREAM_PARAM_T *)arg;
    AX_VENC_STREAM_S stStream;
    AX_S16 syncType = -1;
    AX_U32 totalGetStream = 0;

    s32Ret = AX_VENC_StartRecvFrame(pstPara->VeChn, &stRecvParam);
    if (AX_SUCCESS != s32Ret)
    {
        ALOGE("AX_VENC_StartRecvFrame failed, s32Ret:0x%x", s32Ret);
        return NULL;
    }

    memset(&stStream, 0, sizeof(AX_VENC_STREAM_S));

    while (AX_TRUE == pstPara->bThreadStart)
    {
        s32Ret = AX_VENC_GetStream(pstPara->VeChn, &stStream, syncType);

        if (AX_SUCCESS == s32Ret)
        {
            totalGetStream++;

            AX_Rtsp_SendNalu(pstPara->VeChn, stStream.stPack.pu8Addr, stStream.stPack.u32Len,
                             stStream.stPack.u64PTS, (VENC_INTRA_FRAME == stStream.stPack.enCodingType) ? AX_TRUE : AX_FALSE);

            s32Ret = AX_VENC_ReleaseStream(pstPara->VeChn, &stStream);
            if (AX_SUCCESS != s32Ret)
            {
                // ALOGE("VencChn %d: AX_VENC_ReleaseStream failed!s32Ret:0x%x",pstPara->VeChn,s32Ret);
                goto EXIT;
            }
        }
    }

EXIT:
    ALOGN("VencChn %d: Total get %u encoded frames. getStream Exit!", pstPara->VeChn, totalGetStream);
    return NULL;
}

static AX_S32 SampleVencInit(COMMON_VENC_CASE_E eVencType)
{
    AX_VENC_CHN_ATTR_S stVencChnAttr;
    VIDEO_CONFIG_T config = {0};
    AX_S32 VencChn = 0, s32Ret = 0;

    AX_VENC_MOD_ATTR_S stModAttr;
    stModAttr.enVencType = VENC_MULTI_ENCODER;

    s32Ret = AX_VENC_Init(&stModAttr);
    if (AX_SUCCESS != s32Ret)
    {
        ALOGE("AX_VENC_Init failed, s32Ret:0x%x", s32Ret);
        return s32Ret;
    }

    config.stRCInfo.eRCType = VENC_RC_CBR;
    config.nGOP = 30;
    config.nBitrate = 2000;
    config.stRCInfo.nMinQp = 10;
    config.stRCInfo.nMaxQp = 51;
    config.stRCInfo.nMinIQp = 10;
    config.stRCInfo.nMaxIQp = 51;
    config.stRCInfo.nIntraQpDelta = -2;
    config.nOffsetCropX = 0;
    config.nOffsetCropY = 0;
    config.nOffsetCropW = 0;
    config.nOffsetCropH = 0;

    for (VencChn = 0; VencChn < SAMPLE_VENC_CHN_NUM; VencChn++)
    {
        config.ePayloadType = (eVencType == VENC_CASE_H264) ? PT_H264 : PT_H265;
        switch (VencChn)
        {
        case 0:
            config.nInWidth = gCams[0].stChnAttr.tChnAttr[AX_YUV_SOURCE_ID_MAIN].nWidth;
            config.nInHeight = gCams[0].stChnAttr.tChnAttr[AX_YUV_SOURCE_ID_MAIN].nHeight;
            config.nStride = gCams[0].stChnAttr.tChnAttr[AX_YUV_SOURCE_ID_MAIN].nWidthStride;
            config.nSrcFrameRate = s_sample_framerate;
            config.nDstFrameRate = s_sample_framerate;
            break;
        case 1:
            config.nInWidth = 720;
            config.nInHeight = 576;
            config.nStride = 720;
            config.nSrcFrameRate = s_sample_framerate;
            config.nDstFrameRate = s_sample_framerate;
            break;
        }

        memset(&stVencChnAttr, 0, sizeof(AX_VENC_CHN_ATTR_S));

        stVencChnAttr.stVencAttr.u32MaxPicWidth = 0;
        stVencChnAttr.stVencAttr.u32MaxPicHeight = 0;

        stVencChnAttr.stVencAttr.u32PicWidthSrc = config.nInWidth;   /*the picture width*/
        stVencChnAttr.stVencAttr.u32PicHeightSrc = config.nInHeight; /*the picture height*/

        stVencChnAttr.stVencAttr.u32CropOffsetX = config.nOffsetCropX;
        stVencChnAttr.stVencAttr.u32CropOffsetY = config.nOffsetCropY;
        stVencChnAttr.stVencAttr.u32CropWidth = config.nOffsetCropW;
        stVencChnAttr.stVencAttr.u32CropHeight = config.nOffsetCropH;
        stVencChnAttr.stVencAttr.u32VideoRange = 1; /* 0: Narrow Range(NR), Y[16,235], Cb/Cr[16,240]; 1: Full Range(FR), Y/Cb/Cr[0,255] */

        ALOGN("VencChn %d:w:%d, h:%d, s:%d, Crop:(%d, %d, %d, %d) rcType:%d, payload:%d", gVencChnMapping[VencChn], stVencChnAttr.stVencAttr.u32PicWidthSrc, stVencChnAttr.stVencAttr.u32PicHeightSrc, config.nStride, stVencChnAttr.stVencAttr.u32CropOffsetX, stVencChnAttr.stVencAttr.u32CropOffsetY, stVencChnAttr.stVencAttr.u32CropWidth, stVencChnAttr.stVencAttr.u32CropHeight, config.stRCInfo.eRCType, config.ePayloadType);

        stVencChnAttr.stVencAttr.u32BufSize = config.nStride * config.nInHeight * 3 / 2; /*stream buffer size*/
        stVencChnAttr.stVencAttr.u32MbLinesPerSlice = 0;                                 /*get stream mode is slice mode or frame mode?*/
        stVencChnAttr.stVencAttr.enLinkMode = AX_LINK_MODE;
        stVencChnAttr.stVencAttr.u32GdrDuration = 0;
        /* GOP Setting */
        stVencChnAttr.stGopAttr.enGopMode = VENC_GOPMODE_NORMALP;

        stVencChnAttr.stVencAttr.enType = config.ePayloadType;
        switch (stVencChnAttr.stVencAttr.enType)
        {
        case PT_H265:
        {
            stVencChnAttr.stVencAttr.enProfile = VENC_HEVC_MAIN_PROFILE;
            stVencChnAttr.stVencAttr.enLevel = VENC_HEVC_LEVEL_6;
            stVencChnAttr.stVencAttr.enTier = VENC_HEVC_MAIN_TIER;

            if (config.stRCInfo.eRCType == VENC_RC_CBR)
            {
                AX_VENC_H265_CBR_S stH265Cbr;
                stVencChnAttr.stRcAttr.enRcMode = VENC_RC_MODE_H265CBR;
                stVencChnAttr.stRcAttr.s32FirstFrameStartQp = -1;
                stH265Cbr.u32Gop = config.nGOP;
                stH265Cbr.u32SrcFrameRate = config.nSrcFrameRate;  /* input frame rate */
                stH265Cbr.fr32DstFrameRate = config.nDstFrameRate; /* target frame rate */
                stH265Cbr.u32BitRate = config.nBitrate;
                stH265Cbr.u32MinQp = config.stRCInfo.nMinQp;
                stH265Cbr.u32MaxQp = config.stRCInfo.nMaxQp;
                stH265Cbr.u32MinIQp = config.stRCInfo.nMinIQp;
                stH265Cbr.u32MaxIQp = config.stRCInfo.nMaxIQp;
                stH265Cbr.s32IntraQpDelta = config.stRCInfo.nIntraQpDelta;
                memcpy(&stVencChnAttr.stRcAttr.stH265Cbr, &stH265Cbr, sizeof(AX_VENC_H265_CBR_S));
            }
            else if (config.stRCInfo.eRCType == VENC_RC_VBR)
            {
                AX_VENC_H265_VBR_S stH265Vbr;
                stVencChnAttr.stRcAttr.enRcMode = VENC_RC_MODE_H265VBR;
                stVencChnAttr.stRcAttr.s32FirstFrameStartQp = -1;
                stH265Vbr.u32Gop = config.nGOP;
                stH265Vbr.u32SrcFrameRate = config.nSrcFrameRate;
                stH265Vbr.fr32DstFrameRate = config.nDstFrameRate;
                stH265Vbr.u32MaxBitRate = config.nBitrate;
                stH265Vbr.u32MinQp = config.stRCInfo.nMinQp;
                stH265Vbr.u32MaxQp = config.stRCInfo.nMaxQp;
                stH265Vbr.u32MinIQp = config.stRCInfo.nMinIQp;
                stH265Vbr.u32MaxIQp = config.stRCInfo.nMaxIQp;
                stH265Vbr.s32IntraQpDelta = config.stRCInfo.nIntraQpDelta;
                memcpy(&stVencChnAttr.stRcAttr.stH265Vbr, &stH265Vbr, sizeof(AX_VENC_H265_VBR_S));
            }
            else if (config.stRCInfo.eRCType == VENC_RC_FIXQP)
            {
                AX_VENC_H265_FIXQP_S stH265FixQp;
                stVencChnAttr.stRcAttr.enRcMode = VENC_RC_MODE_H265FIXQP;
                stH265FixQp.u32Gop = config.nGOP;
                stH265FixQp.u32SrcFrameRate = config.nSrcFrameRate;
                stH265FixQp.fr32DstFrameRate = config.nDstFrameRate;
                stH265FixQp.u32IQp = 25;
                stH265FixQp.u32PQp = 30;
                stH265FixQp.u32BQp = 32;
                memcpy(&stVencChnAttr.stRcAttr.stH265FixQp, &stH265FixQp, sizeof(AX_VENC_H265_FIXQP_S));
            }
            break;
        }
        case PT_H264:
        {
            stVencChnAttr.stVencAttr.enProfile = VENC_H264_MAIN_PROFILE;
            stVencChnAttr.stVencAttr.enLevel = VENC_H264_LEVEL_5_2;

            if (config.stRCInfo.eRCType == VENC_RC_CBR)
            {
                AX_VENC_H264_CBR_S stH264Cbr;
                stVencChnAttr.stRcAttr.enRcMode = VENC_RC_MODE_H264CBR;
                stVencChnAttr.stRcAttr.s32FirstFrameStartQp = -1;
                stH264Cbr.u32Gop = config.nGOP;
                stH264Cbr.u32SrcFrameRate = config.nSrcFrameRate;  /* input frame rate */
                stH264Cbr.fr32DstFrameRate = config.nDstFrameRate; /* target frame rate */
                stH264Cbr.u32BitRate = config.nBitrate;
                stH264Cbr.u32MinQp = config.stRCInfo.nMinQp;
                stH264Cbr.u32MaxQp = config.stRCInfo.nMaxQp;
                stH264Cbr.u32MinIQp = config.stRCInfo.nMinIQp;
                stH264Cbr.u32MaxIQp = config.stRCInfo.nMaxIQp;
                stH264Cbr.s32IntraQpDelta = config.stRCInfo.nIntraQpDelta;
                memcpy(&stVencChnAttr.stRcAttr.stH264Cbr, &stH264Cbr, sizeof(AX_VENC_H264_CBR_S));
            }
            else if (config.stRCInfo.eRCType == VENC_RC_VBR)
            {
                AX_VENC_H264_VBR_S stH264Vbr;
                stVencChnAttr.stRcAttr.enRcMode = VENC_RC_MODE_H264VBR;
                stVencChnAttr.stRcAttr.s32FirstFrameStartQp = -1;
                stH264Vbr.u32Gop = config.nGOP;
                stH264Vbr.u32SrcFrameRate = config.nSrcFrameRate;
                stH264Vbr.fr32DstFrameRate = config.nDstFrameRate;
                stH264Vbr.u32MaxBitRate = config.nBitrate;
                stH264Vbr.u32MinQp = config.stRCInfo.nMinQp;
                stH264Vbr.u32MaxQp = config.stRCInfo.nMaxQp;
                stH264Vbr.u32MinIQp = config.stRCInfo.nMinIQp;
                stH264Vbr.u32MaxIQp = config.stRCInfo.nMaxIQp;
                stH264Vbr.s32IntraQpDelta = config.stRCInfo.nIntraQpDelta;
                memcpy(&stVencChnAttr.stRcAttr.stH264Vbr, &stH264Vbr, sizeof(AX_VENC_H264_VBR_S));
            }
            else if (config.stRCInfo.eRCType == VENC_RC_FIXQP)
            {
                AX_VENC_H264_FIXQP_S stH264FixQp;
                stVencChnAttr.stRcAttr.enRcMode = VENC_RC_MODE_H264FIXQP;
                stH264FixQp.u32Gop = config.nGOP;
                stH264FixQp.u32SrcFrameRate = config.nSrcFrameRate;
                stH264FixQp.fr32DstFrameRate = config.nDstFrameRate;
                stH264FixQp.u32IQp = 25;
                stH264FixQp.u32PQp = 30;
                stH264FixQp.u32BQp = 32;
                memcpy(&stVencChnAttr.stRcAttr.stH264FixQp, &stH264FixQp, sizeof(AX_VENC_H264_FIXQP_S));
            }
            break;
        }
        default:
            ALOGE("VencChn %d:Payload type unrecognized.", VencChn);
            return -1;
        }

        AX_S32 ret = AX_VENC_CreateChn(gVencChnMapping[VencChn], &stVencChnAttr);
        if (AX_SUCCESS != ret)
        {
            ALOGE("VencChn %d: AX_VENC_CreateChn failed, s32Ret:0x%x", VencChn, ret);
            return -1;
        }

        /* create get output stream thread */
        gGetStreamPara[VencChn].VeChn = gVencChnMapping[VencChn];
        gGetStreamPara[VencChn].bThreadStart = AX_TRUE;
        gGetStreamPara[VencChn].ePayloadType = config.ePayloadType;
        pthread_create(&gGetStreamPid[VencChn], NULL, VencGetStreamProc, (void *)&gGetStreamPara[VencChn]);
    }

    return 0;
}

static AX_S32 SampleVencDeInit()
{
    AX_S32 VencChn = 0, s32Ret = 0;

    for (VencChn = 0; VencChn < SAMPLE_VENC_CHN_NUM; VencChn++)
    {

        s32Ret = AX_VENC_StopRecvFrame(gVencChnMapping[VencChn]);
        if (0 != s32Ret)
        {
            ALOGE("VencChn %d:AX_VENC_StopRecvFrame failed,s32Ret:0x%x.", gVencChnMapping[VencChn], s32Ret);
            return s32Ret;
        }

        s32Ret = AX_VENC_DestroyChn(gVencChnMapping[VencChn]);
        if (0 != s32Ret)
        {
            ALOGE("VencChn %d:AX_VENC_DestroyChn failed,s32Ret:0x%x.", gVencChnMapping[VencChn], s32Ret);
            return s32Ret;
        }

        if (AX_TRUE == gGetStreamPara[VencChn].bThreadStart)
        {
            gGetStreamPara[VencChn].bThreadStart = AX_FALSE;
            pthread_join(gGetStreamPid[VencChn], NULL);
        }
    }
    s32Ret = AX_VENC_Deinit();
    if (AX_SUCCESS != s32Ret)
    {
        ALOGE("AX_VENC_Deinit failed, s32Ret=0x%x", s32Ret);
        return s32Ret;
    }

    return 0;
}

AX_VOID StartOverlay(AX_VOID)
{
    memset(&g_arrRgnThreadParam[0], 0, sizeof(IVPS_REGION_PARAM_T) * SAMPLE_REGION_COUNT);

    AX_S32 nRet = 0;
    AX_U8 nRgnIndex = 0;

    for (AX_U8 i = 0; i < SAMPLE_IVPS_GROUP_NUM; i++)
    {
        IVPS_RGN_HANDLE hChnRgn = AX_IVPS_RGN_Create();
        if (AX_IVPS_INVALID_REGION_HANDLE != hChnRgn)
        {
            AX_S32 nFilter = 0x11; // only chn0 fliter
            nRet = AX_IVPS_RGN_AttachToFilter(hChnRgn, i, nFilter);
            if (0 != nRet)
            {
                ALOGE("AX_IVPS_RGN_AttachToFilter(Grp: %d, Filter: 0x%x) failed, ret=0x%x", i, nFilter, nRet);
                return;
            }

            g_arrRgnThreadParam[nRgnIndex].hChnRgn = hChnRgn;
            g_arrRgnThreadParam[nRgnIndex].nGroup = i;
            g_arrRgnThreadParam[nRgnIndex].nFilter = nFilter;
            nRgnIndex++;

            ALOGN("AX_IVPS_RGN_AttachToFilter(Grp: %d, Filter: 0x%x) successfully, hChnRgn=%d.", i, nFilter, hChnRgn);
        }
        else
        {
            ALOGE("AX_IVPS_RGN_Create(Grp: %d) failed.", i);
        }
    }
}

AX_VOID StopOverlay(AX_VOID)
{
    AX_S32 nRet = 0;
    AX_U8 nRgnIndex = 0;

    for (AX_U8 i = 0; i < SAMPLE_REGION_COUNT; i++)
    {
        g_arrRgnThreadParam[i].bExit = AX_TRUE;
    }

    for (AX_U8 i = 0; i < SAMPLE_IVPS_GROUP_NUM; i++)
    {
        AX_S32 nFilter = 0x11; // only chn0 fliter
        nRet = AX_IVPS_RGN_DetachFromFilter(g_arrRgnThreadParam[nRgnIndex].hChnRgn, i, nFilter);
        if (0 != nRet)
        {
            ALOGE("AX_IVPS_RGN_DetachFromFilter(Grp: %d, Filter: %x) failed, ret=0x%x", i, nFilter, nRet);
        }

        nRet = AX_IVPS_RGN_Destroy(g_arrRgnThreadParam[nRgnIndex].hChnRgn);
        if (0 != nRet)
        {
            ALOGE("AX_IVPS_RGN_Destroy(Grp: %d) failed, ret=0x%x", i, nRet);
        }

        nRgnIndex++;
    }
}

static int SampleIvpsInit()
{
    AX_S32 s32Ret = 0;
    AX_S32 nGrp = 0, nChn = 0;
    AX_IVPS_GRP_ATTR_S stGrpAttr = {0};
    AX_IVPS_PIPELINE_ATTR_S stPipelineAttr = {0};

    s32Ret = AX_IVPS_Init();
    if (0 != s32Ret)
    {
        ALOGE("AX_IVPS_Init failed,s32Ret:0x%x", s32Ret);
        return s32Ret;
    }

    stPipelineAttr.tFbInfo.PoolId = AX_INVALID_POOLID;
    stPipelineAttr.nOutChnNum = s_sample_ivps_chn_num[nGrp];

    for (nGrp = 0; nGrp < SAMPLE_IVPS_GROUP_NUM; nGrp++)
    {
        stGrpAttr.nInFifoDepth = 1;
        stGrpAttr.ePipeline = AX_IVPS_PIPELINE_DEFAULT;
        s32Ret = AX_IVPS_CreateGrp(nGrp, &stGrpAttr);
        if (0 != s32Ret)
        {
            ALOGE("AX_IVPS_CreateGrp failed,nGrp %d,s32Ret:0x%x", nGrp, s32Ret);
            return s32Ret;
        }

        memset(&stPipelineAttr.tFilter, 0x00, sizeof(stPipelineAttr.tFilter));

        if (s_sample_ldc_mode || s_sample_gdc_dewarp != AX_IVPS_DEWARP_BUTT)
        {
            if (nGrp == 0)
            {
                stPipelineAttr.tFilter[0][0].bEnable = AX_TRUE;
                stPipelineAttr.tFilter[0][0].tFRC.nSrcFrameRate = s_sample_framerate;
                stPipelineAttr.tFilter[0][0].tFRC.nDstFrameRate = s_sample_framerate;
                stPipelineAttr.tFilter[0][0].nDstPicOffsetX0 = 0;
                stPipelineAttr.tFilter[0][0].nDstPicOffsetY0 = 0;
                stPipelineAttr.tFilter[0][0].nDstPicWidth = gCams[0].stChnAttr.tChnAttr[AX_YUV_SOURCE_ID_MAIN].nWidth;
                stPipelineAttr.tFilter[0][0].nDstPicHeight = gCams[0].stChnAttr.tChnAttr[AX_YUV_SOURCE_ID_MAIN].nHeight;
                stPipelineAttr.tFilter[0][0].nDstPicStride = ALIGN_UP(gCams[0].stChnAttr.tChnAttr[AX_YUV_SOURCE_ID_MAIN].nWidthStride, 64);
                stPipelineAttr.tFilter[0][0].nDstFrameWidth = gCams[0].stChnAttr.tChnAttr[AX_YUV_SOURCE_ID_MAIN].nWidth;
                stPipelineAttr.tFilter[0][0].nDstFrameHeight = gCams[0].stChnAttr.tChnAttr[AX_YUV_SOURCE_ID_MAIN].nHeight;
                stPipelineAttr.tFilter[0][0].eDstPicFormat = AX_YUV420_SEMIPLANAR;
                stPipelineAttr.tFilter[0][0].eEngine = AX_IVPS_ENGINE_GDC;
                stPipelineAttr.tFilter[0][0].tGdcCfg.bDewarpEnable = AX_TRUE;
                // if (s_sample_ldc_mode && s_sample_gdc_dewarp == AX_IVPS_DEWARP_PERSPECTIVE) {
                //     stPipelineAttr.tFilter[0][0].tGdcCfg.bDewarpType = AX_IVPS_DEWARP_LDC_PERSPECTIVE;
                // }
                // else if (s_sample_ldc_mode) {
                //     stPipelineAttr.tFilter[0][0].tGdcCfg.bDewarpType = AX_IVPS_DEWARP_LDC;
                // }
                // else if (s_sample_gdc_dewarp == AX_IVPS_DEWARP_PERSPECTIVE) {
                //     stPipelineAttr.tFilter[0][0].tGdcCfg.bDewarpType = AX_IVPS_DEWARP_PERSPECTIVE;
                // }

                if (s_sample_ldc_mode)
                {
                    stPipelineAttr.tFilter[0][0].tGdcCfg.tLdcFactor.bAspect = AX_FALSE;
                    stPipelineAttr.tFilter[0][0].tGdcCfg.tLdcFactor.nXRatio = (AX_S16)s_sample_ldc_xRatio;
                    stPipelineAttr.tFilter[0][0].tGdcCfg.tLdcFactor.nYRatio = (AX_S16)s_sample_ldc_yRatio;
                    stPipelineAttr.tFilter[0][0].tGdcCfg.tLdcFactor.nXYRatio = 0;
                    stPipelineAttr.tFilter[0][0].tGdcCfg.tLdcFactor.nCenterXOffset = 0;
                    stPipelineAttr.tFilter[0][0].tGdcCfg.tLdcFactor.nCenterYOffset = 0;
                    stPipelineAttr.tFilter[0][0].tGdcCfg.tLdcFactor.nDistortionRatio = (AX_S16)s_sample_ldc_dRatio;
                }

                // if (s_sample_gdc_dewarp == AX_IVPS_DEWARP_PERSPECTIVE) {
                //     memcpy(stPipelineAttr.tFilter[0][0].tGdcCfg.tPerspectiveFactor.nPerspectiveMatrix,
                //             s_sample_gdc_perspective_matrix,
                //             sizeof(stPipelineAttr.tFilter[0][0].tGdcCfg.tPerspectiveFactor.nPerspectiveMatrix));
                //     stPipelineAttr.tFilter[0][0].tGdcCfg.tPerspectiveFactor.bMatrixInverse = AX_FALSE;
                //     stPipelineAttr.tFilter[0][0].tGdcCfg.tPerspectiveFactor.nEncryptionType = 1;
                // }
                stPipelineAttr.tFilter[0][0].tGdcCfg.bEnhanceMode = AX_TRUE;
                stPipelineAttr.tFilter[0][0].tGdcCfg.eRotation = AX_IVPS_ROTATION_0;
                stPipelineAttr.tFilter[0][0].tGdcCfg.bMirror = AX_FALSE;
                stPipelineAttr.tFilter[0][0].tGdcCfg.bFlip = AX_FALSE;
            }
        }

        for (nChn = 0; nChn < s_sample_ivps_chn_num[nGrp]; nChn++)
        {
            if (nGrp == 0)
            {
                if (nChn == 0)
                {
                    stPipelineAttr.tFilter[nChn + 1][0].bEnable = AX_TRUE;
                    stPipelineAttr.tFilter[nChn + 1][0].tFRC.nSrcFrameRate = s_sample_framerate;
                    stPipelineAttr.tFilter[nChn + 1][0].tFRC.nDstFrameRate = s_sample_framerate;
                    stPipelineAttr.tFilter[nChn + 1][0].nDstPicOffsetX0 = 0;
                    stPipelineAttr.tFilter[nChn + 1][0].nDstPicOffsetY0 = 0;
                    stPipelineAttr.tFilter[nChn + 1][0].nDstPicWidth = gCams[0].stChnAttr.tChnAttr[AX_YUV_SOURCE_ID_MAIN].nWidth;
                    stPipelineAttr.tFilter[nChn + 1][0].nDstPicHeight = gCams[0].stChnAttr.tChnAttr[AX_YUV_SOURCE_ID_MAIN].nHeight;
                    stPipelineAttr.tFilter[nChn + 1][0].nDstPicStride = ALIGN_UP(gCams[0].stChnAttr.tChnAttr[AX_YUV_SOURCE_ID_MAIN].nWidthStride, 64);
                    stPipelineAttr.tFilter[nChn + 1][0].nDstFrameWidth = gCams[0].stChnAttr.tChnAttr[AX_YUV_SOURCE_ID_MAIN].nWidth;
                    stPipelineAttr.tFilter[nChn + 1][0].nDstFrameHeight = gCams[0].stChnAttr.tChnAttr[AX_YUV_SOURCE_ID_MAIN].nHeight;
                    stPipelineAttr.tFilter[nChn + 1][0].eDstPicFormat = AX_YUV420_SEMIPLANAR;
                    stPipelineAttr.tFilter[nChn + 1][0].eEngine = AX_IVPS_ENGINE_TDP;
                    stPipelineAttr.tFilter[nChn + 1][0].tGdcCfg.eRotation = AX_IVPS_ROTATION_0;

                    stPipelineAttr.tFilter[nChn + 1][1].bEnable = AX_TRUE;
                    stPipelineAttr.tFilter[nChn + 1][1].tFRC.nSrcFrameRate = s_sample_framerate;
                    stPipelineAttr.tFilter[nChn + 1][1].tFRC.nDstFrameRate = s_sample_framerate;
                    stPipelineAttr.tFilter[nChn + 1][1].nDstPicOffsetX0 = 0;
                    stPipelineAttr.tFilter[nChn + 1][1].nDstPicOffsetY0 = 0;
                    stPipelineAttr.tFilter[nChn + 1][1].nDstPicWidth = gCams[0].stChnAttr.tChnAttr[AX_YUV_SOURCE_ID_MAIN].nWidth;
                    stPipelineAttr.tFilter[nChn + 1][1].nDstPicHeight = gCams[0].stChnAttr.tChnAttr[AX_YUV_SOURCE_ID_MAIN].nHeight;
                    stPipelineAttr.tFilter[nChn + 1][1].nDstPicStride = ALIGN_UP(gCams[0].stChnAttr.tChnAttr[AX_YUV_SOURCE_ID_MAIN].nWidthStride, 64);
                    stPipelineAttr.tFilter[nChn + 1][1].nDstFrameWidth = gCams[0].stChnAttr.tChnAttr[AX_YUV_SOURCE_ID_MAIN].nWidth;
                    stPipelineAttr.tFilter[nChn + 1][1].nDstFrameHeight = gCams[0].stChnAttr.tChnAttr[AX_YUV_SOURCE_ID_MAIN].nHeight;
                    stPipelineAttr.tFilter[nChn + 1][1].eDstPicFormat = AX_YUV420_SEMIPLANAR;
                    stPipelineAttr.tFilter[nChn + 1][1].eEngine = AX_IVPS_ENGINE_VO;
                    stPipelineAttr.nOutFifoDepth[nChn] = 0;
                }
                else if (nChn == 1)
                {
                    stPipelineAttr.tFilter[nChn + 1][0].bEnable = AX_TRUE;
                    stPipelineAttr.tFilter[nChn + 1][0].tFRC.nSrcFrameRate = s_sample_framerate;
                    stPipelineAttr.tFilter[nChn + 1][0].tFRC.nDstFrameRate = s_sample_framerate;
                    stPipelineAttr.tFilter[nChn + 1][0].nDstPicOffsetX0 = 0;
                    stPipelineAttr.tFilter[nChn + 1][0].nDstPicOffsetY0 = 0;
                    stPipelineAttr.tFilter[nChn + 1][0].nDstPicWidth = 1920;
                    stPipelineAttr.tFilter[nChn + 1][0].nDstPicHeight = 1080;
                    stPipelineAttr.tFilter[nChn + 1][0].nDstPicStride = ALIGN_UP(stPipelineAttr.tFilter[nChn + 1][0].nDstPicWidth, 64);
                    stPipelineAttr.tFilter[nChn + 1][0].nDstFrameWidth = 1920;
                    stPipelineAttr.tFilter[nChn + 1][0].nDstFrameHeight = 1080;
                    stPipelineAttr.tFilter[nChn + 1][0].eDstPicFormat = AX_YUV420_SEMIPLANAR;
                    stPipelineAttr.tFilter[nChn + 1][0].eEngine = AX_IVPS_ENGINE_TDP;
                    stPipelineAttr.tFilter[nChn + 1][0].tTdpCfg.eRotation = AX_IVPS_ROTATION_0;
                    stPipelineAttr.nOutFifoDepth[nChn] = 1; // for AI runjoint
                }
            }
            else if (nGrp == 1)
            {
                stPipelineAttr.tFilter[nChn + 1][0].bEnable = AX_TRUE;
                stPipelineAttr.tFilter[nChn + 1][0].tFRC.nSrcFrameRate = s_sample_framerate;
                stPipelineAttr.tFilter[nChn + 1][0].tFRC.nDstFrameRate = s_sample_framerate;
                stPipelineAttr.tFilter[nChn + 1][0].nDstPicOffsetX0 = 0;
                stPipelineAttr.tFilter[nChn + 1][0].nDstPicOffsetY0 = 0;
                stPipelineAttr.tFilter[nChn + 1][0].nDstPicWidth = 720;
                stPipelineAttr.tFilter[nChn + 1][0].nDstPicHeight = 576;
                stPipelineAttr.tFilter[nChn + 1][0].nDstPicStride = ALIGN_UP(stPipelineAttr.tFilter[nChn + 1][0].nDstPicWidth, 64);
                stPipelineAttr.tFilter[nChn + 1][0].nDstFrameWidth = 720;
                stPipelineAttr.tFilter[nChn + 1][0].nDstFrameHeight = 576;
                stPipelineAttr.tFilter[nChn + 1][0].eDstPicFormat = AX_YUV420_SEMIPLANAR;
                stPipelineAttr.tFilter[nChn + 1][0].eEngine = AX_IVPS_ENGINE_TDP;
                stPipelineAttr.tFilter[nChn + 1][0].tTdpCfg.eRotation = AX_IVPS_ROTATION_0;

                stPipelineAttr.tFilter[nChn + 1][1].bEnable = AX_TRUE;
                stPipelineAttr.tFilter[nChn + 1][1].tFRC.nSrcFrameRate = s_sample_framerate;
                stPipelineAttr.tFilter[nChn + 1][1].tFRC.nDstFrameRate = s_sample_framerate;
                stPipelineAttr.tFilter[nChn + 1][1].nDstPicOffsetX0 = 0;
                stPipelineAttr.tFilter[nChn + 1][1].nDstPicOffsetY0 = 0;
                stPipelineAttr.tFilter[nChn + 1][1].nDstPicWidth = 720;
                stPipelineAttr.tFilter[nChn + 1][1].nDstPicHeight = 576;
                stPipelineAttr.tFilter[nChn + 1][1].nDstPicStride = ALIGN_UP(stPipelineAttr.tFilter[nChn + 1][0].nDstPicWidth, 64);
                stPipelineAttr.tFilter[nChn + 1][1].nDstFrameWidth = 720;
                stPipelineAttr.tFilter[nChn + 1][1].nDstFrameHeight = 576;
                stPipelineAttr.tFilter[nChn + 1][1].eDstPicFormat = AX_YUV420_SEMIPLANAR;
                stPipelineAttr.tFilter[nChn + 1][1].eEngine = AX_IVPS_ENGINE_VO;
                stPipelineAttr.nOutFifoDepth[nChn] = 0;
            }

            s32Ret = AX_IVPS_SetPipelineAttr(nGrp, &stPipelineAttr);
            if (0 != s32Ret)
            {
                ALOGE("AX_IVPS_SetPipelineAttr failed,nGrp %d,s32Ret:0x%x", nGrp, s32Ret);
                return s32Ret;
            }

            s32Ret = AX_IVPS_EnableChn(nGrp, nChn);
            if (0 != s32Ret)
            {
                ALOGE("AX_IVPS_EnableChn failed,nGrp %d,nChn %d,s32Ret:0x%x", nGrp, nChn, s32Ret);
                return s32Ret;
            }
        }

        s32Ret = AX_IVPS_StartGrp(nGrp);
        if (0 != s32Ret)
        {
            ALOGE("AX_IVPS_StartGrp failed,nGrp %d,s32Ret:0x%x", nGrp, s32Ret);
            return s32Ret;
        }
    }

    StartOverlay();

    return 0;
}

static AX_S32 SampleIvpsDeInit()
{
    AX_S32 s32Ret = 0, nGrp = 0, nChn = 0;

    for (nGrp = 0; nGrp < SAMPLE_IVPS_GROUP_NUM; nGrp++)
    {
        s32Ret = AX_IVPS_StopGrp(nGrp);
        if (0 != s32Ret)
        {
            ALOGE("AX_IVPS_StopGrp failed,nGrp %d,s32Ret:0x%x", nGrp, s32Ret);
            return s32Ret;
        }

        for (nChn = 0; nChn < s_sample_ivps_chn_num[nGrp]; nChn++)
        {
            s32Ret = AX_IVPS_DisableChn(nGrp, nChn);
            if (0 != s32Ret)
            {
                ALOGE("AX_IVPS_DisableChn failed,nGrp %d,nChn %d,s32Ret:0x%x", nGrp, nChn, s32Ret);
                return s32Ret;
            }
        }

        s32Ret = AX_IVPS_DestoryGrp(nGrp);
        if (0 != s32Ret)
        {
            ALOGE("AX_IVPS_DestoryGrp failed,nGrp %d,s32Ret:0x%x", nGrp, s32Ret);
            return s32Ret;
        }
    }

    s32Ret = AX_IVPS_Deinit();
    if (0 != s32Ret)
    {
        ALOGE("AX_IVPS_Deinit failed,s32Ret:0x%x", s32Ret);
        return s32Ret;
    }

    return 0;
}

static AX_VOID *GetFrameThreadForAI(AX_VOID *pThreadParam)
{
    IVPS_GET_THREAD_PARAM_T *t = (IVPS_GET_THREAD_PARAM_T *)pThreadParam;

    IVPS_GRP IvpsGrp = t->nIvpsGrp;
    IVPS_CHN IvpsChn = t->nIvpsChn;
    IVPS_CHN nIvpsIndex = t->nIvpsIndex;

    prctl(PR_SET_NAME, "SAMPLE_IVPS_GET_AI");

    while (!gLoopExit)
    {
        AX_VIDEO_FRAME_S tVideoFrame;

        AX_S32 ret = AX_IVPS_GetChnFrame(IvpsGrp, IvpsChn, &tVideoFrame, -1);

        if (0 != ret)
        {
            if (AX_ERR_IVPS_BUF_EMPTY == ret)
            {
                usleep(1000);
                continue;
            }
            usleep(1000);
            continue;
        }

        tVideoFrame.u64VirAddr[0] = (AX_U32)AX_POOL_GetBlockVirAddr(tVideoFrame.u32BlkId[0]);
        tVideoFrame.u64PhyAddr[0] = AX_POOL_Handle2PhysAddr(tVideoFrame.u32BlkId[0]);
        tVideoFrame.u32FrameSize = tVideoFrame.u32PicStride[0] * tVideoFrame.u32Height * 3 / 2;

        if (gJointHandle)
        {
            sampleRunJoint_RESULT_S *pResult = NULL;

            ret = SampleRunJointProcess(gJointHandle, (const AX_VIDEO_FRAME_S *)&tVideoFrame, &pResult);

            pthread_mutex_lock(&g_result_mutex);
            if(s32ModelType == 1){
                if (0 == ret && pResult)
            {
                memset(&g_sample_algo_result, 0x00, sizeof(g_sample_algo_result));

                AX_U8 i,j = 0;
                for (i = 0; i < pResult->nObjectSize && i < SAMPLE_RECT_BOX_COUNT; i++)
                {
                    g_sample_algo_result.rect[i].fX = pResult->pstObjectItems[i].stRect.x / pResult->nWidth;
                    g_sample_algo_result.rect[i].fY = pResult->pstObjectItems[i].stRect.y / pResult->nHeight;
                    g_sample_algo_result.rect[i].fW = pResult->pstObjectItems[i].stRect.w / pResult->nWidth;
                    g_sample_algo_result.rect[i].fH = pResult->pstObjectItems[i].stRect.h / pResult->nHeight;
                    for(j =0;j < pResult->pstObjectItems[i].nLandmark;j++){
                        g_sample_algo_result.pts[i].point[j].fX = pResult->pstObjectItems[i].landmark[j].x / pResult->nWidth;
                        g_sample_algo_result.pts[i].point[j].fY = pResult->pstObjectItems[i].landmark[j].y / pResult->nHeight;
                        g_sample_algo_result.pts[i].point[j].score = pResult->pstObjectItems[i].landmark[j].score;
                    }
                    
                }
                

                g_sample_algo_result.nSize = i;
            }

            
            ALOGN("==========================================");
            ALOGN("Object Size: %u\n", pResult->nObjectSize);
            ALOGN("Width: %d\n", pResult->nWidth);
            ALOGN("Height: %d\n", pResult->nHeight);
            for (int i = 0; i < pResult->nObjectSize; i++)
            {
                const sampleRunJoint_OBJECT_ITEM_S *objectItem = &(pResult->pstObjectItems[i]);
                ALOGN("Object %u - Label ID: %u, Label Name: %s, Probability: %f",
                      i, objectItem->labelId, objectItem->labelName, objectItem->prob);

                ALOGN("  Rectangle: X=%f, Y=%f, Width=%f, Height=%f",
                      objectItem->stRect.x, objectItem->stRect.y, objectItem->stRect.w, objectItem->stRect.h);

                ALOGN("  Landmarks:");
                for (AX_U32 j = 0; j < objectItem->nLandmark; ++j)
                {
                    const sampleRunJoint_POINT_S *landmark = &(objectItem->landmark[j]);
                    ALOGN("    Landmark %u - X=%f, Y=%f, Score=%f",
                          j, landmark->x, landmark->y, landmark->score);
                }
            }
            ALOGN("==========================================");

            }
            
            pthread_mutex_unlock(&g_result_mutex);

            if (pResult)
            {
                SampleRunJointReleaseResult(pResult);
            }
        }

        ret = AX_IVPS_ReleaseChnFrame(IvpsGrp, IvpsChn, &tVideoFrame);
    }

    return (AX_VOID *)0;
}

AX_VOID *RgnThreadFunc(AX_VOID *pArg)
{
    if (!pArg)
    {
        return (AX_VOID *)0;
    }

    prctl(PR_SET_NAME, "SAMPLE_IVPS_RGN");

    RGN_GROUP_CFG_T tRgnGroupConfig[SAMPLE_REGION_COUNT] = {
        {0, 0x11, 2688, 1520, SAMPLE_RECT_BOX_COUNT, AX_IVPS_RGN_LAYER_COVER},
        {1, 0x11, 720, 576, SAMPLE_RECT_BOX_COUNT, AX_IVPS_RGN_LAYER_COVER},
    };

    IVPS_REGION_PARAM_PTR pThreadParam = (IVPS_REGION_PARAM_PTR)pArg;
    AX_IVPS_FILTER nFilter = pThreadParam->nFilter;
    IVPS_GRP nIvpsGrp = pThreadParam->nGroup;

    AX_U8 nCfgIndex = -1;
    for (AX_U32 i = 0; i < SAMPLE_REGION_COUNT; ++i)
    {
        if (nIvpsGrp == tRgnGroupConfig[i].nGroup && nFilter == tRgnGroupConfig[i].nFilter)
        {
            nCfgIndex = i;
            break;
        }
    }

    if (-1 == nCfgIndex)
    {
        ALOGE("Can not find OSD configuration for group %d, filter 0x%x", nIvpsGrp, nFilter);
        return (AX_VOID *)0;
    }

    ALOGN("[%d][0x%02x] +++", nIvpsGrp, nFilter);

    AX_S32 ret = 0;
    pThreadParam->bExit = AX_FALSE;
    while (!pThreadParam->bExit && !gLoopExit)
    {
        RGN_GROUP_CFG_T *tGrpCfg = &tRgnGroupConfig[nCfgIndex];
        if (0 == tGrpCfg->nRgnNum)
        {
            break;
        }

        AX_IVPS_RGN_DISP_GROUP_S tDisp;
        memset(&tDisp, 0, sizeof(AX_IVPS_RGN_DISP_GROUP_S));

        tDisp.nNum = tGrpCfg->nRgnNum;
        tDisp.tChnAttr.nAlpha = 1024;
        tDisp.tChnAttr.eFormat = AX_FORMAT_ARGB1555;
        tDisp.tChnAttr.nZindex = nIvpsGrp + 1;
        tDisp.tChnAttr.nBitColor.nColor = 0xFF0000;
        tDisp.tChnAttr.nBitColor.bEnable = AX_FALSE;
        tDisp.tChnAttr.nBitColor.nColorInv = 0xFF;
        tDisp.tChnAttr.nBitColor.nColorInvThr = 0xA0A0A0;

        pthread_mutex_lock(&g_result_mutex);
        for (AX_U8 i = 0; i < tDisp.nNum; ++i)
        {
            if (i < g_sample_algo_result.nSize)
            {
                tDisp.arrDisp[i].bShow = AX_TRUE;
                tDisp.arrDisp[i].eType = AX_IVPS_RGN_TYPE_RECT;

                AX_U32 nChnWidth = tGrpCfg->nChnWidth;
                AX_U32 nChnHeight = tGrpCfg->nChnHeight;

                tDisp.arrDisp[i].uDisp.tPolygon.tRect.nX = (AX_U32)(g_sample_algo_result.rect[i].fX * nChnWidth);
                tDisp.arrDisp[i].uDisp.tPolygon.tRect.nY = (AX_U32)(g_sample_algo_result.rect[i].fY * nChnHeight);
                tDisp.arrDisp[i].uDisp.tPolygon.tRect.nW = (AX_U32)(g_sample_algo_result.rect[i].fW * nChnWidth);
                tDisp.arrDisp[i].uDisp.tPolygon.tRect.nH = (AX_U32)(g_sample_algo_result.rect[i].fH * nChnHeight);
                tDisp.arrDisp[i].uDisp.tPolygon.bSolid = AX_FALSE;
                tDisp.arrDisp[i].uDisp.tPolygon.nLineWidth = 2;
                tDisp.arrDisp[i].uDisp.tPolygon.nColor = GREEN;
                tDisp.arrDisp[i].uDisp.tPolygon.nAlpha = 255;
                
            }
            else
            {
                tDisp.arrDisp[i].bShow = AX_FALSE;
            }
        }
        pthread_mutex_unlock(&g_result_mutex);

        ret = AX_IVPS_RGN_Update(pThreadParam->hChnRgn, &tDisp);
        if (0 != ret)
        {
            ALOGE("[%d][0x%02x] AX_IVPS_RGN_Update fail, ret=0x%x, hChnRgn=%d", nIvpsGrp, nFilter, ret, pThreadParam->hChnRgn);
        }

        usleep(60000);
    }

    ALOGN("[%d][0x%02x] ---", nIvpsGrp, nFilter);

    return (AX_VOID *)0;
}

AX_S32 IVPS_ThreadStart(AX_VOID *p)
{
    pthread_t tid = 0;

    // AI
    if (0 != pthread_create(&tid, NULL, GetFrameThreadForAI, (AX_VOID *)&s_sample_ivps_thread_param[1]))
    {

        return -1;
    }

    pthread_detach(tid);

    /* Start region thread */
    for (AX_U8 i = 0; i < SAMPLE_REGION_COUNT; i++)
    {
        if (AX_IVPS_INVALID_REGION_HANDLE != g_arrRgnThreadParam[i].hChnRgn)
        {
            if (0 != pthread_create(&tid, NULL, RgnThreadFunc, (AX_VOID *)&g_arrRgnThreadParam[i]))
            {
                return -1;
            }

            pthread_detach(tid);
        }
    }

    return 0;
}

static AX_VOID *GetYuvFrameThread(AX_VOID *p)
{
    AX_YUV_SOURCE_ID_E nChn = AX_YUV_SOURCE_ID_MAIN;
    AX_U64 nPhyaddr = 0;
    AX_VOID *pViraddr = NULL;

    prctl(PR_SET_NAME, "SAMPLE_YUV_GET");

    FILE *fp = fopen(s_sample_strInputYuvFile, "rb");
    if (fp)
    {
        fseek(fp, 0, SEEK_END);
        AX_U32 nFileSize = ftell(fp);
        fseek(fp, 0, SEEK_SET);

        AX_S32 ret = AX_SYS_MemAlloc(&nPhyaddr, &pViraddr, nFileSize, 256, NULL);

        if (0 == ret)
        {
            AX_U64 *pData = (AX_U64 *)pViraddr;
            fread(pData, nFileSize, 1, fp);
        }

        fclose(fp);
    }

    AXI_DMA_HAND_T nDMAHandle = AX_HAL_DMA_ChnOpen();

    while (!gLoopExit)
    {
        AX_IMG_INFO_T tImgInfo;
        AX_S32 nRet = AX_VIN_GetYuvFrame(0, nChn, &tImgInfo, -1);

        if (0 != nRet)
        {
            continue;
        }

        AX_VIDEO_FRAME_S tDstFrame;
        AX_BLK BlkId = 0;
        AX_U32 nImgSize = 0;
        AX_U64 nPhyAddr = 0;

        memset(&tDstFrame, 0, sizeof(tDstFrame));

        nImgSize = CalcImgSize(tImgInfo.tFrameInfo.stVFrame.u32PicStride[0],
                               tImgInfo.tFrameInfo.stVFrame.u32Width,
                               tImgInfo.tFrameInfo.stVFrame.u32Height,
                               AX_YUV420_SEMIPLANAR,
                               16);

        nRet = BufPoolBlockAddrGet(AX_INVALID_POOLID, nImgSize, &tDstFrame.u64PhyAddr[0], (AX_VOID **)(&tDstFrame.u64VirAddr[0]), &BlkId);

        if (nRet != 0)
        {
            ALOGE("BufPoolBlockAddrGet fail");
            AX_VIN_ReleaseYuvFrame(0, nChn, &tImgInfo);
            continue;
        }

        AX_POOL_IncreaseRefCnt(BlkId, AX_ID_USER);

        AXI_DMA_CFG_T tDMACfg;
        AXI_DMA_XFER_T tDMAXferCfg;

        memset(&tDMACfg, 0x00, sizeof(tDMACfg));
        memset(&tDMAXferCfg, 0x00, sizeof(tDMAXferCfg));

        tDMACfg.lli_num = 1;
        tDMACfg.direction = AX_DMA_MEM_TO_MEM;
        tDMACfg.xfer_mode = AX_DMA_TRF_SINGLE;
        tDMAXferCfg.dst_addr = tDstFrame.u64PhyAddr[0];
        tDMAXferCfg.src_addr = nPhyaddr;
        tDMAXferCfg.xfer_len = nImgSize;
        AX_HAL_DMA_ChnCfg(nDMAHandle, &tDMACfg, &tDMAXferCfg);

        AX_HAL_DMA_ChnStart(nDMAHandle);

        while (AX_HAL_DMA_ChnWaitDone(nDMAHandle, AX_DMA_BROKED_MODE, AX_DMA_WAITING_FOREVER) != 0)
            ;

        AX_VIN_ReleaseYuvFrame(0, nChn, &tImgInfo);

        tDstFrame.enImgFormat = AX_YUV420_SEMIPLANAR;
        tDstFrame.u32Width = tImgInfo.tFrameInfo.stVFrame.u32Width;
        tDstFrame.u32Height = tImgInfo.tFrameInfo.stVFrame.u32Height;
        tDstFrame.u32PicStride[0] = tImgInfo.tFrameInfo.stVFrame.u32PicStride[0];
        tDstFrame.u32PicStride[1] = tImgInfo.tFrameInfo.stVFrame.u32PicStride[1];
        tDstFrame.u32PicStride[2] = tImgInfo.tFrameInfo.stVFrame.u32PicStride[2];
        tDstFrame.u32FrameSize = nImgSize;
        tDstFrame.u32BlkId[0] = BlkId;
        tDstFrame.u64PhyAddr[1] = tDstFrame.u64PhyAddr[0] + tDstFrame.u32PicStride[0] * tDstFrame.u32Height;

        nRet = AX_IVPS_SendFrame(0, &tDstFrame, -1);

        if (nRet != 0)
        {
            ALOGE("AX_IVPS_SendFrame fail nRet=%x", nRet);
            continue;
        }

        if (BlkId != 0)
        {
            AX_POOL_DecreaseRefCnt(BlkId, AX_ID_USER);
            nRet = AX_POOL_ReleaseBlock(BlkId);
        }
    }

    if (nPhyaddr != 0)
    {
        AX_SYS_MemFree(nPhyaddr, pViraddr);
    }

    AX_HAL_DMA_ChnClose(nDMAHandle);

    return (AX_VOID *)0;
}

AX_S32 VIN_ThreadStart(AX_VOID)
{
    pthread_t tid = 0;

    if (0 != pthread_create(&tid, NULL, GetYuvFrameThread, NULL))
    {
        return -1;
    }

    return 0;
}

static void *IspRun(void *args)
{
    AX_U32 i = (AX_U32)args;

    ALOGN("cam %d is running...", i);

    while (!g_isp_force_loop_exit)
    {
        if (!gCams[i].bOpen)
        {
            usleep(40 * 1000);
            continue;
        }

        AX_ISP_Run(gCams[i].nPipeId);
    }
    return NULL;
}

static AX_S32 SysRun()
{
    AX_S32 s32Ret = 0, i;

    g_isp_force_loop_exit = 0;
    for (i = 0; i < MAX_CAMERAS; i++)
    {
        if (gCams[i].bOpen)
            pthread_create(&gCams[i].tIspProcThread, NULL, IspRun, (AX_VOID *)i);
    }

    while (!gLoopExit)
    {
        sleep(1);
    }

    g_isp_force_loop_exit = 1;

    for (i = 0; i < MAX_CAMERAS; i++)
    {
        if (gCams[i].bOpen)
        {
            pthread_cancel(gCams[i].tIspProcThread);
            s32Ret = pthread_join(gCams[i].tIspProcThread, NULL);
            if (s32Ret < 0)
            {
                ALOGE(" isp run thread exit failed,s32Ret:0x%x\n", s32Ret);
            }
        }
    }
    return 0;
}

static AX_VOID __sigint(int iSigNo)
{
    // ALOGN("Catch signal %d!\n", iSigNo);
    gLoopExit = 1;
    return;
}

static AX_S32 LinkControl(AX_BOOL bLink)
{
    /*
    VIN --> IVPS
    (ModId   GrpId   ChnId) | (ModId   GrpId   ChnId)
    --------------------------------------------------
    (VIN        0       1) -> (IVPS     1       0)
    (VIN        0       0) -> (IVPS     0       0)
    */
    AX_MOD_INFO_S srcMod, dstMod;

    if (!s_sample_strInputYuvFile)
    {
        srcMod.enModId = AX_ID_VIN;
        srcMod.s32GrpId = 0;
        srcMod.s32ChnId = 0;

        dstMod.enModId = AX_ID_IVPS;
        dstMod.s32GrpId = 0;
        dstMod.s32ChnId = 0;
        bLink ? AX_SYS_Link(&srcMod, &dstMod) : AX_SYS_UnLink(&srcMod, &dstMod);
    }

    srcMod.enModId = AX_ID_VIN;
    srcMod.s32GrpId = 0;
    srcMod.s32ChnId = 1;

    dstMod.enModId = AX_ID_IVPS;
    dstMod.s32GrpId = 1;
    dstMod.s32ChnId = 0;
    bLink ? AX_SYS_Link(&srcMod, &dstMod) : AX_SYS_UnLink(&srcMod, &dstMod);

    /*
    IVPS --> VENC
    (ModId   GrpId   ChnId) | (ModId   GrpId   ChnId)
    --------------------------------------------------
    (IVPS       1       0) -> (VENC     0       2)
    (IVPS       0       0) -> (VENC     0       0)
    */
    srcMod.enModId = AX_ID_IVPS;
    srcMod.s32GrpId = 0;
    srcMod.s32ChnId = 0;

    dstMod.enModId = AX_ID_VENC;
    dstMod.s32GrpId = 0;
    dstMod.s32ChnId = 0;
    bLink ? AX_SYS_Link(&srcMod, &dstMod) : AX_SYS_UnLink(&srcMod, &dstMod);

    srcMod.enModId = AX_ID_IVPS;
    srcMod.s32GrpId = 1;
    srcMod.s32ChnId = 0;

    dstMod.enModId = AX_ID_VENC;
    dstMod.s32GrpId = 0;
    dstMod.s32ChnId = 2;
    bLink ? AX_SYS_Link(&srcMod, &dstMod) : AX_SYS_UnLink(&srcMod, &dstMod);

    return 0;
}

static AX_S32 SampleLinkInit()
{
    return LinkControl(AX_TRUE);
    ;
}

static AX_S32 SampleLinkDeInit()
{
    return LinkControl(AX_FALSE);
    ;
}

AX_BOOL LoadGdcConfig(const AX_CHAR *strGdcConfig)
{
    AX_INI_HANDLE handle = NULL;
    AX_BOOL bRet = AX_FALSE;

    AX_S32 ret = AX_INI_Load(strGdcConfig, &handle);

    if (ret != 0)
    {
        ALOGE("open file failed!");
        goto EXIT;
    }

    if (handle)
    {
        const AX_CHAR *strLdcSect = "ldc";
        if (AX_INI_HasSection(handle, strLdcSect))
        {
            if (0 == AX_INI_GetIntValue(handle, strLdcSect, "dRatio", &s_sample_ldc_dRatio))
            {
                ALOGI("dRatio = %d", s_sample_ldc_dRatio);
            }
            else
            {
                ALOGE("no dRatio");
                goto EXIT;
            }

            if (0 == AX_INI_GetIntValue(handle, strLdcSect, "xRatio", &s_sample_ldc_xRatio))
            {
                ALOGI("xRatio = %d", s_sample_ldc_xRatio);
            }
            else
            {
                ALOGE("no xRatio");
                goto EXIT;
            }

            if (0 == AX_INI_GetIntValue(handle, strLdcSect, "yRatio", &s_sample_ldc_yRatio))
            {
                ALOGI("yRatio = %d", s_sample_ldc_yRatio);
            }
            else
            {
                ALOGE("no yRatio");
                goto EXIT;
            }

            s_sample_ldc_mode = AX_TRUE;
        }

        const AX_CHAR *strGdcSect = "gdc";
        if (AX_INI_HasSection(handle, strGdcSect))
        {
            AX_S32 dewarpType = 0;

            if (0 == AX_INI_GetIntValue(handle, strGdcSect, "dewarp", &dewarpType))
            {
                ALOGI("dewarpType = %d", dewarpType);
            }
            else
            {
                ALOGN("no dewarp");
                bRet = AX_TRUE;
                goto EXIT;
            }

            s_sample_gdc_dewarp = (AX_IVPS_DEWARP_TYPE_E)dewarpType;

            if (s_sample_gdc_dewarp >= AX_IVPS_DEWARP_BUTT)
            {
                ALOGE("invalid dewarp type(%d)", s_sample_gdc_dewarp);
                goto EXIT;
            }

            if (s_sample_gdc_dewarp == AX_IVPS_DEWARP_PERSPECTIVE)
            {
                const AX_CHAR *strPerspectiveSect = "perspective";
                if (AX_INI_HasSection(handle, strPerspectiveSect))
                {
                    AX_F64 matrix[9] = {0};
                    AX_S32 matrixSize = 0;
                    if (0 == AX_INI_GetArrayValue(handle, strPerspectiveSect, "matrix", matrix, &matrixSize, 9))
                    {
                        ALOGI("matrix size(%d):", matrixSize);
                        for (AX_S32 i = 0; i < matrixSize; i++)
                        {
                            ALOGI("    matrix[%d]: %.20f", i, matrix[i]);
                        }

                        if (matrixSize != 9)
                        {
                            ALOGE("invalid matrix size(%d)", matrixSize);
                            goto EXIT;
                        }

                        for (AX_S32 i = 0; i < matrixSize; i++)
                        {
                            s_sample_gdc_perspective_matrix[i] = (AX_S64)matrix[i];
                        }
                    }
                    else
                    {
                        ALOGE("no matrix");
                        goto EXIT;
                    }
                }
                else
                {
                    ALOGE("no section: %s", strPerspectiveSect);
                    goto EXIT;
                }

                bRet = AX_TRUE;
            }
        }
        else
        {
            ALOGN("no gdc dewarp");
            bRet = AX_TRUE;
            goto EXIT;
        }
    }

EXIT:
    if (handle)
    {
        AX_INI_Close(handle);
    }

    return bRet;
}

static AX_VOID PrintHelp(char *testApp)
{
    printf("Usage:%s -h for help\n\n", testApp);

    printf("\t-c: ISP Test Case:\n");
    printf("\t\t0: Single OS04A10\n");
    printf("\t\t1: Single IMX334\n");
    printf("\t\t2: Single GC4653\n");
    printf("\t\t3: Single OS08A20\n");
    printf("\t\t4: Single OS04A10 Online\n");

    printf("\t-e: SDR/HDR Mode:\n");
    printf("\t\t1: SDR\n");
    printf("\t\t2: HDR 2DOL\n");

    printf("\t-v: Video Encode Type, default is h264\n");
    printf("\t\t0: h264\n");
    printf("\t\t1: h265\n");

    printf("\t-r: Sensor&Video Framerate (framerate need supported by sensor), default is 25\n");

    printf("\t-g: Gdc config, default empty for none Gdc mode\n");
    printf("\t\tfilename: Gdc config file\n");

    printf("\t-i: input source, default empty for input source from camera\n");
    printf("\t\tfilename: input yuv source file\n");

    exit(0);
}

int main(int argc, char *argv[])
{
    AX_S32 isExit = 0, i, ch;
    AX_S32 s32Ret = 0;
    COMMON_SYS_CASE_E eSysCase = SYS_CASE_NONE;
    COMMON_SYS_ARGS_T tCommonArgs = {0};
    AX_SNS_HDR_MODE_E eHdrMode = AX_SNS_LINEAR_MODE;
    SAMPLE_SNS_TYPE_E eSnsType = OMNIVISION_OS04A10;
    COMMON_VENC_CASE_E eVencType = VENC_CASE_H264;
    const AX_CHAR *strGdcConfig = NULL;

    signal(SIGPIPE, SIG_IGN);
    signal(SIGINT, __sigint);

    ALOGN("sample_vin_ivps_joint_venc_rtsp begin\n\n");

    if (argc < 2)
    {
        PrintHelp(argv[0]);
        exit(0);
    }

    while ((ch = getopt(argc, argv, "c:e:v:r:g:i:h")) != -1)
    {
        isExit = 0;
        switch (ch)
        {
        case 'c':
            eSysCase = (COMMON_SYS_CASE_E)atoi(optarg);
            break;
        case 'e':
            eHdrMode = (AX_SNS_HDR_MODE_E)atoi(optarg);
            break;
        case 'v':
            eVencType = (COMMON_VENC_CASE_E)atoi(optarg);
            if (eVencType != VENC_CASE_H264 && eVencType != VENC_CASE_H265)
            {
                isExit = 1;
            }
            break;
        case 'r':
            s_sample_framerate = (AX_S32)atoi(optarg);
            if (s_sample_framerate <= 0)
            {
                s_sample_framerate = 25;
            }
            break;
        case 'g':
            strGdcConfig = optarg;

            if (!strGdcConfig)
            {
                isExit = 1;
            }
            break;
        case 'i':
            s_sample_strInputYuvFile = optarg;

            if (!s_sample_strInputYuvFile)
            {
                isExit = 1;
            }
            break;
        case 'h':
            isExit = 1;
            break;
        default:
            isExit = 1;
            break;
        }
    }

    if (isExit)
    {
        PrintHelp(argv[0]);
        exit(0);
    }

    ALOGN("eSysCase=%d,eHdrMode=%d,eVencType=%d\n", eSysCase, eHdrMode, eVencType);

    if (strGdcConfig)
    {
        if (!LoadGdcConfig(strGdcConfig))
        {
            ALOGE("load gdc config file fail");
            exit(0);
        }
    }

    if (eSysCase == SYS_CASE_SINGLE_OS04A10)
    {
        tCommonArgs.nCamCnt = 1;
        eSnsType = OMNIVISION_OS04A10;
        COMMON_ISP_GetSnsConfig(OMNIVISION_OS04A10, &gCams[0].stSnsAttr, &gCams[0].stSnsClkAttr, &gCams[0].stDevAttr, &gCams[0].stPipeAttr, &gCams[0].stChnAttr);
        if (eHdrMode == AX_SNS_LINEAR_MODE)
        {
            tCommonArgs.nPoolCfgCnt = sizeof(gtSysCommPoolSingleOs04a10Sdr) / sizeof(gtSysCommPoolSingleOs04a10Sdr[0]);
            tCommonArgs.pPoolCfg = gtSysCommPoolSingleOs04a10Sdr;
        }
        else if (eHdrMode == AX_SNS_HDR_2X_MODE)
        {
            tCommonArgs.nPoolCfgCnt = sizeof(gtSysCommPoolSingleOs04a10Hdr) / sizeof(gtSysCommPoolSingleOs04a10Hdr[0]);
            tCommonArgs.pPoolCfg = gtSysCommPoolSingleOs04a10Hdr;
        }
        gCams[0].stPipeAttr.ePipeDataSrc = AX_PIPE_SOURCE_DEV_ONLINE;
        gCams[0].stSnsAttr.nFrameRate = s_sample_framerate;
        gCams[0].stChnAttr.tChnAttr[2].bEnable = AX_FALSE;
    }
    else if (eSysCase == SYS_CASE_SINGLE_OS04A10_ONLINE)
    {
        tCommonArgs.nCamCnt = 1;
        eSnsType = OMNIVISION_OS04A10;
        COMMON_ISP_GetSnsConfig(OMNIVISION_OS04A10, &gCams[0].stSnsAttr, &gCams[0].stSnsClkAttr, &gCams[0].stDevAttr, &gCams[0].stPipeAttr, &gCams[0].stChnAttr);
        if (eHdrMode == AX_SNS_LINEAR_MODE)
        {
            tCommonArgs.nPoolCfgCnt = sizeof(gtSysCommPoolSingleOs04a10OnlineSdr) / sizeof(gtSysCommPoolSingleOs04a10OnlineSdr[0]);
            tCommonArgs.pPoolCfg = gtSysCommPoolSingleOs04a10OnlineSdr;
        }
        else if (eHdrMode == AX_SNS_HDR_2X_MODE)
        {
            tCommonArgs.nPoolCfgCnt = sizeof(gtSysCommPoolSingleOs04a10OnlineHdr) / sizeof(gtSysCommPoolSingleOs04a10OnlineHdr[0]);
            tCommonArgs.pPoolCfg = gtSysCommPoolSingleOs04a10OnlineHdr;
        }
        gCams[0].stPipeAttr.ePipeDataSrc = AX_PIPE_SOURCE_DEV_ONLINE;
        gCams[0].stChnAttr.tChnAttr[0].nDepth = 1;
        gCams[0].stChnAttr.tChnAttr[1].nDepth = 1;
        gCams[0].stChnAttr.tChnAttr[2].nDepth = 1;
        gCams[0].stSnsAttr.nFrameRate = s_sample_framerate;
        gCams[0].stChnAttr.tChnAttr[2].bEnable = AX_FALSE;
    }
    else if (eSysCase == SYS_CASE_SINGLE_IMX334)
    {
        tCommonArgs.nCamCnt = 1;
        eSnsType = SONY_IMX334;
        COMMON_ISP_GetSnsConfig(SONY_IMX334, &gCams[0].stSnsAttr, &gCams[0].stSnsClkAttr, &gCams[0].stDevAttr, &gCams[0].stPipeAttr,
                                &gCams[0].stChnAttr);
        if (eHdrMode == AX_SNS_LINEAR_MODE)
        {
            tCommonArgs.nPoolCfgCnt = sizeof(gtSysCommPoolSingleImx334Sdr) / sizeof(gtSysCommPoolSingleImx334Sdr[0]);
            tCommonArgs.pPoolCfg = gtSysCommPoolSingleImx334Sdr;
            gCams[0].stSnsAttr.eRawType = AX_RT_RAW12;
            gCams[0].stDevAttr.ePixelFmt = AX_FORMAT_BAYER_RAW_12BPP;
            gCams[0].stPipeAttr.ePixelFmt = AX_FORMAT_BAYER_RAW_12BPP;
        }
        else
        {
            tCommonArgs.nPoolCfgCnt = sizeof(gtSysCommPoolSingleImx334Hdr) / sizeof(gtSysCommPoolSingleImx334Hdr[0]);
            tCommonArgs.pPoolCfg = gtSysCommPoolSingleImx334Hdr;
        }
        gCams[0].stSnsAttr.nFrameRate = s_sample_framerate;
        gCams[0].stChnAttr.tChnAttr[2].bEnable = AX_FALSE;
    }
    else if (eSysCase == SYS_CASE_SINGLE_GC4653)
    {
        tCommonArgs.nCamCnt = 1;
        eSnsType = GALAXYCORE_GC4653;
        tCommonArgs.nPoolCfgCnt = sizeof(gtSysCommPoolSingleGc4653) / sizeof(gtSysCommPoolSingleGc4653[0]);
        tCommonArgs.pPoolCfg = gtSysCommPoolSingleGc4653;
        COMMON_ISP_GetSnsConfig(GALAXYCORE_GC4653, &gCams[0].stSnsAttr, &gCams[0].stSnsClkAttr, &gCams[0].stDevAttr, &gCams[0].stPipeAttr,
                                &gCams[0].stChnAttr);
        gCams[0].stSnsAttr.nFrameRate = s_sample_framerate;
        gCams[0].stChnAttr.tChnAttr[2].bEnable = AX_FALSE;
    }
    else if (eSysCase == SYS_CASE_SINGLE_OS08A20)
    {
        tCommonArgs.nCamCnt = 1;
        eSnsType = OMNIVISION_OS08A20;
        COMMON_ISP_GetSnsConfig(OMNIVISION_OS08A20, &gCams[0].stSnsAttr, &gCams[0].stSnsClkAttr, &gCams[0].stDevAttr, &gCams[0].stPipeAttr,
                                &gCams[0].stChnAttr);
        if (eHdrMode == AX_SNS_LINEAR_MODE)
        {
            tCommonArgs.nPoolCfgCnt = sizeof(gtSysCommPoolSingleOs08a20Sdr) / sizeof(gtSysCommPoolSingleOs08a20Sdr[0]);
            tCommonArgs.pPoolCfg = gtSysCommPoolSingleOs08a20Sdr;
            gCams[0].stSnsAttr.eRawType = AX_RT_RAW12;
            gCams[0].stDevAttr.ePixelFmt = AX_FORMAT_BAYER_RAW_12BPP;
            gCams[0].stPipeAttr.ePixelFmt = AX_FORMAT_BAYER_RAW_12BPP;
        }
        else
        {
            tCommonArgs.nPoolCfgCnt = sizeof(gtSysCommPoolSingleOs08a20Hdr) / sizeof(gtSysCommPoolSingleOs08a20Hdr[0]);
            tCommonArgs.pPoolCfg = gtSysCommPoolSingleOs08a20Hdr;
        }
        gCams[0].stSnsAttr.nFrameRate = s_sample_framerate;
        gCams[0].stChnAttr.tChnAttr[2].bEnable = AX_FALSE;
    }
    else
    {
        ALOGE("Unsupported case eSysCase=%d\n", eSysCase);
        PrintHelp(argv[0]);
        exit(0);
    }

    // FIXME.
    if (s_sample_ldc_mode || s_sample_gdc_dewarp != AX_IVPS_DEWARP_BUTT)
    {
        tCommonArgs.pPoolCfg[2].nBlkCnt += 4;
    }

    if (s_sample_strInputYuvFile)
    {
        FILE *fp = fopen(s_sample_strInputYuvFile, "rb");
        if (fp)
        {
            fseek(fp, 0, SEEK_END);
            AX_U32 nFileSize = ftell(fp);

            if (nFileSize != gCams[0].stChnAttr.tChnAttr[AX_YUV_SOURCE_ID_MAIN].nWidth * gCams[0].stChnAttr.tChnAttr[AX_YUV_SOURCE_ID_MAIN].nHeight * 3 / 2)
            {
                ALOGE("%s not matched", s_sample_strInputYuvFile);
                exit(0);
            }

            ALOGN("main channel will use input source: %s", s_sample_strInputYuvFile);

            fclose(fp);
        }
        else
        {
            ALOGE("%s not exist", s_sample_strInputYuvFile);
            exit(0);
        }
    }

    /*step 1:sys init*/
    s32Ret = COMMON_SYS_Init(&tCommonArgs);
    if (s32Ret)
    {
        ALOGE("COMMON_SYS_Init failed,s32Ret:0x%x\n", s32Ret);
        return -1;
    }

    /*step 2:link init*/
    s32Ret = SampleLinkInit();
    if (s32Ret)
    {
        ALOGE("COMMON_SYS_Init failed,s32Ret:0x%x\n", s32Ret);
        goto EXIT_1;
    }

    /*step 3:npu init*/
    for (i = 0; i < tCommonArgs.nCamCnt; i++)
    {
        gCams[i].eSnsType = eSnsType;
        gCams[i].stSnsAttr.eSnsMode = eHdrMode;
        gCams[i].stDevAttr.eSnsMode = eHdrMode;
        gCams[i].stPipeAttr.eSnsMode = eHdrMode;
        if (s_sample_strInputYuvFile)
        {
            gCams[i].stChnAttr.tChnAttr[0].nDepth = 1;
        }
        else
        {
            gCams[i].stChnAttr.tChnAttr[0].nDepth = 0;
        }
        gCams[i].stChnAttr.tChnAttr[1].nDepth = 0;
        gCams[i].stChnAttr.tChnAttr[2].nDepth = 0;
        if (i == 0)
        {
            gCams[i].nDevId = 0;
            gCams[i].nRxDev = AX_MIPI_RX_DEV_0;
            gCams[i].nPipeId = 0;
        }
        else if (i == 1)
        {
            gCams[i].nDevId = 2;
            gCams[i].nRxDev = AX_MIPI_RX_DEV_2;
            gCams[i].nPipeId = 2;
        }
    }

    AX_NPU_SDK_EX_ATTR_T sNpuAttr;
    sNpuAttr.eHardMode = AX_NPU_VIRTUAL_1_1;
    s32Ret = AX_NPU_SDK_EX_Init_with_attr(&sNpuAttr);
    if (0 != s32Ret)
    {
        ALOGE("AX_NPU_SDK_EX_Init_with_attr failed,s32Ret:0x%x\n", s32Ret);
        goto EXIT_2;
    }
    // s32ModelType = 1;
    // s32ModelType 人体关键点+检测模型=1、分割模型=2。
    // s32Ret = SampleRunJointInit("/share/AX620_SDK_V0.31.0_P44_20230302193737_NO519/msp/sample/asj_ai/model/yolov8n_pose_pullup.joint", &gJointHandle,s32ModelType);
    s32ModelType = 2;
    s32Ret = SampleRunJointInit("/share/AX620_SDK_V0.31.0_P44_20230302193737_NO519/msp/sample/asj_ai/model/yolov8n-seg-sub.joint", &gJointHandle,s32ModelType);
    

    if (0 != s32Ret || !gJointHandle)
    {
        ALOGE("runjoint init failed,s32Ret:0x%x\n", s32Ret);
        goto EXIT_2;
    }

    /*step 3:camera init*/
    s32Ret = COMMON_CAM_Init();
    if (0 != s32Ret)
    {
        ALOGE("COMMON_CAM_Init failed,s32Ret:0x%x\n", s32Ret);
        goto EXIT_2;
    }

    AX_Rtsp_Start((AX_S32 *)gVencChnMapping, SAMPLE_VENC_CHN_NUM, eVencType == VENC_CASE_H264 ? AX_TRUE : AX_FALSE);

    for (i = 0; i < tCommonArgs.nCamCnt; i++)
    {
        s32Ret = COMMON_CAM_Open(&gCams[i]);
        if (s32Ret)
        {
            ALOGE("COMMON_CAM_Open failed,s32Ret:0x%x\n", s32Ret);
            goto EXIT_3;
        }
        gCams[i].bOpen = AX_TRUE;
        ALOGN("camera %d is open\n", i);
    }

    /*step 4:ivps init*/
    s32Ret = SampleIvpsInit();
    if (AX_SUCCESS != s32Ret)
    {
        ALOGE("SampleIvpsInit failed,s32Ret:0x%x\n", s32Ret);
        goto EXIT_4;
    }

    /*step 5:venc init*/
    s32Ret = SampleVencInit(eVencType);
    if (AX_SUCCESS != s32Ret)
    {
        ALOGE("SampleVencInit failed,s32Ret:0x%x\n", s32Ret);
        goto EXIT_5;
    }

    /*step 6:sys run*/
    if (s_sample_strInputYuvFile)
    {
        VIN_ThreadStart();
    }

    IVPS_ThreadStart(NULL);

    s32Ret = SysRun();
    if (AX_SUCCESS != s32Ret)
    {
        ALOGE("SysRun error,s32Ret:0x%x\n", s32Ret);
        goto EXIT_6;
    }

EXIT_6:
    SampleVencDeInit();

EXIT_5:
    StopOverlay();
    SampleIvpsDeInit();

EXIT_4:
    for (i = 0; i < tCommonArgs.nCamCnt; i++)
    {
        if (!gCams[i].bOpen)
            continue;
        COMMON_CAM_Close(&gCams[i]);
    }

    AX_Rtsp_Stop();

EXIT_3:
    COMMON_CAM_Deinit();

EXIT_2:
    SampleLinkDeInit();

    if (gJointHandle)
    {
        SampleRunJointDeInit(gJointHandle);
    }

EXIT_1:
    COMMON_SYS_DeInit();

    ALOGN("sample_vin_ivps_joint_venc_rtsp end\n");
    return 0;
}
