当前位置: 首页 > news >正文

【音视频】RTMP协议推流抓包分析

一、RTMP基本原理

RTMP(Real Time Messaging Protocol)是专为实时流媒体传输设计的应用层协议,核心目标是在客户端与服务器间高效传输音视频数据

1.1 基于TCP的可靠连接

RTMP建立在TCP协议之上,利用TCP的可靠传输特性(如流量控制、错误重传)确保数据稳定传输,适合对时序敏感的实时流媒体场景(如直播、视频会议)

1.2 消息分块与流式传输

  1. 消息(Message)与块(Chunk)的分层结构

    • 消息层:将完整数据(如一帧视频、一条命令)封装为消息,包含消息类型、长度、时间戳等头部信息。
    • 块层:将大消息分割为小数据块(Chunk,通常128字节)传输,减少网络缓冲压力,支持实时解包播放。
  2. 消息类型

  • 命令消息:用于客户端与服务器交互(如创建流、播放控制),通过AMF(Action Message Format)编码。
  • 媒体消息:封装音视频数据(如H.264视频、AAC音频),按时间戳有序传输。

在这里插入图片描述

1.3 低延迟与时序控制

  1. 握手同步时间戳
    通过三次握手交换时间戳(见前序握手流程),计算网络延迟(RTT),确保客户端与服务器时序一致,避免播放卡顿。

  2. 流式传输与缓冲策略

    • 服务器边发送边编码,客户端边接收边解码,仅缓冲少量数据(通常几十毫秒到几秒),实现低延迟(一般1-3秒)。
    • 支持动态调整码率,适应网络波动。

1.4 命令交互与媒体传输的协同

  1. 命令交互流程

    • 客户端通过connect命令建立连接,createStream创建流,publish发布媒体或play拉取媒体。
    • 服务器以AMF格式响应命令,返回状态(如成功、错误)。
  2. 媒体数据传输

    • 发布端(如推流软件)将编码后的音视频数据封装为媒体消息,按时间戳顺序发送。
    • 接收端(如播放器)解析消息,按时间戳同步音视频轨道,实现同步播放。

二、RTMP协议握手流程

RTMP(Real Time Messaging Protocol)是Adobe开发的用于实时流媒体传输的应用层协议,其握手流程是建立客户端与服务器连接的关键步骤。以下是RTMP握手的详细流程和技术细节:

在这里插入图片描述

1.1 RTMP握手的基本概念

RTMP建立在TCP连接之上,握手过程属于应用层协议交互,目的是:

  1. 协商协议版本
  2. 同步时间戳
  3. 交换随机数(防重放攻击)
  4. 确认双方通信能力

1.2 握手流程:三次交互(C0/C1/C2与S0/S1/S2)

RTMP握手分为客户端(Client)和服务器(Server)的双向数据交换,共6个数据包,可分为三个阶段:

阶段1:客户端发送C0和C1
  1. C0:版本信息

    • 长度:1字节
    • 内容:客户端支持的RTMP协议版本号(如0x03表示版本3)。
  2. C1:客户端握手包

    • 长度:至少1536字节(含填充数据)
    • 结构:
      • 时间戳(Timestamp):4字节无符号整数(0x00000000),后续由服务器填充有效时间。
      • 时间戳扩展(Timestamp Extended):4字节无符号整数(当时间戳超过0xFFFFFF时使用)。
      • 随机数(Random Data):1528字节随机数据(用于安全验证和防重放)。
阶段2:服务器响应S0和S1,并发送S2
  1. S0:版本确认

    • 长度:1字节
    • 内容:服务器选择的协议版本号(通常与C0一致,若不支持则返回0x00并断开连接)。
  2. S1:服务器握手包

    • 长度:至少1536字节(结构与C1类似)
    • 结构:
      • 时间戳:4字节,服务器生成的当前时间戳(用于客户端同步)。
      • 时间戳扩展:4字节。
      • 随机数:1528字节随机数据(与客户端随机数互验)。
  3. S2:服务器响应包

    • 长度:至少1536字节
    • 结构:
      • 时间戳A:4字节,填充客户端C1中的时间戳(用于客户端确认服务器接收C1的时间)。
      • 时间戳扩展A:4字节。
      • 时间戳B:4字节,服务器S1中的时间戳(用于客户端同步服务器时间)。
      • 时间戳扩展B:4字节。
      • 填充数据:与C1中的随机数长度一致(1528字节)。
阶段3:客户端发送C2,完成握手
  1. C2:客户端响应包
    • 长度:至少1536字节
    • 结构:
      • 时间戳A:4字节,填充服务器S1中的时间戳(用于服务器确认客户端接收S1的时间)。
      • 时间戳扩展A:4字节。
      • 时间戳B:4字节,客户端C1中的时间戳(用于服务器同步客户端时间)。
      • 时间戳扩展B:4字节。
      • 填充数据:与S1中的随机数长度一致(1528字节)。

1.3 握手关键技术细节

  1. 时间戳同步

    • 客户端和服务器通过交换时间戳(Timestamp)计算网络延迟(RTT),用于后续数据传输的时序控制。
    • 时间戳超过0xFFFFFF(约24天)时,需使用时间戳扩展字段(4字节)。
  2. 随机数的作用

    • 1528字节的随机数用于验证双方身份,防止重放攻击(Replay Attack),确保握手的唯一性。
  3. 填充数据

    • 握手包强制填充至1536字节,避免数据包长度泄露信息,增强安全性。
  4. 协议版本兼容

    • 若服务器不支持客户端的版本(如C0为0x04),则返回S0=0x00并断开连接。

1.4 握手流程时序图

客户端                          服务器|                                ||  C0(版本) + C1(时间戳+随机数) → ||                                || ←  S0(版本) + S1(时间戳+随机数)  ||                                ||  C2(响应S1时间戳)            → ||                                || ←  S2(响应C1时间戳)            ||                                |✔  握手完成,开始RTMP消息交互       |

1.5 扩展:RTMPS(加密握手)

  • RTMPS(基于TLS/SSL的RTMP)在握手前增加TLS握手流程,用于加密传输。
  • 握手包结构与RTMP类似,但随机数等数据会通过TLS加密,防止中间人攻击。

三、RTMP 推流抓包分析

3.1 测试环境

  • 推流端:FFmpeg Windows
  • 服务器:SRS Ubuntu
  • 网络:局域网

3.2 推流

  • 使用ffmpeg推流视频到RTMP服务器
  • h264aac格式封装到flv中,然后用RTMP协议发到服务器
ffmpeg -i .\4k_50.mp4 -vcodec h264 -acodec aac -f flv "rtmp://10.22.79.251/live/livestream"

3.3 TCP三次握手

在这里插入图片描述

在这里插入图片描述

3.3.1 TCP第一次握手

在这里插入图片描述

1. 端口标识
  • 源端口(Source Port)61319
    • 客户端的 临时端口(系统随机分配,用于本次连接的临时标识,通常 >1024)。
  • 目的端口(Destination Port)1935
    • 知名端口,RTMP 协议默认端口(多用于直播、流媒体场景),也可能是自定义服务,但 1935 最典型关联 RTMP。
2. 序列号(Sequence Number)
  • 相对序号0(Wireshark 简化显示,方便分析)
  • 原始序号499989480(客户端生成的 初始序列号(ISN),随机值)
    • 作用:TCP 通过序列号保证数据有序性,SYN 包的 ISN 是连接的 “起始编号”,后续数据的 SEQ 会基于此递增。
    • 特殊点:SYN 包自身会 消耗 1 个序列号(即使无数据),因此下一个包的 SEQ 为 ISN + 1
