RTMP 面试速成

一、30 秒版本(先给结论)

RTMP 是 Adobe(前身 Macromedia)设计的应用层流媒体协议,基于 TCP,默认端口 1935。它把音视频、元数据、控制命令切成带时间戳的消息,再拆成小的 Chunk 多路复用传输。连接先 C0/C1/C2 握手,再用 AMF 编码的命令(connect、createStream、publish/play)建立流,之后传输和 FLV tag 格式一致的音视频数据。
它延迟低(约 1~3 秒),主要用在推流端(OBS → CDN)。浏览器播放因为 Flash 淘汰,现在多用 HTTP-FLV、HLS 或 WebRTC。

后面是每个点的展开,让你被追问时答得下去。


二、协议全貌

1. 基本定位

  • 传输层:TCP,所以可靠、有序,但存在队头阻塞,网络差时延迟会累积。
  • 变种:
    • RTMPS:RTMP over TLS
    • RTMPE:Adobe 私有加密(不推荐)
    • RTMPT:HTTP 隧道
    • RTMFP:基于 UDP,和 RTMP 基本不是一回事
  • 负载格式:音视频 payload 基本等同于 FLV tag body。所以 RTMP 和 HTTP-FLV 能很容易互转,这是个高频考点。

2. 握手(Handshake)

1
2
3
4
Client                    Server
|---- C0 + C1 ---------->|
|<--- S0 + S1 + S2 ------|
|---- C2 --------------->|
  • C0/S0:1 字节,版本号(3)。
  • C1/S1:1536 字节 = 时间戳(4)+ 零(4)+ 随机数(1528)。
  • C2/S2:分别是对 S1/C1 的回显。
  • 区分简单握手和复杂握手。复杂握手在随机数里嵌入 HMAC-SHA256 摘要做校验,Flash Player 和部分 CDN 会要求。多数推流工具能自动适配。

3. Chunk(最核心,必考)

消息(Message)太大会阻塞别的消息,所以拆成 Chunk 传输。默认 chunk size = 128 字节,可用 Set Chunk Size 修改,实际常设 4096 或更大以减少头开销。

Chunk 结构:

1
| Basic Header | Message Header | Extended Timestamp | Chunk Data |
  • Basic Header(1~3 字节):高 2 位是 fmt,低 6 位是 **chunk stream id(csid)**。csid 为 0 或 1 表示用 2 或 3 字节扩展 csid。
  • Message Header 按 fmt 分 4 种,体现了”头部压缩”思想:
fmt 头长度 内容 典型场景
0 11 字节 绝对时间戳(3)+ 消息长度(3)+ 类型 ID(1)+ stream ID(4,小端) 流的第一个消息
1 7 字节 时间戳差值 + 长度 + 类型 ID 同一流,长度/类型变了
2 3 字节 仅时间戳差值 长度、类型都不变(如固定帧长音频)
3 0 字节 全部沿用上一个 同一消息的后续 chunk
  • Extended Timestamp:时间戳 ≥ 0xFFFFFF 时,用 4 字节扩展。
  • 为什么要分 Chunk? 多路复用(音频、视频、命令交织)、避免大 I 帧阻塞小的音频包或控制消息、头部压缩。

4. 消息类型(Message Type ID)

ID 含义
1 Set Chunk Size
2 Abort Message
3 Acknowledgement
4 User Control Message(StreamBegin、PingRequest/Response 等)
5 Window Acknowledgement Size
6 Set Peer Bandwidth
8 音频
9 视频
18 数据消息(AMF0,如 onMetaData)
20 命令消息(AMF0)
17 / 15 AMF3 的命令 / 数据

ID 1~6 是协议控制消息,走 chunk stream id 2,message stream id 为 0。

5. AMF(命令的编码方式)

命令、元数据都用 AMF0(或 AMF3)序列化。常见 AMF0 类型:Number(0x00,8 字节 double)、Boolean(0x01)、String(0x02,2 字节长度前缀)、Object(0x03,以 00 00 09 结尾)、Null(0x05)、ECMA Array(0x08)。

