/* ------------------------------------------------------------------
 * Copyright (C) 1998-2009 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.
 * -------------------------------------------------------------------
 */
#define LOG_TAG "pvdec_api"
#include <log/log.h>
#include "mp4dec_lib.h"
#include "vlc_decode.h"
#include "bitstream.h"

#ifndef INT32_MAX
#define INT32_MAX 0x7fffffff
#endif

#ifndef SIZE_MAX
#define SIZE_MAX ((size_t) -1)
#endif

#define OSCL_DISABLE_WARNING_CONDITIONAL_IS_CONSTANT

#ifdef DEC_INTERNAL_MEMORY_OPT
#define QCIF_MBS 99
#define QCIF_BS (4*QCIF_MBS)
#define QCIF_MB_ROWS 11
extern uint8                IMEM_sliceNo[QCIF_MBS];
extern uint8                IMEM_acPredFlag[QCIF_MBS];
extern uint8                IMEM_headerInfo_Mode[QCIF_MBS];
extern uint8                IMEM_headerInfo_CBP[QCIF_MBS];
extern int                  IMEM_headerInfo_QPMB[QCIF_MBS];
extern MacroBlock           IMEM_mblock;
extern MOT                  IMEM_motX[QCIF_BS];
extern MOT                  IMEM_motY[QCIF_BS];
extern BitstreamDecVideo    IMEM_BitstreamDecVideo[4];
extern typeDCStore          IMEM_predDC[QCIF_MBS];
extern typeDCACStore        IMEM_predDCAC_col[QCIF_MB_ROWS+1];

extern VideoDecData         IMEM_VideoDecData[1];
extern Vop                  IMEM_currVop[1];
extern Vop                  IMEM_prevVop[1];
extern PIXEL                IMEM_currVop_yChan[QCIF_MBS*128*3];
extern PIXEL                IMEM_prevVop_yChan[QCIF_MBS*128*3];
extern uint8                IMEM_pstprcTypCur[6*QCIF_MBS];
extern uint8                IMEM_pstprcTypPrv[6*QCIF_MBS];


extern Vop                  IMEM_vopHEADER[2];
extern Vol                  IMEM_VOL[2];
extern Vop                  IMEM_vopHeader[2][1];
extern Vol                  IMEM_vol[2][1];

#endif

/* ======================================================================== */
/*  Function : PVInitVideoDecoder()                                         */
/*  Date     : 04/11/2000, 08/29/2000                                       */
/*  Purpose  : Initialization of the MPEG-4 video decoder library.          */
/*             The return type is Bool instead of PV_STATUS because         */
/*             we don't want to expose PV_STATUS to (outside) programmers   */
/*             that use our decoder library SDK.                            */
/*  In/out   :                                                              */
/*  Return   : PV_TRUE if successed, PV_FALSE if failed.                    */
/*  Modified :                                                              */
/* ======================================================================== */
OSCL_EXPORT_REF Bool PVInitVideoDecoder(VideoDecControls *decCtrl, uint8 *volbuf[],
                                        int32 *volbuf_size, int nLayers, int width, int height, MP4DecodingMode mode)
{
    VideoDecData *video = (VideoDecData *) decCtrl->videoDecoderData;
    Bool status = PV_TRUE;
    int idx;
    BitstreamDecVideo *stream;


    oscl_memset(decCtrl, 0, sizeof(VideoDecControls)); /* fix a size bug.   03/28/2001 */
    decCtrl->nLayers = nLayers;
    for (idx = 0; idx < nLayers; idx++)
    {
        decCtrl->volbuf[idx] = volbuf[idx];
        decCtrl->volbuf_size[idx] = volbuf_size[idx];
    }

    /* memory allocation & initialization */
#ifdef DEC_INTERNAL_MEMORY_OPT
    video = IMEM_VideoDecData;
#else
    video = (VideoDecData *) oscl_malloc(sizeof(VideoDecData));
#endif
    if (video != NULL)
    {
        oscl_memset(video, 0, sizeof(VideoDecData));
        video->memoryUsage = sizeof(VideoDecData);
        video->numberOfLayers = nLayers;
#ifdef DEC_INTERNAL_MEMORY_OPT
        video->vol = (Vol **) IMEM_VOL;
#else
        if ((size_t)nLayers > SIZE_MAX / sizeof(Vol *)) {
            status = PV_FALSE;
            goto fail;
        }

        video->vol = (Vol **) oscl_malloc(nLayers * sizeof(Vol *));
#endif
        if (video->vol == NULL) status = PV_FALSE;
        video->memoryUsage += nLayers * sizeof(Vol *);


        /* we need to setup this pointer for the application to */
        /*    pass it around.                                   */
        decCtrl->videoDecoderData = (void *) video;
        video->videoDecControls = decCtrl;  /* yes. we have a cyclic */
        /* references here :)    */

        /* Allocating Vop space, this has to change when we add */
        /*    spatial scalability to the decoder                */
#ifdef DEC_INTERNAL_MEMORY_OPT
        video->currVop = IMEM_currVop;
        if (video->currVop == NULL) status = PV_FALSE;
        else oscl_memset(video->currVop, 0, sizeof(Vop));
        video->prevVop = IMEM_prevVop;
        if (video->prevVop == NULL) status = PV_FALSE;
        else oscl_memset(video->prevVop, 0, sizeof(Vop));
        video->memoryUsage += (sizeof(Vop) * 2);
        video->vopHeader = (Vop **) IMEM_vopHEADER;
#else

        video->currVop = (Vop *) oscl_malloc(sizeof(Vop));
        if (video->currVop == NULL) status = PV_FALSE;
        else oscl_memset(video->currVop, 0, sizeof(Vop));
        video->prevVop = (Vop *) oscl_malloc(sizeof(Vop));
        if (video->prevVop == NULL) status = PV_FALSE;
        else oscl_memset(video->prevVop, 0, sizeof(Vop));
        video->memoryUsage += (sizeof(Vop) * 2);

        if ((size_t)nLayers > SIZE_MAX / sizeof(Vop *)) {
            status = PV_FALSE;
            goto fail;
        }

        video->vopHeader = (Vop **) oscl_malloc(sizeof(Vop *) * nLayers);
#endif
        if (video->vopHeader == NULL) status = PV_FALSE;
        else oscl_memset(video->vopHeader, 0, sizeof(Vop *)*nLayers);
        video->memoryUsage += (sizeof(Vop *) * nLayers);

        video->initialized = PV_FALSE;
        /* Decode the header to get all information to allocate data */
        if (status == PV_TRUE)
        {
            /* initialize decoded frame counter.   04/24/2001 */
            video->frame_idx = -1;


            for (idx = 0; idx < nLayers; idx++)
            {

#ifdef DEC_INTERNAL_MEMORY_OPT
                video->vopHeader[idx] = IMEM_vopHeader[idx];
#else
                video->vopHeader[idx] = (Vop *) oscl_malloc(sizeof(Vop));
#endif
                if (video->vopHeader[idx] == NULL)
                {
                    status = PV_FALSE;
                    break;
                }
                else
                {
                    oscl_memset(video->vopHeader[idx], 0, sizeof(Vop));
                    video->vopHeader[idx]->timeStamp = 0;
                    video->memoryUsage += (sizeof(Vop));
                }
#ifdef DEC_INTERNAL_MEMORY_OPT
                video->vol[idx] = IMEM_vol[idx];
                video->memoryUsage += sizeof(Vol);
                oscl_memset(video->vol[idx], 0, sizeof(Vol));
                if (video->vol[idx] == NULL) status = PV_FALSE;
                stream = IMEM_BitstreamDecVideo;
#else
                video->vol[idx] = (Vol *) oscl_malloc(sizeof(Vol));
                if (video->vol[idx] == NULL)
                {
                    status = PV_FALSE;
                    break;
                }
                else
                {
                    video->memoryUsage += sizeof(Vol);
                    oscl_memset(video->vol[idx], 0, sizeof(Vol));
                }

                stream = (BitstreamDecVideo *) oscl_malloc(sizeof(BitstreamDecVideo));
#endif
                video->memoryUsage += sizeof(BitstreamDecVideo);
                if (stream == NULL)
                {
                    status = PV_FALSE;
                    break;
                }
                else
                {
                    int32 buffer_size;
                    if ((buffer_size = BitstreamOpen(stream, idx)) < 0)
                    {
                        mp4dec_log("InitVideoDecoder(): Can't allocate bitstream buffer.\n");
                        status = PV_FALSE;
                        break;
                    }
                    video->memoryUsage += buffer_size;
                    video->vol[idx]->bitstream = stream;
                    video->vol[idx]->volID = idx;
                    video->vol[idx]->timeInc_offset = 0;  /*  11/12/01 */
                    video->vlcDecCoeffIntra = &VlcDecTCOEFShortHeader;
                    video->vlcDecCoeffInter = &VlcDecTCOEFShortHeader;
                    if (mode == MPEG4_MODE)
                    {
                        /* Set up VOL header bitstream for frame-based decoding.  08/30/2000 */
                        BitstreamReset(stream, decCtrl->volbuf[idx], decCtrl->volbuf_size[idx]);

                        switch (DecodeVOLHeader(video, idx))
                        {
                            case PV_SUCCESS :
                                if (status == PV_TRUE)
                                    status = PV_TRUE;   /*  we want to make sure that if first layer is bad, second layer is good return PV_FAIL */
                                else
                                    status = PV_FALSE;
                                break;
#ifdef PV_TOLERATE_VOL_ERRORS
                            case PV_BAD_VOLHEADER:
                                status = PV_TRUE;
                                break;
#endif
                            default :
                                status = PV_FALSE;
                                break;
                        }

                    }
                    else
                    {
                        video->shortVideoHeader = PV_TRUE;
                    }

                    if (video->shortVideoHeader == PV_TRUE)
                    {
                        mode = H263_MODE;
                        /* Set max width and height.  In H.263 mode, we use    */
                        /*  volbuf_size[0] to pass in width and volbuf_size[1] */
                        /*  to pass in height.                    04/23/2001 */
                        video->prevVop->temporalRef = 0; /*  11/12/01 */
                        /* Compute some convenience variables:   04/23/2001 */
                        video->vol[idx]->quantType = 0;
                        video->vol[idx]->quantPrecision = 5;
                        video->vol[idx]->errorResDisable = 1;
                        video->vol[idx]->dataPartitioning = 0;
                        video->vol[idx]->useReverseVLC = 0;
                        video->intra_acdcPredDisable = 1;
                        video->vol[idx]->scalability = 0;

                        video->displayWidth = width;
                        video->displayHeight = height;
                        video->width = (width + 15) & -16;
                        video->height = (height + 15) & -16;
                        video->size = (int32)video->width * video->height;

#ifdef PV_ANNEX_IJKT_SUPPORT
                        video->modified_quant = 0;
                        video->advanced_INTRA = 0;
                        video->deblocking = 0;
                        video->slice_structure = 0;
#endif
                    }

                }
            }

        }
        if (status != PV_FALSE)
        {
            status = PVAllocVideoData(decCtrl, width, height, nLayers);
            video->initialized = PV_TRUE;
        }
    }
    else
    {
        status = PV_FALSE;
    }

fail:
    if (status == PV_FALSE) PVCleanUpVideoDecoder(decCtrl);

    return status;
}

