一文详解RTMP协议

发布于:2025-05-24 ⋅ 阅读:(15) ⋅ 点赞:(0)

前言:


1 RTMP基础知识

1.1 RTMP总体解释

**RTMP(实时消息传输协议)作为应用层协议,依赖底层可靠的传输层协议(通常为 TCP)**保障信息传输可靠性。在完成传输层连接建立后,客户端与服务器需通过握手机制建立基于传输层连接的RTMP Connection 连接,该连接负责传输控制信息,例如:

  • SetChunkSize:设置数据块大小,优化数据传输效率;
  • SetACKWindowSize:配置确认窗口大小,控制流量并确保数据有序接收。

通过CreateStream 命令可在 Connection 连接上创建Stream 流连接,专门用于传输具体的音视频数据及控制数据传输的命令信息。

RTMP 的数据传输机制
RTMP 协议对传输数据进行自定义格式化,生成RTMP Message(消息)。为实现多路复用、分包处理及传输公平性,**发送端会将 Message 划分为带有Message ID的Chunk(数据块)。每个 Chunk 可能是一条完整的 Message,也可能是 Message 的一部分。**接收端通过 Chunk 中携带的数据长度、Message ID和Message 总长度,将碎片化的 Chunk 还原为完整的 Message,从而完成信息的收发。

关键流程总结

  • 传输层连接:基于 TCP 建立可靠通信链路;
  • RTMP 握手:初始化 Connection 连接,协商协议参数;
  • 流连接创建:通过 CreateStream 命令建立 Stream,承载音视频数据;
  • 数据分块与重组:Message 拆分为 Chunk 传输,接收端按规则还原完整消息。

1.2 RTMP的框架

在建立有效的 RTMP Connection 连接时,“握手” 是关键的起始步骤。客户端需按顺序向服务器发送 C0、C1、C2 三个 chunk,与此同时,服务器也需按序向客户端发送 S0、S1、S2 三个 chunk ,完成这些操作后,才能顺利开展有效的信息传输。

虽然 RTMP 协议未对这 6 个 Message 的传输顺序做明确规定,但对于 RTMP 协议的实现者而言,必须遵循以下规则:

  • 客户端规则:仅当接收到服务器发来的 S1 后,才可以发送 C2 ;并且只有在收到 S2 之后,才能发送诸如控制信息、真实音视频等其他数据。
  • 服务器规则:只有在接收到客户端发送的 C0 后,才能够发送 S1 ;必须在收到 C1 之后,才可以发送 S2 ;且只有在接收到 C2 后,才能发送控制信息、真实音视频等其他数据。
    在这里插入图片描述

2 RTMP的消息组成

在这里插入图片描述

2.1. 消息(Message)

咱们这里说的 Message,就是符合 RTMP 协议格式的消息,它能被拆分成 Chunk 来发送。Message 有这么几个重要部分:

  • 时间戳(Timestamp):占 4 个字节。它是消息的时间标记,但不是咱们平常说的当前时间,具体情况后面会详细讲。
  • 长度(Length):占 3 个字节。它指的是消息里真正有用的音视频等信息那部分的长度。
  • 类型 Id(TypeId):占 1 个字节。用来标识这条消息是什么类型的。
  • 消息流 ID(Message Stream ID):占 4 个字节,而且是小端格式存储**。它是每个消息独一无二的标识**。拆分 Message 成 Chunk,以及把 Chunk 还原成 Message 的时候,都靠这个 ID 来判断这些 Chunk 是不是属于同一条消息。像音频和视频,会用不同的 Message Stream ID 。

2.2 chunk(Message分块)

RTMP 收发数据的时候,不是直接按 Message 来的,而是把 Message 拆成 Chunk 发送,而且得等一个 Chunk 发完了,才能开始发下一个 Chunk 。每个 Chunk 都带着 MessageID,这样接收的那一方就能根据这个 ID,把 Chunk 重新拼成 Message 。

Chunk 默认大小是 128 字节。在传输的时候,能通过一个叫 Set Chunk Size 的控制信息来设置 Chunk 最大能有多大。发送端和接收端都会各自记着一个 Chunk Size 的数值(像 srs 流媒体服务器默认是 60000 ),两边都能自己设置这个值,改变自己发出去的 Chunk 最大能有多大。**Chunk 大一点呢,计算每个 Chunk 的时间就少了,CPU 占用率也低了,不过发送起来就费时间,尤其是在网速慢的时候,可能会堵住后面更重要信息的发送。Chunk 小一点呢,倒是不容易堵住,但小 Chunk 会带来好多额外的信息(就是 Chunk 里的 Header ),而且发得又多又零碎,在网速快的时候也不能充分利用带宽。**所以实际发送数据的时候,得用不同的 Chunk Size 多试试,通过抓包分析这些办法,找到合适的大小。而且在传输过程中,还能根据当时的网速,还有实际要发的数据大小,灵活调整 Chunk 大小,这样就能尽量让 CPU 用得更合理,也减少信息被堵住的情况

