/*
*
* Copyright 2015 Rockchip Electronics Co. LTD
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#define MODULE_TAG "h264d_parse"

#include <string.h>
#include <stdlib.h>

#include "mpp_mem.h"
#include "mpp_packet_impl.h"
#include "hal_dec_task.h"

#include "h264d_global.h"
#include "h264d_parse.h"
#include "h264d_slice.h"
#include "h264d_sps.h"
#include "h264d_pps.h"
#include "h264d_sei.h"
#include "h264d_init.h"
#include "h264d_fill.h"
#include "rk_hdr_meta_com.h"

#define  HEAD_SYNTAX_MAX_SIZE        (12800)
#define NALU_TYPE_NORMAL_LENGTH      (1)
#define NALU_TYPE_EXT_LENGTH         (5)

static const RK_U8 g_start_precode[3] = {0, 0, 1};

typedef struct h264d_nalu_head_t {
    RK_U16  is_frame_end;
    RK_U16  nalu_type;
    RK_U32  sodb_len;
} H264dNaluHead_t;


static RK_U16 U16_AT(const RK_U8 *ptr)
{
    return ptr[0] << 8 | ptr[1];
}

static RK_U32 U32_AT(const RK_U8 *ptr)
{
    return ptr[0] << 24 | ptr[1] << 16 | ptr[2] << 8 | ptr[3];
}

static RK_U32 parse_nal_size(RK_U8 nal_size, RK_U8 *data)
{
    switch (nal_size) {
    case 1:
        return *data;
    case 2:
        return U16_AT(data);
    case 3:
        return ((RK_U64)data[0] << 16) | U16_AT(&data[1]);
    case 4:
        return U32_AT(data);
    }
    return 0;
}

static void reset_slice(H264dVideoCtx_t *p_Vid)
{
    RK_U32 i = 0, j = 0;
    H264_SLICE_t *currSlice = &p_Vid->p_Cur->slice;

    memset(currSlice, 0, sizeof(H264_SLICE_t));
    //-- re-init parameters
    currSlice->view_id = -1;
    currSlice->p_Vid = p_Vid;
    currSlice->p_Dec = p_Vid->p_Dec;
    currSlice->p_Cur = p_Vid->p_Cur;
    currSlice->p_Inp = p_Vid->p_Inp;
    currSlice->active_sps = p_Vid->active_sps;
    currSlice->active_pps = p_Vid->active_pps;
    currSlice->active_subsps = p_Vid->active_subsps;
    //--- reset listP listB
    for (i = 0; i < MAX_NUM_DPB_LAYERS; i++) {
        currSlice->listP[i] = p_Vid->p_Cur->listP[i];
        currSlice->listB[i] = p_Vid->p_Cur->listB[i];
        for (j = 0; j < MAX_LIST_SIZE; j++) {
            currSlice->listP[i][j] = NULL;
            currSlice->listB[i][j] = NULL;
        }
        currSlice->listXsizeP[i] = 0;
        currSlice->listXsizeB[i] = 0;
    }
    reset_cur_slice(p_Vid->p_Cur, currSlice);
}

static MPP_RET realloc_buffer(RK_U8 **buf, RK_U32 *max_size, RK_U32 add_size)
{
    MPP_RET ret = MPP_ERR_UNKNOW;
    if ((*buf) == NULL) {
        H264D_ERR("[realloc_buffer] pointer is null, %p \n", (*buf));
        ret = MPP_ERR_MALLOC;
        goto __FAILED;
    }

    add_size = MPP_ALIGN(add_size, 16);

    (*buf) = mpp_realloc((*buf), RK_U8, ((*max_size) + add_size));
    if ((*buf) == NULL) {
        H264D_ERR("[realloc_buffer] ERROR: max_size=%d, add_size=%d \n", (*max_size), add_size);
        ret = MPP_ERR_MALLOC;
        goto __FAILED;
    }
    (*max_size) += add_size;

    return ret = MPP_OK;
__FAILED:
    return ret;
}

static void reset_nalu(H264dCurStream_t *p_strm)
{
    if (p_strm->endcode_found) {
        p_strm->startcode_found = p_strm->endcode_found;
        p_strm->nalu_len = 0;
        p_strm->nalu_type = H264_NALU_TYPE_NULL;
        p_strm->endcode_found = 0;
    }
}

static void find_prefix_code(RK_U8 *p_data, H264dCurStream_t *p_strm)
{
    (void)p_data;

    if ((p_strm->prefixdata & 0x00FFFFFF) == 0x00000001) {
        if (p_strm->startcode_found) {
            p_strm->endcode_found = 1;
        } else {
            p_strm->startcode_found = 1;
        }
    }
}

static MPP_RET parser_nalu_header(H264_SLICE_t *currSlice)
{
    MPP_RET ret = MPP_ERR_UNKNOW;

    H264dCurCtx_t   *p_Cur    = currSlice->p_Cur;
    BitReadCtx_t    *p_bitctx = &p_Cur->bitctx;
    H264_Nalu_t     *cur_nal  = &p_Cur->nalu;

    mpp_set_bitread_ctx(p_bitctx, cur_nal->sodb_buf, cur_nal->sodb_len);
    mpp_set_bitread_pseudo_code_type(p_bitctx, PSEUDO_CODE_H264_H265);

    READ_BITS(p_bitctx, 1, &cur_nal->forbidden_bit);
    ASSERT(cur_nal->forbidden_bit == 0);
    {
        RK_S32  *ptmp = NULL;
        ptmp = (RK_S32 *)&cur_nal->nal_reference_idc;
        READ_BITS(p_bitctx, 2, ptmp); //!<  nal_ref_idc
        ptmp = (RK_S32 *)&cur_nal->nalu_type;
        READ_BITS(p_bitctx, 5, ptmp); //!< nalu_type
    }
    cur_nal->ualu_header_bytes = 1;
    currSlice->svc_extension_flag = -1; //!< initialize to -1
    if (cur_nal->nalu_type == H264_NALU_TYPE_PREFIX
        || cur_nal->nalu_type == H264_NALU_TYPE_SLC_EXT) {
        READ_ONEBIT(p_bitctx, &currSlice->svc_extension_flag);
        if (currSlice->svc_extension_flag) {
            currSlice->svcExt.valid = 1;
            p_Cur->p_Dec->svc_valid = 1;
            READ_ONEBIT(p_bitctx,     &currSlice->svcExt.idr_flag);
            READ_BITS(p_bitctx,    6, &currSlice->svcExt.priority_id);
            READ_ONEBIT(p_bitctx,     &currSlice->svcExt.no_inter_layer_pred_flag);
            READ_BITS(p_bitctx,    3, &currSlice->svcExt.dependency_id);
            READ_BITS(p_bitctx,    4, &currSlice->svcExt.quality_id);
            READ_BITS(p_bitctx,    3, &currSlice->svcExt.temporal_id);
            READ_ONEBIT(p_bitctx,     &currSlice->svcExt.use_ref_base_pic_flag);
            READ_ONEBIT(p_bitctx,     &currSlice->svcExt.discardable_flag);
            READ_ONEBIT(p_bitctx,     &currSlice->svcExt.output_flag);

            ASSERT(currSlice->svcExt.no_inter_layer_pred_flag == 1);
            ASSERT(currSlice->svcExt.dependency_id == 0);
            ASSERT(currSlice->svcExt.quality_id == 0);
            ASSERT(currSlice->svcExt.use_ref_base_pic_flag == 0);
        } else { //!< MVC
            currSlice->mvcExt.valid = 1;
            p_Cur->p_Dec->mvc_valid = 1;
            READ_ONEBIT(p_bitctx,     &currSlice->mvcExt.non_idr_flag);
            READ_BITS(p_bitctx,    6, &currSlice->mvcExt.priority_id);
            READ_BITS(p_bitctx,   10, &currSlice->mvcExt.view_id);
            READ_BITS(p_bitctx,    3, &currSlice->mvcExt.temporal_id);
            READ_ONEBIT(p_bitctx,     &currSlice->mvcExt.anchor_pic_flag);
            READ_ONEBIT(p_bitctx,     &currSlice->mvcExt.inter_view_flag);
            READ_ONEBIT(p_bitctx,     &currSlice->mvcExt.reserved_one_bit);
            ASSERT(currSlice->mvcExt.reserved_one_bit == 1);
            currSlice->mvcExt.iPrefixNALU = (cur_nal->nalu_type == H264_NALU_TYPE_PREFIX) ? 1 : 0;
            //!< combine H264_NALU_TYPE_SLC_EXT into H264_NALU_TYPE_SLICE
            if (cur_nal->nalu_type == H264_NALU_TYPE_SLC_EXT) {
                cur_nal->nalu_type = H264_NALU_TYPE_SLICE;
            }
        }
        cur_nal->ualu_header_bytes += 3;
    }
    /* Move forward the bitread offset */
    mpp_set_bitread_ctx(p_bitctx,
                        cur_nal->sodb_buf + cur_nal->ualu_header_bytes,
                        cur_nal->sodb_len - cur_nal->ualu_header_bytes);
    mpp_set_bitread_pseudo_code_type(p_bitctx, PSEUDO_CODE_H264_H265);
    p_Cur->p_Dec->nalu_ret = StartofNalu;

    return ret = MPP_OK;
__BITREAD_ERR:
    p_Cur->p_Dec->nalu_ret = ReadNaluError;
    return ret = p_bitctx->ret;
}

static MPP_RET parser_one_nalu(H264_SLICE_t *currSlice)
{
    MPP_RET ret = MPP_ERR_UNKNOW;

    FUN_CHECK(ret = parser_nalu_header(currSlice));

    if (currSlice->p_Vid->deny_flag &&
        currSlice->p_Cur->nalu.nalu_type != H264_NALU_TYPE_SPS) {
        currSlice->p_Dec->nalu_ret = NaluNotSupport;
        return MPP_OK;
    }
    //!< check whether supprot mvc
    if (currSlice->mvcExt.view_id && currSlice->mvcExt.valid &&
        !currSlice->p_Dec->cfg->base.enable_mvc) {
        currSlice->p_Dec->nalu_ret = MvcDisAble;
        return MPP_OK;
    }

    //!< nalu_parse
    switch (currSlice->p_Cur->nalu.nalu_type) {
    case H264_NALU_TYPE_SLICE:
    case H264_NALU_TYPE_IDR:
        H264D_DBG(H264D_DBG_PARSE_NALU, "nalu_type=SLICE.");
        FUN_CHECK(ret = process_slice(currSlice));
        currSlice->p_Dec->nalu_ret = StartOfPicture;
        break;
    case H264_NALU_TYPE_SPS:
        H264D_DBG(H264D_DBG_PARSE_NALU, "nalu_type=SPS");
        FUN_CHECK(ret = process_sps(currSlice));
        currSlice->p_Dec->nalu_ret = NALU_SPS;
        currSlice->p_Vid->deny_flag = 0;
        break;
    case H264_NALU_TYPE_PPS:
        H264D_DBG(H264D_DBG_PARSE_NALU, "nalu_type=PPS");
        if (process_pps(currSlice) < 0)
            H264D_WARNNING("Function error(%d).\n", __LINE__);
        currSlice->p_Dec->nalu_ret = NALU_PPS;
        break;
    case H264_NALU_TYPE_SUB_SPS:
        H264D_DBG(H264D_DBG_PARSE_NALU, "nalu_type=SUB_SPS");
        if (process_subsps(currSlice) < 0)
            H264D_WARNNING("Function error(%d).\n", __LINE__);
        currSlice->p_Dec->nalu_ret = NALU_SubSPS;
        break;
    case H264_NALU_TYPE_SEI:
        H264D_DBG(H264D_DBG_PARSE_NALU, "nalu_type=SEI");
        ret = process_sei(currSlice);
        currSlice->p_Dec->nalu_ret = NALU_SEI;
        break;
    case H264_NALU_TYPE_SLC_EXT:
        H264D_DBG(H264D_DBG_PARSE_NALU, "Found H264_NALU_TYPE_SLC_EXT.");
        currSlice->p_Dec->nalu_ret = SkipNALU;
        break;
    case H264_NALU_TYPE_PREFIX:
        H264D_DBG(H264D_DBG_PARSE_NALU, "Found H264_NALU_TYPE_PREFIX.");
        process_prefix(currSlice);
        break;
    case H264_NALU_TYPE_AUD:
        H264D_DBG(H264D_DBG_PARSE_NALU, "Found H264_NALU_TYPE_AUD.");
        currSlice->p_Dec->nalu_ret = SkipNALU;
        break;
    case H264_NALU_TYPE_EOSEQ:
        H264D_DBG(H264D_DBG_PARSE_NALU, "Found H264_NALU_TYPE_EOSEQ.");
        currSlice->p_Dec->nalu_ret = SkipNALU;
        break;
    case H264_NALU_TYPE_EOSTREAM:
        H264D_DBG(H264D_DBG_PARSE_NALU, "Found H264_NALU_TYPE_EOSTREAM.");
        currSlice->p_Dec->nalu_ret = SkipNALU;
        break;
    case H264_NALU_TYPE_FILL:
        H264D_DBG(H264D_DBG_PARSE_NALU, "Found H264_NALU_TYPE_FILL.");
        currSlice->p_Dec->nalu_ret = SkipNALU;
        break;
    case H264_NALU_TYPE_VDRD:
        H264D_DBG(H264D_DBG_PARSE_NALU, "Found H264_NALU_TYPE_VDRD.");
        currSlice->p_Dec->nalu_ret = SkipNALU;
        break;
    case H264_NALU_TYPE_DPA:
    case H264_NALU_TYPE_DPB:
    case H264_NALU_TYPE_DPC:
        H264D_DBG(H264D_DBG_PARSE_NALU,
                  "Found H264_NALU_TYPE_DPA DPB DPC, and not supported.");
        currSlice->p_Dec->nalu_ret = NaluNotSupport;
        break;
    default:
        currSlice->p_Dec->nalu_ret = SkipNALU;
        break;
    }

    return ret = MPP_OK;
__FAILED:
    H264D_DBG(H264D_DBG_PARSE_NALU, "parser_one_nalu error.");
    currSlice->p_Dec->nalu_ret = ReadNaluError;
    return ret;
}