Bool PVAllocVideoData(VideoDecControls *decCtrl, int width, int height, int nLayers)
{
    VideoDecData *video = (VideoDecData *) decCtrl->videoDecoderData;
    Bool status = PV_TRUE;
    int nTotalMB;
    int nMBPerRow;
    int32 size;

    if (video->shortVideoHeader == PV_TRUE)
    {
        video->displayWidth = width;
        video->displayHeight = height;
        video->width = (width + 15) & -16;
        video->height = (height + 15) & -16;

        video->nMBPerRow =
            video->nMBinGOB  = video->width / MB_SIZE;
        video->nMBPerCol =
            video->nGOBinVop = video->height / MB_SIZE;
        video->nTotalMB =
            video->nMBPerRow * video->nMBPerCol;
    }

    if (((uint64_t)video->width * video->height) > (uint64_t)INT32_MAX / sizeof(PIXEL)) {
        return PV_FALSE;
    }

    size = (int32)sizeof(PIXEL) * video->width * video->height;
#ifdef PV_MEMORY_POOL
    decCtrl->size = size;
#else
#ifdef DEC_INTERNAL_MEMORY_OPT
    video->currVop->yChan = IMEM_currVop_yChan; /* Allocate memory for all VOP OKA 3/2/1*/
    if (video->currVop->yChan == NULL) status = PV_FALSE;
    video->currVop->uChan = video->currVop->yChan + size;
    video->currVop->vChan = video->currVop->uChan + (size >> 2);

    video->prevVop->yChan = IMEM_prevVop_yChan; /* Allocate memory for all VOP OKA 3/2/1*/
    if (video->prevVop->yChan == NULL) status = PV_FALSE;
    video->prevVop->uChan = video->prevVop->yChan + size;
    video->prevVop->vChan = video->prevVop->uChan + (size >> 2);
#else
    if (size > INT32_MAX / 3) {
        return PV_FALSE;
    }
    video->currVop->yChan = (PIXEL *) oscl_malloc(size * 3 / 2); /* Allocate memory for all VOP OKA 3/2/1*/
    if (video->currVop->yChan == NULL) status = PV_FALSE;

    video->currVop->uChan = video->currVop->yChan + size;
    video->currVop->vChan = video->currVop->uChan + (size >> 2);
    video->prevVop->yChan = (PIXEL *) oscl_malloc(size * 3 / 2); /* Allocate memory for all VOP OKA 3/2/1*/
    if (video->prevVop->yChan == NULL) status = PV_FALSE;

    video->prevVop->uChan = video->prevVop->yChan + size;
    video->prevVop->vChan = video->prevVop->uChan + (size >> 2);
#endif
    video->memoryUsage += (size * 3);
#endif   // MEMORY_POOL
    /* Note that baseVop, enhcVop is only used to hold enhancement */
    /*    layer header information.                  05/04/2000  */
    if (nLayers > 1)
    {
        video->prevEnhcVop = (Vop *) oscl_malloc(sizeof(Vop));
        video->memoryUsage += (sizeof(Vop));
        if (video->prevEnhcVop == NULL)
        {
            status = PV_FALSE;
        }
        else
        {
            oscl_memset(video->prevEnhcVop, 0, sizeof(Vop));
#ifndef PV_MEMORY_POOL
            if (size > INT32_MAX / 3) {
                return PV_FALSE;
            }

            video->prevEnhcVop->yChan = (PIXEL *) oscl_malloc(size * 3 / 2); /* Allocate memory for all VOP OKA 3/2/1*/
            if (video->prevEnhcVop->yChan == NULL) status = PV_FALSE;
            video->prevEnhcVop->uChan = video->prevEnhcVop->yChan + size;
            video->prevEnhcVop->vChan = video->prevEnhcVop->uChan + (size >> 2);
            video->memoryUsage += (3 * size / 2);
#endif
        }
    }

    /* Allocating space for slices, AC prediction flag, and */
    /*    AC/DC prediction storage */
    nTotalMB = video->nTotalMB;
    nMBPerRow = video->nMBPerRow;

#ifdef DEC_INTERNAL_MEMORY_OPT
    video->sliceNo = (uint8 *)(IMEM_sliceNo);
    if (video->sliceNo == NULL) status = PV_FALSE;
    video->memoryUsage += nTotalMB;
    video->acPredFlag = (uint8 *)(IMEM_acPredFlag);
    if (video->acPredFlag == NULL) status = PV_FALSE;
    video->memoryUsage += (nTotalMB);
    video->predDC = (typeDCStore *)(IMEM_predDC);
    if (video->predDC == NULL) status = PV_FALSE;
    video->memoryUsage += (nTotalMB * sizeof(typeDCStore));
    video->predDCAC_col = (typeDCACStore *)(IMEM_predDCAC_col);
    if (video->predDCAC_col == NULL) status = PV_FALSE;
    video->memoryUsage += ((nMBPerRow + 1) * sizeof(typeDCACStore));
    video->predDCAC_row = video->predDCAC_col + 1;
    video->headerInfo.Mode = (uint8 *)(IMEM_headerInfo_Mode);
    if (video->headerInfo.Mode == NULL) status = PV_FALSE;
    video->memoryUsage += nTotalMB;
    video->headerInfo.CBP = (uint8 *)(IMEM_headerInfo_CBP);
    if (video->headerInfo.CBP == NULL) status = PV_FALSE;
    video->memoryUsage += nTotalMB;
    video->QPMB = (int *)(IMEM_headerInfo_QPMB);
    if (video->QPMB == NULL) status = PV_FALSE;
    video->memoryUsage += (nTotalMB * sizeof(int));
    video->mblock = &IMEM_mblock;
    if (video->mblock == NULL) status = PV_FALSE;
    oscl_memset(video->mblock->block, 0, sizeof(int16)*6*NCOEFF_BLOCK); //  Aug 23,2005

    video->memoryUsage += sizeof(MacroBlock);
    video->motX = (MOT *)(IMEM_motX);
    if (video->motX == NULL) status = PV_FALSE;
    video->motY = (MOT *)(IMEM_motY);
    if (video->motY == NULL) status = PV_FALSE;
    video->memoryUsage += (sizeof(MOT) * 8 * nTotalMB);
#else
    video->sliceNo = (uint8 *) oscl_malloc(nTotalMB);
    if (video->sliceNo == NULL) status = PV_FALSE;
    video->memoryUsage += nTotalMB;

    video->acPredFlag = (uint8 *) oscl_malloc(nTotalMB * sizeof(uint8));
    if (video->acPredFlag == NULL) status = PV_FALSE;
    video->memoryUsage += (nTotalMB);

    if ((size_t)nTotalMB > SIZE_MAX / sizeof(typeDCStore)) {
        return PV_FALSE;
    }
    video->predDC = (typeDCStore *) oscl_malloc(nTotalMB * sizeof(typeDCStore));
    if (video->predDC == NULL) status = PV_FALSE;
    video->memoryUsage += (nTotalMB * sizeof(typeDCStore));

    if (nMBPerRow > INT32_MAX - 1
            || (size_t)(nMBPerRow + 1) > SIZE_MAX / sizeof(typeDCACStore)) {
        return PV_FALSE;
    }
    video->predDCAC_col = (typeDCACStore *) oscl_malloc((nMBPerRow + 1) * sizeof(typeDCACStore));
    if (video->predDCAC_col == NULL) status = PV_FALSE;
    video->memoryUsage += ((nMBPerRow + 1) * sizeof(typeDCACStore));

    /* element zero will be used for storing vertical (col) AC coefficients */
    /*  the rest will be used for storing horizontal (row) AC coefficients  */
    video->predDCAC_row = video->predDCAC_col + 1;        /*  ACDC */

    /* Allocating HeaderInfo structure & Quantizer array */
    video->headerInfo.Mode = (uint8 *) oscl_malloc(nTotalMB);
    if (video->headerInfo.Mode == NULL) status = PV_FALSE;
    video->memoryUsage += nTotalMB;
    video->headerInfo.CBP = (uint8 *) oscl_malloc(nTotalMB);
    if (video->headerInfo.CBP == NULL) status = PV_FALSE;
    video->memoryUsage += nTotalMB;

    if ((size_t)nTotalMB > SIZE_MAX / sizeof(int16)) {
        return PV_FALSE;
    }
    video->QPMB = (int16 *) oscl_malloc(nTotalMB * sizeof(int16));
    if (video->QPMB == NULL) status = PV_FALSE;
    video->memoryUsage += (nTotalMB * sizeof(int));

    /* Allocating macroblock space */
    video->mblock = (MacroBlock *) oscl_malloc(sizeof(MacroBlock));
    if (video->mblock == NULL)
    {
        status = PV_FALSE;
    }
    else
    {
        oscl_memset(video->mblock->block, 0, sizeof(int16)*6*NCOEFF_BLOCK); //  Aug 23,2005

        video->memoryUsage += sizeof(MacroBlock);
    }
    /* Allocating motion vector space */
    if ((size_t)nTotalMB > SIZE_MAX / (sizeof(MOT) * 4)) {
        return PV_FALSE;
    }
    video->motX = (MOT *) oscl_malloc(sizeof(MOT) * 4 * nTotalMB);
    if (video->motX == NULL) status = PV_FALSE;
    video->motY = (MOT *) oscl_malloc(sizeof(MOT) * 4 * nTotalMB);
    if (video->motY == NULL) status = PV_FALSE;
    video->memoryUsage += (sizeof(MOT) * 8 * nTotalMB);
#endif

#ifdef PV_POSTPROC_ON
    /* Allocating space for post-processing Mode */
#ifdef DEC_INTERNAL_MEMORY_OPT
    video->pstprcTypCur = IMEM_pstprcTypCur;
    video->memoryUsage += (nTotalMB * 6);
    if (video->pstprcTypCur == NULL)
    {
        status = PV_FALSE;
    }
    else
    {
        oscl_memset(video->pstprcTypCur, 0, 4*nTotalMB + 2*nTotalMB);
    }

    video->pstprcTypPrv = IMEM_pstprcTypPrv;
    video->memoryUsage += (nTotalMB * 6);
    if (video->pstprcTypPrv == NULL)
    {
        status = PV_FALSE;
    }
    else
    {
        oscl_memset(video->pstprcTypPrv, 0, nTotalMB*6);
    }

#else
    if (nTotalMB > INT32_MAX / 6) {
        return PV_FALSE;
    }
    video->pstprcTypCur = (uint8 *) oscl_malloc(nTotalMB * 6);
    video->memoryUsage += (nTotalMB * 6);
    if (video->pstprcTypCur == NULL)
    {
        status = PV_FALSE;
    }
    else
    {
        oscl_memset(video->pstprcTypCur, 0, 4*nTotalMB + 2*nTotalMB);
    }

    video->pstprcTypPrv = (uint8 *) oscl_malloc(nTotalMB * 6);
    video->memoryUsage += (nTotalMB * 6);
    if (video->pstprcTypPrv == NULL)
    {
        status = PV_FALSE;
    }
    else
    {
        oscl_memset(video->pstprcTypPrv, 0, nTotalMB*6);
    }

#endif

#endif

    /* initialize the decoder library */
    video->prevVop->predictionType = I_VOP;
    video->prevVop->timeStamp = 0;
#ifndef PV_MEMORY_POOL
    oscl_memset(video->prevVop->yChan, 16, sizeof(uint8)*size);     /*  10/31/01 */
    oscl_memset(video->prevVop->uChan, 128, sizeof(uint8)*size / 2);

    oscl_memset(video->currVop->yChan, 0, sizeof(uint8)*size*3 / 2);
    if (nLayers > 1)
    {
        oscl_memset(video->prevEnhcVop->yChan, 0, sizeof(uint8)*size*3 / 2);
        video->prevEnhcVop->timeStamp = 0;
    }
    video->concealFrame = video->prevVop->yChan;               /*  07/07/2001 */
    decCtrl->outputFrame = video->prevVop->yChan;              /*  06/19/2002 */
#endif

    /* always start from base layer */
    video->currLayer = 0;
    return status;
}

