/******************************************************************************
 *
 * Copyright (C) 2020 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at:
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 *****************************************************************************
 * Originally developed and contributed by Ittiam Systems Pvt. Ltd, Bangalore
 */
#include <cmath>
#include <cstdlib>
#include <cstring>
#include <memory>
#include <numeric>
#include <utility>
#include <vector>

#include "ih264_typedefs.h"
#include "iv2.h"
#include "ive2.h"
#include "isvce.h"

constexpr WORD32 kMeSpeedPreset[] = {100};
constexpr WORD32 kDeblkLevel[] = {0, 2, 3, 4};
constexpr IVE_AIR_MODE_T kAirMode[] = {IVE_AIR_MODE_NONE};
constexpr IVE_SPEED_CONFIG kEncSpeed[] = {IVE_CONFIG, IVE_SLOWEST,    IVE_NORMAL,
                                          IVE_FAST,   IVE_HIGH_SPEED, IVE_FASTEST};
constexpr IV_PROFILE_T kProfile[] = {IV_PROFILE_BASE, IV_PROFILE_MAIN};
constexpr IVE_RC_MODE_T kRCMode[] = {IVE_RC_NONE, IVE_RC_STORAGE, IVE_RC_CBR_NON_LOW_DELAY,
                                     IVE_RC_CBR_LOW_DELAY};
constexpr IV_COLOR_FORMAT_T kSupportedColorFormats[] = {IV_YUV_420P, IV_YUV_420SP_UV};
constexpr WORD32 kSupportedLevels[] = {10, 9,  11, 12, 13, 20, 21, 22,
                                       30, 31, 32, 40, 41, 42, 50, 51};
constexpr IVE_SLICE_MODE_T kSliceMode[] = {IVE_SLICE_MODE_NONE};
constexpr IV_ARCH_T kArchs[] = {
    ARCH_ARM_NONEON, ARCH_ARM_A9Q, ARCH_ARM_A9A,      ARCH_ARM_A9,      ARCH_ARM_A7,
    ARCH_ARM_A5,     ARCH_ARM_A15, ARCH_ARM_NEONINTR, ARCH_X86_GENERIC, ARCH_X86_SSSE3,
    ARCH_X86_SSE42,  ARCH_ARM_A53, ARCH_ARM_A57,      ARCH_ARM_V8_NEON};
constexpr DOUBLE kSpatialResRatio[] = {1.5, 2};
constexpr UWORD8 kSpatialLayers[] = {1, 2, 3};
constexpr UWORD8 kTemporalLayers[] = {1, 2, 3};
constexpr size_t kAirModeNum = std::size(kAirMode);
constexpr size_t kEncSpeedNum = std::size(kEncSpeed);
constexpr size_t kMeSpeedPresetNum = std::size(kMeSpeedPreset);
constexpr size_t kDeblkLevelNum = std::size(kDeblkLevel);
constexpr size_t kProfileNum = std::size(kProfile);
constexpr size_t kRCModeNum = std::size(kRCMode);
constexpr size_t kSupportedColorFormatsNum = std::size(kSupportedColorFormats);
constexpr size_t kSupportedLevelsNum = std::size(kSupportedLevels);
constexpr size_t kSliceModeNum = std::size(kSliceMode);
constexpr size_t kSpatialResRatioNum = std::size(kSpatialResRatio);
constexpr size_t kSpatialLayersNum = std::size(kSpatialLayers);
constexpr size_t kTemporalLayersNum = std::size(kTemporalLayers);
constexpr size_t kMinQP = 0;
constexpr size_t kMaxQP = 51;
constexpr size_t kMaxWidth = 2560;
constexpr size_t kMaxHeight = 2560;
constexpr size_t kMaxBitrate = 500000000;
constexpr UWORD8 kNumSeiMdcvPrimaries = 3;
constexpr UWORD8 kNumSeiCcvPrimaries = 3;
constexpr double kSvcCompliantDimProb = 0.75;
constexpr size_t kMaxEncodeCalls = 100;

typedef enum ARG_INDICES_T
{
    IDX_WD_BYTE_1,
    IDX_WD_BYTE_2,
    IDX_HT_BYTE_1,
    IDX_HT_BYTE_2,
    IDX_COLOR_FORMAT,
    IDX_ARCH_TYPE,
    IDX_RC_MODE,
    IDX_NUM_CORES,
    IDX_NUM_ARCH,
    IDX_NUM_B_FRAMES,
    IDX_ENC_SPEED,
    IDX_CONSTRAINED_INTRA_FLAG,
    IDX_INTRA_4x4,
    IDX_I_FRAME_QP,
    IDX_P_FRAME_QP,
    IDX_B_FRAME_QP,
    IDX_BITRATE_BYTE_1,
    IDX_BITRATE_BYTE_2,
    IDX_FRAME_RATE,
    IDX_INTRA_REFRESH,
    IDX_ENABLE_HALF_PEL,
    IDX_ENABLE_Q_PEL,
    IDX_ME_SPEED_PRESET,
    IDX_AIR_MODE,
    IDX_DISABLE_DEBLOCK_LEVEL,
    IDX_SEARCH_RANGE_X,
    IDX_SEARCH_RANGE_Y,
    IDX_I_INTERVAL,
    IDX_IDR_INTERVAL,
    IDX_SEI_MDCV_FLAG,
    IDX_SEI_CLL_FLAG,
    IDX_SEI_AVE_FLAG,
    IDX_SEI_CCV_FLAG,
    IDX_PROFILE,
    IDX_ASPECT_RATIO_FLAG,
    IDX_NAL_HRD_FLAG,
    IDX_VCL_HRD_FLAG,
    IDX_ENABLE_FORCE_IDR,
    IDX_ENABLE_DYNAMIC_BITRATE,
    IDX_ENABLE_DYNAMIC_FRAME_RATE,
    IDX_FORCE_IDR_INTERVAL,
    IDX_DYNAMIC_BITRATE_INTERVAL,
    IDX_DYNAMIC_FRAME_RATE_INTERVAL,
    IDX_ENC_LEVEL,
    IDX_RECON_FMT,
    IDX_SLICE_MODE,
    IDX_ENABLE_FAST_SAD,
    IDX_NUM_SPATIAL_LAYERS,
    IDX_NUM_TEMPORAL_LAYERS,
    IDX_SPATIAL_RES_RATIO,
    IDX_SVC_COMPLIANT_DIMS,
    IDX_ENABLE_RECON,
    IDX_ENABLE_NALU_INFO_EXPORT,
    IDX_LAST
} ARG_INDICES_T;

class Codec
{
   public:
    struct FrameDims
    {
        size_t mWidth;
        size_t mHeight;

        FrameDims(size_t w, size_t h) : mWidth(w), mHeight(h) {}
        FrameDims(const std::pair<size_t, size_t> &dimPair)
            : FrameDims(dimPair.first, dimPair.second)
        {
        }
        FrameDims(const FrameDims &other) : FrameDims(other.mWidth, other.mHeight) {}

        void operator=(const FrameDims &other)
        {
            mWidth = other.mWidth;
            mHeight = other.mHeight;
        }

        size_t getFrameSize() const { return (mWidth * mHeight * 3) / 2; };
    };

    struct EncBufs
    {
        std::vector<UWORD8> mInputBuf;
        std::vector<UWORD8> mOutputBuf;
        std::vector<UWORD8> mReconBuf;
        std::vector<isvce_nalu_info_buf_t> mNaluInfoStructBuf;
        std::vector<std::vector<UWORD8>> mNaluInfoDataBuf;
    };