命令消息的格式是:命令名(String)+ 事务 ID(Number)+ 命令对象(Object/Null)+ 参数。

6. 推流完整流程(面试时能画出来就很加分)

1
2
3
4
5
6
7
8
9
10
11
握手完成
C → S: connect(app, tcUrl, flashVer ...)
S → C: Window Ack Size, Set Peer Bandwidth, (Set Chunk Size)
S → C: _result (NetConnection.Connect.Success)
C → S: releaseStream, FCPublish (非标准,但常见)
C → S: createStream
S → C: _result (返回 stream id)
C → S: publish(streamKey, "live")
S → C: onStatus (NetStream.Publish.Start)
C → S: @setDataFrame / onMetaData (分辨率、码率、编码器)
C → S: 音视频 Sequence Header,然后持续发送音视频数据

拉流(play)类似:connect → createStream → play,服务端回 StreamBegin、NetStream.Play.Start,然后下发 metadata 和音视频数据。

7. 音视频 payload(和 FLV 一致)

视频(type 9):

  • 第 1 字节:高 4 位 FrameType(1 = 关键帧,2 = 非关键帧),低 4 位 CodecID(7 = H.264)
  • 第 2 字节:AVCPacketType(0 = Sequence Header,1 = NALU,2 = 序列结束)
  • 3 字节 CompositionTime(CTS),PTS = DTS + CTS。RTMP 头里的时间戳是 DTS。
  • 后面是 NALU,格式是 AVCC(4 字节长度前缀),不是 Annex-B 的 start code。
  • Sequence Header 里是 AVCDecoderConfigurationRecord(含 SPS/PPS),播放端必须先拿到它才能解码。

音频(type 8):

  • 第 1 字节:SoundFormat(10 = AAC)+ 采样率 + 位深 + 声道
  • 第 2 字节:AACPacketType(0 = AudioSpecificConfig,1 = 原始 AAC 帧)

补充:原版 RTMP/FLV 只支持 H.264 等老编码。Enhanced RTMP(社区推动的扩展,用 FourCC 标识)加入了 HEVC、AV1、VP9 的支持,面试提一句说明你了解现状。

8. 流控

Window Acknowledgement Size:接收方每收到这么多字节,要回一个 Acknowledgement。Set Peer Bandwidth 用来限制对端发送速率,有 Hard / Soft / Dynamic 三种限制类型。


三、对比类问题

RTMP HTTP-FLV HLS WebRTC
传输 TCP HTTP 长连接 HTTP 分片 UDP(SRTP)
延迟 约 1~3s 约 1~3s 530s(LL-HLS 约 25s) <500ms
浏览器 不支持 靠 MSE(flv.js) 原生/MSE 原生
CDN 友好 一般 好 最好 较差
典型用途 推流 播放 播放、兼容 连麦、超低延迟

要点:RTMP 和 HTTP-FLV 的数据内容一样,区别在于传输层,所以 CDN 常见做法是 “RTMP 推流 + HTTP-FLV / HLS 分发”。


四、高频追问和答法

  1. 为什么延迟会越来越大? TCP 在弱网下重传和拥塞控制会堆积数据。服务端或播放器可以做追帧(加速播放或丢帧追赶),服务端也可以在发送缓冲堆积时**丢帧(优先丢非关键帧,或整个 GOP)**。
  2. 怎么做到秒开? 服务端缓存 Sequence Header + 最近一个 GOP,新观众连上先收这些,马上能出画面。GOP 越长,秒开越难。
  3. 时间戳怎么处理? 头里是毫秒级 DTS,且会溢出到 32 位(约 49 天)。要处理时间戳回绕、跳变,拉流端和转封装时必须校正。
  4. 推流失败怎么排查? 看顺序:网络和 1935 端口 → 握手是否成功 → connect 是否被拒(app 名、鉴权) → publish 的 stream key 是否正确或被占用 → 服务端回的 onStatus 错误码。
  5. RTMP 的缺点? TCP 队头阻塞、延迟下限受限、无原生浏览器支持、编码支持老旧(靠 Enhanced RTMP 补)、没有内置 FEC 或自适应码率。替代方案有 SRT、WebRTC(WHIP 推流)。