static MPP_RET add_empty_nalu(H264dCurStream_t *p_strm)
{
    MPP_RET ret = MPP_ERR_UNKNOW;
    RK_U8  *p_des = NULL;
    RK_U32 add_size = sizeof(H264dNaluHead_t);

    if ((p_strm->head_offset + add_size) >= p_strm->head_max_size) {
        FUN_CHECK(ret = realloc_buffer(&p_strm->head_buf, &p_strm->head_max_size, HEAD_BUF_ADD_SIZE));
    }
    p_des = &p_strm->head_buf[p_strm->head_offset];
    ((H264dNaluHead_t *)p_des)->is_frame_end  = 1;
    ((H264dNaluHead_t *)p_des)->nalu_type = 0;
    ((H264dNaluHead_t *)p_des)->sodb_len = 0;
    p_strm->head_offset += add_size;

    return ret = MPP_OK;
__FAILED:
    return ret;
}

void mpp_h264d_fill_dynamic_meta(H264dCurCtx_t *p_Cur, const RK_U8 *data, RK_U32 size, RK_U32 hdr_fmt)
{
    MppFrameHdrDynamicMeta *hdr_dynamic_meta = p_Cur->hdr_dynamic_meta;

    if (hdr_fmt == DLBY)
        size += 4;

    if (hdr_dynamic_meta && (hdr_dynamic_meta->size < size)) {
        mpp_free(hdr_dynamic_meta);
        hdr_dynamic_meta = NULL;
    }

    if (!hdr_dynamic_meta) {
        hdr_dynamic_meta = mpp_calloc_size(MppFrameHdrDynamicMeta,
                                           sizeof(MppFrameHdrDynamicMeta) + size);
        if (!hdr_dynamic_meta) {
            mpp_err_f("malloc hdr dynamic data failed!\n");
            return;
        }
    }
    if (size && data) {
        if (hdr_fmt == DLBY) {
            RK_U8 start_code[4] = {0, 0, 0, 1};

            memcpy((RK_U8*)hdr_dynamic_meta->data, start_code, 4);
            memcpy((RK_U8*)hdr_dynamic_meta->data + 4, (RK_U8*)data, size - 4);
        } else
            memcpy((RK_U8*)hdr_dynamic_meta->data, (RK_U8*)data, size);
        hdr_dynamic_meta->size = size;
        hdr_dynamic_meta->hdr_fmt = hdr_fmt;
    }
    p_Cur->hdr_dynamic_meta = hdr_dynamic_meta;
    p_Cur->hdr_dynamic = 1;
}

