news 2026/4/18 6:00:15

RTMP协议

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
RTMP协议

目录

1、基本概念

2、RTMP创建流

2.1、RTMP创建流的基本流程

2.2、RTMP握手

2.3、建立RTMP连接(NetConnection)

2.4、创建RTMP流(NetStream)

2.5、RTMP推流

2.6、RTMP拉流

2.7、传输音视频数据

2.8、连接断开

2.9、RTMP处理流程

3、RTMP消息

3.1、消息(Message)—— 逻辑上的数据单元

3.2、块(Chunk)—— 网络传输的最小单元

3.3、RTMP消息格式(Chunk格式)

3.4、块流(Chunk Stream)—— 多路复用的核心

3.5、RTMP消息类型


1、基本概念

RTMP底层是基于TCP的

定位与角色:RTMP是应用层协议,主要用于直播场景中从推流端(如OBS)到服务器(如SRS/Nginx)的传输,以及服务器间分发。

核心特点:基于TCP,提供低延迟的实时传输,默认使用1935端口。优势是生态成熟,缺点是部分移动端和浏览器(Flash已淘汰)原生支持不佳。

与其他协议的对比:

  • RTMP vs RTSP:RTMP更适合“推流到服务器”进行大规模分发(如直播),RTSP更多用于点对点或IP摄像头的控制与流媒体播放(如安防监控)。

  • RTMP vs HLS:RTMP延迟更低(几秒甚至亚秒级),HLS基于HTTP切片,延迟较高(10秒以上),但兼容性极好(Web/移动端)。

2、RTMP创建流

2.1、RTMP创建流的基本流程
  • socket建立TCP连接

  • RTMP握手

  • 建立RTMP连接(NetConnection)

  • 创建RTMP流(NetStream)

2.2、RTMP握手

s0、s1、s2通常作为一个包发送

握手由三个固定大小的块组成,过程非常简单直接:

  1. C0 + C1: 客户端主动向服务器发送一个数据包,其中C0是一个字节,标识RTMP版本(目前为3),C1是1536字节的随机数据。

  2. S0 + S1 + S2: 服务器收到后,会回复S0(确认版本)和S1(服务器端的随机数据)。紧接着,服务器会基于收到的C1计算出S2,并立即发送给客户端。

  3. C2: 客户端收到S1后,计算出C2并发送给服务器,完成最后的确认。

目的:

1、协商协议版本:通过C0和S0交换版本号,确保双方能互相理解

2、验证连接的有效性:通过来回“打乒乓”的方式,确保对方是真实存在的RTMP端点,而不是无效或恶意的连接。

2.3、建立RTMP连接(NetConnection)

握手完成后,客户端需要建立一个“网络连接”(NetConnection)。可以把它理解为客户端和服务器之间的一个控制通道。这是一个上行链路,所有后续的命令都会通过这个通道发送

  1. 客户端发送connect命令:这是请求建立网络连接(NetConnection)的开始。

  2. 服务器回复协议控制消息:服务器收到connect后,会先发送几个RTMP协议层面的控制消息,作为对连接建立的底层确认。这些消息包括:

    • 确认窗口大小 (Window Acknowledgement Size)—— 服务端的接收窗口大小

    • 设置带宽 (Set Peer Bandwidth)—— 限制客户端发送速率上限

  3. 客户端回复协议控制消息:作为对上一步的回应,客户端会再发送一个确认窗口大小 (Window Acknowledgement Size)消息。—— 客户端的接收窗口大小

  4. 服务器发送_result命令:服务器处理完connect命令后,会发送_result命令(与是否收到客户端的回复协议控制消息无关)。这个命令标志着connect请求在应用层被成功处理,网络连接(NetConnection)正式建立完成。如果connect处理失败,则会发送_error命令

2.4、创建RTMP流(NetStream)

NetStream 代表了真正传输多媒体数据的逻辑通道。所有的音频、视频和元数据都是通过这个通道发送的。创建它的方式,推流和拉流有所不同。

通用步骤:创建 NetStream

