2. H265编码基础回顾:编码结构(GOP、帧类型)、NAL单元与RTP封装

好,咱们正式开始聊H265在实时通信里的那些事。在深入丢包恢复之前,我觉得有必要先把H265的编码基础捋一遍。说白了,你得知道视频是怎么被切开的,每个切片长什么样,又是怎么塞进网络包里的。这些基础不牢,后面讲抗丢包、FEC、重传,你听着会有点飘。

我个人习惯,每次接手一个新项目,第一件事就是翻翻编码器的配置。嗯,看看GOP结构、帧类型分布,心里就有底了。今天这一章,我们就从这三个角度切入:GOP与帧类型、NAL单元结构、以及RTP封装。

2.1 GOP结构与帧类型

先说说GOP,也就是图像组。你可以把它理解成一个视频的“章节”。一个GOP里包含若干帧,这些帧之间通过预测关系连在一起。

H265里常见的帧类型有三种:

  • I帧(IDR帧):关键帧,不依赖任何其他帧。解码器拿到I帧就能独立解码。我遇到过不少新手,以为I帧越多越好,结果带宽全被I帧吃掉了。
  • P帧:前向预测帧,依赖前面的I帧或P帧。说白了,它只记录变化的部分。
  • B帧:双向预测帧,依赖前后两个方向的帧。压缩率最高,但延迟也最大。实时通信里,我一般建议少用甚至不用B帧。

为什么会这样?因为B帧需要等待后面的帧才能解码,这在实时场景里是致命的。你想想看,一个视频通话,如果因为B帧导致延迟增加200ms,用户体验直接崩了。

实时通信中的GOP建议:

  • GOP长度:建议2-5秒。太长会导致IDR帧间隔过大,丢包后恢复慢;太短则带宽浪费。
  • 帧类型:只使用I帧和P帧,禁用B帧。或者使用低延迟B帧(LDB)配置。
  • IDR帧插入:在关键事件(如画面切换、用户加入)时强制插入IDR帧。

我曾经在一个视频会议项目里,默认GOP设成了10秒。结果网络一波动,用户画面卡住10秒才恢复。后来改成2秒的GOP,配合丢包重传,恢复时间降到了1秒以内。

2.2 NAL单元结构

H265的码流是由一个个NAL单元组成的。NAL,全称是Network Abstraction Layer,网络抽象层。它的作用就是把编码后的视频数据包装成适合网络传输的小块。

每个NAL单元包含两部分:

  • NAL Header(2字节):包含NAL单元类型、层ID、时间ID等信息。
  • RBSP(Raw Byte Sequence Payload):实际的编码数据。

H265的NAL单元类型比H264多得多。我整理了一个常用的表格:

NAL类型 类型值 描述 实时通信中的重要性
VPS 32 视频参数集 必须,描述整体编码参数
SPS 33 序列参数集 必须,描述序列级参数
PPS 34 图像参数集 必须,描述图像级参数
IDR 19 瞬时解码刷新帧 关键,用于随机访问和错误恢复
BLA 16/17/18 断链访问帧 用于码流切换
非IDR 1 非关键帧(P/B帧) 常规帧数据
EOS 36 码流结束 可选

这里有个坑,我必须要提醒你。VPS、SPS、PPS这三个参数集,在实时通信中必须可靠传输。一旦丢失,解码器就无法正确解码后续的所有帧。我曾经在一个项目中,因为参数集丢包,导致整个会话花屏了3秒,直到下一个IDR帧才恢复。

注意:在实时通信中,建议将VPS、SPS、PPS放在IDR帧之前,并且使用带外传输或重复发送机制。很多WebRTC实现会把这些参数集放在SDP中传输,或者在每个关键帧前重复发送。

2.3 RTP封装

好了,现在视频数据已经被切成了NAL单元。接下来要把它塞进RTP包里,才能在网络上传输。

H265的RTP封装遵循RFC 7798。核心规则是:

  • 单NAL单元包:一个RTP包只包含一个NAL单元。适用于小尺寸的NAL单元。
  • 聚合包(AP):多个小NAL单元合并到一个RTP包里。减少包头开销。
  • 分片包(FU):一个大NAL单元拆成多个RTP包。适用于超过MTU的NAL单元。

我画了一张图,帮你理清这个封装流程:

H265 RTP封装流程 H265编码器 NAL单元流 封装决策 单NAL包 聚合包(AP) 分片包(FU) RTP包 网络传输 封装规则 • 单NAL: 尺寸 < MTU • 聚合: 多个小NAL • 分片: 尺寸 > MTU • MTU通常=1500字节

在实际的实时通信系统中,我建议你重点关注分片包(FU)的处理。因为网络MTU通常是1500字节,而一个H265的NAL单元,尤其是IDR帧,很容易超过这个大小。

实战技巧:在实现RTP打包时,注意以下几点:

  • 分片包的起始标记(Start bit)和结束标记(End bit)必须正确设置,否则解码器无法重组。
  • 聚合包中的每个NAL单元都需要包含长度字段,方便解码器解析。
  • RTP时间戳应该基于帧的采样时间,而不是NAL单元的发送时间。

我记得有一次调试一个视频卡顿问题,发现是RTP时间戳设置错了。发送端把每个分片包的时间戳都设成了当前时间,结果接收端以为这些分片属于不同的帧,直接导致画面撕裂。嗯,这个坑踩过一次就再也不会犯了。

2.4 本章小结

好了,这一章的内容就到这里。我们回顾了H265的GOP结构、帧类型、NAL单元和RTP封装。这些是后续讨论丢包恢复的基础。你想想看,如果连NAL单元怎么分片、RTP时间戳怎么设置都不清楚,那后面讲FEC、重传、参考帧选择,你肯定听得云里雾里。