/*
 * Copyright (c) 2011 Intel Corporation. All Rights Reserved.
 * Copyright (c) Imagination Technologies Limited, UK
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sub license, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 *
 * The above copyright notice and this permission notice (including the
 * next paragraph) shall be included in all copies or substantial portions
 * of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
 * IN NO EVENT SHALL PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR
 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
 * Authors:
 *    Elaine Wang <elaine.wang@intel.com>
 *    Zeng Li <zeng.li@intel.com>
 *
 */


#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdint.h>
#include <string.h>

#include "psb_def.h"
#include "psb_drv_debug.h"
#include "psb_surface.h"
#include "psb_cmdbuf.h"
#include "pnw_hostcode.h"
#include "pnw_H264ES.h"
#include "pnw_hostheader.h"
#include "va/va_enc_h264.h"
#define TOPAZ_H264_MAX_BITRATE 50000000

#define INIT_CONTEXT_H264ES     context_ENC_p ctx = (context_ENC_p) obj_context->format_data
#define SURFACE(id)    ((object_surface_p) object_heap_lookup( &ctx->obj_context->driver_data->surface_heap, id ))
#define BUFFER(id)  ((object_buffer_p) object_heap_lookup( &ctx->obj_context->driver_data->buffer_heap, id ))
static void pnw_H264ES_QueryConfigAttributes(
        VAProfile __maybe_unused profile,
        VAEntrypoint __maybe_unused entrypoint,
        VAConfigAttrib *attrib_list,
        int num_attribs)
{
    int i;

    drv_debug_msg(VIDEO_DEBUG_GENERAL, "pnw_H264ES_QueryConfigAttributes\n");

    /* RateControl attributes */
    for (i = 0; i < num_attribs; i++) {
        switch (attrib_list[i].type) {
            case VAConfigAttribRTFormat:
                break;

            case VAConfigAttribRateControl:
                attrib_list[i].value = VA_RC_NONE | VA_RC_CBR | VA_RC_VBR | VA_RC_VCM;
	        break;

            case VAConfigAttribEncMaxRefFrames:
                attrib_list[i].value = 1;
                break;

	    default:
		attrib_list[i].value = VA_ATTRIB_NOT_SUPPORTED;
		break;
	}
    }
}


static VAStatus pnw_H264ES_ValidateConfig(
        object_config_p obj_config)
{
    int i;
    /* Check all attributes */
    for (i = 0; i < obj_config->attrib_count; i++) {
        switch (obj_config->attrib_list[i].type) {
            case VAConfigAttribRTFormat:
                /* Ignore */
                break;
            case VAConfigAttribRateControl:
                break;
            default:
                return VA_STATUS_ERROR_ATTR_NOT_SUPPORTED;
        }
    }

    return VA_STATUS_SUCCESS;
}


static VAStatus pnw_H264ES_CreateContext(
        object_context_p obj_context,
        object_config_p obj_config)
{
    VAStatus vaStatus = VA_STATUS_SUCCESS;
    context_ENC_p ctx;
    int i;
    unsigned int eRCmode;

    drv_debug_msg(VIDEO_DEBUG_GENERAL, "pnw_H264ES_CreateContext\n");

    vaStatus = pnw_CreateContext(obj_context, obj_config, 0);

    if (VA_STATUS_SUCCESS != vaStatus)
        return VA_STATUS_ERROR_ALLOCATION_FAILED;

    ctx = (context_ENC_p) obj_context->format_data;

    for (i = 0; i < obj_config->attrib_count; i++) {
        if (obj_config->attrib_list[i].type == VAConfigAttribRateControl)
            break;
    }

    if (i >= obj_config->attrib_count)
        eRCmode = VA_RC_NONE;
    else
        eRCmode = obj_config->attrib_list[i].value;


    if (eRCmode == VA_RC_VBR) {
        ctx->eCodec = IMG_CODEC_H264_VBR;
        ctx->sRCParams.RCEnable = IMG_TRUE;
        ctx->sRCParams.bDisableBitStuffing = IMG_FALSE;
    } else if (eRCmode == VA_RC_CBR) {
        ctx->eCodec = IMG_CODEC_H264_CBR;
        ctx->sRCParams.RCEnable = IMG_TRUE;
        ctx->sRCParams.bDisableBitStuffing = IMG_TRUE;
    } else if (eRCmode == VA_RC_NONE) {
        ctx->eCodec = IMG_CODEC_H264_NO_RC;
        ctx->sRCParams.RCEnable = IMG_FALSE;
        ctx->sRCParams.bDisableBitStuffing = IMG_FALSE;
    } else if (eRCmode == VA_RC_VCM) {
        ctx->eCodec = IMG_CODEC_H264_VCM;
        ctx->sRCParams.RCEnable = IMG_TRUE;
        ctx->sRCParams.bDisableBitStuffing = IMG_FALSE;
    } else
        return VA_STATUS_ERROR_UNSUPPORTED_RT_FORMAT;

    drv_debug_msg(VIDEO_DEBUG_GENERAL, "eCodec is %d\n", ctx->eCodec);
    ctx->eFormat = IMG_CODEC_PL12;      /* use default */

    ctx->Slices = 1;
    ctx->idr_pic_id = 1;
    ctx->buffer_size = 0;
    ctx->initial_buffer_fullness = 0;
    //initialize the frame_rate and qp
    ctx->sRCParams.FrameRate = 30;

    if (getenv("PSB_VIDEO_SIG_CORE") == NULL) {
        ctx->Slices = 2;
        ctx->NumCores = 2;
    }

    ctx->ParallelCores = min(ctx->NumCores, ctx->Slices);

    ctx->IPEControl = pnw__get_ipe_control(ctx->eCodec);

    switch (obj_config->profile) {
        case VAProfileH264Baseline:
            ctx->profile_idc = 5;
            break;
        case VAProfileH264Main:
            ctx->profile_idc = 6;
            break;
        default:
            ctx->profile_idc = 6;
            break;
    }

    return vaStatus;
}

static void pnw_H264ES_DestroyContext(
        object_context_p obj_context)
{
    drv_debug_msg(VIDEO_DEBUG_GENERAL, "pnw_H264ES_DestroyPicture\n");

    pnw_DestroyContext(obj_context);
}

static VAStatus pnw_H264ES_BeginPicture(
        object_context_p obj_context)
{
    INIT_CONTEXT_H264ES;
    VAStatus vaStatus = VA_STATUS_SUCCESS;

    drv_debug_msg(VIDEO_DEBUG_GENERAL, "pnw_H264ES_BeginPicture\n");

    vaStatus = pnw_BeginPicture(ctx);

    return vaStatus;
}

