/*****************************************************************
|
|    AP4 - AVC Parser
|
|    Copyright 2002-2008 Axiomatic Systems, LLC
|
|
|    This file is part of Bento4/AP4 (MP4 Atom Processing Library).
|
|    Unless you have obtained Bento4 under a difference license,
|    this version of Bento4 is Bento4|GPL.
|    Bento4|GPL is free software; you can redistribute it and/or modify
|    it under the terms of the GNU General Public License as published by
|    the Free Software Foundation; either version 2, or (at your option)
|    any later version.
|
|    Bento4|GPL is distributed in the hope that it will be useful,
|    but WITHOUT ANY WARRANTY; without even the implied warranty of
|    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
|    GNU General Public License for more details.
|
|    You should have received a copy of the GNU General Public License
|    along with Bento4|GPL; see the file COPYING.  If not, write to the
|    Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
|    02111-1307, USA.
|
****************************************************************/

/*----------------------------------------------------------------------
|   includes
+---------------------------------------------------------------------*/
#include "Ap4AvcParser.h"
#include "Ap4Utils.h"

/*----------------------------------------------------------------------
|   debugging
+---------------------------------------------------------------------*/
//#define AP4_AVC_PARSER_ENABLE_DEBUG
#if defined(AP4_AVC_PARSER_ENABLE_DEBUG)
#define DBG_PRINTF_0(_x0) printf(_x0)
#define DBG_PRINTF_1(_x0, _x1) printf(_x0, _x1)
#define DBG_PRINTF_2(_x0, _x1, _x2) printf(_x0, _x1, _x2)
#define DBG_PRINTF_3(_x0, _x1, _x2, _x3) printf(_x0, _x1, _x2, _x3)
#define DBG_PRINTF_4(_x0, _x1, _x2, _x3, _x4) printf(_x0, _x1, _x2, _x3, _x4)
#define DBG_PRINTF_5(_x0, _x1, _x2, _x3, _x4, _x5) printf(_x0, _x1, _x2, _x3, _x4, _x5)
#else
#define DBG_PRINTF_0(_x0)
#define DBG_PRINTF_1(_x0, _x1)
#define DBG_PRINTF_2(_x0, _x1, _x2)
#define DBG_PRINTF_3(_x0, _x1, _x2, _x3)
#define DBG_PRINTF_4(_x0, _x1, _x2, _x3, _x4)
#define DBG_PRINTF_5(_x0, _x1, _x2, _x3, _x4, _x5)
#endif

/*----------------------------------------------------------------------
|   AP4_AvcNalParser::NaluTypeName
+---------------------------------------------------------------------*/
const char*
AP4_AvcNalParser::NaluTypeName(unsigned int nalu_type)
{
    switch (nalu_type) {
        case  0: return "Unspecified";
        case  1: return "Coded slice of a non-IDR picture";
        case  2: return "Coded slice data partition A"; 
        case  3: return "Coded slice data partition B";
        case  4: return "Coded slice data partition C";
        case  5: return "Coded slice of an IDR picture";
        case  6: return "Supplemental enhancement information (SEI)";
        case  7: return "Sequence parameter set";
        case  8: return "Picture parameter set";
        case  9: return "Access unit delimiter";
        case 10: return "End of sequence";
        case 11: return "End of stream";
        case 12: return "Filler data";
        case 13: return "Sequence parameter set extension";
        case 14: return "Prefix NAL unit in scalable extension";
        case 15: return "Subset sequence parameter set";
        case 19: return "Coded slice of an auxiliary coded picture without partitioning";
        case 20: return "Coded slice in scalable extension";
        case 28: return "Dolby Vision RPU NAL units";
        case 30: return "Dolby Vision EL NAL units";
        default: return NULL;
    }
}

/*----------------------------------------------------------------------
|   AP4_AvcNalParser::PrimaryPicTypeName
+---------------------------------------------------------------------*/
const char*
AP4_AvcNalParser::PrimaryPicTypeName(unsigned int primary_pic_type)
{
	switch (primary_pic_type) {
        case 0: return "I";
        case 1: return "I, P";
        case 2: return "I, P, B";
        case 3: return "SI";
        case 4: return "SI, SP";
        case 5: return "I, SI";
        case 6: return "I, SI, P, SP";
        case 7: return "I, SI, P, SP, B";
        default: return NULL;
    }
}

/*----------------------------------------------------------------------
|   AP4_AvcNalParser::SliceTypeName
+---------------------------------------------------------------------*/
const char*
AP4_AvcNalParser::SliceTypeName(unsigned int slice_type)
{
	switch (slice_type) {
        case 0: return "P";
        case 1: return "B";
        case 2: return "I";
        case 3:	return "SP";
        case 4: return "SI";
        case 5: return "P";
        case 6: return "B";
        case 7: return "I";
        case 8:	return "SP";
        case 9: return "SI";
        default: return NULL;
    }
}

/*----------------------------------------------------------------------
|   AP4_AvcNalParser::AP4_AvcNalParser
+---------------------------------------------------------------------*/
AP4_AvcNalParser::AP4_AvcNalParser() :
    AP4_NalParser()
{
}

/*----------------------------------------------------------------------
|   AP4_AvcFrameParser::AP4_AvcFrameParser
+---------------------------------------------------------------------*/
AP4_AvcFrameParser::AP4_AvcFrameParser() :
    m_NalUnitType(0),
    m_NalRefIdc(0),
    m_SliceHeader(NULL),
    m_AccessUnitVclNalUnitCount(0),
    m_TotalNalUnitCount(0),
    m_TotalAccessUnitCount(0),
    m_PrevFrameNum(0),
    m_PrevFrameNumOffset(0),
    m_PrevPicOrderCntMsb(0),
    m_PrevPicOrderCntLsb(0),
    m_keepParameterSets(true)
{
    for (unsigned int i=0; i<256; i++) {
        m_PPS[i] = NULL;
        m_SPS[i] = NULL;
    }
}

/*----------------------------------------------------------------------
|   AP4_AvcFrameParser::~AP4_AvcFrameParser
+---------------------------------------------------------------------*/
AP4_AvcFrameParser::~AP4_AvcFrameParser()
{
    for (unsigned int i=0; i<256; i++) {
        delete m_PPS[i];
        delete m_SPS[i];
    }
    
    delete m_SliceHeader;
    
    // cleanup any un-transfered buffers
    for (unsigned int i=0; i<m_AccessUnitData.ItemCount(); i++) {
        delete m_AccessUnitData[i];
    }
}

/*----------------------------------------------------------------------
|   ReadGolomb
+---------------------------------------------------------------------*/
static unsigned int
ReadGolomb(AP4_BitReader& bits)
{
    unsigned int leading_zeros = 0;
    while (bits.ReadBit() == 0) {
        leading_zeros++;
        if (leading_zeros > 32) return 0; // safeguard
    }
    if (leading_zeros) {
        return (1<<leading_zeros)-1+bits.ReadBits(leading_zeros);
    } else {
        return 0;
    }
}