/* ======================================================================== */
/*  Function : PVResetVideoDecoder()                                        */
/*  Date     : 01/14/2002                                                   */
/*  Purpose  : Reset video timestamps                                       */
/*  In/out   :                                                              */
/*  Return   : PV_TRUE if successed, PV_FALSE if failed.                    */
/*  Modified :                                                              */
/* ======================================================================== */
Bool PVResetVideoDecoder(VideoDecControls *decCtrl)
{
    VideoDecData *video = (VideoDecData *) decCtrl->videoDecoderData;
    int idx;

    for (idx = 0; idx < decCtrl->nLayers; idx++)
    {
        video->vopHeader[idx]->timeStamp = 0;
    }
    video->prevVop->timeStamp = 0;
    if (decCtrl->nLayers > 1)
        video->prevEnhcVop->timeStamp = 0;

    oscl_memset(video->mblock->block, 0, sizeof(int16)*6*NCOEFF_BLOCK); //  Aug 23,2005

    return PV_TRUE;
}


/* ======================================================================== */
/*  Function : PVCleanUpVideoDecoder()                                      */
/*  Date     : 04/11/2000, 08/29/2000                                       */
/*  Purpose  : Cleanup of the MPEG-4 video decoder library.                 */
/*  In/out   :                                                              */
/*  Return   : PV_TRUE if successed, PV_FALSE if failed.                    */
/*  Modified :                                                              */
/* ======================================================================== */
OSCL_EXPORT_REF Bool PVCleanUpVideoDecoder(VideoDecControls *decCtrl)
{
    int idx;
    VideoDecData *video = (VideoDecData *) decCtrl->videoDecoderData;
#ifdef DEC_INTERNAL_MEMORY_OPT
    if (video)
    {
#ifdef PV_POSTPROC_ON
        video->pstprcTypCur = NULL;
        video->pstprcTypPrv = NULL;
#endif

        video->acPredFlag       = NULL;
        video->sliceNo          = NULL;
        video->motX             = NULL;
        video->motY             = NULL;
        video->mblock           = NULL;
        video->QPMB             = NULL;
        video->predDC           = NULL;
        video->predDCAC_row     = NULL;
        video->predDCAC_col     = NULL;
        video->headerInfo.Mode  = NULL;
        video->headerInfo.CBP   = NULL;
        if (video->numberOfLayers > 1)
        {
            if (video->prevEnhcVop)
            {
                video->prevEnhcVop->uChan = NULL;
                video->prevEnhcVop->vChan = NULL;
                if (video->prevEnhcVop->yChan) oscl_free(video->prevEnhcVop->yChan);
                oscl_free(video->prevEnhcVop);
            }
        }
        if (video->currVop)
        {
            video->currVop->uChan = NULL;
            video->currVop->vChan = NULL;
            if (video->currVop->yChan)
                video->currVop->yChan = NULL;
            video->currVop = NULL;
        }
        if (video->prevVop)
        {
            video->prevVop->uChan = NULL;
            video->prevVop->vChan = NULL;
            if (video->prevVop->yChan)
                video->prevVop->yChan = NULL;
            video->prevVop = NULL;
        }

        if (video->vol)
        {
            for (idx = 0; idx < video->numberOfLayers; idx++)
            {
                if (video->vol[idx])
                {
                    BitstreamClose(video->vol[idx]->bitstream);
                    video->vol[idx]->bitstream = NULL;
                    video->vol[idx] = NULL;
                }
                video->vopHeader[idx] = NULL;

            }
            video->vol = NULL;
            video->vopHeader = NULL;
        }

        video = NULL;
        decCtrl->videoDecoderData = NULL;
    }

#else

    if (video)
    {
#ifdef PV_POSTPROC_ON
        if (video->pstprcTypCur) oscl_free(video->pstprcTypCur);
        if (video->pstprcTypPrv) oscl_free(video->pstprcTypPrv);
#endif
        if (video->predDC) oscl_free(video->predDC);
        video->predDCAC_row = NULL;
        if (video->predDCAC_col) oscl_free(video->predDCAC_col);
        if (video->motX) oscl_free(video->motX);
        if (video->motY) oscl_free(video->motY);
        if (video->mblock) oscl_free(video->mblock);
        if (video->QPMB) oscl_free(video->QPMB);
        if (video->headerInfo.Mode) oscl_free(video->headerInfo.Mode);
        if (video->headerInfo.CBP) oscl_free(video->headerInfo.CBP);
        if (video->sliceNo) oscl_free(video->sliceNo);
        if (video->acPredFlag) oscl_free(video->acPredFlag);

        if (video->numberOfLayers > 1)
        {
            if (video->prevEnhcVop)
            {
                video->prevEnhcVop->uChan = NULL;
                video->prevEnhcVop->vChan = NULL;
                if (video->prevEnhcVop->yChan) oscl_free(video->prevEnhcVop->yChan);
                oscl_free(video->prevEnhcVop);
            }
        }
        if (video->currVop)
        {

#ifndef PV_MEMORY_POOL
            video->currVop->uChan = NULL;
            video->currVop->vChan = NULL;
            if (video->currVop->yChan)
                oscl_free(video->currVop->yChan);
#endif
            oscl_free(video->currVop);
        }
        if (video->prevVop)
        {
#ifndef PV_MEMORY_POOL
            video->prevVop->uChan = NULL;
            video->prevVop->vChan = NULL;
            if (video->prevVop->yChan)
                oscl_free(video->prevVop->yChan);
#endif
            oscl_free(video->prevVop);
        }

        if (video->vol)
        {
            for (idx = 0; idx < video->numberOfLayers; idx++)
            {
                if (video->vol[idx])
                {
                    if (video->vol[idx]->bitstream)
                    {
                        BitstreamClose(video->vol[idx]->bitstream);
                        oscl_free(video->vol[idx]->bitstream);
                    }
                    oscl_free(video->vol[idx]);
                }

            }
            oscl_free(video->vol);
        }

        for (idx = 0; idx < video->numberOfLayers; idx++)
        {
            if (video->vopHeader[idx]) oscl_free(video->vopHeader[idx]);
        }

        if (video->vopHeader) oscl_free(video->vopHeader);

        oscl_free(video);
        decCtrl->videoDecoderData = NULL;
    }
#endif
    return PV_TRUE;
}
/* ======================================================================== */
/*  Function : PVGetVideoDimensions()                                       */
/*  Date     : 040505                                                       */
/*  Purpose  :                                                              */
/*  In/out   :                                                              */
/*  Return   : the display_width and display_height of                      */
/*          the frame in the current layer.                                 */
/*  Note     : This is not a macro or inline function because we do         */
/*              not want to expose our internal data structure.             */
/*  Modified :                                                              */
/* ======================================================================== */
OSCL_EXPORT_REF void PVGetVideoDimensions(VideoDecControls *decCtrl, int32 *display_width, int32 *display_height)
{
    VideoDecData *video = (VideoDecData *)decCtrl->videoDecoderData;
    *display_width = video->displayWidth;
    *display_height = video->displayHeight;
}

