/*
 *
 * Copyright 2010 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 "mpg4d_parser"

#include <string.h>

#include "mpp_env.h"
#include "mpp_mem.h"
#include "mpp_debug.h"
#include "mpp_bitread.h"

#include "mpg4d_parser.h"
#include "mpg4d_syntax.h"

RK_U32 mpg4d_debug = 0;

#define mpg4d_dbg(flag, fmt, ...)   _mpp_dbg(mpg4d_debug, flag, fmt, ## __VA_ARGS__)
#define mpg4d_dbg_f(flag, fmt, ...) _mpp_dbg_f(mpg4d_debug, flag, fmt, ## __VA_ARGS__)

#define mpg4d_dbg_func(fmt, ...)    mpg4d_dbg_f(MPG4D_DBG_FUNCTION, fmt, ## __VA_ARGS__)
#define mpg4d_dbg_bit(fmt, ...)     mpg4d_dbg(MPG4D_DBG_BITS, fmt, ## __VA_ARGS__)
#define mpg4d_dbg_result(fmt, ...)  mpg4d_dbg(MPG4D_DBG_RESULT, fmt, ## __VA_ARGS__)

#define MPEG4_VIDOBJ_START_CODE             0x00000100  /* ..0x0000011f */
#define MPEG4_VIDOBJLAY_START_CODE          0x00000120  /* ..0x0000012f */
#define MPEG4_VISOBJSEQ_START_CODE          0x000001b0
#define MPEG4_VISOBJSEQ_STOP_CODE           0x000001b1
#define MPEG4_USERDATA_START_CODE           0x000001b2
#define MPEG4_GRPOFVOP_START_CODE           0x000001b3
#define MPEG4_VISOBJ_START_CODE             0x000001b5
#define MPEG4_VOP_START_CODE                0x000001b6

#define MPG4_VOL_STARTCODE                  0x120
#define MPG4_VOL_STOPCODE                   0x12F
#define MPG4_VOS_STARTCODE                  0x1B0
#define MPG4_VOS_STOPCODE                   0x1B1
#define MPG4_USER_DATA_STARTCODE            0x1B2
#define MPG4_GOP_STARTCODE                  0x1B3
#define MPG4_VISUAL_OBJ_STARTCODE           0x1B5
#define MPG4_VOP_STARTCODE                  0x1B6

typedef struct {
    RK_S32 method;

    RK_S32 opaque;
    RK_S32 transparent;
    RK_S32 intra_cae;
    RK_S32 inter_cae;
    RK_S32 no_update;
    RK_S32 upsampling;

    RK_S32 intra_blocks;
    RK_S32 inter_blocks;
    RK_S32 inter4v_blocks;
    RK_S32 gmc_blocks;
    RK_S32 not_coded_blocks;

    RK_S32 dct_coefs;
    RK_S32 dct_lines;
    RK_S32 vlc_symbols;
    RK_S32 vlc_bits;

    RK_S32 apm;
    RK_S32 npm;
    RK_S32 interpolate_mc_q;
    RK_S32 forw_back_mc_q;
    RK_S32 halfpel2;
    RK_S32 halfpel4;

    RK_S32 sadct;
    RK_S32 quarterpel;
} Mpeg4Estimation;

typedef struct Mp4HdrVol_t {
    RK_S32  vo_type;
    RK_U32  low_delay;
    RK_U32  shape;
    RK_S32  time_inc_resolution;
    RK_U32  time_inc_bits;
    RK_S32  width;
    RK_S32  height;
    RK_U32  mb_width;
    RK_U32  mb_height;
    RK_S32  hor_stride;
    RK_S32  ver_stride;
    RK_U32  totalMbInVop;
    RK_U32  interlacing;
    RK_S32  sprite_enable;
    RK_U32  quant_bits;
    RK_U32  quant_type;
    RK_S32  quarter_sample;
    RK_S32  complexity_estimation_disable;
    RK_U32  resync_marker_disable;
    RK_S32  newpred_enable;
    RK_S32  reduced_resolution_enable;
    RK_S32  scalability;
    RK_S32  ver_id;
} Mp4HdrVol;

typedef struct Mp4HdrUserData_t {
    RK_S32  packed_mode;                /* bframes packed bits? (1 = yes) */
} Mp4HdrUserData;

typedef struct Mp4HdrVop_t {
    RK_S32  coding_type;
    RK_U32  frameNumber;
    RK_U32  rounding;
    RK_U32  intra_dc_vlc_threshold;
    RK_U32  top_field_first;
    RK_U32  alternate_vertical_scan;
    RK_U32  fcode_forward;
    RK_U32  fcode_backward;
    RK_U32  quant;                      // OFFSET_OF_QUANT_IN_DEC
    RK_U32  hdr_bits;
} Mp4HdrVop;

typedef struct Mpg4Hdr_t {
    // vol parameter
    Mp4HdrVol       vol;

    // user data parameter
    Mp4HdrUserData  usr;

    // vop header parameter
    Mp4HdrVop       vop;

    // frame related parameter
    RK_S64  pts;
    RK_S32  slot_idx;
    RK_U32  enqueued;

    RK_U32  last_time_base;
    RK_U32  time_base;
    RK_U32  time;
    RK_U32  time_pp;
    RK_U32  time_bp;
    RK_U32  last_non_b_time;
} Mpg4Hdr;


typedef struct {
    // global paramter
    MppBufSlots     frame_slots;
    RK_U32          found_vol;
    RK_U32          found_vop;
    RK_U32          found_i_vop;

    // frame size parameter
    RK_S32          width;
    RK_S32          height;
    RK_S32          hor_stride;
    RK_S32          ver_stride;
    RK_U32          info_change;
    RK_U32          eos;

    // spliter parameter
    RK_U32          state;
    RK_U32          vop_header_found;   // flag: visual object plane header found

    // bit read context
    BitReadCtx_t    *bit_ctx;
    // vos infomation
    RK_U32          profile;
    RK_U32          level;
    RK_U32          custorm_version;
    // commom buffer for header information
    /*
     * NOTE: We assume that quant matrix only used for current frame decoding
     *       So we use only one quant matrix buffer and only support
     */
    RK_U32          new_qm[2];              // [0] - intra [1] - inter
    RK_U8           quant_matrices[128];    // 0-63: intra 64-127: inter
    Mpeg4Estimation estimation;
    Mpg4Hdr         hdr_curr;               /* header for current decoding frame */
    Mpg4Hdr         hdr_ref0;               /* header for reference frame 0 */
    Mpg4Hdr         hdr_ref1;               /* header for reference frame 1 */

    // dpb/output information
    RK_S32          output;
    RK_S64          last_pts;
    RK_S64          pts_inc;
    RK_S64          pts;
    RK_S64          dts;
    RK_U32          frame_num;
    MppDecCfgSet    *dec_cfg;

    // syntax for hal
    mpeg4d_dxva2_picture_context_t *syntax;
} Mpg4dParserImpl;

static RK_S32 log2bin(RK_U32 value)
{
    RK_S32 n = 0;

    while (value) {
        value >>= 1;
        n++;
    }

    return n;
}

static MPP_RET mpg4d_parse_matrix(BitReadCtx_t *gb, RK_U8 * matrix)
{
    RK_S32 i = 0;
    RK_S32 last, value = 0;

    do {
        last = value;
        READ_BITS(gb, 8, &value);
        matrix[i++] = value;
    } while (value != 0 && i < 64);

    if (value != 0)
        return MPP_ERR_STREAM;

    i--;

    while (i < 64) {
        matrix[i++ ] = last;
    }

    return MPP_OK;

__BITREAD_ERR:
    return MPP_ERR_STREAM;
}

static void mpg4d_set_intra_matrix(RK_U8 * quant_matrices, RK_U8 * matrix)
{
    RK_S32 i;
    RK_U8 *intra_matrix = quant_matrices + 0 * 64;

    for (i = 0; i < 64; i++) {
        intra_matrix[i] = (!i) ? (RK_U8)8 : (RK_U8)matrix[i];
    }
}

