4、RTMP协议详解:握手过程、消息格式、Chunk流、推流与拉流实战
RTMP,全称 Real-Time Messaging Protocol,是 Adobe 搞出来的。说白了,它就是直播推流和拉流的老祖宗。虽然现在 WebRTC 和 SRT 很火,但 RTMP 在传统直播链路里依然坚挺。我个人习惯,只要涉及 CDN 推流,第一反应还是 RTMP。
这一章,我们把它拆开揉碎。从握手开始,到消息格式,再到 Chunk 流,最后上手推拉流。嗯,都是实战里必须啃下的硬骨头。
4.1 RTMP 握手过程
RTMP 握手是客户端和服务器建立连接的第一步。它不像 HTTP 那样一问一答,而是双方互发三个固定大小的数据块。
握手分为三个阶段:
- C0/S0:1 个字节,表示 RTMP 版本。目前主流是版本 3。
- C1/S1:1536 个字节,包含时间戳和随机数据。
- C2/S2:1536 个字节,是对对方 S1/C1 的回应。
握手流程其实很简单:
- 客户端发 C0 + C1。
- 服务器收到后,发 S0 + S1。
- 客户端收到 S1,发 C2。
- 服务器收到 C1,发 S2。
- 双方都收到 C2/S2,握手完成。
握手完成后,就可以开始传输真正的音视频数据了。但别急,还有消息格式要搞懂。
4.2 消息格式
RTMP 的消息分为两种:协议控制消息和用户控制消息。协议控制消息用于管理连接,比如设置 Chunk 大小、确认窗口大小等。用户控制消息才是真正的音视频数据。
消息头格式如下:
| 字段 | 长度 | 说明 |
|---|---|---|
| Message Type | 1 字节 | 消息类型 ID,比如 0x08 是音频,0x09 是视频 |
| Payload Length | 3 字节 | 消息体长度 |
| Timestamp | 4 字节 | 时间戳,单位毫秒 |
| Stream ID | 3 字节 | 流 ID,用于区分不同的流 |
消息体就是实际的音视频数据。但 RTMP 不会直接发送这么大的消息,它会切成小块,这就是 Chunk 流。
4.3 Chunk 流
Chunk 流是 RTMP 的精髓。它把大消息切成小块,交错发送。这样,即使网络不好,也能保证低延迟。
Chunk 的基本格式:
- Basic Header:1-3 字节,包含 Chunk Stream ID 和 Chunk Type。
- Message Header:0、3、7、11 字节,取决于 Chunk Type。
- Extended Timestamp:0 或 4 字节,当时间戳超过 0xFFFFFF 时使用。
- Chunk Data:实际数据,长度由 Chunk Size 决定。
Chunk Type 有四种:
| Type | Header 长度 | 说明 |
|---|---|---|
| Type 0 | 11 字节 | 完整消息头,用于新消息或时间戳变化 |
| Type 1 | 7 字节 | 不包含 Stream ID,用于同一流 |
| Type 2 | 3 字节 | 只包含时间戳增量,用于连续 Chunk |
| Type 3 | 0 字节 | 无头,完全复用上一个 Chunk 的头信息 |
Chunk 流的工作流程:
- 发送方把消息切成 Chunk。
- 每个 Chunk 带上 Basic Header 和 Message Header。
- 接收方根据 Chunk Stream ID 重组消息。
- 如果 Chunk 丢失,接收方会等待重传或丢弃。
你想想看,如果一条视频消息有 10KB,切成 128 字节的 Chunk,那就有 80 个 Chunk。这些 Chunk 可以和其他音频、控制消息交错发送。这就是 RTMP 能保持低延迟的原因。
4.4 推流与拉流实战
理论讲完了,我们上手实战。我会用 FFmpeg 和 rtmpdump 来演示。
4.4.1 推流
推流就是把本地音视频数据发送到 RTMP 服务器。我用 FFmpeg 推流:
ffmpeg -re -i input.mp4 -c:v libx264 -c:a aac -f flv rtmp://your-server/live/stream-key
参数说明:
-re:以原始帧率读取,模拟实时推流。-i input.mp4:输入文件。-c:v libx264:视频编码为 H.264。-c:a aac:音频编码为 AAC。-f flv:输出格式为 FLV,RTMP 底层用 FLV 封装。rtmp://your-server/live/stream-key:推流地址。
stream-key 是服务器分配的,不要泄露。我曾经在调试时把 key 写死在代码里,结果被同事抓包看到了,后来改了。
4.4.2 拉流
拉流就是从 RTMP 服务器接收音视频数据。我用 rtmpdump 拉流:
rtmpdump -r rtmp://your-server/live/stream-key -o output.flv
或者用 FFmpeg 拉流并转码:
ffmpeg -i rtmp://your-server/live/stream-key -c copy output.mp4
拉流时,客户端会先发送连接请求,然后创建流,最后接收音视频数据。整个过程就是握手、消息、Chunk 流的逆过程。
4.4.3 实战中的坑
我在项目中遇到过几个常见问题:
- 握手超时:服务器防火墙没开 1935 端口。检查网络策略。
- 推流卡顿:Chunk Size 太小,头部开销大。调大 Chunk Size 到 4096 或 8192。
- 拉流花屏:视频编码参数不对,比如 B 帧太多。推流时加
-bf 0禁用 B 帧。 - 音画不同步:时间戳计算错误。检查推流端的 PTS 和 DTS。
rtmp 或 tcp.port == 1935,一目了然。
4.5 知识体系图
下面这张图展示了 RTMP 协议的核心逻辑:从握手到消息,再到 Chunk 流,最后到推拉流。
这张图把 RTMP 的核心链路串起来了。你从握手开始,经过消息格式,再到 Chunk 流,最后落地到推拉流实战。每一步都环环相扣。
嗯,RTMP 协议就讲到这里。记住,握手是基础,消息是载体,Chunk 流是效率,实战是检验。下次遇到 RTMP 问题,先抓包看握手,再查 Chunk 流,基本能定位 80% 的问题。