五、2 分钟面试回答模板

RTMP 是基于 TCP 的应用层流媒体协议,默认 1935 端口。
连接过程:先 C0/C1/C2 三次握手,然后通过 AMF 编码的命令消息依次做 connect、createStream、publish 或 play 来建立流。
传输方式:数据按消息传输,消息再拆成 Chunk,默认 128 字节,可调整。Chunk 头有 4 种格式,可以压缩重复字段,同时支持音视频和控制消息多路复用,避免大帧阻塞。
载荷:音视频消息的格式和 FLV tag 一致,H.264 要先发 Sequence Header(SPS/PPS),再发 AVCC 格式的 NALU,时间戳是毫秒级 DTS,PTS 靠 CompositionTime 计算。
应用场景:延迟大约 1~3 秒,现在主要用于推流,播放端因为浏览器不支持,通常转成 HTTP-FLV、HLS 或 WebRTC。缺点是 TCP 弱网下延迟会累积。

如果你简历里有直播播放器、拉流或解码相关的项目,可以在最后接一句”我在项目里处理过 xx(比如 Sequence Header 缺失导致花屏、时间戳跳变、延迟追赶)”,这比背协议更能打动面试官。


六、建议的下一步

最好亲手抓一次包:用 OBS 推流到本地 nginx-rtmp(或 SRS),用 Wireshark 过滤 rtmpt 看握手、chunk 头、connect 命令,印象会比背诵深得多。需要的话,我可以接着出一组模拟面试题,你来答,我来点评。

回显(echo)就是:把对方发来的数据原样(或基本原样)再发回给对方。

在 RTMP 握手里怎么回显

1
2
3
4
Client                      Server
|--- C0 + C1 ------------->| C1:我的时间戳 + 1528 字节随机数
|<-- S0 + S1 + S2 ---------| S1:服务端的随机数;S2 = 回显 C1
|--- C2 ------------------>| C2 = 回显 S1

注意是交叉对应:

  • S2 回显的是客户端发来的 C1
  • C2 回显的是服务端发来的 S1

回显具体回了什么

以 C2 为例(S2 同理),1536 字节的结构是:

字段 长度 内容
time 4 字节 抄 S1 里的时间戳
time2 4 字节 收到 S1 时自己的时间戳
random echo 1528 字节 抄 S1 里的随机数

所以严格来说,大部分内容是原样抄回去,中间有 4 字节是填自己收到包的时间。我前面说”回显”是简化说法,面试时说”基本是原样返回对方的数据”就够了。简单握手里很多实现就是直接把整个包拷贝回去。

为什么要回显

  1. 确认对方收到了:你发的随机数,对方原样返回,说明链路双向通了,对方也确实读到了你的包。
  2. 验证对方的身份和有效性:随机数对得上,说明对方不是乱发数据的。
  3. 可以测往返时延:对方回的 time 是你当初发的时间戳,time2 是它收到的时间,据此能估算 RTT。

这里的回显只是握手确认,不是安全机制。真正带校验的是复杂握手里的 HMAC-SHA256 摘要。

HTTP-FLV / HLS / RTSP 面试速成

一、先记住它们的定位

协议 一句话
HTTP-FLV 把 RTMP 里的那份 FLV 数据,改用 HTTP 长连接持续下发。内容和 RTMP 一样,只是传输方式不同
HLS 把直播流切成几秒一个的小文件,用 m3u8 索引,播放器像下载文件一样轮流拉取。延迟高,兼容性和 CDN 友好度最好
RTSP 一个控制协议(像遥控器),只负责建立会话和播放控制。真正的音视频由 RTP/RTCP 传输。安防摄像头的标配

二、HTTP-FLV

30 秒版本

HTTP-FLV 是把 FLV 格式的音视频流通过 HTTP 长连接持续下发的方案。响应不带 Content-Length,使用 chunked 传输,服务器一直往连接里写 FLV tag。它的数据和 RTMP 同源,所以 CDN 通常”RTMP 推流,HTTP-FLV 分发”。延迟约 1~3 秒,浏览器端靠 MSE 解封装播放,比如 flv.js。