static void mpg4d_set_inter_matrix(RK_U8 * quant_matrices, RK_U8 * matrix)
{
    RK_S32 i;
    RK_U8 *inter_matrix = quant_matrices + 1 * 64;

    for (i = 0; i < 64; i++) {
        inter_matrix[i] = (RK_U8) (matrix[i]);
    }
}

static MPP_RET read_vol_complexity_estimation_header(Mpeg4Estimation *e, BitReadCtx_t *gb)
{
    RK_U32 val;

    READ_BITS(gb, 2, &(e->method));                     /* estimation_method */

    if (e->method == 0 || e->method == 1) {
        READ_BITS(gb, 1, &val);
        if (!val) {                                     /* shape_complexity_estimation_disable */
            READ_BITS(gb, 1, &(e->opaque));             /* opaque */
            READ_BITS(gb, 1, &(e->transparent));        /* transparent */
            READ_BITS(gb, 1, &(e->intra_cae));          /* intra_cae */
            READ_BITS(gb, 1, &(e->inter_cae));          /* inter_cae */
            READ_BITS(gb, 1, &(e->no_update));          /* no_update */
            READ_BITS(gb, 1, &(e->upsampling));         /* upsampling */
        }

        READ_BITS(gb, 1, &val);
        if (!val) {                                     /* texture_complexity_estimation_set_1_disable */
            READ_BITS(gb, 1, &(e->intra_blocks));       /* intra_blocks */
            READ_BITS(gb, 1, &(e->inter_blocks));       /* inter_blocks */
            READ_BITS(gb, 1, &(e->inter4v_blocks));     /* inter4v_blocks */
            READ_BITS(gb, 1, &(e->not_coded_blocks));   /* not_coded_blocks */
        }
    }

    SKIP_BITS(gb, 1);

    READ_BITS(gb, 1, &val);
    if (!val) {                                         /* texture_complexity_estimation_set_2_disable */
        READ_BITS(gb, 1, &(e->dct_coefs));              /* dct_coefs */
        READ_BITS(gb, 1, &(e->dct_lines));              /* dct_lines */
        READ_BITS(gb, 1, &(e->vlc_symbols));            /* vlc_symbols */
        READ_BITS(gb, 1, &(e->vlc_bits));               /* vlc_bits */
    }

    READ_BITS(gb, 1, &val);
    if (!val) {                                         /* motion_compensation_complexity_disable */
        READ_BITS(gb, 1, &(e->apm));                    /* apm */
        READ_BITS(gb, 1, &(e->npm));                    /* npm */
        READ_BITS(gb, 1, &(e->interpolate_mc_q));       /* interpolate_mc_q */
        READ_BITS(gb, 1, &(e->forw_back_mc_q));         /* forw_back_mc_q */
        READ_BITS(gb, 1, &(e->halfpel2));               /* halfpel2 */
        READ_BITS(gb, 1, &(e->halfpel4));               /* halfpel4 */
    }

    SKIP_BITS(gb, 1);

    if (e->method == 1) {
        READ_BITS(gb, 1, &val);
        if (!val) {                                     /* version2_complexity_estimation_disable */
            READ_BITS(gb, 1, &(e->sadct));              /* sadct */
            READ_BITS(gb, 1, &(e->quarterpel));         /* quarterpel */
        }
    }

    return MPP_OK;

__BITREAD_ERR:
    return MPP_ERR_STREAM;
}

/* vop estimation header */
static MPP_RET read_vop_complexity_estimation_header(Mpeg4Estimation *e, BitReadCtx_t *gb, Mpg4Hdr *mp4Hdr, int coding_type)
{
    if (e->method == 0 || e->method == 1) {
        if (coding_type == MPEG4_I_VOP) {
            if (e->opaque)  SKIP_BITS(gb, 8);           /* dcecs_opaque */
            if (e->transparent) SKIP_BITS(gb, 8);       /* */
            if (e->intra_cae) SKIP_BITS(gb, 8);         /* */
            if (e->inter_cae) SKIP_BITS(gb, 8);         /* */
            if (e->no_update) SKIP_BITS(gb, 8);         /* */
            if (e->upsampling) SKIP_BITS(gb, 8);        /* */
            if (e->intra_blocks) SKIP_BITS(gb, 8);      /* */
            if (e->not_coded_blocks) SKIP_BITS(gb, 8);  /* */
            if (e->dct_coefs) SKIP_BITS(gb, 8);         /* */
            if (e->dct_lines) SKIP_BITS(gb, 8);         /* */
            if (e->vlc_symbols) SKIP_BITS(gb, 8);       /* */
            if (e->vlc_bits) SKIP_BITS(gb, 8);          /* */
            if (e->sadct)  SKIP_BITS(gb, 8);            /* */
        }

        if (coding_type == MPEG4_P_VOP) {
            if (e->opaque) SKIP_BITS(gb, 8);            /* */
            if (e->transparent) SKIP_BITS(gb, 8);       /* */
            if (e->intra_cae) SKIP_BITS(gb, 8);         /* */
            if (e->inter_cae) SKIP_BITS(gb, 8);         /* */
            if (e->no_update) SKIP_BITS(gb, 8);         /* */
            if (e->upsampling) SKIP_BITS(gb, 8);        /* */
            if (e->intra_blocks) SKIP_BITS(gb, 8);      /* */
            if (e->not_coded_blocks) SKIP_BITS(gb, 8);  /* */
            if (e->dct_coefs) SKIP_BITS(gb, 8);         /* */
            if (e->dct_lines) SKIP_BITS(gb, 8);         /* */
            if (e->vlc_symbols) SKIP_BITS(gb, 8);       /* */
            if (e->vlc_bits) SKIP_BITS(gb, 8);          /* */
            if (e->inter_blocks) SKIP_BITS(gb, 8);      /* */
            if (e->inter4v_blocks) SKIP_BITS(gb, 8);    /* */
            if (e->apm) SKIP_BITS(gb, 8);               /* */
            if (e->npm) SKIP_BITS(gb, 8);               /* */
            if (e->forw_back_mc_q) SKIP_BITS(gb, 8);    /* */
            if (e->halfpel2) SKIP_BITS(gb, 8);          /* */
            if (e->halfpel4) SKIP_BITS(gb, 8);          /* */
            if (e->sadct) SKIP_BITS(gb, 8);             /* */
            if (e->quarterpel) SKIP_BITS(gb, 8);        /* */
        }

        if (coding_type == MPEG4_B_VOP) {
            if (e->opaque)  SKIP_BITS(gb, 8);           /* */
            if (e->transparent) SKIP_BITS(gb, 8);       /* */
            if (e->intra_cae) SKIP_BITS(gb, 8);         /* */
            if (e->inter_cae) SKIP_BITS(gb, 8);         /* */
            if (e->no_update) SKIP_BITS(gb, 8);         /* */
            if (e->upsampling) SKIP_BITS(gb, 8);        /* */
            if (e->intra_blocks) SKIP_BITS(gb, 8);      /* */
            if (e->not_coded_blocks) SKIP_BITS(gb, 8);  /* */
            if (e->dct_coefs) SKIP_BITS(gb, 8);         /* */
            if (e->dct_lines) SKIP_BITS(gb, 8);         /* */
            if (e->vlc_symbols) SKIP_BITS(gb, 8);       /* */
            if (e->vlc_bits) SKIP_BITS(gb, 8);          /* */
            if (e->inter_blocks) SKIP_BITS(gb, 8);      /* */
            if (e->inter4v_blocks) SKIP_BITS(gb, 8);    /* */
            if (e->apm) SKIP_BITS(gb, 8);               /* */
            if (e->npm) SKIP_BITS(gb, 8);               /* */
            if (e->forw_back_mc_q) SKIP_BITS(gb, 8);    /* */
            if (e->halfpel2) SKIP_BITS(gb, 8);          /* */
            if (e->halfpel4) SKIP_BITS(gb, 8);          /* */
            if (e->interpolate_mc_q) SKIP_BITS(gb, 8);  /* */
            if (e->sadct)  SKIP_BITS(gb, 8);            /* */
            if (e->quarterpel) SKIP_BITS(gb, 8);        /* */
        }

#ifdef GMC_SUPPORT
        if (coding_type == MPEG4_S_VOP && mp4Hdr->vol.sprite_enable == MPEG4_SPRITE_STATIC) {
            if (e->intra_blocks) SKIP_BITS(gb, 8);      /* */
            if (e->not_coded_blocks) SKIP_BITS(gb, 8);  /* */
            if (e->dct_coefs) SKIP_BITS(gb, 8);         /* */
            if (e->dct_lines) SKIP_BITS(gb, 8);         /* */
            if (e->vlc_symbols) SKIP_BITS(gb, 8);       /* */
            if (e->vlc_bits) SKIP_BITS(gb, 8);          /* */
            if (e->inter_blocks) SKIP_BITS(gb, 8);      /* */
            if (e->inter4v_blocks) SKIP_BITS(gb, 8);    /* */
            if (e->apm) SKIP_BITS(gb, 8);               /* */
            if (e->npm) SKIP_BITS(gb, 8);               /* */
            if (e->forw_back_mc_q) SKIP_BITS(gb, 8);    /* */
            if (e->halfpel2) SKIP_BITS(gb, 8);          /* */
            if (e->halfpel4) SKIP_BITS(gb, 8);          /* */
            if (e->interpolate_mc_q) SKIP_BITS(gb, 8);  /* */
        }
#else
        (void)mp4Hdr;
#endif
    }

    return MPP_OK;

__BITREAD_ERR:
    return MPP_ERR_STREAM;
}

