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

#include <stdio.h>
#include <string.h>

#include "mpp_common.h"
#include "mpp_buf_slot.h"

#include "h264d_scalist.h"
#include "h264d_dpb.h"
#include "h264d_init.h"

#ifndef INT_MIN
#define INT_MIN     (-2147483647 - 1) /* minimum (signed) int value */
#endif
#ifndef INT_MAX
#define INT_MAX       2147483647      /* maximum (signed) int value */
#endif
static RK_S32 RoundLog2(RK_S32 iValue)
{
    RK_S32 iRet = 0;
    RK_S32 iValue_square = iValue * iValue;

    while ((1 << (iRet + 1)) <= iValue_square) {
        ++iRet;
    }
    iRet = (iRet + 1) >> 1;

    return iRet;
}

static RK_S32 getDpbSize(H264dVideoCtx_t *p_Vid, H264_SPS_t *active_sps)
{
    RK_S32 size = 0, num_views = 0;
    RK_S32 pic_size = (active_sps->pic_width_in_mbs_minus1 + 1)
                      * (active_sps->pic_height_in_map_units_minus1 + 1) * (active_sps->frame_mbs_only_flag ? 1 : 2) * 384;

    switch (active_sps->level_idc) {
    case 9:
        size = 152064;
        break;
    case 10:
        size = 152064;
        break;
    case 11:
        if (!is_prext_profile(active_sps->profile_idc) && (active_sps->constrained_set3_flag == 1))
            size = 152064;
        else
            size = 345600;
        break;
    case 12:
        size = 912384;
        break;
    case 13:
        size = 912384;
        break;
    case 20:
        size = 912384;
        break;
    case 21:
        size = 1824768;
        break;
    case 22:
        size = 3110400;
        break;
    case 30:
        size = 3110400;
        break;
    case 31:
        size = 6912000;
        break;
    case 32:
        size = 7864320;
        break;
    case 40:
        size = 12582912;
        break;
    case 41:
        size = 12582912;
        break;
    case 42:
        size = 13369344;
        break;
    case 50:
        size = 42393600;
        break;
    case 51:
        size = 70778880;
        break;
    case 52:
        size = 70778880;
        break;
    default:
        size = 0;
        break;
    }
    size /= pic_size;
    if (p_Vid->active_mvc_sps_flag &&
        (p_Vid->profile_idc == H264_PROFILE_MVC_HIGH || p_Vid->profile_idc == H264_PROFILE_STEREO_HIGH)) {
        num_views = p_Vid->active_subsps->num_views_minus1 + 1;
        size = MPP_MIN(2 * size, MPP_MAX(1, RoundLog2(num_views)) * 16) / num_views;
    } else {
        size = MPP_MIN(size, 16);
    }
    if (active_sps->vui_parameters_present_flag && active_sps->vui_seq_parameters.bitstream_restriction_flag) {
        RK_S32 size_vui = 0;
        if ((RK_S32)active_sps->vui_seq_parameters.max_dec_frame_buffering > size) {
            H264D_WARNNING("warnnig: max_dec_frame_buffering larger than MaxDpbSize");
        }
        size_vui = MPP_MAX(1, active_sps->vui_seq_parameters.max_dec_frame_buffering);
        if (size_vui < size) {
            H264D_WARNNING("warning: max_dec_frame_buffering(%d) is less than dpb_size(%d) calculated from Profile/Level.\n", size_vui, size);
        }
        size = size_vui;
    }

    if (size < active_sps->max_num_ref_frames) {
        H264D_WARNNING("warnning: DPB size is less than max_num_ref_frames, level(%d), pic_size(%d), max_num_ref_frames(%d).\n",
                       active_sps->level_idc, pic_size, active_sps->max_num_ref_frames);
        size = MPP_MIN(active_sps->max_num_ref_frames, 16);
    }

    return size;
}

static RK_S32 get_pic_num_x(H264_StorePic_t *p, RK_S32 difference_of_pic_nums_minus1)
{
    RK_S32 currPicNum;

    if (p->structure == FRAME)
        currPicNum = p->frame_num;
    else
        currPicNum = 2 * p->frame_num + 1;

    return currPicNum - (difference_of_pic_nums_minus1 + 1);
}

static void unmark_for_reference(H264_DecCtx_t *p_Dec, H264_FrameStore_t* fs)
{
    H264_StorePic_t *cur_pic = NULL;
    if (fs->is_used & 1) {
        if (fs->top_field) {
            fs->top_field->used_for_reference = 0;
            cur_pic = fs->top_field;
        }
    }
    if (fs->is_used & 2) {
        if (fs->bottom_field) {
            fs->bottom_field->used_for_reference = 0;
            cur_pic = fs->bottom_field;
        }
    }
    if (fs->is_used == 3) {
        if (fs->top_field && fs->bottom_field) {
            fs->top_field->used_for_reference = 0;
            fs->bottom_field->used_for_reference = 0;
        }
        fs->frame->used_for_reference = 0;
        cur_pic = fs->frame;
    }
    fs->is_reference = 0;
    (void)cur_pic;
    (void)p_Dec;
}

static RK_U32 is_short_term_reference(H264_FrameStore_t* fs)
{
    if (fs->is_used == 3) { // frame
        if ((fs->frame->used_for_reference) && (!fs->frame->is_long_term)) {
            return 1;
        }
    }

    if (fs->is_used & 1) { // top field
        if (fs->top_field) {
            if ((fs->top_field->used_for_reference) && (!fs->top_field->is_long_term)) {
                return 1;
            }
        }
    }

    if (fs->is_used & 2) { // bottom field
        if (fs->bottom_field) {
            if ((fs->bottom_field->used_for_reference) && (!fs->bottom_field->is_long_term)) {
                return 1;
            }
        }
    }
    return 0;
}

static RK_U32 is_long_term_reference(H264_FrameStore_t* fs)
{

    if (fs->is_used == 3) { // frame
        if ((fs->frame->used_for_reference) && (fs->frame->is_long_term)) {
            return 1;
        }
    }

    if (fs->is_used & 1) { // top field
        if (fs->top_field) {
            if ((fs->top_field->used_for_reference) && (fs->top_field->is_long_term)) {
                return 1;
            }
        }
    }

    if (fs->is_used & 2) { // bottom field
        if (fs->bottom_field) {
            if ((fs->bottom_field->used_for_reference) && (fs->bottom_field->is_long_term)) {
                return 1;
            }
        }
    }
    return 0;
}

static void unmark_for_long_term_reference(H264_FrameStore_t* fs)
{
    if (fs->is_used & 1) {
        if (fs->top_field) {
            fs->top_field->used_for_reference = 0;
            fs->top_field->is_long_term = 0;
        }
    }
    if (fs->is_used & 2) {
        if (fs->bottom_field) {
            fs->bottom_field->used_for_reference = 0;
            fs->bottom_field->is_long_term = 0;
        }
    }
    if (fs->is_used == 3) {
        if (fs->top_field && fs->bottom_field) {
            fs->top_field->used_for_reference = 0;
            fs->top_field->is_long_term = 0;
            fs->bottom_field->used_for_reference = 0;
            fs->bottom_field->is_long_term = 0;
        }
        fs->frame->used_for_reference = 0;
        fs->frame->is_long_term = 0;
    }

    fs->is_reference = 0;
    fs->is_long_term = 0;
}

static void mm_unmark_short_term_for_reference(H264_DpbBuf_t *p_Dpb, H264_StorePic_t *p, RK_S32 difference_of_pic_nums_minus1)
{
    RK_S32 picNumX = 0;
    RK_U32 i = 0;

    picNumX = get_pic_num_x(p, difference_of_pic_nums_minus1);

    for (i = 0; i < p_Dpb->ref_frames_in_buffer; i++) {
        if (p->structure == FRAME) {
            if ((p_Dpb->fs_ref[i]->is_reference == 3) && (p_Dpb->fs_ref[i]->is_long_term == 0)) {
                if (p_Dpb->fs_ref[i]->frame->pic_num == picNumX) {
                    unmark_for_reference(p_Dpb->p_Vid->p_Dec, p_Dpb->fs_ref[i]);
                    return;
                }
            }
        } else {
            if ((p_Dpb->fs_ref[i]->is_reference & 1) && (!(p_Dpb->fs_ref[i]->is_long_term & 1))) {
                if (p_Dpb->fs_ref[i]->top_field->pic_num == picNumX) {
                    p_Dpb->fs_ref[i]->top_field->used_for_reference = 0;
                    p_Dpb->fs_ref[i]->is_reference &= 2;
                    if (p_Dpb->fs_ref[i]->is_used == 3) {
                        p_Dpb->fs_ref[i]->frame->used_for_reference = 0;
                    }
                    return;
                }
            }
            if ((p_Dpb->fs_ref[i]->is_reference & 2) && (!(p_Dpb->fs_ref[i]->is_long_term & 2))) {
                if (p_Dpb->fs_ref[i]->bottom_field->pic_num == picNumX) {
                    p_Dpb->fs_ref[i]->bottom_field->used_for_reference = 0;
                    p_Dpb->fs_ref[i]->is_reference &= 1;
                    if (p_Dpb->fs_ref[i]->is_used == 3) {
                        p_Dpb->fs_ref[i]->frame->used_for_reference = 0;
                    }
                    return;
                }
            }
        }
    }
}

static void mm_unmark_long_term_for_reference(H264_DpbBuf_t *p_Dpb, H264_StorePic_t *p, RK_S32 long_term_pic_num)
{
    RK_U32 i = 0;

    for (i = 0; i < p_Dpb->ltref_frames_in_buffer; i++) {
        if (p->structure == FRAME) {
            if ((p_Dpb->fs_ltref[i]->is_reference == 3) && (p_Dpb->fs_ltref[i]->is_long_term == 3)) {
                if (p_Dpb->fs_ltref[i]->frame->long_term_pic_num == long_term_pic_num) {
                    unmark_for_long_term_reference(p_Dpb->fs_ltref[i]);
                }
            }
        } else {
            if ((p_Dpb->fs_ltref[i]->is_reference & 1) && ((p_Dpb->fs_ltref[i]->is_long_term & 1))) {
                if (p_Dpb->fs_ltref[i]->top_field->long_term_pic_num == long_term_pic_num) {
                    p_Dpb->fs_ltref[i]->top_field->used_for_reference = 0;
                    p_Dpb->fs_ltref[i]->top_field->is_long_term = 0;
                    p_Dpb->fs_ltref[i]->is_reference &= 2;
                    p_Dpb->fs_ltref[i]->is_long_term &= 2;
                    if (p_Dpb->fs_ltref[i]->is_used == 3) {
                        p_Dpb->fs_ltref[i]->frame->used_for_reference = 0;
                        p_Dpb->fs_ltref[i]->frame->is_long_term = 0;
                    }
                    return;
                }
            }
            if ((p_Dpb->fs_ltref[i]->is_reference & 2) && ((p_Dpb->fs_ltref[i]->is_long_term & 2))) {
                if (p_Dpb->fs_ltref[i]->bottom_field->long_term_pic_num == long_term_pic_num) {
                    p_Dpb->fs_ltref[i]->bottom_field->used_for_reference = 0;
                    p_Dpb->fs_ltref[i]->bottom_field->is_long_term = 0;
                    p_Dpb->fs_ltref[i]->is_reference &= 1;
                    p_Dpb->fs_ltref[i]->is_long_term &= 1;
                    if (p_Dpb->fs_ltref[i]->is_used == 3) {
                        p_Dpb->fs_ltref[i]->frame->used_for_reference = 0;
                        p_Dpb->fs_ltref[i]->frame->is_long_term = 0;
                    }
                    return;
                }
            }
        }
    }
}