OSCL_EXPORT_REF void PVGetBufferDimensions(VideoDecControls *decCtrl, int32 *width, int32 *height) {
    VideoDecData *video = (VideoDecData *)decCtrl->videoDecoderData;
    *width = video->width;
    *height = video->height;
}

/* ======================================================================== */
/*  Function : PVGetVideoTimeStamp()                                        */
/*  Date     : 04/27/2000, 08/29/2000                                       */
/*  Purpose  :                                                              */
/*  In/out   :                                                              */
/*  Return   : current time stamp in millisecond.                           */
/*  Note     :                                                              */
/*  Modified :                                                              */
/* ======================================================================== */
uint32 PVGetVideoTimeStamp(VideoDecControls *decCtrl)
{
    VideoDecData *video = (VideoDecData *)decCtrl->videoDecoderData;
    return video->currTimestamp;
}


/* ======================================================================== */
/*  Function : PVSetPostProcType()                                          */
/*  Date     : 07/07/2000                                                   */
/*  Purpose  :                                                              */
/*  In/out   :                                                              */
/*  Return   : Set post-processing filter type.                             */
/*  Note     :                                                              */
/*  Modified : . 08/29/2000 changes the name for consistency.               */
/* ======================================================================== */
OSCL_EXPORT_REF void PVSetPostProcType(VideoDecControls *decCtrl, int mode)
{
    VideoDecData *video = (VideoDecData *)decCtrl->videoDecoderData;
    video->postFilterType = mode;
}


/* ======================================================================== */
/*  Function : PVGetDecBitrate()                                            */
/*  Date     : 08/23/2000                                                   */
/*  Purpose  :                                                              */
/*  In/out   :                                                              */
/*  Return   : This function returns the average bits per second.           */
/*  Note     :                                                              */
/*  Modified :                                                              */
/* ======================================================================== */
int PVGetDecBitrate(VideoDecControls *decCtrl)
{
    VideoDecData *video = (VideoDecData *)decCtrl->videoDecoderData;
    int     idx;
    int32   sum = 0;

    for (idx = 0; idx < BITRATE_AVERAGE_WINDOW; idx++)
    {
        sum += video->nBitsPerVop[idx];
    }
    sum = (sum * video->frameRate) / (10 * BITRATE_AVERAGE_WINDOW);
    return (int) sum;
}