static VAStatus pnw__H264ES_process_sequence_param(context_ENC_p ctx, object_buffer_p obj_buffer)
{
    VAEncSequenceParameterBufferH264 *pSequenceParams;
    pnw_cmdbuf_p cmdbuf = ctx->obj_context->pnw_cmdbuf;
    H264_VUI_PARAMS *pVUI_Params = &(ctx->VUI_Params);
    H264_CROP_PARAMS sCrop;
    int i;
    unsigned int frame_size;
    unsigned int max_bps;

    ASSERT(obj_buffer->type == VAEncSequenceParameterBufferType);
    ASSERT(obj_buffer->num_elements == 1);
    ASSERT(obj_buffer->size == sizeof(VAEncSequenceParameterBufferH264));

    if ((obj_buffer->num_elements != 1) ||
            (obj_buffer->size != sizeof(VAEncSequenceParameterBufferH264))) {
        return VA_STATUS_ERROR_UNKNOWN;
    }

    if(ctx->sRCParams.FrameRate == 0)
        ctx->sRCParams.FrameRate = 30;
    ctx->obj_context->frame_count = 0;

    pSequenceParams = (VAEncSequenceParameterBufferH264 *) obj_buffer->buffer_data;
    obj_buffer->buffer_data = NULL;
    obj_buffer->size = 0;

    if (!pSequenceParams->bits_per_second) {
        pSequenceParams->bits_per_second = ctx->Height * ctx->Width * 30 * 12;
        drv_debug_msg(VIDEO_DEBUG_GENERAL, "bits_per_second is 0, set to %d\n",
                pSequenceParams->bits_per_second);
    }
    ctx->sRCParams.bBitrateChanged =
        (pSequenceParams->bits_per_second == ctx->sRCParams.BitsPerSecond ?
         IMG_FALSE : IMG_TRUE);

    if (pSequenceParams->bits_per_second > TOPAZ_H264_MAX_BITRATE) {
        ctx->sRCParams.BitsPerSecond = TOPAZ_H264_MAX_BITRATE;
        drv_debug_msg(VIDEO_DEBUG_GENERAL, " bits_per_second(%d) exceeds \
                the maximum bitrate, set it with %d\n",
                pSequenceParams->bits_per_second,
                TOPAZ_H264_MAX_BITRATE);
    }

    /* According to Table A-1 Level limits, if resolution is bigger than 625SD,
       min compression ratio is 4, otherwise min compression ratio is 2 */
    max_bps =  (ctx->Width * ctx->Height * 3 / 2 ) * 8 * ctx->sRCParams.FrameRate;
    if (ctx->Width > 720)
        max_bps /= 4;
    else
        max_bps /= 2;

    drv_debug_msg(VIDEO_DEBUG_GENERAL, " width %d height %d, frame rate %d\n",
            ctx->Width, ctx->Height, ctx->sRCParams.FrameRate);
    if (pSequenceParams->bits_per_second > max_bps) {
        drv_debug_msg(VIDEO_DEBUG_ERROR,
                "Invalid bitrate %d, violate ITU-T Rec. H.264 (03/2005) A.3.1"
                "\n clip to %d bps\n", pSequenceParams->bits_per_second, max_bps);
        ctx->sRCParams.BitsPerSecond = max_bps;
    } else {
        /* See 110% target bitrate for VCM. Otherwise, the resulted bitrate is much lower
           than target bitrate */
        if (ctx->eCodec == IMG_CODEC_H264_VCM)
            pSequenceParams->bits_per_second =
                pSequenceParams->bits_per_second / 100 * 110;

        drv_debug_msg(VIDEO_DEBUG_GENERAL, "Bitrate is set to %d\n",
                pSequenceParams->bits_per_second);
        ctx->sRCParams.BitsPerSecond = pSequenceParams->bits_per_second;
    }

    /*if (ctx->sRCParams.IntraFreq != pSequenceParams->intra_period)
      ctx->sRCParams.bBitrateChanged = IMG_TRUE;*/
    ctx->sRCParams.IDRFreq = pSequenceParams->intra_idr_period;

    ctx->sRCParams.Slices = ctx->Slices;
    ctx->sRCParams.QCPOffset = 0;

    if (ctx->sRCParams.IntraFreq != pSequenceParams->intra_period
            && ctx->raw_frame_count != 0
            && ctx->sRCParams.IntraFreq != 0
            && ((ctx->obj_context->frame_count + 1) % ctx->sRCParams.IntraFreq) != 0
            && (!ctx->sRCParams.bDisableFrameSkipping)) {
        drv_debug_msg(VIDEO_DEBUG_ERROR,
                "Changing intra period value in the middle of a GOP is\n"
                "not allowed if frame skip isn't disabled.\n"
                "it can cause I frame been skipped\n");
        free(pSequenceParams);
        return VA_STATUS_ERROR_INVALID_PARAMETER;
    }
    else
        ctx->sRCParams.IntraFreq = pSequenceParams->intra_period;

    frame_size = ctx->sRCParams.BitsPerSecond / ctx->sRCParams.FrameRate;

    if (ctx->bInserHRDParams &&
            ctx->buffer_size != 0 && ctx->initial_buffer_fullness != 0) {
        ctx->sRCParams.BufferSize = ctx->buffer_size;
        ctx->sRCParams.InitialLevel = ctx->buffer_size - ctx->initial_buffer_fullness;
        ctx->sRCParams.InitialDelay = ctx->initial_buffer_fullness;
    }
    else {
        ctx->buffer_size = ctx->sRCParams.BitsPerSecond;
        ctx->initial_buffer_fullness = ctx->sRCParams.BitsPerSecond;
        ctx->sRCParams.BufferSize = ctx->buffer_size;
        ctx->sRCParams.InitialLevel = (3 * ctx->sRCParams.BufferSize) >> 4;
        /* Aligned with target frame size */
        ctx->sRCParams.InitialLevel += (frame_size / 2);
        ctx->sRCParams.InitialLevel /= frame_size;
        ctx->sRCParams.InitialLevel *= frame_size;
        ctx->sRCParams.InitialDelay = ctx->buffer_size - ctx->sRCParams.InitialLevel;
    }

    if (ctx->raw_frame_count == 0) {
        for (i = (ctx->ParallelCores - 1); i >= 0; i--)
            pnw_set_bias(ctx, i);
    }

    pVUI_Params->bit_rate_value_minus1 = ctx->sRCParams.BitsPerSecond / 64 - 1;
    pVUI_Params->cbp_size_value_minus1 = ctx->sRCParams.BufferSize / 64 - 1;
    if (IMG_CODEC_H264_CBR != ctx->eCodec ||
            ctx->sRCParams.bDisableBitStuffing ||
            ctx->sRCParams.bDisableFrameSkipping)
        pVUI_Params->CBR = 0;
    else
        pVUI_Params->CBR = 1;

    pVUI_Params->initial_cpb_removal_delay_length_minus1 = BPH_SEI_NAL_INITIAL_CPB_REMOVAL_DELAY_SIZE - 1;
    pVUI_Params->cpb_removal_delay_length_minus1 = PTH_SEI_NAL_CPB_REMOVAL_DELAY_SIZE - 1;
    pVUI_Params->dpb_output_delay_length_minus1 = PTH_SEI_NAL_DPB_OUTPUT_DELAY_SIZE - 1;
    pVUI_Params->time_offset_length = 24;
    ctx->bInsertVUI = pSequenceParams->vui_parameters_present_flag ? IMG_TRUE: IMG_FALSE;
    if (ctx->bInsertVUI) {
        if (pSequenceParams->num_units_in_tick !=0 && pSequenceParams->time_scale !=0
                && (pSequenceParams->time_scale > pSequenceParams->num_units_in_tick) ) {
            pVUI_Params->Time_Scale = pSequenceParams->time_scale;
            pVUI_Params->num_units_in_tick = pSequenceParams->num_units_in_tick;
        }
        else {
            pVUI_Params->num_units_in_tick = 1;
            pVUI_Params->Time_Scale = ctx->sRCParams.FrameRate * 2;
        }
    }

    if (ctx->bInsertVUI && pSequenceParams->vui_fields.bits.aspect_ratio_info_present_flag &&
            (pSequenceParams->aspect_ratio_idc == 0xff /* Extended_SAR */)) {
        pVUI_Params->aspect_ratio_info_present_flag = IMG_TRUE;
        pVUI_Params->aspect_ratio_idc = 0xff;
        pVUI_Params->sar_width = pSequenceParams->sar_width;
        pVUI_Params->sar_height = pSequenceParams->sar_height;
    }

    sCrop.bClip = pSequenceParams->frame_cropping_flag;
    sCrop.LeftCropOffset = 0;
    sCrop.RightCropOffset = 0;
    sCrop.TopCropOffset = 0;
    sCrop.BottomCropOffset = 0;

    if (!sCrop.bClip) {
        if (ctx->RawHeight & 0xf) {
            sCrop.bClip = IMG_TRUE;
            sCrop.BottomCropOffset = (((ctx->RawHeight + 0xf) & (~0xf)) - ctx->RawHeight) / 2;
        }
        if (ctx->RawWidth & 0xf) {
            sCrop.bClip = IMG_TRUE;
            sCrop.RightCropOffset = (((ctx->RawWidth + 0xf) & (~0xf)) - ctx->RawWidth) / 2;
        }
    } else {
        sCrop.LeftCropOffset = pSequenceParams->frame_crop_left_offset;
        sCrop.RightCropOffset = pSequenceParams->frame_crop_right_offset;
        sCrop.TopCropOffset = pSequenceParams->frame_crop_top_offset;
        sCrop.BottomCropOffset = pSequenceParams->frame_crop_bottom_offset;
    }
    /* sequence header is always inserted */

    memset(cmdbuf->header_mem_p + ctx->seq_header_ofs,
            0,
            HEADER_SIZE);

    /*
       if (ctx->bInserHRDParams) {
       memset(cmdbuf->header_mem_p + ctx->aud_header_ofs,
       0,
       HEADER_SIZE);

       pnw__H264_prepare_AUD_header(cmdbuf->header_mem_p + ctx->aud_header_ofs);
       pnw_cmdbuf_insert_command_package(ctx->obj_context,
       ctx->ParallelCores - 1,
       MTX_CMDID_DO_HEADER,
       &cmdbuf->header_mem,
       ctx->aud_header_ofs);
       }
     */
    if (ctx->eCodec == IMG_CODEC_H264_NO_RC)
        pnw__H264_prepare_sequence_header(cmdbuf->header_mem_p + ctx->seq_header_ofs,
                pSequenceParams->picture_width_in_mbs,
                pSequenceParams->picture_height_in_mbs,
                pSequenceParams->vui_parameters_present_flag,
                pSequenceParams->vui_parameters_present_flag ? (pVUI_Params) : NULL,
                &sCrop,
                pSequenceParams->level_idc, ctx->profile_idc);
    else
        pnw__H264_prepare_sequence_header(cmdbuf->header_mem_p + ctx->seq_header_ofs,
                pSequenceParams->picture_width_in_mbs,
                pSequenceParams->picture_height_in_mbs,
                pSequenceParams->vui_parameters_present_flag,
                pSequenceParams->vui_parameters_present_flag ? (pVUI_Params) : NULL,
                &sCrop,
                pSequenceParams->level_idc, ctx->profile_idc);

    /*Periodic IDR need SPS. We save the sequence header here*/
    if (ctx->sRCParams.IDRFreq != 0) {
        if (NULL == ctx->save_seq_header_p) {
            ctx->save_seq_header_p = malloc(HEADER_SIZE);
            if (NULL == ctx->save_seq_header_p) {
                drv_debug_msg(VIDEO_DEBUG_ERROR, "Ran out of memory!\n");
                free(pSequenceParams);
                return VA_STATUS_ERROR_ALLOCATION_FAILED;
            }
            memcpy((unsigned char *)ctx->save_seq_header_p,
                    (unsigned char *)(cmdbuf->header_mem_p + ctx->seq_header_ofs),
                    HEADER_SIZE);
        }
    }
    ctx->none_vcl_nal++;
    cmdbuf->cmd_idx_saved[PNW_CMDBUF_SEQ_HEADER_IDX] = cmdbuf->cmd_idx;
    /* Send to the last core as this will complete first */
    pnw_cmdbuf_insert_command_package(ctx->obj_context,
            ctx->ParallelCores - 1,
            MTX_CMDID_DO_HEADER,
            &cmdbuf->header_mem,
            ctx->seq_header_ofs);
    free(pSequenceParams);

    return VA_STATUS_SUCCESS;
}