static void unmark_long_term_frame_for_reference_by_frame_idx(H264_DpbBuf_t *p_Dpb, RK_S32 long_term_frame_idx)
{
    RK_U32 i = 0;
    for (i = 0; i < p_Dpb->ltref_frames_in_buffer; i++) {
        if (p_Dpb->fs_ltref[i]->long_term_frame_idx == long_term_frame_idx) {
            unmark_for_long_term_reference(p_Dpb->fs_ltref[i]);
        }
    }
}

static MPP_RET unmark_long_term_field_for_reference_by_frame_idx(H264_DpbBuf_t *p_Dpb, RK_S32 structure,
                                                                 RK_S32 long_term_frame_idx, RK_S32 mark_current, RK_U32 curr_frame_num, RK_S32 curr_pic_num)
{
    RK_U8 i = 0;
    MPP_RET ret = MPP_ERR_UNKNOW;
    H264dVideoCtx_t *p_Vid = p_Dpb->p_Vid;

    VAL_CHECK(ret, structure != FRAME);
    if (curr_pic_num < 0)
        curr_pic_num += (2 * p_Vid->max_frame_num);

    for (i = 0; i < p_Dpb->ltref_frames_in_buffer; i++) {
        if (p_Dpb->fs_ltref[i]->long_term_frame_idx == long_term_frame_idx) {
            if (structure == TOP_FIELD) {
                if (p_Dpb->fs_ltref[i]->is_long_term == 3) {
                    unmark_for_long_term_reference(p_Dpb->fs_ltref[i]);
                } else {
                    if (p_Dpb->fs_ltref[i]->is_long_term == 1) {
                        unmark_for_long_term_reference(p_Dpb->fs_ltref[i]);
                    } else {
                        if (mark_current) {
                            if (p_Dpb->last_picture) {
                                if ((p_Dpb->last_picture != p_Dpb->fs_ltref[i]) || p_Dpb->last_picture->frame_num != curr_frame_num)
                                    unmark_for_long_term_reference(p_Dpb->fs_ltref[i]);
                            } else {
                                unmark_for_long_term_reference(p_Dpb->fs_ltref[i]);
                            }
                        } else {
                            if ((p_Dpb->fs_ltref[i]->frame_num) != (unsigned)(curr_pic_num >> 1)) {
                                unmark_for_long_term_reference(p_Dpb->fs_ltref[i]);
                            }
                        }
                    }
                }
            }
            if (structure == BOTTOM_FIELD) {
                if (p_Dpb->fs_ltref[i]->is_long_term == 3) {
                    unmark_for_long_term_reference(p_Dpb->fs_ltref[i]);
                } else {
                    if (p_Dpb->fs_ltref[i]->is_long_term == 2) {
                        unmark_for_long_term_reference(p_Dpb->fs_ltref[i]);
                    } else {
                        if (mark_current) {
                            if (p_Dpb->last_picture) {
                                if ((p_Dpb->last_picture != p_Dpb->fs_ltref[i]) || p_Dpb->last_picture->frame_num != curr_frame_num)
                                    unmark_for_long_term_reference(p_Dpb->fs_ltref[i]);
                            } else {
                                unmark_for_long_term_reference(p_Dpb->fs_ltref[i]);
                            }
                        } else {
                            if ((p_Dpb->fs_ltref[i]->frame_num) != (unsigned)(curr_pic_num >> 1)) {
                                unmark_for_long_term_reference(p_Dpb->fs_ltref[i]);
                            }
                        }
                    }
                }
            }
        }
    }
    return ret = MPP_OK;
__FAILED:
    return ret;
}

static void mark_pic_long_term(H264_DpbBuf_t *p_Dpb, H264_StorePic_t* p, RK_S32 long_term_frame_idx, RK_S32 picNumX)
{
    RK_U32 i = 0;
    RK_S32 add_top = 0, add_bottom = 0;

    if (p->structure == FRAME) {
        for (i = 0; i < p_Dpb->ref_frames_in_buffer; i++) {
            if (p_Dpb->fs_ref[i]->is_reference == 3) {
                if ((!p_Dpb->fs_ref[i]->frame->is_long_term) && (p_Dpb->fs_ref[i]->frame->pic_num == picNumX)) {
                    p_Dpb->fs_ref[i]->long_term_frame_idx = p_Dpb->fs_ref[i]->frame->long_term_frame_idx = long_term_frame_idx;
                    p_Dpb->fs_ref[i]->frame->long_term_pic_num = long_term_frame_idx;
                    p_Dpb->fs_ref[i]->frame->is_long_term = 1;

                    if (p_Dpb->fs_ref[i]->top_field && p_Dpb->fs_ref[i]->bottom_field) {
                        p_Dpb->fs_ref[i]->top_field->long_term_frame_idx = p_Dpb->fs_ref[i]->bottom_field->long_term_frame_idx = long_term_frame_idx;
                        p_Dpb->fs_ref[i]->top_field->long_term_pic_num = long_term_frame_idx;
                        p_Dpb->fs_ref[i]->bottom_field->long_term_pic_num = long_term_frame_idx;
                        p_Dpb->fs_ref[i]->top_field->is_long_term = p_Dpb->fs_ref[i]->bottom_field->is_long_term = 1;
                    }
                    p_Dpb->fs_ref[i]->is_long_term = 3;
                    return;
                }
            }
        }
        H264D_WARNNING("reference frame for long term marking not found.");
    } else {
        if (p->structure == TOP_FIELD) {
            add_top = 1;
            add_bottom = 0;
        } else {
            add_top = 0;
            add_bottom = 1;
        }
        for (i = 0; i < p_Dpb->ref_frames_in_buffer; i++) {
            if (p_Dpb->fs_ref[i]->is_reference & 1) {
                if ((!p_Dpb->fs_ref[i]->top_field->is_long_term) && (p_Dpb->fs_ref[i]->top_field->pic_num == picNumX)) {
                    if ((p_Dpb->fs_ref[i]->is_long_term) && (p_Dpb->fs_ref[i]->long_term_frame_idx != long_term_frame_idx)) {
                        H264D_WARNNING("assigning long_term_frame_idx different from other field.");
                    }
                    p_Dpb->fs_ref[i]->long_term_frame_idx = p_Dpb->fs_ref[i]->top_field->long_term_frame_idx = long_term_frame_idx;
                    p_Dpb->fs_ref[i]->top_field->long_term_pic_num = 2 * long_term_frame_idx + add_top;
                    p_Dpb->fs_ref[i]->top_field->is_long_term = 1;
                    p_Dpb->fs_ref[i]->is_long_term |= 1;
                    if (p_Dpb->fs_ref[i]->is_long_term == 3) {
                        p_Dpb->fs_ref[i]->frame->is_long_term = 1;
                        p_Dpb->fs_ref[i]->frame->long_term_frame_idx = p_Dpb->fs_ref[i]->frame->long_term_pic_num = long_term_frame_idx;
                    }
                    return;
                }
            }
            if (p_Dpb->fs_ref[i]->is_reference & 2) {
                if ((!p_Dpb->fs_ref[i]->bottom_field->is_long_term) && (p_Dpb->fs_ref[i]->bottom_field->pic_num == picNumX)) {
                    if ((p_Dpb->fs_ref[i]->is_long_term) && (p_Dpb->fs_ref[i]->long_term_frame_idx != long_term_frame_idx)) {
                        H264D_WARNNING("assigning long_term_frame_idx different from other field.");
                    }

                    p_Dpb->fs_ref[i]->long_term_frame_idx = p_Dpb->fs_ref[i]->bottom_field->long_term_frame_idx
                                                            = long_term_frame_idx;
                    p_Dpb->fs_ref[i]->bottom_field->long_term_pic_num = 2 * long_term_frame_idx + add_bottom;
                    p_Dpb->fs_ref[i]->bottom_field->is_long_term = 1;
                    p_Dpb->fs_ref[i]->is_long_term |= 2;
                    if (p_Dpb->fs_ref[i]->is_long_term == 3) {
                        p_Dpb->fs_ref[i]->frame->is_long_term = 1;
                        p_Dpb->fs_ref[i]->frame->long_term_frame_idx = p_Dpb->fs_ref[i]->frame->long_term_pic_num = long_term_frame_idx;
                    }
                    return;
                }
            }
        }
        H264D_WARNNING("reference field for long term marking not found.");
    }
}

static MPP_RET mm_assign_long_term_frame_idx(H264_DpbBuf_t *p_Dpb, H264_StorePic_t* p, RK_S32 difference_of_pic_nums_minus1, RK_S32 long_term_frame_idx)
{
    RK_S32 picNumX = 0;
    RK_U32 i = 0;
    MPP_RET ret = MPP_ERR_UNKNOW;

    picNumX = get_pic_num_x(p, difference_of_pic_nums_minus1);
    //!< remove frames/fields with same long_term_frame_idx
    if (p->structure == FRAME) {
        unmark_long_term_frame_for_reference_by_frame_idx(p_Dpb, long_term_frame_idx);
    } else {
        PictureStructure structure = FRAME;

        for (i = 0; i < p_Dpb->ref_frames_in_buffer; i++) {
            if (p_Dpb->fs_ref[i]->is_reference & 1) {
                if (p_Dpb->fs_ref[i]->top_field->pic_num == picNumX) {
                    structure = TOP_FIELD;
                    break;
                }
            }
            if (p_Dpb->fs_ref[i]->is_reference & 2) {
                if (p_Dpb->fs_ref[i]->bottom_field->pic_num == picNumX) {
                    structure = BOTTOM_FIELD;
                    break;
                }
            }
        }
        VAL_CHECK(ret, structure != FRAME);
        FUN_CHECK(ret = unmark_long_term_field_for_reference_by_frame_idx(p_Dpb, structure, long_term_frame_idx, 0, 0, picNumX));
    }
    mark_pic_long_term(p_Dpb, p, long_term_frame_idx, picNumX);

    return ret = MPP_OK;
__FAILED:
    return ret;
}

static void mm_update_max_long_term_frame_idx(H264_DpbBuf_t *p_Dpb, RK_S32 max_long_term_frame_idx_plus1)
{
    RK_U32 i = 0;

    p_Dpb->max_long_term_pic_idx = max_long_term_frame_idx_plus1 - 1;

    // check for invalid frames
    for (i = 0; i < p_Dpb->ltref_frames_in_buffer; i++) {
        if (p_Dpb->fs_ltref[i]->long_term_frame_idx > p_Dpb->max_long_term_pic_idx) {
            unmark_for_long_term_reference(p_Dpb->fs_ltref[i]);
        }
    }
}

static void mm_unmark_all_short_term_for_reference(H264_DpbBuf_t *p_Dpb)
{
    RK_U32 i = 0;
    for (i = 0; i < p_Dpb->ref_frames_in_buffer; i++) {
        unmark_for_reference(p_Dpb->p_Vid->p_Dec, p_Dpb->fs_ref[i]);
    }
    update_ref_list(p_Dpb);
}

static void mm_unmark_all_long_term_for_reference(H264_DpbBuf_t *p_Dpb)
{
    mm_update_max_long_term_frame_idx(p_Dpb, 0);
}

static void mm_mark_current_picture_long_term(H264_DpbBuf_t *p_Dpb, H264_StorePic_t *p, RK_S32 long_term_frame_idx)
{
    // remove long term pictures with same long_term_frame_idx
    if (p->structure == FRAME) {
        unmark_long_term_frame_for_reference_by_frame_idx(p_Dpb, long_term_frame_idx);
    } else {
        unmark_long_term_field_for_reference_by_frame_idx(p_Dpb, p->structure, long_term_frame_idx, 1, p->pic_num, 0);
    }

    p->is_long_term = 1;
    p->long_term_frame_idx = long_term_frame_idx;
}

static void sliding_window_memory_management(H264_DpbBuf_t *p_Dpb)
{
    RK_U32 i = 0;

    // if this is a reference pic with sliding window, unmark first ref frame
    if (p_Dpb->ref_frames_in_buffer == MPP_MAX(1, p_Dpb->num_ref_frames) - p_Dpb->ltref_frames_in_buffer) {
        for (i = 0; i < p_Dpb->used_size; i++) {
            if (p_Dpb->fs[i]->is_reference && (!(p_Dpb->fs[i]->is_long_term))) {
                unmark_for_reference(p_Dpb->p_Vid->p_Dec, p_Dpb->fs[i]);
                update_ref_list(p_Dpb);
                break;
            }
        }
    }
}

static void check_num_ref(H264_DpbBuf_t *p_Dpb)
{

    if ((RK_S32)(p_Dpb->ltref_frames_in_buffer + p_Dpb->ref_frames_in_buffer) > MPP_MAX(1, p_Dpb->num_ref_frames)) {
        sliding_window_memory_management(p_Dpb);
        H264D_WARNNING("Max number of reference frames exceeded");
    }
}

static RK_U32 is_used_for_reference(H264_FrameStore_t* fs)
{
    RK_U8 is_used_flag = 0;

    if (!fs) {
        return 0;
    }
    if (fs->is_reference) {
        return is_used_flag = 1;
    }

    if (fs->is_used == 3) { // frame
        if (fs->frame->used_for_reference) {
            return is_used_flag = 1;
        }
    }

    if (fs->is_used & 1) { // top field
        if (fs->top_field) {
            if (fs->top_field->used_for_reference) {
                return is_used_flag = 1;
            }
        }
    }

    if (fs->is_used & 2) { // bottom field
        if (fs->bottom_field) {
            if (fs->bottom_field->used_for_reference) {
                return is_used_flag = 1;
            }
        }
    }
    return is_used_flag = 0;
}

static void free_dpb_mark(H264_DecCtx_t *p_Dec, H264_DpbMark_t *p_mark, RK_S32 structure)
{
    if (structure == FRAME) {
        p_mark->top_used = (p_mark->top_used > 0) ? (p_mark->top_used - 1) : 0;
        p_mark->bot_used = (p_mark->bot_used > 0) ? (p_mark->bot_used - 1) : 0;
    } else if (structure == TOP_FIELD) {
        p_mark->top_used = (p_mark->top_used > 0) ? (p_mark->top_used - 1) : 0;
    } else if (structure == BOTTOM_FIELD) {
        p_mark->bot_used = (p_mark->bot_used > 0) ? (p_mark->bot_used - 1) : 0;
    }
    if (p_mark->top_used == 0 && p_mark->bot_used == 0
        && p_mark->out_flag == 0 && (p_mark->slot_idx >= 0)) {
        mpp_buf_slot_clr_flag(p_Dec->frame_slots, p_mark->slot_idx, SLOT_CODEC_USE);
        reset_dpb_mark(p_mark);
    }
}

static MPP_RET remove_frame_from_dpb(H264_DpbBuf_t *p_Dpb, RK_S32 pos)
{
    RK_U32  i = 0;
    MPP_RET ret = MPP_ERR_UNKNOW;
    H264_FrameStore_t* tmp = NULL;
    H264_FrameStore_t* fs = NULL;
    H264_DecCtx_t *p_Dec = NULL;

    INP_CHECK(ret, !p_Dpb);
    fs = p_Dpb->fs[pos];
    INP_CHECK(ret, !fs);
    INP_CHECK(ret, !p_Dpb->p_Vid);
    p_Dec = p_Dpb->p_Vid->p_Dec;
    INP_CHECK(ret, !p_Dec);

    switch (fs->is_used) {
    case 3:
        if (fs->frame)           free_storable_picture(p_Dec, fs->frame);
        if (fs->top_field)       free_storable_picture(p_Dec, fs->top_field);
        if (fs->bottom_field)    free_storable_picture(p_Dec, fs->bottom_field);
        fs->frame = NULL;
        fs->top_field = NULL;
        fs->bottom_field = NULL;
        break;
    case 2:
        if (fs->bottom_field)    free_storable_picture(p_Dec, fs->bottom_field);
        fs->bottom_field = NULL;
        break;
    case 1:
        if (fs->top_field)        free_storable_picture(p_Dec, fs->top_field);
        fs->top_field = NULL;
        break;
    case 0:
        break;
    default:
        H264D_ERR("invalid frame store type.");
        goto __FAILED;
    }

    fs->is_used = 0;
    fs->is_long_term = 0;
    fs->is_reference = 0;
    fs->is_orig_reference = 0;

    // move empty framestore to end of buffer
    tmp = p_Dpb->fs[pos];

    for (i = pos; i < p_Dpb->used_size - 1; i++) {
        p_Dpb->fs[i] = p_Dpb->fs[i + 1];
    }
    p_Dpb->fs[p_Dpb->used_size - 1] = tmp;
    p_Dpb->used_size--;

    return ret = MPP_OK;
__RETURN:
    return ret;
__FAILED:
    return ret = MPP_NOK;


}

static MPP_RET remove_unused_frame_from_dpb(H264_DpbBuf_t *p_Dpb)
{
    RK_U32 i = 0;
    MPP_RET ret = MPP_ERR_UNKNOW;
    INP_CHECK(ret, !p_Dpb);
    // check for frames that were already output and no longer used for reference
    for (i = 0; i < p_Dpb->used_size; i++) {
        if (p_Dpb->fs[i]) {
            if (p_Dpb->fs[i]->is_output && (!is_used_for_reference(p_Dpb->fs[i]))) {
                FUN_CHECK(ret = remove_frame_from_dpb(p_Dpb, i));
                return MPP_OK;
            }
        }
    }
__RETURN:
    return ret;
__FAILED:
    return ret;
}

static RK_S32 get_smallest_poc(H264_DpbBuf_t *p_Dpb, RK_S32 *poc, RK_S32 *pos)
{
    RK_U32 i = 0;
    RK_S32 find_flag = 0;
    RK_S32 min_pos = -1;
    RK_S32 min_poc = INT_MAX;

    *pos = -1;
    *poc = INT_MAX;
    for (i = 0; i < p_Dpb->used_size; i++) {
        if (min_poc > p_Dpb->fs[i]->poc) {
            min_poc = p_Dpb->fs[i]->poc;
            min_pos = i;
        }
        if ((*poc > p_Dpb->fs[i]->poc) && (!p_Dpb->fs[i]->is_output)) {
            *poc = p_Dpb->fs[i]->poc;
            *pos = i;
            find_flag = 1;
        }
    }
    if (!find_flag) {
        *poc = min_poc;
        *pos = min_pos;
    }

    return find_flag;
}

static H264_FrameStore_t *alloc_frame_store()
{
    MPP_RET ret = MPP_ERR_UNKNOW;
    H264_FrameStore_t *f = mpp_calloc(H264_FrameStore_t, 1);
    MEM_CHECK(ret, f);

    f->is_used = 0;
    f->is_reference = 0;
    f->is_long_term = 0;
    f->is_orig_reference = 0;
    f->is_output = 0;

    f->frame = NULL;
    f->top_field = NULL;
    f->bottom_field = NULL;

    return f;
__FAILED:
    (void)ret;
    return NULL;
}

static MPP_RET dpb_combine_field_yuv(H264dVideoCtx_t *p_Vid, H264_FrameStore_t *fs, RK_U8 combine_flag)
{
    MPP_RET ret = MPP_ERR_UNKNOW;

    if (!fs->frame) {
        fs->frame = alloc_storable_picture(p_Vid, FRAME);
        MEM_CHECK(ret, fs->frame);
        ASSERT(fs->top_field->colmv_no_used_flag == fs->bottom_field->colmv_no_used_flag);
        fs->frame->colmv_no_used_flag = fs->top_field->colmv_no_used_flag;
        if (combine_flag) {
            ASSERT(fs->top_field->mem_mark->mark_idx == fs->bottom_field->mem_mark->mark_idx);
            ASSERT(fs->top_field->mem_mark->slot_idx == fs->bottom_field->mem_mark->slot_idx);
            fs->frame->mem_malloc_type = fs->top_field->mem_malloc_type;
            fs->frame->mem_mark = fs->top_field->mem_mark;
        } else if (fs->is_used == 0x01) { // unpaired, have top
            ASSERT(fs->bottom_field->mem_malloc_type == Mem_UnPaired);
            fs->frame->mem_mark = fs->top_field->mem_mark;
        } else if (fs->is_used == 0x02) { // unpaired, have bottom
            ASSERT(fs->top_field->mem_malloc_type == Mem_UnPaired);
            fs->frame->mem_mark = fs->bottom_field->mem_mark;
        } else {
            ASSERT(fs->is_used == 0x03);
            fs->frame->mem_malloc_type = fs->top_field->mem_malloc_type;
            fs->frame->mem_mark = fs->top_field->mem_mark;
        }
    }
    fs->poc = fs->frame->poc = fs->frame->frame_poc = MPP_MIN(fs->top_field->poc, fs->bottom_field->poc);
    fs->bottom_field->frame_poc = fs->top_field->frame_poc = fs->frame->poc;
    fs->bottom_field->top_poc = fs->frame->top_poc = fs->top_field->poc;
    fs->top_field->bottom_poc = fs->frame->bottom_poc = fs->bottom_field->poc;
    fs->frame->used_for_reference = (fs->top_field->used_for_reference && fs->bottom_field->used_for_reference);
    fs->frame->is_long_term = (fs->top_field->is_long_term && fs->bottom_field->is_long_term);
    if (fs->frame->is_long_term) {
        fs->frame->long_term_frame_idx = fs->long_term_frame_idx;
    }
    fs->frame->top_field = fs->top_field;
    fs->frame->bottom_field = fs->bottom_field;
    fs->frame->frame = fs->frame;
    fs->frame->chroma_format_idc = fs->top_field->chroma_format_idc;

    fs->frame->frame_cropping_flag = fs->top_field->frame_cropping_flag;
    if (fs->frame->frame_cropping_flag) {
        fs->frame->frame_crop_top_offset = fs->top_field->frame_crop_top_offset;
        fs->frame->frame_crop_bottom_offset = fs->top_field->frame_crop_bottom_offset;
        fs->frame->frame_crop_left_offset = fs->top_field->frame_crop_left_offset;
        fs->frame->frame_crop_right_offset = fs->top_field->frame_crop_right_offset;
    }
    fs->top_field->frame = fs->bottom_field->frame = fs->frame;
    fs->top_field->top_field = fs->top_field;
    fs->top_field->bottom_field = fs->bottom_field;
    fs->bottom_field->top_field = fs->top_field;
    fs->bottom_field->bottom_field = fs->bottom_field;

    fs->frame->is_mmco_5 = fs->top_field->is_mmco_5 || fs->bottom_field->is_mmco_5;
    fs->frame->poc_mmco5 = MPP_MIN(fs->top_field->top_poc_mmco5, fs->bottom_field->bot_poc_mmco5);
    fs->frame->top_poc_mmco5 = fs->top_field->top_poc_mmco5;
    fs->frame->bot_poc_mmco5 = fs->top_field->bot_poc_mmco5;

    return ret = MPP_OK;
__FAILED:
    return ret ;
}

