/* ------------------------------------------------------------------
 * Copyright (C) 1998-2010 PacketVideo
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 * -------------------------------------------------------------------
 */

#include "avc_enc.h"

#if PROFILING_ON
#include "oscl_tickcount.h"
#endif

#define MAX_SUPPORTED_LAYER 1

/* global static functions */

void CbAvcEncDebugLog_OMX(uint32 *userData, AVCLogType type, char *string1, int val1, int val2)
{
    OSCL_UNUSED_ARG(userData);
    OSCL_UNUSED_ARG(type);
    OSCL_UNUSED_ARG(string1);
    OSCL_UNUSED_ARG(val1);
    OSCL_UNUSED_ARG(val2);

    return;
}

int CbAvcEncMalloc_OMX(void *userData, int32 size, int attribute)
{
    OSCL_UNUSED_ARG(userData);
    OSCL_UNUSED_ARG(attribute);

    uint8 *mem;

    mem = (uint8*) oscl_malloc(size);

    return (int)mem;
}

void CbAvcEncFree_OMX(void *userData, int mem)
{
    OSCL_UNUSED_ARG(userData);

    oscl_free((void*)mem);

    return;
}


int CbAvcEncDPBAlloc_OMX(void *userData, uint frame_size_in_mbs, uint num_buffers)
{
    AvcEncoder_OMX* pAvcEnc = (AvcEncoder_OMX*) userData;

    return pAvcEnc->AVC_DPBAlloc(frame_size_in_mbs, num_buffers);
}


void CbAvcEncFrameUnbind_OMX(void *userData, int indx)
{
    AvcEncoder_OMX* pAvcEnc = (AvcEncoder_OMX*) userData;

    pAvcEnc->AVC_FrameUnbind(indx);

    return;
}

int CbAvcEncFrameBind_OMX(void *userData, int indx, uint8 **yuv)
{
    AvcEncoder_OMX* pAvcEnc = (AvcEncoder_OMX*) userData;

    return pAvcEnc->AVC_FrameBind(indx, yuv);
}



//Class constructor function
AvcEncoder_OMX::AvcEncoder_OMX()
{
    iInitialized = OMX_FALSE;
    iSpsPpsHeaderFlag = OMX_FALSE;
    iReadyForNextFrame = OMX_TRUE;

    oscl_memset((void *)&iAvcHandle, 0, sizeof(AVCHandle));

    iAvcHandle.CBAVC_DPBAlloc = &CbAvcEncDPBAlloc_OMX;
    iAvcHandle.CBAVC_FrameBind = &CbAvcEncFrameBind_OMX;
    iAvcHandle.CBAVC_FrameUnbind = &CbAvcEncFrameUnbind_OMX;
    iAvcHandle.CBAVC_Free = &CbAvcEncFree_OMX;
    iAvcHandle.CBAVC_Malloc = &CbAvcEncMalloc_OMX;
    iAvcHandle.CBAVC_DebugLog = &CbAvcEncDebugLog_OMX;
    iAvcHandle.userData = this;

    ccRGBtoYUV = NULL;
    iYUVIn = NULL;
    iFramePtr = NULL;
    iDPB = NULL;
    iFrameUsed = NULL;

#if PROFILING_ON
    oscl_memset(&iProfileStats, 0, sizeof(PVEncNodeStats));
#endif
}


//Class destructor function
AvcEncoder_OMX::~AvcEncoder_OMX()
{
    //In case encoder cleanup has not been done yet, do it here
    AvcEncDeinit();
}


