github项目地址:https://github.com/VioletDream-SXZ/H.264AnalysisProject
最好使用linux的,我不知道window那个能不能用。
我们先看一下《新一代视频压缩编码标准--H.264/AVC》第七章里面句法表。(虽然我表示这个表并不全)
先理解一下后面那个u,ue,se是什么意思先。
u(v) 读进连续的v个比特,并将它们解释为无符号整数
ue(v) 无符号指数哥伦布熵编码。
se(v) 有符号指数哥伦布熵编码。
其实就算这个说你们也可能是一脸蒙逼。之后再来介绍一下哥伦布编码,你们记住这个东西怎么运算就可以了。
uint32_t x264_analysis_t::Ue(uint8_t * pBuff, uint32_t nLen, uint32_t & nStartBit)
{
uint32_t nZeroNum = 0;
while (nStartBit < nLen * 8)
{
if(pBuff[nStartBit / 8] & (0x80 >> (nStartBit % 8)))
{
break;
}
nZeroNum ++;
nStartBit ++;
}
nStartBit++;
uint64_t dwRet = 0;
for(uint32_t i=0; i> (nStartBit % 8)))
{
dwRet += 1;
}
nStartBit ++;
}
return (1 << nZeroNum) - 1 + dwRet;
}
int x264_analysis_t::Se(uint8_t * pBuff, uint32_t nLen, uint32_t & nStartBit)
{
int UeVal = Ue(pBuff, nLen,nStartBit);
double k = UeVal;
int nValue = ceil(k / 2);
if(UeVal % 2 == 0)
nValue = -nValue;
return nValue;
}
uint64_t x264_analysis_t::U(uint8_t * pBuff, uint32_t BitCount, uint32_t & nStartBit)
{
uint64_t dwRet = 0;
for(uint32_t i=0; i> (nStartBit % 8)))
{
dwRet += 1;
}
nStartBit++;
}
return dwRet;
}
先给你们一组数据,这是在吃鸡游戏直播里面抓出来的sps数据:
uint8_t buff[] = {
0x67, 0x64, 0x00, 0x1f, 0xac, 0xd9, 0x40, 0x50,
0x05, 0xba, 0x10, 0x00, 0x00, 0x03, 0x00, 0x10,
0x00, 0x00, 0x03, 0x03, 0x20, 0xf1, 0x83, 0x19,
0x60
};
源码里面都有,可以参考一下。
然后就是对这个表里面的元素一一解析,解析......然后就出错了,这个表其实还缺少一些东西,需要加上的数据位于seq_parameter_set_id之后,需要加入以下东西:
seq_parameter_set_rbsp() | C | Descriptor |
---|---|---|
if(sps_data->profile_idc == 100 or sps_data->profile_idc == 110 or sps_data->profile_idc == 122 or sps_data->profile_idc == 144) | ||
chroma_format_idc | 0 | Ue(v) |
if( sps_data->chroma_format_idc == 3 ) | ||
residual_colour_transform_flag | 0 | U(1) |
bit_depth_luma_minus8 | 0 | Ue(v) |
bit_depth_chroma_minus8 | 0 | Ue(v) |
qpprime_y_zero_transform_bypass_flag | 0 | U(1) |
seq_scaling_matrix_present_flag | 0 | U(1) |
if(sps_data->seq_scaling_matrix_present_flag) | ||
for(int i=0; i<8; i++ ) | ||
seq_scaling_list_present_flag[i] | 0 | U(1) |
之后就可以按照上面的表一步一步解析了。
关于一些变量的含义在下面说明一下:
- profile_idc: 指明其所用的档次。
0x66 = 基本档次
0x77 = 主要档次
0x88 = 扩展档次 - constraint_setN_flag: 设置需要遵从的规约条件。
- seq_parameter_set_id: 指示所引用的序列参数集id。
- log2_max_frame_num_minus4: 可以求出frame_num的最大值。其求法如下:
MaxFrameNum = 2^(log2_max_frame_num_minus4 + 4) - num_ref_frames:指示参考帧队列可能达到的最大长度。
- pic_width_in_mbs_minus1:用来求一个图像的宽度。也可以算是分辨率的宽度,求法如下:
Width = (pic_width_in_mbs_minus1 + 1) * 16 - pic_height_in_mbs_minus1:用来求一个图像的长度。也可以算是分辨率的长度,求法如下:
Height = (pic_height_in_mbs_minus1 + 1) * 16
关于X264源码初始化SPS部分:
void x264_sps_init( x264_sps_t *sps, int i_id, x264_param_t *param )
{
int csp = param->i_csp & X264_CSP_MASK;
sps->i_id = i_id;
sps->i_mb_width = ( param->i_width + 15 ) / 16;
sps->i_mb_height= ( param->i_height + 15 ) / 16;
sps->i_chroma_format_idc = csp >= X264_CSP_I444 ? CHROMA_444 :
csp >= X264_CSP_I422 ? CHROMA_422 :
csp >= X264_CSP_I420 ? CHROMA_420 : CHROMA_400;
sps->b_qpprime_y_zero_transform_bypass = param->rc.i_rc_method == X264_RC_CQP && param->rc.i_qp_constant == 0;
if( sps->b_qpprime_y_zero_transform_bypass || sps->i_chroma_format_idc == CHROMA_444 )
sps->i_profile_idc = PROFILE_HIGH444_PREDICTIVE;
else if( sps->i_chroma_format_idc == CHROMA_422 )
sps->i_profile_idc = PROFILE_HIGH422;
else if( BIT_DEPTH > 8 )
sps->i_profile_idc = PROFILE_HIGH10;
else if( param->analyse.b_transform_8x8 || param->i_cqm_preset != X264_CQM_FLAT || sps->i_chroma_format_idc == CHROMA_400 )
sps->i_profile_idc = PROFILE_HIGH;
else if( param->b_cabac || param->i_bframe > 0 || param->b_interlaced || param->b_fake_interlaced || param->analyse.i_weighted_pred > 0 )
sps->i_profile_idc = PROFILE_MAIN;
else
sps->i_profile_idc = PROFILE_BASELINE;
sps->b_constraint_set0 = sps->i_profile_idc == PROFILE_BASELINE;
/* x264 doesn't support the features that are in Baseline and not in Main,
* namely arbitrary_slice_order and slice_groups. */
sps->b_constraint_set1 = sps->i_profile_idc <= PROFILE_MAIN;
/* Never set constraint_set2, it is not necessary and not used in real world. */
sps->b_constraint_set2 = 0;
sps->b_constraint_set3 = 0;
sps->i_level_idc = param->i_level_idc;
if( param->i_level_idc == 9 && ( sps->i_profile_idc == PROFILE_BASELINE || sps->i_profile_idc == PROFILE_MAIN ) )
{
sps->b_constraint_set3 = 1; /* level 1b with Baseline or Main profile is signalled via constraint_set3 */
sps->i_level_idc = 11;
}
/* Intra profiles */
if( param->i_keyint_max == 1 && sps->i_profile_idc >= PROFILE_HIGH )
sps->b_constraint_set3 = 1;
sps->vui.i_num_reorder_frames = param->i_bframe_pyramid ? 2 : param->i_bframe ? 1 : 0;
/* extra slot with pyramid so that we don't have to override the
* order of forgetting old pictures */
sps->vui.i_max_dec_frame_buffering =
sps->i_num_ref_frames = X264_MIN(X264_REF_MAX, X264_MAX4(param->i_frame_reference, 1 + sps->vui.i_num_reorder_frames,
param->i_bframe_pyramid ? 4 : 1, param->i_dpb_size));
sps->i_num_ref_frames -= param->i_bframe_pyramid == X264_B_PYRAMID_STRICT;
if( param->i_keyint_max == 1 )
{
sps->i_num_ref_frames = 0;
sps->vui.i_max_dec_frame_buffering = 0;
}
/* number of refs + current frame */
int max_frame_num = sps->vui.i_max_dec_frame_buffering * (!!param->i_bframe_pyramid+1) + 1;
/* Intra refresh cannot write a recovery time greater than max frame num-1 */
if( param->b_intra_refresh )
{
int time_to_recovery = X264_MIN( sps->i_mb_width - 1, param->i_keyint_max ) + param->i_bframe - 1;
max_frame_num = X264_MAX( max_frame_num, time_to_recovery+1 );
}
sps->i_log2_max_frame_num = 4;
while( (1 << sps->i_log2_max_frame_num) <= max_frame_num )
sps->i_log2_max_frame_num++;
sps->i_poc_type = param->i_bframe || param->b_interlaced || param->i_avcintra_class ? 0 : 2;
if( sps->i_poc_type == 0 )
{
int max_delta_poc = (param->i_bframe + 2) * (!!param->i_bframe_pyramid + 1) * 2;
sps->i_log2_max_poc_lsb = 4;
while( (1 << sps->i_log2_max_poc_lsb) <= max_delta_poc * 2 )
sps->i_log2_max_poc_lsb++;
}
sps->b_vui = 1;
sps->b_gaps_in_frame_num_value_allowed = 0;
sps->b_frame_mbs_only = !(param->b_interlaced || param->b_fake_interlaced);
if( !sps->b_frame_mbs_only )
sps->i_mb_height = ( sps->i_mb_height + 1 ) & ~1;
sps->b_mb_adaptive_frame_field = param->b_interlaced;
sps->b_direct8x8_inference = 1;
x264_sps_init_reconfigurable( sps, param );
sps->vui.b_overscan_info_present = param->vui.i_overscan > 0 && param->vui.i_overscan <= 2;
if( sps->vui.b_overscan_info_present )
sps->vui.b_overscan_info = ( param->vui.i_overscan == 2 ? 1 : 0 );
sps->vui.b_signal_type_present = 0;
sps->vui.i_vidformat = ( param->vui.i_vidformat >= 0 && param->vui.i_vidformat <= 5 ? param->vui.i_vidformat : 5 );
sps->vui.b_fullrange = ( param->vui.b_fullrange >= 0 && param->vui.b_fullrange <= 1 ? param->vui.b_fullrange :
( csp >= X264_CSP_BGR ? 1 : 0 ) );
sps->vui.b_color_description_present = 0;
sps->vui.i_colorprim = ( param->vui.i_colorprim >= 0 && param->vui.i_colorprim <= 12 ? param->vui.i_colorprim : 2 );
sps->vui.i_transfer = ( param->vui.i_transfer >= 0 && param->vui.i_transfer <= 18 ? param->vui.i_transfer : 2 );
sps->vui.i_colmatrix = ( param->vui.i_colmatrix >= 0 && param->vui.i_colmatrix <= 14 ? param->vui.i_colmatrix :
( csp >= X264_CSP_BGR ? 0 : 2 ) );
if( sps->vui.i_colorprim != 2 || sps->vui.i_transfer != 2 || sps->vui.i_colmatrix != 2 )
sps->vui.b_color_description_present = 1;
if( sps->vui.i_vidformat != 5 || sps->vui.b_fullrange || sps->vui.b_color_description_present )
sps->vui.b_signal_type_present = 1;
/* FIXME: not sufficient for interlaced video */
sps->vui.b_chroma_loc_info_present = param->vui.i_chroma_loc > 0 && param->vui.i_chroma_loc <= 5 &&
sps->i_chroma_format_idc == CHROMA_420;
if( sps->vui.b_chroma_loc_info_present )
{
sps->vui.i_chroma_loc_top = param->vui.i_chroma_loc;
sps->vui.i_chroma_loc_bottom = param->vui.i_chroma_loc;
}
sps->vui.b_timing_info_present = param->i_timebase_num > 0 && param->i_timebase_den > 0;
if( sps->vui.b_timing_info_present )
{
sps->vui.i_num_units_in_tick = param->i_timebase_num;
sps->vui.i_time_scale = param->i_timebase_den * 2;
sps->vui.b_fixed_frame_rate = !param->b_vfr_input;
}
sps->vui.b_vcl_hrd_parameters_present = 0; // we don't support VCL HRD
sps->vui.b_nal_hrd_parameters_present = !!param->i_nal_hrd;
sps->vui.b_pic_struct_present = param->b_pic_struct;
// NOTE: HRD related parts of the SPS are initialised in x264_ratecontrol_init_reconfigurable
sps->vui.b_bitstream_restriction = !(sps->b_constraint_set3 && sps->i_profile_idc >= PROFILE_HIGH);
if( sps->vui.b_bitstream_restriction )
{
sps->vui.b_motion_vectors_over_pic_boundaries = 1;
sps->vui.i_max_bytes_per_pic_denom = 0;
sps->vui.i_max_bits_per_mb_denom = 0;
sps->vui.i_log2_max_mv_length_horizontal =
sps->vui.i_log2_max_mv_length_vertical = (int)log2f( X264_MAX( 1, param->analyse.i_mv_range*4-1 ) ) + 1;
}
sps->b_avcintra = !!param->i_avcintra_class;
sps->i_cqm_preset = param->i_cqm_preset;
}
从源码中,我们也可以反过来解析nalu单元里面的SPS内容。
解析SPS相关函数:
bool x264_analysis_t::h264_decode_sps(uint8_t * buf, uint32_t nLen, GY::x264_SPS_t * sps_data)
{
uint32_t startBit = 0;
de_emulation_prevention(buf, &nLen);
sps_data->forbidden_zero_bit = U(buf, 1, startBit);
sps_data->nal_ref_idc = U(buf, 2, startBit);
sps_data->nal_uint_type = U(buf, 5, startBit);
if(sps_data->nal_uint_type == 7)
{
sps_data->profile_idc = U(buf, 8, startBit);
sps_data->constraint_set0_flag = U(buf, 1, startBit);
sps_data->constraint_set1_flag = U(buf, 1, startBit);
sps_data->constraint_set2_flag = U(buf, 1, startBit);
sps_data->reserved_zero_5bits = U(buf, 5, startBit);
sps_data->level_idx = U(buf, 8, startBit);
sps_data->seq_parameter_set_id = Ue(buf, nLen, startBit);
if(sps_data->profile_idc == 100 || sps_data->profile_idc == 110 ||
sps_data->profile_idc == 122 || sps_data->profile_idc == 244 ||
sps_data->profile_idc == 44 || sps_data->profile_idc == 83 ||
sps_data->profile_idc == 86 || sps_data->profile_idc == 118 ||
sps_data->profile_idc ==128)
{
sps_data->chroma_format_idc=Ue(buf, nLen, startBit);
if( sps_data->chroma_format_idc == 3 )
sps_data->residual_colour_transform_flag=U(buf, 1, startBit);
sps_data->bit_depth_luma_minus8 = Ue(buf,nLen,startBit);
sps_data->bit_depth_chroma_minus8 = Ue(buf,nLen,startBit);
sps_data->qpprime_y_zero_transform_bypass_flag = U(buf, 1, startBit);
sps_data->seq_scaling_matrix_present_flag = U(buf, 1,startBit);
if(sps_data->seq_scaling_matrix_present_flag)
{
for(int i=0; i<8; i++ )
{
sps_data->seq_scaling_list_present_flag[i] = U(buf, 1, startBit);
}
}
}
sps_data->log2_max_frame_num_minus4 = Ue(buf, nLen, startBit);
sps_data->pic_order_cnt_type = Ue(buf, nLen, startBit);
if(sps_data->pic_order_cnt_type == 0)
sps_data->log2_max_pic_order_cnt_lsb_minus4 = Ue(buf, nLen, startBit);
else if(sps_data->pic_order_cnt_type == 1)
{
sps_data->delta_pic_order_always_zero_flag = U(buf, 1, startBit);
sps_data->offset_for_non_ref_pic = Se(buf, nLen, startBit);
sps_data->offset_for_top_to_bottom_field = Se(buf, nLen, startBit);
sps_data->num_ref_frames_int_pic_order_cnt_cycle = Ue(buf, nLen, startBit);
for(int i=0; inum_ref_frames_int_pic_order_cnt_cycle; ++i)
sps_data->offset_for_ref_frame[i] = Se(buf, nLen, startBit);
}
sps_data->num_ref_frames = Ue(buf, nLen, startBit);
sps_data->gaps_in_frame_num_value_allowed_flag = U(buf, 1, startBit);
sps_data->pic_width_in_mbs_minus1 = Ue(buf, nLen, startBit);
sps_data->pic_height_in_mbs_minus1 = Ue(buf, nLen, startBit);
sps_data->frame_mbs_only_flag = U(buf, 1, startBit);
if(!sps_data->frame_mbs_only_flag)
sps_data->mb_adaptive_frame_field_flag = U(buf, 1, startBit);
sps_data->direct_8x8_inference_flag = U(buf, 1, startBit);
sps_data->frame_cropping_flag = U(buf, 1, startBit);
if(sps_data->frame_cropping_flag)
{
sps_data->frame_crop_left_offset = Ue(buf, nLen, startBit);
sps_data->frame_crop_right_offset = Ue(buf, nLen, startBit);
sps_data->frame_crop_top_offset = Ue(buf, nLen, startBit);
sps_data->frame_crop_bottom_offset = Ue(buf, nLen, startBit);
}
sps_data->vui_parameters_present_flag = U(buf, 1, startBit);
if(sps_data->vui_parameters_present_flag)
{
sps_data->aspect_ratio_info_present_flag = U(buf, 1, startBit);
if(sps_data->aspect_ratio_info_present_flag)
{
sps_data->aspect_ratio_idc = U(buf, 8, startBit);
if(sps_data->aspect_ratio_idc == 255)
{
sps_data->sar_width = U(buf, 16, startBit);
sps_data->sar_height = U(buf, 16, startBit);
}
}
sps_data->overscan_info_present_flag = U(buf, 1,startBit);
if(sps_data->overscan_info_present_flag)
sps_data->overscan_appropriate_flagu = U(buf, 1, startBit);
sps_data->video_signal_type_present_flag = U(buf, 1, startBit);
if (sps_data->video_signal_type_present_flag)
{
sps_data->video_format = U(buf, 3, startBit);
sps_data->video_full_range_flag = U(buf, 1, startBit);
sps_data->colour_description_present_flag = U(buf, 1, startBit);
if (sps_data->colour_description_present_flag)
{
sps_data->colour_primaries=U(buf, 8, startBit);
sps_data->transfer_characteristics=U(buf, 8, startBit);
sps_data->matrix_coefficients=U(buf, 8, startBit);
}
}
sps_data->chroma_loc_info_present_flag = U(buf, 1, startBit);
if(sps_data->chroma_loc_info_present_flag)
{
sps_data->chroma_sample_loc_type_top_field = Ue(buf, nLen, startBit);
sps_data->chroma_sample_loc_type_bottom_field = Ue(buf, nLen, startBit);
}
sps_data->timing_info_present_flag = U(buf, 1, startBit);
if(sps_data->timing_info_present_flag)
{
sps_data->num_units_in_tick = U(buf, 32, startBit);
sps_data->time_scale = U(buf, 32, startBit);
sps_data->fixed_frame_rate_flag = U(buf, 1, startBit);
}
}
return true;
}
else
{
return false;
}
}
关于该函数的定义:
/**
* 该函数用于解析h.264中的序列参数集的变量
* @param buf : 需要解析的h.264码流数据
* @param nLen: 需要解析的h.264码流数据的总长度
* @param sps_data: 解析的结果放于该结构体中。
*/
static bool h264_decode_sps(uint8_t *buf,uint32_t nLen, GY::x264_SPS_t *sps_data);