原文
1。H.264 优点
- 网络亲和性,即可适用于各种传输网络
- 高的视频压缩比
2。H.264 其编解码流程
- 帧间和帧内预测(Estimation)
- 变换(Transform)和反变换
- 量化(Quantization)和反量化
- 环路滤波(Loop Filter)
- 熵编码(Entropy Coding)
3。原理:
H.264 原始码流(又称为裸流)
- 组成:是有一个接一个的 NALU( NAL 单元) 组成的,每个单元都按独立的 NALU 传送
功能
- 视频编码层(VCL, Video Coding Layer)
- VCL 数据即编码处理后的输出,它表示被压缩编码后的视频数据序列
网络提取层(NAL, Network Abstraction Layer)
在 VCL 数据传输或存储之前,这些编码的 VCL 数据,先被映射或封装进 NAL 单元(以下简称 NALU,Nal Unit) 中
- NAL 单元(以下简称 NALU,Nal Unit)的组成
- 一个原始字节序列负荷(RBSP, Raw Byte Sequence Payload)
- RBSP 的基本结构是:在原始编码数据的后面填加了结尾比特(一个 bit“1”若干比特“0”,以便字节对齐)
- 一组对应于视频编码的 NALU 头部信息
- 一个原始字节序列负荷(RBSP, Raw Byte Sequence Payload)
- NAL 单元(以下简称 NALU,Nal Unit)的组成
- 视频编码层(VCL, Video Coding Layer)
如图所示:
NALU (Nal Unit) = NALU头 + RBSP。
H.264 中的结构全部都是以 NALU 为主的,理解了 NALU,就理解 H.264 的结构了。
一帧图片跟 NALU 的关联 :
一帧图片经过 H.264 编码器之后,就被编码为一个或多个片(slice),而装载着这些片(slice)的载体,就是 NALU 了,我们可以来看看 NALU 跟片的关系(slice)
片(slice)的概念不同与帧(frame):
- 帧(frame)是用作描述一张图片的,一帧(frame)对应一张图片
- 片(slice),是 H.264 中提出的新概念,是通过编码图片后切分通过高效的方式整合出来的概念,一张图片至少有一个或多个片(slice)
上图中可以看出,片(slice)都是由 NALU 装载并进行网络传输的,但是这并不代表 NALU 内就一定是切片,这是充分不必要条件,因为 NALU 还有可能装载着其他用作描述视频的信息。
什么是切片(slice)?
是。片之所以被创造出来,是为。如何限制误码的扩散和传输?
- 主要作用:
- 用作宏块(Macroblock)的载体
- 主要目的
- 限制误码的扩散和传输
- 如何限制误码的扩散和传输:
- 每个片(slice)都应该是互相独立被传输的,某片的预测不能以其它片中的宏块(Macroblock)为参考图像。
- 片的预测= 片内预测 +片间预测
- 每个片(slice)都应该是互相独立被传输的,某片的预测不能以其它片中的宏块(Macroblock)为参考图像。
片(slice)的具体结构
- 一 张/帧 图片 = n个分片(n>=1)
- 1个分片 = n个宏块(Macroblock) (n>=1)
上图结构中,我们不难看出,每个分片也包含着头和数据两部分:
分片头中= 分片类型、分片中的宏块类型、分片帧的数量、分片属于那个图像以及对应的帧的设置和参数等信息。
分片数据中则是宏块,这里就是我们要找的存储像素数据的地方
什么是宏块?
宏块是视频信息的主要承载者,因为它包含着每一个像素的亮度和色度信息。视频解码最主要的工作则是提供高效的方式从码流中获得宏块中的像素阵列。
组成部分:
- 宏块= 一个16×16亮度像素+ 一个8×8 Cb + 一个 8×8 Cr 彩色像素块。
- 每个图象中,若干宏块被排列成片的形式。
宏块的结构图:
从上图中,可以看到,宏块中包含了宏块类型、预测类型、Coded Block Pattern、Quantization Parameter、像素的亮度和色度数据集等等信息。
切片(slice)类型跟宏块类型的关系
对于切片(slice)来讲,分为以下几种类型:
0 P-slice. Consists of P-macroblocks (each macro block is predicted using one reference frame) and / or I-macroblocks.
1 B-slice. Consists of B-macroblocks (each macroblock is predicted using one or two reference frames) and / or I-macroblocks.
2 I-slice. Contains only I-macroblocks. Each macroblock is predicted from previously coded blocks of the same slice.
3 SP-slice. Consists of P and / or I-macroblocks and lets you switch between encoded streams.
4 SI-slice. It consists of a special type of SI-macroblocks and lets you switch between encoded streams.
P片:可包 P和I宏块,P 宏块利用前面已编码图象作为参考图象进行帧内预测,一个帧内编码的宏块可进一步作宏块的分割:即 16×16、16×8、8×16 或 8×8 亮度像素块(以及附带的彩色像素);如果选了 8×8 的子宏块,则可再分成各种子宏块的分割,其尺寸为 8×8、8×4、4×8 或 4×4 亮度像素块(以及附带的彩色像素)。
B片:可包 B和I宏块,B 宏块则利用前后向的参考图象进行帧内预测。
I片:只包 I宏块,I 宏块只能利用当前片中已解码的像素作为参考进行帧内预测(不能取其它片中的已解码像素作为参考进行帧内预测)。
SP片(切换P):用于不同编码流之间的切换,包含 P 和/或 I 宏块
SI片:扩展档次中必须具有的切换,它包 了一种特殊类型的编码宏块,叫做 SI 宏块,SI 也是扩展档次中的必备功能。
整体结构
其实 H.264 的码流结构并没有大家想的那么复杂,编码后视频的每一组图像(GOP,图像组)都给予了传输中的序列(PPS)和本身这个帧的图像参数(SPS),所以,我们的整体结构,应该如此:
GOP (图像组)主要用作形容一个 i 帧 到下一个 i 帧之间的间隔了多少个帧,增大图片组能有效的减少编码后的视频体积,但是也会降低视频质量,至于怎么取舍,得看需求了。
其他
NALU 中究竟有哪几种类型,我们来看看 H.264 中源码对 nal_unit_type_e 中的定义
enum nal_unit_type_e
{
NAL_UNKNOWN = 0, // 未使用
NAL_SLICE = 1, // 不分区、非 IDR 图像的片(片的头信息和数据)
NAL_SLICE_DPA = 2, // 片分区 A
NAL_SLICE_DPB = 3, // 片分区 B
NAL_SLICE_DPC = 4, // 片分区 C
NAL_SLICE_IDR = 5, / ref_idc != 0 / // IDR 图像中的片
NAL_SEI = 6, / ref_idc == 0 / // 补充增强信息单元
-
参数集是 H.264 标准的一个新概念,是一种通过改进视频码流结构增强错误恢复能力的方法。
NAL_SPS = 7, // 序列参数集 (包括一个图像序列的所有信息,即两个 IDR 图像间的所有图像信息,如图像尺寸、视频格式等)
NAL_PPS = 8, // 图像参数集 (包括一个图像的所有分片的所有相关信息, 包括图像类型、序列号等,解码时某些序列号的丢失可用来检验信息包的丢失与否)
-
NAL_AUD = 9, // 分界符
NAL_FILLER = 12, // 填充(哑元数据,用于填充字节)
/ ref_idc == 0 for 6,9, 10 (表明下一图像为 IDR 图像),11(表明该流中已没有图像),12 /
};
ps: 以上括号()中的为类型描述
上面NALU类型当中,分片/切片(slice)的概念我们都已经很清楚了,但是用 NALU 作载体的还有 SEI、SPS、PPS 等等。
今天我们不一一聚述这些类型对整个流程的作用了,我们挑出两个符合我们今天主题的类型来讲,PPS 和 SPS。
那么今天我们讲的 H.264 的码流结构相信大家都有个大概轮廓的了解了,总结的一句话就是:
H.264 中,句法元素共被组织成 序列、图像、片、宏块、子宏块五个层次。
文章的最后为了方便小伙伴学习,本博主用 ffmpeg 在 iOS 设备写了一个实例代码,主要功能是利用摄像头实时录制 yuv 格式文件并编码 H.264 后,实时分析 H.264 中每个帧(frame)中的 NALU 的数据,希望对小伙伴们有所帮助,找到入门的方法。
项目地址『所有代码只用做学习只用,并不提供商业用途,如果违反,必究。』)