/* ======================================================================== */
/*  Function : PVGetDecFramerate()                                          */
/*  Date     : 08/23/2000                                                   */
/*  Purpose  :                                                              */
/*  In/out   :                                                              */
/*  Return   : This function returns the average frame per 10 second.       */
/*  Note     : The fps can be calculated by PVGetDecFramerate()/10          */
/*  Modified :                                                              */
/* ======================================================================== */
int PVGetDecFramerate(VideoDecControls *decCtrl)
{
    VideoDecData *video = (VideoDecData *)decCtrl->videoDecoderData;

    return video->frameRate;
}

/* ======================================================================== */
/*  Function : PVGetOutputFrame()                                           */
/*  Date     : 05/07/2001                                                   */
/*  Purpose  :                                                              */
/*  In/out   :                                                              */
/*  Return   : This function returns the pointer to the output frame        */
/*  Note     :                                                              */
/*  Modified :                                                              */
/* ======================================================================== */
uint8 *PVGetDecOutputFrame(VideoDecControls *decCtrl)
{
    return decCtrl->outputFrame;
}

/* ======================================================================== */
/*  Function : PVGetLayerID()                                               */
/*  Date     : 07/09/2001                                                   */
/*  Purpose  :                                                              */
/*  In/out   :                                                              */
/*  Return   : This function returns decoded frame layer id (BASE/ENHANCE)  */
/*  Note     :                                                              */
/*  Modified :                                                              */
/* ======================================================================== */
int PVGetLayerID(VideoDecControls *decCtrl)
{
    VideoDecData *video = (VideoDecData *)decCtrl->videoDecoderData;
    return video->currLayer;
}
/* ======================================================================== */
/*  Function : PVGetDecMemoryUsage()                                        */
/*  Date     : 08/23/2000                                                   */
/*  Purpose  :                                                              */
/*  In/out   :                                                              */
/*  Return   : This function returns the amount of memory used.             */
/*  Note     :                                                              */
/*  Modified :                                                              */
/* ======================================================================== */
int32 PVGetDecMemoryUsage(VideoDecControls *decCtrl)
{
    VideoDecData *video = (VideoDecData *)decCtrl->videoDecoderData;
    return video->memoryUsage;
}


/* ======================================================================== */
/*  Function : PVGetDecBitstreamMode()                                      */
/*  Date     : 08/23/2000                                                   */
/*  Purpose  :                                                              */
/*  In/out   :                                                              */
/*  Return   : This function returns the decoding mode of the baselayer     */
/*              bitstream.                                                  */
/*  Note     :                                                              */
/*  Modified :                                                              */
/* ======================================================================== */
OSCL_EXPORT_REF MP4DecodingMode PVGetDecBitstreamMode(VideoDecControls *decCtrl)
{
    VideoDecData *video = (VideoDecData *)decCtrl->videoDecoderData;
    if (video->shortVideoHeader)
    {
        return H263_MODE;
    }
    else
    {
        return MPEG4_MODE;
    }
}


/* ======================================================================== */
/*  Function : PVExtractVolHeader()                                         */
/*  Date     : 08/29/2000                                                   */
/*  Purpose  :                                                              */
/*  In/out   :                                                              */
/*  Return   : Extract vol header of the bitstream from buffer[].           */
/*  Note     :                                                              */
/*  Modified :                                                              */
/* ======================================================================== */
Bool PVExtractVolHeader(uint8 *video_buffer, uint8 *vol_header, int32 *vol_header_size)
{
    int idx = -1;
    uint8 start_code_prefix[] = { 0x00, 0x00, 0x01 };
    uint8 h263_prefix[] = { 0x00, 0x00, 0x80 };

    if (oscl_memcmp(h263_prefix, video_buffer, 3) == 0) /* we have short header stream */
    {
        oscl_memcpy(vol_header, video_buffer, 32);
        *vol_header_size = 32;
        return TRUE;
    }
    else
    {
        if (oscl_memcmp(start_code_prefix, video_buffer, 3) ||
                (video_buffer[3] != 0xb0 && video_buffer[3] >= 0x20)) return FALSE;

        do
        {
            idx++;
            while (oscl_memcmp(start_code_prefix, video_buffer + idx, 3))
            {
                idx++;
                if (idx + 3 >= *vol_header_size) goto quit;
            }
        }
        while (video_buffer[idx+3] != 0xb3 && video_buffer[idx+3] != 0xb6);

        oscl_memcpy(vol_header, video_buffer, idx);
        *vol_header_size = idx;
        return TRUE;
    }

quit:
    oscl_memcpy(vol_header, video_buffer, *vol_header_size);
    return FALSE;
}


/* ======================================================================== */
/*  Function : PVLocateFrameHeader()                                        */
/*  Date     : 04/8/2005                                                    */
/*  Purpose  :                                                              */
/*  In/out   :                                                              */
/*  Return   : Return the offset to the first SC in the buffer              */
/*  Note     :                                                              */
/*  Modified :                                                              */
/* ======================================================================== */
int32 PVLocateFrameHeader(uint8 *ptr, int32 size)
{
    int count = 0;
    int32 i = size;

    if (size < 1)
    {
        return 0;
    }
    while (i--)
    {
        if ((count > 1) && (*ptr == 0x01))
        {
            i += 2;
            break;
        }

        if (*ptr++)
            count = 0;
        else
            count++;
    }
    return (size - (i + 1));
}


/* ======================================================================== */
/*  Function : PVLocateH263FrameHeader()                                    */
/*  Date     : 04/8/2005                                                    */
/*  Purpose  :                                                              */
/*  In/out   :                                                              */
/*  Return   : Return the offset to the first SC in the buffer              */
/*  Note     :                                                              */
/*  Modified :                                                              */
/* ======================================================================== */
int32 PVLocateH263FrameHeader(uint8 *ptr, int32 size)
{
    int count = 0;
    int32 i = size;

    if (size < 1)
    {
        return 0;
    }

    while (i--)
    {
        if ((count > 1) && ((*ptr & 0xFC) == 0x80))
        {
            i += 2;
            break;
        }

        if (*ptr++)
            count = 0;
        else
            count++;
    }
    return (size - (i + 1));
}


/* ======================================================================== */
/*  Function : PVDecodeVideoFrame()                                         */
/*  Date     : 08/29/2000                                                   */
/*  Purpose  : Decode one video frame and return a YUV-12 image.            */
/*  In/out   :                                                              */
/*  Return   :                                                              */
/*  Note     :                                                              */
/*  Modified : 04/17/2001 removed PV_EOS, PV_END_OF_BUFFER              */
/*           : 08/22/2002 break up into 2 functions PVDecodeVopHeader and */
/*                          PVDecodeVopBody                                 */
/* ======================================================================== */
OSCL_EXPORT_REF Bool PVDecodeVideoFrame(VideoDecControls *decCtrl, uint8 *buffer[],
                                        uint32 timestamp[], int32 buffer_size[], uint use_ext_timestamp[], uint8 *currYUV)
{
    PV_STATUS status = PV_FAIL;
    VopHeaderInfo header_info;

    status = (PV_STATUS)PVDecodeVopHeader(decCtrl, buffer, timestamp, buffer_size, &header_info, use_ext_timestamp, currYUV);
    if (status != PV_TRUE)
        return PV_FALSE;

    if (PVDecodeVopBody(decCtrl, buffer_size) != PV_TRUE)
    {
        return PV_FALSE;
    }

    return PV_TRUE;
}

