// ****************** Copyright (c) 2013 by Gigaset Communications GmbH ******
// Filename : tdav_codec_h264_hw_vpu.cxx
// Owner    : Mohammed Hasan [STC]
//-------------------------------------------------------------------------
// Defintion : H.264 codec plugin using FSL OMX HW codec for encoding and decoding.
// RTP payloader/depayloader follows RFC 3984.
//-------------------------------------------------------------------------
// 05-07-2013, Gigaset Communications GmbH
// Added FSL OMX HW H.264 codec plugin to Doubango Framework
//*************************************************************************

/*
* This file is part of Open Source Doubango Framework.
*
* DOUBANGO is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* DOUBANGO 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with DOUBANGO.
*
*/

#include "tinydav/codecs/h264/tdav_codec_h264_hw_vpu.h"
#if (HAVE_FFMPEG && (!defined(HAVE_H264) || HAVE_H264)) || HAVE_H264_PASSTHROUGH

#include "tinydav/codecs/h264/tdav_codec_h264_rtp.h"
#include "tinydav/video/tdav_converter_video.h"

#include "tinyrtp/rtp/trtp_rtp_packet.h"

#include "tinymedia/tmedia_params.h"

#include "tsk_params.h"

#include "tsk_memory.h"
#include "tsk_debug.h"
#include "tsk_timer.h"

#ifndef INT64_C
#       define INT64_C(c) (c ## LL)
#       define UINT64_C(c) (c ## ULL)
#endif

#ifdef __cplusplus
extern "C" {
#endif
#if HAVE_FFMPEG
#   include <libavcodec/avcodec.h>
#   if (LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(51, 35, 0))
#       include <libavutil/opt.h>
#   endif
#endif
#ifdef __cplusplus
}
#endif

//Android includes
#include <android/log.h>
#include <cutils/properties.h>
#include "OMX_Implement.h"
#include "fsl_osal.h"
#include "Mem.h"
#include "Log.h"

#include "OMX_Core.h"
#include "OMX_Component.h"
#include "Queue.h"

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <pthread.h>

//#define OMX_H264_DEBUG
#define LOG_TAG "OMX H264"

#ifdef OMX_H264_DEBUG
#define  TSK_DEBUG_INFO(...)  __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)
#define  TSK_DEBUG_ERROR(...)  __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)
#define  TSK_DEBUG_WARN(...)  __android_log_print(ANDROID_LOG_WARN,LOG_TAG,__VA_ARGS__)
#else
#define  TSK_DEBUG_INFO(...)
#define  TSK_DEBUG_ERROR(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)
#define  TSK_DEBUG_WARN(...) __android_log_print(ANDROID_LOG_WARN,LOG_TAG,__VA_ARGS__)
#endif // OMX_H264_DEBUG

#define MAX_PORT_NUM 8
#define MAX_PORT_BUFFER 32
#define MAX_QUEUE_SIZE 30
#define MUL_FACTOR 1000
#define SLEEP_TIME 10000
#define TIME_BASE_NUM 1
#define ROTATION_90 90
#define ROTATION_270 270
#define BIT_RATE_CAL_PARAM1 256
#define BIT_RATE_CAL_PARAM2 352
#define BIT_RATE_CAL_PARAM3 288
#define ENC_MIN_PORT_BUF 1
#define ENC_MAX_PORT_BUF 3

#define VPU_ENC_COMP_NAME_AVCENC    "OMX.Freescale.std.video_encoder.avc.hw-based"
#define VPU_ENC_COMP_ROLE_AVCENC    "video_encoder.avc"
#define VPU_DEC_COMP_NAME_AVCDEC    "OMX.Freescale.std.video_decoder.avc.v3.hw-based"
#define VPU_DEC_COMP_ROLE_AVCDEC    "video_decoder.avc"

#define Align(ptr,align)        (((OMX_U32)(ptr)+(align)-1)/(align)*(align))
#define FRAME_ALIGN             (32)
#define MAX_ENC_BUF_TO_KEEP 4
#define MAX_DEC_BUF_TO_KEEP 2
#define LOW_MOTION  1
#define MED_MOTION  2
#define HIGH_MOTION 3

#define DUMMY_1080P_FRAME_PATH "/system/media/dummyframe-1080p.h264"
#define DUMMY_720P_FRAME_PATH "/system/media/dummyframe-720p.h264"
#define DUMMY_VGA_FRAME_PATH "/system/media/dummyframe-vga.h264"
#define DUMMY_QVGA_FRAME_PATH "/system/media/dummyframe-qvga.h264"
#define HT_1080P 1080
#define HT_720P 720
#define HT_VGA 480
#define HT_QVGA 240
#define TH264_WAIT_FOR_PAUSE 1000 /* Time (ms) to wait before entering in PAUSE state*/
#define TH264_WAIT_FOR_RESET 20 /* Time in ms to wait before resetting decoder */

#define MAX_IN_ENC_RB_CAP 4 /* Maximum number of buffers to hold in enc i/p ring buffer (raw buffer)*/
#define MAX_IN_DEC_RB_CAP 8 /* Maximum number of buffers to hold in ring buffer(encoded buffer) */
#define MAX_OUT_DEC_RB_CAP 4 /* Maximum number of buffers to hold in ring buffer (decoded buffer)*/

/* I-Frame request frequency in seconds */
#define HIGH_GOP_SIZE_IN_SEC 0 /* Use it for low resolution and gets i-frames @ 0 sec */
#define MED_GOP_SIZE_IN_SEC 4 /* Use it for medium resolution and gets i-frames @ 4 secs */
#define LOW_GOP_SIZE_IN_SEC 14 /* Use it for high resolution and gets i-frames @ 14 secs*/
#define DEF_GOP_SIZE_IN_SEC 14 /* Use it for high resolution and gets i-frames @ 14 secs*/

#define MAX_GOP_SIZE_ALLOWED 32766
#define MAX_BR_FACT 10

#define VIDEO_DUMP

#ifdef VIDEO_DUMP
FILE *fpEncodedBuffer = NULL;
FILE *fpDecodedBuffer = NULL;
#endif

typedef enum {
    EVENT = 0,
    EMPTY_DONE,
    FILL_DONE,
    EXIT
}MSG_TYPE;

enum {
    kPortIndexInput  = 0,
    kPortIndexOutput = 1
};

typedef struct {
    OMX_EVENTTYPE eEvent;
    OMX_U32 nData1;
    OMX_U32 nData2;
    OMX_PTR pEventData;
}EVENT_TYPE;

typedef struct {
    OMX_BUFFERHEADERTYPE* pBuffer;
}BUFFER;

typedef struct _MSG {
    OMX_HANDLETYPE hComponent;
    MSG_TYPE type;
    union {
        EVENT_TYPE event;
        BUFFER buffer;
    }data;
}MSG;

typedef struct _var_buffer {
    void* buffer;
    int size;
} var_buffer;

typedef struct _ring_buffer {
    var_buffer * buffer;
    void * buffer_end;
    int capacity;
    int size;
    int count;
    void *head;
    void *tail;
    pthread_mutex_t rw_mutex;
} ring_buffer;

typedef struct tdav_codec_h264_hw_vpu_s
{
	TDAV_DECLARE_CODEC_H264_COMMON;

        tsk_bool_t timer_started;
        tsk_timer_manager_handle_t *h_timer;

        // Timer
        struct {
            tsk_timer_id_t id;
            int64_t timeout;
        } timer_idle;

        struct {
            tsk_timer_id_t id;
            int64_t timeout;
        } timer_reset_dec;

        bool video_dump_to_file;
        bool disable_dummy_buffer;
        int idle_timeout_period;
        int reset_timeout_period;

	// Encoder
	struct{
#if HAVE_FFMPEG
		AVCodecContext* context;
#endif
		void* buffer;
		void* dummy_buffer;
		tsk_size_t dummy_buffer_size;
		void* dummy_decode_buffer;
		tsk_size_t dummy_decode_buffer_size;
                OMX_BOOL decoded_first_frame;
                OMX_BOOL encoded_first_frame;
                tsk_size_t buffer_size;
		int64_t frame_count;
		tsk_bool_t force_idr;
		int32_t quality; // [1-31]
		int rotation;

		//hw_vpu
                OMX_COMPONENTTYPE *hComponent;
                OMX_STRING name;
                OMX_STRING role;
                OMX_U32 nPorts;
                OMX_BOOL bAllocater[MAX_PORT_NUM];
                OMX_U32 nBufferHdr[MAX_PORT_NUM];
                OMX_DIRTYPE PortDir[MAX_PORT_NUM];
                OMX_BUFFERHEADERTYPE *pBufferHdr[MAX_PORT_NUM][MAX_PORT_BUFFER];
                OMX_BUFFERHEADERTYPE *pFrameBuffer;
                Queue *pMsgQ;
                EVENT_TYPE sCmdDone;
                fsl_osal_ptr pThreadId;
                OMX_BOOL bInEos;
                OMX_BOOL bError;
                OMX_BOOL bHoldBuffers;
                OMX_BOOL bInitailBufferSubmit;
                OMX_BOOL bThreadRunning;
                OMX_BOOL bClosedEncoder;
                OMX_BOOL source_done;
                OMX_S32 width;
	        OMX_S32 height;
	        OMX_S32 bitrate;
                OMX_S32 inBufEmptyCount;
                OMX_S32 encPacketLoss;
                uint8_t *prev_frame;
                OMX_BUFFERHEADERTYPE *dummy_frame;
                OMX_VIDEO_AVCPROFILETYPE mProfile;
                OMX_VIDEO_AVCLEVELTYPE mLevel;
                OMX_U32 maxBufInQueue;
                OMX_S32 bitrate_fact;
                tsk_bool_t enc_started;
                int in_ring_buf_cap;
                ring_buffer *in_ring_buf; // Raw data from camera source
                uint32_t gop_size;
	} encoder;

	// decoder
	struct{
#if HAVE_FFMPEG
		AVCodecContext* context;
#endif
		void* accumulator;
		void* crop_buffer;
                tsk_size_t decode_frame_size;
		tsk_size_t accumulator_pos;
		tsk_size_t accumulator_size;
		uint16_t last_seq;

		//hw_vpu
                OMX_COMPONENTTYPE *hComponent;
                OMX_STRING name;
                OMX_STRING role;
                OMX_U32 nPorts;
                OMX_BOOL bAllocater[MAX_PORT_NUM];
                OMX_U32 nBufferHdr[MAX_PORT_NUM];
                OMX_DIRTYPE PortDir[MAX_PORT_NUM];
                OMX_BUFFERHEADERTYPE *pBufferHdr[MAX_PORT_NUM][MAX_PORT_BUFFER];
                OMX_BUFFERHEADERTYPE *pFrameBuffer;
                Queue *pMsgQ;
                EVENT_TYPE sCmdDone;
                fsl_osal_ptr pThreadId;
                OMX_BOOL bInEos;
                OMX_BOOL bError;
                OMX_BOOL bHoldBuffers;
                OMX_BOOL bInitailBufferSubmit;
                OMX_BOOL bThreadRunning;
                OMX_BOOL bClosedDecoder;
                OMX_BOOL source_done;
                OMX_S32 decPacketLoss;
                uint8_t *prev_frame;
                OMX_S32 cropLeft;
                OMX_S32 cropTop;
                OMX_U32 cropRight;
                OMX_U32 cropBottom;
                OMX_U32 maxBufInQueue;
                tsk_bool_t dec_started;
                tsk_bool_t decoder_reset;
                int in_ring_buf_cap;
                int out_ring_buf_cap;
                ring_buffer *in_ring_buf; // Encoded data from NW
                ring_buffer *out_ring_buf; // Decoded (raw) data for display
                OMX_S32 inBufEmptyCount;
	} decoder;
}
tdav_codec_h264_hw_vpu_t;
#define TDAV_H264_GOP_SIZE_IN_SECONDS		25

static int tdav_codec_h264_init(tdav_codec_h264_hw_vpu_t* self, profile_idc_t profile);
static int tdav_codec_h264_deinit(tdav_codec_h264_hw_vpu_t* self);
static int tdav_codec_h264_open_encoder(tdav_codec_h264_hw_vpu_t* self);
static int tdav_codec_h264_close_encoder(tdav_codec_h264_hw_vpu_t* self);
static int tdav_codec_h264_open_decoder(tdav_codec_h264_hw_vpu_t* self);
static int tdav_codec_h264_close_decoder(tdav_codec_h264_hw_vpu_t* self);

static void tdav_codec_h264_encap(const tdav_codec_h264_hw_vpu_t* h264, const uint8_t* pdata, tsk_size_t size);
static int hw_vpu_open_decoder(tdav_codec_h264_hw_vpu_t* self);
static int _tdav_session_h264_timer_cb(const void* arg, tsk_timer_id_t timer_id);
static int reset_decoder(tdav_codec_h264_hw_vpu_t *h264);
static void start_reset_dec_timer(tdav_codec_h264_hw_vpu_t *h264);
static OMX_ERRORTYPE open_fsl_omx_decoder(tdav_codec_h264_hw_vpu_t* h264);

// Decoder OMX call backs
OMX_ERRORTYPE gm_EventHandler(
        OMX_IN OMX_HANDLETYPE hComponent,
        OMX_IN OMX_PTR pAppData,
        OMX_IN OMX_EVENTTYPE eEvent,
        OMX_IN OMX_U32 nData1,
        OMX_IN OMX_U32 nData2,
        OMX_IN OMX_PTR pEventData);

OMX_ERRORTYPE gm_EmptyBufferDone(
        OMX_IN OMX_HANDLETYPE hComponent,
        OMX_IN OMX_PTR pAppData,
        OMX_IN OMX_BUFFERHEADERTYPE* pBuffer);

OMX_ERRORTYPE gm_FillBufferDone(
        OMX_OUT OMX_HANDLETYPE hComponent,
        OMX_OUT OMX_PTR pAppData,
        OMX_OUT OMX_BUFFERHEADERTYPE* pBuffer);

OMX_CALLBACKTYPE gCallBacks =
{
    gm_EventHandler,
    gm_EmptyBufferDone,
    gm_FillBufferDone
};

//Decoder OMX functions
OMX_ERRORTYPE read_frame(tdav_codec_h264_hw_vpu_t *h264, OMX_BUFFERHEADERTYPE *pBufferHdr);
OMX_ERRORTYPE write_data(tdav_codec_h264_hw_vpu_t *h264, OMX_BUFFERHEADERTYPE *pBufferHdr);
OMX_ERRORTYPE WaitCommand(
        tdav_codec_h264_hw_vpu_t *h264,
        OMX_COMMANDTYPE Cmd,
        OMX_U32 nParam1,
        OMX_PTR pCmdData);
OMX_ERRORTYPE SendCommand(
        tdav_codec_h264_hw_vpu_t *h264,
        OMX_COMMANDTYPE Cmd,
        OMX_U32 nParam1,
        OMX_PTR pCmdData,
        OMX_BOOL bSync);
OMX_U32 get_component_ports(OMX_HANDLETYPE hComponent);
OMX_ERRORTYPE unload_component(tdav_codec_h264_hw_vpu_t *h264);
OMX_ERRORTYPE prepare_port_buffers(tdav_codec_h264_hw_vpu_t *h264, OMX_U32 nPortIndex);
OMX_ERRORTYPE free_port_buffers(tdav_codec_h264_hw_vpu_t *h264, OMX_U32 nPortIndex);
OMX_ERRORTYPE Load2Idle(tdav_codec_h264_hw_vpu_t *h264);
OMX_ERRORTYPE Idle2Load(tdav_codec_h264_hw_vpu_t *h264);
OMX_ERRORTYPE StateTrans(tdav_codec_h264_hw_vpu_t *h264, OMX_STATETYPE eState);
OMX_ERRORTYPE port_disable(tdav_codec_h264_hw_vpu_t *h264, OMX_U32 nPortIndex);
OMX_ERRORTYPE port_enable(tdav_codec_h264_hw_vpu_t *h264, OMX_U32 nPortIndex);
OMX_ERRORTYPE process_event(tdav_codec_h264_hw_vpu_t *self, MSG *pMsg);
static void * omx_process_thread(void *ptr);
OMX_ERRORTYPE start_data_process(tdav_codec_h264_hw_vpu_t *self);
OMX_ERRORTYPE wait_eos(tdav_codec_h264_hw_vpu_t *self);
static int close_hw_vpu_decoder(tdav_codec_h264_hw_vpu_t *self);
static int hw_vpu_decode(tdav_codec_h264_hw_vpu_t* self, const AVPacket *avpkt);
static int hw_vpu_open_encoder(tdav_codec_h264_hw_vpu_t* self);
static void ProcessBufferForCrop(tdav_codec_h264_hw_vpu_t* self, uint8_t* srcPtr, uint8_t* desPtr, int size);
static void copyDecodedData(tdav_codec_h264_hw_vpu_t* self, void** out_data, int size);

// Encoder OMX call backs
OMX_ERRORTYPE gm_EncEventHandler(
        OMX_IN OMX_HANDLETYPE hComponent,
        OMX_IN OMX_PTR pAppData,
        OMX_IN OMX_EVENTTYPE eEvent,
        OMX_IN OMX_U32 nData1,
        OMX_IN OMX_U32 nData2,
        OMX_IN OMX_PTR pEventData);

OMX_ERRORTYPE gm_EncEmptyBufferDone(
        OMX_IN OMX_HANDLETYPE hComponent,
        OMX_IN OMX_PTR pAppData,
        OMX_IN OMX_BUFFERHEADERTYPE* pBuffer);

OMX_ERRORTYPE gm_EncFillBufferDone(
        OMX_OUT OMX_HANDLETYPE hComponent,
        OMX_OUT OMX_PTR pAppData,
        OMX_OUT OMX_BUFFERHEADERTYPE* pBuffer);

OMX_CALLBACKTYPE gEncCallBacks =
{
    gm_EncEventHandler,
    gm_EncEmptyBufferDone,
    gm_EncFillBufferDone
};

//Encoder OMX functions
OMX_ERRORTYPE read_enc_frame(tdav_codec_h264_hw_vpu_t *h264, OMX_BUFFERHEADERTYPE *pBufferHdr);
OMX_ERRORTYPE write_enc_data(tdav_codec_h264_hw_vpu_t *h264, OMX_BUFFERHEADERTYPE *pBufferHdr);
OMX_ERRORTYPE EncWaitCommand(
        tdav_codec_h264_hw_vpu_t *h264,
        OMX_COMMANDTYPE Cmd,
        OMX_U32 nParam1,
        OMX_PTR pCmdData);
OMX_ERRORTYPE EncSendCommand(
        tdav_codec_h264_hw_vpu_t *h264,
        OMX_COMMANDTYPE Cmd,
        OMX_U32 nParam1,
        OMX_PTR pCmdData,
        OMX_BOOL bSync);
OMX_U32 get_enc_component_ports(OMX_HANDLETYPE hComponent);
OMX_ERRORTYPE unload_enc_component(tdav_codec_h264_hw_vpu_t *h264);
OMX_ERRORTYPE prepare_enc_port_buffers(tdav_codec_h264_hw_vpu_t *h264, OMX_U32 nPortIndex);
OMX_ERRORTYPE free_enc_port_buffers(tdav_codec_h264_hw_vpu_t *h264, OMX_U32 nPortIndex);
OMX_ERRORTYPE EncLoad2Idle(tdav_codec_h264_hw_vpu_t *h264);
OMX_ERRORTYPE EncIdle2Load(tdav_codec_h264_hw_vpu_t *h264);
OMX_ERRORTYPE EncStateTrans(tdav_codec_h264_hw_vpu_t *h264, OMX_STATETYPE eState);
OMX_ERRORTYPE enc_port_disable(tdav_codec_h264_hw_vpu_t *h264, OMX_U32 nPortIndex);
OMX_ERRORTYPE enc_port_enable(tdav_codec_h264_hw_vpu_t *h264, OMX_U32 nPortIndex);
OMX_ERRORTYPE enc_process_event(tdav_codec_h264_hw_vpu_t *self, MSG *pMsg);
static void * enc_omx_process_thread(void *ptr);
OMX_ERRORTYPE enc_start_data_process(tdav_codec_h264_hw_vpu_t *self);
OMX_ERRORTYPE enc_wait_eos(tdav_codec_h264_hw_vpu_t *self);
OMX_ERRORTYPE request_intraVOP(tdav_codec_h264_hw_vpu_t *self);
static int close_hw_vpu_encoder(tdav_codec_h264_hw_vpu_t *self);
static int hw_vpu_encode(tdav_codec_h264_hw_vpu_t* self, const AVPacket *avpkt);
static OMX_U32 setPFramesSpacing(int32_t iFramesInterval, int32_t frameRate);
static void free(tdav_codec_h264_hw_vpu_t *h264);
static int reset_decoder_config(tdav_codec_h264_hw_vpu_t *h264, int width, int height);
static bool set_bitrate_fac_GOP_size(tdav_codec_h264_hw_vpu_t *self);

