3. RTSP协议详解:DESCRIBE/SETUP/PLAY、SDP解析与RTP/RTCP传输

各位同学,今天我们来啃一块硬骨头——RTSP协议。说实话,我刚入行那会儿,觉得RTSP就是个「遥控器」,无非就是播放、暂停、停止。直到我在一个海量摄像头接入的项目里被狠狠坑了一把,才真正意识到:不懂RTSP细节,你连摄像头画面都拉不出来。

RTSP全称是Real Time Streaming Protocol,实时流传输协议。它本身不传视频数据,只负责「控制」。你想想看,就像你看电视,遥控器负责换台、调音量,但真正播放画面的是电视机本身。RTSP就是那个遥控器,而RTP才是那个电视机。

3.1 RTSP请求方法:DESCRIBE / SETUP / PLAY

一个标准的RTSP会话,通常走三步:先问清楚对方有什么(DESCRIBE),再商量怎么传(SETUP),最后喊开始(PLAY)。我习惯把这叫做「三握手」,虽然它和TCP的三次握手不是一回事。

3.1.1 DESCRIBE —— 先问问摄像头支持什么

DESCRIBE请求,说白了就是客户端对摄像头说:「嘿,你有什么格式?报上来。」摄像头会返回一个SDP描述,里面包含了视频编码、分辨率、帧率等信息。

C->S: DESCRIBE rtsp://192.168.1.100:554/stream1 RTSP/1.0
CSeq: 1
Accept: application/sdp

S->C: RTSP/1.0 200 OK
CSeq: 1
Content-Type: application/sdp
Content-Length: 123

v=0
o=- 1234567890 1234567890 IN IP4 192.168.1.100
s=Live Stream
m=video 0 RTP/AVP 96
a=rtpmap:96 H264/90000
a=framerate:25

这里有个坑,我曾经遇到过:有些老摄像头返回的SDP里,rtpmap的编码名称写的是大写「H264」,而有些写的是小写「h264」。如果你用字符串硬匹配,就会匹配不上。我建议解析时统一转成大写再比较。

注意:DESCRIBE请求必须携带 Accept: application/sdp 头,否则有些摄像头会返回415 Unsupported Media Type。我当年调试了整整一个下午才发现是少了这个头。

3.1.2 SETUP —— 商量传输通道

拿到SDP之后,下一步就是SETUP。这一步要确定两件事:用什么传输协议(UDP还是TCP),以及用什么端口。我个人习惯用TCP传输RTP,因为UDP在公网环境下容易丢包,而且很多防火墙会拦UDP。

C->S: SETUP rtsp://192.168.1.100:554/stream1/track1 RTSP/1.0
CSeq: 2
Transport: RTP/AVP/TCP;unicast;interleaved=0-1

S->C: RTSP/1.0 200 OK
CSeq: 2
Transport: RTP/AVP/TCP;unicast;interleaved=0-1;ssrc=12345678
Session: 87654321

注意这里的 interleaved=0-1,0表示RTP通道,1表示RTCP通道。这个数字是成对出现的,不能乱写。我记得有一次写代码时把interleaved写成了0-2,结果RTCP数据一直收不到,排查了半天才发现是通道号不连续。

3.1.3 PLAY —— 开始拉流

SETUP成功后,服务器会返回一个Session ID。接下来用这个Session ID发PLAY请求,摄像头就开始推流了。

C->S: PLAY rtsp://192.168.1.100:554/stream1 RTSP/1.0
CSeq: 3
Session: 87654321
Range: npt=0.000-

S->C: RTSP/1.0 200 OK
CSeq: 3
Session: 87654321
RTP-Info: url=rtsp://192.168.1.100:554/stream1/track1;seq=12345;rtptime=987654321

这里 Range: npt=0.000- 表示从开头开始播放。如果是直播流,其实写不写都行。但如果是录像回放,这个参数就很重要了。

小技巧:有些摄像头在PLAY之后不会立即发数据,需要等几百毫秒。我建议在PLAY之后加一个超时重试机制,3秒内没收到RTP包就重新SETUP一次。

3.2 SDP协议解析

SDP全称Session Description Protocol,会话描述协议。它不负责传输,只负责「描述」。就像你去餐厅吃饭,菜单上写着菜名和价格,但厨师怎么做是另一回事。SDP就是那个菜单。

SDP的格式是key=value,每行一个字段。常用的字段有:

字段 含义 示例
v= 协议版本 v=0
o= 会话发起者 o=- 1234567890 1234567890 IN IP4 192.168.1.100
s= 会话名称 s=Live Stream
m= 媒体描述 m=video 0 RTP/AVP 96
a= 属性行 a=rtpmap:96 H264/90000

解析SDP时,最核心的是 m=a= 行。m= 告诉你有几个媒体流(视频、音频),a= 告诉你每个流的具体参数。我一般会这样解析:

def parse_sdp(sdp_str):
    media_info = {}
    current_media = None
    
    for line in sdp_str.split('\n'):
        line = line.strip()
        if not line:
            continue
            
        if line.startswith('m='):
            # 解析媒体行
            parts = line[2:].split()
            current_media = {
                'type': parts[0],      # video 或 audio
                'port': int(parts[1]),
                'protocol': parts[2],
                'payload_type': int(parts[3])
            }
            media_info[current_media['type']] = current_media
            
        elif line.startswith('a=') and current_media:
            # 解析属性行
            key_value = line[2:].split(':', 1)
            if key_value[0] == 'rtpmap':
                # rtpmap:96 H264/90000
                payload, codec = key_value[1].split(' ', 1)
                codec_name, clock_rate = codec.split('/')
                current_media['codec'] = codec_name.upper()
                current_media['clock_rate'] = int(clock_rate)
                
    return media_info
重点:SDP中的payload type(比如96)是动态分配的,不同摄像头可能用不同的数字表示H264。一定要通过rtpmap行来确认,不能硬编码。

3.3 RTP/RTCP传输机制

好了,现在RTSP已经协商好了,SDP也解析完了,接下来就是真正的数据传输环节——RTP和RTCP。

3.3.1 RTP —— 真正的视频数据

RTP全称Real-time Transport Protocol,实时传输协议。它负责把视频帧切成一个个小包,加上时间戳和序列号,然后发出去。接收端根据这些信息重组画面。

RTP包的结构是这样的:

 0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|V=2|P|X|  CC   |M|     PT      |       sequence number         |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                           timestamp                           |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|           synchronization source (SSRC) identifier            |
+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
|            contributing source (CSRC) identifiers             |
|                             ....                              |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

这里有几个关键字段:

  • sequence number:序列号,每发一个包加1。用来检测丢包和排序。
  • timestamp:时间戳,单位是时钟频率的倒数(比如H264是90000Hz)。用来同步播放。
  • SSRC:同步源标识,每个流唯一。同一个摄像头的视频和音频SSRC不同。

我在项目中遇到过一个问题:有些摄像头的时间戳会溢出回绕。比如从0xFFFFFFFF突然变成0x00000000。如果你不做处理,播放器会以为时间倒流了。我建议用相对时间戳,或者检测到回绕时加一个偏移量。

3.3.2 RTCP —— 反馈和控制

RTCP全称RTP Control Protocol,RTP控制协议。它不传视频数据,只传「报告」。比如丢包率、延迟、抖动等。说白了就是接收端告诉发送端:「你发的包我收到了多少,丢了多少。」

RTCP有几种类型:

  • SR(Sender Report):发送端报告,包含发送的包数和字节数。
  • RR(Receiver Report):接收端报告,包含丢包率、延迟等信息。
  • SDES(Source Description):源描述,包含CNAME等信息。
  • BYE:结束会话。

RTCP的发送频率有讲究。RFC规定RTCP的带宽不能超过总带宽的5%。也就是说,如果你在传10Mbps的视频,RTCP最多只能占500Kbps。我一般会设置RTCP的发送间隔为5秒一次,既不会太频繁占用带宽,又能及时反馈网络状况。

避坑指南:我曾经在一个项目中,RTCP的RR包一直收不到,排查了半天发现是防火墙把RTCP端口给拦了。如果你用TCP传输RTP,RTCP是走同一个TCP连接的不同通道,不存在端口问题。所以我个人强烈建议用TCP传输RTP/RTCP,省心很多。

3.4 总结一下

RTSP、SDP、RTP/RTCP这三者配合起来,就是一个完整的视频流传输体系:

  • RTSP负责「控制」—— 协商参数、开始播放、停止播放。
  • SDP负责「描述」—— 告诉对方视频是什么格式。
  • RTP负责「传输」—— 把视频数据切成包发出去。
  • RTCP负责「反馈」—— 报告网络质量,帮助调整传输策略。

嗯,这一章内容不少,但都是实战中必须掌握的基础。下一章我们会讲如何用代码实现一个RTSP客户端,到时候这些理论知识就会变成实实在在的代码了。