static void init_mpg4_hdr_vol(Mpg4Hdr *header)
{
    memset(&header->vol, 0, sizeof(header->vol));
    header->vol.ver_id = 1;
}

static void init_mpg4_hdr_vop(Mpg4Hdr *header)
{
    memset(&header->vop, 0, sizeof(header->vop));
    header->vop.coding_type = MPEG4_INVALID_VOP;
}

static void init_mpg4_header(Mpg4Hdr *header)
{
    init_mpg4_hdr_vol(header);
    header->usr.packed_mode = 0;
    init_mpg4_hdr_vop(header);
    header->pts         = 0;
    header->slot_idx    = -1;
    header->enqueued    = 0;
}

static MPP_RET mpg4d_parse_vol_header(Mpg4dParserImpl *p, BitReadCtx_t *cb)
{
    RK_U32 val = 0;
    Mpg4Hdr *mp4Hdr = &p->hdr_curr;
    RK_S32 vol_ver_id;
    RK_S32 aspect_ratio;
    RK_S32 vol_control_parameters;

    SKIP_BITS(cb, 1);                                   /* random_accessible_vol */
    READ_BITS(cb, 8, &(mp4Hdr->vol.vo_type));

    READ_BITS(cb, 1, &val); /* is_object_layer_identifier */
    if (val) {
        READ_BITS(cb, 4, &vol_ver_id);  /* video_object_layer_verid */
        SKIP_BITS(cb, 3);                               /* video_object_layer_priority */
    } else {
        vol_ver_id = mp4Hdr->vol.ver_id;
    }

    READ_BITS(cb, 4, &aspect_ratio);

    if (aspect_ratio == MPEG4_VIDOBJLAY_AR_EXTPAR) {    /* aspect_ratio_info */
        RK_S32 par_width, par_height;

        READ_BITS(cb, 8, &par_width);                   /* par_width */
        READ_BITS(cb, 8, &par_height);                  /* par_height */
    }

    READ_BITS(cb, 1, &vol_control_parameters);

    if (vol_control_parameters) {                       /* vol_control_parameters */
        SKIP_BITS(cb, 2);                               /* chroma_format */
        READ_BITS(cb, 1, &(mp4Hdr->vol.low_delay));     /* low_delay flage (1 means no B_VOP) */

        READ_BITS(cb, 1, &val); /* vbv_parameters */
        if (val) {
            RK_U32 bitrate;
            RK_U32 buffer_size;
            RK_U32 occupancy;

            READ_BITS(cb, 15, &val); /* first_half_bit_rate */
            bitrate = val << 15;
            SKIP_BITS(cb, 1);
            READ_BITS(cb, 15, &val); /* latter_half_bit_rate */
            bitrate |= val;
            SKIP_BITS(cb, 1);

            READ_BITS(cb, 15, &val); /* first_half_vbv_buffer_size */
            buffer_size = val << 3;
            SKIP_BITS(cb, 1);
            READ_BITS(cb, 3, &val);
            buffer_size |= val;                             /* latter_half_vbv_buffer_size */

            READ_BITS(cb, 11, &val);
            occupancy = val << 15;                          /* first_half_vbv_occupancy */
            SKIP_BITS(cb, 1);
            READ_BITS(cb, 15, &val);
            occupancy |= val;                               /* latter_half_vbv_occupancy */
            SKIP_BITS(cb, 1);
            mpg4d_dbg_bit("bitrate %d, buffer_size %d, occupancy %d", bitrate, buffer_size, occupancy);
        }
    } else {
        mp4Hdr->vol.low_delay = 0;
    }

    if (mp4Hdr->vol.vo_type == 0 && vol_control_parameters == 0 && mp4Hdr->vop.frameNumber == 0) {
        mpp_log("looks like this file was encoded with (divx4/(old)xvid/opendivx)\n");
        return MPP_NOK;
    }

    READ_BITS(cb, 2, &(mp4Hdr->vol.shape));                 /* video_object_layer_shape */

    if (mp4Hdr->vol.shape != MPEG4_VIDOBJLAY_SHAPE_RECTANGULAR) {
        mpp_log("unsupported shape %d\n", mp4Hdr->vol.shape);
        return MPP_NOK;
    }

    if (mp4Hdr->vol.shape == MPEG4_VIDOBJLAY_SHAPE_GRAYSCALE && vol_ver_id != 1) {
        SKIP_BITS(cb, 4);                                   /* video_object_layer_shape_extension */
    }

    SKIP_BITS(cb, 1);

    READ_BITS(cb, 16, &(mp4Hdr->vol.time_inc_resolution));  /* vop_time_increment_resolution */

    if (mp4Hdr->vol.time_inc_resolution > 0) {
        mp4Hdr->vol.time_inc_bits = MPP_MAX(log2bin(mp4Hdr->vol.time_inc_resolution - 1), 1);
    } else {
        /* for "old" xvid compatibility, set time_inc_bits = 1 */
        mp4Hdr->vol.time_inc_bits = 1;
    }

    SKIP_BITS(cb, 1);

    READ_BITS(cb, 1, &val);
    if (val) {                                          /* fixed_vop_rate */
        SKIP_BITS(cb, mp4Hdr->vol.time_inc_bits);       /* fixed_vop_time_increment */
    }

    if (mp4Hdr->vol.shape != MPEG4_VIDOBJLAY_SHAPE_BINARY_ONLY) {
        if (mp4Hdr->vol.shape == MPEG4_VIDOBJLAY_SHAPE_RECTANGULAR) {
            RK_S32 width, height;

            SKIP_BITS(cb, 1);
            READ_BITS(cb, 13, &width);                  /* video_object_layer_width */
            SKIP_BITS(cb, 1);
            READ_BITS(cb, 13, &height);                 /* video_object_layer_height */
            SKIP_BITS(cb, 1);

            mpg4d_dbg_bit("width %4d height %4d\n", width, height);

            if (width > 1920 || height > 1088) {
                mpp_err("Warning: unsupport larger than 1920x1088\n");
                return MPP_NOK;
            }

            mp4Hdr->vol.width  = width;
            mp4Hdr->vol.height = height;
            mp4Hdr->vol.mb_width  = (mp4Hdr->vol.width  + 15) >> 4;
            mp4Hdr->vol.mb_height = (mp4Hdr->vol.height + 15) >> 4;
            mp4Hdr->vol.totalMbInVop = mp4Hdr->vol.mb_width * mp4Hdr->vol.mb_height;
            mp4Hdr->vol.hor_stride = 16 * mp4Hdr->vol.mb_width;
            mp4Hdr->vol.ver_stride = 16 * mp4Hdr->vol.mb_height;
        }

        READ_BITS(cb, 1, &(mp4Hdr->vol.interlacing));

        READ_BITS(cb, 1, &val);
        if (!val) {                                     /* obmc_disable */
            /* TODO */
            /* fucking divx4.02 has this enabled */
        }

        /* sprite_enable */
        READ_BITS(cb, (vol_ver_id == 1 ? 1 : 2), &(mp4Hdr->vol.sprite_enable));

        if (mp4Hdr->vol.sprite_enable != MPEG4_SPRITE_NONE) {
            mpp_err("GMC is not supported\n");
            return MPP_ERR_PROTOL;
        }

        if (vol_ver_id != 1 &&
            mp4Hdr->vol.shape != MPEG4_VIDOBJLAY_SHAPE_RECTANGULAR) {
            SKIP_BITS(cb, 1);                           /* sadct_disable */
        }

        READ_BITS(cb, 1, &val);
        if (val) {                                      /* not_8_bit */
            READ_BITS(cb, 4, &(mp4Hdr->vol.quant_bits));/* quant_precision */
            SKIP_BITS(cb, 4);                           /* bits_per_pixel */
        } else {
            mp4Hdr->vol.quant_bits = 5;
        }

        if (mp4Hdr->vol.shape == MPEG4_VIDOBJLAY_SHAPE_GRAYSCALE) {
            SKIP_BITS(cb, 1);                           /* no_gray_quant_update */
            SKIP_BITS(cb, 1);                           /* composition_method */
            SKIP_BITS(cb, 1);                           /* linear_composition */
        }

        READ_BITS(cb, 1, &(mp4Hdr->vol.quant_type));    /* quant_type */

        if (mp4Hdr->vol.quant_type) {
            RK_U8 matrix[64];

            READ_BITS(cb, 1, &val);
            p->new_qm[0] = val;
            if (val) {                                  /* load_intra_quant_mat */
                mpg4d_parse_matrix(cb, matrix);
                mpg4d_set_intra_matrix(p->quant_matrices, matrix);
            }

            READ_BITS(cb, 1, &val);
            p->new_qm[1] = val;
            if (val) {                                  /* load_inter_quant_mat */
                mpg4d_parse_matrix(cb, matrix);
                mpg4d_set_inter_matrix(p->quant_matrices, matrix);
            }

            if (mp4Hdr->vol.shape == MPEG4_VIDOBJLAY_SHAPE_GRAYSCALE) {
                mpp_err("SHAPE_GRAYSCALE is not supported\n");
                return MPP_NOK;
            }
        } else {
            p->new_qm[0] = 0;
            p->new_qm[1] = 0;
        }

        if (vol_ver_id != 1) {
            READ_BITS(cb, 1, &(mp4Hdr->vol.quarter_sample));
        } else
            mp4Hdr->vol.quarter_sample = 0;

        /* complexity estimation disable */
        READ_BITS(cb, 1, &(mp4Hdr->vol.complexity_estimation_disable));

        if (!mp4Hdr->vol.complexity_estimation_disable) {
            mpg4d_dbg_bit("read_vol_complexity_estimation_header\n");
            if (read_vol_complexity_estimation_header(&p->estimation, cb))
                return MPP_ERR_STREAM;
        }

        /* resync_marker_disable */
        READ_BITS(cb, 1, &(mp4Hdr->vol.resync_marker_disable));
        if (!mp4Hdr->vol.resync_marker_disable) {
            mpp_log("resync marker enabled\n");
            // return MPEG4_RESYNC_VOP;
        }

        READ_BITS(cb, 1, &val);
        if (val) {                                      /* data_partitioned */
            SKIP_BITS(cb, 1);                           /* reversible_vlc */
        }

        if (vol_ver_id != 1) {
            READ_BITS(cb, 1, &(mp4Hdr->vol.newpred_enable));

            if (mp4Hdr->vol.newpred_enable) {               /* newpred_enable */
                SKIP_BITS(cb, 2);                       /* requested_upstream_message_type */
                SKIP_BITS(cb, 1);                       /* newpred_segment_type */
            }

            /* reduced_resolution_vop_enable */
            READ_BITS(cb, 1, &(mp4Hdr->vol.reduced_resolution_enable));
        } else {
            mp4Hdr->vol.newpred_enable = 0;
            mp4Hdr->vol.reduced_resolution_enable = 0;
        }

        READ_BITS(cb, 1, &mp4Hdr->vol.scalability);         /* scalability */

        if (mp4Hdr->vol.scalability) {
            SKIP_BITS(cb, 1);                           /* hierarchy_type */
            SKIP_BITS(cb, 4);                           /* ref_layer_id */
            SKIP_BITS(cb, 1);                           /* ref_layer_sampling_direc */
            SKIP_BITS(cb, 5);                           /* hor_sampling_factor_n */
            SKIP_BITS(cb, 5);                           /* hor_sampling_factor_m */
            SKIP_BITS(cb, 5);                           /* vert_sampling_factor_n */
            SKIP_BITS(cb, 5);                           /* vert_sampling_factor_m */
            SKIP_BITS(cb, 1);                           /* enhancement_type */

            if (mp4Hdr->vol.shape == MPEG4_VIDOBJLAY_SHAPE_BINARY /* && hierarchy_type==0 */) {
                /* use_ref_shape and so on*/
                SKIP_BITS(cb, ( 1 + 1 + 1 + 5 + 5 + 5 + 5));
            }

            mpp_err("scalability is not supported\n");
            return MPP_NOK;
        }
    } else { /* mp4Hdr->shape == BINARY_ONLY */
        mpp_err("shape %d is not supported\n");
        return MPP_NOK;
    }

    return MPP_OK;

__BITREAD_ERR:
    return MPP_ERR_STREAM;
}

