/*
 * Copyright (C) 2005-2010 Freescale Semiconductor, Inc. All rights reserved.
 *
 */
 
/*
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */
 
/*
 * Module Name:    mfw_gst_vpu_encoder.c
 *
 * Description:    Implementation of Hardware (VPU) Encoder Plugin for Gstreamer.
 *
 * Portability:    This code is written for Linux OS and Gstreamer
 */  
 
/*
 * Changelog: 
 *
 */  


#if !defined(VPU_MX51) && !defined(VPU_MX27) && !defined(VPU_MX53)
#pragma error "Platform defines are not configured correctly - must be MX27, MX51 or MX53"
#endif


/*======================================================================================
                            INCLUDE FILES
=======================================================================================*/
#include <gst/gst.h>
#include <string.h>
#include <fcntl.h>		/* fcntl */
#include <sys/mman.h>	/* mmap */
#include <sys/ioctl.h>	/* fopen/fread */		
#include "vpu_io.h"
#include "vpu_lib.h"
#include "mfw_gst_utils.h"
#include "mfw_gst_vpu_encoder.h"
#if (defined(VPU_MX51) || defined(VPU_MX53))
#include "vpu_jpegtable.h"
#endif

#include "gstbufmeta.h"

//#define GST_DEBUG g_print
//#define GST_FRAMEDBG g_print
#ifndef GST_FRAMEDBG
#define GST_FRAMEDBG
#endif
//#define GST_LOGTIME g_print
#ifndef GST_LOGTIME
#define GST_LOGTIME
#endif

#define GST_ERROR g_print

/*======================================================================================
                                        LOCAL MACROS
=======================================================================================*/
#define	GST_CAT_DEFAULT	mfw_gst_vpuenc_debug
static MfwGstVPU_Enc *vpuenc_global_ptr = NULL;

#define IS_DMABLE_BUFFER(buffer) ( (GST_IS_BUFFER_META(buffer->_gst_reserved[G_N_ELEMENTS(buffer->_gst_reserved)-1])) \
                                 || ( GST_IS_BUFFER(buffer) \
                                 &&  GST_BUFFER_FLAG_IS_SET((buffer),GST_BUFFER_FLAG_LAST)))
#define DMABLE_BUFFER_PHY_ADDR(buffer) (GST_IS_BUFFER_META(buffer->_gst_reserved[G_N_ELEMENTS(buffer->_gst_reserved)-1]) ? \
                                        ((GstBufferMeta *)(buffer->_gst_reserved[G_N_ELEMENTS(buffer->_gst_reserved)-1]))->physical_data : \
                                        GST_BUFFER_OFFSET(buffer))


#define VPU_PIC_TYPE ((vpu_enc->codec == STD_AVC) ? ((vpu_enc->outputInfo->picType>>1)&0x3) : ((vpu_enc->outputInfo->picType)&0x3) )
#define VPU_PIC_TYPE_IDR ( (vpu_enc->codec == STD_AVC) ? (!(vpu_enc->outputInfo->picType&0x1)) : (VPU_PIC_TYPE == 0))
#define VPU_PIC_TYPE_I ( VPU_PIC_TYPE_IDR || (VPU_PIC_TYPE == 0))


/*======================================================================================
                                 STATIC FUNCTION PROTOTYPES
=======================================================================================*/

GST_DEBUG_CATEGORY_STATIC(mfw_gst_vpuenc_debug);

static void mfw_gst_vpuenc_set_property(GObject *,guint,const GValue *,GParamSpec *);
static void mfw_gst_vpuenc_get_property(GObject *,guint,GValue *,GParamSpec *);
static GstFlowReturn mfw_gst_vpuenc_chain(GstPad *pad, GstBuffer *buffer);
static GstStateChangeReturn mfw_gst_vpuenc_change_state(GstElement *element, 
                                                        GstStateChange transition);
static gboolean mfw_gst_vpuenc_sink_event(GstPad * pad, GstEvent * event);
static gboolean mfw_gst_vpuenc_setcaps(GstPad * pad, GstCaps *caps);
static void mfw_gst_vpuenc_base_init(MfwGstVPU_EncClass *klass);
static GType mfw_gst_vpuenc_codec_get_type(void);
static GType mfw_gst_type_vpu_enc_get_type(void);
static void mfw_gst_vpuenc_class_init(MfwGstVPU_EncClass *);
static void mfw_gst_vpuenc_init(MfwGstVPU_Enc *, MfwGstVPU_EncClass *);
void mfw_gst_vpuenc_vpu_finalize(void);


/*======================================================================================
                                     LOCAL FUNCTIONS
=======================================================================================*/
GstFlowReturn mfw_gst_vpuenc_vpuinitialize(MfwGstVPU_Enc *vpu_enc);
RetCode mfw_gst_vpuenc_configure(MfwGstVPU_Enc * vpu_enc);
void mfw_gst_vpuenc_free_framebuffer(MfwGstVPU_Enc * vpu_enc);
gboolean mfw_gst_vpuenc_alloc_framebuffer(MfwGstVPU_Enc * vpu_enc);
void mfw_gst_vpuenc_cleanup(MfwGstVPU_Enc *vpu_enc);
int mfw_gst_vpuenc_fill_headers(MfwGstVPU_Enc *vpu_enc);
GstFlowReturn mfw_gst_vpuenc_send_buffer(MfwGstVPU_Enc *vpu_enc, GstBuffer *buffer);
GstFlowReturn mfw_gst_vpuenc_vpuinitialize(MfwGstVPU_Enc *vpu_enc);
gboolean mfw_gst_vpudec_generate_jpeg_tables(MfwGstVPU_Enc *vpu_enc, Uint8 * * pphuftable, Uint8 * * ppqmattable);

/*=============================================================================
FUNCTION:           mfw_gst_vpuenc_set_property

DESCRIPTION:        Sets the property of the element

ARGUMENTS PASSED:
        object     - pointer to the elements object
        prop_id    - ID of the property;
        value      - value of the property set by the application
        pspec      - pointer to the attributes of the property

RETURN VALUE:       None
PRE-CONDITIONS:     None
POST-CONDITIONS:    None
IMPORTANT NOTES:    None
=============================================================================*/
static void mfw_gst_vpuenc_set_property(GObject * object, guint prop_id,
					const GValue * value,
					GParamSpec * pspec)
{
    MfwGstVPU_Enc *vpu_enc = MFW_GST_VPU_ENC(object);
    
    switch (prop_id) {
    case MFW_GST_VPU_PROF_ENABLE:
    	vpu_enc->profile = g_value_get_boolean(value);
    	GST_DEBUG(">>VPU_ENC: profile=%d\n", vpu_enc->profile);
	break;

    case MFW_GST_VPU_CODEC_TYPE:
    	vpu_enc->codec = g_value_get_enum(value);
    	GST_DEBUG(">>VPU_ENC: codec=%d vpu_enc 0x%x\n", vpu_enc->codec, vpu_enc);
    	vpu_enc->codecTypeProvided = TRUE;
	break;
    
    case MFW_GST_VPUENC_WIDTH:
        vpu_enc->width = g_value_get_uint(value);
        GST_DEBUG(">>VPU_ENC: width=%d\n", vpu_enc->width);
        vpu_enc->widthProvided = TRUE;
        break;
        
    case MFW_GST_VPUENC_HEIGHT:
        vpu_enc->height = g_value_get_uint(value);
        GST_DEBUG(">>VPU_ENC: height=%d\n", vpu_enc->height);
        vpu_enc->heightProvided = TRUE;	
        break;

    case MFW_GST_VPUENC_BITRATE:
        vpu_enc->bitrate = g_value_get_uint(value);
        GST_DEBUG(">>VPU_ENC: bitrate=%d\n", vpu_enc->bitrate );
        break;

    case MFW_GST_VPUENC_QP:
        vpu_enc->qp = g_value_get_uint(value);
        GST_DEBUG(">>VPU_ENC: Quantization Parameter=%d\n", vpu_enc->qp );
        break;

    case MFW_GST_VPUENC_MAX_QP:
        vpu_enc->max_qp = g_value_get_uint(value);
        GST_DEBUG(">>VPU_ENC: CBR Max Quantization Parameter=%d\n", vpu_enc->max_qp );
        break;

    case MFW_GST_VPUENC_INTRA_QP:
        vpu_enc->intra_qp = g_value_get_uint(value);
        GST_DEBUG(">>VPU_ENC: CBR IntraQP Parameter=%d\n", vpu_enc->intra_qp );
        break;


    case MFW_GST_VPUENC_GAMMA:  
        vpu_enc->gamma = g_value_get_uint(value);
        GST_DEBUG(">>VPU_ENC: CBR Gamma=%d\n", vpu_enc->gamma );
        break;

    case MFW_GST_VPUENC_FRAME_RATE:
        vpu_enc->tgt_framerate = g_value_get_float (value);
        if (vpu_enc->tgt_framerate <= 30.0)
            vpu_enc->forcefixrate = TRUE;
        GST_DEBUG(">>VPU_ENC: Target framerate=%f\n", vpu_enc->tgt_framerate);
        break;

    case MFW_GST_VPUENC_GOP:
        vpu_enc->gopsize = g_value_get_uint(value);
        GST_DEBUG(">>VPU_ENC: gopsize=%d\n", vpu_enc->gopsize);
        break;

    case MFW_GST_VPUENC_INTRAREFRESH:
        vpu_enc->intraRefresh = g_value_get_uint(value);
        GST_DEBUG(">>VPU_ENC: intraRefresh=%d\n", vpu_enc->intraRefresh);
        break;
        
    case MFW_GST_VPUENC_FORCEIINTERVAL:
        vpu_enc->forceIFrameInterval = g_value_get_uint(value);
        GST_DEBUG(">>VPU_ENC: iinterval=%d\n", vpu_enc->forceIFrameInterval);
        break;

    case MFW_GST_VPUENC_H263PROFILE0:
        vpu_enc->h263profile0 = g_value_get_boolean(value);
        GST_DEBUG(">>VPU_ENC: H263 Profile 0=%d\n", vpu_enc->h263profile0);
        break;
        
    case MFW_GST_VPUENC_LOOPBACK:
        vpu_enc->loopback = g_value_get_boolean(value);
        GST_DEBUG(">>VPU_ENC: Loopback=%d\n", vpu_enc->loopback);
        break;
        
    case MFW_GST_VPUENC_CROP_LEFT:
        vpu_enc->crop_left = g_value_get_uint(value);
        GST_DEBUG(">>VPU_ENC: crop_left = %d\n", vpu_enc->crop_left);
        break;
        
    case MFW_GST_VPUENC_CROP_TOP:
        vpu_enc->crop_top = g_value_get_uint(value);
        GST_DEBUG(">>VPU_ENC: crop_top =%d\n", vpu_enc->crop_top);
        break;

    case MFW_GST_VPUENC_CROP_RIGHT:
        vpu_enc->crop_right = g_value_get_uint(value);
        GST_DEBUG(">>VPU_ENC: crop_left = %d\n", vpu_enc->crop_right);
        break;
        
    case MFW_GST_VPUENC_CROP_BOTTOM:
        vpu_enc->crop_bottom = g_value_get_uint(value);
        GST_DEBUG(">>VPU_ENC: crop_top =%d\n", vpu_enc->crop_bottom);
        break;
       
    default:
    	G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
    	break;
    }
    return;
}


/*=============================================================================
FUNCTION:           mfw_gst_vpuenc_set_property

DESCRIPTION:        Gets the property of the element

ARGUMENTS PASSED:
        object     - pointer to the elements object
        prop_id    - ID of the property;
        value      - value of the property set by the application
        pspec      - pointer to the attributes of the property

RETURN VALUE:       GstFlowReturn - Success of Failure.
PRE-CONDITIONS:     None
POST-CONDITIONS:    None
IMPORTANT NOTES:    None
=============================================================================*/
static void mfw_gst_vpuenc_get_property(GObject * object, guint prop_id,
					GValue * value, GParamSpec * pspec)
{
    MfwGstVPU_Enc *vpu_enc = MFW_GST_VPU_ENC(object);
    
    switch (prop_id) {
    case MFW_GST_VPU_PROF_ENABLE:
        g_value_set_boolean(value, vpu_enc->profile);
        break;

    case MFW_GST_VPU_CODEC_TYPE:
        g_value_set_enum(value, vpu_enc->codec);
        break;

    case MFW_GST_VPUENC_WIDTH:
        g_value_set_uint(value, vpu_enc->width);
        break;

    case MFW_GST_VPUENC_HEIGHT:
        g_value_set_uint(value, vpu_enc->height);
        break;

    case MFW_GST_VPUENC_BITRATE:
        g_value_set_uint(value, vpu_enc->bitrate );
        break;

    case MFW_GST_VPUENC_QP:
        g_value_set_uint(value, vpu_enc->qp );
        break;

    case MFW_GST_VPUENC_MAX_QP:
        g_value_set_uint(value, vpu_enc->max_qp );
        break;

    case MFW_GST_VPUENC_INTRA_QP:
        g_value_set_uint(value, vpu_enc->intra_qp );
        break;

    case MFW_GST_VPUENC_GAMMA:
        g_value_set_uint(value, vpu_enc->gamma );
        break;

    case MFW_GST_VPUENC_FRAME_RATE:
        g_value_set_float(value, vpu_enc->tgt_framerate );
        break;

    case MFW_GST_VPUENC_GOP:
        g_value_set_uint(value, vpu_enc->gopsize);
        break;

    case MFW_GST_VPUENC_INTRAREFRESH:
        g_value_set_uint(value, vpu_enc->intraRefresh);
        break;
        
    case MFW_GST_VPUENC_FORCEIINTERVAL:
        g_value_set_uint(value, vpu_enc->forceIFrameInterval);
        break;

    case MFW_GST_VPUENC_H263PROFILE0:
        g_value_set_boolean(value, vpu_enc->h263profile0);
        break;

    case MFW_GST_VPUENC_LOOPBACK:
        g_value_set_boolean(value, vpu_enc->loopback);
        break;

    case MFW_GST_VPUENC_CROP_LEFT:
        g_value_set_uint(value, vpu_enc->crop_left);
        break;

    case MFW_GST_VPUENC_CROP_TOP:
        g_value_set_uint(value, vpu_enc->crop_top);
        break;

    case MFW_GST_VPUENC_CROP_RIGHT:
        g_value_set_uint(value, vpu_enc->crop_right);
        break;

    case MFW_GST_VPUENC_CROP_BOTTOM:
        g_value_set_uint(value, vpu_enc->crop_bottom);
        break;

    default:
        G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
        break;
    }
    return;
}


