/*
* 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_vp9.cpp
//! \brief    Defines class for DDI media vp9 encode.
//!

#include "media_libva.h"
#include "media_libva_encoder.h"
#include "media_ddi_encode_vp9.h"
#include "media_libva_util.h"
#include "codec_def_encode_vp9.h"
#include "media_ddi_encode_const.h"
#include "media_ddi_factory.h"
#include "media_libvpx_vp9.h"

extern template class MediaDdiFactoryNoArg<DdiEncodeBase>;
static bool isEncodeVp9Registered =
    MediaDdiFactoryNoArg<DdiEncodeBase>::RegisterCodec<DdiEncodeVp9>(ENCODE_ID_VP9);

DdiEncodeVp9::~DdiEncodeVp9()
{
    if (m_encodeCtx == nullptr)
    {
        return;
    }

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

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

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

    MOS_FreeMemory(m_encodeCtx->pEncodeStatusReport);
    m_encodeCtx->pEncodeStatusReport = 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;

    if (m_encodeCtx->ppNALUnitParams && m_encodeCtx->ppNALUnitParams[0])
    {
        /* ppNALUnitParams[0] indicates the start address of NALUnitParams */
        MOS_FreeMemory(m_encodeCtx->ppNALUnitParams[0]);
        m_encodeCtx->ppNALUnitParams[0] = nullptr;
    }

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

    MOS_FreeMemory(m_segParams);
    m_segParams = nullptr;

    MOS_FreeMemory(m_codedBufStatus);
    m_codedBufStatus = nullptr;
}