1. 先搞懂 FLV 容器格式(必考)

1
2
3
4
5
FLV Header (9 字节)
PreviousTagSize0 (4 字节,固定 0)
Tag1 | PreviousTagSize1
Tag2 | PreviousTagSize2
...
  • FLV Header:'F' 'L' 'V' + 版本(1)+ 标志位(0x05 表示有音频和视频)+ 头长度(9)。
  • Tag Header 共 11 字节:
字段 长度 说明
TagType 1 8 音频,9 视频,18 脚本数据(onMetaData)
DataSize 3 tag body 长度
Timestamp 3 低 24 位,毫秒
TimestampExtended 1 时间戳高 8 位
StreamID 3 恒为 0
  • PreviousTagSize(4 字节)= 11 + DataSize,用于从后往前解析文件。直播里用不上,但格式要求带上。
  • Tag Body 的内容和上次讲的 RTMP 音视频 payload 完全一致(视频的 FrameType/CodecID/AVCPacketType/CTS + AVCC NALU,音频的 AAC 两种包类型)。

2. RTMP 怎么转 HTTP-FLV

RTMP 消息类型 8/9/18 的 payload 直接就是 FLV tag body。服务器只需要:

  1. 先发 FLV Header + PreviousTagSize0
  2. 发 onMetaData 脚本 tag、音视频 Sequence Header
  3. (为了秒开)发缓存的最近一个 GOP
  4. 之后每收到一个 RTMP 音视频消息,套上 11 字节 tag header,再补上 PreviousTagSize,写入 HTTP 响应

3. HTTP 层

  • Content-Type: video/x-flv,Transfer-Encoding: chunked,没有 Content-Length,连接保持不断。
  • 注意:这里的 HTTP chunked 和 RTMP 的 Chunk 不是一回事,面试别混。

4. 播放端

浏览器不能直接播 FLV,flv.js 之类的库做的是:fetch + ReadableStream 读取流 → 在 JS 里解析 FLV → 转封装成 fMP4 → 喂给 MSE(Media Source Extensions) 的 SourceBuffer。所以浏览器要能解码 H.264/AAC,并支持 MSE。桌面端播放器(ffmpeg、mpv 等)则直接支持 FLV over HTTP。

优缺点

  • 优点:延迟低;走 HTTP/HTTPS(80/443),穿防火墙、对 CDN 友好;实现简单。
  • 缺点:仍是 TCP,有队头阻塞,弱网延迟会累积;浏览器依赖 MSE;没有原生自适应码率;原版 FLV 编码支持老旧(靠 Enhanced FLV 扩展 HEVC/AV1)。

三、HLS

30 秒版本

HLS 是 Apple 提出的基于 HTTP 的自适应流媒体协议。服务器把流按关键帧切成几秒一个的分片(TS 或 fMP4),用 m3u8 播放列表索引。直播时播放列表是滑动窗口,播放器不断刷新列表、下载新分片。它天然支持多码率切换,兼容性和 CDN 缓存效果最好,但传统延迟在 1030 秒,LL-HLS 可降到 25 秒左右。

1. 整体结构

1
2
3
4
Master Playlist (多码率入口)
├─ 1080p.m3u8 ──→ seg1.ts, seg2.ts, seg3.ts ...
├─ 720p.m3u8 ──→ ...
└─ 480p.m3u8 ──→ ...

Master Playlist(多码率清单):

1
2
3
4
5
#EXTM3U
#EXT-X-STREAM-INF:BANDWIDTH=2500000,RESOLUTION=1280x720,CODECS="avc1.64001f,mp4a.40.2"
720p/index.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=800000,RESOLUTION=640x360
360p/index.m3u8

Media Playlist(直播):

1
2
3
4
5
6
7
8
9
10
#EXTM3U
#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:6
#EXT-X-MEDIA-SEQUENCE:1200
#EXTINF:6.000,
seg1200.ts
#EXTINF:6.000,
seg1201.ts
#EXTINF:6.000,
seg1202.ts