static MPP_RET store_cur_nalu(H264dCurCtx_t *p_Cur, H264dCurStream_t *p_strm, H264dDxvaCtx_t *dxva_ctx)
{
    MPP_RET ret = MPP_ERR_UNKNOW;
    RK_U8 *p_des = NULL;

    //!< fill head buffer
    if (   (p_strm->nalu_type == H264_NALU_TYPE_SLICE)
           || (p_strm->nalu_type == H264_NALU_TYPE_IDR)
           || (p_strm->nalu_type == H264_NALU_TYPE_SPS)
           || (p_strm->nalu_type == H264_NALU_TYPE_PPS)
           || (p_strm->nalu_type == H264_NALU_TYPE_SUB_SPS)
           || (p_strm->nalu_type == H264_NALU_TYPE_SEI)
           || (p_strm->nalu_type == H264_NALU_TYPE_PREFIX)
           || (p_strm->nalu_type == H264_NALU_TYPE_SLC_EXT)) {

        RK_U32 head_size = MPP_MIN(HEAD_SYNTAX_MAX_SIZE, p_strm->nalu_len);
        RK_U32 add_size = head_size + sizeof(H264dNaluHead_t);

        //!< judge new frame
        if (p_strm->tmp_offset && !p_strm->head_offset) {
            memcpy(p_strm->head_buf, p_strm->tmp_buf, p_strm->tmp_offset);
            p_strm->head_offset += p_strm->tmp_offset;
            p_strm->tmp_offset = 0;

            H264D_LOG("copy tmp header to empty header of new frame");
        }

        if ((p_strm->head_offset + add_size) >= p_strm->head_max_size) {
            FUN_CHECK(ret = realloc_buffer(&p_strm->head_buf, &p_strm->head_max_size, add_size));
        }
        p_des = &p_strm->head_buf[p_strm->head_offset];
        ((H264dNaluHead_t *)p_des)->is_frame_end  = 0;
        ((H264dNaluHead_t *)p_des)->nalu_type = p_strm->nalu_type;
        ((H264dNaluHead_t *)p_des)->sodb_len = head_size;
        memcpy(p_des + sizeof(H264dNaluHead_t), p_strm->nalu_buf, head_size);
        p_strm->head_offset += add_size;

        H264D_LOG("store current header, NAL type %d", p_strm->nalu_type);

        // store NAL header except slice NAL
        if (p_Cur->p_Dec->have_slice_data && p_strm->nalu_type != H264_NALU_TYPE_IDR &&
            p_strm->nalu_type != H264_NALU_TYPE_SLICE) {
            RK_U8 *p_tmp = NULL;

            if ((p_strm->tmp_offset + add_size) >= p_strm->tmp_max_size) {
                FUN_CHECK(ret = realloc_buffer(&p_strm->tmp_buf, &p_strm->tmp_max_size, add_size));
            }

            p_tmp = &p_strm->tmp_buf[p_strm->tmp_offset];
            memcpy(p_tmp, p_des, add_size);
            p_strm->tmp_offset += add_size;

            H264D_LOG("store current header to tmp header, NAL type %d", p_strm->nalu_type);
        }
    }    //!< fill sodb buffer
    if ((p_strm->nalu_type == H264_NALU_TYPE_SLICE)
        || (p_strm->nalu_type == H264_NALU_TYPE_IDR)) {

        RK_U32 add_size = p_strm->nalu_len + sizeof(g_start_precode);

        if ((dxva_ctx->strm_offset + add_size) >= dxva_ctx->max_strm_size) {
            FUN_CHECK(ret = realloc_buffer(&dxva_ctx->bitstream, &dxva_ctx->max_strm_size, add_size));
        }

        p_des = &dxva_ctx->bitstream[dxva_ctx->strm_offset];
        memcpy(p_des, g_start_precode, sizeof(g_start_precode));
        memcpy(p_des + sizeof(g_start_precode), p_strm->nalu_buf, p_strm->nalu_len);
        dxva_ctx->strm_offset += add_size;
    }

    /* Dlby Vision RPUs masquerade as unregistered NALs of type 28. */
    if (p_Cur->p_Dec->cfg->base.enable_hdr_meta && p_strm->nalu_type == H264_NALU_TYPE_UNSPECIFIED28)
        mpp_h264d_fill_dynamic_meta(p_Cur, p_strm->nalu_buf + 2, p_strm->nalu_len - 2, DLBY);

    if (h264d_debug & H264D_DBG_WRITE_ES_EN) {
        H264dInputCtx_t *p_Inp = p_Cur->p_Inp;
        if ((p_strm->nalu_type == H264_NALU_TYPE_SPS)
            || (p_strm->nalu_type == H264_NALU_TYPE_PPS)) {
            if (p_Inp->spspps_update_flag) {
                p_des = &p_Inp->spspps_buf[p_Inp->spspps_offset];
                memcpy(p_des, g_start_precode, sizeof(g_start_precode));
                memcpy(p_des + sizeof(g_start_precode), p_strm->nalu_buf, p_strm->nalu_len);
                p_Inp->spspps_offset += p_strm->nalu_len + sizeof(g_start_precode);
                p_Inp->spspps_len = p_Inp->spspps_offset;
            }
        } else if ((p_strm->nalu_type == H264_NALU_TYPE_SLICE)
                   || (p_strm->nalu_type == H264_NALU_TYPE_IDR)) {
            p_Cur->p_Inp->spspps_update_flag = 1;
            p_Inp->spspps_offset = 0;
        }
    }

    return ret = MPP_OK;
__FAILED:
    return ret;
}

static MPP_RET judge_is_new_frame(H264dCurCtx_t *p_Cur, H264dCurStream_t *p_strm)
{
    MPP_RET ret = MPP_ERR_UNKNOW;
    RK_U32 nalu_header_bytes  = 0;

    BitReadCtx_t    *p_bitctx = &p_Cur->bitctx;
    memset(p_bitctx, 0, sizeof(BitReadCtx_t));
    {
        RK_U32      forbidden_bit = -1;
        RK_U32  nal_reference_idc = -1;
        mpp_set_bitread_ctx(p_bitctx, p_strm->nalu_buf, 4);
        mpp_set_bitread_pseudo_code_type(p_bitctx, PSEUDO_CODE_H264_H265);

        READ_BITS(p_bitctx, 1, &forbidden_bit);
        ASSERT(forbidden_bit == 0);
        READ_BITS(p_bitctx, 2, &nal_reference_idc);
        READ_BITS(p_bitctx, 5, &p_strm->nalu_type);

        nalu_header_bytes = 1;
        if ((p_strm->nalu_type == H264_NALU_TYPE_PREFIX)
            || (p_strm->nalu_type == H264_NALU_TYPE_SLC_EXT)) {
            if (p_strm->nalu_type == H264_NALU_TYPE_SLC_EXT) {
                p_strm->nalu_type = H264_NALU_TYPE_SLICE;
            }
            nalu_header_bytes += 3;
        }
    }
    if ((p_strm->nalu_len == 1)
        && (p_strm->nalu_type == H264_NALU_TYPE_SEI
            || p_strm->nalu_type == H264_NALU_TYPE_SPS
            || p_strm->nalu_type == H264_NALU_TYPE_PPS
            || p_strm->nalu_type == H264_NALU_TYPE_SUB_SPS
            || p_strm->nalu_type == H264_NALU_TYPE_AUD
            /*|| p_strm->nalu_type == H264_NALU_TYPE_PREFIX*/)) { // prefix may insert in slices of one frame
        p_Cur->last_dts = p_Cur->curr_dts;
        p_Cur->last_pts = p_Cur->curr_pts;
        p_Cur->curr_dts = p_Cur->p_Inp->in_dts;
        p_Cur->curr_pts = p_Cur->p_Inp->in_pts;
        if (p_Cur->p_Dec->have_slice_data) {
            p_Cur->p_Dec->is_new_frame = 1;
        }
        p_Cur->p_Dec->have_slice_data = 0;
    } else if ((p_strm->nalu_len > 1)
               && (p_strm->nalu_type == H264_NALU_TYPE_SLICE
                   || p_strm->nalu_type == H264_NALU_TYPE_IDR)) {
        RK_U32 first_mb_in_slice  = 0;
        mpp_set_bitread_ctx(p_bitctx, (p_strm->nalu_buf + nalu_header_bytes), 4); // reset
        mpp_set_bitread_pseudo_code_type(p_bitctx, PSEUDO_CODE_H264_H265);
        READ_UE(p_bitctx, &first_mb_in_slice);
        if (first_mb_in_slice == 0) {
            p_Cur->last_dts = p_Cur->curr_dts;
            p_Cur->last_pts = p_Cur->curr_pts;
            p_Cur->curr_dts = p_Cur->p_Inp->in_dts;
            p_Cur->curr_pts = p_Cur->p_Inp->in_pts;
            if (p_Cur->p_Dec->have_slice_data) {
                p_Cur->p_Dec->is_new_frame = 1;
            }
            p_Cur->p_Dec->have_slice_data = 1;
        } else if (p_strm->tmp_offset != 0 &&
                   first_mb_in_slice != p_Cur->strm.first_mb_in_slice) {
            p_strm->tmp_offset = 0;
            H264D_LOG("reset temporary header");
        }
        p_Cur->strm.first_mb_in_slice = first_mb_in_slice;
    }

    return ret = MPP_OK;
__BITREAD_ERR:
    return ret = p_bitctx->ret;
}