VAStatus DdiEncodeVp9::EncodeInCodecHal(uint32_t numSlices)
{
    DDI_UNUSED(numSlices);

    DDI_CHK_NULL(m_encodeCtx, "nullptr m_encodeCtx", VA_STATUS_ERROR_INVALID_CONTEXT);
    DDI_CHK_NULL(m_encodeCtx->pCodecHal, "nullptr m_encodeCtx->pCodecHal", VA_STATUS_ERROR_INVALID_CONTEXT);

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

    CODEC_VP9_ENCODE_SEQUENCE_PARAMS *seqParams = (PCODEC_VP9_ENCODE_SEQUENCE_PARAMS)(m_encodeCtx->pSeqParams);
    CODEC_VP9_ENCODE_PIC_PARAMS *vp9PicParam = (PCODEC_VP9_ENCODE_PIC_PARAMS)(m_encodeCtx->pPicParams);

    EncoderParams encodeParams;
    MOS_ZeroMemory(&encodeParams, sizeof(EncoderParams));
    encodeParams.ExecCodecFunction = m_encodeCtx->codecFunction;

    /* check whether the target bit rate is initialized for BRC */
    if ((VA_RC_CBR == m_encodeCtx->uiRCMethod) ||
        (VA_RC_VBR == m_encodeCtx->uiRCMethod))
    {
        if (seqParams->TargetBitRate[0] == 0)
        {
            DDI_ASSERTMESSAGE("DDI: No RateControl param for BRC\n!");
            return VA_STATUS_ERROR_INVALID_PARAMETER;
        }
    }

    // Raw Surface
    MOS_SURFACE rawSurface;
    MOS_ZeroMemory(&rawSurface, sizeof(MOS_SURFACE));

    DdiMedia_MediaSurfaceToMosResource(rtTbl->pCurrentRT, &(rawSurface.OsResource));

    switch (rawSurface.OsResource.Format)
    {
    case Format_NV12:
        seqParams->SeqFlags.fields.SourceFormat   = VP9_ENCODED_CHROMA_FORMAT_YUV420;
        seqParams->SeqFlags.fields.SourceBitDepth = VP9_ENCODED_BIT_DEPTH_8;
        break;
    case Format_YUY2:
        seqParams->SeqFlags.fields.SourceFormat   = VP9_ENCODED_CHROMA_FORMAT_YUV422;
        seqParams->SeqFlags.fields.SourceBitDepth = VP9_ENCODED_BIT_DEPTH_8;
        break;
    case Format_UYVY:
        seqParams->SeqFlags.fields.SourceFormat   = VP9_ENCODED_CHROMA_FORMAT_YUV422;
        seqParams->SeqFlags.fields.SourceBitDepth = VP9_ENCODED_BIT_DEPTH_8;
        break;
    case Format_AYUV:
    case Format_A8R8G8B8:
    case Format_A8B8G8R8:
        seqParams->SeqFlags.fields.SourceFormat   = VP9_ENCODED_CHROMA_FORMAT_YUV444;
        seqParams->SeqFlags.fields.SourceBitDepth = VP9_ENCODED_BIT_DEPTH_8;
        break;
    case Format_P010:
        seqParams->SeqFlags.fields.SourceFormat   = VP9_ENCODED_CHROMA_FORMAT_YUV420;
        seqParams->SeqFlags.fields.SourceBitDepth = VP9_ENCODED_BIT_DEPTH_10;
        break;
    case Format_Y210:
        seqParams->SeqFlags.fields.SourceFormat   = VP9_ENCODED_CHROMA_FORMAT_YUV422;
        seqParams->SeqFlags.fields.SourceBitDepth = VP9_ENCODED_BIT_DEPTH_10;
        break;
    case Format_Y410:
    case Format_R10G10B10A2:
    case Format_B10G10R10A2:
        seqParams->SeqFlags.fields.SourceFormat   = VP9_ENCODED_CHROMA_FORMAT_YUV444;
        seqParams->SeqFlags.fields.SourceBitDepth = VP9_ENCODED_BIT_DEPTH_10;
        break;
    // P016 and Y416 aren't supported yet so return error for these formats
    case Format_P016:
    case Format_Y416:
    default:
        DDI_ASSERTMESSAGE("DDI:Incorrect Format for input surface\n!");
        return VA_STATUS_ERROR_INVALID_PARAMETER;
        break;
    }

    rawSurface.Format = rawSurface.OsResource.Format;

    // Recon Surface
    MOS_SURFACE reconSurface;
    MOS_ZeroMemory(&reconSurface, sizeof(MOS_SURFACE));
    reconSurface.dwOffset = 0;

    DdiMedia_MediaSurfaceToMosResource(rtTbl->pCurrentReconTarget, &(reconSurface.OsResource));

    switch (m_encodeCtx->vaProfile)
    {
    case VAProfileVP9Profile1:
        reconSurface.Format                        = Format_AYUV;
        seqParams->SeqFlags.fields.EncodedFormat   = VP9_ENCODED_CHROMA_FORMAT_YUV444;
        seqParams->SeqFlags.fields.EncodedBitDepth = VP9_ENCODED_BIT_DEPTH_8;
        break;
    case VAProfileVP9Profile2:
        reconSurface.Format                        = Format_P010;
        seqParams->SeqFlags.fields.EncodedFormat   = VP9_ENCODED_CHROMA_FORMAT_YUV420;
        seqParams->SeqFlags.fields.EncodedBitDepth = VP9_ENCODED_BIT_DEPTH_10;
        break;
    case VAProfileVP9Profile3:
        reconSurface.Format                        = Format_Y410;
        seqParams->SeqFlags.fields.EncodedFormat   = VP9_ENCODED_CHROMA_FORMAT_YUV444;
        seqParams->SeqFlags.fields.EncodedBitDepth = VP9_ENCODED_BIT_DEPTH_10;
        break;
    case VAProfileVP9Profile0:
    default:
        reconSurface.Format                        = Format_NV12;
        seqParams->SeqFlags.fields.EncodedFormat   = VP9_ENCODED_CHROMA_FORMAT_YUV420;
        seqParams->SeqFlags.fields.EncodedBitDepth = VP9_ENCODED_BIT_DEPTH_8;
        break;
    }

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

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

    encodeParams.psRawSurface               = &rawSurface;
    encodeParams.psReconSurface             = &reconSurface;
    encodeParams.presBitstreamBuffer        = &bitstreamSurface;
    encodeParams.presMbCodeSurface          = &m_encodeCtx->resMbCodeBuffer;

    // Segmentation map buffer
    encodeParams.psMbSegmentMapSurface = &m_encodeCtx->segMapBuffer;
    encodeParams.bSegmentMapProvided   = !Mos_ResourceIsNull(&m_encodeCtx->segMapBuffer.OsResource);

    if (VA_RC_CQP == m_encodeCtx->uiRCMethod)
    {
        seqParams->RateControlMethod          = RATECONTROL_CQP;
        seqParams->TargetBitRate[0]           = 0;
        seqParams->MaxBitRate                 = 0;
        seqParams->MinBitRate                 = 0;
        seqParams->InitVBVBufferFullnessInBit = 0;
        seqParams->VBVBufferSizeInBit         = 0;
    }
    else if (VA_RC_CBR == m_encodeCtx->uiRCMethod)
    {
        seqParams->RateControlMethod = RATECONTROL_CBR;
        seqParams->MaxBitRate        = MOS_MAX(seqParams->MaxBitRate, seqParams->TargetBitRate[0]);
        seqParams->MinBitRate        = MOS_MIN(seqParams->MinBitRate, seqParams->TargetBitRate[0]);
    }
    else if (VA_RC_VBR == m_encodeCtx->uiRCMethod)
    {
        seqParams->RateControlMethod = RATECONTROL_VBR;
    }
    else if(VA_RC_ICQ == m_encodeCtx->uiRCMethod)
    {
        seqParams->RateControlMethod = RATECONTROL_CQL;
    }

    if (m_encodeCtx->bNewSeq)
        seqParams->TargetUsage = vp9TargetUsage;

    /* If the segmentation is not enabled, the SegData will be reset */
    if (vp9PicParam->PicFlags.fields.segmentation_enabled == 0)
    {
        DDI_CHK_NULL(m_segParams, "nullptr m_segParams", VA_STATUS_ERROR_INVALID_PARAMETER);
        for (int i = 0; i < 8; i++)
        {
            MOS_ZeroMemory(&(m_segParams->SegData[i]), sizeof(CODEC_VP9_ENCODE_SEG_PARAMS));
        }
    }
    else if (!isSegParamsChanged && 
            vp9PicParam->PicFlags.fields.frame_type != CODEC_VP9_KEY_FRAME && 
            vp9PicParam->PicFlags.fields.error_resilient_mode == 0)
    {
        /* segmentation is enabled, but segment parameters are not changed */
        vp9PicParam->PicFlags.fields.seg_update_data = 0;
    }

    encodeParams.pSeqParams      = m_encodeCtx->pSeqParams;
    encodeParams.pPicParams      = m_encodeCtx->pPicParams;
    encodeParams.pSliceParams    = m_encodeCtx->pSliceParams;
    encodeParams.ppNALUnitParams = m_encodeCtx->ppNALUnitParams;
    encodeParams.pSegmentParams  = m_segParams;
    
    if(seqParams->NumTemporalLayersMinus1 > 7)
    {
        return VA_STATUS_ERROR_INVALID_PARAMETER;
    }
    for (uint32_t i = 0; i < (seqParams->NumTemporalLayersMinus1+1); i++)
    {
        if (savedFrameRate[i] == 0)
        {
            /* use the default framerate if FrameRate is not passed */
            seqParams->FrameRate[i].uiNumerator   = 30;
            seqParams->FrameRate[i].uiDenominator = 1;
        }
    }

    if (!headerInsertFlag)
    {
        vp9_header_bitoffset picBitOffset;
        uint32_t headerLen = 0;
        uint32_t codecProfile = VP9_PROFILE_0;

        if ((m_encodeCtx->vaProfile >= VAProfileVP9Profile0) &&
            (m_encodeCtx->vaProfile <= VAProfileVP9Profile3))
        {
            codecProfile = m_encodeCtx->vaProfile - VAProfileVP9Profile0;
        }

        Vp9WriteUncompressHeader(m_encodeCtx,
                                   codecProfile,
                                   m_encodeCtx->pbsBuffer->pBase,
                                   &headerLen,
                                   &picBitOffset);

        vp9PicParam->BitOffsetForFirstPartitionSize = picBitOffset.bit_offset_first_partition_size;
        vp9PicParam->BitOffsetForQIndex             = picBitOffset.bit_offset_qindex;
        vp9PicParam->BitOffsetForLFLevel            = picBitOffset.bit_offset_lf_level;
        vp9PicParam->BitOffsetForLFRefDelta         = picBitOffset.bit_offset_ref_lf_delta;
        vp9PicParam->BitOffsetForLFModeDelta        = picBitOffset.bit_offset_mode_lf_delta;
        vp9PicParam->BitOffsetForSegmentation       = picBitOffset.bit_offset_segmentation;
        vp9PicParam->BitSizeForSegmentation         = picBitOffset.bit_size_segmentation;

        m_encodeCtx->ppNALUnitParams[0]->uiNalUnitType             = 0x22;
        m_encodeCtx->ppNALUnitParams[0]->bInsertEmulationBytes     = false;
        m_encodeCtx->ppNALUnitParams[0]->uiSkipEmulationCheckCount = 0;
        m_encodeCtx->ppNALUnitParams[0]->uiSize                    = headerLen;
        m_encodeCtx->ppNALUnitParams[0]->uiOffset                  = 0;
    }

    encodeParams.bNewSeq = m_encodeCtx->bNewSeq;
    if (seqParams->SeqFlags.fields.bResetBRC)
    {
        /* When the BRC needs to be reset, it indicates that the new Seq is issued. */
        encodeParams.bNewSeq = true;
    }

    encodeParams.bNewQmatrixData = m_encodeCtx->bNewQmatrixData;
    encodeParams.bPicQuant       = m_encodeCtx->bPicQuant;

    encodeParams.pBSBuffer = m_encodeCtx->pbsBuffer;

    MOS_STATUS 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;
}

