实时消息传输协议 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 消息进行工作。例子:
- 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 客户端执行以下代码时:
- 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),使用这种格式:(有问题?。。。)
- (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 编码的消息进行协商连接。这些包含关系到建立连接所需要的变量的键值对。一个来自客户端的消息例子如下:
- (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,用一个实例消息。
- (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 数据。连接的时序如下:
- POST /fcs/ident2 HTTP/1.1
- Content-Type: application/x-fcs
- HTTP/1.0 404 Not Found
- POST /open/1 HTTP/1.1
- Content-Type: application/x-fcs
- HTTP/1.1 200 OK
- Content-Type: application/x-fcs
- 1728724019
第一个请求有一个路径 /fcs/ident2,正确的返回是 404 无法查找错误。客户端然后发送了一个 /open/1
请求,服务器以附加一个代表 session 标识的随机数的 200 ok 返回。在以上例子中,返回体中返回 1728724019。
- 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。
RTMP协议就像一个用来装数据包的容器,这些数据可以是AMF格式的数据,也可以是FLV中的视/音频数据。一个单一的连接可以通过不同的通道传输多路网络流。这些通道中的包都是按照固定大小的包传输的。
图1 RTMP交互图
http://blog.csdn.net/xiaguamanshu/article/details/5457290
完整的12字节RTMP包头每个字节的含义:
用途 | 大小(Byte) | 含义 |
Head_Type | 1 | 包头 |
TiMMER | 3 | 时间戳 |
AMFSize | 3 | 数据大小 |
AMFType | 1 | 数据类型 |
StreamID | 4 | 流ID |
一、Head_Type
第一个字节Head_Type的前两个Bit决定了包头的长度.它可以用掩码0xC0进行"与"计算:
Head_Type的前两个Bit和长度对应关系:
Bits | Header Length |
00 | 12 bytes |
01 | 8 bytes |
10 | 4 bytes |
11 | 1 byte |
Head_Type的后面6个Bit和StreamID决定了ChannelID。 StreamID和ChannelID对应关系:StreamID=(ChannelID-4)/5+1 参考red5
ChannelID | Use |
02 | Ping 和ByteRead通道 |
03 | Invoke通道 我们的connect() publish()和自字写的NetConnection.Call() 数据都是在这个通道的 |
04 | Audio和Vidio通道 |
05 06 07 | 服务器保留,经观察FMS2用这些Channel也用来发送音频或视频数据 |
例如在rtmp包里面经常看到的0xC2, 就表示一字节的包头,channel=2.
二、TiMMER
TiMMER占3个字节纪录的是时间戳,音视频流的时间戳是统一排的。可分为绝对时间戳和相对时间戳。
fms对于同一个流,发布的时间戳接受的时间戳是有区别的
publish时间戳,采用相对时间戳,时间戳值等于当前媒体包的绝对时间戳与上个媒体包的绝对时间戳之间的差距,也就是说音视频时间戳在一个时间轴上面.单位毫秒。
play时间戳,相对时间戳,时间戳值等于当前媒体包的绝对时间戳与上个同类型媒体包的绝对时间戳之间的差距, 也就是说音视频时间戳分别为单独的时间轴,单位毫秒。
flv格式文件时间戳,绝对时间戳,时间戳长度3个字节。超过0xFFFFFF后时间戳值等于TimeStamp & 0xFFFFFF。
flv格式文件影片总时间长度保存在onMetaData的duration属性里面,长度为8个字节,是一个翻转的double类型。
三、AMFSize
AMFSize占三个字节,这个长度是AMF长度,可超过RTMP包的最大长度128字节。如果超过了128字节,那么由多个后续RTMP封包组合,每个后续RTMP封包的头只占一个字节。一般就是以0xC?开头。
四、AMFType
AMFSize占三个字节,这个长度是AMF长度,可超过RTMP包的最大长度128字节。
AMFType是包的类型
0×01 | Chunk Size | changes the chunk size for packets |
0×02 | Unknown | |
0×03 | Bytes Read | send every x bytes read by both sides |
0×04 | Ping | ping is a stream control message, has subtypes |
0×05 | Server BW | the servers downstream bw |
0×06 | Client BW | the clients upstream bw |
0×07 | Unknown | |
0×08 | Audio Data | packet containing audio |
0×09 | Video Data | packet containing video data |
0x0A-0x0E | Unknown | |
0x0F | FLEX_STREAM_SEND | TYPE_FLEX_STREAM_SEND |
0x10 | FLEX_SHARED_OBJECT | TYPE_FLEX_SHARED_OBJECT |
0x11 | FLEX_MESSAGE | TYPE_FLEX_MESSAGE |
0×12 | Notify | an invoke which does not expect a reply |
0×13 | Shared Object | has subtypes |
0×14 | Invoke | like remoting call, used for stream actions too. |
0×16 | StreamData | 这是FMS3出来后新增的数据类型,这种类型数据中包含AudioData和VideoData |
五、StreamID
StreamID是音视频流的ID,如果AMFType!=0x08 或!=0x09那么 StreamID为0。
ChannelID 和StreamID之间的计算公式:StreamID=(ChannelID-4)/5+1 参考red5
例如当ChannelID为2、3、4时StreamID都为1 当ChannelID为9的时候StreamID为2
六、封包分析
例如有一个RTMP封包的数据03 00 00 00 00 01 02 14 00 00 00 00 02 00 07 63 6F 6E 6E 65 63 74 00 3F F0 00 00 00 00 00 00 08 ,,,
数据依次解析的含义
03表示12字节头,channelid=3
000000表示Timmer=0
000102表示AMFSize=18
14表示AMFType=Invoke 方法调用
00 00 00 00 表示StreamID = 0
//到此,12字节RTMP头结束
下面的是AMF数据分析,具体的AMF0数据格式请参考
http://www.cnweblog.com/fly2700/archive/2008/04/09/281432.html
02表示String
0007表示String长度7
63 6F 6E 6E 65 63 74 是String的Ascall值"connect"
00表示Double
3F F0 00 00 00 00 00 00 表示double的0.0
08表示Map数据开始
Rtmp包默认的最大长度为128字节,(或通过chunksize改变rtmp包最大长度), 当AMF数据超过128Byte的时候就可能有多个rtmp包组成,如果需要解码的rtmp包太长则被TCP协议分割成多个TCP包.那么解码的时候需要 先将包含rtmp包的tcp封包合并, 再把合并的数据解码,解码后可得到amf格式的数据,将这些AMF数据取出来就可以对AMF数据解码了.服务器和 Flash客户端之间的命令都是用AMF格式的数据在传送,例如connect() publish()等命令和服务器as脚本里面自己定义的一些方法. 常用的数据类型整理如下:
类型说明(ObjType) | 数据 | dataSize |
CORE_String | 0x02 | 2字节 (2字节的数据纪录了String的实际长度) |
CORE_Object | 0x03 | 0字节(开始嵌套0x00000009表示嵌套结束) |
NULL | 0x05 | 0字节 空字节无意义 |
CORE_NUMBER | 0x00 | 8字节 |
CORE_Map | 0x08 | 4字节(开始嵌套) |
CORE_BOOLEAN | 0x01 | 1字节 |
AMF0数据的嵌套关系如下:
Object={ObjType + ObjValue}
ObjType={CORE_BOOLEAN、CORE_NUMBER、CORE_STRING、CORE_DATE、CORE_ARRAY、CORE_MAP、CORE_OBJECT}
CORE_BOOLEAN={Value(1 Byte)}
CORE_NUMBER={Value(8 Byte)}
CORE_String={StringLen(2 Byte) + StringValue(StringLen Byte)}
CORE_DATE={value(10 Byte)}
CORE_Array={ArrayLen(4 Byte) + Object}
CORE_Map={MapNum(4 Byte) + CORE_Object}
CORE_Object={CORE_String + Object}
例如完成握手后,Flash向FMS发送的第一个RTMP数据,内容如下:
上面一段数据由2个RTMP包组成,2个RTMP包头分别用蓝色表示,第一个蓝色的是12字节的包头,后面一个蓝色的C3是一个字节的包头,绿色部分是AMF数据,红色的是AMF数据类型,整个RTMP解码过程如下
[2008-06-18 16:59:20] DecodeInvoke:
[2008-06-18 16:59:20] InvokeName:String:connect
[2008-06-18 16:59:20] InvokeID:Double:0
[2008-06-18 16:59:20] Map:MapNum:0
[2008-06-18 16:59:20] Params:{
[2008-06-18 16:59:20] Key:String:objectEncoding
[2008-06-18 16:59:20] Value:Double:0
[2008-06-18 16:59:20] Key:String:app
[2008-06-18 16:59:20] Value:String:mediaserver
[2008-06-18 16:59:20] Key:String:fpda
[2008-06-18 16:59:20] Value:Bool:0
[2008-06-18 16:59:20] Key:String:tcUrl
[2008-06-18 16:59:20] Value:String:rtmp://127.0.0.1/mediaserver
[2008-06-18 16:59:20] Key:String:audioCodecs
[2008-06-18 16:59:20] Value:Double:615
[2008-06-18 16:59:20] Key:String:videoCodecs
[2008-06-18 16:59:20] Value:Double:76
[2008-06-18 16:59:20] }End Params
[2008-06-18 16:59:20] InvokeParams:String:PUBLISHER
[2008-06-18 16:59:20] InvokeParams:String:streamRecode
详细的数据类型,参考Red5
FMS3中为了实现H.264数据的直播而增加了一个数据类型,这个类型的值为0x16,这个类型不在下表中,如果需要请参看http://www.cnweblog.com/fly2700/archive/2009/02/06/297957.html
enum AMF
{
/**
* Boolean value marker constant
*/
TYPE_BOOLEAN = 0x01,
/**
* String marker constant
*/
TYPE_STRING = 0x02,
/**
* Object marker constant
*/
TYPE_OBJECT = 0x03,
/**
* Movieclip marker constant
*/
TYPE_MOVIECLIP = 0x04 ,
/**
* Null marker constant
*/
TYPE_NULL = 0x05,
/**
* Undefined marker constant
*/
TYPE_UNDEFINED = 0x06,
/**
* Object reference marker constant
*/
TYPE_REFERENCE = 0x07,
/**
* Mixed array marker constant
*/
TYPE_MIXED_ARRAY = 0x08,
/**
* End of object marker constant
*/
TYPE_END_OF_OBJECT = 0x09,
/**
* Array marker constant
*/
TYPE_ARRAY = 0x0A,
/**
* Date marker constant
*/
TYPE_DATE = 0x0B,
/**
* Long string marker constant
*/
TYPE_LONG_STRING = 0x0C,
/**
* Unsupported type marker constant
*/
TYPE_UNSUPPORTED = 0x0D,
/**
* Recordset marker constant
*/
TYPE_RECORDSET = 0x0E,
/**
* XML marker constant
*/
TYPE_XML = 0x0F,
/**
* Class marker constant
*/
TYPE_CLASS_OBJECT = 0x10,
/**
* Object marker constant (for AMF3)
*/
TYPE_AMF3_OBJECT = 0x11,
/**
* true marker constant
*/
VALUE_TRUE = 0x01,
/**
* false marker constant
*/
VALUE_FALSE = 0x00
};
使用rtmp协议从FMS3中拉音视频数据的时候,会收到AMFType=0x16的封包,这种包在FMS2中从没有出现过.
rtmp包头的第8个字节就是AMFType,也就是数据类型。例如AMFType=0x08表示音频包,AMFType=0x04表示Ping包等等。 FMS3中为了实现H.264数据的直播而增加了一个数据类型,这个类型的值为0x16。AMFType=0x16的包中既包含了音频帧也包含了视频帧。 其中音频帧和视频帧是一种新的格式存放的,类似FLV文件存储格式,每个音视频包作为一个Tag,许多的Tag组成了这个AMFType=0x16的数据 类型,Tag的格式如下:
用途 大小(Byte) 数据含义
StreamType 1 流的种类(0x08=音频,0x09=视频)
MediaSize 3 媒体数据区域大小
TiMMER 3 绝对时间戳,单位毫秒
Reserve 4 保留,值为0
MediaData MediaSize 媒体数据,音频或视频
TagLen 4 帧的大小,值为媒体数据区域大小+参数长度(MediaSize+1+3+3+4)