static void ring_buffer_free(ring_buffer *var_rb)
{
    TSK_DEBUG_INFO("free");
    if (var_rb == NULL) {
        TSK_DEBUG_ERROR("Invalid parameter");
        return;
    }

    if (var_rb) {
        if (var_rb->buffer) {
            if (var_rb->buffer->buffer) {
                FSL_FREE(var_rb->buffer->buffer);
                var_rb->buffer->buffer = NULL;
                var_rb->buffer->size = 0;
            }
            FSL_FREE(var_rb->buffer);
            var_rb->buffer = NULL;
        }
        FSL_FREE(var_rb);
        var_rb = NULL;
    }
}

static ring_buffer* ring_buffer_init(int cap, int size)
{
    TSK_DEBUG_INFO("ring_buffer_init with capacity %d, size %d", cap, size);
    ring_buffer *var_rb = NULL;

    var_rb = (ring_buffer*) FSL_MALLOC(sizeof(ring_buffer));
    if (var_rb == NULL) {
        TSK_DEBUG_ERROR("Error allocating memory for var_rb");
        return var_rb;
    }

    var_rb->buffer = (var_buffer*) FSL_MALLOC(sizeof(var_buffer));
    if (var_rb->buffer == NULL) {
        TSK_DEBUG_ERROR("Error allocating memory for var_rb buffer");
        FSL_FREE(var_rb);
        var_rb = NULL;
        return var_rb;
    }

    var_rb->buffer->buffer = (void*) FSL_MALLOC(cap * size);
    if (var_rb->buffer->buffer == NULL) {
        TSK_DEBUG_ERROR("Error allocating memory for var_rb buffer->buffer");
        FSL_FREE(var_rb->buffer);
        var_rb->buffer = NULL;
        FSL_FREE(var_rb);
        var_rb = NULL;
        return var_rb;
    }

    TSK_DEBUG_INFO("ring_buffer_init success with size %d", cap*size);
    var_rb->buffer_end = (char*) (var_rb->buffer->buffer) + (cap * size);
    var_rb->capacity = cap;
    var_rb->size = size;
    var_rb->count = 0;
    var_rb->head = var_rb->tail = var_rb->buffer->buffer;
    var_rb->rw_mutex = PTHREAD_MUTEX_INITIALIZER;
    return var_rb;
}

static int ring_buffer_push(ring_buffer *var_rb, const void *data, int size)
{
    TSK_DEBUG_INFO("var push size %d", size);
    if (!var_rb || !data) {
        TSK_DEBUG_ERROR("var_push:Invalid param");
        return -1;
    }

    if (var_rb->count >= var_rb->capacity) {
        TSK_DEBUG_WARN("Overwriting var ring buffer count %d, cap %d",
                        var_rb->count, var_rb->capacity);
        var_rb->count = var_rb->capacity;
    }

    if (size > var_rb->size) {
        TSK_DEBUG_ERROR("var_push: invalid size: req %d, allocd %d", size, var_rb->size);
        return -1;
    }

    memcpy(var_rb->head, data, size);
    // Lock mutex
    pthread_mutex_lock(&var_rb->rw_mutex);
    var_rb->buffer->size = size;
    var_rb->head = (char*) var_rb->head + var_rb->size;//Increment tail by fixed size (Max buff size)
    if (var_rb->head == var_rb->buffer_end)
        var_rb->head = var_rb->buffer->buffer;

    var_rb->count++;
    // Unlock mutex
    pthread_mutex_unlock(&var_rb->rw_mutex);
    return 0;
}

static void ring_buffer_pop(ring_buffer *var_rb, void *data, int *size, bool realloc)
{
    if (!var_rb || !data) {
        TSK_DEBUG_ERROR("var pop:Invalid param");
        *size = 0;
        return;
    }

    // Lock mutex
    pthread_mutex_lock(&var_rb->rw_mutex);
    if (var_rb->count == 0) {
        TSK_DEBUG_WARN("No data in ring buffer");
        *size = 0;
        // Unlock mutex
        pthread_mutex_unlock(&var_rb->rw_mutex);
        return;
    }
    // Unlock mutex
    pthread_mutex_unlock(&var_rb->rw_mutex);
    TSK_DEBUG_INFO("Pop: Size available %d ", var_rb->buffer->size);

    // Re-allocate data, if requested size < available size
    if ((*size > 0) && (*size < var_rb->buffer->size)) {
        if (realloc) {
            if (!(data = tsk_realloc(data, var_rb->buffer->size))) {
                TSK_DEBUG_ERROR("ring_buffer_pop: Failed to re-allocate buffer");
                return;
            }
        } else {
            TSK_DEBUG_ERROR("Buffer overflow");
            *size = 0;
            return;
        }
    }

    memcpy(data, var_rb->tail, var_rb->buffer->size);
    *size = var_rb->buffer->size;

    // Lock mutex
    pthread_mutex_lock(&var_rb->rw_mutex);
    var_rb->tail = (char*) var_rb->tail + var_rb->size;//Increment tail by fixed size (Max buff size)
    if (var_rb->tail == var_rb->buffer_end)
        var_rb->tail = var_rb->buffer->buffer;

    var_rb->count--;
    // Unlock mutex
    pthread_mutex_unlock(&var_rb->rw_mutex);
}

/* ============ H.264 Base/Main Profile X.X Plugin interface functions ================= */

static int tdav_codec_h264_hw_vpu_set(tmedia_codec_t* self, const tmedia_param_t* param)
{
	tdav_codec_h264_hw_vpu_t* h264 = (tdav_codec_h264_hw_vpu_t*)self;
	if(!self->opened){
		TSK_DEBUG_ERROR("Codec not opened");
		return -1;
	}
	if(param->value_type == tmedia_pvt_int32){
		if(tsk_striequals(param->key, "action")){
			tmedia_codec_action_t action = (tmedia_codec_action_t)TSK_TO_INT32((uint8_t*)param->value);
			switch(action){
				case tmedia_codec_action_encode_idr:
					{
						h264->encoder.force_idr = tsk_true;
						break;
					}
				case tmedia_codec_action_bw_down:
					{
						h264->encoder.quality = TSK_CLAMP(1, (h264->encoder.quality + 1), 31);
#if HAVE_FFMPEG
						h264->encoder.context->global_quality = FF_QP2LAMBDA * h264->encoder.quality;
#endif
						break;
					}
				case tmedia_codec_action_bw_up:
					{
						h264->encoder.quality = TSK_CLAMP(1, (h264->encoder.quality - 1), 31);
#if HAVE_FFMPEG
						h264->encoder.context->global_quality = FF_QP2LAMBDA * h264->encoder.quality;
#endif
						break;
					}
			}
			return 0;
		}
		else if(tsk_striequals(param->key, "rotation")){
			int rotation = *((int32_t*)param->value);
			if(h264->encoder.rotation != rotation){
				if(self->opened){
					int ret;
					h264->encoder.rotation = rotation;
					if((ret = tdav_codec_h264_close_encoder(h264))){
						return ret;
					}
					if((ret = tdav_codec_h264_open_encoder(h264))){
						return ret;
					}
#if 0 // Not working
					if((ret = avcodec_close(h264->encoder.context))){
						TSK_DEBUG_ERROR("Failed to close [%s] codec", TMEDIA_CODEC(h264)->plugin->desc);
						return ret;
					}
					h264->encoder.context->width = (rotation == 90 || rotation == 270) ? TMEDIA_CODEC_VIDEO(h264)->out.height : TMEDIA_CODEC_VIDEO(h264)->out.width;
					h264->encoder.context->height = (rotation == 90 || rotation == 270) ? TMEDIA_CODEC_VIDEO(h264)->out.width : TMEDIA_CODEC_VIDEO(h264)->out.height;
					if((ret = avcodec_open(h264->encoder.context, h264->encoder.codec)) < 0){
						TSK_DEBUG_ERROR("Failed to open [%s] codec", TMEDIA_CODEC(h264)->plugin->desc);
						return ret;
					}
					h264->encoder.force_idr = tsk_true;
#endif
				}
			}
			return 0;
		}
	}
	return -1;
}

static int tdav_codec_h264_hw_vpu_open(tmedia_codec_t* self)
{
	int ret;
	tdav_codec_h264_hw_vpu_t* h264 = (tdav_codec_h264_hw_vpu_t*)self;

	if(!h264){
		TSK_DEBUG_ERROR("Invalid parameter");
		return -1;
	}

        // read property
        char propValue[PROPERTY_VALUE_MAX];
        if (property_get("video_dump_enable", propValue, NULL)) {
            h264->video_dump_to_file = (bool) atoi(propValue);
        } else {
            h264->video_dump_to_file = false;
        }

        // By default, it is set to disable
        if (property_get("disable_dummy_buffer", propValue, NULL)) {
            h264->disable_dummy_buffer = (bool) atoi(propValue);
        } else {
            h264->disable_dummy_buffer = true;
        }

        if (property_get("idle_time_out_period", propValue, NULL)) {
            h264->idle_timeout_period = atoi(propValue);
            if (h264->idle_timeout_period <= 0) {
                h264->idle_timeout_period = TH264_WAIT_FOR_PAUSE;
            }
        }

        if (property_get("reset_time_out_period", propValue, NULL)) {
            h264->reset_timeout_period = atoi(propValue);
            if (h264->reset_timeout_period <= 0) {
                h264->reset_timeout_period = TH264_WAIT_FOR_RESET;
            }
        }

        //TODO: Remove after test
        TSK_DEBUG_WARN("dump %d, disable dummy buffer %d, idle and reset timeout %d & %d",
                        h264->video_dump_to_file, h264->disable_dummy_buffer,
                        h264->idle_timeout_period, h264->reset_timeout_period);
        //hw_vpu
        int err = OMX_Init();
        if (err < 0) {
            TSK_DEBUG_ERROR("Failed to initialize OMX_Init err %d", err);
        }

	/* the caller (base class) already checked that the codec is not opened */

	//	Encoder
	if((ret = tdav_codec_h264_open_encoder(h264))){
		return ret;
	}

	//	Decoder
	if((ret = tdav_codec_h264_open_decoder(h264))){
		return ret;
	}

#ifdef VIDEO_DUMP
    if (h264->video_dump_to_file) {
        fpEncodedBuffer = fopen("/data/cmbs/encoded-stream.h264", "w");
        if (fpEncodedBuffer == NULL) {
           TSK_DEBUG_ERROR("Failed to open file");
        }

        fpDecodedBuffer = fopen("/data/cmbs/decoded-stream.h264", "w");
        if (fpDecodedBuffer == NULL) {
           TSK_DEBUG_ERROR("Failed to open file");
        }
    }
#endif

        h264->encoder.enc_started = tsk_false;
        h264->decoder.dec_started = tsk_false;

        // Create timer for pasuing codec after timeout period
        if (!(h264->h_timer = tsk_timer_manager_create())) {
            TSK_DEBUG_ERROR("Failed to create timer manager for h264");
            //ignore this error
            return 0;
        }

        if ((ret = tsk_timer_manager_start(h264->h_timer)) != 0) {
            TSK_DEBUG_ERROR("Failed to start the timer");
            //Can ignore this error
            return 0;
        }

        h264->timer_started = (ret == 0);

        if (h264->timer_started) {
            h264->timer_idle.timeout = h264->idle_timeout_period;
            h264->timer_idle.id = tsk_timer_manager_schedule(h264->h_timer, h264->timer_idle.timeout, _tdav_session_h264_timer_cb, h264);
            TSK_DEBUG_INFO("Timer sheduled with timeout value %d ms", h264->idle_timeout_period);
        }

	return 0;
}

static int tdav_codec_h264_hw_vpu_close(tmedia_codec_t* self)
{
        TSK_DEBUG_INFO("tdav_codec_h264_hw_vpu_close IN \n");
	tdav_codec_h264_hw_vpu_t* h264 = (tdav_codec_h264_hw_vpu_t*)self;

	if(!h264){
		TSK_DEBUG_ERROR("Invalid parameter");
		return -1;
	}

	/* the caller (base class) alreasy checked that the codec is opened */

	//	Encoder
	tdav_codec_h264_close_encoder(h264);

	//	Decoder
	tdav_codec_h264_close_decoder(h264);

        int err = OMX_Deinit();
        if (err < 0) {
            TSK_DEBUG_ERROR("Failed to de-initialize OMX_Deinit err %d\n", err);
        }

        if (h264->h_timer != tsk_null) {
            tsk_timer_manager_stop(h264->h_timer);
            h264->h_timer = tsk_null;
        }

#ifdef VIDEO_DUMP
    if (h264->video_dump_to_file) {
        if (fpEncodedBuffer) {
            fclose(fpEncodedBuffer);
            fpEncodedBuffer = NULL;
        }
        if (fpDecodedBuffer) {
            fclose(fpDecodedBuffer);
            fpDecodedBuffer = NULL;
        }
    }
#endif
        TSK_DEBUG_INFO("tdav_codec_h264_hw_vpu_close OUT \n");
	return 0;
}

static tsk_size_t tdav_codec_h264_hw_vpu_encode(tmedia_codec_t* self, const void* in_data, tsk_size_t in_size, void** out_data, tsk_size_t* out_max_size)
{
	int ret = 0;

	//TSK_DEBUG_INFO("tdav_codec_h264_hw_vpu_encode: in_size %d, out_max_size %d\n", in_size, *out_max_size);
#if HAVE_FFMPEG
	tsk_bool_t send_idr, send_hdr;
#endif

	tdav_codec_h264_hw_vpu_t* h264 = (tdav_codec_h264_hw_vpu_t*)self;

	if(!self || !in_data || !in_size){
		TSK_DEBUG_ERROR("Invalid parameter");
		return 0;
	}

	if(!self->opened){
		TSK_DEBUG_ERROR("Codec not opened");
		return 0;
	}

#if HAVE_FFMPEG


	// send IDR for:
	//	- the first frame
	//  - remote peer requested an IDR
	//	- every second within the first 4seconds
	send_idr = (
		h264->encoder.frame_count++ == 0
		|| h264 ->encoder.force_idr
		|| ( (h264->encoder.frame_count < (int)TMEDIA_CODEC_VIDEO(h264)->out.fps * 4) && ((h264->encoder.frame_count % TMEDIA_CODEC_VIDEO(h264)->out.fps)==0) )
	   );

	// send SPS and PPS headers for:
	//  - IDR frames (not required but it's the easiest way to deal with pakt loss)
	//  - every 5 seconds after the first 4seconds
	send_hdr = (
		send_idr
		|| ( (h264->encoder.frame_count % (TMEDIA_CODEC_VIDEO(h264)->out.fps * 5))==0 )
		);
	if(send_hdr){
		tdav_codec_h264_encap(h264, h264->encoder.context->extradata, (tsk_size_t)h264->encoder.context->extradata_size);
	}

        // Check if encoder is in Pause state
        if (!h264->encoder.enc_started) {
            // Change state to Executing, if it was in Pause state
            EncStateTrans(h264, OMX_StateExecuting);
            h264->encoder.enc_started = tsk_true;
        }

        if (send_idr) {
            TSK_DEBUG_INFO("Requesting I-Frame");
            request_intraVOP(h264);
        }

	// Encode data
        AVPacket packet;

        /* decode the picture */
        av_init_packet(&packet);
        packet.dts = packet.pts = AV_NOPTS_VALUE;
        packet.size = in_size;
        packet.data = (uint8_t*)in_data;

        if (packet.data) {
	    //ret = hw_vpu_encode(h264, h264->encoder.buffer, h264->encoder.buffer_size, &packet);
	    ret = hw_vpu_encode(h264, &packet);

            // tdav_codec_h264_encap will be called from write_enc_data(),
            // when encoding finished.
	    if(ret > 0){
	        //tdav_codec_h264_encap(h264, (const uint8_t*)h264->encoder.buffer, (tsk_size_t)ret);
                TSK_DEBUG_INFO("tdav_codec_h264_hw_vpu_encode: invalid case");
	    }
        }

	h264 ->encoder.force_idr = tsk_false;

	//TSK_DEBUG_INFO("tdav_codec_h264_hw_vpu_encode OUT\n");
#elif HAVE_H264_PASSTHROUGH

	tdav_codec_h264_encap(h264, (const uint8_t*)in_data, in_size);

#endif
	return 0;
}

static tsk_size_t tdav_codec_h264_hw_vpu_decode(tmedia_codec_t* self, const void* in_data, tsk_size_t in_size, void** out_data, tsk_size_t* out_max_size, const tsk_object_t* proto_hdr)
{
        //TSK_DEBUG_INFO("tdav_codec_h264_hw_vpu_decode IN ");
	tdav_codec_h264_hw_vpu_t* h264 = (tdav_codec_h264_hw_vpu_t*)self;
	const trtp_rtp_header_t* rtp_hdr = (const trtp_rtp_header_t*)proto_hdr;

	const uint8_t* pay_ptr = tsk_null;
	tsk_size_t pay_size = 0;
	int ret;
	tsk_bool_t append_scp;
	tsk_bool_t sps_or_pps;
	tsk_size_t retsize = 0, size_to_copy = 0;
	static tsk_size_t xmax_size = (1920 * 1080 * 3) >> 3;
	static tsk_size_t start_code_prefix_size = sizeof(H264_START_CODE_PREFIX);

	if(!h264 || !in_data || !in_size || !out_data
#if HAVE_FFMPEG
		|| !h264->decoder.context
#endif
		)
	{
		TSK_DEBUG_ERROR("Invalid parameter");
		return 0;
	}

	//TSK_DEBUG_INFO("SeqNo=%hu", rtp_hdr->seq_num);

	/* Packet lost? */
	if((h264->decoder.last_seq + 1) != rtp_hdr->seq_num && h264->decoder.last_seq){
		TSK_DEBUG_WARN("Packet lost, seq_num=%d", (h264->decoder.last_seq + 1));
	}
	h264->decoder.last_seq = rtp_hdr->seq_num;


	/* 5.3. NAL Unit Octet Usage
	  +---------------+
      |0|1|2|3|4|5|6|7|
      +-+-+-+-+-+-+-+-+
      |F|NRI|  Type   |
      +---------------+
	*/
	if(*((uint8_t*)in_data) & 0x80){
		TSK_DEBUG_WARN("F=1");
		/* reset accumulator */
		h264->decoder.accumulator = 0;
		return 0;
	}

	/* get payload */
	if((ret = tdav_codec_h264_get_pay(in_data, in_size, (const void**)&pay_ptr, &pay_size, &append_scp)) || !pay_ptr || !pay_size){
		TSK_DEBUG_ERROR("Depayloader failed to get H.264 content");
		return 0;
	}
	//append_scp = tsk_true;
	size_to_copy = pay_size + (append_scp ? start_code_prefix_size : 0);
	// whether it's SPS or PPS (append_scp is false for subsequent FUA chuncks)
	//sps_or_pps = append_scp && pay_ptr && ((pay_ptr[0] & 0x1F) == 7 || (pay_ptr[0] & 0x1F) == 8);

	// start-accumulator
	if(!h264->decoder.accumulator){
		if(size_to_copy > xmax_size){
			TSK_DEBUG_ERROR("%u too big to contain valid encoded data. xmax_size=%u", size_to_copy, xmax_size);
			return 0;
		}
		if(!(h264->decoder.accumulator = tsk_calloc(size_to_copy, sizeof(uint8_t)))){
			TSK_DEBUG_ERROR("Failed to allocated new buffer");
			return 0;
		}
		h264->decoder.accumulator_size = size_to_copy;
	}
	if((h264->decoder.accumulator_pos + size_to_copy) >= xmax_size){
		TSK_DEBUG_ERROR("BufferOverflow");
		h264->decoder.accumulator_pos = 0;
		return 0;
	}
	if((h264->decoder.accumulator_pos + size_to_copy) > h264->decoder.accumulator_size){
		if(!(h264->decoder.accumulator = tsk_realloc(h264->decoder.accumulator, (h264->decoder.accumulator_pos + size_to_copy)))){
			TSK_DEBUG_ERROR("Failed to reallocated new buffer");
			h264->decoder.accumulator_pos = 0;
			h264->decoder.accumulator_size = 0;
			return 0;
		}
		h264->decoder.accumulator_size = (h264->decoder.accumulator_pos + size_to_copy);
	}

	if(append_scp){
		memcpy(&((uint8_t*)h264->decoder.accumulator)[h264->decoder.accumulator_pos], H264_START_CODE_PREFIX, start_code_prefix_size);
		h264->decoder.accumulator_pos += start_code_prefix_size;
	}
	memcpy(&((uint8_t*)h264->decoder.accumulator)[h264->decoder.accumulator_pos], pay_ptr, pay_size);
	h264->decoder.accumulator_pos += pay_size;
	// end-accumulator

        // Check if decoder is in Pause state
        if (!h264->decoder.dec_started) {
            // Change state to Executing, if it was in Pause state
            StateTrans(h264, OMX_StateExecuting);
            h264->decoder.dec_started = tsk_true;
        }

	if(rtp_hdr->marker){
#if HAVE_FFMPEG
                //TSK_DEBUG_INFO("in_size %d, out_max_size %d\n", in_size, *out_max_size);
		AVPacket packet;

		/* decode the picture */
		av_init_packet(&packet);
		packet.dts = packet.pts = AV_NOPTS_VALUE;
		packet.size = h264->decoder.accumulator_pos;
		packet.data = (uint8_t*)h264->decoder.accumulator;

                ret = hw_vpu_decode(h264, &packet);

		if(ret <0){
			TSK_DEBUG_WARN("Failed to decode the buffer with error code =%d, size=%u, append=%s", ret, h264->decoder.accumulator_pos, append_scp ? "yes" : "no");
			if(TMEDIA_CODEC_VIDEO(self)->in.callback){
				TMEDIA_CODEC_VIDEO(self)->in.result.type = tmedia_video_decode_result_type_error;
				TMEDIA_CODEC_VIDEO(self)->in.result.proto_hdr = proto_hdr;
				TMEDIA_CODEC_VIDEO(self)->in.callback(&TMEDIA_CODEC_VIDEO(self)->in.result);
			}
		} else if ((h264->decoder.out_ring_buf && h264->decoder.out_ring_buf->count > 0)) {
                    tsk_size_t xsize;
                        /* IDR ? */
                        if(((pay_ptr[0] & 0x1F) == 0x05) && TMEDIA_CODEC_VIDEO(self)->in.callback){
                                TSK_DEBUG_INFO("Decoded H.264 IDR");
                                TMEDIA_CODEC_VIDEO(self)->in.result.type = tmedia_video_decode_result_type_idr;
                                TMEDIA_CODEC_VIDEO(self)->in.result.proto_hdr = proto_hdr;
                                TMEDIA_CODEC_VIDEO(self)->in.callback(&TMEDIA_CODEC_VIDEO(self)->in.result);
                        }
			/* fill out */
			xsize = h264->decoder.decode_frame_size;

			if(*out_max_size<xsize){
                                //TSK_DEBUG_INFO("*out_max_size<xsize: *out_max_size %d, xsize %d\n", *out_max_size, xsize);
				if((*out_data = tsk_realloc(*out_data, (xsize + FF_INPUT_BUFFER_PADDING_SIZE)))){
					*out_max_size = xsize;
				}
				else{
					*out_max_size = 0;
					return 0;
				}
			}

			retsize = xsize;
                        copyDecodedData(h264, out_data, xsize);
                        //TSK_DEBUG_INFO("hw vpu: retsize %d ", retsize);
                }
#elif HAVE_H264_PASSTHROUGH
		if(*out_max_size < h264->decoder.accumulator_pos){
			if((*out_data = tsk_realloc(*out_data, h264->decoder.accumulator_pos))){
				*out_max_size = h264->decoder.accumulator_pos;
			}
			else{
				*out_max_size = 0;
				return 0;
			}
		}
		memcpy(*out_data, h264->decoder.accumulator, h264->decoder.accumulator_pos);
		retsize = h264->decoder.accumulator_pos;
#endif

		h264->decoder.accumulator_pos = 0;
        } else if ((h264->decoder.out_ring_buf && h264->decoder.out_ring_buf->count > 0)) {
                // always check for data
                tsk_size_t xsize;
                    /* IDR ? */
                    if(((pay_ptr[0] & 0x1F) == 0x05) && TMEDIA_CODEC_VIDEO(self)->in.callback){
                            TSK_DEBUG_INFO("Decoded H.264 IDR");
                            TMEDIA_CODEC_VIDEO(self)->in.result.type = tmedia_video_decode_result_type_idr;
                            TMEDIA_CODEC_VIDEO(self)->in.result.proto_hdr = proto_hdr;
                            TMEDIA_CODEC_VIDEO(self)->in.callback(&TMEDIA_CODEC_VIDEO(self)->in.result);
                    }
                    /* fill out */
                    xsize = h264->decoder.decode_frame_size;

                    if(*out_max_size<xsize){
                        TSK_DEBUG_INFO(" out_max_size < xsize: out_max_size %d, xsize %d\n", *out_max_size, xsize);
                        if((*out_data = tsk_realloc(*out_data, (xsize + FF_INPUT_BUFFER_PADDING_SIZE)))){
                            *out_max_size = xsize;
                        } else {
                            TSK_DEBUG_INFO("out_max_size<xsize: failed to realloc memeory");
                            *out_max_size = 0;
                            return 0;
                        }
                    }

                    retsize = xsize;
                    copyDecodedData(h264, out_data, xsize);
        }

        //TSK_DEBUG_INFO("tdav_codec_h264_hw_vpu_decode OUT ");
	return retsize;
}

