1. 先搞清楚:RTSP 到底是干什么的
RTSP 这个协议,几乎所有做流媒体的人都接触过,但很多人对它的理解停留在"能拉流播放"这个层面。我第一次在项目里往死里调 RTSP 的时候,被它折磨得不轻:明明 ffmpeg 能拉流,换成自己的播放器就黑屏;OPTIONS 请求通了,DESCRIBE 却一直 401;SETUP 报 All Ports In Use,日志里却没有明确提示……后来把协议栈彻底梳理了一遍,才发现大部分问题都源于同一个误解——把 RTSP 当成一个"传输协议"来用。
1.1 它不是一个"传输视频"的协议
RTSP(Real Time Streaming Protocol,实时流传输协议)由 RFC 2326 定义,2014 年又有 RFC 7826 做了更新。它跑在 TCP 或 UDP 之上,默认端口 554,本质上是一个带状态的应用层控制协议,类似于多媒体界的"遥控器"。
这个"遥控器"负责什么呢?它负责协商会话参数、发起播放、暂停、快进、停止这些操作指令,但真正的音视频数据不经过 RTSP 传输,而是由 RTSP 会话协商出来的 RTP/RTCP 数据流承载。RTP(Real-time Transport Protocol)负责实际搬运音视频包,RTCP 负责传输统计和质量反馈,它们默认走 UDP 端口,通常是 5004、5006 这类偶数位端口。
用个最直白的类比:RTSP 是餐厅里的服务员,你通过服务员点餐、催菜、买单;RTP 是后厨传菜通道,菜从通道直接端到你桌上。服务员身上不端菜,但不代表服务员不重要——没有他把菜名传进后厨,后厨根本不知道你要吃什么。
所以排查 RTSP 问题时,第一反应不应该盯着"数据流为什么断",而是先确认"控制面协商是否成功"。控制面都谈崩了,数据面根本不会开始。
1.2 RTSP 解决的真实场景
RTSP 设计之初就面向**"点播 + 直播 + 控制"**三类需求:
- 点播(VoD):用户可以对流执行播放、暂停、恢复、seek 到任意时间点,这要求协议天然支持状态管理。
- 直播(Live):摄像头、编码器实时推流,客户端可以随时加入,从当前时间点开始接收。
- 多端同步:通过 RTSP 会话描述(SDP)把音频轨、视频轨、字幕轨等描述清楚,客户端可以按需 SETUP 不同的轨道。
这也解释了为什么安防监控、IP Camera、视频编码器、车载 DVR 几乎清一色支持 RTSP:因为这些设备的核心场景是"局域网内低延迟实时取流",RTSP 在这条赛道上积累了二十多年兼容性,VLC、FFmpeg、GStreamer、各种播放器 SDK 全都默认支持它。
提示:另一个真实场景是"多路并发控制"。一个媒体服务器可以同时维护大量 RTSP 会话,每个会话独立状态机,这对大规模监控平台的并发接入(比如一台 NVR 接入几十上百路摄像头)非常关键。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 一次完整 RTSP 会话:从 OPTIONS 到 TEARDOWN
抓包看一次完整的 RTSP 交互,你会发现整个流程特别像一个"状态机握手剧本",每一句对话都有明确的目的和顺序。理解了这个剧本,你才能在任何一步失败时快速定位问题。
2.1 五步握手:控制面的状态机
一次最基本的 RTSP 拉流过程长这样:
code复制C -> S: OPTIONS rtsp://192.168.1.64:554/stream1 RTSP/1.0
S -> C: RTSP/1.0 200 OK
Public: DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE, GET_PARAMETER, SET_PARAMETER
C -> S: DESCRIBE rtsp://192.168.1.64:554/stream1 RTSP/1.0
S -> C: RTSP/1.0 200 OK
Content-Type: application/sdp
(这里就是SDP报文内容)
C -> S: SETUP rtsp://192.168.1.64:554/stream1/track1 RTSP/1.0
Transport: RTP/AVP/UDP;unicast;client_port=6000-6001
S -> C: RTSP/1.0 200 OK
Transport: RTP/AVP/UDP;unicast;client_port=6000-6001;server_port=5004-5005
C -> S: PLAY rtsp://192.168.1.64:554/stream1 RTSP/1.0
S -> C: RTSP/1.0 200 OK
RTP-Info: url=rtsp://.../track1;seq=58874;rtptime=1820395125
C -> S: TEARDOWN rtsp://192.168.1.64:554/stream1 RTSP/1.0
S -> C: RTSP/1.0 200 OK
把这五步拆开看:
- OPTIONS 是客户端向服务器"打招呼",同时探测服务器支持哪些方法。有些摄像头不会把所有方法都在 Public 里列全,但 DESCRIBE 和 SETUP 一定会支持。
- DESCRIBE 是客户端向服务器要"菜谱",也就是 SDP 会话描述。服务器返回音视频轨道的编码格式、分辨率、帧率、采样率、SPS/PPS 等关键参数。
- SETUP 是为某个轨道建立传输通道。这里要指定传输模式是走 UDP 还是 TCP,客户端用哪个端口收 RTP 数据。有多少路轨道就发多少次 SETUP,视频轨和音频轨通常是两次独立的 SETUP。
- PLAY 是真正开始播放的指令,服务器收到后开始往客户端推 RTP 数据。
- TEARDOWN 结束会话,撤销所有通过 SETUP 建立的通道。
理解这个流程,很多问题就迎刃而解了。比如你在代码里只发了 OPTIONS 和 DESCRIBE 就等着收数据,那肯定等不到;再比如你发了 SETUP 但没收到 200 OK,就去解析 RTP 数据,当然是一堆乱码。
2.2 SETUP 阶段最关键:Transport 头决定 RTP 怎么走
SETUP 请求里的 Transport 头堪称整个 RTSP 交互里最重要的"谈判条款",它决定了后续 RTP 数据走什么通道、用什么端口。我建议你把这个字段的所有变体都背下来,因为调试时 90% 的问题都跟它有关。
常见 Transport 头长这样:
| Transport 头写法 | 含义 |
|---|---|
RTP/AVP/UDP;unicast;client_port=6000-6001 |
RTP 走 UDP,客户端端口 6000 收 RTP,6001 收 RTCP(偶数收 RTP,奇数收 RTCP,这是 RTP 的惯例) |
RTP/AVP/TCP;unicast;interleaved=0-1 |
RTP 走 TCP,且交织在 RTSP 的 TCP 连接里,channel 0 传 RTP,channel 1 传 RTCP |
RTP/AVP/UDP;multicast |
组播模式,客户端加入组播地址接收数据 |
第一行是最常见的情况:UDP 单播模式,客户端主动告诉服务器"你往我的 6000 端口砸数据"。
第二行是用 TCP 承载 RTP,这种模式在公网环境下非常实用。因为 UDP 被 NAT 打洞挡住、防火墙拦截的情况实在太多了,把 RTP 数据塞进 RTSP 的 TCP 连接里就能绕过大部分端口封锁。
在 TCP 交织模式下,RTP 数据包前面会有一个 4 字节的帧头:第 1 字节是 $(十六进制 0x24),第 2 字节是 channel 编号,第 3、4 字节是载荷长度。你用 FFmpeg 或自研播放器处理这种流时,要先读 4 字节帧头,再按长度读完整 RTP 包。
2.3 DESCRIBE 返回的 SDP 里藏着哪些必须解析的信息
DESCRIBE 的响应体是一段 SDP 文本,长得像下面这样:
code复制v=0
o=- 1681447428 1681447428 IN IP4 192.168.1.64
s=RTSP Server
t=0 0
m=video 0 RTP/AVP 96
c=IN IP4 0.0.0.0
b=AS:3500
a=rtpmap:96 H264/90000
a=fmtp:96 packetization-mode=1; sprop-parameter-sets=Z2QAHqw2EB/BFskgKACgAAAwADAAgAAHhgBR6Q==,aO+NPGwA==;
a=control:track1
m=audio 0 RTP/AVP 97
c=IN IP4 0.0.0.0
b=AS:128
a=rtpmap:97 MPEG4-GENERIC/16000/1
a=fmtp:97 profile-level-id=1;mode=AAC-hbr;sizelength=13;indexlength=3;indexdeltalength=3;config=1408
a=control:track2
这段文本里,m= 开头的是媒体轨道描述(video 0 RTP/AVP 96 表示视频轨使用 payload type 96),a=rtpmap 描述编码格式(96 对应 H.264),a=fmtp 里则有 H.264 的 SPS/PPS 参数(sprop-parameter-sets)和 AAC 音频的 config。
对应用层开发者来说,SDP 里几个必须提取的东西:
- 轨道数量和控制 URL:每个
m=行对应的a=control指定了该轨道的 URL,SETUP 时必须用它。 - 编码格式和时钟频率:H.264 通常是 90000,AAC 是采样率(如 16000),解码器要靠这个计算时间戳。
- SPS/PPS 和 AAC config:这些二进制的编解码参数如果丢了,播放器会直接花屏或者听不到声音。H.264 的 SPS/PPS 可以后置通过 RTP 的 FU-A 分片携带,但很多服务端只在 SDP 里给一次,客户端必须缓存好。
注意:不要假设 SDP 里一定会有 sprop-parameter-sets。部分摄像头只返回
a=fmtp:96 packetization-mode=1;不带参数集,这时要么等 RTP 流里的关键帧刷新,要么从 RTP 载荷的 SPS/PPS 里解析,要么干脆强制让服务端走 TCP 交织后等 IDR 帧。
3. 为什么很多项目选了 RTMP 而不是 RTSP:选型对比
我经常被问到:"RTSP 和 RTMP 到底选哪个?"这问题没有绝对答案,得看你的流量走向和网络环境。但从工程实践看,两者在公网直播、安防取流这两个场景的取舍非常鲜明。
3.1 协议层面:单向长连接 vs 控制/传输分离
RTMP 由 Adobe 于 2002 年推出,它走 TCP,且所有数据(控制消息、音视频数据、AMF 命令)都复用同一个长连接,不分什么控制面和数据面。连接建立后,客户端可以随时上传或下载音视频数据,非常适合推流上行场景。
RTSP 则把控制面和数据面拆开:控制走 RTSP,数据走 RTP。这种"分工明确"的设计让 RTSP 能做的事情更细,比如精确控制每一路轨道的传输参数,但代价是会话状态更复杂,建立连接的网络交互更多(OPTIONS、DESCRIBE、多路 SETUP、PLAY 四轮起步)。
一个很直观的体验差异:用 RTMP 推流,服务器和客户端只需要一次 TCP 连接就能一直推;用 RTSP 推流或者拉流,必须完整走完那一套控制流程,任何一步丢包或超时都可能卡在中间状态。
3.2 实际体验:公网、延迟、播放兼容性
把两者放在公网环境下对比,差距更明显:
| 维度 | RTSP | RTMP |
|---|---|---|
| 默认传输 | UDP(可用 TCP 交织) | TCP 长连接 |
| 公网穿 NAT | 差(UDP 流量容易被防火墙拦) | 好(TCP 长连穿透率高) |
| 延迟 | UDP 模式可到 300ms 以内 | 通常 1~3 秒 |
| 暂停/seek | 原生支持,体验好 | 协议本身不擅长,依赖服务端实现 |
| Web 端直接播放 | 不支持 | 不支持(需转 HTTP-FLV 等) |
| 安防摄像头兼容性 | 几乎全支持 | 极少支持 |
RTSP 在公网的最大软肋是"UDP 打洞"。除非服务器和客户端都在同一局域网,或者你提前在网关做了端口映射,否则 RTP 数据很难穿过 NAT 到达客户端。虽然有 TCP 交织模式和 RTSP over HTTP 隧道这两种逃生方案,但配置复杂度比 RTMP 高一个档次。
RTMP 在延迟上天然弱势,但因为它只有一条 TCP 长连接,对 NAT 和防火墙的穿透性好太多。这就是为什么这些年直播上行基本被 RTMP 垄断了,而安防取流依然是 RTSP 的天下。
3.3 我给不同场景的选型建议
结合这些年做过的项目,我的建议可以概括成一句话:摄像头在局域网内,选 RTSP;直播要上公网,选 RTMP(或基于 HTTP 的 FLV/HLS);如果两者都要兼顾,做转封装网关。
具体来说:
- 安防监控 / NVR / 车载设备:优先 RTSP。摄像头原生支持,兼容性最好,局域网内延迟低。
- 公网直播推流:选 RTMP 或 SRT。RTMP 生态成熟、工具多;SRT 是基于 UDP 的现代协议,抗丢包能力更强,延迟也低,但服务端支持要自己搭。
- 公网拉流播放:优先考虑转成 HLS 或 WebRTC。HLS 兼容性最好(浏览器直接播),延迟在 5-10 秒;WebRTC 延迟在 500ms 内但需要信令服务端配合。
- 混合场景(如摄像头采集、公网分发):设备端用 RTSP 拉流,服务端用 FFmpeg/GStreamer 转成 HLS 或 RTMP 再分发,这是最稳的架构。
4. 手把手推流和拉流:FFmpeg 与 VLC 实测
讲了这么多理论,上点实操。下面拿 FFmpeg 和 VLC 这两件"流媒体瑞士军刀"跑一遍推流、拉流和录制,顺便把几个容易踩的细节点出来。
4.1 用 FFmpeg 发布和拉取 RTSP 流
先假设你有一台支持 RTSP 的摄像头或者一个 RTSP 服务器。用 FFmpeg 拉流最简单的命令:
bash复制# 拉取摄像头 RTSP 流并打印到终端
ffplay -rtsp_transport tcp rtsp://192.168.1.64:554/stream1
# 拉取 RTSP 流并转封装保存为 MP4
ffmpeg -rtsp_transport tcp -i rtsp://192.168.1.64:554/stream1 -c copy -y output.mp4
# 拉取 RTSP 直播流并推送到 RTMP 服务器
ffmpeg -rtsp_transport tcp -i rtsp://192.168.1.64:554/stream1 -c copy -f flv rtmp://your-server/live/stream
这里 -rtsp_transport tcp 是我强烈建议加上的参数,它强制 FFmpeg 使用 TCP 交织模式,避免 UDP 拉流时被路由器或防火墙拦截。尤其是跨网段拉摄像头流,不加这个参数你可能看到的就是一堆花屏或者直接超时。
如果要推流到 RTSP 服务器(比如用 MediaMTX 或者 GStreamer 搭的 RTSP 服务),命令也很简单:
bash复制# 把本地视频文件推成 RTSP 直播流
ffmpeg -re -i input.mp4 -c copy -f rtsp rtsp://127.0.0.1:8554/live/stream
注意 -re 这个参数,它让 FFmpeg 以文件的原帧率读取并推送,模拟实时推流。不加它 FFmpeg 会以最快速度把整个文件瞬间推完。
4.2 VLC 验证和本地录制
VLC 是排查 RTSP 时最好的"对照播放器"。用它打开网络串流(Ctrl+N),输入:
code复制rtsp://192.168.1.64:554/stream1
如果 VLC 能播,说明服务器本身没问题,问题大概率出在你的代码或播放器 SDK 上。
VLC 还能干两件很实用的活:
- 录制流:播放时点击"录制"按钮,VLC 会把流实时写入本地文件。注意它录的是转封装后的文件,不是原始 RTP 抓包,但格式和参数信息很完整。
- 显示媒体信息:播放时按 Ctrl+I,能看到完整的 SDP 内容、编码格式、码率、帧率等。这在你需要核对摄像头输出参数时非常有用。
我在调试时有个固定套路:先用 VLC 跑通 → 再用 FFmpeg 命令行跑通 → 最后才写代码。这样能先把"服务器问题"和"客户端代码问题"分开,节省大量排查时间。
5. 抓包看 RTSP/RTP:Wireshark 的排查姿势
RTSP 最好的调试工具不是日志,是抓包。Wireshark 对 RTSP 和 RTP 的解码支持得很成熟,但很多人抓了包不会看,这里给一套完整的排查姿势。
5.1 抓包前的准备和过滤语法
抓包前记住几个关键事实:RTSP 控制流量默认走 554 端口(也可能自定义成 8554、10554 等);RTP 流量在 UDP 模式下通常是 5004、5006 或是 SETUP 协商出来的动态端口;TCP 交织模式下 RTP 数据隐藏在 RTSP 的 TCP 连接里,Wireshark 会显示成 "RTSP/TCP" 或 "RTP" 标签。
常用的过滤规则:
text复制# 只看 RTSP 协议
rtsp
# 只看某个主机的 RTSP 流量
ip.addr == 192.168.1.64 && rtsp
# 只看 RTP 流量
rtp
# 看 RTSP 和 RTP 混合流量
rtsp || rtp
# 按端口过滤
tcp.port == 554 || udp.port == 554
# 追踪某条 TCP 流的完整交互
tcp.stream eq 0
如果你抓包后发现 Wireshark 把 RTSP 识别成 HTTP 或其它协议,可以手动解码:在数据包上右键,选择"解码为(Decode As)",把当前端口或流指定为 RTSP 协议。有些场景下 Wireshark 版本较老,确实会出现"decode as 当前没有 rtsp 协议"的情况,去设置里检查 Protocol 列表里 RTSP 是否被禁用了即可。
5.2 典型问题的报文特征
根据报文特征,你可以快速定位大部分 RTSP 问题:
- OPTIONS 有响应但 DESCRIBE 直接 401:看响应里有没有
WWW-Authenticate头。有WWW-Authenticate: Digest realm="IP Camera", nonce=...说明需要摘要认证,你的客户端缺了认证信息。 - SETUP 返回 461 Unsupported Transport:说明你请求的 Transport 参数服务器不支持。先看 OPTIONS 响应里 Public 有没有列出,再确认服务器是否需要固定用 TCP 交织或指定 IP 地址。
- PLAY 返回 200 但画面出不来:抓包里看 SETUP 响应里
server_port是多少,RTP 数据有没有真正到达你声明的client_port。如果 RTP 数据到了但播放器黑屏,看看 SDP 里的 SPS/PPS 有没有正确传给解码器。 - SETUP 报 All Ports In Use:典型的 UDP 端口分配失败。要么是 UDP 端口被占满、防火墙拦截,要么是客户端端口范围太小。换成 TCP 交织模式大概率能解决。
- RTSP 包正常但 RTP 包看不到:如果使用 UDP,确认抓包机上有没有收到目的端口为
client_port的 UDP 包;如果使用 TCP 交织,RTP 帧头以$开头,Wireshark 会解析为 RTP 并附带 channel 信息。
抓包时建议同时抓两端的网卡,这样能区分"数据没发出来"还是"数据发了没到"。比如你用 Wireshark 抓摄像头所在交换机镜像口,能看到服务器发出来的 RTP 包,但客户端收不到,那就是中间链路问题(NAT 映射、防火墙、路由)。
6. 镜头下的 RTSP 地址:安防摄像头取流全规律
在安防领域,RTSP 几乎成了"取流代名词"。海康、大华、小米、宇视、雄迈……每家设备的 RTSP 地址格式都略有差异,但套路上有规律可循。这里整理几个主流品牌的地址规则,方便你查资料时少走弯路。
6.1 海康威视
海康的 RTSP 地址基本格式是:
text复制rtsp://用户名:密码@IP地址:端口/Streaming/Channels/通道号编码
例如:
text复制rtsp://admin:password@192.168.1.64:554/Streaming/Channels/101
rtsp://admin:password@192.168.1.64:554/Streaming/Channels/102
101 通常表示通道 1 的主码流,102 表示通道 1 的子码流,201 表示通道 2 的主码流,以此类推。主码流分辨率高、码率大,适合存储或大屏预览;子码流分辨率低、码率小,适合手机 App 多路预览或者作为低带宽下的备选。
海康部分新版固件还兼容这种格式:
text复制rtsp://admin:password@192.168.1.64:554/h264/ch1/main/av_stream
rtsp://admin:password@192.168.1.64:554/h264/ch1/sub/av_stream
如果你在兼容旧设备,两种格式都可以试试。需要注意,海康默认开了"非法登录锁定",多次密码错误会锁 IP,调试时不要反复输入错误密码。
6.2 小米摄像头
小米摄像头的 RTSP 取流要先在米家 App 里开启"局域网 RTSP 协议"开关,否则地址是无效的。开启后的地址格式通常是:
text复制rtsp://用户名:密码@IP地址:554/streaming/live/1
默认用户名一般是你设置的管理员账号,不是米家账号;密码是设备的验证码或独立设置的流密码。如果你连不上,先去 App 里确认这个小开关有没有打开,这是小米用户踩得最多的坑。
6.3 大华、宇视等常见品牌
大华摄像头的地址格式是:
text复制rtsp://用户名:密码@IP地址:554/cam/realmonitor?channel=1&subtype=0
channel=1 是通道号,subtype=0 是主码流,subtype=1 是子码流。大华对 URL 参数比较敏感,注意别把 subtype 写成 sub_stream。
宇视摄像头的格式接近海康风格:
text复制rtsp://用户名:密码@IP地址:554/unicast/c1/s0/live
c1 是通道 1,s0 是主码流(s1 是子码流)。还有一些杂牌主板(如 XMeye/雄迈)用的是:
text复制rtsp://用户名:密码@IP地址:554/user=admin&password=123456&channel=1&stream=0.sdp?
这种格式比较老,但 XMeye 方案的老设备还在用,遇到"怪地址"别慌,先看它的 Web 管理页面里有没有显示取流地址,有就直接复制。
6.4 公网测试流
日常开发调试不一定有摄像头在手边,我一般用几个公开稳定的 RTSP 测试流来验证播放器或协议栈。
例如:
text复制rtsp://wowzaec2demo.streamlock.net/vod/mp4:BigBuckBunny_115k.mp4
这是我用过很多年的 Wowza 官方测试流,服务器在海外,延迟可能偏高,但用来验证"播放器能不能完整走完 RTSP 流程"足够了。此外还有一些视频平台、直播设备厂家提供的测试源,搜"public rtsp test stream"就能找到一堆。注意公网测试流不建议用于性能压测,稳定性和带宽都没法保障。
7. 常见问题的排查链路实录
讲了这么多理论、流程、抓包,最后分享几个实战里高频出现的问题,把完整排查链路记录下来,你看完可以直接照着操作。
7.1 401 认证问题
现象:OPTIONS 请求成功了,发 DESCRIBE 直接返回 RTSP/1.0 401 Unauthorized。
排查链路:
- 先看响应里有没有
WWW-Authenticate头。如果只有Basic,那是 Base64 明文认证,拼Authorization: Basic base64(username:password)即可。 - 如果是
Digest,需要实现摘要认证算法——客户端先发一次不带认证的请求,收到 401 后取出nonce、realm、opaque等字段,计算response=MD5(MD5(username:realm:password):nonce:MD5(method:uri))再重发请求。 - 有些设备默认关闭了 RTSP 认证或者只允许特定 IP 访问,先到设备 Web 管理页面确认 RTSP 认证方式和访问白名单。
- 还有个别摄像头用
digest时对uri的格式有要求(必须和请求行里的 URL 完全一致,不能多不能少),可以用抓包比对客户端发的请求行和Authorization里的uri字段是否相同。
7.2 SETUP 失败 / UDP 端口问题
现象:DESCRIBE 拿到了 SDP,SETUP 却返回 451 Invalid Parameter 或 461 Unsupported Transport,或者干脆超时。
排查链路:
- 看 SDP 里的
a=control字段。很多新手会直接把整个流地址放进 SETUP,其实 SETUP 的目标必须是a=control给的轨道 URL(如track1)。有些服务器要求 SETUP URL 是完整 URL(rtsp://ip/stream/track1),有些则接受相对路径,需要查设备文档。 - 如果 SDP 里有多个轨道(video + audio),你要按顺序分别 SETUP,而且每路 SETUP 的 client_port 不能重复,视频和音频要用不同的 UDP 端口对。
- 检查 Transport 头格式。UDP 模式一定要写
client_port=偶数-奇数,RTP 用偶数端口、RTCP 用相邻奇数端口。部分摄像头只支持 TCP,把 Transport 改成RTP/AVP/TCP;unicast;interleaved=0-1再试。 - 如果 SETUP 返回 200 但服务器
server_port是 0,说明服务器没有为这个会话分配 RTP 端口,通常是并发连接数满了或者编码资源不足。
7.3 黑屏花屏
现象:PLAY 返回 200,RTP 数据也在收,但画面黑屏、花屏,或者有画面没声音。
排查链路:
- 第一个怀疑对象是 SPS/PPS。海康、大华等摄像头在 SDP 里给的
sprop-parameter-sets是 Base64 编码的 SPS/PPS,解码器必须在收到关键帧前配置好这两个参数。如果你自己解析 SDP,务必把sprop-parameter-sets完整取出来,拆成 SPS 和 PPS 两份传给解码器。 - 第二个怀疑对象是 RTP 时间戳解析错误。H.264 的 RTP 时钟频率是 90000Hz,如果代码里用了视频帧率当频率,时间戳跳变会非常大,导致播放器频繁丢帧或卡顿。注意算时间戳增量时用
rtptime / 90000得到秒,再换算成毫秒。 - 有画面没声音,多半是 SDP 里
m=audio对应的 payload type 和实际 RTP 包里的 PT 不一致,或者 AAC 的config字段没解析出来。AAC 的 config 一般是 16 进制(如0x1408),它封装了采样率、声道数,解码器必须用它初始化。 - 还有一种隐蔽问题:RTP 的 sequence number 有跳变(网络丢包导致),播放器如果把它当成 GOP 边界处理,可能频繁出现画面冻帧。这种情况只能换 TCP 拉流或者调整 Jitter Buffer 大小。
7.4 Web 播放方案与延迟优化
经常有人问:浏览器能不能直接播 RTSP?答案是不能。浏览器原生只支持 HTTP 协议族,RTSP 控制层的交互方式它根本不认,更不用说 RTP 数据封装。所以但凡要上 Web,必须做协议转换:RTSP 拉流 → 服务端转 HLS/HTTP-FLV/WebRTC → 浏览器播放。
如果你追求低延迟,推荐 WebRTC 路线。媒体服务器用 FFmpeg 把 RTSP 流转成 WebRTC 需要的格式,通过 WHIP/WHEP 等信令协议接入,端到端延迟能逼近 500ms 以内。如果对延迟不敏感(比如监控回放),直接转 HLS 最省事,兼容性拉满。
延迟优化的几个手段,按优先级排列:
- 用 TCP 交织拉流,减少 UDP 丢包重传带来的抖动。
- 编码器侧降低 GOP 大小,比如设置为 1~2 秒一个 I 帧,播放器起播速度会快很多。
- 播放器缓存调小,VLC/FFplay 之类的缓存默认偏大,适合稳但延迟高;自己写播放器时把 jitter buffer 控制在 200~500ms 范围。
- 服务端转封装时禁用 B 帧重排,有些播放器对 B 帧解码延迟很敏感,编码侧设置
-tune zerolatency能明显降低端到端延迟。
我在实际项目中验证过:局域网内摄像头 → FFmpeg 转 WebRTC → 浏览器播放,端到端延迟稳定在 300~500ms,这个指标在安防场景下已经非常能打了。
RTSP 这个协议,说复杂不复杂,说简单也不简单,核心在于"控制面和数据面分离"这套设计。你只要把 OPTIONS/DESCRIBE/SETUP/PLAY 这条链路跑顺,再学会看 Transport 头和 SDP 报文,市面上绝大多数摄像头都能顺利取到流。真正需要耐心的是那些藏在细节里的兼容性问题——不同厂商的地址格式差异、认证算法差异、SDP 字段缺失,解决一个就涨一分经验,多踩几次坑自然就熟练了。