VAStatus DdiEncodeVp9::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 = m_encodeCtx->codecFunction;
    codecHalSettings->width       = m_encodeCtx->dworiFrameWidth;
    codecHalSettings->height      = m_encodeCtx->dworiFrameHeight;
    codecHalSettings->mode          = m_encodeCtx->wModeType;
    codecHalSettings->standard      = CODECHAL_VP9;
    codecHalSettings->chromaFormat  = (m_chromaFormat == yuv444) ?
        VP9_ENCODED_CHROMA_FORMAT_YUV444 : VP9_ENCODED_CHROMA_FORMAT_YUV420;
    codecHalSettings->lumaChromaDepth = CODECHAL_LUMA_CHROMA_DEPTH_8_BITS;
    if (m_is10Bit)
    {
        codecHalSettings->lumaChromaDepth = CODECHAL_LUMA_CHROMA_DEPTH_10_BITS;
    }

    VAStatus vaStatus = VA_STATUS_SUCCESS;

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

    m_encodeCtx->pPicParams = (void *)MOS_AllocAndZeroMemory(sizeof(CODEC_VP9_ENCODE_PIC_PARAMS));
    DDI_CHK_NULL(m_encodeCtx->pPicParams, "nullptr m_encodeCtx->pPicParams.", 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);

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

    /* It is enough to allocate 4096 bytes for VP9 packed header */
    m_encodeCtx->pbsBuffer->BufferSize = 4096;
    m_encodeCtx->pbsBuffer->pBase      = (uint8_t *)MOS_AllocAndZeroMemory(m_encodeCtx->pbsBuffer->BufferSize);
    DDI_CHK_NULL(m_encodeCtx->pbsBuffer->pBase, "nullptr m_encodeCtx->pbsBuffer->pBase.", VA_STATUS_ERROR_ALLOCATION_FAILED);

    const int32_t packedNum = 2;
    /* VP9 has only one Packed header.  */
    m_encodeCtx->ppNALUnitParams = (PCODECHAL_NAL_UNIT_PARAMS *)MOS_AllocAndZeroMemory(sizeof(PCODECHAL_NAL_UNIT_PARAMS) * packedNum);
    DDI_CHK_NULL(m_encodeCtx->ppNALUnitParams, "nullptr m_encodeCtx->ppNALUnitParams.", VA_STATUS_ERROR_ALLOCATION_FAILED);

    CODECHAL_NAL_UNIT_PARAMS *nalUnitParams = (CODECHAL_NAL_UNIT_PARAMS *)MOS_AllocAndZeroMemory(sizeof(CODECHAL_NAL_UNIT_PARAMS) * packedNum);
    DDI_CHK_NULL(nalUnitParams, "nullptr nalUnitParams.", VA_STATUS_ERROR_ALLOCATION_FAILED);

    for (int32_t i = 0; i < packedNum; i++)
    {
        m_encodeCtx->ppNALUnitParams[i] = &(nalUnitParams[i]);
    }

    // Allocate segment params
    m_segParams = (CODEC_VP9_ENCODE_SEGMENT_PARAMS *)MOS_AllocAndZeroMemory(sizeof(CODEC_VP9_ENCODE_SEGMENT_PARAMS) * 8);
    DDI_CHK_NULL(m_segParams, "nullptr m_segParams.", VA_STATUS_ERROR_ALLOCATION_FAILED);
    m_encodeCtx->pVpxSegParams = (void*)m_segParams;

    // Allocate coded buffer status
    m_codedBufStatus = (VACodedBufferVP9Status *)MOS_AllocAndZeroMemory(DDI_ENCODE_MAX_STATUS_REPORT_BUFFER * sizeof(VACodedBufferVP9Status));
    DDI_CHK_NULL(m_codedBufStatus, "nullptr m_codedBufStatus.", VA_STATUS_ERROR_ALLOCATION_FAILED);

    /* RT is used as the default target usage */
    vp9TargetUsage = TARGETUSAGE_RT_SPEED;

    return vaStatus;
}

VAStatus DdiEncodeVp9::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 VAEncPackedHeaderParameterBufferType:
            DDI_CHK_STATUS(ParsePackedHeaderParams(data), VA_STATUS_ERROR_INVALID_BUFFER);
            break;

        case VAEncPackedHeaderDataBufferType:
            DDI_CHK_STATUS(ParsePackedHeaderData(data), VA_STATUS_ERROR_INVALID_BUFFER);
            break;

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

        case VAEncMacroblockMapBufferType:
            DDI_CHK_STATUS(ParseSegMapParams(buf), 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;
}

// Reset the paramters before each frame
VAStatus DdiEncodeVp9::ResetAtFrameLevel()
{
    DDI_CHK_NULL(m_encodeCtx, "nullptr m_encodeCtx", VA_STATUS_ERROR_INVALID_PARAMETER);

    lastPackedHeaderType = 0;
    headerInsertFlag     = 0;

    CODEC_VP9_ENCODE_SEQUENCE_PARAMS *vp9SeqParam = (PCODEC_VP9_ENCODE_SEQUENCE_PARAMS)(m_encodeCtx->pSeqParams);

    if (vp9SeqParam)
    {
        vp9SeqParam->SeqFlags.fields.bResetBRC = 0;
        vp9SeqParam->MaxBitRate = 0;
        vp9SeqParam->MinBitRate = 0xffffffff;
    }

    m_encodeCtx->bMBQpEnable = false;

    MOS_ZeroMemory(&(m_encodeCtx->segMapBuffer), sizeof(MOS_SURFACE));

    return VA_STATUS_SUCCESS;
}