/* Encoder Initialization routine */
OMX_ERRORTYPE AvcEncoder_OMX::AvcEncInit(OMX_VIDEO_PORTDEFINITIONTYPE aInputParam,
        OMX_CONFIG_ROTATIONTYPE aInputOrientationType,
        OMX_VIDEO_PORTDEFINITIONTYPE aEncodeParam,
        OMX_VIDEO_PARAM_AVCTYPE aEncodeAvcParam,
        OMX_VIDEO_PARAM_BITRATETYPE aRateControlType,
        OMX_VIDEO_PARAM_QUANTIZATIONTYPE aQuantType,
        OMX_VIDEO_PARAM_MOTIONVECTORTYPE aSearchRange,
        OMX_VIDEO_PARAM_INTRAREFRESHTYPE aIntraRefresh,
        OMX_VIDEO_PARAM_VBSMCTYPE aVbsmcType,
        OMX_VIDEO_PARAM_PROFILELEVELTYPE* aProfileLevel)
{

    AVCEncParams aEncOption; /* encoding options */

    uint32* slice_group = NULL;
    int32 numTotalMBs;
    int32 Ysize16;
    int32 total_mb;
    int32 run_length;

    iSrcWidth = aInputParam.nFrameWidth;
    iSrcHeight = aInputParam.nFrameHeight;
    iFrameOrientation = aInputOrientationType.nRotation;


    if ((OMX_COLOR_FormatYUV420Planar == aInputParam.eColorFormat) ||
            (OMX_COLOR_Format24bitRGB888 == aInputParam.eColorFormat) ||
            (OMX_COLOR_Format12bitRGB444 == aInputParam.eColorFormat) ||
            (OMX_COLOR_FormatYUV420SemiPlanar == aInputParam.eColorFormat) ||
            (OMX_COLOR_FormatYCbYCr == aInputParam.eColorFormat) ||
            (OMX_COLOR_FormatYCrYCb == aInputParam.eColorFormat) ||
            (OMX_COLOR_FormatCbYCrY == aInputParam.eColorFormat) ||
            (OMX_COLOR_FormatCrYCbY == aInputParam.eColorFormat))

    {
        iVideoFormat = aInputParam.eColorFormat;
    }
    else
    {
        return OMX_ErrorUnsupportedSetting;
    }


    if (OMX_TRUE == iInitialized)
    {
        /* clean up before re-initialized */
        PVAVCCleanUpEncoder(&iAvcHandle);
        iAvcHandle.AVCObject = NULL;

        if (iYUVIn)
        {
            oscl_free(iYUVIn);
            iYUVIn = NULL;
        }

    }

    //Verify the input compression format
    if (OMX_VIDEO_CodingUnused != aInputParam.eCompressionFormat)
    {
        //Input port must have no compression supported
        return OMX_ErrorUnsupportedSetting;
    }

    // allocate iYUVIn
    if ((iSrcWidth & 0xF) || (iSrcHeight & 0xF) || (OMX_COLOR_FormatYUV420Planar != iVideoFormat)) /* Not multiple of 16 */
    {
        iYUVIn = (uint8*) oscl_malloc(((((iSrcWidth + 15) >> 4) * ((iSrcHeight + 15) >> 4)) * 3) << 7);
        if (NULL == iYUVIn)
        {
            return OMX_ErrorInsufficientResources;
        }
    }

    /* Initialize the color conversion */
    if (OMX_COLOR_Format24bitRGB888 == iVideoFormat)
    {
        ccRGBtoYUV = CCRGB24toYUV420::New();
        ccRGBtoYUV->Init(iSrcWidth, iSrcHeight, iSrcWidth, iSrcWidth, iSrcHeight, ((iSrcWidth + 15) >> 4) << 4, (iFrameOrientation == 180 ? CCBOTTOM_UP : 0));
    }
    if (OMX_COLOR_Format12bitRGB444 == iVideoFormat)
    {
        ccRGBtoYUV = CCRGB12toYUV420::New();
        ccRGBtoYUV->Init(iSrcWidth, iSrcHeight, iSrcWidth, iSrcWidth, iSrcHeight, ((iSrcWidth + 15) >> 4) << 4, (iFrameOrientation == 180 ? CCBOTTOM_UP : 0));
    }
    if (OMX_COLOR_FormatYUV420SemiPlanar == iVideoFormat)
    {
        ccRGBtoYUV = CCYUV420SEMItoYUV420::New();
        ccRGBtoYUV->Init(iSrcWidth, iSrcHeight, iSrcWidth, iSrcWidth , iSrcHeight, ((iSrcWidth + 15) >> 4) << 4, (iFrameOrientation == 180 ? CCBOTTOM_UP : 0));
    }
    if ((OMX_COLOR_FormatYCbYCr == iVideoFormat) ||
            (OMX_COLOR_FormatYCrYCb == iVideoFormat) ||
            (OMX_COLOR_FormatCbYCrY == iVideoFormat) ||
            (OMX_COLOR_FormatCrYCbY == iVideoFormat))
    {
        /*
        ** YUV422 interleaved format must be selected and compiled with the correct format using CML2 tool;
        ** Otherwise, YUV422 input may not be in correct sequence.
        ** This is needed because it is NOT optimal to have the mode selectable at run-time for color conversion.
        */
        ccRGBtoYUV = CCYUV422toYUV420::New();
        ccRGBtoYUV->Init(iSrcWidth, iSrcHeight, iSrcWidth, iSrcWidth , iSrcHeight, ((iSrcWidth + 15) >> 4) << 4, (iFrameOrientation == 180 ? CCBOTTOM_UP : 0));
    }

    aEncOption.width = aEncodeParam.nFrameWidth;
    aEncOption.height = aEncodeParam.nFrameHeight;
    aEncOption.frame_rate = (uint32)(1000 * ((aEncodeParam.xFramerate >> 16) + (OsclFloat)(aEncodeParam.xFramerate & 0xFFFF) / (1 << 16)));

    switch (aRateControlType.eControlRate)
    {
        case OMX_Video_ControlRateDisable:
        {
            aEncOption.rate_control = AVC_OFF;
            aEncOption.bitrate = 48000; // default
        }
        break;

        //Both the below cases have same body
        case OMX_Video_ControlRateConstant:
        case OMX_Video_ControlRateVariable:
        {
            aEncOption.rate_control = AVC_ON;
            aEncOption.bitrate = aEncodeParam.nBitrate;
        }
        break;

        default:
            return OMX_ErrorUnsupportedSetting;
    }

    //Set the profile of encoder
    switch (aEncodeAvcParam.eProfile)
    {
        case OMX_VIDEO_AVCProfileBaseline:
        {
            aEncOption.profile = AVC_BASELINE;
        }
        break;

        case OMX_VIDEO_AVCProfileMain:
        {
            aEncOption.profile = AVC_MAIN;
        }
        break;

        case OMX_VIDEO_AVCProfileExtended:
        {
            aEncOption.profile = AVC_EXTENDED;
        }
        break;

        case OMX_VIDEO_AVCProfileHigh:
        {
            aEncOption.profile = AVC_HIGH;
        }
        break;

        case OMX_VIDEO_AVCProfileHigh10:
        {
            aEncOption.profile = AVC_HIGH10;
        }
        break;

        case OMX_VIDEO_AVCProfileHigh422:
        {
            aEncOption.profile = AVC_HIGH422;
        }
        break;

        case OMX_VIDEO_AVCProfileHigh444:
        {
            aEncOption.profile = AVC_HIGH444;
        }
        break;

        default:
        {
            aEncOption.profile = AVC_BASELINE;
        }
        break;
    }


    //Set the level of avc encoder
    switch (aEncodeAvcParam.eLevel)
    {
        case OMX_VIDEO_AVCLevel1:
        {
            aEncOption.level = AVC_LEVEL1;
        }
        break;
        case OMX_VIDEO_AVCLevel1b:
        {
            aEncOption.level = AVC_LEVEL1_B;
        }
        break;
        case OMX_VIDEO_AVCLevel11:
        {
            aEncOption.level = AVC_LEVEL1_1;
        }
        break;
        case OMX_VIDEO_AVCLevel12:
        {
            aEncOption.level = AVC_LEVEL1_2;
        }
        break;
        case OMX_VIDEO_AVCLevel13:
        {
            aEncOption.level = AVC_LEVEL1_3;
        }
        break;


        case OMX_VIDEO_AVCLevel2:
        {
            aEncOption.level = AVC_LEVEL2;
        }
        break;
        case OMX_VIDEO_AVCLevel21:
        {
            aEncOption.level = AVC_LEVEL2_1;
        }
        break;
        case OMX_VIDEO_AVCLevel22:
        {
            aEncOption.level = AVC_LEVEL2_2;
        }
        break;


        case OMX_VIDEO_AVCLevel3:
        {
            aEncOption.level = AVC_LEVEL3;
        }
        break;
        case OMX_VIDEO_AVCLevel31:
        {
            aEncOption.level = AVC_LEVEL3_1;
        }
        break;
        case OMX_VIDEO_AVCLevel32:
        {
            aEncOption.level = AVC_LEVEL3_2;
        }
        break;


        case OMX_VIDEO_AVCLevel4:
        {
            aEncOption.level = AVC_LEVEL4;
        }
        break;
        case OMX_VIDEO_AVCLevel41:
        {
            aEncOption.level = AVC_LEVEL4_1;
        }
        break;
        case OMX_VIDEO_AVCLevel42:
        {
            aEncOption.level = AVC_LEVEL4_2;
        }
        break;


        case OMX_VIDEO_AVCLevel5:
        {
            aEncOption.level = AVC_LEVEL5;
        }
        break;
        case OMX_VIDEO_AVCLevel51:
        {
            aEncOption.level = AVC_LEVEL5_1;
        }
        break;

        default:
        {
            aEncOption.level = AVC_LEVEL5_1;
        }
        break;
    }

    aEncOption.initQP = aQuantType.nQpP;

    aEncOption.init_CBP_removal_delay = 1600;   //Default value
    aEncOption.CPB_size = ((uint32)(2 * aEncOption.bitrate));
    if (OMX_VIDEO_PictureTypeI == aEncodeAvcParam.nAllowedPictureTypes) // I-only
    {
        aEncOption.idr_period = 1;
    }
    else
    {
        //IPPPPPPPPPP, no I frame for a long period of time
        if (0xFFFFFFFF == aEncodeAvcParam.nPFrames)
        {
            aEncOption.idr_period = -1;
        }
        else
        {
            aEncOption.idr_period = aEncodeAvcParam.nPFrames;
        }
    }

    if ((OMX_VIDEO_IntraRefreshCyclic == aIntraRefresh.eRefreshMode) ||
            (OMX_VIDEO_IntraRefreshBoth == aIntraRefresh.eRefreshMode))
    {
        aEncOption.intramb_refresh = aIntraRefresh.nCirMBs;
    }


    if ((OMX_VIDEO_IntraRefreshAdaptive == aIntraRefresh.eRefreshMode) ||
            (OMX_VIDEO_IntraRefreshBoth == aIntraRefresh.eRefreshMode))
    {
        aEncOption.auto_scd = AVC_ON;
    }
    else
    {
        aEncOption.auto_scd = AVC_OFF;
    }

    aEncOption.out_of_band_param_set = AVC_ON;      //Default value

    /* default values */
    aEncOption.poc_type = 2;
    aEncOption.log2_max_poc_lsb_minus_4 = 12;
    aEncOption.delta_poc_zero_flag = 0;
    aEncOption.offset_poc_non_ref = 0;
    aEncOption.offset_top_bottom  = 0;
    aEncOption.num_ref_in_cycle = 0;
    aEncOption.offset_poc_ref = NULL;

    aEncOption.num_ref_frame = 1;   //We only support this value

#if (defined(TEST_FULL_AVC_FRAME_MODE) || defined(TEST_FULL_AVC_FRAME_MODE_SC) || defined(TEST_FULL_AVC_FRAME_MODE_I2BNS) || defined(TEST_FULL_AVC_FRAME_MODE_I4BNS))
    aEncOption.num_slice_group = 4;
    aEncOption.fmo_type = 6;
    aEncOption.use_overrun_buffer = AVC_OFF; // since we are to be outputing full frame buffers
#else
    //Since FMO is disabled in our case, num of slice group is always 1
    aEncOption.num_slice_group = 1;
    aEncOption.fmo_type = 0;    //Disabled in this case
    aEncOption.use_overrun_buffer = AVC_ON;
#endif

    if (OMX_VIDEO_AVCLoopFilterEnable == aEncodeAvcParam.eLoopFilterMode)
    {
        aEncOption.disable_db_idc = 0;
        aEncOption.db_filter = AVC_OFF;
    }
    else if (OMX_VIDEO_AVCLoopFilterDisable == aEncodeAvcParam.eLoopFilterMode)
    {
        aEncOption.disable_db_idc = 1;
        aEncOption.db_filter = AVC_ON;
    }
    else if (OMX_VIDEO_AVCLoopFilterDisableSliceBoundary == aEncodeAvcParam.eLoopFilterMode)
    {
        aEncOption.disable_db_idc = 2;
        aEncOption.db_filter = AVC_ON;
    }
    else
    {
        return OMX_ErrorUnsupportedSetting;
    }

    aEncOption.alpha_offset = 0;
    aEncOption.beta_offset = 0;
    aEncOption.constrained_intra_pred = (OMX_TRUE == aEncodeAvcParam.bconstIpred) ? AVC_ON : AVC_OFF;

    aEncOption.data_par = AVC_OFF;
    aEncOption.fullsearch = AVC_OFF;
    aEncOption.search_range = (aSearchRange.sXSearchRange <= aSearchRange.sYSearchRange ? aSearchRange.sXSearchRange : aSearchRange.sYSearchRange);

    if (OMX_Video_MotionVectorPixel == aSearchRange.eAccuracy)
    {
        aEncOption.sub_pel = AVC_OFF;
    }
    else
    {
        aEncOption.sub_pel = AVC_ON;
    }

    if (aVbsmcType.b16x16 != OMX_TRUE)
    {
        return OMX_ErrorBadParameter;
    }

    aEncOption.submb_pred =  AVC_OFF; // default for now, ignore aVbsmcType.8x16, 16x8, 8x8, etc.
    aEncOption.rdopt_mode = AVC_OFF;
    aEncOption.bidir_pred = AVC_OFF;

    Ysize16 = (((aEncOption.width + 15) >> 4) << 4) * (((aEncOption.height + 15) >> 4) << 4);
    numTotalMBs = Ysize16 >> 8;
    slice_group = (uint*) oscl_malloc(sizeof(uint) * numTotalMBs);

    int32 idx = 0;
    int32 ii;
    for (ii = 0; ii < numTotalMBs; ii++)
    {
        slice_group[ii] = idx++;
        if (idx >= aEncOption.num_slice_group)
        {
            idx = 0;
        }
    }
    aEncOption.slice_group = slice_group;

    if (aEncOption.num_slice_group > 1)  /* divide slice group equally */
    {
        run_length = numTotalMBs / aEncOption.num_slice_group;
        total_mb = 0;
        for (idx = 0; idx < aEncOption.num_slice_group; idx++)
        {
            aEncOption.run_length_minus1[idx] = run_length - 1;
            total_mb += run_length;
        }
        if (total_mb < numTotalMBs)
        {
            aEncOption.run_length_minus1[aEncOption.num_slice_group-1] += (numTotalMBs - total_mb);
        }
    }

    /***** Initialize the encoder *****/
    if (AVCENC_SUCCESS != PVAVCEncInitialize(&iAvcHandle, &aEncOption, NULL, NULL))
    {
        iInitialized = OMX_FALSE;
        return OMX_ErrorBadParameter;
    }

    //Updating the profile and level from the encoder after initialization
    AVCProfile profile_idc;
    AVCLevel level_idc;

    if (AVCENC_SUCCESS != PVAVCEncGetProfileLevel(&iAvcHandle, &profile_idc, &level_idc))
    {
        return OMX_ErrorInsufficientResources;
    }

    switch (profile_idc)
    {
        case AVC_BASELINE:
            aProfileLevel->eProfile = OMX_VIDEO_AVCProfileBaseline;
            break;
        case AVC_MAIN:
            aProfileLevel->eProfile = OMX_VIDEO_AVCProfileMain;
            break;
        case AVC_EXTENDED:
            aProfileLevel->eProfile = OMX_VIDEO_AVCProfileExtended;
            break;
        case AVC_HIGH:
            aProfileLevel->eProfile = OMX_VIDEO_AVCProfileHigh;
            break;
        case AVC_HIGH10:
            aProfileLevel->eProfile = OMX_VIDEO_AVCProfileHigh10;
            break;
        case AVC_HIGH422:
            aProfileLevel->eProfile = OMX_VIDEO_AVCProfileHigh422;
            break;
        case AVC_HIGH444:
            aProfileLevel->eProfile = OMX_VIDEO_AVCProfileHigh444;
            break;
        default:
            aProfileLevel->eProfile = OMX_VIDEO_AVCProfileVendorStartUnused;
    }

    switch (level_idc)
    {
        case AVC_LEVEL1_B:
            aProfileLevel->eLevel = OMX_VIDEO_AVCLevel1b;
            break;
        case AVC_LEVEL1:
            aProfileLevel->eLevel = OMX_VIDEO_AVCLevel1;
            break;
        case AVC_LEVEL1_1:
            aProfileLevel->eLevel = OMX_VIDEO_AVCLevel11;
            break;
        case AVC_LEVEL1_2 :
            aProfileLevel->eLevel = OMX_VIDEO_AVCLevel12;
            break;
        case AVC_LEVEL1_3 :
            aProfileLevel->eLevel = OMX_VIDEO_AVCLevel13;
            break;
        case AVC_LEVEL2 :
            aProfileLevel->eLevel = OMX_VIDEO_AVCLevel2;
            break;
        case AVC_LEVEL2_1 :
            aProfileLevel->eLevel = OMX_VIDEO_AVCLevel21;
            break;
        case AVC_LEVEL2_2 :
            aProfileLevel->eLevel = OMX_VIDEO_AVCLevel22;
            break;
        case AVC_LEVEL3 :
            aProfileLevel->eLevel = OMX_VIDEO_AVCLevel3;
            break;
        case AVC_LEVEL3_1 :
            aProfileLevel->eLevel = OMX_VIDEO_AVCLevel31;
            break;
        case AVC_LEVEL3_2 :
            aProfileLevel->eLevel = OMX_VIDEO_AVCLevel32;
            break;
        case AVC_LEVEL4 :
            aProfileLevel->eLevel = OMX_VIDEO_AVCLevel4;
            break;
        case AVC_LEVEL4_1 :
            aProfileLevel->eLevel = OMX_VIDEO_AVCLevel41;
            break;
        case AVC_LEVEL4_2 :
            aProfileLevel->eLevel = OMX_VIDEO_AVCLevel42;
            break;
        case AVC_LEVEL5 :
            aProfileLevel->eLevel = OMX_VIDEO_AVCLevel5 ;
            break;
        case AVC_LEVEL5_1 :
            aProfileLevel->eLevel = OMX_VIDEO_AVCLevel51;
            break;
        case AVC_LEVEL_AUTO:
        default:
            aProfileLevel->eLevel = OMX_VIDEO_AVCLevelVendorStartUnused;
    }

    iIDR = OMX_TRUE;
    iDispOrd = 0;
    iInitialized = OMX_TRUE;
    iReadyForNextFrame = OMX_TRUE;

    oscl_free(slice_group);

    return OMX_ErrorNone;

}