/*----------------------------------------------------------------------
|   SignedGolomb
+---------------------------------------------------------------------*/
static int
SignedGolomb(unsigned int code_num)
{
    if (code_num % 2) {
        return (code_num+1)/2;
    } else {
        return -((int)code_num/2);
    }
}

/*----------------------------------------------------------------------
|   AP4_AvcSequenceParameterSet::AP4_AvcSequenceParameterSet
+---------------------------------------------------------------------*/
AP4_AvcSequenceParameterSet::AP4_AvcSequenceParameterSet() :
    profile_idc(0),
    constraint_set0_flag(0),
    constraint_set1_flag(0),
    constraint_set2_flag(0),
    constraint_set3_flag(0),
    level_idc(0),
    seq_parameter_set_id(0),
    chroma_format_idc(1),
    separate_colour_plane_flag(0),
    bit_depth_luma_minus8(0),
    bit_depth_chroma_minus8(0),
    qpprime_y_zero_transform_bypass_flag(0),
    seq_scaling_matrix_present_flag(0),
    log2_max_frame_num_minus4(0),
    pic_order_cnt_type(0),
    log2_max_pic_order_cnt_lsb_minus4(0),
    delta_pic_order_always_zero_flags(0),
    offset_for_non_ref_pic(0),
    offset_for_top_to_bottom_field(0),
    num_ref_frames_in_pic_order_cnt_cycle(0),
    num_ref_frames(0),
    gaps_in_frame_num_value_allowed_flag(0),
    pic_width_in_mbs_minus1(0),
    pic_height_in_map_units_minus1(0),
    frame_mbs_only_flag(0),
    mb_adaptive_frame_field_flag(0),
    direct_8x8_inference_flag(0),
    frame_cropping_flag(0),
    frame_crop_left_offset(0),
    frame_crop_right_offset(0),
    frame_crop_top_offset(0),
    frame_crop_bottom_offset(0)
{
    AP4_SetMemory(scaling_list_4x4, 0, sizeof(scaling_list_4x4));
    AP4_SetMemory(use_default_scaling_matrix_4x4, 0, sizeof(use_default_scaling_matrix_4x4));
    AP4_SetMemory(scaling_list_8x8, 0, sizeof(scaling_list_8x8));
    AP4_SetMemory(use_default_scaling_matrix_8x8, 0, sizeof(use_default_scaling_matrix_8x8));
    AP4_SetMemory(offset_for_ref_frame, 0, sizeof(offset_for_ref_frame));
}

/*----------------------------------------------------------------------
|   AP4_AvcSequenceParameterSet::GetInfo
+---------------------------------------------------------------------*/
void
AP4_AvcSequenceParameterSet::GetInfo(unsigned int& width, unsigned int& height)
{
    width = (pic_width_in_mbs_minus1+1) * 16;
	height = (2-frame_mbs_only_flag) * (pic_height_in_map_units_minus1+1) * 16;

    if (frame_cropping_flag) {
        unsigned int crop_h = 2*(frame_crop_left_offset+frame_crop_right_offset);
        unsigned int crop_v = 2*(frame_crop_top_offset+frame_crop_bottom_offset)*(2-frame_mbs_only_flag);
		if (crop_h < width) width   -= crop_h;
		if (crop_v < height) height -= crop_v;
	}
}

/*----------------------------------------------------------------------
|   AP4_AvcFrameParser::ParseSPS
+---------------------------------------------------------------------*/
AP4_Result
AP4_AvcFrameParser::ParseSPS(const unsigned char*         data,
                             unsigned int                 data_size,
                             AP4_AvcSequenceParameterSet& sps)
{
    sps.raw_bytes.SetData(data, data_size);
    AP4_DataBuffer unescaped(data, data_size);
    AP4_NalParser::Unescape(unescaped);
    AP4_BitReader bits(unescaped.GetData(), unescaped.GetDataSize());

    bits.SkipBits(8); // NAL Unit Type

    sps.profile_idc = bits.ReadBits(8);
    sps.constraint_set0_flag = bits.ReadBit();
    sps.constraint_set1_flag = bits.ReadBit();
    sps.constraint_set2_flag = bits.ReadBit();
    sps.constraint_set3_flag = bits.ReadBit();
    bits.SkipBits(4);
    sps.level_idc = bits.ReadBits(8);
    sps.seq_parameter_set_id = ReadGolomb(bits);
    if (sps.seq_parameter_set_id > AP4_AVC_SPS_MAX_ID) {
        return AP4_ERROR_INVALID_FORMAT;
    }
    if (sps.profile_idc  ==  100  ||
        sps.profile_idc  ==  110  ||
        sps.profile_idc  ==  122  ||
        sps.profile_idc  ==  244  ||
        sps.profile_idc  ==  44   ||
        sps.profile_idc  ==  83   ||
        sps.profile_idc  ==  86) {
        sps.chroma_format_idc = ReadGolomb(bits);
        sps.separate_colour_plane_flag = 0;
        if (sps.chroma_format_idc == 3) {
            sps.separate_colour_plane_flag = bits.ReadBit();
        }
        sps.bit_depth_luma_minus8 = ReadGolomb(bits);
        sps.bit_depth_chroma_minus8 = ReadGolomb(bits);
        sps.qpprime_y_zero_transform_bypass_flag = bits.ReadBit();
        sps.seq_scaling_matrix_present_flag = bits.ReadBit();
        if (sps.seq_scaling_matrix_present_flag) {
            for (int i=0; i<(sps.chroma_format_idc != 3 ? 8 : 12); i++) {
                unsigned int seq_scaling_list_present_flag = bits.ReadBit();
                if (seq_scaling_list_present_flag) {
                    if (i<6) {
                        int last_scale = 8;
                        int next_scale = 8;
                        for (unsigned int j=0; j<16; j++) {
                            if (next_scale) {
                                int delta_scale = SignedGolomb(ReadGolomb(bits));
                                next_scale = (last_scale + delta_scale + 256) % 256;
                                sps.use_default_scaling_matrix_4x4[i] = (j == 0 && next_scale == 0);
                            }
                            sps.scaling_list_4x4[i].scale[j] = (next_scale == 0 ? last_scale : next_scale);
                            last_scale = sps.scaling_list_4x4[i].scale[j];
                        }
                    } else {
                        int last_scale = 8;
                        int next_scale = 8;
                        for (unsigned int j=0; j<64; j++) {
                            if (next_scale) {
                                int delta_scale = SignedGolomb(ReadGolomb(bits));
                                next_scale = (last_scale + delta_scale + 256) % 256;
                                sps.use_default_scaling_matrix_8x8[i-6] = (j == 0 && next_scale == 0);
                            }
                            sps.scaling_list_8x8[i-6].scale[j] = (next_scale == 0 ? last_scale : next_scale);
                            last_scale = sps.scaling_list_8x8[i-6].scale[j];
                        }
                    }
                }
            }
        }
    }
    sps.log2_max_frame_num_minus4 = ReadGolomb(bits);
    sps.pic_order_cnt_type = ReadGolomb(bits);
    if (sps.pic_order_cnt_type > 2) {
        return AP4_ERROR_INVALID_FORMAT;
    }
    if (sps.pic_order_cnt_type == 0) {
        sps.log2_max_pic_order_cnt_lsb_minus4 = ReadGolomb(bits);
    } else if (sps.pic_order_cnt_type == 1) {
        sps.delta_pic_order_always_zero_flags = bits.ReadBit();
        sps.offset_for_non_ref_pic = SignedGolomb(ReadGolomb(bits));
        sps.offset_for_top_to_bottom_field = SignedGolomb(ReadGolomb(bits));
        sps.num_ref_frames_in_pic_order_cnt_cycle = ReadGolomb(bits);
        if (sps.num_ref_frames_in_pic_order_cnt_cycle > AP4_AVC_SPS_MAX_NUM_REF_FRAMES_IN_PIC_ORDER_CNT_CYCLE) {
            return AP4_ERROR_INVALID_FORMAT;
        }
        for (unsigned int i=0; i<sps.num_ref_frames_in_pic_order_cnt_cycle; i++) {
            sps.offset_for_ref_frame[i] = SignedGolomb(ReadGolomb(bits));
        }
    }
    sps.num_ref_frames                       = ReadGolomb(bits);
    sps.gaps_in_frame_num_value_allowed_flag = bits.ReadBit();
    sps.pic_width_in_mbs_minus1              = ReadGolomb(bits);
    sps.pic_height_in_map_units_minus1       = ReadGolomb(bits);
    sps.frame_mbs_only_flag                  = bits.ReadBit();
    if (!sps.frame_mbs_only_flag) {
        sps.mb_adaptive_frame_field_flag = bits.ReadBit();
    }
    sps.direct_8x8_inference_flag = bits.ReadBit();
    sps.frame_cropping_flag       = bits.ReadBit();
    if (sps.frame_cropping_flag) {
        sps.frame_crop_left_offset   = ReadGolomb(bits);
        sps.frame_crop_right_offset  = ReadGolomb(bits);
        sps.frame_crop_top_offset    = ReadGolomb(bits);
        sps.frame_crop_bottom_offset = ReadGolomb(bits);
    }

    return AP4_SUCCESS;
}