VAStatus DdiEncodeVp9::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);

    VAEncSequenceParameterBufferVP9 *seqParams = (VAEncSequenceParameterBufferVP9 *)ptr;

    CODEC_VP9_ENCODE_SEQUENCE_PARAMS *vp9SeqParams = (PCODEC_VP9_ENCODE_SEQUENCE_PARAMS)(m_encodeCtx->pSeqParams);
    DDI_CHK_NULL(vp9SeqParams, "nullptr vp9SeqParams", VA_STATUS_ERROR_INVALID_PARAMETER);

    vp9SeqParams->wMaxFrameWidth   = seqParams->max_frame_width;
    vp9SeqParams->wMaxFrameHeight  = seqParams->max_frame_height;
    vp9SeqParams->GopPicSize       = seqParams->intra_period;

    /* the bits_per_second is only used when the target bit_rate is not initialized */
    if (vp9SeqParams->TargetBitRate[0] == 0)
    {
        vp9SeqParams->TargetBitRate[0] = MOS_ROUNDUP_DIVIDE(seqParams->bits_per_second, CODECHAL_ENCODE_BRC_KBPS);
    }

    if (vp9SeqParams->GopPicSize != savedGopSize)
    {
        savedGopSize = vp9SeqParams->GopPicSize;
        vp9SeqParams->SeqFlags.fields.bResetBRC = 1;
    }

    return VA_STATUS_SUCCESS;
}

VAStatus DdiEncodeVp9::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);

    VAEncPictureParameterBufferVP9 *picParam = (VAEncPictureParameterBufferVP9 *)ptr;

    if ((picParam->frame_width_src == 0) || (picParam->frame_width_dst == 0))
    {
        DDI_ASSERTMESSAGE("DDI: frame width in VP9 PicParam is zero\n.");
        return VA_STATUS_ERROR_INVALID_PARAMETER;
    }
    if ((picParam->frame_height_src == 0) || (picParam->frame_height_dst == 0))
    {
        DDI_ASSERTMESSAGE("DDI: frame height in VP9 PicParam is zero\n.");
        return VA_STATUS_ERROR_INVALID_PARAMETER;
    }

    CODEC_VP9_ENCODE_PIC_PARAMS *vp9PicParam = (PCODEC_VP9_ENCODE_PIC_PARAMS)(m_encodeCtx->pPicParams);

    DDI_CHK_NULL(vp9PicParam, "nullptr vp9PicParam", VA_STATUS_ERROR_INVALID_PARAMETER);

    MOS_ZeroMemory(vp9PicParam, sizeof(CODEC_VP9_ENCODE_PIC_PARAMS));

    vp9PicParam->PicFlags.fields.frame_type                   = picParam->pic_flags.bits.frame_type;
    vp9PicParam->PicFlags.fields.show_frame                   = picParam->pic_flags.bits.show_frame;
    vp9PicParam->PicFlags.fields.error_resilient_mode         = picParam->pic_flags.bits.error_resilient_mode;
    vp9PicParam->PicFlags.fields.intra_only                   = picParam->pic_flags.bits.intra_only;
    vp9PicParam->PicFlags.fields.allow_high_precision_mv      = picParam->pic_flags.bits.allow_high_precision_mv;
    vp9PicParam->PicFlags.fields.mcomp_filter_type            = picParam->pic_flags.bits.mcomp_filter_type;
    vp9PicParam->PicFlags.fields.frame_parallel_decoding_mode = picParam->pic_flags.bits.frame_parallel_decoding_mode;
    vp9PicParam->PicFlags.fields.reset_frame_context          = picParam->pic_flags.bits.reset_frame_context;
    vp9PicParam->PicFlags.fields.refresh_frame_context        = picParam->pic_flags.bits.refresh_frame_context;
    vp9PicParam->PicFlags.fields.frame_context_idx            = picParam->pic_flags.bits.frame_context_idx;
    vp9PicParam->PicFlags.fields.segmentation_enabled         = picParam->pic_flags.bits.segmentation_enabled;
    vp9PicParam->PicFlags.fields.segmentation_temporal_update = picParam->pic_flags.bits.segmentation_temporal_update;
    vp9PicParam->PicFlags.fields.segmentation_update_map      = picParam->pic_flags.bits.segmentation_update_map;
    vp9PicParam->PicFlags.fields.LosslessFlag                 = picParam->pic_flags.bits.lossless_mode;
    vp9PicParam->PicFlags.fields.comp_prediction_mode         = picParam->pic_flags.bits.comp_prediction_mode;
    vp9PicParam->PicFlags.fields.super_frame                  = picParam->pic_flags.bits.super_frame_flag;
    vp9PicParam->PicFlags.fields.seg_update_data              = picParam->pic_flags.bits.segmentation_enabled;
#if VA_CHECK_VERSION(1, 23, 0)
    vp9PicParam->PicFlags.fields.seg_id_block_size            = picParam->seg_id_block_size;