    Codec()
        : mCodecCtx(nullptr),
          mMemRecords(),
          mMemRecBufs(),
          mEncBufs(),
          mAirMode(IVE_AIR_MODE_NONE),
          mEncSpeed(IVE_NORMAL),
          mRCMode(IVE_RC_NONE),
          mArch(ARCH_NA),
          mSliceMode(IVE_SLICE_MODE_NONE),
          mIvVideoColorFormat(IV_YUV_420P),
          mProfile(IV_PROFILE_BASE),
          mSvcCompDims{kMaxWidth, kMaxHeight},
          mInputDims{kMaxWidth, kMaxHeight},
          mHalfPelEnable(1),
          mQPelEnable(1),
          mIntra4x4(0),
          mEnableFastSad(0),
          mEnableAltRef(0),
          mConstrainedIntraFlag(0),
          mSeiCllFlag(1),
          mSeiAveFlag(1),
          mSeiCcvFlag(1),
          mSeiMdcvFlag(1),
          mAspectRatioFlag(0),
          mNalHrdFlag(0),
          mVclHrdFlag(0),
          mIsForceIdrEnabled(false),
          mIsDynamicBitRateChangeEnabled(false),
          mIsDynamicFrameRateChangeEnabled(false),
          mEnableRecon(false),
          mEnableNaluInfoExport(false),
          mAvcEncLevel(41),
          mNumMemRecords(0),
          mNumCores(1),
          mBframes(0),
          mSliceParam(256),
          mMeSpeedPreset(100),
          mIInterval(60),
          mIDRInterval(60),
          mDisableDeblockLevel(0),
          m_I_QP(22),
          m_P_QP(28),
          m_B_QP(22),
          mIntraRefresh(30),
          mSearchRangeX(64),
          mSearchRangeY(48),
          mForceIdrInterval(0),
          mDynamicBitRateInterval(0),
          mDynamicFrameRateInterval(0),
          mBitrate(6000000),
          mFrameRate(30),
          mNumSpatialLayers(1),
          mNumTemporalLayers(1),
          mSpatialResRatio(2)
    {
    }

    ~Codec() { delMemRecs(); };

    bool initEncoder(const UWORD8 *data);
    bool encodeFrames(const UWORD8 *data, size_t size);

   private:
    void setEncParams(iv_raw_buf_t *psInpRawBuf, std::vector<UWORD8> &buf, const FrameDims &dims,
                      IV_COLOR_FORMAT_T colorFormat = IV_YUV_420P);
    void setFrameType(IV_PICTURE_CODING_TYPE_T eFrameType);
    void setQp();
    void setEncMode(IVE_ENC_MODE_T eEncMode);
    void setDimensions();
    void setNumCores();
    void setFrameRate();
    void setIpeParams();
    void setBitRate();
    void setAirParams();
    void setMeParams();
    void setGopParams();
    void setProfileParams();
    void setDeblockParams();
    void setVbvParams();
    void setDefault();
    void setVuiParams();
    void getBufInfo();
    void setSeiMdcvParams();
    void setSeiCllParams();
    void setSeiAveParams();
    void setSeiCcvParams();
    void logVersion();
    void initEncBufs();
    bool initMemRecs();
    void delMemRecs();

    iv_obj_t *mCodecCtx;
    std::vector<iv_mem_rec_t> mMemRecords;
    std::vector<UWORD8 *> mMemRecBufs;
    EncBufs mEncBufs;

    IVE_AIR_MODE_T mAirMode;
    IVE_SPEED_CONFIG mEncSpeed;
    IVE_RC_MODE_T mRCMode;
    IV_ARCH_T mArch;
    IVE_SLICE_MODE_T mSliceMode;
    IV_COLOR_FORMAT_T mIvVideoColorFormat;
    IV_PROFILE_T mProfile;
    FrameDims mSvcCompDims;
    FrameDims mInputDims;

    bool mHalfPelEnable;
    bool mQPelEnable;
    bool mIntra4x4;
    bool mEnableFastSad;
    bool mEnableAltRef;
    bool mConstrainedIntraFlag;
    bool mSeiCllFlag;
    bool mSeiAveFlag;
    bool mSeiCcvFlag;
    bool mSeiMdcvFlag;
    bool mAspectRatioFlag;
    bool mNalHrdFlag;
    bool mVclHrdFlag;
    bool mIsForceIdrEnabled;
    bool mIsDynamicBitRateChangeEnabled;
    bool mIsDynamicFrameRateChangeEnabled;
    bool mEnableRecon;
    bool mEnableNaluInfoExport;
    UWORD32 mAvcEncLevel;
    UWORD32 mNumMemRecords;
    UWORD32 mNumCores;
    UWORD32 mBframes;
    UWORD32 mSliceParam;
    UWORD32 mMeSpeedPreset;
    UWORD32 mIInterval;
    UWORD32 mIDRInterval;
    UWORD32 mDisableDeblockLevel;
    UWORD32 m_I_QP;
    UWORD32 m_P_QP;
    UWORD32 m_B_QP;
    UWORD32 mIntraRefresh;
    UWORD32 mSearchRangeX;
    UWORD32 mSearchRangeY;
    /* Units - number of frames */
    UWORD32 mForceIdrInterval;
    /* Units - number of frames */
    UWORD32 mDynamicBitRateInterval;
    /* Units - number of frames */
    UWORD32 mDynamicFrameRateInterval;
    UWORD64 mBitrate;
    DOUBLE mFrameRate;
    UWORD8 mNumSpatialLayers;
    UWORD8 mNumTemporalLayers;
    DOUBLE mSpatialResRatio;
};

void Codec::initEncBufs()
{
    size_t frameSize = mInputDims.getFrameSize();
    constexpr size_t minOutBufSize = 0x800;
    size_t outBufSize = std::max(minOutBufSize, frameSize * mNumSpatialLayers);
    size_t naluInfoBufSize = 460 * mNumSpatialLayers;

    mEncBufs.mInputBuf.resize(frameSize);
    mEncBufs.mOutputBuf.resize(outBufSize);

    if(mEnableRecon)
    {
        mEncBufs.mReconBuf.resize(frameSize);
    }

    if(mEnableNaluInfoExport)
    {
        mEncBufs.mNaluInfoStructBuf.resize(mNumSpatialLayers * 2);
        mEncBufs.mNaluInfoDataBuf.resize(mNumSpatialLayers);

        for(auto i = 0; i < mNumSpatialLayers; i++)
        {
            mEncBufs.mNaluInfoDataBuf[i].resize(naluInfoBufSize);
        }
    }
}

bool Codec::initMemRecs()
{
    std::fill(mMemRecBufs.begin(), mMemRecBufs.end(), nullptr);

    for(auto i = 0u; i < mNumMemRecords; i++)
    {
        mMemRecBufs[i] = reinterpret_cast<UWORD8 *>(
            aligned_alloc(mMemRecords[i].u4_mem_alignment, mMemRecords[i].u4_mem_size));
        mMemRecords[i].pv_base = mMemRecBufs[i];

        if(nullptr == mMemRecBufs[i])
        {
            for(auto j = 0u; j < i; j++)
            {
                free(mMemRecBufs[j]);
            }

            return false;
        }
    }

    return true;
}

void Codec::delMemRecs()
{
    for(auto i = 0u; i < mNumMemRecords; i++)
    {
        if(mMemRecBufs[i])
        {
            free(mMemRecBufs[i]);
        }
    }

    std::fill(mMemRecBufs.begin(), mMemRecBufs.end(), nullptr);
}