static tsk_bool_t tdav_codec_h264_hw_vpu_sdp_att_match(const tmedia_codec_t* codec, const char* att_name, const char* att_value)
{
	tdav_codec_h264_hw_vpu_t* h264 = (tdav_codec_h264_hw_vpu_t*)codec;
	tsk_bool_t ret = tsk_true;

	if(!h264){
		TSK_DEBUG_ERROR("Invalid parameter");
		return tsk_false;
	}

	TSK_DEBUG_INFO("Trying to match [%s:%s]", att_name, att_value);

	if(tsk_striequals(att_name, "fmtp")){
		int val_int;
		profile_idc_t profile;
		level_idc_t level;
		tsk_params_L_t* params;

		/* Check whether the profile match (If the profile is missing, then we consider that it's ok) */
		if(tdav_codec_h264_get_profile_and_level(att_value, &profile, &level) != 0){
			TSK_DEBUG_ERROR("Not valid profile-level: %s", att_value);
			return tsk_false;
		}
		if(TDAV_CODEC_H264_COMMON(codec)->profile != profile){
			return tsk_false;
		}
		else{
			if(TDAV_CODEC_H264_COMMON(codec)->level != level){
				unsigned width, height;
				TDAV_CODEC_H264_COMMON(codec)->level = TSK_MIN(TDAV_CODEC_H264_COMMON(codec)->level, level);
				if(tdav_codec_h264_common_size_from_level(TDAV_CODEC_H264_COMMON(codec)->level, &width, &height) != 0){
					return tsk_false;
				}
				TMEDIA_CODEC_VIDEO(codec)->in.width = TMEDIA_CODEC_VIDEO(codec)->out.width = width;
				TMEDIA_CODEC_VIDEO(codec)->in.height = TMEDIA_CODEC_VIDEO(codec)->out.height = height;
			}
		}

		/* e.g. profile-level-id=42e00a; packetization-mode=1; max-br=452; max-mbps=11880 */
		if((params = tsk_params_fromstring(att_value, ";", tsk_true))){

			/* === max-br ===*/
			if((val_int = tsk_params_get_param_value_as_int(params, "max-br")) != -1){
				// should compare "max-br"?
				TMEDIA_CODEC_VIDEO(h264)->out.max_br = val_int*MUL_FACTOR;
			}

			/* === max-mbps ===*/
			if((val_int = tsk_params_get_param_value_as_int(params, "max-mbps")) != -1){
				// should compare "max-mbps"?
				TMEDIA_CODEC_VIDEO(h264)->out.max_mbps = val_int*MUL_FACTOR;
			}

			/* === packetization-mode ===*/
			if((val_int = tsk_params_get_param_value_as_int(params, "packetization-mode")) != -1){
				if((packetization_mode_t)val_int == Single_NAL_Unit_Mode || (packetization_mode_t)val_int == Non_Interleaved_Mode){
					TDAV_CODEC_H264_COMMON(h264)->pack_mode = (packetization_mode_t)val_int;
				}
				else{
					TSK_DEBUG_INFO("packetization-mode not matching");
					ret = tsk_false;
					goto bail;
				}
			}
		}
bail:
		TSK_OBJECT_SAFE_FREE(params);
	}
	else if(tsk_striequals(att_name, "imageattr")){
		unsigned in_width, in_height, out_width, out_height;
		unsigned width, height;
		tsk_size_t s;
		if(tmedia_parse_video_imageattr(att_value, TMEDIA_CODEC_VIDEO(codec)->pref_size, &in_width, &in_height, &out_width, &out_height) != 0){
			return tsk_false;
		}
		// check that 'imageattr' is comform to H.264 'profile-level'
		if(tdav_codec_h264_common_size_from_level(TDAV_CODEC_H264_COMMON(codec)->level, &width, &height) != 0){
			return tsk_false;
		}
		if((s = ((width * height * 3) >> 1)) < ((in_width * in_height * 3) >> 1) || s < ((out_width * out_height * 3) >> 1)){
			return tsk_false;
		}

		TMEDIA_CODEC_VIDEO(codec)->in.width = in_width;
		TMEDIA_CODEC_VIDEO(codec)->in.height = in_height;
		TMEDIA_CODEC_VIDEO(codec)->out.width = out_width;
		TMEDIA_CODEC_VIDEO(codec)->out.height = out_height;
	}

	return ret;
}

static char* tdav_codec_h264_hw_vpu_sdp_att_get(const tmedia_codec_t* self, const char* att_name)
{
	tdav_codec_h264_hw_vpu_t* h264 = (tdav_codec_h264_hw_vpu_t*)self;

	if(!h264 || !att_name){
		TSK_DEBUG_ERROR("Invalid parameter");
		return tsk_null;
	}

	if(tsk_striequals(att_name, "fmtp")){
		char* fmtp = tsk_null;
#if 1
		tsk_sprintf(&fmtp, "profile-level-id=%x; packetization-mode=%d", ((TDAV_CODEC_H264_COMMON(h264)->profile << 16) | TDAV_CODEC_H264_COMMON(h264)->level), TDAV_CODEC_H264_COMMON(h264)->pack_mode);
#else
		tsk_strcat_2(&fmtp, "profile-level-id=%s; packetization-mode=%d; max-br=%d; max-mbps=%d",
			profile_level, TDAV_CODEC_H264_COMMON(h264)->pack_mode, TMEDIA_CODEC_VIDEO(h264)->in.max_br/MUL_FACTOR, TMEDIA_CODEC_VIDEO(h264)->in.max_mbps/MUL_FACTOR);
#endif
		return fmtp;
	}
	else if(tsk_striequals(att_name, "imageattr")){
		return tmedia_get_video_imageattr(TMEDIA_CODEC_VIDEO(self)->pref_size,
			TMEDIA_CODEC_VIDEO(self)->in.width, TMEDIA_CODEC_VIDEO(self)->in.height, TMEDIA_CODEC_VIDEO(self)->out.width, TMEDIA_CODEC_VIDEO(self)->out.height);
	}
	return tsk_null;
}

static int _tdav_session_h264_timer_cb(const void* arg, tsk_timer_id_t timer_id)
{
    int ret = 0;
    tdav_codec_h264_hw_vpu_t* h264 = (tdav_codec_h264_hw_vpu_t*)arg;

    if (!h264) {
        TSK_DEBUG_ERROR("Invalid h264 parameter");
        return ret;
    }

    if (timer_id == h264->timer_idle.id) {
        // Change OMX Codec state to PAUSE if it is still not active after timout period
        if (!h264->encoder.enc_started) {
            TSK_DEBUG_INFO("Setting encoder OMX state to Pause");
            ret = EncStateTrans(h264, OMX_StatePause);
            if (ret != OMX_ErrorNone) {
                TSK_DEBUG_ERROR("Unable to do encoder state trans to Pause");
            }
        }
        if (!h264->decoder.dec_started) {
            TSK_DEBUG_INFO("Setting decoder OMX state to Pause");
            ret = StateTrans(h264, OMX_StatePause);
            if (ret != OMX_ErrorNone) {
                TSK_DEBUG_ERROR("Unable to do decoder state trans to Pause");
            }
        }

        // Stop timer
        if (h264->h_timer) {
            tsk_timer_manager_stop(h264->h_timer);
            h264->timer_started = tsk_false;
            h264->h_timer = tsk_null;
        }
    } else if (timer_id == h264->timer_reset_dec.id) {
        TSK_DEBUG_INFO("reset decoder timer");
        reset_decoder(h264);

        // Stop timer
        if (h264->h_timer) {
            tsk_timer_manager_stop(h264->h_timer);
            h264->timer_started = tsk_false;
            h264->h_timer = tsk_null;
        }
    } else {
        TSK_DEBUG_WARN("Invalid timer_id");
    }

    return ret;
}

/* ============ H.264 Base Profile Plugin interface ================= */

/* constructor */
static tsk_object_t* tdav_codec_h264_hw_vpu_base_ctor(tsk_object_t * self, va_list * app)
{
tdav_codec_h264_hw_vpu_t *h264 = (tdav_codec_h264_hw_vpu_t*)self;
	if(h264){
		/* init base: called by tmedia_codec_create() */
		/* init self */
		if(tdav_codec_h264_init(h264, profile_idc_baseline) != 0){
			return tsk_null;
		}
	}
	return self;
}

/* destructor */
static tsk_object_t* tdav_codec_h264_hw_vpu_base_dtor(tsk_object_t * self)
{
	tdav_codec_h264_hw_vpu_t *h264 = (tdav_codec_h264_hw_vpu_t*)self;
	if(h264){
		/* deinit base */
		tdav_codec_h264_common_deinit(TDAV_CODEC_H264_COMMON(self));
		/* deinit self */
		tdav_codec_h264_deinit(h264);
	}

	return self;
}

/* object definition */
static const tsk_object_def_t tdav_codec_h264_hw_vpu_base_def_s =
{
        sizeof(tdav_codec_h264_hw_vpu_t),
        tdav_codec_h264_hw_vpu_base_ctor,
        tdav_codec_h264_hw_vpu_base_dtor,
        tmedia_codec_cmp,
};

/* plugin definition*/
static const tmedia_codec_plugin_def_t tdav_codec_h264_hw_vpu_base_plugin_def_s =
{
        &tdav_codec_h264_hw_vpu_base_def_s,

        tmedia_video,
        tmedia_codec_id_h264_bp,
        "H264",
        "H264 Base Profile",
        TMEDIA_CODEC_FORMAT_H264_BP,
        tsk_true,
        90000, // rate

        /* audio */
        { 0 },

        /* video */
        {176, 144, 15},

        tdav_codec_h264_hw_vpu_set,
        tdav_codec_h264_hw_vpu_open,
        tdav_codec_h264_hw_vpu_close,
        tdav_codec_h264_hw_vpu_encode,
        tdav_codec_h264_hw_vpu_decode,
        tdav_codec_h264_hw_vpu_sdp_att_match,
        tdav_codec_h264_hw_vpu_sdp_att_get,
};
extern const tmedia_codec_plugin_def_t *tdav_codec_h264_hw_vpu_base_plugin_def_t = &tdav_codec_h264_hw_vpu_base_plugin_def_s;

/* ============ H.264 Main Profile Plugin interface ================= */

/* constructor */
static tsk_object_t* tdav_codec_h264_hw_vpu_main_ctor(tsk_object_t * self, va_list * app)
{
	tdav_codec_h264_hw_vpu_t *h264 = (tdav_codec_h264_hw_vpu_t*)self;
	if(h264){
		/* init base: called by tmedia_codec_create() */
		/* init self */
		if(tdav_codec_h264_init(h264, profile_idc_main) != 0){
			return tsk_null;
		}
	}
	return self;
}
/* destructor */
static tsk_object_t* tdav_codec_h264_hw_vpu_main_dtor(tsk_object_t * self)
{
        TSK_DEBUG_INFO("tdav_codec_h264_hw_vpu_main_dtor IN \n");
	tdav_codec_h264_hw_vpu_t *h264 = (tdav_codec_h264_hw_vpu_t*)self;
	if(h264){
		/* deinit base */
		tdav_codec_h264_common_deinit(TDAV_CODEC_H264_COMMON(self));
		/* deinit self */
		tdav_codec_h264_deinit(h264);
	}

        TSK_DEBUG_INFO("tdav_codec_h264_hw_vpu_main_dtor OUT \n");
	return self;
}

/* object definition */
static const tsk_object_def_t tdav_codec_h264_hw_vpu_main_def_s =
{
	sizeof(tdav_codec_h264_hw_vpu_t),
	tdav_codec_h264_hw_vpu_main_ctor,
	tdav_codec_h264_hw_vpu_main_dtor,
	tmedia_codec_cmp,
};
/* plugin definition*/
static const tmedia_codec_plugin_def_t tdav_codec_h264_hw_vpu_main_plugin_def_s = 
{
	&tdav_codec_h264_hw_vpu_main_def_s,

	tmedia_video,
	tmedia_codec_id_h264_mp,
	"H264",
	"H264 Main Profile",
	TMEDIA_CODEC_FORMAT_H264_MP,
	tsk_true,
	90000, // rate

	/* audio */
	{ 0 },

	/* video */
	{176, 144, 15},

	tdav_codec_h264_hw_vpu_set,
	tdav_codec_h264_hw_vpu_open,
	tdav_codec_h264_hw_vpu_close,
	tdav_codec_h264_hw_vpu_encode,
	tdav_codec_h264_hw_vpu_decode,
	tdav_codec_h264_hw_vpu_sdp_att_match,
	tdav_codec_h264_hw_vpu_sdp_att_get
};
const tmedia_codec_plugin_def_t *tdav_codec_h264_hw_vpu_main_plugin_def_t = &tdav_codec_h264_hw_vpu_main_plugin_def_s;

/* ============ Common To all H264 codecs ================= */

int tdav_codec_h264_open_encoder(tdav_codec_h264_hw_vpu_t* self)
{
#if HAVE_FFMPEG
	int ret;
	tsk_size_t size;

	if(self->encoder.context || self->encoder.hComponent){
		TSK_DEBUG_ERROR("Encoder already opened");
		return -1;
	}

    if((self->encoder.context = avcodec_alloc_context())){
        avcodec_get_context_defaults(self->encoder.context);
    }

    if(!self->encoder.context){
        TSK_DEBUG_ERROR("Failed to allocate context");
        return -1;
    }

#if TDAV_UNDER_X86 && LIBAVCODEC_VERSION_MAJOR <= 53
	self->encoder.context->dsp_mask = (FF_MM_MMX | FF_MM_MMXEXT | FF_MM_SSE);
#endif
	self->encoder.context->pix_fmt		= PIX_FMT_YUV420P;
	self->encoder.context->time_base.num  = TIME_BASE_NUM;
	self->encoder.context->time_base.den  = TMEDIA_CODEC_VIDEO(self)->out.fps;
	self->encoder.context->width = (self->encoder.rotation == ROTATION_90 || self->encoder.rotation == ROTATION_270) ? TMEDIA_CODEC_VIDEO(self)->out.height : TMEDIA_CODEC_VIDEO(self)->out.width;
	self->encoder.context->height = (self->encoder.rotation == ROTATION_90 || self->encoder.rotation == ROTATION_270) ? TMEDIA_CODEC_VIDEO(self)->out.width : TMEDIA_CODEC_VIDEO(self)->out.height;

	self->encoder.context->bit_rate = ((TMEDIA_CODEC_VIDEO(self)->out.width * TMEDIA_CODEC_VIDEO(self)->out.height * BIT_RATE_CAL_PARAM1 / BIT_RATE_CAL_PARAM2 / BIT_RATE_CAL_PARAM3) * MUL_FACTOR);
	self->encoder.context->rc_min_rate = (self->encoder.context->bit_rate >> 3);
	self->encoder.context->rc_max_rate = self->encoder.context->bit_rate;

#if LIBAVCODEC_VERSION_MAJOR <= 53
	self->encoder.context->rc_lookahead = 0;
#endif
	self->encoder.context->global_quality = FF_QP2LAMBDA * self->encoder.quality;

	self->encoder.context->scenechange_threshold = 0;
        self->encoder.context->me_subpel_quality = 0;
#if LIBAVCODEC_VERSION_MAJOR <= 53
        self->encoder.context->partitions = X264_PART_I4X4 | X264_PART_I8X8 | X264_PART_P8X8 | X264_PART_B8X8;
#endif
        self->encoder.context->me_method = ME_EPZS;
        self->encoder.context->trellis = 0;

	self->encoder.context->me_range = 16;
	self->encoder.context->qmin = 10;
	self->encoder.context->qmax = 51;
#if LIBAVCODEC_VERSION_MAJOR <= 53
        self->encoder.context->mb_qmin = self->encoder.context->qmin;
	self->encoder.context->mb_qmax = self->encoder.context->qmax;
#endif
	self->encoder.context->qcompress = 0.6f;
	self->encoder.context->mb_decision = FF_MB_DECISION_SIMPLE;
#if LIBAVCODEC_VERSION_MAJOR <= 53
	self->encoder.context->flags2 |= CODEC_FLAG2_FASTPSKIP;
#else
        self->encoder.context->flags2 |= CODEC_FLAG2_FAST;
#endif
	self->encoder.context->flags |= CODEC_FLAG_LOOP_FILTER;
	self->encoder.context->flags |= CODEC_FLAG_GLOBAL_HEADER;
        self->encoder.context->flags |= CODEC_FLAG_LOW_DELAY;
	self->encoder.context->max_b_frames = 0;
	self->encoder.context->b_frame_strategy = 1;
	self->encoder.context->chromaoffset = 0;

	switch(TDAV_CODEC_H264_COMMON(self)->profile){
		case profile_idc_baseline:
		default:
			self->encoder.context->profile = FF_PROFILE_H264_BASELINE;
			self->encoder.context->level = TDAV_CODEC_H264_COMMON(self)->level;
                        self->encoder.mProfile = OMX_VIDEO_AVCProfileBaseline;
                        self->encoder.mLevel = (OMX_VIDEO_AVCLEVELTYPE)TDAV_CODEC_H264_COMMON(self)->level;
			break;
		case profile_idc_main:
			self->encoder.context->profile = FF_PROFILE_H264_MAIN;
			self->encoder.context->level = TDAV_CODEC_H264_COMMON(self)->level;
                        self->encoder.mProfile = OMX_VIDEO_AVCProfileMain;
                        self->encoder.mLevel = (OMX_VIDEO_AVCLEVELTYPE)TDAV_CODEC_H264_COMMON(self)->level;
			break;
	}

	self->encoder.context->rtp_payload_size = H264_RTP_PAYLOAD_SIZE;
	self->encoder.context->opaque = tsk_null;
	self->encoder.context->gop_size = (TMEDIA_CODEC_VIDEO(self)->out.fps * TDAV_H264_GOP_SIZE_IN_SECONDS);

#if (LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(51, 35, 0))
    if((ret = av_opt_set_int(self->encoder.context->priv_data, "slice-max-size", H264_RTP_PAYLOAD_SIZE, 0))){
	    TSK_DEBUG_ERROR("Failed to set x264 slice-max-size to %d", H264_RTP_PAYLOAD_SIZE);
	}
    if((ret = av_opt_set(self->encoder.context->priv_data, "profile", (self->encoder.context->profile == FF_PROFILE_H264_BASELINE ? "baseline" : "main"), 0))){
	    TSK_DEBUG_ERROR("Failed to set x264 profile");
	}
    if((ret = av_opt_set(self->encoder.context->priv_data, "preset", "veryfast", 0))){
	    TSK_DEBUG_ERROR("Failed to set x264 preset to veryfast");
	}
    if((ret = av_opt_set_int(self->encoder.context->priv_data, "rc-lookahead", 0, 0)) && (ret = av_opt_set_int(self->encoder.context->priv_data, "rc_lookahead", 0, 0))){
        TSK_DEBUG_ERROR("Failed to set x264 rc_lookahead=0");
    }
	if((ret = av_opt_set(self->encoder.context->priv_data, "tune", "animation+zerolatency", 0))){
	    TSK_DEBUG_ERROR("Failed to set x264 tune to zerolatency");
	}
#endif


	size = avpicture_get_size(PIX_FMT_YUV420P, self->encoder.context->width, self->encoder.context->height);

        self->encoder.decoded_first_frame = OMX_FALSE;
        self->encoder.encoded_first_frame = OMX_FALSE;

        if (!set_bitrate_fac_GOP_size(self)) {
            return -1;
        }

        TSK_DEBUG_INFO("open_encoder: bitrate factor %d, gop %d",
                        self->encoder.bitrate_fact, self->encoder.gop_size);

        // Open hw_vpu encoder
        ret = hw_vpu_open_encoder(self);
        if (ret < 0) {
            TSK_DEBUG_ERROR("Error opening encoder, ret code %d", ret);
            return -1;
        }

        if (!self->disable_dummy_buffer) {
            // Send width and height without alignment to match OMX buf len.
            OMX_S32 width = TMEDIA_CODEC_VIDEO(self)->in.width;
            OMX_S32 height = TMEDIA_CODEC_VIDEO(self)->in.height;

            size = width * height * 3 / 2;
            if ((self->encoder.dummy_decode_buffer = tsk_calloc(size, sizeof(uint8_t)))) {
                self->encoder.dummy_decode_buffer_size = size;
                //start encoding asap with dummy buffer
                /* decode the picture */
                AVPacket packet;
                av_init_packet(&packet);
                packet.size = self->encoder.dummy_decode_buffer_size;
                packet.data = (uint8_t*)self->encoder.dummy_decode_buffer;
                TSK_DEBUG_INFO("Sending dummy decode buffer at encoder open");
                hw_vpu_encode(self, &packet);
            }
        }
	return ret;
#elif HAVE_H264_PASSTHROUGH
	return 0;
#endif

	TSK_DEBUG_ERROR("Not expected code called");
	return -1;
}

