RTMP消息

99 阅读11分钟

消息(Message Payload)

若一条消息(消息负载(Message Payload))长度超过 Chunk Size,消息将被拆分为多个块(Chunk),数据存储在chunk data里,接收端接收完成后,会重新组成消息。

示例:消息长度=300字节,Chunk Size=128 → 拆分为3块(128+128+44)

消息格式

消息由 消息头+消息体构成,消息体由消息头决定。

负载信息(消息)类型同前面的分块消息头(chunk message header)的中 Message Type ID 类型。

(chunk message header)
| 字段               | 字节数 | 说明                                                                 |
|--------------------|--------|----------------------------------------------------------------------|
| Timestamp          | 3      | 消息的绝对时间戳(单位:毫秒)。若 ≥ 0xFFFFFF,则固定为0xFFFFFF,需扩展为4字节(见后) |
| Message Length     | 3      | 消息的总长度(单位:字节)                                           |
| Message Type ID    | 1      | 消息类型(如 8=音频,9=视频,18=AMF命令等)                         |
| Stream ID          | 4      | 消息所属流的ID(小端序存储)                                         |

消息头

消息头共占11Byte,包含如下数据:

  • 消息类型:占1Byte,取值[1,7],专用于协议控制消息,和分块chunk message header中的 Message Type ID 类型相同,类型0和1是分块专用,3-6是RTMP消息专用,类型7用于服务器通讯。
  • 载荷数据长度:占3Byte, 表示消息中载荷数据所占字节数。
  • 时间戳:占4Byte,表示该消息的时间戳
  • 消息流ID: 占3Byte, 表示该消息所在的消息流ID.

消息体是实际传输的数据, 消息类型 定义,常见类型如下:

消息类型内容
1set chunk size 设置分块大小
2Abort Message 丢弃消息
3Acknowledgement 响应消息
4User control message 用户控制消息
5Window Acknowledgement Size 窗口响应大小
6设置对方带宽
8音频消息
9视频消息
15、18数据消息(元数据、其他用户数据)
16、19共享消息
17、20Command Message Message 命令消息
22聚合消息(将多条消息合并在一个分块传递)

比如音频消息体

Chunk Header (Type 0):
  fmt=0b00, Chunk Stream ID=3
  Timestamp=0xFFFFFF → 触发扩展
  Message Length=5000x01F4
  Message Type=0x08(音频)
  Stream ID=0x00000002
Extended Timestamp: 0x01000000 → 16777216ms
Payload: AAC音频帧(500字节)

消息类型

设置分块大小-Set Chunk Size(type 1)

消息主体大小字节数描述
Message Body4 字节大端序整数设置新的块大小(单位:字节)

有效范围

  • 块大小范围:​​1 ~ 0xFFFFFFFF()​​(即 1 ~ 4,294,967,295 字节)。
  • 实际应用中,默认128字节,最大65536字节。通常不会设置极大值(避免内存或网络问题)。

​​生效时机​​

  • 接收方在解析到该消息后,​​立即生效​​,后续分块按新大小处理。

​​双向控制​​

  • 客户端和服务器均可发送 Set Chunk Size,双方的分块大小可独立设置。
  • 例如:客户端设置 发送块大小=4096,服务器设置 发送块大小=2048
Chunk Header (Type 0):
  fmt=0b00, Chunk Stream ID=3
  Timestamp=0xFFFFFF → 触发扩展
  Message Length=40x04
  Message Type=0x01(设置分块大小)
  Stream ID=0x00000001
Extended Timestamp: 0x01000000 → 16777216ms
Payload: 4字节

丢弃消息-Abort Message(type 2)

        当一个分块流(Chunk Stream)的传输被意外中断(如推流断开、网络错误)时,接收方可能已收到该流的 部分分块数据。此时发送方需通过 Abort Message 通知接收方:

  • 丢弃未完成数据:清理残留在接收缓冲区中的不完整分块。
  • 释放资源:避免无效数据占用内存或阻塞后续传输。
  • 快速恢复:为同一 Chunk Stream ID 的新数据传输扫清障碍。
Message Body4 字节小端序整数需丢弃的分块流ID(Chunk Stream ID),对应 Chunk Header 中的 Chunk Stream ID