3. 确认号(Acknowledgment Number)
  • 0
    • 因这是 第一次握手(客户端→服务器),客户端尚未收到服务器数据,故确认号为 0
    • 若收到服务器的 SYN+ACK,客户端的 ACK 会变为 服务器 ISN + 1(确认已接收对方的 SYN)。
4. 标志位(Flags)
  • 0x002 (SYN)
    • SYN 标志位为 1,表示这是 连接请求包(三次握手的第一步)。
    • 后续若服务器响应,会返回 SYN+ACK(SYN=1、ACK=1);客户端再回 ACK(ACK=1),完成握手。
5. 窗口大小(Window)
  • 65535
    • 客户端的 接收窗口,告诉服务器:“我最多能接收 65535 字节的数据”。
    • 这是 TCP 流量控制 的核心:服务器发数据不能超过该窗口大小。
6. 校验和(Checksum)
  • 0x6ee3,状态 Unverified
    • 并非错误!现代网卡支持 校验和卸载(硬件计算校验和),抓包时可能未填充,故 Wireshark 标记为 “未验证”,属正常现象。

3.3.2 TCP第二次握手

在这里插入图片描述

1. 端口映射
  • 源端口(Src Port)1935(服务器端端口,持续监听,对应 RTMP 等服务)
  • 目的端口(Dst Port)61319(客户端临时端口,与第一次握手的源端口对应)
    → 体现 “客户端→服务器” 的反向回复:服务器用客户端请求的目的端口(1935)作为源端口,回应客户端的源端口(61319)。
2. 序列号(Sequence Number)
  • 相对序号0(Wireshark 简化显示)
  • 原始序号2916634360(服务器生成的 初始序列号(ISN),随机值)
    → 服务器也需要一个 “起始编号”,后续数据的 SEQ 会基于此递增(和客户端的 ISN 独立)。
3. 确认号(Acknowledgment Number)
  • 相对序号1(Wireshark 简化显示)
  • 原始序号499989481
    计算逻辑:对客户端第一次握手 SYN 包的确认。
    客户端 SYN 包的原始序列号是 499989480,TCP 规定 SYN 包自身消耗 1 个序列号,因此确认号 = 客户端 ISN + 1 = 499989480 + 1 = 499989481
4. 标志位(Flags)
  • 0x012 → 二进制 0001 0010,对应 SYN=1 + ACK=1
    • SYN=1:服务器也发起 “同步请求”,交换自己的 ISN(和客户端的 SYN 呼应)。
    • ACK=1:确认已收到客户端的 SYN 包(通过确认号体现)。
5. 窗口大小(Window)
  • 64240
    → 服务器的 接收窗口,告诉客户端:“我最多能接收 64240 字节的数据”,用于后续流量控制。
6. TCP 选项(Options)
  • 包含 MSS(Maximum Segment Size,最大分段大小)、SACK 允许 等:
    • MSS:协商 “单个 TCP 段的最大数据长度”(通常为 MTU - 40,如以太网 MTU=1500,则 MSS=1460),避免 IP 分片。
    • SACK(Selective Acknowledgment):允许接收方选择性确认失序的数据包,提升重传效率。

3.3.3 TCP第三次握手

在这里插入图片描述

1. 序列号(Sequence Number)
  • 相对值1(Wireshark显示的相对序列号,简化分析)
  • 原始值499989481(实际传输的绝对序列号,用于唯一标识数据)
  • 作用:表示发送方已发送到序列号1(累计计数),无数据 payload(Len: 0),故仅用于确认。
2. 确认号(Acknowledgment Number)
  • 相对值1(相对确认号)
  • 原始值2916634361(绝对确认号)
  • 作用:告知对方“已收到序列号≤1的数据”,请求对方下一个数据包从2开始发送。
3. 标志位(Flags)
  • 0x010(二进制 00010000),仅ACK位置1。
  • 含义:这是一个纯确认报文,不含数据(Len: 0),仅用于确认之前收到的数据包。
