/*
* Copyright (c) 2009-2020, 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_libva_encoder.cpp
//! \brief     libva(and its extension) encoder implementation
//!
#include <unistd.h>
#include "media_libva_encoder.h"
#include "media_ddi_encode_base.h"
#include "media_libva_util.h"
#include "media_libva_caps.h"
#include "media_ddi_factory.h"

#include "hwinfo_linux.h"
#include "codechal_memdecomp.h"
#include "media_interfaces_codechal.h"
#include "media_interfaces_codechal_next.h"
#include "media_interfaces_mmd.h"
#include "cm_device_rt.h"

typedef MediaDdiFactoryNoArg<DdiEncodeBase> DdiEncodeFactory;

PDDI_ENCODE_CONTEXT DdiEncode_GetEncContextFromContextID(VADriverContextP ctx, VAContextID vaCtxID)
{
    uint32_t  ctxType;
    return (PDDI_ENCODE_CONTEXT)DdiMedia_GetContextFromContextID(ctx, vaCtxID, &ctxType);
}

VAStatus DdiEncode_RemoveFromStatusReportQueue(
    PDDI_ENCODE_CONTEXT encCtx,
    PDDI_MEDIA_BUFFER   buf)

{
    DDI_CHK_NULL(encCtx, "nullptr encCtx", VA_STATUS_ERROR_INVALID_CONTEXT);
    DDI_CHK_NULL(encCtx->m_encode, "nullptr encCtx->m_encode", VA_STATUS_ERROR_INVALID_CONTEXT);
    DDI_CHK_NULL(buf, "nullptr buf", VA_STATUS_ERROR_INVALID_PARAMETER);

    VAStatus vaStatus = encCtx->m_encode->RemoveFromStatusReportQueue(buf);

    return vaStatus;
}

VAStatus DdiEncode_RemoveFromEncStatusReportQueue(
    PDDI_ENCODE_CONTEXT            encCtx,
    PDDI_MEDIA_BUFFER              buf,
    DDI_ENCODE_FEI_ENC_BUFFER_TYPE typeIdx)
{
    DDI_CHK_NULL(encCtx, "nullptr encCtx", VA_STATUS_ERROR_INVALID_CONTEXT);
    DDI_CHK_NULL(encCtx->m_encode, "nullptr encCtx->m_encode", VA_STATUS_ERROR_INVALID_CONTEXT);
    DDI_CHK_NULL(buf, "nullptr buf", VA_STATUS_ERROR_INVALID_PARAMETER);

    VAStatus vaStatus = encCtx->m_encode->RemoveFromEncStatusReportQueue(buf, typeIdx);

    return vaStatus;
}

VAStatus DdiEncode_RemoveFromPreEncStatusReportQueue(
    PDDI_ENCODE_CONTEXT            encCtx,
    PDDI_MEDIA_BUFFER              buf,
    DDI_ENCODE_PRE_ENC_BUFFER_TYPE typeIdx)
{
    DDI_CHK_NULL(encCtx, "nullptr encCtx", VA_STATUS_ERROR_INVALID_CONTEXT);
    DDI_CHK_NULL(encCtx->m_encode, "nullptr encCtx->m_encode", VA_STATUS_ERROR_INVALID_CONTEXT);
    DDI_CHK_NULL(buf, "nullptr buf", VA_STATUS_ERROR_INVALID_PARAMETER);

    VAStatus vaStatus = encCtx->m_encode->RemoveFromPreEncStatusReportQueue(buf, typeIdx);

    return vaStatus;
}

bool DdiEncode_CodedBufferExistInStatusReport(
    PDDI_ENCODE_CONTEXT encCtx,
    PDDI_MEDIA_BUFFER   buf)
{
    DDI_CHK_NULL(encCtx, "nullptr encCtx", VA_STATUS_ERROR_INVALID_CONTEXT);
    DDI_CHK_NULL(encCtx->m_encode, "nullptr encCtx->m_encode", VA_STATUS_ERROR_INVALID_CONTEXT);
    DDI_CHK_NULL(buf, "nullptr buf", VA_STATUS_ERROR_INVALID_PARAMETER);

    return encCtx->m_encode->CodedBufferExistInStatusReport(buf);
}

bool DdiEncode_EncBufferExistInStatusReport(
    PDDI_ENCODE_CONTEXT            encCtx,
    PDDI_MEDIA_BUFFER              buf,
    DDI_ENCODE_FEI_ENC_BUFFER_TYPE typeIdx)
{
    DDI_CHK_NULL(encCtx, "nullptr encCtx", VA_STATUS_ERROR_INVALID_CONTEXT);
    DDI_CHK_NULL(encCtx->m_encode, "nullptr encCtx->m_encode", VA_STATUS_ERROR_INVALID_CONTEXT);
    DDI_CHK_NULL(buf, "nullptr buf", VA_STATUS_ERROR_INVALID_PARAMETER);

    return encCtx->m_encode->EncBufferExistInStatusReport(buf, typeIdx);
}

bool DdiEncode_PreEncBufferExistInStatusReport(
    PDDI_ENCODE_CONTEXT            encCtx,
    PDDI_MEDIA_BUFFER              buf,
    DDI_ENCODE_PRE_ENC_BUFFER_TYPE typeIdx)
{
    DDI_CHK_NULL(encCtx, "nullptr encCtx", VA_STATUS_ERROR_INVALID_CONTEXT);
    DDI_CHK_NULL(encCtx->m_encode, "nullptr encCtx->m_encode", VA_STATUS_ERROR_INVALID_CONTEXT);
    DDI_CHK_NULL(buf, "nullptr buf", VA_STATUS_ERROR_INVALID_PARAMETER);

    return encCtx->m_encode->PreEncBufferExistInStatusReport(buf, typeIdx);
}

VAStatus DdiEncode_EncStatusReport(
    PDDI_ENCODE_CONTEXT encCtx,
    DDI_MEDIA_BUFFER    *mediaBuf,
    void                **buf)
{
    DDI_CHK_NULL(encCtx, "nullptr encCtx", VA_STATUS_ERROR_INVALID_CONTEXT);
    DDI_CHK_NULL(encCtx->m_encode, "nullptr encCtx->m_encode", 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);

    VAStatus vaStatus = encCtx->m_encode->EncStatusReport(mediaBuf, buf);

    return vaStatus;
}

VAStatus DdiEncode_PreEncStatusReport(
    PDDI_ENCODE_CONTEXT encCtx,
    DDI_MEDIA_BUFFER    *mediaBuf,
    void                **buf)
{
    DDI_CHK_NULL(encCtx, "nullptr encCtx", VA_STATUS_ERROR_INVALID_CONTEXT);
    DDI_CHK_NULL(encCtx->m_encode, "nullptr encCtx->m_encode", 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);

    VAStatus vaStatus = encCtx->m_encode->PreEncStatusReport(mediaBuf, buf);

    return vaStatus;
}

VAStatus DdiEncode_StatusReport(
    PDDI_ENCODE_CONTEXT encCtx,
    DDI_MEDIA_BUFFER    *mediaBuf,
    void                **buf)
{
    PERF_UTILITY_AUTO(__FUNCTION__, PERF_ENCODE, PERF_LEVEL_DDI);

    DDI_CHK_NULL(encCtx, "nullptr encCtx", VA_STATUS_ERROR_INVALID_CONTEXT);
    DDI_CHK_NULL(encCtx->m_encode, "nullptr encCtx->m_encode", 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);

    VAStatus vaStatus = encCtx->m_encode->StatusReport(mediaBuf, buf);

    return vaStatus;
}

//!
//! \brief  Clean and free encode context structure
//!
//! \param  [in] encCtx
//!     Pointer to ddi encode context
//!
void DdiEncodeCleanUp(PDDI_ENCODE_CONTEXT encCtx)
{
    if (encCtx->m_encode)
    {
        MOS_Delete(encCtx->m_encode);
        encCtx->m_encode = nullptr;
    }

    if (encCtx->pCpDdiInterface)
    {
        Delete_DdiCpInterface(encCtx->pCpDdiInterface);
        encCtx->pCpDdiInterface = nullptr;
    }

    MOS_FreeMemory(encCtx);
    encCtx = nullptr;

    return;
}

/*
 *  vpgEncodeCreateContext - Create an encode context
 *  dpy: display
 *  config_id: configuration for the encode context
 *  picture_width: encode picture width
 *  picture_height: encode picture height
 *  flag: any combination of the following:
 *  VA_PROGRESSIVE (only progressive frame pictures in the sequence when set)
 *  render_targets: render targets (surfaces) tied to the context
 *  num_render_targets: number of render targets in the above array
 *  context: created context id upon return
 */