bool Codec::initEncoder(const UWORD8 *data)
{
    mInputDims = FrameDims{((data[IDX_WD_BYTE_1] << 8) | data[IDX_WD_BYTE_2]) % kMaxWidth,
                           ((data[IDX_HT_BYTE_1] << 8) | data[IDX_HT_BYTE_2]) % kMaxHeight};

    mNumSpatialLayers = kSpatialLayers[data[IDX_NUM_SPATIAL_LAYERS] % kSpatialLayersNum];
    mNumTemporalLayers = kTemporalLayers[data[IDX_NUM_TEMPORAL_LAYERS] % kTemporalLayersNum];
    mSpatialResRatio = kSpatialResRatio[data[IDX_SPATIAL_RES_RATIO] % kSpatialResRatioNum];
    bool useSvcCompliantDims =
        data[IDX_SVC_COMPLIANT_DIMS] <
        static_cast<UWORD8>(std::numeric_limits<UWORD8>::max() * kSvcCompliantDimProb);

    if(useSvcCompliantDims)
    {
        auto getSvcCompliantDims = [&]() -> FrameDims
        {
            auto maxResRatio = pow(mSpatialResRatio, mNumSpatialLayers - 1);
            UWORD32 dimPadding = 0;
            UWORD32 numDecimalDigits = mNumSpatialLayers;
            constexpr auto minDimGcd = 16;
            UWORD32 decPtDelMultiplier = static_cast<UWORD32>(std::pow(10, numDecimalDigits));
            FrameDims dims{mInputDims};

            if(std::fmod(minDimGcd, maxResRatio))
            {
                dimPadding = std::lcm(minDimGcd * decPtDelMultiplier,
                                      static_cast<UWORD32>(maxResRatio * decPtDelMultiplier)) /
                             decPtDelMultiplier;
            }
            else
            {
                dimPadding = static_cast<UWORD32>(minDimGcd * maxResRatio);
            }

            if(mInputDims.mWidth % dimPadding)
            {
                dims.mWidth = mInputDims.mWidth - ((mInputDims.mWidth) % dimPadding) + dimPadding;
            }

            if(mInputDims.mHeight % dimPadding)
            {
                dims.mHeight =
                    mInputDims.mHeight - ((mInputDims.mHeight) % dimPadding) + dimPadding;
            }

            return dims;
        };

        mSvcCompDims = getSvcCompliantDims();
        mInputDims = mSvcCompDims;
    }

    mIvVideoColorFormat =
        kSupportedColorFormats[data[IDX_COLOR_FORMAT] % kSupportedColorFormatsNum];
    mArch = kArchs[data[IDX_ARCH_TYPE] % std::size(kArchs)];
    mRCMode = kRCMode[data[IDX_RC_MODE] % kRCModeNum];
    mNumCores = (data[IDX_NUM_CORES] & 0x07) + 1;
    mBframes = 0;

    mEncSpeed = kEncSpeed[data[IDX_ENC_SPEED] % kEncSpeedNum];
    mConstrainedIntraFlag = data[IDX_CONSTRAINED_INTRA_FLAG] & 0x01;
    mIntra4x4 = data[IDX_INTRA_4x4] & 0x01;
    m_I_QP = data[IDX_I_FRAME_QP];
    m_P_QP = data[IDX_P_FRAME_QP];
    m_B_QP = data[IDX_B_FRAME_QP];
    mBitrate = (((data[IDX_BITRATE_BYTE_1] << 8) | data[IDX_BITRATE_BYTE_2]) * 1000) % kMaxBitrate;
    mFrameRate = data[IDX_FRAME_RATE] % 120;
    mIntraRefresh = data[IDX_INTRA_REFRESH] + 1;
    mHalfPelEnable = data[IDX_ENABLE_HALF_PEL] & 0x01;
    mQPelEnable = data[IDX_ENABLE_Q_PEL] & 0x01;
    mMeSpeedPreset = kMeSpeedPreset[data[IDX_ME_SPEED_PRESET] % kMeSpeedPresetNum];
    mAirMode = kAirMode[data[IDX_AIR_MODE] % kAirModeNum];
    mDisableDeblockLevel = kDeblkLevel[data[IDX_DISABLE_DEBLOCK_LEVEL] % kDeblkLevelNum];
    mSearchRangeX = data[IDX_SEARCH_RANGE_X];
    mSearchRangeY = data[IDX_SEARCH_RANGE_Y];
    mIInterval = data[IDX_I_INTERVAL] + 1;
    mIDRInterval = data[IDX_IDR_INTERVAL] + 1;
    mSeiMdcvFlag = data[IDX_SEI_MDCV_FLAG] & 0x01;
    mSeiCllFlag = data[IDX_SEI_CLL_FLAG] & 0x01;
    mSeiAveFlag = data[IDX_SEI_AVE_FLAG] & 0x01;
    mSeiCcvFlag = data[IDX_SEI_CCV_FLAG] & 0x01;
    mProfile = kProfile[data[IDX_PROFILE] % kProfileNum];
    mAspectRatioFlag = data[IDX_ASPECT_RATIO_FLAG] & 0x01;
    mNalHrdFlag = data[IDX_NAL_HRD_FLAG] & 0x01;
    mVclHrdFlag = data[IDX_VCL_HRD_FLAG] & 0x01;
    mIsForceIdrEnabled = data[IDX_ENABLE_FORCE_IDR] & 0x01;
    mIsDynamicBitRateChangeEnabled = data[IDX_ENABLE_DYNAMIC_BITRATE] & 0x01;
    mIsDynamicFrameRateChangeEnabled = data[IDX_ENABLE_DYNAMIC_FRAME_RATE] & 0x01;
    mForceIdrInterval = data[IDX_FORCE_IDR_INTERVAL] & 0x07;
    mDynamicBitRateInterval = data[IDX_DYNAMIC_BITRATE_INTERVAL] & 0x07;
    mDynamicFrameRateInterval = data[IDX_DYNAMIC_FRAME_RATE_INTERVAL] & 0x07;

    mSliceParam = std::min(256u, static_cast<UWORD32>(mInputDims.mHeight >> 4));
    mAvcEncLevel = kSupportedLevels[data[IDX_ENC_LEVEL] % kSupportedLevelsNum];
    mSliceMode = kSliceMode[data[IDX_SLICE_MODE] % kSliceModeNum];
    mEnableFastSad = data[IDX_ENABLE_FAST_SAD] & 0x01;

    mEnableRecon = !!(data[IDX_ENABLE_RECON] & 1);
    mEnableNaluInfoExport = !!(data[IDX_ENABLE_NALU_INFO_EXPORT] & 1);

    isvce_num_mem_rec_ip_t s_num_mem_rec_ip{};
    isvce_num_mem_rec_op_t s_num_mem_rec_op{};

    isvce_api_cmds_t s_api_cmds{ISVCE_CMD_GET_NUM_MEM_REC, ISVCE_CMD_CT_NA};

    /* Getting Number of MemRecords */
    s_num_mem_rec_ip.s_ive_ip.u4_size = sizeof(isvce_num_mem_rec_ip_t);
    s_num_mem_rec_op.s_ive_op.u4_size = sizeof(isvce_num_mem_rec_op_t);

    if(IV_SUCCESS != isvce_api_function(0, &s_num_mem_rec_ip, &s_num_mem_rec_op, &s_api_cmds))
    {
        return false;
    }

    mNumMemRecords = s_num_mem_rec_op.s_ive_op.u4_num_mem_rec;
    mMemRecords.resize(mNumMemRecords);
    mMemRecBufs.resize(mNumMemRecords);

    for(auto i = 0u; i < mNumMemRecords; i++)
    {
        mMemRecords[i].u4_size = sizeof(iv_mem_rec_t);
        mMemRecords[i].pv_base = nullptr;
        mMemRecords[i].u4_mem_size = 0;
        mMemRecords[i].u4_mem_alignment = 0;
        mMemRecords[i].e_mem_type = IV_NA_MEM_TYPE;
    }

    isvce_fill_mem_rec_ip_t sFillMemRecIp{};
    isvce_fill_mem_rec_op_t sFillMemRecOp{};

    s_api_cmds = {ISVCE_CMD_FILL_NUM_MEM_REC, ISVCE_CMD_CT_NA};

    sFillMemRecIp.s_ive_ip.u4_size = sizeof(isvce_fill_mem_rec_ip_t);
    sFillMemRecOp.s_ive_op.u4_size = sizeof(isvce_fill_mem_rec_op_t);

    sFillMemRecIp.s_ive_ip.ps_mem_rec = mMemRecords.data();
    sFillMemRecIp.s_ive_ip.u4_num_mem_rec = mNumMemRecords;
    sFillMemRecIp.s_ive_ip.u4_max_wd = mInputDims.mWidth;
    sFillMemRecIp.s_ive_ip.u4_max_ht = mInputDims.mHeight;
    sFillMemRecIp.u4_wd = mInputDims.mWidth;
    sFillMemRecIp.u4_ht = mInputDims.mHeight;
    sFillMemRecIp.s_ive_ip.u4_max_level = mAvcEncLevel;
    sFillMemRecIp.s_ive_ip.e_color_format = mIvVideoColorFormat;
    sFillMemRecIp.s_ive_ip.u4_max_ref_cnt = 2;
    sFillMemRecIp.s_ive_ip.u4_max_reorder_cnt = 0;
    sFillMemRecIp.s_ive_ip.u4_max_srch_rng_x = 256;
    sFillMemRecIp.s_ive_ip.u4_max_srch_rng_y = 256;

    sFillMemRecIp.s_svc_inp_params.u1_num_temporal_layers = mNumTemporalLayers;
    sFillMemRecIp.s_svc_inp_params.u1_num_spatial_layers = mNumSpatialLayers;
    sFillMemRecIp.s_svc_inp_params.d_spatial_res_ratio = mSpatialResRatio;

    if(IV_SUCCESS != isvce_api_function(0, &sFillMemRecIp, &sFillMemRecOp, &s_api_cmds))
    {
        return false;
    }

    if(!initMemRecs())
    {
        return false;
    }

    /* Codec Instance Creation */
    isvce_init_ip_t sInitIp{};
    isvce_init_op_t sInitOp{};

    std::vector<UWORD32> sMaxBitrates(mNumSpatialLayers, 240000000);

    mCodecCtx = reinterpret_cast<iv_obj_t *>(mMemRecords[0].pv_base);
    mCodecCtx->u4_size = sizeof(iv_obj_t);
    mCodecCtx->pv_fxns = reinterpret_cast<void *>(isvce_api_function);

    sInitIp.s_ive_ip.u4_size = sizeof(isvce_init_ip_t);
    sInitOp.s_ive_op.u4_size = sizeof(isvce_init_op_t);

    s_api_cmds = {ISVCE_CMD_INIT, ISVCE_CMD_CT_NA};

    sInitIp.s_ive_ip.u4_num_mem_rec = mNumMemRecords;
    sInitIp.s_ive_ip.ps_mem_rec = mMemRecords.data();
    sInitIp.s_ive_ip.u4_max_wd = mInputDims.mWidth;
    sInitIp.s_ive_ip.u4_max_ht = mInputDims.mHeight;
    sInitIp.u4_wd = mInputDims.mWidth;
    sInitIp.u4_ht = mInputDims.mHeight;

    sInitIp.s_ive_ip.u4_max_ref_cnt = 2;
    sInitIp.s_ive_ip.u4_max_reorder_cnt = 0;
    sInitIp.s_ive_ip.u4_max_level = mAvcEncLevel;
    sInitIp.s_ive_ip.e_inp_color_fmt = mIvVideoColorFormat;

    sInitIp.s_ive_ip.u4_enable_recon = mEnableRecon;
    sInitIp.s_ive_ip.e_recon_color_fmt = IV_YUV_420P;
    sInitIp.b_nalu_info_export_enable = mEnableNaluInfoExport;
    sInitIp.s_ive_ip.e_rc_mode = mRCMode;
    sInitIp.s_ive_ip.u4_max_framerate = 120000;
    sInitIp.pu4_max_bitrate = sMaxBitrates.data();
    sInitIp.s_svc_inp_params.u1_num_temporal_layers = mNumTemporalLayers;
    sInitIp.s_svc_inp_params.u1_num_spatial_layers = mNumSpatialLayers;
    sInitIp.s_svc_inp_params.d_spatial_res_ratio = mSpatialResRatio;

    sInitIp.s_ive_ip.u4_num_bframes = mBframes;
    sInitIp.s_ive_ip.e_content_type = IV_PROGRESSIVE;
    sInitIp.s_ive_ip.u4_max_srch_rng_x = 256;
    sInitIp.s_ive_ip.u4_max_srch_rng_y = 256;
    sInitIp.s_ive_ip.e_slice_mode = mSliceMode;
    sInitIp.s_ive_ip.u4_slice_param = mSliceParam;
    sInitIp.s_ive_ip.e_arch = mArch;
    sInitIp.s_ive_ip.e_soc = SOC_GENERIC;
    sInitIp.b_use_default_vui = true;

    if(IV_SUCCESS != isvce_api_function(mCodecCtx, &sInitIp, &sInitOp, &s_api_cmds))
    {
        delMemRecs();

        return false;
    }

    setDefault();
    setNumCores();
    logVersion();
    getBufInfo();
    setDimensions();
    setFrameRate();
    setIpeParams();
    setBitRate();
    setQp();
    setAirParams();
    setVbvParams();
    setMeParams();
    setGopParams();
    setDeblockParams();
    setProfileParams();
    setEncMode(IVE_ENC_MODE_HEADER);
    setVuiParams();
    setSeiMdcvParams();
    setSeiCllParams();
    setSeiAveParams();
    setSeiCcvParams();

    initEncBufs();

    return true;
}