/*=============================================================================
FUNCTION:           mfw_gst_vpuenc_configure

DESCRIPTION:        Configure the IRAM memory

ARGUMENTS PASSED:   vpu_enc - Plug-in context.   

RETURN VALUE:       RETCODE_SUCCESS/ VPU failure return code
PRE-CONDITIONS:     None
POST-CONDITIONS:    None
IMPORTANT NOTES:    None
=============================================================================*/
RetCode mfw_gst_vpuenc_configure(MfwGstVPU_Enc * vpu_enc)
{
    SearchRamParam search_pa = {0};
    RetCode vpu_ret=RETCODE_SUCCESS;
#if (defined(VPU_MX51) || defined(VPU_MX53))
    iram_t iram;
    int ram_size;

    memset(&iram, 0, sizeof(iram_t));
    ram_size = ((vpu_enc->width+ 15) & ~15) * 36 + 2048;
    IOGetIramBase(&iram);
    if ((iram.end - iram.start) < ram_size)
    	ram_size = iram.end - iram.start;
    search_pa.searchRamAddr = iram.start;
    search_pa.SearchRamSize = ram_size;
#else
   #define DEFAULT_SEARCHRAM_ADDR  (0xFFFF4C00)
   search_pa.searchRamAddr = DEFAULT_SEARCHRAM_ADDR;
#endif

    vpu_ret = vpu_EncGiveCommand(vpu_enc->handle, ENC_SET_SEARCHRAM_PARAM, &search_pa);
    if (vpu_ret != RETCODE_SUCCESS) {
        GST_ERROR(">>VPU_ENC: SET_SEARCHRAM_PARAM failed\n");
    }
    return vpu_ret;
}


/*=============================================================================
FUNCTION:           mfw_gst_vpuenc_free_framebuffer

DESCRIPTION:        Free frame buffer allocated memory

ARGUMENTS PASSED:   vpu_enc - Plug-in context.   

RETURN VALUE:       None
PRE-CONDITIONS:     None
POST-CONDITIONS:    None
IMPORTANT NOTES:    None
=============================================================================*/
void mfw_gst_vpuenc_free_framebuffer(MfwGstVPU_Enc * vpu_enc)
{
    int i;
    int framenum = vpu_enc->numframebufs+1;
    vpu_mem_desc * framedesc = vpu_enc->vpuRegisteredFramesDesc;
    FrameBuffer * frame = vpu_enc->vpuRegisteredFrames;

    GST_DEBUG(">>VPU_ENC: mfw_gst_vpuenc_free_framebuffer\n");
    if (framedesc){
        for (i=0;i<framenum;i++) {
            if (framedesc[i].phy_addr){
                IOFreeVirtMem(&(framedesc[i]));
                IOFreePhyMem(&(framedesc[i]));
            }
        }
        g_free(framedesc);
        vpu_enc->vpuRegisteredFramesDesc = NULL;
    }

    if (frame){
        g_free(frame);
        vpu_enc->vpuRegisteredFrames = NULL;
    }
    if (vpu_enc->hdr_data)
        gst_buffer_unref (vpu_enc->hdr_data);
    vpu_enc->hdr_data = NULL;

    if (vpu_enc->codec_data)
        gst_buffer_unref (vpu_enc->codec_data);
    vpu_enc->codec_data = NULL;

}


/*=============================================================================
FUNCTION:           mfw_gst_vpuenc_alloc_framebuffer

DESCRIPTION:        Allocate frame buffer memory

ARGUMENTS PASSED:   vpu_enc - Plug-in context.   

RETURN VALUE:       TRUE (Success)/FALSE (Failure
PRE-CONDITIONS:     None
POST-CONDITIONS:    None
IMPORTANT NOTES:    None
=============================================================================*/
gboolean mfw_gst_vpuenc_alloc_framebuffer(MfwGstVPU_Enc * vpu_enc)
{
    int i;
    int framenum = vpu_enc->numframebufs+1;
    int framebuffersize;
    int picturesize;
    vpu_mem_desc * framedesc;
    FrameBuffer * frame;

    framedesc = g_malloc(sizeof(vpu_mem_desc)*framenum);
    if (framedesc==NULL){
        goto Err;
    }

    frame = g_malloc(sizeof(FrameBuffer)*framenum);
    if (frame==NULL){
        goto Err;
    }
    
    memset(framedesc, 0, (sizeof(vpu_mem_desc)*framenum));
    memset(frame, 0, (sizeof(FrameBuffer)*framenum));

    picturesize = vpu_enc->width * vpu_enc->height;
    framebuffersize = picturesize * 3/2; //7/4 - using this crashes with VGA or higher resolutions encoding from file

    for (i=0;i<framenum;i++) {
        framedesc[i].size = framebuffersize;    
        IOGetPhyMem(&(framedesc[i]));
        if (framedesc[i].phy_addr==0){
            goto Err;
        }
        framedesc[i].virt_uaddr = IOGetVirtMem(&(framedesc[i]));
        frame[i].strideY = vpu_enc->width;
        frame[i].strideC = vpu_enc->width / 2;
        
        frame[i].bufY = framedesc[i].phy_addr;
        frame[i].bufCb = frame[i].bufY  + picturesize;
        frame[i].bufCr = frame[i].bufCb + (picturesize>>2);
#if (defined(VPU_MX51) || defined(VPU_MX53))
        if (vpu_enc->codec==STD_MJPG){
            frame[i].bufMvCol = frame[i].bufCr;
        }else{
            frame[i].bufMvCol = frame[i].bufCr+(picturesize>>2);
        }
#endif
    }

    vpu_enc->vpuRegisteredFrames = frame;
    vpu_enc->vpuRegisteredFramesDesc = framedesc;
    vpu_enc->end_addr = (guint8*)vpu_enc->vpuRegisteredFramesDesc[vpu_enc->numframebufs].virt_uaddr + framebuffersize;
    
    return TRUE;

Err:
    vpu_enc->vpuRegisteredFrames = NULL;
    vpu_enc->vpuRegisteredFramesDesc = NULL;
    vpu_enc->end_addr = NULL;
    mfw_gst_vpuenc_free_framebuffer(vpu_enc);
    return FALSE;
}



/*=============================================================================
FUNCTION:           mfw_gst_vpuenc_cleanup

DESCRIPTION:        Closes the Encoder and frees all the memory allocated
             
ARGUMENTS PASSED:   vpu_enc - Plug-in context.   

RETURN VALUE:       None
PRE-CONDITIONS:     None
POST-CONDITIONS:    None
IMPORTANT NOTES:    None
=============================================================================*/
void mfw_gst_vpuenc_cleanup(MfwGstVPU_Enc *vpu_enc)
{

    int i=0;
    RetCode vpu_ret=RETCODE_SUCCESS;
    int ret=0;
    GST_DEBUG(">>VPU_ENC: mfw_gst_vpuenc_cleanup\n");

#if (defined(VPU_MX51) || defined(VPU_MX53))
    if (vpu_enc->codec==STD_MJPG){
        if ((vpu_enc->encOP) && (vpu_enc->encOP->EncStdParam.mjpgParam.mjpg_hufTable)){
            g_free(vpu_enc->encOP->EncStdParam.mjpgParam.mjpg_hufTable);
			vpu_enc->encOP->EncStdParam.mjpgParam.mjpg_hufTable = NULL;
        }
        if ((vpu_enc->encOP) && (vpu_enc->encOP->EncStdParam.mjpgParam.mjpg_qMatTable)){
            g_free(vpu_enc->encOP->EncStdParam.mjpgParam.mjpg_qMatTable);
			vpu_enc->encOP->EncStdParam.mjpgParam.mjpg_qMatTable = NULL;
        }
    }
#endif

    mfw_gst_vpuenc_free_framebuffer(vpu_enc);
    if(vpu_enc->handle > 0 )
    {
        vpu_ret = vpu_EncClose(vpu_enc->handle);
        if (vpu_ret == RETCODE_FRAME_NOT_COMPLETE) {
            vpu_EncGetOutputInfo(vpu_enc->handle, vpu_enc->outputInfo);
            vpu_ret = vpu_EncClose(vpu_enc->handle);
            if(ret < 0){
                GST_ERROR(">>VPU_ENC: Error %d closing VPU\n",vpu_ret);
            }
        }
        vpu_enc->handle = 0;
        vpu_enc->vpu_init = FALSE;
    }
    
    if(vpu_enc->encOP!=NULL)
    {
        g_free(vpu_enc->encOP);
        vpu_enc->encOP = NULL;
    }
    if(vpu_enc->initialInfo!=NULL)
    {
        g_free(vpu_enc->initialInfo);
        vpu_enc->initialInfo = NULL;
    }
    if(vpu_enc->encParam !=NULL)
    {
        g_free(vpu_enc->encParam);
        vpu_enc->encParam = NULL;
    }
    if(vpu_enc->outputInfo!=NULL)
    {
        g_free(vpu_enc->outputInfo);
        vpu_enc->outputInfo = NULL;
    }

    IOFreeVirtMem(&(vpu_enc->bit_stream_buf));
    IOFreePhyMem(&(vpu_enc->bit_stream_buf));

}


/*=============================================================================
FUNCTION:           mfw_gst_vpuenc_fill_headers

DESCRIPTION:        Writes the Headers incase of MPEG4 and H.264 before encoding 
                    the first frame.
             
ARGUMENTS PASSED:   vpu_enc - Plug-in context.   

RETURN VALUE:       0 (SUCCESS)/ -1 (FAILURE)
PRE-CONDITIONS:     None
POST-CONDITIONS:    None
IMPORTANT NOTES:    None
=============================================================================*/
int mfw_gst_vpuenc_fill_headers(MfwGstVPU_Enc *vpu_enc)
{
    EncHeaderParam enchdr_param = {0};
    guint8      *ptr;
    guint8      *header[NUM_INPUT_BUF];        // header memory specific to code
    guint       headersize[NUM_INPUT_BUF];     // size for each header element
    gint        headercount=0;                 // size of headers
	gint        hdrsize = 0;
    RetCode vpu_ret=RETCODE_SUCCESS;


	// Set the target frame rate - one set in EncOpen is for source
	// we are only handling fractional part for 29.97 NTSC input.
	gint frameRateInfo = 0;
    if (vpu_enc->tgt_framerate == 29.97)
        frameRateInfo = 0x3e87530;
    else   
        frameRateInfo =  (guint) (vpu_enc->tgt_framerate+0.5);
  	GST_DEBUG (">>VPU_ENC: Target frame rate = %d\n", frameRateInfo);
    vpu_EncGiveCommand(vpu_enc->handle, ENC_SET_FRAME_RATE, &frameRateInfo);

    if (vpu_enc->intra_qp != VPU_DEFAULT_QP)
        vpu_EncGiveCommand(vpu_enc->handle, ENC_SET_INTRA_QP, &vpu_enc->intra_qp);

    vpu_enc->num_in_interval = 0;
    vpu_enc->num_enc_in_interval = 0;
    vpu_enc->fDropping_till_IFrame = FALSE;

    // it seems that bono hardware does not enable frame rate conversion so we will do it manually
    // also only handle 30fps reduction - no other variations from src
    if ((vpu_enc->src_framerate == DEFAULT_FRAME_RATE) &&
        (vpu_enc->src_framerate != vpu_enc->tgt_framerate))
    {
        vpu_enc->forcefixrate = FALSE; // do not do force fixed rate if frame reducing
        if (vpu_enc->tgt_framerate == 29.97)
            vpu_enc->num_enc_in_interval = 0;
        else if (vpu_enc->tgt_framerate >= 25.0) {
            vpu_enc->num_in_interval = 6;
            vpu_enc->num_enc_in_interval = 5; // encode 5 then skip 1
        }
        else if (vpu_enc->tgt_framerate >= 20.0) {
            vpu_enc->num_in_interval = 3;
            vpu_enc->num_enc_in_interval = 2; // encode 2 then skip 1
        }
        else if (vpu_enc->tgt_framerate >= 15.0) {
            vpu_enc->num_in_interval = 2;
            vpu_enc->num_enc_in_interval = 1; // encode 1 then skip 1
        }
        else if (vpu_enc->tgt_framerate >= 10.0) {
            vpu_enc->num_in_interval = 3;
            vpu_enc->num_enc_in_interval = 1; // encode 1 then skip 2
        }
        else if (vpu_enc->tgt_framerate >= 7.5) {
            vpu_enc->num_in_interval = 4;
            vpu_enc->num_enc_in_interval = 1; // encode 1 then skip 3
        }
        else if (vpu_enc->tgt_framerate >= 6.0) {
            vpu_enc->num_in_interval = 5;
            vpu_enc->num_enc_in_interval = 1; // encode 1 then skip 5
        }
        else if (vpu_enc->tgt_framerate >= 5.0) {
            vpu_enc->num_in_interval = 6;
            vpu_enc->num_enc_in_interval = 1; // encode 1 then skip 5
        }
        else if (vpu_enc->tgt_framerate >= 3.0) {
            vpu_enc->num_in_interval = 10;
            vpu_enc->num_enc_in_interval = 1; // encode 1 then skip 9
        }
        else if (vpu_enc->tgt_framerate >= 2.0) {
            vpu_enc->num_in_interval = 15;
            vpu_enc->num_enc_in_interval = 1; // encode 1 then skip 14
        } else  {
            vpu_enc->num_in_interval = 30;
            vpu_enc->num_enc_in_interval = 1; // encode 1 then skip 29
        }
        GST_DEBUG (">>VPU_ENC: num_in_interval %d num_enc_in_interval %d\n", 
                        vpu_enc->num_in_interval, vpu_enc->num_enc_in_interval);
        vpu_enc->idx_interval = 1;
    }

    vpu_enc->time_per_frame = gst_util_uint64_scale_int(GST_SECOND, 1, vpu_enc->tgt_framerate+0.5);

    GST_DEBUG(">>VPU_ENC: Target frame rate %f time_per_frame %d\n", vpu_enc->tgt_framerate, vpu_enc->time_per_frame);

    vpu_enc->codec_data = NULL;

    // Must put encode header before encoding output 
    if ((vpu_enc->codec == STD_MPEG4) /* || (vpu_enc->codec == STD_H263)*/ )
    {
        headercount = 0;
        enchdr_param.headerType = VOS_HEADER;
        vpu_ret = vpu_EncGiveCommand(vpu_enc->handle, ENC_PUT_MP4_HEADER, &enchdr_param);
        if (enchdr_param.size==0)
        {
            GST_DEBUG(">>VPU_ENC: Error %d in Allocating memory for VOS_HEADER size %d\n", vpu_ret, hdrsize);
        } else {
            headersize[headercount] = enchdr_param.size;
            hdrsize += headersize[headercount];
            header[headercount]=g_malloc(enchdr_param.size);
            ptr = vpu_enc->start_addr + enchdr_param.buf - vpu_enc->bit_stream_buf.phy_addr;
            memcpy(header[headercount],ptr,enchdr_param.size);
            headercount++;
        }

		enchdr_param.headerType = VIS_HEADER;
		vpu_ret = vpu_EncGiveCommand(vpu_enc->handle, ENC_PUT_MP4_HEADER, &enchdr_param);
        if (enchdr_param.size==0)
        {
            GST_DEBUG(">>VPU_ENC: Error %d in Allocating memory for VIS_HEADER\n", vpu_ret);
        } else {
            header[headercount]=g_malloc(enchdr_param.size);
            headersize[headercount] = enchdr_param.size;
            hdrsize += headersize[headercount];
            ptr = vpu_enc->start_addr + enchdr_param.buf - vpu_enc->bit_stream_buf.phy_addr;
            memcpy(header[headercount],ptr,enchdr_param.size);
            headercount++;
        }

		enchdr_param.headerType = VOL_HEADER;
		vpu_ret = vpu_EncGiveCommand(vpu_enc->handle, ENC_PUT_MP4_HEADER, &enchdr_param);
        if (enchdr_param.size==0)
        {
            GST_DEBUG(">>VPU_ENC: Error %d in Allocating memory for VOL_HEADER\n", vpu_ret);
        } else {
            headersize[headercount] = enchdr_param.size;
            hdrsize += headersize[headercount];
            header[headercount]=g_malloc(enchdr_param.size);
            ptr = vpu_enc->start_addr + enchdr_param.buf - vpu_enc->bit_stream_buf.phy_addr;
            memcpy(header[headercount],ptr,enchdr_param.size);
            headercount++;
        }

	} else if (vpu_enc->codec == STD_AVC)   {

        headercount=2;
		enchdr_param.headerType = SPS_RBSP;
		vpu_ret = vpu_EncGiveCommand(vpu_enc->handle, ENC_PUT_AVC_HEADER, &enchdr_param);
        headersize[SPS_HDR] = enchdr_param.size;
        hdrsize += headersize[SPS_HDR];
        header[SPS_HDR]=g_malloc(enchdr_param.size);
        if (header[SPS_HDR]==NULL)
        {
            GST_ERROR(">>VPU_ENC: Error %d in Allocating memory for SPS_RBSP Header \n", vpu_ret);
            return -1;
        }

        ptr = vpu_enc->start_addr + enchdr_param.buf - vpu_enc->bit_stream_buf.phy_addr;
        memcpy(header[SPS_HDR],ptr,enchdr_param.size);
    	enchdr_param.headerType = PPS_RBSP;
		vpu_ret = vpu_EncGiveCommand(vpu_enc->handle, ENC_PUT_AVC_HEADER, &enchdr_param);
        headersize[PPS_HDR]= enchdr_param.size;
        hdrsize += headersize[PPS_HDR];
        header[1]=g_malloc(enchdr_param.size);
        if (header[PPS_HDR]==NULL)
        {
            GST_ERROR(">>VPU_ENC: Error %d in Allocating memory for PPS_RBSP Header \n", vpu_ret);
            return -1;
        }

        ptr = vpu_enc->start_addr + enchdr_param.buf - vpu_enc->bit_stream_buf.phy_addr;
        memcpy(header[PPS_HDR],ptr,enchdr_param.size);
    } 

    // prepare the header for both first frame and on some I frames
    if (headercount != 0) {
        guint i=0, offset=0;
        //unsigned char *bufhdr;
        vpu_enc->hdr_data = gst_buffer_new_and_alloc(hdrsize);

        for (i=0;i<headercount;i++) {
            memcpy(GST_BUFFER_DATA(vpu_enc->hdr_data)+offset,
                   header[i],headersize[i]);
            offset += headersize[i];
            g_free(header[i]);
        }
        //bufhdr = GST_BUFFER_DATA(vpu_enc->hdr_data);
        //g_print (">>VPU_ENC:hdr_data\n");
        //for (i=0;i<GST_BUFFER_SIZE(vpu_enc->hdr_data);i++)
        //    g_print(" %x",*(bufhdr+i));
        //g_print (" \n");

    }
    return 0 ;
}