AVCEnc_Status AvcEncoder_OMX::AvcEncodeSendInput(OMX_U8*    aInBuffer,
        OMX_U32*   aInBufSize,
        OMX_TICKS  aInTimeStamp)
{
    AVCEnc_Status AvcStatus;

#if PROFILING_ON
    OMX_U32 Start = OsclTickCount::TickCount();
#endif

    if (OMX_COLOR_FormatYUV420Planar == iVideoFormat)
    {
        /* Input Buffer Size Check
         * Input buffer size should be equal to one frame, otherwise drop the frame
         * as it is a corrupt data and don't encode it */
        if (*aInBufSize < (OMX_U32)((iSrcWidth * iSrcHeight * 3) >> 1))
        {
            //Mark the inpur buffer consumed to indicate corrupt frame
            *aInBufSize = 0;
            return AVCENC_FAIL;
        }

        if (iYUVIn) /* iSrcWidth is not multiple of 4 or iSrcHeight is odd number */
        {
            CopyToYUVIn(aInBuffer, iSrcWidth, iSrcHeight,
                        ((iSrcWidth + 15) >> 4) << 4, ((iSrcHeight + 15) >> 4) << 4);
            iVideoIn = iYUVIn;
        }
        else /* otherwise, we can just use aVidIn->iSource */
        {
            iVideoIn = aInBuffer;
        }
    }
    else if (OMX_COLOR_Format12bitRGB444 == iVideoFormat)
    {
        if (*aInBufSize < (OMX_U32)(iSrcWidth * iSrcHeight * 2))
        {
            //Mark the inpur buffer consumed to indicate corrupt frame
            *aInBufSize = 0;
            return AVCENC_FAIL;
        }
        ccRGBtoYUV->Convert((uint8*)aInBuffer, iYUVIn);
        iVideoIn = iYUVIn;
    }
    else if (OMX_COLOR_Format24bitRGB888 == iVideoFormat)
    {
        if (*aInBufSize < (OMX_U32)(iSrcWidth * iSrcHeight * 3))
        {
            //Mark the inpur buffer consumed to indicate corrupt frame
            *aInBufSize = 0;
            return AVCENC_FAIL;
        }
        ccRGBtoYUV->Convert((uint8*)aInBuffer, iYUVIn);
        iVideoIn = iYUVIn;
    }
    else if (OMX_COLOR_FormatYUV420SemiPlanar == iVideoFormat)
    {
        if (*aInBufSize < (OMX_U32)(iSrcWidth * iSrcHeight * 3) >> 1)
        {
            //Mark the inpur buffer consumed to indicate corrupt frame
            *aInBufSize = 0;
            return AVCENC_FAIL;
        }
        ccRGBtoYUV->Convert((uint8*)aInBuffer, iYUVIn);
        iVideoIn = iYUVIn;
    }
    else if ((OMX_COLOR_FormatYCbYCr == iVideoFormat) ||
             (OMX_COLOR_FormatYCrYCb == iVideoFormat) ||
             (OMX_COLOR_FormatCbYCrY == iVideoFormat) ||
             (OMX_COLOR_FormatCrYCbY == iVideoFormat))
    {
        /*
        ** YUV422 interleaved format must be selected and compiled with the correct format using CML2 tool;
        ** Otherwise, YUV422 input may not be in correct sequence.
        ** This is needed because it is NOT optimal to have the mode selectable at run-time for color conversion.
        */
        if (*aInBufSize < (OMX_U32)(iSrcWidth * iSrcHeight * 2))
        {
            //Mark the inpur buffer consumed to indicate corrupt frame
            *aInBufSize = 0;
            return AVCENC_FAIL;
        }
        ccRGBtoYUV->Convert((uint8*)aInBuffer, iYUVIn);
        iVideoIn = iYUVIn;
    }

#if PROFILING_ON
    //End ticks for color conversion time
    OMX_U32 Stop = OsclTickCount::TickCount();
    iProfileStats.iColorConversionTime += (Stop - Start);

    //Start ticks for encoding time
    ++iProfileStats.iTotalNumFrames;
    OMX_U32 StartTime = OsclTickCount::TickCount();
#endif

    /* assign with backward-P or B-Vop this timestamp must be re-ordered */
    // Encoder uses 32 bit timestamps internally - keep track of 64 bit value as well
    iTimeStamp = Oscl_Int64_Utils::get_uint64_lower32(aInTimeStamp / 1000); //timestamp in millisec
    iTimeStamp64 = aInTimeStamp;

    iVidIn.height = ((iSrcHeight + 15) >> 4) << 4;
    iVidIn.pitch = ((iSrcWidth + 15) >> 4) << 4;
    iVidIn.coding_timestamp = iTimeStamp;
    iVidIn.YCbCr[0] = (uint8*)iVideoIn;
    iVidIn.YCbCr[1] = (uint8*)(iVideoIn + iVidIn.height * iVidIn.pitch);
    iVidIn.YCbCr[2] = iVidIn.YCbCr[1] + ((iVidIn.height * iVidIn.pitch) >> 2);
    iVidIn.disp_order = iDispOrd;

    AvcStatus = PVAVCEncSetInput(&iAvcHandle, &iVidIn);

#if PROFILING_ON
    OMX_U32 EndTime = OsclTickCount::TickCount();
    iProfileStats.iTotalEncTime += (EndTime - StartTime);
#endif

    if (AVCENC_SUCCESS == AvcStatus)
    {
        iDispOrd++;
        iReadyForNextFrame = OMX_FALSE;
        return AvcStatus;
    }
    else if (AVCENC_NEW_IDR == AvcStatus)
    {
        iDispOrd++;
        iIDR = OMX_TRUE;
        iReadyForNextFrame = OMX_FALSE;
        return AvcStatus;
    }
    else
    {
        return AvcStatus;
    }

}