static VAStatus pnw__H264ES_insert_SEI_buffer_period(context_ENC_p ctx)
{
    unsigned int ui32nal_initial_cpb_removal_delay;
    unsigned int ui32nal_initial_cpb_removal_delay_offset;
    pnw_cmdbuf_p cmdbuf = ctx->obj_context->pnw_cmdbuf;

    ui32nal_initial_cpb_removal_delay =
        90000 * (1.0 * ctx->sRCParams.InitialDelay / ctx->sRCParams.BitsPerSecond);
    ui32nal_initial_cpb_removal_delay_offset =
        90000 * (1.0 * ctx->buffer_size / ctx->sRCParams.BitsPerSecond)
        - ui32nal_initial_cpb_removal_delay;

    drv_debug_msg(VIDEO_DEBUG_GENERAL, "Insert SEI buffer period message with "
            "ui32nal_initial_cpb_removal_delay(%d) and "
            "ui32nal_initial_cpb_removal_delay_offset(%d)\n",
            ui32nal_initial_cpb_removal_delay,
            ui32nal_initial_cpb_removal_delay_offset);

    memset(cmdbuf->header_mem_p + ctx->sei_buf_prd_ofs,
            0,
            HEADER_SIZE);

    pnw__H264_prepare_SEI_buffering_period_header(
            (MTX_HEADER_PARAMS *)(cmdbuf->header_mem_p + ctx->sei_buf_prd_ofs),
            1, //ui8NalHrdBpPresentFlag,
            0, //ui8nal_cpb_cnt_minus1,
            1 + ctx->VUI_Params.initial_cpb_removal_delay_length_minus1, //ui8nal_initial_cpb_removal_delay_length,
            ui32nal_initial_cpb_removal_delay, //ui32nal_initial_cpb_removal_delay,
            ui32nal_initial_cpb_removal_delay_offset, //ui32nal_initial_cpb_removal_delay_offset,
            0, //ui8VclHrdBpPresentFlag,
            NOT_USED_BY_TOPAZ, //ui8vcl_cpb_cnt_minus1,
            0, //ui32vcl_initial_cpb_removal_delay,
            0 //ui32vcl_initial_cpb_removal_delay_offset
            );
    cmdbuf->cmd_idx_saved[PNW_CMDBUF_SEI_BUF_PERIOD_IDX] = cmdbuf->cmd_idx;
    pnw_cmdbuf_insert_command_package(ctx->obj_context,
            ctx->ParallelCores - 1,
            MTX_CMDID_DO_HEADER,
            &cmdbuf->header_mem,
            ctx->sei_buf_prd_ofs);

    ctx->none_vcl_nal++;
    return VA_STATUS_SUCCESS;
}


static VAStatus pnw__H264ES_insert_SEI_pic_timing(context_ENC_p ctx)
{
    pnw_cmdbuf_p cmdbuf = ctx->obj_context->pnw_cmdbuf;
    uint32_t ui32cpb_removal_delay;

    drv_debug_msg(VIDEO_DEBUG_GENERAL, "Insert SEI picture timing message. \n");

    memset(cmdbuf->header_mem_p + ctx->sei_pic_tm_ofs,
            0,
            HEADER_SIZE);

    /* ui32cpb_removal_delay is zero for 1st frame and will be reset
     * after a IDR frame */
    if (ctx->obj_context->frame_count == 0) {
        if (ctx->raw_frame_count == 0)
            ui32cpb_removal_delay = 0;
        else
            ui32cpb_removal_delay =
                ctx->sRCParams.IDRFreq * ctx->sRCParams.IntraFreq * 2;
    } else
        ui32cpb_removal_delay = 2 * ctx->obj_context->frame_count;

    pnw__H264_prepare_SEI_picture_timing_header(
            (MTX_HEADER_PARAMS *)(cmdbuf->header_mem_p + ctx->sei_pic_tm_ofs),
            1,
            ctx->VUI_Params.cpb_removal_delay_length_minus1,
            ctx->VUI_Params.dpb_output_delay_length_minus1,
            ui32cpb_removal_delay, //ui32cpb_removal_delay,
            2, //ui32dpb_output_delay,
            0, //ui8pic_struct_present_flag,
            0, //ui8pic_struct,
            0, //ui8NumClockTS,
            0, //*aui8clock_timestamp_flag,
            0, //ui8full_timestamp_flag,
            0, //ui8seconds_flag,
            0, //ui8minutes_flag,
            0, //ui8hours_flag,
            0, //ui8seconds_value,
            0, //ui8minutes_value,
            0, //ui8hours_value,
            0, //ui8ct_type,
            0, //ui8nuit_field_based_flag,
            0, //ui8counting_type,
            0, //ui8discontinuity_flag,
            0, //ui8cnt_dropped_flag,
            0, //ui8n_frames,
            0, //ui8time_offset_length,
            0 //i32time_offset)
                );

    cmdbuf->cmd_idx_saved[PNW_CMDBUF_SEI_PIC_TIMING_IDX] = cmdbuf->cmd_idx;
    pnw_cmdbuf_insert_command_package(ctx->obj_context,
            ctx->ParallelCores - 1,
            MTX_CMDID_DO_HEADER,
            &cmdbuf->header_mem,
            ctx->sei_pic_tm_ofs);

    ctx->none_vcl_nal++;
    return VA_STATUS_SUCCESS;
}