/*----------------------------------------------------------------------
|   AP4_AvcPictureParameterSet::AP4_PictureParameterSet
+---------------------------------------------------------------------*/
AP4_AvcPictureParameterSet::AP4_AvcPictureParameterSet() :
    pic_parameter_set_id(0),
    seq_parameter_set_id(0),
    entropy_coding_mode_flag(0),
    pic_order_present_flag(0),
    num_slice_groups_minus1(0),
    slice_group_map_type(0),
    slice_group_change_direction_flag(0),
    slice_group_change_rate_minus1(0),
    pic_size_in_map_units_minus1(0),
    num_ref_idx_10_active_minus1(0),
    num_ref_idx_11_active_minus1(0),
    weighted_pred_flag(0),
    weighted_bipred_idc(0),
    pic_init_qp_minus26(0),
    pic_init_qs_minus26(0),
    chroma_qp_index_offset(0),
    deblocking_filter_control_present_flag(0),
    constrained_intra_pred_flag(0),
    redundant_pic_cnt_present_flag(0)
{
    AP4_SetMemory(run_length_minus1, 0, sizeof(run_length_minus1));
    AP4_SetMemory(top_left, 0, sizeof(top_left));
    AP4_SetMemory(bottom_right, 0, sizeof(bottom_right));
 }

/*----------------------------------------------------------------------
|   AP4_AvcFrameParser::ParsePPS
+---------------------------------------------------------------------*/
AP4_Result
AP4_AvcFrameParser::ParsePPS(const unsigned char*        data,
                             unsigned int                data_size,
                             AP4_AvcPictureParameterSet& pps)
{
    pps.raw_bytes.SetData(data, data_size);
    AP4_DataBuffer unescaped(data, data_size);
    AP4_NalParser::Unescape(unescaped);
    AP4_BitReader bits(unescaped.GetData(), unescaped.GetDataSize());
    
    bits.SkipBits(8); // NAL Unit Type

    pps.pic_parameter_set_id     = ReadGolomb(bits);
    if (pps.pic_parameter_set_id > AP4_AVC_PPS_MAX_ID) {
        return AP4_ERROR_INVALID_FORMAT;
    }
    pps.seq_parameter_set_id     = ReadGolomb(bits);
    if (pps.seq_parameter_set_id > AP4_AVC_SPS_MAX_ID) {
        return AP4_ERROR_INVALID_FORMAT;
    }
    pps.entropy_coding_mode_flag = bits.ReadBit();
    pps.pic_order_present_flag   = bits.ReadBit();
    pps.num_slice_groups_minus1  = ReadGolomb(bits);
    if (pps.num_slice_groups_minus1 >= AP4_AVC_PPS_MAX_SLICE_GROUPS) {
        return AP4_ERROR_INVALID_FORMAT;
    }
    if (pps.num_slice_groups_minus1 > 0) {
        pps.slice_group_map_type = ReadGolomb(bits);
        if (pps.slice_group_map_type == 0) {
            for (unsigned int i=0; i<=pps.num_slice_groups_minus1; i++) {
                pps.run_length_minus1[i] = ReadGolomb(bits);
            }
        } else if (pps.slice_group_map_type == 2) {
            for (unsigned int i=0; i<pps.num_slice_groups_minus1; i++) {
                pps.top_left[i] = ReadGolomb(bits);
                pps.bottom_right[i] = ReadGolomb(bits);
            }
        } else if (pps.slice_group_map_type == 3 ||
                   pps.slice_group_map_type == 4 ||
                   pps.slice_group_map_type == 5) {
            pps.slice_group_change_direction_flag = bits.ReadBit();
            pps.slice_group_change_rate_minus1 = ReadGolomb(bits);
        } else if (pps.slice_group_map_type == 6) {
            pps.pic_size_in_map_units_minus1 = ReadGolomb(bits);
            if (pps.pic_size_in_map_units_minus1 >= AP4_AVC_PPS_MAX_PIC_SIZE_IN_MAP_UNITS) {
                return AP4_ERROR_INVALID_FORMAT;
            }
            unsigned int num_bits_per_slice_group_id;
            if (pps.num_slice_groups_minus1 + 1 > 4) {
                num_bits_per_slice_group_id = 3;
            } else if (pps.num_slice_groups_minus1 + 1 > 2) {
                num_bits_per_slice_group_id = 2;
            } else {
                num_bits_per_slice_group_id = 1;
            }
            for (unsigned int i=0; i<=pps.pic_size_in_map_units_minus1; i++) {
                /*pps.slice_group_id[i] =*/ bits.ReadBits(num_bits_per_slice_group_id);
            }
        }
    }
    pps.num_ref_idx_10_active_minus1 = ReadGolomb(bits);
    pps.num_ref_idx_11_active_minus1 = ReadGolomb(bits);
    pps.weighted_pred_flag           = bits.ReadBit();
    pps.weighted_bipred_idc          = bits.ReadBits(2);
    pps.pic_init_qp_minus26          = SignedGolomb(ReadGolomb(bits));
    pps.pic_init_qs_minus26          = SignedGolomb(ReadGolomb(bits));
    pps.chroma_qp_index_offset       = SignedGolomb(ReadGolomb(bits));
    pps.deblocking_filter_control_present_flag = bits.ReadBit();
    pps.constrained_intra_pred_flag            = bits.ReadBit();
    pps.redundant_pic_cnt_present_flag         = bits.ReadBit();
    
    return AP4_SUCCESS;
}

