/******************************************************************************
 *
 * Copyright (C) 2021 The Android Open Source Project
 *
 * 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.
 *
 *****************************************************************************
 * Originally developed and contributed by Ittiam Systems Pvt. Ltd, Bangalore
 */

/*****************************************************************************/
/*                                                                           */
/*  File Name         : imvcd_nalu_parser.h                                  */
/*                                                                           */
/*  Description       : Functions for MVC NALU parsing                       */
/*                                                                           */
/*****************************************************************************/
#include <string.h>

#include "ih264_typedefs.h"
#include "iv.h"
#include "imvcd.h"
#include "ih264d_error_handler.h"
#include "ih264d_bitstrm.h"
#include "ih264d_defs.h"
#include "ih264d_nal.h"
#include "ih264d_parse_cavlc.h"
#include "ih264d_structs.h"
#include "ih264d_vui.h"
#include "imvcd_defs.h"
#include "imvcd_slice_functions.h"
#include "imvcd_structs.h"
#include "imvcd_utils.h"

static WORD32 imvcd_nalu_mvc_ext_parser(mvc_dec_ctxt_t *ps_mvcd_ctxt, dec_bit_stream_t *ps_bitstrm)
{
    nalu_mvc_ext_t *ps_nalu_mvc_ext =
        &ps_mvcd_ctxt->as_nalu_mvc_ext[ps_mvcd_ctxt->u2_num_views_decoded];

    ps_nalu_mvc_ext->u1_non_idr_flag = ih264d_get_bit_h264(ps_bitstrm);
    ps_nalu_mvc_ext->u1_priority_id = ih264d_get_bits_h264(ps_bitstrm, 6);
    ps_nalu_mvc_ext->u2_view_id = ih264d_get_bits_h264(ps_bitstrm, 10);

    if((ps_nalu_mvc_ext->u2_view_id >= MAX_NUM_VIEWS) ||
       (ps_nalu_mvc_ext->u2_view_id >= ps_mvcd_ctxt->u2_num_views))
    {
        return IVD_INVALID_BITSTREAM;
    }

    ps_nalu_mvc_ext->u1_temporal_id = ih264d_get_bits_h264(ps_bitstrm, 3);
    ps_nalu_mvc_ext->u1_anchor_pic_flag = ih264d_get_bit_h264(ps_bitstrm);
    ps_nalu_mvc_ext->u1_inter_view_flag = ih264d_get_bit_h264(ps_bitstrm);

    if(0 == ih264d_get_bit_h264(ps_bitstrm))
    {
        return IVD_INVALID_BITSTREAM;
    }

    return OK;
}

static WORD32 imvcd_parse_subset_sps(mvc_dec_ctxt_t *ps_mvcd_ctxt, dec_bit_stream_t *ps_bitstrm)
{
    subset_sps_t *ps_subset_sps = NULL;
    dec_seq_params_t *ps_sps = NULL;

    WORD32 i, j, k;
    UWORD8 u1_profile_idc;
    UWORD8 au1_constraint_set_flags[6];
    UWORD8 u1_level_idc;
    UWORD8 u1_seq_parameter_set_id;
    UWORD32 *pu4_bitstrm_buf = ps_bitstrm->pu4_buffer;
    UWORD32 *pu4_bitstrm_ofst = &ps_bitstrm->u4_ofst;
    UWORD32 u4_temp;
    UWORD16 u2_num_views_m1;
    UWORD8 u1_num_refs;
    UWORD8 u1_num_level_values_signalled_m1;
    UWORD8 u2_num_ops_m1;

    dec_struct_t *ps_view_ctxt = &ps_mvcd_ctxt->s_view_dec_ctxt;

    WORD32 i4_error_code = OK;

    if(0 == ps_mvcd_ctxt->u1_num_sps)
    {
        return ERROR_INV_SPS_PPS_T;
    }

    for(i = 0; i < MAX_NUM_SEQ_PARAMS; i++)
    {
        if(ps_view_ctxt->ps_sps[i].u1_is_valid)
        {
            ps_sps = &ps_view_ctxt->ps_sps[i];

            break;
        }
    }

    if(NULL == ps_sps)
    {
        return ERROR_INV_SPS_PPS_T;
    }

    u1_profile_idc = ih264d_get_bits_h264(ps_bitstrm, 8);

    for(i = 0; i < 6; i++)
    {
        au1_constraint_set_flags[i] = ih264d_get_bit_h264(ps_bitstrm);
    }

    if((u1_profile_idc != MULTIVIEW_HIGH_PROFILE_IDC) || (au1_constraint_set_flags[1] == 1))
    {
        return ERROR_FEATURE_UNAVAIL;
    }

    /*****************************************************/
    /* Read reserved_zero_2bits (2 bits)                 */
    /*****************************************************/
    ih264d_get_bits_h264(ps_bitstrm, 2);

    u1_level_idc = ih264d_get_bits_h264(ps_bitstrm, 8);

    u4_temp = ih264d_uev(pu4_bitstrm_ofst, pu4_bitstrm_buf);
    u1_seq_parameter_set_id = u4_temp;

    if(u4_temp & MASK_ERR_SEQ_SET_ID)
    {
        return ERROR_INV_SPS_PPS_T;
    }

    if(ps_sps->u1_level_idc != u1_level_idc)
    {
        ps_view_ctxt->u1_res_changed = 1;

        return IVD_RES_CHANGED;
    }

    ps_subset_sps = &ps_mvcd_ctxt->as_subset_sps[u1_seq_parameter_set_id];

    /* Accounting for the idiocy in 'ih264d_parse_pps' */
    ps_subset_sps->s_sps_data.u1_profile_idc = HIGH_PROFILE_IDC;
    ps_subset_sps->s_sps_data.u1_level_idc = u1_level_idc;
    ps_subset_sps->s_sps_data.u1_seq_parameter_set_id = u1_seq_parameter_set_id;

    ps_subset_sps->s_sps_data.i4_chroma_format_idc = ih264d_uev(pu4_bitstrm_ofst, pu4_bitstrm_buf);

    if(ps_subset_sps->s_sps_data.i4_chroma_format_idc != 1)
    {
        return ERROR_FEATURE_UNAVAIL;
    }

    ps_subset_sps->s_sps_data.i4_bit_depth_luma_minus8 =
        ih264d_uev(pu4_bitstrm_ofst, pu4_bitstrm_buf);

    if(ps_subset_sps->s_sps_data.i4_bit_depth_luma_minus8 != 0)
    {
        return ERROR_FEATURE_UNAVAIL;
    }

    ps_subset_sps->s_sps_data.i4_bit_depth_chroma_minus8 =
        ih264d_uev(pu4_bitstrm_ofst, pu4_bitstrm_buf);

    if(ps_subset_sps->s_sps_data.i4_bit_depth_chroma_minus8 != 0)
    {
        return ERROR_FEATURE_UNAVAIL;
    }

    ps_subset_sps->s_sps_data.i4_qpprime_y_zero_transform_bypass_flag =
        ih264d_get_bit_h264(ps_bitstrm);

    if(ps_subset_sps->s_sps_data.i4_qpprime_y_zero_transform_bypass_flag != 0)
    {
        return ERROR_INV_SPS_PPS_T;
    }

    ps_subset_sps->s_sps_data.i4_seq_scaling_matrix_present_flag = ih264d_get_bit_h264(ps_bitstrm);

    if(ps_subset_sps->s_sps_data.i4_seq_scaling_matrix_present_flag)
    {
        for(i = 0; i < 8; i++)
        {
            ps_subset_sps->s_sps_data.u1_seq_scaling_list_present_flag[i] =
                ih264d_get_bit_h264(ps_bitstrm);
        }
    }

    u4_temp = ih264d_uev(pu4_bitstrm_ofst, pu4_bitstrm_buf);

    if(u4_temp > (MAX_BITS_IN_FRAME_NUM - 4))
    {
        return ERROR_INV_SPS_PPS_T;
    }

    ps_subset_sps->s_sps_data.u1_bits_in_frm_num = 4 + u4_temp;

    ps_subset_sps->s_sps_data.u2_u4_max_pic_num_minus1 =
        (1 << (ps_subset_sps->s_sps_data.u1_bits_in_frm_num)) - 1;

    ps_subset_sps->s_sps_data.u1_pic_order_cnt_type = ih264d_uev(pu4_bitstrm_ofst, pu4_bitstrm_buf);

    if(ps_subset_sps->s_sps_data.u1_pic_order_cnt_type > MAX_PIC_ORDER_CNT_TYPE)
    {
        return ERROR_INV_POC_TYPE_T;
    }

    ps_subset_sps->s_sps_data.u1_num_ref_frames_in_pic_order_cnt_cycle = 1;

    if(ps_subset_sps->s_sps_data.u1_pic_order_cnt_type == 0)
    {
        UWORD32 u1_log2_max_pic_order_cnt_lsb_minus4 =
            ih264d_uev(pu4_bitstrm_ofst, pu4_bitstrm_buf);

        if(u1_log2_max_pic_order_cnt_lsb_minus4 > (MAX_BITS_IN_POC_LSB - 4))
        {
            return ERROR_INV_SPS_PPS_T;
        }

        ps_subset_sps->s_sps_data.u1_log2_max_pic_order_cnt_lsb_minus =
            4 + u1_log2_max_pic_order_cnt_lsb_minus4;

        ps_subset_sps->s_sps_data.i4_max_pic_order_cntLsb =
            (1 << ps_subset_sps->s_sps_data.u1_log2_max_pic_order_cnt_lsb_minus);
    }
    else if(ps_subset_sps->s_sps_data.u1_pic_order_cnt_type == 1)
    {
        ps_subset_sps->s_sps_data.u1_delta_pic_order_always_zero_flag =
            ih264d_get_bit_h264(ps_bitstrm);

        ps_subset_sps->s_sps_data.i4_ofst_for_non_ref_pic =
            ih264d_sev(pu4_bitstrm_ofst, pu4_bitstrm_buf);

        ps_subset_sps->s_sps_data.i4_ofst_for_top_to_bottom_field =
            ih264d_sev(pu4_bitstrm_ofst, pu4_bitstrm_buf);

        ps_subset_sps->s_sps_data.u1_num_ref_frames_in_pic_order_cnt_cycle =
            ih264d_uev(pu4_bitstrm_ofst, pu4_bitstrm_buf);

        if(ps_subset_sps->s_sps_data.u1_num_ref_frames_in_pic_order_cnt_cycle > MVC_MAX_REF_PICS)
        {
            return ERROR_INV_SPS_PPS_T;
        }

        for(i = 0; i < ps_subset_sps->s_sps_data.u1_num_ref_frames_in_pic_order_cnt_cycle; i++)
        {
            ps_subset_sps->s_sps_data.i4_ofst_for_ref_frame[i] =
                ih264d_sev(pu4_bitstrm_ofst, pu4_bitstrm_buf);
        }
    }

    ps_subset_sps->s_sps_data.u1_num_ref_frames = ih264d_uev(pu4_bitstrm_ofst, pu4_bitstrm_buf);

    if((ps_subset_sps->s_sps_data.u1_num_ref_frames > MVC_MAX_REF_PICS))
    {
        return ERROR_NUM_REF;
    }

    ps_subset_sps->s_sps_data.u1_gaps_in_frame_num_value_allowed_flag =
        ih264d_get_bit_h264(ps_bitstrm);

    ps_subset_sps->s_sps_data.u2_frm_wd_in_mbs = 1 + ih264d_uev(pu4_bitstrm_ofst, pu4_bitstrm_buf);

    if(ps_subset_sps->s_sps_data.u2_frm_wd_in_mbs > (H264_MAX_FRAME_WIDTH >> 4))
    {
        return IVD_STREAM_WIDTH_HEIGHT_NOT_SUPPORTED;
    }

    ps_subset_sps->s_sps_data.u2_frm_ht_in_mbs = 1 + ih264d_uev(pu4_bitstrm_ofst, pu4_bitstrm_buf);

    if(ps_subset_sps->s_sps_data.u2_frm_ht_in_mbs > (H264_MAX_FRAME_HEIGHT >> 4))
    {
        return IVD_STREAM_WIDTH_HEIGHT_NOT_SUPPORTED;
    }

    ps_subset_sps->s_sps_data.u2_max_mb_addr =
        ps_subset_sps->s_sps_data.u2_frm_wd_in_mbs * ps_subset_sps->s_sps_data.u2_frm_ht_in_mbs - 1;

    ps_subset_sps->s_sps_data.u2_total_num_of_mbs = ps_subset_sps->s_sps_data.u2_max_mb_addr + 1;

    ps_subset_sps->s_sps_data.u1_frame_mbs_only_flag = ih264d_get_bit_h264(ps_bitstrm);

    if(!ps_subset_sps->s_sps_data.u1_frame_mbs_only_flag)
    {
        return ERROR_FEATURE_UNAVAIL;
    }

    ps_subset_sps->s_sps_data.u1_mb_aff_flag = 0;

    ps_subset_sps->s_sps_data.u1_direct_8x8_inference_flag = ih264d_get_bit_h264(ps_bitstrm);

    /* Frame cropping flag */
    u4_temp = ih264d_get_bit_h264(ps_bitstrm);

    if(u4_temp)
    {
        ps_subset_sps->s_disp_offsets.u2_left_offset =
            ih264d_uev(pu4_bitstrm_ofst, pu4_bitstrm_buf);
        ps_subset_sps->s_disp_offsets.u2_right_offset =
            ih264d_uev(pu4_bitstrm_ofst, pu4_bitstrm_buf);
        ps_subset_sps->s_disp_offsets.u2_top_offset = ih264d_uev(pu4_bitstrm_ofst, pu4_bitstrm_buf);
        ps_subset_sps->s_disp_offsets.u2_bottom_offset =
            ih264d_uev(pu4_bitstrm_ofst, pu4_bitstrm_buf);
    }
    else
    {
        memset(&ps_subset_sps->s_disp_offsets, 0, sizeof(ps_subset_sps->s_disp_offsets));
    }

    if(ps_sps->u2_frm_wd_in_mbs != ps_subset_sps->s_sps_data.u2_frm_wd_in_mbs)
    {
        ps_view_ctxt->u1_res_changed = 1;

        return IVD_RES_CHANGED;
    }

    if(ps_sps->u2_frm_ht_in_mbs != ps_subset_sps->s_sps_data.u2_frm_ht_in_mbs)
    {
        ps_view_ctxt->u1_res_changed = 1;

        return IVD_RES_CHANGED;
    }

    if(ps_view_ctxt->u2_disp_width != (ps_subset_sps->s_sps_data.u2_frm_wd_in_mbs * MB_SIZE -
                                       ps_subset_sps->s_disp_offsets.u2_left_offset -
                                       ps_subset_sps->s_disp_offsets.u2_right_offset))
    {
        ps_view_ctxt->u1_res_changed = 1;

        return IVD_RES_CHANGED;
    }

    if(ps_view_ctxt->u2_disp_height != (ps_subset_sps->s_sps_data.u2_frm_ht_in_mbs * MB_SIZE -
                                        ps_subset_sps->s_disp_offsets.u2_top_offset -
                                        ps_subset_sps->s_disp_offsets.u2_bottom_offset))
    {
        ps_view_ctxt->u1_res_changed = 1;

        return IVD_RES_CHANGED;
    }

    ps_subset_sps->s_sps_data.u1_vui_parameters_present_flag = ih264d_get_bit_h264(ps_bitstrm);

    if(ps_subset_sps->s_sps_data.u1_vui_parameters_present_flag)
    {
        i4_error_code = ih264d_parse_vui_parametres(&ps_subset_sps->s_sps_data.s_vui, ps_bitstrm);

        if(i4_error_code != OK)
        {
            return i4_error_code;
        }

        if(ps_subset_sps->s_sps_data.s_vui.u1_bitstream_restriction_flag &&
           (ps_sps->s_vui.u4_num_reorder_frames !=
            ps_subset_sps->s_sps_data.s_vui.u4_num_reorder_frames))
        {
            ps_view_ctxt->u1_res_changed = 1;

            return IVD_RES_CHANGED;
        }
    }

    if(ih264d_get_bit_h264(ps_bitstrm) != 1)
    {
        return ERROR_INV_SPS_PPS_T;
    }

    u2_num_views_m1 = ih264d_uev(pu4_bitstrm_ofst, pu4_bitstrm_buf);

    if(u2_num_views_m1 >= MAX_NUM_VIEWS)
    {
        return ERROR_INVALID_SEQ_PARAM;
    }

    ps_subset_sps->s_sps_mvc_ext.u2_num_views = 1 + u2_num_views_m1;

    if(ps_view_ctxt->i4_decode_header)
    {
        ps_mvcd_ctxt->u2_num_views = MAX(ps_mvcd_ctxt->u2_num_views, 1 + u2_num_views_m1);
    }
    else if(ps_mvcd_ctxt->u2_num_views != (1 + u2_num_views_m1))
    {
        return ERROR_INVALID_SEQ_PARAM;
    }

    for(i = 0; i <= u2_num_views_m1; i++)
    {
        ps_subset_sps->s_sps_mvc_ext.au2_view_ids[i] =
            ih264d_uev(pu4_bitstrm_ofst, pu4_bitstrm_buf);
    }

    for(i = 0; i < 2; i++)
    {
        ps_subset_sps->s_sps_mvc_ext.as_anchor_ref_data[i][0].u1_num_refs = 0;
        ps_subset_sps->s_sps_mvc_ext.as_non_anchor_ref_data[i][0].u1_num_refs = 0;
    }

    for(i = 1; i <= u2_num_views_m1; i++)
    {
        u1_num_refs = ih264d_uev(pu4_bitstrm_ofst, pu4_bitstrm_buf);
        ps_subset_sps->s_sps_mvc_ext.as_anchor_ref_data[0][i].u1_num_refs = u1_num_refs;

        if(u1_num_refs > MAX_NUM_IVP_REFS)
        {
            return ERROR_INVALID_SEQ_PARAM;
        }

        for(j = 0; j < u1_num_refs; j++)
        {
            ps_subset_sps->s_sps_mvc_ext.as_anchor_ref_data[0][i].au2_ref_view_ids[j] =
                ih264d_uev(pu4_bitstrm_ofst, pu4_bitstrm_buf);
        }

        u1_num_refs = ih264d_uev(pu4_bitstrm_ofst, pu4_bitstrm_buf);
        ps_subset_sps->s_sps_mvc_ext.as_anchor_ref_data[1][i].u1_num_refs = u1_num_refs;

        if(u1_num_refs > MAX_NUM_IVP_REFS)
        {
            return ERROR_INVALID_SEQ_PARAM;
        }

        for(j = 0; j < u1_num_refs; j++)
        {
            ps_subset_sps->s_sps_mvc_ext.as_anchor_ref_data[1][i].au2_ref_view_ids[j] =
                ih264d_uev(pu4_bitstrm_ofst, pu4_bitstrm_buf);
        }
    }

    for(i = 1; i <= u2_num_views_m1; i++)
    {
        u1_num_refs = ih264d_uev(pu4_bitstrm_ofst, pu4_bitstrm_buf);
        ps_subset_sps->s_sps_mvc_ext.as_non_anchor_ref_data[0][i].u1_num_refs = u1_num_refs;

        if(u1_num_refs > MAX_NUM_IVP_REFS)
        {
            return ERROR_INVALID_SEQ_PARAM;
        }

        for(j = 0; j < u1_num_refs; j++)
        {
            ps_subset_sps->s_sps_mvc_ext.as_non_anchor_ref_data[0][i].au2_ref_view_ids[j] =
                ih264d_uev(pu4_bitstrm_ofst, pu4_bitstrm_buf);
        }

        u1_num_refs = ih264d_uev(pu4_bitstrm_ofst, pu4_bitstrm_buf);
        ps_subset_sps->s_sps_mvc_ext.as_non_anchor_ref_data[1][i].u1_num_refs = u1_num_refs;

        if(u1_num_refs > MAX_NUM_IVP_REFS)
        {
            return ERROR_INVALID_SEQ_PARAM;
        }

        for(j = 0; j < u1_num_refs; j++)
        {
            ps_subset_sps->s_sps_mvc_ext.as_non_anchor_ref_data[1][i].au2_ref_view_ids[j] =
                ih264d_uev(pu4_bitstrm_ofst, pu4_bitstrm_buf);
        }
    }

    u1_num_level_values_signalled_m1 = ih264d_uev(pu4_bitstrm_ofst, pu4_bitstrm_buf);
    ps_subset_sps->s_sps_mvc_ext.u1_num_level_values_signalled =
        u1_num_level_values_signalled_m1 + 1;

    if(u1_num_level_values_signalled_m1 >= MAX_NUM_LEVEL_VALUES_SIGNALLED)
    {
        return ERROR_INVALID_SEQ_PARAM;
    }

    for(i = 0; i <= u1_num_level_values_signalled_m1; i++)
    {
        ps_subset_sps->s_sps_mvc_ext.as_mvc_level_info[i].u4_level_idc =
            ih264d_get_bits_h264(ps_bitstrm, 8);

        u2_num_ops_m1 = ih264d_uev(pu4_bitstrm_ofst, pu4_bitstrm_buf);
        ps_subset_sps->s_sps_mvc_ext.as_mvc_level_info->as_mvc_op_data[i].u2_num_ops =
            1 + u2_num_ops_m1;

        if(u2_num_ops_m1 >= MAX_NUM_OPERATING_POINTS)
        {
            return ERROR_INVALID_SEQ_PARAM;
        }

        for(j = 0; j <= u2_num_ops_m1; j++)
        {
            UWORD16 u2_num_target_views_m1;

            ps_subset_sps->s_sps_mvc_ext.as_mvc_level_info->as_mvc_op_data[j].u1_temporal_id =
                ih264d_get_bits_h264(ps_bitstrm, 3);

            u2_num_target_views_m1 = ih264d_uev(pu4_bitstrm_ofst, pu4_bitstrm_buf);

            ps_subset_sps->s_sps_mvc_ext.as_mvc_level_info->as_mvc_op_data[j].u2_num_target_views =
                1 + u2_num_target_views_m1;

            if(u2_num_target_views_m1 >= MAX_NUM_VIEWS)
            {
                return ERROR_INVALID_SEQ_PARAM;
            }

            for(k = 0; k <= u2_num_target_views_m1; k++)
            {
                ps_subset_sps->s_sps_mvc_ext.as_mvc_level_info->as_mvc_op_data[j]
                    .au2_target_view_ids[k] = ih264d_uev(pu4_bitstrm_ofst, pu4_bitstrm_buf);
            }

            ps_subset_sps->s_sps_mvc_ext.as_mvc_level_info->as_mvc_op_data[j].u2_num_views =
                (UWORD16) 1 + ih264d_uev(pu4_bitstrm_ofst, pu4_bitstrm_buf);
        }
    }

    ps_subset_sps->u1_mvc_vui_parameters_present_flag = ih264d_get_bit_h264(ps_bitstrm);

    if(ps_subset_sps->u1_mvc_vui_parameters_present_flag)
    {
        return ERROR_INV_SPS_PPS_T;
    }

    /* In case bitstream read has exceeded the filled size, then
     return an error */
    if(EXCEED_OFFSET(ps_bitstrm))
    {
        return ERROR_INV_SPS_PPS_T;
    }

    ps_subset_sps->s_sps_data.u1_is_valid = 1;

    /* This ensures PPS has valid data in SPS array for reference */
    ps_view_ctxt->ps_sps[ps_subset_sps->s_sps_data.u1_seq_parameter_set_id] =
        ps_subset_sps->s_sps_data;

    ps_mvcd_ctxt->u1_num_subset_sps++;

    return OK;
}