/*=============================================================================
FUNCTION:           mfw_gst_vpuenc_send_buffer

DESCRIPTION:        Send the buffer downstream.
             
ARGUMENTS PASSED:   vpu_enc   - Plug-in context.   
                    buffer    - output buffer to send

RETURN VALUE:       GstFlowReturn - Success of Failure.
PRE-CONDITIONS:     None
POST-CONDITIONS:    None
IMPORTANT NOTES:    None
=============================================================================*/
GstFlowReturn mfw_gst_vpuenc_send_buffer(MfwGstVPU_Enc *vpu_enc, GstBuffer *buffer)
{
    GstFlowReturn retval = GST_FLOW_OK;
    GstClockTime ts;

    TIMESTAMP_OUT(vpu_enc,ts);
    GST_BUFFER_TIMESTAMP(buffer) = ts;

    if (vpu_enc->segment_starttime == 0) {
        vpu_enc->segment_starttime = ts;
        vpu_enc->total_time = ts;
    }

    // adjust the buffer duration for time for encoding
    GST_BUFFER_DURATION(buffer) = vpu_enc->time_per_frame;

    if (vpu_enc->forcefixrate && (vpu_enc->segment_encoded_frame>30) &&
        GST_BUFFER_TIMESTAMP_IS_VALID(buffer) && GST_ELEMENT (vpu_enc)->clock)
    {
        GstClockTime curr_ts=0;
        GstClockTimeDiff diff_ts;
        guint num_frame_delay = 0;
        gboolean video_ahead = FALSE;

        // calculate our current time
        curr_ts = gst_clock_get_time (GST_ELEMENT (vpu_enc)->clock);
        curr_ts -= GST_ELEMENT (vpu_enc)->base_time;

        diff_ts = curr_ts-ts;

        // check if we are ahead or behind by how many frames
        if (curr_ts > ts)
        {
            while (diff_ts > vpu_enc->time_per_frame)
            {
                num_frame_delay++;
                diff_ts -= vpu_enc->time_per_frame;
            }
            if (num_frame_delay>1)
            {
                //GST_DEBUG(">>VPU_ENC: Video is behind %d frames diff %d\n", num_frame_delay, (guint) diff_ts);
                GST_BUFFER_TIMESTAMP(buffer) = curr_ts;
            }
        } else {
            diff_ts = ts-curr_ts;
            while (diff_ts > vpu_enc->time_per_frame)
            {
               num_frame_delay++;
               diff_ts -= vpu_enc->time_per_frame;
            }
            if (num_frame_delay > 1) 
            {
                //GST_DEBUG(">>VPU_ENC: Video is ahead %d frames\n", num_frame_delay);
            }
            video_ahead = TRUE;
        }

		// Drop frame check if current timestamp is later 1 frame                 
        if (vpu_enc->fDropping_till_IFrame || (!video_ahead && (num_frame_delay>1)) ) 
        {   //drop one frame as long as it is a delta frame
            if (GST_BUFFER_FLAG_IS_SET(buffer,GST_BUFFER_FLAG_DELTA_UNIT))
            {
                gst_buffer_unref(buffer);

                // if our qp is really low we should probably try increasing it to minimize 
                // dropping frames in next gop
                if ((vpu_enc->fDropping_till_IFrame == FALSE) && 
                    (vpu_enc->qp != VPU_DEFAULT_QP))
                {
                    vpu_enc->encParam->quantParam++;
                    if(vpu_enc->codec==STD_AVC)
                    {
                        if (vpu_enc->encParam->quantParam > VPU_MAX_H264_QP)
                            vpu_enc->encParam->quantParam = VPU_MAX_H264_QP;                        
                    } else
                    {
                        if (vpu_enc->encParam->quantParam > VPU_MAX_MPEG4_QP)
                            vpu_enc->encParam->quantParam = VPU_MAX_MPEG4_QP;                        
                    }
                }
                vpu_enc->fDropping_till_IFrame = TRUE;
                //GST_DEBUG (">>VPU_ENC: Dropping frame %d qp = %d \n", vpu_enc->num_encoded_frames, vpu_enc->encParam->quantParam);
                return GST_FLOW_OK;
            }
        } else if (video_ahead) {
            GstBuffer * tmp;

            // check if we are more than 1 frame ahead
            // check if current time stamp is faster than current time stamp
            while (num_frame_delay>1)
            {
                retval = gst_pad_alloc_buffer_and_set_caps(vpu_enc->srcpad, 0,
                            4, GST_PAD_CAPS(vpu_enc->srcpad), &tmp);
                if (retval==GST_FLOW_OK){
                    GST_BUFFER_TIMESTAMP(tmp)=vpu_enc->total_time;
                    GST_BUFFER_SIZE(tmp) = 0;
                    GST_BUFFER_FLAG_SET(tmp,GST_BUFFER_FLAG_DELTA_UNIT);
                    retval=gst_pad_push(vpu_enc->srcpad,tmp);
                    GST_DEBUG (" sending dummy frame to keep frame rate %d \n", num_frame_delay);
                }
                vpu_enc->segment_encoded_frame++;
                num_frame_delay--;
                vpu_enc->total_time += vpu_enc->time_per_frame;
            }
        }
    }
    

	//GST_DEBUG (">>VPU_ENC: Pushing buffer size=%d frame %d \n", GST_BUFFER_SIZE(buffer), vpu_enc->segment_encoded_frame);

    retval = gst_pad_push(vpu_enc->srcpad,buffer);
    if(retval != GST_FLOW_OK){
        GST_ERROR(">>VPU_ENC: Error %d in Pushing the Output to Source Pad\n",retval);
        return retval;
    }
 
    vpu_enc->total_time += vpu_enc->time_per_frame;
    vpu_enc->segment_encoded_frame++;
 
    return retval; 
}


