| /****************************************************************************** |
| * |
| * Copyright (C) 2012 Ittiam Systems Pvt Ltd, Bangalore |
| * |
| * 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. |
| * |
| ******************************************************************************/ |
| /** |
| ******************************************************************************* |
| * @file |
| * ihevcd_nal.c |
| * |
| * @brief |
| * Contains functions for NAL level such as search start code etc |
| * |
| * @author |
| * Harish |
| * |
| * @par List of Functions: |
| * |
| * @remarks |
| * None |
| * |
| ******************************************************************************* |
| */ |
| /*****************************************************************************/ |
| /* File Includes */ |
| /*****************************************************************************/ |
| #include <stdio.h> |
| #include <stddef.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <assert.h> |
| |
| #include "ihevc_typedefs.h" |
| #include "iv.h" |
| #include "ivd.h" |
| #include "ihevcd_cxa.h" |
| |
| #include "ihevc_defs.h" |
| #include "ihevc_debug.h" |
| #include "ihevc_structs.h" |
| #include "ihevc_macros.h" |
| #include "ihevc_platform_macros.h" |
| #include "ihevc_cabac_tables.h" |
| |
| |
| #include "ihevcd_defs.h" |
| #include "ihevcd_function_selector.h" |
| #include "ihevcd_structs.h" |
| #include "ihevcd_error.h" |
| #include "ihevcd_nal.h" |
| #include "ihevcd_bitstream.h" |
| #include "ihevcd_parse_headers.h" |
| #include "ihevcd_parse_slice.h" |
| #include "ihevcd_debug.h" |
| /*****************************************************************************/ |
| /* Function Prototypes */ |
| /*****************************************************************************/ |
| |
| /** |
| ******************************************************************************* |
| * |
| * @brief |
| * Search start code from the given buffer pointer |
| * |
| * @par Description: |
| * Search for start code Return the offset of start code if start code is |
| * found If no start code is found till end of given bitstream then treat |
| * it as invalid NAL and return end of buffer as offset |
| * |
| * @param[in] pu1_buf |
| * Pointer to bitstream |
| * |
| * @param[in] bytes_remaining |
| * Number of bytes remaining in the buffer |
| * |
| * @returns Offset to the first byte in NAL after start code |
| * |
| * @remarks |
| * Incomplete start code at the end of input bitstream is not handled. This |
| * has to be taken care outside this func |
| * |
| ******************************************************************************* |
| */ |
| WORD32 ihevcd_nal_search_start_code(UWORD8 *pu1_buf, WORD32 bytes_remaining) |
| { |
| WORD32 ofst; |
| |
| WORD32 zero_byte_cnt; |
| WORD32 start_code_found; |
| |
| ofst = -1; |
| |
| zero_byte_cnt = 0; |
| start_code_found = 0; |
| while(ofst < (bytes_remaining - 1)) |
| { |
| ofst++; |
| if(pu1_buf[ofst] != 0) |
| { |
| zero_byte_cnt = 0; |
| continue; |
| } |
| |
| zero_byte_cnt++; |
| if((ofst < (bytes_remaining - 1)) && |
| (pu1_buf[ofst + 1] == START_CODE_PREFIX_BYTE) && |
| (zero_byte_cnt >= NUM_ZEROS_BEFORE_START_CODE)) |
| { |
| /* Found the start code */ |
| ofst++; |
| start_code_found = 1; |
| break; |
| } |
| } |
| if((0 == start_code_found) && (ofst < bytes_remaining)) |
| { |
| if((START_CODE_PREFIX_BYTE == pu1_buf[ofst]) && |
| (zero_byte_cnt >= NUM_ZEROS_BEFORE_START_CODE)) |
| { |
| /* Found a start code at the end*/ |
| ofst++; |
| } |
| } |
| /* Since ofst started at -1, increment it by 1 */ |
| ofst++; |
| |
| return ofst; |
| } |
| |
| /** |
| ******************************************************************************* |
| * |
| * @brief |
| * Remove emulation prevention byte present in the bitstream till next start |
| * code is found. Emulation prevention byte removed data is stored in a |
| * different buffer |
| * |
| * @par Description: |
| * Assumption is first start code is already found and pu1_buf is pointing |
| * to a byte after the start code Search for Next NAL's start code Return |
| * if start code is found Remove any emulation prevention byte present Copy |
| * data to new buffer If no start code is found, then treat complete buffer |
| * as one nal. |
| * |
| * @param[in] pu1_src |
| * Pointer to bitstream (excludes the initial the start code) |
| * |
| * @param[in] pu1_dst |
| * Pointer to destination buffer |
| * |
| * @param[in] bytes_remaining |
| * Number of bytes remaining |
| * |
| * @param[out] pi4_nal_len |
| * NAL length (length of bitstream parsed) |
| * |
| * @param[out] pi4_dst_len |
| * Destination bitstream size (length of bitstream parsed with emulation bytes |
| * removed) |
| * |
| * @returns Error code from IHEVCD_ERROR_T |
| * |
| * @remarks |
| * Incomplete start code at the end of input bitstream is not handled. This |
| * has to be taken care outside this func |
| * |
| ******************************************************************************* |
| */ |
| IHEVCD_ERROR_T ihevcd_nal_remv_emuln_bytes(UWORD8 *pu1_src, |
| UWORD8 *pu1_dst, |
| WORD32 bytes_remaining, |
| WORD32 *pi4_nal_len, |
| WORD32 *pi4_dst_len) |
| { |
| WORD32 src_cnt; |
| WORD32 dst_cnt; |
| WORD32 zero_byte_cnt; |
| WORD32 start_code_found; |
| UWORD8 u1_src; |
| IHEVCD_ERROR_T ret = (IHEVCD_ERROR_T)IHEVCD_SUCCESS; |
| |
| src_cnt = 0; |
| dst_cnt = 0; |
| zero_byte_cnt = 0; |
| start_code_found = 0; |
| while(src_cnt < (bytes_remaining - 1)) |
| { |
| u1_src = pu1_src[src_cnt++]; |
| |
| pu1_dst[dst_cnt++] = u1_src; |
| if(u1_src != 0) |
| { |
| zero_byte_cnt = 0; |
| continue; |
| } |
| |
| zero_byte_cnt++; |
| if(zero_byte_cnt >= NUM_ZEROS_BEFORE_START_CODE) |
| { |
| u1_src = pu1_src[src_cnt]; |
| if(START_CODE_PREFIX_BYTE == u1_src) |
| { |
| /* Found the start code */ |
| src_cnt -= zero_byte_cnt; |
| dst_cnt -= zero_byte_cnt; |
| start_code_found = 1; |
| break; |
| } |
| else if(EMULATION_PREVENT_BYTE == u1_src) |
| { |
| /* Found the emulation prevention byte */ |
| src_cnt++; |
| zero_byte_cnt = 0; |
| |
| /* Decrement dst_cnt so that the next byte overwrites |
| * the emulation prevention byte already copied to dst above |
| */ |
| } |
| } |
| |
| } |
| |
| if((0 == start_code_found) && (src_cnt < bytes_remaining)) |
| { |
| u1_src = pu1_src[src_cnt++]; |
| if(zero_byte_cnt >= NUM_ZEROS_BEFORE_START_CODE) |
| { |
| |
| if(START_CODE_PREFIX_BYTE == u1_src) |
| { |
| /* Found a start code at the end*/ |
| src_cnt -= zero_byte_cnt; |
| } |
| else if(EMULATION_PREVENT_BYTE == u1_src) |
| { |
| /* Found the emulation prevention byte at the end*/ |
| src_cnt++; |
| /* Decrement dst_cnt so that the next byte overwrites |
| * the emulation prevention byte already copied to dst above |
| */ |
| dst_cnt--; |
| } |
| } |
| else |
| { |
| pu1_dst[dst_cnt++] = u1_src; |
| } |
| |
| |
| } |
| *pi4_nal_len = src_cnt; |
| *pi4_dst_len = dst_cnt; |
| return ret; |
| } |
| /** |
| ******************************************************************************* |
| * |
| * @brief |
| * Decode given NAL unit's header |
| * |
| * @par Description: |
| * Call NAL unit's header decode Section: 7.3.1.2 |
| * |
| * @param[in] ps_bitstrm |
| * Pointer to bitstream context |
| * |
| * @param[out] ps_nal |
| * Pointer to NAL header |
| * |
| * @returns Error code from IHEVCD_ERROR_T |
| * |
| * @remarks |
| * |
| * |
| ******************************************************************************* |
| */ |
| IHEVCD_ERROR_T ihevcd_nal_unit_header(bitstrm_t *ps_bitstrm, nal_header_t *ps_nal) |
| { |
| WORD32 unused; |
| IHEVCD_ERROR_T ret = (IHEVCD_ERROR_T)IHEVCD_SUCCESS; |
| UNUSED(unused); |
| /* Syntax : forbidden_zero_bit */ |
| unused = ihevcd_bits_get(ps_bitstrm, 1); |
| |
| /* Syntax : nal_unit_type */ |
| ps_nal->i1_nal_unit_type = ihevcd_bits_get(ps_bitstrm, 6); |
| |
| /* Syntax : nuh_reserved_zero_6bits */ |
| unused = ihevcd_bits_get(ps_bitstrm, 6); |
| |
| /* Syntax : nuh_temporal_id_plus1 */ |
| ps_nal->i1_nuh_temporal_id = ihevcd_bits_get(ps_bitstrm, 3) - 1; |
| |
| return ret; |
| |
| } |
| |
| /** |
| ******************************************************************************* |
| * |
| * @brief |
| * Decode given NAL |
| * |
| * @par Description: |
| * Based on the NAL type call appropriate decode function Section: 7.3.1.1 |
| * |
| * |
| * @param[in,out] ps_codec |
| * Pointer to codec context (Functions called within will modify contents of |
| * ps_codec) |
| * |
| * @returns Error code from IHEVCD_ERROR_T |
| * |
| * @remarks |
| * |
| * |
| ******************************************************************************* |
| */ |
| IHEVCD_ERROR_T ihevcd_nal_unit(codec_t *ps_codec) |
| { |
| IHEVCD_ERROR_T ret = (IHEVCD_ERROR_T)IHEVCD_SUCCESS; |
| |
| /* NAL Header */ |
| nal_header_t s_nal; |
| |
| ret = ihevcd_nal_unit_header(&ps_codec->s_parse.s_bitstrm, &s_nal); |
| RETURN_IF((ret != (IHEVCD_ERROR_T)IHEVCD_SUCCESS), ret); |
| |
| if(ps_codec->i4_slice_error) |
| s_nal.i1_nal_unit_type = ps_codec->s_parse.ps_slice_hdr->i1_nal_unit_type; |
| |
| /* Setting RASL Output flag */ |
| switch(s_nal.i1_nal_unit_type) |
| { |
| case NAL_BLA_W_LP : |
| case NAL_BLA_W_DLP : |
| case NAL_BLA_N_LP : |
| ps_codec->i4_rasl_output_flag = 0; |
| break; |
| |
| //TODO: After IDR, there is no case of open GOP |
| //To be fixed appropriately by ignoring RASL only if the |
| // required references are not found |
| case NAL_IDR_W_LP : |
| case NAL_IDR_N_LP : |
| ps_codec->i4_rasl_output_flag = 1; |
| break; |
| |
| case NAL_CRA : |
| ps_codec->i4_rasl_output_flag = (0 != ps_codec->i4_cra_as_first_pic) ? 0 : 1; |
| break; |
| |
| default: |
| break; |
| } |
| |
| switch(s_nal.i1_nal_unit_type) |
| { |
| case NAL_BLA_W_LP : |
| case NAL_BLA_W_DLP : |
| case NAL_BLA_N_LP : |
| case NAL_IDR_W_LP : |
| case NAL_IDR_N_LP : |
| case NAL_CRA : |
| case NAL_TRAIL_N : |
| case NAL_TRAIL_R : |
| case NAL_TSA_N : |
| case NAL_TSA_R : |
| case NAL_STSA_N : |
| case NAL_STSA_R : |
| case NAL_RADL_N : |
| case NAL_RADL_R : |
| case NAL_RASL_N : |
| case NAL_RASL_R : |
| if(ps_codec->i4_header_mode) |
| return IHEVCD_SLICE_IN_HEADER_MODE; |
| |
| if((0 == ps_codec->i4_sps_done) || |
| (0 == ps_codec->i4_pps_done)) |
| { |
| return IHEVCD_INVALID_HEADER; |
| } |
| |
| ps_codec->i4_header_in_slice_mode = 0; |
| ps_codec->i4_cra_as_first_pic = 0; |
| |
| ret = ihevcd_parse_slice_header(ps_codec, &s_nal); |
| DEBUG_PRINT_NAL_INFO(ps_codec, s_nal.i1_nal_unit_type); |
| if(ret == (IHEVCD_ERROR_T)IHEVCD_SUCCESS) |
| { |
| if((s_nal.i1_nal_unit_type != NAL_RASL_N && s_nal.i1_nal_unit_type != NAL_RASL_R) || |
| ps_codec->i4_rasl_output_flag || |
| ps_codec->i4_slice_error) |
| ret = ihevcd_parse_slice_data(ps_codec); |
| } |
| break; |
| |
| case NAL_VPS : |
| // ret = ihevcd_parse_vps(ps_codec); |
| DEBUG_PRINT_NAL_INFO(ps_codec, s_nal.i1_nal_unit_type); |
| break; |
| |
| case NAL_SPS : |
| if(0 == ps_codec->i4_header_mode) |
| { |
| ps_codec->i4_header_in_slice_mode = 1; |
| if(ps_codec->i4_sps_done && |
| ps_codec->i4_pic_present) |
| break; |
| } |
| |
| ret = ihevcd_parse_sps(ps_codec); |
| if(ret == (IHEVCD_ERROR_T)IHEVCD_SUCCESS) |
| { |
| sps_t *ps_sps = ps_codec->ps_sps_base + MAX_SPS_CNT - 1; |
| ihevcd_copy_sps(ps_codec, ps_sps->i1_sps_id, MAX_SPS_CNT - 1); |
| } |
| ps_codec->i4_error_code = ret; |
| |
| DEBUG_PRINT_NAL_INFO(ps_codec, s_nal.i1_nal_unit_type); |
| break; |
| |
| case NAL_PPS : |
| if(0 == ps_codec->i4_header_mode) |
| { |
| ps_codec->i4_header_in_slice_mode = 1; |
| if(ps_codec->i4_pps_done && |
| ps_codec->i4_pic_present) |
| break; |
| } |
| |
| ret = ihevcd_parse_pps(ps_codec); |
| if(ret == (IHEVCD_ERROR_T)IHEVCD_SUCCESS) |
| { |
| pps_t *ps_pps = ps_codec->ps_pps_base + MAX_PPS_CNT - 1; |
| ihevcd_copy_pps(ps_codec, ps_pps->i1_pps_id, MAX_PPS_CNT - 1); |
| } |
| ps_codec->i4_error_code = ret; |
| DEBUG_PRINT_NAL_INFO(ps_codec, s_nal.i1_nal_unit_type); |
| break; |
| |
| case NAL_PREFIX_SEI: |
| case NAL_SUFFIX_SEI: |
| if(IVD_DECODE_HEADER == ps_codec->i4_header_mode) |
| { |
| return IHEVCD_SLICE_IN_HEADER_MODE; |
| } |
| |
| ret = ihevcd_parse_sei(ps_codec, &s_nal); |
| break; |
| |
| case NAL_EOS : |
| ps_codec->i4_cra_as_first_pic = 1; |
| break; |
| |
| default: |
| DEBUG_PRINT_NAL_INFO(ps_codec, s_nal.i1_nal_unit_type); |
| break; |
| } |
| |
| return ret; |
| } |
| |