第一章 前言
第三章 有关 SPS 和 PPS 的一切
第四章 有关 Slice 的一切
第七章 帧间编码
第八章 残差的熵编码: CAVLC 和 CABAC
上一小节我们大体将 Slice 分成了 Slice Header 和 Slice Body 两个部分,本小结我们来看一下 Slice Header 中存放了哪些东西。
先上标准文档
Slice Header 中的内容也全部是由指数哥伦布熵编码进行编码的,所以我们可以直接按照 SPS 和 PPS 的解析方式进行解析。这里就不做重复阐述了,我们选择几个比较重要的属性来看看。
我们先来看第一个属性,first_mb_in_slice,这个属性表示的是在这个 Slice 中第一个宏块的序号。我们在介绍 SPS 和 PPS 的时候,简单介绍了一下宏块的概念,我们已经有了一些宏块的概念,知道 H.264 是按照宏块来压缩的。那么 first_mb_in_slice 这个属性是什么意思呢?
要解释 first_mb_in_slice 这个属性,首先要讲明白一点,那就是 Slice 不是帧。我们经常在博客中看到一些说法,例如:这个 NALU 是一个 I 帧,这个 NALU 是一个 P 帧。其实这些说法严格意义上来说都是不太对的。
在编码的时候,一帧图像首先会被分割成若干宏块,然后对每个宏块进行编码压缩,之后,会将宏块写入到 Slice 里面,最后再加上一些修饰变成 NALU。这个过程中有一点要注意,那就是一帧图像产生宏块,不一定要写到一个 Slice 里面。
例如,我将一帧图像编码成了 99 个宏块,我可以把这 99 个宏块都写到一个 Slice 里面,也可以把 0 - 30 的宏块写到一个 Slice 里面,然后把 31-99 的宏块写到下一个宏块里。因此,first_mb_in_slice 这个属性要描述的,就是在当前这个 Slice 里面的第一个宏块,对应的是帧里的第几个宏块。
举个例子:
在上图中,我们把一帧图像分割成序号为 0-23 共计 24 个宏块。我们把蓝色背景的宏块放到一个 Slice 里面,这个 Slice 的 first_mb_in_slice 就是 0。然后我们把红色背景的宏块放到另外一个 Slice 里面,这个 Slice 的 first_mb_in_slice 就是 14。
在 first_mb_in_slice 属性之后,就是 slice_type 了。很明显,这个属性来指明的是 slice 的类型。这个类型的可能的值如下表:
值 | 含义 |
---|---|
0 | P(P Slice) |
1 | B(B Slice) |
2 | I(I Slice) |
3 | SP(SP Slice) |
4 | SI(SI Slice) |
5 | P(P Slice) |
6 | B(B Slice) |
7 | I(I Slice) |
8 | SP(SP Slice) |
9 | SI(SI Slice) |
注意,这张表里的 slice_type 其实 5-9 和 0-4 是完全相同的,因此在使用的时候,我们也常常直接用 slice_type 对 5 去余来处理。
可以看到 slice_type 规定了 slice 的类型,这也解释了之前的一些误区。有人说根据 NALU 的类型就能判读是 I Slice 还是 P Slice 还是 B Slice。其实是错误的,要判断这些必须要读到 slice_type 才行。
slice_type 之后,是 pic_parameter_set_id,这个属性表明该 Slice 要依赖的 PPS 的 id。我们在解析 PPS 的时候,PPS 里面有一个 pic_parameter_set_id 的属性。当你解析出 Slice 中的 pic_parameter_set_id 之后,拿着这个 id 找到与之对应的 PPS 就可以了。然后 PPS 里还有个 seq_parameter_set_id,用这个 id 就可以找到依赖的 SPS 的 id。这样,你就可以为这个 Slice 查找到合适的 SPS 和 PPS 了。
643401840@qq.com
2022-03-23 19:21:27
zekun@sina.cn
2021-12-23 15:44:12