一、引言
通过FFmpeg命令可以获取到H.264裸流文件的视频分辨率:
在vlc中也可以获取到视频分辨率(vlc底层也使用了FFmpeg进行解码):
所以FFmpeg和vlc是怎样获取到H.264编码的视频的分辨率呢?它们其实是通过SPS中的属性(pic_width_in_mbs_minus1、pic_height_in_map_units_minus1 、frame_mbs_only_flag、frame_cropping_flag、chroma_format_idc、SubWidthC、SubHeightC、frame_crop_left_offset、frame_crop_right_offset、frame_crop_top_offset、frame_crop_bottom_offset)解析出这个视频的宽高的。
网上广泛流传的计算视频宽高的公式为:
width = (pic_width_in_mbs_minus1 + 1) * 16;
height = (pic_height_in_map_units_minus1 + 1) * 16;
这是不严谨的,因为某些视频的宽高不是16的倍数。而且还得考虑特殊情况,也就是场编码的情况。
二、根据H.264官方文档得到真正计算视频分辨率的公式
上述属性在H.264官方文档《T-REC-H.264-202108-I!!PDF-E.pdf》第44页到45页描述:
根据H.264官方文档《T-REC-H.264-202108-I!!PDF-E.pdf》第78页:
pic_width_in_mbs_minus1加1是指以宏块为单位的每个解码图像的宽度。
以宏块为单元的图像宽度变量PicWidthInMbs=pic_width_in_mbs_minus1 + 1。
亮度分量的图像宽度变量PicWidthInSamplesL=PicWidthInMbs * 16
pic_height_in_map_units_minus1加1表示以条带组映射为单位的一个解码帧或场的高度。PicHeightInMapUnits=pic_height_in_map_units_minus1+1。
PicSizeInMapUnits=PicWidthInMbs * PicHeightInMapUnits
FrameHeightInMbs = ( 2 – frame_mbs_only_flag ) * PicHeightInMapUnits
根据第22页:
在单色采样中只有一个样点阵列,名义上当做亮度阵列。
在4:2:0样点中,两个色度阵列的高度和宽度均为亮度阵列的一半。
在4:2:2样点中,两个色度阵列的高度等于亮度阵列的高度,宽度为亮度阵列的一半。
在4:2:4样点中,两个色度阵列的高度和宽度与亮度阵列的相等。
SPS属性中的chroma_format_idc = 0,并且separate_colour_plane_flag = 0时,色度格式为单色,此时SubWidthC和SubHeightC没有意义。
chroma_format_idc = 1,并且separate_colour_plane_flag = 0时,色度格式为YUV420,此时SubWidthC为2,SubHeightC为2。
chroma_format_idc = 2,并且separate_colour_plane_flag = 0时,色度格式为YUV422,此时SubWidthC为2,SubHeightC为1。
chroma_format_idc = 3,并且separate_colour_plane_flag = 0时,色度格式为YUV444,此时SubWidthC为1,SubHeightC为1。
chroma_format_idc = 3,并且separate_colour_plane_flag = 1时,色度格式为YUV444,此时SubWidthC和SubHeightC没有意义。
根据第79页:
如果chroma_format_idc等于0,CropUnitX和CropUnitY按下列方式计算:
CropUnitX = 1
CropUnitY = 2- frame_mbs_only_flag
否则(chroma_format_idc等于1、2或3),CropUnitX和CropUnitY按下列公式计算:
CropUnitX = SubWidthC
CropUnitY = SubHeightC * (2 - frame_mbs_only_flag)
当frame_cropping_flag等于0时,frame_crop_left_offset、frame_crop_right_offset、frame_crop_top_offset和frame_crop_bottom_offset的值应等于0。
根据第385页:
输出裁剪后的帧区域的宽度CroppedWidth = PicWidthInSamplesL − CropUnitX * ( frame_crop_left_offset + frame_crop_right_offset )
三、得到最终计算视频分辨率的公式
int SubWidthC;
int SubHeightC;if (sps->chroma_format_idc == 0 && sps->separate_colour_plane_flag == 0) { //monochromeSubWidthC = SubHeightC = 0;
}
else if (sps->chroma_format_idc == 1 && sps->separate_colour_plane_flag == 0) { //4:2:0 SubWidthC = SubHeightC = 2;
}
else if (sps->chroma_format_idc == 2 && sps->separate_colour_plane_flag == 0) { //4:2:2 SubWidthC = 2;SubHeightC = 1;
}
else if (sps->chroma_format_idc == 3) { //4:4:4if (sps->separate_colour_plane_flag == 0) {SubWidthC = SubHeightC = 1;}else if (sps->separate_colour_plane_flag == 1) {SubWidthC = SubHeightC = 0;}
}int PicWidthInMbs = sps->pic_width_in_mbs_minus1 + 1;int PicHeightInMapUnits = sps->pic_height_in_map_units_minus1 + 1;
int FrameHeightInMbs = (2 - sps->frame_mbs_only_flag) * PicHeightInMapUnits;int crop_left = 0;
int crop_right = 0;
int crop_top = 0;
int crop_bottom = 0;if (sps->frame_cropping_flag) {crop_left = sps->frame_crop_left_offset;crop_right = sps->frame_crop_right_offset;crop_top = sps->frame_crop_top_offset;crop_bottom = sps->frame_crop_bottom_offset;
}int width = PicWidthInMbs * 16 - SubWidthC * (crop_left + crop_right);
int height = FrameHeightInMbs * 16 - SubHeightC * (2 - sps->frame_mbs_only_flag) * (crop_top + crop_bottom);
四、计算视频分辨率的例子
假如某个H.264编码的视频的SPS中的chroma_format_idc值为1,pic_width_in_mbs_minus1值为79,pic_height_in_map_units_minus1值为44,frame_mbs_only_flag值为1,frame_cropping_flag值为0,如下所示:
SubWidthC = SubHeightC = 2,int PicWidthInMbs
= sps->pic_width_in_mbs_minus1 + 1
= 79 + 1
= 80int PicHeightInMapUnits
= sps->pic_height_in_map_units_minus1 + 1
= 45int FrameHeightInMbs
= (2 - sps->frame_mbs_only_flag) * PicHeightInMapUnits
= (2 - 1) * 45
= 45int width = PicWidthInMbs * 16 - SubWidthC * (crop_left + crop_right)
= 80 * 16 - 2* (0 + 0)
= 1280int height = FrameHeightInMbs * 16 - SubHeightC * (2 - sps->frame_mbs_only_flag) * (crop_top + crop_bottom)
= 45 * 16 - 2 * (2 - 1) * (0 + 0)
= 720
所以该视频的分辨率为1280 * 720。
五、参考文章
《H264 getting frame height and width from sequence parameter set (SPS) NAL unit》