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工具(tcpdump详解)抓取TCP的包,然后通过Wireshark工具(wireshark抓包新手使用教程)导入进行分析。

# 在Source端抓取tcpdump
adb shell
tcpdump -i any -p -s 0 -w /data/local/capture.pcap
# -i any 表示抓取所有接口(waln0、p2p0等)

最终抓取到的数据包如下图所示,通过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)

OPTIONS * RTSP/1.0\r\n
Date: Wed, 11 Dec 2019 08:31:54 +0000\r\n
Server: stagefright/1.2 (Linux;Android 8.1.0)\r\n
CSeq: 1\r\n
Require: org.wfa.wfd1.0\r\n
\r\n

Response(Sink -> Source)

RTSP/1.0 200 OK\r\n
CSeq: 1\r\n
Public: org.wfa.wfd1.0, GET_PARAMETER, SET_PARAMETER\r\n
\r\n

RTSP M2 Messages

在Sink回复完M1指令后,会由Sink端发起一个RTSP OPTIONS M2请求,以确认Source端所支持的RTSP方法请求。Request和Response如下所示:

Request(Sink -> Source)

OPTIONS * RTSP/1.0\r\n
CSeq: 0\r\n
User-Agent: wfdsinkemu\r\n
Require: org.wfa.wfd1.0\r\n
\r\n

Response(Source -> Sink)

RTSP/1.0 200 OK\r\n
Date: Wed, 11 Dec 2019 08:31:54 +0000\r\n
Server: stagefright/1.2 (Linux;Android 8.1.0)\r\n
CSeq: 0\r\n
Public: org.wfa.wfd1.0, SETUP, TEARDOWN, PLAY, PAUSE, GET_PARAMETER, SET_PARAMETER\r\n
\r\n

RTSP M3 Messages

在Source端回复完M2指令后,会由Source端发起GET_PARAMETER M3请求,以查询Sink端的属性以及能力,所查询的属性列表在请求最后。

Request(Source -> Sink)

GET_PARAMETER rtsp://localhost/wfd1.0 RTSP/1.0\r\n
Date: Wed, 11 Dec 2019 08:31:54 +0000\r\n
Server: stagefright/1.2 (Linux;Android 8.1.0)\r\n
CSeq: 2\r\n
Content-type: text/parameters
Content-length: 83
\r\n
Line-based text data: text/parameters (4 lines)
wfd_content_protection\r\n
wfd_video_formats\r\n
wfd_audio_codecs\r\n
wfd_client_rtp_ports\r\n

Sink端回复M3指令,告诉Source端自身支持的属性及能力,比较重要的几个属性:RTP端口号wfd_client_rtp_ports(传输流媒体用)、所支持的audio及video编解码格式wfd_audio_codecswfd_video_formats等…

Response(Sink -> Source)

RTSP/1.0 200 OK\r\n
CSeq: 2\r\n
Content-type: text/parameters
Content-length: 848
\r\n
Line-based text data: text/parameters (7 lines)
wfd_client_rtp_ports: RTP/AVP/UDP;unicast 20011 0 mode=play\r\n
wfd_audio_codecs: LPCM 00000002 00, AAC 00000001 00\r\n
wfd_video_formats: 78 00 01 01 00008400 00000000 00000000 00 0000 0000 00 none none\r\n
wfd_connector_type: 07\r\n
wfd_uibc_capability: none\r\n
wfd_content_protection: none\r\n
wfd_idr_request_capability: 1\r\n

RTSP M4 Messages

基于Sink端回复的M3指令,将由Source端发起SET_PARAMETER M4指令,以最终设置此次会话里的最佳参数集(收发双方都支持的编解码器类型等等)

Request(Source -> Sink)

SET_PARAMETER rtsp://localhost/wfd1.0 RTSP/1.0\r\n
Date: Wed, 11 Dec 2019 08:31:54 +0000\r\n
Server: stagefright/1.2 (Linux;Android 8.1.0)\r\n
CSeq: 3\r\n
Content-type: text/parameters
Content-length: 247
\r\n
Line-based text data: text/parameters (4 lines)
wfd_video_formats: 00 00 01 01 00000400 00000000 00000000 00 0000 0000 00 none none\r\n
wfd_audio_codecs: AAC 00000001 00\r\n
wfd_presentation_URL: rtsp://192.168.49.5/wfd1.0/streamid=0 none\r\n
wfd_client_rtp_ports: RTP/AVP/UDP;unicast 20011 0 mode=play\r\n