#if PSB_MFLD_DUMMY_CODE
static VAStatus pnw__H264ES_insert_SEI_FPA_param(context_ENC_p ctx, object_buffer_p obj_buffer)
{
    VAStatus vaStatus = VA_STATUS_SUCCESS;
    pnw_cmdbuf_p cmdbuf = ctx->obj_context->pnw_cmdbuf;
    VAEncPackedHeaderParameterBuffer *sei_param_buf = (VAEncPackedHeaderParameterBuffer *)obj_buffer->buffer_data;

    drv_debug_msg(VIDEO_DEBUG_GENERAL, "Insert SEI frame packing arrangement message. \n");
    ctx->sei_pic_data_size = sei_param_buf->bit_length/8;

    return VA_STATUS_SUCCESS;
}

static VAStatus pnw__H264ES_insert_SEI_FPA_data(context_ENC_p ctx, object_buffer_p obj_buffer)
{
    VAStatus vaStatus = VA_STATUS_SUCCESS;
    pnw_cmdbuf_p cmdbuf = ctx->obj_context->pnw_cmdbuf;
    char *sei_data_buf;

    drv_debug_msg(VIDEO_DEBUG_GENERAL, "Insert SEI frame packing arrangement message. \n");

    memset(cmdbuf->header_mem_p + ctx->sei_pic_fpa_ofs,
            0,
            HEADER_SIZE);
    sei_data_buf = (char *)obj_buffer->buffer_data;

    pnw__H264_prepare_SEI_FPA_header((MTX_HEADER_PARAMS *)(cmdbuf->header_mem_p + ctx->sei_pic_fpa_ofs), sei_data_buf, ctx->sei_pic_data_size);
    pnw_cmdbuf_insert_command_package(ctx->obj_context,
            ctx->ParallelCores - 1,
            MTX_CMDID_DO_HEADER,
            &cmdbuf->header_mem,
            ctx->sei_pic_fpa_ofs);

    return VA_STATUS_SUCCESS;
}
#endif

static VAStatus pnw__H264ES_process_picture_param(context_ENC_p ctx, object_buffer_p obj_buffer)
{
    VAStatus vaStatus = VA_STATUS_SUCCESS;
    int i;
    VAEncPictureParameterBufferH264 *pBuffer;
    int need_sps = 0;
    pnw_cmdbuf_p cmdbuf = ctx->obj_context->pnw_cmdbuf;

    ASSERT(obj_buffer->type == VAEncPictureParameterBufferType);

    if ((obj_buffer->num_elements != 1) ||
            (obj_buffer->size != sizeof(VAEncPictureParameterBufferH264))) {
        return VA_STATUS_ERROR_UNKNOWN;
    }

    /* Transfer ownership of VAEncPictureParameterBufferH264 data */
    pBuffer = (VAEncPictureParameterBufferH264 *) obj_buffer->buffer_data;
    obj_buffer->buffer_data = NULL;
    obj_buffer->size = 0;

    ctx->ref_surface = SURFACE(pBuffer->ReferenceFrames[0].picture_id);
    ctx->dest_surface = SURFACE(pBuffer->CurrPic.picture_id);
    ctx->coded_buf = BUFFER(pBuffer->coded_buf);

    //ASSERT(ctx->Width == pBuffer->picture_width);
    //ASSERT(ctx->Height == pBuffer->picture_height);

    if (NULL == ctx->coded_buf) {
        drv_debug_msg(VIDEO_DEBUG_ERROR, "%s L%d Invalid coded buffer handle\n", __FUNCTION__, __LINE__);
        free(pBuffer);
        return VA_STATUS_ERROR_INVALID_BUFFER;
    }
    if ((ctx->sRCParams.IntraFreq != 0) && (ctx->sRCParams.IDRFreq != 0)) { /* period IDR is desired */
        unsigned int is_intra = 0;
        unsigned int intra_cnt = 0;

        ctx->force_idr_h264 = 0;

        if ((ctx->obj_context->frame_count % ctx->sRCParams.IntraFreq) == 0) {
            is_intra = 1; /* suppose current frame is I frame */
            intra_cnt = ctx->obj_context->frame_count / ctx->sRCParams.IntraFreq;
        }

        /* current frame is I frame (suppose), and an IDR frame is desired*/
        if ((is_intra) && ((intra_cnt % ctx->sRCParams.IDRFreq) == 0)) {
            ctx->force_idr_h264 = 1;
            /*When two consecutive access units in decoding order are both IDR access
             * units, the value of idr_pic_id in the slices of the first such IDR
             * access unit shall differ from the idr_pic_id in the second such IDR
             * access unit. We set it with 1 or 0 alternately.*/
            ctx->idr_pic_id = 1 - ctx->idr_pic_id;

            /* it is periodic IDR in the middle of one sequence encoding, need SPS */
            if (ctx->obj_context->frame_count > 0)
                need_sps = 1;

            ctx->obj_context->frame_count = 0;
        }
    }

    /* If VUI header isn't enabled, we'll igore the request for HRD header insertion */
    if (ctx->bInserHRDParams)
        ctx->bInserHRDParams = ctx->bInsertVUI;

    /* For H264, PicHeader only needed in the first picture*/
    if (!(ctx->obj_context->frame_count)) {
        cmdbuf = ctx->obj_context->pnw_cmdbuf;

        if (need_sps) {
            drv_debug_msg(VIDEO_DEBUG_GENERAL, "TOPAZ: insert a SPS before IDR frame\n");
            /* reuse the previous SPS */
            memcpy((unsigned char *)(cmdbuf->header_mem_p + ctx->seq_header_ofs),
                    (unsigned char *)ctx->save_seq_header_p,
                    HEADER_SIZE);

            cmdbuf->cmd_idx_saved[PNW_CMDBUF_SEQ_HEADER_IDX] = cmdbuf->cmd_idx;
            /* Send to the last core as this will complete first */
            pnw_cmdbuf_insert_command_package(ctx->obj_context,
                    ctx->ParallelCores - 1,
                    MTX_CMDID_DO_HEADER,
                    &cmdbuf->header_mem,
                    ctx->seq_header_ofs);
            ctx->none_vcl_nal++;
        }

        if (ctx->bInserHRDParams) {
            pnw__H264ES_insert_SEI_buffer_period(ctx);
            pnw__H264ES_insert_SEI_pic_timing(ctx);
        }

        pnw__H264_prepare_picture_header(cmdbuf->header_mem_p + ctx->pic_header_ofs, IMG_FALSE, ctx->sRCParams.QCPOffset);

        cmdbuf->cmd_idx_saved[PNW_CMDBUF_PIC_HEADER_IDX] = cmdbuf->cmd_idx;
        /* Send to the last core as this will complete first */
        pnw_cmdbuf_insert_command_package(ctx->obj_context,
                ctx->ParallelCores - 1,
                MTX_CMDID_DO_HEADER,
                &cmdbuf->header_mem,
                ctx->pic_header_ofs);
        ctx->none_vcl_nal++;
    }
    else if (ctx->bInserHRDParams)
        pnw__H264ES_insert_SEI_pic_timing(ctx);

    if (ctx->ParallelCores == 1) {
        ctx->coded_buf_per_slice = 0;
        drv_debug_msg(VIDEO_DEBUG_GENERAL, "TOPAZ: won't splite coded buffer(%d) since only one slice being encoded\n",
                ctx->coded_buf->size);
    } else {
        /*Make sure DMA start is 128bits alignment*/
        ctx->coded_buf_per_slice = (ctx->coded_buf->size / ctx->ParallelCores) & (~0xf) ;
        drv_debug_msg(VIDEO_DEBUG_GENERAL, "TOPAZ: the size of coded_buf per slice %d( Total %d) \n", ctx->coded_buf_per_slice,
                ctx->coded_buf->size);
    }

    /* Prepare START_PICTURE params */
    /* FIXME is really need multiple picParams? Need multiple calculate for each? */
    for (i = (ctx->ParallelCores - 1); i >= 0; i--)
        vaStatus = pnw_RenderPictureParameter(ctx, i);

    free(pBuffer);
    return vaStatus;
}