/* ======================================================================== */
/*  Function : PVDecodeVopHeader()                                          */
/*  Date     : 08/22/2002                                                   */
/*  Purpose  : Determine target layer and decode vop header, modified from  */
/*              original PVDecodeVideoFrame.                                */
/*  In/out   :                                                              */
/*  Return   :                                                              */
/*  Note     :                                                              */
/*  Modified :                                                              */
/* ======================================================================== */
Bool PVDecodeVopHeader(VideoDecControls *decCtrl, uint8 *buffer[],
                       uint32 timestamp[], int32 buffer_size[], VopHeaderInfo *header_info, uint use_ext_timestamp [], uint8 *currYUV)
{
    VideoDecData *video = (VideoDecData *) decCtrl->videoDecoderData;
    Vol *currVol;
    Vop *currVop = video->currVop;
    Vop **vopHeader = video->vopHeader;
    BitstreamDecVideo *stream;

    int target_layer;

#ifdef PV_SUPPORT_TEMPORAL_SCALABILITY
    PV_STATUS status = PV_FAIL;
    int idx;
    int32 display_time;

    /* decide which frame to decode next */
    if (decCtrl->nLayers > 1)
    {
        display_time = target_layer = -1;
        for (idx = 0; idx < decCtrl->nLayers; idx++)
        {
            /* do we have data for this layer? */
            if (buffer_size[idx] <= 0)
            {
                timestamp[idx] = -1;
                continue;
            }

            /* did the application provide a timestamp for this vop? */
            if (timestamp[idx] < 0)
            {
                if (vopHeader[idx]->timeStamp < 0)
                {
                    /* decode the timestamp in the bitstream */
                    video->currLayer = idx;
                    stream = video->vol[idx]->bitstream;
                    BitstreamReset(stream, buffer[idx], buffer_size[idx]);

                    while ((status = DecodeVOPHeader(video, vopHeader[idx], FALSE)) != PV_SUCCESS)
                    {
                        /* Try to find a VOP header in the buffer.   08/30/2000. */
                        if (PVSearchNextM4VFrame(stream) != PV_SUCCESS)
                        {
                            /* if we don't have data for enhancement layer, */
                            /*    don't just stop.   09/07/2000.          */
                            buffer_size[idx] = 0;
                            break;
                        }
                    }
                    if (status == PV_SUCCESS)
                    {
                        vopHeader[idx]->timeStamp =
                            timestamp[idx] = CalcVopDisplayTime(video->vol[idx], vopHeader[idx], video->shortVideoHeader);
                        if (idx == 0) vopHeader[idx]->refSelectCode = 1;
                    }
                }
                else
                {
                    /* We've decoded this vop header in the previous run already. */
                    timestamp[idx] = vopHeader[idx]->timeStamp;
                }
            }

            /* Use timestamps to select the next VOP to be decoded */
            if (timestamp[idx] >= 0 && (display_time < 0 || display_time > timestamp[idx]))
            {
                display_time = timestamp[idx];
                target_layer = idx;
            }
            else if (display_time == timestamp[idx])
            {
                /* we have to handle either SNR or spatial scalability here. */
            }
        }
        if (target_layer < 0) return PV_FALSE;

        /* set up for decoding the target layer */
        video->currLayer = target_layer;
        currVol = video->vol[target_layer];
        video->bitstream = stream = currVol->bitstream;

        /* We need to decode the vop header if external timestamp   */
        /*    is provided.    10/04/2000                            */
        if (vopHeader[target_layer]->timeStamp < 0)
        {
            stream = video->vol[target_layer]->bitstream;
            BitstreamReset(stream, buffer[target_layer], buffer_size[target_layer]);

            while (DecodeVOPHeader(video, vopHeader[target_layer], TRUE) != PV_SUCCESS)
            {
                /* Try to find a VOP header in the buffer.   08/30/2000. */
                if (PVSearchNextM4VFrame(stream) != PV_SUCCESS)
                {
                    /* if we don't have data for enhancement layer, */
                    /*    don't just stop.   09/07/2000.          */
                    buffer_size[target_layer] = 0;
                    break;
                }
            }
            video->vol[target_layer]->timeInc_offset = vopHeader[target_layer]->timeInc;
            video->vol[target_layer]->moduloTimeBase = timestamp[target_layer];
            vopHeader[target_layer]->timeStamp = timestamp[target_layer];
            if (target_layer == 0) vopHeader[target_layer]->refSelectCode = 1;
        }
    }
    else /* base layer only decoding */
    {
#endif
        video->currLayer = target_layer = 0;
        currVol = video->vol[0];
        video->bitstream = stream = currVol->bitstream;
        if (buffer_size[0] <= 0) return PV_FALSE;
        BitstreamReset(stream, buffer[0], buffer_size[0]);

        if (video->shortVideoHeader)
        {
            while (DecodeShortHeader(video, vopHeader[0]) != PV_SUCCESS)
            {
                if (PVSearchNextH263Frame(stream) != PV_SUCCESS)
                {
                    /* There is no vop header in the buffer,    */
                    /*   clean bitstream buffer.     2/5/2001   */
                    buffer_size[0] = 0;
                    if (video->initialized == PV_FALSE)
                    {
                        video->displayWidth = video->width = 0;
                        video->displayHeight = video->height = 0;
                    }
                    return PV_FALSE;
                }
            }

            if (use_ext_timestamp[0])
            {
                /* MTB for H263 is absolute TR */
                /* following line is equivalent to  round((timestamp[0]*30)/1001);   11/13/2001 */
                video->vol[0]->moduloTimeBase = 30 * ((timestamp[0] + 17) / 1001) + (30 * ((timestamp[0] + 17) % 1001) / 1001);
                vopHeader[0]->timeStamp = timestamp[0];
            }
            else
                vopHeader[0]->timeStamp = CalcVopDisplayTime(currVol, vopHeader[0], video->shortVideoHeader);
        }
        else
        {
            while (DecodeVOPHeader(video, vopHeader[0], FALSE) != PV_SUCCESS)
            {
                /* Try to find a VOP header in the buffer.   08/30/2000. */
                if (PVSearchNextM4VFrame(stream) != PV_SUCCESS)
                {
                    /* There is no vop header in the buffer,    */
                    /*   clean bitstream buffer.     2/5/2001   */
                    buffer_size[0] = 0;
                    return PV_FALSE;
                }
            }

            if (use_ext_timestamp[0])
            {
                video->vol[0]->timeInc_offset = vopHeader[0]->timeInc;
                video->vol[0]->moduloTimeBase = timestamp[0];  /*  11/12/2001 */
                vopHeader[0]->timeStamp = timestamp[0];
            }
            else
            {
                vopHeader[0]->timeStamp = CalcVopDisplayTime(currVol, vopHeader[0], video->shortVideoHeader);
            }
        }

        /* set up some base-layer only parameters */
        vopHeader[0]->refSelectCode = 1;
#ifdef PV_SUPPORT_TEMPORAL_SCALABILITY
    }
#endif
    timestamp[target_layer] = video->currTimestamp = vopHeader[target_layer]->timeStamp;
#ifdef PV_MEMORY_POOL
    vopHeader[target_layer]->yChan = (PIXEL *)currYUV;
    vopHeader[target_layer]->uChan = (PIXEL *)currYUV + decCtrl->size;
    vopHeader[target_layer]->vChan = (PIXEL *)(vopHeader[target_layer]->uChan) + (decCtrl->size >> 2);
#else
    vopHeader[target_layer]->yChan = currVop->yChan;
    vopHeader[target_layer]->uChan = currVop->uChan;
    vopHeader[target_layer]->vChan = currVop->vChan;
#endif
    oscl_memcpy(currVop, vopHeader[target_layer], sizeof(Vop));

#ifdef PV_SUPPORT_TEMPORAL_SCALABILITY
    vopHeader[target_layer]->timeStamp = -1;
#endif
    /* put header info into the structure */
    header_info->currLayer = target_layer;
    header_info->timestamp = video->currTimestamp;
    header_info->frameType = (MP4FrameType)currVop->predictionType;
    header_info->refSelCode = vopHeader[target_layer]->refSelectCode;
    header_info->quantizer = currVop->quantizer;
    /***************************************/

    return PV_TRUE;
}


