奔着学习的态度,借此试用期要输出文档,把h264的格式和相关知识深入梳理一下。
流媒体分析工具:Elecard StreamEye
一、h264认识
h264是一种视频编码标准,跟常见的视频格式不属于同一类。H.264同时也是MPEG-4第10部分规范(ISO/IEC 14496-10),是一种高度压缩数字视频编解码器标准。这个标准通常被称之为H.264/AVC。h264一般只表现在传输通道上和媒介上,可以降低带宽和空间的占用,其中码率越低,暂用资源越少。h264播放时,需要解码过程。
二、h264流格式
H264主要分为四种类BP、EP、MP、HP,分别对应四种画质的级别。
- BP-Baseline Profile:基本画质,支持I/P帧,无B帧。只支持无交错(Progressive)和CAVLC,主要用于可视电话、会议电视、无线通信等实时视频通信。
- EP-Extended Profile:进阶画质,支持I/B/P/SP/SI帧。只支持无交错(Progressive)和CAVLC主要用于流媒体服务。
- MP-Main Profile:主流画质,支持I/B/P。只是无交错(Progressive)和交错(Interlaced),支持CAVLC和CABAC,主要用于电视广播和视频存储。
- HP-High profile:高级画质, 在mainProfile的基础上增加了8x8内部预测、自定义量化、 无损视频编码和更多的YUV格式。
其他:除了上述几种主要类型外,还引申了其他类型。High 10profile(Hi10P)、High 4:2:2 profile(Hi422P)、High 4:4:4 profile(Hi444P) 3个profile,加上HP-High profile在内,这4种格式均以Main profile为基础,在相同配置情况下,Highprofile(HP)可以比Mainprofile(MP)节省10%的码流量,比MPEG-2 MP节省60%的码流量,具有更好的编码性能。
三、h264帧格式
h264的帧类型主要有I帧、P帧、B帧、SP帧、SI帧,具体h264流中包含哪几种帧类型,有h264流类型定义。
I帧:帧内编码帧,帧表示关键帧,你可以理解为这一帧画面的完整保留;解码时只需要本帧数据就可以完成(因为包含完整画面)。
I帧特点:
- 它是一个全帧压缩编码帧,它将全帧图像信息进行JPEG压缩编码及传输。
- 解码时仅用I 帧的数据就可重构完整图像。
- I 帧描述了图像背景和运动主体的详情。
- I 帧不需要参考其他画面而生成。
- I 帧是P帧和B帧的参考帧(其质量直接影响到同组中以后各帧的质量)。
- I 帧不需要考虑运动矢量。
- I 帧所占数据的信息量比较大。
P帧:前向预测编码帧。P帧表示的是这一帧跟之前的一个关键帧(或P帧)的差别,解码时需要之前缓存的画面叠加上本帧定义的差别,生成最终画面。压缩率约为20(也就是差别帧,P帧没有完整画面数据,只有与前一帧的画面差别的数据)
P帧的预测与重构:
P帧是以 I 帧为参考帧,在 I 帧中找出P帧“某点”的预测值和运动矢量,取预测差值和运动矢量一起传送。在接收端根据运行矢量从 I 帧找出P帧“某点”的预测值并与差值相加以得到P帧“某点”样值,从而可得到完整的P帧。
P帧特点:
- P帧采用运动补偿的方法传送它与前面的I或P帧的差值及运动矢量(预测误差)。
- 解码时必须将I帧中的预测值与预测误差求和后才能重构完整的P帧图像。
- P帧属于前向预测的帧间编码。它只参考前面最靠近它的I帧或P帧。
- P帧可以是其后面P帧的参考帧,也可以是其前后的B帧的参考帧。
- 由于P帧是参考帧,它可能造成解码错误的扩散。
- 由于是差值传送,P帧的压缩比较高。
B帧:双向预测内插编码帧。B帧是双向差别帧,也就是B帧记录的是本帧与前后帧的差别,要解码B帧,不仅要取得之前的缓存画面,还要解码之后的画面,通过前后画面的与本帧数据的叠加取得最终的画面。B帧压缩率高约为50,但是解码时CPU会比较累。
P帧特点:
- B帧是由前面的I或P帧和后面的P帧来进行预测的。
- B帧传送的是它与前面的I或P帧和后面的P帧之间的预测误差及运动矢量。
- B帧是双向预测编码帧。
- B帧压缩比最高,因为它只反映丙参考帧间运动主体的变化情况,预测比较准确
- B帧不是参考帧,不会造成解码错误的扩展。
SP/SI帧:是引入的新技术数据帧,主要为视频流的切换做服务。
假设存在两个视频流A {A1, A2, A3, ... ,An,};B {B1, B2, B3, ... ,Bn,};一个播放器要播放A和B,且要做到对两个视频流进行切换播放,既然按照{A1, A2, B3, ... ,Bn}帧顺序播放。这就存在A2, B3帧切换的解码,如果B3为I帧,则切码进行解码理论可以实现,因为I不依赖前帧,可单独解码,如果B3为非I帧,则理论上切码进行解码是不行的,这就需要引入SP/SI帧。
SP/SI帧的帧特点就是,即使用不同的参考帧作预测,也可以得到完全相同的解码帧,这一特点正好适用于流间切换。
四、GOP
GOP是多个帧的组合,由I帧开始,到I帧前结束,但是这两个I帧并不一定是相邻的两个I帧(既一个GOP可能有多个I帧存在)。一个GOP的第一个帧一定是I帧,但是种特殊的I帧,名为IDR。GOP内其他的I帧均为普通I帧。IDR帧一般同SPS、PPS等数据信息同时存在,因为IDR帧具有清空参考帧队列的特性。
五、解码和播放顺序
注:
- 因为B帧是由前面的I或P帧和后面的P帧来进行预测解码的,顾需要前后依赖帧解码完成后,进行解码。
- 播放顺序即为解码输出顺序。
六、帧数据格式
1.头解析和格式判断
帧(包括SPS、PPS)可视为由NALU头和NALU主体两部分组成,每个NALU的起始会包含一个start code,start code是000001或00000001。
F:1bit forbidden_zero_bit 在 H.264 规范中规定了这一位必须为 0。
NRI:2bit nal_ref_idc. 取二进制0b00~0b11,指示这个NALU的重要性,如00的NALU解码器可以丢弃它而不影响图像的回放,0~3,取值越大,表示当前NAL越重要,需要优先受到保护。如果当前NAL是属于参考帧的片,或是序列参数集,或是图像参数集这些重要的单位时,本元素必需大于0。
Type:5bit nal_unit_type 1~12由H.264使用,24~31由H.264以外的应用使用,简述如下
nal_unit_type(二进制) | nal_unit_type(十进制) | NAL类型 | 备注 |
00000 | 0 | 未使用 | |
00001 | 1 | 不分区,非IDR图像的片 | P帧 \ B帧 |
00010 | 2 | 片分区A | |
00011 | 3 | 片分区B | |
00100 | 4 | 片分区C | |
00101 | 5 | IDR图像中的片 | I帧 |
00110 | 6 | 补充增强信息单元(SEI) | 自定义帧 |
00111 | 7 | 序列参数集 | SPS帧 |
01000 | 8 | 图像参数集 | PPS帧 |
01001 | 9 | 分界符 | |
01010 | 10 | 序列结束 | |
01011 | 11 | 码流结束 | |
01100 | 12 | 填充 | |
01101-10111 | 13-23 | 保留 | |
11000 | 24 | STAP-A 单一时间的组合包 | |
11001 | 25 | STAP-B 单一时间的组合包 | |
11010 | 26 | MTAP16 多个时间的组合包 | |
11011 | 27 | MTAP24多个时间的组合包 | |
11100 | 28 | FU-A 分片的单元 | |
11101 | 29 | FU-B 分片的单元 | |
11110-11111 | 30-31 | 未使用 |
例子:
00 00 00 01 67:00000001为start code,0x67为二进制0110 0111
00 00 00 01 68:00000001为start code,0x68为二进制0110 1000
00 00 00 01 65:00000001为start code,0x65为二进制0110 0101
- 帧数据解析
SPS帧:
u(n):表示n bit无符号整数。
ue(v):表示无符号指数哥伦布编码(UE),动态可变的无符号整数,位数可变,需要通过固定的计算方式得出。
se(v):表示有符号指数哥伦布编码(SE)。
参数名 | 长度(bit) | 值 | 说明 | |
forbidden_zero_bit | u(1) | 0:固定值 | NALU头 F | |
nal_ref_idc | u(2) | 3:表示SPS很重要 | NALU头 NRI | |
nal_unit_type | u(5) | 7:固定值,表示此帧为SPS | NALU头 Type | |
profile_idc | u(8) | 66:BP基本画质 77:MP主流画质 88:EP进阶画质 100:HP-High高级画质 110:Hi10P 高级画质 122:Hi422P 高级画质 144:Hi444P 高级画质 | 规定视频流格式,既画质级别 | |
constraint_set0_flag | u(1) | |||
constraint_set1_flag | u(1) | |||
constraint_set2_flag | u(1) | |||
constraint_set3_flag | u(1) | |||
constraint_set4_flag | u(1) | |||
constraint_set5_flag | u(1) | |||
reserved_zero_2bits | u(2) | 为全0 | 保留 | |
level_idc | u(8) | 10:1等级 支持QCIF及以下格式 380160bps. 11:1.1等级 支持CIF及以下格式 768000bps. 12:1.2等级 支持CIF及以下格式 1536000bps. 13:1.3等级 支持CIF及以下格式 3041280bps. 20:2等级 支持CIF及以下格式 3041280bps. 21:2.1等级 支持HHR、Interlace格式 5068800bps. 22:2.2等级 支持SD/4CIF、Interlace格式 5184000bps. 30:3等级 支持SD/4CIF、Interlace格式 10368000bps. 31:3.1等级 支持720p HD、Interlace格式 27648000bps. 32:3.2等级 支持SXGA、Interlace格式 55296000bps. 40:4等级 支持2Kx1K、Interlace格式 62914560bps. 41:4.1等级 支持2Kx1K、Interlace格式 62914560bps. 42:4.2等级 支持2Kx1K、Interlace格式 125829120bps. 50:5等级 支持3672x1536格式,仅支持帧的编码操作 150994944bps. 51:5.1等级 支持4096x2304格式,仅支持帧的编码操作 251658240bps. | 设置等级,用来规定码率(取样率) | |
seq_parameter_set_id | ue(v) | 0~31 | 序列参数集的id,图像参数集pps可以引用其代表的sps中的参数。 | |
chroma_format_idc | ue(v) | 0:单色 1:4:2:0 2:4:2:2 3:4:4:4 不存在时默认为1 | 亮度取样对应的色度取样 | profile_idc等于44|83|86|100|110|118|122|128|134|138|139|135|244时存在 |
separate_colour_plane_flag | u(1) | 当chroma_format_idc为3时才存在 | ||
bit_depth_luma_minus8 | ue(v) | 0:8位 1:10位 | 视频位深 | |
bit_depth_chroma_minus8 | ue(v) | |||
qpprime_y_zero_transform_bypass_flag | u(1) | y轴 0标志位 | ||
seq_scaling_matrix_present_flag | u(1) | 缩放标志位 | ||
seq_scaling_list_present_flag[n] | n个u(1) | if(chroma_format_idc==3)n=8; else n=12; | 当seq_scaling_matrix_present_flag为3时才存在 | |
log2_max_frame_num_minus4 | ue(v) | 0~12 | 用于计算MaxPicOrderCntLsb的值,该值表示POC的上限。计算方法为MaxPicOrderCntLsb=2^(log2_max_pic_order_cnt_lsb_minus4+4) | |
pic_order_cnt_type | ue(v) | 0:传低位(提高压缩效率,只对POC低位编码传输) 1:传POC偏差(除IDR外后续PB帧循环出现) 2:(不消耗bit)显示顺序与解码顺序一致 | poc (picture order count) 的编码方法 | |
log2_max_pic_order_cnt_lsb_minus4 | ue(v) | 指明变量 MaxPicOrderCntLsb 的值: MaxPicOrderCntLsb=2(log2_max_pic_order_cnt_lsb_minus4+4) | pic_order_cnt_type为0时存在 | |
delta_pic_order_always_zero_flag | u(1) | pic_order_cnt_type为1时存在 | ||
offset_for_non_ref_pic | se(v) | |||
offset_for_top_to_bottom_field | se(v) | |||
num_ref_frames_in_pic_order_cnt_cycle | ue(v) | |||
offset_for_ref_frame[num_ref_frames_in_pic_order_cnt_cycle] | num_ref_frames_in_pic_order_cnt_cycle个se(v) | |||
max_num_ref_frames | ue(v) | 帧模式:0~16 场模式:0~32 | 指定参考帧队列可能达到的最大长度,解码器依照这个句法元素的值开辟存储区,这个存储区用于存放已解码的参考帧, H.264 规定最多可用 16 个参考帧,本句法元素的值最大为 16。值得注意的是这个长度以帧为单位,如果在场模式下,应该相应地扩展一倍。 | |
gaps_in_frame_num_value_allowed_flag | u(1) | 0:不允许 frame_num 不连续,即编码器在任何情况下都不能丢弃图像。 1:允许句法元素 frame_num 可以不连续。 | ||
pic_width_in_mbs_minus1 | ue(v) | 帧宽度,计算公式: 帧宽度=16×(pic_width_in_mbs_minus1+1) | ||
pic_height_in_map_units_minus1 | ue(v) | 帧高度(或场高度),计算公式: 帧高度=(2-frame_mbs_only_flag)*(pic_height_in_map_units_minus1+1) * 16 | ||
frame_mbs_only_flag | u(1) | 0:宏块可能为帧编码或场编码 1:宏块都采用帧编码 | 宏块的编码方式标志位 | |
mb_adaptive_frame_field_flag | u(1) | frame_mbs_only_flag为0时存在 | ||
direct_8x8_inference_flag | u(1) | |||
frame_cropping_flag | u(1) | 0:不裁剪 1:裁剪 | 帧裁剪标志位 | |
frame_crop_left_offset | ue(v) | frame_cropping_flag为1时存在 | ||
frame_crop_right_offset | ue(v) | |||
frame_crop_top_offset | ue(v) | |||
frame_crop_bottom_offset | ue(v) | |||
vui_prameters_present_flag | u(1) | 0:不显示 1:显示 | vui参数显示标志 | |
vui参数 | 当vui_prameters_present_flag为1时存在 |
PPS帧:
参数名 | 长度(bit) | 值 | 说明 | |
forbidden_zero_bit | u(1) | 0:固定值 | NALU头 F | |
nal_ref_idc | u(2) | 3:表示SPS很重要 | NALU头 NRI | |
nal_unit_type | u(5) | 8:固定值,表示此帧为PPS | NALU头 Type | |
pic_parameter_set_id | ue(v) | [0,255] | 当前PPS的id。某个PPS在码流中会被相应的slice引用,slice引用PPS的方式就是在Slice header中保存PPS的id值。 | |
seq_parameter_set_id | ue(v) | [0,31] | 当前PPS所引用的激活的SPS的id。通过这种方式,PPS中也可以取到对应SPS中的参数。 | |
entropy_coding_mode_flag | u(1) | 0:CAVLC (基于上下文的自适应变长编码) 1:CABAC(基于上下文的自适应二进制算术编码) | 指定编码图像所运用的熵编码算法 | |
bottom_field_pic_order_in_frame_present_flag | u(1) | 0:不包含 1:Slice_header中会包含图像顺序相关的语法元素 | ||
num_slice_groups_minus1 | ue(v) | 加 1之后的值表示某一帧中slice group的个数 | ||
slice_group_map_type | ue(v) | num_slice_groups_minus1大于0 | ||
run_length_minus1[num_slice_groups_minus1+1] | num_slice_groups_minus1+1个ue(v) | slice_group_map_type为0 | ||
top_left[[num_slice_groups_minus1] | num_slice_groups_minus1个ue(v) | slice_group_map_type为2 | ||
bottom_right[num_slice_groups_minus1] | num_slice_groups_minus1个ue(v) | |||
slice_group_change_direction_flag | u(1) | slice_group_map_type为3|4|5 | ||
slice_group_change_rate_minus1 | ue(v) | |||
pic_size_in_map_units_minus1 | ue(v) | slice_group_map_type为6 | ||
slice_group_id[pic_size_in_map_units_minus1+1] | u(pic_size_in_map_units_minus1+1) | |||
num_ref_idx_l0_default_active_minus1 | ue(v) | 表示解码时的参考图像列表list_0中的参考图像数目 | ||
num_ref_idx_l1_default_active_minus1 | ue(v) | 表示解码时的参考图像列表list_1中的参考图像数目 | ||
weighted_pred_flag | u(1) | 0:不开启 1:开启 | 表示在P/SP slice中是否开启加权预测。 | |
pic_init_qp_minus26 | se(v) | 加 26的值指明亮度分量的量化参数初始值 | ||
pic_init_qs_minus26 | se(v) | 加 26的值指明亮度分量的量化参数初始值 | ||
chroma_qp_index_offset | se(v) | [-12,12] | 指明色度分量量化参数计算时的参数。 | |
deblocking_filter_control_present_flag | u(1) | 0:使用默认 1:在slice_header中会有相关信息 | 指示去块效应滤波的强度 | |
constrained_intra_pred_flag | u(1) | 0:不存在限制 1:帧内编码宏块不能用帧间编码宏块作为预测 | ||
redundant_pic_cnt_present_flag | u(1) | 0:slice header中没有相应的信息 1:slice header中包含redundant_pic_cnt | 表示Slice header中是否存在redundant_pic_cnt语法元素 |
引申:
- 无符号指数哥伦布编码(UE):
哥伦布编码的码字code_word由三部分组成:code_word = [M个0] + [1] + [Info]
其中,Info是一个携带信息的M位数据,每个哥伦布码的长度为(2M+1)位,每个码字都可由code_num产生。
根据码字code_word解码出code_num值的过程如下:
1. 首先读入M位以"1"为结尾的0;
2. 根据得到的M,读入接下来的M位Info数据;
3. 根据这个公式得到计算结果code_num = Info – 1 + 2M;
- 有符号指数哥伦布编码(SE)
有符号的指数哥伦布编码值是通过无符号的指数哥伦布编码的值通过换算得到的,其换算关系为:n = (-1)^(k+1) * ceil(k/2);