RTP 与 WebRTC 核心逻辑详解:从 `rtph264pay pt=96` 讲清实时音视频传输
📺 B站 嵌入式孙老师:博主个人介绍
📘 博主书籍-京东购买链接*:Yocto项目实战教程
📘 加博主微信,进技术交流群: jerrydev
RTP 与 WebRTC 核心逻辑详解:从 rtph264pay pt=96 讲清实时音视频传输
在音视频开发中,经常会看到这样的 GStreamer 命令:
gst-launch-1.0 videotestsrc is-live=true \
! x264enc tune=zerolatency key-int-max=30 \
! h264parse config-interval=1 \
! rtph264pay pt=96 \
! udpsink host=127.0.0.1 port=5004
很多人能把命令跑起来,但不一定真正理解:
pt=96 是什么意思?
为什么接收端要写 payload=96?
clock-rate=90000 是什么意思?
sequence 和 timestamp 谁负责?
SSRC 有什么用?
marker 标记什么?
rtph264pay 到底做了什么?
这些问题,正是理解 RTP 和 WebRTC 的关键。
一句话先建立主线:
RTP 是实时音视频包格式。
WebRTC 是基于 RTP/RTCP,并增加信令、ICE、DTLS-SRTP、拥塞控制的低延迟通信协议栈。
RTP 告诉接收端:这个包属于哪一路流、是什么编码、排第几个、应该什么时候播放。你的 RTP 笔记中也明确指出,RTP 是“实时音视频包”的格式,它负责描述媒体流、编码类型、包顺序和播放时间。

