// SPDX-FileCopyrightText: © 2025 Allegro DVT <github-ip@allegrodvt.com>
// SPDX-License-Identifier: MIT

#include "EncUtils.h"
#include "IP_EncoderCtx.h"
#include "EncHwScalingList.h"
#include "lib_common/SyntaxConversion.h"
#include "lib_common/Utils.h"
#include "lib_common/AvcLevelsLimit.h"
#include "lib_common_enc/EncBuffersInternal.h"
#include "lib_common_enc/Itu_Utils.h"

/****************************************************************************/
static int32_t getScalingListPresentId(int32_t iSizeId, int32_t iIntraInter, int32_t iYCbCr)
{
  return iSizeId == 0 ? ((iIntraInter * 3) + iYCbCr) : (6 + iIntraInter + iYCbCr * 2);
}

/****************************************************************************/
static void fillScalingList(AL_TEncSettings const* pSettings, AL_TAvcSps* pSPS, int32_t iSizeId, int32_t iIntraInter, int32_t iYCbCr)
{
  int32_t iMatrixId = (3 * iIntraInter) + iYCbCr;
  int32_t iMatrixSize = iSizeId == 0 ? 16 : 64;
  uint8_t* pSL = pSPS->scaling_list_param.ScalingList[iSizeId][iMatrixId];
  int32_t iSLPresentId = getScalingListPresentId(iSizeId, iIntraInter, iYCbCr);

  if(pSettings->SclFlag[iSizeId][iMatrixId] != 0)
  {
    pSPS->seq_scaling_list_present_flag[iSLPresentId] = 1; // scaling list present in file
    Rtos_Memcpy(pSL, pSettings->ScalingList[iSizeId][iMatrixId], iMatrixSize);
    return;
  }

  pSPS->seq_scaling_list_present_flag[iSLPresentId] = 0;

  if(iYCbCr == 0 || !pSPS->seq_scaling_list_present_flag[getScalingListPresentId(iSizeId, iIntraInter, iYCbCr - 1)])
    Rtos_Memcpy(pSL, iSizeId == 0 ? AL_AVC_DefaultScalingLists4x4[iIntraInter] : AL_AVC_DefaultScalingLists8x8[iIntraInter], iMatrixSize);
  else
  {
    pSPS->seq_scaling_list_present_flag[iSLPresentId] = 1;
    Rtos_Memcpy(pSL, pSPS->scaling_list_param.ScalingList[iSizeId][iMatrixId - 1], iMatrixSize);
  }
}

/****************************************************************************/
void AL_AVC_SelectScalingList(AL_TSps* pISPS, AL_TEncSettings const* pSettings)
{
  AL_TAvcSps* pSPS = (AL_TAvcSps*)pISPS;
  AL_EScalingList eScalingList = pSettings->eScalingList;

  Rtos_Assert(eScalingList != AL_SCL_MAX_ENUM);

  static const int32_t iMaxScalingList = 12;

  if(eScalingList == AL_SCL_FLAT)
  {
    pSPS->seq_scaling_matrix_present_flag = 0;

    for(int32_t i = 0; i < iMaxScalingList; i++)
      pSPS->seq_scaling_list_present_flag[i] = 0;

    return;
  }

  pSPS->seq_scaling_matrix_present_flag = 1;

  AL_TEncChanParam const* pChannel = &pSettings->tChParam[0];

  if(AL_IS_XAVC_VBR(pChannel->eProfile) && AL_IS_INTRA_PROFILE(pChannel->eProfile))
  {
    // Scaling matrix shall be in PPS.
    // PPS scaling matrix we use the SPS's one
    pSPS->seq_scaling_matrix_present_flag = 0;
  }

  int32_t iNb8x8SCL = AL_GET_CHROMA_MODE(pSettings->tChParam[0].ePicFormat) == AL_CHROMA_4_4_4 ? 3 : 1;

  if(eScalingList == AL_SCL_CUSTOM)
  {
    for(int32_t iIntraInter = 0; iIntraInter < 2; ++iIntraInter)
    {
      for(int32_t iYCbCr = 0; iYCbCr < 3; iYCbCr++)
        fillScalingList(pSettings, pSPS, 0, iIntraInter, iYCbCr);

      for(int32_t iYCbCr = 0; iYCbCr < iNb8x8SCL; iYCbCr++)
        fillScalingList(pSettings, pSPS, 1, iIntraInter, iYCbCr);
    }
  }
  else if(eScalingList == AL_SCL_DEFAULT)
  {
    for(int32_t i = 0; i < iMaxScalingList; i++)
      pSPS->seq_scaling_list_present_flag[i] = 0;

    for(int32_t iIntraInter = 0; iIntraInter < 2; ++iIntraInter)
    {
      for(int32_t iYCbCr = 0; iYCbCr < 3; iYCbCr++)
        Rtos_Memcpy(pSPS->scaling_list_param.ScalingList[0][(3 * iIntraInter) + iYCbCr], AL_AVC_DefaultScalingLists4x4[iIntraInter], 16);

      for(int32_t iYCbCr = 0; iYCbCr < iNb8x8SCL; iYCbCr++)
        Rtos_Memcpy(pSPS->scaling_list_param.ScalingList[1][(3 * iIntraInter) + iYCbCr], AL_AVC_DefaultScalingLists8x8[iIntraInter], 64);
    }
  }
}

