一、简述
RTMP(Real Time Messaging Protocol)即实时消息传输协议。该协议基于TCP,是Adobe Systems公司为Flash播放器和服务器之间音频、视频和数据传输开发的开放协议。用来解决多媒体数据传输流的多路复用和分包的问题。
- 1.RTMP工作在TCP之上,默认使用端口1935;
- 2.RTMPE在RTMP的基础上增加了加密功能;
- 3.RTMPT封装在HTTP请求之上,可穿透防火墙;
- 4.RTMPS类似RTMPT,增加了TLS/SSL的安全功能;
播放一个RTMP协议的流媒体需要经过以下几个步骤:握手,建立连接,建立流,播放。
需要了解专用词:
消息(Message):基本的数据单元称为消息
消息块(Chunk):传输数据的时候,消息会被拆分成更小的单元,称为消息块
1.1 握手
客户端要向服务器发送C0,C1,C2(按序)三个chunk,服务器向客户端发送S0,S1,S2(按序)三个chunk,然后才能进行有效的信息传输。
需要注意:
1.客户端要等收到S1之后才能发送C2
2.客户端要等收到S2之后才能发送其他信息(控制信息和真实音视频等数据)
3.服务端要等到收到C0之后发送S1
4.服务端必须等到收到C1之后才能发送S2
5.服务端必须等到收到C2之后才能发送其他信息(控制信息和真实音视频等数据)
但是为了让六次通信过程效率提高,就可能出现下面的方式
1.2 建立网络连接
- 1.客户端发送连接命令到服务器,请求与服务应用实例建立连接
- 2.服务器接收到连接命令后,发送消息给客户端用于确认窗口大小,同时连接到发送连接命令的应用程序
- 3.服务器发送设置带宽消息到客户端
- 4.客户端处理设置带宽消息后,发送确认窗口大小消息到服务器端
- 5.服务器发送用户控制消息中的Stream Begin消息到客户端
- 6.服务器发送命令消息中的result,通知客户端连接的状态
1.3 建立网络流
- 1.客户端发送命令消息createStream命令到服务器端
- 2.服务器接收到消息后,发送命令消息中的_result,通知客户端流的状态
1.4 播放
- 1.客户端发送命令消息中play命令到服务器端
- 2.接收到play命令后,服务器端发送设置块大小(ChunkSize)的协议消息
- 3.服务器发送用户控制消息streambegin,告知客户端流ID
- 4.play命令成功后,服务器发送命令消息中NetStream.Play.Start & NetStream.Play.reset,告知客户端play命令执行成功
- 5.此后,服务器发送客户端要播放的音频数据和视频数据
二、基本概念
2.1 消息 Message
基本的数据单元称为消息
- MessageType:消息类型(Message Type ID在1-7的消息用于协议控制;Message Type ID为8,9的消息分别用于传输音频和视频数据;15-20的消息用于发送AMF编码的命令,负责用户与服务器之间的交互)
- PayloadLength:消息体长度
- Time Stamp:标识时间戳
- Stream ID:标识消息所属媒体流,分成Chunk和还原Chunk为Message的时候都是根据这个ID来辨识是否是同一个消息的Chunk的
2.2 消息块 Chunk
网络收发过程中并不会Message为单位发送,jk在网络传输数据时需要将消息拆分成较小的数据块,才适合在相应网络环境下传输。拆分为消息块(Chunk),可以避免优先级低的消息持续发送阻塞优先级高的数据,并且可以提高数据压缩。
- Chunk Basic Header:标识本块
- Chunk Message Header:标识本快负载所属消息
- Extended TimeStamp:当时间戳溢出时需要,扩展用
Chunk的默认大小是128字节,在传输过程中,通过一个叫做Set Chunk Size的控制信息可以设置Chunk数据量的最大值,在发送端和接收端会各自维护一个Chunk Size,可以分别设置这个值来改变自己这一方发送的Chunk的最大大小。大一点可以减少UPC占用率,会占用更多时间在发送上,低带宽下会占用大量传输时间,小一点虽然可以减少阻塞,但是这样会带来额外的信息,并且传输多次也会无法利用高带宽优势。因此他并不适合在高比特率的数据流传输。
2.2.1 Chunk Basic Header(基本的头信息)
包含了 Chunk Stream ID(流通道ID-CSID)和 Chunk Type(Chunk类型-fmt)。
前者用来标识一个特定流通道;后者则是类型。
该信息(基本头信息)长度可能为1、2、3个字节,ChunkType长度固定占2bit。
因此决定头信息长度就是CSID,RTMP协议支持用户自定义。0、1、2由协议保留特殊信息(0表示总共要占用2个字节)
1Byte
0 1 2 3 4 5 6 7
+-+-+-+-+-+-+-+-+
|fmt| cs id |
+-+-+-+-+-+-+-+-+可以看到fmt占用2位,之后6位都是CSID内容,取值范围在[0, 63]。其中用户自定义范围在[3, 63]。
2Byte
0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|fmt| 0 | cs id - 64 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+两字节时,CSID占14位,该协议将使fmt所在的字节中的其他位都置位0。剩下一个字节用来表示CSID,取值范围在[0, 255]。其中由于有6位无法读取,因此需要整体加上64,即[64, 319]。
3Byte
0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|fmt| 1 | cs id - 64 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+三字节时,CSID占用22位,类似的,该协议将使fmt所在的字节中的其他位都置为1,剩下的2个字节来表示CSID,取值范围在[0, 65535]。理由同上,需要整体加上64,即[64, 65599]。
2.2.2 Chunk Message Header(消息的头信息)
包含了要发送实际信息的描述信息。该信息的格式和长度取决于 Basic Header 中的 Chunk Type,共有4中格式。其中第一种格式可以表示其他三种表示的所有数据,但由于其他三种格式是基于对之前的Chunk的差量化的表示,因此可以数据量更小。
Chunk Type = 0
0 1 2 3
0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| timestamp |message length |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| message length (coutinue) |message type id| msg stream id |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| msg stream id |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+占用11个字节,其他三种能表示的数据都可以表示。但是在ChunkStream开始的第一个Chunk和头信息中的时间戳后退(回退播放)的适合必须采用这种格式。
timestamp:时间戳,占用3个字节,当他超过最大值时,三个字节都置位1。这样实际的timestamp会转存到Extended Timestamp字段中,接受端会根据这个情况到Extended TimeStamp中解析实际的时间戳。
msg length:消息数据的长度,占用3个字节,表示实际发送消息的数据(音频、视频)长度。这里是指Chunk的总长度,而不是Chunk Data的长度。
msg type id:消息类型,占用1个字节,表示实际发送消息的类型,8表示音频,9表示视频。
msg stream id:流媒体ID,占用4个字节,表示该Chunk所在的流的ID,与Basic Hander中的CSID一样。Chunk Type = 1
0 1 2 3
0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| timestamp delta |message length |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| message length (coutinue) |message type id|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+占用7个字节,省略的Message Stream Id的4个字节。表示该Chunk与上一次发的Chunk所在的流相同。
timestamp delta:占用3个字节,和type=0时不同,存储的是和上一次Chunk的时间差。相同的在满存储后依旧会转存到Extended Timestamp字段汇总。
其他:参照上面的描述Chunk Type = 2
0 1 2
0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| timestamp delta |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+占用3个字节,相对与type=1时,省去了消息长度和消息类型,表示与上一次发送的Chunk都相同。
Chunk Type = 3
为0字节,表示这个Chunk的MessageHeader与上一次发送的完全相同。时间上面则是当type=0时表示同一个时间戳,type=1或者2时表示同一个时间戳的差,但是比较的时候是正常比较。
2.2.3 Extended Timestamp(扩展时间戳)
这里应该就清除了为什么是在溢出时用到的时间戳信息。当Message Header中的时间戳信息全部置位1时才会在这里启用,表示时间戳信息已溢出,请到扩展时间戳内获取完整时间戳。
2.2.4 Chunk Data
数据块
2.3 消息分块
在消息被分割成多个消息块的过程中,默认每个固定块大小是128字节(最后一个数据库可小于该固定长度)。
在传输过程中,通过一个叫做Set Chunk Size的制信息可以设置Chunk数据量的最大值,在发送端和接受端会各自维护一个Chunk Size,可以分别设置这个值来改变自己这一方发送的Chunk的最大大小。大一点可以减少UPC占用率,会占用更多时间在发送还是那个,低带宽下会占用大量传输时间,小一点虽然可以减少阻塞,但是这样会带来额外的信息,并且传输多次也会无法利用高带宽优势。
传输过程中,发送端首先将媒体数据封装成消息,然后将消息分割成消息块,最后将分割的消息通过TCP协议发送出去。接收端通过TCP协议收到数据后,首先将消息块组合起成消息,然后对消息进行解封装就可以恢复媒体数据。