/*======================================================================================
FUNCTION:           mfw_gst_vpuenc_vpuinitialize

DESCRIPTION:        The main processing function where the data comes in as buffer. This 
                    data is encoded, and then pushed onto the next element for further
                    processing.

ARGUMENTS PASSED:   vpu_enc   - Plug-in context.   
=======================================================================================*/
GstFlowReturn mfw_gst_vpuenc_vpuinitialize(MfwGstVPU_Enc *vpu_enc)
{
    RetCode vpu_ret=RETCODE_SUCCESS;
    GstFlowReturn retval=GST_FLOW_OK;
    GstCaps *caps=NULL;;
    EncInitialInfo initialInfo = { 0 };
    gchar *mime;

	GST_DEBUG (">>VPU_ENC: mfw_gst_vpuenc_vpuinitialize\n");


    // verify that width, height and code type have been provided
    if (!vpu_enc->heightProvided ||
        !vpu_enc->widthProvided  || 
        !vpu_enc->codecTypeProvided) 
    {
        GST_ERROR(">>VPU_ENC: Incomplete command line.\n");
        GError *error = NULL;
        GQuark domain = g_quark_from_string("mfw_vpuencoder");
        error = g_error_new(domain, 10, "fatal error");
        gst_element_post_message(GST_ELEMENT(vpu_enc),
        gst_message_new_error(GST_OBJECT(vpu_enc),error,
                "Incomplete command line - Width, height or codec type was not provided."));
        return GST_FLOW_ERROR;
    }
    	
#if (defined(VPU_MX51) || defined(VPU_MX53))
    if (vpu_enc->format == GST_MAKE_FOURCC('N', 'V', '1', '2')) {
        vpu_enc->encOP->chromaInterleave = 1;
        GST_DEBUG("Input format is NV12.\n");
    }
#endif
    // Revise for cropping settings.
    if ((vpu_enc->width > vpu_enc->crop_left) &&
        (vpu_enc->height > vpu_enc->crop_top) ) {
        vpu_enc->encOP->picWidth = vpu_enc->width  - vpu_enc->crop_left;
        vpu_enc->encOP->picHeight = vpu_enc->height - vpu_enc->crop_top;
        if (vpu_enc->encOP->picWidth > vpu_enc->crop_right)
            vpu_enc->encOP->picWidth -=  vpu_enc->crop_right;

        if (vpu_enc->encOP->picHeight > vpu_enc->crop_bottom)
            vpu_enc->encOP->picHeight -= vpu_enc->crop_bottom;
        
        
    }
    else {
        GST_ERROR("Crop value is exceed the source value\n");
        vpu_enc->encOP->picWidth = vpu_enc->width;
        vpu_enc->encOP->picHeight = vpu_enc->height;
    }
    GST_INFO("VPU encoder resolution:%d x %d\n",
        vpu_enc->encOP->picWidth, vpu_enc->encOP->picHeight);
    vpu_enc->yuv_frame_size = (((vpu_enc->width * vpu_enc->height) *3) / 2);
    vpu_enc->bytes_consumed = 0;
    vpu_enc->encOP->intraRefresh = vpu_enc->intraRefresh;

    // The Frame Rate Value is never set for H.263        
    // For EncOpen this is src frame rate 
	if (vpu_enc->framerate_d)
       vpu_enc->encOP->frameRateInfo = ((vpu_enc->framerate_d-1) << 16) | vpu_enc->framerate_n;
	else 
       vpu_enc->encOP->frameRateInfo =  (guint) (vpu_enc->src_framerate+.5);

    //GST_DEBUG (">>VPU_ENC:frame_rate_d = %d  frame_rate_n %d\n", vpu_enc->framerate_d, vpu_enc->framerate_n);
    //GST_DEBUG (">>VPU_ENC: src frame rate = %x %f\n", vpu_enc->encOP->frameRateInfo, vpu_enc->src_framerate);

    
    // open a VPU's encoder instance 
    vpu_ret = vpu_EncOpen(&vpu_enc->handle,vpu_enc->encOP);
    if (vpu_ret != RETCODE_SUCCESS) {
        GST_ERROR(">>VPU_ENC: vpu_EncOpen failed. Error code is %d \n", vpu_ret);
        GError *error = NULL;
        GQuark domain;
        domain = g_quark_from_string("mfw_vpuencoder");
        error = g_error_new(domain, 10, "fatal error");
        gst_element_post_message(GST_ELEMENT(vpu_enc),
			gst_message_new_error(GST_OBJECT(vpu_enc),error,"vpu_EncOpen failed"));
        return GST_FLOW_ERROR;
    }

    // configure VPU RAM memory
    if (mfw_gst_vpuenc_configure(vpu_enc) != RETCODE_SUCCESS) {
        return GST_FLOW_ERROR;
	}
   
    // get the min number of framebuffers to be allocated  
    vpu_ret = vpu_EncGetInitialInfo(vpu_enc->handle, &initialInfo);
    if (vpu_ret != RETCODE_SUCCESS) {
        GST_ERROR(">>VPU_ENC: vpu_EncGetInitialInfo failed. Error code is %d \n",vpu_ret);
        GError *error = NULL;
        GQuark domain;
        domain = g_quark_from_string("mfw_vpuencoder");
        error = g_error_new(domain, 10, "fatal error");
        gst_element_post_message(GST_ELEMENT(vpu_enc), 
			gst_message_new_error(GST_OBJECT(vpu_enc),
                        error,"vpu_EncGetInitialInfo failed"));
        return GST_FLOW_ERROR;

    }

    // save min number of buffers and allocate 
    vpu_enc->numframebufs = initialInfo.minFrameBufferCount;
    GST_DEBUG(">>VPU_ENC: num frame bufs needed is %d\n" , vpu_enc->numframebufs);
    if (!mfw_gst_vpuenc_alloc_framebuffer(vpu_enc))
    {
        GST_ERROR(">>VPU_ENC: Failure allocating frame buffers\n");
        return GST_FLOW_ERROR;
    }
        
    // register with vpu the allocated frame buffers
    vpu_ret = vpu_EncRegisterFrameBuffer(vpu_enc->handle, 
                                         vpu_enc->vpuRegisteredFrames, 
                                         vpu_enc->numframebufs, 
                                         vpu_enc->encOP->picWidth,
                                         vpu_enc->width);
        
    if (vpu_ret != RETCODE_SUCCESS) {
            GST_ERROR(">>VPU_ENC: vpu_EncRegisterFrameBuffer failed.Error code is %d \n", vpu_ret);
            GError *error = NULL;
            GQuark domain;
            domain = g_quark_from_string("mfw_vpuencoder");
            error = g_error_new(domain, 10, "fatal error");
            gst_element_post_message(GST_ELEMENT(vpu_enc),
                gst_message_new_error(GST_OBJECT(vpu_enc),error,
                                      "vpu_EncRegisterFrameBuffer failed "));
            return GST_FLOW_ERROR;
    }
        
    // if neither QP or Bitrate is set - use the default per codec
    if ((vpu_enc->qp == VPU_DEFAULT_QP) /*&& (vpu_enc->bitrate == 0)*/)
    {      
        if(vpu_enc->codec==STD_AVC)
           vpu_enc->encParam->quantParam = VPU_DEFAULT_H264_QP;
        else
           vpu_enc->encParam->quantParam = VPU_DEFAULT_MPEG4_QP;    

    } else if (vpu_enc->bitrate  && (vpu_enc->qp != VPU_DEFAULT_QP)) {
        // if both bitrate and qp are set - use default QP as bitrate overrides qp
        vpu_enc->encParam->quantParam = vpu_enc->qp;    
    } else  { // else NOW you can use the qp set by cmd line
        vpu_enc->encParam->quantParam = vpu_enc->qp;
    }

    GST_DEBUG (">>VPU_ENC: QP quantparam = %d openparam qp =%d\n", vpu_enc->encParam->quantParam, vpu_enc->encOP->rcIntraQp);

    // Set a default parameters and initialize header
    vpu_enc->encParam->forceIPicture = 0;
    vpu_enc->encParam->skipPicture = 0;

    // Set the crop information.
    vpu_enc->encParam->encTopOffset = vpu_enc->crop_top;
    vpu_enc->encParam->encLeftOffset = vpu_enc->crop_left;
    
    if (mfw_gst_vpuenc_fill_headers(vpu_enc) < 0)
    {
        GError *error = NULL;
        GQuark domain;
        domain = g_quark_from_string("mfw_vpuencoder");
        error = g_error_new(domain, 10, "fatal error");
        gst_element_post_message(GST_ELEMENT(vpu_enc),
                gst_message_new_error(GST_OBJECT(vpu_enc), error,
                                      "Allocation for Headers failed "));
        return GST_FLOW_ERROR;
    }

    // set up source pad capabilities
    if(vpu_enc->codec==STD_MPEG4)
        mime = "video/mpeg";
    else if(vpu_enc->codec==STD_AVC)
        mime = "video/x-h264";
    else if(vpu_enc->codec==STD_H263)
        mime = "video/x-h263";
#if (defined(VPU_MX51) || defined(VPU_MX53))
    else if(vpu_enc->codec==STD_MJPG)
        mime = "image/jpeg";
#endif

    caps = gst_caps_new_simple(mime,
            "height", G_TYPE_INT, vpu_enc->encOP->picHeight,
            "width", G_TYPE_INT, vpu_enc->encOP->picWidth,
            "framerate", GST_TYPE_FRACTION,
            vpu_enc->framerate_n, vpu_enc->framerate_d,
            NULL);

    // some payloaders later do not provide our headers
    // but if we provide it as codec_data it will get through
    if (vpu_enc->codec==STD_MPEG4)
    {
        gst_caps_set_simple (caps,
                             "mpegversion", G_TYPE_INT, 4,
                             "systemstream", G_TYPE_BOOLEAN, FALSE,
                              NULL);      
    }

    gst_pad_set_caps(vpu_enc->srcpad, caps);

    // we are initialized!
    gst_caps_unref (caps);
    vpu_enc->vpu_init=TRUE;

    return retval;
}


/*======================================================================================
FUNCTION:           mfw_gst_vpuenc_copy_sink_start_frame

DESCRIPTION:        This copies the sink buffer into the VPU and starts a frame

ARGUMENTS PASSED:   vpu_enc   - Plug-in context.   
                    buffer - pointer to the input buffer which has the YUV data.

RETURN VALUE:       GstFlowReturn - Success of Failure.
PRE-CONDITIONS:     None
POST-CONDITIONS:    None
IMPORTANT NOTES:    None
=======================================================================================*/
GstFlowReturn mfw_gst_vpuenc_copy_sink_start_frame(MfwGstVPU_Enc *vpu_enc)
{
    RetCode vpu_ret=RETCODE_SUCCESS;
    guint to_copy = GST_BUFFER_SIZE(vpu_enc->gst_buffer) - vpu_enc->gst_copied;

    guint8* end_addr = vpu_enc->curr_addr + to_copy;
    guint8* start = (guint8*)vpu_enc->vpuRegisteredFramesDesc[vpu_enc->numframebufs].virt_uaddr;


    // We have 3 scenarios
    // 1)  Each input buffer is exactly the YUV size of width*height*3/2 - 
    //        happens with camera as source
    // 2)  Each input buffer is smaller than the YUV size so must take multiple copies
    //       default of filesink is 4096.  For last part of last buffer - using bytes_consumed
    //       you can copy the end to the beginning of buffer if needed
    // 3)  Each input buffer is larger than the YUV size so _chain routine must loop until
    //       all YUV buffers have been consumed before unref of input buffer
    //       can simulate this by setting blocksize=<> on filesrc
    
    // handle encoding from file in case we have more to copy than space available
    if (end_addr > vpu_enc->end_addr)
    {
        // can only copy part of the buffer
        //GST_DEBUG (">>VPU_ENC:  getting ready for wrapping input buffer \n");
        to_copy = vpu_enc->end_addr - vpu_enc->curr_addr;
    } 

    // only copy maximum of yuv frame size minus what is already copied - to keep just frame size in buff
    if ((to_copy+vpu_enc->bytes_consumed) > vpu_enc->yuv_frame_size) {
        to_copy = vpu_enc->yuv_frame_size - vpu_enc->bytes_consumed;
    }

	// Setup VPU with the input source buffer
    if (!vpu_enc->bytes_consumed && IS_DMABLE_BUFFER(vpu_enc->gst_buffer)) {
        gint picturesize = vpu_enc->width * vpu_enc->height;
        GST_DEBUG (">>VPU_ENC:  direct from sink \n");

       vpu_enc->vpuInFrame.bufY = DMABLE_BUFFER_PHY_ADDR(vpu_enc->gst_buffer);
        vpu_enc->vpuInFrame.bufCb = vpu_enc->vpuInFrame.bufY+picturesize;
        vpu_enc->vpuInFrame.bufCr = vpu_enc->vpuInFrame.bufCb+(picturesize>>2);
        vpu_enc->encParam->sourceFrame = &(vpu_enc->vpuInFrame);
        gst_buffer_unref(vpu_enc->gst_buffer);
        vpu_enc->gst_buffer = NULL;
    } else { 
        // we must memcpy input
        g_print (">>VPU_ENC:  Memcpy %d from input \n", to_copy);
        vpu_enc->forcefixrate = FALSE;
        vpu_enc->encParam->sourceFrame = &(vpu_enc->vpuRegisteredFrames[vpu_enc->numframebufs]);
        memcpy(vpu_enc->curr_addr, GST_BUFFER_DATA(vpu_enc->gst_buffer) + vpu_enc->gst_copied ,to_copy);

        vpu_enc->gst_copied += to_copy;

        // do not bother with frame rate adjustment if not from camera source
        vpu_enc->forcefixrate = FALSE; 

        // if all of buffer is consumed we can release it 
        if (vpu_enc->gst_copied == GST_BUFFER_SIZE(vpu_enc->gst_buffer))
        {
            gst_buffer_unref(vpu_enc->gst_buffer);
            vpu_enc->gst_buffer = NULL;
            vpu_enc->gst_copied = 0;
        } 
    }

    vpu_enc->bytes_consumed += to_copy;
    vpu_enc->curr_addr += to_copy;

    //GST_DEBUG (">>VPU_ENC: yuvsize %d bytes_consumed %d curr_addr 0x%x\n", vpu_enc->yuv_frame_size, vpu_enc->bytes_consumed, vpu_enc->curr_addr);

    // make sure we aren't starting encoding until we have a full frame size to encode.
    // if not return and wait for more data to copy before starting a frame encode
    if (vpu_enc->bytes_consumed < vpu_enc->yuv_frame_size)
        return GST_FLOW_OK;

    // we might have copied more than a frame so after encode is complete we'll move this from end of buffer
    // and put it at the beginning
    vpu_enc->bytes_consumed -= vpu_enc->yuv_frame_size;

    // all of buffer is consumed so next encode will start at beginning of buffer
    if (vpu_enc->bytes_consumed == 0)
        vpu_enc->curr_addr = start;
    else
        vpu_enc->curr_addr -= vpu_enc->bytes_consumed;  // reset to move to top later
 
    //GST_DEBUG (">>VPU_ENC: Start Decode but %d for next frame \n", vpu_enc->bytes_consumed);

    // force encode as I frame for forceIframeInterval when gopsize is not set
    if ((vpu_enc->forceIFrameInterval!=0) && 
        ((vpu_enc->num_encoded_frames % vpu_enc->forceIFrameInterval) == 0)) {
        vpu_enc->encParam->forceIPicture = 1;
    } else {
        vpu_enc->encParam->forceIPicture = 0;
    }

    // This handles actual skipping - if our target frame rate is lower than our src frame rate
    // we must tell VPU to skip the picture and encode as a small P frame
    // for each of these downsizing frame rates there is an interval of which some will be encoded then
    // the rest skipped.  The num_enc_in_interval is the amount of encodes in interval before skipping 
    // like 20fps encodes 2 then skips 1 with an interval of 3
    // or 15fps encodes 1 then skips 1 with an interval of 2
    // or 10fps encodes 1 then skips 3 with an interval of 4.
    if (vpu_enc->num_enc_in_interval)
    {
        gboolean skipPicture = FALSE;
        if (vpu_enc->idx_interval && (vpu_enc->idx_interval > vpu_enc->num_enc_in_interval))
            skipPicture = TRUE;        
        if (vpu_enc->idx_interval == vpu_enc->num_in_interval)
           vpu_enc->idx_interval = 1;
        else
           vpu_enc->idx_interval++;
        if (skipPicture) 
        {
            vpu_enc->num_total_frames++;
            if (vpu_enc->ts_rx == 0)
                vpu_enc->ts_rx = TIMESTAMP_INDEX_MASK-1;
            else
                vpu_enc->ts_rx--;
            //GST_DEBUG (">>VPU_ENC: Skipping picture %d\n", vpu_enc->num_total_frames);
            return GST_FLOW_OK;
        }
    }

#ifdef GST_LOGTIME
    if (GST_ELEMENT (vpu_enc)->clock)
    {
        vpu_enc->enc_start = gst_clock_get_time (GST_ELEMENT (vpu_enc)->clock);
        vpu_enc->enc_start -= GST_ELEMENT (vpu_enc)->base_time;
    }
#endif

    // Start encoding of one frame
    vpu_ret = vpu_EncStartOneFrame(vpu_enc->handle, vpu_enc->encParam);
    if (vpu_ret != RETCODE_SUCCESS) {
        GST_ERROR(">>VPU_ENC: vpu_EncStartOneFrame failed. Error code is %d \n", vpu_ret);
        return GST_FLOW_ERROR;
    }
    vpu_enc->is_frame_started = TRUE;
	return GST_FLOW_OK;
}



