/****************************************************************************** * * 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; }