static MPP_RET mpg4d_parse_user_data(Mpg4dParserImpl *p, BitReadCtx_t *gb)
{
    RK_U8 tmp[256];
    Mpg4Hdr *mp4Hdr = &p->hdr_curr;
    RK_U32 remain_bit = gb->bytes_left_ * 8 + gb->num_remaining_bits_in_curr_byte_;
    RK_S32 i;

    memset(tmp, 0, 256);

    for (i = 0; i < 255 && gb->bytes_left_; i++) {
        RK_U32 show_bit = MPP_MIN(remain_bit, 23);
        RK_U32 val;

        SHOW_BITS(gb, show_bit, &val);
        if (val == 0)
            break;

        READ_BITS(gb, 8, &val);
        tmp[i] = val;
        remain_bit -= 8;
    }

    /*
     * The value of i in the for loop above must be less than 255 otherwise it will cause
     * tmp [256] = 0 which is overflow
     */
    tmp[i] = 0;

    if (!mp4Hdr->usr.packed_mode) {
        RK_U32 packed = 0;

        if ((tmp[0] == 'D') &&
            (tmp[1] == 'i') &&
            (tmp[2] == 'v') &&
            (tmp[3] == 'X')) {
            RK_U32 j = 4;

            if (tmp[j] <= '4') {
                p->custorm_version = 4;
            } else {
                p->custorm_version = 5;
            }

            while ((tmp[j] >= '0') &&
                   (tmp[j] <= '9'))
                j++;

            if (tmp[j] == 'b') {
                j++;

                while ((tmp[j] >= '0') &&
                       (tmp[j] <= '9'))
                    j++;

                packed = tmp[j];
            } else if ((tmp[j + 0] == 'B') &&
                       (tmp[j + 1] == 'u') &&
                       (tmp[j + 2] == 'i') &&
                       (tmp[j + 3] == 'l') &&
                       (tmp[j + 4] == 'd')) {
                j += 5;

                while ((tmp[j] >= '0') && (tmp[j] <= '9'))
                    j++;

                packed = tmp[j];
            }

            mp4Hdr->usr.packed_mode = ((packed == 'p') ? (1) : (0));
        } else {
            mp4Hdr->usr.packed_mode = 0;
        }
    }

    return MPP_OK;

__BITREAD_ERR:
    return MPP_ERR_STREAM;
}

