4. 编解码器入门

H.264,也叫AVC,是现在流媒体领域最通用的编码格式。没有之一。

我做了这么多年流媒体,见过各种编码格式起起落落,但H.264始终是那个“万金油”。RTSP推流里,90%以上的场景都在用它。为什么?兼容性好,压缩率高,而且专利池虽然复杂,但终端支持度极高。

这一章,我们就把H.264的核心掰开揉碎了讲。不扯玄乎的理论,只讲你在RTSP推流中真正用得上的东西。

4.1 H.264编码标准概览

H.264是ITU-T和ISO/IEC联合制定的视频编码标准。它的核心思路很简单:用更少的比特,传更好的画质。

相比上一代MPEG-4 Part 2,H.264在同等画质下能节省30%-50%的码率。这个提升是革命性的。我记得2010年左右,很多监控系统还在用MPEG-4,码率动不动就8Mbps,换成H.264后直接降到3Mbps,画质还更好。

H.264的核心技术亮点:

  • 帧内预测:利用空间冗余,减少单帧内的数据量
  • 帧间预测:利用时间冗余,通过运动估计压缩连续帧
  • 变换编码:4x4整数DCT变换,减少块效应
  • 熵编码:CAVLC和CABAC两种模式,CABAC压缩率更高
  • 去块效应滤波器:平滑块边界,提升主观画质

说白了,H.264就是一套“找规律”的算法。它把视频拆成一个个小块,然后找出这些块之间的相似性,只记录差异部分。你想想看,一段静止的监控画面,背景几乎不变,H.264只需要传一次背景,后面只传变化的部分——码率自然就下来了。

4.2 NAL单元结构

NAL,全称Network Abstraction Layer,网络抽象层。这是H.264设计里最巧妙的部分之一。

为什么要有NAL?因为H.264的设计者从一开始就考虑到了网络传输。他们把编码后的数据打包成一个个NAL单元,每个单元都可以独立在网络中传输。RTSP推流时,RTP包里面装的就是这些NAL单元。

4.2.1 NAL单元头部

每个NAL单元都有一个1字节的头部,结构如下:

+---------------+---------------+
|0|1|2|3|4|5|6|7|
+---+-----------+
|F| NRI |  Type  |
+---+-----------+
  • F (forbidden_zero_bit):1位,必须为0。如果为1,表示这个NAL单元有错误。
  • NRI (nal_ref_idc):2位,表示这个NAL单元的优先级。00表示不重要,11表示最重要。
  • Type (nal_unit_type):5位,表示NAL单元的类型。

我的经验:在RTSP推流中,如果网络状况不好,我会根据NRI值做丢包策略。NRI为00的SEI或非参考帧的Slice,可以优先丢弃。NRI为11的SPS、PPS和IDR帧,必须保证送达。这个策略我在一个4G弱网环境下用过,效果很明显。

4.2.2 常见的NAL单元类型

Type值 NAL类型 说明 重要性
1 非IDR的Slice 普通视频帧数据 中
5 IDR的Slice 关键帧,解码器从这里开始刷新 高
6 SEI 补充增强信息,如时间戳、字幕 低
7 SPS 序列参数集,包含分辨率、帧率等 最高
8 PPS 图像参数集,包含编码模式、熵编码类型 最高

这里我要强调一下SPS和PPS。这两个NAL单元是解码器的“说明书”。没有它们,解码器根本不知道该怎么解码。在RTSP推流中,SPS和PPS通常通过SDP描述信息传输,或者在RTP流的开头先发送。

我曾经踩过的坑:有一次做RTSP推流,客户端一直黑屏,查了半天发现是SPS和PPS只在SDP里传了一次,但客户端在连接过程中重新协商了参数,导致解码器丢失了SPS/PPS。后来我改成在RTP流中定期插入SPS和PPS,问题就解决了。建议你在推流时,每隔几秒就重复发送一次SPS和PPS,确保新加入的客户端能正常解码。

4.3 Profile与Level选择

Profile和Level是H.264的两个重要参数。它们决定了编码器的能力和复杂度。

4.3.1 Profile(档次)

Profile定义了编码器支持的工具集。档次越高,工具越多,压缩率也越高,但解码器的负担也越重。

Profile 特点 适用场景
Baseline 支持I/P帧,仅CAVLC,无B帧 视频会议、移动设备、低延迟场景
Main 支持I/P/B帧,CAVLC和CABAC 标准广播、存储
High 支持8x8变换、自定义量化矩阵 高清视频、蓝光、流媒体

