RTSP概述
在上一篇博客中我们已经通过Wi-Fi P2P建立好了Source和Sink端的TCP连接,在Miracast后续的音视频传输过程中,将采用RTSP协议来对流媒体进行控制。因此接下来的步骤就到了RTSP协商、会话建立及流媒体传输的阶段。首先,什么是RTSP协议呢?
实时流协议(Real Time Streaming Protocol,RTSP)
是一种网络应用协议,专为娱乐和通信系统的使用,以控制流媒体服务器。该协议用于创建和控制终端之间的媒体会话。媒体服务器的客户端发布VCR命令,例如播放,录制和暂停,以便于实时控制从服务器到客户端(视频点播)或从客户端到服务器(语音录音)的媒体流。
流数据本身的传输不是RTSP的任务。大多数RTSP服务器使用实时传输协议(RTP)和实时传输控制协议(RTCP)结合媒体流传输。关于流媒体传输用到的协议与过程,我们将会在下一篇博客中进行详解。
抓包准备
要分析RTSP的指令协商过程,最好的方法就是抓取TCP的数据包,并进行分析。Android上可以采用tcpdump
工具抓取TCP的包,然后通过Wireshark
工具导入进行分析。具体的工具使用不在这里介绍,大家可自行查找。最终抓取到的数据包如下图所示,通过Wireshark
的过滤功能我们可以很方便的过滤RTSP协议的数据包:
WFD能力协商(Capability Negotiation)
在Source和Sink端的TCP连接成功建立之后,会马上进入到RTSP能力协商的阶段,主要涉及到RTSP M1-M4
指令,双方将按照以下顺序发送与处理消息。
RTSP M1 Messages
由Source端发起一个RTSP OPTIONS M1
请求,以确认Sink端所支持的RTSP方法请求。Request和Response如下所示:
Request(Source -> Sink)
1 | OPTIONS * RTSP/1.0\r\n |
Response(Sink -> Source)
1 | RTSP/1.0 200 OK\r\n |
RTSP M2 Messages
在Sink回复完M1指令后,会由Sink端发起一个RTSP OPTIONS M2
请求,以确认Source端所支持的RTSP方法请求。Request和Response如下所示:
Request(Sink -> Source)
1 | OPTIONS * RTSP/1.0\r\n |
Response(Source -> Sink)
1 | RTSP/1.0 200 OK\r\n |
RTSP M3 Messages
在Source端回复完M2指令后,会由Source端发起GET_PARAMETER M3
请求,以查询Sink端的属性以及能力,所查询的属性列表在请求最后。
Request(Source -> Sink)
1 | GET_PARAMETER rtsp://localhost/wfd1.0 RTSP/1.0\r\n |
Sink端回复M3指令,告诉Source端自身支持的属性及能力,比较重要的几个属性:RTP端口号wfd_client_rtp_ports
(传输流媒体用)、所支持的audio及video编解码格式wfd_audio_codecs
,wfd_video_formats
等…
Response(Sink -> Source)
1 | RTSP/1.0 200 OK\r\n |
RTSP M4 Messages
基于Sink端回复的M3指令,将由Source端发起SET_PARAMETER M4
指令,以最终设置此次会话里的最佳参数集(收发双方都支持的编解码器类型等等)
Request(Source -> Sink)
1 | SET_PARAMETER rtsp://localhost/wfd1.0 RTSP/1.0\r\n |
Response(Sink -> Source)
1 | RTSP/1.0 200 OK\r\n |
wfd_video_formats格式解析
在官方WifiDisplaySource.cpp源码中,有对wfd_video_formats
格式做了详细解释,各个字段的含义如下:
1 | // wfd_video_formats: |
其中比较重要的几个字段解释如下图所示:
Field | Size (octets) | Description |
---|---|---|
Native Resolutions/Refresh Rates bitmap | 1 | Bitmap defined in Table 37 detailing native display resolutions and refresh rates supported by the WFD Sink. |
Profiles bitmap | 1 | Bitmap defined in Table 38 detailing the H.264 profile indicated by this instance of the WFD Video Formats subelement. |
Levels bitmap | 1 | Bitmap defined in Table 39 detailing the H.264 level indicated by this instance of the WFD Video Formats subelement. |
CEA Resolutions/Refresh Rates bitmap | 4 | Bitmap defined in Table 34 detailing CEA resolutions and refresh rates supported by the CODEC. |
VESA Resolutions/Refresh Rates bitmap | 4 | Bitmap defined in Table 35 detailing VESA resolutions and refresh rates supported by the CODEC. |
HH Resolutions/Refresh Rates bitmap | 4 | Bitmap defined in Table 36 detailing HH resolutions and refresh rates supported by the CODEC. |
Native Resolutions/Refresh Rates Bitmap
该字段描述了Sink端设备当前的分辨率与刷新率,占1字节,其中低3位代表了分辨率模式选择位,高5位则代表当前分辨率与刷新率在表中的index。其中000代表选用CEA标准分辨率,而001则代表VESA标准分辨率,详见下表:
Bits | Name | Interpretation |
---|---|---|
2:0 | Table Selection bits | 0b000: Resolution/Refresh rate table selection: Index to CEA resolution/refresh rates (Table 34) 0b001: Resolution/Refresh rate table selection: Index VESA resolution/refresh rates (Table 35) 0b010: Resolution/Refresh rate table selection: Index HH resolutions/refresh rates (Table 36) 0b011~0b111: Reserved |
7:3 | Index bits | Index into resolution/refresh rate table selected by [B2:B0] |
CEA Resolutions/Refresh Rates Bitmap
该字段描述了当前Sink端设备所支持的分辨率与刷新率,占4个字节,总共32位,其中每一位都是一个flag,值为1代表支持该标志位对应的分辨率与刷新率。其中可以同时对多个标志位置1,代表同时支持这几种分辨率与刷新率。
Bits | Index | Interpretation |
---|---|---|
0 | 0 | 640x480 p60 |
1 | 1 | 720x480 p60 |
2 | 2 | 720x480 i60 |
3 | 3 | 720x576 p50 |
4 | 4 | 720x576 i50 |
5 | 5 | 1280x720 p30 |
6 | 6 | 1280x720 p60 |
7 | 7 | 1920x1080 p30 |
8 | 8 | 1920x1080 p60 |
9 | 9 | 1920x1080 i60 |
10 | 10 | 1280x720 p25 |
11 | 11 | 1280x720 p50 |
12 | 12 | 1920x1080 p25 |
13 | 13 | 1920x1080 p50 |
14 | 14 | 1920x1080 i50 |
15 | 15 | 1280x720 p24 |
16 | 16 | 1920x1080 p24 |
31:17 | - | Reserved |
Profiles Bitmap
该字段描述了当前Sink端设备所支持的H.264 profile配置,占1字节,低0位与低1位分别是CBP(Constrained Baseline Profile)与CHP(Constrained High Profile)标志位,置1时代表支持该配置,剩下的6位则为保留位。
Bits | Name | Interpretation |
---|---|---|
0 | CBP bit | 0b0: Constrained Baseline Profile (CBP) not supported 0b1: CBP supported |
1 | CHP bit | 0b0: Constrained High Profile (CHP) not supported 0b1: CHP supported |
7:2 | Reserved | Set to all zeros |
Levels Bitmap
该字段描述了当前Sink端设备所支持的H.264 level限制。其中level是一组特定的约束,表示一个profile所需的解码性能。占1字节,低5位代表了各个level的flag位,置1时表示支持该level。关于H.264中profile与level的介绍不在这里详细展开,有兴趣的可以自行了解。
Bits | Name | Interpretation |
---|---|---|
0 | H.264 Level 3.1 bit | 0b0: H.264 Level 3.1 not supported 0b1: H.264 Level 3.1 supported |
1 | H.264 Level 3.2 bit | 0b0: H.264 Level 3.2 not supported 0b1: H.264 Level 3.2 supported |
2 | H.264 Level 4 bit | 0b0: H.264 Level 4 not supported 0b1: H.264 Level 4 supported |
3 | H.264 Level 4.1 bit | 0b0: H.264 Level 4.1 not supported 0b1: H.264 Level 4.1 supported |
4 | H.264 Level 4.2 bit | 0b0: H.264 Level 4.2 not supported 0b1: H.264 Level 4.2 supported |
7:5 | Reserved | Set to all zeros |
Example
我们这里拿几个官方的Example来帮助大家理解,如第一组数据30 00 02 02 00000040 ...
对应为720p/60
帧,其中
Native 30
二进制表示为0011 0000
,低3位为0,代表CEA标准分辨率;高5位为分辨率index,00110换算为6,对应CEA分辨率表中的Index 61280x720 p60
Profile 02
二进制表示为0000 0010
,表示支持Constrained High ProfileLevels 02
二进制表示为0000 0010
,表示支持H.264 Level 3.2CEA 00000040
二进制0000 0000 0000 0000 0000 0000 0100 0000
,也就是第6位flag置1,对应CEA分辨率表中的Index 61280x720 p60
。此外,CEA字段可以同时对多个标志位置1,代表同时支持这几种分辨率与刷新率。
其他的几个Example大家可以按照上面的思路一一分析,这里不再展开。
1 | // For 720p60: |
WFD会话建立(Session Establishment)
在Sink回复完M4指令后,能力协商的过程就结束了,下一步则是WFD会话建立过程,主要涉及到RTSP M5-M7
指令。双方将按照以下顺序发送与处理消息。
RTSP M5 Messages
由Source端发起SET_PARAMETER M5
请求,通过wfd_trigger_method
参数触发Sink端向Source端进行SETUP、PLAY、PAUSE、TEARDOWN
等请求。如下M5 Request中设置了SETUP
触发请求。
Request(Source -> Sink)
1 | SET_PARAMETER rtsp://localhost/wfd1.0 RTSP/1.0\r\n |
Sink端则正常回复,表示自己已经收到SETUP
触发请求即可。
Response(Sink -> Source)
1 | RTSP/1.0 200 OK\r\n |
RTSP M6 Messages
上面说到了M5 Request中设置了SETUP
触发请求,则此时应该由Sink端主动发送SETUP M6
请求:
Request(Sink -> Source)
1 | SETUP rtsp://192.168.49.5/wfd1.0/streamid=0 RTSP/1.0\r\n |
此时Source端将完成RTSP会话的创建,并返回Session ID:
Response(Source -> Sink)
1 | RTSP/1.0 200 OK\r\n |
RTSP M7 Messages
经过M6指令交互后,RTSP会话已经完成创建。此时将由Sink端发送PLAY M7
请求,告诉发送端可以开始发送流媒体数据了:
Request(Sink -> Source)
1 | PLAY rtsp://192.168.49.5/wfd1.0/streamid=0 RTSP/1.0\r\n |
Source端回复M7指令,并且状态是200 OK
时,WFD Session成功建立。
Response(Source -> Sink)
1 | RTSP/1.0 200 OK\r\n |
经过以上M1-M7
的指令交互,且成功创建WFD会话后,Source与Sink端的协商及会话过程已完成。这个时候Source端会按照Sink指定的UDP端口发送RTP数据包,包含音视频数据。
WFD长连接(keep-alive)
WFD内的长连接机制主要是为了确保WFD会话处于正常的状态,有点类似于TCP长连接的心跳机制,若超时,则Source或Sink端应该断开相关的RTSP与RTP连接。根据官方文档的介绍,我们需要注意以下几点:
超时的设置:Source端可以在M6的Reponse中设置
timeout
参数,以指定超时时间,格式详见上述M6 ResponseSource端需要定期发送M16指令
Sink端在收到M16指令后要马上进行回复
若Source端在指定的超时时间内未收到至少一条M16响应消息,则Source端会主动断开RTSP与RTP连接
若Sink端在指定的超时时间内未收到至少一条M16请求消息,则Sink端应该主动断开RTSP与RTP连接
默认超时时间为60秒,如果需要自己指定,则值应该>10秒
因此,作为Sink端,我们在收到M16指令后,需要马上进行回复,否则可能面临连接断开的风险。而且,当达到一定的超时时间后没有收到Source端的M16请求,我们也应该主动断开连接,超时时间可以通过timeout
属性获取。(刚开始写demo的时候就是忘了这一步,导致连接过一段时间就会断掉,看了官方文档后才发现超时这个问题)
RTSP M16 Messages
M16请求由Source端发起,此消息为一个空的GET_PARAMETER
请求
Request(Source -> Sink)
1 | GET_PARAMETER rtsp://localhost/wfd1.0 RTSP/1.0\r\n |
Sink端收到后进行200 OK
回复即可
Response(Sink -> Source)
1 | RTSP/1.0 200 OK\r\n |
关闭会话
在RTSP M5 Messages
这一节中,我们谈到会由Source端发起SET_PARAMETER M5
请求,触发Sink端发送TEARDOWN
请求。该请求可以使得Source与Sink端的RTSP及RTP连接断开。
RTSP M8 Messages
- 场景1:Source端主动关闭会话:
首先会由Source发送M5请求,并且wfd_trigger_method
的值为TEARDOWN
,触发Sink端发送TEARDOWN M8
指令:
- 场景2:Sink端主动关闭会话:
Sink端直接发送TEARDOWN M8
指令
Request(Sink -> Source)
1 | TEARDOWN rtsp://192.168.49.5/wfd1.0/streamid=0 RTSP/1.0\r\n |
Response(Source -> Sink)
1 | RTSP/1.0 200 OK\r\n |
总结
针对Miracast RTSP协商、会话建立及流媒体传输,我们来进行一下总结。
- 在Source和Sink端的TCP连接成功建立之后,会马上进入到RTSP能力协商的阶段,主要涉及到
M1-M4
指令 - 能力协商的过程结束后,下一步则是WFD会话建立过程,主要涉及到
M5-M7
指令 - WFD会话成功建立后,将由Source端通过UDP连接发送RTP音视频数据包
- 通过
PAUSE、PLAY、TEARDOWN
等指令控制音视频流暂停、播放、关闭 - 通过
M16
指令来维持WFD长连接,确保会话处于正常的状态
我们可以使用下图来对整个WFD会话的生命周期进行总结:
参考
Wi-Fi_Display_Technical_Specification_v2.1_0.pdf
- 4.6 WFD Capability Negotiation
- 4.8 WFD session establishment
- 6.4 RTSP Messages
- 6.5.1 WFD keep-alive