/****************************************************************************/
void AL_AVC_PreprocessScalingList(AL_TSCLParam const* pSclLst, uint8_t chroma_format_idc, TBufferEP* pBufEP)
{
  AL_THwScalingList HwSclLst[2][6];

  AL_AVC_GenerateHwScalingList(pSclLst, chroma_format_idc, &HwSclLst);
  AL_AVC_WriteEncHwScalingList(pSclLst, &HwSclLst, chroma_format_idc, pBufEP->tMD.pVirtualAddr + EP1_BUF_SCL_LST.Offset);

  pBufEP->uFlags |= EP1_BUF_SCL_LST.Flag;
}

/****************************************************************************/
static void AL_AVC_UpdateHrdParameters(AL_TAvcSps* pSPS, AL_TSubHrdParam* pSubHrdParam, int32_t const iCpbSize, AL_TEncSettings const* pSettings)
{
  pSubHrdParam->bit_rate_value_minus1[0] = (pSettings->tChParam[0].tRCParam.uMaxBitRate / pSettings->NumView) >> 6;
  pSPS->vui_param.hrd_param.cpb_cnt_minus1[0] = 0;
  AL_Decomposition(&(pSubHrdParam->bit_rate_value_minus1[0]), &pSPS->vui_param.hrd_param.bit_rate_scale);

  Rtos_Assert(pSubHrdParam->bit_rate_value_minus1[0] <= (UINT32_MAX - 1));

  pSubHrdParam->cpb_size_value_minus1[0] = (iCpbSize + 15) >> 4;
  AL_Decomposition(&(pSubHrdParam->cpb_size_value_minus1[0]), &pSPS->vui_param.hrd_param.cpb_size_scale);

  Rtos_Assert(pSubHrdParam->cpb_size_value_minus1[0] <= (UINT32_MAX - 1));

  pSubHrdParam->cbr_flag[0] = (pSettings->tChParam[0].tRCParam.eRCMode == AL_RC_CBR) ? 1 : 0;

  pSPS->vui_param.hrd_param.initial_cpb_removal_delay_length_minus1 = 31; // int(log((double)iCurrDelay) / log(2.0));
  pSPS->vui_param.hrd_param.au_cpb_removal_delay_length_minus1 = 31;
  pSPS->vui_param.hrd_param.dpb_output_delay_length_minus1 = 31;
  pSPS->vui_param.hrd_param.time_offset_length = 0;
}

