| /*-------------------------------------------------------------------------- |
| Copyright (c) 2010 - 2017, The Linux Foundation. All rights reserved. |
| |
| Redistribution and use in source and binary forms, with or without |
| modification, are permitted provided that the following conditions are met: |
| * Redistributions of source code must retain the above copyright |
| notice, this list of conditions and the following disclaimer. |
| * Redistributions in binary form must reproduce the above copyright |
| notice, this list of conditions and the following disclaimer in the |
| documentation and/or other materials provided with the distribution. |
| * Neither the name of The Linux Foundation nor |
| the names of its contributors may be used to endorse or promote |
| products derived from this software without specific prior written |
| permission. |
| |
| THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
| AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
| NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
| CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
| EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
| PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; |
| OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, |
| WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR |
| OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF |
| ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| --------------------------------------------------------------------------*/ |
| /*======================================================================== |
| |
| O p e n M M |
| V i d e o U t i l i t i e s |
| |
| *//** @file VideoUtils.cpp |
| This module contains utilities and helper routines. |
| |
| @par EXTERNALIZED FUNCTIONS |
| |
| @par INITIALIZATION AND SEQUENCING REQUIREMENTS |
| (none) |
| |
| *//*====================================================================== */ |
| |
| /* ======================================================================= |
| |
| INCLUDE FILES FOR MODULE |
| |
| ========================================================================== */ |
| #include "h264_utils.h" |
| #include "extra_data_handler.h" |
| #include <string.h> |
| #include <stdlib.h> |
| #include <limits.h> |
| #include <sys/time.h> |
| #ifdef _ANDROID_ |
| #include <cutils/properties.h> |
| extern "C" { |
| #include<utils/Log.h> |
| } |
| |
| #endif |
| |
| /* ======================================================================= |
| |
| DEFINITIONS AND DECLARATIONS FOR MODULE |
| |
| This section contains definitions for constants, macros, types, variables |
| and other items needed by this module. |
| |
| ========================================================================== */ |
| |
| |
| #define MAX_SUPPORTED_LEVEL 32 |
| |
| RbspParser::RbspParser (const uint8 *_begin, const uint8 *_end) |
| : begin (_begin), end(_end), pos (- 1), bit (0), |
| cursor (0xFFFFFF), advanceNeeded (true) |
| { |
| } |
| |
| // Destructor |
| /*lint -e{1540} Pointer member neither freed nor zeroed by destructor |
| * No problem |
| */ |
| RbspParser::~RbspParser () {} |
| |
| // Return next RBSP byte as a word |
| uint32 RbspParser::next () |
| { |
| if (advanceNeeded) advance (); |
| //return static_cast<uint32> (*pos); |
| return static_cast<uint32> (begin[pos]); |
| } |
| |
| // Advance RBSP decoder to next byte |
| void RbspParser::advance () |
| { |
| ++pos; |
| //if (pos >= stop) |
| if (begin + pos == end) { |
| /*lint -e{730} Boolean argument to function |
| * I don't see a problem here |
| */ |
| //throw false; |
| ALOGV("H264Parser-->NEED TO THROW THE EXCEPTION..."); |
| } |
| cursor <<= 8; |
| //cursor |= static_cast<uint32> (*pos); |
| cursor |= static_cast<uint32> (begin[pos]); |
| if ((cursor & 0xFFFFFF) == 0x000003) { |
| advance (); |
| } |
| advanceNeeded = false; |
| } |
| |
| // Decode unsigned integer |
| uint32 RbspParser::u (uint32 n) |
| { |
| uint32 i, s, x = 0; |
| for (i = 0; i < n; i += s) { |
| s = static_cast<uint32>STD_MIN(static_cast<int>(8 - bit), |
| static_cast<int>(n - i)); |
| x <<= s; |
| |
| x |= ((next () >> ((8 - static_cast<uint32>(bit)) - s)) & |
| ((1 << s) - 1)); |
| |
| bit = (bit + s) % 8; |
| if (!bit) { |
| advanceNeeded = true; |
| } |
| } |
| return x; |
| } |
| |
| // Decode unsigned integer Exp-Golomb-coded syntax element |
| uint32 RbspParser::ue () |
| { |
| int leadingZeroBits = -1; |
| for (uint32 b = 0; !b; ++leadingZeroBits) { |
| b = u (1); |
| } |
| return ((1 << leadingZeroBits) - 1) + |
| u (static_cast<uint32>(leadingZeroBits)); |
| } |
| |
| // Decode signed integer Exp-Golomb-coded syntax element |
| int32 RbspParser::se () |
| { |
| const uint32 x = ue (); |
| if (!x) return 0; |
| else if (x & 1) return static_cast<int32> ((x >> 1) + 1); |
| else return - static_cast<int32> (x >> 1); |
| } |
| |
| void H264_Utils::allocate_rbsp_buffer(uint32 inputBufferSize) |
| { |
| m_rbspBytes = (byte *) calloc(1,inputBufferSize); |
| m_prv_nalu.nal_ref_idc = 0; |
| m_prv_nalu.nalu_type = NALU_TYPE_UNSPECIFIED; |
| } |
| |
| H264_Utils::H264_Utils(): m_height(0), |
| m_width(0), |
| m_rbspBytes(NULL), |
| m_au_data (false) |
| { |
| initialize_frame_checking_environment(); |
| } |
| |
| H264_Utils::~H264_Utils() |
| { |
| /* if(m_pbits) |
| { |
| delete(m_pbits); |
| m_pbits = NULL; |
| } |
| */ |
| if (m_rbspBytes) { |
| free(m_rbspBytes); |
| m_rbspBytes = NULL; |
| } |
| } |
| |
| /***********************************************************************/ |
| /* |
| FUNCTION: |
| H264_Utils::initialize_frame_checking_environment |
| |
| DESCRIPTION: |
| Extract RBSP data from a NAL |
| |
| INPUT/OUTPUT PARAMETERS: |
| None |
| |
| RETURN VALUE: |
| boolean |
| |
| SIDE EFFECTS: |
| None. |
| */ |
| /***********************************************************************/ |
| void H264_Utils::initialize_frame_checking_environment() |
| { |
| m_forceToStichNextNAL = false; |
| m_au_data = false; |
| m_prv_nalu.nal_ref_idc = 0; |
| m_prv_nalu.nalu_type = NALU_TYPE_UNSPECIFIED; |
| } |
| |
| /***********************************************************************/ |
| /* |
| FUNCTION: |
| H264_Utils::extract_rbsp |
| |
| DESCRIPTION: |
| Extract RBSP data from a NAL |
| |
| INPUT/OUTPUT PARAMETERS: |
| <In> |
| buffer : buffer containing start code or nal length + NAL units |
| buffer_length : the length of the NAL buffer |
| start_code : If true, start code is detected, |
| otherwise size nal length is detected |
| size_of_nal_length_field: size of nal length field |
| |
| <Out> |
| rbsp_bistream : extracted RBSP bistream |
| rbsp_length : the length of the RBSP bitstream |
| nal_unit : decoded NAL header information |
| |
| RETURN VALUE: |
| boolean |
| |
| SIDE EFFECTS: |
| None. |
| */ |
| /***********************************************************************/ |
| |
| boolean H264_Utils::extract_rbsp(OMX_IN OMX_U8 *buffer, |
| OMX_IN OMX_U32 buffer_length, |
| OMX_IN OMX_U32 size_of_nal_length_field, |
| OMX_OUT OMX_U8 *rbsp_bistream, |
| OMX_OUT OMX_U32 *rbsp_length, |
| OMX_OUT NALU *nal_unit) |
| { |
| byte coef1, coef2, coef3; |
| uint32 pos = 0; |
| uint32 nal_len = buffer_length; |
| uint32 sizeofNalLengthField = 0; |
| uint32 zero_count; |
| boolean eRet = true; |
| boolean start_code = (size_of_nal_length_field==0)?true:false; |
| |
| if (start_code) { |
| // Search start_code_prefix_one_3bytes (0x000001) |
| coef2 = buffer[pos++]; |
| coef3 = buffer[pos++]; |
| do { |
| if (pos >= buffer_length) { |
| ALOGE("ERROR: In %s() - line %d", __func__, __LINE__); |
| return false; |
| } |
| |
| coef1 = coef2; |
| coef2 = coef3; |
| coef3 = buffer[pos++]; |
| } while (coef1 || coef2 || coef3 != 1); |
| } else if (size_of_nal_length_field) { |
| /* This is the case to play multiple NAL units inside each access unit*/ |
| /* Extract the NAL length depending on sizeOfNALength field */ |
| sizeofNalLengthField = size_of_nal_length_field; |
| nal_len = 0; |
| while (size_of_nal_length_field--) { |
| nal_len |= buffer[pos++]<<(size_of_nal_length_field<<3); |
| } |
| if (nal_len >= buffer_length) { |
| ALOGE("ERROR: In %s() - line %d", __func__, __LINE__); |
| return false; |
| } |
| } |
| |
| if (nal_len > buffer_length) { |
| ALOGE("ERROR: In %s() - line %d", __func__, __LINE__); |
| return false; |
| } |
| if (pos + 1 > (nal_len + sizeofNalLengthField)) { |
| ALOGE("ERROR: In %s() - line %d", __func__, __LINE__); |
| return false; |
| } |
| if ((nal_unit->forbidden_zero_bit = (buffer[pos] & 0x80)) != 0) { |
| ALOGE("ERROR: In %s() - line %d", __func__, __LINE__); |
| } |
| nal_unit->nal_ref_idc = (buffer[pos] & 0x60) >> 5; |
| nal_unit->nalu_type = buffer[pos++] & 0x1f; |
| ALOGV("@#@# Pos = %x NalType = %x buflen = %d", |
| pos-1, nal_unit->nalu_type, buffer_length); |
| *rbsp_length = 0; |
| |
| |
| if ( nal_unit->nalu_type == NALU_TYPE_EOSEQ || |
| nal_unit->nalu_type == NALU_TYPE_EOSTREAM) |
| return (nal_len + sizeofNalLengthField); |
| |
| zero_count = 0; |
| while (pos < (nal_len+sizeofNalLengthField)) { //similar to for in p-42 |
| if ( zero_count == 2 ) { |
| if ( buffer[pos] == 0x03 ) { |
| pos ++; |
| zero_count = 0; |
| continue; |
| } |
| if ( buffer[pos] <= 0x01 ) { |
| if ( start_code ) { |
| *rbsp_length -= 2; |
| pos -= 2; |
| return pos; |
| } |
| } |
| zero_count = 0; |
| } |
| zero_count ++; |
| if ( buffer[pos] != 0 ) |
| zero_count = 0; |
| |
| rbsp_bistream[(*rbsp_length)++] = buffer[pos++]; |
| } |
| |
| return eRet; |
| } |
| |
| /*=========================================================================== |
| FUNCTION: |
| H264_Utils::iSNewFrame |
| |
| DESCRIPTION: |
| Returns true if NAL parsing successfull otherwise false. |
| |
| INPUT/OUTPUT PARAMETERS: |
| <In> |
| buffer : buffer containing start code or nal length + NAL units |
| buffer_length : the length of the NAL buffer |
| start_code : If true, start code is detected, |
| otherwise size nal length is detected |
| size_of_nal_length_field: size of nal length field |
| <out> |
| isNewFrame: true if the NAL belongs to a differenet frame |
| false if the NAL belongs to a current frame |
| |
| RETURN VALUE: |
| boolean true, if nal parsing is successful |
| false, if the nal parsing has errors |
| |
| SIDE EFFECTS: |
| None. |
| ===========================================================================*/ |
| bool H264_Utils::isNewFrame(OMX_BUFFERHEADERTYPE *p_buf_hdr, |
| OMX_IN OMX_U32 size_of_nal_length_field, |
| OMX_OUT OMX_BOOL &isNewFrame) |
| { |
| NALU nal_unit; |
| uint16 first_mb_in_slice = 0; |
| OMX_IN OMX_U32 numBytesInRBSP = 0; |
| OMX_IN OMX_U8 *buffer = p_buf_hdr->pBuffer; |
| OMX_IN OMX_U32 buffer_length = p_buf_hdr->nFilledLen; |
| bool eRet = true; |
| |
| ALOGV("isNewFrame: buffer %p buffer_length %d " |
| "size_of_nal_length_field %d", buffer, buffer_length, |
| size_of_nal_length_field); |
| |
| if ( false == extract_rbsp(buffer, buffer_length, size_of_nal_length_field, |
| m_rbspBytes, &numBytesInRBSP, &nal_unit) ) { |
| ALOGE("ERROR: In %s() - extract_rbsp() failed", __func__); |
| isNewFrame = OMX_FALSE; |
| eRet = false; |
| } else { |
| nalu_type = nal_unit.nalu_type; |
| switch (nal_unit.nalu_type) { |
| case NALU_TYPE_IDR: |
| case NALU_TYPE_NON_IDR: { |
| ALOGV("AU Boundary with NAL type %d ",nal_unit.nalu_type); |
| if (m_forceToStichNextNAL) { |
| isNewFrame = OMX_FALSE; |
| } else { |
| RbspParser rbsp_parser(m_rbspBytes, (m_rbspBytes+numBytesInRBSP)); |
| first_mb_in_slice = rbsp_parser.ue(); |
| |
| if ((!first_mb_in_slice) || /*(slice.prv_frame_num != slice.frame_num ) ||*/ |
| ( (m_prv_nalu.nal_ref_idc != nal_unit.nal_ref_idc) && ( nal_unit.nal_ref_idc * m_prv_nalu.nal_ref_idc == 0 ) ) || |
| /*( ((m_prv_nalu.nalu_type == NALU_TYPE_IDR) && (nal_unit.nalu_type == NALU_TYPE_IDR)) && (slice.idr_pic_id != slice.prv_idr_pic_id) ) || */ |
| ( (m_prv_nalu.nalu_type != nal_unit.nalu_type ) && ((m_prv_nalu.nalu_type == NALU_TYPE_IDR) || (nal_unit.nalu_type == NALU_TYPE_IDR)) ) ) { |
| //ALOGV("Found a New Frame due to NALU_TYPE_IDR/NALU_TYPE_NON_IDR"); |
| isNewFrame = OMX_TRUE; |
| } else { |
| isNewFrame = OMX_FALSE; |
| } |
| } |
| m_au_data = true; |
| m_forceToStichNextNAL = false; |
| break; |
| } |
| case NALU_TYPE_SPS: |
| case NALU_TYPE_PPS: |
| case NALU_TYPE_SEI: { |
| ALOGV("Non-AU boundary with NAL type %d", nal_unit.nalu_type); |
| if (m_au_data) { |
| isNewFrame = OMX_TRUE; |
| m_au_data = false; |
| } else { |
| isNewFrame = OMX_FALSE; |
| } |
| |
| m_forceToStichNextNAL = true; |
| break; |
| } |
| case NALU_TYPE_ACCESS_DELIM: |
| case NALU_TYPE_UNSPECIFIED: |
| case NALU_TYPE_EOSEQ: |
| case NALU_TYPE_EOSTREAM: |
| default: { |
| isNewFrame = OMX_FALSE; |
| // Do not update m_forceToStichNextNAL |
| break; |
| } |
| } // end of switch |
| } // end of if |
| m_prv_nalu = nal_unit; |
| ALOGV("get_h264_nal_type - newFrame value %d",isNewFrame); |
| return eRet; |
| } |
| |
| void perf_metrics::start() |
| { |
| if (!active) { |
| start_time = get_act_time(); |
| active = true; |
| } |
| } |
| |
| void perf_metrics::stop() |
| { |
| OMX_U64 stop_time = get_act_time(); |
| if (active) { |
| proc_time += (stop_time - start_time); |
| active = false; |
| } |
| } |
| |
| void perf_metrics::end(OMX_U32 units_cntr) |
| { |
| stop(); |
| ALOGV("--> Processing time : [%.2f] Sec", (float)proc_time / 1e6); |
| if (units_cntr) { |
| ALOGV("--> Avrg proc time : [%.2f] mSec", proc_time / (float)(units_cntr * 1e3)); |
| } |
| } |
| |
| void perf_metrics::reset() |
| { |
| start_time = 0; |
| proc_time = 0; |
| active = false; |
| } |
| |
| OMX_U64 perf_metrics::get_act_time() |
| { |
| struct timeval act_time = {0, 0}; |
| gettimeofday(&act_time, NULL); |
| return (act_time.tv_usec + act_time.tv_sec * 1e6); |
| } |
| |
| OMX_U64 perf_metrics::processing_time_us() |
| { |
| return proc_time; |
| } |
| |
| h264_stream_parser::h264_stream_parser() |
| { |
| reset(); |
| #ifdef PANSCAN_HDLR |
| panscan_hdl = new panscan_handler(); |
| if (!panscan_hdl) { |
| ALOGE("ERROR: Panscan hdl was not allocated!"); |
| } else if (!panscan_hdl->initialize(10)) { |
| ALOGE("ERROR: Allocating memory for panscan!"); |
| delete panscan_hdl; |
| panscan_hdl = NULL; |
| } |
| #else |
| memset(&panscan_param, 0, sizeof(panscan_param)); |
| panscan_param.rect_id = NO_PAN_SCAN_BIT; |
| #endif |
| } |
| |
| h264_stream_parser::~h264_stream_parser() |
| { |
| #ifdef PANSCAN_HDLR |
| if (panscan_hdl) { |
| delete panscan_hdl; |
| panscan_hdl = NULL; |
| } |
| #endif |
| } |
| |
| void h264_stream_parser::reset() |
| { |
| curr_32_bit = 0; |
| bits_read = 0; |
| zero_cntr = 0; |
| emulation_code_skip_cntr = 0; |
| emulation_sc_enabled = true; |
| bitstream = NULL; |
| bitstream_bytes = 0; |
| memset(&vui_param, 0, sizeof(vui_param)); |
| vui_param.fixed_fps_prev_ts = LLONG_MAX; |
| memset(&sei_buf_period, 0, sizeof(sei_buf_period)); |
| memset(&sei_pic_timing, 0, sizeof(sei_pic_timing)); |
| memset(&frame_packing_arrangement,0,sizeof(frame_packing_arrangement)); |
| frame_packing_arrangement.cancel_flag = 1; |
| mbaff_flag = 0; |
| } |
| |
| void h264_stream_parser::init_bitstream(OMX_U8* data, OMX_U32 size) |
| { |
| bitstream = data; |
| bitstream_bytes = size; |
| curr_32_bit = 0; |
| bits_read = 0; |
| zero_cntr = 0; |
| emulation_code_skip_cntr = 0; |
| } |
| |
| void h264_stream_parser::parse_vui(bool vui_in_extradata) |
| { |
| OMX_U32 value = 0; |
| ALOGV("parse_vui: IN"); |
| if (vui_in_extradata) |
| while (!extract_bits(1) && more_bits()); // Discard VUI enable flag |
| if (!more_bits()) |
| return; |
| |
| vui_param.aspect_ratio_info_present_flag = extract_bits(1); //aspect_ratio_info_present_flag |
| if (vui_param.aspect_ratio_info_present_flag) { |
| ALOGV("Aspect Ratio Info present!"); |
| aspect_ratio_info(); |
| } |
| |
| if (extract_bits(1)) //overscan_info_present_flag |
| extract_bits(1); //overscan_appropriate_flag |
| if (extract_bits(1)) { //video_signal_type_present_flag |
| extract_bits(3); //video_format |
| extract_bits(1); //video_full_range_flag |
| if (extract_bits(1)) { //colour_description_present_flag |
| extract_bits(8); //colour_primaries |
| extract_bits(8); //transfer_characteristics |
| extract_bits(8); //matrix_coefficients |
| } |
| } |
| if (extract_bits(1)) { //chroma_location_info_present_flag |
| uev(); //chroma_sample_loc_type_top_field |
| uev(); //chroma_sample_loc_type_bottom_field |
| } |
| vui_param.timing_info_present_flag = extract_bits(1); |
| if (vui_param.timing_info_present_flag) { |
| vui_param.num_units_in_tick = extract_bits(32); |
| vui_param.time_scale = extract_bits(32); |
| vui_param.fixed_frame_rate_flag = extract_bits(1); |
| ALOGV("Timing info present in VUI!"); |
| ALOGV(" num units in tick : %u", vui_param.num_units_in_tick); |
| ALOGV(" time scale : %u", vui_param.time_scale); |
| ALOGV(" fixed frame rate : %u", vui_param.fixed_frame_rate_flag); |
| } |
| vui_param.nal_hrd_parameters_present_flag = extract_bits(1); |
| if (vui_param.nal_hrd_parameters_present_flag) { |
| ALOGV("nal hrd params present!"); |
| hrd_parameters(&vui_param.nal_hrd_parameters); |
| } |
| vui_param.vcl_hrd_parameters_present_flag = extract_bits(1); |
| if (vui_param.vcl_hrd_parameters_present_flag) { |
| ALOGV("vcl hrd params present!"); |
| hrd_parameters(&vui_param.vcl_hrd_parameters); |
| } |
| if (vui_param.nal_hrd_parameters_present_flag || |
| vui_param.vcl_hrd_parameters_present_flag) |
| vui_param.low_delay_hrd_flag = extract_bits(1); |
| vui_param.pic_struct_present_flag = extract_bits(1); |
| ALOGV("pic_struct_present_flag : %u", vui_param.pic_struct_present_flag); |
| if (extract_bits(1)) { //bitstream_restriction_flag |
| extract_bits(1); //motion_vectors_over_pic_boundaries_flag |
| uev(); //max_bytes_per_pic_denom |
| uev(); //max_bits_per_mb_denom |
| uev(); //log2_max_mv_length_vertical |
| uev(); //log2_max_mv_length_horizontal |
| uev(); //num_reorder_frames |
| uev(); //max_dec_frame_buffering |
| } |
| ALOGV("parse_vui: OUT"); |
| } |
| |
| void h264_stream_parser::aspect_ratio_info() |
| { |
| ALOGV("aspect_ratio_info: IN"); |
| OMX_U32 aspect_ratio_idc = 0; |
| OMX_U32 aspect_ratio_x = 0; |
| OMX_U32 aspect_ratio_y = 0; |
| aspect_ratio_idc = extract_bits(8); //aspect_ratio_idc |
| switch (aspect_ratio_idc) { |
| case 1: |
| aspect_ratio_x = 1; |
| aspect_ratio_y = 1; |
| break; |
| case 2: |
| aspect_ratio_x = 12; |
| aspect_ratio_y = 11; |
| break; |
| case 3: |
| aspect_ratio_x = 10; |
| aspect_ratio_y = 11; |
| break; |
| case 4: |
| aspect_ratio_x = 16; |
| aspect_ratio_y = 11; |
| break; |
| case 5: |
| aspect_ratio_x = 40; |
| aspect_ratio_y = 33; |
| break; |
| case 6: |
| aspect_ratio_x = 24; |
| aspect_ratio_y = 11; |
| break; |
| case 7: |
| aspect_ratio_x = 20; |
| aspect_ratio_y = 11; |
| break; |
| case 8: |
| aspect_ratio_x = 32; |
| aspect_ratio_y = 11; |
| break; |
| case 9: |
| aspect_ratio_x = 80; |
| aspect_ratio_y = 33; |
| break; |
| case 10: |
| aspect_ratio_x = 18; |
| aspect_ratio_y = 11; |
| break; |
| case 11: |
| aspect_ratio_x = 15; |
| aspect_ratio_y = 11; |
| break; |
| case 12: |
| aspect_ratio_x = 64; |
| aspect_ratio_y = 33; |
| break; |
| case 13: |
| aspect_ratio_x = 160; |
| aspect_ratio_y = 99; |
| break; |
| case 14: |
| aspect_ratio_x = 4; |
| aspect_ratio_y = 3; |
| break; |
| case 15: |
| aspect_ratio_x = 3; |
| aspect_ratio_y = 2; |
| break; |
| case 16: |
| aspect_ratio_x = 2; |
| aspect_ratio_y = 1; |
| break; |
| case 255: |
| aspect_ratio_x = extract_bits(16); //sar_width |
| aspect_ratio_y = extract_bits(16); //sar_height |
| break; |
| default: |
| ALOGV("-->aspect_ratio_idc: Reserved Value "); |
| break; |
| } |
| ALOGV("-->aspect_ratio_idc : %u", aspect_ratio_idc); |
| ALOGV("-->aspect_ratio_x : %u", aspect_ratio_x); |
| ALOGV("-->aspect_ratio_y : %u", aspect_ratio_y); |
| vui_param.aspect_ratio_info.aspect_ratio_idc = aspect_ratio_idc; |
| vui_param.aspect_ratio_info.aspect_ratio_x = aspect_ratio_x; |
| vui_param.aspect_ratio_info.aspect_ratio_y = aspect_ratio_y; |
| ALOGV("aspect_ratio_info: OUT"); |
| } |
| |
| void h264_stream_parser::hrd_parameters(h264_hrd_param *hrd_param) |
| { |
| OMX_U32 idx; |
| ALOGV("hrd_parameters: IN"); |
| hrd_param->cpb_cnt = uev() + 1; |
| hrd_param->bit_rate_scale = extract_bits(4); |
| hrd_param->cpb_size_scale = extract_bits(4); |
| ALOGV("-->cpb_cnt : %u", hrd_param->cpb_cnt); |
| ALOGV("-->bit_rate_scale : %u", hrd_param->bit_rate_scale); |
| ALOGV("-->cpb_size_scale : %u", hrd_param->cpb_size_scale); |
| if (hrd_param->cpb_cnt > MAX_CPB_COUNT) { |
| ALOGV("ERROR: Invalid hrd_param->cpb_cnt [%u]!", hrd_param->cpb_cnt); |
| return; |
| } |
| for (idx = 0; idx < hrd_param->cpb_cnt && more_bits(); idx++) { |
| hrd_param->bit_rate_value[idx] = uev() + 1; |
| hrd_param->cpb_size_value[idx] = uev() + 1; |
| hrd_param->cbr_flag[idx] = extract_bits(1); |
| ALOGV("-->bit_rate_value [%d] : %u", idx, hrd_param->bit_rate_value[idx]); |
| ALOGV("-->cpb_size_value [%d] : %u", idx, hrd_param->cpb_size_value[idx]); |
| ALOGV("-->cbr_flag [%d] : %u", idx, hrd_param->cbr_flag[idx]); |
| } |
| hrd_param->initial_cpb_removal_delay_length = extract_bits(5) + 1; |
| hrd_param->cpb_removal_delay_length = extract_bits(5) + 1; |
| hrd_param->dpb_output_delay_length = extract_bits(5) + 1; |
| hrd_param->time_offset_length = extract_bits(5); |
| ALOGV("-->initial_cpb_removal_delay_length : %u", hrd_param->initial_cpb_removal_delay_length); |
| ALOGV("-->cpb_removal_delay_length : %u", hrd_param->cpb_removal_delay_length); |
| ALOGV("-->dpb_output_delay_length : %u", hrd_param->dpb_output_delay_length); |
| ALOGV("-->time_offset_length : %u", hrd_param->time_offset_length); |
| ALOGV("hrd_parameters: OUT"); |
| } |
| |
| void h264_stream_parser::parse_sei() |
| { |
| OMX_U32 value = 0, processed_bytes = 0; |
| OMX_U8 *sei_msg_start = bitstream; |
| OMX_U32 sei_unit_size = bitstream_bytes; |
| ALOGV("@@parse_sei: IN sei_unit_size(%u)", sei_unit_size); |
| while ((processed_bytes + 2) < sei_unit_size && more_bits()) { |
| init_bitstream(sei_msg_start + processed_bytes, sei_unit_size - processed_bytes); |
| ALOGV("-->NALU_TYPE_SEI"); |
| OMX_U32 payload_type = 0, payload_size = 0, aux = 0; |
| do { |
| value = extract_bits(8); |
| payload_type += value; |
| processed_bytes++; |
| } while (value == 0xFF); |
| ALOGV("-->payload_type : %u", payload_type); |
| do { |
| value = extract_bits(8); |
| payload_size += value; |
| processed_bytes++; |
| } while (value == 0xFF); |
| ALOGV("-->payload_size : %u", payload_size); |
| if (payload_size > 0) { |
| switch (payload_type) { |
| case BUFFERING_PERIOD: |
| sei_buffering_period(); |
| break; |
| case PIC_TIMING: |
| sei_picture_timing(); |
| break; |
| case PAN_SCAN_RECT: |
| sei_pan_scan(); |
| break; |
| case SEI_PAYLOAD_FRAME_PACKING_ARRANGEMENT: |
| parse_frame_pack(); |
| break; |
| default: |
| ALOGV("-->SEI payload type [%u] not implemented! size[%u]", payload_type, payload_size); |
| } |
| } |
| processed_bytes += (payload_size + emulation_code_skip_cntr); |
| ALOGV("-->SEI processed_bytes[%u]", processed_bytes); |
| } |
| ALOGV("@@parse_sei: OUT"); |
| } |
| |
| void h264_stream_parser::sei_buffering_period() |
| { |
| OMX_U32 idx; |
| OMX_U32 value = 0; |
| h264_hrd_param *hrd_param = NULL; |
| ALOGV("@@sei_buffering_period: IN"); |
| value = uev(); // seq_parameter_set_id |
| ALOGV("-->seq_parameter_set_id : %u", value); |
| if (value > 31) { |
| ALOGV("ERROR: Invalid seq_parameter_set_id [%u]!", value); |
| return; |
| } |
| sei_buf_period.is_valid = false; |
| if (vui_param.nal_hrd_parameters_present_flag) { |
| hrd_param = &vui_param.nal_hrd_parameters; |
| if (hrd_param->cpb_cnt > MAX_CPB_COUNT) { |
| ALOGV("ERROR: Invalid hrd_param->cpb_cnt [%u]!", hrd_param->cpb_cnt); |
| return; |
| } |
| for (idx = 0; idx < hrd_param->cpb_cnt ; idx++) { |
| sei_buf_period.is_valid = true; |
| sei_buf_period.initial_cpb_removal_delay[idx] = extract_bits(hrd_param->initial_cpb_removal_delay_length); |
| sei_buf_period.initial_cpb_removal_delay_offset[idx] = extract_bits(hrd_param->initial_cpb_removal_delay_length); |
| ALOGV("-->initial_cpb_removal_delay : %u", sei_buf_period.initial_cpb_removal_delay[idx]); |
| ALOGV("-->initial_cpb_removal_delay_offset : %u", sei_buf_period.initial_cpb_removal_delay_offset[idx]); |
| } |
| } |
| if (vui_param.vcl_hrd_parameters_present_flag) { |
| hrd_param = &vui_param.vcl_hrd_parameters; |
| if (hrd_param->cpb_cnt > MAX_CPB_COUNT) { |
| ALOGV("ERROR: Invalid hrd_param->cpb_cnt [%u]!", hrd_param->cpb_cnt); |
| return; |
| } |
| for (idx = 0; idx < hrd_param->cpb_cnt ; idx++) { |
| sei_buf_period.is_valid = true; |
| sei_buf_period.initial_cpb_removal_delay[idx] = extract_bits(hrd_param->initial_cpb_removal_delay_length); |
| sei_buf_period.initial_cpb_removal_delay_offset[idx] = extract_bits(hrd_param->initial_cpb_removal_delay_length); |
| ALOGV("-->initial_cpb_removal_delay : %u", sei_buf_period.initial_cpb_removal_delay[idx]); |
| ALOGV("-->initial_cpb_removal_delay_offset : %u", sei_buf_period.initial_cpb_removal_delay_offset[idx]); |
| } |
| } |
| sei_buf_period.au_cntr = 0; |
| ALOGV("@@sei_buffering_period: OUT"); |
| } |
| |
| void h264_stream_parser::sei_picture_timing() |
| { |
| ALOGV("@@sei_picture_timing: IN"); |
| OMX_U32 time_offset_len = 0, cpb_removal_len = 24, dpb_output_len = 24; |
| OMX_U8 cbr_flag = 0; |
| sei_pic_timing.is_valid = true; |
| if (vui_param.nal_hrd_parameters_present_flag) { |
| cpb_removal_len = vui_param.nal_hrd_parameters.cpb_removal_delay_length; |
| dpb_output_len = vui_param.nal_hrd_parameters.dpb_output_delay_length; |
| time_offset_len = vui_param.nal_hrd_parameters.time_offset_length; |
| cbr_flag = vui_param.nal_hrd_parameters.cbr_flag[0]; |
| } else if (vui_param.vcl_hrd_parameters_present_flag) { |
| cpb_removal_len = vui_param.vcl_hrd_parameters.cpb_removal_delay_length; |
| dpb_output_len = vui_param.vcl_hrd_parameters.dpb_output_delay_length; |
| time_offset_len = vui_param.vcl_hrd_parameters.time_offset_length; |
| cbr_flag = vui_param.vcl_hrd_parameters.cbr_flag[0]; |
| } |
| sei_pic_timing.cpb_removal_delay = extract_bits(cpb_removal_len); |
| sei_pic_timing.dpb_output_delay = extract_bits(dpb_output_len); |
| ALOGV("-->cpb_removal_len : %u", cpb_removal_len); |
| ALOGV("-->dpb_output_len : %u", dpb_output_len); |
| ALOGV("-->cpb_removal_delay : %u", sei_pic_timing.cpb_removal_delay); |
| ALOGV("-->dpb_output_delay : %u", sei_pic_timing.dpb_output_delay); |
| if (vui_param.pic_struct_present_flag) { |
| sei_pic_timing.pic_struct = extract_bits(4); |
| sei_pic_timing.num_clock_ts = 0; |
| switch (sei_pic_timing.pic_struct) { |
| case 0: |
| case 1: |
| case 2: |
| sei_pic_timing.num_clock_ts = 1; |
| break; |
| case 3: |
| case 4: |
| case 7: |
| sei_pic_timing.num_clock_ts = 2; |
| break; |
| case 5: |
| case 6: |
| case 8: |
| sei_pic_timing.num_clock_ts = 3; |
| break; |
| default: |
| ALOGE("sei_picture_timing: pic_struct invalid!"); |
| } |
| ALOGV("-->num_clock_ts : %u", sei_pic_timing.num_clock_ts); |
| for (OMX_U32 i = 0; i < sei_pic_timing.num_clock_ts && more_bits(); i++) { |
| sei_pic_timing.clock_ts_flag = extract_bits(1); |
| if (sei_pic_timing.clock_ts_flag) { |
| ALOGV("-->clock_timestamp present!"); |
| sei_pic_timing.ct_type = extract_bits(2); |
| sei_pic_timing.nuit_field_based_flag = extract_bits(1); |
| sei_pic_timing.counting_type = extract_bits(5); |
| sei_pic_timing.full_timestamp_flag = extract_bits(1); |
| sei_pic_timing.discontinuity_flag = extract_bits(1); |
| sei_pic_timing.cnt_dropped_flag = extract_bits(1); |
| sei_pic_timing.n_frames = extract_bits(8); |
| ALOGV("-->f_timestamp_flg : %u", sei_pic_timing.full_timestamp_flag); |
| ALOGV("-->n_frames : %u", sei_pic_timing.n_frames); |
| sei_pic_timing.seconds_value = 0; |
| sei_pic_timing.minutes_value = 0; |
| sei_pic_timing.hours_value = 0; |
| if (sei_pic_timing.full_timestamp_flag) { |
| sei_pic_timing.seconds_value = extract_bits(6); |
| sei_pic_timing.minutes_value = extract_bits(6); |
| sei_pic_timing.hours_value = extract_bits(5); |
| } else if (extract_bits(1)) { |
| ALOGV("-->seconds_flag enabled!"); |
| sei_pic_timing.seconds_value = extract_bits(6); |
| if (extract_bits(1)) { |
| ALOGV("-->minutes_flag enabled!"); |
| sei_pic_timing.minutes_value = extract_bits(6); |
| if (extract_bits(1)) { |
| ALOGV("-->hours_flag enabled!"); |
| sei_pic_timing.hours_value = extract_bits(5); |
| } |
| } |
| } |
| sei_pic_timing.time_offset = 0; |
| if (time_offset_len > 0) |
| sei_pic_timing.time_offset = iv(time_offset_len); |
| ALOGV("-->seconds_value : %u", sei_pic_timing.seconds_value); |
| ALOGV("-->minutes_value : %u", sei_pic_timing.minutes_value); |
| ALOGV("-->hours_value : %u", sei_pic_timing.hours_value); |
| ALOGV("-->time_offset : %d", sei_pic_timing.time_offset); |
| } |
| } |
| } |
| ALOGV("@@sei_picture_timing: OUT"); |
| } |
| |
| void h264_stream_parser::sei_pan_scan() |
| { |
| #ifdef _ANDROID_ |
| char property_value[PROPERTY_VALUE_MAX] = {0}; |
| OMX_S32 enable_panscan_log = 0; |
| property_get("vendor.vidc.dec.debug.panframedata", property_value, "0"); |
| enable_panscan_log = atoi(property_value); |
| #endif |
| #ifdef PANSCAN_HDLR |
| h264_pan_scan *pan_scan_param = panscan_hdl->get_free(); |
| #else |
| h264_pan_scan *pan_scan_param = &panscan_param; |
| #endif |
| |
| if (!pan_scan_param) { |
| ALOGE("sei_pan_scan: ERROR: Invalid pointer!"); |
| return; |
| } |
| |
| pan_scan_param->rect_id = uev(); |
| if (pan_scan_param->rect_id > 0xFF) { |
| ALOGE("sei_pan_scan: ERROR: Invalid rect_id[%u]!", (unsigned int)pan_scan_param->rect_id); |
| pan_scan_param->rect_id = NO_PAN_SCAN_BIT; |
| return; |
| } |
| |
| pan_scan_param->rect_cancel_flag = extract_bits(1); |
| |
| if (pan_scan_param->rect_cancel_flag) |
| pan_scan_param->rect_id = NO_PAN_SCAN_BIT; |
| else { |
| pan_scan_param->cnt = uev() + 1; |
| if (pan_scan_param->cnt > MAX_PAN_SCAN_RECT) { |
| ALOGE("sei_pan_scan: ERROR: Invalid num of rect [%u]!", (unsigned int)pan_scan_param->cnt); |
| pan_scan_param->rect_id = NO_PAN_SCAN_BIT; |
| return; |
| } |
| |
| for (OMX_U32 i = 0; i < pan_scan_param->cnt; i++) { |
| pan_scan_param->rect_left_offset[i] = sev(); |
| pan_scan_param->rect_right_offset[i] = sev(); |
| pan_scan_param->rect_top_offset[i] = sev(); |
| pan_scan_param->rect_bottom_offset[i] = sev(); |
| |
| } |
| pan_scan_param->rect_repetition_period = uev(); |
| #ifdef PANSCAN_HDLR |
| if (pan_scan_param->rect_repetition_period > 1) |
| // Repetition period is decreased by 2 each time panscan data is used |
| pan_scan_param->rect_repetition_period *= 2; |
| #endif |
| #ifdef _ANDROID_ |
| if (enable_panscan_log) { |
| print_pan_data(pan_scan_param); |
| } |
| #endif |
| } |
| } |
| |
| void h264_stream_parser::print_pan_data(h264_pan_scan *pan_scan_param) |
| { |
| ALOGE("@@print_pan_data: IN"); |
| |
| ALOGE("-->rect_id : %u", (unsigned int)pan_scan_param->rect_id); |
| ALOGE("-->rect_cancel_flag : %u", pan_scan_param->rect_cancel_flag); |
| |
| ALOGE("-->cnt : %u", (unsigned int)pan_scan_param->cnt); |
| |
| for (OMX_U32 i = 0; i < pan_scan_param->cnt; i++) { |
| ALOGE("-->rect_left_offset : %d", (int)pan_scan_param->rect_left_offset[i]); |
| ALOGE("-->rect_right_offset : %d", (int)pan_scan_param->rect_right_offset[i]); |
| ALOGE("-->rect_top_offset : %d", (int)pan_scan_param->rect_top_offset[i]); |
| ALOGE("-->rect_bottom_offset : %d", (int)pan_scan_param->rect_bottom_offset[i]); |
| } |
| ALOGE("-->repetition_period : %u", (unsigned int)pan_scan_param->rect_repetition_period); |
| |
| ALOGE("@@print_pan_data: OUT"); |
| } |
| |
| void h264_stream_parser::parse_sps() |
| { |
| OMX_U32 value = 0, scaling_matrix_limit; |
| ALOGV("@@parse_sps: IN"); |
| value = extract_bits(8); //profile_idc |
| profile = value; |
| extract_bits(8); //constraint flags and reserved bits |
| extract_bits(8); //level_idc |
| uev(); //sps id |
| if (value == 100 || value == 110 || value == 122 || value == 244 || |
| value == 44 || value == 83 || value == 86 || value == 118) { |
| if (uev() == 3) { //chroma_format_idc |
| extract_bits(1); //separate_colour_plane_flag |
| scaling_matrix_limit = 12; |
| } else |
| scaling_matrix_limit = 12; |
| uev(); //bit_depth_luma_minus8 |
| uev(); //bit_depth_chroma_minus8 |
| extract_bits(1); //qpprime_y_zero_transform_bypass_flag |
| if (extract_bits(1)) { //seq_scaling_matrix_present_flag |
| for (unsigned int i = 0; i < scaling_matrix_limit && more_bits(); i++) { |
| if (extract_bits(1)) { ////seq_scaling_list_present_flag[ i ] |
| if (i < 6) |
| scaling_list(16); |
| else |
| scaling_list(64); |
| } |
| } |
| } |
| } |
| uev(); //log2_max_frame_num_minus4 |
| value = uev(); //pic_order_cnt_type |
| if (value == 0) |
| uev(); //log2_max_pic_order_cnt_lsb_minus4 |
| else if (value == 1) { |
| extract_bits(1); //delta_pic_order_always_zero_flag |
| sev(); //offset_for_non_ref_pic |
| sev(); //offset_for_top_to_bottom_field |
| value = uev(); // num_ref_frames_in_pic_order_cnt_cycle |
| for (unsigned int i = 0; i < value; i++) |
| sev(); //offset_for_ref_frame[ i ] |
| } |
| uev(); //max_num_ref_frames |
| extract_bits(1); //gaps_in_frame_num_value_allowed_flag |
| value = uev(); //pic_width_in_mbs_minus1 |
| value = uev(); //pic_height_in_map_units_minus1 |
| if (!extract_bits(1)) //frame_mbs_only_flag |
| mbaff_flag = extract_bits(1); //mb_adaptive_frame_field_flag |
| extract_bits(1); //direct_8x8_inference_flag |
| if (extract_bits(1)) { //frame_cropping_flag |
| uev(); //frame_crop_left_offset |
| uev(); //frame_crop_right_offset |
| uev(); //frame_crop_top_offset |
| uev(); //frame_crop_bottom_offset |
| } |
| if (extract_bits(1)) //vui_parameters_present_flag |
| parse_vui(false); |
| ALOGV("@@parse_sps: OUT"); |
| } |
| |
| void h264_stream_parser::scaling_list(OMX_U32 size_of_scaling_list) |
| { |
| OMX_S32 last_scale = 8, next_scale = 8, delta_scale; |
| for (unsigned int j = 0; j < size_of_scaling_list; j++) { |
| if (next_scale != 0) { |
| delta_scale = sev(); |
| next_scale = (last_scale + delta_scale + 256) % 256; |
| } |
| last_scale = (next_scale == 0)? last_scale : next_scale; |
| } |
| } |
| |
| OMX_U32 h264_stream_parser::extract_bits(OMX_U32 n) |
| { |
| OMX_U32 value = 0; |
| if (n > 32) { |
| ALOGE("ERROR: extract_bits limit to 32 bits!"); |
| return value; |
| } |
| value = curr_32_bit >> (32 - n); |
| if (bits_read < n) { |
| n -= bits_read; |
| read_word(); |
| value |= (curr_32_bit >> (32 - n)); |
| if (bits_read < n) { |
| ALOGV("ERROR: extract_bits underflow!"); |
| value >>= (n - bits_read); |
| n = bits_read; |
| } |
| } |
| bits_read -= n; |
| curr_32_bit <<= n; |
| return value; |
| } |
| |
| void h264_stream_parser::read_word() |
| { |
| curr_32_bit = 0; |
| bits_read = 0; |
| while (bitstream_bytes && bits_read < 32) { |
| if (*bitstream == EMULATION_PREVENTION_THREE_BYTE && |
| zero_cntr >= 2 && emulation_sc_enabled) { |
| ALOGV("EMULATION_PREVENTION_THREE_BYTE: Skip 0x03 byte aligned!"); |
| emulation_code_skip_cntr++; |
| } else { |
| curr_32_bit <<= 8; |
| curr_32_bit |= *bitstream; |
| bits_read += 8; |
| } |
| if (*bitstream == 0) |
| zero_cntr++; |
| else |
| zero_cntr = 0; |
| bitstream++; |
| bitstream_bytes--; |
| } |
| curr_32_bit <<= (32 - bits_read); |
| } |
| |
| OMX_U32 h264_stream_parser::uev() |
| { |
| OMX_U32 lead_zero_bits = 0, code_num = 0; |
| while (!extract_bits(1) && more_bits()) |
| lead_zero_bits++; |
| code_num = lead_zero_bits == 0 ? 0 : |
| (1 << lead_zero_bits) - 1 + extract_bits(lead_zero_bits); |
| return code_num; |
| } |
| |
| bool h264_stream_parser::more_bits() |
| { |
| return (bitstream_bytes > 0 || bits_read > 0); |
| } |
| |
| OMX_S32 h264_stream_parser::sev() |
| { |
| OMX_U32 code_num = uev(); |
| OMX_S32 ret; |
| ret = (code_num + 1) >> 1; |
| return ((code_num & 1) ? ret : -ret); |
| } |
| |
| OMX_S32 h264_stream_parser::iv(OMX_U32 n_bits) |
| { |
| OMX_U32 code_num = extract_bits(n_bits); |
| OMX_S32 ret = (code_num >> (n_bits - 1))? (-1)*(~(code_num & ~(0x1 << (n_bits - 1))) + 1) : code_num; |
| return ret; |
| } |
| |
| OMX_U32 h264_stream_parser::get_nal_unit_type(OMX_U32 *nal_unit_type) |
| { |
| OMX_U32 value = 0, consumed_bytes = 3; |
| *nal_unit_type = NALU_TYPE_UNSPECIFIED; |
| ALOGV("-->get_nal_unit_type: IN"); |
| value = extract_bits(24); |
| while (value != 0x00000001 && more_bits()) { |
| value <<= 8; |
| value |= extract_bits(8); |
| consumed_bytes++; |
| } |
| if (value != 0x00000001) { |
| ALOGE("ERROR in get_nal_unit_type: Start code not found!"); |
| } else { |
| if (extract_bits(1)) { // forbidden_zero_bit |
| ALOGE("WARNING: forbidden_zero_bit should be zero!"); |
| } |
| value = extract_bits(2); |
| ALOGV("-->nal_ref_idc : %x", value); |
| *nal_unit_type = extract_bits(5); |
| ALOGV("-->nal_unit_type : %x", *nal_unit_type); |
| consumed_bytes++; |
| if (consumed_bytes > 5) { |
| ALOGE("-->WARNING: Startcode was found after the first 4 bytes!"); |
| } |
| } |
| ALOGV("-->get_nal_unit_type: OUT"); |
| return consumed_bytes; |
| } |
| |
| OMX_U32 h264_stream_parser::get_profile() |
| { |
| return profile; |
| } |
| |
| OMX_S64 h264_stream_parser::calculate_buf_period_ts(OMX_S64 timestamp) |
| { |
| OMX_S64 clock_ts = timestamp; |
| ALOGV("calculate_ts(): IN"); |
| if (sei_buf_period.au_cntr == 0) |
| clock_ts = sei_buf_period.reference_ts = timestamp; |
| else if (sei_pic_timing.is_valid && VALID_TS(sei_buf_period.reference_ts)) { |
| clock_ts = sei_buf_period.reference_ts + sei_pic_timing.cpb_removal_delay * |
| 1e6 * vui_param.num_units_in_tick / vui_param.time_scale; |
| } |
| sei_buf_period.au_cntr++; |
| ALOGV("calculate_ts(): OUT"); |
| return clock_ts; |
| } |
| |
| OMX_S64 h264_stream_parser::calculate_fixed_fps_ts(OMX_S64 timestamp, OMX_U32 DeltaTfiDivisor) |
| { |
| if (VALID_TS(timestamp)) |
| vui_param.fixed_fps_prev_ts = timestamp; |
| else if (VALID_TS(vui_param.fixed_fps_prev_ts)) |
| vui_param.fixed_fps_prev_ts += DeltaTfiDivisor * 1e6 * |
| vui_param.num_units_in_tick / vui_param.time_scale; |
| return vui_param.fixed_fps_prev_ts; |
| } |
| |
| void h264_stream_parser::parse_frame_pack() |
| { |
| #ifdef _ANDROID_ |
| char property_value[PROPERTY_VALUE_MAX] = {0}; |
| OMX_S32 enable_framepack_log = 0; |
| |
| property_get("vendor.vidc.dec.debug.panframedata", property_value, "0"); |
| enable_framepack_log = atoi(property_value); |
| #endif |
| ALOGV("%s:%d parse_frame_pack", __func__, __LINE__); |
| |
| frame_packing_arrangement.id = uev(); |
| |
| frame_packing_arrangement.cancel_flag = extract_bits(1); |
| if (!frame_packing_arrangement.cancel_flag) { |
| frame_packing_arrangement.type = extract_bits(7); |
| frame_packing_arrangement.quincunx_sampling_flag = extract_bits(1); |
| frame_packing_arrangement.content_interpretation_type = extract_bits(6); |
| frame_packing_arrangement.spatial_flipping_flag = extract_bits(1); |
| frame_packing_arrangement.frame0_flipped_flag = extract_bits(1); |
| frame_packing_arrangement.field_views_flag = extract_bits(1); |
| frame_packing_arrangement.current_frame_is_frame0_flag = extract_bits(1); |
| frame_packing_arrangement.frame0_self_contained_flag = extract_bits(1); |
| frame_packing_arrangement.frame1_self_contained_flag = extract_bits(1); |
| |
| if (!frame_packing_arrangement.quincunx_sampling_flag && |
| frame_packing_arrangement.type != 5) { |
| frame_packing_arrangement.frame0_grid_position_x = extract_bits(4); |
| frame_packing_arrangement.frame0_grid_position_y = extract_bits(4); |
| frame_packing_arrangement.frame1_grid_position_x = extract_bits(4); |
| frame_packing_arrangement.frame1_grid_position_y = extract_bits(4); |
| } |
| frame_packing_arrangement.reserved_byte = extract_bits(8); |
| frame_packing_arrangement.repetition_period = uev(); |
| } |
| frame_packing_arrangement.extension_flag = extract_bits(1); |
| |
| #ifdef _ANDROID_ |
| if (enable_framepack_log) { |
| print_frame_pack(); |
| } |
| #endif |
| } |
| |
| void h264_stream_parser::print_frame_pack() |
| { |
| ALOGV("## frame_packing_arrangement.id = %u", frame_packing_arrangement.id); |
| ALOGV("## frame_packing_arrangement.cancel_flag = %u", |
| frame_packing_arrangement.cancel_flag); |
| if (!frame_packing_arrangement.cancel_flag) { |
| ALOGV("## frame_packing_arrangement.type = %u", |
| frame_packing_arrangement.type); |
| ALOGV("## frame_packing_arrangement.quincunx_sampling_flag = %u", |
| frame_packing_arrangement.quincunx_sampling_flag); |
| ALOGV("## frame_packing_arrangement.content_interpretation_type = %u", |
| frame_packing_arrangement.content_interpretation_type); |
| ALOGV("## frame_packing_arrangement.spatial_flipping_flag = %u", |
| frame_packing_arrangement.spatial_flipping_flag); |
| ALOGV("## frame_packing_arrangement.frame0_flipped_flag = %u", |
| frame_packing_arrangement.frame0_flipped_flag); |
| ALOGV("## frame_packing_arrangement.field_views_flag = %u", |
| frame_packing_arrangement.field_views_flag); |
| ALOGV("## frame_packing_arrangement.current_frame_is_frame0_flag = %u", |
| frame_packing_arrangement.current_frame_is_frame0_flag); |
| ALOGV("## frame_packing_arrangement.frame0_self_contained_flag = %u", |
| frame_packing_arrangement.frame0_self_contained_flag); |
| ALOGV("## frame_packing_arrangement.frame1_self_contained_flag = %u", |
| frame_packing_arrangement.frame1_self_contained_flag); |
| ALOGV("## frame_packing_arrangement.reserved_byte = %u", |
| frame_packing_arrangement.reserved_byte); |
| ALOGV("## frame_packing_arrangement.repetition_period = %u", |
| frame_packing_arrangement.repetition_period); |
| ALOGV("## frame_packing_arrangement.extension_flag = %u", |
| frame_packing_arrangement.extension_flag); |
| } |
| } |
| /* API'S EXPOSED TO OMX COMPONENT */ |
| |
| void h264_stream_parser::get_frame_pack_data( |
| OMX_QCOM_FRAME_PACK_ARRANGEMENT *frame_pack) |
| { |
| ALOGV("%s:%d get frame data", __func__, __LINE__); |
| memcpy(&frame_pack->id,&frame_packing_arrangement.id, |
| FRAME_PACK_SIZE*sizeof(OMX_U32)); |
| return; |
| } |
| |
| |
| bool h264_stream_parser::is_mbaff() |
| { |
| ALOGV("%s:%d MBAFF flag=%d", __func__, __LINE__,mbaff_flag); |
| return mbaff_flag; |
| } |
| |
| void h264_stream_parser::get_frame_rate(OMX_U32 *frame_rate) |
| { |
| if (vui_param.num_units_in_tick != 0) |
| *frame_rate = vui_param.time_scale / (2 * vui_param.num_units_in_tick); |
| } |
| |
| void h264_stream_parser::parse_nal(OMX_U8* data_ptr, OMX_U32 data_len, OMX_U32 nal_type, bool enable_emu_sc) |
| { |
| OMX_U32 nal_unit_type = NALU_TYPE_UNSPECIFIED, cons_bytes = 0; |
| ALOGV("parse_nal(): IN nal_type(%u)", nal_type); |
| if (!data_len) |
| return; |
| init_bitstream(data_ptr, data_len); |
| emulation_sc_enabled = enable_emu_sc; |
| if (nal_type != NALU_TYPE_VUI) { |
| cons_bytes = get_nal_unit_type(&nal_unit_type); |
| if (nal_type != nal_unit_type && nal_type != NALU_TYPE_UNSPECIFIED) { |
| ALOGV("Unexpected nal_type(%x) expected(%x)", nal_unit_type, nal_type); |
| return; |
| } |
| } |
| switch (nal_type) { |
| case NALU_TYPE_SPS: |
| if (more_bits()) |
| parse_sps(); |
| #ifdef PANSCAN_HDLR |
| panscan_hdl->get_free(); |
| #endif |
| break; |
| case NALU_TYPE_SEI: |
| init_bitstream(data_ptr + cons_bytes, data_len - cons_bytes); |
| parse_sei(); |
| break; |
| case NALU_TYPE_VUI: |
| parse_vui(true); |
| break; |
| default: |
| ALOGV("nal_unit_type received : %u", nal_type); |
| } |
| ALOGV("parse_nal(): OUT"); |
| } |
| |
| #ifdef PANSCAN_HDLR |
| void h264_stream_parser::update_panscan_data(OMX_S64 timestamp) |
| { |
| panscan_hdl->update_last(timestamp); |
| } |
| #endif |
| |
| void h264_stream_parser::fill_aspect_ratio_info(OMX_QCOM_ASPECT_RATIO *dest_aspect_ratio) |
| { |
| if (dest_aspect_ratio && vui_param.aspect_ratio_info_present_flag) { |
| dest_aspect_ratio->aspectRatioX = vui_param.aspect_ratio_info.aspect_ratio_x; |
| dest_aspect_ratio->aspectRatioY = vui_param.aspect_ratio_info.aspect_ratio_y; |
| } |
| } |
| |
| void h264_stream_parser::fill_pan_scan_data(OMX_QCOM_PANSCAN *dest_pan_scan, OMX_S64 timestamp) |
| { |
| #ifdef PANSCAN_HDLR |
| h264_pan_scan *pan_scan_param = panscan_hdl->get_populated(timestamp); |
| #else |
| h264_pan_scan *pan_scan_param = &panscan_param; |
| #endif |
| if (pan_scan_param) { |
| if (!(pan_scan_param->rect_id & NO_PAN_SCAN_BIT)) { |
| PRINT_PANSCAN_PARAM(*pan_scan_param); |
| dest_pan_scan->numWindows = pan_scan_param->cnt; |
| for (unsigned int i = 0; i < dest_pan_scan->numWindows; i++) { |
| dest_pan_scan->window[i].x = pan_scan_param->rect_left_offset[i]; |
| dest_pan_scan->window[i].y = pan_scan_param->rect_top_offset[i]; |
| dest_pan_scan->window[i].dx = pan_scan_param->rect_right_offset[i]; |
| dest_pan_scan->window[i].dy = pan_scan_param->rect_bottom_offset[i]; |
| } |
| #ifndef PANSCAN_HDLR |
| if (pan_scan_param->rect_repetition_period == 0) |
| pan_scan_param->rect_id = NO_PAN_SCAN_BIT; |
| else if (pan_scan_param->rect_repetition_period > 1) |
| pan_scan_param->rect_repetition_period = |
| (pan_scan_param->rect_repetition_period == 2)? 0 : |
| (pan_scan_param->rect_repetition_period - 1); |
| #endif |
| } else |
| pan_scan_param->rect_repetition_period = 0; |
| } |
| } |
| |
| OMX_S64 h264_stream_parser::process_ts_with_sei_vui(OMX_S64 timestamp) |
| { |
| bool clock_ts_flag = false; |
| OMX_S64 clock_ts = timestamp; |
| OMX_U32 deltaTfiDivisor = 2; |
| if (vui_param.timing_info_present_flag) { |
| if (vui_param.pic_struct_present_flag) { |
| if (sei_pic_timing.clock_ts_flag) { |
| clock_ts = ((sei_pic_timing.hours_value * 60 + sei_pic_timing.minutes_value) * 60 + sei_pic_timing.seconds_value) * 1e6 + |
| (sei_pic_timing.n_frames * (vui_param.num_units_in_tick * (1 + sei_pic_timing.nuit_field_based_flag)) + sei_pic_timing.time_offset) * |
| 1e6 / vui_param.time_scale; |
| ALOGV("-->CLOCK TIMESTAMP : %lld", clock_ts); |
| clock_ts_flag = true; |
| } |
| if (vui_param.fixed_frame_rate_flag) { |
| switch (sei_pic_timing.pic_struct) { |
| case 1: |
| case 2: |
| deltaTfiDivisor = 1; |
| break; |
| case 0: |
| case 3: |
| case 4: |
| deltaTfiDivisor = 2; |
| break; |
| case 5: |
| case 6: |
| deltaTfiDivisor = 3; |
| break; |
| case 7: |
| deltaTfiDivisor = 4; |
| break; |
| case 8: |
| deltaTfiDivisor = 6; |
| break; |
| default: |
| ALOGE("process_ts_with_sei_vui: pic_struct invalid!"); |
| } |
| } |
| } |
| if (!clock_ts_flag) { |
| if (vui_param.fixed_frame_rate_flag) |
| clock_ts = calculate_fixed_fps_ts(timestamp, deltaTfiDivisor); |
| else if (sei_buf_period.is_valid) |
| clock_ts = calculate_buf_period_ts(timestamp); |
| } |
| } else { |
| ALOGV("NO TIMING information present in VUI!"); |
| } |
| sei_pic_timing.is_valid = false; // SEI data is valid only for current frame |
| return clock_ts; |
| } |
| |
| #ifdef PANSCAN_HDLR |
| |
| panscan_handler::panscan_handler() : panscan_data(NULL) {} |
| |
| panscan_handler::~panscan_handler() |
| { |
| if (panscan_data) { |
| free(panscan_data); |
| panscan_data = NULL; |
| } |
| } |
| |
| bool panscan_handler::initialize(int num_data) |
| { |
| bool ret = false; |
| if (!panscan_data) { |
| panscan_data = (PANSCAN_NODE *) malloc (sizeof(PANSCAN_NODE) * num_data); |
| if (panscan_data) { |
| panscan_free.add_multiple(panscan_data, num_data); |
| ret = true; |
| } |
| } else { |
| ALOGE("ERROR: Old panscan memory must be freed to allocate new"); |
| } |
| return ret; |
| } |
| |
| h264_pan_scan *panscan_handler::get_free() |
| { |
| h264_pan_scan *data = NULL; |
| PANSCAN_NODE *panscan_node = panscan_used.watch_last(); |
| panscan_node = (!panscan_node || VALID_TS(panscan_node->start_ts))? |
| panscan_free.remove_first() : |
| panscan_used.remove_last(); |
| if (panscan_node) { |
| panscan_node->start_ts = LLONG_MAX; |
| panscan_node->end_ts = LLONG_MAX; |
| panscan_node->pan_scan_param.rect_id = NO_PAN_SCAN_BIT; |
| panscan_node->active = false; |
| panscan_used.add_last(panscan_node); |
| data = &panscan_node->pan_scan_param; |
| } |
| return data; |
| } |
| |
| h264_pan_scan *panscan_handler::get_populated(OMX_S64 frame_ts) |
| { |
| h264_pan_scan *data = NULL; |
| PANSCAN_NODE *panscan_node = panscan_used.watch_first(); |
| while (panscan_node && !data) { |
| if (VALID_TS(panscan_node->start_ts)) { |
| if (panscan_node->active && frame_ts < panscan_node->start_ts) |
| panscan_node->start_ts = frame_ts; |
| if (frame_ts >= panscan_node->start_ts) |
| if (frame_ts < panscan_node->end_ts) { |
| data = &panscan_node->pan_scan_param; |
| panscan_node->active = true; |
| } else { |
| panscan_free.add_last(panscan_used.remove_first()); |
| panscan_node = panscan_used.watch_first(); |
| } |
| else |
| // Finish search if current timestamp has not reached |
| // start timestamp of first panscan data. |
| panscan_node = NULL; |
| } else { |
| // Only one panscan data is stored for clips |
| // with invalid timestamps in every frame |
| data = &panscan_node->pan_scan_param; |
| panscan_node->active = true; |
| } |
| } |
| if (data) { |
| if (data->rect_repetition_period == 0) |
| panscan_free.add_last(panscan_used.remove_first()); |
| else if (data->rect_repetition_period > 1) |
| data->rect_repetition_period -= 2; |
| } |
| PRINT_PANSCAN_DATA(panscan_node); |
| return data; |
| } |
| |
| void panscan_handler::update_last(OMX_S64 frame_ts) |
| { |
| PANSCAN_NODE *panscan_node = panscan_used.watch_last(); |
| if (panscan_node && !VALID_TS(panscan_node->start_ts)) { |
| panscan_node->start_ts = frame_ts; |
| PRINT_PANSCAN_DATA(panscan_node); |
| if (panscan_node->prev) { |
| if (frame_ts < panscan_node->prev->end_ts) |
| panscan_node->prev->end_ts = frame_ts; |
| else if (!VALID_TS(frame_ts)) |
| panscan_node->prev->pan_scan_param.rect_repetition_period = 0; |
| PRINT_PANSCAN_DATA(panscan_node->prev); |
| } |
| } |
| } |
| |
| template <class NODE_STRUCT> |
| void omx_dl_list<NODE_STRUCT>::add_multiple(NODE_STRUCT *data_arr, int data_num) |
| { |
| for (int idx = 0; idx < data_num; idx++) |
| add_last(&data_arr[idx]); |
| } |
| |
| template <class NODE_STRUCT> |
| NODE_STRUCT *omx_dl_list<NODE_STRUCT>::remove_first() |
| { |
| NODE_STRUCT *data = head; |
| if (head) { |
| if (head->next) { |
| head = head->next; |
| head->prev = NULL; |
| } else |
| head = tail = NULL; |
| data->next = data->prev = NULL; |
| } |
| return data; |
| } |
| |
| template <class NODE_STRUCT> |
| NODE_STRUCT *omx_dl_list<NODE_STRUCT>::remove_last() |
| { |
| NODE_STRUCT *data = tail; |
| if (tail) { |
| if (tail->prev) { |
| tail = tail->prev; |
| tail->next = NULL; |
| } else |
| head = tail = NULL; |
| data->next = data->prev = NULL; |
| } |
| return data; |
| } |
| |
| template <class NODE_STRUCT> |
| void omx_dl_list<NODE_STRUCT>::add_last(NODE_STRUCT* data_ptr) |
| { |
| if (data_ptr) { |
| data_ptr->next = NULL; |
| data_ptr->prev = tail; |
| if (tail) { |
| tail->next = data_ptr; |
| tail = data_ptr; |
| } else |
| head = tail = data_ptr; |
| } |
| } |
| |
| template <class NODE_STRUCT> |
| NODE_STRUCT* omx_dl_list<NODE_STRUCT>::watch_first() |
| { |
| return head; |
| } |
| |
| template <class NODE_STRUCT> |
| NODE_STRUCT* omx_dl_list<NODE_STRUCT>::watch_last() |
| { |
| return tail; |
| } |
| |
| #endif |