static void write_picture(H264_StorePic_t *p, H264dVideoCtx_t *p_Vid)
{
    MppFrame mframe = NULL;
    H264_DpbMark_t *p_mark = NULL;
    H264dErrCtx_t *p_err = &p_Vid->p_Dec->errctx;

    p_mark = p->mem_mark;
    if ((p->mem_malloc_type == Mem_Malloc
         || p->mem_malloc_type == Mem_TopOnly
         || p->mem_malloc_type == Mem_BotOnly)
        && p->structure == FRAME && p_mark->out_flag) {
        mpp_buf_slot_get_prop(p_Vid->p_Dec->frame_slots, p_mark->slot_idx, SLOT_FRAME_PTR, &mframe);

        //!< discard unpaired
        if (p->mem_malloc_type == Mem_TopOnly || p->mem_malloc_type == Mem_BotOnly) {
            if (p_err->used_ref_flag) {
                mpp_frame_set_errinfo(mframe, MPP_FRAME_ERR_UNKNOW);
            } else {
                mpp_frame_set_discard(mframe, MPP_FRAME_ERR_UNKNOW);
            }
        }
        //!<  discard less than first i frame poc
        if ((p_err->i_slice_no < 2) && (p->poc < p_err->first_iframe_poc)) {
            if (p_err->used_ref_flag && p_err->first_iframe_is_output) {
                if ((p->slice_type % 5) == H264_B_SLICE)
                    mpp_frame_set_discard(mframe, MPP_FRAME_ERR_UNKNOW);
                else
                    mpp_frame_set_errinfo(mframe, MPP_FRAME_ERR_UNKNOW);
            } else {
                if (p_Vid->dpb_fast_out)
                    mpp_frame_set_discard(mframe, MPP_FRAME_ERR_UNKNOW);
            }
        }

        if (p_Vid->p_Dec->mvc_valid) {
            H264_DpbMark_t *match_mark = NULL;
            H264_DpbMark_t *out_mark_list[2] = {NULL, NULL};
            RK_U32 i = 0;
            RK_S32 match_flag = 0;
            RK_U32 is_base_view = p_mark->pic->layer_id == 0 ? 1 : 0;

            // find pic with the same poc at base view
            for (i = 0; i < MAX_MARK_SIZE; i++) {
                match_mark = &p_Vid->p_Dec->dpb_mark[i];

                if (!match_mark || !match_mark->pic || !match_mark->out_flag || match_mark->slot_idx < 0)
                    continue;

                if (match_mark->pic->layer_id != p_mark->pic->layer_id) {
                    if (match_mark->pic->poc < p_mark->pic->poc) {
                        match_flag = 1;
                        break;
                    } else if (match_mark->pic->poc == p_mark->pic->poc) {
                        match_flag = 2;
                        break;
                    } else {
                        match_flag = 0;
                    }
                }
            }

            if (!match_flag) {
                H264D_DBG(H264D_DBG_DPB_INFO, "no other view matched with current view_id %d, poc %d",
                          p_mark->pic->layer_id, p_mark->pic->poc);
                out_mark_list[0] = p_mark;
            } else if (match_flag == 1) {
                H264D_DBG(H264D_DBG_DPB_INFO, "find match view at %d, slot_idx %d, view_id %d, poc %d",
                          i, match_mark->slot_idx, match_mark->pic->layer_id, match_mark->pic->poc);
                out_mark_list[0] = match_mark;
                out_mark_list[1] = p_mark;
            } else if (match_flag == 2) {
                H264D_DBG(H264D_DBG_DPB_INFO, "find match view at %d, slot_idx %d, view_id %d, poc %d",
                          i, match_mark->slot_idx, match_mark->pic->layer_id, match_mark->pic->poc);
                if (is_base_view) {
                    out_mark_list[0] = p_mark;
                    out_mark_list[1] = match_mark;
                } else {
                    out_mark_list[0] = match_mark;
                    out_mark_list[1] = p_mark;
                }
            }

            for (i = 0; i < 2; i++) {
                if (out_mark_list[i]) {
                    mpp_buf_slot_set_flag(p_Vid->p_Dec->frame_slots, out_mark_list[i]->slot_idx, SLOT_QUEUE_USE);
                    mpp_buf_slot_enqueue(p_Vid->p_Dec->frame_slots, out_mark_list[i]->slot_idx, QUEUE_DISPLAY);
                    out_mark_list[i]->out_flag = 0;
                    p_Vid->p_Dec->last_frame_slot_idx = out_mark_list[i]->slot_idx;
                }
            }
        } else {
            H264D_DBG(H264D_DBG_DPB_INFO, "display frame view_id %d slot_idx %d poc %d",
                      p_mark->pic->layer_id, p_mark->slot_idx, p_mark->pic->poc);
            mpp_buf_slot_set_flag(p_Vid->p_Dec->frame_slots, p_mark->slot_idx, SLOT_QUEUE_USE);
            mpp_buf_slot_enqueue(p_Vid->p_Dec->frame_slots, p_mark->slot_idx, QUEUE_DISPLAY);
            p_Vid->p_Dec->last_frame_slot_idx = p_mark->slot_idx;
            p_mark->out_flag = 0;
        }
    }
}

static MPP_RET write_unpaired_field(H264dVideoCtx_t *p_Vid, H264_FrameStore_t *fs)
{
    MPP_RET ret = MPP_ERR_UNKNOW;
    H264_StorePic_t *p = NULL;

    VAL_CHECK(ret, fs->is_used < 3);
    if (fs->is_used & 0x01) { // we have a top field, construct an empty bottom field
        p = fs->top_field;
        fs->bottom_field = alloc_storable_picture(p_Vid, BOTTOM_FIELD);
        MEM_CHECK(ret, fs->bottom_field);
        fs->bottom_field->mem_malloc_type = Mem_UnPaired;
        fs->bottom_field->chroma_format_idc = p->chroma_format_idc;
        FUN_CHECK(ret = dpb_combine_field_yuv(p_Vid, fs, 0));
        fs->frame->view_id = fs->view_id;
        fs->frame->mem_malloc_type = Mem_TopOnly;
        H264D_WARNNING("write frame, line %d", __LINE__);
        write_picture(fs->frame, p_Vid);
    }

    if (fs->is_used & 0x02) { // we have a bottom field, construct an empty top field
        p = fs->bottom_field;
        fs->top_field = alloc_storable_picture(p_Vid, TOP_FIELD);
        MEM_CHECK(ret, fs->top_field);
        fs->top_field->mem_malloc_type = Mem_UnPaired;
        fs->top_field->chroma_format_idc = p->chroma_format_idc;
        fs->top_field->frame_cropping_flag = fs->bottom_field->frame_cropping_flag;
        if (fs->top_field->frame_cropping_flag) {
            fs->top_field->frame_crop_top_offset = fs->bottom_field->frame_crop_top_offset;
            fs->top_field->frame_crop_bottom_offset = fs->bottom_field->frame_crop_bottom_offset;
            fs->top_field->frame_crop_left_offset = fs->bottom_field->frame_crop_left_offset;
            fs->top_field->frame_crop_right_offset = fs->bottom_field->frame_crop_right_offset;
        }
        FUN_CHECK(ret = dpb_combine_field_yuv(p_Vid, fs, 0));
        fs->frame->view_id = fs->view_id;
        fs->frame->mem_malloc_type = Mem_BotOnly;
        H264D_WARNNING("write frame, line %d", __LINE__);
        write_picture(fs->frame, p_Vid);
    }
    fs->is_used = 3;

    return ret = MPP_OK;
__FAILED:
    return ret;
}

static MPP_RET flush_direct_output(H264dVideoCtx_t *p_Vid)
{
    MPP_RET ret = MPP_ERR_UNKNOW;

    FUN_CHECK(ret = write_unpaired_field(p_Vid, &p_Vid->out_buffer));
    free_storable_picture(p_Vid->p_Dec, p_Vid->out_buffer.frame);
    p_Vid->out_buffer.frame = NULL;
    free_storable_picture(p_Vid->p_Dec, p_Vid->out_buffer.top_field);
    p_Vid->out_buffer.top_field = NULL;
    free_storable_picture(p_Vid->p_Dec, p_Vid->out_buffer.bottom_field);
    p_Vid->out_buffer.bottom_field = NULL;
    p_Vid->out_buffer.is_used = 0;

    return ret = MPP_OK;
__FAILED:
    return ret;
}

static MPP_RET write_stored_frame(H264dVideoCtx_t *p_Vid, H264_DpbBuf_t *p_Dpb, H264_FrameStore_t *fs)
{
    MPP_RET ret = MPP_ERR_UNKNOW;
    INP_CHECK(ret, !p_Vid);
    INP_CHECK(ret, !fs);
    //!< make sure no direct output field is pending
    FUN_CHECK(ret = flush_direct_output(p_Vid));

    if (fs->is_used < 3) {
        FUN_CHECK(ret = write_unpaired_field(p_Vid, fs));
        if (fs->top_field)       free_storable_picture(p_Vid->p_Dec, fs->top_field);
        if (fs->bottom_field)    free_storable_picture(p_Vid->p_Dec, fs->bottom_field);
        fs->top_field = NULL;
        fs->bottom_field = NULL;
    } else {
        H264D_WARNNING("write frame, line %d", __LINE__);
        write_picture(fs->frame, p_Vid);
    }
    p_Dpb->last_output_poc = fs->poc;
    fs->is_output = 1;

    return ret = MPP_OK;
__RETURN:
    return ret;
__FAILED:
    return ret;
}

static MPP_RET output_one_frame_from_dpb(H264_DpbBuf_t *p_Dpb)
{
    RK_S32 poc = 0, pos = 0;
    MPP_RET ret = MPP_ERR_UNKNOW;
    H264dVideoCtx_t *p_Vid = p_Dpb->p_Vid;

    //!< find smallest POC
    if (get_smallest_poc(p_Dpb, &poc, &pos)) {
        //!< JVT-P072 ends
        H264D_WARNNING("write_stored_frame, line %d", __LINE__);
        FUN_CHECK(ret = write_stored_frame(p_Vid, p_Dpb, p_Dpb->fs[pos]));
        //!< free frame store and move empty store to end of buffer
        if (!is_used_for_reference(p_Dpb->fs[pos])) {
            FUN_CHECK(ret = remove_frame_from_dpb(p_Dpb, pos));
        }
    }
    return ret = MPP_OK;
__FAILED:
    return ret;
}