VAStatus DdiEncode_CreateContext(
    VADriverContextP ctx,
    VAConfigID       config_id,
    int32_t          picture_width,
    int32_t          picture_height,
    int32_t          flag,
    VASurfaceID     *render_targets,
    int32_t          num_render_targets,
    VAContextID     *context)
{
    PERF_UTILITY_AUTO(__FUNCTION__, PERF_ENCODE, PERF_LEVEL_DDI);

    DDI_CHK_NULL(ctx, "nullptr ctx", VA_STATUS_ERROR_INVALID_CONTEXT);
    DDI_CHK_NULL(ctx->pDriverData, "nullptr ctx->pDriverData", VA_STATUS_ERROR_INVALID_CONTEXT);

    PDDI_MEDIA_CONTEXT mediaDrvCtx = DdiMedia_GetMediaContext(ctx);
    DDI_CHK_NULL(mediaDrvCtx->m_caps, "nullptr m_caps", VA_STATUS_ERROR_INVALID_CONTEXT);

    VAProfile profile;
    VAEntrypoint entrypoint;
    uint32_t rcMode = 0;
    uint32_t feiFunction = 0;
    VAStatus vaStatus = mediaDrvCtx->m_caps->GetEncConfigAttr(
            config_id + DDI_CODEC_GEN_CONFIG_ATTRIBUTES_ENC_BASE,
            &profile,
            &entrypoint,
            &rcMode,
            &feiFunction);
    DDI_CHK_RET(vaStatus, "Invalide config_id!");

    vaStatus = mediaDrvCtx->m_caps->CheckEncodeResolution(
            profile,
            picture_width,
            picture_height);
    if (vaStatus != VA_STATUS_SUCCESS)
    {
        return VA_STATUS_ERROR_RESOLUTION_NOT_SUPPORTED;
    }

    if (num_render_targets > DDI_MEDIA_MAX_SURFACE_NUMBER_CONTEXT)
    {
        return VA_STATUS_ERROR_MAX_NUM_EXCEEDED;
    }

    std::string    encodeKey = mediaDrvCtx->m_caps->GetEncodeCodecKey(profile, entrypoint, feiFunction);
    DdiEncodeBase *ddiEncode = DdiEncodeFactory::CreateCodec(encodeKey);
    DDI_CHK_NULL(ddiEncode, "nullptr ddiEncode", VA_STATUS_ERROR_UNIMPLEMENTED);

    // first create encoder context
    ddiEncode->m_encodeCtx = (PDDI_ENCODE_CONTEXT)MOS_AllocAndZeroMemory(sizeof(DDI_ENCODE_CONTEXT));
    DDI_CHK_NULL(ddiEncode->m_encodeCtx, "nullptr ddiEncode->m_encodeCtx", VA_STATUS_ERROR_ALLOCATION_FAILED);

    PDDI_ENCODE_CONTEXT encCtx = ddiEncode->m_encodeCtx;
    encCtx->m_encode           = ddiEncode;

    //initialize DDI level cp interface
    MOS_CONTEXT mosCtx = { };
    encCtx->pCpDdiInterface = Create_DdiCpInterface(mosCtx);
    if (nullptr == encCtx->pCpDdiInterface)
    {
        vaStatus = VA_STATUS_ERROR_ALLOCATION_FAILED;
        DdiEncodeCleanUp(encCtx);
        return vaStatus;
    }

    // Get the buf manager for codechal create
    mosCtx.bufmgr          = mediaDrvCtx->pDrmBufMgr;
    mosCtx.m_gpuContextMgr = mediaDrvCtx->m_gpuContextMgr;
    mosCtx.m_cmdBufMgr     = mediaDrvCtx->m_cmdBufMgr;
    mosCtx.fd              = mediaDrvCtx->fd;
    mosCtx.iDeviceId       = mediaDrvCtx->iDeviceId;
    mosCtx.m_skuTable      = mediaDrvCtx->SkuTable;
    mosCtx.m_waTable       = mediaDrvCtx->WaTable;
    mosCtx.m_gtSystemInfo  = *mediaDrvCtx->pGtSystemInfo;
    mosCtx.m_platform      = mediaDrvCtx->platform;

    mosCtx.ppMediaMemDecompState = &mediaDrvCtx->pMediaMemDecompState;
    mosCtx.pfnMemoryDecompress   = mediaDrvCtx->pfnMemoryDecompress;
    mosCtx.pfnMediaMemoryCopy    = mediaDrvCtx->pfnMediaMemoryCopy;
    mosCtx.pfnMediaMemoryCopy2D  = mediaDrvCtx->pfnMediaMemoryCopy2D;
    mosCtx.ppMediaCopyState      = &mediaDrvCtx->pMediaCopyState;
    mosCtx.m_gtSystemInfo          = *mediaDrvCtx->pGtSystemInfo;
    mosCtx.m_auxTableMgr         = mediaDrvCtx->m_auxTableMgr;
    mosCtx.pGmmClientContext     = mediaDrvCtx->pGmmClientContext;

    mosCtx.m_osDeviceContext     = mediaDrvCtx->m_osDeviceContext;
    mosCtx.m_apoMosEnabled       = mediaDrvCtx->m_apoMosEnabled;

    mosCtx.m_userSettingPtr      = mediaDrvCtx->m_userSettingPtr;

    mosCtx.pPerfData             = (PERF_DATA *)MOS_AllocAndZeroMemory(sizeof(PERF_DATA));
    if (nullptr == mosCtx.pPerfData)
    {
        vaStatus = VA_STATUS_ERROR_ALLOCATION_FAILED;
        DdiEncodeCleanUp(encCtx);
        return vaStatus;
    }

    encCtx->vaEntrypoint  = entrypoint;
    encCtx->vaProfile     = profile;
    encCtx->uiRCMethod    = rcMode;
    encCtx->wModeType     = mediaDrvCtx->m_caps->GetEncodeCodecMode(profile, entrypoint);
    encCtx->codecFunction = mediaDrvCtx->m_caps->GetEncodeCodecFunction(profile, entrypoint, feiFunction);

    if (entrypoint == VAEntrypointEncSliceLP)
    {
        encCtx->bVdencActive  = true;
    }

    //Both dual pipe and LP pipe should support 10bit for below profiles
    // - HEVCMain10 profile
    // - VAProfileVP9Profile2
    // - VAProfileVP9Profile3
    if (profile == VAProfileVP9Profile2 ||
        profile == VAProfileVP9Profile3)
    {
        encCtx->m_encode->m_is10Bit = true;
    }

    if (profile == VAProfileVP9Profile1 ||
        profile == VAProfileVP9Profile3)
    {
        encCtx->m_encode->m_chromaFormat = DdiEncodeBase::yuv444;
    }

    CODECHAL_STANDARD_INFO standardInfo;
    MOS_ZeroMemory(&standardInfo, sizeof(CODECHAL_STANDARD_INFO));
    standardInfo.CodecFunction = encCtx->codecFunction;
    standardInfo.Mode          = encCtx->wModeType;
    Codechal *pCodecHal = CodechalDevice::CreateFactory(
        nullptr,
        &mosCtx,
        &standardInfo,
        nullptr);
    if (pCodecHal == nullptr)
    {
        pCodecHal = CodechalDeviceNext::CreateFactory(
            nullptr,
            &mosCtx,
            &standardInfo,
            nullptr);

        if (nullptr == pCodecHal)
        {
            // add anything necessary here to free the resource
            vaStatus = VA_STATUS_ERROR_ALLOCATION_FAILED;
            DdiEncodeCleanUp(encCtx);
            return vaStatus;
        }
    }

    encCtx->pCodecHal = pCodecHal;

    // Setup some initial data
    encCtx->dworiFrameWidth   = picture_width;
    encCtx->dworiFrameHeight  = picture_height;
    encCtx->wPicWidthInMB     = (uint16_t)(DDI_CODEC_NUM_MACROBLOCKS_WIDTH(picture_width));
    encCtx->wPicHeightInMB    = (uint16_t)(DDI_CODEC_NUM_MACROBLOCKS_HEIGHT(picture_height));
    encCtx->dwFrameWidth      = encCtx->wPicWidthInMB * CODECHAL_MACROBLOCK_WIDTH;
    encCtx->dwFrameHeight     = encCtx->wPicHeightInMB * CODECHAL_MACROBLOCK_HEIGHT;
    //recoder old resolution for dynamic resolution  change
    encCtx->wContextPicWidthInMB  = encCtx->wPicWidthInMB;
    encCtx->wContextPicHeightInMB = encCtx->wPicHeightInMB;
    encCtx->wOriPicWidthInMB      = encCtx->wPicWidthInMB;
    encCtx->wOriPicHeightInMB     = encCtx->wPicHeightInMB;
    encCtx->targetUsage           = TARGETUSAGE_RT_SPEED;
    // Attach PMEDIDA_DRIVER_CONTEXT
    encCtx->pMediaCtx = mediaDrvCtx;

    encCtx->pCpDdiInterface->SetCpFlags(flag);
    encCtx->pCpDdiInterface->SetCpParams(CP_TYPE_NONE, encCtx->m_encode->m_codechalSettings);

    vaStatus = encCtx->m_encode->ContextInitialize(encCtx->m_encode->m_codechalSettings);

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

    MOS_STATUS eStatus = pCodecHal->Allocate(encCtx->m_encode->m_codechalSettings);

    PMOS_INTERFACE osInterface = pCodecHal->GetOsInterface();
    if (osInterface != nullptr                                                       &&
        !osInterface->apoMosEnabled                                                  &&
        MEDIA_IS_SKU(osInterface->pfnGetSkuTable(osInterface), FtrMemoryCompression) &&
        !mediaDrvCtx->pMediaMemDecompState)
    {
        mediaDrvCtx->pMediaMemDecompState =
            static_cast<MediaMemDecompState*>(MmdDevice::CreateFactory(&mosCtx));
    }

    if (eStatus != MOS_STATUS_SUCCESS)
    {
        vaStatus = VA_STATUS_ERROR_ALLOCATION_FAILED;
        DdiEncodeCleanUp(encCtx);
        return vaStatus;
    }

    vaStatus = encCtx->m_encode->InitCompBuffer();
    if (vaStatus != VA_STATUS_SUCCESS)
    {
        vaStatus = VA_STATUS_ERROR_ALLOCATION_FAILED;
        DdiEncodeCleanUp(encCtx);
        return vaStatus;
    }

    // register the render target surfaces for this encoder instance
    // This is a must as driver has the constraint, 127 surfaces per context
    for (int32_t i = 0; i < num_render_targets; i++)
    {
        DDI_MEDIA_SURFACE *surface;

        surface = DdiMedia_GetSurfaceFromVASurfaceID(mediaDrvCtx, render_targets[i]);
        if (nullptr == surface)
        {
            DDI_ASSERTMESSAGE("DDI: invalid render target %d in vpgEncodeCreateContext.", i);
            vaStatus = VA_STATUS_ERROR_INVALID_SURFACE;
            DdiEncodeCleanUp(encCtx);
            return vaStatus;
        }
        encCtx->RTtbl.pRT[i] = surface;
        encCtx->RTtbl.iNumRenderTargets++;
    }

    // convert PDDI_ENCODE_CONTEXT to VAContextID
    DdiMediaUtil_LockMutex(&mediaDrvCtx->EncoderMutex);
    PDDI_MEDIA_VACONTEXT_HEAP_ELEMENT vaContextHeapElmt = DdiMediaUtil_AllocPVAContextFromHeap(mediaDrvCtx->pEncoderCtxHeap);
    if (nullptr == vaContextHeapElmt)
    {
        DdiMediaUtil_UnLockMutex(&mediaDrvCtx->EncoderMutex);
        vaStatus = VA_STATUS_ERROR_MAX_NUM_EXCEEDED;
        DdiEncodeCleanUp(encCtx);
        return vaStatus;
    }

    vaContextHeapElmt->pVaContext = (void*)encCtx;
    mediaDrvCtx->uiNumEncoders++;
    *context = (VAContextID)(vaContextHeapElmt->uiVaContextID + DDI_MEDIA_VACONTEXTID_OFFSET_ENCODER);
    DdiMediaUtil_UnLockMutex(&mediaDrvCtx->EncoderMutex);

    return vaStatus;
}