响应消息-Acknowledgement (type 3)

        用于 带宽控制流量管理 的关键机制,接收方通过此消息通知发送方已成功接收的字节数,帮助发送方动态调整传输速率以避免网络拥塞。

  • 流量控制:接收方周期性反馈已接收的数据量,发送方根据此信息调整发送速率。
  • 避免拥塞:防止发送方速率超过接收方处理能力或网络带宽上限。
  • 可靠性增强:在弱网环境下辅助检测丢包或延迟。
Message Body4 字节大端序整数已接收字节数

窗口响应大小-Window Acknowledgement Size (type 5)

        窗口响应大小是流量控制的关键机制,用于管理数据传输速率和防止接收方被数据淹没。双方建立连接后服务端发送 窗口响应大小 告知接收端窗口响应大小,客户端达接收到此大小的数据后,必须响应消息(类型3)。

比如:服务器希望向客户端发送1024Byte数据后,客户端需要返回Acknowledgement确认消息,以达到流量控制的目的:

  • 窗口大小:接收方在需要确认前可以接收的最大字节数
  • 确认机制:接收方定期发送确认包(上面的类型3 Acknowledgement)
  • 流量控制:防止发送方过快地发送数据
Message Body4 字节大端序整数窗口响应大小

设置对方宽带-Set Peer Bandwidth (type 6)

如果接收端的窗口响应需要和发送端(服务器)不同时,则接收端需要反过来设置发送端的宽带,占用5Byte。

字段大小(字节)描述
Window Size4窗口大小(字节)
Limit Type1限制类型

限制类型可取值0,1或2,含义如下:

  • 0:硬限制,消息接收端必须以规定带宽发送数据
  • 1:软限制,带宽由接收端决定,发送端可对其加以限制
  • 2:动态限制,消息接收可以为硬限制,也可以是软限制。

用户控制消息-User Control Messages(type 4)

发送端通过用户控制消息想接收端发送关于用户控制事件的消息,长度可变,包含事件类型和事件数据两部分,事件类型占用2字节,其余为事件数据。

字段大小(字节)描述
Event Type2事件类型
Event Data可变事件数据

分为7种事件类型:

事件类型名称事件数据描述
0Stream Begin流ID流已就绪
1Stream EOF流ID流已结束
2Stream Dry流ID媒体流数据不足,服务端没有足够的数据发给客户端
3Set Buffer Length流ID + 缓冲区长度设置缓冲区大小,开始处理流之前,客户端告知服务器缓冲区大小
4Stream Is Recorded流ID当前流为录播流
6Ping Request时间戳检测客户端是否在线,携带服务器时间戳
7Ping Response时间戳客户端向服务器响应消息,表示在线

共享对象消息-Shared Object Messages (type 16/19)

一种强大的实时数据同步机制,允许多个客户端通过服务器同步共享数据状态

类型值名称方向描述
0x01ConnectC→S客户端请求连接共享对象
0x02DisconnectC→S客户端断开共享对象
0x03Set AttributeC↔S客户端设置共享对象属性
0x04Update DataS→C服务器通知除源客户端外的所有客户端更新共享数据
0x05Update AttributeS→C客户端设置Set Attribute 成功后,服务器会返回成功,更新属性值
0x06Send MessageC↔S客户端请求服务器向所有客户端广播(发送)消息到共享对象
0x07StatusS→C状态通知
0x08Clear DataS→C服务器通知客户端清除所有数据
0x09Delete DataS→C服务器通知客户端删除特定数据(插槽)
0x0ADelete Data sucessC→S客户端返回删除成功
0x0BConnect sucessS→C服务器发送连接成功事件

消息头:

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
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|  类型 (1)    |               共享对象名称 (变长)             |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|               版本 (4)        |   持久化标志 (1)   | 保留 (3) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

Set Attribute 消息体

+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|  属性名长度 (2)  |  属性名 (变长)  |  属性类型 (1)  |  属性值 (变长) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

命令消息类型(Type 17/19)

命令消息类型 17 和 19 是 AMF3 编码的命令消息,分别用于客户端到服务器和服务器到客户端的通信。

  1. 类型 17:客户端发起的命令请求
  2. 类型 19:服务器响应和通知
  3. AMF3 编码:紧凑高效的二进制格式

命令类型:

命令名称方向描述参数示例
connectC→S客户端连接到服务器app: 应用名, flashVer: 客户端版本, tcUrl: 服务器URL等
disconnectC→S客户端断开连接
createStreamC→S客户端请求创建一个流
deleteStreamC→S客户端删除一个流streamId: 流ID
playC→S客户端请求播放一个流streamName: 流名称, start: 开始时间, duration: 持续时间, reset: 是否重置
play2C→S扩展播放命令,支持不同的播放参数parameters: 参数对象
pauseC→S客户端暂停或恢复播放pause: 是否暂停, time: 暂停的时间点
seekC→S客户端定位到指定时间offset: 时间偏移量
publishC→S客户端发布一个流streamName: 流名称, type: 发布类型(live, record, append)
closeStreamC→S客户端关闭流
receiveAudioC→S客户端通知服务器是否接收音频bool: 是否接收
receiveVideoC→S客户端通知服务器是否接收视频bool: 是否接收
getStreamLengthC→S客户端获取流的长度streamName: 流名称
FCPublishC→S由Flash Media Server定义,开始发布流streamName: 流名称
FCUnpublishC→S由Flash Media Server定义,停止发布流streamName: 流名称
releaseStreamC→S客户端释放流streamName: 流名称
_resultS→C服务器对客户端命令的成功响应命令的返回值,例如createStream返回流ID
_errorS→C服务器对客户端命令的错误响应错误信息
onStatusS→C服务器通知客户端状态变化info: 包含level, code, description等状态信息
onMetaDataS→C服务器发送流的元数据包含视频宽度、高度、帧率等
onFIS→C服务器发送文件信息(用于文件播放)文件信息
onBWDoneS→C服务器通知客户端带宽检测完成bandwidth: 带宽值
checkBandwidthS→C服务器通知客户端开始带宽检测
getBandwidthS→C服务器请求客户端报告带宽
sampleAccessS→C服务器通知客户端是否允许采样音频和视频audio: 是否允许音频采样, video: 是否允许视频采样
setDataFrameC→S客户端设置数据帧(用于发送元数据)元数据对象
pingS↔C心跳检测(Adobe的私有命令)timestamp: 时间戳
pongS↔C心跳响应(Adobe的私有命令)timestamp: 时间戳
callS↔C自定义命令任意自定义数据

流程:

sequenceDiagram
    participant C as Client
    participant S as Server
    
    C->>S: connect (类型17)
    S->>C: Window Acknowledgement Size
    S->>C: Set Peer Bandwidth
    S->>C: onBWDone (类型19)
    S->>C: _result (类型19) for connect
    C->>S: createStream (类型17)
    S->>C: _result (类型19) with stream ID
    C->>S: play (类型17)
    S->>C: onStatus (类型19) NetStream.Play.Start
    S->>C: |音视频数据|
    C->>S: onStatus (类型19) NetStream.Play.Stop
