/******************************************************************************
* FILE PURPOSE: Run Stand-alone XDM compliant Encoder
******************************************************************************
* FILE NAME:   siuVctEncode.c
*
* DESCRIPTION: Contains routines that support stand-alone Encoder test
*
* TABS: NONE
*
*
* (C) Copyright 2010, Texas Instruments Incorporated
 * 
 *  Redistribution and use in source and binary forms, with or without 
 *  modification, are permitted provided that the following conditions 
 *  are met:
 *
 *    Redistributions of source code must retain the above copyright 
 *    notice, this list of conditions and the following disclaimer.
 *
 *    Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the 
 *    documentation and/or other materials provided with the   
 *    distribution.
 *
 *    Neither the name of Texas Instruments Incorporated nor the names of
 *    its contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
 *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
 *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 *  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
 *  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
 *  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
 *  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 *  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 *  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
 *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
 *  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
*/
/* Standard C header files                                                    */
#include <stdio.h>
#include <stdlib.h>
#include "siuVctCodecAPI.h"
#include "siuVctFileIO.h"
#include "ividmc/siuVidMc.h"
#include "common/siuloc.h"
#include "osal/siuFcOsal.h"

#include <ti/xdais/ires.h>

extern IALG_Handle ALG_create(IALG_Fxns *fxns, IALG_Handle p, IALG_Params *params);
extern Void ALG_delete(IALG_Handle alg);

/******************************************************************************
* FUNCTION PURPOSE: Main Function acting as a client for Video Encode Call
******************************************************************************
* DESCRIPTION: Main Function acting as a client for Video Encode Call
*
* XDAS_Int32 tvct_test_entry_encode_xdm0p9(
*    const char *codecname        - name of the codec
*    XDAS_Int8 *inputData         - input  data ptr in DDR
*    XDAS_Int8 *outputData        - output data ptr in DDR
*    XDAS_UInt32 framesToEncode   - # of frames to encode
* )
*****************************************************************************/
#pragma DATA_SECTION( inputData, ".codec_input");
XDAS_Int8 inputData[4177920]; /*1920*1088*2*/
/* Output data buffer */
#define CORE_OUTPUT_BUF_SIZE  (9 * 32768)
#define MC_ENC_MAXCORES 6
volatile XDAS_UInt32 max_output_bufsize = 0;
XDAS_Int8  outputData_ABCD[MC_ENC_MAXCORES][CORE_OUTPUT_BUF_SIZE];
#pragma DATA_SECTION(outputData_ABCD, ".codec_output")

/* Number of bytes generated by primary core, for the most recent frame */
volatile XDAS_Int32 outputSLICES_ABCD[MC_ENC_MAXCORES];
/* Total number of bytes generated by primary core */
volatile XDAS_Int32 bytesGeneratedABCD[MC_ENC_MAXCORES];

/* These two helper core variables have to be accessed by primary core */
/* at the end of frame processing (to write data into the file)        */
#pragma DATA_SECTION(outputSLICES_ABCD,  ".shared_variables")
#pragma DATA_SECTION(bytesGeneratedABCD, ".shared_variables")

#define MAX_ENC_INST_SIZE 5000
XDAS_Int8 encInst2[MAX_ENC_INST_SIZE];
extern cregister volatile unsigned int TSCL;
extern cregister volatile unsigned int DNUM;

extern int CORE_TEAM_MAPPING[];
#pragma    DATA_SECTION(encMultiCoreSync, ".multiCoreSync")
/*---------------------------------------------------------------------------*/
/*  Buffer for multicore synchronozation in testapplication                  */
/*---------------------------------------------------------------------------*/
XDAS_Int8  encMultiCoreSync[IVIDMC_NUM_BARRIERS];

extern volatile tulong hdResolution;