static MPP_RET adaptive_memory_management(H264_DpbBuf_t *p_Dpb, H264_StorePic_t *p)
{
    H264_DRPM_t *tmp_drpm = NULL;
    MPP_RET ret = MPP_ERR_UNKNOW;
    H264dVideoCtx_t *p_Vid = p_Dpb->p_Vid;

    p_Vid->last_has_mmco_5 = 0;
    VAL_CHECK(ret, !p->idr_flag && p->adaptive_ref_pic_buffering_flag);
    while (p->dec_ref_pic_marking_buffer) {
        tmp_drpm = p->dec_ref_pic_marking_buffer;
        switch (tmp_drpm->memory_management_control_operation) {
        case 0:
            VAL_CHECK(ret, tmp_drpm->Next == NULL);
            break;
        case 1:
            mm_unmark_short_term_for_reference(p_Dpb, p, tmp_drpm->difference_of_pic_nums_minus1);
            update_ref_list(p_Dpb);
            break;
        case 2:
            mm_unmark_long_term_for_reference(p_Dpb, p, tmp_drpm->long_term_pic_num);
            update_ltref_list(p_Dpb);
            break;
        case 3:
            mm_assign_long_term_frame_idx(p_Dpb, p, tmp_drpm->difference_of_pic_nums_minus1, tmp_drpm->long_term_frame_idx);
            update_ref_list(p_Dpb);
            update_ltref_list(p_Dpb);
            break;
        case 4:
            mm_update_max_long_term_frame_idx(p_Dpb, tmp_drpm->max_long_term_frame_idx_plus1);
            update_ltref_list(p_Dpb);
            break;
        case 5:
            mm_unmark_all_short_term_for_reference(p_Dpb);
            mm_unmark_all_long_term_for_reference(p_Dpb);
            p_Vid->last_has_mmco_5 = 1;
            break;
        case 6:
            //!< conceal max_long_term_frame_idx_plus1
            if (!tmp_drpm->max_long_term_frame_idx_plus1) {
                tmp_drpm->max_long_term_frame_idx_plus1 = p_Dpb->num_ref_frames;
            }
            mm_mark_current_picture_long_term(p_Dpb, p, tmp_drpm->long_term_frame_idx);
            check_num_ref(p_Dpb);
            break;
        default:
            ret = MPP_NOK;
            goto __FAILED;
        }
        p->dec_ref_pic_marking_buffer = tmp_drpm->Next;
    }
    if (p_Vid->last_has_mmco_5) { //!< similar IDR frame
        p->pic_num = p->frame_num = 0;
        switch (p->structure) {
        case TOP_FIELD:
            p->is_mmco_5 = 1;
            p->top_poc_mmco5 = p->top_poc;
            p->poc = p->top_poc = 0;
            break;

        case BOTTOM_FIELD:
            p->is_mmco_5 = 1;
            p->bot_poc_mmco5 = p->bottom_poc;
            p->poc = p->bottom_poc = 0;
            break;

        case FRAME:
            p->is_mmco_5 = 1;
            p->top_poc_mmco5 = p->top_poc;
            p->bot_poc_mmco5 = p->bottom_poc;
            p->poc_mmco5 = MPP_MIN(p->top_poc, p->bottom_poc);
            p->top_poc -= p->poc;
            p->bottom_poc -= p->poc;

            p->poc = MPP_MIN(p->top_poc, p->bottom_poc);
            p->frame_poc = p->poc;
            break;

        }
        if (p->layer_id == 0) {
            FUN_CHECK(ret = flush_dpb(p_Vid->p_Dpb_layer[0], 1));
        } else {
            FUN_CHECK(ret = flush_dpb(p_Vid->p_Dpb_layer[1], 2));
        }
    }
    return ret = MPP_OK;
__FAILED:
    return ret;
}

static MPP_RET dpb_split_field(H264dVideoCtx_t *p_Vid, H264_FrameStore_t *fs)
{
    MPP_RET ret = MPP_ERR_UNKNOW;
    H264_StorePic_t *frame = fs->frame;
    H264_StorePic_t *fs_top = NULL, *fs_btm = NULL;

    fs->poc = frame->poc;
    if (!frame->frame_mbs_only_flag) {
        fs_top = fs->top_field    = alloc_storable_picture(p_Vid, TOP_FIELD);
        fs_btm = fs->bottom_field = alloc_storable_picture(p_Vid, BOTTOM_FIELD);
        MEM_CHECK(ret, fs_top && fs_btm);
        fs_top->colmv_no_used_flag = frame->colmv_no_used_flag;
        fs_btm->colmv_no_used_flag = frame->colmv_no_used_flag;

        if (frame->mem_malloc_type == Mem_Malloc || frame->mem_malloc_type == Mem_Clone) {
            fs_top->mem_mark = frame->mem_mark;
            fs_btm->mem_mark = frame->mem_mark;
            fs_top->mem_malloc_type = frame->mem_malloc_type;
            fs_btm->mem_malloc_type = frame->mem_malloc_type;
            frame->mem_mark->bot_used += 1;    // picture memory add 1
            frame->mem_mark->top_used += 1;
        }
        fs_top->poc = frame->top_poc;
        fs_btm->poc = frame->bottom_poc;
        fs_top->layer_id = frame->layer_id;
        fs_btm->layer_id = frame->layer_id;

        fs_top->view_id = frame->view_id;
        fs_btm->view_id = frame->view_id;
        fs_top->frame_poc = frame->frame_poc;

        fs_top->bottom_poc = fs_btm->bottom_poc = frame->bottom_poc;
        fs_top->top_poc = fs_btm->top_poc = frame->top_poc;
        fs_btm->frame_poc = frame->frame_poc;

        fs_top->used_for_reference = fs_btm->used_for_reference = frame->used_for_reference;
        fs_top->is_long_term = fs_btm->is_long_term = frame->is_long_term;
        fs->long_term_frame_idx = fs_top->long_term_frame_idx = fs_btm->long_term_frame_idx = frame->long_term_frame_idx;
        fs_top->mb_aff_frame_flag = fs_btm->mb_aff_frame_flag = frame->mb_aff_frame_flag;

        frame->top_field = fs_top;
        frame->bottom_field = fs_btm;
        frame->frame = frame;
        fs_top->bottom_field = fs_btm;
        fs_top->frame = frame;
        fs_top->top_field = fs_top;
        fs_btm->top_field = fs_top;
        fs_btm->frame = frame;
        fs_btm->bottom_field = fs_btm;

        fs_top->is_mmco_5 = frame->is_mmco_5;
        fs_btm->is_mmco_5 = frame->is_mmco_5;
        fs_top->poc_mmco5 = frame->poc_mmco5;
        fs_btm->poc_mmco5 = frame->poc_mmco5;
        fs_top->top_poc_mmco5 = frame->top_poc_mmco5;
        fs_btm->bot_poc_mmco5 = frame->bot_poc_mmco5;

        fs_top->view_id = fs_btm->view_id = fs->view_id;
        fs_top->inter_view_flag = fs->inter_view_flag[0];
        fs_btm->inter_view_flag = fs->inter_view_flag[1];

        fs_top->chroma_format_idc = fs_btm->chroma_format_idc = frame->chroma_format_idc;
        fs_top->iCodingType = fs_btm->iCodingType = frame->iCodingType;
        fs_top->slice_type = fs_btm->slice_type = frame->slice_type;
    } else {
        fs->top_field = NULL;
        fs->bottom_field = NULL;
        frame->top_field = NULL;
        frame->bottom_field = NULL;
        frame->frame = frame;
    }
    return ret = MPP_OK;
__FAILED:
    mpp_mem_pool_put_f(p_Vid->pic_st, fs->top_field);
    mpp_mem_pool_put_f(p_Vid->pic_st, fs->bottom_field);
    fs->top_field = NULL;
    fs->bottom_field = NULL;
    return ret;
}

static MPP_RET dpb_combine_field(H264dVideoCtx_t *p_Vid, H264_FrameStore_t *fs, RK_U8 combine_flag)
{
    MPP_RET ret = MPP_ERR_UNKNOW;

    FUN_CHECK(ret = dpb_combine_field_yuv(p_Vid, fs, combine_flag));
    fs->frame->layer_id = fs->layer_id;
    fs->frame->view_id = fs->view_id;
    fs->frame->iCodingType = fs->top_field->iCodingType; //FIELD_CODING;
    fs->frame->frame_num = fs->top_field->frame_num;
    fs->frame->is_output = fs->is_output;
    fs->frame->slice_type = fs->slice_type;

    return ret = MPP_OK;
__FAILED:
    return ret;
}

static MPP_RET direct_output(H264dVideoCtx_t *p_Vid, H264_DpbBuf_t *p_Dpb, H264_StorePic_t *p)
{
    MPP_RET ret = MPP_ERR_UNKNOW;

    memcpy(&p_Vid->old_pic, p, sizeof(H264_StorePic_t));
    p_Vid->last_pic = &p_Vid->old_pic;
    if (p->structure == FRAME) {
        //!< we have a frame (or complementary field pair), so output it directly
        FUN_CHECK(ret = flush_direct_output(p_Vid));
        H264D_WARNNING("write frame, line %d", __LINE__);
        write_picture(p, p_Vid);
        p_Dpb->last_output_poc = p->poc;
        free_storable_picture(p_Vid->p_Dec, p);
        p_Dpb->last_picture = NULL;
        p_Vid->out_buffer.is_used = 0;
        p_Vid->out_buffer.is_directout = 0;
        goto __RETURN;
    }

    if (p->structure == TOP_FIELD) {
        if (p_Vid->out_buffer.is_used & 1) {
            FUN_CHECK(ret = flush_direct_output(p_Vid));
        }
        p_Vid->out_buffer.top_field = p;
        p_Vid->out_buffer.is_used |= 1;
        p_Vid->out_buffer.frame_num = p->pic_num;
        p_Vid->out_buffer.is_directout = 1;
        p_Dpb->last_picture = &p_Vid->out_buffer;
    }

    if (p->structure == BOTTOM_FIELD) {
        if (p_Vid->out_buffer.is_used & 2) {
            FUN_CHECK(ret = flush_direct_output(p_Vid));
        }
        p_Vid->out_buffer.bottom_field = p;
        p_Vid->out_buffer.is_used |= 2;
        p_Vid->out_buffer.frame_num = p->pic_num;
        p_Vid->out_buffer.is_directout = 1;
        p_Dpb->last_picture = &p_Vid->out_buffer;
    }

    if (p_Vid->out_buffer.is_used == 3) {
        //!< we have both fields, so output them
        FUN_CHECK(ret = dpb_combine_field_yuv(p_Vid, &p_Vid->out_buffer, 0));
        p_Vid->out_buffer.frame->view_id = p_Vid->out_buffer.view_id;
        H264D_WARNNING("write frame, line %d", __LINE__);
        write_picture(p_Vid->out_buffer.frame, p_Vid);
        free_storable_picture(p_Vid->p_Dec, p_Vid->out_buffer.frame);
        p_Vid->out_buffer.frame = NULL;
        free_storable_picture(p_Vid->p_Dec, p_Vid->out_buffer.top_field);
        p_Vid->out_buffer.top_field = NULL;
        free_storable_picture(p_Vid->p_Dec, p_Vid->out_buffer.bottom_field);
        p_Vid->out_buffer.bottom_field = NULL;
        p_Vid->out_buffer.is_used = 0;
        p_Vid->out_buffer.is_directout = 0;
        p_Dpb->last_output_poc = p->poc;
        p_Dpb->last_picture = NULL;
    }

__RETURN:
    return ret = MPP_OK;
__FAILED:
    return ret;
}