/*----------------------------------------------------------------------
|   AP4_AvcSliceHeader::AP4_AvcSliceHeader
+---------------------------------------------------------------------*/
AP4_AvcSliceHeader::AP4_AvcSliceHeader() :
    size(0),
    first_mb_in_slice(0),
    slice_type(0),
    pic_parameter_set_id(0),
    colour_plane_id(0),
    frame_num(0),
    field_pic_flag(0),
    bottom_field_flag(0),
    idr_pic_id(0),
    pic_order_cnt_lsb(0),
    redundant_pic_cnt(0),
    direct_spatial_mv_pred_flag(0),
    num_ref_idx_active_override_flag(0),
    num_ref_idx_l0_active_minus1(0),
    num_ref_idx_l1_active_minus1(0),
    ref_pic_list_reordering_flag_l0(0),
    reordering_of_pic_nums_idc(0),
    abs_diff_pic_num_minus1(0),
    long_term_pic_num(0),
    ref_pic_list_reordering_flag_l1(0),
    luma_log2_weight_denom(0),
    chroma_log2_weight_denom(0),
    cabac_init_idc(0),
    slice_qp_delta(0),
    sp_for_switch_flag(0),
    slice_qs_delta(0),
    disable_deblocking_filter_idc(0),
    slice_alpha_c0_offset_div2(0),
    slice_beta_offset_div2(0),
    slice_group_change_cycle(0),
    no_output_of_prior_pics_flag(0),
    long_term_reference_flag(0),
    difference_of_pic_nums_minus1(0),
    long_term_frame_idx(0),
    max_long_term_frame_idx_plus1(0)
{
    delta_pic_order_cnt[0] = delta_pic_order_cnt[1] = 0;
}