int tdav_codec_h264_close_encoder(tdav_codec_h264_hw_vpu_t* self)
{
#if HAVE_FFMPEG
	if(self->encoder.context){
		//avcodec_close(self->encoder.context);
		av_free(self->encoder.context);
		self->encoder.context = tsk_null;
	}
#endif
       if(self->encoder.dummy_buffer){
           TSK_FREE(self->encoder.dummy_buffer);
       }
       if(self->encoder.dummy_decode_buffer){
           TSK_FREE(self->encoder.dummy_decode_buffer);
       }

	self->encoder.frame_count = 0;

        // close vpu encoder
        if (self->encoder.hComponent) {
            close_hw_vpu_encoder(self);
            self->encoder.hComponent = NULL;
        }

	return 0;
}

int tdav_codec_h264_open_decoder(tdav_codec_h264_hw_vpu_t* self)
{

    //TSK_DEBUG_INFO("tdav_codec_h264_open_decoder in\n");
#if HAVE_FFMPEG
	int ret;

	if(self->decoder.context || self->decoder.hComponent){
		TSK_DEBUG_ERROR("Decoder already opened");
		return -1;
	}

	self->decoder.context = avcodec_alloc_context();
	avcodec_get_context_defaults(self->decoder.context);

	self->decoder.context->pix_fmt = PIX_FMT_YUV420P;
	self->decoder.context->flags2 |= CODEC_FLAG2_FAST;
	self->decoder.context->width = Align(TMEDIA_CODEC_VIDEO(self)->in.width, FRAME_ALIGN);//(TMEDIA_CODEC_VIDEO(self)->in.width + 31) & (~31);
	self->decoder.context->height = Align(TMEDIA_CODEC_VIDEO(self)->in.height, FRAME_ALIGN);//(TMEDIA_CODEC_VIDEO(self)->in.height + 31) & (~31);

#if TDAV_UNDER_WINDOWS
//	self->decoder.context->dsp_mask = (FF_MM_MMX | FF_MM_MMXEXT | FF_MM_SSE);
#endif


        self->decoder.decode_frame_size = self->decoder.context->width * self->decoder.context->height * 3 / 2;
        TSK_DEBUG_INFO("open_decoder width, height (%d, %d),dec size %d\n",
                             self->decoder.context->width, self->decoder.context->height, self->decoder.decode_frame_size);

        if (!(self->decoder.crop_buffer = tsk_calloc(self->decoder.decode_frame_size, sizeof(uint8_t)))) {
            TSK_DEBUG_ERROR("Failed to allocate decoder crop buffer");
            return -2;
        }

        // Open hw_vpu decoder
        ret = hw_vpu_open_decoder(self);
        if (ret < 0) {
            TSK_DEBUG_ERROR("Error opening decoder, ret code %d", ret);
            return -1;
        }

        if (!self->disable_dummy_buffer) {
            OMX_S32 frame_len = 0;
            int height = TMEDIA_CODEC_VIDEO(self)->in.height;
            const char* path = tsk_null;

            if (height == HT_1080P) {
                path = DUMMY_1080P_FRAME_PATH;
            } else if (height == HT_720P) {
                path = DUMMY_720P_FRAME_PATH;
            } else if (height == HT_VGA) {
                path = DUMMY_VGA_FRAME_PATH;
            } else if (height == HT_QVGA) {
                path = DUMMY_QVGA_FRAME_PATH;
            }

            TSK_DEBUG_INFO("open_decoder: File path %s", path);
            if (path != tsk_null) {
                FILE *fp = fopen(path, "rb");
                if (fp != NULL) {
                    fseek(fp, 0L, SEEK_END);
                    frame_len = ftell(fp);
                    fseek(fp, 0L, SEEK_SET);
                    TSK_DEBUG_INFO("frame_len %d", frame_len);
                    if(!(self->encoder.dummy_buffer = tsk_calloc(frame_len, sizeof(uint8_t)))){
                        TSK_DEBUG_ERROR("Failed to allocate encoder buffer, can be ignred");
                        fclose(fp);
                        return ret;
                    }
                    self->encoder.dummy_buffer_size = fread(self->encoder.dummy_buffer, 1, frame_len, fp);
                    fclose(fp);

                    AVPacket packet;
                    /* decode the picture */
                    av_init_packet(&packet);
                    packet.size = self->encoder.dummy_buffer_size;
                    packet.data = (uint8_t*)self->encoder.dummy_buffer;

                    TSK_DEBUG_INFO("sending dummy encoded frame with size %d", self->encoder.dummy_buffer_size);
                    ret = hw_vpu_decode(self, &packet);
                } else {
                    TSK_DEBUG_ERROR("Failed to open file at %s with %d", path, errno);
                }
            }
        }
        //TSK_DEBUG_INFO("open_decoder out\n");
	return ret;

#elif HAVE_H264_PASSTHROUGH
	return 0;
#endif

	TSK_DEBUG_ERROR("Unexpected code called");
	return -1;
}

int tdav_codec_h264_close_decoder(tdav_codec_h264_hw_vpu_t* self)
{
        //TSK_DEBUG_INFO("tdav_codec_h264_close_decoder IN \n");
#if HAVE_FFMPEG
	if(self->decoder.context){
		//avcodec_close(self->decoder.context);
		av_free(self->decoder.context);
		self->decoder.context = tsk_null;
	}

#endif
        if (self->decoder.hComponent) {
            close_hw_vpu_decoder(self);
            self->decoder.hComponent = NULL;
        }

        if (self->decoder.accumulator) {
	    TSK_FREE(self->decoder.accumulator);
	    self->decoder.accumulator_pos = 0;
        }

	if(self->decoder.crop_buffer){
            TSK_FREE(self->decoder.crop_buffer);
	}

        TSK_DEBUG_INFO("tdav_codec_h264_close_decoder OUT \n");
	return 0;
}

int tdav_codec_h264_init(tdav_codec_h264_hw_vpu_t* self, profile_idc_t profile)
{
	int ret = 0;
	level_idc_t level;

	if(!self){
		TSK_DEBUG_ERROR("Invalid parameter");
		return -1;
	}

	if((ret = tdav_codec_h264_common_init(TDAV_CODEC_H264_COMMON(self)))){
		TSK_DEBUG_ERROR("tdav_codec_h264_common_init() faile with error code=%d", ret);
		return ret;
	}

	if((ret = tdav_codec_h264_common_level_from_size(TMEDIA_CODEC_VIDEO(self)->out.width, TMEDIA_CODEC_VIDEO(self)->out.height, &level))){
		TSK_DEBUG_ERROR("Failed to find level for size=[%u, %u]", TMEDIA_CODEC_VIDEO(self)->out.width, TMEDIA_CODEC_VIDEO(self)->out.height);
		return ret;
	}

	TDAV_CODEC_H264_COMMON(self)->pack_mode = H264_PACKETIZATION_MODE;
	TDAV_CODEC_H264_COMMON(self)->profile = profile;
	TDAV_CODEC_H264_COMMON(self)->level = level;
	TMEDIA_CODEC_VIDEO(self)->in.max_mbps = TMEDIA_CODEC_VIDEO(self)->out.max_mbps = H264_MAX_MBPS*MUL_FACTOR;
	TMEDIA_CODEC_VIDEO(self)->in.max_br = TMEDIA_CODEC_VIDEO(self)->out.max_br = H264_MAX_BR*MUL_FACTOR;


#if HAVE_H264_PASSTHROUGH
	TMEDIA_CODEC(self)->passthrough = tsk_true;
#endif

	self->encoder.quality = 1;

	/* allocations MUST be done by open() */
	return ret;
}

int tdav_codec_h264_deinit(tdav_codec_h264_hw_vpu_t* self)
{
	if(!self){
		TSK_DEBUG_ERROR("Invalid parameter");
		return -1;
	}

	return 0;
}

static void tdav_codec_h264_encap(const tdav_codec_h264_hw_vpu_t* h264, const uint8_t* pdata, tsk_size_t size)
{
	static const tsk_size_t size_of_scp = sizeof(H264_START_CODE_PREFIX); /* we know it's equal to 4 .. */
	register tsk_size_t i;
	tsk_size_t last_scp, prev_scp;
	tsk_size_t _size;

	if(!pdata || size < size_of_scp){
		return;
	}

	if(pdata[0] == 0 && pdata[1] == 0){
		if(pdata[2] == 1){
			pdata += 3, size -= 3;
		}
		else if(pdata[2] == 0 && pdata[3] == 1){
			pdata += 4, size -= 4;
		}
	}

	_size = (size - size_of_scp);
	last_scp = 0, prev_scp = 0;
	for(i = size_of_scp; i<_size; i++){
		if(pdata[i] == 0 && pdata[i+1] == 0 && (pdata[i+2] == 1 || (pdata[i+2] == 0 && pdata[i+3] == 1))){  /* Find Start Code Prefix */
			prev_scp = last_scp;
			if((i - last_scp) >= H264_RTP_PAYLOAD_SIZE || 1){
				tdav_codec_h264_rtp_callback(TDAV_CODEC_H264_COMMON(h264), pdata + prev_scp,
					(i - prev_scp), (prev_scp == size));
			}
			last_scp = i;
			i += (pdata[i+2] == 1) ? 3 : 4;
		}
	}

	if(last_scp < (int32_t)size){
			tdav_codec_h264_rtp_callback(TDAV_CODEC_H264_COMMON(h264), pdata + last_scp,
				(size - last_scp), tsk_true);
	}
}

//OMX code
OMX_ERRORTYPE read_enc_frame(tdav_codec_h264_hw_vpu_t *h264, OMX_BUFFERHEADERTYPE *pBufferHdr)
{
    //TSK_DEBUG_INFO("read_enc_frame:");
    OMX_U32 size = 0;
    OMX_S32 frame_len = 0;

    if ((!h264 || !h264->encoder.in_ring_buf) || (!pBufferHdr || !pBufferHdr->pBuffer)) {
        TSK_DEBUG_ERROR("read_enc_frame: invalid parameter");
        return OMX_ErrorUndefined;
    }

    if (!h264->encoder.bThreadRunning) {
        TSK_DEBUG_ERROR("read_enc_frame: thread exited\n");
        return OMX_ErrorNone;
    }

    if (h264->encoder.in_ring_buf->count > 0) {
        int size = pBufferHdr->nAllocLen;
        bool realloc = tsk_false;// Don't allow to re-alloc OMX buffer

        TSK_DEBUG_INFO("read_enc_frame: pop enc i/p buffer");
        ring_buffer_pop(h264->encoder.in_ring_buf, (void*) pBufferHdr->pBuffer, &size, realloc);
        if (size > 0) {
            pBufferHdr->nFilledLen = size;
            pBufferHdr->nFlags = OMX_BUFFERFLAG_ENDOFFRAME;
        } else {
            pBufferHdr->nFlags = OMX_BUFFERFLAG_EOS;
            h264->encoder.bInEos = OMX_TRUE;
            TSK_DEBUG_INFO("Input EOS.\n");
        }
        //TSK_DEBUG_INFO("read_enc_frame: got frame with size %d after %d tries",
        //                pBufferHdr->nFilledLen, h264->encoder.inBufEmptyCount);
        h264->encoder.inBufEmptyCount = 0;
    } else if (h264->encoder.encoded_first_frame == OMX_FALSE) {
            if (h264->encoder.dummy_decode_buffer != NULL && h264->encoder.dummy_decode_buffer_size > 0) {
                TSK_DEBUG_INFO("read_enc_frame: sending the same frame");
                memcpy(pBufferHdr->pBuffer, h264->encoder.dummy_decode_buffer, h264->encoder.dummy_decode_buffer_size);

                pBufferHdr->nFilledLen = h264->encoder.dummy_decode_buffer_size;
                pBufferHdr->nFlags = OMX_BUFFERFLAG_ENDOFFRAME;
            }
    } else {
       if(h264->encoder.dummy_decode_buffer){
           TSK_FREE(h264->encoder.dummy_decode_buffer);
       }
        pBufferHdr->nFilledLen = 0;
        h264->encoder.inBufEmptyCount++;
    }

    return OMX_ErrorNone;
}

OMX_ERRORTYPE read_frame(tdav_codec_h264_hw_vpu_t *h264, OMX_BUFFERHEADERTYPE *pBufferHdr)
{
    OMX_U32 size = 0;
    OMX_S32 frame_len = 0;

    if ((!h264 || !h264->decoder.in_ring_buf) || (!pBufferHdr || !pBufferHdr->pBuffer)) {
        TSK_DEBUG_ERROR("read_frame: invalid parameter");
        return OMX_ErrorUndefined;
    }

    if (!h264->decoder.bThreadRunning) {
        TSK_DEBUG_ERROR("read_frame: thread exited\n");
        return OMX_ErrorNone;
    }

    pBufferHdr->nFilledLen = 0;
    pBufferHdr->nOffset = 0;

    if (h264->decoder.in_ring_buf->count > 0) {
        int size = pBufferHdr->nAllocLen;
        bool realloc = tsk_false;// Don't allow to re-alloc OMX buffer

        ring_buffer_pop(h264->decoder.in_ring_buf, (void*) pBufferHdr->pBuffer, &size, realloc);
        if (size > 0) {
            pBufferHdr->nFilledLen = size;
            pBufferHdr->nFlags = OMX_BUFFERFLAG_ENDOFFRAME;
        } else {
            pBufferHdr->nFlags = OMX_BUFFERFLAG_EOS;
            h264->decoder.bInEos = OMX_TRUE;
            TSK_DEBUG_WARN("Input EOS.\n");
        }
        //TSK_DEBUG_INFO("read_frame: got frame with size %d after %d tries",
        //                pBufferHdr->nFilledLen, h264->decoder.inBufEmptyCount);
        h264->decoder.inBufEmptyCount = 0;
    } else if (h264->encoder.decoded_first_frame == OMX_FALSE) {
            if (h264->encoder.dummy_buffer != NULL && h264->encoder.dummy_buffer_size > 0) {
                TSK_DEBUG_INFO("read_frame: sending the same frame");
                memcpy(pBufferHdr->pBuffer, h264->encoder.dummy_buffer, h264->encoder.dummy_buffer_size);

                pBufferHdr->nFilledLen = h264->encoder.dummy_buffer_size;
                pBufferHdr->nFlags = OMX_BUFFERFLAG_ENDOFFRAME;
            }
    } else {
       if(h264->encoder.dummy_buffer){
           TSK_FREE(h264->encoder.dummy_buffer);
       }
        pBufferHdr->nFilledLen = 0;
        h264->decoder.inBufEmptyCount++;
        TSK_DEBUG_INFO("No data available for decoder i/p port");
    }
    return OMX_ErrorNone;
}

OMX_ERRORTYPE write_enc_data(tdav_codec_h264_hw_vpu_t *h264, OMX_BUFFERHEADERTYPE *pBufferHdr)
{
    if ((!h264) || (!pBufferHdr || !pBufferHdr->pBuffer)) {
        TSK_DEBUG_ERROR("write_enc_data: Invalid parameter");
        return OMX_ErrorUndefined;
    }

    //TSK_DEBUG_INFO("write_enc_data: pBufferHdr->nFilledLen %d\n", pBufferHdr->nFilledLen);

    if (!h264->encoder.bThreadRunning) {
        TSK_DEBUG_WARN("Encoder thread is not running");
        return OMX_ErrorNone;
    }

    if (pBufferHdr->nFilledLen > 0) {
        h264->encoder.encoded_first_frame = OMX_TRUE;

#ifdef VIDEO_DUMP
    if (h264->video_dump_to_file) {
        if (fpEncodedBuffer)
            fwrite((void*)pBufferHdr->pBuffer, pBufferHdr->nFilledLen, 1, fpEncodedBuffer);
    }
#endif
        tdav_codec_h264_encap(h264, (uint8_t*)pBufferHdr->pBuffer, pBufferHdr->nFilledLen);
        h264->encoder.encPacketLoss++;
        pBufferHdr->nFilledLen = 0;
        pBufferHdr->nOffset = 0;

        if(pBufferHdr->nFlags & OMX_BUFFERFLAG_EOS) {
            TSK_DEBUG_INFO("Output EOS.\n");
        }
        return OMX_ErrorNone;
    }

    pBufferHdr->nFilledLen = 0;
    pBufferHdr->nOffset = 0;

    if(pBufferHdr->nFlags & OMX_BUFFERFLAG_EOS) {
        TSK_DEBUG_INFO("Output EOS.\n");
    }
    return OMX_ErrorNone;
}