static VAStatus pnw__H264ES_encode_one_slice(context_ENC_p ctx,
        VAEncSliceParameterBuffer *pBuffer)
{
    pnw_cmdbuf_p cmdbuf = ctx->obj_context->pnw_cmdbuf;
    unsigned int MBSkipRun, FirstMBAddress;
    unsigned char deblock_idc;
    unsigned char is_intra = 0;
    int slice_param_idx;
    PIC_PARAMS *psPicParams = (PIC_PARAMS *)(cmdbuf->pic_params_p);
    VAStatus vaStatus = VA_STATUS_SUCCESS;

    /*Slice encoding Order:
     *1.Insert Do header command
     *2.setup InRowParams
     *3.setup Slice params
     *4.Insert Do slice command
     * */

    if (pBuffer->slice_height > (ctx->Height / 16) ||
           pBuffer->start_row_number > (ctx->Height / 16) ||
           (pBuffer->slice_height + pBuffer->start_row_number) > (ctx->Height / 16)) {
        drv_debug_msg(VIDEO_DEBUG_ERROR, "slice height %d or start row number %d is too large",
                pBuffer->slice_height,  pBuffer->start_row_number);
        return VA_STATUS_ERROR_INVALID_PARAMETER;
    }
    MBSkipRun = (pBuffer->slice_height * ctx->Width) / 16;
    deblock_idc = pBuffer->slice_flags.bits.disable_deblocking_filter_idc;

    /*If the frame is skipped, it shouldn't be a I frame*/
    if (ctx->force_idr_h264 || (ctx->obj_context->frame_count == 0)) {
        is_intra = 1;
    } else
        is_intra = (ctx->sRCParams.RCEnable && ctx->sRCParams.FrameSkip) ? 0 : pBuffer->slice_flags.bits.is_intra;

    FirstMBAddress = (pBuffer->start_row_number * ctx->Width) / 16;

    memset(cmdbuf->header_mem_p + ctx->slice_header_ofs
            + ctx->obj_context->slice_count * HEADER_SIZE,
            0,
            HEADER_SIZE);

    /* Insert Do Header command, relocation is needed */
    pnw__H264_prepare_slice_header(cmdbuf->header_mem_p + ctx->slice_header_ofs
            + ctx->obj_context->slice_count * HEADER_SIZE,
            is_intra,
            pBuffer->slice_flags.bits.disable_deblocking_filter_idc,
            ctx->obj_context->frame_count,
            FirstMBAddress,
            MBSkipRun,
            0,
            ctx->force_idr_h264,
            IMG_FALSE,
            IMG_FALSE,
            ctx->idr_pic_id);

    /* ensure that this slice is consequtive to that last processed by the target core */
    /*
       ASSERT( -1 == ctx->LastSliceNum[ctx->SliceToCore]
       || ctx->obj_context->slice_count == 1 + ctx->LastSliceNum[ctx->SliceToCore] );
     */
    /* note the slice number the target core is now processing */
    ctx->LastSliceNum[ctx->SliceToCore] = ctx->obj_context->slice_count;

    pnw_cmdbuf_insert_command_package(ctx->obj_context,
            ctx->SliceToCore,
            MTX_CMDID_DO_HEADER,
            &cmdbuf->header_mem,
            ctx->slice_header_ofs + ctx->obj_context->slice_count * HEADER_SIZE);
    if (!(ctx->sRCParams.RCEnable && ctx->sRCParams.FrameSkip)) {
        /*Only reset on the first frame. It's more effective than DDK. Have confirmed with IMG*/
        if (ctx->obj_context->frame_count == 0)
            pnw_reset_encoder_params(ctx);
        if ((pBuffer->start_row_number == 0) && pBuffer->slice_flags.bits.is_intra) {
            ctx->BelowParamsBufIdx = (ctx->BelowParamsBufIdx + 1) & 0x1;
        }

        slice_param_idx = (pBuffer->slice_flags.bits.is_intra ? 0 : 1) * ctx->slice_param_num
            + ctx->obj_context->slice_count;
        if (VAEncSliceParameter_Equal(&ctx->slice_param_cache[slice_param_idx], pBuffer) == 0) {
            /* cache current param parameters */
            memcpy(&ctx->slice_param_cache[slice_param_idx],
                    pBuffer, sizeof(VAEncSliceParameterBuffer));

            /* Setup InParams value*/
            pnw_setup_slice_params(ctx,
                    pBuffer->start_row_number * 16,
                    pBuffer->slice_height * 16,
                    pBuffer->slice_flags.bits.is_intra,
                    ctx->obj_context->frame_count > 0,
                    psPicParams->sInParams.SeInitQP);
        }

        /* Insert do slice command and setup related buffer value */
        pnw__send_encode_slice_params(ctx,
                pBuffer->slice_flags.bits.is_intra,
                pBuffer->start_row_number * 16,
                deblock_idc,
                ctx->obj_context->frame_count,
                pBuffer->slice_height * 16,
                ctx->obj_context->slice_count);

        drv_debug_msg(VIDEO_DEBUG_GENERAL, "Now frame_count/slice_count is %d/%d\n",
                ctx->obj_context->frame_count, ctx->obj_context->slice_count);
    }
    ctx->obj_context->slice_count++;

    return vaStatus;
}

/* convert from VAEncSliceParameterBufferH264  to VAEncSliceParameterBuffer */
static VAStatus pnw__convert_sliceparameter_buffer(VAEncSliceParameterBufferH264 *pBufferH264,
                                                   VAEncSliceParameterBuffer *pBuffer,
                                                   int picture_width_in_mbs,
                                                   unsigned int num_elemenent)
{
    unsigned int i;

    for (i = 0; i < num_elemenent; i++) {
        pBuffer->start_row_number = pBufferH264->macroblock_address / picture_width_in_mbs;
        pBuffer->slice_height =  pBufferH264->num_macroblocks / picture_width_in_mbs;
        pBuffer->slice_flags.bits.is_intra =
            (((pBufferH264->slice_type == 2) || (pBufferH264->slice_type == 7)) ? 1 : 0);
        pBuffer->slice_flags.bits.disable_deblocking_filter_idc =  pBufferH264->disable_deblocking_filter_idc;

        /* next conversion */
        pBuffer++;
        pBufferH264++;
    }

    return 0;
}