void shipOutNal ( XDAS_Int32    *pNalu, XDAS_Int32    *pPacketSizeInBytes)
{ /* Code is common for both cores (loaded into sharem memory) */
  int i;
  for(i = 0; i < MC_ENC_MAXCORES; i++)
  {
    if((CORE_TEAM_MAPPING[i] != IVIDMC_CORE_NOT_USED) && (DNUM == CORE_TEAM_MAPPING[i]))
    {
      outputSLICES_ABCD[i]  += *pPacketSizeInBytes;
      bytesGeneratedABCD[i] += *pPacketSizeInBytes;
      break;
    }
  }
}


XDAS_Int32 tvct_test_entry_encode_xdm0p9 (const char *codecname, XDAS_UInt32 framesToEncode,
           XDAS_UInt32 nCores, void *codecParams)
{
    /* Input/Output  buffers  and their sizes */
    XDAS_Int8* inputbuf[XDM_MAX_IO_BUFFERS];
    XDAS_Int8* pOutputBuf  [XDM_MAX_IO_BUFFERS];
    XDAS_Int32 inputbufsize[XDM_MAX_IO_BUFFERS];
    XDAS_Int32 outBufSize  [XDM_MAX_IO_BUFFERS];
    XDAS_Int32 ii;
    XDAS_UInt32 topSliceline, bottomSliceline;

    IVIDENC_Fxns         *ividEncfxns;
    IVIDENC_InArgs       *inArgs;
    IVIDENC_OutArgs      *outArgs;
    IVIDENC_Params       *staticParams;
    IVIDENC_DynamicParams *dynamicParams;
    IVIDENC_Status       *status;
    IVIDMC_t             mcVidEncParams;
    void *encFxns;
    XDAS_UInt32  BytesRead=0;
    XDAS_UInt32  TSCL_Begin, TSCL_End;

    /* Algorithm specific handle */
    IALG_Handle handle;

    /* Input/Output Buffer Descriptor variables */
    XDM_BufDesc inputBufDesc;
    XDM_BufDesc outputBufDesc;

    XDAS_UInt32 bytesConsumed;

    /* Other variables */
    XDAS_UInt32  frameCount;
    XDAS_Int32   retVal;
    XDAS_Int32   RetVal=0;
    vidEncoderAPI_t *encAPI;
    IRES_Fxns    *resFxns;

    siuVct_DDR_Init();

    if(DNUM==CORE_TEAM_MAPPING[0])
      siuVct_FileIO_Start();

    bytesConsumed = 0;

    encAPI = getVidEncoderAPI(codecname);

    if (encAPI == NULL){
       printf("Encoder is not installed .. exiting \n");
       return -1;
    }

    if(encAPI->encInstanceSize >= MAX_ENC_INST_SIZE) {
       printf("Encoder Instance Size Exceeds allocated value.. exiting \n");
       return -1;
    }
    encAPI->encInitInstance((void *)encInst2);

    /* Initialization of multi-core params. Do this before encSetStaticParams
        so that it can be populated to StaticParams */
    mcVidEncParams.swbarr        = siuVidMc_Swbarr;
    mcVidEncParams.shmmap        = siuVidMc_Map_Shmem;
    mcVidEncParams.shmmunmap     = siuVidMc_Unmap_Shmem;
    mcVidEncParams.shmmap_sync   = siuVidMc_Shmem_Sync;
    mcVidEncParams.Lock          = NULL;
    mcVidEncParams.UnLock        = NULL;
    mcVidEncParams.ncores        = nCores;
    mcVidEncParams.core_task_ID  = (DNUM==CORE_TEAM_MAPPING[0])?IVIDMC_TASK_MASTER:IVIDMC_TASK_SLAVE;
    mcVidEncParams.coreID        = DNUM;
    siuVidMc_Init_ShmemTab(DNUM);

    /* Specify top and bottom slice lines for different number of cores. */
    /* Note that the number of image rows must be multiple of 16s        */ 
    if(hdResolution == 1080) {
    /* To obtain better load balancing on 1080p, specify a smaller image area
       for the master core and have equal distribution for the remaining cores*/
        if (nCores == 1) {
            topSliceline = 0;
            bottomSliceline = 1088;
        }
        if (nCores == 2) {
            if (DNUM == 0) {
                topSliceline = 0;
                bottomSliceline = 528;
            } else {
                topSliceline = 528;
                bottomSliceline = 1088;
            }
        }
        if (nCores == 3) {
            if (DNUM == 0) {
                topSliceline = 0;
                bottomSliceline = 352;
            } else if (DNUM == 1) {
                topSliceline = 352;
                bottomSliceline = 720;
            } else if (DNUM == 2) {
                topSliceline = 720;
                bottomSliceline = 1088;
            }
        }
        if (nCores == 4) {
            if (DNUM == 0) {
                topSliceline = 0;
                bottomSliceline = 272;
            } else if (DNUM == 1) {
                topSliceline = 272;
                bottomSliceline = 544;
            } else if (DNUM == 2) {
                topSliceline = 544;
                bottomSliceline = 816;
            } else if (DNUM == 3) {
                topSliceline = 816;
                bottomSliceline = 1088;
            }
        }
        if (nCores == 6) {
            if (DNUM == 0) {
                topSliceline = 0;
                bottomSliceline = 128;
            } else if (DNUM == 1) {
                topSliceline = 128;
                bottomSliceline = 320;
            } else if (DNUM == 2) {
                topSliceline = 320;
                bottomSliceline = 512;
            } else if (DNUM == 3) {
                topSliceline = 512;
                bottomSliceline = 704;
            } else if (DNUM == 4) {
                topSliceline = 704;
                bottomSliceline = 896;
            } else if (DNUM == 5) {
                topSliceline = 896;
                bottomSliceline = 1088;
            }
        }
    } else if (hdResolution == 720) {
        if (nCores == 1) {
            topSliceline = 0;
            bottomSliceline = 720;
        }
        if (nCores == 2) {
            if (DNUM == 0) {
                topSliceline = 0;
                bottomSliceline = 352;
            } else {
                topSliceline = 352;
                bottomSliceline = 720;
            }
        }
        if (nCores == 3) {
            if (DNUM == 0) {
                topSliceline = 0;
                bottomSliceline = 240;
            } else if (DNUM == 1) {
                topSliceline = 240;
                bottomSliceline = 480;
            } else if (DNUM == 2) {
                topSliceline = 480;
                bottomSliceline = 720;
            }
        }
        if (nCores == 4) {
            if (DNUM == 0) {
                topSliceline = 0;
                bottomSliceline = 192;
            } else if (DNUM == 1) {
                topSliceline = 192;
                bottomSliceline = 368;
            } else if (DNUM == 2) {
                topSliceline = 368;
                bottomSliceline = 544;
            } else if (DNUM == 3) {
                topSliceline = 544;
                bottomSliceline = 720;
            }
        }
        if (nCores == 6)
        {
            if (DNUM == 0)
            {
                topSliceline = 0;
                bottomSliceline = 112;
            }
            else if (DNUM == 1)
            {
                topSliceline = 112;
                bottomSliceline = 224;
            }
            else if (DNUM == 2)
            {
                topSliceline = 224;
                bottomSliceline = 336;
            }
            else if (DNUM == 3)
            {
                topSliceline = 336;
                bottomSliceline = 464;
            }
            else if (DNUM == 4)
            {
                topSliceline = 464;
                bottomSliceline = 592;
            }
            else if (DNUM == 5)
            {
                topSliceline = 592;
                bottomSliceline = 720;
            }
        }
    }
    else if(hdResolution == 288)
    {
    	
    	topSliceline = 0;
    	bottomSliceline = 288;
    }

    encAPI->encSetMcParams((void *)encInst2, &mcVidEncParams, topSliceline, bottomSliceline);

    /* Initialization of parameters needed for Algorithm Instance create */
    encAPI->encSetStaticParams((void *)encInst2, (void *)codecParams);

    /* Get XDM0.9 pointers from the codec */
    encFxns               =  encAPI->encGetFxns((void *)encInst2);
    inArgs                = (IVIDENC_InArgs *)encAPI->encGetInArgs((void *)encInst2);
    outArgs               = (IVIDENC_OutArgs *)encAPI->encGetOutArgs((void *)encInst2);
    staticParams          = (IVIDENC_Params *)encAPI->encGetStaticParams((void *)encInst2);
    dynamicParams         = (IVIDENC_DynamicParams *)encAPI->encGetDynamicParams((void *)encInst2);
    status                = (IVIDENC_Status *)encAPI->encGetStatus((void *)encInst2);

    /* Initializing Variables */
    frameCount                   = 0; /* Tracks the number of frames decoded */

    siuVidMc_Swbarr(DNUM,IVIDMC_SWBARR10,nCores);

    if ((handle =  (IALG_Handle)ALG_create (
        (IALG_Fxns *) encFxns,
        (IALG_Handle) NULL,
        (IALG_Params *) staticParams)) == NULL)
    {
        printf("\nFailed to Create Instance... Exiting for this configuration..");
        return -1;
    }

    siuVidMc_Swbarr(DNUM,IVIDMC_SWBARR11,nCores);

    /* Assigning Algorithm handle fxns field to ividdecfxns */
    ividEncfxns = (IVIDENC_Fxns *)handle->fxns ;

    printf("\nAlgorithm Instance Creation Done...\n");

    /* Set run time dynamic parameters */
    encAPI->encSetDynamicParams((void *)encInst2, (void *)staticParams);

    /* set space for buffer descriptors                                       */
    outputBufDesc.bufs = (XDAS_Int8 **)pOutputBuf;
    outputBufDesc.bufSizes = (XDAS_Int32 *)outBufSize ;

    /* Assign resources to the algorithm */
    if(siuContext.rmanConfigFlag) {
      resFxns = encAPI->encGetIresFxns();

      /* Activate the Algorithm                                               */
      handle->fxns->algActivate(handle);
      if (siu_osal_fc_assign_resources((void *)handle, (void *)resFxns) == FALSE) {
          printf("Assign Resource Failed \n");
          return (-1);
      }      
     /* Deactivate the Algorithm                                              */
      handle->fxns->algDeactivate(handle);
      siu_osal_wbinv_cache_all();
      siuContext.rmanConfigFlag = 0;
    }

    if(DNUM == CORE_TEAM_MAPPING[0])
    {
        for(ii = 0; ii < MC_ENC_MAXCORES; ii ++)
        {
            bytesGeneratedABCD[ii] = 0;
            outputSLICES_ABCD[ii]  = 0;
        }
        /*-----------------------------------------------------------------------*/
        /*  End of stream indicator to slave. if encMultiCoreSync[0] is set         */
        /*  to 1 then slave exits the frame processing loop                      */
        /*-----------------------------------------------------------------------*/
        encMultiCoreSync[0] = 0;
    }

    siuVidMc_Swbarr(DNUM,IVIDMC_SWBARR12,nCores);

    /* Do-While Loop for Encode Call                                          */
    do
    {
        /* Activate the Algorithm                                               */
        handle->fxns->algActivate(handle);

        /* Optional: Set Run time parameters in the Algorithm via control()     */
        RetVal = ividEncfxns->control((IVIDENC_Handle)handle, XDM_SETPARAMS,
            (IVIDENC_DynamicParams *)dynamicParams, (IVIDENC_Status *)status);

        if(RetVal == XDM_EFAIL)
        {
            printf("\n Control function returned an Error...  ");
            break; /* Error Condition: Application may want to break off          */
        }

        RetVal= ividEncfxns->control((IVIDENC_Handle)handle,
            XDM_GETBUFINFO,
            (IVIDENC_DynamicParams *)dynamicParams,
            (IVIDENC_Status *)status);

        if(RetVal == XDM_EFAIL)
        {
            printf("\n Control function returned an Error...  ");
            break; /* Error Condition: Application may want to break off          */
        }

        handle->fxns->algDeactivate(handle);

        /*Fill up the buffers as required by algorithm                            */
        inputBufDesc.numBufs = status->bufInfo.minNumInBufs ;
        inputBufDesc.bufs = inputbuf;
        inputBufDesc.bufs[0]  = inputData;
        inputBufDesc.bufSizes = inputbufsize;
        inputBufDesc.bufSizes[0] = status->bufInfo.minInBufSize[0];

        for(ii=0; ii< (status->bufInfo.minNumInBufs - 1);ii++ )
        {
            inputBufDesc.bufs[ii+1] = inputBufDesc.bufs[ii] +
                status->bufInfo.minInBufSize[ii];
            inputBufDesc.bufSizes[ii+1]  =
                status->bufInfo.minInBufSize[ii +1];
        }

        outputBufDesc.numBufs     = status->bufInfo.minNumOutBufs ;

        for(ii = 0; ii < MC_ENC_MAXCORES; ii ++)
        {
          if((CORE_TEAM_MAPPING[ii] != IVIDMC_CORE_NOT_USED) && (DNUM == CORE_TEAM_MAPPING[ii]))
          {
            outputBufDesc.bufs[0] = (XDAS_Int8 *)&outputData_ABCD[DNUM][0];
            break;
          }
        }
        outputBufDesc.bufSizes[0] = status->bufInfo.minOutBufSize[0];

        for(ii=0; ii< (status->bufInfo.minNumOutBufs-1); ii++ )
        {
            outputBufDesc.bufs[ii+1] = outputBufDesc.bufs[ii] +
                status->bufInfo.minOutBufSize[ii];
            outputBufDesc.bufSizes[ii+1] =
                status->bufInfo.minOutBufSize[ii+1];
        }

        if(DNUM==CORE_TEAM_MAPPING[0]) {
            /* Read from Input File                                                 */
            BytesRead = 0;
            for(ii=0; ii < inputBufDesc.numBufs ; ii++)
            {
              bytesConsumed   = siuVct_ReadInputData(inputBufDesc.bufs[ii], inputBufDesc.bufSizes[ii], bytesConsumed);
              BytesRead      += bytesConsumed;
            }

            if( BytesRead == 0) {
              printf("\n Bitstream Ended...  ");
              encMultiCoreSync[0] = 1;
              siuVidMc_Swbarr(DNUM,IVIDMC_SWBARR13,nCores);
              break; /* Bitstream ended: Break off the while loop                  */
            }
        }
        siuVidMc_Swbarr(DNUM,IVIDMC_SWBARR13,nCores);
        if(DNUM!=CORE_TEAM_MAPPING[0])
        {
            /*-------------------------------------------------------------------------*/
            /* Slave core - Check for end of frames                                    */
            /*-------------------------------------------------------------------------*/
            if(encMultiCoreSync[0])
                break;
        }

        handle->fxns->algActivate(handle);

        TSCL_Begin = TSCL;

        /* Basic Algorithm process() call */
        retVal = ividEncfxns->process((IVIDENC_Handle)handle,
            (XDM_BufDesc *)&inputBufDesc,
            (XDM_BufDesc *)&outputBufDesc,
            (IVIDENC_InArgs *)inArgs,
            (IVIDENC_OutArgs *)outArgs);

        TSCL_End   = TSCL;

        if(retVal == XDM_EFAIL)
        {
            printf("\n Process function returned an Error...  ");
            break; /* Error Condition: Application may want to break off   */
        }

        if (outArgs->bytesGenerated > max_output_bufsize)
            max_output_bufsize = outArgs->bytesGenerated;
        if (outArgs->bytesGenerated > CORE_OUTPUT_BUF_SIZE)
        {
            printf("\n Overflow on local output buffer...  ");
            break; /* Error Condition: Application may want to break off   */
        }

        /* Optional: Read status via control()                                  */
        ividEncfxns->control((IVIDENC_Handle)handle,
            XDM_GETSTATUS,
            (IVIDENC_DynamicParams *)dynamicParams,
            (IVIDENC_Status *)status);

        /* DeActivate the Algorithm                                             */
        handle->fxns->algDeactivate(handle);
        siu_osal_wbinv_cache_all();

        if(DNUM != CORE_TEAM_MAPPING[0])
        {
            printf("\n Encoded Frame # %d, Cycles consumed = %d  ",frameCount, (TSCL_End - TSCL_Begin));
            fflush(stdout);
            frameCount++;
        }
        siuVidMc_Swbarr(DNUM,IVIDMC_SWBARR14,nCores);

       if(DNUM == CORE_TEAM_MAPPING[0])
       {
            printf("\n M_Encoded Frame # %d, Cycles consumed = %d, BytesGenerated = %d, DNUM=%d ",frameCount, (TSCL_End - TSCL_Begin), outputSLICES_ABCD[0], DNUM);
            fflush(stdout);

            /* Write the output frame */
            siuVct_WriteOutputData((XDAS_Int8 *)outputData_ABCD[0], outputSLICES_ABCD[0]);
            frameCount++;

            outputSLICES_ABCD[0] = 0;

            for(ii = 1; ii < MC_ENC_MAXCORES; ii ++)
            {
                if(CORE_TEAM_MAPPING[ii] == IVIDMC_CORE_NOT_USED)
                {
                    continue;
                }
                siuVct_WriteOutputData((XDAS_Int8 *)outputData_ABCD[ii], outputSLICES_ABCD[ii]);
                printf("\n S_Encoded Frame # %d, Cycles consumed = %d, BytesGenerated = %d, DNUM=%d ",frameCount-1, (TSCL_End - TSCL_Begin), outputSLICES_ABCD[ii], DNUM);
                fflush(stdout);
                outputSLICES_ABCD[ii] = 0;
            }

            /* Check for Break off condition */
            if (frameCount >= framesToEncode)
            {
                printf("\n Specified number of Frames Encoded...  ");
                encMultiCoreSync[0] = 1;
            }
       }

       siuVidMc_Swbarr(DNUM,IVIDMC_SWBARR15,nCores);
       if(encMultiCoreSync[0])
       {
           break;
       }
    }  while(1);  /* end of Do-While loop */

    /* Delete the Algorithm instance object specified by handle */
    ALG_delete (handle);

    if(frameCount == 0)
    {
        frameCount = 1; /* To avoid division with zero */
    }

    if(DNUM==CORE_TEAM_MAPPING[0]) {
        int totalBytes = 0;
        siuVct_FileIO_Stop();


        printf("\n --------------  SUMMARY --------------------\n");
        for(ii = 0; ii < MC_ENC_MAXCORES; ii ++)
        {
            if(CORE_TEAM_MAPPING[ii] == IVIDMC_CORE_NOT_USED)
            {
                continue;
            }
            totalBytes += bytesGeneratedABCD[ii];
        }
        printf (" TOTAL bytes=%d\n", totalBytes);
        printf("  Total number of Frames          = %d\n",
            (XDAS_UInt32)frameCount);
        printf("  Bit Rate at 30 frames/Sec       = %d Kbps\n",(
            XDAS_UInt32)(((totalBytes*8*30)/frameCount)/1000));
        printf("  Width and Height                = %d, %d \n",
            dynamicParams->inputWidth,
            dynamicParams->inputHeight);
        printf (" --------------    END   --------------------\n");
    } else {
        printf("\nSee master core output for summary.\n");
    }

    printf("\n End of execution\n");

    /* return the number of bytes written to DDR, i.e codec output */
    return(0);
} /* tvct_test_entry_encode_xdm0p9 */

/* nothing past this point */
