RTMP协议整理

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类型,表示暂停或者恢复的时间,以毫秒为单位