/*======================================================================================
FUNCTION:           mfw_gst_vpuenc_chain

DESCRIPTION:        The main processing function where the data comes in as buffer. This 
                    data is encoded, and then pushed onto the next element for further
                    processing.

ARGUMENTS PASSED:   pad - pointer to the sinkpad of this element
                    buffer - pointer to the input buffer which has the raw data.

RETURN VALUE:       GstFlowReturn - Success of Failure.
PRE-CONDITIONS:     None
POST-CONDITIONS:    None
IMPORTANT NOTES:    None
=======================================================================================*/
static GstFlowReturn mfw_gst_vpuenc_chain(GstPad *pad, GstBuffer *buffer)
{
    MfwGstVPU_Enc *vpu_enc = MFW_GST_VPU_ENC(GST_PAD_PARENT(pad));
    RetCode vpu_ret=RETCODE_SUCCESS;
    GstFlowReturn retval=GST_FLOW_OK;
    GstCaps *src_caps=GST_PAD_CAPS(vpu_enc->srcpad);
    GstBuffer *outbuffer=NULL;
	gboolean frame_started=vpu_enc->is_frame_started;
    gint i=0;

    // Initialize VPU if first time
    if(vpu_enc->vpu_init==FALSE) {
        retval = mfw_gst_vpuenc_vpuinitialize(vpu_enc);
        if (retval != GST_FLOW_OK){
            GST_ERROR(">>VPU_ENC: mfw_gst_vpuenc_vpuinitialize error is %d",retval);
            return retval;
        }
        vpu_enc->curr_addr = (guint8*)vpu_enc->vpuRegisteredFramesDesc[vpu_enc->numframebufs].virt_uaddr;
    }

    vpu_enc->gst_copied = 0;
    vpu_enc->gst_buffer = buffer;

    // remember timestamp in circular buffer but ignore duplicate time stamps
    if (vpu_enc->timestamp_buffer[vpu_enc->ts_rx] != GST_BUFFER_TIMESTAMP(buffer))
    {
        TIMESTAMP_IN(vpu_enc, GST_BUFFER_TIMESTAMP(buffer));

#if 0
        if (vpu_enc->forcefixrate)
        {
            // Save the start time before encoding - we'll use that to adjust our duration
            vpu_enc->time_before_enc = gst_clock_get_time (GST_ELEMENT (vpu_enc)->clock);
            vpu_enc->time_before_enc -= GST_ELEMENT (vpu_enc)->base_time;        
        }
#endif
    }


    // A frame was not started, copy data, and start frame and exit
	if (!frame_started)
	{
        vpu_ret = mfw_gst_vpuenc_copy_sink_start_frame(vpu_enc);
        if (vpu_enc->gst_buffer) {
            if (vpu_ret != GST_FLOW_OK)
               return vpu_ret;
        } else if (!vpu_enc->loopback) {
            if (vpu_enc->num_total_frames == 0)
               return GST_FLOW_OK;           
        }
    }

    // if frame started get output - loop until all of input buffer has been consumed
	do 
	{
        gint offset=0;
        gboolean isIFrame = FALSE;

        // Wait for the VPU to complete the Processing from previous frame
        if (vpu_enc->is_frame_started==TRUE) {
            gfloat avg_bitrate_per_sec=0.0, avg_bitrate_per_stream=0.0;

            vpu_ret =RETCODE_FAILURE;
            if (frame_started)
            {
                // Now get the output
                vpu_ret = vpu_EncGetOutputInfo(vpu_enc->handle, vpu_enc->outputInfo);
               
            } 
            if (vpu_ret != RETCODE_SUCCESS)
            {
                while (vpu_IsBusy()) {
                    vpu_WaitForInt(100);
                }
               // Now get the output
                vpu_ret = vpu_EncGetOutputInfo(vpu_enc->handle, vpu_enc->outputInfo);
            }

            if (vpu_ret != RETCODE_SUCCESS) {
                GST_ERROR(">>VPU_ENC: vpu_EncGetOutputInfo failed in chain. Error code is %d \n",vpu_ret);
                return GST_FLOW_ERROR;
            }

#ifdef GST_LOGTIME
            if (GST_ELEMENT (vpu_enc)->clock)
            {
                isIFrame = ((vpu_enc->outputInfo->picType&3)==0);
                vpu_enc->enc_stop = gst_clock_get_time (GST_ELEMENT (vpu_enc)->clock);
                vpu_enc->enc_stop -= GST_ELEMENT (vpu_enc)->base_time;
                if (isIFrame)
                   GST_LOGTIME(">>VPU_ENC: I frame encoding time is %d usec\n", (guint)(vpu_enc->enc_stop - vpu_enc->enc_start)/1000);
                else
                   GST_LOGTIME(">>VPU_ENC: Delta frame encoding time is %d usec\n", (guint)(vpu_enc->enc_stop - vpu_enc->enc_start)/1000);
            }
#endif

            vpu_enc->is_frame_started=FALSE;

            vpu_enc->num_total_frames++;
            vpu_enc->num_encoded_frames++;  

            // Calculate two bitrates - 
            // first is the bitrate per frame rate so kbits per second - add in over num frames or frame rate 
            // second is average over stream.
            
            vpu_enc->bits_per_second += vpu_enc->outputInfo->bitstreamSize;
            vpu_enc->frame_size[vpu_enc->fs_write] = vpu_enc->outputInfo->bitstreamSize;
            vpu_enc->fs_write = (vpu_enc->fs_write+1) & TIMESTAMP_INDEX_MASK;
            if (vpu_enc->num_encoded_frames > (guint)(vpu_enc->tgt_framerate+0.5))
            {   // subtract oldest frame size beyond scope of last num frames per sec
                vpu_enc->bits_per_second -= vpu_enc->frame_size[vpu_enc->fs_read];
                vpu_enc->fs_read = (vpu_enc->fs_read+1) & TIMESTAMP_INDEX_MASK;                              
                //GST_FRAMEDBG("\n>>VPU_ENC: bits_per sec %d read %d write %d \n", vpu_enc->bits_per_second, vpu_enc->fs_read, vpu_enc->fs_write);
            }
            avg_bitrate_per_sec = (vpu_enc->bits_per_second <<3)/1024; // bits needed so multiply by 8 multiple by 1K
            if (avg_bitrate_per_sec > 1.0)
                GST_FRAMEDBG("\n>>VPU_ENC: AVG BITRATE PER SEC is %f Target is %d \n", avg_bitrate_per_sec, vpu_enc->bitrate);

            vpu_enc->bits_per_stream += vpu_enc->outputInfo->bitstreamSize;
            avg_bitrate_per_stream = ((vpu_enc->bits_per_stream<<3) / vpu_enc->num_encoded_frames) * vpu_enc->tgt_framerate;
            avg_bitrate_per_stream = avg_bitrate_per_stream/ 1024;

            if (vpu_enc->outputInfo->picType>2)
                return GST_FLOW_OK;

            // decide when to send headers.   We used to only send them on the first frame
            // but found out that in networking mode they need to be sent on every I frame
            // and in general every 60 frames.
            isIFrame = ((vpu_enc->outputInfo->picType&3)==0);
            if (isIFrame) 
            {
               // if last gop had no dropped frames and qp was increased - drop it down
               if (!vpu_enc->fDropping_till_IFrame && 
                   (vpu_enc->qp != VPU_DEFAULT_QP) && 
                   (vpu_enc->encParam->quantParam != vpu_enc->qp))
               {
                   vpu_enc->encParam->quantParam = vpu_enc->qp;
               }
               vpu_enc->fDropping_till_IFrame = FALSE;
		       GST_FRAMEDBG (">>VPU_ENC IFRAME ==>>frame size %d ==>>stream AVG BIT RATE %f \n QP=%d MAX_QP=%d GAMMA=%d gop_size=%d\n", 
                             vpu_enc->outputInfo->bitstreamSize, avg_bitrate_per_stream, vpu_enc->qp, vpu_enc->max_qp, vpu_enc->gamma, vpu_enc->gopsize) ;
            } else {
               vpu_enc->frames_since_hdr_sent++;
		       GST_FRAMEDBG (">>VPU_ENC Delta frame ==>>frame size %d ==>>stream AVG BIT RATE  %f\n", 
                             vpu_enc->outputInfo->bitstreamSize, avg_bitrate_per_stream);
            }

#define MAX_HDR_INTERVAL 60
            //GST_DEBUG (" encoder output info pictype=%x cnt=%d\n", vpu_enc->outputInfo->picType, vpu_enc->frames_since_hdr_sent);

		    if ((vpu_enc->num_encoded_frames == 1) || isIFrame) // on first frame send headers down
                //(isIFrame && (vpu_enc->frames_since_hdr_sent >= MAX_HDR_INTERVAL)))
            {
                vpu_enc->frames_since_hdr_sent = 1;  // reset frames in gop
                if (vpu_enc->hdr_data)
                    offset = GST_BUFFER_SIZE(vpu_enc->hdr_data);
            }

	    	// Allocate a buffer - if first frame totalsize includes header size
        	retval = gst_pad_alloc_buffer_and_set_caps(vpu_enc->srcpad, 0,
            	        vpu_enc->outputInfo->bitstreamSize+offset,
                	    src_caps, &outbuffer);
    	    if (retval != GST_FLOW_OK) {
    	        GST_ERROR(">>VPU_ENC: Error %d in allocating the srcpad buffer",retval);
            	return retval;
        	}

            // For first frame copy over header data
        	if (offset)  // on first frame send headers down
            {
  	           memcpy(GST_BUFFER_DATA(outbuffer),
                          GST_BUFFER_DATA(vpu_enc->hdr_data),
                          offset);
            } else offset = 0;
 
           // Copy VPU encoded frame  - for first frame do it after headers (offset)
            memcpy(GST_BUFFER_DATA(outbuffer)+offset,vpu_enc->start_addr,
                        vpu_enc->outputInfo->bitstreamSize);

            // Set the buffer size of the output buffer plus headers if first frame
            GST_BUFFER_SIZE(outbuffer) = vpu_enc->outputInfo->bitstreamSize + offset;

            // if this is not an I frame mark it
            if (vpu_enc->outputInfo->picType){
                GST_BUFFER_FLAG_SET(outbuffer,GST_BUFFER_FLAG_DELTA_UNIT);
            }

            // send the output buffer downstream
            retval = mfw_gst_vpuenc_send_buffer(vpu_enc, outbuffer);

            vpu_enc->curr_addr = (guint8*)vpu_enc->vpuRegisteredFramesDesc[vpu_enc->numframebufs].virt_uaddr;
        }

	    // In the case of a frame started we must copy and start a frame before leaving
        // or if we have a buffer not completely consumed - must keep looping 
		if (vpu_enc->gst_buffer && (retval == GST_FLOW_OK))
		{
	        retval = mfw_gst_vpuenc_copy_sink_start_frame(vpu_enc);
		}
	} while (vpu_enc->gst_buffer && (retval == GST_FLOW_OK));

done:
    // if error happened be sure to release the input buffer
    if (vpu_enc->gst_buffer)
    {
        gst_buffer_unref(vpu_enc->gst_buffer);
    }
    return retval;
}

#if (defined(VPU_MX51) || defined(VPU_MX53))
/*=============================================================================
FUNCTION:           CalcMJPEGQuantTables

DESCRIPTION:        recalculate the mjpeg quant table for different bitrate encoding.

ARGUMENTS PASSED:   pointers to huffman and quantization tables

RETURN VALUE:       0 (SUCCESS)/ -1 (FAILURE)
PRE-CONDITIONS:     None
POST-CONDITIONS:    None
IMPORTANT NOTES:    None
=============================================================================*/

static void mfw_gst_CalcMJPEGQuantTables(MfwGstVPU_Enc *vpu_enc)
{

    gint   i;
    guint  temp, new_quality;
    guint  m_iFrameHeight, m_iFrameWidth, m_iBitRate, quality, compress_ratio;
	gfloat m_iFrameRate;


    quality = 93;
	compress_ratio = 3;
	new_quality = 0;
	m_iFrameHeight = vpu_enc->height;
	m_iFrameWidth = vpu_enc->width;
	m_iBitRate = vpu_enc->bitrate;
	m_iFrameRate = vpu_enc->tgt_framerate;
    // found all vailable information
    if ( ( 0 >= m_iBitRate) ||
         ( 0 >= m_iFrameRate ) ||
         ( 0 >= m_iFrameWidth ) ||
         ( 0 >= m_iFrameHeight) )
    {
        // use the defaul table, does not do anything.
        return;
    }

    // compression ratio formula 
    //          W * H * fps * 12
    // ratio = -----------------------
    //              bps
    compress_ratio = ((long)m_iFrameHeight * m_iFrameWidth * m_iFrameRate * 12 ) / ((long)m_iBitRate * 1024) ;
    GST_DEBUG("encode frame rate m_iFrameRate = %d %d\n", m_iFrameRate, m_iBitRate);
    GST_DEBUG("compress ratio compress_ratio = %d %d\n", compress_ratio, m_iBitRate);

    // look up curve -- compress_ratio VS Quality to get a reasonable value of Quality

    if ( CompressRatioTable[0] <= compress_ratio)
    {
        quality = QualityLookupTable[0];
    }
    else if (CompressRatioTable[LOOKUP_TAB_MAX-1] >= compress_ratio)
    {
        quality = QualityLookupTable[LOOKUP_TAB_MAX-1];
    }
    else
    {
        gint k;
        for (k = 0; k < LOOKUP_TAB_MAX-2; k++)
        {
            if ((CompressRatioTable[k]>compress_ratio) && (CompressRatioTable[k+1]<=compress_ratio))
            {
                // compress_ratio is in this area
                temp = (QualityLookupTable[k+1] - QualityLookupTable[k]) * 
                    (compress_ratio - CompressRatioTable[k+1]) / 
                    (CompressRatioTable[k] - CompressRatioTable[k+1]);
                quality = QualityLookupTable[k+1] - temp; 
            }
        }
    }

    // re-calculate the Q-matrix

    if (50 > quality)
        new_quality = 5000 / quality;
    else
        new_quality = 200 - 2 * quality; 

    GST_DEBUG("quality = %d %d", quality, new_quality);
    /* FIXME: The low quality value will cause MJPEG cannot be decoded */
    if (new_quality < 10)
        return;
	
    // recalculate  luma Quantification table
    for (i = 0; i< 64; i++)
    {
        temp = ((unsigned int)lumaQ2[i] * new_quality + 50) /100;
        if (temp <= 0) temp = 1;
        if (temp > 255) temp = 255;

        lumaQ2[i] = (unsigned char)temp;
    }
    GST_DEBUG("Luma Quant Table is \n");
    for(i = 0; i< 64; i = i+8)
    {
        GST_DEBUG("0x%2x, 0x%2x, 0x%2x, 0x%2x, 0x%2x, 0x%2x, 0x%2x, 0x%2x, \n", 
            lumaQ2[i], lumaQ2[i+1], lumaQ2[i+2], lumaQ2[i+3],
            lumaQ2[i+4], lumaQ2[i+5], lumaQ2[i+6], lumaQ2[i+7]);
    }

    // chromaB Quantification Table
    for (i = 0; i< 64; i++)
    {
        temp = ((unsigned int)chromaBQ2[i] * new_quality + 50) /100;
        if (temp <= 0) temp = 1;
        if (temp > 255) temp = 255;

        chromaBQ2[i] = (unsigned char)temp;
    }
    GST_DEBUG("chromaB Quantification Table is \n");
    for(i = 0; i< 64; i = i+8)
    {
        GST_DEBUG("0x%2x, 0x%2x, 0x%2x, 0x%2x, 0x%2x, 0x%2x, 0x%2x, 0x%2x, \n", 
            chromaBQ2[i], chromaBQ2[i+1], chromaBQ2[i+2], chromaBQ2[i+3],
            chromaBQ2[i+4], chromaBQ2[i+5], chromaBQ2[i+6], chromaBQ2[i+7]);
    }

    // chromaR Quantification Table
    for (i = 0; i< 64; i++)
    {
        temp = ((unsigned int)chromaRQ2[i] * new_quality + 50) /100;
        if (temp <= 0) temp = 1;
        if (temp > 255) temp = 255;

        chromaRQ2[i] = (unsigned char)temp;
    }
    GST_DEBUG("chromaR Quantification Table is \n");
    for(i = 0; i< 64; i = i+8)
    {
        GST_DEBUG("0x%2x, 0x%2x, 0x%2x, 0x%2x, 0x%2x, 0x%2x, 0x%2x, 0x%2x, \n", 
            chromaRQ2[i], chromaRQ2[i+1], chromaRQ2[i+2], chromaRQ2[i+3],
            chromaRQ2[i+4], chromaRQ2[i+5], chromaRQ2[i+6], chromaRQ2[i+7]);
    }

    return;
}