块格式
在这里插入图片描述

2.3 chunk头数据

RTMP协议里的Chunk头信息是实现数据传输与解析的关键内容,它主要包含 Basic Header(基本头)Message Header(消息头)Extended Timestamp(扩展时间戳) 这三个部分。下面为你展开详细介绍:

一、Basic Header(基本头)
在这里插入图片描述

作用:用于标识 Chunk Stream ID(CSID,流通道ID)Chunk Type(fmt,Chunk类型),并且要以最少的字节数来进行编码。

  1. 字段构成
  • fmt(Chunk Type):占据2位(0 - 3),用于决定Message Header的格式。
  • CSID(Chunk Stream ID):其长度会根据总字节数的不同而变化,具体如下表所示:
Basic Header长度 fmt(位) CSID(位) CSID取值范围 说明
1字节 2位 6位 0 - 63 用户自定义范围是3 - 630 - 2为协议保留值(2代表控制信息)。
2字节 2位 8位(仅第二个字节) 64 - 319 第一个字节的高6位设为0,CSID = 第二个字节的值 + 64。
3字节 2位 14位(第二、三字节) 64 - 65599 采用小端存储,CSID = (第三字节 × 256) + 第二字节 + 64。
  1. 特殊CSID说明
  • CSID = 0:表示Basic Header占用2字节,CSID范围是64 - 319
  • CSID = 1:表示Basic Header占用3字节,CSID范围是64 - 65599
  • CSID = 2:代表该Chunk是控制信息或者命令信息(例如setChunkSize等)。
  1. 编码原则
  • 要优先使用最短的字节数来表示CSID。比如,当CSID为100时,因为64 ≤ 100 ≤ 319,所以使用2字节的Basic Header进行编码。
  • 虽然2字节和3字节的CSID范围在64 - 319有重叠部分,但实际应用中应选择2字节的编码方式。

二、Message Header(消息头)
作用:描述Chunk所承载的实际数据信息,其格式由Basic Header中的fmt字段决定,一共有4种类型,具体如下表所示:

fmt 名称 长度(字节) 字段构成 适用场景
0 Type 0 11 timestamp(3字节)、message length(3字节)、message type ID(1字节)、msg stream ID(4字节,小端) 流开始的第一个Chunk、时间戳回退(如快退播放)、元数据(onMetaData)等场景。
1 Type 1 7 timestamp delta(3字节)、message length(3字节)、message type ID(1字节) 与前一个Chunk的msg stream ID相同的场景,例如同一流中的连续Chunk。
2 Type 2 3 timestamp delta(3字节) 与前一个Chunk的msg stream IDmessage lengthmessage type ID都相同的场景。
3 Type 3 0 无字段 完全复用前一个Chunk的Message Header,例如同一消息拆分的连续Chunk。

在这里插入图片描述

关键字段说明

  1. Timestamp相关字段
    • timestamp(绝对时间戳):占据3字节,最大值为0xFFFFFF(即16777215)。当实际时间戳超过这个最大值时,这3个字节会全部置为0xFFFFFF,此时需要通过扩展时间戳来获取真实值。
    • timestamp delta(时间戳差):同样占据3字节,表示与前一个Chunk的时间差,其最大值和处理方式与绝对时间戳相同。
  2. 扩展时间戳触发条件:当timestamptimestamp delta的值大于0xFFFFFF时,就需要使用4字节的扩展时间戳字段,此时原字段的值为0xFFFFFF,扩展字段存储的是真实值(绝对时间戳或时间差)。
  3. 小端存储msg stream ID和3字节Basic Header中的CSID都采用小端存储方式,即低位字节在前,高位字节在后。例如,msg stream ID = 0x12345678,在存储时顺序为0x78 0x56 0x34 0x12

在此类场景中,Chunk Stream ID 是 msg stream ID 在数据分块中的具体体现,两者本质上指向同一流的标识,但应用场景不同(前者是逻辑流标识,后者是分块数据的元数据)

