/*
 * 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  "h264e_dpb"

#include <string.h>

#include "mpp_mem.h"
#include "mpp_common.h"

#include "h264e_debug.h"
#include "h264e_dpb.h"
#include "h264e_slice.h"

void h264e_dpb_dump_usage(H264eDpb *dpb, const char *fmt)
{
    RK_S32 i = 0;
    char buf[256];
    RK_S32 pos = 0;

    pos += snprintf(buf, sizeof(buf) - 1, "total %2d ", dpb->total_cnt);

    for (i = 0; i < dpb->total_cnt; i++) {
        H264eDpbFrm *frm = &dpb->frames[i];

        pos += snprintf(buf + pos, sizeof(buf) - 1 - pos, "%04x ", frm->on_used);
    }
    mpp_log("%s %s", fmt, buf);
}

void h264e_dpb_dump_frm(H264eDpb *dpb, const char *caller, RK_S32 line)
{
    RK_S32 i = 0;

    mpp_log_f("dump dpb frame info in %s line %d\n", caller, line);

    mpp_log_f("dpb %p total count %d size %d\n", dpb, dpb->total_cnt, dpb->dpb_size);
    mpp_log_f("dpb %5s %5s %5s %5s %5s %5s %5s %5s %5s\n",
              "slot", "use", "seq", "type", "tid", "ref", "idx", "mode", "arg");

    for (i = 0; i < dpb->total_cnt; i++) {
        H264eDpbFrm *frm = &dpb->frames[i];
        EncFrmStatus *status = &frm->status;

        mpp_log_f("frm %5d %5d %5d %5s %5d %5s %5d %5x %5d\n",
                  i, frm->on_used, status->seq_idx,
                  (status->is_intra) ? (status->is_idr ? "I" : "i" ) :
                      status->is_non_ref ? "p" : "P",
                      status->temporal_id,
                      status->is_non_ref ? "non" : status->is_lt_ref ? "lt" : "st",
                      status->lt_idx,
                      status->ref_mode,
                      status->ref_arg);
    }
}

void h264e_dpb_dump_listX(H264eDpbFrm **list, RK_S32 count)
{
    RK_S32 i;

    for (i = 0; i < count; i++) {
        H264eDpbFrm *frm = list[i];
        EncFrmStatus *status = &frm->status;

        mpp_log_f("frm  %2d   %d  %-3d %s    %-3d %-3s %-3d %-4x %-3d\n",
                  i, frm->on_used, status->seq_idx,
                  (status->is_intra) ? (status->is_idr ? "I" : "i" ) :
                      status->is_non_ref ? "p" : "P",
                      status->temporal_id,
                      status->is_non_ref ? "non" : status->is_lt_ref ? "lt" : "st",
                      status->lt_idx,
                      status->ref_mode,
                      status->ref_arg);
    }
}

void h264e_dpb_dump_list(H264eDpb *dpb)
{
    mpp_log_f("dump dpb list info\n");

    mpp_log_f("dpb  size %d used %d st %d lt %d\n",
              dpb->dpb_size, dpb->used_size, dpb->st_size, dpb->lt_size);

    if (dpb->st_size + dpb->lt_size) {
        mpp_log_f("list slot use seq type tid ref idx mode arg\n", dpb, dpb->total_cnt);

        h264e_dpb_dump_listX(dpb->list, dpb->st_size + dpb->lt_size);
    }
}

MPP_RET h264e_dpb_init(H264eDpb *dpb, H264eReorderInfo *reorder, H264eMarkingInfo *marking)
{
    RK_S32 i;

    h264e_dbg_dpb("enter %p\n", dpb);

    memset(dpb, 0, sizeof(*dpb));

    dpb->reorder    = reorder;
    dpb->marking    = marking;
    for (i = 0; i < (RK_S32)MPP_ARRAY_ELEMS(dpb->frames); i++)
        dpb->frames[i].slot_idx = i;

    h264e_dbg_dpb("leave %p\n", dpb);

    return MPP_OK;
}

MPP_RET h264e_dpb_setup(H264eDpb *dpb, MppEncCfgSet* cfg, H264eSps *sps)
{
    MPP_RET ret = MPP_OK;
    MppEncCpbInfo *info = mpp_enc_ref_cfg_get_cpb_info(cfg->ref_cfg);

    h264e_dbg_dpb("enter %p\n", dpb);

    RK_S32 ref_frm_num = sps->num_ref_frames;
    RK_S32 log2_max_frm_num = sps->log2_max_frame_num_minus4 + 4;
    RK_S32 log2_max_poc_lsb = sps->log2_max_poc_lsb_minus4 + 4;

    /* NOTE: new configure needs to clear dpb first */
    h264e_dpb_init(dpb, dpb->reorder, dpb->marking);

    memcpy(&dpb->info, info, sizeof(dpb->info));
    dpb->dpb_size = info->dpb_size;
    dpb->total_cnt = info->dpb_size + 1;
    dpb->max_frm_num = 1 << log2_max_frm_num;
    dpb->max_poc_lsb = (1 << log2_max_poc_lsb);
    dpb->poc_type = sps->pic_order_cnt_type;

    if (cfg->hw.extra_buf)
        dpb->total_cnt++;

    h264e_dbg_dpb("max  ref frm num %d total slot %d\n",
                  ref_frm_num, dpb->total_cnt);
    h264e_dbg_dpb("log2 max frm num %d -> %d\n",
                  log2_max_frm_num, dpb->max_frm_num);
    h264e_dbg_dpb("log2 max poc lsb %d -> %d\n",
                  log2_max_poc_lsb, dpb->max_poc_lsb);

    h264e_dbg_dpb("leave %p\n", dpb);

    return ret;
}