void Codec::setDimensions()
{
    isvce_ctl_set_dimensions_ip_t s_frame_dimensions_ip{};
    isvce_ctl_set_dimensions_op_t s_frame_dimensions_op{};

    isvce_api_cmds_t s_api_cmds{ISVCE_CMD_VIDEO_CTL, ISVCE_CMD_CTL_SET_DIMENSIONS};

    s_frame_dimensions_ip.s_ive_ip.u4_ht = mInputDims.mHeight;
    s_frame_dimensions_ip.s_ive_ip.u4_wd = mInputDims.mWidth;

    s_frame_dimensions_ip.s_ive_ip.u4_timestamp_high = 0;
    s_frame_dimensions_ip.s_ive_ip.u4_timestamp_low = 0;

    s_frame_dimensions_ip.s_ive_ip.u4_size = sizeof(isvce_ctl_set_dimensions_ip_t);
    s_frame_dimensions_op.s_ive_op.u4_size = sizeof(isvce_ctl_set_dimensions_op_t);

    isvce_api_function(mCodecCtx, &s_frame_dimensions_ip, &s_frame_dimensions_op, &s_api_cmds);
}

void Codec::setNumCores()
{
    isvce_ctl_set_num_cores_ip_t sNumCoresIp{};
    isvce_ctl_set_num_cores_op_t sNumCoresOp{};

    isvce_api_cmds_t s_api_cmds{ISVCE_CMD_VIDEO_CTL, ISVCE_CMD_CTL_SET_NUM_CORES};

    sNumCoresIp.s_ive_ip.u4_num_cores = mNumCores;

    sNumCoresIp.s_ive_ip.u4_timestamp_high = 0;
    sNumCoresIp.s_ive_ip.u4_timestamp_low = 0;

    sNumCoresIp.s_ive_ip.u4_size = sizeof(isvce_ctl_set_num_cores_ip_t);
    sNumCoresOp.s_ive_op.u4_size = sizeof(isvce_ctl_set_num_cores_op_t);

    isvce_api_function(mCodecCtx, (void *) &sNumCoresIp, (void *) &sNumCoresOp, &s_api_cmds);
}

void Codec::setDefault()
{
    isvce_ctl_setdefault_ip_t sDefaultIp{};
    isvce_ctl_setdefault_op_t sDefaultOp{};

    isvce_api_cmds_t s_api_cmds{ISVCE_CMD_VIDEO_CTL, ISVCE_CMD_CTL_SETDEFAULT};

    sDefaultIp.s_ive_ip.u4_timestamp_high = 0;
    sDefaultIp.s_ive_ip.u4_timestamp_low = 0;

    sDefaultIp.s_ive_ip.u4_size = sizeof(isvce_ctl_setdefault_ip_t);
    sDefaultOp.s_ive_op.u4_size = sizeof(isvce_ctl_setdefault_op_t);

    isvce_api_function(mCodecCtx, &sDefaultIp, &sDefaultOp, &s_api_cmds);
}

void Codec::getBufInfo()
{
    isvce_ctl_getbufinfo_ip_t s_get_buf_info_ip{};
    isvce_ctl_getbufinfo_op_t s_get_buf_info_op{};

    isvce_api_cmds_t s_api_cmds{ISVCE_CMD_VIDEO_CTL, ISVCE_CMD_CTL_GETBUFINFO};

    s_get_buf_info_ip.s_ive_ip.u4_size = sizeof(isvce_ctl_getbufinfo_ip_t);
    s_get_buf_info_op.s_ive_op.u4_size = sizeof(isvce_ctl_getbufinfo_op_t);

    s_get_buf_info_ip.s_ive_ip.u4_max_ht = mInputDims.mHeight;
    s_get_buf_info_ip.s_ive_ip.u4_max_wd = mInputDims.mWidth;
    s_get_buf_info_ip.s_ive_ip.e_inp_color_fmt = mIvVideoColorFormat;

    isvce_api_function(mCodecCtx, &s_get_buf_info_ip, &s_get_buf_info_op, &s_api_cmds);
}