/*----------------------------------------------------------------------
|   AP4_AvcFrameParser::ParseSliceHeader
+---------------------------------------------------------------------*/
AP4_Result
AP4_AvcFrameParser::ParseSliceHeader(const AP4_UI08*     data,
                                     unsigned int        data_size,
                                     unsigned int        nal_unit_type,
                                     unsigned int        nal_ref_idc,
                                     AP4_AvcSliceHeader& slice_header)
{
    AP4_DataBuffer unescaped(data, data_size);
    AP4_NalParser::Unescape(unescaped);
    AP4_BitReader bits(unescaped.GetData(), unescaped.GetDataSize());

    // init the computer fields
    slice_header.size = 0;
    
    slice_header.first_mb_in_slice    = ReadGolomb(bits);
    slice_header.slice_type           = ReadGolomb(bits);
    slice_header.pic_parameter_set_id = ReadGolomb(bits);
    if (slice_header.pic_parameter_set_id > AP4_AVC_PPS_MAX_ID) {
        return AP4_ERROR_INVALID_FORMAT;
    }
    const AP4_AvcPictureParameterSet* pps = m_PPS[slice_header.pic_parameter_set_id];
    if (pps == NULL) {
        return AP4_ERROR_INVALID_FORMAT;
    }
    const AP4_AvcSequenceParameterSet* sps = m_SPS[pps->seq_parameter_set_id];
    if (sps == NULL) {
        return AP4_ERROR_INVALID_FORMAT;
    }
    if (sps->separate_colour_plane_flag) {
        slice_header.colour_plane_id = bits.ReadBits(2);
    }
    slice_header.frame_num = bits.ReadBits(sps->log2_max_frame_num_minus4 + 4);
    if (!sps->frame_mbs_only_flag) {
        slice_header.field_pic_flag = bits.ReadBit();
        if (slice_header.field_pic_flag) {
            slice_header.bottom_field_flag = bits.ReadBit();
        }
    }
    if (nal_unit_type == AP4_AVC_NAL_UNIT_TYPE_CODED_SLICE_OF_IDR_PICTURE) {
        slice_header.idr_pic_id = ReadGolomb(bits);
    }
    if (sps->pic_order_cnt_type == 0) {
        slice_header.pic_order_cnt_lsb = bits.ReadBits(sps->log2_max_pic_order_cnt_lsb_minus4 + 4);
        if (pps->pic_order_present_flag && !slice_header.field_pic_flag) {
            slice_header.delta_pic_order_cnt[0] = SignedGolomb(ReadGolomb(bits));
        }
    }
    if (sps->pic_order_cnt_type == 1 && !sps->delta_pic_order_always_zero_flags) {
        slice_header.delta_pic_order_cnt[0] = SignedGolomb(ReadGolomb(bits));
        if (pps->pic_order_present_flag && !slice_header.field_pic_flag) {
            slice_header.delta_pic_order_cnt[1] = SignedGolomb(ReadGolomb(bits));
        }
    }
    if (pps->redundant_pic_cnt_present_flag) {
        slice_header.redundant_pic_cnt = ReadGolomb(bits);
    }
    
    unsigned int slice_type = slice_header.slice_type % 5; // this seems to be implicit in the spec
    
    if (slice_type == AP4_AVC_SLICE_TYPE_B) {
        slice_header.direct_spatial_mv_pred_flag = bits.ReadBit();
    }
    
    if (slice_type == AP4_AVC_SLICE_TYPE_P  ||
        slice_type == AP4_AVC_SLICE_TYPE_SP ||
        slice_type == AP4_AVC_SLICE_TYPE_B) {
        slice_header.num_ref_idx_active_override_flag = bits.ReadBit();
        
        if (slice_header.num_ref_idx_active_override_flag) {
            slice_header.num_ref_idx_l0_active_minus1 = ReadGolomb(bits);
            if ((slice_header.slice_type % 5) == AP4_AVC_SLICE_TYPE_B) {
                slice_header.num_ref_idx_l1_active_minus1 = ReadGolomb(bits);
            }
        } else {
            slice_header.num_ref_idx_l0_active_minus1 = pps->num_ref_idx_10_active_minus1;
            slice_header.num_ref_idx_l1_active_minus1 = pps->num_ref_idx_11_active_minus1;
        }
    }
    
    // ref_pic_list_reordering
    if ((slice_header.slice_type % 5) != 2 && (slice_header.slice_type % 5) != 4) {
        slice_header.ref_pic_list_reordering_flag_l0 = bits.ReadBit();
        if (slice_header.ref_pic_list_reordering_flag_l0) {
            do {
                slice_header.reordering_of_pic_nums_idc = ReadGolomb(bits);
                if (slice_header.reordering_of_pic_nums_idc == 0 ||
					slice_header.reordering_of_pic_nums_idc == 1) {
                    slice_header.abs_diff_pic_num_minus1 = ReadGolomb(bits);
                } else if (slice_header.reordering_of_pic_nums_idc == 2) {
                    slice_header.long_term_pic_num = ReadGolomb(bits);
                }
            } while (slice_header.reordering_of_pic_nums_idc != 3);
        }
    }
    if ((slice_header.slice_type % 5) == 1) {
        slice_header.ref_pic_list_reordering_flag_l1 = bits.ReadBit();
        if (slice_header.ref_pic_list_reordering_flag_l1) {
            do {
                slice_header.reordering_of_pic_nums_idc = ReadGolomb(bits);
                if (slice_header.reordering_of_pic_nums_idc == 0 ||
					slice_header.reordering_of_pic_nums_idc == 1) {
                    slice_header.abs_diff_pic_num_minus1 = ReadGolomb(bits);
                } else if (slice_header.reordering_of_pic_nums_idc == 2) {
                    slice_header.long_term_pic_num = ReadGolomb(bits);
                }
            } while (slice_header.reordering_of_pic_nums_idc != 3);
        }
    }
    
    if ((pps->weighted_pred_flag &&
        (slice_type == AP4_AVC_SLICE_TYPE_P || slice_type == AP4_AVC_SLICE_TYPE_SP)) ||
		(pps->weighted_bipred_idc == 1 && slice_type == AP4_AVC_SLICE_TYPE_B)) {
        // pred_weight_table
        slice_header.luma_log2_weight_denom = ReadGolomb(bits);
        
        if (sps->chroma_format_idc != 0) {
            slice_header.chroma_log2_weight_denom = ReadGolomb(bits);
        }
        
        for (unsigned int i=0; i<=slice_header.num_ref_idx_l0_active_minus1; i++) {
            unsigned int luma_weight_l0_flag = bits.ReadBit();
            if (luma_weight_l0_flag) {
                /* slice_header.luma_weight_l0[i] = SignedGolomb( */ ReadGolomb(bits);
                /* slice_header.luma_offset_l0[i] = SignedGolomb( */ ReadGolomb(bits);
            }
            if (sps->chroma_format_idc != 0) {
                unsigned int chroma_weight_l0_flag = bits.ReadBit();
                if (chroma_weight_l0_flag) {
                    for (unsigned int j=0; j<2; j++) {
                        /* slice_header.chroma_weight_l0[i][j] = SignedGolomb( */ ReadGolomb(bits);
                        /* slice_header.chroma_offset_l0[i][j] = SignedGolomb( */ ReadGolomb(bits);
                    }
                }
            }
        }
        if ((slice_header.slice_type % 5) == 1) {
            for (unsigned int i=0; i<=slice_header.num_ref_idx_l1_active_minus1; i++) {
                unsigned int luma_weight_l1_flag = bits.ReadBit();
                if (luma_weight_l1_flag) {
                    /* slice_header.luma_weight_l1[i] = SignedGolomb( */ ReadGolomb(bits);
                    /* slice_header.luma_offset_l1[i] = SignedGolomb( */ ReadGolomb(bits);
                }
                if (sps->chroma_format_idc != 0) {
                    unsigned int chroma_weight_l1_flag = bits.ReadBit();
                    if (chroma_weight_l1_flag) {
                        for (unsigned int j=0; j<2; j++) {
                            /* slice_header.chroma_weight_l1[i][j] = SignedGolomb( */ ReadGolomb(bits);
                            /* slice_header.chroma_offset_l1[i][j] = SignedGolomb( */ ReadGolomb(bits);
                        }
                    }
                }
            }
        }
    }

    if (nal_ref_idc != 0) {
        // dec_ref_pic_marking
        if (nal_unit_type == AP4_AVC_NAL_UNIT_TYPE_CODED_SLICE_OF_IDR_PICTURE) {
            slice_header.no_output_of_prior_pics_flag = bits.ReadBit();
            slice_header.long_term_reference_flag     = bits.ReadBit();
        } else {
            unsigned int adaptive_ref_pic_marking_mode_flag = bits.ReadBit();
            if (adaptive_ref_pic_marking_mode_flag) {
                unsigned int memory_management_control_operation = 0;
                do {
                    memory_management_control_operation = ReadGolomb(bits);
                    if (memory_management_control_operation == 1 || memory_management_control_operation == 3) {
                        slice_header.difference_of_pic_nums_minus1 = ReadGolomb(bits);
                    }
                    if (memory_management_control_operation == 2) {
                        slice_header.long_term_pic_num = ReadGolomb(bits);
                    }
                    if (memory_management_control_operation == 3 || memory_management_control_operation == 6) {
                        slice_header.long_term_frame_idx = ReadGolomb(bits);
                    }
                    if (memory_management_control_operation == 4) {
                        slice_header.max_long_term_frame_idx_plus1 = ReadGolomb(bits);
                    }
                } while (memory_management_control_operation != 0);
            }
        }
    }
    if (pps->entropy_coding_mode_flag && slice_type != AP4_AVC_SLICE_TYPE_I && slice_type != AP4_AVC_SLICE_TYPE_SI) {
        slice_header.cabac_init_idc = ReadGolomb(bits);
    }
    slice_header.slice_qp_delta = ReadGolomb(bits);
    if (slice_type == AP4_AVC_SLICE_TYPE_SP || slice_type == AP4_AVC_SLICE_TYPE_SI) {
        if (slice_type == AP4_AVC_SLICE_TYPE_SP) {
            slice_header.sp_for_switch_flag = bits.ReadBit();
        }
        slice_header.slice_qs_delta = SignedGolomb(ReadGolomb(bits));
    }
    if (pps->deblocking_filter_control_present_flag) {
        slice_header.disable_deblocking_filter_idc = ReadGolomb(bits);
        if (slice_header.disable_deblocking_filter_idc != 1) {
            slice_header.slice_alpha_c0_offset_div2 = SignedGolomb(ReadGolomb(bits));
            slice_header.slice_beta_offset_div2     = SignedGolomb(ReadGolomb(bits));
        }
    }
    if (pps->num_slice_groups_minus1 > 0 &&
        pps->slice_group_map_type >= 3   &&
        pps->slice_group_map_type <= 5) {
        slice_header.slice_group_change_cycle = ReadGolomb(bits);
    }

    /* compute the size */
    slice_header.size = bits.GetBitsRead();
    
    return AP4_SUCCESS;
}