#endif

    vp9PicParam->SrcFrameWidthMinus1          = picParam->frame_width_src - 1;
    vp9PicParam->SrcFrameHeightMinus1         = picParam->frame_height_src - 1;

    vp9PicParam->DstFrameWidthMinus1          = picParam->frame_width_dst - 1;
    vp9PicParam->DstFrameHeightMinus1         = picParam->frame_height_dst - 1;

    /* width_src and width_dst won't be zero at the same time
     * If only one of them is zero, assume that there is no dynamica scaling.
     * In such case it is dervied.
     */
    if ((picParam->frame_width_src == 0) || (picParam->frame_width_dst == 0))
    {
        if (picParam->frame_width_src == 0)
        {
            vp9PicParam->SrcFrameWidthMinus1 = picParam->frame_width_dst - 1;
        }
        else
        {
            vp9PicParam->DstFrameWidthMinus1 = picParam->frame_width_src - 1;
        }
    }

    /* Handle the zero height by using the mechanism similar to width */
    if ((picParam->frame_height_src == 0) || (picParam->frame_height_dst == 0))
    {
        if (picParam->frame_height_src == 0)
        {
            vp9PicParam->SrcFrameHeightMinus1 = picParam->frame_height_dst - 1;
        }
        else
        {
            vp9PicParam->DstFrameHeightMinus1 = picParam->frame_height_src - 1;
        }
    }

    vp9PicParam->filter_level                 = picParam->filter_level;
    vp9PicParam->sharpness_level              = picParam->sharpness_level;

    vp9PicParam->LumaACQIndex                 = picParam->luma_ac_qindex;
    vp9PicParam->LumaDCQIndexDelta            = picParam->luma_dc_qindex_delta;
    vp9PicParam->ChromaACQIndexDelta          = picParam->chroma_ac_qindex_delta;
    vp9PicParam->ChromaDCQIndexDelta          = picParam->chroma_dc_qindex_delta;

    vp9PicParam->RefFlags.fields.LastRefIdx        = picParam->ref_flags.bits.ref_last_idx;
    vp9PicParam->RefFlags.fields.GoldenRefIdx      = picParam->ref_flags.bits.ref_gf_idx;
    vp9PicParam->RefFlags.fields.AltRefIdx         = picParam->ref_flags.bits.ref_arf_idx;
    vp9PicParam->RefFlags.fields.LastRefSignBias   = picParam->ref_flags.bits.ref_last_sign_bias;
    vp9PicParam->RefFlags.fields.GoldenRefSignBias = picParam->ref_flags.bits.ref_gf_sign_bias;
    vp9PicParam->RefFlags.fields.AltRefSignBias    = picParam->ref_flags.bits.ref_arf_sign_bias;

    vp9PicParam->RefFlags.fields.ref_frame_ctrl_l0   = picParam->ref_flags.bits.ref_frame_ctrl_l0;
    vp9PicParam->RefFlags.fields.ref_frame_ctrl_l1   = picParam->ref_flags.bits.ref_frame_ctrl_l1;
    vp9PicParam->RefFlags.fields.refresh_frame_flags = picParam->refresh_frame_flags;
    vp9PicParam->temporal_id                         = picParam->ref_flags.bits.temporal_id;

    for (int32_t i = 0; i < 4; i++)
    {
        vp9PicParam->LFRefDelta[i] = picParam->ref_lf_delta[i];
    }

    vp9PicParam->LFModeDelta[0] = picParam->mode_lf_delta[0];
    vp9PicParam->LFModeDelta[1] = picParam->mode_lf_delta[1];

    vp9PicParam->sharpness_level = picParam->sharpness_level;

    vp9PicParam->BitOffsetForFirstPartitionSize = picParam->bit_offset_first_partition_size;
    vp9PicParam->BitOffsetForQIndex             = picParam->bit_offset_qindex;
    vp9PicParam->BitOffsetForLFLevel            = picParam->bit_offset_lf_level;
    vp9PicParam->BitOffsetForLFRefDelta         = picParam->bit_offset_ref_lf_delta;
    vp9PicParam->BitOffsetForLFModeDelta        = picParam->bit_offset_mode_lf_delta;
    vp9PicParam->BitOffsetForSegmentation       = picParam->bit_offset_segmentation;
    vp9PicParam->BitSizeForSegmentation         = picParam->bit_size_segmentation;

    vp9PicParam->log2_tile_rows = picParam->log2_tile_rows;
    vp9PicParam->log2_tile_columns = picParam->log2_tile_columns;

    vp9PicParam->SkipFrameFlag  = picParam->skip_frame_flag;
    vp9PicParam->NumSkipFrames  = picParam->number_skip_frames;
    vp9PicParam->SizeSkipFrames = picParam->skip_frames_size;

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

    auto recon = DdiMedia_GetSurfaceFromVASurfaceID(mediaCtx, picParam->reconstructed_frame);
    if(m_encodeCtx->vaProfile == VAProfileVP9Profile1
       ||m_encodeCtx->vaProfile == VAProfileVP9Profile2
       ||m_encodeCtx->vaProfile == VAProfileVP9Profile3)
    {
        recon = DdiMedia_ReplaceSurfaceWithVariant(recon, m_encodeCtx->vaEntrypoint);
    }
    DDI_CHK_RET(RegisterRTSurfaces(rtTbl, recon),"RegisterRTSurfaces failed!");

    SetupCodecPicture(mediaCtx, rtTbl, &vp9PicParam->CurrReconstructedPic,
                                             picParam->reconstructed_frame, false);
    rtTbl->pCurrentReconTarget = recon;
    DDI_CHK_NULL(rtTbl->pCurrentReconTarget, "NULL rtTbl->pCurrentReconTarget", VA_STATUS_ERROR_INVALID_PARAMETER);

    // curr orig pic
    vp9PicParam->CurrOriginalPic.FrameIdx = GetRenderTargetID(rtTbl, rtTbl->pCurrentReconTarget);
    vp9PicParam->CurrOriginalPic.PicFlags = vp9PicParam->CurrReconstructedPic.PicFlags;

    for (int32_t i = 0; i < 8; i++)
    {
        if (picParam->reference_frames[i] != VA_INVALID_SURFACE)
        {
            UpdateRegisteredRTSurfaceFlag(rtTbl, DdiMedia_GetSurfaceFromVASurfaceID(mediaCtx, picParam->reference_frames[i]));
        }
        SetupCodecPicture(
            mediaCtx,
            rtTbl,
            &vp9PicParam->RefFrameList[i],
            picParam->reference_frames[i],
            true);
    }

    DDI_MEDIA_BUFFER *buf = nullptr;

    buf = DdiMedia_GetBufferFromVABufferID(mediaCtx, picParam->coded_buf);
    DDI_CHK_NULL(buf, "nullptr buf", VA_STATUS_ERROR_INVALID_PARAMETER);
    RemoveFromStatusReportQueue(buf);
    DdiMedia_MediaBufferToMosResource(buf, &(m_encodeCtx->resBitstreamBuffer));

    return VA_STATUS_SUCCESS;
}

VAStatus DdiEncodeVp9::ParsePackedHeaderParams(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);

    m_encodeCtx->bLastPackedHdrIsSlice = false;

    VAEncPackedHeaderParameterBuffer *packedHeaderParamBuf = (VAEncPackedHeaderParameterBuffer *)ptr;

    if (packedHeaderParamBuf->type != VAEncPackedHeaderRawData)
    {
        DDI_ASSERTMESSAGE("DDI: incorrect packed header type %d\n.", packedHeaderParamBuf->type);
        return VA_STATUS_ERROR_INVALID_PARAMETER;
    }

    // VP9 will always only have 1 NAL type (PPS)
    m_encodeCtx->ppNALUnitParams[0]->uiNalUnitType             = 0x22;
    m_encodeCtx->ppNALUnitParams[0]->bInsertEmulationBytes     = false;
    m_encodeCtx->ppNALUnitParams[0]->uiSkipEmulationCheckCount = 0;
    m_encodeCtx->ppNALUnitParams[0]->uiSize                    = (packedHeaderParamBuf->bit_length + 7) / 8;
    m_encodeCtx->ppNALUnitParams[0]->uiOffset                  = 0;

    lastPackedHeaderType = VAEncPackedHeaderRawData;

    return VA_STATUS_SUCCESS;
}