关键标签:

标签 含义
#EXT-X-TARGETDURATION 分片最大时长(秒,整数)
#EXT-X-MEDIA-SEQUENCE 列表中第一个分片的序号,窗口滑动时递增
#EXTINF 下一个分片的时长
#EXT-X-ENDLIST 表示流结束(点播有,直播没有)
#EXT-X-DISCONTINUITY 编码参数或时间戳不连续(如换流、广告插入)
#EXT-X-KEY 加密信息(AES-128 等)
#EXT-X-MAP fMP4 的初始化段(init segment)

2. 分片格式

  • MPEG-TS:固定 188 字节包,有 PAT/PMT 表描述节目,音视频打包成 PES。老方案,兼容性极好。
  • fMP4(CMAF):较新(协议版本 7 起),开销更小,可与 DASH 共用一套分片,HEVC 也只能走 fMP4。

3. 直播工作流程

  1. 推流端(通常 RTMP)→ 服务器
  2. 服务器按关键帧切片,所以 GOP 必须和分片对齐(例如 GOP 2 秒,分片 6 秒就是 3 个 GOP)
  3. 每切好一片,更新 m3u8,删掉最老的分片(滑动窗口)
  4. 播放器下载 m3u8 → 下载分片 → 解码播放,并按分片时长周期性刷新 m3u8

4. 为什么延迟高

规范建议播放器直播时不要从距列表末尾不足 3 个 TargetDuration 的位置开始播放。所以延迟的下限大约是 3 × 分片时长,再加上切片、CDN 传播和播放器缓冲。分片 6 秒,延迟就是 18 秒起步。降延迟的办法是缩短分片和 GOP,但分片越小,请求数越多,CDN 压力越大。

5. LL-HLS(低延迟 HLS)