static VAStatus pnw__H264ES_process_slice_param(context_ENC_p ctx, object_buffer_p obj_buffer)
{
    /* Prepare InParams for macros of current slice, insert slice header, insert do slice command */
    VAEncSliceParameterBuffer *pBuf_per_core = NULL, *pBuffer = NULL;
    pnw_cmdbuf_p cmdbuf = ctx->obj_context->pnw_cmdbuf;
    PIC_PARAMS *psPicParams = (PIC_PARAMS *)(cmdbuf->pic_params_p);
    bool pBufferAlloced = false;
    unsigned int i, j, slice_per_core;
    VAStatus vaStatus = VA_STATUS_SUCCESS;

    ASSERT(obj_buffer->type == VAEncSliceParameterBufferType);

    if (obj_buffer->num_elements > (ctx->Height / 16)) {
        vaStatus = VA_STATUS_ERROR_INVALID_PARAMETER;
        goto out2;
    }
    cmdbuf = ctx->obj_context->pnw_cmdbuf;
    psPicParams = (PIC_PARAMS *)cmdbuf->pic_params_p;

    /* Transfer ownership of VAEncPictureParameterBuffer data */
    if (obj_buffer->size == sizeof(VAEncSliceParameterBufferH264)) {
        drv_debug_msg(VIDEO_DEBUG_GENERAL, "Receive VAEncSliceParameterBufferH264 buffer");
        pBuffer = calloc(obj_buffer->num_elements, sizeof(VAEncSliceParameterBuffer));

        if (pBuffer == NULL) {
            drv_debug_msg(VIDEO_DEBUG_ERROR, "Run out of memory!\n");
            vaStatus = VA_STATUS_ERROR_ALLOCATION_FAILED;
            goto out2;
        }

        pBufferAlloced = true;

        pnw__convert_sliceparameter_buffer((VAEncSliceParameterBufferH264 *)obj_buffer->buffer_data,
                                           pBuffer,
                                           ctx->Width / 16,
                                           obj_buffer->num_elements);
    } else if (obj_buffer->size == sizeof(VAEncSliceParameterBuffer)) {
        drv_debug_msg(VIDEO_DEBUG_GENERAL, "Receive VAEncSliceParameterBuffer buffer");
        pBuffer = (VAEncSliceParameterBuffer *) obj_buffer->buffer_data;
    } else {
        drv_debug_msg(VIDEO_DEBUG_ERROR, "Buffer size(%d) is wrong. It should be %d or %d\n",
                obj_buffer->size, sizeof(VAEncSliceParameterBuffer),
                sizeof(VAEncSliceParameterBufferH264));
        vaStatus = VA_STATUS_ERROR_INVALID_PARAMETER;
        goto out2;
    }

    obj_buffer->size = 0;

    /*In case the slice number changes*/
    if ((ctx->slice_param_cache != NULL) && (obj_buffer->num_elements != ctx->slice_param_num)) {
        drv_debug_msg(VIDEO_DEBUG_GENERAL, "Slice number changes. Previous value is %d. Now it's %d\n",
                ctx->slice_param_num, obj_buffer->num_elements);
        free(ctx->slice_param_cache);
        ctx->slice_param_cache = NULL;
        ctx->slice_param_num = 0;
    }

    if (NULL == ctx->slice_param_cache) {
        ctx->slice_param_num = obj_buffer->num_elements;
        drv_debug_msg(VIDEO_DEBUG_GENERAL, "Allocate %d VAEncSliceParameterBuffer cache buffers\n", 2 * ctx->slice_param_num);
        ctx->slice_param_cache = calloc(2 * ctx->slice_param_num, sizeof(VAEncSliceParameterBuffer));
        if (NULL == ctx->slice_param_cache) {
            drv_debug_msg(VIDEO_DEBUG_ERROR, "Run out of memory!\n");

            /* free the converted VAEncSliceParameterBuffer */
            if (pBufferAlloced)
                free(pBuffer);
            free(obj_buffer->buffer_data);
            return VA_STATUS_ERROR_ALLOCATION_FAILED;
        }
    }

    ctx->sRCParams.Slices = obj_buffer->num_elements;
    if (getenv("PSB_VIDEO_SIG_CORE") == NULL) {
        if ((ctx->ParallelCores == 2) && (obj_buffer->num_elements == 1)) {
            /*Need to replace unneccesary MTX_CMDID_STARTPICs with MTX_CMDID_PAD*/
            for (i = 0; i < (ctx->ParallelCores - 1); i++) {
                *(cmdbuf->cmd_idx_saved[PNW_CMDBUF_START_PIC_IDX] + i * 4) &= (~MTX_CMDWORD_ID_MASK);
                *(cmdbuf->cmd_idx_saved[PNW_CMDBUF_START_PIC_IDX] + i * 4) |= MTX_CMDID_PAD;
            }
            drv_debug_msg(VIDEO_DEBUG_GENERAL, " Remove unneccesary %d MTX_CMDID_STARTPIC commands from cmdbuf\n",
                    ctx->ParallelCores - obj_buffer->num_elements);
            ctx->ParallelCores = obj_buffer->num_elements;

            /* All header generation commands should be send to core 0*/
            for (i = PNW_CMDBUF_SEQ_HEADER_IDX; i < PNW_CMDBUF_SAVING_MAX; i++) {
                if (cmdbuf->cmd_idx_saved[i] != 0)
                    *(cmdbuf->cmd_idx_saved[i]) &=
                        ~(MTX_CMDWORD_CORE_MASK << MTX_CMDWORD_CORE_SHIFT);
            }

            ctx->SliceToCore = ctx->ParallelCores - 1;
        }
    }

    slice_per_core = obj_buffer->num_elements / ctx->ParallelCores;
    pBuf_per_core = pBuffer;
    for (i = 0; i < slice_per_core; i++) {
        pBuffer = pBuf_per_core;
        for (j = 0; j < ctx->ParallelCores; j++) {
            vaStatus = pnw__H264ES_encode_one_slice(ctx, pBuffer);
            if (vaStatus != VA_STATUS_SUCCESS)
                goto out1;
            if (0 == ctx->SliceToCore) {
                ctx->SliceToCore = ctx->ParallelCores;
            }
            ctx->SliceToCore--;

            ASSERT(ctx->obj_context->slice_count < MAX_SLICES_PER_PICTURE);
            /*Move to the next buffer which will be sent to core j*/
            pBuffer += slice_per_core;
        }
        pBuf_per_core++; /* Move to the next buffer */
    }

    /*Cope with last slice when slice number is odd and parallelCores is even*/
    if (obj_buffer->num_elements > (slice_per_core * ctx->ParallelCores)) {
        ctx->SliceToCore = 0;
        pBuffer -= slice_per_core;
        pBuffer ++;
        vaStatus = pnw__H264ES_encode_one_slice(ctx, pBuffer);
    }
out1:
    /* free the converted VAEncSliceParameterBuffer */
    if (pBufferAlloced)
        free(pBuffer);

out2:
    free(obj_buffer->buffer_data);
    obj_buffer->buffer_data = NULL;

    return vaStatus;
}