Response(Sink -> Source)

RTSP/1.0 200 OK\r\n
CSeq: 3\r\n
\r\n

wfd_video_formats格式解析

在官方WifiDisplaySource.cpp源码中,有对wfd_video_formats格式做了详细解释,各个字段的含义如下:

// wfd_video_formats:
// 1 byte "native"
// 1 byte "preferred-display-mode-supported" 0 or 1
// one or more avc codec structures
// 1 byte profile
// 1 byte level
// 4 byte CEA mask
// 4 byte VESA mask
// 4 byte HH mask
// 1 byte latency
// 2 byte min-slice-slice
// 2 byte slice-enc-params
// 1 byte framerate-control-support
// max-hres (none or 2 byte)
// max-vres (none or 2 byte)

其中比较重要的几个字段解释如下图所示:

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

例子

我们这里拿几个官方的例子来帮助大家理解,如第一组数据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 Profile
  • Levels 02二进制表示为0000 0010,表示支持H.264 Level 3.2
  • CEA 00000040二进制0000 0000 0000 0000 0000 0000 0100 0000,也就是第6位flag置1,对应CEA分辨率表中的Index 61280x720 p60。此外,CEA字段可以同时对多个标志位置1,代表同时支持这几种分辨率与刷新率。

其他的几个例子大家可以按照上面的思路一一分析,这里不再展开。

// For 720p60:
// use "30 00 02 02 00000040 00000000 00000000 00 0000 0000 00 none none\r\n"
// For 720p30:
// use "28 00 02 02 00000020 00000000 00000000 00 0000 0000 00 none none\r\n"
// For 720p24:
// use "78 00 02 02 00008000 00000000 00000000 00 0000 0000 00 none none\r\n"
// For 1080p30:
// use "38 00 02 02 00000080 00000000 00000000 00 0000 0000 00 none none\r\n"

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)

SET_PARAMETER rtsp://localhost/wfd1.0 RTSP/1.0\r\n
Date: Wed, 11 Dec 2019 08:31:54 +0000\r\n
Server: stagefright/1.2 (Linux;Android 8.1.0)\r\n
CSeq: 4\r\n
Content-type: text/parameters
Content-length: 27
\r\n
Line-based text data: text/parameters (1 lines)
wfd_trigger_method: SETUP\r\n

Sink端则正常回复,表示自己已经收到SETUP触发请求即可。

Response(Sink -> Source)

RTSP/1.0 200 OK\r\n
CSeq: 4\r\n
\r\n

RTSP M6 Messages

上面说到了M5 Request中设置了SETUP触发请求,则此时应该由Sink端主动发送SETUP M6请求:

Request(Sink -> Source)

SETUP rtsp://192.168.49.5/wfd1.0/streamid=0 RTSP/1.0\r\n
CSeq: 1\r\n
Transport: RTP/AVP/UDP;unicast;client_port=20011
\r\n

此时Source端将完成RTSP会话的创建,并返回Session ID:

Response(Source -> Sink)

TSP/1.0 200 OK\r\n
Date: Wed, 11 Dec 2019 08:31:55 +0000\r\n
Server: stagefright/1.2 (Linux;Android 8.1.0)\r\n
CSeq: 1\r\n
Session: 1804289383;timeout=30
Transport: RTP/AVP/UDP;unicast;client_port=20011;server_port=26466
\r\n

RTSP M7 Messages

经过M6指令交互后,RTSP会话已经完成创建。此时将由Sink端发送PLAY M7请求,告诉发送端可以开始发送流媒体数据了:

Request(Sink -> Source)

PLAY rtsp://192.168.49.5/wfd1.0/streamid=0 RTSP/1.0\r\n
CSeq: 2\r\n
Session: 1804289383;timeout=30
\r\n

Source端回复M7指令,并且状态是200 OK时,WFD Session成功建立。

Response(Source -> Sink)

RTSP/1.0 200 OK\r\n
Date: Wed, 11 Dec 2019 08:31:55 +0000\r\n
Server: stagefright/1.2 (Linux;Android 8.1.0)\r\n
CSeq: 2\r\n
Session: 1804289383;timeout=30
Range: npt=now-\r\n
\r\n