/*=============================================================================
FUNCTION:           mfw_gst_vpudec_generate_jpeg_tables

DESCRIPTION:        Generate JPEG tables for MJPEG support

ARGUMENTS PASSED:   pointers to huffman and quantization tables

RETURN VALUE:       0 (SUCCESS)/ -1 (FAILURE)
PRE-CONDITIONS:     None
POST-CONDITIONS:    None
IMPORTANT NOTES:    None
=============================================================================*/
gboolean mfw_gst_vpudec_generate_jpeg_tables(MfwGstVPU_Enc *vpu_enc, Uint8 * * pphuftable, Uint8 * * ppqmattable)
{
    Uint8 * qMatTable;
    Uint8 * huffTable;
    int i;

    huffTable = g_malloc(VPU_HUFTABLE_SIZE);
    qMatTable = g_malloc(VPU_QMATTABLE_SIZE);

    if ((!huffTable)||(!qMatTable))
        goto Err;

    memset(huffTable, 0, VPU_HUFTABLE_SIZE);
    memset(qMatTable, 0, VPU_QMATTABLE_SIZE);

    for(i = 0; i < 16; i += 4)
	{
		huffTable[i] = lumaDcBits[i + 3];
		huffTable[i + 1] = lumaDcBits[i + 2];
		huffTable[i + 2] = lumaDcBits[i + 1];
		huffTable[i + 3] = lumaDcBits[i];
	}
	for(i = 16; i < 32 ; i += 4)
	{
		huffTable[i] = lumaDcValue[i + 3 - 16];
		huffTable[i + 1] = lumaDcValue[i + 2 - 16];
		huffTable[i + 2] = lumaDcValue[i + 1 - 16];
		huffTable[i + 3] = lumaDcValue[i - 16];
	}
	for(i = 32; i < 48; i += 4)
	{
		huffTable[i] = lumaAcBits[i + 3 - 32];
		huffTable[i + 1] = lumaAcBits[i + 2 - 32];
		huffTable[i + 2] = lumaAcBits[i + 1 - 32];
		huffTable[i + 3] = lumaAcBits[i - 32];
	}
	for(i = 48; i < 216; i += 4)
	{
		huffTable[i] = lumaAcValue[i + 3 - 48];
		huffTable[i + 1] = lumaAcValue[i + 2 - 48];
		huffTable[i + 2] = lumaAcValue[i + 1 - 48];
		huffTable[i + 3] = lumaAcValue[i - 48];
	}
	for(i = 216; i < 232; i += 4)
	{
		huffTable[i] = chromaDcBits[i + 3 - 216];
		huffTable[i + 1] = chromaDcBits[i + 2 - 216];
		huffTable[i + 2] = chromaDcBits[i + 1 - 216];
		huffTable[i + 3] = chromaDcBits[i - 216];
	}
	for(i = 232; i < 248; i += 4)
	{
		huffTable[i] = chromaDcValue[i + 3 - 232];
		huffTable[i + 1] = chromaDcValue[i + 2 - 232];
		huffTable[i + 2] = chromaDcValue[i + 1 - 232];
		huffTable[i + 3] = chromaDcValue[i - 232];
	}
	for(i = 248; i < 264; i += 4)
	{
		huffTable[i] = chromaAcBits[i + 3 - 248];
		huffTable[i + 1] = chromaAcBits[i + 2 - 248];
		huffTable[i + 2] = chromaAcBits[i + 1 - 248];
		huffTable[i + 3] = chromaAcBits[i - 248];
	}
	for(i = 264; i < 432; i += 4)
	{
		huffTable[i] = chromaAcValue[i + 3 - 264];
		huffTable[i + 1] = chromaAcValue[i + 2 - 264];
		huffTable[i + 2] = chromaAcValue[i + 1 - 264];
		huffTable[i + 3] = chromaAcValue[i - 264];
	}


    /* accoring to the bitrate, recalculate the quant table */
	mfw_gst_CalcMJPEGQuantTables(vpu_enc);
	

	/* Rearrange and insert pre-defined Q-matrix to deticated variable. */
	for(i = 0; i < 64; i += 4)
	{
		qMatTable[i] = lumaQ2[i + 3];
		qMatTable[i + 1] = lumaQ2[i + 2];
		qMatTable[i + 2] = lumaQ2[i + 1];
		qMatTable[i + 3] = lumaQ2[i];
	}
	for(i = 64; i < 128; i += 4)
	{
		qMatTable[i] = chromaBQ2[i + 3 - 64];
		qMatTable[i + 1] = chromaBQ2[i + 2 - 64];
		qMatTable[i + 2] = chromaBQ2[i + 1 - 64];
		qMatTable[i + 3] = chromaBQ2[i - 64];
	}
	for(i = 128; i < 192; i += 4)
	{
		qMatTable[i] = chromaRQ2[i + 3 - 128];
		qMatTable[i + 1] = chromaRQ2[i + 2 - 128];
		qMatTable[i + 2] = chromaRQ2[i + 1 - 128];
		qMatTable[i + 3] = chromaRQ2[i - 128];
	}

    *pphuftable = huffTable;
    *ppqmattable = qMatTable;
    return TRUE;

Err:
    if (huffTable)
        g_free(huffTable);
    if (qMatTable)
        g_free(qMatTable);
    return FALSE;
}
#endif

/*======================================================================================
FUNCTION:           mfw_gst_vpuenc_change_state

DESCRIPTION:        This function keeps track of different states of pipeline.

ARGUMENTS PASSED:
                element     -   pointer to element
                transition  -   state of the pipeline

RETURN VALUE:
                GST_STATE_CHANGE_FAILURE    - the state change failed
                GST_STATE_CHANGE_SUCCESS    - the state change succeeded
                GST_STATE_CHANGE_ASYNC      - the state change will happen
                                                asynchronously
                GST_STATE_CHANGE_NO_PREROLL - the state change cannot be prerolled

PRE-CONDITIONS:     None
POST-CONDITIONS:    None
IMPORTANT NOTES:    None
=======================================================================================*/
static GstStateChangeReturn mfw_gst_vpuenc_change_state(GstElement *element, 
                                                        GstStateChange transition)
{
    GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
    MfwGstVPU_Enc *vpu_enc   = MFW_GST_VPU_ENC(element);
    gint vpu_ret=0;
    gfloat time_val = 0.0;

    switch (transition) 
    {
        case GST_STATE_CHANGE_NULL_TO_READY:
	    {
            vpu_versioninfo ver;

            GST_DEBUG("\n>>VPU_ENC: VPU State: Null to Ready\n");
            vpu_ret = vpu_Init(NULL);
            if(vpu_ret < 0){
                GST_DEBUG(">>VPU_ENC: Error %d in initializing the VPU \n",vpu_ret);
                return GST_STATE_CHANGE_FAILURE;
            }

            vpu_ret = vpu_GetVersionInfo(&ver);
            if (vpu_ret) {
                GST_DEBUG(">>VPU_ENC: Error %d in geting the VPU version ",vpu_ret);
                vpu_UnInit();
                return GST_STATE_CHANGE_FAILURE;
            }


            g_print(YELLOW_STR("VPU Version: firmware %d.%d.%d; libvpu: %d.%d.%d \n", 
                           ver.fw_major, ver.fw_minor, ver.fw_release,
                           ver.lib_major, ver.lib_minor, ver.lib_release));


            #define MFW_GST_VPU_ENCODER_PLUGIN VERSION
            PRINT_PLUGIN_VERSION(MFW_GST_VPU_ENCODER_PLUGIN); 

            break;
	    }
        case GST_STATE_CHANGE_READY_TO_PAUSED:
	    {
            guint8 *virt_bit_stream_buf=NULL;
            CodStd mode;

            GST_DEBUG("\n>>VPU_ENC: VPU State: Ready to Paused vpu_enc=0x%x\n\n",vpu_enc);

            vpu_enc->encOP          = g_malloc(sizeof(EncOpenParam));
            if(vpu_enc->encOP == NULL){
                GST_DEBUG(">>VPU_ENC: Error in allocating encoder"
                    "open parameter structure \n");
                mfw_gst_vpuenc_cleanup(vpu_enc);
                return GST_STATE_CHANGE_FAILURE;
            }
            
            vpu_enc->initialInfo    = g_malloc(sizeof(EncInitialInfo));
            if(vpu_enc->initialInfo == NULL){
                GST_DEBUG(">>VPU_ENC: Error in allocating encoder"
                    "initial info structure \n");
                mfw_gst_vpuenc_cleanup(vpu_enc);
                return GST_STATE_CHANGE_FAILURE;
            }

            vpu_enc->encParam       = g_malloc(sizeof(EncParam));
            if(vpu_enc->encParam == NULL){
                GST_DEBUG(">>VPU_ENC: Error in allocating encoder"
                    "parameter structure \n");
                mfw_gst_vpuenc_cleanup(vpu_enc);
                return GST_STATE_CHANGE_FAILURE;
            }

            vpu_enc->outputInfo     = g_malloc(sizeof(EncOutputInfo));
            if(vpu_enc->outputInfo == NULL){
                GST_DEBUG(">>VPU_ENC: Error in allocating encoder"
                    "output structure \n");
                mfw_gst_vpuenc_cleanup(vpu_enc);
                return GST_STATE_CHANGE_FAILURE;
            }
            
            memset(vpu_enc->initialInfo  ,0,sizeof(EncInitialInfo));
            memset(vpu_enc->encParam ,0,sizeof(EncParam));
            memset(vpu_enc->encOP,0,sizeof(EncOpenParam));
            memset(vpu_enc->outputInfo,0,sizeof(EncOutputInfo));
            memset(&vpu_enc->bit_stream_buf, 0, sizeof(vpu_mem_desc));
            
            vpu_enc->bit_stream_buf.size = BUFF_FILL_SIZE;
            IOGetPhyMem(&vpu_enc->bit_stream_buf);
            virt_bit_stream_buf = (guint8 *)IOGetVirtMem(&vpu_enc->bit_stream_buf);
            vpu_enc->start_addr = virt_bit_stream_buf;
            vpu_enc->curr_addr = NULL;
            vpu_enc->encOP->bitstreamBuffer = vpu_enc->bit_stream_buf.phy_addr;
            vpu_enc->encOP->bitstreamBufferSize = BUFF_FILL_SIZE ;
 
            GST_DEBUG(">>VPU_ENC: codec=%d\n",vpu_enc->codec);
            mode = vpu_enc->encOP->bitstreamFormat = vpu_enc->codec;

            vpu_enc->vpu_init=FALSE;
            vpu_enc->is_frame_started=FALSE;
            vpu_enc->num_total_frames = 0;
            vpu_enc->num_encoded_frames = 0;
            vpu_enc->frames_since_hdr_sent = 1;
            vpu_enc->fs_read = vpu_enc->fs_write = 0;
            vpu_enc->bits_per_second = 0;
            vpu_enc->bits_per_stream = 0;

            vpu_enc->handle=0;
            vpu_enc->encOP->bitRate = vpu_enc->bitrate;
            vpu_enc->encOP->initialDelay = 0;
            vpu_enc->encOP->vbvBufferSize = 0;	/* 0 = ignore 8 */
            vpu_enc->encParam->enableAutoSkip = 1;
            vpu_enc->encOP->gopSize = vpu_enc->gopsize;	/* only first picture is I */
            vpu_enc->encOP->slicemode.sliceMode = 0;	/* 1 slice per picture */
            vpu_enc->encOP->slicemode.sliceSizeMode = 0;
            vpu_enc->encOP->slicemode.sliceSize = 4000;	/* not used if sliceMode is 0 */
            vpu_enc->encOP->rcIntraQp = vpu_enc->qp;

            if (vpu_enc->bitrate != 0)
            {
#ifdef VPU_MX27
                if (vpu_enc->max_qp != VPU_DEFAULT_QP) 
                    vpu_enc->encOP->maxQp = vpu_enc->max_qp;
                vpu_enc->encOP->Gamma = vpu_enc->gamma;
#else
                /* Disable the userQpMax and userGamma to avoid mosaic issue */
#if 0
                if (vpu_enc->max_qp != VPU_DEFAULT_QP) 
                    vpu_enc->encOP->userQpMax = vpu_enc->max_qp;
                vpu_enc->encOP->userGamma = vpu_enc->gamma;
#endif                
#endif        
            }

            
            if (mode == STD_MPEG4) {
                vpu_enc->encOP->EncStdParam.mp4Param.mp4_dataPartitionEnable = 0;
                vpu_enc->encOP->EncStdParam.mp4Param.mp4_reversibleVlcEnable = 0;
                vpu_enc->encOP->EncStdParam.mp4Param.mp4_intraDcVlcThr = 0;
                vpu_enc->encOP->EncStdParam.mp4Param.mp4_hecEnable = 0; 
                vpu_enc->encOP->EncStdParam.mp4Param.mp4_verid = 2; 
                if (vpu_enc->bitrate == 0)
                {
                    if (vpu_enc->encOP->rcIntraQp == VPU_DEFAULT_QP)                
                        vpu_enc->encOP->rcIntraQp = VPU_DEFAULT_MPEG4_QP;                                
                    else if (vpu_enc->encOP->rcIntraQp > VPU_MAX_MPEG4_QP)
                        vpu_enc->encOP->rcIntraQp = VPU_MAX_MPEG4_QP;
                }
                else {
                    /* Set with the default MPEG4 QP parameter */
                    vpu_enc->encOP->rcIntraQp = VPU_MAX_MPEG4_QP;
                }
            } else if (mode == STD_H263) {
                vpu_enc->encOP->EncStdParam.h263Param.h263_annexKEnable = 0;
                vpu_enc->encOP->EncStdParam.h263Param.h263_annexTEnable = 0;
                if (vpu_enc->h263profile0 == TRUE)
                {
                    vpu_enc->encOP->EncStdParam.h263Param.h263_annexJEnable = 0;
                } else {
                    vpu_enc->encOP->EncStdParam.h263Param.h263_annexJEnable = 1;
                }
                if (vpu_enc->bitrate == 0)
                {
                    if (vpu_enc->encOP->rcIntraQp == VPU_DEFAULT_QP)
                        vpu_enc->encOP->rcIntraQp = VPU_DEFAULT_MPEG4_QP;
                    else if (vpu_enc->encOP->rcIntraQp > VPU_MAX_MPEG4_QP)
                        vpu_enc->encOP->rcIntraQp = VPU_MAX_MPEG4_QP;
                }
                else {
                    /* Set with the default MPEG4 QP parameter */
                    vpu_enc->encOP->rcIntraQp = VPU_MAX_MPEG4_QP;
                }
            } else if (mode == STD_AVC){
                vpu_enc->encOP->EncStdParam.avcParam.avc_constrainedIntraPredFlag = 0;
                vpu_enc->encOP->EncStdParam.avcParam.avc_disableDeblk = 0;
                vpu_enc->encOP->EncStdParam.avcParam.avc_deblkFilterOffsetAlpha = 0;
                vpu_enc->encOP->EncStdParam.avcParam.avc_deblkFilterOffsetBeta = 0;
                vpu_enc->encOP->EncStdParam.avcParam.avc_chromaQpOffset = 0;
                vpu_enc->encOP->EncStdParam.avcParam.avc_audEnable = 0;
                vpu_enc->encOP->EncStdParam.avcParam.avc_fmoEnable = 0;
                vpu_enc->encOP->EncStdParam.avcParam.avc_fmoType = 0;
                vpu_enc->encOP->EncStdParam.avcParam.avc_fmoSliceNum = 1;
                if (vpu_enc->bitrate == 0)
                {
                    if (vpu_enc->encOP->rcIntraQp == VPU_DEFAULT_QP)
                        vpu_enc->encOP->rcIntraQp = VPU_DEFAULT_H264_QP;
                    else if (vpu_enc->encOP->rcIntraQp > VPU_MAX_H264_QP)
                        vpu_enc->encOP->rcIntraQp = VPU_MAX_H264_QP;
                }
                else {
                    /* Set with the default H264 QP parameter */
                    vpu_enc->encOP->rcIntraQp = VPU_DEFAULT_H264_QP;
                }
#if (defined(VPU_MX51) || defined(VPU_MX53))
                vpu_enc->encOP->EncStdParam.avcParam.avc_fmoSliceSaveBufSize=32;
            } else if (mode == STD_MJPG){
                vpu_enc->encOP->EncStdParam.mjpgParam.mjpg_hufTable = 
                vpu_enc->encOP->EncStdParam.mjpgParam.mjpg_qMatTable = NULL;
                if (mfw_gst_vpudec_generate_jpeg_tables(vpu_enc, (&(vpu_enc->encOP->EncStdParam.mjpgParam.mjpg_hufTable)), 
                    (&(vpu_enc->encOP->EncStdParam.mjpgParam.mjpg_qMatTable)))){
                    vpu_enc->encOP->EncStdParam.mjpgParam.mjpg_sourceFormat = 0; /* encConfig.mjpgChromaFormat */
                    vpu_enc->encOP->EncStdParam.mjpgParam.mjpg_restartInterval = 60;
                    vpu_enc->encOP->EncStdParam.mjpgParam.mjpg_thumbNailEnable = 0;
                    vpu_enc->encOP->EncStdParam.mjpgParam.mjpg_thumbNailWidth = 0;
                    vpu_enc->encOP->EncStdParam.mjpgParam.mjpg_thumbNailHeight = 0;
                }else{
                    return GST_STATE_CHANGE_FAILURE;
                }
#endif
            } else {
                GST_ERROR(">>VPU_ENC: Invalid codec standard mode \n");
                mfw_gst_vpuenc_cleanup(vpu_enc);
                return GST_STATE_CHANGE_FAILURE;
            }
            break;
	    }
        case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
        {
            GST_DEBUG("\n>>VPU_ENC: VPU State: Paused to Playing\n");
	        break;
        }
        default:
	        break;
    }

    ret = vpu_enc->parent_class->change_state(element, transition);
    GST_DEBUG("\n>>VPU_ENC: State Change for VPU returned %d\n", ret);

    switch (transition) 
    {
        case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
        {
            GST_DEBUG("\n>>VPU_ENC: VPU State: Playing to Paused\n");
            break;
        }
        case GST_STATE_CHANGE_PAUSED_TO_READY:
        {
            GST_DEBUG("\n>>VPU_ENC: VPU State: Paused to Ready\n");
            mfw_gst_vpuenc_cleanup(vpu_enc);
            break;
        }   
        case GST_STATE_CHANGE_READY_TO_NULL:
        {
            GST_DEBUG("\n>>VPU_ENC: VPU State: Ready to Null\n");
            vpu_UnInit();
            GST_DEBUG("\n>>VPU_ENC: after vpu_uninit\n");
            break;
        }
        default:
            break;
    }

    return ret;

}