OMX_ERRORTYPE write_data(tdav_codec_h264_hw_vpu_t *h264, OMX_BUFFERHEADERTYPE *pBufferHdr)
{
    if ((!h264 || !h264->decoder.in_ring_buf) || (!pBufferHdr || !pBufferHdr->pBuffer)) {
        TSK_DEBUG_ERROR("write_data: Invalid parameter");
        return OMX_ErrorUndefined;
    }

    //TSK_DEBUG_INFO("write data: pBufferHdr->nFilledLen %d\n", pBufferHdr->nFilledLen);

    if (!h264->decoder.bThreadRunning) {
        TSK_DEBUG_WARN("Decoder thread is not running");
        return OMX_ErrorNone;
    }

    // Buffer will be invalid, if dynamic port setting is going on.
    if (h264->decoder.bHoldBuffers) {
        TSK_DEBUG_WARN("write_data: Dynamic port setting is going on, buffer will be invalid here");
        pBufferHdr->nFilledLen = 0;
        pBufferHdr->nOffset = 0;
        return OMX_ErrorNone;
    }

    OMX_PTR pBufferOrig = pBufferHdr->pBuffer;
    OMX_U32 size = pBufferHdr->nFilledLen;

    if (size > 0) {
        h264->encoder.decoded_first_frame = OMX_TRUE;
        if ((h264->decoder.context->width != TMEDIA_CODEC_VIDEO(h264)->in.width) ||
            (h264->decoder.context->height != TMEDIA_CODEC_VIDEO(h264)->in.height)) {
            uint8_t *srcPtr = (uint8_t*)pBufferOrig;
            uint8_t *cropPtr = (uint8_t*)h264->decoder.crop_buffer;

            if (cropPtr) {
                // Process buffer and copy to crop_buffer
                ProcessBufferForCrop(h264, srcPtr, cropPtr, size);
                ring_buffer_push(h264->decoder.out_ring_buf, (void*)cropPtr, size);
                pBufferOrig = (OMX_PTR)cropPtr;
            } else {
                ring_buffer_push(h264->decoder.out_ring_buf, (void*)srcPtr, size);
            }
        } else {
            ring_buffer_push(h264->decoder.out_ring_buf, pBufferOrig, size);
        }

#ifdef VIDEO_DUMP
    if (h264->video_dump_to_file) {
        // Use this for short duration calls as this could become huge dump size
        if (fpDecodedBuffer)
            fwrite(pBufferOrig, size, 1, fpDecodedBuffer);
    }
#endif
    }
    pBufferHdr->nFilledLen = 0;
    pBufferHdr->nOffset = 0;
    return OMX_ErrorNone;
}

OMX_ERRORTYPE gm_EncEventHandler(
        OMX_IN OMX_HANDLETYPE hComponent,
        OMX_IN OMX_PTR pAppData,
        OMX_IN OMX_EVENTTYPE eEvent,
        OMX_IN OMX_U32 nData1,
        OMX_IN OMX_U32 nData2,
        OMX_IN OMX_PTR pEventData)
{
    MSG sMsg;
    tdav_codec_h264_hw_vpu_t *h264 = (tdav_codec_h264_hw_vpu_t*)pAppData;

    //TSK_DEBUG_ERROR("Client receive event [eEvent:%d, nData1: %d, nData2: %d]\n",
    //        (int)eEvent, (int)nData1, (int)nData2);

    if(eEvent == OMX_EventCmdComplete) {
        h264->encoder.sCmdDone.eEvent = OMX_EventCmdComplete;
        h264->encoder.sCmdDone.nData1 = nData1;
        h264->encoder.sCmdDone.nData2 = nData2;
        h264->encoder.sCmdDone.pEventData = pEventData;
    }
    else if(eEvent == OMX_EventError)
        h264->encoder.bError = OMX_TRUE;
    else {
        sMsg.hComponent = hComponent;
        sMsg.type = EVENT;
        sMsg.data.event.eEvent = eEvent;
        sMsg.data.event.nData1 = nData1;
        sMsg.data.event.nData2 = nData2;
        sMsg.data.event.pEventData = pEventData;
        h264->encoder.pMsgQ->Add(&sMsg);
    }

    return OMX_ErrorNone;
}

OMX_ERRORTYPE gm_EventHandler(
        OMX_IN OMX_HANDLETYPE hComponent,
        OMX_IN OMX_PTR pAppData,
        OMX_IN OMX_EVENTTYPE eEvent,
        OMX_IN OMX_U32 nData1,
        OMX_IN OMX_U32 nData2,
        OMX_IN OMX_PTR pEventData)
{
    MSG sMsg;
    tdav_codec_h264_hw_vpu_t *h264 = (tdav_codec_h264_hw_vpu_t*)pAppData;

    //TSK_DEBUG_ERROR("Client receive event [eEvent:%d, nData1: %d, nData2: %d]\n",
    //        (int)eEvent, (int)nData1, (int)nData2);

    if(eEvent == OMX_EventCmdComplete) {
        h264->decoder.sCmdDone.eEvent = OMX_EventCmdComplete;
        h264->decoder.sCmdDone.nData1 = nData1;
        h264->decoder.sCmdDone.nData2 = nData2;
        h264->decoder.sCmdDone.pEventData = pEventData;
    }
    else if(eEvent == OMX_EventError) {
        TSK_DEBUG_ERROR("Client receive event [eEvent:%d, nData1: %d, nData2: %d]",
                        (int)eEvent, (int)nData1, (int)nData2);
        if (!h264->decoder.bError) {
            TSK_DEBUG_WARN("Reseting OMX thread and decoder");
            h264->decoder.bError = OMX_TRUE;
            sMsg.type = EXIT;
            h264->decoder.pMsgQ->Add(&sMsg);
            // Reset Decoder
            start_reset_dec_timer(h264);
        }
    }
    else {
        sMsg.hComponent = hComponent;
        sMsg.type = EVENT;
        sMsg.data.event.eEvent = eEvent;
        sMsg.data.event.nData1 = nData1;
        sMsg.data.event.nData2 = nData2;
        sMsg.data.event.pEventData = pEventData;
        h264->decoder.pMsgQ->Add(&sMsg);
    }

    return OMX_ErrorNone;
}

OMX_ERRORTYPE gm_EncEmptyBufferDone(
        OMX_IN OMX_HANDLETYPE hComponent,
        OMX_IN OMX_PTR pAppData,
        OMX_IN OMX_BUFFERHEADERTYPE* pBuffer)
{
    MSG sMsg;
    tdav_codec_h264_hw_vpu_t *h264 = (tdav_codec_h264_hw_vpu_t*)pAppData;

    //TSK_DEBUG_ERROR("Client receive EncEmptyBufferDone [%p]\n", pBuffer);

    sMsg.hComponent = hComponent;
    sMsg.type = EMPTY_DONE;
    sMsg.data.buffer.pBuffer = pBuffer;
    h264->encoder.pMsgQ->Add(&sMsg);

    return OMX_ErrorNone;
}

OMX_ERRORTYPE gm_EmptyBufferDone(
        OMX_IN OMX_HANDLETYPE hComponent,
        OMX_IN OMX_PTR pAppData,
        OMX_IN OMX_BUFFERHEADERTYPE* pBuffer)
{
    MSG sMsg;
    tdav_codec_h264_hw_vpu_t *h264 = (tdav_codec_h264_hw_vpu_t*)pAppData;

    //TSK_DEBUG_ERROR("Client receive EmptyBufferDone [%p]\n", pBuffer);

    sMsg.hComponent = hComponent;
    sMsg.type = EMPTY_DONE;
    sMsg.data.buffer.pBuffer = pBuffer;
    h264->decoder.pMsgQ->Add(&sMsg);

    return OMX_ErrorNone;
}

OMX_ERRORTYPE gm_EncFillBufferDone(
        OMX_OUT OMX_HANDLETYPE hComponent,
        OMX_OUT OMX_PTR pAppData,
        OMX_OUT OMX_BUFFERHEADERTYPE* pBuffer)
{
    MSG sMsg;
    tdav_codec_h264_hw_vpu_t *h264 = (tdav_codec_h264_hw_vpu_t*)pAppData;

    //TSK_DEBUG_ERROR("Client receive EncFillBufferDone [%p]\n", pBuffer);

    write_enc_data(h264, pBuffer);

    sMsg.hComponent = hComponent;
    sMsg.type = FILL_DONE;
    sMsg.data.buffer.pBuffer = pBuffer;
    h264->encoder.pMsgQ->Add(&sMsg);
    return OMX_ErrorNone;
}

OMX_ERRORTYPE gm_FillBufferDone(
        OMX_OUT OMX_HANDLETYPE hComponent,
        OMX_OUT OMX_PTR pAppData,
        OMX_OUT OMX_BUFFERHEADERTYPE* pBuffer)
{
    MSG sMsg;
    tdav_codec_h264_hw_vpu_t *h264 = (tdav_codec_h264_hw_vpu_t*)pAppData;

    //TSK_DEBUG_ERROR("Client receive FillBufferDone [%p]\n", pBuffer);
    if (!h264->decoder.bThreadRunning)
        return OMX_ErrorNone;

    if (pBuffer->nFilledLen)
        write_data(h264, pBuffer);

    sMsg.hComponent = hComponent;
    sMsg.type = FILL_DONE;
    sMsg.data.buffer.pBuffer = pBuffer;
    h264->decoder.pMsgQ->Add(&sMsg);
    return OMX_ErrorNone;
}

OMX_U32 get_enc_component_ports(OMX_HANDLETYPE hComponent)
{
    OMX_PORT_PARAM_TYPE sPortPara;
    OMX_U32 aPorts, vPorts, iPorts, oPorts;

    OMX_INIT_STRUCT(&sPortPara, OMX_PORT_PARAM_TYPE);
    aPorts = vPorts = iPorts = oPorts = 0;

    if(OMX_ErrorNone == OMX_GetParameter(hComponent, OMX_IndexParamAudioInit, &sPortPara))
        aPorts = sPortPara.nPorts;
    if(OMX_ErrorNone == OMX_GetParameter(hComponent, OMX_IndexParamVideoInit, &sPortPara))
        vPorts = sPortPara.nPorts;
    if(OMX_ErrorNone == OMX_GetParameter(hComponent, OMX_IndexParamImageInit, &sPortPara))
        iPorts = sPortPara.nPorts;
    if(OMX_ErrorNone == OMX_GetParameter(hComponent, OMX_IndexParamOtherInit, &sPortPara))
        oPorts = sPortPara.nPorts;

    return aPorts + vPorts + iPorts + oPorts;
}

OMX_U32 get_component_ports(OMX_HANDLETYPE hComponent)
{
    OMX_PORT_PARAM_TYPE sPortPara;
    OMX_U32 aPorts, vPorts, iPorts, oPorts;

    OMX_INIT_STRUCT(&sPortPara, OMX_PORT_PARAM_TYPE);
    aPorts = vPorts = iPorts = oPorts = 0;

    if(OMX_ErrorNone == OMX_GetParameter(hComponent, OMX_IndexParamAudioInit, &sPortPara))
        aPorts = sPortPara.nPorts;
    if(OMX_ErrorNone == OMX_GetParameter(hComponent, OMX_IndexParamVideoInit, &sPortPara))
        vPorts = sPortPara.nPorts;
    if(OMX_ErrorNone == OMX_GetParameter(hComponent, OMX_IndexParamImageInit, &sPortPara))
        iPorts = sPortPara.nPorts;
    if(OMX_ErrorNone == OMX_GetParameter(hComponent, OMX_IndexParamOtherInit, &sPortPara))
        oPorts = sPortPara.nPorts;

    return aPorts + vPorts + iPorts + oPorts;
}

OMX_ERRORTYPE EncStateTrans(tdav_codec_h264_hw_vpu_t *h264, OMX_STATETYPE eState)
{
    //TSK_DEBUG_INFO("EncStateTrans IN");
    OMX_ERRORTYPE ret = OMX_ErrorNone;

    if ( !h264 || !h264->encoder.hComponent) {
        TSK_DEBUG_ERROR("EncStateTrans: Invalid parameter");
        return ret;
    }

    OMX_COMPONENTTYPE *hComponent = NULL;
    OMX_STATETYPE eCurState = OMX_StateInvalid;

    hComponent = h264->encoder.hComponent;
    OMX_GetState(hComponent, &eCurState);

    if (eCurState == eState) {
        return ret;
    }

    ret = EncSendCommand(h264, OMX_CommandStateSet,eState,NULL, OMX_FALSE);
    if(ret != OMX_ErrorNone) {
        TSK_DEBUG_ERROR("State trans to %d failed.\n", eState);
        return ret;
    }

    /* Loaded->Idle */
    if(eCurState == OMX_StateLoaded && eState == OMX_StateIdle)
        EncLoad2Idle(h264);
    /* Exec->Idle */
    else if(eCurState == OMX_StateExecuting && eState == OMX_StateIdle)
        h264->encoder.bHoldBuffers = OMX_TRUE;
    /* Pause->Idle */
    else if(eCurState == OMX_StatePause && eState == OMX_StateIdle)
        h264->encoder.bHoldBuffers = OMX_TRUE;
    /* Idle->Loaded */
    else if(eCurState == OMX_StateIdle && eState == OMX_StateLoaded)
        EncIdle2Load(h264);

    TSK_DEBUG_INFO("Called EncWaitCommand in state trans\n");
    ret = EncWaitCommand(h264, OMX_CommandStateSet, eState, NULL);
    if(ret != OMX_ErrorNone)
        return ret;

    TSK_DEBUG_INFO("State trans: [%x] -> [%d] done.\n", eCurState, eState);

    //TSK_DEBUG_INFO("EncStateTrans OUT");
    return ret;
}

OMX_ERRORTYPE StateTrans(tdav_codec_h264_hw_vpu_t *h264, OMX_STATETYPE eState)
{
    //TSK_DEBUG_INFO("StateTrans IN\n");
    OMX_ERRORTYPE ret = OMX_ErrorNone;

    if ( !h264 || !h264->decoder.hComponent) {
        TSK_DEBUG_ERROR("StateTrans: Invalid parameter");
        return OMX_ErrorNone;
    }

    OMX_COMPONENTTYPE *hComponent = NULL;
    OMX_STATETYPE eCurState = OMX_StateInvalid;

    hComponent = h264->decoder.hComponent;
    OMX_GetState(hComponent, &eCurState);

    if (eCurState == eState) {
        return ret;
    }

    ret = SendCommand(h264, OMX_CommandStateSet,eState,NULL, OMX_FALSE);
    if(ret != OMX_ErrorNone) {
        TSK_DEBUG_ERROR("State trans to %d failed.\n", eState);
        return ret;
    }

    /* Loaded->Idle */
    if(eCurState == OMX_StateLoaded && eState == OMX_StateIdle)
        Load2Idle(h264);
    /* Exec->Idle */
    else if(eCurState == OMX_StateExecuting && eState == OMX_StateIdle)
        h264->decoder.bHoldBuffers = OMX_TRUE;
    /* Pause->Idle */
    else if(eCurState == OMX_StatePause && eState == OMX_StateIdle)
        h264->decoder.bHoldBuffers = OMX_TRUE;
    /* Idle->Loaded */
    else if(eCurState == OMX_StateIdle && eState == OMX_StateLoaded)
        Idle2Load(h264);
    else {}

    ret = WaitCommand(h264, OMX_CommandStateSet, eState, NULL);
    if(ret != OMX_ErrorNone)
        return ret;

    //TSK_DEBUG_INFO("State trans: [%x] -> [%d] done.\n", eCurState, eState);

    return ret;
}

OMX_ERRORTYPE EncSendCommand(
        tdav_codec_h264_hw_vpu_t *h264,
        OMX_COMMANDTYPE Cmd,
        OMX_U32 nParam1,
        OMX_PTR pCmdData,
        OMX_BOOL bSync)
{
    OMX_ERRORTYPE ret = OMX_ErrorNone;
    OMX_HANDLETYPE hComponent = h264->encoder.hComponent;

    h264->encoder.sCmdDone.eEvent = OMX_EventMax;
    h264->encoder.bError = OMX_FALSE;
    ret = OMX_SendCommand(hComponent, Cmd, nParam1, pCmdData);
    if(ret != OMX_ErrorNone)
        return ret;

    if(bSync == OMX_TRUE)
        EncWaitCommand(h264, Cmd, nParam1, pCmdData);

    return OMX_ErrorNone;
}

OMX_ERRORTYPE SendCommand(
        tdav_codec_h264_hw_vpu_t *h264,
        OMX_COMMANDTYPE Cmd,
        OMX_U32 nParam1,
        OMX_PTR pCmdData,
        OMX_BOOL bSync)
{
    OMX_ERRORTYPE ret = OMX_ErrorNone;
    OMX_HANDLETYPE hComponent = h264->decoder.hComponent;

    h264->decoder.sCmdDone.eEvent = OMX_EventMax;
    h264->decoder.bError = OMX_FALSE;
    ret = OMX_SendCommand(hComponent, Cmd, nParam1, pCmdData);
    if(ret != OMX_ErrorNone)
        return ret;

    if(bSync == OMX_TRUE)
        WaitCommand(h264, Cmd, nParam1, pCmdData);

    return OMX_ErrorNone;
}

OMX_ERRORTYPE EncWaitCommand(
        tdav_codec_h264_hw_vpu_t *h264,
        OMX_COMMANDTYPE Cmd,
        OMX_U32 nParam1,
        OMX_PTR pCmdData)
{
    OMX_ERRORTYPE ret = OMX_ErrorNone;

    while(1) {
        if(h264->encoder.sCmdDone.eEvent == OMX_EventCmdComplete
                && h264->encoder.sCmdDone.nData1 == (OMX_U32)Cmd
                && h264->encoder.sCmdDone.nData2 == nParam1
                && h264->encoder.sCmdDone.pEventData == pCmdData)
            break;
        else if(h264->encoder.bError == OMX_TRUE) {
            ret = OMX_ErrorUndefined;
            break;
        }
        else
            usleep(SLEEP_TIME);
    }

    return ret;
}

OMX_ERRORTYPE WaitCommand(
        tdav_codec_h264_hw_vpu_t *h264,
        OMX_COMMANDTYPE Cmd,
        OMX_U32 nParam1,
        OMX_PTR pCmdData)
{
    OMX_ERRORTYPE ret = OMX_ErrorNone;

    while(1) {
        if(h264->decoder.sCmdDone.eEvent == OMX_EventCmdComplete
                && h264->decoder.sCmdDone.nData1 == (OMX_U32)Cmd
                && h264->decoder.sCmdDone.nData2 == nParam1
                && h264->decoder.sCmdDone.pEventData == pCmdData)
            break;
        else if(h264->decoder.bError == OMX_TRUE) {
            ret = OMX_ErrorUndefined;
            break;
        }
        else
            usleep(SLEEP_TIME);
    }

    return ret;
}

OMX_ERRORTYPE EncLoad2Idle(tdav_codec_h264_hw_vpu_t *h264)
{
    OMX_U32 i;
    for(i=0; i<h264->encoder.nPorts; i++)
        prepare_enc_port_buffers(h264, i);

    return OMX_ErrorNone;
}

OMX_ERRORTYPE Load2Idle(tdav_codec_h264_hw_vpu_t *h264)
{
    OMX_U32 i;
    for(i=0; i<h264->decoder.nPorts; i++)
        prepare_port_buffers(h264, i);

    return OMX_ErrorNone;
}

OMX_ERRORTYPE EncIdle2Load(tdav_codec_h264_hw_vpu_t *h264)
{
    OMX_U32 i;

    for(i=0; i<h264->encoder.nPorts; i++)
        free_enc_port_buffers(h264, i);

    return OMX_ErrorNone;
}

OMX_ERRORTYPE Idle2Load(tdav_codec_h264_hw_vpu_t *h264)
{
    OMX_U32 i;

    for(i=0; i<h264->decoder.nPorts; i++)
        free_port_buffers(h264, i);

    return OMX_ErrorNone;
}

OMX_ERRORTYPE unload_enc_component(tdav_codec_h264_hw_vpu_t *h264)
{
    OMX_FreeHandle(h264->encoder.hComponent);
    fsl_osal_thread_destroy(h264->encoder.pThreadId);
    return OMX_ErrorNone;
}

OMX_ERRORTYPE unload_component(tdav_codec_h264_hw_vpu_t *h264)
{
    OMX_FreeHandle(h264->decoder.hComponent);
    fsl_osal_thread_destroy(h264->decoder.pThreadId);
    return OMX_ErrorNone;
}

OMX_ERRORTYPE prepare_enc_port_buffers(tdav_codec_h264_hw_vpu_t *h264, OMX_U32 nPortIndex)
{
    OMX_COMPONENTTYPE *hComponent = NULL;
    OMX_PARAM_PORTDEFINITIONTYPE sPortDef;
    OMX_BUFFERHEADERTYPE *pBufferHdr = NULL;
    OMX_U8 *pBuffer = NULL;
    OMX_U32 i;

    hComponent = h264->encoder.hComponent;
    OMX_INIT_STRUCT(&sPortDef, OMX_PARAM_PORTDEFINITIONTYPE);
    sPortDef.nPortIndex = nPortIndex;
    OMX_GetParameter(hComponent, OMX_IndexParamPortDefinition, &sPortDef);
    //TSK_DEBUG_INFO("Port #%d height: %d, width %d\n", nPortIndex, sPortDef.format.video.nFrameHeight, sPortDef.format.video.nFrameWidth);

    //TSK_DEBUG_INFO("prepare_port_enc_buffers: CFmt 0x%x, SliceHt %d, MIMEType %s, \n",
    //sPortDef.format.video.eColorFormat, sPortDef.format.video.nSliceHeight, sPortDef.format.video.cMIMEType);

    for(i=0; i<sPortDef.nBufferCountActual; i++) {
        if(h264->encoder.bAllocater[nPortIndex] == OMX_TRUE) {
            OMX_AllocateBuffer(hComponent, &pBufferHdr, nPortIndex, NULL, sPortDef.nBufferSize);
            //TSK_DEBUG_INFO("Port #%d Allocate buffer done with buffer size : %d..\n", nPortIndex, sPortDef.nBufferSize);
        }
        else {
            pBuffer = (OMX_U8*)FSL_MALLOC(sPortDef.nBufferSize);
            OMX_UseBuffer(hComponent, &pBufferHdr, nPortIndex, NULL, sPortDef.nBufferSize, pBuffer);
            //TSK_DEBUG_INFO("Port #%d Use buffer done with buffer size : %d.\n", nPortIndex, sPortDef.nBufferSize);
        }
        h264->encoder.pBufferHdr[nPortIndex][i] = pBufferHdr;
    }
    h264->encoder.nBufferHdr[nPortIndex] = sPortDef.nBufferCountActual;

    return OMX_ErrorNone;
}