static MPP_RET output_dpb_normal(H264_DpbBuf_t *p_Dpb)
{
    MPP_RET ret = MPP_NOK;
    RK_S32 min_poc = 0;
    RK_S32 min_pos = 0;
    RK_S32 poc_inc = 0;

    while ((p_Dpb->last_output_poc > INT_MIN)
           && (get_smallest_poc(p_Dpb, &min_poc, &min_pos))) {
        poc_inc = min_poc - p_Dpb->last_output_poc;
        if ((p_Dpb->last_output_poc > INT_MIN) && (abs(poc_inc) & 0x1))
            p_Dpb->poc_interval = 1;
        if ((min_poc - p_Dpb->last_output_poc) <= p_Dpb->poc_interval) {
            FUN_CHECK(ret = write_stored_frame(p_Dpb->p_Vid, p_Dpb, p_Dpb->fs[min_pos]));
        } else {
            break;
        }
    }
    while (!remove_unused_frame_from_dpb(p_Dpb));

    return MPP_OK;
__FAILED:
    return ret;
}
static MPP_RET output_dpb_fastplay(H264_DpbBuf_t *p_Dpb, H264_FrameStore_t *fs, RK_U32 is_i_frm)
{
    MPP_RET ret = MPP_NOK;

    if ((p_Dpb->p_Vid->dpb_fast_out && is_i_frm)) {
        FUN_CHECK(ret = write_stored_frame(p_Dpb->p_Vid, p_Dpb, fs));
    } else
        output_dpb_normal(p_Dpb);

    return MPP_OK;
__FAILED:
    return ret;
}
static MPP_RET output_dpb_fastplay_once(H264_DpbBuf_t *p_Dpb, H264_FrameStore_t *fs, RK_U32 is_i_frm)
{
    MPP_RET ret = MPP_NOK;

    if (p_Dpb->p_Vid->dpb_fast_out && is_i_frm && !p_Dpb->p_Vid->dpb_first_fast_played) {
        FUN_CHECK(ret = write_stored_frame(p_Dpb->p_Vid, p_Dpb, fs));
        p_Dpb->p_Vid->dpb_first_fast_played = 1;
    } else {
        // disable fast play once in the second gop
        if (p_Dpb->p_Vid->dpb_fast_out && is_i_frm && p_Dpb->p_Vid->dpb_first_fast_played) {
            p_Dpb->p_Vid->dpb_fast_out = MPP_DISABLE_FAST_PLAY;
        }
        output_dpb_normal(p_Dpb);
    }

    return MPP_OK;
__FAILED:
    return ret;
}

static MPP_RET scan_dpb_output(H264_DpbBuf_t *p_Dpb, H264_StorePic_t *p)
{
    MPP_RET ret = MPP_NOK;
    H264_FrameStore_t *fs = p_Dpb->fs[p_Dpb->used_size - 1];

    if (fs->is_used == 3) {
        H264dErrCtx_t *p_err = &p_Dpb->p_Vid->p_Dec->errctx;
        RK_U32 is_i_frm = p_err->i_slice_no < 2 && p_Dpb->last_output_poc == INT_MIN;

        if (p_Dpb->p_Vid->p_Dec->cfg->base.fast_out)
            FUN_CHECK(ret = write_stored_frame(p_Dpb->p_Vid, p_Dpb, fs));
        else {
            switch (p_Dpb->p_Vid->p_Dec->cfg->base.enable_fast_play) {
            case MPP_DISABLE_FAST_PLAY:
                FUN_CHECK(ret = output_dpb_normal(p_Dpb));
                break;
            case MPP_ENABLE_FAST_PLAY:
                FUN_CHECK(ret = output_dpb_fastplay(p_Dpb, fs, is_i_frm));
                break;
            case MPP_ENABLE_FAST_PLAY_ONCE:
                FUN_CHECK(ret = output_dpb_fastplay_once(p_Dpb, fs, is_i_frm));
                break;
            default:
                H264D_ERR("unsupport output mode");
            }
        }

        if (is_i_frm)
            p_err->first_iframe_is_output = fs->is_output;
    }
    (void )p;
    return MPP_OK;
__FAILED:
    return ret;
}

static void flush_one_dpb_mark(H264_DecCtx_t *p_Dec, H264_DpbMark_t *p_mark)
{
    if (NULL == p_mark)
        return;
    if (p_mark->out_flag && (p_mark->slot_idx >= 0)) {
        MppFrame mframe = NULL;

        mpp_buf_slot_get_prop(p_Dec->frame_slots, p_mark->slot_idx, SLOT_FRAME_PTR, &mframe);
        if (mframe) {
            H264D_DBG(H264D_DBG_SLOT_FLUSH, "[DPB_BUF_FLUSH] slot_idx=%d, top_used=%d, bot_used=%d",
                      p_mark->slot_idx, p_mark->top_used, p_mark->bot_used);
            mpp_frame_set_discard(mframe, 1);
            mpp_buf_slot_set_flag(p_Dec->frame_slots, p_mark->slot_idx, SLOT_QUEUE_USE);
            mpp_buf_slot_enqueue(p_Dec->frame_slots, p_mark->slot_idx, QUEUE_DISPLAY);
            mpp_buf_slot_clr_flag(p_Dec->frame_slots, p_mark->slot_idx, SLOT_CODEC_USE);
            p_Dec->last_frame_slot_idx = p_mark->slot_idx;
        }
        reset_dpb_mark(p_mark);
        return;
    }
    H264D_DBG(H264D_DBG_WARNNING, "out_flag %d slot_idx %d\n", p_mark->out_flag, p_mark->slot_idx);
}

/*!
***********************************************************************
* \brief
*    store picture to dpb
***********************************************************************
*/
//extern "C"
MPP_RET store_picture_in_dpb(H264_DpbBuf_t *p_Dpb, H264_StorePic_t *p)
{
    MPP_RET ret = MPP_ERR_UNKNOW;
    H264dVideoCtx_t *p_Vid = p_Dpb->p_Vid;
    RK_U32 max_buf_size = 0;

    VAL_CHECK(ret, NULL != p);  //!< if frame, check for new store
    //!< set use flag
    if (p->mem_mark && (p->mem_mark->slot_idx >= 0)) {
        mpp_buf_slot_set_flag(p_Vid->p_Dec->frame_slots, p->mem_mark->slot_idx, SLOT_CODEC_USE);
    } else {
        H264D_ERR("error, p->mem_mark == NULL");
    }
    //!< deal with all frames in dpb
    p_Vid->last_has_mmco_5 = 0;
    p_Vid->last_pic_bottom_field = p->structure == BOTTOM_FIELD;
    if (p->idr_flag) {
        FUN_CHECK(ret = idr_memory_management(p_Dpb, p));
    } else {    //!< adaptive memory management
        if (p->used_for_reference && p->adaptive_ref_pic_buffering_flag) {
            FUN_CHECK(ret = adaptive_memory_management(p_Dpb, p));
        }
    }
    //!< if necessary, combine top and botteom to frame
    if (get_field_dpb_combine_flag(p_Dpb->last_picture, p)) {
        if (p_Dpb->last_picture->is_directout) {
            FUN_CHECK(ret = direct_output(p_Vid, p_Dpb, p));  //!< output frame
        } else {
            FUN_CHECK(ret = insert_picture_in_dpb(p_Vid, p_Dpb->last_picture, p, 1));  //!< field_dpb_combine
            scan_dpb_output(p_Dpb, p);
        }
        memcpy(&p_Vid->old_pic, p, sizeof(H264_StorePic_t));
        p_Vid->last_pic = &p_Vid->old_pic;
        p_Dpb->last_picture = NULL;
        goto __RETURN;
    }
    //!< sliding window
    if (!p->idr_flag && p->used_for_reference && !p->adaptive_ref_pic_buffering_flag) {
        ASSERT(!p->idr_flag);
        sliding_window_memory_management(p_Dpb);
        p->is_long_term = 0;
    }
    while (!remove_unused_frame_from_dpb(p_Dpb));
    H264D_DBG(H264D_DBG_DPB_INFO, "before out, dpb[%d] used_size %d, size %d",
              p_Dpb->layer_id, p_Dpb->used_size, p_Dpb->size);
    //!< when full output one frame or more then setting max_buf_size
    max_buf_size = p_Vid->p_Inp->max_buf_size;
    if (max_buf_size)
        H264D_DBG(H264D_DBG_DPB_INFO, "max_buf_size=%d\n", max_buf_size);
    while (p_Dpb->used_size >= p_Dpb->size ||
           (max_buf_size && p_Dpb->used_size >= max_buf_size)) {
        RK_S32 min_poc = 0, min_pos = 0;
        RK_S32 find_flag = 0;

        remove_unused_frame_from_dpb(p_Dpb);
        find_flag = get_smallest_poc(p_Dpb, &min_poc, &min_pos);
        if (!p->used_for_reference) {
            if ((!find_flag) || (p->poc < min_poc)) {
                FUN_CHECK(ret = direct_output(p_Vid, p_Dpb, p));  //!< output frame
                goto __RETURN;
            }
        }
        //!< used for reference, but not find, then flush a frame in the first
        if ((!find_flag) || (p->poc < min_poc)) {
            min_pos = 0;
            unmark_for_reference(p_Vid->p_Dec, p_Dpb->fs[min_pos]);
            if (!p_Dpb->fs[min_pos]->is_output) {
                H264D_WARNNING("write_stored_frame, line %d", __LINE__);
                FUN_CHECK(ret = write_stored_frame(p_Vid, p_Dpb, p_Dpb->fs[min_pos]));
            }
            FUN_CHECK(ret = remove_frame_from_dpb(p_Dpb, min_pos));
            p->is_long_term = 0;
        } else {
            FUN_CHECK(ret = output_one_frame_from_dpb(p_Dpb));
        }
    }
    H264D_DBG(H264D_DBG_DPB_INFO, "after out, dpb[%d] used_size %d, size %d",
              p_Dpb->layer_id, p_Dpb->used_size, p_Dpb->size);
    //!< store current decoder picture at end of dpb
    FUN_CHECK(ret = insert_picture_in_dpb(p_Vid, p_Dpb->fs[p_Dpb->used_size], p, 0));
    if (p->structure != FRAME) {
        p_Dpb->last_picture = p_Dpb->fs[p_Dpb->used_size];
    } else {
        p_Dpb->last_picture = NULL;
    }
    memcpy(&p_Vid->old_pic, p, sizeof(H264_StorePic_t));
    p_Vid->last_pic = &p_Vid->old_pic;

    p_Dpb->used_size++;
    H264D_DBG(H264D_DBG_DPB_INFO, "[DPB_size] p_Dpb->used_size=%d", p_Dpb->used_size);
    if (!p_Vid->p_Dec->mvc_valid)
        scan_dpb_output(p_Dpb, p);
    update_ref_list(p_Dpb);
    update_ltref_list(p_Dpb);

__RETURN:
    return ret = MPP_OK;
__FAILED:
    flush_one_dpb_mark(p_Vid->p_Dec, p->mem_mark);
    return ret;
}