/* ======================================================================== */
/*  Function : PVDecodeVopBody()                                            */
/*  Date     : 08/22/2002                                                   */
/*  Purpose  : Decode vop body after the header is decoded, modified from   */
/*              original PVDecodeVideoFrame.                                */
/*  In/out   :                                                              */
/*  Return   :                                                              */
/*  Note     :                                                              */
/*  Modified :                                                              */
/* ======================================================================== */
Bool PVDecodeVopBody(VideoDecControls *decCtrl, int32 buffer_size[])
{
    PV_STATUS status = PV_FAIL;
    VideoDecData *video = (VideoDecData *) decCtrl->videoDecoderData;
    int target_layer = video->currLayer;
    Vol *currVol = video->vol[target_layer];
    Vop *currVop = video->currVop;
    Vop *prevVop = video->prevVop;
    Vop *tempVopPtr;
    int bytes_consumed = 0; /* Record how many bits we used in the buffer.   04/24/2001 */

    int idx;

    if (currVop->vopCoded == 0)                  /*  07/03/2001 */
    {
        PV_BitstreamByteAlign(currVol->bitstream);
        /* We should always clear up bitstream buffer.   10/10/2000 */
        bytes_consumed = (getPointer(currVol->bitstream) + 7) >> 3;

        if (bytes_consumed > currVol->bitstream->data_end_pos)
        {
            bytes_consumed = currVol->bitstream->data_end_pos;
        }

        if (bytes_consumed < buffer_size[target_layer])
        {
            /* If we only consume part of the bits in the buffer, take those */
            /*  out.     04/24/2001 */
            /*          oscl_memcpy(buffer[target_layer], buffer[target_layer]+bytes_consumed,
                            (buffer_size[target_layer]-=bytes_consumed)); */
            buffer_size[target_layer] -= bytes_consumed;
        }
        else
        {
            buffer_size[target_layer] = 0;
        }
#ifdef PV_MEMORY_POOL

        if (target_layer)
        {
            if (video->prevEnhcVop->timeStamp > video->prevVop->timeStamp)
            {
                video->prevVop = video->prevEnhcVop;
            }
        }

        if (!video->prevVop->yChan) {
            ALOGE("b/35269635");
            android_errorWriteLog(0x534e4554, "35269635");
            return PV_FALSE;
        }
        oscl_memcpy(currVop->yChan, video->prevVop->yChan, (decCtrl->size*3) / 2);

        video->prevVop = prevVop;

        video->concealFrame = currVop->yChan;       /*  07/07/2001 */

        video->vop_coding_type = currVop->predictionType; /*  07/09/01 */

        decCtrl->outputFrame = currVop->yChan;

        /* Swap VOP pointers.  No enhc. frame oscl_memcpy() anymore!   04/24/2001 */
        if (target_layer)
        {
            tempVopPtr = video->prevEnhcVop;
            video->prevEnhcVop = video->currVop;
            video->currVop = tempVopPtr;
        }
        else
        {
            tempVopPtr = video->prevVop;
            video->prevVop = video->currVop;
            video->currVop = tempVopPtr;
        }
#else
        if (target_layer)       /* this is necessary to avoid flashback problems   06/21/2002*/
        {
            video->prevEnhcVop->timeStamp = currVop->timeStamp;
        }
        else
        {
            video->prevVop->timeStamp = currVop->timeStamp;
        }
#endif
        video->vop_coding_type = currVop->predictionType; /*  07/09/01 */
        /* the following is necessary to avoid displaying an notCoded I-VOP at the beginning of a session
        or after random positioning  07/03/02*/
        if (currVop->predictionType == I_VOP)
        {
            video->vop_coding_type = P_VOP;
        }


        return PV_TRUE;
    }
    /* ======================================================= */
    /*  Decode vop body (if there is no error in the header!)  */
    /* ======================================================= */

    /* first, we need to select a reference frame */
    if (decCtrl->nLayers > 1)
    {
        if (currVop->predictionType == I_VOP)
        {
            /* do nothing here */
        }
        else if (currVop->predictionType == P_VOP)
        {
            switch (currVop->refSelectCode)
            {
                case 0 : /* most recently decoded enhancement vop */
                    /* Setup video->prevVop before we call PV_DecodeVop().   04/24/2001 */
                    if (video->prevEnhcVop->timeStamp >= video->prevVop->timeStamp)
                        video->prevVop = video->prevEnhcVop;
                    break;

                case 1 : /* most recently displayed base-layer vop */
                    if (target_layer)
                    {
                        if (video->prevEnhcVop->timeStamp > video->prevVop->timeStamp)
                            video->prevVop = video->prevEnhcVop;
                    }
                    break;

                case 2 : /* next base-layer vop in display order */
                    break;

                case 3 : /* temporally coincident base-layer vop (no MV's) */
                    break;
            }
        }
        else /* we have a B-Vop */
        {
            mp4dec_log("DecodeVideoFrame(): B-VOP not supported.\n");
        }
    }

    /* This is for the calculation of the frame rate and bitrate. */
    idx = ++video->frame_idx % BITRATE_AVERAGE_WINDOW;

    /* Calculate bitrate for this layer.   08/23/2000 */
    status = PV_DecodeVop(video);
    video->nBitsPerVop[idx] = getPointer(currVol->bitstream);
    video->prevTimestamp[idx] = currVop->timeStamp;

    /* restore video->prevVop after PV_DecodeVop().   04/24/2001 */
//  if (currVop->refSelectCode == 0) video->prevVop = prevVop;
    video->prevVop = prevVop;

    /* Estimate the frame rate.   08/23/2000 */
    video->duration = video->prevTimestamp[idx];
    video->duration -= video->prevTimestamp[(++idx)%BITRATE_AVERAGE_WINDOW];
    if (video->duration > 0)
    { /* Only update framerate when the timestamp is right */
        video->frameRate = (int)(FRAMERATE_SCALE) / video->duration;
    }

    /* We should always clear up bitstream buffer.   10/10/2000 */
    bytes_consumed = (getPointer(currVol->bitstream) + 7) >> 3; /*  11/4/03 */

    if (bytes_consumed > currVol->bitstream->data_end_pos)
    {
        bytes_consumed = currVol->bitstream->data_end_pos;
    }

    if (bytes_consumed < buffer_size[target_layer])
    {
        /* If we only consume part of the bits in the buffer, take those */
        /*  out.     04/24/2001 */
        /*      oscl_memcpy(buffer[target_layer], buffer[target_layer]+bytes_consumed,
                    (buffer_size[target_layer]-=bytes_consumed)); */
        buffer_size[target_layer] -= bytes_consumed;
    }
    else
    {
        buffer_size[target_layer] = 0;
    }
    switch (status)
    {
        case PV_FAIL :
            return PV_FALSE;        /* this will take care of concealment if we lose whole frame  */

        case PV_END_OF_VOP :
            /* we may want to differenciate PV_END_OF_VOP and PV_SUCCESS */
            /*    in the future.     05/10/2000                      */

        case PV_SUCCESS :
            /* Nohting is wrong :). */


            video->concealFrame = video->currVop->yChan;       /*  07/07/2001 */

            video->vop_coding_type = video->currVop->predictionType; /*  07/09/01 */

            decCtrl->outputFrame = video->currVop->yChan;

            /* Swap VOP pointers.  No enhc. frame oscl_memcpy() anymore!   04/24/2001 */
            if (target_layer)
            {
                tempVopPtr = video->prevEnhcVop;
                video->prevEnhcVop = video->currVop;
                video->currVop = tempVopPtr;
            }
            else
            {
                tempVopPtr = video->prevVop;
                video->prevVop = video->currVop;
                video->currVop = tempVopPtr;
            }
            break;

        default :
            /* This will never happen */
            break;
    }

    return PV_TRUE;
}

#ifdef PV_MEMORY_POOL
OSCL_EXPORT_REF void PVSetReferenceYUV(VideoDecControls *decCtrl, uint8 *YUV)
{
    VideoDecData *video = (VideoDecData *)decCtrl->videoDecoderData;
    video->prevVop->yChan = (PIXEL *)YUV;
    video->prevVop->uChan = (PIXEL *)YUV + video->size;
    video->prevVop->vChan = (PIXEL *)video->prevVop->uChan + (decCtrl->size >> 2);
    oscl_memset(video->prevVop->yChan, 16, sizeof(uint8)*decCtrl->size);     /*  10/31/01 */
    oscl_memset(video->prevVop->uChan, 128, sizeof(uint8)*decCtrl->size / 2);
    video->concealFrame = video->prevVop->yChan;               /*  07/07/2001 */
    decCtrl->outputFrame = video->prevVop->yChan;              /*  06/19/2002 */
}
#endif


/* ======================================================================== */
/*  Function : VideoDecoderErrorDetected()                                  */
/*  Date     : 06/20/2000                                                   */
/*  Purpose  :                                                              */
/*  In/out   :                                                              */
/*  Return   : This function will be called everytime an error int the      */
/*              bitstream is detected.                                      */
/*  Note     :                                                              */
/*  Modified :                                                              */
/* ======================================================================== */
uint VideoDecoderErrorDetected(VideoDecData *)
{
    /* This is only used for trapping bitstream error for debuging */
    return 0;
}