VAStatus DdiEncodeVp9::ParsePackedHeaderData(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);

    BSBuffer *bsBuffer = m_encodeCtx->pbsBuffer;
    DDI_CHK_NULL(bsBuffer, "nullptr bsBuffer", VA_STATUS_ERROR_INVALID_PARAMETER);

    if (lastPackedHeaderType != VAEncPackedHeaderRawData)
    {
        DDI_ASSERTMESSAGE("DDI: the packed header param/data is not passed in pair \n.");
        return VA_STATUS_ERROR_INVALID_PARAMETER;
    }

    /* Only one header data is enough */
    if (headerInsertFlag)
    {
        return VA_STATUS_SUCCESS;
    }

    // Since VP9 only has 1 NAL type it's safe to reset each time unconditionally
    bsBuffer->pCurrent    = bsBuffer->pBase;
    bsBuffer->SliceOffset = 0;
    bsBuffer->BitOffset   = 0;
    bsBuffer->BitSize     = 0;

    // copy pps header data
    uint32_t hdrDataSize = m_encodeCtx->ppNALUnitParams[0]->uiSize;
    DDI_CHK_RET(
        MOS_SecureMemcpy(
            bsBuffer->pCurrent,
            bsBuffer->BufferSize,
            (uint8_t *)ptr,
            hdrDataSize),
        "DDI:packed header size is too large to be supported!");

    m_encodeCtx->ppNALUnitParams[0]->uiOffset = bsBuffer->pCurrent - bsBuffer->pBase;

    bsBuffer->pCurrent += hdrDataSize;
    bsBuffer->SliceOffset += hdrDataSize;
    bsBuffer->BitSize += hdrDataSize * 8;
    headerInsertFlag   = true;

    return VA_STATUS_SUCCESS;
}

/*
 * For VP9 this buffer is used to contain segment params
 */
VAStatus DdiEncodeVp9::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);
    DDI_CHK_NULL(m_segParams, "nullptr m_segParams", VA_STATUS_ERROR_INVALID_PARAMETER);

    VAEncMiscParameterTypeVP9PerSegmantParam *segParams = (VAEncMiscParameterTypeVP9PerSegmantParam *)ptr;
    isSegParamsChanged = false;

    for (int32_t i = 0; i < 8; ++i)
    {
        if (   m_segParams->SegData[i].SegmentFlags.fields.SegmentReferenceEnabled != segParams->seg_data[i].seg_flags.bits.segment_reference_enabled
            || m_segParams->SegData[i].SegmentFlags.fields.SegmentReference != segParams->seg_data[i].seg_flags.bits.segment_reference
            || m_segParams->SegData[i].SegmentFlags.fields.SegmentSkipped != segParams->seg_data[i].seg_flags.bits.segment_reference_skipped
            || m_segParams->SegData[i].SegmentQIndexDelta != MOS_CLAMP_MIN_MAX(segParams->seg_data[i].segment_qindex_delta, -255, 255)
            || m_segParams->SegData[i].SegmentLFLevelDelta != MOS_CLAMP_MIN_MAX(segParams->seg_data[i].segment_lf_level_delta, -63, 63))
            isSegParamsChanged = true;

        m_segParams->SegData[i].SegmentFlags.fields.SegmentReferenceEnabled =
            segParams->seg_data[i].seg_flags.bits.segment_reference_enabled;
        m_segParams->SegData[i].SegmentFlags.fields.SegmentReference =
            segParams->seg_data[i].seg_flags.bits.segment_reference;
        m_segParams->SegData[i].SegmentFlags.fields.SegmentSkipped =
            segParams->seg_data[i].seg_flags.bits.segment_reference_skipped;

        m_segParams->SegData[i].SegmentQIndexDelta  =
            MOS_CLAMP_MIN_MAX(segParams->seg_data[i].segment_qindex_delta, -255, 255);

        m_segParams->SegData[i].SegmentLFLevelDelta =
            MOS_CLAMP_MIN_MAX(segParams->seg_data[i].segment_lf_level_delta, -63, 63);
    }

    return VA_STATUS_SUCCESS;
}

VAStatus DdiEncodeVp9::ParseMiscParamVBV(void *data)
{
    VAEncMiscParameterHRD *vaEncMiscParamHRD = (VAEncMiscParameterHRD *)data;

    CODEC_VP9_ENCODE_SEQUENCE_PARAMS *seqParams = (CODEC_VP9_ENCODE_SEQUENCE_PARAMS *)m_encodeCtx->pSeqParams;

    if ((seqParams == nullptr) || (vaEncMiscParamHRD == nullptr))
    {
        return VA_STATUS_ERROR_INVALID_PARAMETER;
    }

    seqParams->VBVBufferSizeInBit         = vaEncMiscParamHRD->buffer_size;
    seqParams->InitVBVBufferFullnessInBit = vaEncMiscParamHRD->initial_buffer_fullness;

    seqParams->UpperVBVBufferLevelThresholdInBit = 800000;
    seqParams->LowerVBVBufferLevelThresholdInBit = 320000;

    if ((savedHrdSize != seqParams->VBVBufferSizeInBit) ||
        (savedHrdBufFullness != seqParams->InitVBVBufferFullnessInBit))
    {
        savedHrdSize        = seqParams->VBVBufferSizeInBit;
        savedHrdBufFullness = seqParams->InitVBVBufferFullnessInBit;
        seqParams->SeqFlags.fields.bResetBRC = 0x1;
    }

    return VA_STATUS_SUCCESS;
}

