1. 背景
H.264(又称 AVC,Advanced Video Coding)是一种广泛应用的视频压缩标准,具有较高的压缩效率和良好的视频质量。
在开发过程中,时常会遇到采用H264编码的视频流传输和播放有关的音视频问题,尤其是在初期会遇视频无法播放的问题:
- 采集端编码问题(原始H164视频流就是错的)?
- 播放端没接收到视频流(传输过程有问题)?
- 播放端解码有问题(接收到的H264视频流是错的)?
下面通过对H264数据裸流的解析来加深对H264视频流的理解,以助于在遇到此类问题时快速发现问题所在。
2. 编码数据裸流
H264 的码流结构它主要有两种格式:Annex B 和 AVCC。Annex B 格式以 0x000001 或 0x00000001 开头,AVCC 格式以所在的 NALU 的长度开头,以 Annex B 为例。
[图片上传失败...(image-3032a9-1742465023083)]
对于一个 H.264 裸流来说,就是一系列 NALU 的集合 ,每个 NALU 既可以表示图像数据,也可以表示处理图像所需要的参数数据。
[图片上传失败...(image-a43f39-1742465023083)]
3. NALU
NALU(Network Abstraction Layer Unit,网络抽象层单元)是H.264编码标准中的一种数据封装结构,用于将编码后的视频数据组织成便于传输和存储的格式。NALU在H.264流媒体传输和存储中起到了关键作用
NALU头部是一个1字节(8位)的字段,其结构如下:
| 位数 | 名称 | 含义 |
|---|---|---|
| 1bit | forbidden_zero_bit | 始终为0,若为1表示数据错误 |
| 2bits | nal_ref_idc | 表示参考帧优先级(0~3) |
| 5bits | nal_unit_type | 表示NALU的类型(如I帧、P帧等) |
示例
也就是说看到的 67、47、27都是PPS,只是优先级不同
| nal_ref_idc | nal_unit_type | NALU头部 (十六进制) | 含义 |
|---|---|---|---|
| 3(最高优先级) | 7 (SPS) | 67 | 通常的 SPS |
| 2(较高优先级) | 7 (SPS) | 47 | 也是 SPS,但优先级较低 |
| 1(次优先级) | 7 (SPS) | 27 | SPS,最低优先级 |
| 3(最高优先级) | 5 (IDR帧) | 65 | 通常的 IDR帧 |
4. NALU类型(nal_unit_type)
NALU类型决定了该单元的功能,常见类型包括:
| 类型编号 | 名称 | 描述 |
|---|---|---|
| 1 | 非IDR图像片段 | 普通P帧、B帧的编码数据 |
| 5 | IDR图像片段 | 关键帧(I帧),用于随机访问 |
| 6 | SEI信息 | 补充增强信息(如时间戳、特效等) |
| 7 | SPS | 序列参数集,描述视频基本参数(如分辨率) |
| 8 | PPS | 图像参数集,描述编码块等局部信息 |
| 9 | 分隔符 | 标志帧间的边界 |
5. Web端接收到的数据
SPS
帧1
[图片上传失败...(image-a220a7-1742465023083)]
PPS
帧2
[图片上传失败...(image-8204f5-1742465023083)]
IDR
帧3
[图片上传失败...(image-78758f-1742465023083)]
P
帧4
[图片上传失败...(image-145c02-1742465023083)]
[图片上传失败...(image-fa4ddf-1742465023083)]
6. 帧的类型
必须的帧:
1. SPS (Sequence Parameter Set) - 帧 1:
- NALU 类型: 7(67/47/27)
- 内容: 0000 0001 6764 0033 acb4 0220 096f 2f29 ...
- 作用: SPS 帧包含了视频序列的全局参数,例如视频的分辨率、编码级别、色彩格式等。解码器需要这些参数来正确解码视频流。
- 是否必须: 必须。SPS 是初始化解码器所需的基本信息,缺少这个帧,解码器无法正确解码后续的帧。
2. PPS (Picture Parameter Set) - 帧 2:
- NALU 类型: 8(68/48/28)
- 内容: 0000 0001 68ee 06f2 c0 ...
- 作用: PPS 帧包含了特定帧的编码参数,例如宏块类型、预测模式等,通常与 SPS 一起使用。
- 是否必须: 必须。PPS 是解码每个视频帧时所需的参数,解码器需要它来正确解码和播放视频。
3. IDR (Instantaneous Decoder Refresh) Frame - 帧 3:
- NALU 类型: 5(65/45/25)
- 内容: 0000 0001 65b8 40f7 fbf4 689f 0291 9990 ...
- 作用: IDR 帧是一个关键帧,解码器可以从这个帧开始重置并独立解码,不依赖于之前的帧。
- 是否必须: 必须。IDR 帧是解码器重新同步视频流的关键,尤其是在随机访问点开始播放时。如果缺少这个帧,解码器可能会出现图像破损或无法正确播放视频。
非必须的帧:
4. P Frame - 帧 4:
- NALU 类型: 1(61/41/21)
- 内容: 0000 0001 41e2 1822 ffda 6ac0 007d 38a9 ...
- 作用: P 帧是一种预测帧,依赖之前的帧(IDR 或其他 P 帧)进行解码,它保存了相对于参考帧的变化信息。
- 是否必须: 非必须。虽然 P 帧对于保持视频流的连续性很重要,但它并不是解码器必须的帧。如果缺少 P 帧,视频可能会出现丢帧现象,但不会完全无法播放。
7. 帧的顺序:67->68->65->41
必须的顺序: SPS → PPS → IDR。
连续性: 这三者通常是连续出现的,确保解码器在收到 SPS 和 PPS 之后可以立即开始解码 IDR 帧。
1. SPS (Sequence Parameter Set)
- 顺序: 必须第一个出现。
- 原因: SPS 帧提供了全局参数,解码器需要这些参数来初始化解码器的状态,例如分辨率、编码级别等。解码器在收到 SPS 之前无法正确解码后续的任何帧。
2. PPS (Picture Parameter Set)
- 顺序: 紧随 SPS 之后出现。
- 原因: PPS 帧提供了特定帧的编码参数,解码器需要这些参数来解码实际的视频帧。PPS 通常在 SPS 之后出现,以便解码器在解码视频数据之前已具备所有必要的配置信息。
3. IDR (Instantaneous Decoder Refresh) Frame
- 顺序: 在 SPS 和 PPS 之后出现。
- 原因: IDR 帧是关键帧,解码器从这个帧开始解码视频流。IDR 帧依赖于之前提供的 SPS 和 PPS 来正确解码视频内容。
8. 总结
这样只需要查看采集端编码后和接收端接收到的H264裸流,是否符合上面所说的情况,缺少关键帧、没有有起始码、帧顺序不对所产生的问题都一目了然。