#ifdef ENABLE_LOG
#include <stdio.h>
#include <stdarg.h>
/* ======================================================================== */
/*  Function : m4vdec_dprintf()                                             */
/*  Date     : 08/15/2000                                                   */
/*  Purpose  : This is a function that logs messages in the mpeg4 video     */
/*             decoder.  We can call the standard PacketVideo PVMessage     */
/*             from inside this function if necessary.                      */
/*  In/out   :                                                              */
/*  Return   :                                                              */
/*  Note     : To turn on the logging, LOG_MP4DEC_MESSAGE must be defined   */
/*              when compiling this file (only this file).                  */
/*  Modified :                                                              */
/* ======================================================================== */
void m4vdec_dprintf(char *format, ...)
{
    FILE *log_fp;
    va_list args;
    va_start(args, format);

    /* open the log file */
    log_fp = fopen("\\mp4dec_log.txt", "a+");
    if (log_fp == NULL) return;
    /* output the message */
    vfprintf(log_fp, format, args);
    fclose(log_fp);

    va_end(args);
}
#endif


/* ======================================================================== */
/*  Function : IsIntraFrame()                                               */
/*  Date     : 05/29/2000                                                   */
/*  Purpose  :                                                              */
/*  In/out   :                                                              */
/*  Return   : The most recently decoded frame is an Intra frame.           */
/*  Note     :                                                              */
/*  Modified :                                                              */
/* ======================================================================== */
Bool IsIntraFrame(VideoDecControls *decCtrl)
{
    VideoDecData *video = (VideoDecData *)decCtrl->videoDecoderData;
    return (video->vop_coding_type == I_VOP);
}

/* ======================================================================== */
/*  Function : PVDecPostProcess()                                           */
/*  Date     : 01/09/2002                                                   */
/*  Purpose  : PostProcess one video frame and return a YUV-12 image.       */
/*  In/out   :                                                              */
/*  Return   :                                                              */
/*  Note     :                                                              */
/*  Modified :                                                              */
/* ======================================================================== */
void PVDecPostProcess(VideoDecControls *decCtrl, uint8 *outputYUV)
{
    uint8 *outputBuffer;
#ifdef PV_POSTPROC_ON
    VideoDecData *video = (VideoDecData *) decCtrl->videoDecoderData;
    int32 tmpvar;
    if (outputYUV)
    {
        outputBuffer = outputYUV;
    }
    else
    {
        if (video->postFilterType)
        {
            outputBuffer = video->currVop->yChan;
        }
        else
        {
            outputBuffer = decCtrl->outputFrame;
        }
    }

    if (video->postFilterType)
    {
        /* Post-processing,  */
        PostFilter(video, video->postFilterType, outputBuffer);
    }
    else
    {
        if (outputYUV)
        {
            /* Copy decoded frame to the output buffer. */
            tmpvar = (int32)video->width * video->height;
            oscl_memcpy(outputBuffer, decCtrl->outputFrame, tmpvar*3 / 2);           /*  3/3/01 */
        }
    }
#else
    outputBuffer = decCtrl->outputFrame;
    outputYUV;
#endif
    decCtrl->outputFrame = outputBuffer;
    return;
}


/* ======================================================================== */
/*  Function : PVDecSetReference(VideoDecControls *decCtrl, uint8 *refYUV,  */
/*                              int32 timestamp)                            */
/*  Date     : 07/22/2003                                                   */
/*  Purpose  : Get YUV reference frame from external source.                */
/*  In/out   : YUV 4-2-0 frame containing new reference frame in the same   */
/*   : dimension as original, i.e., doesn't have to be multiple of 16 !!!.  */
/*  Return   :                                                              */
/*  Note     :                                                              */
/*  Modified :                                                              */
/* ======================================================================== */
Bool PVDecSetReference(VideoDecControls *decCtrl, uint8 *refYUV, uint32 timestamp)
{
    VideoDecData *video = (VideoDecData *) decCtrl->videoDecoderData;
    Vop *prevVop = video->prevVop;
    int width = video->width;
    uint8 *dstPtr, *orgPtr, *dstPtr2, *orgPtr2;
    int32 size = (int32)width * video->height;


    /* set new parameters */
    prevVop->timeStamp = timestamp;
    prevVop->predictionType = I_VOP;

    dstPtr = prevVop->yChan;
    orgPtr = refYUV;
    oscl_memcpy(dstPtr, orgPtr, size);
    dstPtr = prevVop->uChan;
    dstPtr2 = prevVop->vChan;
    orgPtr = refYUV + size;
    orgPtr2 = orgPtr + (size >> 2);
    oscl_memcpy(dstPtr, orgPtr, (size >> 2));
    oscl_memcpy(dstPtr2, orgPtr2, (size >> 2));

    video->concealFrame = video->prevVop->yChan;
    video->vop_coding_type = I_VOP;
    decCtrl->outputFrame = video->prevVop->yChan;

    return PV_TRUE;
}

/* ======================================================================== */
/*  Function : PVDecSetEnhReference(VideoDecControls *decCtrl, uint8 *refYUV,   */
/*                              int32 timestamp)                            */
/*  Date     : 07/23/2003                                                   */
/*  Purpose  : Get YUV enhance reference frame from external source.        */
/*  In/out   : YUV 4-2-0 frame containing new reference frame in the same   */
/*   : dimension as original, i.e., doesn't have to be multiple of 16 !!!.  */
/*  Return   :                                                              */
/*  Note     :                                                              */
/*  Modified :                                                              */
/* ======================================================================== */
Bool PVDecSetEnhReference(VideoDecControls *decCtrl, uint8 *refYUV, uint32 timestamp)
{
    VideoDecData *video = (VideoDecData *) decCtrl->videoDecoderData;
    Vop *prevEnhcVop = video->prevEnhcVop;
    uint8 *dstPtr, *orgPtr, *dstPtr2, *orgPtr2;
    int32 size = (int32) video->width * video->height;

    if (video->numberOfLayers <= 1)
        return PV_FALSE;


    /* set new parameters */
    prevEnhcVop->timeStamp = timestamp;
    prevEnhcVop->predictionType = I_VOP;

    dstPtr = prevEnhcVop->yChan;
    orgPtr = refYUV;
    oscl_memcpy(dstPtr, orgPtr, size);
    dstPtr = prevEnhcVop->uChan;
    dstPtr2 = prevEnhcVop->vChan;
    orgPtr = refYUV + size;
    orgPtr2 = orgPtr + (size >> 2);
    oscl_memcpy(dstPtr, orgPtr, (size >> 2));
    oscl_memcpy(dstPtr2, orgPtr2, (size >> 2));
    video->concealFrame = video->prevEnhcVop->yChan;
    video->vop_coding_type = I_VOP;
    decCtrl->outputFrame = video->prevEnhcVop->yChan;

    return PV_TRUE;
}


/* ======================================================================== */
/*  Function : PVGetVolInfo()                                               */
/*  Date     : 08/06/2003                                                   */
/*  Purpose  : Get the vol info(only base-layer).                           */
/*  In/out   :                                                              */
/*  Return   :                                                              */
/*  Note     :                                                              */
/*  Modified : 06/24/2004                                                   */
/* ======================================================================== */
Bool PVGetVolInfo(VideoDecControls *decCtrl, VolInfo *pVolInfo)
{
    Vol *currVol;

    if (pVolInfo == NULL || decCtrl == NULL || decCtrl->videoDecoderData == NULL ||
            ((VideoDecData *)decCtrl->videoDecoderData)->vol[0] == NULL) return PV_FALSE;

    currVol = ((VideoDecData *)(decCtrl->videoDecoderData))->vol[0];

    // get the VOL info
    pVolInfo->shortVideoHeader = (int32)((VideoDecData *)(decCtrl->videoDecoderData))->shortVideoHeader;
    pVolInfo->dataPartitioning = (int32)currVol->dataPartitioning;
    pVolInfo->errorResDisable  = (int32)currVol->errorResDisable;
    pVolInfo->useReverseVLC    = (int32)currVol->useReverseVLC;
    pVolInfo->scalability      = (int32)currVol->scalability;
    pVolInfo->nbitsTimeIncRes  = (int32)currVol->nbitsTimeIncRes;
    pVolInfo->profile_level_id = (int32)currVol->profile_level_id;

    return PV_TRUE;
}