+----------------+----------------+----------------+----------------+
|  命令名称       |  事务ID        |  参数对象      |  可选参数       |
|  (String)      |  (Number)      |  (Object)      |  (Object)      |
+----------------+----------------+----------------+----------------+
|  "connect"     |  1             |  {             |  {             |
|                |                |    "app": str, |    "fpad": bool|
|                |                |    "tcUrl": str,|    "capabilities": int|
|                |                |    "flashVer": str|    ...       |
|                |                |  }             |  }             |
+----------------+----------------+----------------+----------------+
响应:
+----------------+----------------+----------------+----------------+
|  命令名称       |  事务ID        |  连接属性      |  信息对象       |
|  (String)      |  (Number)      |  (Object)      |  (Object)      |
+----------------+----------------+----------------+----------------+
|  "_result"     |  1             |  {             |  {             |
|                |                |    "fmsVer": str,|  "level": "status"|
|                |                |    "capabilities": int|  "code": "NetConnection.Connect.Success"|
|                |                |  }             |  "description": str|
|                |                |                |  "objectEncoding": int|
|                |                |                |  }             |
+----------------+----------------+----------------+----------------+
+----------------+----------------+----------------+----------------+
|  命令名称       |  事务ID        |  参数1         |  参数N         |
|  (String)      |  (Number)      |  (Any)        |  (Any)        |
+----------------+----------------+----------------+----------------+
|  "myMethod"    |  123           |  "param1"      |  42           |
+----------------+----------------+----------------+----------------+
响应:
+----------------+----------------+----------------+----------------+
|  命令名称       |  事务ID        |  结果          |                |
|  (String)      |  (Number)      |  (Any)        |                |
+----------------+----------------+----------------+----------------+
|  "_result"     |  123           |  "success"    |                |
+----------------+----------------+----------------+----------------+
+----------------+----------------+----------------+
|  命令名称       |  事务ID        |  保留参数       |
|  (String)      |  (Number)      |  (Null)        |
+----------------+----------------+----------------+
|  "createStream"|  事务ID        |  null          |
+----------------+----------------+----------------+
响应:
+----------------+----------------+----------------+----------------+
|  命令名称       |  事务ID        |  保留参数       |  流ID          |
|  (String)      |  (Number)      |  (Null)        |  (Number)      |
+----------------+----------------+----------------+----------------+
|  "_result"     |  事务ID        |  null          |  流ID值        |
+----------------+----------------+----------------+----------------+
+----------------+----------------+----------------+----------------+
|  命令名称       |  事务ID        |  保留参数      |  流名称         |
|  (String)      |  (Number)      |  (Null)        |  (String)      |
+----------------+----------------+----------------+----------------+
|  "play"        |  0             |  null          |  "streamName"  |
+----------------+----------------+----------------+----------------+
|  开始时间       |  持续时间       |  重置标志       |
|  (Number)      |  (Number)      |  (Boolean)     |
+----------------+----------------+----------------+
|  -2            |  -1            |  true          |
+----------------+----------------+----------------+
响应:
+----------------+----------------+----------------+----------------+
|  命令名称       |  事务ID        |  保留参数      |  信息对象       |
|  (String)      |  (Number)      |  (Null)        |  (Object)      |
+----------------+----------------+----------------+----------------+
|  "onStatus"    |  0             |  null          |  {             |
|                |                |                |    "level": "status", |
|                |                |                |    "code": "NetStream.Play.Start", |
|                |                |                |    "description": "Playing liveStream" |
|                |                |                |  }             |
+----------------+----------------+----------------+----------------+
+----------------+----------------+----------------+----------------+
|  命令名称       |  事务ID        |  命令对象      |  流ID          |
|  (String)      |  (Number)      |  (Null)        |  (Number)      |
+----------------+----------------+----------------+----------------+
|  "deleteStream"|  事务ID        |  null          |  流ID          |
+----------------+----------------+----------------+----------------+
响应:
+----------------+----------------+----------------+----------------+
|  命令名称       |  事务ID        |  命令对象      |  结果对象      |
|  (String)      |  (Number)      |  (Null)        |  (Object)      |
+----------------+----------------+----------------+----------------+
|  "_result"     |  5             |  null          |  {}            |
+----------------+----------------+----------------+----------------+
+----------------+----------------+----------------+----------------+----------------+
|  命令名称       |  事务ID        |  流名称         |  时间偏移       |  可选参数       |
|  (String)      |  (Number)      |  (String)      |  (Number)      |  (Boolean)     |
+----------------+----------------+----------------+----------------+----------------+
|  "seek"   |  0             |  "stream1"   |  10000         |  true          |
+----------------+----------------+----------------+----------------+----------------+
响应:
+----------------+----------------+----------------+----------------+
|  命令名称       |  事务ID        |  保留参数      |  状态对象       |
|  (String)      |  (Number)      |  (Null)       |  (Object)      |
+----------------+----------------+----------------+----------------+
|  "onStatus" |  0             |  null         |  {             |
|                |                |                |    "level": "status", |
|                |                |                |    "code": "NetStream.Seek.Notify", |
|                |                |                |    "description": "Seek to 10000ms" |
|                |                |                |  }             |
+----------------+----------------+----------------+----------------+
+----------------+----------------+----------------+----------------+----------------+
|  命令名称       |  事务ID        |  保留参数      |  暂停标志       |  暂停位置       |
|  (String)      |  (Number)      |  (Null)       |  (Boolean)     |  (Number)      |
+----------------+----------------+----------------+----------------+----------------+
|  "pause"   |  0             |  null         |  true          |  -2000         |
+----------------+----------------+----------------+----------------+----------------+
响应:
+----------------+----------------+----------------+----------------+
|  命令名称       |  事务ID        |  保留参数      |  状态对象       |
|  (String)      |  (Number)      |  (Null)       |  (Object)      |
+----------------+----------------+----------------+----------------+
|  "onStatus" |  0             |  null         |  {             |
|                |                |                |    "level": "status", |
|                |                |                |    "code": "NetStream.Pause.Notify", |
|                |                |                |    "description": "Paused stream" |
|                |                |                |  }             |
+----------------+----------------+----------------+----------------+