/*
* Copyright (c) 2017, Intel Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
//!
//! \file     media_ddi_encode_mpeg2.cpp
//! \brief    Defines class for DDI media mpeg2 encode.
//!

#include "media_libva_encoder.h"
#include "media_libva_util.h"
#include "media_ddi_encode_mpeg2.h"
#include "media_ddi_encode_const.h"
#include "media_ddi_factory.h"

extern template class MediaDdiFactoryNoArg<DdiEncodeBase>;
static bool isEncodeMpeg2Registered =
    MediaDdiFactoryNoArg<DdiEncodeBase>::RegisterCodec<DdiEncodeMpeg2>(ENCODE_ID_MPEG2);

DdiEncodeMpeg2::~DdiEncodeMpeg2()
{
    RemoveUserData();

    MOS_FreeMemory(m_encodeCtx->pSeqParams);
    m_encodeCtx->pSeqParams = nullptr;

    MOS_FreeMemory(m_encodeCtx->pPicParams);
    m_encodeCtx->pPicParams = nullptr;

    if (nullptr != m_encodeCtx->ppNALUnitParams)
    {
        // Allocate one contiguous memory for the NALUnitParams buffers
        // only need to free one time
        MOS_FreeMemory(m_encodeCtx->ppNALUnitParams[0]);
        m_encodeCtx->ppNALUnitParams[0] = nullptr;

        MOS_FreeMemory(m_encodeCtx->ppNALUnitParams);
        m_encodeCtx->ppNALUnitParams = nullptr;
    }

    MOS_FreeMemory(m_encodeCtx->pVuiParams);
    m_encodeCtx->pVuiParams = nullptr;

    MOS_FreeMemory(m_encodeCtx->pSliceParams);
    m_encodeCtx->pSliceParams = nullptr;

    MOS_FreeMemory(m_encodeCtx->pEncodeStatusReport);
    m_encodeCtx->pEncodeStatusReport = nullptr;

    MOS_FreeMemory(m_encodeCtx->pSEIFromApp);
    m_encodeCtx->pSEIFromApp = nullptr;

    MOS_FreeMemory(m_encodeCtx->pSliceHeaderData);
    m_encodeCtx->pSliceHeaderData = nullptr;

    if (m_encodeCtx->pbsBuffer)
    {
        MOS_FreeMemory(m_encodeCtx->pbsBuffer->pBase);
        m_encodeCtx->pbsBuffer->pBase = nullptr;

        MOS_FreeMemory(m_encodeCtx->pbsBuffer);
        m_encodeCtx->pbsBuffer = nullptr;
    }
    MOS_FreeMemory(m_encodeCtx->pQmatrixParams);
    m_encodeCtx->pQmatrixParams = nullptr;
}

VAStatus DdiEncodeMpeg2::ContextInitialize(
    CodechalSetting *codecHalSettings)
{
    DDI_CHK_NULL(m_encodeCtx, "nullptr m_encodeCtx.", VA_STATUS_ERROR_INVALID_CONTEXT);
    DDI_CHK_NULL(m_encodeCtx->pCpDdiInterface, "nullptr m_encodeCtx->pCpDdiInterface.", VA_STATUS_ERROR_INVALID_CONTEXT);
    DDI_CHK_NULL(codecHalSettings, "nullptr codecHalSettings.", VA_STATUS_ERROR_INVALID_CONTEXT);

    codecHalSettings->codecFunction = CODECHAL_FUNCTION_ENC_PAK;
    codecHalSettings->width       = m_encodeCtx->dwFrameWidth;
    codecHalSettings->height      = m_encodeCtx->dwFrameHeight;
    codecHalSettings->mode          = m_encodeCtx->wModeType;
    codecHalSettings->standard      = CODECHAL_MPEG2;

    m_encodeCtx->pSeqParams = (void *)MOS_AllocAndZeroMemory(sizeof(CodecEncodeMpeg2SequenceParams));
    DDI_CHK_NULL(m_encodeCtx->pSeqParams, "nullptr m_encodeCtx->pSeqParams.", VA_STATUS_ERROR_ALLOCATION_FAILED);

    m_encodeCtx->pPicParams = (void *)MOS_AllocAndZeroMemory(sizeof(CodecEncodeMpeg2PictureParams));
    DDI_CHK_NULL(m_encodeCtx->pPicParams, "nullptr m_encodeCtx->pPicParams.", VA_STATUS_ERROR_ALLOCATION_FAILED);

    m_encodeCtx->pQmatrixParams = (void *)MOS_AllocAndZeroMemory(sizeof(CodecEncodeMpeg2QmatixParams));
    DDI_CHK_NULL(m_encodeCtx->pQmatrixParams, "nullptr m_encodeCtx->pQmatrixParams.", VA_STATUS_ERROR_ALLOCATION_FAILED);

    m_encodeCtx->pVuiParams = (void *)MOS_AllocAndZeroMemory(sizeof(CodecEncodeMpeg2VuiParams));
    DDI_CHK_NULL(m_encodeCtx->pVuiParams, "nullptr m_encodeCtx->pVuiParams.", VA_STATUS_ERROR_ALLOCATION_FAILED);

    // Allocate SliceParams
    // slice params num should equal to the mbHeight  //attention !!! zxf
    m_encodeCtx->pSliceParams = (void *)MOS_AllocAndZeroMemory((m_encodeCtx->dwFrameHeight >> 4) * sizeof(CodecEncodeMpeg2SliceParmas));
    DDI_CHK_NULL(m_encodeCtx->pSliceParams, "nullptr m_encodeCtx->pSliceParams.", VA_STATUS_ERROR_ALLOCATION_FAILED);

    // Allocate Encode Status Report
    m_encodeCtx->pEncodeStatusReport = (void *)MOS_AllocAndZeroMemory(CODECHAL_ENCODE_STATUS_NUM * sizeof(EncodeStatusReport));
    DDI_CHK_NULL(m_encodeCtx->pEncodeStatusReport, "nullptr m_encodeCtx->pEncodeStatusReport.", VA_STATUS_ERROR_ALLOCATION_FAILED);

    // Allocate SEI structure
    m_encodeCtx->pSEIFromApp = (CodechalEncodeSeiData *)MOS_AllocAndZeroMemory(sizeof(CodechalEncodeSeiData));
    DDI_CHK_NULL(m_encodeCtx->pSEIFromApp, "nullptr m_encodeCtx->pSEIFromApp.", VA_STATUS_ERROR_ALLOCATION_FAILED);

    m_encodeCtx->pSliceHeaderData = (CODEC_ENCODER_SLCDATA *)MOS_AllocAndZeroMemory(m_encodeCtx->wPicHeightInMB *
                                                                                    sizeof(CODEC_ENCODER_SLCDATA));
    DDI_CHK_NULL(m_encodeCtx->pSliceHeaderData, "nullptr m_encodeCtx->pSliceHeaderData.", VA_STATUS_ERROR_ALLOCATION_FAILED);

    // Create the bit stream buffer to hold the packed headers from application
    m_encodeCtx->pbsBuffer = (BSBuffer *)MOS_AllocAndZeroMemory(sizeof(BSBuffer));
    DDI_CHK_NULL(m_encodeCtx->pbsBuffer, "nullptr m_encodeCtx->pbsBuffer.", VA_STATUS_ERROR_ALLOCATION_FAILED);

    m_encodeCtx->pbsBuffer->pBase = (uint8_t *)MOS_AllocAndZeroMemory(m_encodeCtx->wPicHeightInMB * PACKED_HEADER_SIZE_PER_ROW);
    DDI_CHK_NULL(m_encodeCtx->pbsBuffer->pBase, "nullptr m_encodeCtx->pbsBuffer->pBase.", VA_STATUS_ERROR_ALLOCATION_FAILED);

    m_encodeCtx->pbsBuffer->BufferSize = m_encodeCtx->wPicHeightInMB * PACKED_HEADER_SIZE_PER_ROW;

    return VA_STATUS_SUCCESS;
}

VAStatus DdiEncodeMpeg2::RenderPicture(
    VADriverContextP ctx,
    VAContextID      context,
    VABufferID       *buffers,
    int32_t          numBuffers)
{
    VAStatus vaStatus = VA_STATUS_SUCCESS;

    DDI_FUNCTION_ENTER();

    DDI_CHK_NULL(ctx, "nullptr context", VA_STATUS_ERROR_INVALID_CONTEXT);

    DDI_MEDIA_CONTEXT *mediaCtx = DdiMedia_GetMediaContext(ctx);
    DDI_CHK_NULL(mediaCtx, "nullptr mediaCtx", VA_STATUS_ERROR_INVALID_CONTEXT);

    DDI_CHK_NULL(m_encodeCtx, "nullptr m_encodeCtx", VA_STATUS_ERROR_INVALID_CONTEXT);

    for (int32_t i = 0; i < numBuffers; i++)
    {
        DDI_MEDIA_BUFFER *buf = DdiMedia_GetBufferFromVABufferID(mediaCtx, buffers[i]);
        DDI_CHK_NULL(buf, "Invalid buffer.", VA_STATUS_ERROR_INVALID_BUFFER);
        if (buf->uiType == VAEncMacroblockDisableSkipMapBufferType)
        {
            DdiMedia_MediaBufferToMosResource(buf, &(m_encodeCtx->resPerMBSkipMapBuffer));
            m_encodeCtx->bMbDisableSkipMapEnabled = true;
            continue;
        }
        uint32_t dataSize = buf->iSize;
        // can use internal function instead of DdiMedia_MapBuffer here?
        void *data = nullptr;
        DdiMedia_MapBuffer(ctx, buffers[i], &data);
        DDI_CHK_NULL(data, "nullptr data.", VA_STATUS_ERROR_INVALID_BUFFER);

        switch (buf->uiType)
        {
        case VAIQMatrixBufferType:
        case VAQMatrixBufferType:
            DDI_CHK_STATUS(Qmatrix(data), VA_STATUS_ERROR_INVALID_BUFFER);
            break;

        case VAEncSequenceParameterBufferType:
            DDI_CHK_STATUS(ParseSeqParams(data), VA_STATUS_ERROR_INVALID_BUFFER);
            m_encodeCtx->bNewSeq = true;
            break;

        case VAEncPictureParameterBufferType:
            DDI_CHK_STATUS(ParsePicParams(mediaCtx, data), VA_STATUS_ERROR_INVALID_BUFFER);
            DDI_CHK_STATUS(
                    AddToStatusReportQueue((void *)m_encodeCtx->resBitstreamBuffer.bo),
                    VA_STATUS_ERROR_INVALID_BUFFER);
            break;

        case VAEncSliceParameterBufferType:
            {
                uint32_t numSlices = buf->uiNumElements;
                DDI_CHK_STATUS(ParseSlcParams(mediaCtx, data, numSlices), VA_STATUS_ERROR_INVALID_BUFFER);
            }
            break;

        case VAEncPackedHeaderParameterBufferType:
            vaStatus = ParsePackedHeaderParams(data);
            break;

        case VAEncPackedHeaderDataBufferType:
            vaStatus = ParsePackedHeaderData(data);
            break;

        case VAEncMiscParameterBufferType:
            DDI_CHK_STATUS(ParseMiscParams(data), VA_STATUS_ERROR_INVALID_BUFFER);
            break;

        case VAEncQPBufferType:
            DdiMedia_MediaBufferToMosResource(buf, &m_encodeCtx->resMBQpBuffer);
            m_encodeCtx->bMBQpEnable = true;
            break;

        default:
            DDI_ASSERTMESSAGE("not supported buffer type.");
            break;
        }
        DdiMedia_UnmapBuffer(ctx, buffers[i]);
    }

    DDI_FUNCTION_EXIT(vaStatus);
    return vaStatus;
}

VAStatus DdiEncodeMpeg2::EncodeInCodecHal(
    uint32_t numSlices)
{
    DDI_CHK_NULL(m_encodeCtx, "nullptr m_encodeCtx", VA_STATUS_ERROR_INVALID_PARAMETER);
    DDI_CHK_NULL(m_encodeCtx->pMediaCtx, "nullptr m_encodeCtx->pMediaCtx", VA_STATUS_ERROR_INVALID_PARAMETER);
    DDI_CHK_NULL(m_encodeCtx->pCodecHal, "nullptr m_encodeCtx->pCodecHal", VA_STATUS_ERROR_INVALID_PARAMETER);

    EncoderParams encodeParams;
    MOS_ZeroMemory(&encodeParams, sizeof(EncoderParams));

    if (m_encodeCtx->bVdencActive == true)
    {
        encodeParams.ExecCodecFunction = CODECHAL_FUNCTION_ENC_VDENC_PAK;
    }
    else
    {
        encodeParams.ExecCodecFunction = CODECHAL_FUNCTION_ENC_PAK;
    }

    DDI_CODEC_RENDER_TARGET_TABLE *rtTbl = &(m_encodeCtx->RTtbl);

    // Raw Surface
    MOS_SURFACE rawSurface;
    MOS_ZeroMemory(&rawSurface, sizeof(MOS_SURFACE));
    rawSurface.Format   = Format_NV12;
    rawSurface.dwOffset = 0;
    DdiMedia_MediaSurfaceToMosResource(rtTbl->pCurrentRT, &(rawSurface.OsResource));

    // Recon Surface
    MOS_SURFACE reconSurface;
    MOS_ZeroMemory(&reconSurface, sizeof(MOS_SURFACE));
    reconSurface.Format   = Format_NV12;
    reconSurface.dwOffset = 0;
    DdiMedia_MediaSurfaceToMosResource(rtTbl->pCurrentReconTarget, &(reconSurface.OsResource));

    //clear registered recon/ref surface flags
    DDI_CHK_RET(ClearRefList(&m_encodeCtx->RTtbl, false), "ClearRefList failed!");

    // Bitstream surface
    MOS_RESOURCE bitstreamSurface;
    MOS_ZeroMemory(&bitstreamSurface, sizeof(MOS_RESOURCE));
    bitstreamSurface        = m_encodeCtx->resBitstreamBuffer;  // in render picture
    bitstreamSurface.Format = Format_Buffer;

    encodeParams.psRawSurface        = &rawSurface;
    encodeParams.psReconSurface      = &reconSurface;
    encodeParams.presBitstreamBuffer = &bitstreamSurface;

    MOS_SURFACE mbQpSurface;
    if (m_encodeCtx->bMBQpEnable)
    {
        MOS_ZeroMemory(&mbQpSurface, sizeof(MOS_SURFACE));
        mbQpSurface.Format             = Format_Buffer_2D;
        mbQpSurface.dwOffset           = 0;
        mbQpSurface.OsResource         = m_encodeCtx->resMBQpBuffer;
        encodeParams.psMbQpDataSurface = &mbQpSurface;
        encodeParams.bMbQpDataEnabled  = true;
    }

    encodeParams.bMbDisableSkipMapEnabled = m_encodeCtx->bMbDisableSkipMapEnabled;

    MOS_SURFACE disableSkipMapSurface;
    if (encodeParams.bMbDisableSkipMapEnabled)
    {
        MOS_ZeroMemory(&disableSkipMapSurface, sizeof(MOS_SURFACE));
        disableSkipMapSurface.Format           = Format_Buffer;
        disableSkipMapSurface.dwOffset         = 0;
        disableSkipMapSurface.OsResource       = m_encodeCtx->resPerMBSkipMapBuffer;
        encodeParams.psMbDisableSkipMapSurface = &disableSkipMapSurface;
    }

    encodeParams.pMpeg2UserDataListHead = m_userDataListHead;

    CodecEncodeMpeg2SequenceParams *mpeg2Sps = (CodecEncodeMpeg2SequenceParams *)m_encodeCtx->pSeqParams;
    mpeg2Sps->m_targetUsage               = m_encodeCtx->targetUsage;

    encodeParams.pSeqParams   = m_encodeCtx->pSeqParams;
    encodeParams.pVuiParams   = m_encodeCtx->pVuiParams;
    encodeParams.pPicParams   = m_encodeCtx->pPicParams;
    encodeParams.pSliceParams = m_encodeCtx->pSliceParams;

    // Sequence data
    encodeParams.bNewSeq = m_encodeCtx->bNewSeq;
    // VUI
    encodeParams.bNewVuiData = m_encodeCtx->bNewVuiData;

    // Slice level data
    encodeParams.dwNumSlices = numSlices;

    // IQmatrix params
    encodeParams.bNewQmatrixData = m_encodeCtx->bNewQmatrixData;
    encodeParams.bPicQuant       = m_encodeCtx->bPicQuant;
    encodeParams.ppNALUnitParams = m_encodeCtx->ppNALUnitParams;
    encodeParams.pSeiData        = m_encodeCtx->pSEIFromApp;
    encodeParams.pSeiParamBuffer = m_encodeCtx->pSEIFromApp->pSEIBuffer;
    encodeParams.dwSEIDataOffset = 0;

    CODEC_AVC_IQ_MATRIX_PARAMS iqMatrixParams;
    MOS_STATUS                    status = MOS_SecureMemcpy(&iqMatrixParams.ScalingList4x4,
        6 * 16 * sizeof(uint8_t),
        &m_scalingLists4x4,
        6 * 16 * sizeof(uint8_t));
    if (MOS_STATUS_SUCCESS != status)
    {
        DDI_ASSERTMESSAGE("DDI:Failed to copy scaling list 4x4!");
        return VA_STATUS_ERROR_INVALID_PARAMETER;
    }

    status = MOS_SecureMemcpy(&iqMatrixParams.ScalingList8x8,
        2 * 64 * sizeof(uint8_t),
        &m_scalingLists8x8,
        2 * 64 * sizeof(uint8_t));
    if (MOS_STATUS_SUCCESS != status)
    {
        DDI_ASSERTMESSAGE("DDI:Failed to copy scaling list 8x8!");
        return VA_STATUS_ERROR_INVALID_PARAMETER;
    }

    encodeParams.pIQMatrixBuffer = &iqMatrixParams;

    // whether driver need to pack slice header
    if (true == m_encodeCtx->bHavePackedSliceHdr)
    {
        encodeParams.bAcceleratorHeaderPackingCaps = false;
    }
    else
    {
        encodeParams.bAcceleratorHeaderPackingCaps = true;
    }

    encodeParams.pBSBuffer      = m_encodeCtx->pbsBuffer;
    encodeParams.pSlcHeaderData = (void *)m_encodeCtx->pSliceHeaderData;

    status = m_encodeCtx->pCodecHal->Execute(&encodeParams);
    if (MOS_STATUS_SUCCESS != status)
    {
        DDI_ASSERTMESSAGE("DDI:Failed in Codechal!");
        return VA_STATUS_ERROR_ENCODING_ERROR;
    }

    return VA_STATUS_SUCCESS;
}

// parse quant matrix extension from app
VAStatus DdiEncodeMpeg2::Qmatrix(void *ptr)
{
    DDI_CHK_NULL(m_encodeCtx, "nullptr m_encodeCtx", VA_STATUS_ERROR_INVALID_PARAMETER);
    DDI_CHK_NULL(ptr, "nullptr ptr", VA_STATUS_ERROR_INVALID_PARAMETER);

    VAIQMatrixBufferMPEG2 *qMatrix = (VAQMatrixBufferMPEG2 *)ptr;

    CodecEncodeMpeg2QmatixParams *mpeg2QParams = (CodecEncodeMpeg2QmatixParams *)m_encodeCtx->pQmatrixParams;
    DDI_CHK_NULL(mpeg2QParams, "nullptr mpeg2QParams", VA_STATUS_ERROR_INVALID_PARAMETER);

    mpeg2QParams->m_newQmatrix[0] = qMatrix->load_intra_quantiser_matrix;
    mpeg2QParams->m_newQmatrix[1] = qMatrix->load_non_intra_quantiser_matrix;
    mpeg2QParams->m_newQmatrix[2] = qMatrix->load_chroma_intra_quantiser_matrix;
    mpeg2QParams->m_newQmatrix[3] = qMatrix->load_chroma_non_intra_quantiser_matrix;

    if (mpeg2QParams->m_newQmatrix[0])
    {
        for (uint8_t idx = 0; idx < 64; idx++)
        {
            mpeg2QParams->m_qmatrix[0][CODEC_AVC_Qmatrix_scan_8x8[idx]] = qMatrix->intra_quantiser_matrix[idx];
        }
    }
    if (mpeg2QParams->m_newQmatrix[1])
    {
        for (uint8_t idx = 0; idx < 64; idx++)
        {
            mpeg2QParams->m_qmatrix[1][CODEC_AVC_Qmatrix_scan_8x8[idx]] = qMatrix->non_intra_quantiser_matrix[idx];
        }
    }
    if (mpeg2QParams->m_newQmatrix[2])
    {
        for (uint8_t idx = 0; idx < 64; idx++)
        {
            mpeg2QParams->m_qmatrix[2][CODEC_AVC_Qmatrix_scan_8x8[idx]] = qMatrix->chroma_intra_quantiser_matrix[idx];
        }
    }
    if (mpeg2QParams->m_newQmatrix[3])
    {
        for (uint8_t idx = 0; idx < 64; idx++)
        {
            mpeg2QParams->m_qmatrix[3][CODEC_AVC_Qmatrix_scan_8x8[idx]] = qMatrix->chroma_non_intra_quantiser_matrix[idx];
        }
    }

    return VA_STATUS_SUCCESS;
}

// reset the parameters before each frame
VAStatus DdiEncodeMpeg2::ResetAtFrameLevel()
{
    DDI_CHK_NULL(m_encodeCtx, "nullptr m_encodeCtx", VA_STATUS_ERROR_INVALID_PARAMETER);

    // Assume there is only one SPS parameter
    CodecEncodeMpeg2SequenceParams *mpeg2SeqParams = (CodecEncodeMpeg2SequenceParams *)(m_encodeCtx->pSeqParams);
    DDI_CHK_NULL(mpeg2SeqParams, "nullptr mpeg2SeqParams", VA_STATUS_ERROR_INVALID_PARAMETER);
    mpeg2SeqParams->m_resetBRC = false;

    m_encodeCtx->dwNumSlices  = 0x0;
    m_encodeCtx->indexNALUnit = 0x0;

    // reset bsbuffer every frame
    m_encodeCtx->pbsBuffer->pCurrent    = m_encodeCtx->pbsBuffer->pBase;
    m_encodeCtx->pbsBuffer->SliceOffset = 0x0;
    m_encodeCtx->pbsBuffer->BitOffset   = 0x0;
    m_encodeCtx->pbsBuffer->BitSize     = 0x0;
    m_encodeCtx->bNewVuiData            = false;
    m_encodeCtx->bMBQpEnable            = false;

    RemoveUserData();

    // clear the packed header information
    if (nullptr != m_encodeCtx->ppNALUnitParams)
    {
        MOS_ZeroMemory(m_encodeCtx->ppNALUnitParams[0], sizeof(CODECHAL_NAL_UNIT_PARAMS) * CODEC_ENCODE_MPEG2_MAX_NAL_TYPE);
    }

    return VA_STATUS_SUCCESS;
}

// parse mepg2 sequence parameters from app
VAStatus DdiEncodeMpeg2::ParseSeqParams(void *ptr)
{
    DDI_CHK_NULL(m_encodeCtx, "nullptr m_encodeCtx", VA_STATUS_ERROR_INVALID_PARAMETER);
    DDI_CHK_NULL(ptr, "nullptr ptr", VA_STATUS_ERROR_INVALID_PARAMETER);

    VAEncSequenceParameterBufferMPEG2 *seqParams = (VAEncSequenceParameterBufferMPEG2 *)ptr;

    CodecEncodeMpeg2SequenceParams *mpeg2Sps = (CodecEncodeMpeg2SequenceParams *)m_encodeCtx->pSeqParams;

    CodecEncodeMpeg2PictureParams *halMpeg2PPS = (CodecEncodeMpeg2PictureParams *)m_encodeCtx->pPicParams;
    DDI_CHK_NULL(mpeg2Sps, "nullptr mpeg2Sps", VA_STATUS_ERROR_INVALID_PARAMETER);
    DDI_CHK_NULL(halMpeg2PPS, "nullptr halMpeg2PPS", VA_STATUS_ERROR_INVALID_PARAMETER);

    MOS_ZeroMemory(mpeg2Sps, sizeof(CodecEncodeMpeg2SequenceParams));

    mpeg2Sps->m_frameWidth                = seqParams->picture_width;
    mpeg2Sps->m_frameHeight               = seqParams->picture_height;
    mpeg2Sps->m_profile                   = seqParams->sequence_extension.bits.profile_and_level_indication & 0x70;
    mpeg2Sps->m_level                     = seqParams->sequence_extension.bits.profile_and_level_indication & 0x0f;
    mpeg2Sps->m_chromaFormat              = seqParams->sequence_extension.bits.chroma_format;
    mpeg2Sps->m_lowDelay                  = seqParams->sequence_extension.bits.low_delay;
    mpeg2Sps->m_noAcceleratorSPSInsertion = false;
    mpeg2Sps->m_progressiveSequence       = seqParams->sequence_extension.bits.progressive_sequence;
    mpeg2Sps->m_rateControlMethod         = VARC2HalRC(m_encodeCtx->uiRCMethod);
    mpeg2Sps->m_resetBRC                  = false;  // if reset the brc each i frame
    mpeg2Sps->m_bitrate                   = seqParams->bits_per_second;
    mpeg2Sps->m_vbvBufferSize             = seqParams->vbv_buffer_size;
    //set Initial vbv buffer fullness to half of vbv buffer size in byte
    mpeg2Sps->m_initVBVBufferFullnessInBit = seqParams->vbv_buffer_size * (CODEC_ENCODE_MPEG2_VBV_BUFFER_SIZE_UNITS >> 1);
    mpeg2Sps->m_maxBitRate                 = mpeg2Sps->m_bitrate;
    mpeg2Sps->m_minBitRate                 = mpeg2Sps->m_bitrate;

    if ((m_encodeCtx->uiRCMethod == VA_RC_CBR) && (seqParams->vbv_buffer_size > 0))
    {
        //set maxframe size to half of vbv buffer size in byte, in codechal, this value will set the minimum value between this value and picture_with * picture_height
        mpeg2Sps->m_userMaxFrameSize = seqParams->vbv_buffer_size * (CODEC_ENCODE_MPEG2_VBV_BUFFER_SIZE_UNITS >> 4);
    }
    else
    {
        mpeg2Sps->m_userMaxFrameSize = seqParams->picture_width * seqParams->picture_height * 3 / 2;
    }

    mpeg2Sps->m_aspectRatio   = seqParams->aspect_ratio_information;
    mpeg2Sps->m_frameRateExtD = seqParams->sequence_extension.bits.frame_rate_extension_d;
    mpeg2Sps->m_frameRateExtN = seqParams->sequence_extension.bits.frame_rate_extension_n;
    mpeg2Sps->m_frameRateCode = (uint32_t)CalculateFramerateCode(seqParams->frame_rate, mpeg2Sps->m_frameRateExtD, mpeg2Sps->m_frameRateExtN);

    if (mpeg2Sps->m_frameRateCode == 0)
    {
        DDI_ASSERTMESSAGE("invalidate frame rate code");
        return VA_STATUS_ERROR_INVALID_PARAMETER;
    }

    halMpeg2PPS->m_gopPicSize = seqParams->intra_period;
    halMpeg2PPS->m_gopRefDist = seqParams->ip_period;
    m_timeCode                = seqParams->gop_header.bits.time_code;
    m_newTimeCode             = true;
    halMpeg2PPS->m_gopOptFlag = seqParams->gop_header.bits.closed_gop;

    return VA_STATUS_SUCCESS;
}

// parse mepg2 picture prarameters from app
VAStatus DdiEncodeMpeg2::ParsePicParams(
    DDI_MEDIA_CONTEXT *mediaCtx,
    void              *ptr)
{
    DDI_CHK_NULL(mediaCtx, "nullptr mediaCtx", VA_STATUS_ERROR_INVALID_PARAMETER);
    DDI_CHK_NULL(m_encodeCtx, "nullptr m_encodeCtx", VA_STATUS_ERROR_INVALID_PARAMETER);
    DDI_CHK_NULL(ptr, "nullptr ptr", VA_STATUS_ERROR_INVALID_PARAMETER);

    DDI_CODEC_RENDER_TARGET_TABLE *rtTbl = &(m_encodeCtx->RTtbl);

    VAEncPictureParameterBufferMPEG2 *picParams = (VAEncPictureParameterBufferMPEG2 *)ptr;

    CodecEncodeMpeg2PictureParams *mpeg2PicParams = (CodecEncodeMpeg2PictureParams *)(m_encodeCtx->pPicParams);
    DDI_CHK_NULL(mpeg2PicParams, "nullptr mpeg2PicParams", VA_STATUS_ERROR_INVALID_PARAMETER);

    CodecEncodeMpeg2SequenceParams *mpeg2SeqParams = (CodecEncodeMpeg2SequenceParams *)(m_encodeCtx->pSeqParams);
    DDI_CHK_NULL(mpeg2SeqParams, "nullptr mpeg2SeqParams", VA_STATUS_ERROR_INVALID_PARAMETER);

    float frameRate = 0;
    if ((mpeg2SeqParams->m_frameRateCode > 0) && (mpeg2SeqParams->m_frameRateCode < (sizeof(frameRateTable) / sizeof(frameRateTable[0]))))
    {
        //calculate real frame rate from frame_rate_code, frame_rate_extension_n and frame_rate_extension_d
        frameRate = frameRateTable[mpeg2SeqParams->m_frameRateCode - 1].value * (mpeg2SeqParams->m_frameRateExtN + 1) / (mpeg2SeqParams->m_frameRateExtD + 1);
    }
    else
    {
        //frame_rate_code is invalid, set frame_rate to 30 defaultly.
        frameRate = 30.0;
    }

    uint32_t frameRateRounded = (uint32_t)(frameRate * 100 + 50) / 100;

    mpeg2PicParams->m_lastPicInStream          = picParams->last_picture;
    mpeg2PicParams->m_pictureCodingType        = picParams->picture_type == VAEncPictureTypeIntra ? I_TYPE : (picParams->picture_type == VAEncPictureTypePredictive ? P_TYPE : B_TYPE);
    mpeg2PicParams->m_interleavedFieldBFF      = !picParams->picture_coding_extension.bits.top_field_first;
    mpeg2PicParams->m_fieldCodingFlag          = false;
    mpeg2PicParams->m_fieldFrameCodingFlag     = (!mpeg2SeqParams->m_progressiveSequence) && (!picParams->picture_coding_extension.bits.progressive_frame);
    mpeg2PicParams->m_pic4MVallowed            = 1;
    mpeg2PicParams->m_fcode00                  = picParams->f_code[0][0];
    mpeg2PicParams->m_fcode01                  = picParams->f_code[0][1];
    mpeg2PicParams->m_fcode10                  = picParams->f_code[1][0];
    mpeg2PicParams->m_fcode11                  = picParams->f_code[1][1];
    mpeg2PicParams->m_intraDCprecision         = picParams->picture_coding_extension.bits.intra_dc_precision;
    mpeg2PicParams->m_concealmentMotionVectors = picParams->picture_coding_extension.bits.concealment_motion_vectors;
    mpeg2PicParams->m_qscaleType               = picParams->picture_coding_extension.bits.q_scale_type;  //determin quantiser_scale with scale code

    mpeg2PicParams->m_intraVlcFormat    = picParams->picture_coding_extension.bits.intra_vlc_format;
    mpeg2PicParams->m_alternateScan     = picParams->picture_coding_extension.bits.alternate_scan;
    mpeg2PicParams->m_framePredFrameDCT = picParams->picture_coding_extension.bits.frame_pred_frame_dct;

    mpeg2PicParams->m_progressiveField     = picParams->picture_coding_extension.bits.progressive_frame;
    mpeg2PicParams->m_repeatFirstField     = picParams->picture_coding_extension.bits.repeat_first_field;
    mpeg2PicParams->m_compositeDisplayFlag = picParams->picture_coding_extension.bits.composite_display_flag;
    mpeg2PicParams->m_temporalReference    = picParams->temporal_reference;
    mpeg2PicParams->m_vbvDelay             = picParams->vbv_delay;

    if (mpeg2PicParams->m_compositeDisplayFlag == 1)
    {
        mpeg2PicParams->m_vaxis           = picParams->composite_display.bits.v_axis;
        mpeg2PicParams->m_fieldSequence   = picParams->composite_display.bits.field_sequence;
        mpeg2PicParams->m_subCarrier      = picParams->composite_display.bits.sub_carrier;
        mpeg2PicParams->m_burstAmplitude  = picParams->composite_display.bits.burst_amplitude;
        mpeg2PicParams->m_subCarrierPhase = picParams->composite_display.bits.sub_carrier_phase;
    }
    if ( picParams->reconstructed_picture == VA_INVALID_SURFACE)
    {
        return VA_STATUS_ERROR_INVALID_PARAMETER;
    }
    auto recon = DdiMedia_GetSurfaceFromVASurfaceID(mediaCtx, picParams->reconstructed_picture);
    DDI_CHK_RET(RegisterRTSurfaces(&m_encodeCtx->RTtbl, recon),"RegisterRTSurfaces failed!");

    mpeg2PicParams->m_currReconstructedPic.FrameIdx = GetRenderTargetID(rtTbl, recon);
    mpeg2PicParams->m_currReconstructedPic.PicFlags = PICTURE_FRAME;

    // be attention , that codec hal use this value to manager the reference list
    mpeg2PicParams->m_currOriginalPic.FrameIdx = GetRenderTargetID(rtTbl, recon);
    mpeg2PicParams->m_currOriginalPic.PicFlags = mpeg2PicParams->m_currReconstructedPic.PicFlags;

    if (DDI_CODEC_INVALID_FRAME_INDEX != picParams->forward_reference_picture)
    {
        auto fwRef = DdiMedia_GetSurfaceFromVASurfaceID(mediaCtx, picParams->forward_reference_picture);
        UpdateRegisteredRTSurfaceFlag(&m_encodeCtx->RTtbl,fwRef);
        mpeg2PicParams->m_refFrameList[0].FrameIdx = GetRenderTargetID(rtTbl, fwRef);
        mpeg2PicParams->m_refFrameList[0].PicFlags = PICTURE_FRAME;
    }
    else
    {
        mpeg2PicParams->m_refFrameList[0].FrameIdx = (uint8_t)DDI_CODEC_INVALID_FRAME_INDEX;
        mpeg2PicParams->m_refFrameList[0].PicFlags = PICTURE_INVALID;
    }
    if (DDI_CODEC_INVALID_FRAME_INDEX != picParams->backward_reference_picture)
    {
        auto bwRef = DdiMedia_GetSurfaceFromVASurfaceID(mediaCtx, picParams->backward_reference_picture);
        UpdateRegisteredRTSurfaceFlag(&m_encodeCtx->RTtbl,bwRef);
        mpeg2PicParams->m_refFrameList[1].FrameIdx = GetRenderTargetID(rtTbl, bwRef);
        mpeg2PicParams->m_refFrameList[1].PicFlags = PICTURE_FRAME;
    }
    else
    {
        mpeg2PicParams->m_refFrameList[1].FrameIdx = (uint8_t)DDI_CODEC_INVALID_FRAME_INDEX;
        mpeg2PicParams->m_refFrameList[1].PicFlags = PICTURE_INVALID;
    }
    if (mpeg2PicParams->m_pictureCodingType == I_TYPE)
    {
        mpeg2PicParams->m_newGop = true;
    }
    else
    {
        mpeg2PicParams->m_newGop = false;
    }
    rtTbl->pCurrentReconTarget = recon;;

    DDI_MEDIA_BUFFER *buf = DdiMedia_GetBufferFromVABufferID(mediaCtx, picParams->coded_buf);

    DDI_CHK_NULL(buf, "nullptr buf", VA_STATUS_ERROR_INVALID_PARAMETER);
    RemoveFromStatusReportQueue(buf);
    DdiMedia_MediaBufferToMosResource(buf, &(m_encodeCtx->resBitstreamBuffer));
    mpeg2PicParams->m_numSlice = 0;

    //According to MPEG2 spec, GOP header time_code include 6 fields and the ranges of values are Hour (0~23), Minute(0~59), Marker bit(always 1), Second (0~59), Picuture (0~59), Drop_frame flag( 0 or 1).
    //split timecode to element
    uint32_t timeCode = m_timeCode;

    uint32_t timeCodeDropframe = (timeCode >> 24) & 0x1;

    uint32_t timeCodeHr = (timeCode >> 19) & 0x1f;

    uint32_t timeCodeMin = (timeCode >> 13) & 0x3f;

    uint32_t timeCodeMarkerBit = 0x1;

    uint32_t timeCodeSec = (timeCode >> 6) & 0x3f;

    uint32_t timeCodePic = (timeCode)&0x3f;
    //update each timecode element when parsing new picparams
    if (false == m_newTimeCode)
    {
        timeCodePic++;
        if (timeCodePic >= frameRateRounded)
        {
            timeCodePic = 0;
            timeCodeSec++;
        }
        if (timeCodeSec > maxTimeCodeSec)
        {
            timeCodeSec = 0;
            timeCodeMin++;
        }
        if (timeCodeMin > maxTimeCodeMin)
        {
            timeCodeMin = 0;
            timeCodeHr++;
        }
        if (timeCodeHr > maxTimeCodeHr)
        {
            timeCodeHr = 0;
        }
    }
    else
    {
        m_newTimeCode = false;
    }

    //fill all updated elements back to timecode in the format used by codechal
    mpeg2PicParams->m_timeCode = ((timeCodePic & 0x3f) |
                                  ((timeCodeSec & 0x3f) << 6) |
                                  ((timeCodeMarkerBit & 1) << 12) |
                                  ((timeCodeMin & 0x3f) << 13) |
                                  ((timeCodeHr & 0x1f) << 19) |
                                  (timeCodeDropframe & 1) << 24);

    m_timeCode                       = mpeg2PicParams->m_timeCode;
    mpeg2PicParams->m_skipFrameFlag  = 0;
    mpeg2PicParams->m_numSkipFrames  = 0;
    mpeg2PicParams->m_sizeSkipFrames = 0;

    return VA_STATUS_SUCCESS;
}

// parse mpeg2 slice parameters from app
VAStatus DdiEncodeMpeg2::ParseSlcParams(
    DDI_MEDIA_CONTEXT *mediaCtx,
    void              *ptr,
    uint32_t          numSlices)
{
    DDI_CHK_NULL(mediaCtx, "nullptr mediaCtx", VA_STATUS_ERROR_INVALID_PARAMETER);
    DDI_CHK_NULL(m_encodeCtx, "nullptr m_encodeCtx", VA_STATUS_ERROR_INVALID_PARAMETER);
    DDI_CHK_NULL(ptr, "nullptr ptr", VA_STATUS_ERROR_INVALID_PARAMETER);

    VAEncSliceParameterBufferMPEG2 *vaEncSlcParamsMPEG2 = (VAEncSliceParameterBufferMPEG2 *)ptr;

    CodecEncodeMpeg2SliceParmas *mpeg2SlcParams = (CodecEncodeMpeg2SliceParmas *)m_encodeCtx->pSliceParams;

    CodecEncodeMpeg2PictureParams *mpeg2PicParams = (CodecEncodeMpeg2PictureParams *)(m_encodeCtx->pPicParams);
    DDI_CHK_NULL(mpeg2SlcParams, "nullptr mpeg2SlcParams", VA_STATUS_ERROR_INVALID_PARAMETER);
    DDI_CHK_NULL(mpeg2PicParams, "nullptr mpeg2PicParams", VA_STATUS_ERROR_INVALID_PARAMETER);

    // Move to a free MPEG2Slice parameter buffer
    uint8_t numOfSlices = m_encodeCtx->dwNumSlices;
    mpeg2SlcParams += numOfSlices;

    //attention memory area protection
    MOS_ZeroMemory(mpeg2SlcParams, sizeof(CodecEncodeMpeg2SliceParmas) * numSlices);

    CodecEncodeMpeg2SequenceParams *mpeg2Sps = (CodecEncodeMpeg2SequenceParams *)m_encodeCtx->pSeqParams;
    auto picWidthInMb = CODECHAL_GET_WIDTH_IN_MACROBLOCKS(mpeg2Sps->m_frameWidth);

    for (uint32_t slcCount = 0; slcCount < numSlices; slcCount++)
    {
        mpeg2SlcParams->m_numMbsForSlice     = vaEncSlcParamsMPEG2->num_macroblocks;
        mpeg2SlcParams->m_firstMbX           = (vaEncSlcParamsMPEG2->macroblock_address % picWidthInMb);
        mpeg2SlcParams->m_firstMbY           = vaEncSlcParamsMPEG2->macroblock_address / picWidthInMb;
        mpeg2SlcParams->m_intraSlice         = vaEncSlcParamsMPEG2->is_intra_slice;
        mpeg2SlcParams->m_quantiserScaleCode = vaEncSlcParamsMPEG2->quantiser_scale_code;
        mpeg2SlcParams++;
        vaEncSlcParamsMPEG2++;
    }
    m_encodeCtx->dwNumSlices += numSlices;
    mpeg2PicParams->m_numSlice = m_encodeCtx->dwNumSlices;

    return VA_STATUS_SUCCESS;
}

// Since sequence header and picture header will be packed in codechal, we don't support packed header from application
VAStatus DdiEncodeMpeg2::ParsePackedHeaderParams(void *ptr)
{
    DDI_CHK_NULL(ptr, "invalid paked header pointer", VA_STATUS_ERROR_INVALID_PARAMETER);
    DDI_CHK_NULL(m_encodeCtx, "invalid encode context", VA_STATUS_ERROR_INVALID_CONTEXT);

    VAEncPackedHeaderParameterBuffer *packedHeaderParamBuf = (VAEncPackedHeaderParameterBuffer *)ptr;
    if (packedHeaderParamBuf->type != VAEncPackedHeaderRawData)
    {
        return VA_STATUS_ERROR_INVALID_PARAMETER;
    }

    CodecEncodeMpeg2UserDataList *userDataNode = (CodecEncodeMpeg2UserDataList *)MOS_AllocAndZeroMemory(sizeof(CodecEncodeMpeg2UserDataList));
    DDI_CHK_NULL(userDataNode, "nullptr userDataNode.", VA_STATUS_ERROR_ALLOCATION_FAILED);

    if (nullptr == m_userDataListHead)
    {
        m_userDataListHead = userDataNode;
        m_userDataListTail = nullptr;
    }
    if (m_userDataListTail)
    {
        CodecEncodeMpeg2UserDataList *userDataListTail = (CodecEncodeMpeg2UserDataList *)m_userDataListTail;
        userDataListTail->m_nextItem                   = userDataNode;
    }
    m_userDataListTail = userDataNode;

    uint32_t size            = (packedHeaderParamBuf->bit_length + 7) >> 3;
    userDataNode->m_userData = MOS_AllocAndZeroMemory(size);
    if (nullptr == userDataNode->m_userData)
    {
        MOS_FreeMemory(userDataNode);
        return VA_STATUS_ERROR_ALLOCATION_FAILED;
    }
    userDataNode->m_userDataSize = size;

    return VA_STATUS_SUCCESS;
}
// Since sequence header and picture header will be packed in codechal, we don't support packed header from application
VAStatus DdiEncodeMpeg2::ParsePackedHeaderData(void *ptr)
{
    DDI_CHK_NULL(ptr, "nullptr ptr", VA_STATUS_ERROR_INVALID_PARAMETER);
    DDI_CHK_NULL(m_encodeCtx, "nullptr m_encodeCtx", VA_STATUS_ERROR_INVALID_CONTEXT);

    CodecEncodeMpeg2UserDataList *userDataListTail = (CodecEncodeMpeg2UserDataList *)m_userDataListTail;
    if (userDataListTail && userDataListTail->m_userDataSize)
    {
        MOS_SecureMemcpy(
            userDataListTail->m_userData,
            userDataListTail->m_userDataSize,
            ptr,
            userDataListTail->m_userDataSize);
        return VA_STATUS_SUCCESS;
    }
    return VA_STATUS_ERROR_INVALID_PARAMETER;
}

// it should be named DdiMpeg2Encode_ParseMiscParamVBV, because HRD is used by AVC, VBV is used by MPEG2
void DdiEncodeMpeg2::ParseMiscParamVBV(void *data)
{
    VAEncMiscParameterHRD *vaEncMiscParamHRD = (VAEncMiscParameterHRD *)data;

    CodecEncodeMpeg2SequenceParams *mpeg2SeqParams = (CodecEncodeMpeg2SequenceParams *)m_encodeCtx->pSeqParams;

    mpeg2SeqParams->m_vbvBufferSize              = vaEncMiscParamHRD->buffer_size;
    mpeg2SeqParams->m_initVBVBufferFullnessInBit = vaEncMiscParamHRD->initial_buffer_fullness;
    mpeg2SeqParams->m_rateControlMethod          = RATECONTROL_CBR;
}

// Parse the frame rate paramters from app
void DdiEncodeMpeg2::ParseMiscParamFR(void *data)
{
    CodecEncodeMpeg2SequenceParams *mpeg2SeqParams = (CodecEncodeMpeg2SequenceParams *)(m_encodeCtx->pSeqParams);

    VAEncMiscParameterFrameRate *vaencMiscParamFR = (VAEncMiscParameterFrameRate *)data;

    float frameRate                 = (float)(vaencMiscParamFR->framerate & 0xffff);
    uint32_t denominator            = (vaencMiscParamFR->framerate >> 16) & 0xffff;
    if(denominator == 0)
    {
        denominator = 1;
    }
    frameRate                       = frameRate / denominator;
    mpeg2SeqParams->m_frameRateCode = (uint32_t)CalculateFramerateCode(frameRate,
        mpeg2SeqParams->m_frameRateExtD,
        mpeg2SeqParams->m_frameRateExtN);
}

// Parse rate control related information from app
void DdiEncodeMpeg2::ParseMiscParamRC(void *data)
{
    VAEncMiscParameterRateControl *vaEncMiscParamRC = (VAEncMiscParameterRateControl *)data;

    CodecEncodeMpeg2SequenceParams *mpeg2SeqParams = (CodecEncodeMpeg2SequenceParams *)(m_encodeCtx->pSeqParams);

    mpeg2SeqParams->m_bitrate = MOS_ROUNDUP_DIVIDE(vaEncMiscParamRC->bits_per_second, CODECHAL_ENCODE_BRC_KBPS);

    if (VA_RC_CQP == m_encodeCtx->uiRCMethod)
    {
        mpeg2SeqParams->m_rateControlMethod = (RATECONTROL_CBR | RATECONTROL_VBR);
    }

    if (VA_RC_CBR == m_encodeCtx->uiRCMethod)
    {
        mpeg2SeqParams->m_maxBitRate        = mpeg2SeqParams->m_bitrate;
        mpeg2SeqParams->m_minBitRate        = mpeg2SeqParams->m_bitrate;
        mpeg2SeqParams->m_rateControlMethod = RATECONTROL_CBR;
    }
    else
    {
        mpeg2SeqParams->m_maxBitRate = mpeg2SeqParams->m_bitrate;
        if (vaEncMiscParamRC->target_percentage > 50)
        {
            mpeg2SeqParams->m_minBitRate = mpeg2SeqParams->m_bitrate * (2 * vaEncMiscParamRC->target_percentage - 100) / 100;
        }
        else
        {
            mpeg2SeqParams->m_minBitRate = 0;
        }

        mpeg2SeqParams->m_bitrate           = mpeg2SeqParams->m_bitrate * vaEncMiscParamRC->target_percentage / 100;
        mpeg2SeqParams->m_rateControlMethod = RATECONTROL_VBR;

        if ((m_encodeCtx->uiTargetBitRate != mpeg2SeqParams->m_bitrate) ||
            (m_encodeCtx->uiMaxBitRate != mpeg2SeqParams->m_maxBitRate))
        {
            mpeg2SeqParams->m_resetBRC   = 0x1;
            m_encodeCtx->uiTargetBitRate = mpeg2SeqParams->m_bitrate;
            m_encodeCtx->uiMaxBitRate    = mpeg2SeqParams->m_maxBitRate;
        }
    }
}

// Parse some other parameters, now there are no Private data from APP
void DdiEncodeMpeg2::ParseMiscParamEncQuality(void *data)
{
    VAEncMiscParameterEncQuality *vaEncMiscParamEncQuality = (VAEncMiscParameterEncQuality *)data;

    CodecEncodeMpeg2SequenceParams *mpeg2SeqParams = (CodecEncodeMpeg2SequenceParams *)(m_encodeCtx->pSeqParams);

    mpeg2SeqParams->m_forcePanicModeControl = 1;
    mpeg2SeqParams->m_panicModeDisable = (uint8_t)vaEncMiscParamEncQuality->PanicModeDisable;

}

void DdiEncodeMpeg2::ParseMiscParamEncQualityLevel(void *data)
{
    VAEncMiscParameterBufferQualityLevel *vaMiscParamQualityLevel = (VAEncMiscParameterBufferQualityLevel*) data;
    m_encodeCtx->targetUsage= vaMiscParamQualityLevel->quality_level;
    // check TU, correct to default if wrong
    if ((m_encodeCtx->targetUsage > TARGETUSAGE_BEST_SPEED) ||
        (0 == m_encodeCtx->targetUsage))
    {
        m_encodeCtx->targetUsage = TARGETUSAGE_RT_SPEED;
        DDI_ASSERTMESSAGE("Target usage from application is not correct, should be in [0,7]!\n");
    }
}

void DdiEncodeMpeg2::ParseMiscParamMaxFrame(void *data)
{
    if (nullptr == m_encodeCtx || nullptr == data)
    {
        DDI_ASSERTMESSAGE("invalidate input parameters.");
        return;
    }

    CodecEncodeMpeg2SequenceParams *mpeg2SeqParams = (CodecEncodeMpeg2SequenceParams *)(m_encodeCtx->pSeqParams);

    VAEncMiscParameterBufferMaxFrameSize *maxFrameSize = (VAEncMiscParameterBufferMaxFrameSize *)data;

    if (VAEncMiscParameterTypeMaxFrameSize == maxFrameSize->type)
    {
        mpeg2SeqParams->m_userMaxFrameSize = maxFrameSize->max_frame_size;
    }
}

void DdiEncodeMpeg2::ParseMiscParamTypeExtension(void *data)
{
    if (nullptr == m_encodeCtx || nullptr == data)
    {
        DDI_ASSERTMESSAGE("invalidate input parameters.");
        return;
    }

    CodecEncodeMpeg2VuiParams *mpeg2VuiParams = (CodecEncodeMpeg2VuiParams *)(m_encodeCtx->pVuiParams);

    VAEncMiscParameterExtensionDataSeqDisplayMPEG2 *displayExt = (VAEncMiscParameterExtensionDataSeqDisplayMPEG2 *)data;

    if (displayExt->extension_start_code_identifier != Mpeg2sequenceDisplayExtension)
    {
        return;
    }

    m_encodeCtx->bNewVuiData                  = true;
    mpeg2VuiParams->m_videoFormat             = displayExt->video_format;
    mpeg2VuiParams->m_colourDescription       = displayExt->colour_description;
    mpeg2VuiParams->m_colourPrimaries         = displayExt->colour_primaries;
    mpeg2VuiParams->m_transferCharacteristics = displayExt->transfer_characteristics;
    mpeg2VuiParams->m_matrixCoefficients      = displayExt->matrix_coefficients;
    mpeg2VuiParams->m_displayHorizontalSize   = displayExt->display_horizontal_size;
    mpeg2VuiParams->m_displayVerticalSize     = displayExt->display_vertical_size;
}

// parse VAEncMiscParameterSkipFrame to PCODEC_MPEG2_ENCODE_PIC_PARAMS
void DdiEncodeMpeg2::ParseMiscParamSkipFrame(void *data)
{
    if (nullptr == m_encodeCtx || nullptr == data)
    {
        DDI_ASSERTMESSAGE("invalidate input parameters.");
        return;
    }

    // Assume only one PPS here, modify when enable multiple PPS support
    CodecEncodeMpeg2PictureParams *mpeg2PicParams = (CodecEncodeMpeg2PictureParams *)(m_encodeCtx->pPicParams);

    VAEncMiscParameterSkipFrame *vaEncMiscParamSkipFrame = (VAEncMiscParameterSkipFrame *)data;

    if (nullptr == mpeg2PicParams)
    {
        DDI_ASSERTMESSAGE("invalidate mpeg2 picture header.");
        return;
    }
    //MPEG2 only support normal skip frame
    if (FRAME_SKIP_NORMAL < mpeg2PicParams->m_skipFrameFlag)
    {
        DDI_ASSERTMESSAGE("invalidate input parameters, skip_frame_flag should equal to 0 or 1.");
        return;
    }

    // populate skipped frame params from DDI
    mpeg2PicParams->m_skipFrameFlag  = vaEncMiscParamSkipFrame->skip_frame_flag;
    mpeg2PicParams->m_numSkipFrames  = vaEncMiscParamSkipFrame->num_skip_frames;
    mpeg2PicParams->m_sizeSkipFrames = vaEncMiscParamSkipFrame->size_skip_frames;
}

// Parse Misc Params
VAStatus DdiEncodeMpeg2::ParseMiscParams(void *ptr)
{
    DDI_CHK_NULL(m_encodeCtx, "nullptr m_encodeCtx", VA_STATUS_ERROR_INVALID_PARAMETER);
    DDI_CHK_NULL(ptr, "nullptr ptr", VA_STATUS_ERROR_INVALID_PARAMETER);

    VAEncMiscParameterBuffer *miscParamBuf = (VAEncMiscParameterBuffer *)ptr;
    DDI_CHK_NULL(miscParamBuf->data, "nullptr miscParamBuf->data", VA_STATUS_ERROR_INVALID_PARAMETER);

    switch ((int32_t)(miscParamBuf->type))
    {
    case VAEncMiscParameterTypeHRD:
    {
        ParseMiscParamVBV((void *)miscParamBuf->data);
        break;
    }
    case VAEncMiscParameterTypeFrameRate:
    {
        ParseMiscParamFR((void *)miscParamBuf->data);
        break;
    }
    case VAEncMiscParameterTypeRateControl:
    {
        ParseMiscParamRC((void *)miscParamBuf->data);
        break;
    }
    case VAEncMiscParameterTypeEncQuality:
    {
        ParseMiscParamEncQuality((void *)miscParamBuf->data);
        break;
    }
    case VAEncMiscParameterTypeMaxFrameSize:
    {
        ParseMiscParamMaxFrame((void *)miscParamBuf->data);
        break;
    }
    case VAEncMiscParameterTypeExtensionData:
    {
        ParseMiscParamTypeExtension((void *)miscParamBuf->data);
        break;
    }
    case VAEncMiscParameterTypeSkipFrame:
    {
        ParseMiscParamSkipFrame((void *)miscParamBuf->data);
        break;
    }
    case VAEncMiscParameterTypeQualityLevel:
    {
        ParseMiscParamEncQualityLevel((void *)miscParamBuf->data);
        break;
    }

    default:
    {
        DDI_ASSERTMESSAGE("DDI: unsupported misc parameter type.");
        return VA_STATUS_ERROR_FLAG_NOT_SUPPORTED;
    }
    }

    return VA_STATUS_SUCCESS;
}

// get framerate code from frame rate value;
// frame_rate = frame_rate_value * (frame_rate_extension_n + 1)  (frame_rate_extension_d + 1)
uint32_t DdiEncodeMpeg2::CalculateFramerateCode(
    float   frameRate,
    uint8_t frameRateExtD,
    uint8_t frameRateExtN)
{
    if (frameRate <= 0.0)
    {
        return 0;
    }

    float frameRateValue = frameRate * (frameRateExtD + 1) / (frameRateExtN + 1);

    uint32_t delta         = 0xffffffff;
    uint32_t frameRateCode = 0;
    for (uint32_t i = 0; i < sizeof(frameRateTable) / sizeof(frameRateTable[0]); i++)
    {
        float tempResidual = frameRateTable[i].value - frameRateValue;
        tempResidual       = tempResidual < 0 ? -tempResidual : tempResidual;

        uint32_t residual = (uint32_t)(tempResidual * 1000);
        if (residual < delta)
        {
            delta         = residual;
            frameRateCode = frameRateTable[i].code;
        }
    }
    // if the gap from spec and custom requirement is too huge, error will be report
    if (delta > 1000)
    {
        return 0;
    }
    else
    {
        return frameRateCode;
    }
}

VAStatus DdiEncodeMpeg2::RemoveUserData()
{
    DDI_CHK_NULL(m_encodeCtx, "invalid encode context", VA_STATUS_ERROR_INVALID_CONTEXT);

    if (m_userDataListHead)
    {
        CodecEncodeMpeg2UserDataList *cur  = nullptr;
        CodecEncodeMpeg2UserDataList *next = nullptr;
        for (cur = (CodecEncodeMpeg2UserDataList *)m_userDataListHead; cur != nullptr; cur = next)
        {
            next = cur->m_nextItem;
            MOS_FreeMemory(cur->m_userData);
            MOS_FreeMemory(cur);
        }
        m_userDataListHead = nullptr;
        m_userDataListTail = nullptr;
    }
    return VA_STATUS_SUCCESS;
}

uint32_t DdiEncodeMpeg2::getSliceParameterBufferSize()
{
    return sizeof(VAEncSliceParameterBufferMPEG2);
}

uint32_t DdiEncodeMpeg2::getSequenceParameterBufferSize()
{
    return sizeof(VAEncSequenceParameterBufferMPEG2);
}

uint32_t DdiEncodeMpeg2::getPictureParameterBufferSize()
{
    return sizeof(VAEncPictureParameterBufferMPEG2);
}

uint32_t DdiEncodeMpeg2::getQMatrixBufferSize()
{
    return sizeof(VAIQMatrixBufferMPEG2);
}