/****************************************************************************/
static void AL_AVC_GenerateSPS_Resolution(AL_TAvcSps* pSPS, uint16_t uWidth, uint16_t uHeight, AL_TEncSettings const* pSettings)
{
  uint8_t uLog2MaxCuSize = pSettings->tChParam->uLog2MaxCuSize;

  int32_t iMBWidth = ROUND_UP_POWER_OF_TWO(uWidth, uLog2MaxCuSize);
  int32_t iMBHeight = ROUND_UP_POWER_OF_TWO(uHeight, uLog2MaxCuSize);

  int32_t iCropLeft = 0;
  int32_t iCropTop = 0;

  iCropLeft = pSettings->tChParam->uOutputCropPosX;
  iCropTop = pSettings->tChParam->uOutputCropPosY;

  if(pSettings->tChParam->uOutputCropWidth)
    uWidth = pSettings->tChParam->uOutputCropWidth;

  if(pSettings->tChParam->uOutputCropHeight)
    uHeight = pSettings->tChParam->uOutputCropHeight;

  int32_t iFrameHeightInMbs = (2 - pSPS->frame_mbs_only_flag) * iMBHeight;
  int32_t iFrameCroppedHeight = (2 - pSPS->frame_mbs_only_flag) * uHeight;

  int32_t iCropRight = (iMBWidth << uLog2MaxCuSize) - (iCropLeft + uWidth);
  int32_t iCropBottom = (iFrameHeightInMbs << uLog2MaxCuSize) - (iCropTop + iFrameCroppedHeight);

  AL_EChromaMode eChromaMode = AL_GET_CHROMA_MODE(pSettings->tChParam->ePicFormat);

  int32_t iCropUnitX = eChromaMode == AL_CHROMA_4_2_0 || eChromaMode == AL_CHROMA_4_2_2 ? 2 : 1;
  int32_t iCropUnitY = (eChromaMode == AL_CHROMA_4_2_0 ? 2 : 1) * (2 - pSPS->frame_mbs_only_flag);

  pSPS->pic_width_in_mbs_minus1 = iMBWidth - 1;

  // When frame_mbs_only_flag == 0, height in MB is always counted for a *field* picture,
  // even if we are encoding frame pictures
  // (see spec sec.7.4.2.1 and eq.7-15)
  pSPS->pic_height_in_map_units_minus1 = iMBHeight - 1;

  pSPS->frame_crop_left_offset = iCropLeft / iCropUnitX;
  pSPS->frame_crop_right_offset = iCropRight / iCropUnitX;
  pSPS->frame_crop_top_offset = iCropTop / iCropUnitY;
  pSPS->frame_crop_bottom_offset = iCropBottom / iCropUnitY;
  pSPS->frame_cropping_flag = (pSPS->frame_crop_left_offset || pSPS->frame_crop_right_offset
                               || pSPS->frame_crop_top_offset || pSPS->frame_crop_bottom_offset) ? 1 : 0;

  AL_TEncChanParam const* pChannel = &pSettings->tChParam[0];

  if(AL_IS_XAVC_CBG(pChannel->eProfile) && AL_IS_INTRA_PROFILE(pChannel->eProfile))
    AL_UpdateSarAspectRatio(&pSPS->vui_param, uWidth, uHeight, pSettings->eAspectRatio);
  else
  {
    AL_UpdateAspectRatio(&pSPS->vui_param, uWidth, uHeight, pSettings->eAspectRatio);
  }
}