/*Encode routine */
AVCEnc_Status AvcEncoder_OMX::AvcEncodeVideo(OMX_U8* aOutBuffer,
        OMX_U32*   aOutputLength,
        OMX_BOOL*  aBufferOverRun,
        OMX_U8**   aOverBufferPointer,
        OMX_U8*    aInBuffer,
        OMX_U32*   aInBufSize,
        OMX_TICKS  aInTimeStamp,
        OMX_TICKS* aOutTimeStamp,
        OMX_BOOL*  aSyncFlag)
{
    AVCEnc_Status AvcStatus;
    AVCEnc_Status ReturnValue;
    AVCEnc_Status   SetInputStatus = AVCENC_SUCCESS;
    AVCFrameIO recon;
    uint Size;
    int  NALType = 0;

    if (OMX_FALSE == iSpsPpsHeaderFlag)
    {
        Size = *aOutputLength;
        AvcStatus = PVAVCEncodeNAL(&iAvcHandle, aOutBuffer, &Size, &NALType);

        //No more SPS and PPS NAL's to come, mark the header flag as true
        if (AVCENC_WRONG_STATE == AvcStatus)
        {
            *aOutputLength = 0;
            iSpsPpsHeaderFlag = OMX_TRUE;
            return AVCENC_WRONG_STATE;
        }

        switch (NALType)
        {
            case AVC_NALTYPE_SPS:
            case AVC_NALTYPE_PPS:
            {
                *aOutputLength = Size;
                //Mark the SPS and PPS buffers with the first input bufer timestamp
                *aOutTimeStamp = aInTimeStamp;
                return AVCENC_SUCCESS;
            }

            default:
            {
                //No more SPS and PPS NAL's to come, mark the header flag as true
                *aOutputLength = 0;
                iSpsPpsHeaderFlag = OMX_TRUE;
                return AVCENC_FAIL;
            }
        }
    }


    if (OMX_TRUE == iReadyForNextFrame)
    {
        SetInputStatus = AvcEncodeSendInput(aInBuffer, aInBufSize, aInTimeStamp);
    }

    if ((AVCENC_SUCCESS == SetInputStatus) || (AVCENC_NEW_IDR == SetInputStatus))       //success
    {
        Size = *aOutputLength;

#if PROFILING_ON
        OMX_U32 Start = OsclTickCount::TickCount();
#endif
        AvcStatus = PVAVCEncodeNAL(&iAvcHandle, (uint8*)aOutBuffer, &Size, &NALType);

#if PROFILING_ON
        OMX_U32 Stop = OsclTickCount::TickCount();
        iProfileStats.iTotalEncTime += (Stop - Start);

        if (AVCENC_PICTURE_READY == AvcStatus)
        {
            ++iProfileStats.iNumFramesEncoded;
            iProfileStats.iDuration = iTimeStamp64;
        }
#endif

        if (AVCENC_SUCCESS == AvcStatus)
        {
            *aSyncFlag = iIDR;
            //Calling the overrun buffer api to see whether overrun buffer has been used or not
            *aOverBufferPointer = PVAVCEncGetOverrunBuffer(&iAvcHandle);

            if (NULL != *aOverBufferPointer)
            {
                //Output data has been filled in the over run buffer, mark the flag as true
                *aBufferOverRun = OMX_TRUE;
            }

            ReturnValue = AVCENC_SUCCESS;
        }
        else if (AVCENC_PICTURE_READY == AvcStatus)
        {
            //Calling the overrun buffer api to see whether overrun buffer has been used or not
            *aOverBufferPointer = PVAVCEncGetOverrunBuffer(&iAvcHandle);

            if (NULL != *aOverBufferPointer)
            {
                //Output data has been filled in the over run buffer, mark the flag as true
                *aBufferOverRun = OMX_TRUE;
            }

            *aSyncFlag = iIDR;
            if (iIDR == OMX_TRUE)
            {
                iIDR = OMX_FALSE;
            }

            ReturnValue = AVCENC_PICTURE_READY;

            iReadyForNextFrame = OMX_TRUE;  //ready to receive another set of input frame

            AvcStatus = PVAVCEncGetRecon(&iAvcHandle, &recon);
            if (AVCENC_SUCCESS == AvcStatus)
            {
                PVAVCEncReleaseRecon(&iAvcHandle, &recon);
            }
        }
        else if (AVCENC_SKIPPED_PICTURE == AvcStatus)
        {
            *aOutputLength = 0;
            iReadyForNextFrame = OMX_TRUE;  //ready to receive another set of input frame
            return AVCENC_SKIPPED_PICTURE;
        }
        else
        {
            *aOutputLength = 0;
            iReadyForNextFrame = OMX_TRUE;  //ready to receive another set of input frame
            return AVCENC_FAIL;
        }

        *aOutputLength = Size;
        *aOutTimeStamp = iTimeStamp64;//timestamp in microsec

        return ReturnValue;

    }
    else if (AVCENC_SKIPPED_PICTURE == SetInputStatus)
    {
        *aOutputLength = 0;
        return AVCENC_SKIPPED_PICTURE;
    }
    else
    {
        *aOutputLength = 0;
        return AVCENC_FAIL;
    }
}