OMX_ERRORTYPE prepare_port_buffers(tdav_codec_h264_hw_vpu_t *h264, OMX_U32 nPortIndex)
{
    OMX_COMPONENTTYPE *hComponent = NULL;
    OMX_PARAM_PORTDEFINITIONTYPE sPortDef;
    OMX_BUFFERHEADERTYPE *pBufferHdr = NULL;
    OMX_U8 *pBuffer = NULL;
    OMX_U32 i;

    hComponent = h264->decoder.hComponent;
    OMX_INIT_STRUCT(&sPortDef, OMX_PARAM_PORTDEFINITIONTYPE);
    sPortDef.nPortIndex = nPortIndex;
    OMX_GetParameter(hComponent, OMX_IndexParamPortDefinition, &sPortDef);

    for(i=0; i<sPortDef.nBufferCountActual; i++) {
        if(h264->decoder.bAllocater[nPortIndex] == OMX_TRUE) {
            OMX_AllocateBuffer(hComponent, &pBufferHdr, nPortIndex, NULL, sPortDef.nBufferSize);
            //TSK_DEBUG_INFO("Port #%d Allocate buffer done with buffer size : %d..\n", nPortIndex, sPortDef.nBufferSize);
        }
        else {
            pBuffer = (OMX_U8*)FSL_MALLOC(sPortDef.nBufferSize);
            OMX_UseBuffer(hComponent, &pBufferHdr, nPortIndex, NULL, sPortDef.nBufferSize, pBuffer);
            //TSK_DEBUG_INFO("Port #%d Use buffer done with buffer size : %d.\n", nPortIndex, sPortDef.nBufferSize);
        }
        h264->decoder.pBufferHdr[nPortIndex][i] = pBufferHdr;
    }
    h264->decoder.nBufferHdr[nPortIndex] = sPortDef.nBufferCountActual;

    h264->decoder.context->width = sPortDef.format.video.nFrameWidth;
    h264->decoder.context->height = sPortDef.format.video.nFrameHeight;

    if (nPortIndex == kPortIndexOutput) {
        OMX_CONFIG_RECTTYPE rect;
        OMX_INIT_STRUCT(&rect, OMX_CONFIG_RECTTYPE);
        rect.nPortIndex = nPortIndex;

        OMX_GetConfig(hComponent, OMX_IndexConfigCommonOutputCrop, &rect);
        TSK_DEBUG_INFO("prepare_port_buffers: rect: w %d, h %d, (left %d, top %d)",
                        rect.nWidth, rect.nHeight, rect.nLeft, rect.nTop);

        h264->decoder.cropLeft = rect.nLeft;
        h264->decoder.cropTop = rect.nTop;
        h264->decoder.cropRight = rect.nLeft + rect.nWidth;
        h264->decoder.cropBottom = rect.nTop + rect.nHeight;

        // Reset config if rquired
        reset_decoder_config(h264, rect.nWidth, rect.nHeight);
    }
    return OMX_ErrorNone;
}

OMX_ERRORTYPE free_enc_port_buffers(tdav_codec_h264_hw_vpu_t *h264, OMX_U32 nPortIndex)
{
    //TSK_DEBUG_INFO("free_enc_port_buffers: port #%d free buffer done.\n", nPortIndex);
    OMX_ERRORTYPE ret = OMX_ErrorNone;
    OMX_COMPONENTTYPE *hComponent;
    OMX_U32 i;

    hComponent = h264->encoder.hComponent;
    for(i=0; i<h264->encoder.nBufferHdr[nPortIndex]; i++) {
        OMX_U8 *ptr = NULL;
        if(h264->encoder.pBufferHdr[nPortIndex][i] == NULL)
            continue;
        ptr = h264->encoder.pBufferHdr[nPortIndex][i]->pBuffer;
        OMX_FreeBuffer(hComponent, nPortIndex, h264->encoder.pBufferHdr[nPortIndex][i]);
        h264->encoder.pBufferHdr[nPortIndex][i] = 0;
        if(h264->encoder.bAllocater[nPortIndex] == OMX_FALSE)
            FSL_FREE(ptr);
    }

    return OMX_ErrorNone;
}

OMX_ERRORTYPE free_port_buffers(tdav_codec_h264_hw_vpu_t *h264, OMX_U32 nPortIndex)
{
    OMX_ERRORTYPE ret = OMX_ErrorNone;
    OMX_COMPONENTTYPE *hComponent;
    OMX_U32 i;

    hComponent = h264->decoder.hComponent;
    for(i=0; i<h264->decoder.nBufferHdr[nPortIndex]; i++) {
        OMX_U8 *ptr = NULL;
        if(h264->decoder.pBufferHdr[nPortIndex][i] == NULL)
            continue;
        ptr = h264->decoder.pBufferHdr[nPortIndex][i]->pBuffer;
        OMX_FreeBuffer(hComponent, nPortIndex, h264->decoder.pBufferHdr[nPortIndex][i]);
        h264->decoder.pBufferHdr[nPortIndex][i] = 0;
        if(h264->decoder.bAllocater[nPortIndex] == OMX_FALSE)
            FSL_FREE(ptr);
    }

    return OMX_ErrorNone;
}

OMX_ERRORTYPE enc_port_disable(tdav_codec_h264_hw_vpu_t *h264, OMX_U32 nPortIndex)
{
    OMX_COMPONENTTYPE *hComponent;
    OMX_ERRORTYPE ret = OMX_ErrorNone;

    hComponent = h264->encoder.hComponent;

    ret = EncSendCommand(h264, OMX_CommandPortDisable, nPortIndex, NULL, OMX_FALSE);
    if(ret != OMX_ErrorNone)
        return ret;

    h264->encoder.bHoldBuffers = OMX_TRUE;

    free_enc_port_buffers(h264, nPortIndex);

    EncWaitCommand(h264, OMX_CommandPortDisable, nPortIndex, NULL);

    TSK_DEBUG_INFO("enc port disbale done.\n");

    return OMX_ErrorNone;
}

OMX_ERRORTYPE port_disable(tdav_codec_h264_hw_vpu_t *h264, OMX_U32 nPortIndex)
{
    OMX_COMPONENTTYPE *hComponent;
    OMX_ERRORTYPE ret = OMX_ErrorNone;

    hComponent = h264->decoder.hComponent;

    ret = SendCommand(h264, OMX_CommandPortDisable, nPortIndex, NULL, OMX_FALSE);
    if(ret != OMX_ErrorNone)
        return ret;

    h264->decoder.bHoldBuffers = OMX_TRUE;

    free_port_buffers(h264, nPortIndex);

    WaitCommand(h264, OMX_CommandPortDisable, nPortIndex, NULL);

    //TSK_DEBUG_INFO("port disbale done.\n");

    return OMX_ErrorNone;
}

OMX_ERRORTYPE enc_port_enable(tdav_codec_h264_hw_vpu_t *h264, OMX_U32 nPortIndex)
{
    //TSK_DEBUG_ERROR("enc_port_enable \n");
    OMX_ERRORTYPE ret = OMX_ErrorNone;
    OMX_COMPONENTTYPE *hComponent;
    OMX_U32 i;

    hComponent = h264->encoder.hComponent;
    ret = EncSendCommand(h264, OMX_CommandPortEnable, nPortIndex, NULL, OMX_FALSE);
    if(ret != OMX_ErrorNone)
        return ret;

    prepare_enc_port_buffers(h264, nPortIndex);

    EncWaitCommand(h264, OMX_CommandPortEnable, nPortIndex, NULL);

    h264->encoder.bHoldBuffers = OMX_FALSE;
    for(i=0; i<h264->encoder.nBufferHdr[nPortIndex]; i++) {
        if(h264->encoder.PortDir[nPortIndex] == OMX_DirInput) {
            TSK_DEBUG_ERROR("enc_port_enable: OMX_DirInput");
            read_enc_frame(h264, h264->encoder.pBufferHdr[nPortIndex][i]);
            OMX_EmptyThisBuffer(h264->encoder.hComponent, h264->encoder.pBufferHdr[nPortIndex][i]);
        }
    }

    return OMX_ErrorNone;
}

OMX_ERRORTYPE port_enable(tdav_codec_h264_hw_vpu_t *h264, OMX_U32 nPortIndex)
{
    //TSK_DEBUG_INFO("port_enable \n");
    OMX_ERRORTYPE ret = OMX_ErrorNone;
    OMX_COMPONENTTYPE *hComponent;
    OMX_U32 i;

    hComponent = h264->decoder.hComponent;
    ret = SendCommand(h264, OMX_CommandPortEnable, nPortIndex, NULL, OMX_FALSE);
    if(ret != OMX_ErrorNone)
        return ret;

    prepare_port_buffers(h264, nPortIndex);

    WaitCommand(h264, OMX_CommandPortEnable, nPortIndex, NULL);

    h264->decoder.bHoldBuffers = OMX_FALSE;
    for(i=0; i<h264->decoder.nBufferHdr[nPortIndex]; i++) {
        if(h264->decoder.PortDir[nPortIndex] == OMX_DirInput) {
            read_frame(h264, h264->decoder.pBufferHdr[nPortIndex][i]);
            OMX_EmptyThisBuffer(h264->decoder.hComponent, h264->decoder.pBufferHdr[nPortIndex][i]);
        }
    }

    return OMX_ErrorNone;
}

static int close_hw_vpu_encoder(tdav_codec_h264_hw_vpu_t *h264)
{
    TSK_DEBUG_INFO("close_hw_vpu_encoder IN \n");
    // Send exit to enc_omx_process_thread
    MSG sMsg;
    sMsg.type = EXIT;
    h264->encoder.pMsgQ->Add(&sMsg);

#if 0
    if (h264->encoder.prev_frame) {
        FSL_FREE(h264->encoder.prev_frame);
        h264->encoder.prev_frame = NULL;
    }
#endif

    EncStateTrans(h264, OMX_StateIdle);
    EncStateTrans(h264, OMX_StateLoaded);
    unload_enc_component(h264);
    TSK_DEBUG_INFO("close_hw_vpu_encoder: Enc pkt loss %d\n", h264->encoder.encPacketLoss);
    h264->encoder.pMsgQ->Free();

    ring_buffer_free(h264->encoder.in_ring_buf);
    h264->encoder.bClosedEncoder = OMX_TRUE;
    TSK_DEBUG_INFO("close_hw_vpu_encoder OUT \n");
    return 0;
}

static int close_hw_vpu_decoder(tdav_codec_h264_hw_vpu_t *h264)
{
    TSK_DEBUG_INFO("close_hw_vpu_decoder IN");
    // Send exit to omx_process_thread
    MSG sMsg;
    sMsg.type = EXIT;
    h264->decoder.pMsgQ->Add(&sMsg);

    if (h264->decoder.prev_frame) {
        FSL_FREE(h264->decoder.prev_frame);
        h264->decoder.prev_frame = NULL;
    }

    StateTrans(h264, OMX_StateIdle);
    StateTrans(h264, OMX_StateLoaded);
    unload_component(h264);
    h264->decoder.pMsgQ->Free();

    TSK_DEBUG_INFO("close_hw_vpu_deccoder: Dec pkt loss %d\n", h264->decoder.decPacketLoss);

    ring_buffer_free(h264->decoder.in_ring_buf);
    ring_buffer_free(h264->decoder.out_ring_buf);

    h264->decoder.bClosedDecoder = OMX_TRUE;
    TSK_DEBUG_INFO("close_hw_vpu_decoder OUT");
    return 0;
}

static int hw_vpu_open_encoder(tdav_codec_h264_hw_vpu_t* self)
{
    if (!self) {
        TSK_DEBUG_ERROR("hw_vpu_open_encoder: Invalid parameter");
        return -1;
    }

    tdav_codec_h264_hw_vpu_t* h264 = (tdav_codec_h264_hw_vpu_t*)self;

    OMX_ERRORTYPE ret = OMX_ErrorNone;
    OMX_HANDLETYPE hComponent = NULL;
    OMX_PARAM_PORTDEFINITIONTYPE sPortDef;
    OMX_U32 i=0;
    efsl_osal_return_type_t fsl_ret;
    QUEUE_ERRORTYPE queue_ret = QUEUE_SUCCESS;
    OMX_S32 gm_video_bitRate = 0;
    OMX_S32 req_frame_rate =0;
    int factor=0, fps=0;
    int max_buffer_size = 0;
    int max_capacity = 0;
    OMX_VIDEO_PARAM_AVCTYPE h264type;
    char propValue[PROPERTY_VALUE_MAX];
    int ring_buff_cap = 0;

    // Create input and output queues
    h264->encoder.pMsgQ = FSL_NEW(Queue, ());
    if(h264->encoder.pMsgQ == NULL) {
        TSK_DEBUG_ERROR("Error creating event Queue instance");
        goto free;
    }
    queue_ret = h264->encoder.pMsgQ->Create((fsl_osal_u32)MAX_QUEUE_SIZE, (fsl_osal_u32)sizeof(MSG), (efsl_osal_bool)(E_FSL_OSAL_TRUE));
    if (queue_ret != QUEUE_SUCCESS) {
        TSK_DEBUG_ERROR("Create event message queue failed.\n");
        goto free;
    }

    //create ring buffer
    // Ring buffer capacity
    h264->encoder.in_ring_buf_cap = MAX_IN_ENC_RB_CAP;
    if (property_get("ringbuf_enc_in_size", propValue, NULL)) {
        ring_buff_cap = atoi(propValue);
        if (ring_buff_cap > 0) {
            h264->encoder.in_ring_buf_cap = ring_buff_cap;
        }
    }

    // Ring buffer for input raw data from camera
    // Create ring buffer with max decoded buffer size
    max_buffer_size = (h264->encoder.context->width * h264->encoder.context->height *3) >> 1;
    max_capacity = h264->encoder.in_ring_buf_cap;
    h264->encoder.in_ring_buf = ring_buffer_init(max_capacity, max_buffer_size);

    if (h264->encoder.in_ring_buf == NULL) {
        TSK_DEBUG_ERROR("Error allocating input ring buffer for encoder");
        goto free;
    }

    h264->encoder.width = h264->encoder.context->width;
    h264->encoder.height = h264->encoder.context->height;
    factor = h264->encoder.bitrate_fact;
    fps = TMEDIA_CODEC_VIDEO(self)->out.fps;
    // Set bit rate to auto (0) by setting bitrate factor to 0 in config file
    h264->encoder.bitrate = (h264->encoder.width * h264->encoder.height * fps * factor * 0.07);

    TSK_DEBUG_WARN("hw_vpu_open_encoder: h264->encoder.bitrate %d \n", h264->encoder.bitrate);

    //Initialize
    h264->encoder.name = (OMX_STRING) VPU_ENC_COMP_NAME_AVCENC;
    h264->encoder.role = (OMX_STRING) VPU_ENC_COMP_ROLE_AVCENC;

    ret = OMX_GetHandle(&hComponent, h264->encoder.name, h264, &gEncCallBacks);
    if(ret != OMX_ErrorNone) {
        TSK_DEBUG_ERROR("Load component %s failed.\n", h264->encoder.name);
        goto free;
    }

    OMX_PARAM_COMPONENTROLETYPE CurRole;
    OMX_INIT_STRUCT(&CurRole, OMX_PARAM_COMPONENTROLETYPE);
    fsl_ret = fsl_osal_memcpy(&CurRole.cRole, h264->encoder.role, OMX_MAX_STRINGNAME_SIZE);
    if (fsl_ret != E_FSL_OSAL_SUCCESS) {
        TSK_DEBUG_ERROR("Unable to do memcpy, invalid src or dst");
        goto free;
    }

    ret = OMX_SetParameter(hComponent, OMX_IndexParamStandardComponentRole, &CurRole);
    if (ret != OMX_ErrorNone) {
        TSK_DEBUG_ERROR("Unable to set components role");
        goto free;
    }

    OMX_INIT_STRUCT(&sPortDef, OMX_PARAM_PORTDEFINITIONTYPE);
    sPortDef.nPortIndex = kPortIndexInput;
    ret = OMX_GetParameter(hComponent, OMX_IndexParamPortDefinition, &sPortDef);
    if (ret != OMX_ErrorNone) {
        TSK_DEBUG_ERROR("Unable to get components port def");
        goto free;
    }

    sPortDef.format.video.nFrameWidth = h264->encoder.width;
    sPortDef.format.video.nFrameHeight = h264->encoder.height;
    sPortDef.format.video.xFramerate = TMEDIA_CODEC_VIDEO(self)->out.fps * Q16_SHIFT;
    sPortDef.nBufferSize = h264->encoder.width * h264->encoder.height * 3 / 2;
    ret = OMX_SetParameter(hComponent, OMX_IndexParamPortDefinition, &sPortDef);
    if (ret != OMX_ErrorNone) {
        TSK_DEBUG_ERROR("Unable to set components port def");
        goto free;
    }

    // Check width, height, frame rate and buffer size
    ret = OMX_GetParameter(hComponent, OMX_IndexParamPortDefinition, &sPortDef);
    if (ret != OMX_ErrorNone) {
        TSK_DEBUG_ERROR("2 Unable to get components port def");
        goto free;
    }

    req_frame_rate = TMEDIA_CODEC_VIDEO(self)->out.fps;

    TSK_DEBUG_INFO("encoder: W %d, H %d, FR %d, BufSize %d",
                    sPortDef.format.video.nFrameWidth,
                    sPortDef.format.video.nFrameHeight,
                    sPortDef.format.video.xFramerate / Q16_SHIFT, sPortDef.nBufferSize);

    OMX_VIDEO_PARAM_BITRATETYPE sBitrate;
    gm_video_bitRate = h264->encoder.bitrate;

    OMX_INIT_STRUCT(&sBitrate, OMX_VIDEO_PARAM_BITRATETYPE);
    sBitrate.nPortIndex = kPortIndexOutput;
    ret = OMX_GetParameter(hComponent, OMX_IndexParamVideoBitrate, &sBitrate);
    if (ret != OMX_ErrorNone) {
        TSK_DEBUG_ERROR("Unable to get components bit rate");
        goto free;
    }

    sBitrate.eControlRate = OMX_Video_ControlRateVariable; //OMX_Video_ControlRateConstant;
    sBitrate.nTargetBitrate = gm_video_bitRate;
    ret = OMX_SetParameter(hComponent, OMX_IndexParamVideoBitrate, &sBitrate);
    if (ret != OMX_ErrorNone) {
        TSK_DEBUG_ERROR("Unable to set components bit rate");
        goto free;
    }

    TSK_DEBUG_INFO("encoder: Bitrate %d", sBitrate.nTargetBitrate);

    OMX_INIT_STRUCT(&sPortDef, OMX_PARAM_PORTDEFINITIONTYPE);
    sPortDef.nPortIndex = kPortIndexOutput;
    ret = OMX_GetParameter(hComponent, OMX_IndexParamPortDefinition, &sPortDef);
    if (ret != OMX_ErrorNone) {
        TSK_DEBUG_ERROR("Unable to get components port def for %d", kPortIndexOutput);
        goto free;
    }

    sPortDef.format.video.nBitrate = gm_video_bitRate;
    ret = OMX_SetParameter(hComponent, OMX_IndexParamPortDefinition, &sPortDef);
    if (ret != OMX_ErrorNone) {
        TSK_DEBUG_ERROR("Unable to set components port def for %d", kPortIndexOutput);
        goto free;
    }

    h264->encoder.hComponent = (OMX_COMPONENTTYPE*)hComponent;
    h264->encoder.nPorts = get_enc_component_ports(hComponent);
    OMX_INIT_STRUCT(&sPortDef, OMX_PARAM_PORTDEFINITIONTYPE);
    for(i=0; i<h264->encoder.nPorts; i++) {
        sPortDef.nPortIndex = i;
        ret = OMX_GetParameter(hComponent, OMX_IndexParamPortDefinition, &sPortDef);
        if (ret != OMX_ErrorNone) {
            TSK_DEBUG_ERROR("Unable to get components port def for %d", i);
            goto free;
        }

        h264->encoder.PortDir[i] = sPortDef.eDir;
        if(h264->encoder.PortDir[i] == OMX_DirInput)
            h264->encoder.bAllocater[i] = OMX_TRUE;  //in buffer allocated by vpu component
        if(h264->encoder.PortDir[i] == OMX_DirOutput)
            h264->encoder.bAllocater[i] = OMX_TRUE;   // out buffer allocated by component
    }

    // Set GOP / P-Frame
    OMX_INIT_STRUCT(&h264type, OMX_VIDEO_PARAM_AVCTYPE);
    h264type.nPortIndex = kPortIndexOutput;
    ret = OMX_GetParameter(hComponent, OMX_IndexParamVideoAvc, &h264type);
    if (ret != OMX_ErrorNone) {
        TSK_DEBUG_ERROR("Unable to get components AVC param for port %d", kPortIndexOutput);
    }

    TSK_DEBUG_INFO("hw_vpu_open_encoder:Get p-Frame (GOP) %d , B-Frame %d",
                     h264type.nPFrames+1, h264type.nBFrames);

    h264type.nAllowedPictureTypes = OMX_VIDEO_PictureTypeI | OMX_VIDEO_PictureTypeP;

    if (h264type.eProfile == OMX_VIDEO_AVCProfileBaseline) {
        h264type.nSliceHeaderSpacing = 0;
        h264type.bUseHadamard = OMX_TRUE;
        h264type.nRefFrames = 1;
        h264type.nBFrames = 0;
        h264type.nPFrames = h264->encoder.gop_size;
        if (h264type.nPFrames == 0) {
            h264type.nAllowedPictureTypes = OMX_VIDEO_PictureTypeI;
        }
        h264type.nRefIdx10ActiveMinus1 = 0;
        h264type.nRefIdx11ActiveMinus1 = 0;
        h264type.bEntropyCodingCABAC = OMX_FALSE;
        h264type.bWeightedPPrediction = OMX_FALSE;
        h264type.bconstIpred = OMX_FALSE;
        h264type.bDirect8x8Inference = OMX_FALSE;
        h264type.bDirectSpatialTemporal = OMX_FALSE;
        h264type.nCabacInitIdc = 0;
    }

    if (h264type.nBFrames != 0) {
        h264type.nAllowedPictureTypes |= OMX_VIDEO_PictureTypeB;
    }

    h264type.bEnableUEP = OMX_FALSE;
    h264type.bEnableFMO = OMX_FALSE;
    h264type.bEnableASO = OMX_FALSE;
    h264type.bEnableRS = OMX_FALSE;
    h264type.bFrameMBsOnly = OMX_TRUE;
    h264type.bMBAFF = OMX_FALSE;
    h264type.eLoopFilterMode = OMX_VIDEO_AVCLoopFilterEnable;

    ret = OMX_SetParameter(hComponent, OMX_IndexParamVideoAvc, &h264type);
    if (ret != OMX_ErrorNone) {
        TSK_DEBUG_ERROR("Unable to set components AVC param");
    }

    TSK_DEBUG_WARN("'%s' loaded with (%dx%d) resolution and pFrame %d ",
                    h264->encoder.name,
                    sPortDef.format.video.nFrameWidth,
                    sPortDef.format.video.nFrameHeight,
                    h264type.nPFrames+1);

    // TODO: Enable when FSL supports prfile setting
#if 0
    // Setting H264 profile and level
    OMX_VIDEO_PARAM_AVCTYPE h264type;
    OMX_INIT_STRUCT(&h264type, OMX_VIDEO_PARAM_AVCTYPE);
    h264type.nPortIndex = kPortIndexOutput;
    ret = OMX_GetParameter(hComponent, OMX_IndexParamVideoAvc, &h264type);
    if (ret != OMX_ErrorNone) {
        TSK_DEBUG_ERROR("Unable to get components AVC param %d", kPortIndexOutput);
        goto free;
    }

    TSK_DEBUG_ERROR("hw_vpu_open_encoder: prof %x, level %x\n", h264type.eProfile, h264type.eLevel);

    // Get supported profile and level
    OMX_VIDEO_PARAM_PROFILELEVELTYPE param;
    OMX_INIT_STRUCT(&param, OMX_VIDEO_PARAM_PROFILELEVELTYPE);
    param.nPortIndex = kPortIndexOutput;

    for (param.nProfileIndex = 0;; ++param.nProfileIndex) {
        ret = OMX_GetParameter(hComponent, OMX_IndexParamVideoProfileLevelQuerySupported, &param);
        if (ret != OMX_ErrorNone) break;

        TSK_DEBUG_INFO("Supported profile: %d, level %d\n", param.eProfile, param.eLevel);

        if (param.eProfile == self->encoder.mProfile &&
            param.eLevel <= self->encoder.mLevel) {
            TSK_DEBUG_INFO("requested profile found\n");
            h264type.eProfile = self->encoder.mProfile;
            h264type.eLevel = self->encoder.mLevel;
            break;
        }
    }

    if (h264type.eProfile == OMX_VIDEO_AVCProfileBaseline) {
        TSK_DEBUG_INFO("OMX_VIDEO_AVCProfileBaseline\n");
        h264type.nSliceHeaderSpacing = 0;
        h264type.bUseHadamard = OMX_TRUE;
        h264type.nRefFrames = 1;
        h264type.nBFrames = 0;
        h264type.nPFrames = setPFramesSpacing(self->encoder.context->gop_size, TMEDIA_CODEC_VIDEO(self)->out.fps);
        if (h264type.nPFrames == 0) {
            h264type.nAllowedPictureTypes = OMX_VIDEO_PictureTypeI;
        }
        h264type.nRefIdx10ActiveMinus1 = 0;
        h264type.nRefIdx11ActiveMinus1 = 0;
        h264type.bEntropyCodingCABAC = OMX_FALSE;
        h264type.bWeightedPPrediction = OMX_FALSE;
        h264type.bconstIpred = OMX_FALSE;
        h264type.bDirect8x8Inference = OMX_FALSE;
        h264type.bDirectSpatialTemporal = OMX_FALSE;
        h264type.nCabacInitIdc = 0;
    }

    if (h264type.nBFrames != 0) {
        h264type.nAllowedPictureTypes |= OMX_VIDEO_PictureTypeB;
    }

    h264type.bEnableUEP = OMX_FALSE;
    h264type.bEnableFMO = OMX_FALSE;
    h264type.bEnableASO = OMX_FALSE;
    h264type.bEnableRS = OMX_FALSE;
    h264type.bFrameMBsOnly = OMX_TRUE;
    h264type.bMBAFF = OMX_FALSE;
    h264type.eLoopFilterMode = OMX_VIDEO_AVCLoopFilterEnable;

    ret = OMX_SetParameter(hComponent, OMX_IndexParamVideoAvc, &h264type);
    if (ret != OMX_ErrorNone) {
        TSK_DEBUG_ERROR("Unable to set components AVC param");
        goto free;
    }
#endif

    ret = EncStateTrans(h264, OMX_StateIdle);
    if (ret != OMX_ErrorNone) {
        TSK_DEBUG_ERROR("Unable to do state trans to Idle");
        goto free;
    }

    ret = EncStateTrans(h264, OMX_StateExecuting);
    if (ret != OMX_ErrorNone) {
        TSK_DEBUG_ERROR("Unable to do state trans to Exec");
        goto free;
    }

    h264->encoder.bClosedEncoder = OMX_FALSE;
    fsl_ret = fsl_osal_thread_create(&h264->encoder.pThreadId, NULL, enc_omx_process_thread, h264);
    if (fsl_ret != E_FSL_OSAL_SUCCESS) {
        TSK_DEBUG_ERROR("Unable to create encoder thread");
        goto free;
    }

    h264->encoder.bInitailBufferSubmit = OMX_TRUE;
    h264->encoder.prev_frame = NULL;
    h264->encoder.source_done = OMX_FALSE;
    // TODO: Update maxBufInQueue, when requested by Phone app
    h264->encoder.maxBufInQueue = MAX_ENC_BUF_TO_KEEP;
    TSK_DEBUG_INFO("hw_vpu_open_encoder OUT");
    return (int)ret;

free:
    free(h264);
    ring_buffer_free(h264->encoder.in_ring_buf);
    OMX_FreeHandle(h264->encoder.hComponent);
    h264->encoder.hComponent = NULL;
    return (int)ret;
}