/****************************************************************************/
void AL_AVC_GenerateSPS(AL_TSps* pISPS, AL_TEncSettings const* pSettings, int32_t iMaxRef, int32_t iMaxBuffering, int32_t iMaxReordering, int32_t iCpbSize)
{
  AL_TAvcSps* pSPS = (AL_TAvcSps*)pISPS;
  AL_TEncChanParam const* pChannel = &pSettings->tChParam[0];

  AL_EChromaMode eChromaMode = AL_GET_CHROMA_MODE(pChannel->ePicFormat);

  uint32_t uCSFlags = AL_GET_CS_FLAGS(pChannel->eProfile);

  // --------------------------------------------------------------------------
  pSPS->profile_idc = AL_GET_PROFILE_IDC(pChannel->eProfile);

  pSPS->constraint_set0_flag = (uCSFlags) & 1;
  pSPS->constraint_set1_flag = (uCSFlags >> 1) & 1;
  pSPS->constraint_set2_flag = (uCSFlags >> 2) & 1;
  pSPS->constraint_set3_flag = (uCSFlags >> 3) & 1;
  pSPS->constraint_set4_flag = (uCSFlags >> 4) & 1;
  pSPS->constraint_set5_flag = (uCSFlags >> 5) & 1;
  pSPS->chroma_format_idc = eChromaMode;
  pSPS->separate_colour_plane_flag = 0;
  pSPS->bit_depth_luma_minus8 = AL_GET_BITDEPTH_LUMA(pChannel->ePicFormat) - 8;
  pSPS->bit_depth_chroma_minus8 = AL_GET_BITDEPTH_CHROMA(pChannel->ePicFormat) - 8;
  pSPS->qpprime_y_zero_transform_bypass_flag = 0;

  AL_AVC_SelectScalingList(pISPS, pSettings);

  pSPS->level_idc = pChannel->uLevel;
  pSPS->seq_parameter_set_id = 0;
  pSPS->pic_order_cnt_type = 0; // TDMB = 2;

  pSPS->max_num_ref_frames = iMaxRef;
  pSPS->gaps_in_frame_num_value_allowed_flag = 0;
  pSPS->log2_max_pic_order_cnt_lsb_minus4 = AL_GET_SPS_LOG2_MAX_POC(pChannel->uSpsParam) - 4;

  if(AL_IS_XAVC(pChannel->eProfile))
    pSPS->log2_max_pic_order_cnt_lsb_minus4 = 0;

  pSPS->log2_max_frame_num_minus4 = 0;

  bool bGopParamModeFlagPyramidal = false;
  bGopParamModeFlagPyramidal = pChannel->tGopParam.eMode & AL_GOP_FLAG_PYRAMIDAL;

  if(bGopParamModeFlagPyramidal && pChannel->tGopParam.uNumB == 15)
    pSPS->log2_max_frame_num_minus4 = 1;

  else if(AL_IsGdrEnabled(&pSettings->tChParam[0]))
    pSPS->log2_max_frame_num_minus4 = 6; // 6 is to support AVC 8K GDR

  // frame_mbs_only_flag:
  // - is set to 0 whenever possible (we allow field pictures).
  // - must be set to 1 in Baseline (sec. A.2.1), or for certain levels (Table A-4).

  pSPS->frame_mbs_only_flag = 1;

  // direct_8x8_inference_flag:
  // - is set to 1 whenever possible.
  // - must be set to 1 when level >= 3.0 (Table A-4), or when frame_mbs_only_flag == 0 (sec. 7.4.2.1).
  pSPS->direct_8x8_inference_flag = 1;

  { // Comply to section A.3.3.2
    // The table A-4 gives specific values to frame_mbs_only_flag and direct_8x8_inference_flag for
    // specific combinations of profile & level

    if((pChannel->eProfile == AL_PROFILE_AVC_MAIN)
       | (pChannel->eProfile == AL_PROFILE_AVC_HIGH)
       | (pChannel->eProfile == AL_PROFILE_AVC_PROG_HIGH)
       // | ( pChannel->eProfile == AL_PROFILE_AVC_CONSTRAINED_HIGH )
       | (pChannel->eProfile == AL_PROFILE_AVC_HIGH10)
       // | ( pChannel->eProfile == AL_PROFILE_AVC_PROG_HIGH10 )
       | (pChannel->eProfile == AL_PROFILE_AVC_HIGH_422)
       | (pChannel->eProfile == AL_PROFILE_AVC_HIGH_444_PRED)
       | (pChannel->eProfile == AL_PROFILE_AVC_HIGH_INTRA)
       | (pChannel->eProfile == AL_PROFILE_AVC_HIGH10_INTRA)
       | (pChannel->eProfile == AL_PROFILE_AVC_HIGH_422_INTRA)
       | (pChannel->eProfile == AL_PROFILE_AVC_HIGH_444_INTRA)
       | (pChannel->eProfile == AL_PROFILE_AVC_CAVLC_444_INTRA))
    {
      if(pChannel->uLevel <= 20 || pChannel->uLevel >= 42)
        pSPS->frame_mbs_only_flag = 1;

      if(pChannel->uLevel >= 30)
        pSPS->direct_8x8_inference_flag = 1;
    }
  }

  pSPS->mb_adaptive_frame_field_flag = 0;

  pSPS->vui_parameters_present_flag = 1;
#if defined(ANDROID) || defined(__ANDROID_API__)
  pSPS->vui_parameters_present_flag = 0;
#endif

  pSPS->vui_param.chroma_loc_info_present_flag = (eChromaMode == AL_CHROMA_4_2_0) ? 1 : 0;

  if(AL_IS_XAVC(pChannel->eProfile))
    pSPS->vui_param.chroma_loc_info_present_flag = 0;
  pSPS->vui_param.chroma_sample_loc_type_top_field = 0;
  pSPS->vui_param.chroma_sample_loc_type_bottom_field = 0;

  AL_AVC_GenerateSPS_Resolution(pSPS, pChannel->uEncWidth, pChannel->uEncHeight, pSettings);

  pSPS->vui_param.overscan_info_present_flag = 0;

  if(AL_IS_XAVC_CBG(pChannel->eProfile) && AL_IS_INTRA_PROFILE(pChannel->eProfile) && (pSPS->pic_width_in_mbs_minus1 <= 119) && (pSPS->pic_height_in_map_units_minus1 <= 67))
  {
    pSPS->vui_param.overscan_info_present_flag = 1;
    pSPS->vui_param.overscan_appropriate_flag = 1;
  }

  // Video Signal type
  pSPS->vui_param.video_format = VIDEO_FORMAT_UNSPECIFIED;

  if(AL_IS_XAVC_CBG(pChannel->eProfile) && AL_IS_INTRA_PROFILE(pChannel->eProfile))
    pSPS->vui_param.video_format = VIDEO_FORMAT_COMPONENT;

  pSPS->vui_param.video_full_range_flag = pSettings->tChParam[0].bVideoFullRange;

  // Colour parameter information
  pSPS->vui_param.colour_primaries = AL_H273_ColourDescToColourPrimaries(pSettings->tColorConfig.eColourDescription);
  pSPS->vui_param.transfer_characteristics = AL_TransferCharacteristicsToVUIValue(pSettings->tColorConfig.eTransferCharacteristics);
  pSPS->vui_param.matrix_coefficients = AL_ColourMatrixCoefficientsToVUIValue(pSettings->tColorConfig.eColourMatrixCoeffs);

  pSPS->vui_param.colour_description_present_flag =
    (pSPS->vui_param.colour_primaries != AL_H273_ColourDescToColourPrimaries(AL_COLOUR_DESC_UNSPECIFIED)) ||
    (pSPS->vui_param.transfer_characteristics != AL_TransferCharacteristicsToVUIValue(AL_TRANSFER_CHARAC_UNSPECIFIED)) ||
    (pSPS->vui_param.matrix_coefficients != AL_ColourMatrixCoefficientsToVUIValue(AL_COLOUR_MAT_COEFF_UNSPECIFIED));

  if(AL_IS_XAVC(pChannel->eProfile))
    pSPS->vui_param.colour_description_present_flag = 1;

  pSPS->vui_param.video_signal_type_present_flag =
    (pSPS->vui_param.video_format != VIDEO_FORMAT_UNSPECIFIED) ||
    (pSPS->vui_param.video_full_range_flag != 0) ||
    pSPS->vui_param.colour_description_present_flag;

  // Timing information
  AL_UpdateVuiTimingInfo(&pSPS->vui_param, 0, &pSettings->tChParam[0].tRCParam, 2);

  pSPS->vui_param.fixed_frame_rate_flag = 0;

  if(AL_IS_XAVC(pChannel->eProfile))
    pSPS->vui_param.fixed_frame_rate_flag = 1;

  // NAL HRD
  pSPS->vui_param.hrd_param.nal_hrd_parameters_present_flag = 0;

  if(AL_IS_XAVC(pChannel->eProfile) && !(AL_IS_INTRA_PROFILE(pChannel->eProfile)))
  {
    pSPS->vui_param.hrd_param.nal_hrd_parameters_present_flag = 1;
    AL_AVC_UpdateHrdParameters(pSPS, &(pSPS->vui_param.hrd_param.nal_sub_hrd_param), iCpbSize, pSettings);
  }

  // VCL HRD
  pSPS->vui_param.hrd_param.vcl_hrd_parameters_present_flag = 1;

  if(AL_IS_XAVC_CBG(pChannel->eProfile) && AL_IS_INTRA_PROFILE(pChannel->eProfile))
    pSPS->vui_param.hrd_param.vcl_hrd_parameters_present_flag = 0;

  if(pSPS->vui_param.hrd_param.vcl_hrd_parameters_present_flag)
    AL_AVC_UpdateHrdParameters(pSPS, &(pSPS->vui_param.hrd_param.vcl_sub_hrd_param), iCpbSize, pSettings);

  // low Delay
  pSPS->vui_param.hrd_param.low_delay_hrd_flag[0] = 0;

  // Picture structure information
  pSPS->vui_param.pic_struct_present_flag = 1;

  if(AL_IS_XAVC_CBG(pChannel->eProfile) && AL_IS_INTRA_PROFILE(pChannel->eProfile))
    pSPS->vui_param.pic_struct_present_flag = 0;

  pSPS->vui_param.bitstream_restriction_flag = 1;

  pSPS->vui_param.motion_vectors_over_pic_boundaries_flag = 1;
  pSPS->vui_param.max_bytes_per_pic_denom = 0;
  pSPS->vui_param.max_bits_per_min_cu_denom = 0; // called max_bits_per_mb_denom in AVC
  pSPS->vui_param.log2_max_mv_length_horizontal = ceil_log2(4 * AL_AVC_GetMaxMotionVectorWidth(pChannel->uLevel));
  pSPS->vui_param.log2_max_mv_length_vertical = ceil_log2(4 * AL_AVC_GetMaxMotionVectorHeight(pChannel->uLevel));
  pSPS->vui_param.max_num_reorder_frames = iMaxReordering;
  pSPS->vui_param.max_dec_frame_buffering = iMaxBuffering;

  // MVC Extension
}