/*----------------------------------------------------------------------
|   AP4_AvcFrameParser::GetSliceSPS
+---------------------------------------------------------------------*/
AP4_AvcSequenceParameterSet*
AP4_AvcFrameParser::GetSliceSPS(AP4_AvcSliceHeader& sh)
{
    // lookup the SPS
    AP4_AvcPictureParameterSet*  pps = m_PPS[sh.pic_parameter_set_id];
    if (pps == NULL) return NULL;
    return m_SPS[pps->seq_parameter_set_id];
}

/*----------------------------------------------------------------------
|   AP4_AvcFrameParser::SameFrame
|   14496-10 7.4.1.2.4 Detection of the first VCL NAL unit of a primary coded picture
+---------------------------------------------------------------------*/
bool
AP4_AvcFrameParser::SameFrame(unsigned int nal_unit_type_1, unsigned int nal_ref_idc_1, AP4_AvcSliceHeader& sh1,
                              unsigned int nal_unit_type_2, unsigned int nal_ref_idc_2, AP4_AvcSliceHeader& sh2)
{
    if (sh1.frame_num != sh2.frame_num) {
        return false;
    }
    
    if (sh1.pic_parameter_set_id != sh2.pic_parameter_set_id) {
        return false;
    }
    
    if (sh1.field_pic_flag != sh2.field_pic_flag) {
        return false;
    }

    if (sh1.field_pic_flag) {
        if (sh1.bottom_field_flag != sh2.bottom_field_flag) {
            return false;
        }
    }
    
    if ((nal_ref_idc_1 == 0 || nal_ref_idc_2 == 0) && (nal_ref_idc_1 != nal_ref_idc_2)) {
        return false;
    }
    
    // lookup the SPS
    AP4_AvcSequenceParameterSet* sps = GetSliceSPS(sh1);
    if (sps == NULL) return false;
    
    if (sps->pic_order_cnt_type == 0) {
        if (sh1.pic_order_cnt_lsb      != sh2.pic_order_cnt_lsb ||
            sh1.delta_pic_order_cnt[0] != sh2.delta_pic_order_cnt[0]) {
            return false;
        }
    }
    
    if (sps->pic_order_cnt_type == 1) {
        if (sh1.delta_pic_order_cnt[0] != sh2.delta_pic_order_cnt[0] ||
            sh1.delta_pic_order_cnt[1] != sh2.delta_pic_order_cnt[1]) {
            return false;
        }
    }
    
    if (nal_unit_type_1 == AP4_AVC_NAL_UNIT_TYPE_CODED_SLICE_OF_IDR_PICTURE ||
        nal_unit_type_2 == AP4_AVC_NAL_UNIT_TYPE_CODED_SLICE_OF_IDR_PICTURE) {
        if (nal_unit_type_1 != nal_unit_type_2) {
            return false;
        }
    }
    
    if (nal_unit_type_1 == AP4_AVC_NAL_UNIT_TYPE_CODED_SLICE_OF_IDR_PICTURE &&
        nal_unit_type_2 == AP4_AVC_NAL_UNIT_TYPE_CODED_SLICE_OF_IDR_PICTURE) {
        if (sh1.idr_pic_id != sh2.idr_pic_id) {
            return false;
        }
    }
    
    return true;
}