H264eDpbFrm *find_cpb_frame(H264eDpb *dpb, EncFrmStatus *frm)
{
    H264eDpbFrm *frms = dpb->frames;
    RK_S32 seq_idx = frm->seq_idx;
    RK_S32 cnt = dpb->total_cnt;
    RK_S32 i;

    if (!frm->valid)
        return NULL;

    h264e_dbg_dpb("frm %d start finding slot\n", frm->seq_idx);

    for (i = 0; i < cnt; i++) {
        EncFrmStatus *p = &frms[i].status;

        if (p->valid && p->seq_idx == seq_idx) {
            h264e_dbg_dpb("frm %d match slot %d valid %d\n",
                          p->seq_idx, i, p->valid);

            mpp_assert(p->is_non_ref == frm->is_non_ref);
            mpp_assert(p->is_lt_ref == frm->is_lt_ref);
            mpp_assert(p->lt_idx == frm->lt_idx);
            mpp_assert(p->temporal_id == frm->temporal_id);
            return &frms[i];
        }
    }

    mpp_err_f("can not find match frm %d\n", seq_idx);
    h264e_dpb_dump_frms(dpb);
    abort();

    return NULL;
}


static int cmp_st_list(const void *p0, const void *p1)
{
    H264eDpbFrm *frm0 = *(H264eDpbFrm **)p0;
    H264eDpbFrm *frm1 = *(H264eDpbFrm **)p1;

    if (frm0->frame_num == frm1->frame_num)
        return 0;
    if (frm0->frame_num < frm1->frame_num)
        return 1;
    else
        return -1;
}

static int cmp_lt_list(const void *p0, const void *p1)
{
    H264eDpbFrm *frm0 = *(H264eDpbFrm **)p0;
    H264eDpbFrm *frm1 = *(H264eDpbFrm **)p1;

    if (frm0->lt_idx == frm1->lt_idx)
        return 0;
    if (frm0->lt_idx > frm1->lt_idx)
        return 1;
    else
        return -1;
}

/*
 * Build list function
 *
 * This function should build the default list for current frame.
 * Then check the reference frame is the default one or not.
 * Reorder command is need if the reference frame is not match.
 */