核心思想是不等整个分片切完就开始发:

  • Partial Segment(#EXT-X-PART):分片再细分成几百毫秒的小块,生成后就能被请求。
  • Blocking Playlist Reload:播放器请求带上期望的 _HLS_msn/_HLS_part,服务器挂起请求,直到该片段可用才响应,不用频繁轮询。
  • Preload Hint(#EXT-X-PRELOAD-HINT):提前告知下一个小块的地址,播放器可预先发请求。

延迟约 2~5 秒。

优缺点

  • 优点:纯 HTTP,CDN 缓存友好、可扩展性最强;iOS/Safari/Android 原生支持;天然多码率自适应;支持 DRM(FairPlay)。
  • 缺点:延迟高;分片和列表带来额外存储与请求开销;低延迟版本实现复杂。

四、RTSP

30 秒版本

RTSP 是一个应用层的流媒体控制协议,文本格式,语法类似 HTTP,默认端口 554。它负责会话的建立和播放控制(OPTIONS、DESCRIBE、SETUP、PLAY、TEARDOWN 等),会话描述用 SDP,真正的音视频数据由 RTP 承载,RTCP 做质量反馈和音视频同步。传输可以走 UDP,也可以通过 TCP 交织(interleaved)复用在同一条连接上。常用于 IP 摄像头和安防监控,延迟可以做到很低,但浏览器不支持。

1. 核心方法

方法 作用
OPTIONS 询问服务器支持哪些方法
DESCRIBE 获取媒体描述(返回 SDP)
SETUP 为每条轨道(音频、视频)协商传输方式,建立会话
PLAY 开始播放
PAUSE 暂停
TEARDOWN 结束会话
ANNOUNCE / RECORD 推流方向(把流发给服务器)
GET_PARAMETER 常用作保活心跳

2. 典型交互流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
C→S: OPTIONS rtsp://host/live RTSP/1.0
CSeq: 1
S→C: RTSP/1.0 200 OK (Public: OPTIONS, DESCRIBE, SETUP, PLAY, TEARDOWN)

C→S: DESCRIBE rtsp://host/live RTSP/1.0
CSeq: 2
Accept: application/sdp
S→C: 200 OK + SDP

C→S: SETUP rtsp://host/live/trackID=0 RTSP/1.0
CSeq: 3
Transport: RTP/AVP;unicast;client_port=5000-5001
S→C: 200 OK
Session: 12345678
Transport: RTP/AVP;unicast;client_port=5000-5001;server_port=6000-6001

(音频轨道再 SETUP 一次)

C→S: PLAY rtsp://host/live RTSP/1.0
CSeq: 5
Session: 12345678
S→C: 200 OK
← RTP 音视频数据流 →

C→S: TEARDOWN ...
  • 每个请求带 CSeq(序列号),会话靠 Session 头标识。
  • 有会话超时(默认约 60 秒),需要心跳保活。
  • 认证支持 Basic 和 Digest(返回 401 后重发带认证头的请求)。

3. SDP:会话描述

1
2
3
4
5
6
7
m=video 0 RTP/AVP 96
a=rtpmap:96 H264/90000
a=fmtp:96 packetization-mode=1;sprop-parameter-sets=<SPS base64>,<PPS base64>
a=control:trackID=0
m=audio 0 RTP/AVP 97
a=rtpmap:97 MPEG4-GENERIC/44100/2
a=control:trackID=1

SDP 告诉客户端有几路媒体、什么编码、时钟频率、各轨道的控制 URL。H.264 的 SPS/PPS 常放在 sprop-parameter-sets 里,所以 RTSP 不像 RTMP 那样靠 Sequence Header 包带参数集。

4. 数据传输方式(重点)

方式 特点
RTP over UDP 延迟最低;偶发丢包会花屏;穿 NAT/防火墙麻烦。RTP 用偶数端口,RTCP 用相邻奇数端口
RTP over TCP(interleaved) RTP 数据复用在 RTSP 的 TCP 连接里,可靠、穿防火墙容易,代价是有队头阻塞
RTSP over HTTP 隧道 穿透严格网络

交织模式下,数据格式是:'$'(0x24) + channel(1 字节) + length(2 字节) + RTP/RTCP 包。通道号 0/1 通常对应视频的 RTP/RTCP,2/3 对应音频。

5. RTP 和 RTCP

RTP 头(固定 12 字节):版本(2 位)、P、X、CC、M(Marker)、PT(负载类型)、序列号(16 位)、时间戳(32 位)、SSRC。

  • 序列号:接收端用来检测丢包和乱序。
  • 时间戳:是媒体时钟,不是毫秒。视频固定 90 kHz,音频等于采样率。
  • M 位:视频中通常标记一帧的最后一个包。

H.264 的 RTP 封装(RFC 6184):

  • Single NAL:一个 NALU 一个包(小 NALU,如 SPS/PPS)
  • STAP-A:多个小 NALU 聚合成一个包
  • FU-A:大 NALU(如 I 帧)按 MTU(约 1400 字节)分片,接收端重组

RTCP:SR/RR 报告丢包率和抖动;SR 里带 NTP 时间 ↔ RTP 时间戳的对应关系,接收端靠它做音视频同步(音频和视频的 RTP 时钟不同,不经 RTCP 对齐无法同步)。

优缺点

  • 优点:延迟低(UDP 下可到几百毫秒);标准化,摄像头和 NVR 普遍支持(ONVIF 生态);控制能力强(暂停、倍速、定位)。
  • 缺点:浏览器不支持;UDP 穿 NAT 难;不适合大规模 CDN 分发;不同厂商实现差异大,兼容坑多。

RTSP 和 RTMP 的本质区别

  • RTMP:控制和数据混在一条 TCP 连接里,协议自带 Chunk 复用。
  • RTSP:控制通道和数据通道分离(控制走 TCP,数据通常走 UDP RTP)。
  • 时间戳也不同:RTMP 是毫秒级 DTS,RTSP/RTP 是媒体时钟(90 kHz),需要 RTCP 才能映射到绝对时间。

五、四个协议总对比

RTMP HTTP-FLV HLS RTSP/RTP
层级 应用层,完整协议 HTTP + FLV 容器 HTTP + 分片 控制协议 + RTP 数据
传输 TCP TCP(HTTP) TCP(HTTP) UDP 或 TCP 交织
延迟 1~3s 1~3s 传统 1030s,LL-HLS 25s 0.1~1s
浏览器 不支持 MSE(flv.js) 原生/MSE 不支持
CDN 友好 一般 好 最好 差
自适应码率 无 无 有 无
典型场景 推流 直播播放 兼容性播放、点播 摄像头、监控

一个典型的直播链路:

1
2
3
4
5
OBS ──RTMP──→ 源站(SRS/nginx-rtmp)──→ HTTP-FLV(Web/App 播放)
├→ HLS / LL-HLS(兼容性播放)
└→ WebRTC(连麦、超低延迟)

IP 摄像头 ──RTSP/RTP──→ 流媒体服务器(ZLMediaKit/mediamtx)──→ 转 RTMP / FLV / HLS / WebRTC 给浏览器

六、高频追问

  1. HTTP-FLV 和 RTMP 的区别? 数据格式同源(都是 FLV tag body),RTMP 是私有协议自带 Chunk 复用和命令交互;HTTP-FLV 就是 HTTP 响应里持续写 FLV。HTTP-FLV 更容易过防火墙、走 CDN,也方便做鉴权和统计。
  2. 为什么直播推流用 RTMP,播放却不用? RTMP 只能靠 Flash 在浏览器播放,Flash 淘汰后没有原生支持;推流端(OBS、编码器)实现成熟,所以推流继续沿用。
  3. HLS 怎么降延迟? 缩短分片和 GOP;改用 fMP4 + LL-HLS(Partial Segment + 阻塞刷新);播放器缩小缓冲、调整起播位置。代价是请求量增加。
  4. RTSP 为什么经常花屏? UDP 丢包导致 NALU 分片(FU-A)不完整,解码出错,直到下一个关键帧才恢复。解决:改 TCP 交织传输,或接收端检测序列号缺口后丢弃整帧并等待 I 帧。
  5. RTSP 的 SDP 里没有 SPS/PPS 怎么办? 部分摄像头会在码流中周期性插入 SPS/PPS(随 I 帧发送),解码器要从码流里解析,不能只依赖 SDP。
  6. HLS 的分片为什么必须从关键帧开始? 每个分片要能独立解码,播放器可能从任意分片起播或切换码率,不以 IDR 开头就花屏;多码率时各档位的分片边界还要对齐,才能无缝切换。

七、建议

如果你的项目里拉过 B 站这类直播流,面试时可以主动讲你实际接触到的协议(FLV 流还是 HLS 分片)、遇到过的问题(起播黑屏、时间戳跳变、断流重连),这比背协议细节更有说服力。

动手验证的话,可以用 ffprobe/ffplay 分别拉 HTTP-FLV、HLS、RTSP 流,再配合 Wireshark 看报文,印象会很深。

需要的话,我可以把 RTMP / HTTP-FLV / HLS / RTSP / WebRTC 合在一起出一套模拟面试题,你先答,我再逐题点评。

是的,HTTP-FLV 通常用 HTTP/1.1 的 Transfer-Encoding: chunked,但它和 RTMP 的 Chunk 名字相同,作用完全不同。 先把这几个”分片”分到不同层级,就不会混了。

一、HTTP chunked 是什么

HTTP 响应的长度未知(直播永远不结束),所以不能写 Content-Length。HTTP/1.1 的解决办法是分块编码,每块的格式是:

1
2
3
4
<十六进制长度>\r\n
<数据>\r\n
...
0\r\n\r\n ← 结束块(直播里永远不会发)

要点:

  • 它只是传输层的”定界”手段,告诉客户端”这一块有多长,流还没结束”。
  • 块边界和 FLV tag 边界没有任何关系。一个 FLV tag 可能跨多个 HTTP 块,一个块里也可能有好几个 tag。客户端的 HTTP 库会先拆掉 chunked 封装,应用层(比如 flv.js 用 fetch 的 ReadableStream)拿到的只是一条连续的字节流,自己按 FLV 格式解析。
  • 不是必须用 chunked:HTTP/1.0 风格的”不带长度,服务器关闭连接表示结束”也可以。HTTP/2、HTTP/3 里根本没有 chunked 编码,数据靠 DATA 帧传输,流式下发照样成立。

二、和 RTMP Chunk 的区别

HTTP chunked RTMP Chunk
层级 HTTP 传输编码 RTMP 应用层协议自己的格式
目的 在不知道总长度时定界 多路复用,让大消息不阻塞小消息
多路复用 没有,一个响应就是一条串行字节流 有,不同 csid 的 chunk 可以交织发送
头部 只有长度 fmt 0/1/2/3 四种头,压缩重复字段
是否理解内容 完全不理解数据是什么 知道消息类型、流 ID、时间戳
块大小 由服务器随意决定 默认 128 字节,可用 Set Chunk Size 协商
与媒体边界的关系 无关 一个 Message 拆成多个 Chunk,接收端按 csid 重组回完整 Message

核心区别:RTMP 的 Chunk 是为了让音频、视频、控制命令在同一条 TCP 连接里穿插传输(比如一个几百 KB 的 I 帧拆开发,中间可以插入音频包),HTTP chunked 只是一条流的长度标记,不存在交织。

HTTP-FLV 能做到的”音视频同时下发”,靠的是 FLV 容器本身把音视频 tag 按时间顺序串行排好,而不是靠多路复用。所以它的音视频 tag 必须在服务器端就按时间戳顺序交错写入,而 RTMP 是由 chunk stream id 区分。

三、这几个协议里的”分片”各是哪一层

可以把所谓”分片”分成三个层面:

层面 谁在做 粒度 目的
① 传输/定界 HTTP chunked、RTMP Chunk、TCP 分段 字节~KB 在字节流上划分边界、复用
② 媒体打包 RTP 的 FU-A、TS 的 188 字节包 约 188 B~1.4 KB 适配网络 MTU、便于丢包恢复
③ 内容切片 HLS 的 TS/fMP4 分片、LL-HLS 的 Part 秒级 / 百毫秒级 让每个文件可独立下载、缓存、解码

逐个协议对照:

协议 分片方式 边界是否有媒体语义 丢包/出错时
RTMP Message → Chunk(默认 128 B) 无。Chunk 只是搬运单位 跑在 TCP 上,由 TCP 重传,应用层不处理丢包
HTTP-FLV 无自己的分片,一条持续 HTTP 响应,底层可能是 HTTP chunked 无。边界靠 FLV tag 自己的长度字段 同样由 TCP 重传
HLS 按关键帧切成多秒的文件,每片可独立解码 有,必须从 IDR 开始,片间边界对齐 某个分片下载失败可重试或换码率,影响限于这一片
RTSP/RTP 一帧按 MTU 拆成多个 RTP 包(FU-A),用序列号标识 有部分语义:M 位标记帧尾,时间戳相同的包属于同一帧 UDP 丢包由接收端用序列号检测,只能丢弃整帧并等下一个关键帧

四、一个容易混淆的点:RTMP Chunk 不是为了适配 MTU

RTMP 的 Chunk 大小(128 B 或 4096 B)和网络 MTU 没关系,TCP 层会自己再分段。拆 Chunk 是为了应用层的公平调度和头部压缩。而 RTP 的 FU-A 分片才是专门按 MTU 来切的,因为 UDP 没有分段重组能力,IP 层分片还会放大丢包影响。

五、面试回答模板

HTTP-FLV 用的 chunked 是 HTTP/1.1 的传输编码,用来在响应长度未知时定界,和 FLV 的 tag 边界无关,客户端拿到的是连续字节流;HTTP/2 以后甚至没有 chunked。RTMP 的 Chunk 是协议自己的应用层机制,把 Message 拆成小块,不同 chunk stream 可以交织,解决大帧阻塞小包的问题,并通过 fmt 0~3 压缩头部。HLS 的分片是内容层面的切片,按关键帧切成几秒的独立文件。RTP 的分片是按 MTU 把一帧拆成多个包,靠序列号检测丢包。四者层级和目的都不同。