无论推流还是拉流,都需要先创建这个流通道。

  1. 客户端发送createStream命令:客户端通过之前建立的控制通道(NetConnection)向服务器发送createStream命令,请求创建一个新的流。

  2. 服务器回复_result:服务器处理请求,如果成功,会在_result命令中携带一个Stream ID返回给客户端。这个ID非常重要,它标识了即将进行音视频传输的通道,后续所有的数据包都会使用这个ID进行封装。

推流端的特殊步骤:releaseStream在发送createStream之前,推流端(publisher)通常会先发送一个releaseStream命令。这个名字很直白——“释放流”。目的是确保要发布的流名没有被之前的会话占用,避免冲突,起到一个“重置/清理”的作用。

2.5、RTMP推流

推流端 (Publisher) 的流程

  1. 发送publish命令:推流端发送publish命令,告诉服务器:“我要开始推流了!” 这个命令会携带流的名称(如mystream)和流的类型(live表示实时直播,record表示录制)。

  2. 服务器回复onStatus:服务器收到publish命令后,会开始准备接收数据。准备就绪后,它会发送一个onStatus命令给推流端,其中的code字段为NetStream.Publish.Start,这相当于一个“开始发布”的许可。

  3. 发送元数据 (onMetaData):推流端在正式推送音视频数据前,会先发送一个包含onMetaDatasetDataFrame命令。这个数据包非常重要,它包含了视频的宽高、编码格式(H.264)、音频的采样率、频道数等描述信息。播放器需要先收到这些信息才能正确解码后续的音视频数据。

2.6、RTMP拉流

  1. 发送play命令:拉流端发送play命令,并附上想观看的流名称(如mystream),请求服务器开始发送该流的数据。

  2. 服务器回复onStatus:服务器找到对应的流后,会发送多个onStatus命令来通知播放器状态变化,例如NetStream.Play.Reset(重置播放)和NetStream.Play.Start(开始播放)。

  3. 接收元数据 (onMetaData):紧接着,服务器会将推流端发来的那个包含onMetaDatasetDataFrame命令原样转发给播放器,告知其音视频的基本属性。

2.7、传输音视频数据

当所有的握手、连接、创建流和发布/播放的命令都成功完成后,就进入了最核心的数据传输阶段。

客户端(推流端)开始将经过编码和封装的音频、视频数据,按照FLV(Flash Video)的格式打包成RTMP消息块(Chunk),源源不断地发送给服务器。服务器收到后,会将这些数据块重新组装成消息,并转发给所有正在 play 这个流的客户端(拉流端)。

例如,一个H.264视频帧会被封装成FLV Tag,其中包含帧类型(关键帧/普通帧)、编码器ID(7 for H.264)和实际的数据(NALU)。AAC音频也是类似的流程。

通过这样一系列严谨的指令交互,RTMP协议实现了可靠、高效的直播推拉流服务。

2.8、连接断开

1、断开 NetStream

客户端发送closeStream命令,告诉服务器要关闭当前的流通道。

服务器回复onStatus命令,确认流已关闭。对于推流端,onStatus中的codeNetStream.Unpublish.Success;对于拉流端,服务器可能不发送onStatus或发送NetStream.Play.Stop

2、断开 NetConnection

客户端发送 close 命令,请求关闭网络连接。

服务器回复 _result 命令,确认连接已关闭。至此,RTMP 应用层的连接完全断开。

3、TCP 四次挥手

客户端发起 TCP 四次挥手,彻底关闭底层的 TCP 连接。

两种断开方式的差异

客户端主动断开时,流程如上图所示:先发closeStream,再发close,收到_result后开始 TCP 挥手。

服务器主动断开时,服务器直接发送onStatus命令(NetConnection.Connect.Closed),然后服务器发起 TCP 挥手,客户端不需要发送closeStreamclose命令。

推流端与拉流端断开 NetStream 的差异

推流端发送closeStream后,服务器会回复onStatusNetStream.Unpublish.Success),明确告知推流端服务器已停止接收数据。

拉流端发送closeStream后,服务器通常不回复onStatus,直接停止发送数据即可,因为拉流端主动断开不需要服务器确认。

2.9、RTMP处理流程
  • 完整通信流程

    • 建立TCP连接

    • RTMP握手

    • 创建RTMP连接(NetConnection)

    • 创建流(NetStream)

    • 推流/拉流

    • 断开链接