/*
 * vaDestroyContext - Destroy a context
 * dpy: display
 * context: context to be destroyed
 */
VAStatus DdiEncode_DestroyContext(VADriverContextP ctx, VAContextID context)
{
    DDI_CHK_NULL(ctx, "nullptr ctx", VA_STATUS_ERROR_INVALID_CONTEXT);
    DDI_CHK_NULL(ctx->pDriverData, "nullptr ctx->pDriverData", VA_STATUS_ERROR_INVALID_CONTEXT);

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

    // assume the VAContextID is encoder ID
    PDDI_ENCODE_CONTEXT encCtx  = DdiEncode_GetEncContextFromContextID(ctx, context);
    DDI_CHK_NULL(encCtx, "nullptr encCtx", VA_STATUS_ERROR_INVALID_CONTEXT);
    DDI_CHK_NULL(encCtx->pCodecHal, "nullptr encCtx->pCodecHal", VA_STATUS_ERROR_INVALID_CONTEXT);

    Codechal *codecHal = encCtx->pCodecHal;

    if (nullptr != encCtx->m_encode)
    {
        encCtx->m_encode->FreeCompBuffer();
        if(nullptr != encCtx->m_encode->m_codechalSettings)
        {
            MOS_Delete(encCtx->m_encode->m_codechalSettings);
            encCtx->m_encode->m_codechalSettings = nullptr;
        }
    }

    if (codecHal->GetOsInterface() && codecHal->GetOsInterface()->pOsContext)
    {
        MOS_FreeMemory(codecHal->GetOsInterface()->pOsContext->pPerfData);
        codecHal->GetOsInterface()->pOsContext->pPerfData = nullptr;
    }

    // destroy codechal
    codecHal->Destroy();
    MOS_Delete(codecHal);

    if (encCtx->pCpDdiInterface)
    {
        Delete_DdiCpInterface(encCtx->pCpDdiInterface);
        encCtx->pCpDdiInterface = nullptr;
    }

    if (nullptr != encCtx->m_encode)
    {
        MOS_Delete(encCtx->m_encode);
        encCtx->m_encode = nullptr;
    }

    MOS_FreeMemory(encCtx);
    encCtx = nullptr;

    uint32_t encIndex = (uint32_t)context;
    encIndex &= DDI_MEDIA_MASK_VACONTEXTID;

    DdiMediaUtil_LockMutex(&mediaCtx->EncoderMutex);
    DdiMediaUtil_ReleasePVAContextFromHeap(mediaCtx->pEncoderCtxHeap, encIndex);
    mediaCtx->uiNumEncoders--;
    DdiMediaUtil_UnLockMutex(&mediaCtx->EncoderMutex);

    return VA_STATUS_SUCCESS;
}