/*----------------------------------------------------------------------
|   AP4_AvcFrameParser::CheckIfAccessUnitIsCompleted
+---------------------------------------------------------------------*/
void
AP4_AvcFrameParser::CheckIfAccessUnitIsCompleted(AccessUnitInfo& access_unit_info)
{
    if (m_SliceHeader == NULL) {
        return;
    }
    if (!m_AccessUnitVclNalUnitCount) {
        return;
    }
    DBG_PRINTF_0(">>>>>>> New Access Unit\n");
    m_AccessUnitVclNalUnitCount = 0;
    
    AP4_AvcSequenceParameterSet* sps = GetSliceSPS(*m_SliceHeader);
    if (sps == NULL) return;
    int max_frame_num = 1 << (sps->log2_max_frame_num_minus4 + 4);
    
    // compute the picture type
    enum {
        AP4_AVC_PIC_TYPE_FRAME,
        AP4_AVC_PIC_TYPE_TOP_FIELD,
        AP4_AVC_PIC_TYPE_BOTTOM_FIELD
    } pic_type;
	if (sps->frame_mbs_only_flag || !m_SliceHeader->field_pic_flag) {
        pic_type = AP4_AVC_PIC_TYPE_FRAME;
    } else if (m_SliceHeader->bottom_field_flag) {
        pic_type = AP4_AVC_PIC_TYPE_BOTTOM_FIELD;
    } else {
        pic_type = AP4_AVC_PIC_TYPE_TOP_FIELD;
    }

    // prepare to compute the picture order count
    int top_field_pic_order_cnt = 0;
    int bottom_field_pic_order_cnt = 0;
    unsigned int frame_num_offset = 0;
    unsigned int frame_num = m_SliceHeader->frame_num;
    if (m_NalUnitType == AP4_AVC_NAL_UNIT_TYPE_CODED_SLICE_OF_IDR_PICTURE) {
        m_PrevPicOrderCntMsb = 0;
        m_PrevPicOrderCntLsb = 0;
    } else {
        if (frame_num < m_PrevFrameNum) {
            // wrap around
            frame_num_offset = m_PrevFrameNumOffset + max_frame_num;
        } else {
            frame_num_offset = m_PrevFrameNumOffset;
        }
    }
    
    // compute the picture order count
    int pic_order_cnt_msb = 0;
    if (sps->pic_order_cnt_type == 0) {
		unsigned int max_pic_order_cnt_lsb = 1 << (sps->log2_max_pic_order_cnt_lsb_minus4 + 4);
        if (m_SliceHeader->pic_order_cnt_lsb < m_PrevPicOrderCntLsb &&
            m_PrevPicOrderCntLsb - m_SliceHeader->pic_order_cnt_lsb >= max_pic_order_cnt_lsb/2) {
            pic_order_cnt_msb = m_PrevPicOrderCntMsb + max_pic_order_cnt_lsb;
        } else if (m_SliceHeader->pic_order_cnt_lsb > m_PrevPicOrderCntLsb &&
                   m_SliceHeader->pic_order_cnt_lsb - m_PrevPicOrderCntLsb > max_pic_order_cnt_lsb/2) {
            pic_order_cnt_msb = m_PrevPicOrderCntMsb - max_pic_order_cnt_lsb;
        } else {
            pic_order_cnt_msb = m_PrevPicOrderCntMsb;
        }
        
        if (pic_type != AP4_AVC_PIC_TYPE_BOTTOM_FIELD) {
            top_field_pic_order_cnt = pic_order_cnt_msb+m_SliceHeader->pic_order_cnt_lsb;
        }
        if (pic_type != AP4_AVC_PIC_TYPE_TOP_FIELD) {
            if (!m_SliceHeader->field_pic_flag) {
                bottom_field_pic_order_cnt = top_field_pic_order_cnt + m_SliceHeader->delta_pic_order_cnt[0];
            } else {
                bottom_field_pic_order_cnt = pic_order_cnt_msb + m_SliceHeader->pic_order_cnt_lsb;
            }
        }
    } else if (sps->pic_order_cnt_type == 1) {
        unsigned int abs_frame_num = 0;
        if (sps->num_ref_frames_in_pic_order_cnt_cycle) {
            abs_frame_num = frame_num_offset + frame_num;
        }
        if (m_NalRefIdc == 0 && abs_frame_num > 0) {
            --abs_frame_num;
        }
        
        int expected_pic_order_cnt = 0;
        if (abs_frame_num >0) {
            unsigned int pic_order_cnt_cycle_cnt = (abs_frame_num-1)/sps->num_ref_frames_in_pic_order_cnt_cycle;
            unsigned int frame_num_in_pic_order_cnt_cycle = (abs_frame_num-1)%sps->num_ref_frames_in_pic_order_cnt_cycle;
            
            int expected_delta_per_pic_order_cnt_cycle = 0;
            for (unsigned int i=0; i<sps->num_ref_frames_in_pic_order_cnt_cycle; i++) {
                expected_delta_per_pic_order_cnt_cycle += sps->offset_for_ref_frame[i];
            }
            expected_pic_order_cnt = pic_order_cnt_cycle_cnt * expected_delta_per_pic_order_cnt_cycle;
            for (unsigned int i=0; i<frame_num_in_pic_order_cnt_cycle; i++) {
                expected_pic_order_cnt += sps->offset_for_ref_frame[i];
            }
        }
        if (m_NalRefIdc == 0) {
            expected_pic_order_cnt += sps->offset_for_non_ref_pic;
        }
    
        if (!m_SliceHeader->field_pic_flag) {
            top_field_pic_order_cnt    = expected_pic_order_cnt + m_SliceHeader->delta_pic_order_cnt[0];
            bottom_field_pic_order_cnt = top_field_pic_order_cnt + sps->offset_for_top_to_bottom_field + m_SliceHeader->delta_pic_order_cnt[1];
        } else if (!m_SliceHeader->bottom_field_flag) {
            top_field_pic_order_cnt = expected_pic_order_cnt + m_SliceHeader->delta_pic_order_cnt[0];
        } else {
            bottom_field_pic_order_cnt = expected_pic_order_cnt + sps->offset_for_top_to_bottom_field + m_SliceHeader->delta_pic_order_cnt[0];
        }
    } else if (sps->pic_order_cnt_type == 2) {
		int pic_order_cnt;
        if (m_NalUnitType == AP4_AVC_NAL_UNIT_TYPE_CODED_SLICE_OF_IDR_PICTURE) {
            pic_order_cnt = 0;
		} else if (m_NalRefIdc == 0) {
            pic_order_cnt = 2*(frame_num_offset+frame_num)-1;
        } else {
            pic_order_cnt = 2*(frame_num_offset+frame_num);
        }
        
        if (!m_SliceHeader->field_pic_flag) {
            top_field_pic_order_cnt    = pic_order_cnt;
            bottom_field_pic_order_cnt = pic_order_cnt;
        } else if (m_SliceHeader->bottom_field_flag) {
            bottom_field_pic_order_cnt = pic_order_cnt;
        } else {
            top_field_pic_order_cnt = pic_order_cnt;
        }
    }

    unsigned int pic_order_cnt;
	if (pic_type == AP4_AVC_PIC_TYPE_FRAME) {
		pic_order_cnt = top_field_pic_order_cnt < bottom_field_pic_order_cnt ? top_field_pic_order_cnt : bottom_field_pic_order_cnt;
	} else if (pic_type == AP4_AVC_PIC_TYPE_TOP_FIELD) {
		pic_order_cnt = top_field_pic_order_cnt;
	} else {
		pic_order_cnt = bottom_field_pic_order_cnt;
    }
    
    // emit the access unit (transfer ownership)
    access_unit_info.nal_units     = m_AccessUnitData;
    access_unit_info.is_idr        = (m_NalUnitType == AP4_AVC_NAL_UNIT_TYPE_CODED_SLICE_OF_IDR_PICTURE);
    access_unit_info.decode_order  = m_TotalAccessUnitCount;
    access_unit_info.display_order = pic_order_cnt;
    m_AccessUnitData.Clear();
    ++m_TotalAccessUnitCount;
    
    // update state
    m_PrevFrameNum       = frame_num;
    m_PrevFrameNumOffset = frame_num_offset;
    if (m_NalRefIdc) {
        m_PrevPicOrderCntMsb = pic_order_cnt_msb;
        m_PrevPicOrderCntLsb = m_SliceHeader->pic_order_cnt_lsb;
    }
}

/*----------------------------------------------------------------------
|   AP4_AvcFrameParser::AppendNalUnitData
+---------------------------------------------------------------------*/
void
AP4_AvcFrameParser::AppendNalUnitData(const unsigned char* data, unsigned int data_size)
{
    m_AccessUnitData.Append(new AP4_DataBuffer(data, data_size));
}

/*----------------------------------------------------------------------
|   AP4_AvcFrameParser::Feed
+---------------------------------------------------------------------*/
AP4_Result
AP4_AvcFrameParser::Feed(const void*     data,
                         AP4_Size        data_size,
                         AP4_Size&       bytes_consumed,
                         AccessUnitInfo& access_unit_info,
                         bool            eos)
{
    const AP4_DataBuffer* nal_unit = NULL;

    // feed the NAL unit parser
    AP4_Result result = m_NalParser.Feed(data, data_size, bytes_consumed, nal_unit, eos);
    if (AP4_FAILED(result)) {
        return result;
    }
    
    if (bytes_consumed < data_size) {
        // there will be more to parse
        eos = false;
    }
    
    return Feed(nal_unit ? nal_unit->GetData() : NULL,
                nal_unit ? nal_unit->GetDataSize() : 0,
                access_unit_info,
                eos);
}