我个人习惯在RTSP推流中使用High Profile。为什么?因为压缩率更高。在同样的码率下,High Profile的画质明显优于Baseline。但要注意,一些老旧的嵌入式设备可能不支持High Profile解码。

4.3.2 Level(级别)

Level定义了编码器的性能上限,包括分辨率、帧率、码率等。

Level 最大分辨率 最大帧率 最大码率
3.0 720x576 30fps 10Mbps
3.1 1280x720 30fps 14Mbps
4.0 1920x1080 30fps 20Mbps
4.1 1920x1080 60fps 50Mbps
5.0 3840x2160 30fps 135Mbps

我的建议:选择Level时,不要只看分辨率。还要考虑帧率和码率。比如你推1080p 60fps的视频,Level至少要到4.1。如果码率超过20Mbps,也要相应提高Level。我一般会留20%的余量,避免编码器在极限情况下出现异常。

4.4 H.264在RTSP中的应用

RTSP推流中,H.264的传输主要涉及两个层面:SDP描述和RTP封装。

4.4.1 SDP中的H.264描述

在RTSP的DESCRIBE响应中,SDP会描述H.264的编码参数。一个典型的SDP片段如下:

m=video 0 RTP/AVP 96
a=rtpmap:96 H264/90000
a=fmtp:96 packetization-mode=1;profile-level-id=64001E;sprop-parameter-sets=Z2QAHqw2AoAB9wEAAAMA,KOIMgA==
  • packetization-mode:1表示使用非交错模式,这是RTSP推流最常用的模式。
  • profile-level-id:16进制编码的Profile和Level。64001E表示High Profile,Level 3.0。
  • sprop-parameter-sets:Base64编码的SPS和PPS,用逗号分隔。

4.4.2 RTP封装

H.264的RTP封装遵循RFC 3984标准。主要有三种模式:

  • 单一NAL单元模式:一个RTP包只装一个NAL单元。适用于小尺寸的NAL单元。
  • 聚合模式:多个小NAL单元合并到一个RTP包中。适用于SEI、SPS、PPS等小数据。
  • 分片模式:一个大NAL单元拆成多个RTP包。适用于IDR帧等大数据。

在实际的RTSP推流中,我几乎只用单一NAL单元模式和分片模式。聚合模式用得少,因为SPS和PPS我都是单独发送的。

RTSP推流中H.264的关键配置:

  • Profile:推荐High Profile,兼容性好的场景用Baseline
  • Level:根据分辨率和帧率选择,留20%余量
  • GOP大小:建议2-5秒一个IDR帧,太大会导致seek延迟
  • B帧数量:RTSP推流建议不超过2个B帧,太多会增加延迟
  • 码率控制:推荐CBR(恒定码率),避免网络抖动

4.5 H.264编码流程与NAL单元生成

为了让你更直观地理解H.264在RTSP中的工作流程,我画了一张图。

H.264编码与NAL单元生成流程 原始视频帧 H.264编码器 编码后的数据 NAL封装 SPS/PPS生成 SPS/PPS NAL NAL封装 RTP封装 RTSP推流 关键说明: 1. SPS/PPS在编码开始时生成,之后可定期重复发送 2. IDR帧是解码器的同步点,GOP从IDR开始 3. 非IDR帧依赖前面的参考帧,丢包会导致花屏 4. RTP封装时,大NAL单元需要分片传输

这张图展示了从原始视频帧到RTSP推流的完整流程。你可以看到,SPS和PPS是独立于视频帧生成的,它们在整个推流过程中只需要发送一次(但建议定期重复)。

4.6 实战建议

最后,我总结几条在RTSP推流中使用H.264的实战建议:

  1. Profile选择:优先用High Profile。如果遇到解码兼容性问题,降级到Baseline。
  2. GOP设置:2-5秒一个IDR帧。太短会浪费码率,太长会导致seek延迟。
  3. B帧控制:RTSP推流建议用0-2个B帧。B帧越多,延迟越高。
  4. 码率控制:用CBR模式。VBR虽然画质好,但码率波动大,容易导致网络拥塞。
  5. SPS/PPS重复:每隔3-5秒重复发送一次SPS和PPS,确保新客户端能正常解码。
  6. NAL单元分片:IDR帧如果太大,一定要用分片模式传输,避免RTP包超过MTU。

我曾经踩过的坑:有一次在弱网环境下做RTSP推流,我用了VBR码率控制。结果画面剧烈变化时,码率瞬间飙升到30Mbps,直接把网络打爆了。后来我改成CBR,虽然画质略有下降,但推流稳定多了。在RTSP推流中,稳定性永远比画质重要。