/*==================================================================================================
FUNCTION:           mfw_gst_vpuenc_sink_event

DESCRIPTION:        Send an event to sink  pad of downstream element

ARGUMENTS PASSED:
        pad        -    pointer to pad
        event      -    pointer to event

RETURN VALUE:
        TRUE       -    event is handled properly
        FALSE      -	event is not handled properly

PRE-CONDITIONS:     None
POST-CONDITIONS:    None
IMPORTANT NOTES:    None
==================================================================================================*/
static gboolean mfw_gst_vpuenc_sink_event(GstPad * pad, GstEvent * event)
{
    MfwGstVPU_Enc *vpu_enc  = NULL;
    gboolean    ret=TRUE;
    vpu_enc  = MFW_GST_VPU_ENC(GST_PAD_PARENT(pad));
    
    switch (GST_EVENT_TYPE(event)) {
    case GST_EVENT_NEWSEGMENT:
        {
            GstFormat format;
            gint64 start, stop, position;
            gdouble rate;
            
            gst_event_parse_new_segment(event, NULL, &rate, &format,
                &start, &stop, &position);
            
            if (format == GST_FORMAT_BYTES) {
                ret = gst_pad_push_event(vpu_enc->srcpad,
                    gst_event_new_new_segment(FALSE, 
                    1.0, GST_FORMAT_TIME,0,GST_CLOCK_TIME_NONE,
                    0));
            }        
            break;
        }

    case GST_EVENT_EOS:
        {
            GST_DEBUG(">>VPU_ENC: EOS Total Number of frames = %d encoded frames=%d\n", 
                     vpu_enc->num_total_frames, vpu_enc->num_encoded_frames);
            ret = gst_pad_push_event(vpu_enc->srcpad, event);
            
            if (TRUE != ret) {
                GST_ERROR("\n>>VPU_ENC:  Error in pushing the event,result	is %d\n",
                    ret);
                gst_event_unref(event);
            }
            break;
        }
    default:
        {
            ret = gst_pad_event_default(pad, event);
            break;
        }
    }
    return ret;
}

/*======================================================================================
FUNCTION:           mfw_gst_vpuenc_setcaps

DESCRIPTION:        This function negoatiates the caps set on the sink pad

ARGUMENTS PASSED:
                pad   -   pointer to the sinkpad of this element
                caps  -   pointer to the caps set

RETURN VALUE:
               TRUE   negotiation success full
               FALSE  negotiation Failed

PRE-CONDITIONS:     None
POST-CONDITIONS:    None
IMPORTANT NOTES:    None
=======================================================================================*/
static gboolean mfw_gst_vpuenc_setcaps(GstPad * pad, GstCaps *caps)
{
    MfwGstVPU_Enc *vpu_enc  = NULL;
    gint width=0;
    gint height=0;

    vpu_enc = MFW_GST_VPU_ENC(gst_pad_get_parent(pad));
    GstStructure *structure = gst_caps_get_structure(caps, 0);
    gst_structure_get_int(structure, "width", &width);
    if (width != 0)
    {        
        vpu_enc->width = width;
        vpu_enc->widthProvided = TRUE;
    }
    gst_structure_get_int(structure, "height", &height);
    if(height!=0)
    {
        vpu_enc->height = height;
        vpu_enc->heightProvided = TRUE;
    }

    gst_structure_get_fourcc(structure, "format", &vpu_enc->format);

    GST_DEBUG("\n>>VPU_ENC: Input Height is %d\n", vpu_enc->height);
    gst_structure_get_fraction(structure, "framerate", &vpu_enc->framerate_n,&vpu_enc->framerate_d);

    if((vpu_enc->framerate_n != 0) && (vpu_enc->framerate_d != 0))
    {
        vpu_enc->src_framerate = (gfloat)vpu_enc->framerate_n/vpu_enc->framerate_d;        
    } 
    GST_DEBUG(">>VPU_ENC: src framerate=%f\n",vpu_enc->src_framerate);
    gst_object_unref(vpu_enc);
    return gst_pad_set_caps(pad, caps);
}

/*=======================================================================================
FUNCTION:           mfw_gst_vpuenc_base_init

DESCRIPTION:        Element details are registered with the plugin during
                    _base_init ,This function will initialise the class and child
                    class properties during each new child class creation

ARGUMENTS PASSED:   klass - void pointer

RETURN VALUE:       None
PRE-CONDITIONS:     None
POST-CONDITIONS:    None
IMPORTANT NOTES:    None
=======================================================================================*/
static void mfw_gst_vpuenc_base_init(MfwGstVPU_EncClass *klass)
{

    GstElementClass *element_class = GST_ELEMENT_CLASS(klass);

    gst_element_class_add_pad_template(element_class,
				       gst_static_pad_template_get
				       (&mfw_gst_vpuenc_src_factory));

    gst_element_class_add_pad_template(element_class,
				       gst_static_pad_template_get
				       (&mfw_gst_vpuenc_sink_factory));

    gst_element_class_set_details(element_class, &mfw_gst_vpuenc_details);

}

/*======================================================================================
FUNCTION:           mfw_gst_vpuenc_codec_get_type

DESCRIPTION:        Gets an enumeration for the different 
                    codec standards supported by the encoder

ARGUMENTS PASSED:   None

RETURN VALUE:       Enumerated type of the codec standards supported by the encoder

PRE-CONDITIONS:     None
POST-CONDITIONS:    None
IMPORTANT NOTES:    None
========================================================================================*/
static GType mfw_gst_vpuenc_codec_get_type(void)
{
  static GType vpuenc_codec_type = 0;
  static GEnumValue vpuenc_codecs[] = {
        {STD_MPEG4, STR(STD_MPEG4), "std_mpeg4"},
        {STD_H263, STR(STD_H263), "std_h263"},
        {STD_AVC, STR(STD_AVC), "std_avc"},
#if (defined(VPU_MX51) || defined(VPU_MX53))
        {STD_MJPG, STR(STD_MJPG), "std_mjpg"},
#endif
        {0, NULL, NULL},
      };
   if (!vpuenc_codec_type) {
        vpuenc_codec_type = g_enum_register_static ("MfwGstVpuEncCodecs", vpuenc_codecs);
   }


   return vpuenc_codec_type;
}