/****************************************************************************/
void AL_AVC_GeneratePPS(AL_TPps* pIPPS, AL_TEncSettings const* pSettings, AL_TSps const* pSPS)
{
  AL_TAvcPps* pPPS = (AL_TAvcPps*)pIPPS;
  AL_TEncChanParam const* pChannel = &pSettings->tChParam[0];

  pPPS->pic_parameter_set_id = 0;
  pPPS->seq_parameter_set_id = 0;

  pPPS->entropy_coding_mode_flag = (pChannel->eEntropyMode == AL_MODE_CABAC) ? 1 : 0;
  pPPS->bottom_field_pic_order_in_frame_present_flag = 0;

  pPPS->num_slice_groups_minus1 = 0;
  int32_t iNumRef = GetNumActiveRefIdx(pChannel);

  pPPS->num_ref_idx_l0_active_minus1 = iNumRef - 1;
  pPPS->num_ref_idx_l1_active_minus1 = iNumRef - 1;

  AL_EWPMode eWPMode = AL_WP_DEFAULT;
  pPPS->weighted_pred_flag = (eWPMode == AL_WP_EXPLICIT) ? 1 : 0;
  pPPS->weighted_bipred_idc = (eWPMode == AL_WP_DEFAULT) ? 0 : (eWPMode == AL_WP_EXPLICIT) ? 1 : 2;

  pPPS->pic_init_qp_minus26 = 0;
  pPPS->pic_init_qs_minus26 = 0;
  pPPS->chroma_qp_index_offset = Clip3(pChannel->iCbPicQpOffset, -12, 12);
  pPPS->second_chroma_qp_index_offset = Clip3(pChannel->iCrPicQpOffset, -12, 12);

  pPPS->deblocking_filter_control_present_flag = 1; // TDMB = 0;

  pPPS->constrained_intra_pred_flag = (pChannel->eEncTools & AL_OPT_CONST_INTRA_PRED) ? 1 : 0;
  pPPS->redundant_pic_cnt_present_flag = 0;
  pPPS->transform_8x8_mode_flag = pChannel->uLog2MaxTuSize > 2 ? 1 : 0;
  pPPS->pic_scaling_matrix_present_flag = 0;

  if(AL_IS_XAVC_VBR(pChannel->eProfile) && AL_IS_INTRA_PROFILE(pChannel->eProfile))
  {
    // Scaling matrix are disabled in SPS but enabled in PPS.
    // PPS scaling matrix use SPS's one
    AL_EScalingList eScalingList = pSettings->eScalingList;

    if(eScalingList != AL_SCL_FLAT)
      pPPS->pic_scaling_matrix_present_flag = 1;
  }
  pPPS->pSPS = (AL_TAvcSps*)pSPS;
}