void Codec::setFrameRate()
{
    isvce_ctl_set_frame_rate_ip_t sFrameRateIp{};
    isvce_ctl_set_frame_rate_op_t sFrameRateOp{};

    isvce_api_cmds_t s_api_cmds{ISVCE_CMD_VIDEO_CTL, ISVCE_CMD_CTL_SET_FRAMERATE};

    sFrameRateIp.s_ive_ip.u4_src_frame_rate = (UWORD32) mFrameRate;
    sFrameRateIp.s_ive_ip.u4_tgt_frame_rate = (UWORD32) mFrameRate;

    sFrameRateIp.s_ive_ip.u4_timestamp_high = 0;
    sFrameRateIp.s_ive_ip.u4_timestamp_low = 0;

    sFrameRateIp.s_ive_ip.u4_size = sizeof(isvce_ctl_set_frame_rate_ip_t);
    sFrameRateOp.s_ive_op.u4_size = sizeof(isvce_ctl_set_frame_rate_op_t);

    isvce_api_function(mCodecCtx, &sFrameRateIp, &sFrameRateOp, &s_api_cmds);
}

void Codec::setIpeParams()
{
    isvce_ctl_set_ipe_params_ip_t sIpeParamsIp{};
    isvce_ctl_set_ipe_params_op_t sIpeParamsOp{};

    isvce_api_cmds_t s_api_cmds{ISVCE_CMD_VIDEO_CTL, ISVCE_CMD_CTL_SET_IPE_PARAMS};

    sIpeParamsIp.s_ive_ip.u4_enable_intra_4x4 = mIntra4x4;
    sIpeParamsIp.s_ive_ip.u4_enc_speed_preset = mEncSpeed;

    sIpeParamsIp.s_ive_ip.u4_timestamp_high = 0;
    sIpeParamsIp.s_ive_ip.u4_timestamp_low = 0;

    sIpeParamsIp.s_ive_ip.u4_size = sizeof(isvce_ctl_set_ipe_params_ip_t);
    sIpeParamsOp.s_ive_op.u4_size = sizeof(isvce_ctl_set_ipe_params_op_t);

    isvce_api_function(mCodecCtx, &sIpeParamsIp, &sIpeParamsOp, &s_api_cmds);
}

void Codec::setBitRate()
{
    isvce_ctl_set_bitrate_ip_t sBitrateIp{};
    isvce_ctl_set_bitrate_op_t sBitrateOp{};

    isvce_api_cmds_t s_api_cmds{ISVCE_CMD_VIDEO_CTL, ISVCE_CMD_CTL_SET_BITRATE};
    std::vector<UWORD32> sTargetBitrates(mNumSpatialLayers, mBitrate);

    sBitrateIp.pu4_target_bitrate = sTargetBitrates.data();

    sBitrateIp.s_ive_ip.u4_timestamp_high = 0;
    sBitrateIp.s_ive_ip.u4_timestamp_low = 0;

    sBitrateIp.s_ive_ip.u4_size = sizeof(isvce_ctl_set_bitrate_ip_t);
    sBitrateOp.s_ive_op.u4_size = sizeof(isvce_ctl_set_bitrate_op_t);

    isvce_api_function(mCodecCtx, &sBitrateIp, &sBitrateOp, &s_api_cmds);
}

void Codec::setFrameType(IV_PICTURE_CODING_TYPE_T eFrameType)
{
    isvce_ctl_set_frame_type_ip_t sFrameTypeIp{};
    isvce_ctl_set_frame_type_op_t sFrameTypeOp{};

    isvce_api_cmds_t s_api_cmds{ISVCE_CMD_VIDEO_CTL, ISVCE_CMD_CTL_SET_FRAMETYPE};

    sFrameTypeIp.s_ive_ip.e_frame_type = eFrameType;

    sFrameTypeIp.s_ive_ip.u4_timestamp_high = 0;
    sFrameTypeIp.s_ive_ip.u4_timestamp_low = 0;

    sFrameTypeIp.s_ive_ip.u4_size = sizeof(isvce_ctl_set_frame_type_ip_t);
    sFrameTypeOp.s_ive_op.u4_size = sizeof(isvce_ctl_set_frame_type_op_t);

    isvce_api_function(mCodecCtx, &sFrameTypeIp, &sFrameTypeOp, &s_api_cmds);
}

void Codec::setQp()
{
    constexpr UWORD8 u1NumSliceTypes = 3;
    isvce_ctl_set_qp_ip_t s_QpIp{};
    isvce_ctl_set_qp_op_t s_QpOp{};

    isvce_api_cmds_t s_api_cmds{ISVCE_CMD_VIDEO_CTL, ISVCE_CMD_CTL_SET_QP};
    std::vector<UWORD32> sQps(u1NumSliceTypes * mNumSpatialLayers);
    std::vector<UWORD32> sMinQps(u1NumSliceTypes * mNumSpatialLayers);
    std::vector<UWORD32> sMaxQps(u1NumSliceTypes * mNumSpatialLayers);

    s_QpIp.pu4_i_qp = sQps.data();
    s_QpIp.pu4_i_qp_min = sMinQps.data();
    s_QpIp.pu4_i_qp_max = sMaxQps.data();

    s_QpIp.pu4_p_qp = sQps.data() + mNumSpatialLayers;
    s_QpIp.pu4_p_qp_min = sMinQps.data() + mNumSpatialLayers;
    s_QpIp.pu4_p_qp_max = sMaxQps.data() + mNumSpatialLayers;

    s_QpIp.pu4_b_qp = sQps.data() + mNumSpatialLayers * 2;
    s_QpIp.pu4_b_qp_min = sMinQps.data() + mNumSpatialLayers * 2;
    s_QpIp.pu4_b_qp_max = sMaxQps.data() + mNumSpatialLayers * 2;

    for(auto i = 0; i < mNumSpatialLayers; i++)
    {
        s_QpIp.pu4_i_qp[i] = m_I_QP;
        s_QpIp.pu4_i_qp_max[i] = kMaxQP;
        s_QpIp.pu4_i_qp_min[i] = kMinQP;

        s_QpIp.pu4_p_qp[i] = m_P_QP;
        s_QpIp.pu4_p_qp_max[i] = kMaxQP;
        s_QpIp.pu4_p_qp_min[i] = kMinQP;

        s_QpIp.pu4_b_qp[i] = m_B_QP;
        s_QpIp.pu4_b_qp_max[i] = kMaxQP;
        s_QpIp.pu4_b_qp_min[i] = kMinQP;
    }

    s_QpIp.s_ive_ip.u4_size = sizeof(isvce_ctl_set_qp_ip_t);
    s_QpOp.s_ive_op.u4_size = sizeof(isvce_ctl_set_qp_op_t);

    isvce_api_function(mCodecCtx, &s_QpIp, &s_QpOp, &s_api_cmds);
}

void Codec::setEncMode(IVE_ENC_MODE_T eEncMode)
{
    isvce_ctl_set_enc_mode_ip_t sEncModeIp{};
    isvce_ctl_set_enc_mode_op_t sEncModeOp{};

    isvce_api_cmds_t s_api_cmds{ISVCE_CMD_VIDEO_CTL, ISVCE_CMD_CTL_SET_ENC_MODE};

    sEncModeIp.s_ive_ip.e_enc_mode = eEncMode;

    sEncModeIp.s_ive_ip.u4_timestamp_high = 0;
    sEncModeIp.s_ive_ip.u4_timestamp_low = 0;

    sEncModeIp.s_ive_ip.u4_size = sizeof(isvce_ctl_set_enc_mode_ip_t);
    sEncModeOp.s_ive_op.u4_size = sizeof(isvce_ctl_set_enc_mode_op_t);

    isvce_api_function(mCodecCtx, &sEncModeIp, &sEncModeOp, &s_api_cmds);
}

void Codec::setVbvParams()
{
    isvce_ctl_set_vbv_params_ip_t sVbvIp{};
    isvce_ctl_set_vbv_params_op_t sVbvOp{};

    isvce_api_cmds_t s_api_cmds{ISVCE_CMD_VIDEO_CTL, ISVCE_CMD_CTL_SET_VBV_PARAMS};
    std::vector<UWORD32> sBufferDelays(mNumSpatialLayers, 1000);

    sVbvIp.pu4_vbv_buffer_delay = sBufferDelays.data();

    sVbvIp.s_ive_ip.u4_timestamp_high = 0;
    sVbvIp.s_ive_ip.u4_timestamp_low = 0;

    sVbvIp.s_ive_ip.u4_size = sizeof(isvce_ctl_set_vbv_params_ip_t);
    sVbvOp.s_ive_op.u4_size = sizeof(isvce_ctl_set_vbv_params_op_t);

    isvce_api_function(mCodecCtx, &sVbvIp, &sVbvOp, &s_api_cmds);
}