/*======================================================================================
FUNCTION:           mfw_gst_vpuenc_class_init

DESCRIPTION:        Initialise the class.(specifying what signals,
                    arguments and virtual functions the class has and setting up
                    global states)

ARGUMENTS PASSED:   klass - pointer to H.264Encoder element class

RETURN VALUE:       None
PRE-CONDITIONS:     None
POST-CONDITIONS:    None
IMPORTANT NOTES:    None
=======================================================================================*/
static void mfw_gst_vpuenc_class_init(MfwGstVPU_EncClass *klass)
{
    GObjectClass *gobject_class         = NULL;
    GstElementClass *gstelement_class   = NULL;
	
    gobject_class                       = (GObjectClass *) klass;
    gstelement_class                    = (GstElementClass *) klass;
    gstelement_class->change_state      = mfw_gst_vpuenc_change_state;
    gobject_class->set_property = mfw_gst_vpuenc_set_property;
    gobject_class->get_property = mfw_gst_vpuenc_get_property;
    gobject_class->finalize     = mfw_gst_vpuenc_vpu_finalize;
    gobject_class->dispose      = mfw_gst_vpuenc_vpu_finalize;

    
    g_object_class_install_property(gobject_class, MFW_GST_VPU_PROF_ENABLE,
				    g_param_spec_boolean("profile", "Profile", 
                    "enable time profile of the vpu encoder plug-in",
                    FALSE, G_PARAM_READWRITE));

    g_object_class_install_property(gobject_class, MFW_GST_VPU_CODEC_TYPE,
				    g_param_spec_enum("codec-type", "codec_type", 
                    "selects the codec type for encoding",
                    mfw_gst_vpuenc_codec_get_type(), STD_AVC, G_PARAM_READWRITE));

    g_object_class_install_property (gobject_class, MFW_GST_VPUENC_WIDTH,
        g_param_spec_uint("width", "Width", 
        "gets the width of the input frame to be encoded",
        0, MAX_WIDTH, 0, G_PARAM_READWRITE));

    g_object_class_install_property (gobject_class, MFW_GST_VPUENC_HEIGHT,
        g_param_spec_uint("height", "Height", 
        "gets the Height of the input frame to be encoded",
        0, MAX_HEIGHT, 0, G_PARAM_READWRITE));

    g_object_class_install_property (gobject_class, MFW_GST_VPUENC_FRAME_RATE,
        g_param_spec_float("framerate", "FrameRate", 
        "gets the framerate at which the input stream is to be encoded",
        0,60.0, 30.0, G_PARAM_READWRITE));


    g_object_class_install_property (gobject_class, MFW_GST_VPUENC_BITRATE,
        g_param_spec_uint("bitrate", "Bitrate", 
        "gets the bitrate (in kbps) at which stream is to be encoded",
        0,G_MAXINT, 0, G_PARAM_READWRITE));

    g_object_class_install_property (gobject_class, MFW_GST_VPUENC_QP,
        g_param_spec_uint("qp", "QP", 
        "gets the quantization parameter - range is 0-51",
        0,51, 0, G_PARAM_READWRITE));

    g_object_class_install_property (gobject_class, MFW_GST_VPUENC_MAX_QP,
        g_param_spec_uint("max_qp", "MAX_QP", 
        "Maximum quantization parameter for CBR - range is 0-51 for H264 and 1-31 for MPEG4 - lower value brings better video quality but higher frame sizes",
        0,51, 0, G_PARAM_READWRITE));

    g_object_class_install_property (gobject_class, MFW_GST_VPUENC_GAMMA,
        g_param_spec_uint("gamma", "GAMMA", 
        "gamma value for CBR - range is 1-32768 default is 0 - this tells VPU the speed on changing qp - lower will cause better video quality",
        0,32768, 0, G_PARAM_READWRITE));

    g_object_class_install_property (gobject_class, MFW_GST_VPUENC_GOP,
        g_param_spec_uint("gopsize", "Gopsize", 
        "gets the GOP size at which stream is to be encoded",
        0,G_MAXINT, 0, G_PARAM_READWRITE));

    g_object_class_install_property (gobject_class, MFW_GST_VPUENC_INTRAREFRESH,
        g_param_spec_uint("intrarefresh", "intraRefresh", 
        "0 - Intra MB refresh is not used. Otherwise - At least N MB's in every P-frame will be encoded as intra MB's.",
        0,1350, 0, G_PARAM_READWRITE));  // max is D1 / 16 /16 to get max MBs - should be lower if resolution is lower

    g_object_class_install_property (gobject_class, MFW_GST_VPUENC_FORCEIINTERVAL,
        g_param_spec_uint("iinterval", "IInterval", 
        "I frame force interval",
        0,G_MAXINT, 10, G_PARAM_READWRITE));

    g_object_class_install_property(gobject_class, MFW_GST_VPUENC_H263PROFILE0,
				    g_param_spec_boolean("h263profile0", "H263 Profile0", 
                    "enable encoding of H.263 profile 0 when codec-type is set to std_h263",
                    FALSE, G_PARAM_READWRITE));

    g_object_class_install_property(gobject_class, MFW_GST_VPUENC_LOOPBACK,
				    g_param_spec_boolean("loopback", "Loopback", 
                    "disables parallelization for performance - turn off if pipeline with decoder",
                    TRUE, G_PARAM_READWRITE));

    g_object_class_install_property (gobject_class, MFW_GST_VPUENC_INTRA_QP,
        g_param_spec_uint("intra_qp", "INTRA_QP", 
        "QP to use for all I frames - range is 0-51 for H264 and 1-31 for MPEG4 - lower value brings better video quality but higher frame sizes",
        0,51, 0, G_PARAM_READWRITE));
    
    g_object_class_install_property (gobject_class, MFW_GST_VPUENC_CROP_LEFT,
        g_param_spec_uint("crop-left", "crop-left", 
        "The left crop value of input frame to be encoded",
        0, MAX_WIDTH, 0, G_PARAM_READWRITE));

    g_object_class_install_property (gobject_class, MFW_GST_VPUENC_CROP_TOP,
        g_param_spec_uint("crop-top", "crop-top", 
        "The top crop value of input frame to be encoded",
        0, MAX_HEIGHT, 0, G_PARAM_READWRITE));

    g_object_class_install_property (gobject_class, MFW_GST_VPUENC_CROP_RIGHT,
        g_param_spec_uint("crop-right", "crop-right", 
        "The right crop value of input frame to be encoded",
        0, MAX_WIDTH, 0, G_PARAM_READWRITE));

    g_object_class_install_property (gobject_class, MFW_GST_VPUENC_CROP_BOTTOM,
        g_param_spec_uint("crop-bottom", "crop-bottom", 
        "The bottom crop value of input frame to be encoded",
        0, MAX_HEIGHT, 0, G_PARAM_READWRITE));


}
void  vpuenc_free_gst_buf_meta(GstBuffer *buf)
{
    gint index = G_N_ELEMENTS(buf->_gst_reserved)-1;
    GST_DEBUG("vpu free buffer\n");
    gst_buffer_meta_free(buf->_gst_reserved[index]);
    mfw_free_hw_buffer(((GstBufferMeta *)(buf->_gst_reserved[index]))->priv);
    buf = NULL;
}

/*=============================================================================
FUNCTION:           mfw_gst_vpuenc_buffer_alloc   
        
DESCRIPTION:        This function initailise the sl driver
                    and gets the new buffer for display             

ARGUMENTS PASSED:  
          bsink :   pointer to GstBaseSink
		  buf   :   pointer to new GstBuffer
		  size  :   size of the new buffer
          offset:   buffer offset
		  caps  :   pad capability
        
RETURN VALUE:       GST_FLOW_OK/GST_FLOW_ERROR
      
PRE-CONDITIONS:     None
POST-CONDITIONS:    None
IMPORTANT NOTES:    None
=============================================================================*/
static GstFlowReturn
mfw_gst_vpuenc_buffer_alloc(GstPad *pad, guint64 offset, guint size,
								 GstCaps *caps, GstBuffer **buf)
{
    GstBuffer *ibuf = NULL;
    GstElement *parent = gst_pad_get_parent(pad);
    GST_DEBUG("called vpuenc buffer alloc\n");
   {
        gint index;
        GstBufferMeta *bufmeta;
        unsigned int *vaddr, *paddr; 
        void *handle;
        if ((handle = mfw_new_hw_buffer(size, &paddr, &vaddr, 0)) == 0) {
            GST_ERROR(">>VPU_ENC: Could not allocate hardware buffer\n");
            *buf =  NULL; 
            return GST_FLOW_ERROR;
        }
        ibuf = gst_buffer_new();
        GST_BUFFER_SIZE(ibuf) = size;
        GST_BUFFER_DATA(ibuf)= vaddr;
        bufmeta = gst_buffer_meta_new();
        GST_BUFFER_MALLOCDATA(ibuf) = ibuf;
        GST_BUFFER_FREE_FUNC(ibuf) = vpuenc_free_gst_buf_meta;

        index = G_N_ELEMENTS(ibuf->_gst_reserved)-1;
        bufmeta->physical_data = (gpointer) paddr;
        bufmeta->priv = handle;
        ibuf->_gst_reserved[index] = bufmeta;
        
    }



    if (ibuf != NULL)
    {
        *buf = GST_BUFFER_CAST(ibuf);
        GST_BUFFER_SIZE(*buf) =size;
        gst_buffer_set_caps(*buf, caps);

        return GST_FLOW_OK;
    }
    else
    {
        g_print("\n VPU_ENC: Could not allocate buffer\n");
        *buf = NULL;
        return GST_FLOW_ERROR;

    }
}

/*======================================================================================
FUNCTION:           mfw_gst_vpuenc_init

DESCRIPTION:        Create the pad template that has been registered with the
                    element class in the _base_init

ARGUMENTS PASSED:   vpu_enc - pointer to vpu_encoder element structure

RETURN VALUE:       None
PRE-CONDITIONS:     None
POST-CONDITIONS:    None
IMPORTANT NOTES:    None
=======================================================================================*/
static void mfw_gst_vpuenc_init(MfwGstVPU_Enc *vpu_enc, MfwGstVPU_EncClass *gclass)
{
    GstElementClass *klass = GST_ELEMENT_GET_CLASS(vpu_enc);

    vpuenc_global_ptr = vpu_enc;

    /* create the sink and src pads */
    vpu_enc->sinkpad =
        gst_pad_new_from_template(gst_element_class_get_pad_template
        (klass, "sink"), "sink");
    vpu_enc->srcpad =
        gst_pad_new_from_template(gst_element_class_get_pad_template
        (klass, "src"), "src");
    gst_element_add_pad(GST_ELEMENT(vpu_enc), vpu_enc->sinkpad);
    gst_element_add_pad(GST_ELEMENT(vpu_enc), vpu_enc->srcpad);
    vpu_enc->parent_class = g_type_class_peek_parent(gclass);
    gst_pad_set_chain_function(vpu_enc->sinkpad, mfw_gst_vpuenc_chain);
    gst_pad_set_event_function(vpu_enc->sinkpad,
			       GST_DEBUG_FUNCPTR
			       (mfw_gst_vpuenc_sink_event));
    gst_pad_set_bufferalloc_function(vpu_enc->sinkpad, GST_DEBUG_FUNCPTR(mfw_gst_vpuenc_buffer_alloc));
    gst_pad_set_setcaps_function(vpu_enc->sinkpad, mfw_gst_vpuenc_setcaps);

    vpu_enc->codec         = STD_AVC;
    vpu_enc->width         = DEFAULT_HEIGHT;
    vpu_enc->height        = DEFAULT_WIDTH;
    vpu_enc->src_framerate = DEFAULT_FRAME_RATE;
	vpu_enc->tgt_framerate = DEFAULT_FRAME_RATE;
    vpu_enc->bitrate       = 0;
    vpu_enc->qp            = VPU_DEFAULT_QP;
    vpu_enc->max_qp        = VPU_DEFAULT_QP;
    vpu_enc->intra_qp      = VPU_DEFAULT_QP;
    vpu_enc->gamma         = 4096;
    vpu_enc->gopsize       = DEFAULT_GOP_SIZE;
    vpu_enc->codecTypeProvided = TRUE;
    vpu_enc->heightProvided    = FALSE;
    vpu_enc->widthProvided     = FALSE;
    vpu_enc->ts_rx = vpu_enc->ts_rx=0;

    vpu_enc->forcefixrate = FALSE;

    vpu_enc->segment_starttime     = 0;
    vpu_enc->segment_encoded_frame = 0;
    vpu_enc->total_time            = 0;
    vpu_enc->framerate_d           = vpu_enc->framerate_n = 0;
    vpu_enc->vpuRegisteredFramesDesc = NULL;
    vpu_enc->vpuRegisteredFrames     = NULL;
    vpu_enc->numframebufs            = 0;
    vpu_enc->intraRefresh            = 0;
    vpu_enc->h263profile0            = FALSE;
    vpu_enc->loopback                = TRUE;
    vpu_enc->forceIFrameInterval     = DEFAULT_I_FRAME_INTERVAL;
    vpu_enc->format                  = GST_MAKE_FOURCC('I', '4', '2', '0');

    vpu_enc->crop_top = 0;
    vpu_enc->crop_left = 0;
    vpu_enc->crop_right = 0;
    vpu_enc->crop_bottom = 0;

    memset(&(vpu_enc->vpuInFrame), 0, sizeof(FrameBuffer));

}

/*======================================================================================
FUNCTION:           plugin_init

DESCRIPTION:        Special function , which is called as soon as the plugin or
                    element is loaded and information returned by this function
                    will be cached in central registry

ARGUMENTS PASSED:   plugin - pointer to container that contains features loaded
                             from shared object module

RETURN VALUE:       return TRUE or FALSE depending on whether it loaded initialized any
                    dependency correctly

PRE-CONDITIONS:     None
POST-CONDITIONS:    None
IMPORTANT NOTES:    None
=======================================================================================*/
static gboolean plugin_init(GstPlugin *plugin)
{

    return gst_element_register(plugin, "mfw_vpuencoder",
				GST_RANK_PRIMARY, MFW_GST_TYPE_VPU_ENC);
}


GST_PLUGIN_DEFINE (
          GST_VERSION_MAJOR,	                    /* major version of gstreamer */
		  GST_VERSION_MINOR,	                    /* minor version of gstreamer */
		  "mfw_vpuencoder",	                        /* name of our  plugin */
		  "Encodes Raw YUV Data to MPEG4 SP,"
#if (defined(VPU_MX51) || defined(VPU_MX53))        /* what our plugin actually does */
          "or H.264 BP, H.263 or MJPG Format data to Raw YUV Data",
#else
          "or H.264 BP, or H.263 data to Raw YUV Data",
#endif                 
		  plugin_init,	                            /* first function to be called */
		  VERSION,
		  GST_LICENSE_UNKNOWN,
		  FSL_GST_MM_PLUGIN_PACKAGE_NAME, FSL_GST_MM_PLUGIN_PACKAGE_ORIG)

/*======================================================================================
FUNCTION:           mfw_gst_type_vpu_enc_get_type

DESCRIPTION:        Interfaces are initiated in this function.you can register one
                    or more interfaces after having registered the type itself.

ARGUMENTS PASSED:   None

RETURN VALUE:       Numerical value ,which represents the unique identifier of 
                    this element.

PRE-CONDITIONS:     None
POST-CONDITIONS:    None
IMPORTANT NOTES:    None
========================================================================================*/
static GType mfw_gst_type_vpu_enc_get_type(void)
{
    static GType vpu_enc_type = 0;
    

    if (!vpu_enc_type) 
    {
	    static const GTypeInfo vpu_enc_info = 
        {
	        sizeof(MfwGstVPU_EncClass),
	        (GBaseInitFunc) mfw_gst_vpuenc_base_init,
	        NULL,
	        (GClassInitFunc) mfw_gst_vpuenc_class_init,
	        NULL,
	        NULL,
	        sizeof(MfwGstVPU_Enc),
	        0,
	        (GInstanceInitFunc) mfw_gst_vpuenc_init,
	    };
	    vpu_enc_type = g_type_register_static(GST_TYPE_ELEMENT,
		    	                    "MfwGstVPU_Enc",
			                        &vpu_enc_info, 0);
    }

    GST_DEBUG_CATEGORY_INIT(mfw_gst_vpuenc_debug,
			    "mfw_vpuencoder", 0,
			    "FreeScale's VPU  Encoder's Log");

    return vpu_enc_type;
}



/*======================================================================================
FUNCTION:           mfw_gst_vpuenc_vpu_finalize

DESCRIPTION:        Handles cleanup of any unreleased memory if player is closed

ARGUMENTS PASSED:   None
RETURN VALUE:       None
PRE-CONDITIONS:     None
POST-CONDITIONS:    None
IMPORTANT NOTES:    None
========================================================================================*/
void __attribute__ ((destructor)) mfw_gst_vpuenc_vpu_finalize(void);

void mfw_gst_vpuenc_vpu_finalize(void)
{
    MfwGstVPU_Enc *vpu_enc = vpuenc_global_ptr;
    if (vpu_enc == NULL) {
       GST_DEBUG(">>VPU_ENC: vpu_enc is null,exit no clean up needed\n");
       return;
    }
    GST_DEBUG(">>VPU_ENC: Destructor - final cleanup \n");
    mfw_gst_vpuenc_cleanup(vpu_enc);
    vpu_UnInit();
    vpuenc_global_ptr = NULL;
}