//Deinitialize the avc encoder here and perform the cleanup and free operations
OMX_ERRORTYPE AvcEncoder_OMX::AvcEncDeinit()
{
    if (OMX_TRUE == iInitialized)
    {
        PVAVCCleanUpEncoder(&iAvcHandle);
        iAvcHandle.AVCObject = NULL;

        iInitialized = OMX_FALSE;
        iReadyForNextFrame = OMX_TRUE;

        if (iYUVIn)
        {
            oscl_free(iYUVIn);
            iYUVIn = NULL;
        }
    }

    if (iFrameUsed)
    {
        oscl_free(iFrameUsed);
        iFrameUsed = NULL;
    }

    if (ccRGBtoYUV)
    {
        OSCL_DELETE(ccRGBtoYUV);
        ccRGBtoYUV = NULL;
    }

    if (iDPB)
    {
        oscl_free(iDPB);
        iDPB = NULL;
    }

    if (iFramePtr)
    {
        oscl_free(iFramePtr);
        iFramePtr = NULL;
    }

    return OMX_ErrorNone;
}


int AvcEncoder_OMX::AVC_DPBAlloc(uint frame_size_in_mbs, uint num_buffers)
{
    int ii;
    uint frame_size = (frame_size_in_mbs << 8) + (frame_size_in_mbs << 7);

    if (iDPB) oscl_free(iDPB); // free previous one first

    iDPB = (uint8*) oscl_malloc(sizeof(uint8) * frame_size * num_buffers);
    if (iDPB == NULL)
    {
        return 0;
    }

    iNumFrames = num_buffers;

    if (iFrameUsed) oscl_free(iFrameUsed); // free previous one

    iFrameUsed = (bool*) oscl_malloc(sizeof(bool) * num_buffers);
    if (iFrameUsed == NULL)
    {
        return 0;
    }

    if (iFramePtr) oscl_free(iFramePtr); // free previous one
    iFramePtr = (uint8**) oscl_malloc(sizeof(uint8*) * num_buffers);
    if (iFramePtr == NULL)
    {
        return 0;
    }

    iFramePtr[0] = iDPB;
    iFrameUsed[0] = false;

    for (ii = 1; ii < (int)num_buffers; ii++)
    {
        iFrameUsed[ii] = false;
        iFramePtr[ii] = iFramePtr[ii-1] + frame_size;
    }

    return 1;
}