/*
 * Creates a buffer for "num_elements" elements of "size" bytes and
 * initalize with "data".
 * if "data" is null, then the contents of the buffer data store
 * are undefined.
 * Basically there are two ways to get buffer data to the server side. One is
 * to call vaCreateBuffer() with a non-null "data", which results the data being
 * copied to the data store on the server side.  A different method that
 * eliminates this copy is to pass null as "data" when calling vaCreateBuffer(),
 * and then use vaMapBuffer() to map the data store from the server side to the
 * client address space for access.
 * Note: image buffers are created by the library, not the client. Please see
 * vaCreateImage on how image buffers are managed.
 */

VAStatus DdiEncode_CreateBuffer(
    VADriverContextP ctx,
    VAContextID      context,
    VABufferType     type,
    uint32_t         size,
    uint32_t         num_elements,
    void            *data,
    VABufferID      *buf_id)
{
    DDI_CHK_NULL(ctx, "nullptr context!", VA_STATUS_ERROR_INVALID_CONTEXT);

    DDI_ENCODE_CONTEXT *encCtx = DdiEncode_GetEncContextFromContextID(ctx, context);
    DDI_CHK_NULL(encCtx, "nullptr encCtx", VA_STATUS_ERROR_INVALID_CONTEXT);
    DDI_CHK_NULL(encCtx->m_encode, "nullptr encCtx->m_encode", VA_STATUS_ERROR_INVALID_CONTEXT);

    VAStatus vaStatus = encCtx->m_encode->CreateBuffer(ctx, type, size, num_elements, data, buf_id);

    return vaStatus;
}