static int hw_vpu_open_decoder(tdav_codec_h264_hw_vpu_t* self)
{
    if (!self) {
        TSK_DEBUG_ERROR("hw_vpu_open_decoder: Invalid parameter");
        return -1;
    }

    tdav_codec_h264_hw_vpu_t* h264 = (tdav_codec_h264_hw_vpu_t*)self;

    TSK_DEBUG_INFO("hw_vpu_open_decoder IN");
    OMX_ERRORTYPE ret = OMX_ErrorNone;
    QUEUE_ERRORTYPE queue_ret = QUEUE_SUCCESS;
    int max_buffer_size = 0;
    int max_capacity = 0;
    char propValue[PROPERTY_VALUE_MAX];
    int ring_buff_cap = 0;

    // Create input and output queues
    h264->decoder.pMsgQ = FSL_NEW(Queue, ());
    if(h264->decoder.pMsgQ == NULL) {
        TSK_DEBUG_ERROR("Error creating event Queue instance");
        goto free;
    }
    queue_ret = h264->decoder.pMsgQ->Create((fsl_osal_u32)MAX_QUEUE_SIZE, (fsl_osal_u32)sizeof(MSG), (efsl_osal_bool)(E_FSL_OSAL_TRUE));
    if (queue_ret != QUEUE_SUCCESS) {
        TSK_DEBUG_ERROR("Create event message queue failed.\n");
        goto free;
    }

    //create ring buffer
    // Ring buffer capacity
    h264->decoder.in_ring_buf_cap = MAX_IN_DEC_RB_CAP;
    if (property_get("ringbuf_dec_in_size", propValue, NULL)) {
        ring_buff_cap = atoi(propValue);
        if (ring_buff_cap > 0) {
            h264->decoder.in_ring_buf_cap = ring_buff_cap;
        }
    }

    // Ring buffer for input RTP data (encoded data)
    // Create ring buffer with max encoded buffer size
    // Increased buffer size to accommodate varaible frame size, due to VBR.
    max_buffer_size = (h264->decoder.context->width * h264->decoder.context->height * 7) >> 3;
    max_capacity = h264->decoder.in_ring_buf_cap;

    h264->decoder.in_ring_buf = ring_buffer_init(max_capacity, max_buffer_size);
    if (h264->decoder.in_ring_buf == NULL) {
        TSK_DEBUG_ERROR("Error allocating input ring buffer for decoder");
        goto free;
    }

    // Ring buffer for output decoded data
    // Ring buffer capacity
    h264->decoder.out_ring_buf_cap = MAX_OUT_DEC_RB_CAP;
    if (property_get("ringbuf_dec_out_size", propValue, NULL)) {
        ring_buff_cap = atoi(propValue);
        if (ring_buff_cap > 0) {
            h264->decoder.out_ring_buf_cap = ring_buff_cap;
        }
    }

    // Create ring buffer with max decoded buffer size
    max_buffer_size = (h264->decoder.context->width * h264->decoder.context->height *3) >> 1;
    max_capacity = h264->decoder.out_ring_buf_cap;

    h264->decoder.out_ring_buf = ring_buffer_init(max_capacity, max_buffer_size);
    if (h264->decoder.out_ring_buf == NULL) {
        TSK_DEBUG_ERROR("Error allocating output ring buffer for decoder");
        goto free;
    }

    ret = open_fsl_omx_decoder(h264);
    if (ret != OMX_ErrorNone) {
        TSK_DEBUG_ERROR("Error opening omx h264 codec");
        goto free;
    }

    TSK_DEBUG_INFO("hw_vpu_open_decoder OUT");
    return (int)ret;

free:
    // Free queue
    free(h264);
    ring_buffer_free(h264->decoder.in_ring_buf);
    ring_buffer_free(h264->decoder.out_ring_buf);
    return (int)ret;
}

static OMX_ERRORTYPE open_fsl_omx_decoder(tdav_codec_h264_hw_vpu_t* h264)
{
    TSK_DEBUG_INFO("open_fsl_omx_decoder");
    if (!h264) {
        TSK_DEBUG_ERROR("open_fsl_omx_decoder: Invalid parameter passed");
        return OMX_ErrorUndefined;
    }

    OMX_ERRORTYPE ret = OMX_ErrorNone;
    OMX_HANDLETYPE hComponent = NULL;
    OMX_PARAM_PORTDEFINITIONTYPE sPortDef;
    OMX_U32 i;
    QUEUE_ERRORTYPE queue_ret = QUEUE_SUCCESS;
    efsl_osal_return_type_t fsl_ret;

    //Initialize
    h264->decoder.name = (OMX_STRING) VPU_DEC_COMP_NAME_AVCDEC;
    h264->decoder.role = (OMX_STRING) VPU_DEC_COMP_ROLE_AVCDEC;

    ret = OMX_GetHandle(&hComponent, h264->decoder.name, h264, &gCallBacks);
    if(ret != OMX_ErrorNone) {
        TSK_DEBUG_ERROR("Load component %s failed.\n", h264->decoder.name);
        goto free;
    }

    OMX_PARAM_COMPONENTROLETYPE CurRole;
    OMX_INIT_STRUCT(&CurRole, OMX_PARAM_COMPONENTROLETYPE);
    fsl_osal_memcpy(&CurRole.cRole, h264->decoder.role, OMX_MAX_STRINGNAME_SIZE);
    ret = OMX_SetParameter(hComponent, OMX_IndexParamStandardComponentRole, &CurRole);
    if (ret != OMX_ErrorNone) {
        TSK_DEBUG_ERROR("Unable to set component role");
        goto free;
    }

    h264->decoder.hComponent = (OMX_COMPONENTTYPE*)hComponent;
    h264->decoder.nPorts = get_component_ports(hComponent);
    OMX_INIT_STRUCT(&sPortDef, OMX_PARAM_PORTDEFINITIONTYPE);
    for(i=0; i<h264->decoder.nPorts; i++) {
        sPortDef.nPortIndex = i;
        ret = OMX_GetParameter(hComponent, OMX_IndexParamPortDefinition, &sPortDef);
        if (ret != OMX_ErrorNone) {
            TSK_DEBUG_ERROR("Unable to get components port def");
            goto free;
        }

        TSK_DEBUG_INFO("open_fsl_omx_decoder: CFmt %x", sPortDef.format.video.eColorFormat);

        h264->decoder.PortDir[i] = sPortDef.eDir;
        if(h264->decoder.PortDir[i] == OMX_DirInput) {
            h264->decoder.bAllocater[i] = OMX_FALSE;  //in buffer allocated by client
            sPortDef.format.video.eCompressionFormat = OMX_VIDEO_CodingAVC;
            sPortDef.format.video.nFrameWidth = h264->decoder.context->width;
            sPortDef.format.video.nFrameHeight = h264->decoder.context->height;

            ret = OMX_SetParameter(hComponent, OMX_IndexParamPortDefinition, &sPortDef);
            if (ret != OMX_ErrorNone) {
                TSK_DEBUG_ERROR("Unable to set components port def");
                goto free;
            }

	    TSK_DEBUG_INFO("open_fsl_omx_decoder: set width to %d, and hight to %d\n",
	     h264->decoder.context->width, h264->decoder.context->height);
        }
        if(h264->decoder.PortDir[i] == OMX_DirOutput)
            h264->decoder.bAllocater[i] = OMX_TRUE;   // out buffer allocated by component
    }

    TSK_DEBUG_WARN("open_fsl_omx_decoder: Load component %s done with resolution (%dx%d)",
                    h264->decoder.name,
                    sPortDef.format.video.nFrameWidth,
                    sPortDef.format.video.nFrameHeight);

    ret = StateTrans(h264, OMX_StateIdle);
    if (ret != OMX_ErrorNone) {
        TSK_DEBUG_ERROR("Unable to do state trans to Idle");
        goto free;
    }

    ret = StateTrans(h264, OMX_StateExecuting);
    if (ret != OMX_ErrorNone) {
        TSK_DEBUG_ERROR("Unable to do state trans to Exec");
        goto free;
    }

    h264->decoder.bClosedDecoder = OMX_FALSE;
    fsl_ret = fsl_osal_thread_create(&h264->decoder.pThreadId, NULL, omx_process_thread, h264);
    if (fsl_ret != E_FSL_OSAL_SUCCESS) {
        TSK_DEBUG_ERROR("Unable to create decoder thread");
        goto free;
    }

    h264->decoder.bInitailBufferSubmit = OMX_TRUE;
    h264->decoder.prev_frame = NULL;
    h264->decoder.source_done = OMX_FALSE;
    // TODO: Update maxBufInQueue, when requested by Phone app
    h264->decoder.maxBufInQueue = MAX_DEC_BUF_TO_KEEP;
    h264->decoder.decoder_reset = tsk_false;
    TSK_DEBUG_INFO("open_fsl_omx_decoder OUT");
    return ret;

free:
    OMX_FreeHandle(h264->decoder.hComponent);
    h264->decoder.hComponent = NULL;
    return ret;
}

/* Start encode process, if not started.
 * Force push buffer to OMX encoder if not passed during read request.
 * Push raw data to ring buffer and return.
 * Encoded data will be made availble in write_enc_data().
 * @return, return -1 if error, otherwise 0.
*/
static int hw_vpu_encode(tdav_codec_h264_hw_vpu_t* self, const AVPacket *avpkt)
{
    if (!self || !avpkt) {
        TSK_DEBUG_ERROR("hw_vpu_encode: Invalid parameter");
        return -1;
    }

    // copy to ensure we do not change avpkt
    AVPacket pkt = *avpkt;

    if (!self->encoder.source_done) {
        if (!self->encoder.in_ring_buf || !avpkt->data) {
            TSK_DEBUG_WARN("hw_vpu_encode: Invalid data/rb for encode");
            return -1;
        }

        // Fed buffer to encoder, if missed in read_enc_frame()
        if (self->encoder.inBufEmptyCount >= 1) {
            TSK_DEBUG_INFO("hw_vpu_encode:Ring buf count %d", self->encoder.in_ring_buf->count);
            if (self->encoder.in_ring_buf->count >= ENC_MAX_PORT_BUF) {
                for(int i=0; i<self->encoder.nBufferHdr[0]; i++) {
                    read_enc_frame(self, self->encoder.pBufferHdr[0][i]);
                    OMX_EmptyThisBuffer(self->encoder.hComponent, self->encoder.pBufferHdr[0][i]);
                }
            } else if (self->encoder.in_ring_buf->count >= ENC_MIN_PORT_BUF) {
                read_enc_frame(self, self->encoder.pBufferHdr[0][0]);
                OMX_EmptyThisBuffer(self->encoder.hComponent, self->encoder.pBufferHdr[0][0]);
            }
        }

        // Push incoming data from camera source to ring buffer
        if (ring_buffer_push(self->encoder.in_ring_buf, avpkt->data, avpkt->size)) {
            // Increment enc packet
            self->encoder.encPacketLoss++;
        }
    }

    if (self->encoder.bInitailBufferSubmit) {
        OMX_STATETYPE eState = OMX_StateInvalid;
        OMX_GetState(self->encoder.hComponent, &eState);

        if (eState == OMX_StateExecuting) {
            //TSK_DEBUG_INFO("hw_vpu_encode: initial buffer submit\n");
            enc_start_data_process(self);
            self->encoder.bInitailBufferSubmit = OMX_FALSE;
        }
    }
    return 0;
}

/* Start decode process, if not started.
 * Push RTP encoded data to ring buffer and return.
 * Decoded data will be read in copyDecodedData() if availble.
 * @return, return -1 if error, otherwise 0.
*/
static int hw_vpu_decode(tdav_codec_h264_hw_vpu_t* self, const AVPacket *avpkt)
{
    if (!self || !avpkt) {
        TSK_DEBUG_ERROR("hw_vpu_decode: Invalid parameter");
        return -1;
    }

    // copy to ensure we do not change avpkt
    AVPacket pkt = *avpkt;

    TSK_DEBUG_INFO("hw_vpu_decode: IN");

    if (!self->decoder.source_done) {
        if (!self->decoder.in_ring_buf || !avpkt->data) {
            TSK_DEBUG_WARN("hw_vpu_decode: Invalid data/rb for decode");
            return -1;
        }

        // Push incoming data from NW to ring buffer
        if (ring_buffer_push(self->decoder.in_ring_buf, avpkt->data, avpkt->size)) {
            // Increment dec packet
            self->decoder.decPacketLoss++;
        }

        // Start decoder process
        if (self->decoder.bInitailBufferSubmit) {
            OMX_STATETYPE eState = OMX_StateInvalid;
            OMX_GetState(self->decoder.hComponent, &eState);

            if (eState == OMX_StateExecuting) {
                TSK_DEBUG_INFO("hw_vpu_decode: initial buffer submit\n");
                start_data_process(self);
                self->decoder.bInitailBufferSubmit = OMX_FALSE;
            }
        }
    }
    return 0;
}

/*
 * Decoded data will be read in copyDecodedData() if availble.
 * @return, return -1 if error, otherwise 0.
*/
static void copyDecodedData(tdav_codec_h264_hw_vpu_t* self, void** out_data, int size)
{
    //TSK_DEBUG_INFO("copyDecodedData IN");

    if (!(*out_data) || !(self)) {
        TSK_DEBUG_WARN("copyDecodedData: Invalid *out_data or self ptr");
        return;
    }

    //TODO: Check if self->decoder.prev_frame is required
    bool realloc = tsk_true;

    ring_buffer_pop(self->decoder.out_ring_buf, *out_data, &size, realloc);

    if (size > 0) {
        // Decrement dec packet
        self->decoder.decPacketLoss--;
    }
}