#define  MAX_ES_FILE_SIZE  (100*1024*1024)
static RK_U32 global_file_fid = 0;
static RK_U64 global_flie_size = 0;
/*!
***********************************************************************
* \brief
*    open stream file to store packet
***********************************************************************
*/
MPP_RET open_stream_file(H264dInputCtx_t *p_Inp, char *path)
{
    if (h264d_debug & H264D_DBG_WRITE_ES_EN) {

        sprintf(p_Inp->fname[0], "%s/rkv_h264d_file_00.h264", path);
        sprintf(p_Inp->fname[1], "%s/rkv_h264d_file_01.h264", path);

        p_Inp->fp = fopen(p_Inp->fname[global_file_fid], "ab"); {
            if (p_Inp->fp == NULL) {
                H264D_WARNNING("[open_stream_file] can not open stream file, %s.", p_Inp->fname[global_file_fid]);
            }
        }
    }

    return MPP_OK;
}
/*!
***********************************************************************
* \brief
*    write stream file
***********************************************************************
*/
MPP_RET fwrite_stream_to_file(H264dInputCtx_t *p_Inp, RK_U8 *pdata, RK_U32 len)
{
    if (p_Inp->fp && (h264d_debug & H264D_DBG_WRITE_ES_EN)) {
        if (p_Inp->fp) {
            fwrite(pdata, sizeof(RK_U8), len, p_Inp->fp);
            fflush(p_Inp->fp);
        }
        global_flie_size += len;
        if (global_flie_size > MAX_ES_FILE_SIZE) {
            MPP_FCLOSE(p_Inp->fp);
            global_file_fid = 1 - global_file_fid;
            global_flie_size = 0;
            p_Inp->fp = fopen(p_Inp->fname[global_file_fid], "wb");
            if (p_Inp->fp == NULL) {
                H264D_WARNNING("[open_stream_file] can not open stream file, %s", p_Inp->fname[global_file_fid]);
            } else {
                fwrite(p_Inp->spspps_buf, sizeof(RK_U8), p_Inp->spspps_len, p_Inp->fp);
                fflush(p_Inp->fp);
            }
        }
    }

    return MPP_OK;
}
/*!
***********************************************************************
* \brief
*    close stream file
***********************************************************************
*/
MPP_RET close_stream_file(H264dInputCtx_t *p_Inp)
{
    if (h264d_debug & H264D_DBG_WRITE_ES_EN) {
        if (p_Inp->fp) {
            fflush(p_Inp->fp);
            MPP_FCLOSE(p_Inp->fp);
        }
    }

    return MPP_OK;
}
/*!
***********************************************************************
* \brief
*    clear last redundancy header if exist
***********************************************************************
*/
static MPP_RET clear_extra_header(H264dCurStream_t *p_strm)
{
    MPP_RET ret = MPP_OK;

    if (p_strm->tmp_offset >= p_strm->head_offset) {
        H264D_WARNNING("tmp header data is too long! skip clear operation");
        ret = MPP_NOK;
    } else {
        RK_U32 pos = p_strm->head_offset - p_strm->tmp_offset;
        RK_U8 *p_des = &p_strm->head_buf[pos];

        memset(p_des, 0, p_strm->tmp_offset);
        p_strm->head_offset -= p_strm->tmp_offset;

        H264D_LOG("clear last redundancy header in slice");
    }

    return ret;
}
/*!
***********************************************************************
* \brief
*    prepare function for parser
***********************************************************************
*/
MPP_RET parse_prepare(H264dInputCtx_t *p_Inp, H264dCurCtx_t *p_Cur)
{
    MPP_RET ret = MPP_ERR_UNKNOW;

    H264_DecCtx_t   *p_Dec   = p_Inp->p_Dec;
    H264dCurStream_t *p_strm = &p_Cur->strm;
    MppPacketImpl *pkt_impl  = (MppPacketImpl *)p_Inp->in_pkt;

    p_Dec->nalu_ret = NALU_NULL;
    p_Inp->task_valid = 0;

    //!< check eos
    if (p_Inp->pkt_eos && !p_Inp->in_length) {
        FUN_CHECK(ret = store_cur_nalu(p_Cur, p_strm, p_Dec->dxva_ctx));
        FUN_CHECK(ret = add_empty_nalu(p_strm));
        p_Dec->p_Inp->task_valid = p_Dec->p_Inp->task_eos ? 0 : 1;
        p_Dec->p_Inp->task_eos = 1;
        H264D_LOG("----- end of stream ----");
        goto __RETURN;
    }
    //!< check input
    if (!p_Inp->in_length) {
        p_Dec->nalu_ret = HaveNoStream;
        goto __RETURN;
    }

    while (pkt_impl->length > 0) {
        p_strm->curdata = &p_Inp->in_buf[p_strm->nalu_offset++];
        pkt_impl->length--;
        p_strm->prefixdata = (p_strm->prefixdata << 8) | (*p_strm->curdata);
        if (p_strm->startcode_found) {
            if (p_strm->nalu_len >= p_strm->nalu_max_size) {
                FUN_CHECK(ret = realloc_buffer(&p_strm->nalu_buf, &p_strm->nalu_max_size, NALU_BUF_ADD_SIZE));
            }
            p_strm->nalu_buf[p_strm->nalu_len++] = *p_strm->curdata;
            if ((p_strm->nalu_len == NALU_TYPE_NORMAL_LENGTH)
                || (p_strm->nalu_len == NALU_TYPE_EXT_LENGTH)) {
                FUN_CHECK(ret = judge_is_new_frame(p_Cur, p_strm));
                if (p_Cur->p_Dec->is_new_frame) {
                    if (p_strm->tmp_offset)
                        clear_extra_header(p_strm);
                    FUN_CHECK(ret = add_empty_nalu(p_strm));
                    p_strm->head_offset = 0;
                    p_strm->first_mb_in_slice = 0;
                    p_Cur->p_Inp->task_valid = 1;
                    p_Cur->p_Dec->is_new_frame = 0;

                    H264D_LOG("new frame is found");
                    break;
                }
            }
        }

        find_prefix_code(p_strm->curdata, p_strm);

        if (p_strm->endcode_found) {
            p_strm->nalu_len -= START_PREFIX_3BYTE;
            if (p_strm->nalu_len > START_PREFIX_3BYTE) {
                while ((p_strm->nalu_len > 0) &&
                       (p_strm->nalu_buf[p_strm->nalu_len - 1] == 0x00)) {
                    p_strm->nalu_len--;
                }
            }
            p_Dec->nalu_ret = EndOfNalu;
            FUN_CHECK(ret = store_cur_nalu(p_Cur, p_strm, p_Dec->dxva_ctx));
            reset_nalu(p_strm);
            break;
        }
    }
    p_Inp->in_length = pkt_impl->length;
    //!< check input
    if (!p_Inp->in_length) {
        p_strm->nalu_offset = 0;
        p_Dec->nalu_ret = HaveNoStream;
    }

    if (p_Inp->pkt_eos && p_Inp->in_length < 4) {
        FUN_CHECK(ret = store_cur_nalu(p_Cur, p_strm, p_Dec->dxva_ctx));
        FUN_CHECK(ret = add_empty_nalu(p_strm));
        p_Dec->p_Inp->task_valid = 1;
        p_Dec->p_Inp->task_eos = 1;
        H264D_LOG("----- found eos in last non-empty packet ----");
    }

__RETURN:

    return ret = MPP_OK;
__FAILED:
    return ret;
}