经过以上M1-M7的指令交互,且成功创建WFD会话后,Source与Sink端的协商及会话过程已完成。这个时候Source端会按照Sink指定的UDP端口发送RTP数据包,包含音视频数据。

总结M1-M7过程:

Message Method Direct 简述 重点
M1 OPTIONS Source -> Sink 打招呼
M2 OPTIONS Source <- Sink 打招呼
M3 GET_PARAMETER Source -> Sink 你支持什么音视频格式 wfd_video_formats/wfd_audio_codecs
M4 SET_PARAMETER Source -> Sink 我们使用这个格式吧
M5 SETUP Source -> Sink 建立连接吧
M6 SETUP Source <- Sink 建立连接吧 client_port/server_port/Session ID
M7 PLAY Source <- Sink 开始发送数据吧

关闭会话

RTSP M5 Messages这一节中,我们谈到会由Source端发起SET_PARAMETER M5请求,触发Sink端发送TEARDOWN请求。该请求可以使得Source与Sink端的RTSP及RTP连接断开。

RTSP M8 Messages

  1. 场景1:Source端主动关闭会话:

    首先会由Source发送M5请求,并且wfd_trigger_method的值为TEARDOWN,触发Sink端发送TEARDOWN M8指令:
  2. 场景2:Sink端主动关闭会话:

    Sink端直接发送TEARDOWN M8指令

Request(Sink -> Source)

TEARDOWN rtsp://192.168.49.5/wfd1.0/streamid=0 RTSP/1.0\r\n
CSeq: 3\r\n
Session: 1804289383
\r\n

Response(Source -> Sink)

RTSP/1.0 200 OK\r\n
CSeq: 3\r\n
Date: Tue, Dec 17 2019 07:20:28 GMT\r\n
\r\n

总结

针对Miracast RTSP协商、会话建立及流媒体传输,我们来进行一下总结。

  • 在Source和Sink端的TCP连接成功建立之后,会马上进入到RTSP能力协商的阶段,主要涉及到M1-M4指令
  • 能力协商的过程结束后,下一步则是WFD会话建立过程,主要涉及到M5-M7指令
  • WFD会话成功建立后,将由Source端通过UDP连接发送RTP音视频数据包
  • 通过PAUSE、PLAYTEARDOWN等指令控制音视频流暂停、播放、关闭
  • 通过M16指令来维持WFD长连接,确保会话处于正常的状态

我们可以使用下图来对整个WFD会话的生命周期进行总结:

参考:

WFD_RTSP交互包分析

Miracast技术详解(二):RTSP协议