/*
 * Get ready to encode a picture
 */
VAStatus DdiEncode_BeginPicture(
    VADriverContextP ctx,
    VAContextID      context,
    VASurfaceID      render_target)
{
    PERF_UTILITY_AUTO(__FUNCTION__, PERF_ENCODE, PERF_LEVEL_DDI);

    DDI_FUNCTION_ENTER();

    DDI_CHK_NULL(ctx, "nullptr context in vpgEncodeBeginPicture!", VA_STATUS_ERROR_INVALID_CONTEXT);

    // assume the VAContextID is encoder ID
    PDDI_ENCODE_CONTEXT encCtx = DdiEncode_GetEncContextFromContextID(ctx, context);
    DDI_CHK_NULL(encCtx, "nullptr encCtx", VA_STATUS_ERROR_INVALID_CONTEXT);
    DDI_CHK_NULL(encCtx->m_encode, "nullptr encCtx->m_encode", VA_STATUS_ERROR_INVALID_CONTEXT);

    VAStatus vaStatus = encCtx->m_encode->BeginPicture(ctx, context, render_target);
    DDI_FUNCTION_EXIT(vaStatus);
    return vaStatus;
}

/*
 * Send encode buffers to the server.
 * Buffers are automatically destroyed afterwards
 */
VAStatus DdiEncode_RenderPicture(
    VADriverContextP ctx,
    VAContextID      context,
    VABufferID      *buffers,
    int32_t          num_buffers)
{
    VAStatus        vaStatus                  = VA_STATUS_SUCCESS;
    int32_t         numOfBuffers              = num_buffers;
    int32_t         priority                  = 0;
    int32_t         priorityIndexInBuffers    = -1;
    bool            updatePriority            = false;

    PERF_UTILITY_AUTO(__FUNCTION__, PERF_ENCODE, PERF_LEVEL_DDI);

    DDI_FUNCTION_ENTER();

    DDI_CHK_NULL(ctx, "nullptr context in vpgEncodeRenderPicture!", VA_STATUS_ERROR_INVALID_CONTEXT);

    // assume the VAContextID is encoder ID
    PDDI_ENCODE_CONTEXT encCtx = DdiEncode_GetEncContextFromContextID(ctx, context);
    DDI_CHK_NULL(encCtx, "nullptr encCtx", VA_STATUS_ERROR_INVALID_CONTEXT);
    DDI_CHK_NULL(encCtx->m_encode, "nullptr encCtx->m_encode", VA_STATUS_ERROR_INVALID_CONTEXT);

    priorityIndexInBuffers = DdiMedia_GetGpuPriority(ctx, buffers, numOfBuffers, &updatePriority, &priority);
    if (priorityIndexInBuffers != -1)
    {
        if(updatePriority)
        {
            vaStatus = DdiEncode_SetGpuPriority(encCtx, priority);
            if(vaStatus != VA_STATUS_SUCCESS)
                return vaStatus;
        }
        MovePriorityBufferIdToEnd(buffers, priorityIndexInBuffers, numOfBuffers);
        numOfBuffers--;
    }
    if (numOfBuffers == 0)
        return vaStatus;

    vaStatus = encCtx->m_encode->RenderPicture(ctx, context, buffers, numOfBuffers);
    DDI_FUNCTION_EXIT(vaStatus);
    return vaStatus;
}