/* This function removes emulation byte "0x03" from bitstream(EBSP to RBSP).
   It also converts bytestream format into 32 bit little - endian format. */
static WORD32 imvcd_transform_nalu(dec_bit_stream_t *ps_bitstrm, UWORD8 *pu1_nal_unit,
                                   UWORD32 u4_numbytes_in_nal_unit)
{
    UWORD32 ui4_word;
    UWORD8 u1_cur_byte;

    static const UWORD32 u4_num_bytes_in_word = sizeof(ui4_word) / sizeof(u1_cur_byte);
    UWORD32 u4_num_bytes_in_rbsp = 0;
    WORD32 i = 0, j;
    WORD8 c_count = 0;
    UWORD32 *puc_bitstream_buffer = (UWORD32 *) pu1_nal_unit;
    UWORD8 u1_nal_header_size = 1;
    UWORD8 u1_num_bytes_copied = 0;

    ps_bitstrm->pu4_buffer = puc_bitstream_buffer;

    ui4_word = *pu1_nal_unit++;
    u1_num_bytes_copied++;

    if((NAL_UNIT_TYPE(ui4_word) == PREFIX_NAL) ||
       (NAL_UNIT_TYPE(ui4_word) == CODED_SLICE_EXTENSION))
    {
        u1_nal_header_size += 3;
    }

    for(j = 0; j < 2; j++)
    {
        u1_cur_byte = *pu1_nal_unit++;

        ui4_word = ((ui4_word << 8) | u1_cur_byte);
        u1_num_bytes_copied++;

        c_count++;
        u4_num_bytes_in_rbsp++;

        if(u1_cur_byte != 0x00)
        {
            c_count = 0;
        }
    }

    if(u4_numbytes_in_nal_unit > 2)
    {
        i = ((u4_numbytes_in_nal_unit - 3));
    }

    for(; i > 8; i -= 4)
    {
        // loop 0
        u1_cur_byte = *pu1_nal_unit++;

        if(c_count == NUM_OF_ZERO_BYTES_BEFORE_START_CODE &&
           u1_cur_byte == EMULATION_PREVENTION_BYTE)
        {
            c_count = 0;
            u1_cur_byte = *pu1_nal_unit++;
            i--;
        }

        ui4_word = ((ui4_word << 8) | u1_cur_byte);
        u1_num_bytes_copied++;
        if(u4_num_bytes_in_word == u1_num_bytes_copied)
        {
            *puc_bitstream_buffer = ui4_word;
            puc_bitstream_buffer++;
            u1_num_bytes_copied = 0;
        }

        c_count++;
        if(u1_cur_byte != 0x00) c_count = 0;

        // loop 1
        u1_cur_byte = *pu1_nal_unit++;

        if(c_count == NUM_OF_ZERO_BYTES_BEFORE_START_CODE &&
           u1_cur_byte == EMULATION_PREVENTION_BYTE)
        {
            c_count = 0;
            u1_cur_byte = *pu1_nal_unit++;
            i--;
        }
        ui4_word = ((ui4_word << 8) | u1_cur_byte);
        u1_num_bytes_copied++;
        if(u4_num_bytes_in_word == u1_num_bytes_copied)
        {
            *puc_bitstream_buffer = ui4_word;
            puc_bitstream_buffer++;
            u1_num_bytes_copied = 0;
        }

        c_count++;
        if(u1_cur_byte != 0x00) c_count = 0;

        // loop 2
        u1_cur_byte = *pu1_nal_unit++;

        if(c_count == NUM_OF_ZERO_BYTES_BEFORE_START_CODE &&
           u1_cur_byte == EMULATION_PREVENTION_BYTE)
        {
            c_count = 0;
            u1_cur_byte = *pu1_nal_unit++;
            i--;
        }

        ui4_word = ((ui4_word << 8) | u1_cur_byte);
        u1_num_bytes_copied++;
        if(u4_num_bytes_in_word == u1_num_bytes_copied)
        {
            *puc_bitstream_buffer = ui4_word;
            puc_bitstream_buffer++;
            u1_num_bytes_copied = 0;
        }

        c_count++;
        if(u1_cur_byte != 0x00) c_count = 0;

        // loop 3
        u1_cur_byte = *pu1_nal_unit++;

        if(c_count == NUM_OF_ZERO_BYTES_BEFORE_START_CODE &&
           u1_cur_byte == EMULATION_PREVENTION_BYTE)
        {
            c_count = 0;
            u1_cur_byte = *pu1_nal_unit++;
            i--;
        }

        ui4_word = ((ui4_word << 8) | u1_cur_byte);
        u1_num_bytes_copied++;
        if(u4_num_bytes_in_word == u1_num_bytes_copied)
        {
            *puc_bitstream_buffer = ui4_word;
            puc_bitstream_buffer++;
            u1_num_bytes_copied = 0;
        }

        c_count++;
        if(u1_cur_byte != 0x00) c_count = 0;

        u4_num_bytes_in_rbsp += 4;
    }

    for(; i > 0; i--)
    {
        u1_cur_byte = *pu1_nal_unit++;

        if(c_count == NUM_OF_ZERO_BYTES_BEFORE_START_CODE &&
           u1_cur_byte == EMULATION_PREVENTION_BYTE)
        {
            c_count = 0;
            i--;
            u1_cur_byte = *pu1_nal_unit++;
        }

        ui4_word = ((ui4_word << 8) | u1_cur_byte);
        u4_num_bytes_in_rbsp++;

        if((u4_num_bytes_in_rbsp & 0x03) == 0x03)
        {
            *puc_bitstream_buffer = ui4_word;
            puc_bitstream_buffer++;
        }
        c_count++;
        if(u1_cur_byte != 0x00) c_count = 0;
    }

    *puc_bitstream_buffer = (ui4_word << ((3 - (((u4_num_bytes_in_rbsp << 30) >> 30))) << 3));
    ps_bitstrm->u4_ofst = 0;
    ps_bitstrm->u4_max_ofst = ((u4_num_bytes_in_rbsp + u1_nal_header_size) << 3);

    return (u4_num_bytes_in_rbsp);
}

