/*
* Copyright (c) 2018, 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     codechal_encode_jpeg_g11.cpp
//! \brief    Defines state class for JPEG encoder.
//!

#include "codechal_encode_jpeg.h"
#include "codechal_encode_jpeg_g11.h"
#if USE_CODECHAL_DEBUG_TOOL
#include "mos_util_user_interface.h"
#endif


CodechalEncodeJpegStateG11::CodechalEncodeJpegStateG11(
        CodechalHwInterface* hwInterface,
        CodechalDebugInterface* debugInterface,
        PCODECHAL_STANDARD_INFO standardInfo): CodechalEncodeJpegState(hwInterface, debugInterface, standardInfo),
    m_sinlgePipeVeState(nullptr)
{
    CODECHAL_ENCODE_FUNCTION_ENTER;

    memset(m_refList, 0, sizeof(m_refList));

    CODECHAL_ENCODE_CHK_NULL_NO_STATUS_RETURN(m_osInterface);
    Mos_CheckVirtualEngineSupported(m_osInterface, false, true);
    Mos_SetVirtualEngineSupported(m_osInterface, true);
}


CodechalEncodeJpegStateG11::~CodechalEncodeJpegStateG11()
{
    if (m_sinlgePipeVeState)
    {
        MOS_FreeMemAndSetNull(m_sinlgePipeVeState);
    }
}

MOS_STATUS CodechalEncodeJpegStateG11::SetAndPopulateVEHintParams(
    PMOS_COMMAND_BUFFER  cmdBuffer)
{
    MOS_STATUS                      eStatus = MOS_STATUS_SUCCESS;

    CODECHAL_ENCODE_FUNCTION_ENTER;

    if (!MOS_VE_SUPPORTED(m_osInterface))
    {
        return eStatus;
    }

    if (!MOS_VE_CTXBASEDSCHEDULING_SUPPORTED(m_osInterface))
    {
        MOS_VIRTUALENGINE_SET_PARAMS  vesetParams;
        MOS_ZeroMemory(&vesetParams, sizeof(vesetParams));
        vesetParams.bNeedSyncWithPrevious = true;
        vesetParams.bSFCInUse = false;
        CODECHAL_ENCODE_CHK_STATUS_RETURN(CodecHalEncodeSinglePipeVE_SetHintParams(m_sinlgePipeVeState, &vesetParams));
    }
    CODECHAL_ENCODE_CHK_STATUS_RETURN(CodecHalEncodeSinglePipeVE_PopulateHintParams(m_sinlgePipeVeState, cmdBuffer, true));

    return eStatus;
}

MOS_STATUS CodechalEncodeJpegStateG11::UserFeatureKeyReport()
{
    MOS_STATUS eStatus = MOS_STATUS_SUCCESS;

    CODECHAL_ENCODE_FUNCTION_ENTER;

    CODECHAL_ENCODE_CHK_STATUS_RETURN(CodechalEncoderState::UserFeatureKeyReport());

#if (_DEBUG || _RELEASE_INTERNAL)

    // VE2.0 Reporting
    CodecHalEncode_WriteKey(__MEDIA_USER_FEATURE_VALUE_ENABLE_ENCODE_VE_CTXSCHEDULING_ID, MOS_VE_CTXBASEDSCHEDULING_SUPPORTED(m_osInterface), m_osInterface->pOsContext);

#endif // _DEBUG || _RELEASE_INTERNAL
    return eStatus;
}

MOS_STATUS CodechalEncodeJpegStateG11::ExecuteSliceLevel()
{
    MOS_STATUS eStatus = MOS_STATUS_SUCCESS;

    CODECHAL_ENCODE_FUNCTION_ENTER;

    MOS_COMMAND_BUFFER cmdBuffer;
    CODECHAL_ENCODE_CHK_STATUS_RETURN(m_osInterface->pfnGetCommandBuffer(m_osInterface, &cmdBuffer, 0));

    if (m_encodeParams.dwNumSlices != 1)
    {
        CODECHAL_ENCODE_ASSERTMESSAGE("JPEG encode only one scan is supported.");
    }

    MOS_SURFACE *surface = &m_rawSurface;

    bool useSingleDefaultQuantTable = (m_jpegQuantMatrixSent == false &&
        ((surface->Format == Format_A8R8G8B8) ||
        (surface->Format == Format_X8R8G8B8) ||
            (surface->Format == Format_A8B8G8R8) ||
            (surface->Format == Format_X8B8G8R8)));

    CodecJpegQuantMatrix *tempJpegQuantMatrix = (CodecJpegQuantMatrix *)MOS_AllocAndZeroMemory(sizeof(CodecJpegQuantMatrix));
    CODECHAL_ENCODE_CHK_NULL_RETURN(tempJpegQuantMatrix);

    static_assert(JPEG_MAX_NUM_QUANT_TABLE_INDEX <= JPEG_MAX_NUM_OF_QUANTMATRIX,
        "access to CodecJpegQuantMatrix is controlled by numQuantTables");

    uint32_t numQuantTables = JPEG_MAX_NUM_QUANT_TABLE_INDEX;
    for (uint32_t scanCount = 0; scanCount < m_encodeParams.dwNumSlices; scanCount++)
    {
        MHW_VDBOX_QM_PARAMS fqmParams;
        MOS_ZeroMemory(&fqmParams, sizeof(fqmParams));

        // set MFX_FQM_STATE
        fqmParams.pJpegQuantMatrix = tempJpegQuantMatrix;

        // For monochrome inputs there will be only 1 quantization table and huffman table sent
        if (m_jpegPicParams->m_inputSurfaceFormat == codechalJpegY8)
        {
            numQuantTables = 1;
            m_encodeParams.dwNumHuffBuffers = 2; //for Y8 only 2 huff tables
        }
        // If there is only 1 quantization table copy over the table to 2nd and 3rd table in JPEG state (used for frame header)
        // OR For RGB input surfaces, if the app does not send quantization tables, then use luma quant table for all 3 components
        else if (m_jpegPicParams->m_numQuantTable == 1 || useSingleDefaultQuantTable)
        {
            for (auto i = 1; i < JPEG_MAX_NUM_QUANT_TABLE_INDEX; i++)
            {
                m_jpegQuantTables->m_quantTable[i].m_precision = m_jpegQuantTables->m_quantTable[0].m_precision;
                m_jpegQuantTables->m_quantTable[i].m_tableID = m_jpegQuantTables->m_quantTable[0].m_tableID;

                eStatus = MOS_SecureMemcpy(&m_jpegQuantTables->m_quantTable[i].m_qm[0], JPEG_NUM_QUANTMATRIX * sizeof(uint16_t),
                    &m_jpegQuantTables->m_quantTable[0].m_qm[0], JPEG_NUM_QUANTMATRIX * sizeof(uint16_t));
                if (eStatus != MOS_STATUS_SUCCESS)
                {
                    CODECHAL_ENCODE_ASSERTMESSAGE("Failed to copy memory.");
                    MOS_SafeFreeMemory(tempJpegQuantMatrix);
                    return eStatus;
                }
            }
        }
        // If there are 2 quantization tables copy over the second table to 3rd table in JPEG state since U and V share the same table (used for frame header)
        else if (m_jpegPicParams->m_numQuantTable == 2)
        {
            m_jpegQuantTables->m_quantTable[2].m_precision = m_jpegQuantTables->m_quantTable[1].m_precision;
            m_jpegQuantTables->m_quantTable[2].m_tableID = m_jpegQuantTables->m_quantTable[1].m_tableID;

            eStatus = MOS_SecureMemcpy(&m_jpegQuantTables->m_quantTable[2].m_qm[0], JPEG_NUM_QUANTMATRIX * sizeof(uint16_t),
                &m_jpegQuantTables->m_quantTable[1].m_qm[0], JPEG_NUM_QUANTMATRIX * sizeof(uint16_t));
            if (eStatus != MOS_STATUS_SUCCESS)
            {
                CODECHAL_ENCODE_ASSERTMESSAGE("Failed to copy memory.");
                MOS_SafeFreeMemory(tempJpegQuantMatrix);
                return eStatus;
            }

        }
        // else 3 quantization tables are sent by the application for non monochrome input formats. In that case, do nothing.

        for (uint32_t i = 0; i < numQuantTables; i++)
        {
            fqmParams.pJpegQuantMatrix->m_jpegQMTableType[i] = m_jpegQuantTables->m_quantTable[i].m_tableID; // Used to distinguish between Y,U,V quantization tables for the same scan

            for (auto j = 0; j < JPEG_NUM_QUANTMATRIX; j++)
            {
                uint32_t k = jpeg_qm_scan_8x8[j];

                // copy over Quant matrix in raster order from zig zag
                fqmParams.pJpegQuantMatrix->m_quantMatrix[i][k] = (uint8_t)m_jpegQuantTables->m_quantTable[i].m_qm[j];
            }
        }

        eStatus = (MOS_STATUS) m_mfxInterface->AddMfxJpegFqmCmd(&cmdBuffer, &fqmParams, numQuantTables);
        if (eStatus != MOS_STATUS_SUCCESS)
        {
            MOS_SafeFreeMemory(tempJpegQuantMatrix);
            CODECHAL_ENCODE_CHK_STATUS_RETURN(eStatus);
        }

        // set MFC_JPEG_HUFF_TABLE - Convert encoded huffman table to actual table for HW
        // We need a different params struct for JPEG Encode Huffman table because JPEG decode huffman table has Bits and codes,
        // whereas JPEG encode huffman table has huffman code lengths and values
        MHW_VDBOX_ENCODE_HUFF_TABLE_PARAMS   huffTableParams[JPEG_MAX_NUM_HUFF_TABLE_INDEX];
        for (uint32_t i = 0; i < m_encodeParams.dwNumHuffBuffers; i++)
        {
            CodechalEncodeJpegHuffTable huffmanTable;// intermediate table for each AC/DC component which will be copied to huffTableParams
            MOS_ZeroMemory(&huffmanTable, sizeof(huffmanTable));

            eStatus = (MOS_STATUS) ConvertHuffDataToTable(m_jpegHuffmanTable->m_huffmanData[i], &huffmanTable);
            if (eStatus != MOS_STATUS_SUCCESS)
            {
                MOS_SafeFreeMemory(tempJpegQuantMatrix);
                CODECHAL_ENCODE_CHK_STATUS_RETURN(eStatus);
            }

            huffTableParams[m_jpegHuffmanTable->m_huffmanData[i].m_tableID].HuffTableID = m_jpegHuffmanTable->m_huffmanData[i].m_tableID;

            if (m_jpegHuffmanTable->m_huffmanData[i].m_tableClass == 0) // DC table
            {
                eStatus = (MOS_STATUS) MOS_SecureMemcpy(
                    huffTableParams[m_jpegHuffmanTable->m_huffmanData[i].m_tableID].pDCCodeValues,
                    JPEG_NUM_HUFF_TABLE_DC_HUFFVAL * sizeof(uint16_t),
                    &huffmanTable.m_huffCode,
                    JPEG_NUM_HUFF_TABLE_DC_HUFFVAL * sizeof(uint16_t));
                if (eStatus != MOS_STATUS_SUCCESS)
                {
                    MOS_SafeFreeMemory(tempJpegQuantMatrix);
                    CODECHAL_ENCODE_CHK_STATUS_RETURN(eStatus);
                }

                eStatus = (MOS_STATUS) MOS_SecureMemcpy(huffTableParams[m_jpegHuffmanTable->m_huffmanData[i].m_tableID].pDCCodeLength,
                    JPEG_NUM_HUFF_TABLE_DC_HUFFVAL * sizeof(uint8_t),
                    &huffmanTable.m_huffSize,
                    JPEG_NUM_HUFF_TABLE_DC_HUFFVAL * sizeof(uint8_t));
                if (eStatus != MOS_STATUS_SUCCESS)
                {
                    MOS_SafeFreeMemory(tempJpegQuantMatrix);
                    CODECHAL_ENCODE_CHK_STATUS_RETURN(eStatus);
                }
            }
            else // AC Table
            {
                eStatus = (MOS_STATUS) MOS_SecureMemcpy(huffTableParams[m_jpegHuffmanTable->m_huffmanData[i].m_tableID].pACCodeValues,
                    JPEG_NUM_HUFF_TABLE_AC_HUFFVAL * sizeof(uint16_t),
                    &huffmanTable.m_huffCode,
                    JPEG_NUM_HUFF_TABLE_AC_HUFFVAL * sizeof(uint16_t));
                if (eStatus != MOS_STATUS_SUCCESS)
                {
                    MOS_SafeFreeMemory(tempJpegQuantMatrix);
                    CODECHAL_ENCODE_CHK_STATUS_RETURN(eStatus);
                }

                eStatus = (MOS_STATUS) MOS_SecureMemcpy(huffTableParams[m_jpegHuffmanTable->m_huffmanData[i].m_tableID].pACCodeLength,
                    JPEG_NUM_HUFF_TABLE_AC_HUFFVAL * sizeof(uint8_t),
                    &huffmanTable.m_huffSize,
                    JPEG_NUM_HUFF_TABLE_AC_HUFFVAL * sizeof(uint8_t));
                if (eStatus != MOS_STATUS_SUCCESS)
                {
                    MOS_SafeFreeMemory(tempJpegQuantMatrix);
                    CODECHAL_ENCODE_CHK_STATUS_RETURN(eStatus);
                }
            }
        }

        // Send 2 huffman table commands - 1 for Luma and one for chroma for non-monchrome input formats
        // If only one table is sent by the app (2 buffers), send the same table for Luma and chroma
        bool repeatHuffTable = false;
        if ((m_encodeParams.dwNumHuffBuffers / 2 < JPEG_MAX_NUM_HUFF_TABLE_INDEX)
            && (m_jpegPicParams->m_inputSurfaceFormat != codechalJpegY8))
        {
            repeatHuffTable = true;

            // Copy over huffman data to the other two data buffers for JPEG picture header
            for (uint32_t i = 0; i < m_encodeParams.dwNumHuffBuffers; i++)
            {
                m_jpegHuffmanTable->m_huffmanData[i + 2].m_tableClass = m_jpegHuffmanTable->m_huffmanData[i].m_tableClass;
                m_jpegHuffmanTable->m_huffmanData[i + 2].m_tableID = m_jpegHuffmanTable->m_huffmanData[i].m_tableID;

                eStatus = MOS_SecureMemcpy(&m_jpegHuffmanTable->m_huffmanData[i + 2].m_bits[0],
                    sizeof(uint8_t) * JPEG_NUM_HUFF_TABLE_AC_BITS,
                    &m_jpegHuffmanTable->m_huffmanData[i].m_bits[0],
                    sizeof(uint8_t) * JPEG_NUM_HUFF_TABLE_AC_BITS);
                if (eStatus != MOS_STATUS_SUCCESS)
                {
                    CODECHAL_ENCODE_ASSERTMESSAGE("Failed to copy memory.");
                    MOS_SafeFreeMemory(tempJpegQuantMatrix);
                    return eStatus;
                }

                eStatus = MOS_SecureMemcpy(&m_jpegHuffmanTable->m_huffmanData[i + 2].m_huffVal[0],
                    sizeof(uint8_t) * JPEG_NUM_HUFF_TABLE_AC_HUFFVAL,
                    &m_jpegHuffmanTable->m_huffmanData[i].m_huffVal[0],
                    sizeof(uint8_t) * JPEG_NUM_HUFF_TABLE_AC_HUFFVAL);
                if (eStatus != MOS_STATUS_SUCCESS)
                {
                    CODECHAL_ENCODE_ASSERTMESSAGE("Failed to copy memory.");
                    MOS_SafeFreeMemory(tempJpegQuantMatrix);
                    return eStatus;
                }
            }
        }

        // the number of huffman commands is half of the huffman buffers sent by the app, since AC and DC buffers are combined into one command
        for (uint32_t i = 0; i < m_encodeParams.dwNumHuffBuffers / 2; i++)
        {
            if (repeatHuffTable)
            {
                eStatus = (MOS_STATUS) (m_mfxInterface->AddMfcJpegHuffTableStateCmd(&cmdBuffer, &huffTableParams[i]));
                if (eStatus != MOS_STATUS_SUCCESS)
                {
                    MOS_SafeFreeMemory(tempJpegQuantMatrix);
                    CODECHAL_ENCODE_CHK_STATUS_RETURN(eStatus);
                }
            }

            eStatus = (MOS_STATUS) m_mfxInterface->AddMfcJpegHuffTableStateCmd(&cmdBuffer, &huffTableParams[i]);
            if (eStatus != MOS_STATUS_SUCCESS)
            {
                MOS_SafeFreeMemory(tempJpegQuantMatrix);
                CODECHAL_ENCODE_CHK_STATUS_RETURN(eStatus);
            }
        }

        // set MFC_JPEG_SCAN_OBJECT
        MhwVdboxJpegScanParams scanObjectParams;
        scanObjectParams.mode = m_mode;
        scanObjectParams.inputSurfaceFormat = (CodecEncodeJpegInputSurfaceFormat)m_jpegPicParams->m_inputSurfaceFormat;
        scanObjectParams.dwPicWidth = m_jpegPicParams->m_picWidth;
        scanObjectParams.dwPicHeight = m_jpegPicParams->m_picHeight;
        scanObjectParams.pJpegEncodeScanParams = m_jpegScanParams;

        eStatus = (MOS_STATUS) m_mfxInterface->AddMfcJpegScanObjCmd(&cmdBuffer, &scanObjectParams);
        if (eStatus != MOS_STATUS_SUCCESS)
        {
            MOS_SafeFreeMemory(tempJpegQuantMatrix);
            CODECHAL_ENCODE_CHK_STATUS_RETURN(eStatus);
        }

        // set MFC_JPEG_PAK_INSERT_OBJECT
        MHW_VDBOX_PAK_INSERT_PARAMS pakInsertObjectParams;
        MOS_ZeroMemory(&pakInsertObjectParams, sizeof(pakInsertObjectParams));

        // The largest component written through the MFC_JPEG_PAK_INSERT_OBJECT command is Huffman table
        pakInsertObjectParams.pBsBuffer = (BSBuffer *)MOS_AllocAndZeroMemory(sizeof(CodechalEncodeJpegFrameHeader));
        if (pakInsertObjectParams.pBsBuffer == nullptr)
        {
            MOS_SafeFreeMemory(tempJpegQuantMatrix);
            CODECHAL_ENCODE_CHK_NULL_RETURN(nullptr);
        }

        if(!m_fullHeaderInAppData)
        {
            // Add SOI (0xFFD8) (only if it was sent by the application)
            eStatus = (MOS_STATUS)PackSOI(pakInsertObjectParams.pBsBuffer);
            if (eStatus != MOS_STATUS_SUCCESS)
            {
                MOS_SafeFreeMemory(pakInsertObjectParams.pBsBuffer->pBase);
                MOS_SafeFreeMemory(pakInsertObjectParams.pBsBuffer);
                MOS_SafeFreeMemory(tempJpegQuantMatrix);
                CODECHAL_ENCODE_CHK_STATUS_RETURN(eStatus);
            }
            pakInsertObjectParams.dwOffset = 0;
            pakInsertObjectParams.dwBitSize = pakInsertObjectParams.pBsBuffer->BufferSize;
            pakInsertObjectParams.bLastHeader = false;
            pakInsertObjectParams.bEndOfSlice = false;
            pakInsertObjectParams.bResetBitstreamStartingPos = 1; // from discussion with HW Architect
            eStatus = (MOS_STATUS) m_mfxInterface->AddMfxPakInsertObject(&cmdBuffer, nullptr, &pakInsertObjectParams);
            if (eStatus != MOS_STATUS_SUCCESS)
            {
                MOS_SafeFreeMemory(pakInsertObjectParams.pBsBuffer->pBase);
                MOS_SafeFreeMemory(pakInsertObjectParams.pBsBuffer);
                MOS_SafeFreeMemory(tempJpegQuantMatrix);
                CODECHAL_ENCODE_CHK_STATUS_RETURN(eStatus);
            }
            MOS_FreeMemory(pakInsertObjectParams.pBsBuffer->pBase);
        }

        // Add Application data if it was sent by application
        if (m_applicationData != nullptr)
        {
            uint8_t* appDataChunk = nullptr;
            uint32_t appDataChunkSize = m_appDataSize;

            // We can write a maximum of 1020 words per command, so if the size of the app data is
            // more than 1020 we need to send multiple commands for writing out app data
            uint32_t numAppDataCmdsNeeded = 1;
            uint32_t appDataCmdSizeResidue = 0;
            if (m_appDataSize > 1020)
            {
                numAppDataCmdsNeeded = m_appDataSize / 1020;
                appDataCmdSizeResidue = m_appDataSize % 1020;

                appDataChunkSize = 1020;
            }

            appDataChunk = (uint8_t*)MOS_AllocAndZeroMemory(appDataChunkSize);
            if (appDataChunk == nullptr)
            {
                MOS_SafeFreeMemory(pakInsertObjectParams.pBsBuffer->pBase);
                MOS_SafeFreeMemory(pakInsertObjectParams.pBsBuffer);
                MOS_SafeFreeMemory(tempJpegQuantMatrix);
                CODECHAL_ENCODE_CHK_NULL_RETURN(nullptr);
            }

            for (uint32_t i = 0; i < numAppDataCmdsNeeded; i++)
            {
                uint8_t *copyAddress = (uint8_t*)(m_applicationData)+(i * appDataChunkSize);

                MOS_SecureMemcpy(appDataChunk, appDataChunkSize,
                    copyAddress, appDataChunkSize);

                eStatus = (MOS_STATUS)PackApplicationData(pakInsertObjectParams.pBsBuffer, appDataChunk, appDataChunkSize);
                if (eStatus != MOS_STATUS_SUCCESS)
                {
                    MOS_SafeFreeMemory(pakInsertObjectParams.pBsBuffer->pBase);
                    MOS_SafeFreeMemory(pakInsertObjectParams.pBsBuffer);
                    MOS_SafeFreeMemory(tempJpegQuantMatrix);
                    MOS_SafeFreeMemory(appDataChunk);
                    CODECHAL_ENCODE_CHK_STATUS_RETURN(eStatus);
                }
                pakInsertObjectParams.dwOffset = 0;
                pakInsertObjectParams.dwBitSize = pakInsertObjectParams.pBsBuffer->BufferSize;
                //if full header is included in application data, it will be the last header to insert and last chunk of it should be marked with EndOfSlice
                if ((appDataCmdSizeResidue == 0) && m_fullHeaderInAppData && (i == numAppDataCmdsNeeded - 1))
                {
                    pakInsertObjectParams.bLastHeader = true;
                    pakInsertObjectParams.bEndOfSlice = true;
                }
                else
                {
                    pakInsertObjectParams.bLastHeader = false;
                    pakInsertObjectParams.bEndOfSlice = false;
                }
                pakInsertObjectParams.bResetBitstreamStartingPos = 1; // from discussion with HW Architect
                eStatus = (MOS_STATUS)m_mfxInterface->AddMfxPakInsertObject(&cmdBuffer, nullptr,
                    &pakInsertObjectParams);
                if (eStatus != MOS_STATUS_SUCCESS)
                {
                    MOS_SafeFreeMemory(pakInsertObjectParams.pBsBuffer->pBase);
                    MOS_SafeFreeMemory(pakInsertObjectParams.pBsBuffer);
                    MOS_SafeFreeMemory(tempJpegQuantMatrix);
                    MOS_SafeFreeMemory(appDataChunk);
                    CODECHAL_ENCODE_CHK_STATUS_RETURN(eStatus);
                }
            }

            if (appDataCmdSizeResidue != 0)
            {
                uint8_t* lastAddress = (uint8_t*)(m_applicationData)+(numAppDataCmdsNeeded * appDataChunkSize);
                appDataChunkSize = appDataCmdSizeResidue;

                MOS_SecureMemcpy(appDataChunk, appDataChunkSize,
                    lastAddress,
                    appDataChunkSize);

                eStatus = (MOS_STATUS)PackApplicationData(pakInsertObjectParams.pBsBuffer, appDataChunk, appDataCmdSizeResidue);
                if (eStatus != MOS_STATUS_SUCCESS)
                {
                    MOS_SafeFreeMemory(pakInsertObjectParams.pBsBuffer->pBase);
                    MOS_SafeFreeMemory(pakInsertObjectParams.pBsBuffer);
                    MOS_SafeFreeMemory(tempJpegQuantMatrix);
                    MOS_SafeFreeMemory(appDataChunk);
                    CODECHAL_ENCODE_CHK_STATUS_RETURN(eStatus);
                }
                pakInsertObjectParams.dwOffset = 0;
                pakInsertObjectParams.dwBitSize = pakInsertObjectParams.pBsBuffer->BufferSize;
                if(m_fullHeaderInAppData)
                {
                    pakInsertObjectParams.bLastHeader = true;
                    pakInsertObjectParams.bEndOfSlice = true;
                }
                else
                {
                    pakInsertObjectParams.bLastHeader = false;
                    pakInsertObjectParams.bEndOfSlice = false;
                }
                pakInsertObjectParams.bResetBitstreamStartingPos = 1; // from discussion with HW Architect
                eStatus = (MOS_STATUS)m_mfxInterface->AddMfxPakInsertObject(&cmdBuffer, nullptr,
                    &pakInsertObjectParams);
                if (eStatus != MOS_STATUS_SUCCESS)
                {
                    MOS_SafeFreeMemory(pakInsertObjectParams.pBsBuffer->pBase);
                    MOS_SafeFreeMemory(pakInsertObjectParams.pBsBuffer);
                    MOS_SafeFreeMemory(tempJpegQuantMatrix);
                    MOS_SafeFreeMemory(appDataChunk);
                    CODECHAL_ENCODE_CHK_STATUS_RETURN(eStatus);
                }
            }

            MOS_FreeMemory(appDataChunk);
        }

        if(!m_fullHeaderInAppData)
        {
        // Add Quant Table for Y
        eStatus = (MOS_STATUS)PackQuantTable(pakInsertObjectParams.pBsBuffer, jpegComponentY);
        if (eStatus != MOS_STATUS_SUCCESS)
        {
            MOS_SafeFreeMemory(pakInsertObjectParams.pBsBuffer->pBase);
            MOS_SafeFreeMemory(pakInsertObjectParams.pBsBuffer);
            MOS_SafeFreeMemory(tempJpegQuantMatrix);
            CODECHAL_ENCODE_CHK_STATUS_RETURN(eStatus);
        }

        pakInsertObjectParams.dwOffset = 0;
        pakInsertObjectParams.dwBitSize = pakInsertObjectParams.pBsBuffer->BufferSize;
        pakInsertObjectParams.bLastHeader = false;
        pakInsertObjectParams.bEndOfSlice = false;
        pakInsertObjectParams.bResetBitstreamStartingPos = 1; // from discussion with HW Architect
        eStatus = (MOS_STATUS)m_mfxInterface->AddMfxPakInsertObject(&cmdBuffer, nullptr,
            &pakInsertObjectParams);
        if (eStatus != MOS_STATUS_SUCCESS)
        {
            MOS_SafeFreeMemory(pakInsertObjectParams.pBsBuffer->pBase);
            MOS_SafeFreeMemory(pakInsertObjectParams.pBsBuffer);
            MOS_SafeFreeMemory(tempJpegQuantMatrix);
            CODECHAL_ENCODE_CHK_STATUS_RETURN(eStatus);
        }
        MOS_FreeMemory(pakInsertObjectParams.pBsBuffer->pBase);

        if (!useSingleDefaultQuantTable)
        {
            // Since there is no U and V in monochrome format, donot add Quantization table header for U and V components
            if (m_jpegPicParams->m_inputSurfaceFormat != codechalJpegY8)
            {
                // Add quant table for U
                eStatus = (MOS_STATUS)PackQuantTable(pakInsertObjectParams.pBsBuffer, jpegComponentU);
                if (eStatus != MOS_STATUS_SUCCESS)
                {
                    MOS_SafeFreeMemory(pakInsertObjectParams.pBsBuffer->pBase);
                    MOS_SafeFreeMemory(pakInsertObjectParams.pBsBuffer);
                    MOS_SafeFreeMemory(tempJpegQuantMatrix);
                    CODECHAL_ENCODE_CHK_STATUS_RETURN(eStatus);
                }
                pakInsertObjectParams.dwOffset = 0;
                pakInsertObjectParams.dwBitSize = pakInsertObjectParams.pBsBuffer->BufferSize;
                pakInsertObjectParams.bLastHeader = false;
                pakInsertObjectParams.bEndOfSlice = false;
                pakInsertObjectParams.bResetBitstreamStartingPos = 1; // from discussion with HW Architect
                eStatus = (MOS_STATUS)m_mfxInterface->AddMfxPakInsertObject(&cmdBuffer, nullptr,
                    &pakInsertObjectParams);
                if (eStatus != MOS_STATUS_SUCCESS)
                {
                    MOS_SafeFreeMemory(pakInsertObjectParams.pBsBuffer->pBase);
                    MOS_SafeFreeMemory(pakInsertObjectParams.pBsBuffer);
                    MOS_SafeFreeMemory(tempJpegQuantMatrix);
                    CODECHAL_ENCODE_CHK_STATUS_RETURN(eStatus);
                }
                MOS_FreeMemory(pakInsertObjectParams.pBsBuffer->pBase);

                // Add quant table for V
                eStatus = (MOS_STATUS)PackQuantTable(pakInsertObjectParams.pBsBuffer, jpegComponentV);
                if (eStatus != MOS_STATUS_SUCCESS)
                {
                    MOS_SafeFreeMemory(pakInsertObjectParams.pBsBuffer->pBase);
                    MOS_SafeFreeMemory(pakInsertObjectParams.pBsBuffer);
                    MOS_SafeFreeMemory(tempJpegQuantMatrix);
                    CODECHAL_ENCODE_CHK_STATUS_RETURN(eStatus);
                }
                pakInsertObjectParams.dwOffset = 0;
                pakInsertObjectParams.dwBitSize = pakInsertObjectParams.pBsBuffer->BufferSize;
                pakInsertObjectParams.bLastHeader = false;
                pakInsertObjectParams.bEndOfSlice = false;
                pakInsertObjectParams.bResetBitstreamStartingPos = 1; // from discussion with HW Architect
                eStatus = (MOS_STATUS)m_mfxInterface->AddMfxPakInsertObject(&cmdBuffer, nullptr,
                    &pakInsertObjectParams);
                if (eStatus != MOS_STATUS_SUCCESS)
                {
                    MOS_SafeFreeMemory(pakInsertObjectParams.pBsBuffer->pBase);
                    MOS_SafeFreeMemory(pakInsertObjectParams.pBsBuffer);
                    MOS_SafeFreeMemory(tempJpegQuantMatrix);
                    CODECHAL_ENCODE_CHK_STATUS_RETURN(eStatus);
                }
                MOS_FreeMemory(pakInsertObjectParams.pBsBuffer->pBase);
            }
        }

        // Add Frame Header
        eStatus = (MOS_STATUS)PackFrameHeader(pakInsertObjectParams.pBsBuffer, useSingleDefaultQuantTable);
        if (eStatus != MOS_STATUS_SUCCESS)
        {
            MOS_SafeFreeMemory(pakInsertObjectParams.pBsBuffer->pBase);
            MOS_SafeFreeMemory(pakInsertObjectParams.pBsBuffer);
            MOS_SafeFreeMemory(tempJpegQuantMatrix);
            CODECHAL_ENCODE_CHK_STATUS_RETURN(eStatus);
        }
        pakInsertObjectParams.dwOffset = 0;
        pakInsertObjectParams.dwBitSize = pakInsertObjectParams.pBsBuffer->BufferSize;
        pakInsertObjectParams.bLastHeader = false;
        pakInsertObjectParams.bEndOfSlice = false;
        pakInsertObjectParams.bResetBitstreamStartingPos = 1; // from discussion with HW Architect
        eStatus = (MOS_STATUS)m_mfxInterface->AddMfxPakInsertObject(&cmdBuffer, nullptr,
            &pakInsertObjectParams);
        if (eStatus != MOS_STATUS_SUCCESS)
        {
            MOS_SafeFreeMemory(pakInsertObjectParams.pBsBuffer->pBase);
            MOS_SafeFreeMemory(pakInsertObjectParams.pBsBuffer);
            MOS_SafeFreeMemory(tempJpegQuantMatrix);
            CODECHAL_ENCODE_CHK_STATUS_RETURN(eStatus);
        }
        MOS_FreeMemory(pakInsertObjectParams.pBsBuffer->pBase);

        // Add Huffman Table for Y - DC table, Y- AC table, U/V - DC table, U/V - AC table
        for (uint32_t i = 0; i < m_encodeParams.dwNumHuffBuffers; i++)
        {
            eStatus = (MOS_STATUS)PackHuffmanTable(pakInsertObjectParams.pBsBuffer, i);
            if (eStatus != MOS_STATUS_SUCCESS)
            {
                MOS_SafeFreeMemory(pakInsertObjectParams.pBsBuffer->pBase);
                MOS_SafeFreeMemory(pakInsertObjectParams.pBsBuffer);
                MOS_SafeFreeMemory(tempJpegQuantMatrix);
                CODECHAL_ENCODE_CHK_STATUS_RETURN(eStatus);
            }
            pakInsertObjectParams.dwOffset = 0;
            pakInsertObjectParams.dwBitSize = pakInsertObjectParams.pBsBuffer->BufferSize;
            pakInsertObjectParams.bLastHeader = false;
            pakInsertObjectParams.bEndOfSlice = false;
            pakInsertObjectParams.bResetBitstreamStartingPos = 1; // from discussion with HW Architect
            eStatus = (MOS_STATUS)m_mfxInterface->AddMfxPakInsertObject(&cmdBuffer, nullptr,
                &pakInsertObjectParams);
            if (eStatus != MOS_STATUS_SUCCESS)
            {
                MOS_SafeFreeMemory(pakInsertObjectParams.pBsBuffer->pBase);
                MOS_SafeFreeMemory(pakInsertObjectParams.pBsBuffer);
                MOS_SafeFreeMemory(tempJpegQuantMatrix);
                CODECHAL_ENCODE_CHK_STATUS_RETURN(eStatus);
            }
            MOS_FreeMemory(pakInsertObjectParams.pBsBuffer->pBase);
        }

        // Restart Interval - Add only if the restart interval is not zero
        if (m_jpegScanParams->m_restartInterval != 0)
        {
            eStatus = (MOS_STATUS)PackRestartInterval(pakInsertObjectParams.pBsBuffer);
            if (eStatus != MOS_STATUS_SUCCESS)
            {
                MOS_SafeFreeMemory(pakInsertObjectParams.pBsBuffer->pBase);
                MOS_SafeFreeMemory(pakInsertObjectParams.pBsBuffer);
                MOS_SafeFreeMemory(tempJpegQuantMatrix);
                CODECHAL_ENCODE_CHK_STATUS_RETURN(eStatus);
            }
            pakInsertObjectParams.dwOffset = 0;
            pakInsertObjectParams.dwBitSize = pakInsertObjectParams.pBsBuffer->BufferSize;
            pakInsertObjectParams.bLastHeader = false;
            pakInsertObjectParams.bEndOfSlice = false;
            pakInsertObjectParams.bResetBitstreamStartingPos = 1; // from discussion with HW Architect
            eStatus = (MOS_STATUS)m_mfxInterface->AddMfxPakInsertObject(&cmdBuffer, nullptr,
                &pakInsertObjectParams);
            if (eStatus != MOS_STATUS_SUCCESS)
            {
                MOS_SafeFreeMemory(pakInsertObjectParams.pBsBuffer->pBase);
                MOS_SafeFreeMemory(pakInsertObjectParams.pBsBuffer);
                MOS_SafeFreeMemory(tempJpegQuantMatrix);
                CODECHAL_ENCODE_CHK_STATUS_RETURN(eStatus);
            }
            MOS_FreeMemory(pakInsertObjectParams.pBsBuffer->pBase);
        }

        // Add scan header
        eStatus = (MOS_STATUS)PackScanHeader(pakInsertObjectParams.pBsBuffer);
        if (eStatus != MOS_STATUS_SUCCESS)
        {
            MOS_SafeFreeMemory(pakInsertObjectParams.pBsBuffer->pBase);
            MOS_SafeFreeMemory(pakInsertObjectParams.pBsBuffer);
            MOS_SafeFreeMemory(tempJpegQuantMatrix);
            CODECHAL_ENCODE_CHK_STATUS_RETURN(eStatus);
        }
        pakInsertObjectParams.dwOffset = 0;
        pakInsertObjectParams.dwBitSize = pakInsertObjectParams.pBsBuffer->BufferSize;
        pakInsertObjectParams.bLastHeader = true;
        pakInsertObjectParams.bEndOfSlice = true;
        pakInsertObjectParams.bResetBitstreamStartingPos = 1; // from discussion with HW Architect
        eStatus = (MOS_STATUS)m_mfxInterface->AddMfxPakInsertObject(&cmdBuffer, nullptr,
            &pakInsertObjectParams);
        if (eStatus != MOS_STATUS_SUCCESS)
        {
            MOS_SafeFreeMemory(pakInsertObjectParams.pBsBuffer->pBase);
            MOS_SafeFreeMemory(pakInsertObjectParams.pBsBuffer);
            MOS_SafeFreeMemory(tempJpegQuantMatrix);
            CODECHAL_ENCODE_CHK_STATUS_RETURN(eStatus);
        }
        MOS_FreeMemory(pakInsertObjectParams.pBsBuffer->pBase);
        }
        MOS_FreeMemory(pakInsertObjectParams.pBsBuffer);
    }

    eStatus = ReadMfcStatus(&cmdBuffer);
    if (eStatus != MOS_STATUS_SUCCESS)
    {
        MOS_SafeFreeMemory(tempJpegQuantMatrix);
        CODECHAL_ENCODE_CHK_STATUS_RETURN(eStatus);
    }

    eStatus = EndStatusReport(&cmdBuffer, CODECHAL_NUM_MEDIA_STATES);
    if (eStatus != MOS_STATUS_SUCCESS)
    {
        MOS_SafeFreeMemory(tempJpegQuantMatrix);
        CODECHAL_ENCODE_CHK_STATUS_RETURN(eStatus);
    }

    eStatus = m_miInterface->AddMiBatchBufferEnd(&cmdBuffer, nullptr);
    if (eStatus != MOS_STATUS_SUCCESS)
    {
        MOS_SafeFreeMemory(tempJpegQuantMatrix);
        CODECHAL_ENCODE_CHK_STATUS_RETURN(eStatus);
    }

    std::string pakPassName = "PAK_PASS" + std::to_string(static_cast<uint32_t>(m_currPass));
    CODECHAL_DEBUG_TOOL(
        eStatus = m_debugInterface->DumpCmdBuffer(
            &cmdBuffer,
            CODECHAL_NUM_MEDIA_STATES,
            pakPassName.data());
        if (eStatus != MOS_STATUS_SUCCESS)
        {
            MOS_SafeFreeMemory(tempJpegQuantMatrix);
            CODECHAL_ENCODE_CHK_STATUS_RETURN(eStatus);
        }

    //CODECHAL_ENCODE_CHK_STATUS_RETURN(CodecHal_DbgReplaceAllCommands(
    //    m_debugInterface,
    //    &cmdBuffer));
    )

        m_osInterface->pfnReturnCommandBuffer(m_osInterface, &cmdBuffer, 0);

    eStatus = SetAndPopulateVEHintParams(&cmdBuffer);
    if (eStatus != MOS_STATUS_SUCCESS)
    {
        MOS_SafeFreeMemory(tempJpegQuantMatrix);
        CODECHAL_ENCODE_CHK_STATUS_RETURN(eStatus);
    }
    eStatus = m_osInterface->pfnSubmitCommandBuffer(m_osInterface, &cmdBuffer, m_renderContextUsesNullHw);
    if (eStatus != MOS_STATUS_SUCCESS)
    {
        MOS_SafeFreeMemory(tempJpegQuantMatrix);
        CODECHAL_ENCODE_CHK_STATUS_RETURN(eStatus);
    }

    if (tempJpegQuantMatrix != nullptr)
    {
        MOS_FreeMemory(tempJpegQuantMatrix);
        tempJpegQuantMatrix = nullptr;
    }

    return eStatus;
}

MOS_STATUS CodechalEncodeJpegStateG11::Initialize(CodechalSetting  *settings)
{
    MOS_STATUS eStatus = MOS_STATUS_SUCCESS;

    CODECHAL_ENCODE_FUNCTION_ENTER;

    CODECHAL_ENCODE_CHK_NULL_RETURN(m_osInterface);
    CODECHAL_ENCODE_CHK_NULL_RETURN(m_miInterface);
    CODECHAL_ENCODE_CHK_NULL_RETURN(settings);

    CODECHAL_ENCODE_CHK_STATUS_RETURN(CodechalEncoderState::Initialize(settings));

    // Picture Level Commands
    CODECHAL_ENCODE_CHK_STATUS_RETURN(
        m_hwInterface->GetMfxStateCommandsDataSize(
            CODECHAL_ENCODE_MODE_JPEG,
            &m_pictureStatesSize,
            &m_picturePatchListSize,
            0));

    // Slice Level Commands (cannot be placed in 2nd level batch)
    CODECHAL_ENCODE_CHK_STATUS_RETURN(
        m_hwInterface->GetMfxPrimitiveCommandsDataSize(
            CODECHAL_ENCODE_MODE_JPEG,
            &m_sliceStatesSize,
            &m_slicePatchListSize,
            0));

    if (MOS_VE_SUPPORTED(m_osInterface))
    {
        m_sinlgePipeVeState = (PCODECHAL_ENCODE_SINGLEPIPE_VIRTUALENGINE_STATE)MOS_AllocAndZeroMemory(sizeof(CODECHAL_ENCODE_SINGLEPIPE_VIRTUALENGINE_STATE));
        CODECHAL_ENCODE_CHK_NULL_RETURN(m_sinlgePipeVeState);
        eStatus = (MOS_STATUS) CodecHalEncodeSinglePipeVE_InitInterface(m_hwInterface, m_sinlgePipeVeState);
        if (eStatus != MOS_STATUS_SUCCESS)
        {
            MOS_SafeFreeMemory(m_sinlgePipeVeState);
            CODECHAL_ENCODE_CHK_STATUS_RETURN(eStatus);
        }
    }

    return eStatus;
}

MOS_STATUS CodechalEncodeJpegStateG11::SetGpuCtxCreatOption()
{
    MOS_STATUS eStatus = MOS_STATUS_SUCCESS;

    CODECHAL_ENCODE_FUNCTION_ENTER;

    if (!MOS_VE_CTXBASEDSCHEDULING_SUPPORTED(m_osInterface))
    {
        CODECHAL_ENCODE_CHK_STATUS_RETURN(CodechalEncoderState::SetGpuCtxCreatOption());
    }
    else
    {
        m_gpuCtxCreatOpt = MOS_New(MOS_GPUCTX_CREATOPTIONS_ENHANCED);
        CODECHAL_ENCODE_CHK_NULL_RETURN(m_gpuCtxCreatOpt);

        CODECHAL_ENCODE_CHK_STATUS_RETURN(CodecHalEncodeSinglePipeVE_ConstructParmsForGpuCtxCreation(
            m_sinlgePipeVeState,
            (PMOS_GPUCTX_CREATOPTIONS_ENHANCED)m_gpuCtxCreatOpt));
    }

    return eStatus;
}

