实时消息传输协议 RTMP(Real Time Messaging Protocol)
http://blog.csdn.net/defonds/article/details/17403225
译序:本文是维基百科关于 RTMP 的解释, 关于 RTMP 官方规范参见 RTMP 规范,关于 RTMP 官方规范的中文版,参见《Adobe 官方公布的 RTMP 规范》。以下是维基百科原文:
实时消息传输协议(RTMP)最初是由 Macromedia 为互联网上 Flash player 和服务器之间传输音频、视频以及数据流而开发的一个私有协议。Adobe 收购 Macromedia 购以后,公布了这个协议的一部分,以备公共使用。
RTMP 协议有多个变种:
1. 工作在 TCP 协议之上,并使用默认端口号 1935 的明文协议。
2. RTMPS 使用 TLS/SSL 连接的 RTMP 协议。
3. RTMPE 使用 Adobe 自己的安全机制的加密 RTMP。虽然它的实现细节是私有的,但使用的是行业标准加密原语。RTMPE 的设计是有缺陷的,它本身并不提供实际的安全。
4. RTMPT 封装在 HTTP 请求内部以穿越防火墙的协议。RTMPT 常用于建立 TCP 端口 80 和 443 的请求以绕开很多公司的流量过滤。封装的会话中可能会携带纯 RTMP、RTMPS 或者 RTMPE 包。
尽管 RTMP 协议的本意是一个用于播放 Flash 视频的协议,但它也往往用于其他应用,比如 Adobe LiveCycle数据服务。
基本操作
RTMP(RTMFP 除外)是一个基于 TCP
的、持久连接并提供低延迟通信的协议。为了能够顺利地传输流,并且传递尽可能多的信息,RTMP
对流进行分段,客户端和服务器可以对分段长度进行协商,尽管有时分段长度是不变的:对于音频数据默认分段长度是 64
字节,视频数据和大部分其他数据类型默认分段长度是为 128 字节。来自不同流的段会被隔离,并对单一连接的段进行合成。对于比较长的数据块,RTMP
会在每一段中携带一个单字节头,所以开销很小。然而,事实应用中,不同的段并不互相交叉。交叉和合成是在数据包层完成的,RTMP
包穿过不同的活跃通道进行交叉,用这种方法来保证每个通道都满足各自的带宽、延迟以及其他一些服务质量的需求。这种模型下交叉的 RTMP
包被视为不可分割的,并且在分段级别是不交叉的。
RTMP 定义了一些虚拟通道,通过它们可以发送和接收 RTMP 包,并且这些通道彼此是独立运作的。例如,可能会有一个用于处理
RPC
请求和响应的通道,一个用于视频流数据的通道,一个用于音频流数据的通道,一个用于带外控制信息(分段长度协商,等等)的通道,等等。在一个典型的
RTMP 会话中,在任意给定时间内,可能会有几个通道是同时活跃的。当 RTMP
数据被编码时,会产生一个包头。包头定义了其他一些事项,要发送到通道的 id,这一包产生时的 timestamp
(如果需要的话),以及这一包的有效负载。包头后紧跟这一包实际负载的内容,包的内容是在发送给连接前根据当前协商好的分段长度分割好的。包头自己不会分
段,并且包头的长度也不会被计入这一包第一个分段的长度中去。换句话说,分段的主题仅仅是
RTMP 的包负载(音频数据)。
更高一层讲,RTMP 压缩 MP3 或者 AAC 音频和 FLV1 视频多媒体流,并使用 AMF (Action Message
Format) 协议进行远程方法调用 (RPCs)。所需的 RPC 服务都是异步的,它们使用单一的 client/server
request/response 模型,因此不需要实时通信。
加密
RTMP 会话可以使用以下两种方法的任意一种进行加密:
使用行业标准 TLS/SSL 机制。底层的 RTMP 会话呗简单地封装在一个普通的 TLS/SSL 会话中。
使用 RTMPE,将 RTMP 会话封装在一个轻量级的加密层里。
普遍认为,会话开始时的 TLS/SSL 握手是非常计算密集型的。Adobe 开发了 RTMPE
作为一个轻量级的替代,给提供加密服务的高流量的站点一个实用的选择。Adobe 通告 RTMPE
作为一个安全内容传递的方法,以避免模拟客户端的操作,这种说法是错误的。RTMPE 仅使用了 Diffie-Hellman
机制,没有提供任何一方的身份验证,这很容易在会话初始化时受到中间人攻击。
HTTP 隧道
在 RTMP 隧道 (RTMPT) 中,RTMP 数据被密封起来并通过 HTTP 进行交换,来自客户端(在这种情况下客户端是为 media player)的信息发送给服务器上的端口 80 (HTTP 默认端口号)。
由于 HTTP 头的缘故,RTMPT 中的信息要比等效的非通道的 RTMP 信息大,在非通道 RTMP 不可以的场景中,比如当客户端处于一个阻止非 HTTP 和 非 HTTPS 网络流通时,RTMPT 可以便利地使用。
这个协议通过使用 POST url 发送命令和使用 POST 体发送 AMF 消息进行工作。例子:
[plain] view plaincopyprint?
- POST /open/1 HTTP/1.1
用于打开一个连接。
规范文档
Adobe 公开声明的规范是 2009 年 6 月 15 日发布的 RTMP
specification。那个规范,忽略了(应该说没有公开) 协议实现的关键细节。很难单单根据发布的规范将 RTMP
协议整合进我们的程序里,太多必不可少的细节被忽略了,我们只能够通过学习实现了这个协议的应用 (比如 librtmp),以及根据测试 TCP/IP
数据包捕获,来断定其很少的细节。
Adobe 关于使用这一协议的 license 要求 RTMP 服务器的实现满足这一规范。
Adobe 发布的规范中缺少的细节包括:
- 关于 RTMP
握手没有只言片语的描述。如果(关于握手)做的不正确,服务器的实现将无法传递 H.264/AAC 内容。如果握手错误,Flash player
会默默地接收 H.264 内容失败。但是所有的客户端实现能够正常工作,因为 RTMP 服务器在这方面比较宽容(包括 FMS)。 - 发送的块只以最大块大小发送;超出那个大小的块仍会被发送,这个块带有整个块大小的头,但是当超出最大块大小后,一个类型为 4 的块头会被发送,紧跟其后的是这一块被分割出来的下一部分。
- 关于流的管理信息的解释缺失(31 和 32)。FMS 会时不时发送这些包。
数据包格式
数据包由客户端和服务器之间建立的 TCP 连接进行发送。数据包包含一个头和一个体,至于连接和控制命令使用
AMF(Action Message
Format)编码。头分为基本报头(在图中显示为分离出来的那块)和块消息报头。基本报头是数据包唯一不变的部分,常常由一个复合字节组成,两个有效位
代表块类型(规范中的格式),其余的组成了流
id。根据前者的值,一些消息头字段可以被忽略掉,这些字段由前面的数据包根据后面的值派生出来,基本报头可以使用两个额外字节进行扩展(图中的情况总共
有三个字节)。块消息报头包含
meta-data 信息,比如消息大小(以字节为单位),Timestamp
以及消息类型。最后一个值是一个单独的字节,它定义了这个包是一个音频包,或者视频包,或者命令以及 "低层次" RTMP 包比如一个 RTMP
Ping。
以下示例,一个 flash 客户端执行以下代码时:
[javascript] view plaincopyprint?
- var stream:NetStream = new NetStream(connectionObject);
这将生成以下块:
ASCII | |
03 00 0b 68 00 00 19 00 00 00 00 63 72 65 61 74 65 53 74 72 65 61 6D 00 40 00 00 00 00 00 00 00 05 |
c r e a t e S t r e a m . @ . . . . . . . . |
这一包以一个字节的基本报头开始,两个有效位(b00000011) 定义了块头类型 0,其余部分(b00000011) 定义了块的流 ID 是 3。头类型会有 4 种可能的值,他们的意义分别是:
- b00 = 12 字节头(完整的头)。
- b01 = 8 字节 - 像类型 b00。不包含消息 ID(后四个字节)。
- b10 = 4 字节 - 包含有基本报头和 timestamp (3 个字节)。
- b11 = 1 字节 - 只包含有基本头。
最后一个类型 (b11) 常用于聚合信息的情况,在上面的例子中,第二个消息会以 id 为 0xC3 (b11000011)
起始,意味着所有消息报头字段要以流 ID 为 3 的消息传递(恰恰是其上的消息)。六个最低的有效位组成流 ID,取值范围是 3 到
65599。一些值具有特殊意义,比如 1 代表一个扩展的 ID 格式,这时会有两个字节跟随。值为 2 用于底层的消息,例如 Ping
和设置客户端带宽。
接下来的 RTMP 报头的字节(包含以上数据包例子中的值)详解如下:
- 字节 #1 (0x03) = 块头类型。
- 字节 #2-4 (0x000b68) = Timestamp。
- 字节 #5-7 (0x000019) = 包长度 - 在这个例子中是 0x000019 = 25 字节。
- 字节 #8 (0x14) = 消息类型 ID - 0x14 (20) 定义了一个 AMF0 编码的命令消息。
- 字节 #9-12 (0x00000000) = 消息流 ID。这个以小端排序(很奇怪)。
消息类型 ID 字节定义了当前包是否包含音频/视频数据,一个远程对象或者一个命令。一些可能的值如下:
- 0x01 = 设置包大小消息。
- 0x04 = Ping 消息。
- 0x05 = 服务器带宽。
- 0x06 = 客户端带宽。
- 0x08 = 音频包。
- 0x09 = 视频包。
- 0x11 = 一个 AMF3 类型命令。
- 0x12 = 调用 (onMetaData 信息会这样发送)。
- 0x14 = 一个 AMF0 类型的命令。
紧跟报头,0x02 表示一个大小为 0x000C 的串,0x63 0x72 ... 0x6D ("createStream" 命令)。其后是 0X00 代表 ID 为 2.0 的事务。最后一个字节是 0x05 (null) 表示没有参数。
Invoke 消息结构 (0x14, 0x11)
以上所述的几种消息类型,比如 Ping 和设置客户端/服务端带宽,被认为底层 RTMP 协议消息,它们不使用 AMF
编码格式。换句话说,命令消息,无论是 AMF0 (Message Type of 0x14) 还是 AMF3 (0x11),使用这种格式:
[plain] view plaincopyprint?
- (String) <Command Name>
- (Number) <Transaction Id>
- (Mixed) <Argument> ex. Null, String, Object: {key1:value1, key2:value2 ... }
事务 id 用于有回复的命令。这个值可以是上面例子中的一个字符串,也可以是一个或者多个对象,每个由一个键值对组成,键值对的键经常编码为字符串值,键值对的值可以是任意 AMF 数字类型,包括复杂的类型,比如数组。
Ping 消息结构 (0x04)
Ping 消息不是 AMF 编码。它们以一个流 Id 起始,带有一个完整 (类型 0) 报头,并有一个类型为 0x04 的消息。报头后面紧随六个字节:
- #0-1 - Ping 类型。
- #2-3 - 第二个参数 (对于特定的 Ping 类型有意义)。
- #4-5 - 第三个参数 (一样)。
消息体的前两个字节定义了 Ping 的类型,有六种可能取到的值。
- 类型 0 - 清除流:当连接已建立而没有带有更多数据时发送。
- 类型 1 - 清除缓存。
- 类型 3 - 客户端缓存时间。第三个参数以毫秒为单位持有这个值。
- 类型 4 - 重置流。
- 类型 6 - 从服务器 Ping 客户端。第二个参数是当前时间。
- 类型 7 - 客户端回复的 Pong。第二个参数是客户端接收到 Ping 的时间。
Pong 是 Ping 回复的名字,它使用以上介绍的值。
ServerBw/ClientBw 消息结构 (0x05, 0x06)
这个关联到和客户端向上流、服务器向下流比特率相关的消息。消息体由 4 个字节组成,表示带宽的值,它可能有一个扩展字节来设置
Limit 类型。这个可以有三种可能的值:hard、soft 或者 dynamic (就是 soft 或者 hard 任一)。
设置块大小 (0x01)
这个值可以在报体的四个字节里接收到。存在默认值 128 字节,当你想要改变默认值时才会发生这个消息。
协议
握手
建立 TCP 连接后,RTMP
连接会在两端交互三个包的握手之后建立(这在官方文档里也被引用为块)。在官方规范中,这里描述为客户端发送包 C0-2,服务器端发送包
S0-2,不要和 RTMP 包混淆,RTMP 包只会在握手完成之后才能交互。这些包拥有自己的结构,C1 包含一个设置 "epoch"
timestamp 的字段,但是因为这个可以设置为 0,正如第三方实现过的,这个包可以被简化。客户端通过发送一个带有代表现有协议版本号常数值
0x03 的 C0 包初始化连接。它可以直接跟随 C1 而无须等待接收到
S0,它带有 1536 字节,前四个字节代表 timestamp,其他的随意 (第三方实现中可以将其设置为 0)。C2 和 S2 分别是 S1
和 C1 的回声,接收到它们之后,握手才被认为结束。
连接
这一点上,客户端和服务器会通过交互 AMF 编码的消息进行协商连接。这些包含关系到建立连接所需要的变量的键值对。一个来自客户端的消息例子如下:
[plain] view plaincopyprint?
- (Invoke) “connect”
- (Transaction ID) 1.0
- (Object1) { app: “sample”, flashVer: “MAC 10,2,153,2”, swfUrl: null,
- tcUrl: “rtmpt://127.0.0.1/sample “, fpad: false,
- capabilities: 9947.75 , audioCodecs: 3191, videoCodecs: 252,
- videoFunction: 1 , pageUrl: null, objectEncoding: 3.0 }
FMS 和其他市县使用一个 "app"
的概念来概念化为音频/视频和其他内容定义一个容器,具体实现是,在服务根目录下的一个文件夹,其下含有将要被流化的媒体文件。第一个变量含有这一
app 的名 "sample",这个是由 Wowza 服务器用于测试所提供的名字。flashVer 字符串和 Action-script 的
getversion() 方法返回的值一样。audioCodec 和 videoCodec 使用 double
编码,它们的含义可以在原始规范里找到。videoFunction
的值是 true,在这里明显是 SUPPORT_VID_CLIENT_SEEK 变量。特别有趣的是
objectEncoding,它定义了通信的其余部分是否会使用扩展的 AMF3 格式。因为当前默认版本为 3,flash 客户端必须被以
Action-script 代码显式告知去使用 AMF0。服务器会用 ServerBW 回复,一个 ClientBW 和一个
SetPacketSize 消息序列,最终跟随一个 Invoke,用一个实例消息。
[plain] view plaincopyprint?
- (Invoke) “_result”
- (transaction ID) 1.0
- (Object1) { fmsVer: "FMS/3,5,5,2004", capabilities: 31.0, mode: 1.0 }
- (Object2) { level: “status”, code: “NetConnection.Connect.Success",
- description: “Connection succeeded”,
- data: (array) { version: “3,5,5,2004” },
- clientId: 1728724019, objectEncoding: 3.0 }
上面的一些值会被连续加载到一个通用的 Action-script Object,这个对象随后被传递给 NetConnection 事件监听者。clientId 将会为这个连接开启的会话建立一个编号。Object 编码必须匹配前面设定的值。
播放视频
要开始一个视频流,客户端发送一个由一个 ping 消息跟随的 "createStream" 调用,其后再跟随一个以文件名为参数的 "play" 调用。服务器随后以一系列的 "onStatus" 命令和带有视频数据的 RTMP 消息进行回复。
连接建立以后,媒体由封装为 FLV tag 内容的 RTMP 消息对类型 8 和 类型 9 的音频和视频交叠发送。
HTTP 隧道 (RTMPT)
这一节讲解 RTMP 的 HTTP 隧道式版本。它交互在端口 80,并在 HTTP POST 请求和回复内部传递 AMF 数据。连接的时序如下:
[plain] view plaincopyprint?
- POST /fcs/ident2 HTTP/1.1
- Content-Type: application/x-fcs\r\n
- HTTP/1.0 404 Not Found
- POST /open/1 HTTP/1.1
- Content-Type: application/x-fcs\r\n
- HTTP/1.1 200 OK
- Content-Type: application/x-fcs\r\n
- 1728724019
第一个请求有一个路径 /fcs/ident2,正确的返回是 404 无法查找错误。客户端然后发送了一个 /open/1
请求,服务器以附加一个代表 session 标识的随机数的 200 ok 返回。在以上例子中,返回体中返回 1728724019。
[plain] view plaincopyprint?
- POST /idle/1728724019/0 HTTP/1.1
- HTTP/1.1 200 OK
- 0x01
从此开始,/idle/<session id>/<sequence #> 会轮询请求,session
id 是由服务器生成并返回,sequence 是一个为以 1 开始以后每次请求递增的数字。正确的回复是一个报体中带有指示内部时间的整数,200
OK。AMF 数据通过 /send/<session id>/<sequence #> 发送。
软件实现
客户端软件
最广泛采用的 RTMP 客户端软件是 Adobe Flash Player,它能够支持来自 RTMP 服务器的音视频流的回放(当它被安装为一个 web 浏览器的插件时)。
只能部分支持 RTMP 的客户端软件宝库开源的 media player XBMC,它提供了播放 RTMP (不包括 RTMPE) 流的初步支持。
开源的命令行工具 rtmpdump 用于回放或者将整个 RTMP 流 (包括 Adobe 用于加密的 RTMPE)
保存到磁盘。RTMPdump 可以运行在 Linux、Android、Solaris、MacOSX 以及大部分其他的 Unix
派生操作系统,当然也可以运行在微软 Windows。最初支持所有 32 位版本的 Windows 系统,包括 Windows 98,从版本
2.2 起,这款软件只能泡在 Windows XP 或者更高版本的 Windows 系统之上(尽管早期版本保持功能齐全)。
RTMPdump 的一个分叉,没有包含 Adobe 声称违反了美国 DMCA 的 RTMPdump 代码,以
FLVstreamer 发布了。它的开发是 2008 年 Adobe 抵制 RTMPdump 的一个直接反应。FLVstreamer
可以将来自任意一台 RTMP 服务器的音频流或者视频流保存到磁盘,只要流没有开启 RTMPE。FLVstreamer 运行于 Windows
XP 或者更高版本的 Windows 系统之上,但不支持早期版本的 Windows。
2009 年十月,在美国以外的国家,MPlayer 网站 重新启动了 RTMPdump
的研发。现有版本大大改进了功能,并且使用了 C 语言重写,大大利用了 C 的优势。尤其是,主要功能被内置到一个库 (librtmp)
中,其他程序可以很容易使用这些功能。RTMPdump 的开发者们也为其他一些开源项目 (诸如
MPlayer、FFmpeg、XBMC、cURL、VLC) 提供了 librtmp 的支持。这些项目对 librtmp 的使用,拥有了完全
RTMP 的支持。
开源的 Gnash,一个在 Linux 平台上对于 Macromedia Flash Player 的替代,拟就为 Linux 支持 RTMP streaming。
服务器端软件
一些全面执行 RTMP 的服务器有:
- Adobe FMS。
- Adobe 生命周期数据服务。
- 亚马逊 S3 和亚马逊 Cloudfront 可以使用 RTMP 流。
- haXeVideo 是一个完全由 haXe 语言开发的多流 FLV 流媒体服务器。
- RealNetworks 的 Helix Universal Server 可以支持 RTMP、RTMPT 和 RTMPS 流的直播和点播。
- Red5 Media Server 是一个 Java 开源项目,为 Adobe Flash Player 和其他客户端技术提供了一个强大的视频流和多用户解决方案。
- Erlyvideo 具有广泛的功能:不仅仅是文件流化,而且可以使用 RTMP 对 MPEG-TS 或者 Shoutcast 为 flash 客户端重新流化。
- Unreal Media Server 支持实时和缓存 RTMP 流的直播。
- Wowza Media Server。
- WebORB Integration Server (交流版和云版,为 .NET 和 Java 企业版提供了 RTMP/RTMPT/RTMPS 消息和媒体流化支持)。
- FreeSWITCH RTMP 流媒体 mod_rtmp 可用,并允许和其他 VoIP 协议 (SIP, H.323) 互连。
- FFmpeg
- Nginx with RTMP Module
- XSplit Broadcaster
探索和研发
- crtmpserver 探索者们对 RTMFP 协议进行逆向工程。现在还是个半成品。
- Blue5 - 一个意图创建开源版本的 RTMPE 和 RTMFP 的项目。
- kbmMW 为 Delphi/C++Builder 支持 RTMP 的企业版多层次开发工具。
另请参阅
- RTMPDump
- Protected Streaming Info about RTMPS and RTMPE
- Real Time Media Flow Protocol(RTMFP),基于 UDP
- Video on Demand (VoD)
参考资料
- http://help.adobe.com/en_US/flashlite/dev/4/WSa2ec538c80d45833-4e519ada123e088b6aa-8000.html
- Using RPC services in Flex Data Services 2
- http://livedocs.adobe.com/flashmediaserver/3.0/docs/help.html?content=01_overview_basics_13.html
- http://blogs.adobe.com/flashmedia/2010/02/understanding_ado
- http://trac.red5.org/wiki/Documentation/Tutorials/Ping
- "Updates:2009-11-01"
- "Linux Funding"
- erlyvideo website
外部链接
原文链接:http://en.wikipedia.org/wiki/Real_Time_Messaging_Protocol。