3、RTMP消息

3.1、消息(Message)—— 逻辑上的数据单元

消息是RTMP中应用层看到的基本数据单元。无论是音频数据、视频数据,还是控制命令,在逻辑上都被封装成消息。

一个消息包含以下字段:消息类型ID、负载长度、时间戳、消息流ID、消息体。

3.2、块(Chunk)—— 网络传输的最小单元

消息在真正发送时,会被分割成更小的块。为什么?设想一下:如果一个大的视频帧(比如200KB)直接发送,而它后面紧跟着一个音频帧(只有几十字节),在TCP流式传输中,音频数据必须等视频帧发完才能发,这就会造成音频延迟或卡顿。

分块解决了这个问题:把大消息切成小块,音频小块可以穿插在视频小块之间发送。

默认块大小是128字节,可以通过协议控制消息动态调整。

分块过程示意

假设有一个307字节的视频消息,块大小为128字节,分块过程如下:

原始消息(307字节) ┌────────────────────────────────────────────────┐ │ 307字节 │ └────────────────────────────────────────────────┘ ↓ 分块 块1(128字节) 块2(128字节) 块3(51字节) ┌─────────────┐ ┌─────────────┐ ┌──────────┐ │ 块头 + 128 │ │ 块头 + 128 │ │ 块头+51 │ └─────────────┘ └─────────────┘ └──────────┘

每个块在网络上独立传输,接收端根据块头中的信息将同一消息的多个块重新组装。

3.3、RTMP消息格式(Chunk格式)

RTMP Header由三部分组成:基本头信息(Basic Header)、消息头信息(Messgae Header)、扩展时间戳(Extended Timestamp)。其中后两个为可选。

3.1.1、基本头信息(Basic Header):

Basic Header长度可变,由第一个字节的fmt(2位) 和CSID(6位)共同决定

  • CSID编码规则:

    • 当CSID=2-63时:Basic Header占1字节

    • 当CSID=0时:Basic Header占2字节,实际CSID=64+第二个字节值

    • 当CSID=1时:Basic Header占4字节,实际CSID=64+256+后3字节值

3.1.2、消息头信息(Messgae Header):

字段长度说明
时间戳3字节消息的时间戳,用于音视频同步(分割前是4字节,分割后是3字节)
负载长度3字节消息体的字节数
消息类型ID1字节标识消息类型(8=音频,9=视频,20=AMF0命令等)
消息流ID4字节标识所属的消息流
  • Message Header长度由 fmt 值决定:

    • fmt=00b(0):完整11字节(时间戳3B+消息长度3B+类型ID 1B+流ID 4B)

    • fmt=01b(1):7字节(省略流ID)

    • fmt=10b(2):3字节(仅保留时间戳)

    • fmt=11b(3):0字节(完全省略)

  • 省略逻辑:基于数据分块传输时的冗余信息消除:

    • 同一流可省略流ID

    • 同一消息可省略类型ID

    • 固定长度可省略消息长度

    • 时间戳相同可省略时间戳

3.1.3、扩展时间戳(Extended Timestamp):

当且仅当 Message Header 中的时间戳值为0xFFFFFF时启用

作用:当3字节时间戳不足以表示实际时间值时提供扩展支持

3.4、块流(Chunk Stream)—— 多路复用的核心

3.4.1、什么是块流?

块流是一个虚拟的逻辑通道,用块流ID(Chunk Stream ID)标识。多个块流可以复用在同一条TCP连接上。

3.4.2、为什么要多路复用?

在实际直播中,需要同时传输:

  • 视频数据(量大、优先级相对低)

  • 音频数据(量小、优先级高)

  • 控制消息(量极小、优先级最高)

通过将它们放在不同的块流中,可以在网络拥塞时优先发送音频和控制消息的块,确保声音不卡顿、命令及时响应。

3.4.3、块流与消息流的关系

  • 消息流ID:标识逻辑上的消息流(由应用层定义)

  • 块流ID:标识逻辑传输通道