/*!
***********************************************************************
* \brief
*    free frame store picture
***********************************************************************
*/
//extern "C"
void free_frame_store(H264_DecCtx_t *p_Dec, H264_FrameStore_t* f)
{
    if (f) {
        if (f->frame) {
            free_storable_picture(p_Dec, f->frame);
            f->frame = NULL;
        }
        if (f->top_field) {
            free_storable_picture(p_Dec, f->top_field);
            f->top_field = NULL;
        }
        if (f->bottom_field) {
            free_storable_picture(p_Dec, f->bottom_field);
            f->bottom_field = NULL;
        }
        MPP_FREE(f);
    }
}

/*!
***********************************************************************
* \brief
*    free dpb
***********************************************************************
*/
//extern "C"
void free_dpb(H264_DpbBuf_t *p_Dpb)
{
    RK_U32 i = 0;
    H264dVideoCtx_t *p_Vid = p_Dpb->p_Vid;

    if (p_Dpb->fs) {
        for (i = 0; i < p_Dpb->allocated_size; i++) {
            free_frame_store(p_Vid->p_Dec, p_Dpb->fs[i]);
            p_Dpb->fs[i] = NULL;
        }
        MPP_FREE(p_Dpb->fs);
    }
    MPP_FREE(p_Dpb->fs_ref);
    MPP_FREE(p_Dpb->fs_ltref);
    if (p_Dpb->fs_ilref) {
        for (i = 0; i < 1; i++) {
            free_frame_store(p_Vid->p_Dec, p_Dpb->fs_ilref[i]);
            p_Dpb->fs_ilref[i] = NULL;
        }
        MPP_FREE(p_Dpb->fs_ilref);
    }
    p_Dpb->last_output_view_id = -1;
    p_Dpb->last_output_poc = INT_MIN;
    p_Dpb->init_done = 0;
    if (p_Vid->no_ref_pic) {
        free_storable_picture(p_Vid->p_Dec, p_Vid->no_ref_pic);
        p_Vid->no_ref_pic = NULL;
    }
}

/*!
***********************************************************************
* \brief
*    alloc one picture
***********************************************************************
*/
//extern "C"
void update_ref_list(H264_DpbBuf_t *p_Dpb)
{
    RK_U8 i = 0, j = 0;
    for (i = 0, j = 0; i < p_Dpb->used_size; i++) {
        if (is_short_term_reference(p_Dpb->fs[i])) {
            p_Dpb->fs_ref[j++] = p_Dpb->fs[i];
        }
    }

    p_Dpb->ref_frames_in_buffer = j;

    while (j < p_Dpb->size) {
        p_Dpb->fs_ref[j++] = NULL;
    }
}
/*!
***********************************************************************
* \brief
*    alloc one picture
***********************************************************************
*/
//extern "C"
void update_ltref_list(H264_DpbBuf_t *p_Dpb)
{
    RK_U8 i = 0, j = 0;
    for (i = 0, j = 0; i < p_Dpb->used_size; i++) {
        if (is_long_term_reference(p_Dpb->fs[i])) {
            p_Dpb->fs_ltref[j++] = p_Dpb->fs[i];
        }
    }

    p_Dpb->ltref_frames_in_buffer = j;

    while (j < p_Dpb->size) {
        p_Dpb->fs_ltref[j++] = NULL;
    }
}

/*!
***********************************************************************
* \brief
*    alloc one picture
***********************************************************************
*/
//extern "C"
MPP_RET idr_memory_management(H264_DpbBuf_t *p_Dpb, H264_StorePic_t *p)
{
    RK_U32 i = 0;
    RK_S32 type = -1;
    MPP_RET ret = MPP_ERR_UNKNOW;
    H264dErrCtx_t *p_err = &p_Dpb->p_Vid->p_Dec->errctx;

    if (p->no_output_of_prior_pics_flag) {
        //!< free all stored pictures
        for (i = 0; i < p_Dpb->used_size; i++) {
            //!< reset all reference settings
            //Set is_output flag to true to avoid real output this frame to
            //display queue. These frames will be mark as unused and remove from
            //dpb at flush_dpb
            p_Dpb->fs[i]->is_output = 1;
        }
    }

    type = (p->layer_id == 0) ? 1 : 2;
    FUN_CHECK(ret = flush_dpb(p_Dpb, type));

    p_Dpb->last_picture = NULL;

    update_ref_list(p_Dpb);
    update_ltref_list(p_Dpb);
    p_Dpb->last_output_poc = INT_MIN;
    p_err->i_slice_no = 1;

    if (p->long_term_reference_flag) {
        p_Dpb->max_long_term_pic_idx = 0;
        p->is_long_term = 1;
        p->long_term_frame_idx = 0;
    } else {
        p_Dpb->max_long_term_pic_idx = -1;
        p->is_long_term = 0;
    }
    p_Dpb->last_output_view_id = -1;

    return ret = MPP_OK;

__FAILED:
    for (i = 0; i < p_Dpb->used_size; i++) { // when error and free malloc buf
        free_frame_store(p_Dpb->p_Vid->p_Dec, p_Dpb->fs[i]);
    }
    return ret;
}


/*!
***********************************************************************
* \brief
*    alloc one picture
***********************************************************************
*/
//extern "C"
MPP_RET insert_picture_in_dpb(H264dVideoCtx_t *p_Vid, H264_FrameStore_t *fs, H264_StorePic_t *p, RK_U8 combine_flag)
{
    MPP_RET ret = MPP_ERR_UNKNOW;

    ASSERT(p  != NULL);
    ASSERT(fs != NULL);

    switch (p->structure) {
    case FRAME:
        fs->frame = p;
        fs->is_used = 3;
        if (p->used_for_reference) {
            fs->is_reference = 3;
            fs->is_orig_reference = 3;
            if (p->is_long_term) {
                fs->is_long_term = 3;
                fs->long_term_frame_idx = p->long_term_frame_idx;
            }
        }
        fs->inter_view_flag[0] = fs->inter_view_flag[1] = p->inter_view_flag;
        fs->anchor_pic_flag[0] = fs->anchor_pic_flag[1] = p->anchor_pic_flag;
        FUN_CHECK(ret = dpb_split_field(p_Vid, fs));
        fs->poc = p->poc;
        break;
    case TOP_FIELD:
        fs->top_field = p;
        fs->is_used |= 1;
        fs->inter_view_flag[0] = p->inter_view_flag;
        fs->anchor_pic_flag[0] = p->anchor_pic_flag;
        if (p->used_for_reference) {
            fs->is_reference |= 1;
            fs->is_orig_reference |= 1;
            if (p->is_long_term) {
                fs->is_long_term |= 1;
                fs->long_term_frame_idx = p->long_term_frame_idx;
            }
        }
        if (fs->is_used == 3) {
            FUN_CHECK(ret = dpb_combine_field(p_Vid, fs, combine_flag));
        } else {
            fs->poc = p->poc;
        }
        break;
    case BOTTOM_FIELD:
        fs->bottom_field = p;
        fs->is_used |= 2;
        fs->inter_view_flag[1] = p->inter_view_flag;
        fs->anchor_pic_flag[1] = p->anchor_pic_flag;
        if (p->used_for_reference) {
            fs->is_reference |= 2;
            fs->is_orig_reference |= 2;
            if (p->is_long_term) {
                fs->is_long_term |= 2;
                fs->long_term_frame_idx = p->long_term_frame_idx;
            }
        }
        if (fs->is_used == 3) {
            FUN_CHECK(ret = dpb_combine_field(p_Vid, fs, combine_flag));
        } else {
            fs->poc = p->poc;
        }
        break;
    }
    fs->layer_id = p->layer_id;
    fs->view_id = p->view_id;
    fs->frame_num = p->pic_num;
    fs->is_output = p->is_output;
    fs->slice_type = p->slice_type;
    fs->structure = p->structure;

    return ret = MPP_OK;
__FAILED:
    return ret;
}


/*!
***********************************************************************
* \brief
*    alloc one picture
***********************************************************************
*/
//extern "C"
void free_storable_picture(H264_DecCtx_t *p_Dec, H264_StorePic_t *p)
{
    if (p) {
        if (p->mem_malloc_type == Mem_Malloc
            || p->mem_malloc_type == Mem_Clone) {
            free_dpb_mark(p_Dec, p->mem_mark, p->structure);
        }
        if (p->mem_malloc_type == Mem_TopOnly) {
            free_dpb_mark(p_Dec, p->mem_mark, TOP_FIELD);
        }
        if (p->mem_malloc_type == Mem_BotOnly) {
            free_dpb_mark(p_Dec, p->mem_mark, BOTTOM_FIELD);
        }
        mpp_mem_pool_put_f(p_Dec->p_Vid->pic_st, p);
    }
}

/*!
***********************************************************************
* \brief
*    alloc one picture
***********************************************************************
*/
//extern "C"
H264_StorePic_t *alloc_storable_picture(H264dVideoCtx_t *p_Vid, RK_S32 structure)
{
    MPP_RET ret = MPP_ERR_UNKNOW;
    H264_StorePic_t *s = mpp_mem_pool_get_f(p_Vid->pic_st);

    MEM_CHECK(ret, s);
    s->view_id = -1;
    s->structure = structure;
    (void)p_Vid;

    return s;
__FAILED:
    (void)ret;
    return NULL;
}

/*!
***********************************************************************
* \brief
*    alloc one picture
***********************************************************************
*/
//extern "C"
RK_U32 get_field_dpb_combine_flag(H264_FrameStore_t *p_last, H264_StorePic_t *p)
{
    RK_U32 combine_flag = 0;

    if ((p->structure == TOP_FIELD) || (p->structure == BOTTOM_FIELD)) {
        // check for frame store with same pic_number
        if (p_last) {
            if ((RK_S32)p_last->frame_num == p->pic_num) {
                if (((p->structure == TOP_FIELD) && (p_last->is_used == 2))
                    || ((p->structure == BOTTOM_FIELD) && (p_last->is_used == 1))) {
#if 1
                    if ((p->used_for_reference && p_last->is_orig_reference) ||
                        (!p->used_for_reference && !p_last->is_orig_reference)) {
                        combine_flag = 1;
                    }
#else
                    combine_flag = 1;
#endif
                }
            }
            /* set err to unpaired filed */
            if (!combine_flag) {
                struct h264_store_pic_t *pic = NULL;

                pic = p_last->structure == TOP_FIELD ? p_last->top_field : p_last->bottom_field;
                if (pic && pic->mem_mark->mframe && !pic->combine_flag)
                    mpp_frame_set_errinfo(pic->mem_mark->mframe, 1);
            }
        }
    }
    return combine_flag;
}