// Parse the frame rate paramters from app
VAStatus DdiEncodeVp9::ParseMiscParamFR(void *data)
{
    VAEncMiscParameterFrameRate *vaFrameRate = (VAEncMiscParameterFrameRate *)data;
    CODEC_VP9_ENCODE_SEQUENCE_PARAMS *seqParams = (PCODEC_VP9_ENCODE_SEQUENCE_PARAMS)(m_encodeCtx->pSeqParams);

    /* This is the optional */
    if ((vaFrameRate == nullptr) || (seqParams == nullptr) ||
        (vaFrameRate->framerate_flags.bits.temporal_id > seqParams->NumTemporalLayersMinus1))
    {
        return VA_STATUS_ERROR_INVALID_PARAMETER;
    }

    uint32_t temporalId = vaFrameRate->framerate_flags.bits.temporal_id;

    if (vaFrameRate->framerate != savedFrameRate[temporalId])
    {
          savedFrameRate[temporalId] = vaFrameRate->framerate;
          seqParams->SeqFlags.fields.bResetBRC |= 0x1;

          uint32_t frameRate = vaFrameRate->framerate;
          seqParams->FrameRate[temporalId].uiNumerator   = frameRate & (0xFFFF);
          seqParams->FrameRate[temporalId].uiDenominator = (frameRate >> 16) & (0xFFFF);
          if (seqParams->FrameRate[temporalId].uiDenominator == 0)
          {
              seqParams->FrameRate[temporalId].uiDenominator = 1;
          }
    }

    return VA_STATUS_SUCCESS;
}

// Parse rate control related information from app
VAStatus DdiEncodeVp9::ParseMiscParamRC(void *data)
{
    CODEC_VP9_ENCODE_SEQUENCE_PARAMS *seqParams = (PCODEC_VP9_ENCODE_SEQUENCE_PARAMS)(m_encodeCtx->pSeqParams);
    DDI_CHK_NULL(seqParams, "nullptr vp9SeqParams", VA_STATUS_ERROR_INVALID_PARAMETER);

    VAEncMiscParameterRateControl *vaEncMiscParamRC = (VAEncMiscParameterRateControl *)data;
    DDI_CHK_NULL(data, "nullptr ptr", VA_STATUS_ERROR_INVALID_PARAMETER);

    uint32_t temporalId = vaEncMiscParamRC->rc_flags.bits.temporal_id;
    DDI_CHK_LESS(temporalId, (seqParams->NumTemporalLayersMinus1+1),
        "invalid temporal id", VA_STATUS_ERROR_INVALID_PARAMETER);

    uint32_t bitRate                     = MOS_ROUNDUP_DIVIDE(vaEncMiscParamRC->bits_per_second, CODECHAL_ENCODE_BRC_KBPS);
    seqParams->MaxBitRate                = MOS_MAX(seqParams->MaxBitRate, bitRate);
    seqParams->SeqFlags.fields.bResetBRC = vaEncMiscParamRC->rc_flags.bits.reset;  // adding reset here. will apply both CBR and VBR

    if (VA_RC_CBR == m_encodeCtx->uiRCMethod)
    {
        seqParams->TargetBitRate[temporalId] = bitRate;
        seqParams->MinBitRate                = MOS_MIN(seqParams->MinBitRate, bitRate);
        seqParams->RateControlMethod         = RATECONTROL_CBR;
        if (savedTargetBit[temporalId] != bitRate)
        {
            savedTargetBit[temporalId] = bitRate;
            seqParams->SeqFlags.fields.bResetBRC |= 0x1;
        }
    }
    else if (VA_RC_VBR == m_encodeCtx->uiRCMethod || VA_RC_ICQ == m_encodeCtx->uiRCMethod)
    {
        seqParams->TargetBitRate[temporalId] = bitRate * vaEncMiscParamRC->target_percentage / 100;  // VBR target bits
        uint32_t minBitRate = bitRate * abs((int32_t)(2 * vaEncMiscParamRC->target_percentage) - 100) / 100;
        seqParams->MinBitRate = MOS_MIN(seqParams->TargetBitRate[temporalId], minBitRate);
        seqParams->RateControlMethod = RATECONTROL_VBR;

        if ((savedTargetBit[temporalId] != seqParams->TargetBitRate[temporalId]) ||
            (savedMaxBitRate[temporalId] != bitRate))
        {
            savedTargetBit[temporalId]           = seqParams->TargetBitRate[temporalId];
            seqParams->SeqFlags.fields.bResetBRC |= 0x1;
            savedMaxBitRate[temporalId]          = bitRate;
        }
    }

    if (VA_RC_ICQ == m_encodeCtx->uiRCMethod)
    {
        seqParams->ICQQualityFactor  = vaEncMiscParamRC->ICQ_quality_factor;
        seqParams->RateControlMethod = RATECONTROL_CQL;
    }

    /* the reset flag in RC will be considered. */
    seqParams->SeqFlags.fields.bResetBRC |= vaEncMiscParamRC->rc_flags.bits.reset;  // adding reset here. will apply both CBR and VBR

    /* Enabling Dynamic Scaling */
    seqParams->SeqFlags.fields.EnableDynamicScaling = vaEncMiscParamRC->rc_flags.bits.enable_dynamic_scaling;

    return VA_STATUS_SUCCESS;
}

VAStatus DdiEncodeVp9::ParseMiscParamEncQuality(void *data)
{
    DDI_UNUSED(m_encodeCtx);
    DDI_UNUSED(data);

    /* Ignore it */
    return VA_STATUS_SUCCESS;
}

VAStatus DdiEncodeVp9::ParseMiscParamQualityLevel(void *data)
{
    DDI_CHK_NULL(data, "nullptr data", VA_STATUS_ERROR_INVALID_PARAMETER);

    VAEncMiscParameterBufferQualityLevel *vaEncMiscParamQualityLevel = (VAEncMiscParameterBufferQualityLevel *)data;

    /* it will be mapped to 1, 4, 7.
     * 1-2 mapped to the 1
     * 6-7 mapped  the 7.
     * 0-3-4-5 mapped to 4.
     */

    if (vaEncMiscParamQualityLevel->quality_level == 0)
    {
        vp9TargetUsage = TARGETUSAGE_RT_SPEED;
    }
    else if (vaEncMiscParamQualityLevel->quality_level >= TARGETUSAGE_HI_SPEED)
    {
        vp9TargetUsage = TARGETUSAGE_BEST_SPEED;
    }
    else if (vaEncMiscParamQualityLevel->quality_level <= TARGETUSAGE_HI_QUALITY)
    {
#ifdef _FULL_OPEN_SOURCE
        vp9TargetUsage = TARGETUSAGE_RT_SPEED;
#else
        vp9TargetUsage = TARGETUSAGE_BEST_QUALITY;
#endif
    }
    else
    {
        vp9TargetUsage = TARGETUSAGE_RT_SPEED;
    }

    return VA_STATUS_SUCCESS;
}