//Request for an I frame while encoding is in process
OMX_ERRORTYPE AvcEncoder_OMX::AvcRequestIFrame()
{
    if (AVCENC_SUCCESS != PVAVCEncIDRRequest(&iAvcHandle))
    {
        return OMX_ErrorUndefined;
    }

    return OMX_ErrorNone;

}

//Routine to update bitrate dynamically when encoding is in progress
OMX_BOOL AvcEncoder_OMX::AvcUpdateBitRate(OMX_U32 aEncodedBitRate)
{
    int32 BitRate[2] = {0, 0};
    BitRate[0] = aEncodedBitRate;

    if (AVCENC_SUCCESS != PVAVCEncUpdateBitRate(&iAvcHandle, BitRate[0]))
    {
        return OMX_FALSE;
    }

    return OMX_TRUE;
}

//Routine to update frame rate dynamically when encoding is in progress
OMX_BOOL AvcEncoder_OMX::AvcUpdateFrameRate(OMX_U32 aEncodeFramerate)
{
    OMX_U32 EncFrameRate[2] = {0, 0};
    EncFrameRate[0] = aEncodeFramerate >> 16;

    if (AVCENC_SUCCESS != PVAVCEncUpdateFrameRate(&iAvcHandle, (uint32)(1000*EncFrameRate[0]), 1000))
    {
        return OMX_FALSE;
    }

    return OMX_TRUE;

}