VAStatus DdiEncode_EndPicture(VADriverContextP ctx, VAContextID context)
{
    PERF_UTILITY_AUTO(__FUNCTION__, PERF_ENCODE, PERF_LEVEL_DDI);

    DDI_FUNCTION_ENTER();

    DDI_CHK_NULL(ctx, "nullptr context in vpgEncodeEndPicture!", VA_STATUS_ERROR_INVALID_CONTEXT);

    // assume the VAContextID is encoder ID
    PDDI_ENCODE_CONTEXT encCtx = DdiEncode_GetEncContextFromContextID(ctx, context);
    DDI_CHK_NULL(encCtx, "nullptr encCtx", VA_STATUS_ERROR_INVALID_CONTEXT);
    DDI_CHK_NULL(encCtx->m_encode, "nullptr encCtx->m_encode", VA_STATUS_ERROR_INVALID_CONTEXT);

    VAStatus vaStatus = encCtx->m_encode->EndPicture(ctx, context);
    DDI_FUNCTION_EXIT(vaStatus);
    return vaStatus;
}

VAStatus DdiEncode_MfeSubmit(
    VADriverContextP    ctx,
    VAMFContextID      mfe_context,
    VAContextID        *contexts,
    int32_t             num_contexts
)
{
    PDDI_MEDIA_CONTEXT mediaCtx               = DdiMedia_GetMediaContext(ctx);
    DDI_CHK_NULL(mediaCtx, "nullptr mediaCtx", VA_STATUS_ERROR_INVALID_CONTEXT);

    uint32_t ctxType                          = DDI_MEDIA_CONTEXT_TYPE_NONE;
    PDDI_ENCODE_MFE_CONTEXT encodeMfeContext  = (PDDI_ENCODE_MFE_CONTEXT)DdiMedia_GetContextFromContextID(ctx, mfe_context, &ctxType);
    DDI_CHK_NULL(encodeMfeContext, "nullptr encodeMfeContext", VA_STATUS_ERROR_INVALID_CONTEXT);

    std::vector<PDDI_ENCODE_CONTEXT>    encodeContexts;
    PDDI_ENCODE_CONTEXT encodeContext = nullptr;
    int32_t validContextNumber        = 0;
    // Set mfe encoder params for this submission
    for (int32_t i = 0; i < num_contexts; i++)
    {
        encodeContext                 = DdiEncode_GetEncContextFromContextID(ctx, contexts[i]);
        DDI_CHK_NULL(encodeContext, "nullptr encodeContext", VA_STATUS_ERROR_INVALID_CONTEXT);
        CodechalEncoderState *encoder = dynamic_cast<CodechalEncoderState *>(encodeContext->pCodecHal);
        DDI_CHK_NULL(encoder, "nullptr codechal encoder", VA_STATUS_ERROR_INVALID_CONTEXT);

        if (!encoder->m_mfeEnabled ||
            encoder->m_mfeEncodeSharedState != encodeMfeContext->mfeEncodeSharedState)
        {
            return VA_STATUS_ERROR_INVALID_CONTEXT;
        }

        // make sure the context has called BeginPicture&RenderPicture&EndPicture
        if (encodeContext->RTtbl.pRT[0] == nullptr
            || encodeContext->dwNumSlices <= 0
            || encodeContext->EncodeParams.pBSBuffer != encodeContext->pbsBuffer)
        {
            return VA_STATUS_ERROR_INVALID_PARAMETER;
        }

        encoder->m_mfeEncodeParams.submitIndex  = i;
        encoder->m_mfeEncodeParams.submitNumber = num_contexts;
        encodeContexts.push_back(encodeContext);
        validContextNumber++;
    }

    CmDevice *device = encodeMfeContext->mfeEncodeSharedState->pCmDev;
    CmTask   *task   = encodeMfeContext->mfeEncodeSharedState->pCmTask;
    CmQueue  *queue  = encodeMfeContext->mfeEncodeSharedState->pCmQueue;
    CodechalEncodeMdfKernelResource *resMbencKernel = encodeMfeContext->mfeEncodeSharedState->resMbencKernel;
    SurfaceIndex *vmeSurface    = encodeMfeContext->mfeEncodeSharedState->vmeSurface;
    SurfaceIndex *commonSurface = encodeMfeContext->mfeEncodeSharedState->commonSurface;


    MOS_ZeroMemory(encodeMfeContext->mfeEncodeSharedState, sizeof(MfeSharedState));

    encodeMfeContext->mfeEncodeSharedState->pCmDev   = device;
    encodeMfeContext->mfeEncodeSharedState->pCmTask  = task;
    encodeMfeContext->mfeEncodeSharedState->pCmQueue = queue;
    encodeMfeContext->mfeEncodeSharedState->resMbencKernel = resMbencKernel;
    encodeMfeContext->mfeEncodeSharedState->vmeSurface     = vmeSurface;
    encodeMfeContext->mfeEncodeSharedState->commonSurface  = commonSurface;

    encodeMfeContext->mfeEncodeSharedState->encoders.clear();

    // Call Enc functions for all the sub contexts
    MOS_STATUS status = MOS_STATUS_SUCCESS;
    for (int32_t i = 0; i < validContextNumber; i++)
    {
        encodeContext  = encodeContexts[i];
        if (encodeContext->vaEntrypoint != VAEntrypointFEI )
        {
            encodeContext->EncodeParams.ExecCodecFunction = CODECHAL_FUNCTION_ENC;
        }
        else
        {
            encodeContext->EncodeParams.ExecCodecFunction = CODECHAL_FUNCTION_FEI_ENC;
        }

        CodechalEncoderState *encoder = dynamic_cast<CodechalEncoderState *>(encodeContext->pCodecHal);
        DDI_CHK_NULL(encoder, "nullptr encoder", VA_STATUS_ERROR_INVALID_CONTEXT);

        encodeMfeContext->mfeEncodeSharedState->encoders.push_back(encoder);

        status = encoder->Execute(&encodeContext->EncodeParams);
        if (MOS_STATUS_SUCCESS != status)
        {
            DDI_ASSERTMESSAGE("DDI:Failed in Execute Enc!");
            return VA_STATUS_ERROR_ENCODING_ERROR;
        }
    }

    // Call Pak functions for all the sub contexts
    for (int32_t i = 0; i < validContextNumber; i++)
    {
        encodeContext  = encodeContexts[i];
        if (encodeContext->vaEntrypoint != VAEntrypointFEI )
        {
            encodeContext->EncodeParams.ExecCodecFunction = CODECHAL_FUNCTION_PAK;
        }
        else
        {
            encodeContext->EncodeParams.ExecCodecFunction = CODECHAL_FUNCTION_FEI_PAK;
        }

        CodechalEncoderState *encoder = dynamic_cast<CodechalEncoderState *>(encodeContext->pCodecHal);
        DDI_CHK_NULL(encoder, "nullptr encoder", VA_STATUS_ERROR_INVALID_CONTEXT);

        status = encoder->Execute(&encodeContext->EncodeParams);
        if (MOS_STATUS_SUCCESS != status)
        {
            DDI_ASSERTMESSAGE("DDI:Failed in Execute Pak!");
            return VA_STATUS_ERROR_ENCODING_ERROR;
        }
    }

    return VA_STATUS_SUCCESS;
}