void h264e_dpb_build_list(H264eDpb *dpb, EncCpbStatus *cpb)
{
    RK_S32 i, j;
    RK_S32 st_size = 0;
    RK_S32 lt_size = 0;

    h264e_dbg_dpb("enter %p\n", dpb);

    /* clear list */
    memset(dpb->list, 0, sizeof(dpb->list));

    if (cpb->curr.is_intra) {
        h264e_dbg_dpb("leave %p\n", dpb);
        return ;
    }

    // 2.1 init list
    // 2.1.1 found all short term and long term ref
    h264e_dbg_list("cpb init scaning start\n");

    for (i = 0; i < MAX_CPB_REFS; i++) {
        EncFrmStatus *frm = &cpb->init[i];

        if (!frm->valid)
            continue;

        mpp_assert(!frm->is_non_ref);

        h264e_dbg_list("idx %d frm %d valid %d is_non_ref %d lt_ref %d\n",
                       i, frm->seq_idx, frm->valid, frm->is_non_ref, frm->is_lt_ref);

        H264eDpbFrm *p = find_cpb_frame(dpb, frm);
        if (!frm->is_lt_ref) {
            dpb->stref[st_size++] = p;
            p->status.val = frm->val;
            h264e_dbg_list("found st %d st_size %d %p\n", i, st_size, frm);
        } else {
            dpb->ltref[lt_size++] = p;
            p->status.val = frm->val;
            h264e_dbg_list("found lt %d lt_size %d %p\n", i, lt_size, frm);
        }
    }

    h264e_dbg_list("cpb init scaning done\n");
    h264e_dbg_dpb("dpb_size %d st_size %d lt_size %d\n", dpb->dpb_size, st_size, lt_size);

    // sort st list
    if (st_size > 1) {
        if (h264e_debug & H264E_DBG_LIST) {
            mpp_log_f("dpb st list before sort\n");
            h264e_dpb_dump_listX(dpb->stref, st_size);
        }

        qsort(dpb->stref, st_size, sizeof(dpb->stref[0]), cmp_st_list);

        if (h264e_debug & H264E_DBG_LIST) {
            mpp_log_f("dpb st list after  sort\n");
            h264e_dpb_dump_listX(dpb->stref, st_size);
        }
    }

    if (lt_size > 1) {
        if (h264e_debug & H264E_DBG_LIST) {
            mpp_log_f("dpb lt list before sort\n");
            h264e_dpb_dump_listX(dpb->ltref, lt_size);
        }

        qsort(dpb->ltref, lt_size, sizeof(dpb->ltref[0]), cmp_lt_list);

        if (h264e_debug & H264E_DBG_LIST) {
            mpp_log_f("dpb lt list after  sort\n");
            h264e_dpb_dump_listX(dpb->ltref, lt_size);
        }
    }

    // generate list before reorder
    memset(dpb->list, 0, sizeof(dpb->list));
    j = 0;
    for (i = 0; i < st_size; i++)
        dpb->list[j++] = dpb->stref[i];

    for (i = 0; i < lt_size; i++)
        dpb->list[j++] = dpb->ltref[i];

    dpb->st_size = st_size;
    dpb->lt_size = lt_size;

    mpp_assert(dpb->dpb_size >= st_size + lt_size);

    if (h264e_debug & H264E_DBG_LIST)
        h264e_dpb_dump_list(dpb);

    /* generate syntax */
    h264e_reorder_wr_rewind(dpb->reorder);

    if (dpb->st_size + dpb->lt_size) {
        H264eDpbFrm *curr = dpb->curr;
        H264eDpbFrm *refr = dpb->refr;
        H264eDpbFrm *def_ref = dpb->list[0];

        RK_S32 curr_frm_cnt = curr->status.seq_idx;
        RK_S32 set_ref_frm_cnt = refr->status.seq_idx;
        RK_S32 def_ref_frm_cnt = def_ref->status.seq_idx;

        h264e_dbg_list("refer curr %d def %d set %d reorder %d\n",
                       curr_frm_cnt, def_ref_frm_cnt, set_ref_frm_cnt,
                       (def_ref_frm_cnt != set_ref_frm_cnt));

        if (def_ref_frm_cnt != set_ref_frm_cnt) {
            H264eRplmo op;

            h264e_dbg_list("reorder to frm %d\n", refr->status.seq_idx);

            mpp_assert(!refr->status.is_non_ref);

            op.modification_of_pic_nums_idc = (refr->status.is_lt_ref) ? (2) : (0);
            if (refr->status.is_lt_ref) {
                op.modification_of_pic_nums_idc = 2;
                op.long_term_pic_idx = refr->status.lt_idx;

                h264e_dbg_list("reorder lt idx %d \n", op.long_term_pic_idx);
            } else {
                op.modification_of_pic_nums_idc = curr->frame_num > refr->frame_num ? 0 : 1;
                op.abs_diff_pic_num_minus1 = MPP_ABS(curr->frame_num - refr->frame_num) - 1;

                h264e_dbg_list("reorder st cur %d refr %d diff - 1 %d\n",
                               curr->frame_num, refr->frame_num,
                               op.abs_diff_pic_num_minus1);
            }

            h264e_reorder_wr_op(dpb->reorder, &op);
        }
    } else {
        h264e_dbg_list("refer NULL\n");
    }

    h264e_dbg_dpb("leave %p\n", dpb);
}