void Codec::setAirParams()
{
    isvce_ctl_set_air_params_ip_t sAirIp{};
    isvce_ctl_set_air_params_op_t sAirOp{};

    isvce_api_cmds_t s_api_cmds{ISVCE_CMD_VIDEO_CTL, ISVCE_CMD_CTL_SET_AIR_PARAMS};

    sAirIp.s_ive_ip.e_air_mode = mAirMode;
    sAirIp.s_ive_ip.u4_air_refresh_period = mIntraRefresh;

    sAirIp.s_ive_ip.u4_timestamp_high = 0;
    sAirIp.s_ive_ip.u4_timestamp_low = 0;

    sAirIp.s_ive_ip.u4_size = sizeof(isvce_ctl_set_air_params_ip_t);
    sAirOp.s_ive_op.u4_size = sizeof(isvce_ctl_set_air_params_op_t);

    isvce_api_function(mCodecCtx, &sAirIp, &sAirOp, &s_api_cmds);
}

void Codec::setMeParams()
{
    isvce_ctl_set_me_params_ip_t sMeParamsIp{};
    isvce_ctl_set_me_params_op_t sMeParamsOp{};

    isvce_api_cmds_t s_api_cmds{ISVCE_CMD_VIDEO_CTL, ISVCE_CMD_CTL_SET_ME_PARAMS};

    sMeParamsIp.s_ive_ip.u4_enable_fast_sad = mEnableFastSad;
    sMeParamsIp.s_ive_ip.u4_enable_alt_ref = mEnableAltRef;

    sMeParamsIp.s_ive_ip.u4_enable_hpel = mHalfPelEnable;
    sMeParamsIp.s_ive_ip.u4_enable_qpel = mQPelEnable;
    sMeParamsIp.s_ive_ip.u4_me_speed_preset = mMeSpeedPreset;
    sMeParamsIp.s_ive_ip.u4_srch_rng_x = mSearchRangeX;
    sMeParamsIp.s_ive_ip.u4_srch_rng_y = mSearchRangeY;

    sMeParamsIp.s_ive_ip.u4_timestamp_high = 0;
    sMeParamsIp.s_ive_ip.u4_timestamp_low = 0;

    sMeParamsIp.s_ive_ip.u4_size = sizeof(isvce_ctl_set_me_params_ip_t);
    sMeParamsOp.s_ive_op.u4_size = sizeof(isvce_ctl_set_me_params_op_t);

    isvce_api_function(mCodecCtx, &sMeParamsIp, &sMeParamsOp, &s_api_cmds);
}

void Codec::setGopParams()
{
    isvce_ctl_set_gop_params_ip_t sGopParamsIp{};
    isvce_ctl_set_gop_params_op_t sGopParamsOp{};

    isvce_api_cmds_t s_api_cmds{ISVCE_CMD_VIDEO_CTL, ISVCE_CMD_CTL_SET_GOP_PARAMS};

    sGopParamsIp.s_ive_ip.u4_i_frm_interval = mIInterval;
    sGopParamsIp.s_ive_ip.u4_idr_frm_interval = mIDRInterval;

    sGopParamsIp.s_ive_ip.u4_timestamp_high = 0;
    sGopParamsIp.s_ive_ip.u4_timestamp_low = 0;

    sGopParamsIp.s_ive_ip.u4_size = sizeof(isvce_ctl_set_gop_params_ip_t);
    sGopParamsOp.s_ive_op.u4_size = sizeof(isvce_ctl_set_gop_params_op_t);

    isvce_api_function(mCodecCtx, &sGopParamsIp, &sGopParamsOp, &s_api_cmds);
}

void Codec::setProfileParams()
{
    isvce_ctl_set_profile_params_ip_t sProfileParamsIp{};
    isvce_ctl_set_profile_params_op_t sProfileParamsOp{};

    isvce_api_cmds_t s_api_cmds{ISVCE_CMD_VIDEO_CTL, ISVCE_CMD_CTL_SET_PROFILE_PARAMS};

    sProfileParamsIp.s_ive_ip.e_profile = mProfile;
    if(sProfileParamsIp.s_ive_ip.e_profile == IV_PROFILE_BASE)
    {
        sProfileParamsIp.s_ive_ip.u4_entropy_coding_mode = 0;
    }
    else
    {
        sProfileParamsIp.s_ive_ip.u4_entropy_coding_mode = 1;
    }

    sProfileParamsIp.s_ive_ip.u4_timestamp_high = 0;
    sProfileParamsIp.s_ive_ip.u4_timestamp_low = 0;

    sProfileParamsIp.s_ive_ip.u4_size = sizeof(isvce_ctl_set_profile_params_ip_t);
    sProfileParamsOp.s_ive_op.u4_size = sizeof(isvce_ctl_set_profile_params_op_t);

    isvce_api_function(mCodecCtx, &sProfileParamsIp, &sProfileParamsOp, &s_api_cmds);
}

void Codec::setDeblockParams()
{
    isvce_ctl_set_deblock_params_ip_t sDeblockParamsIp{};
    isvce_ctl_set_deblock_params_op_t sDeblockParamsOp{};

    isvce_api_cmds_t s_api_cmds{ISVCE_CMD_VIDEO_CTL, ISVCE_CMD_CTL_SET_DEBLOCK_PARAMS};

    sDeblockParamsIp.s_ive_ip.u4_disable_deblock_level = mDisableDeblockLevel;

    sDeblockParamsIp.s_ive_ip.u4_timestamp_high = 0;
    sDeblockParamsIp.s_ive_ip.u4_timestamp_low = 0;

    sDeblockParamsIp.s_ive_ip.u4_size = sizeof(isvce_ctl_set_deblock_params_ip_t);
    sDeblockParamsOp.s_ive_op.u4_size = sizeof(isvce_ctl_set_deblock_params_op_t);

    isvce_api_function(mCodecCtx, &sDeblockParamsIp, &sDeblockParamsOp, &s_api_cmds);
}

void Codec::setVuiParams()
{
    isvce_vui_ip_t sVuiParamsIp{};
    isvce_vui_op_t sVuiParamsOp{};

    isvce_api_cmds_t s_api_cmds{ISVCE_CMD_VIDEO_CTL, ISVCE_CMD_CTL_SET_VUI_PARAMS};

    sVuiParamsIp.u1_aspect_ratio_info_present_flag = mAspectRatioFlag;
    sVuiParamsIp.u1_aspect_ratio_idc = 0;
    sVuiParamsIp.u2_sar_width = 0;
    sVuiParamsIp.u2_sar_height = 0;
    sVuiParamsIp.u1_overscan_info_present_flag = 0;
    sVuiParamsIp.u1_overscan_appropriate_flag = 0;
    sVuiParamsIp.u1_video_signal_type_present_flag = 1;
    sVuiParamsIp.u1_video_format = 0;
    sVuiParamsIp.u1_video_full_range_flag = 0;
    sVuiParamsIp.u1_colour_description_present_flag = 0;
    sVuiParamsIp.u1_colour_primaries = 0;
    sVuiParamsIp.u1_transfer_characteristics = 0;
    sVuiParamsIp.u1_matrix_coefficients = 0;
    sVuiParamsIp.u1_chroma_loc_info_present_flag = 0;
    sVuiParamsIp.u1_chroma_sample_loc_type_top_field = 0;
    sVuiParamsIp.u1_chroma_sample_loc_type_bottom_field = 0;
    sVuiParamsIp.u1_vui_timing_info_present_flag = 0;
    sVuiParamsIp.u4_vui_num_units_in_tick = 0;
    sVuiParamsIp.u4_vui_time_scale = 0;
    sVuiParamsIp.u1_fixed_frame_rate_flag = 0;
    sVuiParamsIp.u1_nal_hrd_parameters_present_flag = mNalHrdFlag;
    sVuiParamsIp.u1_vcl_hrd_parameters_present_flag = mVclHrdFlag;
    sVuiParamsIp.u1_low_delay_hrd_flag = 0;
    sVuiParamsIp.u1_pic_struct_present_flag = 0;
    sVuiParamsIp.u1_bitstream_restriction_flag = 0;
    sVuiParamsIp.u1_motion_vectors_over_pic_boundaries_flag = 0;
    sVuiParamsIp.u1_max_bytes_per_pic_denom = 0;
    sVuiParamsIp.u1_max_bits_per_mb_denom = 0;
    sVuiParamsIp.u1_log2_max_mv_length_horizontal = 0;
    sVuiParamsIp.u1_log2_max_mv_length_vertical = 0;
    sVuiParamsIp.u1_num_reorder_frames = 0;
    sVuiParamsIp.u1_max_dec_frame_buffering = 0;

    sVuiParamsIp.u4_size = sizeof(isvce_vui_ip_t);
    sVuiParamsOp.u4_size = sizeof(isvce_vui_op_t);

    isvce_api_function(mCodecCtx, &sVuiParamsIp, &sVuiParamsOp, &s_api_cmds);
}

