- 教程
- 技术博客
- 文档
【免费下载链接】YCBlogs
技术博客笔记大汇总,包括Java基础,线程,并发,数据结构;Android技术博客等等;常用设计模式;常见的算法;网络协议知识点;部分flutter笔记;还包括平时开发中遇到的bug汇总,当然也在工作之余收集了大量的面试题,长期更新维护并且修正,持续完善……开源的文件是markdown格式的!转载请注明出处,谢谢!
本篇技术指南以仓库文档 net/16.看直播流程.md 为主体,围绕"视频到底是怎么从主播摄像头一路传到观众屏幕"这一核心问题,系统梳理视频编码、NALU 封装、RTMP 推流/拉流、流媒体分发与解码播放的完整链路。读完你将掌握视频压缩的原理与帧类型、H.264 码流结构、RTMP 握手与 Chunk 分块机制,以及流媒体 CDN 的部署形态,能看懂直播场景下的网络协议设计与常见性能问题。
01. 三个名词系列:先给视频技术"定个位"
无论是直播还是点播,本质都是对视频数据的传输。视频人人都爱看,但视频技术里的名词实在太多,这里先列出三个名词系列,建立整体印象:
- 名词系列一(文件格式):AVI、MPEG、RMVB、MP4、MOV、FLV、WebM、WMV、ASF、MKV。其中 RMVB、MP4 最常见——这是视频"装"在什么文件里,即封装容器格式。
- 名词系列二(编码标准):H.261、H.262、H.263、H.264、H.265。这一系列普通人很少听说,但它是视频压缩的核心,要重点关注 H.264。
- 名词系列三(MPEG 家族):MPEG-1、MPEG-2、MPEG-4、MPEG-7。MPEG 好像听说过,但后面的数字代表什么,又熟悉又陌生。
先回答一个基础问题:视频是什么?其实就是快速播放一连串连续的图片。
- 每一张图片称为一帧(Frame)。只要每秒钟帧数足够多、播放足够快,比如每秒 30 帧,人眼就分辨不出这是独立图片——这就是帧率(FPS,Frames Per Second)。
- 每一帧由像素组成,假设分辨率为 1024×768(这个像素数不算多),每个像素由 RGB 三色组成,每种颜色 8 位,共 24 位。
来算一笔账,看每秒钟的视频有多大:
每秒数据量 = 30 帧 × 1024 × 768 × 24 bit = 566,231,040 bit = 70,778,880 Byte ≈ 67.5 MB/s如果是一分钟呢?约 4,246,732,800 Bytes,已经接近 4 个 GB。这个数据量完全无法存储和传输——硬盘很快会满,带宽也远远不够用。解决办法就是编码:用尽量少的 bit 保存视频,同时保证播放时画面仍然精美。编码本质是一个压缩过程。这条"数据量爆炸 → 必须压缩"的推导逻辑,也是理解后续所有直播技术的前提。
02. 视频和图片为什么能压缩:四大冗余特性
之所以能对视频流中的图片进行压缩,是因为视频和图片天然存在以下特点:
| 冗余类型 | 含义 | 压缩思路 |
|---|---|---|
| 空间冗余 | 图像相邻像素之间有较强相关性,相邻像素往往是渐变的而非突变的 | 没必要每个像素都完整保存,可以隔几个保存一个,中间的用算法计算出来 |
| 时间冗余 | 视频序列的相邻图像之间内容相似,连续图片不是突变的 | 可以根据已有图片进行预测和推断,只记录变化部分 |
| 视觉冗余 | 人的视觉系统对某些细节不敏感 | 允许丢失一部分人眼不易察觉的数据 |
| 编码冗余 | 不同像素值出现的概率不同 | 概率高的用较少字节、概率低的用较多字节,类似霍夫曼编码(Huffman Coding)的思路 |
编码算法非常复杂且多种多样,但所有编码过程本质相似:先利用空间冗余压缩单帧内的数据,再利用时间冗余压缩帧与帧之间的数据,最后通过熵编码(如 Huffman、CABAC)压缩符号本身的表示。围绕这些原理,仓库中 net/01.请求网络过程梳理.md 从应用层、传输层到网络层逐层描述了数据包在互联网上的完整旅程,而直播视频的压缩数据也要经历同样的分层封装与传输。
03. 视频编码的两大流派:ITU 与 MPEG 的统一
编码标准不能各搞一套,否则开发视频播放的人会累死。视频编码有两大流派:
- 流派一:ITU(International Telecommunications Union,国际电信联盟)的 VCEG(Video Coding Experts Group)。既然是电信组织,最初做视频编码主要侧重传输。名词系列二(H.26x 系列)就是这个组织制定的标准。
- 流派二:ISO(International Standards Organization)的 MPEG(Moving Picture Experts Group)。ISO 旗下 MPEG 原本主要做视频存储,比如编码后保存在 VCD、DVD 中,后来也慢慢侧重视频传输。名词系列三(MPEG 系列)就是这个组织制定的标准。
后来,ITU-T(国际电信联盟电信标准化部门)与 MPEG 联合制定了 H.264 / MPEG-4 AVC,这是直播场景中最需要重点关注的编码标准。
经过编码之后,生动活泼的一帧帧图像,就变成了一串串看不懂的二进制。这个二进制可以放进一个文件里,按照一定格式保存起来——这就是名词系列一(封装格式)。所谓封装格式,就是规定"前几个字节是什么含义、后几个字节是什么含义,数据部分存放编码结果"的容器规范。
04. 如何看直播视频:推流、接流、转码、拉流与分发
编码好的二进制不仅存在文件里,也可以通过某种网络协议封装后放在互联网上传输,这时候就可以进行网络直播了。整个直播体系包含以下几个环节:
- 推流(Publish):网络协议把编码好的视频流,从主播端推送到服务器。
- 接流(接收):服务器上运行着相同协议的服务端,接收这些网络包,从中取出视频流,这个过程称为接流。
- 转码(Transcode):服务端对视频流进行一定处理,例如从一个编码格式转成另一种格式。因为观众的客户端千差万别,转码可以保证所有观众都能看到直播。
- 拉流(Play):流处理完毕后,等待观众的客户端来请求视频流,观众客户端请求的过程称为拉流。
- 分发(Distribution):如果有大量观众同时观看一个直播,都从同一台服务器拉流压力太大,因此需要一个视频分发网络,把视频预先加载到就近的边缘节点,大部分观众从边缘节点拉取视频,降低源站服务器压力。
- 解码播放(Decode & Play):观众客户端将视频流拉下来后,需要解码——即编码过程的逆过程,把二进制重新转变成一帧帧图片,在客户端播放出来。
分发网络的具体形态,在仓库文档 net/14.CDN是什么东西.md 中有详细展开:CDN 系统分为边缘节点、区域节点、中心节点多层缓存,用户就近访问,不命中再逐级向上回源,如同电商的分布式仓储体系。
05. 编码过程详解:I 帧、P 帧、B 帧与 NALU 结构
5.1 视频序列的三种帧
如果每张图片都完整编码,数据量依然巨大,因此会将视频序列分成三种帧:
- I 帧(Intra-coded frame,关键帧):包含完整的图片,只需本帧数据即可完成解码。
- P 帧(Predictive frame,前向预测编码帧):表示这一帧与之前一个关键帧(或 P 帧)的差别。解码时需要把之前缓存的画面,叠加本帧定义的差别,生成最终画面。
- B 帧(Bi-directional frame,双向预测内插编码帧):记录本帧与前后帧的差别。解码 B 帧不仅要取得之前的缓存画面,还要解码之后的画面,通过前后画面数据与本帧数据叠加得到最终画面。
可以看出,I 帧最完整,B 帧压缩率最高。压缩后帧的序列通常按照 IBBP 的间隔出现,这就是时序编码的基本思想。
5.2 一帧的空间划分:片、宏块、子块
在一帧中,画面被分成多个片(Slice),每个片又分成多个宏块(Macroblock),每个宏块再分成多个子块(Sub-block)。这样把一张大图分解成一个个小块,方便进行空间上的编码——块与块之间可以独立或参考预测,配合空间冗余特性实现压缩。
5.3 NALU:为网络传输而生的码流结构
尽管时空非常立体地组成了一个序列,最终还是要压缩成一个二进制流。这个流是有结构的,由一个个NALU(Network Abstraction Layer Unit,网络提取层单元)组成。设计成这种格式就是为了传输——网络上默认传输的是一包包数据,因此码流也分成一个个单元。
每个 NALU 的结构为:
| 起始标识符(Start Code) | NALU 头(Header) | Payload(数据) |- 起始标识符:标识 NALU 之间的间隔;
- NALU 头:主要配置 NALU 的类型;
- Payload:承载 NALU 的实际数据。
在 NALU 头中,最核心的内容是类型NAL Type,常见的两种类型是:
| NAL Type | 名称 | 含义 |
|---|---|---|
| 0x07 | SPS(Sequence Parameter Set) | 序列参数集,包含一个图像序列的所有信息,如图像尺寸、视频格式等 |
| 0x08 | PPS(Picture Parameter Set) | 图像参数集,包含一个图像的所有分片的相关信息,如图像类型、序列号等 |
在传输视频流之前,必须先传输这两类参数,否则无法解码。为了保证容错性,每一个 I 帧前面都会重新传一遍这两个参数集合。
- 如果 NALU Header 中的类型是 SPS 或 PPS,则 Payload 中存放的是真正的参数集内容;
- 如果类型是帧,则 Payload 中存放的是真正的视频数据,一帧一帧存放。由于一帧内容较多,每个 NALU 保存的是一个片(Slice)。对于每一片到底是 I 帧、P 帧还是 B 帧,在片结构里也有一个 Header,里面有类型,后面跟着片的内容。
这样整个码流格式就出来了:一个视频 → 拆分成一系列帧 → 每帧拆分成一系列片 → 每片放在一个 NALU 中 → NALU 之间通过特殊起始标识符分隔 →在每一个 I 帧的第一片前面,插入单独保存 SPS 和 PPS 的 NALU,最终形成一个长长的 NALU 序列。
06. 推流:如何把数据流打包传输到对端(RTMP 协议)
有了 NALU 码流,是否就能直接在网上传输开始直播了?还不行。还需要把这个二进制流打包成网络包进行发送,这里使用RTMP(Real-Time Messaging Protocol)协议,这就是第二个过程——推流。
6.1 建立连接:TCP 连接 + RTMP 连接
RTMP 基于 TCP,因此首先要建立 TCP 连接。关于 TCP 的可靠性机制,可参见仓库文档 net/06.TCP详细基础介绍.md:三次握手建立连接、序列号解决乱序、ACK 确认保证不丢包、滑动窗口做流量控制。
在 TCP 连接的基础上,还需要建立一个 RTMP 连接——在程序里调用 RTMP 类库的Connect函数显式创建连接。RTMP 之所以需要单独的连接,是因为双方要商量两件事:
- 版本号:如果客户端、服务器版本号不一致,则不能工作;
- 时间戳:视频播放中时间非常重要,后续数据流互通时常要携带时间戳差值,因此一开始双方就要知道对方的时间戳。
为沟通这些事情,需要发送六条消息:客户端发送C0、C1、C2,服务器发送S0、S1、S2。握手过程如下:
- 客户端发送 C0 表示自己的版本号(不必等待对方回复),然后发送 C1 表示自己的时间戳;
- 服务器收到 C0 后才返回 S0 表明自己的版本号,如果版本不匹配可以断开连接;
- 服务器发送完 S0 后不必等待,直接发送自己的时间戳 S1;
- 客户端收到 S1 后,发送确认对方时间戳的 ACKC2;
- 服务器收到 C1 后,发送确认对方时间戳的 ACKS2,握手完成。
客户端 服务器 |--- C0 (版本号) ------------------->| |--- C1 (时间戳) ------------------->| |<-- S0 (版本号) --------------------| |<-- S1 (时间戳) --------------------| |--- C2 (确认S1时间戳) ------------->| |<-- S2 (确认C1时间戳) --------------|握手之后,双方互相传递一些控制信息,例如Chunk 块大小、窗口大小等。真正传输数据时,还需要创建一个流(Stream),然后通过这个 Stream 来推流(publish)。
6.2 RTMP 消息封装:NALU → Message → Chunk
推流过程就是把 NALU 放在Message里发送,这个 Message 也称RTMP Packet 包。发送时需要注意:
- 去掉 NALU 的起始标识符,因为这部分对 RTMP 协议没有用;
- 将SPS 和 PPS 参数集封装成一个 RTMP 包先行发送,然后再发送一个个片的 NALU。
关键机制在于:RTMP 收发数据并不是以 Message 为单位的,而是把 Message 拆分成 Chunk 发送,而且必须在一个 Chunk 发送完成之后,才能开始发送下一个 Chunk。每个 Chunk 都带有Message ID,表示它属于哪个 Message,接收端按照这个 ID 将 Chunk 组装成 Message。前面连接时设置的Chunk 块大小就是指这个 Chunk。
将大消息拆成小块再发送,可以在低带宽情况下减少网络拥塞。下面是一个具体分块例子:假设一个视频消息的长度为 307,Chunk 大小约定为 128,于是拆分为三个 Chunk:
| Chunk | Type | 说明 | 承载数据 |
|---|---|---|---|
| 第 1 个 | Type=0 | Chunk 头完整;Timestamp=1000,Length=307,消息类型=9(视频),Stream ID=12346 | 128 字节 Data |
| 第 2 个 | Type=3 | 头和第一个一样,不再重复发送头 | 128 字节 Data |
| 第 3 个 | Type=3 | 剩余长度 307 − 128 − 128 = 51 字节 | 51 字节 Data |
Message: 总长 307 字节, 类型 9 (视频), Stream ID 12346, Timestamp 1000 Chunk1 [Type=0, 完整头] ----------> 128 字节 Chunk2 [Type=3, 复用头] ----------> 128 字节 Chunk3 [Type=3, 复用头] ----------> 51 字节其中Chunk Type=0 表示 Chunk 头完整;Chunk Type=3 表示头部与上一个 Chunk 一样,不再重复发送,从而节省开销。就这样,数据源源不断地到达流媒体服务器。
6.3 大规模分发:中心层 + 边缘层
当大量观众通过 RTMP 协议从流媒体服务器拉流时,如果都去同一个地方拉取,服务器压力会很大;而且用户分布在全国甚至全球,都去统一地点下载,时延也会很长,因此必须引入分发网络:
- 边缘层:部署在全国各地、横跨各大运营商,与用户距离很近;
- 中心层:流媒体服务集群,负责内容转发,也负责转码服务(例如把 RTMP 协议的码流转换为 HLS 码流);
- 智能负载均衡系统:根据用户地理位置信息,就近选择边缘服务器,为用户提供推流/拉流服务。
这一分发架构与 net/14.CDN是什么东西.md 中描述的 CDN 分层缓存体系一致:边缘节点无法命中时向区域节点、中心节点逐级回源。该文档还特别指出,流媒体数据量大、回源压力大,因此多采用"主动推送"模式把热点数据提前推到边缘节点,而非静态页面的"未命中再拉取"模式;同时流媒体 CDN 常提供预处理服务,如转码成不同码流、对视频分片,让用户按带宽选择"超清、标清、流畅"等清晰度。
6.4 关于直播实时性的延伸:为什么很多直播用 UDP 自研协议
仓库文档 net/07.UDP详细基础介绍.md 对直播场景的协议选择做了重要补充:RTMP 基于 TCP,而 TCP 的严格顺序传输要求前一个包收到后才能确认下一个。对直播而言,老的视频帧丢了其实也就丢了,就算重传回来用户也不在意,他们要的是新帧;如果一直等待,就会卡顿,所以直播实时性比可靠性更重要,宁可丢包也不要卡顿。此外网络不好时 TCP 会主动降低发送速度,这对直播是致命的。因此很多直播应用基于 UDP 实现了自己的视频传输协议,在应用层做重传与拥塞处理。这解释了为什么大型直播系统在 RTMP/HLS 之外,还会大量采用基于 UDP 的私有协议(如 QUIC 类方案)来保障低时延。
07. 拉流:观众的客户端如何看到视频
接下来看观众的客户端通过 RTMP 拉流的过程:
- 客户端通过 RTMP 协议从边缘节点拉取码流,先读到的是 H.264 的解码参数,即SPS 和 PPS;
- 之后持续收到一个个 Chunk,接收端按 Message ID 将 Chunk 组装成 Message,再还原为 NALU;
- 将 NALU 组成的一个个帧交给解码器解码;
- 解码出的帧交给播放器播放——一个绚丽多彩的视频画面就出来了。
可以看到,拉流正是推流的逆过程:RTMP 分块传输 → 组装 Message → 提取 NALU → 依赖 SPS/PPS 参数解码 → 按 I/P/B 帧关系还原画面 → 播放。整个过程中,CDN 边缘节点承担了绝大部分流量,源站与中心层只负责转码与兜底分发。
08. 小结:一条完整的直播链路
回顾全文,直播链路可归纳为四个关键环节:
- 编码压缩:视频名词多但万变不离其宗,编码两大流派(ITU 与 MPEG)最终达成一致,通过时间、空间的各种算法压缩数据,H.264/MPEG-4 AVC 是直播最核心的编码标准;
- NALU 组织:压缩好的数据为了传输组成一系列 NALU,按照帧和片依次排列,每个 I 帧前携带 SPS/PPS 参数集保证可解码;
- RTMP 打包:排列好的 NALU 在网络传输时按 RTMP 包格式包装,RTMP 包再拆分成 Chunk 传输,Chunk Type=3 复用头部以减少开销;
- 分发与拉流:推送到流媒体集群的视频流经过转码和 CDN 边缘分发,客户端通过 RTMP 协议拉取,还原为 NALU,解码成视频帧进行播放。
如果继续深入这个话题,建议配套阅读仓库中以下关联文档:直播分发依赖的 CDN 体系见 net/14.CDN是什么东西.md;RTMP 底层 TCP 的可靠性、流量控制与拥塞控制见 net/06.TCP详细基础介绍.md;直播实时性痛点与基于 UDP 的自研协议背景见 net/07.UDP详细基础介绍.md;HTTP 2.0 多路复用与 QUIC 的演进(同样影响直播传输)见 net/15.案例看新闻流程.md。
- 教程
- 技术博客
- 文档
【免费下载链接】YCBlogs
技术博客笔记大汇总,包括Java基础,线程,并发,数据结构;Android技术博客等等;常用设计模式;常见的算法;网络协议知识点;部分flutter笔记;还包括平时开发中遇到的bug汇总,当然也在工作之余收集了大量的面试题,长期更新维护并且修正,持续完善……开源的文件是markdown格式的!转载请注明出处,谢谢!
相关推荐
如何快速上手todo.txt:5分钟掌握核心格式规则
如何快速上手todo.txt:5分钟掌握核心格式规则 todo.txt是一款轻量级的任务管理工具,它采用纯文本格式存储任务,让你无需依赖复杂软件即可高效管理待办
流放之路Build规划终极指南:PathOfBuilding三步搞定天赋与伤害计算
流放之路Build规划终极指南:PathOfBuilding三步搞定天赋与伤害计算 天赋点加了又删、装备取舍全凭感觉,是你玩流放之路最磨人的两件事。PathOf
桌面应用如何快速实现iOS直播推流:RTMP协议与GPUImage滤镜的完整指南
如何快速实现iOS直播推流:RTMP协议与GPUImage滤镜的完整指南 在移动直播应用开发中,实现流畅的视频推流和实时美颜滤镜是提升用户体验的关键。TimLi
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考