/*!
***********************************************************************
* \brief
*    prepare function for parser
***********************************************************************
*/
MPP_RET parse_prepare_fast(H264dInputCtx_t *p_Inp, H264dCurCtx_t *p_Cur)
{
    MPP_RET ret = MPP_ERR_UNKNOW;

    H264_DecCtx_t   *p_Dec   = p_Inp->p_Dec;
    H264dCurStream_t *p_strm = &p_Cur->strm;
    MppPacketImpl *pkt_impl  = (MppPacketImpl *)p_Inp->in_pkt;

    p_Dec->nalu_ret = NALU_NULL;
    p_Inp->task_valid = 0;

    while (pkt_impl->length > 0) {
        p_strm->curdata = &p_Inp->in_buf[p_strm->nalu_offset++];
        pkt_impl->length--;
        p_strm->prefixdata = (p_strm->prefixdata << 8) | (*p_strm->curdata);
        if (p_strm->startcode_found) {
            if (p_strm->nalu_len >= p_strm->nalu_max_size) {
                FUN_CHECK(ret = realloc_buffer(&p_strm->nalu_buf, &p_strm->nalu_max_size, NALU_BUF_ADD_SIZE));
            }
            p_strm->nalu_buf[p_strm->nalu_len++] = *p_strm->curdata;
            if (p_strm->nalu_len == 1) {
                p_strm->nalu_type = p_strm->nalu_buf[0] & 0x1F;

                if (p_strm->nalu_type == H264_NALU_TYPE_SLICE
                    || p_strm->nalu_type == H264_NALU_TYPE_IDR || p_strm->nalu_type == H264_NALU_TYPE_SLC_EXT) {

                    if (p_strm->nalu_type == H264_NALU_TYPE_SLC_EXT)
                        p_strm->nalu_type = H264_NALU_TYPE_SLICE;

                    p_strm->nalu_len += (RK_U32)pkt_impl->length;
                    if (p_strm->nalu_len >= p_strm->nalu_max_size) {
                        RK_U32 add_size =  pkt_impl->length + 1 - p_strm->nalu_max_size;
                        FUN_CHECK(ret = realloc_buffer(&p_strm->nalu_buf, &p_strm->nalu_max_size, MPP_MAX(NALU_BUF_ADD_SIZE, add_size)));
                    }
                    memcpy(&p_strm->nalu_buf[0], p_strm->curdata, pkt_impl->length + 1);
                    pkt_impl->length = 0;
                    p_Cur->p_Inp->task_valid = 1;
                    break;
                }
            }
        }

        find_prefix_code(p_strm->curdata, p_strm);

        if (p_strm->endcode_found) {
            p_strm->nalu_len -= START_PREFIX_3BYTE;
            while (p_strm->nalu_len > 0 && p_strm->nalu_buf[p_strm->nalu_len - 1] == 0x00) {
                p_strm->nalu_len--;
            }
            p_Dec->nalu_ret = EndOfNalu;
            FUN_CHECK(ret = store_cur_nalu(p_Cur, p_strm, p_Dec->dxva_ctx));
            reset_nalu(p_strm);
            break;
        }
    }
    if (p_Cur->p_Inp->task_valid) {
        FUN_CHECK(ret = store_cur_nalu(p_Cur, p_strm, p_Dec->dxva_ctx));
        FUN_CHECK(ret = add_empty_nalu(p_strm));
        p_strm->head_offset = 0;
        p_Cur->last_dts = p_Cur->p_Inp->in_dts;
        p_Cur->last_pts = p_Cur->p_Inp->in_pts;
    }
    p_Inp->in_length = pkt_impl->length;
    //!< check input
    if (!p_Inp->in_length) {
        if (!p_Cur->p_Inp->task_valid) {
            p_Dec->nalu_ret = EndOfNalu;
            FUN_CHECK(ret = store_cur_nalu(p_Cur, p_strm, p_Dec->dxva_ctx));
        } else {
            p_Dec->nalu_ret = HaveNoStream;
        }
        p_strm->nalu_offset = 0;
        p_strm->endcode_found = 1;

        reset_nalu(p_strm);
        p_strm->startcode_found = 0;
    }

    return ret = MPP_OK;
__FAILED:
    return ret;
}
/*!
***********************************************************************
* \brief
*    main function for parser avcC header
***********************************************************************
*/
MPP_RET parse_prepare_avcC_header(H264dInputCtx_t *p_Inp, H264dCurCtx_t *p_Cur)
{
    RK_S32 i = 0;
    MPP_RET ret = MPP_ERR_UNKNOW;

    H264dCurStream_t *p_strm = &p_Cur->strm;
    RK_U8 *pdata = p_Inp->in_buf;
    MppPacketImpl *pkt_impl  = (MppPacketImpl *)p_Inp->in_pkt;

    //!< free nalu_buffer
    MPP_FREE(p_strm->nalu_buf);
    if (p_Inp->in_length < 7) {
        H264D_ERR("avcC too short, len=%d \n", p_Inp->in_length);
        goto __FAILED;
    }
    if (pdata[0] != 1) {
        goto __FAILED;
    }
    p_Inp->profile = pdata[1];
    p_Inp->level = pdata[3];
    p_Inp->nal_size = 1 + (pdata[4] & 3);
    p_Inp->sps_num = pdata[5] & 31;

    pdata += 6;
    for (i = 0; i < p_Inp->sps_num; ++i) {
        p_strm->nalu_len = U16_AT(pdata);
        pdata += 2;
        p_strm->nalu_type = H264_NALU_TYPE_SPS;
        p_strm->nalu_buf = pdata;
        FUN_CHECK(ret = store_cur_nalu(p_Cur, p_strm, p_Cur->p_Dec->dxva_ctx));
        pdata += p_strm->nalu_len;
    }
    p_strm->nalu_buf = NULL;
    p_Inp->pps_num = *pdata;
    ++pdata;
    for (i = 0; i < p_Inp->pps_num; ++i) {
        p_strm->nalu_len = U16_AT(pdata);
        pdata += 2;
        p_strm->nalu_type = H264_NALU_TYPE_PPS;
        p_strm->nalu_buf = pdata;
        FUN_CHECK(ret = store_cur_nalu(p_Cur, p_strm, p_Cur->p_Dec->dxva_ctx));
        pdata += p_strm->nalu_len;
    }
    pkt_impl->length = 0;
    p_strm->nalu_buf = NULL;
    p_strm->startcode_found = 1;

    ret = MPP_OK;
__FAILED:
    return ret;
}
/*!
***********************************************************************
* \brief
*    main function for parser avcC data
***********************************************************************
*/
MPP_RET parse_prepare_avcC_data(H264dInputCtx_t *p_Inp, H264dCurCtx_t *p_Cur)
{
    MPP_RET ret = MPP_ERR_UNKNOW;

    H264dCurStream_t *p_strm = &p_Cur->strm;
    MppPacketImpl *pkt_impl  = (MppPacketImpl *)p_Inp->in_pkt;

    p_strm->nalu_buf = NULL;
    p_Inp->task_valid = 0;
    if (p_Inp->pkt_eos) {
        p_Inp->task_eos = 1;
        if (p_Inp->in_length)
            p_Inp->task_valid = 1;
        return MPP_OK;
    }
    VAL_CHECK(ret, (p_Inp->nal_size > 0));
    p_strm->curdata = &p_Inp->in_buf[p_strm->nalu_offset];
    while (p_Inp->in_length > 0) {
        if (p_strm->startcode_found) {
            p_strm->nalu_len = parse_nal_size(p_Inp->nal_size, p_strm->curdata);
            if (p_strm->nalu_len <= 0 ||  p_strm->nalu_len >= p_Inp->in_length) {
                p_Cur->p_Dec->is_new_frame = 1;
                p_Cur->p_Dec->have_slice_data = 0;
                pkt_impl->length = 0;
                p_Inp->in_length = 0;
                p_strm->nalu_len = 0;
                p_strm->nalu_offset = 0;
                p_strm->startcode_found = 1;
                p_strm->endcode_found = 0;
                p_strm->nalu_buf  = NULL;
                goto __FAILED;
            }

            p_strm->nalu_buf = p_strm->curdata + p_Inp->nal_size;
            judge_is_new_frame(p_Cur, p_strm);
            if (p_Cur->p_Dec->is_new_frame) {
                p_Cur->p_Dec->have_slice_data = 0;
                p_strm->startcode_found = 1;
                p_strm->endcode_found = 0;
                break;
            }

            p_strm->curdata += p_Inp->nal_size;
            p_strm->nalu_offset += p_Inp->nal_size;
            pkt_impl->length -= p_Inp->nal_size;
            p_Inp->in_length -= p_Inp->nal_size;

            p_strm->nalu_buf = p_strm->curdata;
            p_strm->nalu_type = p_strm->nalu_buf[0] & 0x1F;
            p_strm->startcode_found = 0;
            p_strm->endcode_found = 1;

            FUN_CHECK(ret = store_cur_nalu(p_Cur, p_strm, p_Cur->p_Dec->dxva_ctx));
            p_strm->curdata += p_strm->nalu_len;
            p_strm->nalu_offset += p_strm->nalu_len;
            pkt_impl->length -= p_strm->nalu_len;
            p_Inp->in_length -= p_strm->nalu_len;
            p_strm->startcode_found = 1;
            p_strm->endcode_found = 0;
            p_strm->nalu_len = 0;
        }

        if (p_Inp->in_length < p_Inp->nal_size) {
            p_Cur->p_Dec->is_new_frame = 1;
            p_Cur->p_Dec->have_slice_data = 0;
            pkt_impl->length = 0;
            p_Inp->in_length = 0;
            p_strm->nalu_len = 0;
            p_strm->nalu_offset = 0;
            p_strm->startcode_found = 1;
            p_strm->endcode_found = 0;
            p_strm->nalu_buf  = NULL;
            break;
        }
    }
    //!< one frame end
    if (p_Cur->p_Dec->is_new_frame) {
        if (p_strm->tmp_offset)
            clear_extra_header(p_strm);
        //!< add an empty nalu to tell frame end
        FUN_CHECK(ret = add_empty_nalu(p_strm));
        //!< reset curstream parameters
        p_strm->head_offset = 0;
        p_strm->first_mb_in_slice = 0;
        p_Cur->p_Inp->task_valid = 1;
        p_Cur->p_Dec->is_new_frame = 0;

        p_Cur->last_dts = p_Inp->in_dts;
        p_Cur->last_pts = p_Inp->in_pts;
    }
    p_strm->nalu_buf = NULL;
    ret = MPP_OK;
__FAILED:
    //p_strm->nalu_len = 0;
    p_Cur->last_dts  = p_Inp->in_dts;
    p_Cur->last_pts  = p_Inp->in_pts;
    p_Inp->p_Dec->nalu_ret = HaveNoStream;

    return ret;
}