//Routine to update IDR frame interval dynamically when encoding is in progress
OMX_BOOL AvcEncoder_OMX::AvcUpdateIDRFrameInterval(OMX_U32 aIDRInterval)
{
    /* Note -- Here, aIDRInterval defines encoding of IDR frame after every nPFrames.
    ** aIDRInterval is different from aIDRPeriod.
    */
    if (AVCENC_SUCCESS != PVAVCEncUpdateIDRInterval(&iAvcHandle, (int)aIDRInterval))
    {
        return OMX_FALSE;
    }

    return OMX_TRUE;

}

/* ///////////////////////////////////////////////////////////////////////// */
void AvcEncoder_OMX::AVC_FrameUnbind(int indx)
{
    /*if (indx < iNumFrames)
    {
        iFrameUsed[indx] = false;
    }*/

    OSCL_UNUSED_ARG(indx);
    return ;
}

/* ///////////////////////////////////////////////////////////////////////// */
int AvcEncoder_OMX::AVC_FrameBind(int indx, uint8** yuv)
{
    /*if ((iFrameUsed[indx]==true) || (indx >= iNumFrames))
    {
        return 0; // already in used
    }
    iFrameUsed[indx] = true;*/

    *yuv = iFramePtr[indx];
    return 1;
}



/* ///////////////////////////////////////////////////////////////////////// */
/* Copy from YUV input to YUV frame inside AvcEnc lib                       */
/* When input is not YUV, the color conv will write it directly to iVideoInOut. */
/* ///////////////////////////////////////////////////////////////////////// */