VAStatus DdiEncode_SetGpuPriority(
    PDDI_ENCODE_CONTEXT encCtx,
    int32_t             priority
)
{
    DDI_CHK_NULL(encCtx, "nullptr encCtx", VA_STATUS_ERROR_INVALID_CONTEXT);

    if(encCtx->pCodecHal != nullptr)
    {
        PMOS_INTERFACE osInterface = encCtx->pCodecHal->GetOsInterface();
        DDI_CHK_NULL(osInterface, "nullptr osInterface.", VA_STATUS_ERROR_ALLOCATION_FAILED);

        //Set Gpu priority for encoder
        osInterface->pfnSetGpuPriority(osInterface, priority);

        //Get the CMRT osInterface of encode
        CodechalEncoderState *encoder = dynamic_cast<CodechalEncoderState *>(encCtx->pCodecHal);
        DDI_CHK_NULL(encoder, "nullptr encoder", VA_STATUS_ERROR_INVALID_CONTEXT);

        if(encoder->m_cmDev != nullptr)
        {
            //Set Gpu priority for CMRT OsInterface
            CmDeviceRTBase *cm_device = dynamic_cast<CmDeviceRTBase *>(encoder->m_cmDev);
            DDI_CHK_NULL(cm_device, "nullptr cm_device", VA_STATUS_ERROR_INVALID_PARAMETER);

            PCM_HAL_STATE cm_hal_state = cm_device->GetHalState();
            if(cm_hal_state->osInterface != nullptr)
                cm_hal_state->osInterface->pfnSetGpuPriority(cm_hal_state->osInterface, priority);
        }
    }

    return VA_STATUS_SUCCESS;
}