void Codec::setSeiMdcvParams()
{
    isvce_ctl_set_sei_mdcv_params_ip_t sSeiMdcvParamsIp{};
    isvce_ctl_set_sei_mdcv_params_op_t sSeiMdcvParamsOp{};

    isvce_api_cmds_t s_api_cmds{ISVCE_CMD_VIDEO_CTL, ISVCE_CMD_CTL_SET_SEI_MDCV_PARAMS};

    sSeiMdcvParamsIp.u1_sei_mdcv_params_present_flag = mSeiMdcvFlag;
    if(mSeiMdcvFlag)
    {
        for(int i4_count = 0; i4_count < kNumSeiMdcvPrimaries; ++i4_count)
        {
            sSeiMdcvParamsIp.au2_display_primaries_x[i4_count] = 30000;
            sSeiMdcvParamsIp.au2_display_primaries_y[i4_count] = 35000;
        }
        sSeiMdcvParamsIp.u2_white_point_x = 30000;
        sSeiMdcvParamsIp.u2_white_point_y = 35000;
        sSeiMdcvParamsIp.u4_max_display_mastering_luminance = 100000000;
        sSeiMdcvParamsIp.u4_min_display_mastering_luminance = 50000;
    }

    sSeiMdcvParamsIp.u4_timestamp_high = 0;
    sSeiMdcvParamsIp.u4_timestamp_low = 0;

    sSeiMdcvParamsIp.u4_size = sizeof(isvce_ctl_set_sei_mdcv_params_ip_t);
    sSeiMdcvParamsOp.u4_size = sizeof(isvce_ctl_set_sei_mdcv_params_op_t);

    isvce_api_function(mCodecCtx, &sSeiMdcvParamsIp, &sSeiMdcvParamsOp, &s_api_cmds);
}

void Codec::setSeiCllParams()
{
    isvce_ctl_set_sei_cll_params_ip_t sSeiCllParamsIp{};
    isvce_ctl_set_sei_cll_params_op_t sSeiCllParamsOp{};

    isvce_api_cmds_t s_api_cmds{ISVCE_CMD_VIDEO_CTL, ISVCE_CMD_CTL_SET_SEI_CLL_PARAMS};

    sSeiCllParamsIp.u1_sei_cll_params_present_flag = mSeiCllFlag;

    if(mSeiCllFlag)
    {
        sSeiCllParamsIp.u2_max_content_light_level = 0;
        sSeiCllParamsIp.u2_max_pic_average_light_level = 0;
    }

    sSeiCllParamsIp.u4_timestamp_high = 0;
    sSeiCllParamsIp.u4_timestamp_low = 0;

    sSeiCllParamsIp.u4_size = sizeof(isvce_ctl_set_sei_cll_params_ip_t);
    sSeiCllParamsOp.u4_size = sizeof(isvce_ctl_set_sei_cll_params_op_t);

    isvce_api_function(mCodecCtx, &sSeiCllParamsIp, &sSeiCllParamsOp, &s_api_cmds);
}

void Codec::setSeiAveParams()
{
    isvce_ctl_set_sei_ave_params_ip_t sSeiAveParamsIp{};
    isvce_ctl_set_sei_ave_params_op_t sSeiAveParamsOp{};

    isvce_api_cmds_t s_api_cmds{ISVCE_CMD_VIDEO_CTL, ISVCE_CMD_CTL_SET_SEI_AVE_PARAMS};

    sSeiAveParamsIp.u1_sei_ave_params_present_flag = mSeiAveFlag;

    if(mSeiAveFlag)
    {
        sSeiAveParamsIp.u4_ambient_illuminance = 1;
        sSeiAveParamsIp.u2_ambient_light_x = 0;
        sSeiAveParamsIp.u2_ambient_light_y = 0;
    }

    sSeiAveParamsIp.u4_timestamp_high = 0;
    sSeiAveParamsIp.u4_timestamp_low = 0;

    sSeiAveParamsIp.u4_size = sizeof(isvce_ctl_set_sei_ave_params_ip_t);
    sSeiAveParamsOp.u4_size = sizeof(isvce_ctl_set_sei_ave_params_op_t);

    isvce_api_function(mCodecCtx, &sSeiAveParamsIp, &sSeiAveParamsOp, &s_api_cmds);
}

void Codec::setSeiCcvParams()
{
    isvce_ctl_set_sei_ccv_params_ip_t sSeiCcvParamsIp{};
    isvce_ctl_set_sei_ccv_params_op_t sSeiCcvParamsOp{};

    isvce_api_cmds_t s_api_cmds{ISVCE_CMD_VIDEO_CTL, ISVCE_CMD_CTL_SET_SEI_CCV_PARAMS};

    sSeiCcvParamsIp.u1_sei_ccv_params_present_flag = mSeiCcvFlag;

    if(mSeiCcvFlag)
    {
        sSeiCcvParamsIp.u1_ccv_cancel_flag = 0;
        sSeiCcvParamsIp.u1_ccv_persistence_flag = 1;
        sSeiCcvParamsIp.u1_ccv_primaries_present_flag = 1;
        sSeiCcvParamsIp.u1_ccv_min_luminance_value_present_flag = 1;
        sSeiCcvParamsIp.u1_ccv_max_luminance_value_present_flag = 1;
        sSeiCcvParamsIp.u1_ccv_avg_luminance_value_present_flag = 1;
        sSeiCcvParamsIp.u1_ccv_reserved_zero_2bits = 0;
        for(int i4_count = 0; i4_count < kNumSeiCcvPrimaries; ++i4_count)
        {
            sSeiCcvParamsIp.ai4_ccv_primaries_x[i4_count] = 1;
            sSeiCcvParamsIp.ai4_ccv_primaries_y[i4_count] = 1;
        }
        sSeiCcvParamsIp.u4_ccv_min_luminance_value = 1;
        sSeiCcvParamsIp.u4_ccv_max_luminance_value = 1;
        sSeiCcvParamsIp.u4_ccv_avg_luminance_value = 1;
    }

    sSeiCcvParamsIp.u4_timestamp_high = 0;
    sSeiCcvParamsIp.u4_timestamp_low = 0;

    sSeiCcvParamsIp.u4_size = sizeof(isvce_ctl_set_sei_ccv_params_ip_t);
    sSeiCcvParamsOp.u4_size = sizeof(isvce_ctl_set_sei_ccv_params_op_t);

    isvce_api_function(mCodecCtx, &sSeiCcvParamsIp, &sSeiCcvParamsOp, &s_api_cmds);
}

void Codec::logVersion()
{
    isvce_ctl_getversioninfo_ip_t s_ctl_set_getversioninfo_ip{};
    isvce_ctl_getversioninfo_op_t s_ctl_set_getversioninfo_op{};

    CHAR ac_version_string[512];

    isvce_api_cmds_t s_api_cmds{ISVCE_CMD_VIDEO_CTL, ISVCE_CMD_CTL_GETVERSION};

    s_ctl_set_getversioninfo_ip.s_ive_ip.pu1_version = (UWORD8 *) ac_version_string;
    s_ctl_set_getversioninfo_ip.s_ive_ip.u4_version_bufsize = sizeof(ac_version_string);
    s_ctl_set_getversioninfo_ip.s_ive_ip.u4_size = sizeof(isvce_ctl_getversioninfo_ip_t);
    s_ctl_set_getversioninfo_op.s_ive_op.u4_size = sizeof(isvce_ctl_getversioninfo_op_t);

    isvce_api_function(mCodecCtx, (void *) &s_ctl_set_getversioninfo_ip,
                       (void *) &s_ctl_set_getversioninfo_op, &s_api_cmds);
}