一、先从一条 GStreamer 命令讲清 RTP 主线
先看发送端:
gst-launch-1.0 videotestsrc is-live=true \
! x264enc tune=zerolatency key-int-max=30 \
! h264parse config-interval=1 \
! rtph264pay pt=96 \
! udpsink host=127.0.0.1 port=5004
这条命令可以拆成 5 层:
videotestsrc
-> 生成原始视频帧
x264enc
-> 编码成 H264
h264parse
-> 整理 H264 码流,处理 SPS/PPS
rtph264pay
-> 把 H264 封装成 RTP 包
udpsink
-> 通过 UDP 发送出去
最关键的是这一句:
! rtph264pay pt=96 \
它表示:
把 H264 码流封装成 RTP 包,并把 RTP 头部里的 Payload Type 设置为 96。
也就是说,rtph264pay 做的不是编码,它做的是 RTP 封包。
编码器输出的是 H264 码流:
H264 NALU
H264 NALU
H264 NALU
rtph264pay 之后变成:
RTP Header + H264 Payload
RTP Header + H264 Payload
RTP Header + H264 Payload
接收端命令:
gst-launch-1.0 udpsrc port=5004 caps="application/x-rtp,media=video,encoding-name=H264,payload=96,clock-rate=90000" \
! rtph264depay \
! h264parse \
! avdec_h264 \
! autovideosink
接收端这里必须写:
payload=96
clock-rate=90000
encoding-name=H264
原因很简单:RTP 包头里只有 payload type = 96,但 96 本身只是一个动态编号,接收端必须通过 caps 或 SDP 知道 96 对应 H264/90000。你的 RTP 笔记中也特别强调:RTP 包里只有 payload type = 96,接收端必须知道 96 代表 H264/90000。
二、pt=96 到底是什么意思?
pt 是 Payload Type 的缩写,中文可以理解为“负载类型”。
RTP 包分为两部分:
RTP Header
+ RTP Payload
RTP Header 中有一个字段叫 Payload Type:
Payload Type:
告诉接收端 RTP Payload 里面装的是什么编码数据。
比如:
PT = 96
Payload = H264 数据
PT = 97
Payload = H265 数据
PT = 111
Payload = Opus 音频数据
但是要注意:96、97、98 这类编号不是天然固定的。
RTP 中的 payload type 分为两类:
静态 Payload Type:
一些老格式曾经有固定编号。
动态 Payload Type:
96~127 通常用于动态协商。
在现代音视频系统中,H264、H265、VP8、Opus 等经常使用动态 PT。比如:
payload type 96 可以是 H264。
payload type 96 也可以在另一个会话里表示 VP8。
所以,pt=96 真正表达的是:
发送端把当前 RTP 包的 payload type 字段设置成 96。
至于 96 代表什么,必须由 SDP 或 caps 告诉接收端。
这就是为什么发送端写:
rtph264pay pt=96
接收端必须对应写:
payload=96
encoding-name=H264
clock-rate=90000
如果发送端是:
rtph264pay pt=98
接收端就必须改成:
gst-launch-1.0 udpsrc port=5004 caps="application/x-rtp,media=video,encoding-name=H264,payload=98,clock-rate=90000" \
! rtph264depay \
! h264parse \
! avdec_h264 \
! autovideosink
如果发送端 pt=98,接收端仍然写 payload=96,就可能出现:
UDP 包收到了。
RTP 包也到了。
但是 depayloader 不认这个 payload。
最终表现为黑屏或无法解码。
所以面试时可以这样回答:
pt 是 RTP Payload Type,用来标识 RTP payload 的编码类型。
96 是动态 payload type,必须通过 SDP 或 caps 映射成 H264/90000。
发送端 pt 和接收端 payload 必须一致,否则会出现有包但无法解码的问题。
三、clock-rate=90000 是什么意思?
接收端 caps 中还有一个关键参数:
clock-rate=90000
它表示 RTP timestamp 的时钟频率。
对视频来说,H264/H265 RTP 常用 90000Hz 时钟。可以理解为:
RTP timestamp 每秒走 90000 个单位。
如果视频是 30fps:
每帧时间 = 1 / 30 秒
每帧 timestamp 增量 = 90000 / 30 = 3000
所以连续三帧可能是:
第 1 帧 timestamp = 90000
第 2 帧 timestamp = 93000
第 3 帧 timestamp = 96000
如果是 60fps:
每帧 timestamp 增量 = 90000 / 60 = 1500
也就是:
第 1 帧 timestamp = 90000
第 2 帧 timestamp = 91500
第 3 帧 timestamp = 93000
这里要分清:
clock-rate 不是视频帧率。
clock-rate 是 RTP timestamp 使用的时间基准。
帧率是:
30fps
60fps
clock-rate 是:
H264 视频常用 90000。
音频则通常和采样率相关,例如 Opus 常见 48000。
RTP timestamp 的作用是让接收端知道什么时候播放这一帧。你的 RTP 笔记中也说明,30fps H264 时 timestamp 每帧约增加 3000,同一帧拆成多个 RTP 包时 timestamp 不变。
四、sequence、timestamp、marker:RTP 最重要的三个运行参数
一个 H264 帧可能很大,不能放进一个 UDP 包,所以需要拆成多个 RTP 包。
假设一帧 H264 的 RTP timestamp 是 90000,拆成三个 RTP 包:
RTP 包 1:
sequence = 100
timestamp = 90000
marker = 0
RTP 包 2:
sequence = 101
timestamp = 90000
marker = 0
RTP 包 3:
sequence = 102
timestamp = 90000
marker = 1
这三个字段的关系非常重要。
1. sequence:包序号
sequence 是 RTP 包级别的序号。
100 -> 101 -> 102 -> 103
它用于判断:
有没有丢包?
有没有乱序?
有没有重复包?
例如:
正常:
100, 101, 102, 103
丢包:
100, 101, 105
乱序:
100, 102, 101
如果 sequence 断了,可能出现:
花屏
卡顿
马赛克
关键帧丢失后长时间不恢复
2. timestamp:媒体播放时间
timestamp 是媒体时间,不是包序号。
同一帧拆成多个 RTP 包时:
sequence 不同。
timestamp 相同。
不同帧之间:
timestamp 按帧间隔递增。
所以:
sequence 解决“包顺序”。
timestamp 解决“播放时间”。
3. marker:一帧结束标记
对于视频 RTP,marker 常用于标识一帧结束。
marker = 0:
当前 RTP 包不是这一帧最后一个包。
marker = 1:
当前 RTP 包通常是一帧的最后一个 RTP 包。
接收端可以根据 marker 辅助判断帧边界,然后把这一帧送给解码器。
但是要注意:
marker 的具体语义和 payload 格式有关。
在 H264 RTP 中,通常可以理解为访问单元结束。
工程中简单记:
sequence 看丢包。
timestamp 看播放时间。
marker 看一帧是否结束。
你的 RTP 笔记中也把这几个字段总结为排障重点:sequence 看包顺序,timestamp 看播放时间,payload type 看编码类型,SSRC 看媒体源,marker 看帧边界。
五、SSRC 是什么?为什么多路流时很重要?
SSRC 全称是 Synchronization Source Identifier,可以理解为 RTP 流的源标识。
一个 RTP 会话中可能有多路媒体:
视频主码流
视频子码流
音频流
屏幕共享流
这些流需要区分,SSRC 就是用来标识“这一路 RTP 流是谁”。
例如:
Video RTP:
SSRC = 0x11111111
Audio RTP:
SSRC = 0x22222222
接收端看到 SSRC,就知道:
这个包属于哪一路媒体流。
如果 SSRC 混乱,可能出现:
音视频流混在一起。
多路视频无法区分。
WebRTC 中 track 对应关系异常。
RTCP 反馈无法正确对应发送流。
在 WebRTC 中,SSRC 也很重要。RTCP 的丢包反馈、PLI、NACK、统计信息都要和对应的 SSRC 关联。
所以可以这样记:
payload type:
表示这包是什么编码。
SSRC:
表示这包属于哪一路流。
sequence:
表示这一路流中的第几个包。
timestamp:
表示这一路流中的媒体播放时间。
六、MTU 与 RTP 分包:为什么一个 H264 帧要拆包?
网络传输中,UDP 包不能无限大。以太网常见 MTU 是 1500 字节,扣掉 IP/UDP/RTP 头部后,RTP payload 通常要控制在 1200 字节左右更稳。
所以实际发送时经常设置:
rtph264pay pt=96 mtu=1200
它的意思是:
把 H264 RTP 包尽量控制在 1200 字节左右。
如果 H264 NALU 很大,rtph264pay 会把它拆成多个 RTP 包。
例如一个 IDR 帧很大:
H264 IDR NALU = 80KB
经过 RTP payloader 后:
RTP packet 1
RTP packet 2
RTP packet 3
...
RTP packet N
这些包通常:
sequence 连续递增。
timestamp 相同。
最后一个包 marker = 1。
如果 MTU 设置过大,可能导致:
IP 分片。
网络丢包概率增加。
某些网络设备丢弃分片包。
实时性变差。
如果 MTU 设置过小,可能导致:
RTP 包数量增加。
包头开销变大。
CPU 和网络包处理压力增加。
工程经验:
局域网测试可以 1200~1400。
公网/WebRTC 常见会偏向 1200 左右。
RTP/UDP 传输不要盲目把单包做得太大。
七、h264parse config-interval=1 和 RTP 有什么关系?
发送端命令中还有这一句:
! h264parse config-interval=1 \
它不是 RTP 参数,但对 RTP 播放非常关键。
H264 解码需要 SPS/PPS:
SPS:
序列参数集,包含 profile、level、分辨率等信息。
PPS:
图像参数集,包含熵编码、参考帧等图像级参数。
如果接收端没有拿到 SPS/PPS,即使 RTP 包到了,也可能黑屏。
所以:
有 RTP 包,不代表能解码。
能解码,需要 codec 参数完整。
config-interval=1 可以让 H264 参数集周期性出现,方便接收端中途加入时也能起播。
常见问题:
接收端启动时错过了最开始的 SPS/PPS。
后面一直收到 P 帧,但没有参数集。
结果表现为有包但黑屏。
处理方式:
周期性发送 SPS/PPS。
关键帧前带 SPS/PPS。
WebRTC 收到 PLI 后尽快发 IDR。
你的 RTP 笔记中也提到,H264 解码必须拿到 SPS/PPS,没有 SPS/PPS 时会出现有 RTP 包但黑屏,GStreamer 中常用 h264parse config-interval=1 周期性带上参数集。
八、RTP 参数和 SDP/caps 的对应关系
GStreamer 接收端中写的是 caps:
application/x-rtp,
media=video,
encoding-name=H264,
payload=96,
clock-rate=90000
在 RTSP 或 WebRTC 中,对应信息通常写在 SDP 里。
例如 RTSP DESCRIBE 返回的 SDP:
m=video 0 RTP/AVP 96
a=rtpmap:96 H264/90000
a=fmtp:96 packetization-mode=1;sprop-parameter-sets=...
a=control:trackID=0
这段 SDP 表示:
m=video:
这是一条视频流。
RTP/AVP 96:
使用 RTP,payload type 是 96。
a=rtpmap:96 H264/90000:
96 对应 H264,clock-rate 是 90000。
a=fmtp:96:
H264 相关格式参数,例如 packetization-mode、SPS/PPS。
a=control:trackID=0:
RTSP 控制路径。
你的 RTSP 笔记中也给出过类似 SDP 示例:payload type 96 是 H264,clock-rate 是 90000,track 控制路径是 trackID=0。
所以,下面两种表达本质相同。
GStreamer caps:
payload=96
encoding-name=H264
clock-rate=90000
SDP:
a=rtpmap:96 H264/90000
核心逻辑:
发送端 RTP 包里写 PT=96。
接收端通过 caps 或 SDP 知道 96 是 H264/90000。
然后选择 rtph264depay 和 H264 decoder。
如果 SDP 写错,比如:
a=rtpmap:96 VP8/90000
但实际发的是 H264,就会出现:
RTP 包到了。
payload type 也匹配。
但解码链路选错。
最终无法播放。
九、RTP 到 WebRTC:WebRTC 内部也在用 RTP
WebRTC 不是一个单独协议,而是一套协议栈:
Signaling
-> SDP
-> ICE / STUN / TURN
-> DTLS-SRTP
-> RTP / RTCP
你的 WebRTC 笔记中也把 WebRTC 的核心协议栈总结为 Signaling、SDP、ICE/STUN/TURN、DTLS-SRTP、RTP/RTCP。
和普通 RTP 不同,WebRTC 不发送裸 RTP,而是发送 SRTP:
RTP
-> SRTP 加密
-> UDP
WebRTC 建连流程可以简化为:
1. 浏览器和服务器通过信令交换 SDP offer/answer。
2. 双方交换 ICE candidate。
3. ICE 找到能通的网络路径。
4. ICE connected 后进行 DTLS 握手。
5. DTLS 生成 SRTP 密钥。
6. 开始发送 SRTP/RTP 媒体包。
对于摄像头低延迟浏览器预览,常见架构是:
IP Camera
-> RTSP/RTP/H264
-> Media Gateway
-> WebRTC/SRTP/H264
-> Browser
媒体网关做的事情是:
1. 从摄像头拉 RTSP。
2. 解析 SDP,知道 PT、codec、clock-rate、SPS/PPS。
3. 接收 RTP H264。
4. 和浏览器做 WebRTC SDP 协商。
5. 建立 ICE/DTLS。
6. 把 H264 作为 WebRTC RTP track 发给浏览器。
如果编码兼容,可以不转码:
RTSP/RTP/H264
-> WebRTC/SRTP/H264
如果编码不兼容,例如摄像头输出 H265,而浏览器不支持当前 H265 播放链路,就可能需要:
H265 解码
-> H264 编码
-> WebRTC/SRTP/H264
十、实际代码示例:RTP 参数如何对应
1. 发送 H264 RTP,PT=96
gst-launch-1.0 videotestsrc is-live=true \
! video/x-raw,width=1280,height=720,framerate=30/1 \
! x264enc tune=zerolatency key-int-max=30 bitrate=2000 speed-preset=veryfast \
! h264parse config-interval=1 \
! rtph264pay pt=96 mtu=1200 \
! udpsink host=127.0.0.1 port=5004
关键参数解释:
pt=96:
RTP payload type 设置为 96。
mtu=1200:
控制 RTP 包大小,避免过大的 UDP 包。
key-int-max=30:
30fps 下约 1 秒一个关键帧。
config-interval=1:
周期性携带 SPS/PPS。
bitrate=2000:
控制编码码率,避免网络被打满。
2. 接收 H264 RTP,必须匹配 PT
gst-launch-1.0 udpsrc port=5004 caps="application/x-rtp,media=video,encoding-name=H264,payload=96,clock-rate=90000" \
! rtpjitterbuffer latency=50 \
! rtph264depay \
! h264parse \
! avdec_h264 \
! autovideosink sync=false
关键参数解释:
payload=96:
必须和发送端 pt=96 对应。
clock-rate=90000:
H264 RTP timestamp 的时间基准。
rtpjitterbuffer latency=50:
给 50ms 缓冲,用于处理乱序和抖动。
sync=false:
测试时减少渲染同步等待,便于低延迟观察。
3. 改成 PT=98 的完整对应
发送端:
gst-launch-1.0 videotestsrc is-live=true \
! x264enc tune=zerolatency key-int-max=30 \
! h264parse config-interval=1 \
! rtph264pay pt=98 mtu=1200 \
! udpsink host=127.0.0.1 port=5004
接收端必须同步改:
gst-launch-1.0 udpsrc port=5004 caps="application/x-rtp,media=video,encoding-name=H264,payload=98,clock-rate=90000" \
! rtpjitterbuffer latency=50 \
! rtph264depay \
! h264parse \
! avdec_h264 \
! autovideosink
结论:
发送端 pt 改了,接收端 payload 也必须改。
否则 RTP 包到了,也可能无法进入正确的 depayloader。
4. H265 RTP 示例
发送端:
gst-launch-1.0 videotestsrc is-live=true \
! x265enc tune=zerolatency key-int-max=30 bitrate=2000 \
! h265parse config-interval=1 \
! rtph265pay pt=96 mtu=1200 \
! udpsink host=127.0.0.1 port=5006
接收端:
gst-launch-1.0 udpsrc port=5006 caps="application/x-rtp,media=video,encoding-name=H265,payload=96,clock-rate=90000" \
! rtpjitterbuffer latency=50 \
! rtph265depay \
! h265parse \
! avdec_h265 \
! autovideosink
对比 H264:
H264:
rtph264pay / rtph264depay
encoding-name=H264
H265:
rtph265pay / rtph265depay
encoding-name=H265
但是 RTP 基础参数仍然相同:
payload type
clock-rate
sequence
timestamp
SSRC
marker
MTU
十一、实际调试:怎么确认 RTP 参数是否正确?
1. 抓 RTP 包
tcpdump -i any udp port 5004 -w rtp.pcap
用 Wireshark 打开后,过滤:
rtp
udp.port == 5004
重点看:
Payload Type:
是否是 96。
Sequence Number:
是否连续递增。
Timestamp:
是否按帧率正常递增。
SSRC:
是否稳定。
Marker:
是否在帧结束处置位。
Payload:
是否是 H264 RTP payload。
你的 RTP 笔记中也给出了类似排查方法:先确认有没有包,再看 sequence,再看 timestamp,再看 H264 参数集。
2. 看 sequence 是否丢包
正常:
3000, 3001, 3002, 3003
丢包:
3000, 3001, 3005
如果丢包发生在普通 P 帧上,可能只是轻微花屏。
如果丢包发生在 IDR 关键帧上,影响会更明显:
画面花屏。
解码失败。
等待下一个关键帧恢复。
3. 看 timestamp 是否正确
30fps H264:
timestamp 增量约 3000。
60fps H264:
timestamp 增量约 1500。
如果 timestamp 增量异常,可能出现:
播放速度不对。
帧间隔异常。
音视频同步异常。
4. 看 payload type 是否匹配
发送端:
pt=96
接收端:
payload=96
SDP:
a=rtpmap:96 H264/90000
三者必须一致。
如果不一致,典型现象是:
tcpdump 能抓到 UDP 包。
Wireshark 能看到 RTP。
但 GStreamer 不显示画面。
WebRTC bytesReceived 增长,但 framesDecoded 不增长。
十二、面试核心逻辑题
题 1:rtph264pay pt=96 中的 pt=96 是什么意思?
答:
pt 是 RTP Payload Type。
pt=96 表示 RTP 包头里的 payload type 字段设置为 96。
96 是动态 payload type,本身不固定表示 H264。
必须通过 SDP 或 caps 告诉接收端 96 对应 H264/90000。
题 2:发送端 pt=96,接收端为什么要写 payload=96?
答:
RTP 包头中只携带 payload type 数字。
接收端需要根据 payload=96 匹配对应的 depayloader 和 codec。
如果发送端 pt 和接收端 payload 不一致,可能出现有 RTP 包但无法解码的问题。
题 3:clock-rate=90000 是帧率吗?
答:
不是。
clock-rate 是 RTP timestamp 的时间基准。
H264/H265 视频 RTP 通常使用 90000Hz。
如果是 30fps,每帧 timestamp 增量是 90000 / 30 = 3000。
如果是 60fps,每帧 timestamp 增量是 90000 / 60 = 1500。
题 4:sequence 和 timestamp 的区别是什么?
答:
sequence 是 RTP 包序号,用来判断丢包、乱序、重复包。
timestamp 是媒体时间戳,用来控制播放时间和音视频同步。
举例:
同一帧拆成 3 个 RTP 包:
sequence:
100, 101, 102
timestamp:
都是 90000
题 5:marker 位有什么用?
答:
对于视频 RTP,marker 通常用于标记一帧结束。
接收端可以根据 marker 辅助判断帧边界。
典型情况:
一帧拆成多个 RTP 包:
前几个包 marker=0
最后一个包 marker=1
题 6:SSRC 是什么?
答:
SSRC 是 RTP 流的同步源标识,用于区分不同媒体流。
例如音频一路 SSRC,视频一路 SSRC,多路视频也会有不同 SSRC。
RTCP 反馈也需要根据 SSRC 找到对应媒体流。
题 7:为什么有 RTP 包但黑屏?
答:
常见原因:
payload type 和 SDP/caps 不匹配。
缺少 SPS/PPS。
没有收到 IDR 关键帧。
H264 profile 不兼容。
RTP 丢包严重。
timestamp 或 packetization 异常。
解码器不支持当前码流。
排查顺序:
先看有没有 RTP 包。
再看 PT 是否匹配。
再看 SDP/caps 是否正确。
再看 SPS/PPS。
再看 IDR。
最后看解码器兼容性。
题 8:WebRTC 中还用 RTP 吗?
答:
用。
WebRTC 媒体层仍然是 RTP/RTCP。
只是 WebRTC 不发送裸 RTP,而是通过 DTLS 协商密钥后发送 SRTP。
完整逻辑:
RTP/RTCP
-> SRTP/SRTCP
-> ICE 选出的网络路径
-> UDP/TURN
题 9:RTSP 的 SDP 和 RTP 参数有什么关系?
答:
RTSP DESCRIBE 会返回 SDP。
SDP 中描述 payload type、codec、clock-rate、fmtp、track 等信息。
接收端根据 SDP 知道 RTP 包里的 PT 对应什么编码。
例如:
a=rtpmap:96 H264/90000
表示:
payload type 96 是 H264。
clock-rate 是 90000。
题 10:RTP 排障最核心看哪几个字段?
答:
sequence:
看丢包、乱序。
timestamp:
看播放时间和同步。
payload type:
看编码映射是否正确。
SSRC:
看媒体流是否混乱。
marker:
看帧边界。
总结
rtph264pay pt=96 看起来只是一个小参数,但它背后串起了 RTP 的核心逻辑:
H264 码流
-> rtph264pay 封装成 RTP
-> RTP Header 中写入 PT、sequence、timestamp、SSRC、marker
-> UDP 发送
-> 接收端通过 caps/SDP 理解 PT=96 是 H264/90000
-> rtph264depay 取出 H264
-> decoder 解码显示
最重要的记忆方式:
pt / payload:
编码类型映射。
clock-rate:
timestamp 时间基准。
sequence:
包顺序。
timestamp:
播放时间。
SSRC:
哪一路流。
marker:
一帧结束。
mtu:
RTP 包大小控制。
SPS/PPS:
H264 解码参数。
RTP 是 RTSP 和 WebRTC 都绕不开的基础。RTSP 中,RTP 负责真正传 H264/H265 包;WebRTC 中,RTP 经过 SRTP 加密后继续承担媒体传输。掌握 pt=96 这一类参数,就能真正理解音视频流为什么“有包但黑屏”、为什么“丢包会花屏”、为什么“timestamp 错会播放异常”、为什么 WebRTC 排障最终也要看 RTP/RTCP。
火山引擎视频云技术社区,是面向 AI 音视频开发者的技术交流平台。这里汇聚源自抖音、豆包等亿级 DAU 产品的 RTC、直播、点播、AI 媒体处理、音视频互动技术,提供接入指南、最佳实践、性能调优、场景案例、Demo 代码、开源项目、白皮书和 API 文档。社区汇聚官方工程师与一线开发者,为 AI 视频通话、数字人、AI 视频处理等应用的开发与落地提供技术支持。
更多推荐
所有评论(0)