static MPP_RET enlarge_dpb(H264_DpbBuf_t *p_Dpb, RK_U32 size)
{
    RK_U32 i = 0;
    MPP_RET ret = MPP_ERR_UNKNOW;
    void *tmp;

    if (!p_Dpb || !size || !p_Dpb->init_done)
        return MPP_ERR_VALUE;

    if (p_Dpb->size >= size) {
        H264D_WARNNING("DPB could not be shrinked!\n");
        return MPP_ERR_VALUE;
    }

    tmp = mpp_calloc(H264_FrameStore_t*, size);
    memcpy(tmp, p_Dpb->fs, sizeof(H264_FrameStore_t*) * p_Dpb->size);
    mpp_free(p_Dpb->fs);
    p_Dpb->fs = tmp;

    tmp = mpp_calloc(H264_FrameStore_t*, size);
    memcpy(tmp, p_Dpb->fs_ref, sizeof(H264_FrameStore_t*) * p_Dpb->size);
    mpp_free(p_Dpb->fs_ref);
    p_Dpb->fs_ref = tmp;

    tmp = mpp_calloc(H264_FrameStore_t*, size);
    memcpy(tmp, p_Dpb->fs_ltref, sizeof(H264_FrameStore_t*) * p_Dpb->size);
    mpp_free(p_Dpb->fs_ltref);
    p_Dpb->fs_ltref = tmp;

    tmp = mpp_calloc(H264_FrameStore_t*, size);
    memcpy(tmp, p_Dpb->fs_ilref, sizeof(H264_FrameStore_t*) * p_Dpb->size);
    mpp_free(p_Dpb->fs_ilref);
    p_Dpb->fs_ilref = tmp;

    for (i = p_Dpb->size; i < size; i++) {
        p_Dpb->fs[i] = alloc_frame_store();
        MEM_CHECK(ret, p_Dpb->fs[i]);
        p_Dpb->fs_ref[i] = NULL;
        p_Dpb->fs_ltref[i] = NULL;
        p_Dpb->fs[i]->layer_id = -1;
        p_Dpb->fs[i]->view_id = -1;
        p_Dpb->fs[i]->inter_view_flag[0] = p_Dpb->fs[i]->inter_view_flag[1] = 0;
        p_Dpb->fs[i]->anchor_pic_flag[0] = p_Dpb->fs[i]->anchor_pic_flag[1] = 0;
    }

    p_Dpb->size = size;
    p_Dpb->allocated_size = size;
    return ret = MPP_OK;

__FAILED:
    return ret;
}

MPP_RET check_mvc_dpb(H264dVideoCtx_t *p_Vid, H264_DpbBuf_t *p_Dpb_layer_0,  H264_DpbBuf_t* p_Dpb_layer_1)
{
    MPP_RET ret = MPP_OK;

    if (!p_Vid || !p_Dpb_layer_0 || !p_Dpb_layer_1 || !p_Dpb_layer_0->init_done) {
        return ret = MPP_ERR_VALUE;
    }

    H264D_DBG(H264D_DBG_DPB_INFO, "p_Dpb[0].size %d vs p_Dpb[1].size %d\n",
              p_Dpb_layer_0->size, p_Dpb_layer_1->size);

    p_Dpb_layer_0->size = MPP_MIN(p_Dpb_layer_0->size, MAX_DPB_SIZE / 2);
    p_Dpb_layer_1->size = MPP_MIN(p_Dpb_layer_1->size, MAX_DPB_SIZE / 2);

    if (p_Dpb_layer_0->size == p_Dpb_layer_1->size) {
        ret = MPP_OK;
    } else if (p_Dpb_layer_0->size > p_Dpb_layer_1->size) {
        ret = enlarge_dpb(p_Dpb_layer_1, p_Dpb_layer_0->size);
        H264D_DBG(H264D_DBG_DPB_INFO, "Enlarge DPB[1] to %d", p_Dpb_layer_0->size);
    } else {
        ret = enlarge_dpb(p_Dpb_layer_0, p_Dpb_layer_1->size);
        H264D_DBG(H264D_DBG_DPB_INFO, "Enlarge DPB[0] to %d", p_Dpb_layer_1->size);
    }

    p_Vid->dpb_size[0] = p_Dpb_layer_0->size;
    p_Vid->dpb_size[1] = p_Dpb_layer_1->size;

    return ret;
}

/*!
***********************************************************************
* \brief
*    init dpb
***********************************************************************
*/
//extern "C"
MPP_RET init_dpb(H264dVideoCtx_t *p_Vid, H264_DpbBuf_t *p_Dpb, RK_S32 type)  // type=1 AVC type=2 MVC
{
    RK_U32 i = 0;
    MPP_RET ret = MPP_ERR_UNKNOW;
    H264_SPS_t *active_sps = p_Vid->active_sps;
    RK_U32 dpb_size;

    if (!active_sps) {
        ret = MPP_NOK;
        goto __FAILED;
    }
    p_Dpb->p_Vid = p_Vid;
    if (p_Dpb->init_done) {
        free_dpb(p_Dpb);
    }
    dpb_size = getDpbSize(p_Vid, active_sps);
    p_Dpb->size = MPP_MAX(1, dpb_size);
    p_Dpb->allocated_size = p_Dpb->size;
    p_Dpb->num_ref_frames = active_sps->max_num_ref_frames;
    if (active_sps->max_dec_frame_buffering < active_sps->max_num_ref_frames) {
        H264D_WARNNING("DPB size at specified level is smaller than reference frames");
    }
    p_Dpb->used_size = 0;
    p_Dpb->last_picture = NULL;
    p_Dpb->ref_frames_in_buffer = 0;
    p_Dpb->ltref_frames_in_buffer = 0;
    //--------
    p_Dpb->fs       = mpp_calloc(H264_FrameStore_t*, p_Dpb->size);
    p_Dpb->fs_ref   = mpp_calloc(H264_FrameStore_t*, p_Dpb->size);
    p_Dpb->fs_ltref = mpp_calloc(H264_FrameStore_t*, p_Dpb->size);
    p_Dpb->fs_ilref = mpp_calloc(H264_FrameStore_t*, 1);  //!< inter-layer reference (for multi-layered codecs)
    MEM_CHECK(ret, p_Dpb->fs && p_Dpb->fs_ref && p_Dpb->fs_ltref && p_Dpb->fs_ilref);
    for (i = 0; i < p_Dpb->size; i++) {
        p_Dpb->fs[i] = alloc_frame_store();
        MEM_CHECK(ret, p_Dpb->fs[i]);
        p_Dpb->fs_ref[i] = NULL;
        p_Dpb->fs_ltref[i] = NULL;
        p_Dpb->fs[i]->layer_id = -1;
        p_Dpb->fs[i]->view_id = -1;
        p_Dpb->fs[i]->inter_view_flag[0] = p_Dpb->fs[i]->inter_view_flag[1] = 0;
        p_Dpb->fs[i]->anchor_pic_flag[0] = p_Dpb->fs[i]->anchor_pic_flag[1] = 0;
    }
    if (type == 2) {
        p_Dpb->fs_ilref[0] = alloc_frame_store();
        MEM_CHECK(ret, p_Dpb->fs_ilref[0]);
        //!< These may need some cleanups
        p_Dpb->fs_ilref[0]->view_id = -1;
        p_Dpb->fs_ilref[0]->inter_view_flag[0] = p_Dpb->fs_ilref[0]->inter_view_flag[1] = 0;
        p_Dpb->fs_ilref[0]->anchor_pic_flag[0] = p_Dpb->fs_ilref[0]->anchor_pic_flag[1] = 0;
        //!< given that this is in a different buffer, do we even need proc_flag anymore?
    } else {
        p_Dpb->fs_ilref[0] = NULL;
    }
    //!< allocate a dummy storable picture
    if (!p_Vid->no_ref_pic) {
        p_Vid->no_ref_pic = alloc_storable_picture(p_Vid, FRAME);
        MEM_CHECK(ret, p_Vid->no_ref_pic);
        p_Vid->no_ref_pic->top_field = p_Vid->no_ref_pic;
        p_Vid->no_ref_pic->bottom_field = p_Vid->no_ref_pic;
        p_Vid->no_ref_pic->frame = p_Vid->no_ref_pic;
    }
    p_Dpb->last_output_poc = INT_MIN;
    p_Dpb->last_output_view_id = -1;
    p_Vid->last_has_mmco_5 = 0;
    p_Dpb->init_done = 1;

    return ret = MPP_OK;
__FAILED:
    return ret;
}
/*!
***********************************************************************
* \brief
*    flush dpb
***********************************************************************
*/
//extern "C"
MPP_RET flush_dpb(H264_DpbBuf_t *p_Dpb, RK_S32 type)
{
    RK_U32 i = 0;
    MPP_RET ret = MPP_ERR_UNKNOW;

    INP_CHECK(ret, !p_Dpb);
    //!< diagnostics
    if (!p_Dpb->init_done) {
        goto __RETURN;
    }
    H264D_DBG(H264D_DBG_DPB_INFO, "dpb layer %d, used_size %d",
              p_Dpb->layer_id, p_Dpb->used_size);
    //!< mark all frames unused
    for (i = 0; i < p_Dpb->used_size; i++) {
        if (p_Dpb->fs[i] && p_Dpb->p_Vid) {
            VAL_CHECK(ret, p_Dpb->fs[i]->layer_id == p_Dpb->layer_id);
            unmark_for_reference(p_Dpb->p_Vid->p_Dec, p_Dpb->fs[i]);
        }
    }
    while (!remove_unused_frame_from_dpb(p_Dpb));
    //!< output frames in POC order
    while (p_Dpb->used_size) {
        FUN_CHECK(ret = output_one_frame_from_dpb(p_Dpb));
    }
    p_Dpb->last_output_poc = INT_MIN;
    (void)type;
__RETURN:
    return ret = MPP_OK;
__FAILED:
    return ret;
}
/*!
***********************************************************************
* \brief
*    write out all frames
***********************************************************************
*/
//extern "C"
MPP_RET output_dpb(H264_DecCtx_t *p_Dec, H264_DpbBuf_t *p_Dpb)
{
    MPP_RET ret = MPP_ERR_UNKNOW;

    while (!remove_unused_frame_from_dpb(p_Dpb));

    (void)p_Dec;
    return ret = MPP_OK;
}
/*!
***********************************************************************
* \brief
*    parse sps and process sps
***********************************************************************
*/
//extern "C"
MPP_RET exit_picture(H264dVideoCtx_t *p_Vid, H264_StorePic_t **dec_pic)
{
    MPP_RET ret = MPP_ERR_UNKNOW;

    //!< return if the last picture has already been finished
    if (!(*dec_pic) || !p_Vid->exit_picture_flag
        || !p_Vid->have_outpicture_flag || !p_Vid->iNumOfSlicesDecoded) {
        goto __RETURN;
    }
    FUN_CHECK(ret = store_picture_in_dpb(p_Vid->p_Dpb_layer[(*dec_pic)->layer_id], *dec_pic));
    *dec_pic = NULL;

__RETURN:
    return ret = MPP_OK;
__FAILED:
    return ret;
}