三、Extended Timestamp(扩展时间戳)
作用:当时间戳或时间戳差超过3字节的最大值时,用于存储真实值。

  • 长度:4字节,最大值为0xFFFFFFFF(即4294967295)。
  • 解析规则
    • 如果timestamp字段为0xFFFFFF,那么真实时间戳就是扩展时间戳的值。
    • 如果timestamp delta字段为0xFFFFFF,那么真实时间差就是扩展时间戳的值。
  • 示例:假设timestamp = 0xFFFFFF,扩展时间戳为0x01234567,那么真实时间戳为0x01234567(即19895023)。

四、四种Message Header对比

类型 长度(字节) 复用的前一个Chunk字段 适用场景举例
Type 0 11 无复用,所有字段都为绝对信息 流初始化时发送的第一个视频帧或音频帧
Type 1 7 复用msg stream ID 同一流中,连续发送的不同类型的媒体数据
Type 2 3 复用msg stream IDmessage lengthmessage type ID 同一流中,相同类型且数据长度相同的连续帧
Type 3 0 完全复用所有Message Header字段 同一消息拆分成多个Chunk时(如大视频帧拆分)

关于“Type 0 使用绝对时间戳、Type 1 使用相对时间戳”的说法,

设计逻辑的核心差异

维度 Type 0(绝对时间戳) Type 1(相对时间戳)
核心目标 全局同步、跨流交互、实时性校准 局部同步、依赖主轨道、灵活调整
适用场景 主轨道(视频、主音频)、直播流 辅助轨道(字幕、音频描述)、离线编辑
优势 一致性强、可直接对比时间 轻量化、调整便捷、不依赖全局基准
局限性 存储成本高、跨域同步需时间校准 无法独立使用、依赖参考点定义

这种设计本质是为了在同步精度灵活性之间取得平衡:主轨道通过绝对时间戳确保全局秩序,辅助轨道通过相对时间戳降低耦合度,提升适配性。具体实现需结合具体协议或格式的规范(如 RFC 文档、行业标准)进一步分析。

2.4 块数据

用户实际发送的音视频数据需适配chunkSize(块大小),以下通过两个示例说明 RTMP 协议中 Chunk 的拆分与编码逻辑:
示例 1:音频流的 Chunk 编码优化
假设待发送的音频消息需拆分为 Chunk 传输,且chunkSize为 128 字节:

第一个 Chunk(首个消息):
Chunk Type=0(无参考 Chunk,需完整头信息)
Basic Header=1 字节(CSID=3,<127)
Message Header=11 字节(绝对时间戳1000,类型 ID 等)
Data=32 字节
总长度 = 1+11+32=44 字节
第二个 Chunk(与前 Chunk 的 CSID、类型、数据长度相同):
Chunk Type=2(复用前 Chunk 的流 ID、类型、长度,仅传时间戳差)
Message Header=3 字节(时间戳差20,1020-1000)
Data=32 字节
总长度 = 1+3+32=36 字节
第三、四个 Chunk(完全复用前 Chunk 的头信息):
Chunk Type=3(头信息全复用,无需传输任何头字段)
Data=32 字节
总长度 = 1+32=33 字节

核心逻辑:通过 Chunk Type 的差分编码(Type0→Type2→Type3),逐步压缩头信息,减少冗余传输。
示例 2:大尺寸消息的 Chunk 拆分
若消息负载长度为 307 字节(>128 字节),需拆分为多个 Chunk:

第一个 Chunk(消息起始):
Chunk Type=0(完整头信息,时间戳1000)
Data=128 字节(取最大 ChunkSize)
总长度 = 1+11+128=140 字节
第二个 Chunk(消息续传,复用头信息):
Chunk Type=3(完全复用前 Chunk 的头信息,时间戳不变)
Data=128 字节
总长度 = 1+128=129 字节
第三个 Chunk(消息剩余部分):
Chunk Type=3(继续复用头信息)
Data=51 字节(307-128×2)
总长度 = 1+51=52 字节

核心逻辑:大消息按 ChunkSize 拆分,后续 Chunk 通过 Type3 复用头信息,仅传输数据分片,避免重复编码。
关键结论
Chunk Type 的作用:
Type0:用于首个 Chunk 或头信息不可复用时(需完整头)。
Type2/3:通过复用前 Chunk 的头信息(流 ID、类型、长度、时间戳差),减少传输冗余。
Type3 的双重用途:
续传同⼀消息:如示例 2 中,多个 Type3 Chunk 属于同⼀ Message,共享时间戳。
启动新消息:若新消息的头信息与前 Chunk 完全一致(如相同流 ID、类型、长度),可直接用 Type3 快速启动,无需重传头信息。