static void * enc_omx_process_thread(void *ptr)
{
    TSK_DEBUG_INFO("\n enc_omx_process_thread IN \n");
    tdav_codec_h264_hw_vpu_t *self = (tdav_codec_h264_hw_vpu_t*)ptr;
    OMX_BUFFERHEADERTYPE *pBufferHdr = NULL;
    MSG sMsg;

    while(!self->encoder.bClosedEncoder) {
        self->encoder.bThreadRunning = OMX_TRUE;
        self->encoder.pMsgQ->Get(&sMsg);
        if(sMsg.type == EVENT) {
            enc_process_event(self, &sMsg);
        }
        else if(sMsg.type == EMPTY_DONE) {
            pBufferHdr = sMsg.data.buffer.pBuffer;

            if(self->encoder.bHoldBuffers != OMX_TRUE && self->encoder.bInEos != OMX_TRUE) {
               read_enc_frame(self, pBufferHdr);
               if(pBufferHdr->nFilledLen)
                   OMX_EmptyThisBuffer(sMsg.hComponent, pBufferHdr);
            }
        }
        else if(sMsg.type == FILL_DONE) {
            pBufferHdr = sMsg.data.buffer.pBuffer;
            if(self->encoder.bHoldBuffers != OMX_TRUE) {
                OMX_FillThisBuffer(sMsg.hComponent, pBufferHdr);
            }
        }
        else if(sMsg.type == EXIT) {
            TSK_DEBUG_INFO("sMsg.type == EXIT \n");
            break;
        }
    }
    self->encoder.bThreadRunning = OMX_FALSE;
    self->encoder.source_done = OMX_TRUE;

    TSK_DEBUG_INFO("enc_omx_process_thread OUT \n");
    return NULL;
}

static void * omx_process_thread(void *ptr)
{
    TSK_DEBUG_INFO("omx_process_thread IN \n");
    tdav_codec_h264_hw_vpu_t *self = (tdav_codec_h264_hw_vpu_t*)ptr;
    OMX_BUFFERHEADERTYPE *pBufferHdr = NULL;
    MSG sMsg;

    while(!self->decoder.bClosedDecoder) {
        self->decoder.bThreadRunning = OMX_TRUE;
        self->decoder.pMsgQ->Get(&sMsg);
        if(sMsg.type == EVENT) {
            process_event(self, &sMsg);
            while(self->decoder.in_ring_buf->count < 0) {
                // sleep for 10ms to get input data
                //TSK_DEBUG_INFO("in EVENT waiting for input Q to fill\n");
                usleep(SLEEP_TIME);
            }
        }
        else if(sMsg.type == EMPTY_DONE) {
            pBufferHdr = sMsg.data.buffer.pBuffer;

            if(self->decoder.bHoldBuffers != OMX_TRUE && self->decoder.bInEos != OMX_TRUE) {
               read_frame(self, pBufferHdr);
               OMX_EmptyThisBuffer(sMsg.hComponent, pBufferHdr);
            }
        }
        else if(sMsg.type == FILL_DONE) {
            pBufferHdr = sMsg.data.buffer.pBuffer;
            if(self->decoder.bHoldBuffers != OMX_TRUE) {
                //TSK_DEBUG_INFO("omx_process_thread: calling fillbuffer \n");
                OMX_FillThisBuffer(sMsg.hComponent, pBufferHdr);
            }
        }
        else if(sMsg.type == EXIT) {
            TSK_DEBUG_INFO("sMsg.type == EXIT \n");
            break;
        }
    }
    self->decoder.bThreadRunning = OMX_FALSE;
    self->decoder.source_done = OMX_TRUE;

    TSK_DEBUG_INFO("omx_process_thread OUT \n");
    return NULL;
}

OMX_ERRORTYPE enc_start_data_process(tdav_codec_h264_hw_vpu_t *h264)
{
    //TSK_DEBUG_INFO("enc_start_data_process");
    OMX_ERRORTYPE ret = OMX_ErrorNone;
    OMX_U32 i;

    h264->encoder.bHoldBuffers = OMX_FALSE;

    /* Send output buffers */
    for(i=0; i<h264->encoder.nBufferHdr[1]; i++) {
        h264->encoder.pBufferHdr[1][i]->nFilledLen = 0;
        h264->encoder.pBufferHdr[1][i]->nOffset = 0;
        OMX_FillThisBuffer(h264->encoder.hComponent, h264->encoder.pBufferHdr[1][i]);
    }

    if (h264->encoder.bInitailBufferSubmit == true) {
        read_enc_frame(h264, h264->encoder.pBufferHdr[0][0]);
        h264->encoder.dummy_frame = h264->encoder.pBufferHdr[0][0];
        for(i=0; i < 3; i++) {
            //TSK_DEBUG_INFO("Enc - OMX_EmptyThisBuffer i %d", (i+1));
            OMX_EmptyThisBuffer(h264->encoder.hComponent, h264->encoder.dummy_frame);
        }
    } else {
    /* Send input buffers */
    for(i=0; i<h264->encoder.nBufferHdr[0]; i++) {
        read_enc_frame(h264, h264->encoder.pBufferHdr[0][i]);
        OMX_EmptyThisBuffer(h264->encoder.hComponent, h264->encoder.pBufferHdr[0][i]);
    }
    }
    return ret;
}

OMX_ERRORTYPE start_data_process(tdav_codec_h264_hw_vpu_t *h264)
{
    //TSK_DEBUG_INFO("start_data_process");
    OMX_ERRORTYPE ret = OMX_ErrorNone;
    OMX_U32 i;

    h264->decoder.bHoldBuffers = OMX_FALSE;

    /* Send output buffers */
    for(i=0; i<h264->decoder.nBufferHdr[1]; i++) {
        h264->decoder.pBufferHdr[1][i]->nFilledLen = 0;
        h264->decoder.pBufferHdr[1][i]->nOffset = 0;
        OMX_FillThisBuffer(h264->decoder.hComponent, h264->decoder.pBufferHdr[1][i]);
    }

    /* Send input buffers */
    for(i=0; i<h264->decoder.nBufferHdr[0]; i++) {
        read_frame(h264, h264->decoder.pBufferHdr[0][i]);
        OMX_EmptyThisBuffer(h264->decoder.hComponent, h264->decoder.pBufferHdr[0][i]);
    }
    return ret;
}

OMX_ERRORTYPE process_event(tdav_codec_h264_hw_vpu_t *self, MSG *pMsg)
{
    //TSK_DEBUG_INFO("process_event IN");
    OMX_EVENTTYPE event = pMsg->data.event.eEvent;
    OMX_U32 nPortIndex = pMsg->data.event.nData1;

    if(event == OMX_EventPortSettingsChanged) {
        port_disable(self, nPortIndex);
        port_enable(self, nPortIndex);

        OMX_STATETYPE eState = OMX_StateInvalid;
        OMX_GetState(self->decoder.hComponent, &eState);
        if(eState == OMX_StateExecuting) {
            /* Send output buffers */
            OMX_S32 i;
            for(i=0; (int)i<self->decoder.nBufferHdr[1]; i++) {
                self->decoder.pBufferHdr[1][i]->nFilledLen = 0;
                self->decoder.pBufferHdr[1][i]->nOffset = 0;
                OMX_FillThisBuffer(self->decoder.hComponent, self->decoder.pBufferHdr[1][i]);
            }
        }
    }
    return OMX_ErrorNone;
}

OMX_ERRORTYPE enc_process_event(tdav_codec_h264_hw_vpu_t *self, MSG *pMsg)
{
    //TSK_DEBUG_INFO("enc_process_event IN\n");
    OMX_EVENTTYPE event = pMsg->data.event.eEvent;
    OMX_U32 nPortIndex = pMsg->data.event.nData1;

    if(event == OMX_EventPortSettingsChanged) {
        enc_port_disable(self, nPortIndex);
        enc_port_enable(self, nPortIndex);

        OMX_STATETYPE eState = OMX_StateInvalid;
        OMX_GetState(self->encoder.hComponent, &eState);
        if(eState == OMX_StateExecuting) {
            /* Send output buffers */
            OMX_S32 i;
            for(i=0; (int)i<self->encoder.nBufferHdr[1]; i++) {
                self->encoder.pBufferHdr[1][i]->nFilledLen = 0;
                self->encoder.pBufferHdr[1][i]->nOffset = 0;
                OMX_FillThisBuffer(self->encoder.hComponent, self->encoder.pBufferHdr[1][i]);
            }
        }
    }
    return OMX_ErrorNone;
}

static OMX_U32 setPFramesSpacing(int32_t iFramesInterval, int32_t frameRate)
{
    if (iFramesInterval < 0) {
        return 0xFFFFFFFF;
    } else if (iFramesInterval == 0) {
        return 0;
    }
    OMX_U32 ret = frameRate * iFramesInterval - 1;
    return ret;
}

static void free(tdav_codec_h264_hw_vpu_t *h264)
{
    if (!h264) return;

    //Encoder process queue
    if (h264->encoder.pMsgQ)
        FSL_FREE(h264->encoder.pMsgQ);

    //Decoder process queue
    if (h264->decoder.pMsgQ)
        FSL_FREE(h264->decoder.pMsgQ);
}

static void ProcessBufferForCrop(tdav_codec_h264_hw_vpu_t* self, uint8_t* srcPtr, uint8_t* desPtr, int size)
{
    if ( (!self) || (!srcPtr) || (!desPtr)) {
        TSK_DEBUG_ERROR("invalid self ptr or input or output buffer");
        return;
    }

    int cropLeft = self->decoder.cropLeft;
    int cropTop = self->decoder.cropTop;
    int cropRight = self->decoder.cropRight;
    int cropBottom = self->decoder.cropBottom;

    uint8_t *buf, *out_data_ptr, *pCropYuv;
    int cropWidth = 0, cropHeight = 0, i = 0;

    buf = srcPtr;
    out_data_ptr = desPtr;

    int width = self->decoder.context->width;
    int height = self->decoder.context->height;

    pCropYuv = buf;
    cropWidth = cropRight - cropLeft;
    cropHeight = cropBottom - cropTop;

    pCropYuv += width * cropTop;
    pCropYuv += cropLeft;

    for (i = 0; i < cropHeight; i++) {
        if (!out_data_ptr || !pCropYuv) {
            TSK_DEBUG_ERROR("ProcessBufferForCrop:1 buffer invalidated.");
            return;
        }
        memcpy(out_data_ptr, pCropYuv, cropWidth);
        out_data_ptr += cropWidth;
        pCropYuv += width;
    }

    cropWidth /= 2;
    cropHeight /= 2;

    pCropYuv = buf + (width * height);
    pCropYuv += (width / 2) * (cropTop / 2);
    pCropYuv += cropLeft / 2;

    for (i = 0; i < cropHeight; i++) {
        if (!out_data_ptr || !pCropYuv) {
            TSK_DEBUG_ERROR("ProcessBufferForCrop:2 buffer invalidated.");
            return;
        }
        memcpy(out_data_ptr, pCropYuv, cropWidth);
        out_data_ptr += cropWidth;
        pCropYuv += width / 2;
    }

    pCropYuv = buf + (width * height) * 5 / 4;
    pCropYuv += (width / 2) * (cropTop / 2);
    pCropYuv += cropLeft / 2;

    for (i = 0; i < cropHeight; i++) {
        if (!out_data_ptr || !pCropYuv) {
            TSK_DEBUG_ERROR("ProcessBufferForCrop:3 buffer invalidated.");
            return;
        }
        memcpy(out_data_ptr, pCropYuv, cropWidth);
        out_data_ptr += cropWidth;
        pCropYuv += width / 2;
    }
}

/* Decoder config changed, re-calculate decode frame size,
 * update Doubango with changed resolution,
 * re-allocate crop buffer,
 * re-allocate input and output decoder ring buffer with changed frame size
*/
static int reset_decoder_config(tdav_codec_h264_hw_vpu_t *h264, int width, int height)
{
    if (!h264) {
        TSK_DEBUG_ERROR("reset_decoder_config: Invalid parameter");
        return -1;
    }

    int in_width = Align(TMEDIA_CODEC_VIDEO(h264)->in.width, FRAME_ALIGN);
    int in_height = Align(TMEDIA_CODEC_VIDEO(h264)->in.height, FRAME_ALIGN);
    int dec_width = h264->decoder.context->width;
    int dec_height = h264->decoder.context->height;
    int max_buffer_size = 0;
    int max_capacity = 0;

    TSK_DEBUG_INFO("reset_decoder_config: old res (%dx%d), new res (%dx%d)",
          in_width, in_height, dec_width, dec_height);

    if ((dec_width != in_width) || (dec_height != in_height)) {
        TSK_DEBUG_WARN("reset_decoder_config: Need to reconfigure decoder settings");
        h264->decoder.decode_frame_size = dec_width * dec_height * 3 / 2;
        TSK_DEBUG_WARN("reset_decoder_config: width, height (%d, %d),dec size %d",
             dec_width, dec_height, h264->decoder.decode_frame_size);

        // Update Doubango's width and height without alignment
        TMEDIA_CODEC_VIDEO(h264)->in.width = dec_width;
        TMEDIA_CODEC_VIDEO(h264)->in.height = dec_height;

        if (!(h264->decoder.crop_buffer = tsk_realloc(h264->decoder.crop_buffer, h264->decoder.decode_frame_size))) {
            TSK_DEBUG_ERROR("reset_decoder_config: Failed to re-allocate crop buffer");
            return -1;
        }

        // Free decoder input ring buffer
        ring_buffer_free(h264->decoder.in_ring_buf);

        // re-allocate decoder input ring buffer
        max_buffer_size = (dec_width * dec_height *3) >> 3;
        max_capacity = h264->decoder.in_ring_buf_cap;
        h264->decoder.in_ring_buf = ring_buffer_init(max_capacity, max_buffer_size);

        if (h264->decoder.in_ring_buf == NULL) {
            TSK_DEBUG_ERROR("Error allocating input ring buffer for decoder");
            return -1;
        }

        // Free decoder out ring buffer
        ring_buffer_free(h264->decoder.out_ring_buf);

        // re-allocate decoder output ring buffer
        max_buffer_size = (h264->decoder.context->width * h264->decoder.context->height *3) >> 1;
        max_capacity = h264->decoder.out_ring_buf_cap;
        h264->decoder.out_ring_buf = ring_buffer_init(max_capacity, max_buffer_size);
        TSK_DEBUG_INFO("h264->decoder.out_ring_buf %p", h264->decoder.out_ring_buf);

        if (h264->decoder.out_ring_buf == NULL) {
            TSK_DEBUG_ERROR("Error allocating output ring buffer for decoder");
            return -1;
        }
    }
    return 0;
}

/* Close and re-open decoder
*/
int reset_decoder(tdav_codec_h264_hw_vpu_t *h264)
{
    TSK_DEBUG_INFO("reset_decoder");
    if (!h264) {
        TSK_DEBUG_ERROR("reset_decoder: invalid parameter");
        return -1;
    }

    if (!h264->decoder.decoder_reset) {
        // Set flag to indicate decoder reset is started
        h264->decoder.decoder_reset = tsk_true;
    } else {
        TSK_DEBUG_WARN("reset_decoder: decoder reset already started");
        return 0;
    }

    // Free resources
#if 0
    if (h264->decoder.prev_frame) {
        FSL_FREE(h264->decoder.prev_frame);
        h264->decoder.prev_frame = NULL;
    }
#endif
    StateTrans(h264, OMX_StateIdle);
    StateTrans(h264, OMX_StateLoaded);
    unload_component(h264);
    h264->decoder.bClosedDecoder = OMX_TRUE;

    TSK_DEBUG_INFO("reset_decoder: opening OMX decoder");
    return open_fsl_omx_decoder(h264);
}

static void start_reset_dec_timer(tdav_codec_h264_hw_vpu_t *h264)
{
    TSK_DEBUG_INFO("start_reset_dec_timer");
    int ret;

    if (!h264) {
        TSK_DEBUG_ERROR("Invalid parameter");
        return;
    }

    // Create timer for pasuing codec after timeout period
    if (!(h264->h_timer = tsk_timer_manager_create())) {
        TSK_DEBUG_ERROR("Failed to create timer manager for h264");
        //ignore this error
        return;
    }

    if ((ret = tsk_timer_manager_start(h264->h_timer)) != 0) {
        TSK_DEBUG_ERROR("Failed to start the timer");
        //Can ignore this error
        return;
    }

    h264->timer_started = (ret == 0);

    if (h264->timer_started) {
        h264->timer_reset_dec.timeout = h264->reset_timeout_period;
        h264->timer_reset_dec.id = tsk_timer_manager_schedule(h264->h_timer, h264->timer_reset_dec.timeout, _tdav_session_h264_timer_cb, h264);
        TSK_DEBUG_INFO("Dec reset timer sheduled with timeout value %d ms", h264->reset_timeout_period);
    }
}

OMX_ERRORTYPE request_intraVOP(tdav_codec_h264_hw_vpu_t *self)
{
    if (!self) {
        TSK_DEBUG_ERROR("request_intraVOP: invalid parameter");
        return OMX_ErrorUndefined;
    }

    OMX_ERRORTYPE ret = OMX_ErrorNone;
    OMX_CONFIG_INTRAREFRESHVOPTYPE vop;

    OMX_INIT_STRUCT(&vop, OMX_CONFIG_INTRAREFRESHVOPTYPE);
    vop.nPortIndex = (OMX_U32) kPortIndexOutput; // output
    vop.IntraRefreshVOP = OMX_TRUE;

    ret = OMX_SetConfig(self->encoder.hComponent, OMX_IndexConfigVideoIntraVOPRefresh, &vop);
    if (ret != OMX_ErrorNone) {
        TSK_DEBUG_ERROR("request_intraVOP: Unable to set IntraVOPRefresh for %d", kPortIndexOutput);
    }
    TSK_DEBUG_INFO("request_intraVOP: Set IntraVOPRefresh %d", vop.IntraRefreshVOP);
    return ret;
}

bool set_bitrate_fac_GOP_size(tdav_codec_h264_hw_vpu_t *self)
{
    TSK_DEBUG_INFO("set_bitrate_fac_GOP_size");
    if (!self) {
        TSK_DEBUG_ERROR("set_bitrate_fac_GOP_size: invalid parameter");
        return false;
    }

    int in_height = TMEDIA_CODEC_VIDEO(self)->in.height;
    char propValue[PROPERTY_VALUE_MAX];
    int value = 0;

    if (in_height == HT_1080P) {
        self->encoder.bitrate_fact = LOW_MOTION;
        self->encoder.gop_size = LOW_GOP_SIZE_IN_SEC;
        if (property_get("bit_rate_fac_full_hd", propValue, NULL)) {
            value = atoi(propValue);
            if (value >= 0 && value < MAX_BR_FACT) {
                self->encoder.bitrate_fact = value;
            }
        }
        if (property_get("gop_size_full_hd", propValue, NULL)) {
            value = atoi(propValue);
            if (value >= 0 && value < MAX_GOP_SIZE_ALLOWED) {
                self->encoder.gop_size = value;
            }
        }
    } else if (in_height == HT_720P) {
        self->encoder.bitrate_fact = MED_MOTION;
        self->encoder.gop_size = LOW_GOP_SIZE_IN_SEC;
        if (property_get("bit_rate_fac_hd", propValue, NULL)) {
            value = atoi(propValue);
            if (value >= 0 && value < MAX_BR_FACT) {
                self->encoder.bitrate_fact = value;
            }
        }
        if (property_get("gop_size_hd", propValue, NULL)) {
            value = atoi(propValue);
            if (value >= 0 && value < MAX_GOP_SIZE_ALLOWED) {
                self->encoder.gop_size = value;
            }
        }
    } else if (in_height == HT_VGA) {
        self->encoder.bitrate_fact = MED_MOTION;
        self->encoder.gop_size = HIGH_GOP_SIZE_IN_SEC;
        if (property_get("bit_rate_fac_vga", propValue, NULL)) {
            value = atoi(propValue);
            if (value >= 0 && value < MAX_BR_FACT) {
                self->encoder.bitrate_fact = value;
            }
        }
        if (property_get("gop_size_vga", propValue, NULL)) {
            value = atoi(propValue);
            if (value >= 0 && value < MAX_GOP_SIZE_ALLOWED) {
                self->encoder.gop_size = value;
            }
        }
    } else if (in_height == HT_QVGA) {
        self->encoder.bitrate_fact = HIGH_MOTION;
        self->encoder.gop_size = HIGH_GOP_SIZE_IN_SEC;
        if (property_get("bit_rate_fac_qvga", propValue, NULL)) {
            value = atoi(propValue);
            if (value >= 0 && value < MAX_BR_FACT) {
                self->encoder.bitrate_fact = value;
            }
        }
        if (property_get("gop_size_qvga", propValue, NULL)) {
            value = atoi(propValue);
            if (value >= 0 && value < MAX_GOP_SIZE_ALLOWED) {
                self->encoder.gop_size = value;
            }
        }
    } else {
        self->encoder.bitrate_fact = MED_MOTION;
        self->encoder.gop_size = DEF_GOP_SIZE_IN_SEC;
        if (property_get("bit_rate_fac_default", propValue, NULL)) {
            value = atoi(propValue);
            if (value >= 0 && value < MAX_BR_FACT) {
                self->encoder.bitrate_fact = value;
            }
        }
        if (property_get("gop_size_default", propValue, NULL)) {
            value = atoi(propValue);
            if (value >= 0 && value < MAX_GOP_SIZE_ALLOWED) {
                self->encoder.gop_size = value;
            }
        }
    }
    return true;
}
#endif /* HAVE_FFMPEG */