static MPP_RET mpeg4_parse_gop_header(Mpg4dParserImpl *p, BitReadCtx_t *gb)
{
    (void) p;
    RK_S32 hours, minutes, seconds;

    READ_BITS(gb, 5, &hours);
    READ_BITS(gb, 6, &minutes);
    SKIP_BITS(gb, 1);
    READ_BITS(gb, 6, &seconds);

    SKIP_BITS(gb, 1); /* closed_gov */
    SKIP_BITS(gb, 1); /* broken_link */

    return MPP_OK;

__BITREAD_ERR:
    return MPP_ERR_STREAM;
}

static MPP_RET mpeg4_parse_profile_level(Mpg4dParserImpl *p, BitReadCtx_t *bc)
{
    READ_BITS(bc, 4, &p->profile);
    READ_BITS(bc, 4, &p->level);
    return MPP_OK;

__BITREAD_ERR:
    return MPP_ERR_STREAM;
}

static MPP_RET mpeg4_parse_vop_header(Mpg4dParserImpl *p, BitReadCtx_t *gb)
{
    RK_U32 val;
    RK_U32 time_incr = 0;
    RK_S32 time_increment = 0;
    Mpg4Hdr *mp4Hdr = &p->hdr_curr;

    READ_BITS(gb, 2, &(mp4Hdr->vop.coding_type));       /* vop_coding_type */

    READ_BITS(gb, 1, &val);
    while (val != 0) {
        time_incr++;                                    /* time_base */
        READ_BITS(gb, 1, &val);
    }

    SKIP_BITS(gb, 1);

    if (mp4Hdr->vol.time_inc_bits) {
        /* vop_time_increment */
        READ_BITS(gb, mp4Hdr->vol.time_inc_bits, &time_increment);
    }

    if (mp4Hdr->vop.coding_type != MPEG4_B_VOP) {
        mp4Hdr->last_time_base = mp4Hdr->time_base;
        mp4Hdr->time_base += time_incr;
        mp4Hdr->time = mp4Hdr->time_base * mp4Hdr->vol.time_inc_resolution + time_increment;
        mp4Hdr->time_pp = (RK_S32)(mp4Hdr->time - mp4Hdr->last_non_b_time);
        mp4Hdr->last_non_b_time = mp4Hdr->time;
    } else {
        mp4Hdr->time = (mp4Hdr->last_time_base + time_incr) * mp4Hdr->vol.time_inc_resolution + time_increment;
        mp4Hdr->time_bp = mp4Hdr->time_pp - (RK_S32)(mp4Hdr->last_non_b_time - mp4Hdr->time);
    }

    SKIP_BITS(gb, 1);

    READ_BITS(gb, 1, &val);
    if (!val) {                                         /* vop_coded */
        mp4Hdr->vop.coding_type = MPEG4_N_VOP;
        mpg4d_dbg_result("found N frame\n");
        return MPP_OK;
    }
    /* do coding_type detection here in order to save time_bp / time_pp */
    if (mp4Hdr->vop.coding_type == MPEG4_B_VOP &&
        (p->hdr_ref0.slot_idx == -1 || p->hdr_ref1.slot_idx == -1)) {
        mpg4d_dbg_result("MPEG4 DIVX 5 PBBI case found!\n");
        return MPP_NOK;
    }
    if (mp4Hdr->vop.coding_type == MPEG4_I_VOP)
        p->found_i_vop = 1;

    if (mp4Hdr->vol.newpred_enable) {
        RK_S32 vop_id;
        RK_S32 vop_id_for_prediction;

        READ_BITS(gb, (MPP_MIN(mp4Hdr->vol.time_inc_bits + 3, 15)), &vop_id);

        READ_BITS(gb, 1, &val);
        if (val) {
            /* vop_id_for_prediction_indication */
            READ_BITS(gb, MPP_MIN(mp4Hdr->vol.time_inc_bits + 3, 15), &vop_id_for_prediction);
        }

        SKIP_BITS(gb, 1);
    }

    if ((mp4Hdr->vol.shape != MPEG4_VIDOBJLAY_SHAPE_BINARY_ONLY) &&
        ((mp4Hdr->vop.coding_type == MPEG4_P_VOP) ||
         (mp4Hdr->vop.coding_type == MPEG4_S_VOP &&
          mp4Hdr->vol.sprite_enable == MPEG4_SPRITE_GMC))) {
        READ_BITS(gb, 1, &(mp4Hdr->vop.rounding));          /* rounding_type */
    }

    if (mp4Hdr->vol.reduced_resolution_enable &&
        mp4Hdr->vol.shape == MPEG4_VIDOBJLAY_SHAPE_RECTANGULAR &&
        (mp4Hdr->vop.coding_type == MPEG4_P_VOP || mp4Hdr->vop.coding_type == MPEG4_I_VOP)) {

        READ_BITS(gb, 1, &val);
    }

    mpp_assert(mp4Hdr->vol.shape == MPEG4_VIDOBJLAY_SHAPE_RECTANGULAR);

    if (mp4Hdr->vol.shape != MPEG4_VIDOBJLAY_SHAPE_BINARY_ONLY) {
        if (!mp4Hdr->vol.complexity_estimation_disable) {
            read_vop_complexity_estimation_header(&p->estimation, gb, mp4Hdr, mp4Hdr->vop.coding_type);
        }

        READ_BITS(gb, 3, &val);
        /* intra_dc_vlc_threshold */
        mp4Hdr->vop.intra_dc_vlc_threshold = val;
        mp4Hdr->vop.top_field_first = 0;
        mp4Hdr->vop.alternate_vertical_scan = 0;

        if (mp4Hdr->vol.interlacing) {
            READ_BITS(gb, 1, &(mp4Hdr->vop.top_field_first));
            READ_BITS(gb, 1, &(mp4Hdr->vop.alternate_vertical_scan));
        }
    }

    if ((mp4Hdr->vol.sprite_enable == MPEG4_SPRITE_STATIC ||
         mp4Hdr->vol.sprite_enable == MPEG4_SPRITE_GMC) &&
        mp4Hdr->vop.coding_type == MPEG4_S_VOP) {
        mpp_err("unsupport split mode %d coding type %d\n",
                mp4Hdr->vol.sprite_enable, mp4Hdr->vop.coding_type);
        return MPP_ERR_STREAM;
    }

    READ_BITS(gb, mp4Hdr->vol.quant_bits, &(mp4Hdr->vop.quant));
    if (mp4Hdr->vop.quant < 1)                              /* vop_quant */
        mp4Hdr->vop.quant = 1;

    if (mp4Hdr->vop.coding_type != MPEG4_I_VOP) {
        READ_BITS(gb, 3, &(mp4Hdr->vop.fcode_forward));     /* fcode_forward */
    }

    if (mp4Hdr->vop.coding_type == MPEG4_B_VOP) {
        READ_BITS(gb, 3, &(mp4Hdr->vop.fcode_backward));    /* fcode_backward */
    }

    if (!mp4Hdr->vol.scalability) {
        if ((mp4Hdr->vol.shape != MPEG4_VIDOBJLAY_SHAPE_RECTANGULAR) &&
            (mp4Hdr->vop.coding_type != MPEG4_I_VOP)) {
            SKIP_BITS(gb, 1);                           /* vop_shape_coding_type */
        }
    }

    // NOTE: record header used bits here
    mp4Hdr->vop.hdr_bits = gb->used_bits;

    return MPP_OK;
__BITREAD_ERR:
    return MPP_ERR_STREAM;
}