/***************************************************************************/
void AL_AVC_UpdateSPS(AL_TSps* pISPS, AL_TEncSettings const* pSettings, AL_TEncPicStatus const* pPicStatus, AL_HLSInfo const* pHLSInfo, AL_THeadersCtx* pHdrs)
{
  if(!pPicStatus->bIsFirstSlice)
    return;

  if(AL_IsWriteSps(pHdrs, pHLSInfo->uSpsId) || pHLSInfo->uFrameRate) // In case frame rate update, regenerate the SPS and it will be inserted at next IDR
  {
    AL_TAvcSps* pSPS = (AL_TAvcSps*)pISPS;
    AL_TDimension tDim = AL_GetPpsDim(pHdrs, pHLSInfo->uSpsId);
    AL_AVC_GenerateSPS_Resolution(pSPS, tDim.iWidth, tDim.iHeight, pSettings);

    if(pHLSInfo->uFrameRate)
    {
      AL_TRCParam rcParam;
      rcParam.uFrameRate = pHLSInfo->uFrameRate;
      rcParam.uClkRatio = pHLSInfo->uClkRatio;
      AL_UpdateVuiTimingInfo(&pSPS->vui_param, 0, &rcParam, 2);
    }

    pSPS->seq_parameter_set_id = pHLSInfo->uSpsId;
  }

  AL_ReleaseSps(pHdrs, pHLSInfo->uSpsId);
}

