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. Linux中国开源社区停止运营

    layout: post title: "Linux 中国开源社区停止运营" tags: - "Linux" 昨天看到老王发的公众号文章,Linux中国开源社区 ...

  2. AdoQuery 多列 查询 定位方法

    frmClientDm.TopItemSkuShow_adoq.Locate('top_outer_iid;top_outer_sid', VarArrayOf([top_outer_iid,top_ ...

  3. .NET Core开发实战(第24课:文件提供程序:让你可以将文件放在任何地方)--学习笔记

    24 | 文件提供程序:让你可以将文件放在任何地方 文件提供程序核心类型: 1.IFileProvider 2.IFileInfo 3.IDirectoryContents IFileProvider ...

  4. Pandas—to_csv()写入函数参数详解

    1. to_csv函数的参数 DataFrame.to_csv(path_or_buf=None, sep=',', na_rep='', float_format=None, columns=Non ...

  5. 《Boosting Document-Level Relation Extraction by Mining and Injecting Logical Rules》论文阅读笔记

    代码 原文地址 摘要 文档级关系抽取(DocRE)旨在从文档中抽取出所有实体对的关系.DocRE 面临的一个主要难题是实体对关系之间的复杂依赖性.与大部分隐式地学习强大表示的现有方法不同,最新的 Lo ...

  6. NC19995 [HAOI2015]树上操作

    题目链接 题目 题目描述 有一棵点数为 N 的树,以点 1 为根,且树点有边权. 然后有 M 个 操作,分为三种: 操作 1 :把某个节点 x 的点权增加 a . 操作 2 :把某个节点 x 为根的子 ...

  7. Linux 中hdparm命令参数说明

    hdparm命令提供了一个命令行的接口用于读取和设置IDE或SCSI硬盘参数. 语法 hdparm(选项)(参数) 选项 -a:设定读取文件时,预先存入块区的分区数,若不加上选项,则显示目前的设定: ...

  8. 轻松玩转Makefile | 基础用法

    前言 本文通过几个简单的示例,可以快速了解Makefile的基本使用方法,适用于编译我们平时练习所编写的小量代码. 1. make命令 Makefile文件内容: all为目标,这里没有依赖的文件,这 ...

  9. 【framework】RootWindowContainer简介

    1 前言 ​ RootWindowContainer 是窗口容器的根容器,子容器是 DisplayContent.关于其父类及祖父类的介绍,见→WindowContainer简介.Configurat ...

  10. 使用 Visual Studio 断点调试 DLL

    继上文说到使用 IDA 和 WinDbg 调试无 dmp 文件  那么在有源码的情况下可以直接断点调试 DLL,目的是查看 DLL 内部的函数调用 场景: 程序执行到某个 DLL 时突然崩溃,先确定 ...