Miracast技术详解(二):RTSP协议的更多相关文章

  1. P2P技术详解(二):P2P中的NAT穿越(打洞)方案详解

    1.内容概述 P2P即点对点通信,或称为对等联网,与传统的服务器客户端模式(如下图"P2P结构模型"所示)有着明显的区别,在即时通讯方案中应用广泛(比如IM应用中的实时音视频通信. ...

  2. IPv6技术详解:基本概念、应用现状、技术实践(下篇)

    本文来自微信技术架构部的原创技术分享. 1.前言 在上篇<IPv6技术详解:基本概念.应用现状.技术实践(上篇)>,我们讲解了IPV6的基本概念. 本篇将继续从以下方面展开对IPV6的讲解 ...

  3. IPv6技术详解:基本概念、应用现状、技术实践(上篇)

    本文来自微信技术架构部的原创技术分享. 1.前言 普及IPV6喊了多少年了,连苹果的APP上架App Store也早已强制IPV6的支持,然并卵,因为历史遗留问题,即使在IPV4地址如果饥荒的情况下, ...

  4. CDN学习笔记二(技术详解)

    一本好的入门书是带你进入陌生领域的明灯,<CDN技术详解>绝对是带你进入CDN行业的那盏最亮的明灯.因此,虽然只是纯粹的重点抄录,我也要把<CDN技术详解>的精华放上网.公诸同 ...

  5. Zookeeper系列二:分布式架构详解、分布式技术详解、分布式事务

    一.分布式架构详解 1.分布式发展历程 1.1 单点集中式 特点:App.DB.FileServer都部署在一台机器上.并且访问请求量较少 1.2  应用服务和数据服务拆分  特点:App.DB.Fi ...

  6. CDN技术详解及实现原理

    CDN技术详解 一本好的入门书是带你进入陌生领域的明灯,<CDN技术详解>绝对是带你进入CDN行业的那盏最亮的明灯.因此,虽然只是纯粹的重点抄录,我也要把<CDN技术详解>的精 ...

  7. 架构设计:远程调用服务架构设计及zookeeper技术详解(下篇)

    一.下篇开头的废话 终于开写下篇了,这也是我写远程调用框架的第三篇文章,前两篇都被博客园作为[编辑推荐]的文章,很兴奋哦,嘿嘿~~~~,本人是个很臭美的人,一定得要截图为证: 今天是2014年的第一天 ...

  8. SSE技术详解:一种全新的HTML5服务器推送事件技术

    前言 一般来说,Web端即时通讯技术因受限于浏览器的设计限制,一直以来实现起来并不容易,主流的Web端即时通讯方案大致有4种:传统Ajax短轮询.Comet技术.WebSocket技术.SSE(Ser ...

  9. Protocol Buffer技术详解(语言规范)

    Protocol Buffer技术详解(语言规范) 该系列Blog的内容主体主要源自于Protocol Buffer的官方文档,而代码示例则抽取于当前正在开发的一个公司内部项目的Demo.这样做的目的 ...

  10. 腾讯技术分享:GIF动图技术详解及手机QQ动态表情压缩技术实践

    本文来自腾讯前端开发工程师“ wendygogogo”的技术分享,作者自评:“在Web前端摸爬滚打的码农一枚,对技术充满热情的菜鸟,致力为手Q的建设添砖加瓦.” 1.GIF格式的历史 GIF ( Gr ...

随机推荐

  1. MarkDown书写语法(常用格式)

    实际上每个 Markdown 应用程序都实现了稍有不同的 Markdown 语法,熟悉MarkDown书写语法常用格式,满足日常文字编辑需求 1.标题 请在单词或短语前面添加井号 (#) .# 的数量 ...

  2. Collectors.toMap的暗坑与避免方式

    使用Java的stream中的Collectors可以很方便地做容器间的转换,可以少写很多代码.但是其中有暗含的坑需要注意和避免,本文探讨Collectors.toMap(JDK8版本). Colle ...

  3. 21.2 静态TLS--《Windows核心编程》

    部分笔记来自于:https://blog.csdn.net/Steven_programe_life/article/details/103358251?utm_medium=distribute.p ...

  4. Adoquery.Refresh 慎用。。。。非常严重,会带来各种问题。

    adoquery.refresh 各种问题,根本启不到刷新的作用.完全不刷新的节奏. 修改成已经打印后,如果用adoquery.refresh的话,这两个订单 并不会被刷新掉,惨吧......

  5. HBase-统计表总行数的三种方式

    由于Hbase是列式数据库,没有提供类似SQL的数据查询语句,可以通过以下三种方式获取表的总行数. 1. 使用Hbase自带的Count命令 hbase提供了count命令可以在hbase交互界面使用 ...

  6. Python xpath语法与 lxml 模块

    XPath 语法 XPath 使用路径表达式来选取 XML 文档中的节点或节点集.节点是通过沿着路径 (path) 或者步 (steps) 来选取的. XML 实例文档 我们将在下面的例子中使用这个 ...

  7. 配置主机访问virtualbox中redhat7.3虚拟机网络(其他系统配置也类似)

    为什么默认无法访问? virtualbox默认分配一个NAT网络,这个是给虚拟机操作系统访问互联网用的,默认主机通过这个ip段无法直接访问虚拟机.[网卡1] 需要添加一块网卡 在虚拟机关闭状态下,点[ ...

  8. SpringBoot+Shiro+LayUI权限管理系统项目-6.实现角色管理

    1.说明 只讲解关键部分,详细看源码,文章下方捐赠或QQ联系捐赠获取. 2.功能展示 3.业务模型 @Data @EqualsAndHashCode(callSuper = false) @Acces ...

  9. java generic 介绍

    一 介绍: 在Java SE 1.5之前,没有泛型的情况的下,通过对类型Object的引用来实现参数的"任意化","任意化"带来的缺点是要做显式的强制类型转换, ...

  10. MyBaits查询MySQL日期类型结果相差8个小时

    问题描述 在Java项目中使用MyBatis作为ORM框架,但是查询出的MySQL日期类型字段值总是比数据库表里的值多8个小时. 具体说明: MySQL数据库表字段类型为timestamp,映射的Ja ...