2.5 总结

为什么需要 Chunk 机制?
在互联网传输中,数据需按序发送与接收。若采用固定大小的消息块:

  • 大消息(如视频帧):长时间占用带宽,导致后续小消息(如控制指令、音频帧)排队等待,引发延迟或卡顿。
  • 小消息(如控制信息):可能因等待大消息完成而无法及时传输,影响系统响应速度(如播放控制、网络状态通知)。
    Chunk 机制如何解决问题?
    RTMP 通过将 Message 拆分为可变长度的 Chunk(默认 128 字节,可动态调整),实现以下优势:
  1. 多路复用与公平调度
    示例:同时传输视频 I 帧(244KB)、音频帧(8KB)和控制消息(2KB)。
    传统方式:需先完成 244KB 视频传输,再处理后续数据,导致音频 / 控制延迟。
  2. Chunk 方式:
  • 视频帧拆分为 1906 个 Chunk(244KB ÷ 128 字节 ≈ 1906)。
  • 音频帧拆分为 63 个 Chunk(8KB ÷ 128 字节 = 63)。
  • 控制消息拆分为 16 个 Chunk(2KB ÷ 128 字节 = 16)。
    调度策略:系统可交错发送不同类型的 Chunk(如控制 Chunk → 音频 Chunk → 视频 Chunk),确保小消息优先传输,避免长时间阻塞。

实时性保障
关键计算:
假设带宽为 10Mbit/s,传输 244KB 视频帧需 190ms(244×1024×8 ÷ 10×1024×1024)。
若要求 25 帧 / 秒实时播放,每帧最大传输时间为 40ms(1000ms ÷ 25),需 47.6Mbit/s 带宽(244KB×25×8 ÷ 1000ms)。
Chunk 的作用:
通过拆分视频帧,将 190ms 的连续传输分散为多个 40ms 的片段,允许系统在片段间插入其他数据(如音频、控制),降低单一数据的阻塞风险。

动态适应网络变化
Chunk Size 可配置:
高带宽场景:增大 Chunk Size(如 60000 字节),减少头部开销,提升吞吐量。
低带宽场景:减小 Chunk Size(如 128 字节),降低单 Chunk 传输时间,避免长时间占用链路。
示例:弱网环境下,若视频帧按 60000 字节 Chunk 发送,单 Chunk 需 48ms(60000×8 ÷ 10×1024×1024),超出 40ms 限制;而 128 字节 Chunk 仅需 0.1ms,可灵活调度。

FFmpeg 在发送 RTMP Chunk 时 不使用多线程,而是通过 单线程 + 异步 I/O 的模式实现高效传输。

RTMP传输基本流程

在这里插入图片描述

3 消息优先等级

在这里插入图片描述
RTMP 将消息(Message)分为三类,每类对应不同的优先级,优先级由高到低依次为:

  • 高优先级(High Priority)
    类型:控制消息(Control Messages)。
    常见场景:
    流控制消息(如 SET_CHUNK_SIZE、ABORT_MESSAGE),用于协商传输参数或中断流。
    协议控制消息(如 ACKNOWLEDGEMENT、USER_CONTROL),用于流量控制、心跳检测等。
    特点:必须优先发送,否则可能导致连接中断或参数失序。
  • 中优先级(Medium Priority)
    类型:元数据消息(Metadata Messages)。
    常见场景:
    流元数据(如视频编码参数、音频采样率)、用户自定义数据(如直播标题、标签)。
    特点:需在媒体数据前或同步发送,确保播放端正确解析媒体内容,但允许一定延迟。
  • 低优先级(Low Priority)
    类型:媒体数据消息(Media Data Messages)。
    常见场景:
    音视频数据(如 H.264 视频帧、AAC 音频帧)。
    特点:允许分块传输(拆分为 Chunk),但需按顺序发送以保证播放端解码流畅。

音频消息优先级高于视频消息。在没有控制消息插队时,会先发送音频消息的 Chunk,等音频消息的 Chunk 发送一段后(不是全部发完才切换 ),如果有视频消息的 Chunk 也需要发送,就会切换过去发送视频消息的 Chunk,然后再根据情况切换回音频或者继续发视频等,实现音频和视频消息 Chunk 的交替发送。


网站公告

今日签到

点亮在社区的每一天
去签到