/*--------------------------------------------------------------------------
Copyright (c) 2010 - 2013, 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("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("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