bool Codec::encodeFrames(const UWORD8 *data, size_t size)
{
    isvce_video_encode_ip_t sEncodeIp{};
    isvce_video_encode_op_t sEncodeOp{};

    isvce_api_cmds_t s_api_cmds{ISVCE_CMD_VIDEO_ENCODE, ISVCE_CMD_CT_NA};
    iv_raw_buf_t *psInpRawBuf = &sEncodeIp.s_ive_ip.s_inp_buf;
    iv_raw_buf_t *psRecRawBuf = &sEncodeIp.s_ive_ip.s_recon_buf;

    size_t frameSize = mInputDims.getFrameSize();
    auto bytesLeft = std::min(size, frameSize);
    auto bytesConsumed = 0;
    UWORD32 numFrames = 0;

    sEncodeIp.s_ive_ip.s_out_buf.pv_buf = mEncBufs.mOutputBuf.data();
    sEncodeIp.s_ive_ip.s_out_buf.u4_bytes = 0;
    sEncodeIp.s_ive_ip.s_out_buf.u4_bufsize = static_cast<UWORD32>(mEncBufs.mOutputBuf.size());
    sEncodeOp.s_ive_op.s_out_buf.pv_buf = nullptr;
    sEncodeIp.s_ive_ip.pv_bufs = nullptr;
    sEncodeIp.s_ive_ip.pv_mb_info = nullptr;
    sEncodeIp.s_ive_ip.pv_pic_info = nullptr;
    sEncodeIp.s_ive_ip.u4_mb_info_type = 0;
    sEncodeIp.s_ive_ip.u4_pic_info_type = 0;
    sEncodeIp.s_ive_ip.u4_is_last = 0;

    sEncodeIp.s_ive_ip.u4_timestamp_high = 0;
    sEncodeIp.s_ive_ip.u4_timestamp_low = 0;

    memset(psInpRawBuf, 0, sizeof(iv_raw_buf_t));
    psInpRawBuf->u4_size = sizeof(iv_raw_buf_t);
    psInpRawBuf->e_color_fmt = mIvVideoColorFormat;

    sEncodeIp.s_ive_ip.u4_size = sizeof(isvce_video_encode_ip_t);
    sEncodeOp.s_ive_op.u4_size = sizeof(isvce_video_encode_op_t);

    isvce_api_function(mCodecCtx, &sEncodeIp, &sEncodeOp, &s_api_cmds);

    if(mEnableNaluInfoExport)
    {
        sEncodeIp.ps_nalu_info_buf = mEncBufs.mNaluInfoStructBuf.data();
        sEncodeOp.ps_nalu_info_buf = mEncBufs.mNaluInfoStructBuf.data() + mNumSpatialLayers;
    }

    while(!sEncodeOp.s_ive_op.u4_is_last && (kMaxEncodeCalls > (mNumSpatialLayers * numFrames)))
    {
        if(mEnableRecon)
        {
            setEncParams(psRecRawBuf, mEncBufs.mReconBuf, mInputDims);
        }

        if(mEnableNaluInfoExport)
        {
            for(auto i = 0; i < mNumSpatialLayers; i++)
            {
                sEncodeIp.ps_nalu_info_buf[i].pu1_buf = mEncBufs.mNaluInfoDataBuf[i].data();
                sEncodeIp.ps_nalu_info_buf[i].u4_num_bytes = 0;
                sEncodeIp.ps_nalu_info_buf[i].u4_buf_size =
                    static_cast<UWORD32>(mEncBufs.mNaluInfoDataBuf[i].size());
            }
        }

        if(size > 0)
        {
            bytesLeft = std::min(size, frameSize);
            std::copy(data, data + bytesLeft, mEncBufs.mInputBuf.begin());
            std::fill(std::next(mEncBufs.mInputBuf.begin(), bytesLeft), mEncBufs.mInputBuf.end(),
                      data[0]);
            setEncParams(psInpRawBuf, mEncBufs.mInputBuf, mInputDims, mIvVideoColorFormat);

            bytesConsumed = bytesLeft;
        }
        else
        {
            sEncodeIp.s_ive_ip.u4_is_last = 1;

            for(auto i = 0; i < 3; i++)
            {
                psInpRawBuf->apv_bufs[i] = nullptr;
            }

            bytesConsumed = 0;
        }

        if(mIsForceIdrEnabled && !sEncodeIp.s_ive_ip.u4_is_last)
        {
            if(numFrames == mForceIdrInterval)
            {
                setFrameType(IV_IDR_FRAME);
            }
        }

        if(mIsDynamicBitRateChangeEnabled && !sEncodeIp.s_ive_ip.u4_is_last)
        {
            if(numFrames == mDynamicBitRateInterval)
            {
                if(data[0] & 0x01)
                {
                    mBitrate *= 2;
                }
                else
                {
                    mBitrate /= 2;
                }

                setBitRate();
            }
        }

        if(mIsDynamicFrameRateChangeEnabled && !sEncodeIp.s_ive_ip.u4_is_last)
        {
            if(numFrames == mDynamicFrameRateInterval)
            {
                if(size > 1 && data[1] & 0x01)
                {
                    mFrameRate *= 2;
                }
                else
                {
                    mFrameRate /= 2;
                }

                setFrameRate();
            }
        }

        isvce_api_function(mCodecCtx, &sEncodeIp, &sEncodeOp, &s_api_cmds);

        if(!sEncodeOp.s_ive_op.u4_is_last)
        {
            numFrames++;
            data += bytesConsumed;
            size -= bytesConsumed;
        }
    }

    return true;
}

void Codec::setEncParams(iv_raw_buf_t *psInpRawBuf, std::vector<UWORD8> &buf, const FrameDims &dims,
                         IV_COLOR_FORMAT_T colorFormat)
{
    switch(colorFormat)
    {
        case IV_YUV_420SP_UV:
        case IV_YUV_420SP_VU:
        {
            WORD32 yStride = dims.mWidth;
            WORD32 uStride = dims.mWidth / 2;

            psInpRawBuf->apv_bufs[0] = buf.data();
            psInpRawBuf->apv_bufs[1] = buf.data() + dims.mWidth * dims.mHeight;

            psInpRawBuf->au4_wd[0] = dims.mWidth;
            psInpRawBuf->au4_wd[1] = dims.mWidth;

            psInpRawBuf->au4_ht[0] = dims.mHeight;
            psInpRawBuf->au4_ht[1] = dims.mHeight / 2;

            psInpRawBuf->au4_strd[0] = yStride;
            psInpRawBuf->au4_strd[1] = uStride;

            break;
        }
        default:
        {
            WORD32 yStride = dims.mWidth;
            WORD32 uStride = dims.mWidth / 2;
            WORD32 vStride = dims.mWidth / 2;

            psInpRawBuf->apv_bufs[0] = buf.data();
            psInpRawBuf->apv_bufs[1] = buf.data() + dims.mWidth * dims.mHeight;
            psInpRawBuf->apv_bufs[2] = buf.data() + (dims.mWidth * dims.mHeight * 5) / 4;

            psInpRawBuf->au4_wd[0] = dims.mWidth;
            psInpRawBuf->au4_wd[1] = dims.mWidth / 2;
            psInpRawBuf->au4_wd[2] = dims.mWidth / 2;

            psInpRawBuf->au4_ht[0] = dims.mHeight;
            psInpRawBuf->au4_ht[1] = dims.mHeight / 2;
            psInpRawBuf->au4_ht[2] = dims.mHeight / 2;

            psInpRawBuf->au4_strd[0] = yStride;
            psInpRawBuf->au4_strd[1] = uStride;
            psInpRawBuf->au4_strd[2] = vStride;

            break;
        }
    }
}

extern "C" int LLVMFuzzerTestOneInput(const UWORD8 *data, size_t size)
{
    if(size < IDX_LAST)
    {
        return 0;
    }

    std::unique_ptr<Codec> codec = std::make_unique<Codec>();

    if(codec->initEncoder(data))
    {
        codec->encodeFrames(data, size);
    }

    return 0;
}