void h264e_dpb_build_marking(H264eDpb *dpb)
{
    H264eDpbFrm *frm = dpb->curr;
    H264eMarkingInfo *marking = dpb->marking;

    h264e_dbg_dpb("enter %p\n", dpb);

    h264e_marking_wr_rewind(marking);

    // refernce frame can not mark itself as unreferenced.
    if (frm->status.is_idr) {
        marking->idr_flag = 1;
        marking->no_output_of_prior_pics = 0;
        marking->long_term_reference_flag = frm->status.is_lt_ref;
        goto DONE;
    }

    marking->idr_flag = 0;
    marking->long_term_reference_flag = 0;
    marking->adaptive_ref_pic_buffering = 0;

    h264e_dbg_dpb("frm %d ref %d lt %d T%d\n",
                  frm->status.seq_idx, !frm->status.is_non_ref,
                  frm->status.is_lt_ref, frm->status.temporal_id);

    if (frm->status.is_non_ref)
        goto DONE;

    // When current frame is lt_ref update max_lt_idx
    if (frm->status.is_lt_ref) {
        H264eMmco op;

        dpb->next_max_lt_idx = dpb->info.max_lt_idx;

        if (dpb->next_max_lt_idx != dpb->curr_max_lt_idx) {
            RK_S32 max_lt_idx_p1 = dpb->next_max_lt_idx + 1;

            op.mmco = 4;
            op.max_long_term_frame_idx_plus1 = max_lt_idx_p1;

            h264e_marking_wr_op(marking, &op);
            h264e_dbg_mmco("add mmco 4 %d\n", max_lt_idx_p1);

            dpb->curr_max_lt_idx = dpb->next_max_lt_idx;
        }

        op.mmco = 6;
        op.long_term_frame_idx = frm->lt_idx;

        h264e_marking_wr_op(marking, &op);
        h264e_dbg_mmco("add mmco 6 %d\n", frm->lt_idx);
    }

    // TODO: check and mark used frame
DONE:
    h264e_dbg_dpb("dpb size %d used %d\n", dpb->dpb_size, dpb->used_size);
    h264e_dbg_dpb("leave %p\n", dpb);
}