/*----------------------------------------------------------------------
|   AP4_AvcFrameParser::Feed
+---------------------------------------------------------------------*/
AP4_Result
AP4_AvcFrameParser::Feed(const AP4_UI08* nal_unit,
                         AP4_Size        nal_unit_size,
                         AccessUnitInfo& access_unit_info,
                         bool            last_unit)
{
    AP4_Result result;
    
    // default return values
    access_unit_info.Reset();
    
    if (nal_unit && nal_unit_size) {
        unsigned int nal_unit_type = nal_unit[0]&0x1F;
        const char*  nal_unit_type_name = AP4_AvcNalParser::NaluTypeName(nal_unit_type);
        unsigned int nal_ref_idc = (nal_unit[0]>>5)&3;
        if (nal_unit_type_name == NULL) nal_unit_type_name = "UNKNOWN";
        DBG_PRINTF_5("NALU %5d: ref=%d, size=%5d, type=%02d (%s) ",
               m_TotalNalUnitCount,
               nal_ref_idc,
               nal_unit_size,
               nal_unit_type,
               nal_unit_type_name);
        if (nal_unit_type == AP4_AVC_NAL_UNIT_TYPE_ACCESS_UNIT_DELIMITER) {
            unsigned int primary_pic_type = (nal_unit[1]>>5);
            const char*  primary_pic_type_name = AP4_AvcNalParser::PrimaryPicTypeName(primary_pic_type);
            if (primary_pic_type_name == NULL) primary_pic_type_name = "UNKNOWN";
            DBG_PRINTF_2("[%d:%s]\n", primary_pic_type, primary_pic_type_name);

            CheckIfAccessUnitIsCompleted(access_unit_info);
        } else if (nal_unit_type == AP4_AVC_NAL_UNIT_TYPE_CODED_SLICE_OF_NON_IDR_PICTURE ||
                   nal_unit_type == AP4_AVC_NAL_UNIT_TYPE_CODED_SLICE_OF_IDR_PICTURE     ||
                   nal_unit_type == AP4_AVC_NAL_UNIT_TYPE_CODED_SLICE_DATA_PARTITION_A) {
            AP4_AvcSliceHeader* slice_header = new AP4_AvcSliceHeader;
            result = ParseSliceHeader(nal_unit+1,
                                      nal_unit_size-1,
                                      nal_unit_type,
                                      nal_ref_idc,
                                      *slice_header);
            if (AP4_FAILED(result)) {
                return AP4_ERROR_INVALID_FORMAT;
            }
            
            const char* slice_type_name = AP4_AvcNalParser::SliceTypeName(slice_header->slice_type);
            if (slice_type_name == NULL) slice_type_name = "?";
            DBG_PRINTF_5(" pps_id=%d, header_size=%d, frame_num=%d, slice_type=%d (%s), ",
                   slice_header->pic_parameter_set_id,
                   slice_header->size,
                   slice_header->frame_num,
                   slice_header->slice_type,
                   slice_type_name);
            DBG_PRINTF_0("\n");
            if (slice_header) {
                if (m_SliceHeader &&
                    !SameFrame(m_NalUnitType, m_NalRefIdc, *m_SliceHeader,
                               nal_unit_type, nal_ref_idc, *slice_header)) {
                    CheckIfAccessUnitIsCompleted(access_unit_info);
                    m_AccessUnitVclNalUnitCount = 1;
                } else {
                    // continuation of an access unit
                    ++m_AccessUnitVclNalUnitCount;
                }
            }

            // buffer this NAL unit
            AppendNalUnitData(nal_unit, nal_unit_size);
            delete m_SliceHeader;
            m_SliceHeader = slice_header;
            m_NalUnitType = nal_unit_type;
            m_NalRefIdc   = nal_ref_idc;
        } else if (nal_unit_type == AP4_AVC_NAL_UNIT_TYPE_PPS) {
            AP4_AvcPictureParameterSet* pps = new AP4_AvcPictureParameterSet;
            result = ParsePPS(nal_unit, nal_unit_size, *pps);
            if (AP4_FAILED(result)) {
                DBG_PRINTF_0("PPS ERROR!!!\n");
                delete pps;
            } else {
                delete m_PPS[pps->pic_parameter_set_id];
                m_PPS[pps->pic_parameter_set_id] = pps;
                DBG_PRINTF_2("PPS sps_id=%d, pps_id=%d\n", pps->seq_parameter_set_id, pps->pic_parameter_set_id);
                
                // keep the PPS with the NAL unit (this is optional)
                AppendNalUnitData(nal_unit, nal_unit_size);
                CheckIfAccessUnitIsCompleted(access_unit_info);
            }
        } else if (nal_unit_type == AP4_AVC_NAL_UNIT_TYPE_SPS) {
            AP4_AvcSequenceParameterSet* sps = new AP4_AvcSequenceParameterSet;
            result = ParseSPS(nal_unit, nal_unit_size, *sps);
            if (AP4_FAILED(result)) {
                DBG_PRINTF_0("SPS ERROR!!!\n");
                delete sps;
            } else {
                delete m_SPS[sps->seq_parameter_set_id];
                m_SPS[sps->seq_parameter_set_id] = sps;
                DBG_PRINTF_1("SPS sps_id=%d\n", sps->seq_parameter_set_id);
                CheckIfAccessUnitIsCompleted(access_unit_info);
            }
        } else if (nal_unit_type == AP4_AVC_NAL_UNIT_TYPE_SEI) {
            AppendNalUnitData(nal_unit, nal_unit_size);
            CheckIfAccessUnitIsCompleted(access_unit_info);
            DBG_PRINTF_0("\n");
        } else if (nal_unit_type >= 14 && nal_unit_type <= 18) {
            CheckIfAccessUnitIsCompleted(access_unit_info);
            DBG_PRINTF_0("\n");
        } else if (nal_unit_type == AP4_AVC_NAL_UNIT_TYPE_UNSPECIFIED28) {
             AppendNalUnitData(nal_unit, nal_unit_size);
             CheckIfAccessUnitIsCompleted(access_unit_info);
             DBG_PRINTF_0("\n");
        } else if (nal_unit_type == AP4_AVC_NAL_UNIT_TYPE_UNSPECIFIED30) {
             AppendNalUnitData(nal_unit, nal_unit_size);
             CheckIfAccessUnitIsCompleted(access_unit_info);
             DBG_PRINTF_0("\n");
        } else {
            DBG_PRINTF_0("\n");
        }
        m_TotalNalUnitCount++;
    }
    
    // flush if needed
    if (last_unit && access_unit_info.nal_units.ItemCount() == 0) {
        DBG_PRINTF_0("------ last unit\n");
        CheckIfAccessUnitIsCompleted(access_unit_info);
    }
    
    return AP4_SUCCESS;
}

/*----------------------------------------------------------------------
|   AP4_AvcFrameParser::AccessUnitInfo::Reset
+---------------------------------------------------------------------*/
void
AP4_AvcFrameParser::AccessUnitInfo::Reset()
{
    for (unsigned int i=0; i<nal_units.ItemCount(); i++) {
        delete nal_units[i];
    }
    nal_units.Clear();
    is_idr = false;
    decode_order = 0;
    display_order = 0;
}