void AvcEncoder_OMX::CopyToYUVIn(uint8 *YUV, int width, int height, int width_16, int height_16)
{
    uint8 *y, *u, *v, *yChan, *uChan, *vChan;
    int y_ind, ilimit, jlimit, i, j, ioffset;
    int size = width * height;
    int size16 = width_16 * height_16;

    /* do padding at the bottom first */
    /* do padding if input RGB size(height) is different from the output YUV size(height_16) */
    if (height < height_16 || width < width_16) /* if padding */
    {
        int offset = (height < height_16) ? height : height_16;

        offset = (offset * width_16);

        if (width < width_16)
        {
            offset -= (width_16 - width);
        }

        yChan = (uint8*)(iYUVIn + offset);
        oscl_memset(yChan, 16, size16 - offset); /* pad with zeros */

        uChan = (uint8*)(iYUVIn + size16 + (offset >> 2));
        oscl_memset(uChan, 128, (size16 - offset) >> 2);

        vChan = (uint8*)(iYUVIn + size16 + (size16 >> 2) + (offset >> 2));
        oscl_memset(vChan, 128, (size16 - offset) >> 2);
    }

    /* then do padding on the top */
    yChan = (uint8*)iYUVIn; /* Normal order */
    uChan = (uint8*)(iYUVIn + size16);
    vChan = (uint8*)(uChan + (size16 >> 2));

    u = (uint8*)(&(YUV[size]));
    v = (uint8*)(&(YUV[size*5/4]));

    /* To center the output */
    if (height_16 > height)   /* output taller than input */
    {
        if (width_16 >= width)  /* output wider than or equal input */
        {
            i = ((height_16 - height) >> 1) * width_16 + (((width_16 - width) >> 3) << 2);
            /* make sure that (width_16-width)>>1 is divisible by 4 */
            j = ((height_16 - height) >> 2) * (width_16 >> 1) + (((width_16 - width) >> 4) << 2);
            /* make sure that (width_16-width)>>2 is divisible by 4 */
        }
        else  /* output narrower than input */
        {
            i = ((height_16 - height) >> 1) * width_16;
            j = ((height_16 - height) >> 2) * (width_16 >> 1);
            YUV += ((width - width_16) >> 1);
            u += ((width - width_16) >> 2);
            v += ((width - width_16) >> 2);
        }
        oscl_memset((uint8 *)yChan, 16, i);
        yChan += i;
        oscl_memset((uint8 *)uChan, 128, j);
        uChan += j;
        oscl_memset((uint8 *)vChan, 128, j);
        vChan += j;
    }
    else   /* output shorter or equal input */
    {
        if (width_16 >= width)   /* output wider or equal input */
        {
            i = (((width_16 - width) >> 3) << 2);
            /* make sure that (width_16-width)>>1 is divisible by 4 */
            j = (((width_16 - width) >> 4) << 2);
            /* make sure that (width_16-width)>>2 is divisible by 4 */
            YUV += (((height - height_16) >> 1) * width);
            u += (((height - height_16) >> 1) * width) >> 2;
            v += (((height - height_16) >> 1) * width) >> 2;
        }
        else  /* output narrower than input */
        {
            i = 0;
            j = 0;
            YUV += (((height - height_16) >> 1) * width + ((width - width_16) >> 1));
            u += (((height - height_16) >> 1) * width + ((width - width_16) >> 1)) >> 2;
            v += (((height - height_16) >> 1) * width + ((width - width_16) >> 1)) >> 2;
        }
        oscl_memset((uint8 *)yChan, 16, i);
        yChan += i;
        oscl_memset((uint8 *)uChan, 128, j);
        uChan += j;
        oscl_memset((uint8 *)vChan, 128, j);
        vChan += j;
    }

    /* Copy with cropping or zero-padding */
    if (height < height_16)
        jlimit = height;
    else
        jlimit = height_16;

    if (width < width_16)
    {
        ilimit = width;
        ioffset = width_16 - width;
    }
    else
    {
        ilimit = width_16;
        ioffset = 0;
    }

    /* Copy Y */
    /* Set up pointer for fast looping */
    y = (uint8*)YUV;

    if (width == width_16 && height == height_16) /* no need to pad */
    {
        oscl_memcpy(yChan, y, size);
    }
    else
    {
        for (y_ind = 0; y_ind < (jlimit - 1) ; y_ind++)
        {
            oscl_memcpy(yChan, y, ilimit);
            oscl_memset(yChan + ilimit, 16, ioffset); /* pad with zero */
            yChan += width_16;
            y += width;
        }
        oscl_memcpy(yChan, y, ilimit); /* last line no padding */
    }
    /* Copy U and V */
    /* Set up pointers for fast looping */
    if (width == width_16 && height == height_16) /* no need to pad */
    {
        oscl_memcpy(uChan, u, size >> 2);
        oscl_memcpy(vChan, v, size >> 2);
    }
    else
    {
        for (y_ind = 0; y_ind < (jlimit >> 1) - 1; y_ind++)
        {
            oscl_memcpy(uChan, u, ilimit >> 1);
            oscl_memcpy(vChan, v, ilimit >> 1);
            oscl_memset(uChan + (ilimit >> 1), 128, ioffset >> 1);
            oscl_memset(vChan + (ilimit >> 1), 128, ioffset >> 1);
            uChan += (width_16 >> 1);
            u += (width >> 1);
            vChan += (width_16 >> 1);
            v += (width >> 1);
        }
        oscl_memcpy(uChan, u, ilimit >> 1); /* last line no padding */
        oscl_memcpy(vChan, v, ilimit >> 1);
    }

    return ;
}

OMX_BOOL AvcEncoder_OMX::GetSpsPpsHeaderFlag()
{
    return iSpsPpsHeaderFlag;
}


// This function gives the maximum output buffer requirement and
// can only be called after encoder's initialization
OMX_ERRORTYPE AvcEncoder_OMX::AVCOutBufferSize(OMX_U32 *aMaxVideoFrameSize)
{
    int MaxFrameSize = 0;
    if (AVCENC_SUCCESS != PVAVCEncGetMaxOutputBufferSize(&iAvcHandle, &MaxFrameSize))
    {
        return OMX_ErrorUndefined;
    }
    *aMaxVideoFrameSize = (OMX_U32)MaxFrameSize;
    return OMX_ErrorNone;
}