static void mpg4d_fill_picture_parameters(const Mpg4dParserImpl *p,
                                          DXVA_PicParams_MPEG4_PART2 *pp)
{
    const Mpg4Hdr *hdr_curr = &p->hdr_curr;
    const Mpg4Hdr *hdr_ref0 = &p->hdr_ref0;
    const Mpg4Hdr *hdr_ref1 = &p->hdr_ref1;

    pp->short_video_header = 0;
    pp->vop_coding_type = hdr_curr->vop.coding_type;
    pp->vop_quant = hdr_curr->vop.quant;
    pp->wDecodedPictureIndex = hdr_curr->slot_idx;
    pp->wForwardRefPictureIndex = hdr_ref0->slot_idx;
    pp->wBackwardRefPictureIndex = hdr_ref1->slot_idx;
    pp->vop_time_increment_resolution = hdr_curr->vol.time_inc_resolution;
    pp->TRB[0] = 0; /* FIXME: */
    pp->TRD[0] = 0; /* FIXME: */
    pp->unPicPostProc = 0; /* FIXME: */
    pp->interlaced = hdr_curr->vol.interlacing;
    pp->quant_type = hdr_curr->vol.quant_type;
    pp->quarter_sample = hdr_curr->vol.quarter_sample;
    pp->resync_marker_disable = 0; /* FIXME: */
    pp->data_partitioned = 0; /* FIXME: */
    pp->reversible_vlc = 0; /* FIXME: */
    pp->reduced_resolution_vop_enable = hdr_curr->vol.reduced_resolution_enable;
    pp->vop_coded = (hdr_curr->vop.coding_type != MPEG4_N_VOP);
    pp->vop_rounding_type = hdr_curr->vop.rounding;
    pp->intra_dc_vlc_thr = hdr_curr->vop.intra_dc_vlc_threshold;
    pp->top_field_first = hdr_curr->vop.top_field_first;
    pp->alternate_vertical_scan_flag = hdr_curr->vop.alternate_vertical_scan;
    pp->profile_and_level_indication = (p->profile << 4) | p->level;
    pp->video_object_layer_verid = hdr_curr->vol.ver_id;
    pp->vop_width = hdr_curr->vol.width;
    pp->vop_height = hdr_curr->vol.height;
    pp->sprite_enable = hdr_curr->vol.sprite_enable;
    pp->no_of_sprite_warping_points = 0; /* FIXME: */
    pp->sprite_warping_accuracy = 0; /* FIXME: */
    memset(pp->warping_mv, 0, sizeof(pp->warping_mv));
    pp->vop_fcode_forward = hdr_curr->vop.fcode_forward;
    pp->vop_fcode_backward = hdr_curr->vop.fcode_backward;
    pp->StatusReportFeedbackNumber = 0; /* FIXME: */
    pp->Reserved16BitsA = 0; /* FIXME: */
    pp->Reserved16BitsB = 0; /* FIXME: */

    // Rockchip special data
    pp->custorm_version = p->custorm_version;
    pp->prev_coding_type = (hdr_ref0->vop.coding_type == MPEG4_INVALID_VOP) ? (0) : (hdr_ref0->vop.coding_type);
    pp->time_bp = hdr_curr->time_bp;
    pp->time_pp = hdr_curr->time_pp;
    pp->header_bits = hdr_curr->vop.hdr_bits;
}

static void mpg4d_fill_quantization_matrices(const Mpg4dParserImpl *p,
                                             DXVA_QmatrixData *qm)
{
    RK_S32 i = 0;

    qm->bNewQmatrix[0] = p->new_qm[0];
    qm->bNewQmatrix[1] = p->new_qm[1];
    qm->bNewQmatrix[2] = 0;
    qm->bNewQmatrix[3] = 0;

    // intra Y
    if (p->new_qm[0]) {
        for (i = 0; i < 64; i++) {
            qm->Qmatrix[0][i] = p->quant_matrices[i];
        }
    } else {
        memset(qm->Qmatrix[0], 0, sizeof(qm->Qmatrix[0]));
    }

    // inter Y
    if (p->new_qm[1]) {
        for (i = 0; i < 64; i++) {
            qm->Qmatrix[1][i] = p->quant_matrices[64 + i];
        }
    } else {
        memset(qm->Qmatrix[1], 0, sizeof(qm->Qmatrix[1]));
    }

    memset(qm->Qmatrix[2], 0, sizeof(qm->Qmatrix[2]));
    memset(qm->Qmatrix[3], 0, sizeof(qm->Qmatrix[3]));
}

static void mpg4_syntax_init(mpeg4d_dxva2_picture_context_t *syntax)
{
    DXVA2_DecodeBufferDesc *data = &syntax->desc[0];

    //!< commit picture paramters
    memset(data, 0, sizeof(*data));
    data->CompressedBufferType = DXVA2_PictureParametersBufferType;
    data->pvPVPState = (void *)&syntax->pp;
    data->DataSize = sizeof(syntax->pp);
    syntax->data[0] = data;

    //!< commit Qmatrix
    data = &syntax->desc[1];
    memset(data, 0, sizeof(*data));
    data->CompressedBufferType = DXVA2_InverseQuantizationMatrixBufferType;
    data->pvPVPState = (void *)&syntax->qm;
    data->DataSize = sizeof(syntax->qm);
    data->NumMBsInBuffer = 0;
    syntax->data[1] = data;

    //!< commit bitstream
    data = &syntax->desc[2];
    memset(data, 0, sizeof(*data));
    data->CompressedBufferType = DXVA2_BitStreamDateBufferType;
    syntax->data[2] = data;
}

MPP_RET mpp_mpg4_parser_init(Mpg4dParser *ctx, ParserCfg *cfg)
{
    BitReadCtx_t *bit_ctx = mpp_calloc(BitReadCtx_t, 1);
    Mpg4dParserImpl *p = mpp_calloc(Mpg4dParserImpl, 1);
    mpeg4d_dxva2_picture_context_t *syntax = mpp_calloc(mpeg4d_dxva2_picture_context_t, 1);
    MppBufSlots frame_slots = cfg->frame_slots;

    p->dec_cfg = cfg->cfg;

    if (NULL == p || NULL == bit_ctx || NULL == syntax) {
        mpp_err_f("malloc context failed\n");
        if (p)
            mpp_free(p);
        if (bit_ctx)
            mpp_free(bit_ctx);
        if (syntax)
            mpp_free(syntax);
        return MPP_NOK;
    }

    mpg4d_dbg_func("in\n");

    mpp_buf_slot_setup(frame_slots, 8);
    p->frame_slots      = frame_slots;
    p->state            = -1;
    p->vop_header_found = 0;
    p->bit_ctx          = bit_ctx;
    init_mpg4_header(&p->hdr_curr);
    init_mpg4_header(&p->hdr_ref0);
    init_mpg4_header(&p->hdr_ref1);
    mpg4_syntax_init(syntax);
    p->syntax = syntax;

    mpp_env_get_u32("mpg4d_debug", &mpg4d_debug, 0);

    mpg4d_dbg_func("out\n");

    *ctx = p;
    return MPP_OK;
}

MPP_RET mpp_mpg4_parser_deinit(Mpg4dParser ctx)
{
    Mpg4dParserImpl *p = (Mpg4dParserImpl *)ctx;

    mpg4d_dbg_func("in\n");

    if (p) {
        if (p->bit_ctx) {
            mpp_free(p->bit_ctx);
            p->bit_ctx = NULL;
        }
        if (p->syntax) {
            mpp_free(p->syntax);
            p->syntax = NULL;
        }
        mpp_free(p);
    }

    mpg4d_dbg_func("out\n");

    return MPP_OK;
}

MPP_RET mpp_mpg4_parser_flush(Mpg4dParser ctx)
{
    Mpg4dParserImpl *p = (Mpg4dParserImpl *)ctx;
    MppBufSlots slots = p->frame_slots;
    Mpg4Hdr *hdr_ref0 = &p->hdr_ref0;
    RK_S32 index = hdr_ref0->slot_idx;

    mpg4d_dbg_func("in\n");

    if (!hdr_ref0->enqueued && index >= 0) {
        mpp_buf_slot_set_flag(slots, index, SLOT_QUEUE_USE);
        mpp_buf_slot_enqueue(slots, index, QUEUE_DISPLAY);
        hdr_ref0->enqueued = 1;
    }

    mpg4d_dbg_func("out\n");

    return MPP_OK;
}