VAStatus DdiEncodeVp9::ParseMiscParameterTemporalLayerParams(void *data)
{
    DDI_CHK_NULL(data, "nullptr data", VA_STATUS_ERROR_INVALID_PARAMETER);

    CODEC_VP9_ENCODE_SEQUENCE_PARAMS *seqParams = (PCODEC_VP9_ENCODE_SEQUENCE_PARAMS)(m_encodeCtx->pSeqParams);

    VAEncMiscParameterTemporalLayerStructure *vaEncTempLayerStruct = (VAEncMiscParameterTemporalLayerStructure *)data;
    DDI_CHK_LESS(vaEncTempLayerStruct->number_of_layers, (CODECHAL_ENCODE_VP9_MAX_NUM_TEMPORAL_LAYERS+1),
        "invalid number of temporal layers", VA_STATUS_ERROR_INVALID_PARAMETER);

    if (vaEncTempLayerStruct->number_of_layers > 0)
    {
        seqParams->NumTemporalLayersMinus1 = vaEncTempLayerStruct->number_of_layers - 1;
    }
    else
    {
        seqParams->NumTemporalLayersMinus1 = 0;
    }

    return VA_STATUS_SUCCESS;
}

VAStatus DdiEncodeVp9::ParseSegMapParams(DDI_MEDIA_BUFFER *buf)
{
    DDI_CHK_NULL(m_encodeCtx, "nullptr m_encodeCtx", VA_STATUS_ERROR_INVALID_PARAMETER);

    m_encodeCtx->segMapBuffer.Format   = Format_Buffer_2D;
    m_encodeCtx->segMapBuffer.dwOffset = 0;
    DdiMedia_MediaBufferToMosResource(buf, &((m_encodeCtx->segMapBuffer).OsResource));
    return VA_STATUS_SUCCESS;
}

VAStatus DdiEncodeVp9::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);

    VAStatus vaStatus = VA_STATUS_SUCCESS;
    switch ((int32_t)(miscParamBuf->type))
    {
    case VAEncMiscParameterTypeHRD:
    {
        vaStatus = ParseMiscParamVBV((void *)miscParamBuf->data);
        break;
    }
    case VAEncMiscParameterTypeFrameRate:
    {
        vaStatus = ParseMiscParamFR((void *)miscParamBuf->data);
        break;
    }
    case VAEncMiscParameterTypeRateControl:
    {
        vaStatus = ParseMiscParamRC((void *)miscParamBuf->data);
        break;
    }
    case VAEncMiscParameterTypeEncQuality:
    {
        vaStatus = ParseMiscParamEncQuality((void *)miscParamBuf->data);
        break;
    }
    case VAEncMiscParameterTypeTemporalLayerStructure:
    {
        vaStatus = ParseMiscParameterTemporalLayerParams((void *)miscParamBuf->data);
        break;
    }
    case VAEncMiscParameterTypeQualityLevel:
    {
        vaStatus = ParseMiscParamQualityLevel((void *)miscParamBuf->data);
        break;
    }
    default:
    {
        DDI_ASSERTMESSAGE("DDI: unsupported misc parameter type.");
        return VA_STATUS_ERROR_INVALID_PARAMETER;
    }
    }

    return vaStatus;
}

VAStatus DdiEncodeVp9::ReportExtraStatus(
    EncodeStatusReport   *encodeStatusReport,
    VACodedBufferSegment *codedBufferSegment)
{
    DDI_FUNCTION_ENTER();

    DDI_CHK_NULL(encodeStatusReport, "nullptr encodeStatusReport", VA_STATUS_ERROR_INVALID_PARAMETER);
    DDI_CHK_NULL(codedBufferSegment, "nullptr codedBufferSegment", VA_STATUS_ERROR_INVALID_PARAMETER);

    VAStatus vaStatus = VA_STATUS_SUCCESS;

    // The coded buffer status are one-to-one correspondence with report buffers, even though the index is updated.
    VACodedBufferVP9Status *codedBufStatus = &(m_codedBufStatus[m_encodeCtx->statusReportBuf.ulUpdatePosition]);
    codedBufStatus->loop_filter_level = encodeStatusReport->loopFilterLevel;
    codedBufStatus->long_term_indication = encodeStatusReport->LongTermIndication;
    codedBufStatus->next_frame_width = encodeStatusReport->NextFrameWidthMinus1 + 1;
    codedBufStatus->next_frame_height = encodeStatusReport->NextFrameHeightMinus1 + 1;

    /*
     * Ignore the private status buffer temporarily. According to the comment for VACodedBufferVP9Status in VA-API,
     * driver must set codedBufferSegment->status to be VA_CODED_BUF_STATUS_CODEC_SPECIFIC, however
     * VA_CODED_BUF_STATUS_CODEC_SPECIFIC is not defined in VA-API
     */
    // codedBufferSegment->next = codedBufStatus;

    return vaStatus;
}

void DdiEncodeVp9::SetupCodecPicture(
    DDI_MEDIA_CONTEXT                     *mediaCtx,
    DDI_CODEC_RENDER_TARGET_TABLE         *rtTbl,
    CODEC_PICTURE                         *codecHalPic,
    VASurfaceID                           surfaceID,
    bool                                  picReference)
{
    if(VA_INVALID_SURFACE != surfaceID)
    {
        DDI_MEDIA_SURFACE *surface = DdiMedia_GetSurfaceFromVASurfaceID(mediaCtx, surfaceID);
        codecHalPic->FrameIdx = GetRenderTargetID(rtTbl, surface);
    }
    else
    {
        codecHalPic->FrameIdx = (uint8_t)DDI_CODEC_INVALID_FRAME_INDEX;
    }

    if (picReference)
    {
        if (codecHalPic->FrameIdx == (uint8_t)DDI_CODEC_INVALID_FRAME_INDEX)
        {
            codecHalPic->PicFlags = PICTURE_INVALID;
        }
        else
        {
            codecHalPic->PicFlags = PICTURE_SHORT_TERM_REFERENCE;
        }
    }
    else
    {
        codecHalPic->PicFlags = PICTURE_FRAME;
    }
}

uint32_t DdiEncodeVp9::getSequenceParameterBufferSize()
{
        return sizeof(VAEncSequenceParameterBufferVP9);
}

uint32_t DdiEncodeVp9::getPictureParameterBufferSize()
{
        return sizeof(VAEncPictureParameterBufferVP9);
}

uint32_t DdiEncodeVp9::getQMatrixBufferSize()
{
        return sizeof(VAEncSegParamVP9);
}
