# 前端需要了解的RTMP知识
# 概述
RTMP是Real Time Messaging Protocol的简称,是基于何时的传输层协议上(如tcp协议)的应用层级别的协议,其设计是为了组包多媒体传输流(如视频,音频以及交互内容)
# 介绍
RTMP协议是由Adobe公司提出的一种应用层的协议,上面引述的是RTMP官方文档的introduction部分,其目的是为了给诸如视频、音频以及数据消息通信双方提供实时的信息双向流。RTMP在实现上对不同类型的消息实施不同的权重,这样可以实现对当传输量受限时的底层流传输消息进行排队。
# 定义
载荷(Payload):包含在包中的数据
包(Packet):数据包由固定头和载荷数据组成,一些底层协议可能需要对被定义包进行封装
端口(Port):TCP/IP端口,由主机号来区分多地址
传输地址(Transport address):网络地址和端口的组合
消息流(Message stream):消息流通信的逻辑频道
消息流ID(Message stream ID):每个消息都有一个预期消息流匹配的id
块(Chunk):消息片段。消息在被网络传输前会被分成小的部分,所有的消息会通过多路由时间序端对端传输
块流(Chunk stream):通信的逻辑频道允许块以某一特定方向传送,块流可以从客户端传到服务端,也可以从服务端传回客户端
块流ID(Chunk stream ID):每个块流都有一个与之匹配的id
多路复用(Multiplexing):分离的音视频数据可以连续播放的过程,使得音视频可以同时传送
多路分用(DeMultiplexing):多路复用的逆过程,可以将交错的音视频数据聚集成原始的音视频数据
远程过程调用(Remote Procedure Call):允许客户端或服务端可以调用另一端服务端或客户端子程序或子进程的请求
元数据(Metadata):数据的描述。电影的元数据包括电影标题,时长,上映日期等等。
应用实例(Application Instance):客户端可以连接并发送连接请求的服务端实例
动作消息格式(Action Message Format):用来序列化AS(ActionScript 动作脚本)实例对象(object graphs)的压缩的二进制格式,有AMF0和AMF3两种版本
# 字节序 & 对齐 & 时间格式
所有的整字段以字节序来传输,并且是大端排列,不做特殊说明均为十进制。时间戳以毫秒记,从0开始,32位长;时间间隔用毫秒的无符号整数表示,可能是24或32位长度
# RTMP块流
尽管RTMP是被设计用来实时传输消息的,但它也可以用来处理任何消息。RTMP非常适合用于大量的音视频应用,虽然并不会提供任何优化,但却被用于更高级别的协议来进行优化,例如直播视频服务器可能会选择在客户端网速较慢的情况下丢弃视频消息而保证音频消息的实时到达。
# 消息格式
消息格式可被分成许多块来提供多路复用,这些消息需要包括含以下字段:
字段名 | 含义 | 大小 |
---|---|---|
时间戳(Timestamp) | 消息的时间戳 | 4字节 |
长度(Length) | 消息载荷的长度,如果消息头不能被省略,需要包含长度 | 3字节 |
类型ID(Type ID) | 类型id被用于消息控制 | 1字节 |
消息流ID(Message Stream ID) | 任意数值,小端存储 | 4字节 |
# 握手
RTMP握手不同于其他协议,它包含3个固定大小的块而不是可变大小的块
# 握手次序
开始时,客户端发送C0和C1
客户端必须在收到S1才能发送C2,收到S2才能发送任何其他数据
服务端必须等到收到C0才能发送S0和S1,并且最好能在收到C1后。服务端等到收到C1才能发送S2,服务端必须等到收到C2才能发送其他数据
# C0 & S0 格式
C0和S0是一个8位组,被认为是一个8位整数段。对C0而言,这个字段被认为是客户端RTMP请求的版本;对S0而言,这个字段被认为是服务端选择的RTMP版本。默认是3,0-2被废弃,4-31被用于未来的版本,32-255没有被允许
0 1 2 3 4 5 6 7
+-+-+-+-+-+-+-+-+
| version |
+-+-+-+-+-+-+-+-+
# C1 & S1 格式
C1和S1是一个1536位的长度组,包含如下字段:
字段名 | 含义 | 大小 |
---|---|---|
时间(Time) | 包含时间戳,可能是0也可能是任意的数值,用于时间标识 | 4字节 |
零(Zero) | 必须都是0 | 4字节 |
任意数据(Random data) | 任意数值,并不需要加密 | 1528字节 |
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| time (4 bytes) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| zero (4 bytes) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| random bytes |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| random bytes |
| (cont) |
| .... |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
# C2 & S2 格式
C2和S2是一个1536位的长度组,对应S1和C1依次的回复,包含如下字段:
字段名 | 含义 | 大小 |
---|---|---|
时间1(Time1) | 时间戳,S1(C2发送)或C1(S2发送) | 4字节 |
时间2(Time2) | 时间戳,之前S1或C1 | 4字节 |
任意数据(Random data) | 任意数值,可快速验证连接带宽或延时 | 1528字节 |
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| time (4 bytes) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| time2 (4 bytes) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| random bytes |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| random bytes |
| (cont) |
| .... |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
# 握手简图
未初始化阶段(Uninitialized):协议版本在这个阶段发送,客户端和服务端都是未初始化的。客户端在C0包中发送协议版本,如果服务端支持这个协议版本,其将会对应发送S0和S1;如果服务端不支持,服务端会采取合适的动作。在RTMP中,这个动作是中止连接。
版本发送阶段(Version Sent):在未初始化阶段之后客户端和服务端都处于版本发送阶段。客户端处于等待S1包,服务端处于等待C1包。在收到相应的等待的包后,客户端发送C2而服务端会发送S2,然后进入确认发送阶段。
确认发送阶段(Ack Sent):客户端和服务端响应的等待S2和C2
握手结束阶段(Handshake Done):客户端和服务端交换消息
+---------------+ +---------------+
| Client | TCP/IP Network | Server |
+---------------+ | +---------------+
| | |
Uninitialized | Uninitialized
| C0 | |
| -------------------> | C0 |
| | -------------------> |
| C1 | |
| -------------------> | S0 |
| | <------------------- |
| | S1 |
Version sent | <------------------- |
| S0 | |
| <------------------- | |
| S1 | |
| <------------------- | Version sent
| | C1 |
| | -------------------> |
| C2 | |
| -------------------> | S2 |
| | <------------------- |
Ack sent | Ack sent
| S2 | |
| <------------------- | |
| | C2 |
| | -------------------> |
Handshake Done | Handshake Done
| | |
# 分块
握手之后,连接便可以进行多路传输块流,每个块携带着一种信息流的一种类型,每块流都有一个独一无二的id即块流id,通过网络传输,每个块必须满载,在接收端会根据id进行重新组装。分块允许通过更高阶的协议进行小信息的分拆,比如可以通过阻止大的低优先级消息(如视频)而阻塞小的高优先级消息(如音频或控制)。分块同样也允许更少的间接消耗将小的消息进行发送,比如块头包含有代表内容的压缩信息,也可能反过来包含在信息之中。块的大小是可配置的,大的块可减少CPU使用,但是却可能由于较多的写操作而带来其他内容低带宽的延迟,小的块不适合高比特流的传输,块的大小在每个方向上独立保持。
# 块格式
每个块包含头和数据,头包含三部分:
字段名 | 含义 | 大小 |
---|---|---|
基础头(Basic Header) | 块流id和块类型 | 1或3字节 |
消息头(Message Header) | 消息的主要内容 | 0,3,7或11字节 |
扩展时间戳(Extended Timestamp) | 某种情境下的时间戳 | 0或4字节 |
块数据(Chunk Data) | 块的载荷 | 可变字节 |
+---------------+----------------+--------------------+----------------+
| Basic Header | Message Header | Extended Timestamp | Chunk Data |
+---------------+----------------+--------------------+----------------+
| |
| <------------------ Chunk Header -----------------> |
Chunk Format
1.块基本头
有三种类型:
字段名 | 含义 | 大小 |
---|---|---|
块流id(cs id) | 数值2~63 | 6位 |
格式(fmt) | 格式标识 | 2位 |
块流id-64(cs id - 64) | 数值减去64 | 8或16位 |
块基础头1:
0 1 2 3 4 5 6 7
+-+-+-+-+-+-+-+-+
|fmt| cs id |
+-+-+-+-+-+-+-+-+
块基础头2:
0 1
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|fmt| 0 | cs id - 64 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
块基础头3:
0 1 2
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|fmt| 1 | cs id - 64 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2.块消息头
有四种类型:
Type 0:11字节
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| timestamp | message length|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| message length (cont) |message type id| msg stream id |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| message stream id (cont) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Chunk Message Header - Type 0
Type 1:7字节
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| timestamp delta | message length|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| message length (cont) |message type id|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Chunk Message Header - Type 1
Type 2:3字节
0 1 2
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| timestamp delta |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Chunk Message Header - Type 2
Type 3:没有头
公共头字段:
字段名 | 含义 | 大小 |
---|---|---|
事件戳间隔(timestamp delta) | 类型1或类型2 | 3字节 |
消息长度(message length) | 类型0或类型1 | 3字节 |
消息类型id(message type id) | 类型0或类型1 | 1字节 |
消息流id(message stream id) | 类型0 | 4字节 |
3.扩展时间戳
当时间戳大于 16777215(0xFFFFFF)时使用
# 协议控制消息
消息类型ID为1,2,3,5,6是协议控制消息,这些内容需要块流协议做基础
# 设置块大小
用于提示设置最大字节,最大128字节,服务端和客户端可以修改这个值,并升级另一端的大小
# 中止消息
用于提示等待完成消息,之后丢弃部分接收的消息
# 识别
当接收字节和视窗大小相同时,客户端或服务端必须发送给对应端确认消息
# 视窗识别大小
当进行确认时,客户端或服务端发送给对应端视窗大小
# 设置端带宽
客户端和服务端向对应端发送输出限制
# RTMP消息格式
RTMP可用各种传输层协议
# RTMP消息
消息可以包含音频,视频,数据以及其他消息格式
1.消息头
包含消息类型,长度,时间戳,消息流id
2.消息载荷
实际消息承载内容
# 用户控制消息
消息类型为4
# RTMP命令消息
客户端和服务端的消息相互操作
# 消息类型
1.命令消息
AMF0格式值为20 AMF3格式值为17
2.数据消息
AMF0格式值为18 AMF3格式值为15
3.共享对象消息
AMF0格式值为19 AMF3格式值为16
4.音频消息
值为8
5.视频消息
值为9
6.聚合消息
值为22
7.用户控制消息事件
# 命令类型
1.网络连接命令
连接 调用 创建流
2.网络流命令
播放 播放2 删除流 接收音频 接收视频 发布 寻址 暂停