static VAStatus pnw__H264ES_process_misc_param(context_ENC_p ctx, object_buffer_p obj_buffer)
{
    /* Prepare InParams for macros of current slice, insert slice header, insert do slice command */
    VAEncMiscParameterBuffer *pBuffer;
    VAEncMiscParameterRateControl *rate_control_param;
    VAEncMiscParameterAIR *air_param;
    VAEncMiscParameterMaxSliceSize *max_slice_size_param;
    VAEncMiscParameterFrameRate *frame_rate_param;
    VAEncMiscParameterHRD *hrd_param;
    VAStatus vaStatus = VA_STATUS_SUCCESS;
    unsigned int max_bps;
    unsigned int frame_size;

    ASSERT(obj_buffer->type == VAEncMiscParameterBufferType);


    /* Transfer ownership of VAEncMiscParameterBuffer data */
    pBuffer = (VAEncMiscParameterBuffer *) obj_buffer->buffer_data;
    obj_buffer->size = 0;

    if (ctx->eCodec != IMG_CODEC_H264_VCM
            && (pBuffer->type != VAEncMiscParameterTypeHRD
                && pBuffer->type != VAEncMiscParameterTypeRateControl
                && pBuffer->type != VAEncMiscParameterTypeFrameRate)) {
        drv_debug_msg(VIDEO_DEBUG_GENERAL, "Buffer type %d isn't supported in none VCM mode.\n",
                pBuffer->type);
        free(obj_buffer->buffer_data);
        obj_buffer->buffer_data = NULL;
        return VA_STATUS_SUCCESS;
    }

    switch (pBuffer->type) {
        case VAEncMiscParameterTypeFrameRate:
            frame_rate_param = (VAEncMiscParameterFrameRate *)pBuffer->data;

            if (frame_rate_param->framerate < 1 || frame_rate_param->framerate > 65535) {
                vaStatus = VA_STATUS_ERROR_INVALID_PARAMETER;
                break;
            }

            if (ctx->sRCParams.FrameRate == frame_rate_param->framerate)
                break;

            drv_debug_msg(VIDEO_DEBUG_GENERAL, "frame rate changed from %d to %d\n",
                    ctx->sRCParams.FrameRate,
                    frame_rate_param->framerate);
            ctx->sRCParams.FrameRate = frame_rate_param->framerate;
            ctx->sRCParams.bBitrateChanged = IMG_TRUE;

            ctx->sRCParams.FrameRate = (frame_rate_param->framerate < 1) ? 1 :
                ((65535 < frame_rate_param->framerate) ? 65535 : frame_rate_param->framerate);
            break;

        case VAEncMiscParameterTypeRateControl:
            rate_control_param = (VAEncMiscParameterRateControl *)pBuffer->data;

            /* Currently, none VCM mode only supports frame skip and bit stuffing
             * disable flag and doesn't accept other parameters in
             * buffer of VAEncMiscParameterTypeRateControl type */
            if (rate_control_param->rc_flags.value != 0 || ctx->raw_frame_count == 0) {
                if (rate_control_param->rc_flags.bits.disable_frame_skip)
                    ctx->sRCParams.bDisableFrameSkipping = IMG_TRUE;
                if (rate_control_param->rc_flags.bits.disable_bit_stuffing)
                    ctx->sRCParams.bDisableBitStuffing = IMG_TRUE;
                drv_debug_msg(VIDEO_DEBUG_GENERAL,
                        "bDisableFrameSkipping is %d and bDisableBitStuffing is %d\n",
                        ctx->sRCParams.bDisableFrameSkipping, ctx->sRCParams.bDisableBitStuffing);
            }

            if (rate_control_param->initial_qp > 51 ||
                    rate_control_param->min_qp > 51) {
                drv_debug_msg(VIDEO_DEBUG_ERROR, "Initial_qp(%d) and min_qpinitial_qp(%d) "
                        "are invalid.\nQP shouldn't be larger than 51 for H264\n",
                        rate_control_param->initial_qp, rate_control_param->min_qp);
                vaStatus = VA_STATUS_ERROR_INVALID_PARAMETER;
                break;
            }

            if (rate_control_param->window_size > 2000) {
                drv_debug_msg(VIDEO_DEBUG_ERROR, "window_size is too much!\n");
                vaStatus = VA_STATUS_ERROR_INVALID_PARAMETER;
                break;
            }

            /* Check if any none-zero RC parameter is changed*/
            if ((rate_control_param->bits_per_second == 0 ||
                        rate_control_param->bits_per_second == ctx->sRCParams.BitsPerSecond) &&
                    (rate_control_param->window_size == 0 ||
                    ctx->sRCParams.BufferSize == ctx->sRCParams.BitsPerSecond / 1000 * rate_control_param->window_size) &&
                    (ctx->sRCParams.MinQP == rate_control_param->min_qp) &&
                    (ctx->sRCParams.InitialQp == rate_control_param->initial_qp) &&
                    (rate_control_param->basic_unit_size == 0 ||
                     ctx->sRCParams.BUSize == rate_control_param->basic_unit_size)) {
		     drv_debug_msg(VIDEO_DEBUG_GENERAL, "%s No RC parameter is changed\n",
				     __FUNCTION__);
                break;
	    }
            else if (ctx->raw_frame_count != 0 || ctx->eCodec == IMG_CODEC_H264_VCM)
                ctx->sRCParams.bBitrateChanged = IMG_TRUE;

            /* The initial target bitrate is set by Sequence parameter buffer.
               Here is for changed bitrate only */
            if (rate_control_param->bits_per_second > TOPAZ_H264_MAX_BITRATE) {
                drv_debug_msg(VIDEO_DEBUG_ERROR, " bits_per_second(%d) exceeds \
                        the maximum bitrate, set it with %d\n",
                        rate_control_param->bits_per_second,
                        TOPAZ_H264_MAX_BITRATE);
                break;
            }
            /* The initial target bitrate is set by Sequence parameter buffer.
               Here is for changed bitrate only */
            if (rate_control_param->bits_per_second != 0 &&
                    ctx->raw_frame_count != 0) {
                drv_debug_msg(VIDEO_DEBUG_GENERAL,
                        "bitrate is changed from %d to %d on frame %d\n",
                        ctx->sRCParams.BitsPerSecond,
                        rate_control_param->bits_per_second,
                        ctx->raw_frame_count);
                max_bps =  (ctx->Width * ctx->Height * 3 / 2 ) * 8 * ctx->sRCParams.FrameRate;
                if (ctx->Width > 720)
                    max_bps /= 4;
                else
                    max_bps /= 2;

                drv_debug_msg(VIDEO_DEBUG_GENERAL, " width %d height %d, frame rate %d\n",
                        ctx->Width, ctx->Height, ctx->sRCParams.FrameRate);
                if (rate_control_param->bits_per_second > max_bps) {
                    drv_debug_msg(VIDEO_DEBUG_ERROR,
                            "Invalid bitrate %d, violate ITU-T Rec. H.264 (03/2005) A.3.1"
                            "\n clip to %d bps\n", rate_control_param->bits_per_second, max_bps);
                    ctx->sRCParams.BitsPerSecond = max_bps;
                } else {
                    /* See 110% target bitrate for VCM. Otherwise, the resulted bitrate is much lower
                       than target bitrate */
                    if (ctx->eCodec == IMG_CODEC_H264_VCM)
                        rate_control_param->bits_per_second =
                            rate_control_param->bits_per_second / 100 * 110;
                    drv_debug_msg(VIDEO_DEBUG_GENERAL, "Bitrate is set to %d\n",
                            rate_control_param->bits_per_second);
                    ctx->sRCParams.BitsPerSecond = rate_control_param->bits_per_second;
                }
            }

            if (rate_control_param->min_qp != 0)
                ctx->sRCParams.MinQP = rate_control_param->min_qp;
            if (rate_control_param->window_size != 0) {
                ctx->sRCParams.BufferSize =
                    ctx->sRCParams.BitsPerSecond / 1000 * rate_control_param->window_size;
		if (ctx->sRCParams.FrameRate == 0) {
                    drv_debug_msg(VIDEO_DEBUG_ERROR, "frame rate can't be zero. Set it to 30");
                    ctx->sRCParams.FrameRate = 30;
                }

                frame_size = ctx->sRCParams.BitsPerSecond / ctx->sRCParams.FrameRate;
                if (frame_size == 0) {
                    drv_debug_msg(VIDEO_DEBUG_ERROR, "Bitrate is too low %d\n",
                            ctx->sRCParams.BitsPerSecond);
                    break;
                }
                ctx->sRCParams.InitialLevel = (3 * ctx->sRCParams.BufferSize) >> 4;
                ctx->sRCParams.InitialLevel += (frame_size / 2);
                ctx->sRCParams.InitialLevel /= frame_size;
                ctx->sRCParams.InitialLevel *= frame_size;
                ctx->sRCParams.InitialDelay =
                    ctx->sRCParams.BufferSize - ctx->sRCParams.InitialLevel;
            }

            if (rate_control_param->initial_qp != 0)
                ctx->sRCParams.InitialQp = rate_control_param->initial_qp;
            if (rate_control_param->basic_unit_size != 0)
                ctx->sRCParams.BUSize = rate_control_param->basic_unit_size;

            drv_debug_msg(VIDEO_DEBUG_GENERAL,
                    "Set Misc parameters(frame %d): window_size %d, initial qp %d\n" \
                    "\tmin qp %d, bunit size %d\n",
                    ctx->raw_frame_count,
                    rate_control_param->window_size,
                    rate_control_param->initial_qp,
                    rate_control_param->min_qp,
                    rate_control_param->basic_unit_size);
            break;

        case VAEncMiscParameterTypeMaxSliceSize:
            max_slice_size_param = (VAEncMiscParameterMaxSliceSize *)pBuffer->data;

            /*The max slice size should not be bigger than 1920x1080x1.5x8 */
            if (max_slice_size_param->max_slice_size > 24883200) {
                drv_debug_msg(VIDEO_DEBUG_ERROR,"Invalid max_slice_size. It should be 1~ 24883200.\n");
                vaStatus = VA_STATUS_ERROR_INVALID_PARAMETER;
                break;
            }

            if (ctx->max_slice_size == max_slice_size_param->max_slice_size)
                break;

            drv_debug_msg(VIDEO_DEBUG_GENERAL, "max slice size changed to %d\n",
                    max_slice_size_param->max_slice_size);

            ctx->max_slice_size = max_slice_size_param->max_slice_size;

            break;

        case VAEncMiscParameterTypeAIR:
            air_param = (VAEncMiscParameterAIR *)pBuffer->data;

            if (air_param->air_num_mbs > 65535 ||
                    air_param->air_threshold > 65535) {
                vaStatus = VA_STATUS_ERROR_INVALID_PARAMETER;
                break;
            }

            drv_debug_msg(VIDEO_DEBUG_GENERAL,"air slice size changed to num_air_mbs %d "
                    "air_threshold %d, air_auto %d\n",
                    air_param->air_num_mbs, air_param->air_threshold,
                    air_param->air_auto);

            if (((ctx->Height * ctx->Width) >> 8) < (int)air_param->air_num_mbs)
                air_param->air_num_mbs = ((ctx->Height * ctx->Width) >> 8);
            if (air_param->air_threshold == 0)
                drv_debug_msg(VIDEO_DEBUG_GENERAL,"%s: air threshold is set to zero\n",
                        __func__);
            ctx->num_air_mbs = air_param->air_num_mbs;
            ctx->air_threshold = air_param->air_threshold;
            //ctx->autotune_air_flag = air_param->air_auto;

            break;

        case VAEncMiscParameterTypeHRD:
            hrd_param = (VAEncMiscParameterHRD *)pBuffer->data;

            if (hrd_param->buffer_size == 0
                    || hrd_param->initial_buffer_fullness == 0)
                drv_debug_msg(VIDEO_DEBUG_GENERAL, "Find zero value for buffer_size "
                        "and initial_buffer_fullness.\n"
                        "Will assign default value to them later \n");

            if (ctx->initial_buffer_fullness > ctx->buffer_size) {
                drv_debug_msg(VIDEO_DEBUG_ERROR, "initial_buffer_fullnessi(%d) shouldn't be"
                        " larger that buffer_size(%d)!\n",
                        hrd_param->initial_buffer_fullness,
                        hrd_param->buffer_size);
                vaStatus = VA_STATUS_ERROR_INVALID_PARAMETER;
                break;
            }

            if (!ctx->sRCParams.RCEnable) {
                drv_debug_msg(VIDEO_DEBUG_ERROR, "Only when rate control is enabled,"
                        " VAEncMiscParameterTypeHRD will take effect.\n");
                break;
            }

            ctx->buffer_size = hrd_param->buffer_size;
            ctx->initial_buffer_fullness = hrd_param->initial_buffer_fullness;
            ctx->bInserHRDParams = IMG_TRUE;
            drv_debug_msg(VIDEO_DEBUG_GENERAL, "hrd param buffer_size set to %d "
                    "initial buffer fullness set to %d\n",
                    ctx->buffer_size, ctx->initial_buffer_fullness);

            break;

        default:
            vaStatus = VA_STATUS_ERROR_UNKNOWN;
            DEBUG_FAILURE;
            break;
    }

    free(obj_buffer->buffer_data);
    obj_buffer->buffer_data = NULL;

    return vaStatus;
}



static VAStatus pnw_H264ES_RenderPicture(
        object_context_p obj_context,
        object_buffer_p *buffers,
        int num_buffers)
{
    INIT_CONTEXT_H264ES;
    VAStatus vaStatus = VA_STATUS_SUCCESS;
    int i;

    drv_debug_msg(VIDEO_DEBUG_GENERAL,"pnw_H264ES_RenderPicture\n");

    for (i = 0; i < num_buffers; i++) {
        object_buffer_p obj_buffer = buffers[i];

        switch (obj_buffer->type) {
            case VAEncSequenceParameterBufferType:
                drv_debug_msg(VIDEO_DEBUG_GENERAL, "pnw_H264_RenderPicture got VAEncSequenceParameterBufferType\n");
                vaStatus = pnw__H264ES_process_sequence_param(ctx, obj_buffer);
                DEBUG_FAILURE;
                break;

            case VAEncPictureParameterBufferType:
                drv_debug_msg(VIDEO_DEBUG_GENERAL, "pnw_H264_RenderPicture got VAEncPictureParameterBuffer\n");
                vaStatus = pnw__H264ES_process_picture_param(ctx, obj_buffer);
                DEBUG_FAILURE;
                break;

            case VAEncSliceParameterBufferType:
                drv_debug_msg(VIDEO_DEBUG_GENERAL, "pnw_H264_RenderPicture got VAEncSliceParameterBufferType\n");
                vaStatus = pnw__H264ES_process_slice_param(ctx, obj_buffer);
                DEBUG_FAILURE;
                break;

            case VAEncMiscParameterBufferType:
                drv_debug_msg(VIDEO_DEBUG_GENERAL, "pnw_H264_RenderPicture got VAEncMiscParameterBufferType\n");
                vaStatus = pnw__H264ES_process_misc_param(ctx, obj_buffer);
                DEBUG_FAILURE;
                break;
#if PSB_MFLD_DUMMY_CODE
            case VAEncPackedHeaderParameterBufferType:
                drv_debug_msg(VIDEO_DEBUG_GENERAL, "pnw_H264_RenderPicture got VAEncPackedHeaderParameterBufferType\n");
                vaStatus = pnw__H264ES_insert_SEI_FPA_param(ctx, obj_buffer);
                DEBUG_FAILURE;
                break;
            case VAEncPackedHeaderDataBufferType:
                drv_debug_msg(VIDEO_DEBUG_GENERAL, "pnw_H264_RenderPicture got VAEncPackedHeaderDataBufferType\n");
                vaStatus = pnw__H264ES_insert_SEI_FPA_data(ctx, obj_buffer);
                DEBUG_FAILURE;
                break;
#endif

            default:
                vaStatus = VA_STATUS_ERROR_UNKNOWN;
                DEBUG_FAILURE;
        }
        if (vaStatus != VA_STATUS_SUCCESS) {
            break;
        }
    }

    return vaStatus;
}

static VAStatus pnw_H264ES_EndPicture(
        object_context_p obj_context)
{
    INIT_CONTEXT_H264ES;
    pnw_cmdbuf_p cmdbuf = ctx->obj_context->pnw_cmdbuf;
    PIC_PARAMS *psPicParams = (PIC_PARAMS *)cmdbuf->pic_params_p;
    VAStatus vaStatus = VA_STATUS_SUCCESS;
    unsigned char core = 0;

    drv_debug_msg(VIDEO_DEBUG_GENERAL, "pnw_H264ES_EndPicture\n");

    /* Unlike MPEG4 and H263, slices number is defined by user */
    for (core = 0; core < ctx->ParallelCores; core++) {
        psPicParams = (PIC_PARAMS *)
            (cmdbuf->pic_params_p +  ctx->pic_params_size * core);
        psPicParams->NumSlices =  ctx->sRCParams.Slices;
    }

    vaStatus = pnw_EndPicture(ctx);

    return vaStatus;
}


struct format_vtable_s pnw_H264ES_vtable = {
queryConfigAttributes:
    pnw_H264ES_QueryConfigAttributes,
    validateConfig:
        pnw_H264ES_ValidateConfig,
    createContext:
        pnw_H264ES_CreateContext,
    destroyContext:
        pnw_H264ES_DestroyContext,
    beginPicture:
        pnw_H264ES_BeginPicture,
    renderPicture:
        pnw_H264ES_RenderPicture,
    endPicture:
        pnw_H264ES_EndPicture
};

VAStatus pnw_set_frame_skip_flag(
        object_context_p obj_context)
{
    INIT_CONTEXT_H264ES;
    VAStatus vaStatus = VA_STATUS_SUCCESS;


    if (ctx && ctx->previous_src_surface) {
        SET_SURFACE_INFO_skipped_flag(ctx->previous_src_surface->psb_surface, 1);
        drv_debug_msg(VIDEO_DEBUG_GENERAL, "Detected a skipped frame for surface 0x%08x.\n",
            ctx->previous_src_surface->psb_surface);
    }

    return vaStatus;
}