/*!
***********************************************************************
* \brief
*    main function for parser
***********************************************************************
*/
MPP_RET parse_loop(H264_DecCtx_t *p_Dec)
{
    MPP_RET ret = MPP_ERR_UNKNOW;
    RK_U8 *p_curdata = NULL;
    RK_U8 while_loop_flag = 1;
    H264dNaluHead_t *p_head = NULL;

    INP_CHECK(ret, !p_Dec);
    //!< ==== loop ====
    p_Dec->next_state = SliceSTATE_ResetSlice;
    p_curdata = p_Dec->p_Cur->strm.head_buf;

    while (while_loop_flag) {
        switch (p_Dec->next_state) {
        case SliceSTATE_ResetSlice:
            reset_slice(p_Dec->p_Vid);
            p_Dec->next_state = SliceSTATE_ReadNalu;
            H264D_DBG(H264D_DBG_LOOP_STATE, "SliceSTATE_ResetSlice");
            break;
        case SliceSTATE_ReadNalu:
            p_head = (H264dNaluHead_t *)p_curdata;
            if (p_head->is_frame_end) {
                p_Dec->next_state = SliceSTATE_RegisterOneFrame;
                p_Dec->nalu_ret = HaveNoStream;
            } else {
                p_curdata += sizeof(H264dNaluHead_t);
                memset(&p_Dec->p_Cur->nalu, 0, sizeof(H264_Nalu_t));
                p_Dec->p_Cur->nalu.sodb_buf = p_curdata;
                p_Dec->p_Cur->nalu.sodb_len = p_head->sodb_len;
                p_curdata += p_head->sodb_len;
                p_Dec->nalu_ret = EndOfNalu;
                p_Dec->next_state = SliceSTATE_ParseNalu;
            }
            H264D_DBG(H264D_DBG_LOOP_STATE, "SliceSTATE_ReadNalu");
            break;
        case SliceSTATE_ParseNalu:
            FUN_CHECK(ret = parser_one_nalu(&p_Dec->p_Cur->slice));
            if (p_Dec->nalu_ret == StartOfSlice) {
                p_Dec->next_state = SliceSTATE_GetSliceData;
            } else if (p_Dec->nalu_ret == StartOfPicture) {
                p_Dec->next_state = SliceSTATE_InitPicture;
            }  else if (p_Dec->nalu_ret == MvcDisAble) {
                H264D_LOG("xxxxxxxx MVC disable");
                goto __FAILED;
            } else if (p_Dec->nalu_ret == NaluNotSupport) {
                H264D_LOG("NALU not support, abort decoding");
                goto __FAILED;
            } else {
                p_Dec->next_state = SliceSTATE_ReadNalu;
            }
            H264D_DBG(H264D_DBG_LOOP_STATE, "SliceSTATE_ParseNalu");
            break;
        case SliceSTATE_InitPicture:
            if (!p_Dec->p_Vid->iNumOfSlicesDecoded) {
                FUN_CHECK(ret = init_picture(&p_Dec->p_Cur->slice));
                p_Dec->is_parser_end = 1;
            }
            p_Dec->next_state = SliceSTATE_GetSliceData;
            H264D_DBG(H264D_DBG_LOOP_STATE, "SliceSTATE_InitPicture");
            break;
        case SliceSTATE_GetSliceData:
            FUN_CHECK(ret = fill_slice_syntax(&p_Dec->p_Cur->slice, p_Dec->dxva_ctx));
            p_Dec->p_Vid->iNumOfSlicesDecoded++;
            if (p_Dec->is_parser_end) {
                p_Dec->next_state = SliceSTATE_RegisterOneFrame;
            } else {
                p_Dec->next_state = SliceSTATE_ResetSlice;
            }
            H264D_DBG(H264D_DBG_LOOP_STATE, "SliceSTATE_GetSliceData");
            break;
        case SliceSTATE_RegisterOneFrame:
            if (!p_Dec->is_parser_end) {
                ret = MPP_NOK;
                goto __FAILED;
            }
            commit_buffer(p_Dec->dxva_ctx);
            while_loop_flag = 0;
            p_Dec->next_state = SliceSTATE_ReadNalu;
            H264D_DBG(H264D_DBG_LOOP_STATE, "SliceSTATE_RegisterOneFrame");
            break;
        default:
            ret = MPP_NOK;
            break;
        }
    }

__RETURN:
    return ret = MPP_OK;
__FAILED:
    p_Dec->nalu_ret = NALU_NULL;
    p_Dec->dxva_ctx->slice_count = 0;
    p_Dec->dxva_ctx->strm_offset = 0;
    p_Dec->p_Vid->iNumOfSlicesDecoded = 0;
    p_Dec->p_Vid->exit_picture_flag   = 0;

    return ret;
}