MPP_RET h264e_dpb_proc(H264eDpb *dpb, EncCpbStatus *cpb)
{
    EncFrmStatus *curr = &cpb->curr;
    EncFrmStatus *refr = &cpb->refr;
    EncFrmStatus *init = cpb->init;
    H264eDpbFrm *frames = dpb->frames;
    H264eDpbRt *rt = &dpb->rt;
    RK_S32 used_size = 0;
    RK_S32 seq_idx = curr->seq_idx;
    RK_S32 i;

    h264e_dbg_dpb("enter %p total %d\n", dpb, dpb->total_cnt);

    if (curr->is_idr) {
        for (i = 0; i < H264E_MAX_REFS_CNT + 1; i++) {
            frames[i].dpb_used = 0;
            frames[i].status.valid = 0;
        }
        dpb->used_size = 0;
        dpb->curr_max_lt_idx = 0;
        dpb->next_max_lt_idx = 0;
    } else {
        if (curr->seq_idx == rt->last_seq_idx) {
            h264e_dbg_dpb("NOTE: reenc found at %d\n", curr->seq_idx);
            memcpy(rt, &dpb->rt_bak, sizeof(*rt));
            memcpy(frames, dpb->frm_bak, sizeof(dpb->frm_bak));
        }
    }

    if (h264e_debug & H264E_DBG_DPB)
        h264e_dpb_dump_frms(dpb);
    /*
     * 1. scan and map cpb to internal cpb h264edpb is bridge from
     *    user defined cpb to hal slot index
     */
    for (i = 0; i < MAX_CPB_REFS; i++) {
        H264eDpbFrm *frm = find_cpb_frame(dpb, &init[i]);
        dpb->map[i] = frm;
        if (frm) {
            if (!frm->on_used && !frm->status.force_pskip_is_ref)
                mpp_err_f("warning frm %d is used by cpb but on not used status\n",
                          frm->seq_idx);
            frm->dpb_used = 1;
            used_size++;
        }
    }

    mpp_assert(dpb->used_size == used_size);
    h264e_dbg_dpb("frm %d init cpb used size %d vs %d\n", seq_idx,
                  used_size, dpb->used_size);

    /* backup current runtime status */
    memcpy(&dpb->rt_bak, rt, sizeof(dpb->rt_bak));
    memcpy(dpb->frm_bak, frames, sizeof(dpb->frm_bak));

    /* mark current cpb */
    RK_S32 found_curr = 0;

    /* 2. scan all slot for used map and find a slot for current */
    dpb->curr = NULL;

    h264e_dbg_dpb("frm %d start finding slot for storage\n", seq_idx);
    for (i = 0; i < H264E_MAX_REFS_CNT + 1; i++) {
        H264eDpbFrm *p = &frames[i];
        RK_S32 curr_frm_num = rt->last_frm_num + rt->last_is_ref;
        RK_S32 curr_poc_lsb = rt->last_poc_lsb;
        RK_S32 curr_poc_msb = rt->last_poc_msb;
        RK_S32 valid = 0;
        RK_S32 j;

        for (j = 0; j < MAX_CPB_REFS; j++) {
            if (p == dpb->map[j] || p->as_pskip_ref) {
                valid = 1;
                break;
            }
        }

        h264e_dbg_dpb("slot %2d check in cpb init valid %d used %04x\n",
                      i, valid, p->on_used);

        if (valid) {
            if ((!p->as_pskip_ref) &&
                (!p->on_used || !p->status.valid)) {
                mpp_assert(p->on_used);
                mpp_assert(p->status.valid);
                h264e_dpb_dump_frms(dpb);
            }
            continue;
        }

        h264e_dbg_dpb("slot %2d used %04x checking\n", i, p->on_used);

        if (p->hal_used)
            continue;

        if (found_curr) {
            p->dpb_used = 0;
            p->status.valid = 0;
            continue;
        }

        h264e_dbg_dpb("slot %2d used %04x mark as current\n", i, p->on_used);

        dpb->curr = p;
        p->status.val = curr->val;
        p->seq_idx = curr->seq_idx;
        if (curr->is_idr) {
            p->frame_num = 0;
            p->poc = 0;
            curr_frm_num = 0;
            curr_poc_lsb = 0;
            curr_poc_msb = 0;
        } else {
            if (curr_frm_num >= dpb->max_frm_num)
                curr_frm_num = 0;

            p->frame_num = curr_frm_num;

            if (dpb->poc_type == 0)
                curr_poc_lsb += 2;
            else if (dpb->poc_type == 2)
                curr_poc_lsb += 1 + rt->last_is_ref;
            else
                mpp_err_f("do not support poc type 1\n");

            if (curr_poc_lsb >= dpb->max_poc_lsb) {
                curr_poc_lsb = 0;
                curr_poc_msb++;
            }

            p->poc = curr_poc_lsb + curr_poc_msb * dpb->max_poc_lsb;

        }
        p->lt_idx = curr->lt_idx;
        p->dpb_used = 1;

        rt->last_seq_idx = curr->seq_idx;
        rt->last_is_ref  = !curr->is_non_ref;
        rt->last_frm_num = curr_frm_num;
        rt->last_poc_lsb = curr_poc_lsb;
        rt->last_poc_msb = curr_poc_msb;

        found_curr = 1;
        h264e_dbg_dpb("frm %d update slot %d with frame_num %d poc %d\n",
                      seq_idx, i, p->frame_num, p->poc);
    }

    mpp_assert(dpb->curr);

    if (NULL == dpb->curr)
        mpp_err_f("frm %d failed to find a slot for curr %d\n", seq_idx);

    h264e_dbg_dpb("frm %d start finding slot for refr %d\n", seq_idx, refr->seq_idx);
    dpb->refr = find_cpb_frame(dpb, refr);
    if (NULL == dpb->refr)
        dpb->refr = dpb->curr;

    h264e_dbg_dpb("frm %d curr %d refr %d start building list\n",
                  seq_idx, dpb->curr->slot_idx, dpb->refr->slot_idx);

    h264e_dpb_build_list(dpb, cpb);

    if (h264e_debug & H264E_DBG_DPB)
        h264e_dpb_dump_frms(dpb);

    h264e_dpb_build_marking(dpb);

    if (h264e_debug & H264E_DBG_DPB)
        h264e_dpb_dump_frms(dpb);

    h264e_dbg_dpb("leave %p\n", dpb);

    return MPP_OK;
}