WORD32 imvcd_nalu_parser(mvc_dec_ctxt_t *ps_mvcd_ctxt, UWORD8 *pu1_bitstream_buf,
                         UWORD32 i4_nalu_length)
{
    AVC_EXT_NALU_ID_T e_nalu_id;

    UWORD8 u1_first_byte;

    dec_struct_t *ps_view_ctxt = &ps_mvcd_ctxt->s_view_dec_ctxt;
    dec_bit_stream_t *ps_bitstrm = ps_view_ctxt->ps_bitstrm;

    WORD32 i4_error_code = NOT_OK;

    if((NULL != pu1_bitstream_buf) && (i4_nalu_length > 0))
    {
        imvcd_transform_nalu(ps_bitstrm, pu1_bitstream_buf, i4_nalu_length);

        u1_first_byte = ih264d_get_bits_h264(ps_bitstrm, 8);

        if(NAL_FORBIDDEN_BIT(u1_first_byte))
        {
            return NOT_OK;
        }

        e_nalu_id = NAL_UNIT_TYPE(u1_first_byte);
        ps_view_ctxt->u1_nal_unit_type = e_nalu_id;

        // if any other nal unit other than slice nal is encountered in between a
        // frame break out of loop without consuming header
        if((ps_view_ctxt->u4_slice_start_code_found == 1) &&
           (ps_view_ctxt->u1_pic_decode_done != 1) && is_slice_nalu_type(e_nalu_id))
        {
            return ERROR_INCOMPLETE_FRAME;
        }

        switch(e_nalu_id)
        {
            case PREFIX_NAL:
            {
                if(!ps_view_ctxt->i4_decode_header)
                {
                    if(1 == ih264d_get_bit_h264(ps_bitstrm))
                    {
                        return IVD_INVALID_BITSTREAM;
                    }

                    i4_error_code = imvcd_nalu_mvc_ext_parser(ps_mvcd_ctxt, ps_bitstrm);

                    if(i4_error_code != OK)
                    {
                        return i4_error_code;
                    }
                }

                break;
            }
            case SUBSET_SPS:
            {
                ih264d_rbsp_to_sodb(ps_view_ctxt->ps_bitstrm);

                i4_error_code = imvcd_parse_subset_sps(ps_mvcd_ctxt, ps_bitstrm);

                if(OK != i4_error_code)
                {
                    return i4_error_code;
                }

                ps_view_ctxt->i4_header_decoded |= 1 << SUBSET_SPS;

                break;
            }
            case SLICE_NON_IDR:
            case SLICE_IDR:
            {
                if(!ps_view_ctxt->i4_decode_header)
                {
                    if(is_header_decoded(ps_view_ctxt->i4_header_decoded, SPS) &&
                       is_header_decoded(ps_view_ctxt->i4_header_decoded, PPS))
                    {
                        nalu_mvc_ext_t *ps_nalu_mvc_ext = imvcd_get_cur_nalu_mvc_ext(ps_mvcd_ctxt);

                        ps_view_ctxt->u4_slice_start_code_found = 1;

                        if((0 == ps_mvcd_ctxt->u2_num_views_decoded) &&
                           !ps_nalu_mvc_ext->u1_inter_view_flag)
                        {
                            ps_nalu_mvc_ext->u1_inter_view_flag = 1;
                        }

                        ih264d_rbsp_to_sodb(ps_view_ctxt->ps_bitstrm);

                        i4_error_code = imvcd_parse_decode_slice(ps_mvcd_ctxt);

                        if(i4_error_code != OK)
                        {
                            return i4_error_code;
                        }
                    }
                    else
                    {
                        return IVD_INVALID_BITSTREAM;
                    }
                }

                break;
            }
            case CODED_SLICE_EXTENSION:
            {
                if(!ps_view_ctxt->i4_decode_header)
                {
                    if(is_header_decoded(ps_view_ctxt->i4_header_decoded, SPS) &&
                       is_header_decoded(ps_view_ctxt->i4_header_decoded, PPS) &&
                       is_header_decoded(ps_view_ctxt->i4_header_decoded, SUBSET_SPS))
                    {
                        ps_view_ctxt->u4_slice_start_code_found = 1;

                        if(1 == ih264d_get_bit_h264(ps_bitstrm))
                        {
                            return IVD_INVALID_BITSTREAM;
                        }

                        i4_error_code = imvcd_nalu_mvc_ext_parser(ps_mvcd_ctxt, ps_bitstrm);

                        if(i4_error_code != OK)
                        {
                            return i4_error_code;
                        }

                        ih264d_rbsp_to_sodb(ps_view_ctxt->ps_bitstrm);

                        i4_error_code = imvcd_parse_decode_slice(ps_mvcd_ctxt);

                        if(i4_error_code != OK)
                        {
                            return i4_error_code;
                        }
                    }
                    else
                    {
                        return IVD_INVALID_BITSTREAM;
                    }
                }

                break;
            }
            default:
            {
                i4_error_code = ERROR_UNKNOWN_NAL;

                break;
            }
        }
    }

    return i4_error_code;
}
