/*
* Copyright (c) 2009-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_vp8.cpp
//! \brief    Defines class for DDI media vp8 encode.
//!

#include "media_libva.h"
#include "media_libva_encoder.h"
#include "media_libva_util.h"
#include "media_ddi_encode_vp8.h"
#include "media_ddi_encode_const.h"
#include "media_ddi_factory.h"

extern template class MediaDdiFactoryNoArg<DdiEncodeBase>;
static bool isEncodeVp8Registered =
    MediaDdiFactoryNoArg<DdiEncodeBase>::RegisterCodec<DdiEncodeVp8>(ENCODE_ID_VP8);

DdiEncodeVp8::~DdiEncodeVp8()
{
    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;
    }
}

VAStatus DdiEncodeVp8::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->dwFrameWidth;
    codecHalSettings->height      = m_encodeCtx->dwFrameHeight;
    codecHalSettings->mode          = m_encodeCtx->wModeType;
    codecHalSettings->standard      = CODECHAL_VP8;
    VAStatus vaStatus               = VA_STATUS_SUCCESS;

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

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

    if (m_encodeCtx->codecFunction == CODECHAL_FUNCTION_HYBRIDPAK)
    {
        m_encodeCtx->pSliceParams = (void *)MOS_AllocAndZeroMemory(sizeof(CODECHAL_VP8_HYBRIDPAK_FRAMEUPDATE));
        DDI_CHK_NULL(m_encodeCtx->pSliceParams, "nullptr m_encodeCtx->pSliceParams.", VA_STATUS_ERROR_ALLOCATION_FAILED);
    }

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

    // 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);

    uint32_t bufferSize         = CODECHAL_VP8_FRAME_HEADER_SIZE;
    m_encodeCtx->pbsBuffer->pBase = (uint8_t *)MOS_AllocAndZeroMemory(bufferSize);
    DDI_CHK_NULL(m_encodeCtx->pbsBuffer->pBase, "nullptr m_encodeCtx->pbsBuffer->pBase.", VA_STATUS_ERROR_ALLOCATION_FAILED);

    m_encodeCtx->pbsBuffer->BufferSize = bufferSize;

    // calculate mv offset
    uint32_t numMBs = DDI_CODEC_NUM_MACROBLOCKS_WIDTH(m_encodeCtx->dwFrameWidth) * DDI_CODEC_NUM_MACROBLOCKS_HEIGHT(m_encodeCtx->dwFrameHeight);

    if (VA_RC_CQP == m_encodeCtx->uiRCMethod)
    {
        m_mvOffset = MOS_ALIGN_CEIL(CODECHAL_VP8_MB_CODE_SIZE * sizeof(uint32_t),
                                      CODECHAL_VP8_MB_CODE_ALIGNMENT) *
                                  numMBs;
    }
    else
    {
        m_mvOffset = MOS_ALIGN_CEIL(CODECHAL_VP8_MB_CODE_SIZE * sizeof(uint32_t) + CODECHAL_VP8_MB_MV_CODE_SIZE, CODECHAL_VP8_MB_CODE_ALIGNMENT) + MOS_ALIGN_CEIL(CODECHAL_VP8_MB_CODE_SIZE * sizeof(uint32_t), CODECHAL_VP8_MB_CODE_ALIGNMENT) * numMBs;
    }

    return vaStatus;
}

VAStatus DdiEncodeVp8::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);
    DDI_CHK_NULL(m_encodeCtx->pCodecHal, "nullptr m_encodeCtx->pCodecHal", 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 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;
}

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

    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 vaStatus     = VA_STATUS_SUCCESS;

    // Get encoded frame information from status buffer queue.
    while (VA_STATUS_SUCCESS == (vaStatus = GetSizeFromStatusReportBuffer(mediaBuf, &size, &status, &index)))
    {
        if ((index >= 0) && (size != 0))  //Get the matched encoded buffer information
        {
            uint32_t sizeOfExtStatusReportBuf          = 0;  //reset or not used

            // 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;

            //making a segment for the StatusReport: pointer for the StatusReport and the size of it
            VACodedBufferSegment *pVACodedBufferSegmentForStatusReport;
            pVACodedBufferSegmentForStatusReport = m_encodeCtx->BufMgr.pCodedBufferSegmentForStatusReport;
            DDI_CHK_NULL(pVACodedBufferSegmentForStatusReport, "nullptr check in pVACodedBufferSegmentForStatusReport", VA_STATUS_ERROR_INVALID_CONTEXT);

            pVACodedBufferSegmentForStatusReport->size = sizeOfExtStatusReportBuf;
            pVACodedBufferSegmentForStatusReport->buf  = (void *)((char *)(m_encodeCtx->BufMgr.pCodedBufferSegment->buf) + size);
            pVACodedBufferSegmentForStatusReport->next = nullptr;

            VACodedBufferSegment *pFindTheLastEntry;
            pFindTheLastEntry = m_encodeCtx->BufMgr.pCodedBufferSegment;
            // if HDCP2 is enabled, the second segment for counter values is already there  So, move the connection as the third one
            if (m_encodeCtx->pCpDdiInterface->IsHdcp2Enabled())
                pFindTheLastEntry = (VACodedBufferSegment *)pFindTheLastEntry->next;
            // connect Status report here
            pFindTheLastEntry->next = pVACodedBufferSegmentForStatusReport;

            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;
        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;
            status = (encodeStatusReport[0].AverageQp & VA_CODED_BUF_STATUS_PICTURE_AVE_QP_MASK);
            // 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;
                break;
            }
            //Add encoded frame information into status buffer queue.
            continue;
        }
        else if (CODECHAL_STATUS_INCOMPLETE == encodeStatusReport[0].CodecStatus)
        {
            CodechalEncoderState *encoder = dynamic_cast<CodechalEncoderState *>(m_encodeCtx->pCodecHal);
            if (encoder != nullptr && encoder->m_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;
                m_encodeCtx->statusReportBuf.ulUpdatePosition = (m_encodeCtx->statusReportBuf.ulUpdatePosition + 1) % DDI_ENCODE_MAX_STATUS_REPORT_BUFFER;
                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;
                m_encodeCtx->statusReportBuf.ulUpdatePosition = (m_encodeCtx->statusReportBuf.ulUpdatePosition + 1) % DDI_ENCODE_MAX_STATUS_REPORT_BUFFER;
                DDI_ASSERTMESSAGE("Something unexpected happened in HW, return error to application");
                break;
            }
        }
        else if (CODECHAL_STATUS_ERROR == encodeStatusReport[0].CodecStatus)
        {
            DDI_NORMALMESSAGE("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;
            m_encodeCtx->statusReportBuf.ulUpdatePosition = (m_encodeCtx->statusReportBuf.ulUpdatePosition + 1) % DDI_ENCODE_MAX_STATUS_REPORT_BUFFER;
            break;
        }
        else
        {
            break;
        }
    }

    if (vaStatus != VA_STATUS_SUCCESS)
    {
        return vaStatus;
    }

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

VAStatus DdiEncodeVp8::InitCompBuffer()
{
    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_CODEC_COM_BUFFER_MGR *pBufMgr = &(m_encodeCtx->BufMgr);

    // memory allocation in the beging of the sequence for extended StatusReport and then save it from DDI_CODEC_COM_BUFFER_MGR, for example, VP8
    VACodedBufferSegment *pVACodedBufferSegmentForStatusReport = (VACodedBufferSegment *)MOS_AllocAndZeroMemory(sizeof(VACodedBufferSegment));
    if (pVACodedBufferSegmentForStatusReport == nullptr)
    {
        return VA_STATUS_ERROR_ALLOCATION_FAILED;
    }
    pBufMgr->pCodedBufferSegmentForStatusReport = pVACodedBufferSegmentForStatusReport;

    DDI_CHK_RET(DdiEncodeBase::InitCompBuffer(), "InitCompBuffer failed!");

    return VA_STATUS_SUCCESS;
}

void DdiEncodeVp8::FreeCompBuffer()
{
    DdiEncodeBase::FreeCompBuffer();

    DDI_CODEC_COM_BUFFER_MGR *pBufMgr = &(m_encodeCtx->BufMgr);
    if (pBufMgr->pCodedBufferSegmentForStatusReport != nullptr)
    {
        MOS_FreeMemory(pBufMgr->pCodedBufferSegmentForStatusReport);
        pBufMgr->pCodedBufferSegmentForStatusReport = nullptr;
    }
}

VAStatus DdiEncodeVp8::ResetAtFrameLevel()
{
    DDI_CHK_NULL(m_encodeCtx, "nullptr m_encodeCtx", VA_STATUS_ERROR_INVALID_PARAMETER);

    CODEC_VP8_ENCODE_SEQUENCE_PARAMS *seqParams = (CODEC_VP8_ENCODE_SEQUENCE_PARAMS *)(m_encodeCtx->pSeqParams);
    DDI_CHK_NULL(seqParams, "nullptr seqParams", VA_STATUS_ERROR_INVALID_PARAMETER);
    seqParams->ResetBRC = 0x0;

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

    return VA_STATUS_SUCCESS;
}

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

    VAQMatrixBufferVP8 *quantParams = (VAQMatrixBufferVP8 *)ptr;

    CODEC_VP8_ENCODE_QUANT_DATA *vp8QuantParams = (CODEC_VP8_ENCODE_QUANT_DATA *)(m_encodeCtx->pQmatrixParams);
    DDI_CHK_NULL(vp8QuantParams, "nullptr vp8QuantParams", VA_STATUS_ERROR_INVALID_PARAMETER);

    MOS_ZeroMemory(vp8QuantParams, sizeof(CODEC_VP8_ENCODE_QUANT_DATA));

    for (int32_t i = 0; i < 4; i++)
    {
        vp8QuantParams->QIndex[i] = quantParams->quantization_index[i];
    }

    for (int32_t i = 0; i < 5; i++)
    {
        vp8QuantParams->QIndexDelta[i] = quantParams->quantization_index_delta[i];
    }

    return VA_STATUS_SUCCESS;
}

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

    VAEncSequenceParameterBufferVP8 *seqParams = (VAEncSequenceParameterBufferVP8 *)ptr;

    CODEC_VP8_ENCODE_SEQUENCE_PARAMS *vp8SeqParams = (CODEC_VP8_ENCODE_SEQUENCE_PARAMS *)(m_encodeCtx->pSeqParams);
    DDI_CHK_NULL(vp8SeqParams, "nullptr vp8SeqParams", VA_STATUS_ERROR_INVALID_PARAMETER);

    MOS_ZeroMemory(vp8SeqParams, sizeof(CODEC_VP8_ENCODE_SEQUENCE_PARAMS));

    vp8SeqParams->FrameWidth       = seqParams->frame_width;
    vp8SeqParams->FrameWidthScale  = seqParams->frame_width_scale;
    vp8SeqParams->FrameHeight      = seqParams->frame_height;
    vp8SeqParams->FrameHeightScale = seqParams->frame_height_scale;

    vp8SeqParams->GopPicSize = seqParams->intra_period;

    vp8SeqParams->TargetBitRate[0] = MOS_ROUNDUP_DIVIDE(seqParams->bits_per_second, CODECHAL_ENCODE_BRC_KBPS);

    return VA_STATUS_SUCCESS;
}

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

    VAEncPictureParameterBufferVP8 *picParams = (VAEncPictureParameterBufferVP8 *)ptr;

    CODEC_VP8_ENCODE_PIC_PARAMS *vp8PicParams = (CODEC_VP8_ENCODE_PIC_PARAMS *)(m_encodeCtx->pPicParams);
    DDI_CHK_NULL(vp8PicParams, "nullptr vp8PicParams", VA_STATUS_ERROR_INVALID_PARAMETER);

    MOS_ZeroMemory(vp8PicParams, sizeof(CODEC_VP8_ENCODE_PIC_PARAMS));

    vp8PicParams->frame_type                  = picParams->pic_flags.bits.frame_type;
    vp8PicParams->version                     = picParams->pic_flags.bits.version;
    vp8PicParams->show_frame                  = picParams->pic_flags.bits.show_frame;
    vp8PicParams->color_space                 = picParams->pic_flags.bits.color_space;
    vp8PicParams->clamping_type               = picParams->pic_flags.bits.clamping_type;
    vp8PicParams->segmentation_enabled        = picParams->pic_flags.bits.segmentation_enabled;
    vp8PicParams->update_mb_segmentation_map  = picParams->pic_flags.bits.update_mb_segmentation_map;
    vp8PicParams->update_segment_feature_data = picParams->pic_flags.bits.update_segment_feature_data;
    vp8PicParams->filter_type                 = picParams->pic_flags.bits.loop_filter_type;
    vp8PicParams->loop_filter_adj_enable      = picParams->pic_flags.bits.loop_filter_adj_enable;
    vp8PicParams->CodedCoeffTokenPartition    = picParams->pic_flags.bits.num_token_partitions;
    vp8PicParams->refresh_golden_frame        = picParams->pic_flags.bits.refresh_golden_frame;
    vp8PicParams->refresh_alternate_frame     = picParams->pic_flags.bits.refresh_alternate_frame;
    vp8PicParams->copy_buffer_to_golden       = picParams->pic_flags.bits.copy_buffer_to_golden;
    vp8PicParams->copy_buffer_to_alternate    = picParams->pic_flags.bits.copy_buffer_to_alternate;
    vp8PicParams->sign_bias_golden            = picParams->pic_flags.bits.sign_bias_golden;
    vp8PicParams->sign_bias_alternate         = picParams->pic_flags.bits.sign_bias_alternate;
    vp8PicParams->refresh_entropy_probs       = picParams->pic_flags.bits.refresh_entropy_probs;
    vp8PicParams->refresh_last                = picParams->pic_flags.bits.refresh_last;
    vp8PicParams->mb_no_coeff_skip            = picParams->pic_flags.bits.mb_no_coeff_skip;
    vp8PicParams->forced_lf_adjustment        = picParams->pic_flags.bits.forced_lf_adjustment;

    if (vp8PicParams->frame_type == 0)
    {
        vp8PicParams->ref_frame_ctrl = 0;
    }
    else
    {
        vp8PicParams->ref_frame_ctrl = (!picParams->ref_flags.bits.no_ref_last) | ((!picParams->ref_flags.bits.no_ref_gf) << 1) | ((!picParams->ref_flags.bits.no_ref_arf) << 2);
    }

    // first_ref and second_ref parameters are currently passed through the reserved parameter by the application
    vp8PicParams->first_ref  = (picParams->ref_flags.bits.reserved >> 18) & 0x3;
    vp8PicParams->second_ref = (picParams->ref_flags.bits.reserved >> 16) & 0x3;
#ifdef ANDROID
    vp8PicParams->temporal_id = picParams->ref_flags.bits.temporal_id;
#endif
    // Copy list of 4 loop filter level values, delta values for ref frame and coding mode based MB-level
    for (int32_t i = 0; i < 4; i++)
    {
        vp8PicParams->loop_filter_level[i] = picParams->loop_filter_level[i];
        vp8PicParams->ref_lf_delta[i]      = picParams->ref_lf_delta[i];
        vp8PicParams->mode_lf_delta[i]     = picParams->mode_lf_delta[i];
    }

    vp8PicParams->sharpness_level = picParams->sharpness_level;
    vp8PicParams->ClampQindexHigh = picParams->clamp_qindex_high;
    vp8PicParams->ClampQindexLow  = picParams->clamp_qindex_low;

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

    rtTbl->pCurrentReconTarget = DdiMedia_GetSurfaceFromVASurfaceID(mediaCtx, picParams->reconstructed_frame);
    DDI_CHK_NULL(rtTbl->pCurrentReconTarget, "nullptr rtTbl->pCurrentReconTarget", VA_STATUS_ERROR_INVALID_PARAMETER);
    RegisterRTSurfaces(rtTbl,rtTbl->pCurrentReconTarget);

    SetupCodecPicture(mediaCtx, rtTbl, &vp8PicParams->CurrReconstructedPic, picParams->reconstructed_frame, false);
    // curr orig pic
    vp8PicParams->CurrOriginalPic.FrameIdx = GetRenderTargetID(rtTbl, rtTbl->pCurrentReconTarget);
    vp8PicParams->CurrOriginalPic.PicFlags = vp8PicParams->CurrReconstructedPic.PicFlags;

    SetupCodecPicture(mediaCtx, rtTbl, &vp8PicParams->LastRefPic, picParams->ref_last_frame, true);

    SetupCodecPicture(mediaCtx, rtTbl, &vp8PicParams->GoldenRefPic, picParams->ref_gf_frame, true);

    SetupCodecPicture(mediaCtx, rtTbl, &vp8PicParams->AltRefPic, picParams->ref_arf_frame, true);

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

    return VA_STATUS_SUCCESS;
}

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

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

    switch ((int32_t)(miscParamBuf->type))
    {
    case VAEncMiscParameterTypeHRD:
    {
        ParseMiscParamVBV((void *)miscParamBuf->data);
        break;
    }
    case VAEncMiscParameterTypeFrameRate:
    {
        ParseMiscParamFR((void *)miscParamBuf->data);
        break;
    }
    case VAEncMiscParameterTypeQualityLevel:  // for target usage
    {
        ParseBufferQualityLevel((void *)miscParamBuf->data);
        break;
    }
    case VAEncMiscParameterTypeRateControl:
    {
        ParseMiscParamRC((void *)miscParamBuf->data);
        break;
    }
    case VAEncMiscParameterTypeTemporalLayerStructure:
    {
        ParseMiscParameterTemporalLayerParams((void *)miscParamBuf->data);
        break;
    }
    default:
    {
        DDI_ASSERTMESSAGE("DDI: unsupported misc parameter type.");
        return VA_STATUS_ERROR_INVALID_PARAMETER;
    }
    }

    return VA_STATUS_SUCCESS;
}

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

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

    CODEC_VP8_ENCODE_SEQUENCE_PARAMS *seqParams = (PCODEC_VP8_ENCODE_SEQUENCE_PARAMS)(m_encodeCtx->pSeqParams);

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

    // Raw Surface
    MOS_SURFACE rawSurface;
    MOS_ZeroMemory(&rawSurface, sizeof(MOS_SURFACE));
    rawSurface.Format   = Format_NV12;
    rawSurface.dwOffset = 0;

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

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

    MOS_RESOURCE mbCodeSurface;
    MOS_ZeroMemory(&mbCodeSurface, sizeof(MOS_RESOURCE));
    mbCodeSurface                    = m_encodeCtx->resMbCodeBuffer;
    encodeParams.psRawSurface        = &rawSurface;
    encodeParams.psReconSurface      = &reconSurface;
    encodeParams.presBitstreamBuffer = &bitstreamSurface;
    encodeParams.presMbCodeSurface   = &mbCodeSurface;

    // Segmentation map buffer
    encodeParams.psMbSegmentMapSurface = &m_encodeCtx->segMapBuffer;

    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        = seqParams->TargetBitRate[0];
        seqParams->MinBitRate        = seqParams->TargetBitRate[0];
    }
    else if (VA_RC_VBR == m_encodeCtx->uiRCMethod)
    {
        seqParams->RateControlMethod = RATECONTROL_VBR;
    }
    if((m_encodeCtx->uiTargetBitRate != seqParams->TargetBitRate[0]) || (m_encodeCtx->uiMaxBitRate != seqParams-> MaxBitRate))
    {
        if(m_encodeCtx->uiTargetBitRate )
        {
            seqParams->ResetBRC = 0x1;
        }
        m_encodeCtx->uiTargetBitRate = seqParams->TargetBitRate[0];
        m_encodeCtx->uiMaxBitRate = seqParams->MaxBitRate;
    }

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

    encodeParams.uiFrameRate = m_encodeCtx->uFrameRate;

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

    // IQmatrix params
    encodeParams.bNewQmatrixData = m_encodeCtx->bNewQmatrixData;
    encodeParams.bPicQuant       = m_encodeCtx->bPicQuant;

    encodeParams.pQuantData = m_encodeCtx->pQmatrixParams;

    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 DdiEncodeVp8::ParseSegMapParams(DDI_MEDIA_BUFFER *buf)
{
    DDI_CHK_NULL(m_encodeCtx, "nullptr m_encodeCtx", VA_STATUS_ERROR_INVALID_PARAMETER);

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

uint32_t DdiEncodeVp8::AddExtStatusReportParam(DDI_MEDIA_BUFFER *buf, uint32_t size)
{
    return 0;
}

void DdiEncodeVp8::ParseBufferQualityLevel(void *data)
{
    VAEncMiscParameterBufferQualityLevel *vaEncMiscParamQualityLevel = (VAEncMiscParameterBufferQualityLevel *)data;

    CODEC_VP8_ENCODE_SEQUENCE_PARAMS *seqParams = (CODEC_VP8_ENCODE_SEQUENCE_PARAMS *)m_encodeCtx->pSeqParams;
    seqParams->TargetUsage                      = vaEncMiscParamQualityLevel->quality_level;
}

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

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

    seqParams->VBVBufferSizeInBit         = vaEncMiscParamHRD->buffer_size;
    seqParams->InitVBVBufferFullnessInBit = vaEncMiscParamHRD->initial_buffer_fullness;
    seqParams->RateControlMethod          = RATECONTROL_CBR;
}

void DdiEncodeVp8::ParseMiscParamFR(void *data)
{
    VAEncMiscParameterFrameRate *vaFrameRate = (VAEncMiscParameterFrameRate *)data;
    CODEC_VP8_ENCODE_SEQUENCE_PARAMS *seqParams = (PCODEC_VP8_ENCODE_SEQUENCE_PARAMS)m_encodeCtx->pSeqParams;

    uint32_t numerator = (vaFrameRate->framerate & 0xffff) * 100;
    auto denominator = (vaFrameRate->framerate >> 16)&0xfff;
    if(denominator == 0)
    {
        denominator = 1;
    }

    uint32_t tmpId = 0;
#ifdef ANDROID
    tmpId = vaFrameRate->framerate_flags.bits.temporal_id;
#endif
    seqParams->FramesPer100Sec[tmpId] = numerator/denominator;
    if(m_framesPer100Sec && m_framesPer100Sec != seqParams->FramesPer100Sec[tmpId])
    {
        seqParams->ResetBRC = 0x1;
    }
    m_framesPer100Sec = seqParams->FramesPer100Sec[tmpId];
}

void DdiEncodeVp8::ParseMiscParamRC(void *data)
{
    CODEC_VP8_ENCODE_SEQUENCE_PARAMS *seqParams = (CODEC_VP8_ENCODE_SEQUENCE_PARAMS *)(m_encodeCtx->pSeqParams);

    VAEncMiscParameterRateControl *vaEncMiscParamRC = (VAEncMiscParameterRateControl *)data;

    uint32_t tmpId = 0;
#ifdef ANDROID
    tmpId = vaEncMiscParamRC->rc_flags.bits.temporal_id;
#endif
    seqParams->MaxBitRate           = MOS_ROUNDUP_DIVIDE(vaEncMiscParamRC->bits_per_second, CODECHAL_ENCODE_BRC_KBPS);
    seqParams->TargetBitRate[tmpId] = seqParams->MaxBitRate;
    seqParams->ResetBRC             = vaEncMiscParamRC->rc_flags.bits.reset;  // adding reset here. will apply both CBR and VBR
    seqParams->MBBRC                = vaEncMiscParamRC->rc_flags.bits.mb_rate_control;

    if (VA_RC_CBR == m_encodeCtx->uiRCMethod)
    {
        seqParams->MinBitRate        = seqParams->MaxBitRate;
        seqParams->RateControlMethod = RATECONTROL_CBR;
    }
    else if (VA_RC_VBR == m_encodeCtx->uiRCMethod)
    {
        seqParams->MinBitRate           = seqParams->MaxBitRate * (2 * vaEncMiscParamRC->target_percentage - 100) / 100;
        seqParams->TargetBitRate[tmpId] = seqParams->MaxBitRate * vaEncMiscParamRC->target_percentage / 100;  // VBR target bits
        seqParams->RateControlMethod    = RATECONTROL_VBR;
    }
}

void DdiEncodeVp8::ParseMiscParamPrivate(void *data)
{
    DDI_UNUSED(data);
    // Nothing to do here
    return;
}

void DdiEncodeVp8::ParseMiscParameterTemporalLayerParams(void *data)
{
    CODEC_VP8_ENCODE_SEQUENCE_PARAMS *seqParams = (CODEC_VP8_ENCODE_SEQUENCE_PARAMS *)(m_encodeCtx->pSeqParams);

    VAEncMiscParameterTemporalLayerStructure *vaEncTempLayerStruct = (VAEncMiscParameterTemporalLayerStructure *)data;

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

void DdiEncodeVp8::SetupCodecPicture(
    DDI_MEDIA_CONTEXT                     *mediaCtx,
    DDI_CODEC_RENDER_TARGET_TABLE         *rtTbl,
    CODEC_PICTURE                         *codecHalPic,
    VASurfaceID                           surfaceID,
    bool                                  picReference)
{
    if(DDI_CODEC_INVALID_FRAME_INDEX != 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;  // No long term references in VP8
        }
    }
    else
    {
        codecHalPic->PicFlags = PICTURE_FRAME;
    }
}

uint32_t DdiEncodeVp8::getSequenceParameterBufferSize()
{
    return sizeof(VAEncSequenceParameterBufferVP8);
}

uint32_t DdiEncodeVp8::getPictureParameterBufferSize()
{
    return sizeof(VAEncPictureParameterBufferVP8);
}

uint32_t DdiEncodeVp8::getQMatrixBufferSize()
{
    return sizeof(VAQMatrixBufferVP8);
}