/***************************************************************************/
bool AL_AVC_UpdateAUD(AL_TAud* pAud, AL_TEncSettings const* pSettings, AL_TEncPicStatus const* pPicStatus, int32_t iLayerID)
{
  pAud->eType = pPicStatus->eType;
  return pSettings->bEnableAUD && isBaseLayer(iLayerID);
}

/***************************************************************************/
bool AL_AVC_UpdatePPS(AL_TPps* pIPPS, AL_TEncPicStatus const* pPicStatus, AL_HLSInfo const* pHLSInfo, AL_THeadersCtx* pHdrs, bool bForcePpsIdToZero)
{
  AL_TAvcPps* pPPS = (AL_TAvcPps*)pIPPS;
  bool bMustWritePPS = false;

  pPPS->pic_init_qp_minus26 = pPicStatus->iPpsQP - 26;

  pPPS->seq_parameter_set_id = pHLSInfo->uSpsId;
  pPPS->pic_parameter_set_id = bForcePpsIdToZero ? 0 : pHLSInfo->uPpsId;

  pPPS->chroma_qp_index_offset = pHLSInfo->iCbPicQpOffset;
  pPPS->second_chroma_qp_index_offset = pHLSInfo->iCrPicQpOffset;

  if(pPicStatus->bIsFirstSlice)
  {
    bMustWritePPS = AL_IsWritePps(pHdrs, pHLSInfo->uPpsId);
    AL_ReleasePps(pHdrs, pHLSInfo->uPpsId);
  }

  return bMustWritePPS;
}