void h264e_dpb_check(H264eDpb *dpb, EncCpbStatus *cpb)
{
    H264eDpbFrm *curr = dpb->curr;
    H264eDpbFrm *refr = dpb->refr;
    RK_S32 i;

    h264e_dbg_dpb("enter %p\n", dpb);

    h264e_dbg_dpb("frm %d refr -> frm %d ready\n",
                  curr->seq_idx, refr->seq_idx);

    if (curr->status.is_non_ref) {
        curr->dpb_used = 0;
        curr->status.valid = 0;
    } else {
        /* sliding window process */
        RK_S32 used_size = dpb->used_size;
        RK_S32 dpb_size = dpb->dpb_size;

        h264e_dbg_dpb("frm %d %s insert dpb used %d max %d\n",
                      curr->seq_idx, curr->status.is_lt_ref ? "lt" : "st",
                      used_size, dpb_size);

        used_size++;

        /* replace lt ref with same lt_idx */
        if (curr->status.is_lt_ref) {
            RK_S32 lt_idx = curr->lt_idx;

            dpb->lt_size++;

            for (i = 0; i < dpb->total_cnt; i++) {
                H264eDpbFrm *tmp = &dpb->frames[i];

                if (tmp == curr)
                    continue;

                if (!tmp->dpb_used)
                    continue;

                if (!tmp->status.valid)
                    continue;

                mpp_assert(!tmp->status.is_non_ref);

                if (!tmp->status.is_lt_ref)
                    continue;

                if (tmp->lt_idx == lt_idx) {
                    tmp->dpb_used = 0;
                    tmp->status.valid = 0;
                    h264e_dbg_dpb("frm %d lt_idx %d replace %d\n",
                                  curr->seq_idx, curr->lt_idx, tmp->slot_idx);
                    dpb->lt_size--;
                    used_size--;
                }
            }
        } else {
            dpb->st_size++;
        }

        if (used_size > dpb_size) {
            mpp_assert(dpb->lt_size <= dpb_size);

            while (used_size > dpb_size) {
                RK_S32 small_poc = 0x7fffffff;
                H264eDpbFrm *unref = NULL;

                h264e_dbg_dpb("sliding window cpb proc: st %d lt %d max %d\n",
                              dpb->st_size, dpb->lt_size, dpb_size);

                for (i = 0; i < dpb->total_cnt; i++) {
                    H264eDpbFrm *tmp = &dpb->frames[i];

                    h264e_dbg_dpb("frm %d num %d poc %d\n",
                                  tmp->seq_idx, tmp->frame_num, tmp->poc);

                    if (tmp->as_pskip_ref)
                        continue;

                    if (!tmp->on_used)
                        continue;

                    if (!tmp->status.valid)
                        continue;

                    mpp_assert(!tmp->status.is_non_ref);

                    if (tmp->status.is_lt_ref)
                        continue;

                    if (tmp->poc < small_poc) {
                        unref = tmp;
                        small_poc = tmp->poc;

                        h264e_dbg_dpb("frm %d update smallest poc to %d\n",
                                      tmp->seq_idx, tmp->poc);
                    }
                }

                if (unref) {
                    h264e_dbg_dpb("removing frm %d poc %d\n",
                                  unref->seq_idx, unref->poc);

                    unref->dpb_used = 0;
                    dpb->st_size--;
                    mpp_assert(dpb->st_size >= 0);
                    used_size--;
                } else
                    break;
            }
        }

        dpb->used_size = used_size;
        if (used_size > dpb_size) {
            mpp_err_f("error found used_size %d > dpb_size %d\n", used_size, dpb_size);
            mpp_assert(used_size <= dpb_size);
        }
    }

    h264e_dbg_dpb("curr %d done used_size %d\n", curr->seq_idx, dpb->used_size);

    RK_S32 used_size = 0;
    for (i = 0; i < MAX_CPB_REFS; i++) {
        dpb->map[i] = find_cpb_frame(dpb, &cpb->final[i]);
        if (dpb->map[i])
            used_size++;
    }

    h264e_dbg_dpb("curr %d cpb final used_size %d vs %d\n",
                  curr->seq_idx, used_size, dpb->used_size);
    mpp_assert(dpb->used_size == used_size);

    h264e_dbg_dpb("leave %p\n", dpb);
}

MPP_RET h264e_dpb_hal_start(H264eDpb *dpb, RK_S32 slot_idx)
{
    H264eDpbFrm *frm = &dpb->frames[slot_idx];

    frm->hal_used++;
    //h264e_dpb_dump_usage(dpb, __FUNCTION__);
    return MPP_OK;
}

MPP_RET h264e_dpb_hal_end(H264eDpb *dpb, RK_S32 slot_idx)
{
    H264eDpbFrm *frm = &dpb->frames[slot_idx];

    frm->hal_used--;
    //h264e_dpb_dump_usage(dpb, __FUNCTION__);
    return MPP_OK;
}