多个不同消息流的块可以复用到同一个块流中,也可以一个消息流独占一个块流。接收端通过块流ID找到对应的块流,再通过消息流ID区分不同的消息,最后将同一个消息的多个块按顺序组装还原。

块流和消息流都是逻辑通道,它们都是在同一条TCP物理连接之上划分的、不同粒度的逻辑抽象。

3.4.4、完整数据流示意

发送端: 消息1(视频,200KB)──→ 分块 ──→ 块1.1, 块1.2, 块1.3... 消息2(音频,2KB) ──→ 分块 ──→ 块2.1 消息3(控制,小) ──→ 分块 ──→ 块3.1 ​ ↓ 复用(交错发送) TCP发送队列:[块2.1] [块3.1] [块1.1] [块1.2] [块2.2] [块1.3]... (音频和控制消息优先) ​ 接收端: 按块流ID分类 → 按消息流ID组装 → 还原原始消息
3.5、RTMP消息类型

Message Header 中的 TypeID 字段,1字节

协议控制消息:1~3,5~6。负责管理数据传输的底层机制,确保传输高效可靠。

用户控制消息:4。如果说协议控制消息是管"管道"的,那用户控制消息就是管"管道里的水"的。它主要用于通知对方关于流(Stream)的状态变化,比如流创建、结束、缓冲区长度等。

数据消息:8,9。传输的音视频数据。

命令消息:15~20。交互命令。比如publish、connect、play等。

聚合消息:22。将多个小消息打包成一个,减少头部开销,提高传输效率

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/18 5:57:13

可持续编码实践:ESG开发标准

在数字化浪潮席卷全球的今天,软件不仅是驱动经济增长的引擎,其本身也已成为一个不容忽视的环境影响源。信息通信技术领域的高能耗问题日益凸显,将可持续性融入软件开发全生命周期,已成为行业不可回避的责任。对于软件测试从业者而…

作者头像 李华
网站建设 2026/4/18 5:54:12

快速部署通义千问1.5-1.8B-Chat模型:vllm部署与chainlit前端配置

快速部署通义千问1.5-1.8B-Chat模型:vllm部署与chainlit前端配置 想快速体验一个能流畅对话、还能帮你处理文本任务的本地AI助手吗?今天要介绍的通义千问1.5-1.8B-Chat-GPTQ-Int4模型,就是一个绝佳的选择。它体积小巧,经过量化优…

作者头像 李华
网站建设 2026/4/18 5:49:14

C语言这么牛,它自身又是用什么语言写的?真相很硬核

你有没有想过一个问题:世界上第一个C语言编译器,它是用什么语言写的?要解开这个谜团,我们得回到计算机的起点 CPU真正能读懂的,只有由0和1组成的机器语言。这是所有故事的基石。 那么,第一步是怎么走的呢&a…

作者头像 李华
网站建设 2026/4/18 5:49:14

【好靶场】你知道unionId吗

基础知识微信开放平台是一个公司的总账号,AppID 是旗下每个应用的唯一标识,UnionID 则是用户在该公司所有应用里的统一身份,用于跨应用识别同一用户。这样微信用户在同一家公司下面的应用(公众号、小程序等)下&#xf…

作者头像 李华
网站建设 2026/4/18 5:44:14

从理论到实测:全国电赛D题电路特性测试仪之输出阻抗、增益与上限频率实战解析

1. 输出阻抗测量:从理论到实战的关键细节 输出阻抗是电子电路设计中一个看似简单却暗藏玄机的参数。在实际比赛中,我们团队最初对输出阻抗的理解停留在课本定义上,直到动手测量才发现理论到实践的鸿沟。输出阻抗本质上反映了电路带负载能力的…

作者头像 李华
网站建设 2026/4/18 5:41:17

18个超实用文献检索网站,找论文再也不费劲(国内外全都有)

之前有跟大家分享过6个好用的科研绘图网站,有需要的小伙伴可以收藏备用。那么今天up给大家分享国内外常用文献检索的实用网站,都是我亲测过的。 如果碰到付费的文献需要下载,大家可以试试我之前提到过的Book学术,输入文献名,就能一键帮你抓取文献并免费下载,链接在这里:…

作者头像 李华