MPP_RET mpp_mpg4_parser_reset(Mpg4dParser ctx)
{
    Mpg4dParserImpl *p = (Mpg4dParserImpl *)ctx;
    MppBufSlots slots = p->frame_slots;
    Mpg4Hdr *hdr_ref0 = &p->hdr_ref0;
    Mpg4Hdr *hdr_ref1 = &p->hdr_ref1;
    RK_S32 index = hdr_ref0->slot_idx;

    mpg4d_dbg_func("in\n");

    if (index >= 0) {
        if (!hdr_ref0->enqueued) {
            mpp_buf_slot_set_flag(slots, index, SLOT_QUEUE_USE);
            mpp_buf_slot_enqueue(slots, index, QUEUE_DISPLAY);
            hdr_ref0->enqueued = 1;
        }
        mpp_buf_slot_clr_flag(slots, index, SLOT_CODEC_USE);
        hdr_ref0->slot_idx = -1;
    }

    index = hdr_ref1->slot_idx;
    if (index >= 0) {
        mpp_buf_slot_clr_flag(slots, index, SLOT_CODEC_USE);
        hdr_ref1->slot_idx = -1;
    }

    p->found_i_vop      = 0;
    p->found_vop        = 0;
    p->state            = -1;
    p->vop_header_found = 0;

    mpg4d_dbg_func("out\n");

    return MPP_OK;
}

MPP_RET mpp_mpg4_parser_split(Mpg4dParser ctx, MppPacket dst, MppPacket src)
{
    MPP_RET ret = MPP_NOK;
    Mpg4dParserImpl *p = (Mpg4dParserImpl *)ctx;
    RK_U8 *src_buf = (RK_U8 *)mpp_packet_get_pos(src);
    RK_U32 src_len = (RK_U32)mpp_packet_get_length(src);
    RK_U32 src_eos = mpp_packet_get_eos(src);
    RK_S64 src_pts = mpp_packet_get_pts(src);
    RK_U8 *dst_buf = (RK_U8 *)mpp_packet_get_data(dst);
    RK_U32 dst_len = (RK_U32)mpp_packet_get_length(dst);
    RK_U32 src_pos = 0;

    mpg4d_dbg_func("in\n");

    // find the began of the vop
    if (!p->vop_header_found) {
        // add last startcode to the new frame data
        if ((dst_len < sizeof(p->state))
            && ((p->state & 0x00FFFFFF) == 0x000001)) {
            dst_buf[0] = 0;
            dst_buf[1] = 0;
            dst_buf[2] = 1;
            dst_len = 3;
        }
        while (src_pos < src_len) {
            p->state = (p->state << 8) | src_buf[src_pos];
            dst_buf[dst_len++] = src_buf[src_pos++];
            if (p->state == MPG4_VOP_STARTCODE) {
                p->vop_header_found = 1;
                mpp_packet_set_pts(dst, src_pts);
                break;
            }
        }
    }
    // find the end of the vop
    if (p->vop_header_found) {
        while (src_pos < src_len) {
            p->state = (p->state << 8) | src_buf[src_pos];
            dst_buf[dst_len++] = src_buf[src_pos++];
            if ((p->state & 0x00FFFFFF) == 0x000001) {
                dst_len -= 3;
                p->vop_header_found = 0;
                ret = MPP_OK; // split complete
                break;
            }
        }
    }
    // the last packet
    if (src_eos && src_pos >= src_len) {
        mpp_packet_set_eos(dst);
        ret = MPP_OK;
    }
    // reset the src and dst
    mpp_packet_set_length(dst, dst_len);
    mpp_packet_set_pos(src, src_buf + src_pos);

    mpg4d_dbg_func("out\n");

    return ret;
}

MPP_RET mpp_mpg4_parser_decode(Mpg4dParser ctx, MppPacket pkt)
{
    MPP_RET ret = MPP_OK;
    Mpg4dParserImpl *p = (Mpg4dParserImpl *)ctx;
    BitReadCtx_t *gb = p->bit_ctx;
    RK_U8 *buf = mpp_packet_get_data(pkt);
    RK_S32 len = (RK_S32)mpp_packet_get_length(pkt);
    RK_U32 startcode = 0xff;

    mpg4d_dbg_func("in\n");

    // setup bit read context
    mpp_set_bitread_ctx(gb, buf, len);
    p->found_vop = 0;

    while (gb->bytes_left_) {
        RK_U32 val = 0;

        READ_BITS(gb, 8, &val);
        startcode = (startcode << 8) | val;

        if ((startcode & 0xFFFFFF00) != 0x100)
            continue;

        mpg4d_dbg_bit("found startcode at byte %d\n", gb->used_bits >> 3);

        if (mpg4d_debug & MPG4D_DBG_STARTCODE) {
            mpp_log_f("start code %03x\n", startcode);
            if (startcode <= 0x11F)
                mpp_log_f("Video Object Start");
            else if (startcode <= 0x12F)
                mpp_log_f("Video Object Layer Start");
            else if (startcode <= 0x13F)
                mpp_log_f("Reserved");
            else if (startcode <= 0x15F)
                mpp_log_f("FGS bp start");
            else if (startcode <= 0x1AF)
                mpp_log_f("Reserved");
            else if (startcode == 0x1B0)
                mpp_log_f("Visual Object Seq Start");
            else if (startcode == 0x1B1)
                mpp_log_f("Visual Object Seq End");
            else if (startcode == 0x1B2)
                mpp_log_f("User Data");
            else if (startcode == 0x1B3)
                mpp_log_f("Group of VOP start");
            else if (startcode == 0x1B4)
                mpp_log_f("Video Session Error");
            else if (startcode == 0x1B5)
                mpp_log_f("Visual Object Start");
            else if (startcode == 0x1B6)
                mpp_log_f("Video Object Plane start");
            else if (startcode == 0x1B7)
                mpp_log_f("slice start");
            else if (startcode == 0x1B8)
                mpp_log_f("extension start");
            else if (startcode == 0x1B9)
                mpp_log_f("fgs start");
            else if (startcode == 0x1BA)
                mpp_log_f("FBA Object start");
            else if (startcode == 0x1BB)
                mpp_log_f("FBA Object Plane start");
            else if (startcode == 0x1BC)
                mpp_log_f("Mesh Object start");
            else if (startcode == 0x1BD)
                mpp_log_f("Mesh Object Plane start");
            else if (startcode == 0x1BE)
                mpp_log_f("Still Texture Object start");
            else if (startcode == 0x1BF)
                mpp_log_f("Texture Spatial Layer start");
            else if (startcode == 0x1C0)
                mpp_log_f("Texture SNR Layer start");
            else if (startcode == 0x1C1)
                mpp_log_f("Texture Tile start");
            else if (startcode == 0x1C2)
                mpp_log_f("Texture Shape Layer start");
            else if (startcode == 0x1C3)
                mpp_log_f("stuffing start");
            else if (startcode <= 0x1C5)
                mpp_log_f("reserved");
            else if (startcode <= 0x1FF)
                mpp_log_f("System start");
        }

        if (startcode >= MPG4_VOL_STARTCODE && startcode <= MPG4_VOL_STOPCODE) {
            ret = mpg4d_parse_vol_header(p, gb);
            if (!p->found_vol)
                p->found_vol = (ret == MPP_OK);
        } else if (startcode == MPG4_USER_DATA_STARTCODE) {
            ret = mpg4d_parse_user_data(p, gb);
        } else if (startcode == MPG4_GOP_STARTCODE) {
            ret = mpeg4_parse_gop_header(p, gb);
        } else if (startcode == MPG4_VOS_STARTCODE) {
            ret = mpeg4_parse_profile_level(p, gb);
        } else if (startcode == MPG4_VOP_STARTCODE) {
            ret = mpeg4_parse_vop_header(p, gb);
            if (MPP_OK == ret) {
                RK_S32 coding_type = p->hdr_curr.vop.coding_type;
                mpg4d_dbg_bit("frame %d coding_type %d\n", p->frame_num, coding_type);
                p->frame_num++;

                if (coding_type == MPEG4_N_VOP) {
                    ret = MPP_OK;
                    mpp_align_get_bits(gb);
                    continue;
                }

                p->found_vop = p->found_i_vop;
            }
        }

        if (ret)
            goto __BITREAD_ERR;

        mpp_align_get_bits(gb);

        if (p->found_vol && p->found_vop)
            break;
    }

    if (p->found_vol) {
        mpg4d_dbg_result("found vol w %d h %d\n", p->hdr_curr.vol.width, p->hdr_curr.vol.height);
        p->width    = p->hdr_curr.vol.width;
        p->height   = p->hdr_curr.vol.height;
    }

    p->pts  = mpp_packet_get_pts(pkt);
    p->dts  = mpp_packet_get_dts(pkt);

    ret = (p->found_vol && p->found_vop) ? (MPP_OK) : (MPP_NOK);

__BITREAD_ERR:
    mpg4d_dbg_result("found vol %d vop %d ret %d\n", p->found_vol, p->found_vop, ret);

    if (ret) {
        mpp_packet_set_pos(pkt, buf);
        mpp_packet_set_length(pkt, 0);
    } else {
        RK_U32 used_bytes = gb->used_bits >> 3;
        mpp_packet_set_pos(pkt, buf + used_bytes);
    }

    p->eos = mpp_packet_get_eos(pkt);

    mpg4d_dbg_func("out\n");

    return ret;
}

