從H264/H265碼流中獲取寬、高及幀率
在做碼流分析時,影象解析度、幀率這類的基本資訊,當然不可少。本文介紹如何從NAL中計算到影象寬、高,還有解析度。於是H264和H265有相似性,就在一起寫了。
一、從碼流獲得寬、高
1、H264
寬高可從SPS欄位計算得到,公式如下:
Width = (pic_width_in_mbs_minus1+1)*16; Height = (pic_height_in_map_units_minus1+1)*16;
但以上是針對寬高是16的整數倍的情況,當不是16整數倍時,frame_cropping_flag值為1,frame_mbs_only_flag為1,公式如下:
(也可以認為統一使用下面的公式)
width = ((pic_width_in_mbs_minus1 +1)*16) - frame_crop_left_offset*2 - frame_crop_right_offset*2; height= ((2 - frame_mbs_only_flag)* (pic_height_in_map_units_minus1 +1) * 16) - \ (frame_crop_top_offset * 2) - (frame_crop_bottom_offset * 2);
比如一個1080P視訊的SPS資訊如下:
pic_width_in_mbs_minus1 : 119 pic_height_in_map_units_minus1 : 67 frame_mbs_only_flag : 1 mb_adaptive_frame_field_flag : 0 direct_8x8_inference_flag : 1 frame_cropping_flag : 1 frame_crop_left_offset : 0 frame_crop_right_offset : 0 frame_crop_top_offset : 0 frame_crop_bottom_offset : 4
根據第二個公式
width = (119+1) * 18 - 0*2 - 0*2 = 1920 height = (2-1) * (67+1)*16 - 0*2 - 4*2 = 1088 - 8 = 1080
以上公式是一年多以前在網上找的,仔細看手冊,上面的公式有侷限性。根據H264手冊Table6-1及7.4.2.1.1,參考mkvtoolnix程式碼,比如穩妥的計算方法如下:
// 寬高計算公式 width = (sps->pic_width_in_mbs_minus1+1) * 16; height = (2 - sps->frame_mbs_only_flag)* (sps->pic_height_in_map_units_minus1 +1) * 16); if(sps->frame_cropping_flag) { unsigned int crop_unit_x; unsigned int crop_unit_y; if (0 == sps->chroma_format_idc) // monochrome { crop_unit_x = 1; crop_unit_y = 2 - sps->frame_mbs_only_flag; } else if (1 == sps->chroma_format_idc) // 4:2:0 { crop_unit_x = 2; crop_unit_y = 2 * (2 - sps->frame_mbs_only_flag); } else if (2 == sps->chroma_format_idc) // 4:2:2 { crop_unit_x = 2; crop_unit_y = 2 - sps->frame_mbs_only_flag; } else // 3 == sps.chroma_format_idc // 4:4:4 { crop_unit_x = 1; crop_unit_y = 2 - sps->frame_mbs_only_flag; } width -= crop_unit_x * (sps->frame_crop_left_offset + sps->frame_crop_right_offset); height -= crop_unit_y * (sps->frame_crop_top_offset + sps->frame_crop_bottom_offset); }
注:
一些H264分析工具,比如CodecVisa和H264Visa,1080P的視訊,解析度為1920×1088。這是不正確的。
2、H265
H.265類似,但SPS的欄位不同了。公式如下:
width = sps->pic_width_in_luma_samples; height = sps->pic_height_in_luma_samples;
當視窗有裁剪時(conformance_window_flag為1),計算如下:
sub_width_c = ((1==chroma_format_idc)||(2 == chroma_format_idc))&&(0==separate_colour_plane_flag)?2:1; sub_height_c = (1==chroma_format_idc)&& (0 == separate_colour_plane_flag)?2:1; width -= (sub_width_c*conf_win_right_offset + sub_width_c*conf_win_left_offset); height -= (sub_height_c*conf_win_bottom_offset + sub_height_c*conf_win_top_offset);
上式根據H265手冊Table6-1及7.4.3.2.1小節計算寬、高。注意,手冊里加了1,但實際不使用。
參考mkvtoolnix討論:https://github.com/mbunkus/mkvtoolnix/issues/1152
注:對於1080P的視訊,H265直接用pic_width_in_luma_samples及pic_height_in_luma_samples即得到正確的值。但對於一些奇葩的解析度,還沒有測試過。
三、幀率
H264和H265幀率計算公式相同,如下:
max_framerate = (float)(sps->vui.vui_time_scale) / (float)(sps->vui.vui_num_units_in_tick);
使用x264編碼YUV序列,設定為25fps時,time_scale為50,num_units_in_tick為1,計算得50fps,與實際不符。而x265用同樣的引數編碼,計算得到的幀率是正常的。
網上有說法,當nuit_field_based_flag為1時,要再除以2。另外說x264將該值設定為0,所以得到的值不是實際值。參見:http://forum.doom9.org/showthread.php?t=153019
目前還沒研究透這一點。