/*
* 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_base.cpp
//! \brief    Implements base class for DDI media encode and encode parameters parser
//!

#include "media_libva_util.h"
#include "media_libva_common.h"
#include "media_ddi_encode_base.h"

DdiEncodeBase::DdiEncodeBase()
    :DdiMediaBase()
{
    m_codechalSettings = CodechalSetting::CreateCodechalSetting();
}

VAStatus DdiEncodeBase::BeginPicture(
    VADriverContextP    ctx,
    VAContextID         context,
    VASurfaceID         renderTarget)
{
    DDI_FUNCTION_ENTER();

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

    PDDI_MEDIA_CONTEXT mediaCtx = DdiMedia_GetMediaContext(ctx);
    DDI_CHK_NULL(mediaCtx, "Null mediaCtx", VA_STATUS_ERROR_INVALID_CONTEXT);

    DDI_MEDIA_SURFACE *curRT = (DDI_MEDIA_SURFACE *)DdiMedia_GetSurfaceFromVASurfaceID(mediaCtx, renderTarget);
    DDI_CHK_NULL(curRT, "Null curRT", VA_STATUS_ERROR_INVALID_SURFACE);

    DDI_CODEC_RENDER_TARGET_TABLE *rtTbl = &(m_encodeCtx->RTtbl);
    // raw input frame
    rtTbl->pCurrentRT = curRT;
    if (m_encodeCtx->codecFunction == CODECHAL_FUNCTION_FEI_PRE_ENC)
    {
        DDI_CHK_RET(RegisterRTSurfaces(rtTbl, curRT),"RegisterRTSurfaces failed!");
    }
    // reset some the parameters in picture level
    ResetAtFrameLevel();

    DDI_FUNCTION_EXIT(VA_STATUS_SUCCESS);
    return VA_STATUS_SUCCESS;
}

VAStatus DdiEncodeBase::EndPicture(
    VADriverContextP    ctx,
    VAContextID         context)
{
    DDI_FUNCTION_ENTER();

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

    PDDI_MEDIA_CONTEXT mediaCtx = DdiMedia_GetMediaContext(ctx);
    DDI_CHK_NULL(mediaCtx, "Null mediaCtx", VA_STATUS_ERROR_INVALID_CONTEXT);

    VAStatus status = EncodeInCodecHal(m_encodeCtx->dwNumSlices);
    ClearPicParams();
    if (VA_STATUS_SUCCESS != status)
    {
        DDI_ASSERTMESSAGE("DDI:DdiEncode_EncodeInCodecHal return failure.");
        return VA_STATUS_ERROR_ENCODING_ERROR;
    }

    DDI_CODEC_RENDER_TARGET_TABLE *rtTbl = &(m_encodeCtx->RTtbl);
    rtTbl->pCurrentRT                    = nullptr;
    m_encodeCtx->bNewSeq                 = false;

    DDI_CODEC_COM_BUFFER_MGR *bufMgr = &(m_encodeCtx->BufMgr);
    bufMgr->dwNumSliceData           = 0;
    bufMgr->dwEncodeNumSliceControl  = 0;

    DDI_FUNCTION_EXIT(VA_STATUS_SUCCESS);
    return VA_STATUS_SUCCESS;
}

VAStatus DdiEncodeBase::AddToStatusReportQueue(void *codedBuf)
{
    DDI_CHK_NULL(m_encodeCtx->pCpDdiInterface, "Null m_encodeCtx->pCpDdiInterface", VA_STATUS_ERROR_INVALID_CONTEXT);
    DDI_CHK_NULL(codedBuf, "Null codedBuf", VA_STATUS_ERROR_INVALID_BUFFER);

    int32_t idx                                       = m_encodeCtx->statusReportBuf.ulHeadPosition;
    m_encodeCtx->statusReportBuf.infos[idx].pCodedBuf = codedBuf;
    m_encodeCtx->statusReportBuf.infos[idx].uiSize    = 0;
    m_encodeCtx->statusReportBuf.infos[idx].uiStatus  = 0;
    MOS_STATUS status = m_encodeCtx->pCpDdiInterface->StoreCounterToStatusReport(&m_encodeCtx->statusReportBuf.infos[idx]);
    if (status != MOS_STATUS_SUCCESS)
    {
        return VA_STATUS_ERROR_INVALID_BUFFER;
    }
    m_encodeCtx->statusReportBuf.ulHeadPosition = (m_encodeCtx->statusReportBuf.ulHeadPosition + 1) % DDI_ENCODE_MAX_STATUS_REPORT_BUFFER;

    return VA_STATUS_SUCCESS;

}

VAStatus DdiEncodeBase::InitCompBuffer()
{
    DDI_CHK_NULL(m_encodeCtx, "Null m_encodeCtx.", VA_STATUS_ERROR_INVALID_CONTEXT);
    DDI_CHK_NULL(m_encodeCtx->pCpDdiInterface, "Null m_encodeCtx->pCpDdiInterface.", VA_STATUS_ERROR_INVALID_CONTEXT);

    DDI_CODEC_COM_BUFFER_MGR *bufMgr = &(m_encodeCtx->BufMgr);
    PDDI_MEDIA_CONTEXT      mediaCtx = m_encodeCtx->pMediaCtx;

    bufMgr->dwEncodeNumSliceControl = 0;

    // create status reporting structure
    bufMgr->pCodedBufferSegment = (VACodedBufferSegment *)MOS_AllocAndZeroMemory(sizeof(VACodedBufferSegment));
    if (bufMgr->pCodedBufferSegment == nullptr)
    {
        return VA_STATUS_ERROR_ALLOCATION_FAILED;
    }
    bufMgr->pCodedBufferSegment->next = nullptr;

    DDI_CHK_RET(m_encodeCtx->pCpDdiInterface->InitHdcp2Buffer(bufMgr), "fail to init hdcp2 buffer!");

    return VA_STATUS_SUCCESS;
}

void DdiEncodeBase::FreeCompBuffer()
{
    DDI_CHK_NULL(m_encodeCtx, "Null m_encodeCtx.", );
    DDI_CHK_NULL(m_encodeCtx->pCpDdiInterface, "Null m_encodeCtx->pCpDdiInterface.", );
    DDI_CHK_NULL(m_encodeCtx->pMediaCtx, "Null m_encodeCtx->pMediaCtx.", );

    PDDI_MEDIA_CONTEXT mediaCtx = m_encodeCtx->pMediaCtx;
    DDI_CODEC_COM_BUFFER_MGR *bufMgr   = &(m_encodeCtx->BufMgr);
    // free  encode bitstream buffer object
    MOS_FreeMemory(bufMgr->pSliceData);
    bufMgr->pSliceData = nullptr;

    m_encodeCtx->pCpDdiInterface->FreeHdcp2Buffer(bufMgr);

    // free status report struct
    MOS_FreeMemory(bufMgr->pCodedBufferSegment);
    bufMgr->pCodedBufferSegment = nullptr;
}

VAStatus DdiEncodeBase::StatusReport(
    DDI_MEDIA_BUFFER    *mediaBuf,
    void                **buf)
{
    DDI_CHK_NULL(m_encodeCtx->pCpDdiInterface, "Null m_encodeCtx->pCpDdiInterface", VA_STATUS_ERROR_INVALID_CONTEXT);
    DDI_CHK_NULL(mediaBuf, "Null mediaBuf", VA_STATUS_ERROR_INVALID_CONTEXT);
    DDI_CHK_NULL(buf, "Null buf", VA_STATUS_ERROR_INVALID_CONTEXT);

    m_encodeCtx->BufMgr.pCodedBufferSegment->status    = 0;

    //when this function is called, there must be a frame is ready, will wait until get the right information.
    uint32_t size         = 0;
    int32_t  index        = 0;
    uint32_t status       = 0;
    uint32_t timeOutCount = 0;
    VAStatus eStatus      = VA_STATUS_SUCCESS;

    // Get encoded frame information from status buffer queue.
    while (VA_STATUS_SUCCESS == (eStatus = GetSizeFromStatusReportBuffer(mediaBuf, &size, &status, &index)))
    {
        if ((index >= 0) && ((size != 0) || (status & VA_CODED_BUF_STATUS_BAD_BITSTREAM))) //Get the matched encoded buffer information
        {
            // the first segment in the single-link list: pointer for the coded bitstream and the size
            m_encodeCtx->BufMgr.pCodedBufferSegment->buf    = DdiMediaUtil_LockBuffer(mediaBuf, MOS_LOCKFLAG_READONLY);
            m_encodeCtx->BufMgr.pCodedBufferSegment->size   = size;
            m_encodeCtx->BufMgr.pCodedBufferSegment->status = status;

            if (status & VA_CODED_BUF_STATUS_BAD_BITSTREAM)
            {
                return VA_STATUS_ERROR_ENCODING_ERROR;
            }
            break;
        }

        mos_bo_wait_rendering(mediaBuf->bo);

        EncodeStatusReport *encodeStatusReport = (EncodeStatusReport*)m_encodeCtx->pEncodeStatusReport;
        encodeStatusReport->bSequential = true;  //Query the encoded frame status in sequential.

        uint16_t numStatus = 1;
        MOS_STATUS mosStatus = MOS_STATUS_SUCCESS;
        mosStatus = m_encodeCtx->pCodecHal->GetStatusReport(encodeStatusReport, numStatus);
        if (MOS_STATUS_NOT_ENOUGH_BUFFER == mosStatus)
        {
            return VA_STATUS_ERROR_NOT_ENOUGH_BUFFER;
        } else if (MOS_STATUS_SUCCESS != mosStatus)
        {
            return VA_STATUS_ERROR_ENCODING_ERROR;
        }

        if (CODECHAL_STATUS_SUCCESSFUL == encodeStatusReport[0].CodecStatus)
        {
            // Only AverageQP is reported at this time. Populate other bits with relevant informaiton later;
            status = (encodeStatusReport[0].AverageQp & VA_CODED_BUF_STATUS_PICTURE_AVE_QP_MASK);
            if(m_encodeCtx->wModeType == CODECHAL_ENCODE_MODE_AVC)
            {
                CodecEncodeAvcFeiPicParams *feiPicParams = (CodecEncodeAvcFeiPicParams*) m_encodeCtx->pFeiPicParams;
                if ((feiPicParams != NULL) && (feiPicParams->dwMaxFrameSize != 0))
                {
                    // The reported the pass number should be multi-pass PAK caused by the MaxFrameSize.
                    // if the suggestedQpYDelta is 0, it means that MaxFrameSize doesn't trigger multi-pass PAK.
                    // The MaxMbSize triggers multi-pass PAK, the cases should be ignored when reporting the PAK pass.
                    if ((encodeStatusReport[0].SuggestedQpYDelta == 0) && (encodeStatusReport[0].NumberPasses != 1))
                    {
                        encodeStatusReport[0].NumberPasses = 1;
                    }
                }
            }
            status = status | ((encodeStatusReport[0].NumberPasses) & 0xf)<<24;
            // fill hdcp related buffer
            DDI_CHK_RET(m_encodeCtx->pCpDdiInterface->StatusReportForHdcp2Buffer(&m_encodeCtx->BufMgr, encodeStatusReport), "fail to get hdcp2 status report!");
            if (UpdateStatusReportBuffer(encodeStatusReport[0].bitstreamSize, status) != VA_STATUS_SUCCESS)
            {
                m_encodeCtx->BufMgr.pCodedBufferSegment->buf  = DdiMediaUtil_LockBuffer(mediaBuf, MOS_LOCKFLAG_READONLY);
                m_encodeCtx->BufMgr.pCodedBufferSegment->size = 0;
                m_encodeCtx->BufMgr.pCodedBufferSegment->status |= VA_CODED_BUF_STATUS_BAD_BITSTREAM;
                m_encodeCtx->statusReportBuf.ulUpdatePosition = (m_encodeCtx->statusReportBuf.ulUpdatePosition + 1) % DDI_ENCODE_MAX_STATUS_REPORT_BUFFER;
                return VA_STATUS_ERROR_ENCODING_ERROR;
            }

            // Report extra status for completed coded buffer
            eStatus = ReportExtraStatus(encodeStatusReport, m_encodeCtx->BufMgr.pCodedBufferSegment);
            if (VA_STATUS_SUCCESS != eStatus)
            {
                break;
            }

            //Add encoded frame information into status buffer queue.
            continue;
        }
        else if (CODECHAL_STATUS_INCOMPLETE == encodeStatusReport[0].CodecStatus)
        {
            bool inlineEncodeStatusUpdate;
            CodechalEncoderState *encoder = dynamic_cast<CodechalEncoderState *>(m_encodeCtx->pCodecHal);
            inlineEncodeStatusUpdate = encoder == nullptr ? false : encoder->m_inlineEncodeStatusUpdate;

            if (inlineEncodeStatusUpdate)
            {
                m_encodeCtx->BufMgr.pCodedBufferSegment->buf  = DdiMediaUtil_LockBuffer(mediaBuf, MOS_LOCKFLAG_READONLY);
                m_encodeCtx->BufMgr.pCodedBufferSegment->size = 0;
                m_encodeCtx->BufMgr.pCodedBufferSegment->status |= VA_CODED_BUF_STATUS_BAD_BITSTREAM;
                UpdateStatusReportBuffer(encodeStatusReport[0].bitstreamSize, m_encodeCtx->BufMgr.pCodedBufferSegment->status);
                DDI_ASSERTMESSAGE("Something unexpected happened in HW, return error to application");
                break;
            }
            // Wait until encode PAK complete, sometimes we application detect encoded buffer object is Idle, may Enc done, but Pak not.
            uint32_t maxTimeOut                               = 100000;  //set max sleep times to 100000 = 1s, other wise return error.
            if (timeOutCount < maxTimeOut)
            {
                //sleep 10 us to wait encode complete, it won't impact the performance.
                uint32_t sleepTime                            = 10;      //sleep 10 us when encode is not complete.
                usleep(sleepTime);
                timeOutCount++;
                continue;
            }
            else
            {
                //if HW didn't response in 1s, assume there is an error in encoding process, return error to App.
                m_encodeCtx->BufMgr.pCodedBufferSegment->buf  = DdiMediaUtil_LockBuffer(mediaBuf, MOS_LOCKFLAG_READONLY);
                m_encodeCtx->BufMgr.pCodedBufferSegment->size = 0;
                m_encodeCtx->BufMgr.pCodedBufferSegment->status |= VA_CODED_BUF_STATUS_BAD_BITSTREAM;
                UpdateStatusReportBuffer(encodeStatusReport[0].bitstreamSize, m_encodeCtx->BufMgr.pCodedBufferSegment->status);
                DDI_ASSERTMESSAGE("Something unexpected happened in HW, return error to application");
                return VA_STATUS_ERROR_ENCODING_ERROR;
            }
        }
        else if (CODECHAL_STATUS_ERROR == encodeStatusReport[0].CodecStatus)
        {
            DDI_ASSERTMESSAGE("Encoding failure due to HW issue");
            m_encodeCtx->BufMgr.pCodedBufferSegment->buf  = DdiMediaUtil_LockBuffer(mediaBuf, MOS_LOCKFLAG_READONLY);
            m_encodeCtx->BufMgr.pCodedBufferSegment->size = 0;
            m_encodeCtx->BufMgr.pCodedBufferSegment->status |= VA_CODED_BUF_STATUS_BAD_BITSTREAM;
            UpdateStatusReportBuffer(encodeStatusReport[0].bitstreamSize, m_encodeCtx->BufMgr.pCodedBufferSegment->status);
            return VA_STATUS_ERROR_ENCODING_ERROR;
        }
        else
        {
            break;
        }
    }

    if (eStatus != VA_STATUS_SUCCESS)
    {
        return VA_STATUS_ERROR_OPERATION_FAILED;
    }

    *buf = m_encodeCtx->BufMgr.pCodedBufferSegment;
    return VA_STATUS_SUCCESS;
}

VAStatus DdiEncodeBase::EncStatusReport(
    DDI_MEDIA_BUFFER    *mediaBuf,
    void                **buf)
{
    DDI_CHK_NULL(mediaBuf, "Null mediaBuf", VA_STATUS_ERROR_INVALID_CONTEXT);
    DDI_CHK_NULL(buf, "Null buf", VA_STATUS_ERROR_INVALID_CONTEXT);

    EncodeStatusReport* encodeStatusReport = (EncodeStatusReport*)m_encodeCtx->pEncodeStatusReport;
    uint16_t numStatus    = 1;
    uint32_t maxTimeOut   = 500000;  //set max sleep times to 500000 = 5s, other wise return error.
    uint32_t sleepTime    = 10;  //sleep 10 us when encode is not complete.
    uint32_t timeOutCount = 0;

    //when this function is called, there must be a frame is ready, will wait until get the right information.
    while (1)
    {
        encodeStatusReport->bSequential = true;  //Query the encoded frame status in sequential.
        m_encodeCtx->pCodecHal->GetStatusReport(encodeStatusReport, numStatus);

        if (CODECHAL_STATUS_SUCCESSFUL == encodeStatusReport[0].CodecStatus)
        {
            // Only AverageQP is reported at this time. Populate other bits with relevant informaiton later;
            uint32_t status = (encodeStatusReport[0].AverageQp & VA_CODED_BUF_STATUS_PICTURE_AVE_QP_MASK);
            status = status | ((encodeStatusReport[0].NumberPasses & 0xf)<<24);
            if (UpdateEncStatusReportBuffer(status) != VA_STATUS_SUCCESS)
            {
                return VA_STATUS_ERROR_INVALID_BUFFER;
            }
            break;
        }
        else if (CODECHAL_STATUS_INCOMPLETE == encodeStatusReport[0].CodecStatus)
        {
            // Wait until encode PAK complete, sometimes we application detect encoded buffer object is Idle, may Enc done, but Pak not.
            if (timeOutCount < maxTimeOut)
            {
                //sleep 10 us to wait encode complete, it won't impact the performance.
                usleep(sleepTime);
                timeOutCount++;
                continue;
            }
            else
            {
                //if HW didn't response in 5s, assume there is an error in encoding process, return error to App.
                return VA_STATUS_ERROR_ENCODING_ERROR;
            }
        }
        else
        {
            // App will call twice StatusReport() for 1 frame, for the second call, just return.
            break;
        }
    }

    if (mediaBuf->bo)
    {
        *buf = DdiMediaUtil_LockBuffer(mediaBuf, MOS_LOCKFLAG_READONLY);
    }
    return VA_STATUS_SUCCESS;
}

VAStatus DdiEncodeBase::PreEncStatusReport(
    DDI_MEDIA_BUFFER    *mediaBuf,
    void                **buf)
{
    DDI_CHK_NULL(mediaBuf, "Null mediaBuf", VA_STATUS_ERROR_INVALID_CONTEXT);
    DDI_CHK_NULL(buf, "Null buf", VA_STATUS_ERROR_INVALID_CONTEXT);

    EncodeStatusReport* encodeStatusReport = (EncodeStatusReport*)m_encodeCtx->pEncodeStatusReport;
    uint16_t numStatus    = 1;
    uint32_t maxTimeOut   = 500000;  //set max sleep times to 500000 = 5s, other wise return error.
    uint32_t sleepTime    = 10;  //sleep 10 us when encode is not complete.
    uint32_t timeOutCount = 0;

    //when this function is called, there must be a frame is ready, will wait until get the right information.
    while (1)
    {
        encodeStatusReport->bSequential = true;  //Query the encoded frame status in sequential.
        m_encodeCtx->pCodecHal->GetStatusReport(encodeStatusReport, numStatus);

        if (CODECHAL_STATUS_SUCCESSFUL == encodeStatusReport[0].CodecStatus)
        {
            // Only AverageQP is reported at this time. Populate other bits with relevant informaiton later;
            uint32_t status = (encodeStatusReport[0].AverageQp & VA_CODED_BUF_STATUS_PICTURE_AVE_QP_MASK);
            status = status | ((encodeStatusReport[0].NumberPasses & 0xf)<<24);
            if (UpdatePreEncStatusReportBuffer(status) != VA_STATUS_SUCCESS)
            {
                return VA_STATUS_ERROR_INVALID_BUFFER;
            }
            break;
        }
        else if (CODECHAL_STATUS_INCOMPLETE == encodeStatusReport[0].CodecStatus)
        {
            // Wait until encode PAK complete, sometimes we application detect encoded buffer object is Idle, may Enc done, but Pak not.
            if (timeOutCount < maxTimeOut)
            {
                //sleep 10 us to wait encode complete, it won't impact the performance.
                usleep(sleepTime);
                timeOutCount++;
                continue;
            }
            else
            {
                //if HW didn't response in 5s, assume there is an error in encoding process, return error to App.
                return VA_STATUS_ERROR_ENCODING_ERROR;
            }
        }
        else
        {
            // App will call twice PreEncStatusReport() for 1 frame, for the second call, just return.
            break;
        }
    }

    if (mediaBuf->bo)
    {
        *buf = DdiMediaUtil_LockBuffer(mediaBuf, MOS_LOCKFLAG_READONLY);
    }
    return VA_STATUS_SUCCESS;
}

VAStatus DdiEncodeBase::RemoveFromStatusReportQueue(DDI_MEDIA_BUFFER *buf)
{
    VAStatus eStatus = VA_STATUS_SUCCESS;

    DDI_CHK_NULL(m_encodeCtx, "Null m_encodeCtx", VA_STATUS_ERROR_INVALID_CONTEXT);
    DDI_CHK_NULL(buf, "Null buf", VA_STATUS_ERROR_INVALID_CONTEXT);

    int32_t    index  = 0;
    uint32_t   size   = 0;
    uint32_t   status = 0;

    eStatus = GetSizeFromStatusReportBuffer(buf, &size, &status, &index);
    if (VA_STATUS_SUCCESS != eStatus)
    {
        return eStatus;
    }

    if (index >= 0)
    {
        m_encodeCtx->statusReportBuf.infos[index].pCodedBuf = nullptr;
        m_encodeCtx->statusReportBuf.infos[index].uiSize    = 0;
    }
    return eStatus;
}

VAStatus DdiEncodeBase::RemoveFromEncStatusReportQueue(
    DDI_MEDIA_BUFFER                  *buf,
    DDI_ENCODE_FEI_ENC_BUFFER_TYPE    typeIdx)
{
    VAStatus eStatus = VA_STATUS_SUCCESS;

    DDI_CHK_NULL(m_encodeCtx, "Null m_encodeCtx", VA_STATUS_ERROR_INVALID_CONTEXT);
    DDI_CHK_NULL(buf, "Null buf", VA_STATUS_ERROR_INVALID_CONTEXT);

    if ((typeIdx < 0) || (typeIdx >= FEI_ENC_BUFFER_TYPE_MAX))
    {
        DDI_ASSERTMESSAGE("ENC RemoveFromEncStatusReportBuffer, gets invalid buffer type index! .");
        return VA_STATUS_ERROR_INVALID_CONTEXT;
    }

    int32_t  index  = 0;
    uint32_t status = 0;

    eStatus = GetIndexFromEncStatusReportBuffer(buf, typeIdx, &status, &index);
    if (VA_STATUS_SUCCESS != eStatus)
    {
        return eStatus;
    }

    if (index >= 0)
    {
        m_encodeCtx->statusReportBuf.encInfos[index].pEncBuf[typeIdx] = nullptr;
    }

    return eStatus;
}

VAStatus DdiEncodeBase::RemoveFromPreEncStatusReportQueue(
    DDI_MEDIA_BUFFER                  *buf,
    DDI_ENCODE_PRE_ENC_BUFFER_TYPE    typeIdx)
{
    VAStatus eStatus = VA_STATUS_SUCCESS;

    DDI_CHK_NULL(m_encodeCtx, "Null m_encodeCtx", VA_STATUS_ERROR_INVALID_CONTEXT);
    DDI_CHK_NULL(buf, "Null buf", VA_STATUS_ERROR_INVALID_CONTEXT);

    if ((typeIdx < 0) || (typeIdx >= PRE_ENC_BUFFER_TYPE_MAX))
    {
        DDI_ASSERTMESSAGE("PRE ENC RemoveFromEncStatusReportBuffer, gets invalid buffer type index! .");
        return VA_STATUS_ERROR_INVALID_CONTEXT;
    }

    int32_t  index  = 0;
    uint32_t status = 0;

    eStatus = GetIndexFromPreEncStatusReportBuffer(buf, typeIdx, &status, &index);
    if (VA_STATUS_SUCCESS != eStatus)
    {
        return eStatus;
    }

    bool bufferIsUpdated = m_encodeCtx->statusReportBuf.ulUpdatePosition < m_encodeCtx->statusReportBuf.ulHeadPosition ?
                            (index < m_encodeCtx->statusReportBuf.ulUpdatePosition)
                            : (m_encodeCtx->statusReportBuf.ulUpdatePosition == m_encodeCtx->statusReportBuf.ulHeadPosition ?
                                true
                                : ((index < m_encodeCtx->statusReportBuf.ulUpdatePosition)
                                  &&(index > m_encodeCtx->statusReportBuf.ulHeadPosition)));

    // Remove updated status report buffer
    if (index >= 0 && bufferIsUpdated)
    {
        m_encodeCtx->statusReportBuf.preencInfos[index].pPreEncBuf[typeIdx] = nullptr;
        m_encodeCtx->statusReportBuf.preencInfos[index].uiBuffers = 0;
    }

    return eStatus;
}

VAStatus DdiEncodeBase::GetSizeFromStatusReportBuffer(
    DDI_MEDIA_BUFFER    *buf,
    uint32_t            *size,
    uint32_t            *status,
    int32_t             *index)
{
    VAStatus eStatus = VA_STATUS_SUCCESS;

    DDI_CHK_NULL(m_encodeCtx, "Null m_encodeCtx", VA_STATUS_ERROR_INVALID_CONTEXT);
    DDI_CHK_NULL(m_encodeCtx->pCpDdiInterface, "Null m_encodeCtx->pCpDdiInterface", VA_STATUS_ERROR_INVALID_CONTEXT);
    DDI_CHK_NULL(buf, "Null buf", VA_STATUS_ERROR_INVALID_CONTEXT);
    DDI_CHK_NULL(size, "Null size", VA_STATUS_ERROR_INVALID_CONTEXT);
    DDI_CHK_NULL(status, "Null status", VA_STATUS_ERROR_INVALID_CONTEXT);
    DDI_CHK_NULL(index, "Null index", VA_STATUS_ERROR_INVALID_CONTEXT);

    int32_t i = 0;
    for (i = 0; i < DDI_ENCODE_MAX_STATUS_REPORT_BUFFER; i++)
    {
        // check if the buffer has already been added to status report queue
        if (m_encodeCtx->statusReportBuf.infos[i].pCodedBuf == (void *)buf->bo)
        {
            *size   = m_encodeCtx->statusReportBuf.infos[i].uiSize;
            *status = m_encodeCtx->statusReportBuf.infos[i].uiStatus;

            break;
        }
    }

    if (i >= DDI_ENCODE_MAX_STATUS_REPORT_BUFFER)
    {
        // no matching buffer has been found
        *size   = 0;
        i       = DDI_CODEC_INVALID_BUFFER_INDEX;
        eStatus = MOS_STATUS_INVALID_HANDLE;
    }

    *index = i;

    return eStatus;
}

VAStatus DdiEncodeBase::GetIndexFromEncStatusReportBuffer(
    DDI_MEDIA_BUFFER                  *buf,
    DDI_ENCODE_FEI_ENC_BUFFER_TYPE    typeIdx,
    uint32_t                          *status,
    int32_t                           *index)
{
    VAStatus eStatus = VA_STATUS_SUCCESS;

    DDI_CHK_NULL(m_encodeCtx, "Null m_encodeCtx", VA_STATUS_ERROR_INVALID_CONTEXT);
    DDI_CHK_NULL(buf, "Null buf", VA_STATUS_ERROR_INVALID_CONTEXT);
    DDI_CHK_NULL(status, "Null status", VA_STATUS_ERROR_INVALID_CONTEXT);
    DDI_CHK_NULL(index, "Null index", VA_STATUS_ERROR_INVALID_CONTEXT);

    if ((typeIdx < 0) || (typeIdx >= FEI_ENC_BUFFER_TYPE_MAX))
    {
        DDI_ASSERTMESSAGE("ENC GetIndexFromEncStatusReportBuffer, gets invalid buffer type index! .");
        return VA_STATUS_ERROR_INVALID_CONTEXT;
    }

    int32_t i = 0;
    for (i = 0; i < DDI_ENCODE_MAX_STATUS_REPORT_BUFFER; i++)
    {
        // check if the buffer has already been added to status report queue
        if (m_encodeCtx->statusReportBuf.encInfos[i].pEncBuf[typeIdx] == (void *)buf->bo)
        {
            *status = m_encodeCtx->statusReportBuf.encInfos[i].uiStatus;
            break;
        }
    }

    if (i >= DDI_ENCODE_MAX_STATUS_REPORT_BUFFER)
    {
        // no matching buffer has been found
        i       = DDI_CODEC_INVALID_BUFFER_INDEX;
        eStatus = VA_STATUS_ERROR_INVALID_CONTEXT;
    }

    *index = i;

    return eStatus;
}

VAStatus DdiEncodeBase::GetIndexFromPreEncStatusReportBuffer(
    DDI_MEDIA_BUFFER                  *buf,
    DDI_ENCODE_PRE_ENC_BUFFER_TYPE    typeIdx,
    uint32_t                          *status,
    int32_t                           *index)
{
    VAStatus eStatus = VA_STATUS_SUCCESS;

    DDI_CHK_NULL(m_encodeCtx, "Null m_encodeCtx", VA_STATUS_ERROR_INVALID_CONTEXT);
    DDI_CHK_NULL(buf, "Null buf", VA_STATUS_ERROR_INVALID_CONTEXT);
    DDI_CHK_NULL(status, "Null status", VA_STATUS_ERROR_INVALID_CONTEXT);
    DDI_CHK_NULL(index, "Null index", VA_STATUS_ERROR_INVALID_CONTEXT);

    if ((typeIdx < 0) || (typeIdx >= PRE_ENC_BUFFER_TYPE_MAX))
    {
        DDI_ASSERTMESSAGE("PRE ENC GetIndexFromPreEncStatusReportBuffer, gets invalid buffer type index! .");
        return VA_STATUS_ERROR_INVALID_CONTEXT;
    }

    int32_t i = 0;
    for (i = 0; i < DDI_ENCODE_MAX_STATUS_REPORT_BUFFER; i++)
    {
        // check if the buffer has already been added to status report queue
        if (m_encodeCtx->statusReportBuf.preencInfos[i].pPreEncBuf[typeIdx] == (void *)buf->bo)
        {
            *status = m_encodeCtx->statusReportBuf.preencInfos[i].uiStatus;
            break;
        }
    }

    if (i >= DDI_ENCODE_MAX_STATUS_REPORT_BUFFER)
    {
        // no matching buffer has been found
        i       = DDI_CODEC_INVALID_BUFFER_INDEX;
        eStatus = VA_STATUS_ERROR_INVALID_CONTEXT;
    }

    *index = i;

    return eStatus;
}

bool DdiEncodeBase::CodedBufferExistInStatusReport(DDI_MEDIA_BUFFER *buf)
{
    if (nullptr == m_encodeCtx || nullptr == buf)
    {
        return false;
    }

    for (int32_t i = 0; i < DDI_ENCODE_MAX_STATUS_REPORT_BUFFER; i++)
    {
        if (m_encodeCtx->statusReportBuf.infos[i].pCodedBuf == (void *)buf->bo)
        {
            return true;
        }
    }
    return false;
}

bool DdiEncodeBase::EncBufferExistInStatusReport(
    DDI_MEDIA_BUFFER                  *buf,
    DDI_ENCODE_FEI_ENC_BUFFER_TYPE    typeIdx)
{
    if (nullptr == m_encodeCtx || nullptr == buf)
    {
        return false;
    }

    if ((typeIdx < 0) || (typeIdx >= FEI_ENC_BUFFER_TYPE_MAX))
    {
        DDI_ASSERTMESSAGE("ENC EncBufferExistInStatusReport, gets invalid buffer type index! .");
        return false;
    }

    for (int32_t i = 0; i < DDI_ENCODE_MAX_STATUS_REPORT_BUFFER; i++)
    {
        if (m_encodeCtx->statusReportBuf.encInfos[i].pEncBuf[typeIdx] == (void *)buf->bo)
        {
            return true;
        }
    }
    return false;
}

bool DdiEncodeBase::PreEncBufferExistInStatusReport(
    DDI_MEDIA_BUFFER                  *buf,
    DDI_ENCODE_PRE_ENC_BUFFER_TYPE    typeIdx)
{
    if (nullptr == m_encodeCtx || nullptr == buf)
    {
        return false;
    }

    if ((typeIdx < 0) || (typeIdx >= PRE_ENC_BUFFER_TYPE_MAX))
    {
        DDI_ASSERTMESSAGE("ENC EncBufferExistInStatusReport, gets invalid buffer type index! .");
        return false;
    }

    for (int32_t i = 0; i < DDI_ENCODE_MAX_STATUS_REPORT_BUFFER; i++)
    {
        if (m_encodeCtx->statusReportBuf.preencInfos[i].pPreEncBuf[typeIdx] == (void *)buf->bo)
        {
            return true;
        }
    }
    return false;
}

uint8_t DdiEncodeBase::VARC2HalRC(uint32_t vaRC)
{
    if ((VA_RC_VBR == vaRC) || ((VA_RC_VBR | VA_RC_MB) == vaRC))
    {
        return (uint8_t)RATECONTROL_VBR;
    }
    else if (VA_RC_CQP == vaRC)
    {
        return (uint8_t)RATECONTROL_CQP;
    }
    else if (VA_RC_ICQ == vaRC)
    {
        return (uint8_t)RATECONTROL_ICQ;
    }
    else if (VA_RC_VCM == vaRC)
    {
        return (uint8_t)RATECONTROL_VCM;
    }
    else if (VA_RC_QVBR == vaRC)
    {
        return (uint8_t)RATECONTROL_QVBR;
    }
    else if (VA_RC_AVBR == vaRC)
    {
        return (uint8_t)RATECONTROL_AVBR;
    }
    else  // VA_RC_CBR or VA_RC_CBR|VA_RC_MB
    {
        return (uint8_t)RATECONTROL_CBR;
    }
}

VAStatus DdiEncodeBase::UpdateStatusReportBuffer(
    uint32_t    size,
    uint32_t    status)
{
    VAStatus eStatus = VA_STATUS_SUCCESS;

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

    int32_t i = m_encodeCtx->statusReportBuf.ulUpdatePosition;
    if (m_encodeCtx->statusReportBuf.infos[i].pCodedBuf != nullptr &&
        m_encodeCtx->statusReportBuf.infos[i].uiSize == 0)
    {
        m_encodeCtx->statusReportBuf.infos[i].uiSize   = size;
        m_encodeCtx->statusReportBuf.infos[i].uiStatus = status;
        m_encodeCtx->statusReportBuf.ulUpdatePosition  = (m_encodeCtx->statusReportBuf.ulUpdatePosition + 1) % DDI_ENCODE_MAX_STATUS_REPORT_BUFFER;
    }
    else
    {
        DDI_ASSERTMESSAGE("DDI: Buffer is not enough in UpdateStatusReportBuffer! .");
        eStatus = VA_STATUS_ERROR_OPERATION_FAILED;
    }

    return eStatus;
}

VAStatus DdiEncodeBase::UpdateEncStatusReportBuffer(uint32_t status)
{
    VAStatus  eStatus                         = VA_STATUS_SUCCESS;
    bool      distortionEnable               = false;
    bool      mbCodeMvOrCTBCmdCuRecordEnable = false;

    DDI_CHK_NULL(m_encodeCtx, "Null m_encodeCtx", VA_STATUS_ERROR_INVALID_CONTEXT);
    DDI_CHK_NULL(m_encodeCtx->pFeiPicParams, "Null m_encodeCtx->pFeiPicParams", VA_STATUS_ERROR_INVALID_CONTEXT);

    if(m_encodeCtx->wModeType == CODECHAL_ENCODE_MODE_AVC)
    {
        distortionEnable               = ((CodecEncodeAvcFeiPicParams *)(m_encodeCtx->pFeiPicParams))->DistortionEnable;
        mbCodeMvOrCTBCmdCuRecordEnable = ((CodecEncodeAvcFeiPicParams *)(m_encodeCtx->pFeiPicParams))->MbCodeMvEnable;
    }
    else if(m_encodeCtx->wModeType == CODECHAL_ENCODE_MODE_HEVC)
    {
        distortionEnable               = ((CodecEncodeHevcFeiPicParams *)(m_encodeCtx->pFeiPicParams))->bDistortionEnable;
        mbCodeMvOrCTBCmdCuRecordEnable = ((CodecEncodeHevcFeiPicParams *)(m_encodeCtx->pFeiPicParams))->bCTBCmdCuRecordEnable;
    }

    int32_t i = m_encodeCtx->statusReportBuf.ulUpdatePosition;
    if (((m_encodeCtx->statusReportBuf.encInfos[i].pEncBuf[0] != nullptr) && mbCodeMvOrCTBCmdCuRecordEnable) ||
        ((m_encodeCtx->statusReportBuf.encInfos[i].pEncBuf[1] != nullptr) && mbCodeMvOrCTBCmdCuRecordEnable) ||
        ((m_encodeCtx->statusReportBuf.encInfos[i].pEncBuf[2] != nullptr) && distortionEnable))
    {
        m_encodeCtx->statusReportBuf.encInfos[i].uiStatus = status;
        m_encodeCtx->statusReportBuf.ulUpdatePosition     = (m_encodeCtx->statusReportBuf.ulUpdatePosition + 1) % DDI_ENCODE_MAX_STATUS_REPORT_BUFFER;
    }
    else
    {
        DDI_ASSERTMESSAGE("Buffer is not enough in UpdateEncStatusReportBuffer! .");
        eStatus = VA_STATUS_ERROR_OPERATION_FAILED;
    }

    if ((i + 1) == DDI_ENCODE_MAX_STATUS_REPORT_BUFFER)
    {
        for (int32_t cnt = 0; cnt < DDI_ENCODE_MAX_STATUS_REPORT_BUFFER; cnt++)
        {
            m_encodeCtx->statusReportBuf.encInfos[cnt].uiBuffers = 0;
        }
    }

    return eStatus;
}

VAStatus DdiEncodeBase::UpdatePreEncStatusReportBuffer(uint32_t status)
{
    bool                    toUpdateStatistics;
    VAStatus                eStatus = VA_STATUS_SUCCESS;
    FeiPreEncParams         *preEncParams;

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

    preEncParams = (FeiPreEncParams*)(m_encodeCtx->pPreEncParams);
    DDI_CHK_NULL(preEncParams, "Null preEncParams", VA_STATUS_ERROR_INVALID_CONTEXT);

    int32_t i = m_encodeCtx->statusReportBuf.ulUpdatePosition;
    toUpdateStatistics = (!preEncParams->bDisableStatisticsOutput) &&
                          ((!preEncParams->bInterlaced) ? (m_encodeCtx->statusReportBuf.preencInfos[i].pPreEncBuf[1] != nullptr)
                                                         : ((m_encodeCtx->statusReportBuf.preencInfos[i].pPreEncBuf[1] != nullptr) &&
                                                               (m_encodeCtx->statusReportBuf.preencInfos[i].pPreEncBuf[2] != nullptr)));
    if (((m_encodeCtx->statusReportBuf.preencInfos[i].pPreEncBuf[0] != nullptr) && (!preEncParams->bDisableMVOutput)) || toUpdateStatistics)
    {
        m_encodeCtx->statusReportBuf.preencInfos[i].uiStatus = status;
        m_encodeCtx->statusReportBuf.ulUpdatePosition        = (m_encodeCtx->statusReportBuf.ulUpdatePosition + 1) % DDI_ENCODE_MAX_STATUS_REPORT_BUFFER;
    }
    else
    {
        DDI_ASSERTMESSAGE("Buffer is not enough in UpdatePreEncStatusReportBuffer! .");
        eStatus = VA_STATUS_ERROR_OPERATION_FAILED;
    }

    if ((i + 1) == DDI_ENCODE_MAX_STATUS_REPORT_BUFFER)
    {
        for (int32_t cnt = 0; cnt < DDI_ENCODE_MAX_STATUS_REPORT_BUFFER; cnt++)
        {
            m_encodeCtx->statusReportBuf.preencInfos[cnt].uiBuffers = 0;
        }
    }

    return eStatus;
}

VAStatus DdiEncodeBase::CreateBuffer(
    VADriverContextP    ctx,
    VABufferType        type,
    uint32_t            size,
    uint32_t            elementsNum,
    void                *data,
    VABufferID          *bufId)
{
    VAStatus va = VA_STATUS_SUCCESS;

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

    // for VAEncSliceParameterBufferType buffer, VAEncQPBufferType buffer and 
    // VAEncMacroblockMapBufferType buffer, the number of elements can be greater than 1
    if ((type != VAEncSliceParameterBufferType) &&
        (type != VAEncQPBufferType) &&
        (type != VAEncMacroblockMapBufferType) &&
        (elementsNum > 1))
    {
        return VA_STATUS_ERROR_INVALID_PARAMETER;
    }

    if (0 == size)
    {
        return VA_STATUS_ERROR_INVALID_PARAMETER;
    }

    // for coded buffer, does not support to upload some data directly
    if ((VAEncCodedBufferType == type) && (nullptr != data))
    {
        DDI_ASSERTMESSAGE("DDI:can not initialize the coded buffer!");
        return VA_STATUS_ERROR_INVALID_PARAMETER;
    }

    // for FEI ENC output buffers
    if ((m_encodeCtx->codecFunction == CODECHAL_FUNCTION_FEI_ENC) && (nullptr != data) &&
        ((VAEncFEIMVBufferType == type) || (VAEncFEIMBCodeBufferType == type) || (VAEncFEIDistortionBufferType == type) ||  (VAEncFEICURecordBufferType == type)))
    {
        DDI_ASSERTMESSAGE("DDI:can not initialize the MVs, CURecord, MBcode and Distortion buffer for FEI ENC only!");
        return VA_STATUS_ERROR_INVALID_PARAMETER;
    }

    if ((m_encodeCtx->codecFunction == CODECHAL_FUNCTION_FEI_PRE_ENC) && (nullptr != data) &&
        ((VAStatsMVBufferType == type) || (VAStatsStatisticsBufferType == type) || (VAStatsStatisticsBottomFieldBufferType == type)))
    {
        DDI_ASSERTMESSAGE("DDI:can not initialize the MV and Statistics buffer!");
        return VA_STATUS_ERROR_INVALID_PARAMETER;
    }

    DDI_MEDIA_BUFFER *buf = (DDI_MEDIA_BUFFER *)MOS_AllocAndZeroMemory(sizeof(DDI_MEDIA_BUFFER));
    if (buf == nullptr)
    {
        return VA_STATUS_ERROR_ALLOCATION_FAILED;
    }

    PDDI_MEDIA_CONTEXT mediaCtx = DdiMedia_GetMediaContext(ctx);

    buf->pMediaCtx     = mediaCtx;
    buf->uiNumElements = elementsNum;
    buf->uiType        = type;
    buf->uiOffset      = 0;

    uint32_t bufSize = 0;
    uint32_t expectedSize = 0xffffffff;

    switch ((int32_t)type)
    {
    case VAProbabilityBufferType:
    case VAEncCodedBufferType:
    {
        buf->iSize  = size;
        buf->format = Media_Format_Buffer;
        va           = DdiMediaUtil_CreateBuffer(buf, mediaCtx->pDrmBufMgr);
        if (va != VA_STATUS_SUCCESS)
        {
            MOS_FreeMemory(buf);
            return VA_STATUS_ERROR_ALLOCATION_FAILED;
        }
        break;
    }
    case VAEncMacroblockMapBufferType:
    {
        if(m_cpuFormat)
        {
            // elementsNum should be 1, ignore here just for robustness
            bufSize = size;
            break;
        }

        buf->uiWidth = MOS_ALIGN_CEIL(size, 64);
        if (size != buf->uiWidth)
        {
            va = VA_STATUS_ERROR_INVALID_PARAMETER;
            CleanUpBufferandReturn(buf);
            return va;
        }
        bufSize            = size * elementsNum;
        buf->uiHeight      = elementsNum;
        buf->uiPitch       = buf->uiWidth;
        buf->iSize         = bufSize;
        buf->format        = Media_Format_2DBuffer;
        buf->uiNumElements = 1;

        va = DdiMediaUtil_CreateBuffer(buf, mediaCtx->pDrmBufMgr);
        if (va != VA_STATUS_SUCCESS)
        {
            MOS_FreeMemory(buf);
            return VA_STATUS_ERROR_ALLOCATION_FAILED;
        }
        break;
    }
    case VAEncMacroblockDisableSkipMapBufferType:
    {
        buf->uiHeight = m_encodeCtx->wPicHeightInMB;
        buf->uiWidth  = m_encodeCtx->wPicWidthInMB;
        buf->iSize    = m_encodeCtx->wPicHeightInMB * m_encodeCtx->wPicWidthInMB;
        buf->format   = Media_Format_2DBuffer;

        va = DdiMediaUtil_CreateBuffer(buf, mediaCtx->pDrmBufMgr);
        if (va != VA_STATUS_SUCCESS)
        {
            MOS_FreeMemory(buf);
            return VA_STATUS_ERROR_ALLOCATION_FAILED;
        }
        break;
    }
    case VAEncSliceParameterBufferType:
    {
        // elementsNum could be larger than 1 for this case
        // modify to support MPEG2 later
        // Currently the slice boundary is at MB row level
        // Here size is assumed to be the size of the slice control parameter for one single slice
        // and elementsNum is the number of slices
        expectedSize = getSliceParameterBufferSize();

        if ((size < expectedSize) ||
            (0 == elementsNum) ||
            (elementsNum > (m_encodeCtx->dwFrameHeight / CODECHAL_MACROBLOCK_HEIGHT)))
        {
            va = VA_STATUS_ERROR_INVALID_PARAMETER;
            CleanUpBufferandReturn(buf);
            return va;
        }

        bufSize = size * elementsNum;
        break;
    }
    case VAEncSequenceParameterBufferType:  // does not exist for JPEG
    {
        // elementsNum should be 1, ignore here just for robustness
        bufSize = size;
        expectedSize = getSequenceParameterBufferSize();

        if (bufSize < expectedSize)
        {
            va = VA_STATUS_ERROR_INVALID_PARAMETER;
            CleanUpBufferandReturn(buf);
            return va;
        }
        break;
    }
    case VAEncPictureParameterBufferType:
    {
        // elementsNum should be 1, ignore here just for robustness
        bufSize = size;
        expectedSize = getPictureParameterBufferSize();

        if (bufSize < expectedSize)
        {
            va = VA_STATUS_ERROR_INVALID_PARAMETER;
            CleanUpBufferandReturn(buf);
            return va;
        }
        break;
    }
    case VAIQMatrixBufferType:
    case VAQMatrixBufferType:
    {
        // elementsNum should be 1, ignore here just for robustness
        bufSize = size;
        expectedSize = getQMatrixBufferSize();

        if (bufSize < expectedSize)
        {
            va = VA_STATUS_ERROR_INVALID_PARAMETER;
            CleanUpBufferandReturn(buf);
            return va;
        }
        break;
    }
    case VAEncPackedHeaderParameterBufferType:  // doesnt exist for JPEG
    {
        // elementsNum should be 1, ignore here just for robustness
        bufSize = size;
        if (bufSize < sizeof(VAEncPackedHeaderParameterBuffer))
        {
            va = VA_STATUS_ERROR_INVALID_PARAMETER;
            CleanUpBufferandReturn(buf);
            return va;
        }
        break;
    }
    case VAEncPackedHeaderDataBufferType:  // doesnt exist for JPEG
    {
        // elementsNum should be 1, ignore here just for robustness
        bufSize = size;
        break;
    }
    case VAEncMiscParameterBufferType:  // doesnt exist for JPEG
    {
        // elementsNum should be 1, ignore here just for robustness
        bufSize = size;

        if (bufSize < sizeof(VAEncMiscParameterBuffer))
        {
            va = VA_STATUS_ERROR_INVALID_PARAMETER;
            CleanUpBufferandReturn(buf);
            return va;
        }
        break;
    }
    case VAHuffmanTableBufferType:  // only for JPEG
    {
        bufSize = size;

        if (bufSize < sizeof(VAHuffmanTableBufferJPEGBaseline))
        {
            va = VA_STATUS_ERROR_INVALID_PARAMETER;
            CleanUpBufferandReturn(buf);
            return va;
        }
        break;
    }
    case VAEncFEIMBControlBufferType:
    {
        bufSize       = size;
        buf->iSize  = size;
        buf->format = Media_Format_Buffer;
        va           = DdiMediaUtil_CreateBuffer(buf, mediaCtx->pDrmBufMgr);
        if (va != VA_STATUS_SUCCESS)
        {
            MOS_FreeMemory(buf);
            return VA_STATUS_ERROR_ALLOCATION_FAILED;
        }
        break;
    }
    case VAEncFEIMVPredictorBufferType:
    {
        bufSize       = size;
        buf->iSize  = size;
        buf->format = Media_Format_Buffer;
        va           = DdiMediaUtil_CreateBuffer(buf, mediaCtx->pDrmBufMgr);
        if (va != VA_STATUS_SUCCESS)
        {
            MOS_FreeMemory(buf);
            return VA_STATUS_ERROR_ALLOCATION_FAILED;
        }
        break;
    }
    case VAEncQPBufferType:
    {
        //The permb qp buffer of legacy encoder is a 2D buffer, because dynamic resolution change, we cant determine the buffer size with the resolution information in encoder context
        //so the size information should be from application, the width should be the size, the height is the elementsNum to define this 2D buffer,width should always 64 byte alignment.
        //please pay attention: 1 byte present 1 MB QP values for AVC, 4 bytes present 1 MB QP values for MPEG2, lowest byte is the real QP value, other 3 byes is other mb level contrl
        //which havent been exposed. the permb QP buffer of FEI is 1D buffer.
        if (CODECHAL_FUNCTION_ENC_PAK == m_encodeCtx->codecFunction ||
            CODECHAL_FUNCTION_ENC_VDENC_PAK == m_encodeCtx->codecFunction ||
            (((CODECHAL_FUNCTION_FEI_ENC_PAK == m_encodeCtx->codecFunction) || (CODECHAL_FUNCTION_FEI_ENC == m_encodeCtx->codecFunction)) &&
              (m_encodeCtx->wModeType == CODECHAL_ENCODE_MODE_HEVC)))
        {
            buf->uiWidth = MOS_ALIGN_CEIL(size, 64);
            if (size != buf->uiWidth)
            {
                va = VA_STATUS_ERROR_INVALID_PARAMETER;
                CleanUpBufferandReturn(buf);
                return va;
            }
            bufSize            = size * elementsNum;
            buf->uiHeight      = elementsNum;
            buf->uiPitch       = buf->uiWidth;
            buf->iSize         = bufSize;
            buf->format        = Media_Format_2DBuffer;
            buf->uiNumElements = 1;
        }
        else
        {
            bufSize       = size;
            buf->iSize  = size;
            buf->format = Media_Format_Buffer;
        }
        va = DdiMediaUtil_CreateBuffer(buf, mediaCtx->pDrmBufMgr);
        if (va != VA_STATUS_SUCCESS)
        {
            MOS_FreeMemory(buf);
            return VA_STATUS_ERROR_ALLOCATION_FAILED;
        }
        break;
    }
    case VAEncFEICTBCmdBufferType:
    case VAEncFEIMVBufferType:
    {
        bufSize       = size;
        buf->iSize  = bufSize;
        buf->format = Media_Format_Buffer;
        va           = DdiMediaUtil_CreateBuffer(buf, mediaCtx->pDrmBufMgr);
        if (va != VA_STATUS_SUCCESS)
        {
            MOS_FreeMemory(buf);
            return VA_STATUS_ERROR_ALLOCATION_FAILED;
        }
        break;
    }
    case VAEncFEICURecordBufferType:
    case VAEncFEIMBCodeBufferType:
    {
        bufSize       = size;
        buf->iSize  = bufSize;
        buf->format = Media_Format_Buffer;
        va           = DdiMediaUtil_CreateBuffer(buf, mediaCtx->pDrmBufMgr);
        if (va != VA_STATUS_SUCCESS)
        {
            MOS_FreeMemory(buf);
            return VA_STATUS_ERROR_ALLOCATION_FAILED;
        }
        break;
    }
    case VAEncFEIDistortionBufferType:
    {
        bufSize       = size;
        buf->iSize  = bufSize;
        buf->format = Media_Format_Buffer;
        va           = DdiMediaUtil_CreateBuffer(buf, mediaCtx->pDrmBufMgr);
        if (va != VA_STATUS_SUCCESS)
        {
            MOS_FreeMemory(buf);
            return VA_STATUS_ERROR_ALLOCATION_FAILED;
        }
        break;
    }
    case VAStatsStatisticsParameterBufferType:
    {
        // elementsNum should be 1, ignore here just for robustness
        bufSize = size;
        if (bufSize < sizeof(VAStatsStatisticsParameterH264))
        {
            va = VA_STATUS_ERROR_INVALID_PARAMETER;
            CleanUpBufferandReturn(buf);
            return va;
        }

        break;
    }
    case VAStatsMVPredictorBufferType:
    {
        bufSize       = size;
        buf->iSize  = size * elementsNum;
        buf->format = Media_Format_Buffer;
        va           = DdiMediaUtil_CreateBuffer(buf, mediaCtx->pDrmBufMgr);
        if (va != VA_STATUS_SUCCESS)
        {
            MOS_FreeMemory(buf);
            return VA_STATUS_ERROR_ALLOCATION_FAILED;
        }

        break;
    }
    case VAStatsMVBufferType:
    {
        bufSize       = size;
        buf->iSize  = size * elementsNum;
        buf->format = Media_Format_Buffer;
        va           = DdiMediaUtil_CreateBuffer(buf, mediaCtx->pDrmBufMgr);
        if (va != VA_STATUS_SUCCESS)
        {
            MOS_FreeMemory(buf);
            return VA_STATUS_ERROR_ALLOCATION_FAILED;
        }
        break;
    }
    case VAStatsStatisticsBufferType:
    case VAStatsStatisticsBottomFieldBufferType:
    {
        bufSize       = size;
        buf->iSize  = size * elementsNum;
        buf->format = Media_Format_Buffer;
        va           = DdiMediaUtil_CreateBuffer(buf, mediaCtx->pDrmBufMgr);
        if (va != VA_STATUS_SUCCESS)
        {
            MOS_FreeMemory(buf);
            return VA_STATUS_ERROR_ALLOCATION_FAILED;
        }
        break;
    }
#if VA_CHECK_VERSION(1, 10, 0)
    case VAContextParameterUpdateBufferType:
    {
        bufSize       = size;
        if (bufSize < sizeof(VAContextParameterUpdateBuffer))
        {
            va = VA_STATUS_ERROR_INVALID_PARAMETER;
            CleanUpBufferandReturn(buf);
            return va;
        }
        break;
    }
#endif
    default:
    {
        bufSize = size * elementsNum;

        if (0 == bufSize)
        {
            va = VA_STATUS_ERROR_INVALID_PARAMETER;
            CleanUpBufferandReturn(buf);
            return va;
        }

        va = m_encodeCtx->pCpDdiInterface->CreateBuffer(type, buf, size, elementsNum);
        if (va  == VA_STATUS_ERROR_UNSUPPORTED_BUFFERTYPE)
        {
            MOS_FreeMemory(buf);
            DDI_ASSERTMESSAGE("DDI: non supported buffer type = %d, size = %d, num = %d", type, size, elementsNum);
            return va;
        }

        break;
    }
    }

    if ((VAEncCodedBufferType != type) &&
        (VAEncMacroblockMapBufferType != type || m_cpuFormat) &&
        (VAEncFEIMVBufferType != type) &&
        (VAEncFEIMBCodeBufferType != type) &&
        (VAEncFEICTBCmdBufferType != type)      &&
        (VAEncFEICURecordBufferType != type)    &&
        (VAEncFEIDistortionBufferType != type) &&
        (VAEncFEIMBControlBufferType != type) &&
        (VAEncFEIMVPredictorBufferType != type) &&
        (VAStatsMVBufferType != type) &&
        (VAStatsStatisticsBufferType != type) &&
        (VAStatsStatisticsBottomFieldBufferType != type) &&
        (VAStatsMVPredictorBufferType != type) &&
        (VAEncQPBufferType != type) &&
        (VAEncMacroblockDisableSkipMapBufferType != (int32_t)type) &&
        (VAProbabilityBufferType != (int32_t)type))
    {
        buf->pData = (uint8_t*)MOS_AllocAndZeroMemory(bufSize);
        if (nullptr == buf->pData)
        {
            va = VA_STATUS_ERROR_ALLOCATION_FAILED;
            CleanUpBufferandReturn(buf);
            return va;
        }
        buf->iSize  = bufSize;
        buf->format = Media_Format_CPU;
    }

    PDDI_MEDIA_BUFFER_HEAP_ELEMENT bufferHeapElement = DdiMediaUtil_AllocPMediaBufferFromHeap(mediaCtx->pBufferHeap);
    if (nullptr == bufferHeapElement)
    {
        va = VA_STATUS_ERROR_MAX_NUM_EXCEEDED;
        CleanUpBufferandReturn(buf);
        return va;
    }

    bufferHeapElement->pBuffer   = buf;
    bufferHeapElement->pCtx      = (void*)m_encodeCtx;
    bufferHeapElement->uiCtxType = DDI_MEDIA_CONTEXT_TYPE_ENCODER;
    *bufId                        = bufferHeapElement->uiVaBufferID;
    mediaCtx->uiNumBufs++;

    // return success if data is nullptr, no need to copy data
    if (data == nullptr)
    {
        return va;
    }

    DdiMediaUtil_LockBuffer(buf, MOS_LOCKFLAG_WRITEONLY | MOS_LOCKFLAG_READONLY);

    // HW may use bigger than 64 pitch alignment for 2D. In such a case linear copying spoils the data
    // and has to be executed line by line. 'size' is in fact input data pitch.
    MOS_STATUS eStatus = MOS_STATUS_SUCCESS;
    if (elementsNum > 1 && buf->format == Media_Format_2DBuffer && size < buf->uiPitch)
    {
        uint8_t *pDst = buf->pData + buf->uiOffset;
        uint8_t *pSrc = (uint8_t *) data;
        for (int32_t i = 0; i < elementsNum && eStatus == MOS_STATUS_SUCCESS; i++)
            eStatus = MOS_SecureMemcpy(pDst + i*buf->uiPitch, size, pSrc + i*size, size);
    }
    else
    {
        eStatus = MOS_SecureMemcpy((void*)(buf->pData + buf->uiOffset), bufSize, (void*)data, bufSize);
    }

    DdiMediaUtil_UnlockBuffer(buf);

    if (eStatus != MOS_STATUS_SUCCESS)
    {
        DdiMedia_DestroyBufFromVABufferID(mediaCtx, bufferHeapElement->uiVaBufferID);
        va = VA_STATUS_ERROR_OPERATION_FAILED;
        CleanUpBufferandReturn(buf);
        return va;
    }

    // return success
    return va;
}

void DdiEncodeBase::CleanUpBufferandReturn(DDI_MEDIA_BUFFER *buf)
{
    if (buf)
    {
        MOS_FreeMemory(buf->pData);
        MOS_FreeMemory(buf);
    }
}

uint32_t DdiEncodeBase::getSliceParameterBufferSize()
{
    return 0xffffffff;
}

uint32_t DdiEncodeBase::getSequenceParameterBufferSize()
{
    return 0xffffffff;
}

uint32_t DdiEncodeBase::getPictureParameterBufferSize()
{
    return 0xffffffff;
}

uint32_t DdiEncodeBase::getQMatrixBufferSize()
{
    return 0xffffffff;
}

void DdiEncodeBase::ClearPicParams()
{
}