MPP_RET mpp_mpg4_parser_setup_syntax(Mpg4dParser ctx, MppSyntax *syntax)
{
    Mpg4dParserImpl *p = (Mpg4dParserImpl *)ctx;
    mpeg4d_dxva2_picture_context_t *syn = p->syntax;

    mpg4d_dbg_func("in\n");

    mpg4d_fill_picture_parameters(p, &syn->pp);
    mpg4d_fill_quantization_matrices(p, &syn->qm);

    // fill bit stream parameter
    syn->data[2]->DataSize   = p->bit_ctx->buf_len;
    syn->data[2]->DataOffset = p->hdr_curr.vop.hdr_bits;
    syn->data[2]->pvPVPState = p->bit_ctx->buf;

    syntax->number = 3;
    syntax->data = syn->data;

    mpg4d_dbg_func("out\n");

    return MPP_OK;
}

MPP_RET mpp_mpg4_parser_setup_hal_output(Mpg4dParser ctx, RK_S32 *output)
{
    Mpg4dParserImpl *p = (Mpg4dParserImpl *)ctx;
    Mpg4Hdr *hdr_curr = &p->hdr_curr;
    RK_S32 index = -1;

    mpg4d_dbg_func("in\n");

    if (p->found_i_vop && hdr_curr->vop.coding_type != MPEG4_N_VOP) {
        MppBufSlots slots = p->frame_slots;
        RK_U32 frame_mode = MPP_FRAME_FLAG_FRAME;
        MppFrame frame = NULL;

        mpp_frame_init(&frame);
        mpp_frame_set_width(frame, p->width);
        mpp_frame_set_height(frame, p->height);
        mpp_frame_set_hor_stride(frame, MPP_ALIGN(p->width, 16));
        mpp_frame_set_ver_stride(frame, MPP_ALIGN(p->height, 16));

        /*
         * set slots information
         * 1. output index MUST be set
         * 2. get unused index for output if needed
         * 3. set output index as hal_input
         * 4. set frame information to output index
         * 5. if one frame can be display, it SHOULD be enqueued to display queue
         */
        mpp_buf_slot_get_unused(slots, &index);
        mpp_buf_slot_set_flag(slots, index, SLOT_HAL_OUTPUT);
        mpp_frame_set_pts(frame, p->pts);
        mpp_frame_set_dts(frame, p->dts);

        if (hdr_curr->vol.interlacing) {
            frame_mode = MPP_FRAME_FLAG_PAIRED_FIELD;
            if (hdr_curr->vop.top_field_first)
                frame_mode |= MPP_FRAME_FLAG_TOP_FIRST;
            else
                frame_mode |= MPP_FRAME_FLAG_BOT_FIRST;
        }

        if ((p->dec_cfg->base.enable_vproc & MPP_VPROC_MODE_DETECTION) &&
            frame_mode == MPP_FRAME_FLAG_FRAME)
            frame_mode = MPP_FRAME_FLAG_DEINTERLACED;

        mpp_frame_set_mode(frame, frame_mode);

        mpp_buf_slot_set_prop(slots, index, SLOT_FRAME, frame);
        mpp_frame_deinit(&frame);
        mpp_assert(NULL == frame);

        hdr_curr->slot_idx = index;
    }

    p->output = index;
    *output = index;

    mpg4d_dbg_func("out\n");

    return MPP_OK;
}

MPP_RET mpp_mpg4_parser_setup_refer(Mpg4dParser ctx, RK_S32 *refer, RK_S32 max_ref)
{
    Mpg4dParserImpl *p = (Mpg4dParserImpl *)ctx;
    Mpg4Hdr *hdr_curr = &p->hdr_curr;
    MppBufSlots slots = p->frame_slots;
    RK_S32 index;

    mpg4d_dbg_func("in\n");

    memset(refer, -1, sizeof(max_ref * sizeof(*refer)));
    if (hdr_curr->vop.coding_type != MPEG4_N_VOP) {
        index = p->hdr_ref0.slot_idx;
        if (index >= 0) {
            mpp_buf_slot_set_flag(slots, index, SLOT_HAL_INPUT);
            refer[0] = index;
        }
        index = p->hdr_ref1.slot_idx;
        if (index >= 0) {
            mpp_buf_slot_set_flag(slots, index, SLOT_HAL_INPUT);
            refer[1] = index;
        }
    }

    mpg4d_dbg_func("out\n");

    return MPP_OK;
}

MPP_RET mpp_mpg4_parser_update_dpb(Mpg4dParser ctx)
{
    Mpg4dParserImpl *p = (Mpg4dParserImpl *)ctx;
    MppBufSlots slots = p->frame_slots;
    Mpg4Hdr *hdr_curr = &p->hdr_curr;
    Mpg4Hdr *hdr_ref0 = &p->hdr_ref0;
    Mpg4Hdr *hdr_ref1 = &p->hdr_ref1;
    RK_S32 coding_type = hdr_curr->vop.coding_type;
    RK_S32 index = p->output;

    mpg4d_dbg_func("in\n");

    // update pts increacement
    if (p->pts != p->last_pts)
        p->pts_inc = p->pts - p->last_pts;

    switch (coding_type) {
    case MPEG4_B_VOP : {
        mpp_assert((hdr_ref0->slot_idx >= 0) && (hdr_ref1->slot_idx >= 0));
        // B frame -> index current frame
        // output current frame and do not change ref0 and ref1
        index = hdr_curr->slot_idx;
        mpp_buf_slot_set_flag(slots, index, SLOT_QUEUE_USE);
        mpp_buf_slot_enqueue(slots, index, QUEUE_DISPLAY);
    }
    /*
     * NOTE: here fallback to N vop - do nothing
     */
    case MPEG4_INVALID_VOP :
    case MPEG4_N_VOP : {
    } break;
    case MPEG4_I_VOP :
    case MPEG4_P_VOP :
    case MPEG4_S_VOP :
        // the other case -> index reference 0
        index = hdr_ref0->slot_idx;
        if (!hdr_ref0->enqueued && index >= 0) {
            mpp_buf_slot_set_flag(slots, index, SLOT_QUEUE_USE);
            mpp_buf_slot_enqueue(slots, index, QUEUE_DISPLAY);
        }
        // non B frame send this frame to reference queue
        mpp_buf_slot_set_flag(slots, hdr_curr->slot_idx, SLOT_CODEC_USE);

        // release ref1
        index = hdr_ref1->slot_idx;
        if (index >= 0)
            mpp_buf_slot_clr_flag(slots, index, SLOT_CODEC_USE);

        // swap ref0 to ref1, current to ref0
        *hdr_ref1 = *hdr_ref0;
        *hdr_ref0 = *hdr_curr;
        hdr_curr->pts       = 0;
    }

    init_mpg4_hdr_vop(hdr_curr);
    hdr_curr->slot_idx = -1;
    p->last_pts = p->pts;

    mpg4d_dbg_func("out\n");

    return MPP_OK;
}



