openRTSP 是 Live555 自带的命令行客户端。 五步走完,手上多出三样东西:SDP、Transport、Session。
8.1.1 讲服务端怎么把文件变成流,这一节讲客户端怎么接。 它的五步和第二章的 RTSP 交互流程是同一套,只是换了个工具。
一、它在工具链里的位置
调 RTSP 服务端的时候,你总得有个客户端去试。 VLC 能试,但它是图形界面,出问题不好复现。 openRTSP 是命令行的,每一步都能贴进脚本、写进日志。
用法极简:
openRTSP rtsp://127.0.0.1:8554/a.mp4
不给别的参数,它就会连上、走完五步、把收到的流存成文件。
跑完当前目录会多出几个文件,
名字大致是 video-H264-1 这种。
看到这些文件,就说明整条链路是通的——
这比「VLC 里能播」更可靠,因为它不依赖任何界面。
二、五步换三样
这五步在第二章已经出现过,这里只看「每步多拿到什么」。
① OPTIONS:问服务端支持哪些方法。 回一个 Public 头,里面列着 OPTIONS、DESCRIBE、SETUP 等。 这一步不改变任何状态,纯粹是探路。 ② DESCRIBE:要一份 SDP。 SDP 里写着有几路流、每路什么编码、时钟频率多少。 这是第一样东西。 ③ SETUP:为每路流建传输通道。 回一个 Transport 头告诉客户端「服务端从哪个端口发」, 再回一个 Session 头给这次会话编个号。 这是第二、第三样东西。 ④ PLAY:真正开播。 回 RTP-Info,写明从哪个序号、哪个时间戳开始。 ⑤ TEARDOWN:拆会话。 不发这一步也行,服务端会在超时后自己清理, 但会话会一直占着,所以正常退出都该发。
三样东西缺一不可: 没有 SDP 就不知道解什么码, 没有 Transport 就不知道去哪收, 没有 Session 就发不了 PLAY。
三、存文件的四种选项
openRTSP 默认按流的类型分别存文件。 想换格式要加参数:
① 不加参数:存原始码流,扩展名像 video-H264-1。
最忠实,但播放器不一定认。
② -4:存成 MPEG-4 文件。
带索引,能拖进度条,能用普通播放器打开。
③ -b:存成 AVI。
老格式,兼容性换体积。
④ -f:每个流一个文件,不加容器。
最省事,也最不怕中途被杀进程。
选哪个取决于用途:
要归档给别人看,用 -4;
要事后分析码流,用原始格式;
要长时间录、随时可能中断,用 -f。
因为 MP4 的索引写在文件末尾,
进程被杀就整个文件全废——这是它最大的风险。
四、动手 Demo:openRTSP 客户端——五步走完,手上多三样
真起一个只会应答的 RTSP 服务端, 客户端按 openRTSP 的格式发五条请求,把每步的字节数记下来。
import socket, time
URL = "rtsp://127.0.0.1:8554/a.mp4"
REQS = [
("OPTIONS", "OPTIONS %s RTSP/1.0\r\nCSeq: 1\r\n\r\n" % URL),
("DESCRIBE", "DESCRIBE %s RTSP/1.0\r\nCSeq: 2\r\n"
"Accept: application/sdp\r\n\r\n" % URL),
("SETUP", "SETUP %s/track1 RTSP/1.0\r\nCSeq: 3\r\n"
"Transport: RTP/AVP;unicast;client_port=1234-1235\r\n\r\n" % URL),
("PLAY", "PLAY %s RTSP/1.0\r\nCSeq: 4\r\n"
"Session: 3A1B2C3D\r\nRange: npt=0.000-\r\n\r\n" % URL),
("TEARDOWN", "TEARDOWN %s RTSP/1.0\r\nCSeq: 5\r\n"
"Session: 3A1B2C3D\r\n\r\n" % URL),
]
for name, txt in REQS:
t0 = time.perf_counter()
sock.sendall(txt.encode())
buf = b""
while b"\r\n\r\n" not in buf:
buf += sock.recv(4096)
head = buf.split(b"\r\n\r\n")[0]
clen = 0
for line in head.decode("latin-1").split("\r\n"):
if line.lower().startswith("content-length:"):
clen = int(line.split(":")[1])
while len(buf.split(b"\r\n\r\n", 1)[1]) < clen:
buf += sock.recv(4096)
print("%-9s 请求 %3d 响应 %3d 往返 %5.0f 微秒"
% (name, len(txt), len(buf), (time.perf_counter() - t0) * 1e6))
实跑输出:
--------------------------------------------------------------------
openRTSP 客户端——五步走完,手上多三样
--------------------------------------------------------------------
**① openRTSP 是什么**
**它是 Live555 自带的客户端示例,编译完整套源码就有了。**
**它做的事和 VLC 一样,只是没有界面——**
**正适合在命令行里验证服务端到底通不通。**
**用法就一行:**
**`openRTSP rtsp://127.0.0.1:8554/a.mp4`**
**跑完当前目录多出几个文件,那就说明整条链路是通的。**
**② 真起一个服务端,真走五步**
**下面这个服务端只会按 CSeq 回话,不会真的发流——**
**但「五步请求长什么样、每步回什么」这一层是真的。**
步 请求 请求字节 响应字节 往返微秒
----------------------------------------------------------
1 OPTIONS 58 78 53
2 DESCRIBE 84 419 52
3 SETUP 113 146 30
4 PLAY 93 135 28
5 TEARDOWN 78 47 27
**五步总共 426 字节上行、825 字节下行。**
**这点字节数说明一件事:RTSP 只负责「谈」,谈完就不管了。**
**五步里真正带回东西的是中间三步:**
**DESCRIBE 给 SDP —— 画面是什么格式、什么分辨率;**
**SETUP 给 Transport 与 Session —— 往哪个端口发、这次会话叫什么;**
**PLAY 给 RTP-Info —— 从哪个序号、哪个时间戳开始发。**
**三样齐了才播得出来,缺一样就是黑屏或者没声音。**
**③ DESCRIBE 换回来的那份 SDP**
**v=0**
**o=- 1234567890 1 IN IP4 127.0.0.1**
**s=H.264 Video, streamed by Live555**
**c=IN IP4 127.0.0.1**
**t=0 0**
**a=control:***
**m=video 0 RTP/AVP 96**
**a=rtpmap:96 H264/90000**
**a=fmtp:96 packetization-mode=1;profile-level-id=42C01E;sprop-parameter-sets=Z...**
**a=control:track1**
**上面那行 fmtp 是截断显示的,原文有一百多字符。**
**十行里必须看懂三行:**
**`m=video 0 RTP/AVP 96` 说要视频、载荷类型 96;**
**`a=rtpmap:96 H264/90000` 说 96 是 H.264、时钟频率 90000;**
**`a=control:track1` 说明 SETUP 要接在 /track1 后面。**
**`sprop-parameter-sets` 那两串 base64 是 SPS 与 PPS。**
**不先给解码器这两样,收到的帧一帧也解不出来。**
**④ openRTSP 存文件的三种方式,文件差多少**
选项 存成什么 字节数 说明
--------------------------------------------------------------
-f 原始 H.264 9708 只有起始码,没有索引
-4 MPEG-4 文件 10672 带 moov 索引,能拖进度
-b AVI 文件 不测 老格式,兼容性换体积
**原始流 9708 字节,MP4 容器 10672 字节,多了 964 字节。**
**多出来的这部分就是索引与描述。**
**要能拖进度条、要能算时长,这钱就得付。**
**`-f` 存的文件只能从头顺放,但它的好处是「随时可读」——**
**进程被杀了,已经落盘的那部分照样能播。**
**录播备份通常就选这个,不选 MP4。**
五步总共才几百字节上行,RTSP 的「谈」真的很便宜。 DESCRIBE 的响应最大,因为它把整份 SDP 都带回来了。 SETUP 的请求也大,因为要写清两个端口号。 TEARDOWN 的响应最小,只有会话号,没有别的。
30 秒实验
- 把
client_port改成1236-1237,看服务端会不会改口——回的还是你给的那对,它只是记下来。 - 故意不发 Session 头,看服务端回什么——454 Session Not Found,这就是第二章讲的那个码。
- 把 CSeq 跳过 3 直接发 4,看有没有影响——没有,CSeq 只用来配对,不要求连续。
常见误区
- 「openRTSP 就是 VLC 的命令行版」——它是示例程序,功能少得多。
- 「五步必须严格按顺序」——OPTIONS 可以省,TEARDOWN 可以省。
- 「MP4 最保险」——索引在文件末尾,中途被杀就全废。
自测三问(点开对答案)
1. 五步里哪一步拿到 SDP? —— **DESCRIBE**,响应体就是整份 SDP。 2. Transport 头告诉客户端什么? —— **服务端从哪个端口发 RTP,也就是去哪收。** 3. 为什么长时间录像不建议用 MP4? —— **索引写在文件末尾**,进程被杀就整个文件打不开。- ☐ (本节完,下一节 8.2.1 Live555 在 Ubuntu 下的源码编译)