4. 窗口大小(Window)
  • 原始值255
  • 计算后大小65280(因窗口缩放因子为256255 × 256 = 65280
  • 作用:告知对方“当前接收缓冲区还能容纳65280字节的数据”,用于流量控制。
5. 头部长度(Header Length)
  • 20字节(二进制 0101,表示5个32位字,5×4=20
  • 含义:TCP头部无选项字段(最小头部长度),仅包含基本字段。

通过TCP的三次握手,成功建立起TCP连接,接着下面就是RTMP的握手了

3.4 RTMP三次握手

在这里插入图片描述

3.4.1 RTMP第一次握手

在这里插入图片描述

握手初始化(C0+C1 包)

RTMP 握手分三步,此包是 客户端向服务器发送的首阶段握手数据

1. C0:协议版本(1 字节)
  • Protocol version: 03 → 表示使用 RTMP/1.0 版本(0x03 是 RTMP 标准版本号,兼容多数服务器)。
2. C1:握手数据(1536 字节,此处为分段续传)

包含 时间戳、随机数、零填充 等字段:

  • 时间戳:同步双方时钟,保证音视频流的时序一致性。
  • 随机数:防伪造,验证连接真实性。
  • 零填充:固定 1536 字节长度(RTMP 握手包格式要求)。
3. 分段传输的原因

C0+C1 总长 1 + 1536 = 1537 字节,而 TCP 的 MSS(最大分段大小)为 1460(以太网常见 MTU=1500,减去 IP 头 20 + TCP 头 20,剩余 1460),因此:

  • 第一个 TCP 段传 1460 字节(包含 C0 和 1459 字节的 C1);
  • 本次段传 77 字节(C1 的剩余部分,1537 - 1460 = 77)。

3.4.2 RTMP第二次握手

在这里插入图片描述

RTMP 握手分三步,此包是 客户端对服务器 S0+S1+S2 的最终确认,核心作用:

  1. 确认服务器的握手数据
    • 验证服务器 S1 中的时间戳、随机数等参数,确保双方同步。
    • 告知服务器:“我已收到并认可你的握手响应(S0+S1+S2)”。
  2. 完成握手流程
    • 客户端发 C0+C1 → 服务器回 S0+S1+S2 → 客户端发 C2,三阶段握手完成,RTMP 连接正式就绪。

RTMP 层:握手响应(S0+S1+S2 包)

RTMP 握手的核心是 “版本协商 + 时间戳同步 + 随机数验证”,此包包含三部分:

1. S0:协议版本(1 字节)
  • Protocol version: 03 → 与客户端 C0 的版本一致(RTMP/1.0),确保 版本兼容
2. S1:服务器握手数据(1536 字节,分段传输)

包含 服务器时间戳、随机数、零填充

  • 时间戳:同步双方时钟,保证音视频流的时序一致性
  • 随机数:验证连接真实性,防止伪造。
  • 零填充:固定 1536 字节长度(RTMP 握手包格式要求)。
3. S2:客户端握手数据的 “回声”
  • 内容与客户端 C1 包完全一致(图中 Handshake data 部分)。
  • 作用:验证服务器是否正确接收并解析了客户端的 C1 包,确保 双向同步

RTMP第三次握手

在这里插入图片描述

这个报文是 RTMP握手阶段的C2包(客户端发送的第二次握手响应),承载在TCP连接中,属于RTMP握手的最终确认环节。以下从 协议层级、RTMP握手逻辑、字段细节 展开分析:

RTMP的C2包
C2包 的关键功能:
  1. 时间戳双向同步
    • 回传服务器S1的时间戳(让服务器计算RTT:服务器当前时间 - S1发送时间)。
    • 携带客户端C2的时间戳(让服务器同步客户端时序)。
  2. 随机数校验
    • 包含与C1一致的随机数据片段,验证握手完整性(防止伪造)。
  3. 握手完成标志
    • C2发送后,RTMP握手正式完成,后续进入应用层交互(如connect命令、媒体流传输)。

通过RTMP三次握手,成功建立RTMP通讯,此时可以开始传输RTMP协议数据了

3.5 connect报文

在这里插入图片描述

RTMP 头部(RTMP Header)

RTMP 消息通过 Chunk(分块) 传输,头部包含基础控制信息:

字段值 / 解析作用
Format0Chunk 格式为 完整头(包含所有字段:时间戳、体大小、类型、流 ID),用于首条消息或 Chunk 参数变化时。
Chunk Stream ID3Chunk 流 ID,3 是 RTMP 约定的 控制流(Control Stream),用于传输协议控制命令(如 connectcreateStream)。
Timestamp0时间戳(毫秒),此处为初始连接,时间戳置 0。
Body size141消息体长度(141 字节),即后续 AMF0 编码数据的大小。
Type ID0x14(十进制 20消息类型:AMF0 Command(RTMP 规定 Type ID=20 表示 AMF0 编码的命令消息)。
Stream ID0流 ID,0 表示 全局流(用于控制消息,非媒体流)。
RTMP 消息体(AMF0 编码)

RTMP 使用 AMF0(Action Message Format 0) 序列化命令和数据,此包的结构为 “命令名 + 事务 ID + 命令参数”

1. 命令名:connect
  • AMF0 类型String (0x02)
  • 内容connect
    → 告知服务器:执行 “连接到应用” 的命令
2. 事务 ID(Transaction ID)
  • AMF0 类型Number (0x00)
  • 1
    → 用于 匹配响应:服务器返回的 _result_error 消息会携带相同事务 ID(此处为 1),确保请求 - 响应对应。
3. 命令参数(Object)

以 AMF0 对象形式传递连接参数,包含 4 个键值对

键(Key)值(Value)意义
appString 'live'要连接的 应用名称(服务器端配置的应用,如直播推流的 live 应用)。
typeString 'nonprivate'连接类型:nonprivate 表示 公开连接(无访问限制)。
flashVerString 'FMLE/3.0 (compatible; Lavf62.0.102)'模拟的 Flash 版本:
- FMLE 是 Adobe Flash Media Live Encoder(推流工具);
- Lavf62.0.102 是 FFmpeg 的格式库版本(实际是 FFmpeg推流)。
tcUrlString 'rtmp://10.22.79.251:1935/live'目标 RTMP URL:
rtmp://<服务器IP>:<端口>/<应用名>,明确连接的服务器和应用。
报文的核心意义:RTMP 连接协商的 “敲门砖”
  1. 阶段定位
    RTMP 握手(C0/C1→S0/S1/S2→C2)完成后,进入 应用层连接协商阶段,此包是客户端发起的 第一个应用层命令

  2. 对服务器的要求
    服务器需验证:

    • app 是否存在(如 live 应用是否配置);
    • tcUrl 是否合法(IP、端口、应用名是否匹配);
    • 客户端身份(通过 flashVer 等字段,部分服务器验证推流工具合法性)。
  3. 后续流程
    服务器会返回 _result_error 消息(携带事务 ID=1):

    • 若成功:返回连接参数(如 fmsVercapabilities 等),客户端可继续创建流(createStream 命令)。
    • 若失败:返回错误码(如 NetConnection.Connect.Rejected),连接终止。

3.6 Window Acknowledgement Size 报文

在这里插入图片描述

这是 RTMP 协议的 Window Acknowledgement Size 控制报文,用于 应用层流量控制(告知对方自身的接收窗口大小),以下从 RTMP 结构协议逻辑 解析:

RTMP 头部(RTMP Header)解析
字段值 / 解析作用
Format0Chunk 格式为 完整头(包含所有字段),用于首条控制消息或参数变化时。
Chunk Stream ID2Chunk 流 ID,2 是 RTMP 约定的 控制流(用于传输窗口、块大小等协议控制消息)。
Timestamp0时间戳(毫秒),此处为控制消息,时间戳置 0。
Body size4消息体长度(4 字节,刚好存储一个 32 位整数)。
Type ID0x05(十进制 5消息类型:Window Acknowledgement Size(RTMP 规定 Type ID=5 表示此控制消息)。
Stream ID0流 ID,0 表示 全局控制流(非媒体流,仅用于协议控制)。
RTMP 消息体(Window Acknowledgement Size)
  • 内容Window acknowledgement size: 2500000
  • 本质:一个 32 位无符号整数(4 字节),表示 接收方的应用层接收窗口大小(单位:字节)。
协议逻辑:RTMP 应用层的 “流量控制”
  1. 为什么需要应用层窗口?
    TCP 层已有窗口机制,但 RTMP 是 流式协议(音视频传输对时序、吞吐量更敏感),应用层窗口可:

    • 精准控制媒体数据(如区分音视频流和控制流的窗口);
    • 适配 RTMP 分块(Chunk)机制(按 Chunk 统计数据量,而非 TCP 段)。
  2. 窗口的作用
    接收方告知发送方:“我最多能接收 2500000 字节的 RTMP 数据,超过后请等待我的 ACK 确认再发”

    • 发送方需累计发送的数据量(按 RTMP Chunk 计算),达到窗口大小时,必须等待接收方的 Acknowledgement 消息才能继续发送。
上下文关联:连接初始化阶段的协商

结合前序报文(如 connect 命令),此包发生在 RTMP 握手完成、连接协商阶段

  • 服务器通过此消息 设置自身的接收窗口(告诉客户端:“我最多收 250 万字节,别发太快”);
  • 后续客户端需遵守此窗口限制,避免发送过量数据导致接收方缓冲区溢出。
与 TCP 窗口的区别
维度RTMP 应用层窗口(此报文)TCP 传输层窗口
控制层级应用层(RTMP 协议内)传输层(TCP 协议内)
统计对象RTMP Chunk 的数据量TCP 段的字节数
作用适配流媒体传输的时序、分块特性保证网络层的可靠传输、拥塞控制

总结:应用层流量控制的 “信号灯”

这个报文是 RTMP 协议的流量控制核心

  • 通过 Window Acknowledgement Size 告知对方自身的接收能力,避免数据泛滥;
  • 与 TCP 窗口协同,从 应用层 + 传输层 两层保障流媒体传输的稳定性(既防网络拥塞,也防应用层处理不过来)。

后续若客户端发送的数据量累计达到 2500000 字节,需等待服务器的 Acknowledgement 消息(Type ID=6)才能继续发送,否则会触发流控等待。

3.7 Set Peer Bandwidth 报文、Set Chunk Size报文

在这里插入图片描述

这是 RTMP 协议的两个核心控制报文(同一条 TCP 段中携带,属于 应用层传输参数协商 阶段),分别是 Set Peer Bandwidth(设置对端带宽)Set Chunk Size(设置分块大小),用于优化流媒体传输效率。以下分层解析:

整体上下文:RTMP 连接初始化后的“参数协商”

在 RTMP 握手(C0/C1/S0/S1/S2/C2)和 connect 命令之后,服务器会发送 控制消息 协商 带宽、分块大小 等传输参数,为后续音视频流传输铺路。

报文 1:Set Peer Bandwidth(Type ID = 0x06)

1. RTMP 头部
字段值/解析作用
Format0完整 Chunk 头(首条控制消息,携带所有字段)。
Chunk Stream ID2控制流(Chunk Stream ID=2,RTMP 约定用于传输带宽、块大小等控制消息)。
Timestamp0控制消息无时间戳,置 0。
Body size5消息体长度(5 字节:4 字节窗口 + 1 字节限制类型)。
Type ID0x06(十进制 6消息类型:Set Peer Bandwidth(设置对端发送带宽限制)。
Stream ID0全局控制流(非媒体流)。
2. RTMP 消息体
  • Window acknowledgement size2500000(250 万字节)→ 告知客户端:“你发送的 RTMP 数据累计不能超过 250 万字节,否则需等待我的确认”
  • Limit typeDynamic (2) → 带宽限制类型:
    • Dynamic 表示 “动态限制”(客户端可根据接收方窗口灵活调整发送速率,非强制硬限制);
    • 其他类型:Hard(硬限制,必须严格遵守)、Soft(软限制,可短暂突破)。

报文 2:Set Chunk Size(Type ID = 0x01)

1. RTMP 头部
字段值/解析作用
Format0完整 Chunk 头(首条控制消息,携带所有字段)。
Chunk Stream ID2控制流(同一会话的控制消息复用 Chunk Stream ID=2)。
Timestamp0控制消息无时间戳,置 0。
Body size4消息体长度(4 字节,存储分块大小)。
Type ID0x01(十进制 1消息类型:Set Chunk Size(设置 RTMP 分块大小)。
Stream ID0全局控制流(非媒体流)。
2. RTMP 消息体
  • Chunk size60000 → 将 RTMP 分块大小设置为 60000 字节(默认分块大小为 128 字节)。
  • 作用
    • 更大的分块可 减少 Chunk 头部开销(每个 Chunk 需携带头部,大分块降低头部占比);
    • 提升 大流量场景(如直播推流)的传输效率(减少分片次数,降低 CPU 消耗)。

协议逻辑:“带宽 + 分块”双优化

  1. Set Peer Bandwidth 的意义
    服务器限制客户端的 发送速率上限,避免自身接收缓冲区溢出(结合之前的 Window Acknowledgement Size,从 “发送方速率”和“接收方容量” 双向控制流量)。

  2. Set Chunk Size 的意义
    协商更高效的 RTMP 分块策略,适配大带宽传输(如 60000 字节接近 TCP MSS,减少 IP/TCP 分片,提升吞吐量)。

  3. 与 TCP 层的协同

    • TCP 层通过窗口、拥塞控制保证 网络可靠性
    • RTMP 层通过 Set Peer BandwidthSet Chunk Size 优化 应用层传输效率,两者结合让流媒体传输更稳定、高效。

总结:传输参数的“黄金配置”

这两个报文是 RTMP 连接的 “性能调优阶段”

  • Set Peer Bandwidth 限制发送速率,防止拥塞;
  • Set Chunk Size 放大分块,提升效率;
  • 两者共同为后续 音视频流传输(如 publish 推流、play 拉流)奠定基础,让数据既“流得稳”又“流得快”。

后续若客户端发送大尺寸视频帧,会按 60000 字节的分块传输,减少协议开销;同时遵守 250 万字节的带宽限制,避免压垮服务器。

3.8 Set Chunk Size报文、releaseStream报文、FCublish报文、createStream报文、_checkbw报文

在这里插入图片描述

在这里插入图片描述

这组报文属于 RTMP 协议的“流管理与控制阶段”,是客户端向服务器发起的一系列 应用层命令,用于 清理旧流、创建新流、检测带宽 等关键操作,为后续音视频传输铺路。以下逐一分解:

1. Set Chunk Size (60000):优化传输效率

  • 类型:RTMP 控制消息(Type ID = 0x01),Chunk Stream ID = 2(控制流)。
  • 作用
    确认服务器的分块大小设置(将 RTMP 分块大小设为 60000 字节),大幅提升大流量传输效率(默认分块 128 字节,大分块减少协议头部开销)。

2. releaseStream('livestream'):清理残留流

  • 类型:AMF0 命令(Type ID = 0x14),Chunk Stream ID = 3(命令流)。
  • 结构
    [命令名: "releaseStream", 事务ID: 2, 参数: [Null, "livestream"]]
  • 作用
    释放服务器上已存在的 livestream 流(避免重名冲突,清理历史残留资源)。

3. FCublish('livestream'):声明发布类型

  • 类型:AMF0 命令(Type ID = 0x14),Chunk Stream ID = 3
  • 结构
    [命令名: "FCublish", 事务ID: 3, 参数: [Null, "livestream"]]
  • 作用
    告知服务器:即将以 “文件式发布”(File Publish) 模式推流(区别于实时流的 Publish,常用于预存内容传输)。

4. createStream():创建新流实例

  • 类型:AMF0 命令(Type ID = 0x14),Chunk Stream ID = 3
  • 结构
    [命令名: "createStream", 事务ID: 4, 参数: [Null]]
  • 作用
    请求服务器 创建新的媒体流实例,服务器会返回唯一 流ID(如 1),后续音视频数据将通过该流传输。

5. _checkbw()(重复多次):动态带宽检测

  • 类型:AMF0 命令(Type ID = 0x14),Chunk Stream ID = 3
  • 结构
    [命令名: "_checkbw", 事务ID: 5, 参数: [Null]]
  • 作用
    客户端请求服务器 检测当前连接的带宽能力(服务器可能回发测试数据),用于动态调整推流码率,避免网络拥塞。

协议逻辑:RTMP 流建立的“标准流程”

这些命令遵循 “清理→声明→创建→检测” 的顺序,环环相扣:

  1. 清理旧流releaseStream 确保新流无冲突。
  2. 声明类型FCublish 告知服务器发布模式(文件/实时)。
  3. 创建载体createStream 生成流ID,作为数据传输的“管道”。
  4. 带宽适配_checkbw 动态感知网络,优化传输参数(码率、分块等)。

关键设计细节

  • Chunk Stream ID 分工
    • 2 专用于 控制消息(分块、带宽),3 专用于 应用命令(流管理),体现 RTMP 的 “逻辑流复用” 设计。
  • 事务ID递增:从 2 开始逐次加 1,确保 请求-响应一一对应(服务器回复 _result 时携带相同 ID)。

总结:推流前的“筹备战役”

这组报文是 RTMP 推流的“前置条件”

  • 优化传输(大分块提升效率);
  • 清理冲突(旧流释放);
  • 明确意图(发布类型);
  • 创建载体(新流ID);
  • 适配网络(带宽检测)。

所有步骤完成后,客户端才会执行 publish 推流play 拉流,正式进入音视频传输阶段。每个命令都是流稳定运行的基石。

3.9 _result报文

在这里插入图片描述

_result 响应(关联 releaseStream 命令)

1. RTMP 头部解析
字段值/解析作用
Format0完整 Chunk 头(首次回复 releaseStream 命令,携带所有字段)。
Chunk Stream ID3命令流(与客户端的 releaseStream 命令同流,保持会话关联)。
Timestamp0响应无时间戳(控制消息类响应,置 0)。
Body size21消息体长度(21 字节,AMF0 编码的响应数据)。
Type ID0x14(十进制 20消息类型:AMF0 Command(应用层响应,如 _result)。
Stream ID0全局流(命令阶段,尚未关联具体媒体流)。
2. RTMP 消息体(AMF0 编码)

“响应标识 + 事务ID + 结果参数” 结构解析:

  • 响应标识String '_result'(AMF0 字符串,长度 7)→ 告知客户端:“这是命令的响应”
  • 事务IDNumber 2(AMF0 数值)→ 与客户端 releaseStream 命令的事务ID(2严格匹配,确保 请求-响应一一对应
  • 结果参数
    • Null(AMF0 空值)→ 占位参数。
    • Object(可选,图中未完全展开)→ 包含具体结果(如 codeNetStream.Release.Success 表示成功,或 NetStream.Release.Failed 表示失败)。

协议逻辑:“命令-响应”闭环

  1. 关联客户端命令
    _result 响应对应客户端在 Frame 113 发送的 releaseStream('livestream') 命令(通过 Call for this response in frame: 113 关联)。

  2. 核心作用
    服务器告知客户端:“你请求释放的 livestream 流,处理结果是成功/失败”

    • 若成功:客户端可安全创建新流(如 createStream),避免重名冲突。
    • 若失败:客户端需处理错误(如流不存在、权限不足)。

RTMP 命令-响应机制的关键

  • 事务ID绑定:每个命令(如 releaseStreamcreateStream)都有唯一事务ID,响应通过ID关联,确保流程有序。
  • _result 通用响应:RTMP 用 _result 统一回复命令,区别于错误响应 _error(后者携带具体错误码)。

总结:流资源释放的“确认函”

这个报文是 RTMP 流管理的关键反馈

  • TCP 层用 PSH+ACK 保证响应及时、可靠;
  • RTMP 层通过 _result 和事务ID,明确 releaseStream 命令的执行结果;
  • 只有收到成功响应,客户端才会继续执行 创建新流(createStream)、推流(publish 等后续操作,确保流资源干净、无冲突。

若响应为失败,需排查流是否存在、权限是否充足等问题。

3.10 多个_result报文

在这里插入图片描述

这是 RTMP 协议中服务器对客户端多个命令的批量响应(_result 报文),对应之前客户端发送的 releaseStreamFCublishcreateStream 等命令,标志 流管理阶段的关键确认。以下分层解析:

报文核心特征:“命令-响应”的批量闭环

1. 关联关系

每个 _result 报文通过 Call for this response in frame: 113 关联到客户端在 Frame 113 发送的命令(releaseStreamFCublishcreateStream 等)。

2. 事务ID匹配

RTMP 通过 事务ID(Number 字段) 绑定请求和响应:

  • 事务ID=2 → 对应 releaseStream('livestream') 命令;
  • 事务ID=3 → 对应 FCublish('livestream') 命令;
  • 事务ID=4 → 对应 createStream() 命令。

单个 _result 报文结构(以事务ID=2为例)

1. RTMP 头部
字段值/解析作用
Format0完整 Chunk 头(首条响应,携带所有字段)。
Chunk Stream ID3命令流(与客户端命令同流,保持会话关联)。
Timestamp0响应无时间戳(控制类响应,置 0)。
Body size21(示例)消息体长度(AMF0 编码数据大小)。
Type ID0x14(AMF0 Command)响应类型:告知客户端这是命令的结果反馈。
Stream ID0全局流(命令阶段,未关联具体媒体流)。
2. RTMP 消息体(AMF0 编码)
String '_result'  // 响应标识:“这是命令结果”
Number 2          // 事务ID:匹配客户端的 releaseStream 命令(事务ID=2)
Null              // 占位参数
Object {          // 结果数据(示例,实际含 code、description 等)code: "NetStream.Release.Success", description: "Stream released successfully"
}

协议逻辑:多命令的“结果确认链”

  1. releaseStream 响应(事务ID=2)

    • 若成功(code=NetStream.Release.Success):旧流 livestream 已释放,无重名冲突。
    • 若失败(code=NetStream.Release.Failed):需排查流是否存在、权限问题。
  2. FCublish 响应(事务ID=3)

    • 若成功(code=NetStream.Publish.Start):服务器认可“文件式发布”模式,准备接收数据。
    • 若失败(code=NetStream.Publish.Denied):可能因发布模式不支持、权限不足。
  3. createStream 响应(事务ID=4)

    • 若成功:服务器返回 新流ID(如 Number: 1,隐藏在 Object 中),客户端后续通过该 ID 推流。
    • 若失败:需检查服务器资源(如流数量上限)。

整体流程意义:推流前的“安全检查”

这些 _result 报文是 RTMP 推流的“通行证”

  • 确保 旧流已清理releaseStream 成功);
  • 确保 发布模式被认可FCublish 成功);
  • 确保 新流已创建createStream 成功,拿到流ID)。

只有全部响应成功,客户端才会执行 publish 推流play 拉流,正式进入音视频传输阶段。

总结:“批量确认,流程推进”

这组报文体现 RTMP “命令-响应”的严谨性

  • 每个命令通过事务ID精准绑定响应,避免流程混乱;
  • 服务器批量回复提升效率,客户端快速判断前序操作是否合规;
  • 是流管理阶段的 “最终检查点”,决定后续音视频传输能否启动。

若某条响应失败,客户端需终止流程并报错(如流冲突、权限不足)。

3.11 publish报文

在这里插入图片描述

RTMP 头部(RTMP Header)解析

字段值/解析作用
Format0完整 Chunk 头(首次向新流发送命令,携带所有字段)。
Chunk Stream ID8媒体流ID(区别于之前的控制流2、命令流3,专用於音视频数据传输)。
Timestamp0推流起始时间戳(初始化为0,后续帧会递增)。
Body size40消息体长度(40字节,AMF0编码的命令参数)。
Type ID0x14(十进制 20消息类型:AMF0 Command(应用层推流命令)。
Stream ID0全局流(publish 阶段,尚未完全关联具体流,后续通过 Chunk Stream ID 区分)。

RTMP 消息体(AMF0 编码)解析

“命令名 + 事务ID + 参数” 结构拆解:

1. 命令名:publish
  • AMF0 类型String (0x02),长度 7
  • 作用:告知服务器:“执行推流命令”
2. 事务ID:6
  • AMF0 类型Number (0x00),值 6
  • 作用:唯一标识此命令,服务器回复 _result 时需携带相同 ID,确保 请求-响应对应
3. 参数(3个字段)
参数索引类型内容/解析作用
1Null (0x05)占位符RTMP 命令格式要求,无实际意义。
2String (0x02)livestream,长度 10推流的目标流名称(与之前 releaseStreamFCublish 一致,确保流名统一)。
3String (0x02)live,长度 4发布类型
- live:实时流(边推边播,不存储);
- record:录制流(存储到服务器);
- append:追加到已有录制流。

协议逻辑:推流的“启动信号”

  1. 阶段定位
    发生在 createStream 成功响应后(已创建新流,拿到流ID),是 从“流管理”到“数据传输”的转折点

  2. 对服务器的要求
    服务器需验证:

    • 流名称 livestream 是否可用(已通过 releaseStream 清理,通常合法);
    • 发布类型 live 是否支持(服务器配置是否允许实时推流);
    • 客户端权限(是否允许推流到该应用)。
  3. 后续流程
    服务器返回 _result 响应(事务ID=6):

    • 若成功(code=NetStream.Publish.Start):客户端开始发送 音视频数据(视频帧、音频帧,通过 Chunk Stream ID=8 传输)。
    • 若失败(code=NetStream.Publish.Denied):推流终止,需排查权限、流冲突等问题。

关键设计细节

  • Chunk Stream ID=8 的意义
    专用於 媒体流传输,与控制流(2)、命令流(3)分离,实现 RTMP 多路复用(控制、命令、媒体数据并行传输,互不阻塞)。
  • 发布类型 live 的影响
    决定服务器如何处理数据(实时转发给观众,或同时录制),是推流行为的核心配置。

总结:推流的“发令枪”

这个报文是 RTMP 推流的启动指令,承载:

  • 目标流名livestream)和 发布类型live),明确推流规则;
  • 通过事务ID(6)关联响应,确保流程可控;
  • 切换到专用媒体流(Chunk Stream ID=8),为后续音视频数据传输铺路。

它标志着 从“协议协商”到“实际数据传输”的跨越,是直播/点播流程的核心转折点。

3.12 onFCublish 报文

在这里插入图片描述

这是 RTMP 协议中服务器向客户端发送的 onFCublish 异步事件报文,用于 实时通知推流状态变化(此处为“推流成功启动”)

RTMP 头部(RTMP Header)解析

字段值/解析作用
Format0完整 Chunk 头(首次发送此事件,携带所有字段)。
Chunk Stream ID5服务器事件流ID(专用於异步事件通知,区别于客户端的命令流/媒体流)。
Timestamp0事件类消息无时间戳(或置初始值),聚焦状态通知。
Body size40(实际按数据计算,示例中体部长度对应)消息体长度(AMF0 编码的事件参数)。
Type ID0x14(十进制 20消息类型:AMF0 Command(应用层事件通知,本质是服务器主动发送的命令)。
Stream ID1关联到之前 createStream 创建的 媒体流ID(标识此事件属于哪个流)。

RTMP 消息体(AMF0 编码)解析

“事件名 + 事务ID + 参数” 结构拆解:

1. 事件名:onFCublish
  • AMF0 类型String (0x02),长度 11
  • 作用:告知客户端:“触发了文件式发布(FCublish)的状态变化事件”(对应之前客户端的 FCublish 命令)。
2. 事务ID:0
  • AMF0 类型Number (0x00),值 0
  • 作用:因是 服务器主动发起的异步事件(非客户端请求的响应),无对应事务ID,故置 0
3. 参数(3个字段)
参数索引类型内容/解析作用
1Null (0x05)占位符RTMP 事件格式要求,无实际意义。
2Object (0x03)包含 codedescription 两个键值对具体事件内容,反馈推流状态:
- codeString 'NetStream.Publish.Start'(长度 22事件码:推流成功启动(核心状态标识)。
- descriptionString 'Started publishing stream.'(长度 27描述信息:补充状态细节(如“开始推流”)。

协议逻辑:“异步事件通知”的设计

  1. _result 的区别

    • _result同步响应(客户端发命令→服务器回结果,一一对应)。
    • onFCublish异步事件(服务器主动推送状态变化,无需客户端请求)。
      → 前者是“请求-响应”,后者是“状态订阅”,更高效实时。
  2. 事件的触发条件
    当客户端 publish 命令成功执行(流创建、权限验证通过),服务器 主动触发 onFCublish 事件,告知客户端“推流已启动”。

  3. 对客户端的要求
    客户端需 预先注册事件监听器(如 onFCublishonStatus),才能接收并处理此类异步通知。

上下文关联:推流流程的“关键信号”

  • 前序动作:客户端发送 publish('livestream', 'live') 命令,请求推流。
  • 后续动作:
    • 客户端收到 onFCublish 后,立即开始 发送音视频数据(视频帧、音频帧,通过 Chunk Stream ID=8 传输);
    • 服务器同时开始 转发数据给观众(或存储,依发布类型 live/record 决定)。

总结:推流的“实时状态灯”

这个报文是 RTMP 异步事件驱动设计 的体现:

  • 通过 onFCublish 主动通知推流启动,无需客户端轮询,提升响应速度;
  • 事件体中的 codedescription 明确状态(成功启动),指导客户端下一步操作(发媒体数据);
  • 标志 从“命令交互”到“数据传输”的无缝衔接,是直播流程中“推流正式开始”的核心信号。

若事件码为 NetStream.Publish.Failed,则需排查权限、流冲突等问题。

3.13 Audio Data 报文在这里插入图片描述

RTMP 头部(RTMP Header)解析

字段值/解析作用
Format2增量 Chunk 头:复用前一个音频 Chunk 的头部参数(如 Chunk Stream IDType ID),仅更新 时间戳增量体部长度,大幅减少头部开销。
Chunk Stream ID4音频专属流ID(RTMP 多路复用设计,音频、视频、数据通常用不同 Chunk Stream ID,如音频=4,视频=5,避免相互阻塞)。
Timestamp delta23相对于前一个音频 Chunk 的时间戳增量(毫秒),累计时间戳 141ms(通过 Timestamp: 141 (calculated) 体现)。
Body size隐含在体部(对应音频帧长度)音频帧的实际字节数(如示例中体部数据长度)。
Type ID隐含(因 Format=2,复用前序 Type ID,通常为 0x08 表示 Audio Message)消息类型:音频数据(无需显式传输,通过 Chunk Stream ID 和上下文推断)。

RTMP 体部(Audio Data)解析

1. 音频格式标识(Control 字段)
  • 0xaf(二进制 10101111
  • 解析
    • 高 4 位(1010:表示 AAC 编码(RTMP 音频格式中,0x0A 对应 AAC)。
    • 低 4 位(1111:表示 原始 AAC 数据帧(而非 AAC 序列头,序列头通常在推流起始阶段发送,标识编码参数如采样率、声道数)。
2. 音频数据内容
  • 示例数据01211004608c1c...
  • 本质HE-AAC 编码的音频帧(结合 0xaf 和实际推流场景,推测为高效的 HE-AAC 格式,适应低带宽直播)。
  • 时间关联:时间戳增量 23ms 符合 AAC 帧时长特性(44.1kHz 采样率下,一帧 1024 采样点,时长约 23.2ms,与 23ms 接近,验证为一帧完整音频数据)。

协议逻辑:音频流的“高效传输”

  1. 阶段定位
    发生在 publish 命令成功 + onFCublish 事件接收后,属于 实际音视频数据传输阶段(推流的核心环节)。

  2. RTMP 多路复用的体现

    • 音频(Chunk Stream ID=4)、视频(假设 ID=5)、控制命令(ID=3)并行传输,互不干扰。
    • Format=2 的增量 Chunk 头,让连续音频帧的头部开销降到最低(仅需传输时间戳增量和数据,无需重复发送 Type、Stream ID 等固定参数)。
  3. 与 TCP 的协作

    • TCP 的 PSH + ACK 保证音频帧 及时交付(PSH 让数据立即入队,ACK 保证不丢包)。
    • RTMP 的 Chunk 拆分机制,适配 TCP MSS(避免 IP 分片,提升传输效率)。

关键细节:音频编码与流控

  • HE-AAC 选择的原因
    高效压缩,适合低带宽直播场景(如移动网络),牺牲少量音质换取更高码率适配性。
  • 序列头的隐含前提
    推流起始阶段必然发送过 AAC 序列头(Type ID=0x00,携带 Audio Specific Config),包含采样率、声道数、profile 等参数,后续帧(Type ID=0x08)无需重复发送。

总结:直播音频的“载体帧”

这个报文是 RTMP 推流中音频数据的最小传输单元

  • 通过 增量 Chunk 头 优化传输效率,适合连续音频流;
  • 采用 HE-AAC 编码 适配低带宽,时间戳增量验证帧完整性;
  • 标志推流进入 “实际数据传输” 的稳定阶段,是观众听到声音的直接来源。

若音频帧丢失或乱序,会导致直播声音卡顿、失真,体现 RTMP 与 TCP 协同保障媒体流稳定的重要性。

3.14 Video Data 报文

在这里插入图片描述

RTMP 头部(RTMP Header)解析

字段值/解析作用
Format1增量 Chunk 头:复用前一视频 Chunk 的固定参数(如 Type IDStream ID),仅更新 时间戳增量体部长度,大幅降低头部开销(连续视频帧的高效传输关键)。
Chunk Stream ID6视频专属流ID(RTMP 多路复用设计,与音频流、控制流分离,避免相互阻塞,确保视频帧独立传输)。
Timestamp delta20相对于前一个视频 Chunk 的时间戳增量(毫秒),累计时间戳 120ms(通过 Timestamp: 120 (calculated) 体现,保证视频帧时序正确)。
Body size242视频帧的实际字节数(H.264 编码的单帧数据大小)。
Type ID0x09(隐含,因 Format=1 复用前序 Type ID,显式标识为 Video Data)消息类型:视频数据(推流阶段的核心负载)。

RTMP 体部(Video Data)解析

1. 视频控制字段(Control Byte)
  • 0x27(二进制 00100111
  • 分解解析
    • 高 4 位(0010:表示 帧类型为 Inter-frame(P 帧)(RTMP 视频类型定义:0x1=关键帧/I 帧,0x2=预测帧/P 帧,此处 0010 对应 P 帧,依赖前一帧解码,压缩比高)。
    • 低 4 位(0111:表示 编码格式为 H.264(RTMP 编码类型定义:0x07 对应 H.264,是直播场景的主流编码)。
2. H.264 视频数据内容
  • 示例数据010000104000000e9419f5145152c7f00000030000003000000300000030000003...
  • 本质H.264 的 NAL 单元(Network Abstraction Layer),属于 P 帧数据(帧间压缩,依赖前一帧重构画面)。
  • 依赖前提:推流起始阶段必然发送过 H.264 序列头(SPS/PPS)(Video Data 类型为 0x17,携带编码参数如分辨率、帧率、码率,后续 P 帧无需重复发送配置)。

协议逻辑:视频流的“高效传输策略”

  1. 阶段定位
    发生在 publish 命令成功 + onFCublish 事件接收后,属于 实际音视频数据持续传输阶段(推流的核心业务环节)。

  2. RTMP 多路复用的极致体现

    • 视频(Chunk Stream ID=6)、音频(如 ID=4)、控制命令(ID=3)并行独立传输,即使某一流阻塞(如网络抖动),不影响其他流(如控制命令仍可交互)。
    • Format=1 的增量 Chunk 头:连续视频帧仅需传输 时间戳增量数据,头部开销从完整头的 ~12 字节降至 ~4 字节,提升传输效率 60%+
  3. 与 H.264 编码的协同

    • I 帧(关键帧):间隔发送(如每 2 秒),携带完整画面信息,确保解码起点。
    • P 帧(预测帧):连续发送,仅记录与前帧的差异,大幅降低带宽消耗(适合直播低延迟、高帧率场景)。

关键细节:视频传输的“稳定性保障”

  • 时间戳增量的意义20ms 对应 50fps 帧率(1000ms/20ms=50),符合直播常见帧率(如 25/30/50fps),保证画面流畅性。
  • TCP 与 RTMP 的适配
    • 视频帧大小 242 字节 < TCP MSS(通常 1460 字节),避免 IP 分片,降低网络层处理开销
    • RTMP Chunk 拆分机制自动适配 TCP 传输,无需应用层手动分片。

总结:直播视频的“最小传输单元”

这个报文是 RTMP 推流中视频数据的核心载体

  • 通过 增量 Chunk 头 实现高效传输,适配连续视频流的高频率发送;
  • 采用 H.264 编码的 P 帧,平衡带宽与画质,支撑实时直播;
  • 依托 多路复用 设计,确保视频流与其他流(音频、控制)并行稳定传输。

若视频帧丢失或时间戳紊乱,会导致直播画面卡顿、花屏,体现 RTMP 与 TCP 协同保障媒体流时序和完整性的关键作用。

3.15 FCUnpublish、deleteStream报文

在这里插入图片描述

报文 1:FCUnpublish('livestream')(取消文件式发布)

1. RTMP 头部
字段值/解析作用
Format1增量 Chunk 头(复用命令流 Chunk Stream ID=3 的前序参数,仅更新事务ID)。
Chunk Stream ID3命令流(与 createStreampublish 等命令复用同一逻辑流,保证会话关联)。
Timestamp delta0命令连续发送,时间戳增量为 0(相对时间无变化)。
Body size37消息体长度(AMF0 编码的命令参数)。
Type ID0x14(AMF0 Command)命令类型:告知服务器执行“取消文件式发布”操作。
2. RTMP 消息体(AMF0 编码)
String 'FCUnpublish'  // 命令名:取消之前的 FCublish 操作
Number 7              // 事务ID:唯一标识此命令(与响应 `_result` 关联)
Null                  // 占位参数
String 'livestream'   // 目标流名:与之前 publish 的流名一致

作用:告知服务器 停止对 livestream 的文件式发布(如停止录制或存储),释放相关资源。

报文 2:deleteStream(1)(删除流实例)

1. RTMP 头部
字段值/解析作用
Format1增量 Chunk 头(复用命令流 Chunk Stream ID=3 的参数,连续命令优化)。
Chunk Stream ID3命令流(延续会话,保证操作连贯性)。
Timestamp delta0命令连续发送,时间戳增量为 0。
Body size34消息体长度(AMF0 编码的命令参数)。
Type ID0x14(AMF0 Command)命令类型:告知服务器执行“删除流实例”操作。
2. RTMP 消息体(AMF0 编码)
String 'deleteStream'  // 命令名:删除流实例
Number 8               // 事务ID:唯一标识此命令(与响应 `_result` 关联)
Null                   // 占位参数
Number 1               // 流ID:对应之前 `createStream` 响应的流ID(如 1)

作用:请求服务器 删除 ID=1 的流实例(推流阶段创建的媒体流),彻底清理资源(避免残留流占用内存)。

协议逻辑:流生命周期的“收尾动作”

  1. 阶段定位
    发生在 推流结束后(如直播停止、客户端断开前),属于 流销毁阶段,与“创建→发布→销毁”的生命周期闭环对应。

  2. 操作顺序的必要性

    • FCUnpublish:停止文件式发布的副作用(如录制),确保数据不再写入存储。
    • deleteStream:删除流实例,释放服务器内存、端口等资源。
      → 若顺序颠倒,可能导致发布状态残留或流资源无法彻底释放。
  3. 与服务器响应的关联
    服务器会针对每个命令返回 _result 响应(事务ID=7 和 8),告知操作是否成功:

    • 成功:资源彻底释放;
    • 失败:需排查(如流已被删除、权限不足)。

关键设计细节

  • 流ID的强关联deleteStream 的流ID(1)必须是 createStream 响应中分配的ID,确保精准删除目标流。
  • 命令流的复用Chunk Stream ID=3 贯穿所有应用层命令(create、publish、unpublish、delete),体现 RTMP “逻辑流复用” 的高效设计。

总结:流资源的“优雅回收”

这两个报文是 RTMP 推流的“收尾关键”

  • 通过 FCUnpublish 终止发布行为,切断数据写入;
  • 通过 deleteStream 销毁流实例,释放服务器资源;
  • 形成 “创建→发布→销毁” 的完整生命周期,避免资源泄漏,保障服务器长期稳定运行。

若缺少这些步骤,服务器可能残留无效流,导致资源耗尽或新流冲突,体现 RTMP 协议对“资源管理”的严谨性。

3.16 onFCUnpublish 报文

在这里插入图片描述

RTMP 头部(RTMP Header)解析

字段值/解析作用
Format0完整 Chunk 头(首次发送此事件,携带所有核心字段,确保客户端完整解析)。
Chunk Stream ID5服务器事件专属流ID(与 onFCublish 同属事件流,区分命令流、媒体流)。
Timestamp0事件类消息无实际时间戳(聚焦状态通知,置初始值)。
Body size105消息体长度(AMF0 编码的事件参数总大小)。
Type ID0x14(十进制 20消息类型:AMF0 Command(服务器主动发起的事件通知,本质是应用层命令)。
Stream ID1关联到 之前创建的媒体流ID(如 createStream 响应的流ID=1),明确事件所属流。

RTMP 消息体(AMF0 编码)解析

“事件名 + 事务ID + 参数” 结构拆解:

1. 事件名:onFCUnpublish
  • AMF0 类型String (0x02),长度 13
  • 作用:告知客户端:“触发了‘取消文件式发布’的状态变化事件”(对应客户端的 FCUnpublish 命令)。
2. 事务ID:0
  • AMF0 类型Number (0x00),值 0
  • 作用:因是 服务器主动发起的异步事件(非客户端请求的响应),无对应事务ID,故置 0(区别于 _result 的事务ID关联)。
3. 参数(3个字段)
参数索引类型内容/解析作用
1Null (0x05)占位符RTMP 事件格式要求,无实际意义。
2Object (0x03)包含 codedescription 两个键值对具体事件内容,反馈“取消发布”的结果:
- codeString 'NetStream.Unpublish.Success'(长度 26事件码:取消发布成功(核心状态标识,表明服务器已停止文件式发布逻辑)。
- descriptionString 'Stop publishing stream.'(长度 20描述信息:补充状态细节(如“已停止推流发布”)。

协议逻辑:“取消发布”的闭环确认

  1. 与客户端命令的关联
    此事件对应客户端在 Frame 14050 发送的 FCUnpublish('livestream') 命令(通过流ID和事件名关联)。

  2. 核心作用
    服务器告知客户端:“你请求取消的文件式发布操作已成功执行”,包括:

    • 停止对 livestream 的录制/存储(若之前是 record 类型);
    • 释放发布相关的临时资源(如缓冲区、编码器上下文)。
  3. 对后续流程的影响
    客户端收到此事件后,才会发起 deleteStream 命令(删除流实例),确保:

    • 先“停止发布行为”,再“销毁流载体”,避免资源泄漏或状态紊乱。

RTMP 异步事件的设计价值

  • 实时性:服务器主动推送状态,无需客户端轮询,降低延迟。
  • 解耦性:命令(FCUnpublish)和事件(onFCUnpublish)分离,客户端只需监听事件即可响应状态变化,代码更简洁。
  • 幂等性保障:事件码 NetStream.Unpublish.Success 确保操作仅成功一次,避免重复取消导致的异常。

总结:流销毁的“第一步确认”

这个报文是 RTMP 流生命周期管理的关键环节

  • 标志 “发布状态”的干净清除(停止录制、释放临时资源);
  • 通过异步事件机制,实现 命令-反馈的高效协同
  • 为后续 deleteStream 命令(销毁流实例) 奠定基础,保障服务器资源彻底回收。

若事件码为 NetStream.Unpublish.Failed,需排查流是否已被删除、权限问题等,确保流销毁流程的完整性。

四、TCP 关闭

这里是强制关闭的,因此没有TCP的四次挥手
在这里插入图片描述

这是 客户端主动发送的 TCP 连接复位报文(RST + ACK,用于 强制终止 RTMP 会话的底层 TCP 连接,标志 整个推流流程的最终收尾

TCP 头部核心字段解析

字段值/解析作用
源端口/目的端口61319 → 1935延续 RTMP 会话(RTMP 默认端口 1935,客户端临时端口 61319)。
标志位(Flags)0x014RST + ACK- ACK:确认服务器之前的报文(保证 TCP 可靠性,回应最后一个 ACK);
- RST强制复位连接(无需协商,立即关闭,丢弃缓冲区数据)。
序列号(Seq)7897823(相对值)客户端发送的当前数据序号(延续会话的序列上下文)。
确认号(Ack)4507(相对值)确认服务器发送的最后一个数据(Ack = 服务器最后 Seq + 1,保证数据接收完整)。

协议逻辑:“强制断开”的场景

  1. RTMP 会话的收尾阶段
    前序报文已完成 FCUnpublish(取消发布)→ onFCUnpublish(确认取消) 流程,流资源已逻辑释放,此时需要 物理断开 TCP 连接,彻底释放网络资源。

  2. RST 的触发原因

    • 正常收尾:客户端推流结束,选择 快速断开(相比 FIN 优雅关闭,RST 更高效,适合流媒体这种“一次性会话”场景)。
    • 异常保护:若客户端检测到连接异常(如服务器无响应、本地资源不足),也会发 RST 强制断开,避免资源泄漏。
  3. FIN 的区别

    • FIN:优雅关闭(双方协商,逐步释放资源,确保数据完整);
    • RST:强制关闭(无需协商,立即终止,可能丢弃未发送数据)。
      → 流媒体场景中,因数据多为实时性(丢失末尾数据影响小),常用 RST 快速断开。

RTMP 上下文的关联意义

  • 前序流程完整:从 connect 连接 → publish 推流 → FCUnpublish 取消 → onFCUnpublish 确认,已完成 “连接→推流→销毁→确认” 的完整生命周期。
  • TCP 是 RTMP 的载体:RTMP 依赖 TCP 传输,RST 断开 TCP 后,RTMP 会话彻底终止,服务器会回收所有关联资源(流、端口、缓冲区等)。

总结:“会话的最终终止符”

这个报文是 RTMP 推流会话的“物理断开信号”

  • 通过 RST + ACK 强制终止 TCP 连接,快速释放网络资源;
  • 标志整个 “连接→推流→销毁” 流程的彻底结束,客户端和服务器不再维持会话状态;
  • 体现 TCP 协议的 “健壮性”(允许强制断开应对异常)和 RTMP 应用的“高效性”(选择快速断开适配流媒体场景)。

若在推流过程中发送 RST,则可能是异常中断;此处因前序资源释放完整,属于 正常收尾的高效实现

更多资料:https://github.com/0voice

http://www.lryc.cn/news/575017.html

相关文章:

  • 【大厂机试题解法笔记】分解连续正整数组合/ 分解正整数
  • FPGA笔记——ZYNQ-7020运行PS端的USB 2.0端口作为硬盘
  • 卡萨帝发布AI深度科技:实现从守护生活到守护文明的升级
  • DuDuTalk | 武汉赛思云科技有限公司通过武汉市人工智能企业认定!
  • Nginx + Tomcat 负载均衡搭建
  • Postman中设置定时自动运行接口测试
  • 基于开源AI大模型、AI智能名片与S2B2C商城小程序的美食菜单社交化营销创新研究
  • uniapp vue3版本的一些小细节!
  • Kubernetes、Docker Swarm 与 Nomad 容器编排方案深度对比与选型指导
  • 分页器封装
  • mongodb 新手入门,原理,优化,详细介绍 附上代码
  • github 上的php项目
  • 【LLaMA-Factory 实战系列】四、API 篇 - 部署推理服务与批量调用实战
  • Vue 3 响应式核心:深入理解 ref 与 reactive 的选择之道
  • Java中的synchronized和锁
  • 在NPU平台上,如何尝试跑通Ktransformers + DeepSeek R1?
  • 基于LangChat搭建RAG与Function Call结合的聊天机器人方案
  • 前端使用rtsp视频流接入海康威视摄像头
  • QT 学习笔记摘要(三)
  • HCIA-IP路由基础
  • C++ 多线程深度解析:掌握并行编程的艺术与实践
  • 基于CMS的黄道吉日万年历源码(自适应)
  • 商务年度总结汇报PPT模版分享
  • 板凳-------Mysql cookbook学习 (十--10)
  • LeetCode 3258.统计满足K约束的子字符串数量1
  • HTML表单元素
  • 线性结构之链表
  • 深度学习实战112-基于大模型Qwen+RAG+推荐算法的作业互评管理系统设计与实现
  • 机器学习01
  • SpringBoot高校党务系统