前言
了解H264视频编码格式的小伙伴都知道,H264编码中存在两个非常重要的参数集。没错,它们就是序列参数集(SPS)和图像参数集(PPS),而且通常情况下,PPS会依赖SPS中的部分参数信息,同时,视频码流的宽高信息也存储在SPS中。那么如何从中获取视频的宽高信息呢,就是今天本文的主要内容。
正文
一、SPS的结构
对H264码流进行解码时,肯定会用到SPS中的相关参数,因此,我们非常有必要了解其中参数的含义。其中,H.264标准协议中(文档的7.3.2.1.1部分)规定的SPS格式如下图所示:
接下来,介绍一下上图中的部分参数。
(1) profile_idc
标识当前H.264码流的profile。我们知道,H.264中定义了三种常用的档次profile:
基准档次:baseline profile;
主要档次:main profile;
扩展档次:extended profile;
在H.264的SPS中,第一个字节表示profile_idc,根据profile_idc的值可以确定码流符合哪一种档次。判断规律如下:
profile_idc = 66 → baseline profile;
profile_idc = 77 → main profile;
profile_idc = 88 → extended profile;
在新版的标准中,还包括了High、High 10、High 4:2:2、High 4:4:4、High 10 Intra、High
4:2:2 Intra、High 4:4:4 Intra、CAVLC 4:4:4 Intra等,每一种都由不同的profile_idc表示。
另外,constraint_set0_flag ~ constraint_set5_flag是在编码的档次方面对码流增加的其他一些额外限制性条件。
(2) level_idc
标识当前码流的Level。编码的Level定义了某种条件下的最大视频分辨率、最大视频帧率等参数,码流所遵从的level由level_idc指定。
当前码流中,level_idc = 0x1e = 30,因此码流的级别为3。
(3) seq_parameter_set_id
表示当前的序列参数集的id。通过该id值,图像参数集pps可以引用其代表的sps中的参数。
(4) log2_max_frame_num_minus4
用于计算MaxFrameNum的值。计算公式如下:
MaxFrameNum = 2^(log2_max_frame_num_minus4 4)
MaxFrameNum是frame_num的上限值,frame_num是图像序号的一种表示方法,在帧间编码中常用作一种参考帧标记的手段。
(5) pic_order_cnt_type
表示解码picture order count(POC)的方法。POC是另一种计量图像序号的方式,与frame_num有着不同的计算方法。该语法元素的取值为0、1或2。
(6) log2_max_pic_order_cnt_lsb_minus4
用于计算MaxPicOrderCntLsb的值,该值表示POC的上限。计算公式如下:
MaxPicOrderCntLsb = 2^(log2_max_pic_order_cnt_lsb_minus4 4)
(7) max_num_ref_frames
用于表示参考帧的最大数目。
(8) gaps_in_frame_num_value_allowed_flag
标识位,说明frame_num中是否允许不连续的值。
(9) pic_width_in_mbs_minus1
用于计算图像的宽度,单位为宏块个数。
(10) pic_height_in_map_units_minus1
使用PicHeightInMapUnits来度量视频中一帧图像的高度。PicHeightInMapUnits并非图像明确的以像素或宏块为单位的高度,而需要考虑该宏块是帧编码或场编码。
(11) frame_mbs_only_flag
标识位,说明宏块的编码方式。当该标识位为0时,宏块可能为帧编码或场编码;该标识位为1时,所有宏块都采用帧编码。根据该标识位取值不同,PicHeightInMapUnits的含义也不同,为0时表示一场数据按宏块计算的高度,为1时表示一帧数据按宏块计算的高度。
按照宏块计算的图像实际高度FrameHeightInMbs的计算方法为:
FrameHeightInMbs = ( 2 − frame_mbs_only_flag ) * PicHeightInMapUnits
(12) mb_adaptive_frame_field_flag
标识位,说明是否采用了宏块级的帧场自适应编码。当该标识位为0时,不存在帧编码和场编码之间的切换;当标识位为1时,宏块可能在帧编码和场编码模式之间进行选择。
(13) direct_8x8_inference_flag
标识位,用于B_Skip、B_Direct模式运动矢量的推导计算。
(14) frame_cropping_flag
标识位,说明是否需要对输出的图像帧进行裁剪。
(15) vui_parameters_present_flag
标识位,说明SPS中是否存在VUI信息。
二、SPS的存储位置
在H264码流中,都是以"0x00 0x00 0x01"或者"0x00 0x00 0x00 0x01"作为起始码的,找到起始码之后,使用开始码之后的第一个字节的低5位判断是否为7,也就是SPS类型标识,伪代码如下:
data[3] & 0x1f == 7 data[4] & 0x1f == 7
SPS 对于H264而言,就是编码后的第一帧,如果是读取的H264文件,就是第一个帧界定符和第二个帧界定符之间的数据,一般长度是4。
三、如何计算宽高信息
根据SPS信息计算视频宽高的常用公式如下:
代码语言:javascript复制width = (pic_width_in_mbs_minus1 1)*16;
height = (pic_height_in_map_units_minus1 1)*16;
代码语言:javascript复制
但是这是针对宽高是16的整数倍的情况,如果宽高不是16整数倍时,frame_cropping_flag值为1,frame_mbs_only_flag为1,公式如下:(也可以认为这是统一公式)
代码语言:javascript复制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
代码如下:
代码语言:javascript复制 // 宽高计算公式
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);
}
作者简介: