RTMP是直播业务使用的重要协议,整理以备查。
简介
RTMP HandShake
RTMP握手成功后,保证了两端支持RTMP协议,然后才能进行RTMP协议层面的消息交互。
RTMP Chunk
Chunk Format

Basic Header
- Basic Header的总长度可能是: 1, 2 或 3字节, 实际是哪一种取决于字段CSID的范围
- 包含2个字段:
- Chunk Type: Format, 简写: fmt。长度固定为: 2bit(比特位)
- Chunk stream ID: 简称CSID。Basic Header的总长度(1, 2或3字节)减去fmt使用的2bit即是CSID可能的长度,长度可能为: 6(8-2), 14(16-2)或22(24-2)bit
- 协议规定: 支持用户自定义[3, 65599]之间的CSID,而0, 1, 2由协议保留用作表示特殊信息
- 0: 表示Basic Header总共要占用2个字节, CSID在[64, 319]之间
- 1: 表示Basic Header总共要占用3个字节, CSID在[64, 65599]之间
- 2: 表示该Chunk是控制信息和一些命令信息
当Basic Header的长度为1字节时
如图所示: CSID长度为6bit, 除去保留的CSID,自定义范围为[3, 63]
当Basic Header的长度为2字节时
如图所示: 此时协议要求将与fmt所在字节的后6bit位都设置为0,剩下的1个字节表示CSID - 64。8bit位可表示[0, 255]共256个数,即CSID的范围为[64, 319]。
当Basic Header的长度为3字节时
如果所示: 此时协议要求将与fmt所在字节的后6比特位设置为1(即000001), 剩下的2个字节(小端存储)表示CSID-64。16bit位可表示[0, 65535], 即CSID的范围为[64, 65599]。
另外,可以观察到Basic Header的长度为2字节时的CSID的范围[64, 319]是[64, 65599]的子集。
wireshark抓包
- CSID值为2时(协议保留值)
- CSID值在区间[3, 64]时 (Basic Header的长度为1字节)
伪代码
first_byte = read_1byte()
b_27 = first_byte & 0x3f
if b_27 == 0:
basic_header_len = 2 (bytes)
else if b_27 == 1:
basic_header_len = 3 (bytes)
else if b_27 > 1:
basis_header_len = 1 (bytes)
Message Header
- Message Header的总长度可能是: 11, 7, 3 或 0 字节,实际是哪一种取决于Basic Header的fmt字段的值(2bit位,共4种可能)
当fmt = 0时, 总长度为11字节
包含如下4个字段:
- timestamp: 时间戳,占用3个字节。
- 3个字节最多表示到16777215 = 2^24 - 1, 当它的值超过这个最大值时,这3个字节全部需置为1。这样实际的时间戳会转存到Extended Timestamp字段中,接收端在判断timestamp字段这3个字节全部为1时,需要去解析Extended Timestamp字段获取时间戳。
- message length: 消息数据的长度,占用3个字节。
- 表示实际发送的数据(如: 音频帧、视频帧)等数据的长度,单位是: 字节
- message type id: 消息类型的ID,占用1个字节。
- 表示实际发送的数据的类型,比如:8代表音频数据,9代表视频数据
- message stream id: 消息流的ID,占用4个字节。
- 表示该chunk所在的流的ID,和Basic Header的CSID一样,采用小端存储方式
当fmt = 1时,总长度为7字节
与fmt = 0相比,省掉了message stream id字段(4个字节)。包含3个字段:
- timestamp delta: 占用3个字节。
- 与fmt = 0时的timestamp含义不同,存储的是和上一个chunk的时间差。同样,超过最大值后,需用到Extended Timestamp字段
- message length: 消息数据的长度,占用3个字节。
- message type id: 消息类型的ID,占用1个字节。
当fmt = 2时,总长度为3字节
与fmt = 1相比,省掉了message length字段(3字节)和message type id(1字节)。仅包含1个字段:
- timestamp delta: 占用3个字节。
当fmt = 3时,总长度为0字节
0字节。表示这个chunk的Message Header和前一个Chunk完全相同。
Extended Timestamp
- 含义:扩展时间戳
- 协议规定:
- 只有当Message Header中的timestamp (delta)字段设置为全1 (0x00ffffff)时,本字段才能存在
- 如果Message Header中的timestamp (delta)字段的值小于0x00ffffff, 则本字段一定不能出现
- 如果Message Header中的timestamp (delta)字段不存在(fmt = 3时), 则本字段也一定不能出现
Chunk Data
- 含义: 用户层面上真正想要发送的与协议无关的数据
- 长度:在(0, ChunkSize)之间,由Message Header的message length(Body size)字段指定大小
- 抓包示例:
协议控制消息(Protocol Control Message)
chunk流使用一些特殊的值表示协议的控制消息,特殊值协议规定:
- Basic Header字段CSID必须为2
- Message Header的字段message stream id必须为0(0表示控制信息)
- Message Header的字段message type id可能的值: 1~6
Set Chunk Size
- message type id = 1
- 含义:设置Chunk Data的最大长度,Message Header的字段message length不能超过该值。如果要发送的Message超过设置的Chunk Size, 则必须拆分成Chunk发送。
- Chunk Data: Chunk Size值
- wireshark抓包
Abort Message
- message type id = 2
- 含义: 当一个Message被拆分为多个Chunk,接收端只接收到了部分Chunk时:
- 如果发送该控制消息给发送端,可以告知发送端不再传输该Message的Chunk
- 而接收端如果收到该控制消息,则需要丢弃这些不完整的Chunk
- Chunk Data: CSID, 表示丢弃该CSID的所有已接收到的Chunk
Acknowledgement
- message type id = 3
- 含义: 当收到对端的消息大小等于窗口大小时,接收端需发送该控制消息给对端,告诉对端可以继续发送数据
- 窗口大小是在没有接收端对端的ACK确认消息之前能发送的字节数的最大值
- Chunk Data: 序列号Sequence Number, 表明到当前时间为止已经接收到的字节数(4byte大小)
User Control Message
- message type id = 4
- 含义:用户控制消息,客户端或服务端发送本消息通知对方用户的控制消息
- Chunk Data: 本消息承载事件类型和事件数据
- 头两个字节用于标识事件类型 (2byte)
- 事件类型之后是事件数据, 长度是可变的
Window Acknowledgement Size
- message type id = 5
- 含义: 发送端在收到接收端的两个Acknowledgement消息间最多可以发送的字节数
- wireshark抓包
Set Peer Bandwidth
- message type id = 6
- 含义: 发送本消息更新对端的输出带宽
- Chunk Data:
- aws: Acknowledgement Windows Size (4byte)
- Limit type: 0-硬限制 1-软限制 2-动态限制
- wireshark抓包
数据消息
Audio Message
- message type id = 8
- 含义: 音频数据
Video Message
- message type id = 9
- 含义: 视频数据
Metadata Message
- message type id = 18
- 含义: 音视频编码、视频宽高等元数据信息
命令消息(Command Message)
协议规定message type id= 17 或 20 表示命令消息。
- 命令消息由3部分组成:命令的名字(如: connect)、TransactionID表示命令的标识、Object表示相关参数。使用AMF编码。
- 接收端收到命令消息后,返回如下3种消息之一:
- _result消息: 表示接受命令,对端可以继续往下执行流程
- _error消息: 表示拒绝该命令要执行的操作
- method name消息: 方法名,例如: verifyClient或contactExternalServer。
NetConnection Commands(连接层的命令)
connect
- 含义: 用于客户端向服务器发起连接请求
- Chunk Data字段:
- Command Name: 命令名字(String), 如: "connect"
- Transaction ID: 事务ID(Number), 固定值为1
- Command Object: 键值对集合表示的命令参数(Object)
- Optional User Arguments: 自定义的额外参数(Object)
- wireshark抓包
Call
- 含义: 用于在对端执行某函数,即RPC
- Chunk Data字段:
- Procedure Name: 要调用的远程方法(String)
- Transaction ID: 事务ID(Number)。如果需对端反馈响应,则该值设置为非0,否则设置为0
- Command Object: 键值对集合表示的命令参数(Object)
- Optional User Arguments: 自定义的额外参数(Object)
- 如果发送Call命令端的Transaction ID非0,则接收命令端需作出响应,响应的字段如下:
- Command Name: 命令名字(String)
- Transaction ID: 接收到的消息的Transaction ID
- Command Object: 键值对集合表示的命令参数(Object)
- Response: 命令已成功执行(Object)
createStream
- 含义: 创建传递具体消息的stream通道,从而可以在这路流中传递具体信息
- Chunk Data字段:
- Command Name: 命令名字(String), 如: "createStream"
- Transaction ID: 事务ID(Number)
- Command Object: 键值对集合表示的命令参数(Object)
- wireshark抓包
NetStream Commands(流连接上的命令)
NetStream建立在NetConnection之上。一个TCP连接上只能创建一个NetConnection,但一个NetConnection之上可以创建多个NetStream来建立不同的流通道传输数据。
服务端收到NetStream Commands后,会通过onStatus的命令来响应客户端,描述当前NetStream状态。
onStatus命令的字段结构如下:
- Command Name: 命令名字(String), 为"onStatus"
- Transaction ID: 事务ID(Number), 固定值为0
- Command Object: Null类型
- InfoObject: 状态信息(Object),至少包含3个属性
- level: String类型,可能值: "warning"、"status"、"error"
- code: String类型,代表具体的状态, 比如: "NetStream.Play.Start"表示开始播流
- description: String类型,状态的文字描述
- wireshark抓包示例
play
- 含义: 客户端请求服务器接收数据(如果传输的信息时视频的话,则请求开始播流)
- Chunk Data字段如下:
- Command Name: 命令名字(String), 为"play"
- Transaction ID: 事务ID(Number), 固定值为0
- Command Object: Null类型
- Stream Name: 流名字(String)
- Start: 开始位置(Number, 可选参数), 表示从何时开始播流,以秒为单位
- -2: 表示播放直播流,如果直播流不存在,则播放录播文件,如果录播文件也没有,则等待流开播 (默认值)
- -1: 表示只播放直播流,忽略录播文件
-
=0: 表示从该流的该时间点开始播放
- Duration:时长(Number, 可选参数),以秒为单位
- -1: 表示直播或录播停止后才能回退播放(默认值)
- 0: 表示从当前帧开始播放
- reset: Boolean类型,可选参数
- true: 表示清除之前的流,重新开始一路播放
- false: 表示保留原来的流,向本地的播放列表中再添加一条播放流
play2
- 含义: 和play不同的是, 该命令可以将当前正在播放的流切换到同样数据但不同码率的流上,服务器会维护多种码率的文件来供客户端使用play2命令来切换
- Chunk Data字段如下:
- Command Name: 命令名字(String), 为"play2"
- Transaction ID: 事务ID(Number), 固定值为2
- Start Time:
- oldStreamName:
- Stream Name:
- Duration:
- Transition:
deleteStream
- 含义: 当NetStream对象销毁的时候发送删除流命令
- Chunk Data字段如下:
- Command Name: 命令名字(String), 为"deleteStream"
- Transaction ID: 事务ID(Number), 固定值为0
- Command Object: Null类型
- Stream ID: Number类型,要销毁的流的ID
- 服务端不返回任何响应
receiveAudio
- 含义: 告知服务端是否要发送音频
- Chunk Data字段如下:
- Command Name: 命令名字(String), 为"receiveAudio"
- Transaction ID: 事务ID(Number), 固定值为0
- Command Object: Null类型
- Bool Flag: Boolean类型, 表示是否接收音频流
- 服务端不返回任何响应
receiveVideo
- 含义: 告知服务端是否要发送视频
- Chunk Data字段如下:
- Command Name: 命令名字(String), 为"receiveAudio"
- Transaction ID: 事务ID(Number), 固定值为0
- Command Object: Null类型
- Bool Flag: Boolean类型, 表示是否接收视频流
- 服务端不返回任何响应
publish
- 含义: 客户端向服务器发起请求推流到服务器
- Chunk Data字段如下:
- Command Name: 命令名字(String), 为"publish"
- Transaction ID: 事务ID(Number), 固定值为0
- Command Object: Null类型
- Publishing Name: 流名字(String)
- Publishing Type:推流类型(String)
- "live": 表示推流文件无需在服务器端存储
- "record": 将推流数据保存在服务器端覆盖写
- "append": 将推流数据保存在服务器端,追加写
- wireshark抓包示例
seek
- 含义: 定位到视频或音频的某个位置,以毫秒为单位
- Chunk Data字段如下:
- Command Name: 命令名字(String), 为"seek"
- Transaction ID: 事务ID(Number), 固定值为0
- Command Object: Null类型
- milliSeconds: Number类型,表示定位到该文件的xx毫秒处
pause
- 含义: 客户端告知服务端停止或恢复播放
- Chunk Data字段如下:
- Command Name: 命令名字(String), 为"pause"
- Transaction ID: 事务ID(Number), 固定值为0
- Command Object: Null类型
- Pause/Unpause Flag: Boolean类型。true表示暂停,false表示恢复
- milliSeconds: Number类型,表示暂停或者恢复的时间,以毫秒为单位