1. 简介html
RTMP是Real Time Messaging Protocol(实时消息传输协议),此协议基于TCP协议,是一个协议族,包括RTMP基本协议及RTMPT/RTMPS/RTMPE等多种变种。安全
在RTMP协议中信令和媒体数据都称之为Message,在网络中传输这些Message,为了区分它们确定是要加一个Message head的,因此RTMP协议也有一个Message head;由于RTMP协议是基于TCP的,因为TCP的包长度是有限制的(通常来讲不超过1500个字节),而RTMP的Message长度提升哟可能很大,像一个视频帧的包可能会有几十甚至几千K,这个问题就必然有一个分片的问题,在RTMP协议中对应的说法就是chunk,每个Message + head都是由一个和多个chunk组成的。服务器
2. RTMP协议就像一个用来装数据包的容器,这些数据既能够是AMF格式的数据,也能够是FLV中的视/音频数据。.net
网络链接(Connection)
一个Actionscript链接并播放一个流的简单代码:
var videoInstance:Video = your_video_instance;
var nc:NetConnection = new NetConnection();
var connected:Boolean = nc.connect("rtmp:/localhost/myapp");
var ns:NetStream = new NetStream(nc);
videoInstance.attachVideo(ns);
ns.play("flvName");
默认端口为1935
3. 握手请求及应答
Client → Server :向服务器发出握手请求.这不属于协议包一部分,该握手请求第一个字节为(0×03),其后跟着1536个字节.尽管看上去这部分的内容对于RTMP协议来讲并非相当重要的,但也不可随意对待.
Server → Client :服务器向客户端回应握手请求.这部分的数据仍然不属于RTMP协议的部分.该回应的起始字节仍然为(0x03),可是后边跟着两个长度为1536个字节(一共为3072字节 )的包块.第一个1536块看上去彷佛能够是任意内容,甚至好像能够是Null都没有关系.第二个1536的代码块,是上一步客户端向服务器端发送的握手请求的内容.
Client→Server:把上一步服务器向客户端回应的第二块1536个字节的数据块。
至此客户端与服务器端的握手结束,下面将发送RTMP协议的包内容.
Client → Server :向服务器发送链接包.
Server → Client :服务器回应.
。。。。。。。
其实RTMP包结构就是使用了AMF格式.
下面是一个关于客户端向服务器端发送流的流程:
Client→Server :发送一个建立流的请求.
Server→Client :返回一个表示流的索引号.
Client→Server :开始发送.
Client→Server :发送视音频数据包(这些包在同一个频道(channel)并用流的索引号来惟一标识).
3.1 RTMP协议封包
RTMP协议封包 由一个包头和一个包体组成,包头能够是4种长度的任意一种:12, 8, 4, 1 byte(s)。完整的RTMP包头应该是12bytes,包含了时间戳,AMFSize,AMFType,StreamID信息,8字节的包头只纪录了时间戳,AMFSize,AMFType,其余字节的包头纪录信息依次类推 。包体最大长度默认为128字节,经过chunkSize可改变包体最大长度,一般当一段AFM数据超过128字节后,超过128的部分就放到了其余的RTMP封包中,包头为一个字节.
完整的12字节RTMP包头每一个字节的含义:
用途 |
大小(Byte) |
含义 |
Head_Type |
1 |
包头 |
TIMER |
3 |
时间戳 |
AMFSize |
3 |
数据大小 |
AMFType |
1 |
数据类型 |
StreamID |
4 |
1流ID |
(1)第一个字节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
ChannelID |
Use |
02 |
Ping 和ByteRead通道 |
03 |
Invoke通道 咱们的connect() publish()和自字写的NetConnection.Call() 数据都是在这个通道的 |
04 |
Audio和Vidio通道 |
05 06 07 |
服务器保留,经观察FMS2用这些Channel也用来发送音频或视频数据 |
例如在rtmp包里面常常看到的0xC2, 就表示一字节的包头,channel=2.
(2)TIMMER
TiMMER占3个字节纪录的是时间戳,音视频流的时间戳是统一排的。可分为绝对时间戳和相对时间戳。
fms对于同一个流,发布的时间戳接受的时间戳是有区别的
publish时间戳,采用相对时间戳,时间戳值等于当前媒体包的绝对时间戳与上个媒体包的绝对时间戳之间的差距,也就是说音视频时间戳在一个时间轴上面.单位毫秒。
play时间戳,相对时间戳,时间戳值等于当前媒体包的绝对时间戳与上个同类型媒体包的绝对时间戳之间的差距, 也就是说音视频时间戳分别为单独的时间轴,单位毫秒。
flv格式文件时间戳,绝对时间戳,时间戳长度3个字节。超过0xFFFFFF后时间戳值等于TimeStamp & 0xFFFFFF。
flv格式文件影片总时间长度保存在onMetaData的duration属性里面,长度为8个字节,是一个翻转的double类型。
(3)AMFSize
AMFSize占三个字节,这个长度是AMF长度,可超过RTMP包的最大长度128字节。若是超过了128字节,那么由多个后续RTMP封包组合,每一个后续RTMP封包的头只占一个字节。通常就是以0xC?开头。
(4)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 |
(5)StreamID
StreamID是音视频流的ID,若是AMFType!=0x08 或!=0x09那么 StreamID为0。
ChannelID 和StreamID之间的计算公式:StreamID=(ChannelID-4)/5+1
例如当ChannelID为二、三、4时StreamID都为1 当ChannelID为9的时候StreamID为2
参考:http://blog.sina.com.cn/s/blog_4ae178ba0100wis6.html
rtmp_specification_1.0.pdf: