什么是容器,什么是码流:MP4、FLV、TS 与裸流不要再混了
前言:为什么很多人学了一堆 H.264、RTP、FFmpeg,最后还是会把“码流”和“容器”说混
只要开始接触音视频工程,几乎迟早会遇到这些名字:
- H.264
- H.265
- AAC
- MP4
- FLV
- TS
- Annex B
- AVCC
刚开始学的时候,很多人会下意识把它们都放进一个大筐里,统称成“视频格式”。
这也不奇怪,因为从使用者视角看,它们确实都和“音视频文件能不能播”“流能不能推”“数据怎么传”有关。
但只要你真的开始写代码、抓包、看 FFmpeg、查播放器问题,很快就会发现:
如果你分不清“码流”和“容器”,后面很多东西都会一直混。
比如这些问题:
- H.264 到底是不是 MP4
- 为什么同样是 H.264,有时是
.h264,有时又在.mp4里 - FLV 和 TS 到底是“协议”还是“文件格式”
- 为什么裸流里会看到 SPS / PPS / NALU,而 MP4 里看起来又不是那个样子
- FFmpeg 里为什么有 demux / mux,但编码器又单独是一层
- 为什么推 RTMP 时常常会看到 H.264 + FLV 这样的组合
你会发现,这些问题表面不同,但根底上都在问一件事:
媒体内容本身,和承载这些内容的外层组织方式,到底怎么分层理解?
所以这篇文章的目标,就是把这件事一次讲清楚。
重点讲清:
- 什么是码流,什么是容器
- 为什么 MP4、FLV、TS 都不是“视频编码格式”
- 裸流到底裸在哪里
- 编码、码流、封装、传输在整条链路里分别站哪一层
- FFmpeg 里 demux / mux 到底在处理什么
- 如果你自己写一个最小音视频程序,这些层会怎么出现在代码里
而且这篇按你要求,我会继续把四样都带上:
- 核心关键代码示例
- 结构图
- 流程图
- 时序图
我希望你读完之后,不只是“知道容器和码流不一样”,而是真的能建立一套稳定的分层认知,以后看到 MP4、FLV、TS、裸 H.264、RTP、FFmpeg 这些东西时,脑子不会再打架。
一、先给一句最重要的话:码流是媒体内容的压缩表达,容器是对这些媒体数据及其时间信息的组织与承载
如果让我先给一句最重要的话,我会这样说:
码流是音视频内容经过编码后的压缩表达形式,而容器是把这些媒体数据、时间信息、轨道信息按某种规则组织起来的承载层。
这句话特别重要。
因为它直接把两个最容易混掉的层给分开了:
- 码流,更偏内容本体怎么被压缩表示
- 容器,更偏这些内容怎么被装起来、索引起来、同步起来、对外呈现成文件或流
你可以先粗暴记成:
- 码流:像“货物本身”
- 容器:像“装货的箱子 + 货单 + 时间表”
这个比喻虽然不完美,但对建立直觉非常有帮助。
二、先看总图:编码、码流、容器、传输到底在整条链路里处于哪一层
先上总图。
flowchart LR
A[原始音视频数据] --> B[编码器 H.264/H.265/AAC]
B --> C[压缩码流 ES]
C --> D[封装器 Mux]
D --> E[容器 MP4/FLV/TS]
E --> F[文件存储或网络传输]
如果从播放侧反过来看,就是:
flowchart LR
A[文件或网络输入] --> B[Demux 解封装]
B --> C[取出压缩码流 Packet]
C --> D[解码器]
D --> E[原始Frame / PCM]
E --> F[渲染 / 播放]
这两张图一起看,基本能把很多概念一下拉直:
- 编码器负责把原始数据压成码流
- 容器不是编码器,它只是把压缩数据组织起来
- demux / mux 工作在容器层
- decoder / encoder 工作在码流层和原始媒体层之间
这也是为什么 FFmpeg 里:
- codec 是一层
- format 又是一层
它们本来就不是一个问题。
三、什么是码流,它到底“流”在哪里
3.1 码流的本质
码流你可以先理解成:
编码器输出出来的一串压缩媒体数据。
比如:
- H.264 码流
- H.265 码流
- AAC 码流
它回答的核心问题是:
- 原始图像 / 音频样本,如何被压缩表达
- 压缩后的比特序列长什么样
- 解码器如何从这些比特里恢复出原始媒体
所以码流更偏:
内容压缩表示层。
3.2 视频码流里常见会看到什么
比如 H.264 / H.265 这类视频码流里,你常会看到:
- NALU
- SPS / PPS / VPS
- slice
- IDR / 非 IDR 图像数据
- Annex B 或 AVCC 这类组织方式
这些东西都在围绕“压缩视频内容怎么表达”展开。
也就是说,它们关心的不是:
- 文件名叫什么
- 能不能拖动进度条
- 里面有几条音轨
而是:
- 这个压缩视频内容本身怎么编码与组织
3.3 音频码流也一样
比如 AAC,码流层更关心:
- 压缩后的音频帧
- ADTS 头或 AudioSpecificConfig
- 解码器如何拿到正确参数
所以“码流”不是视频专属概念,音频也完全有。
四、什么是容器,它到底“装”了什么
4.1 容器的本质
容器你可以理解成:
把一个或多个媒体轨道、时间戳、索引、元信息按规则组织起来的封装格式。
这句话里有几个关键词特别重要:
- 一个或多个轨道
- 时间戳
- 索引
- 元信息
- 组织规则
这说明容器关心的事情,比单纯“装字节”多很多。
4.2 容器通常装哪些东西
一个典型容器里,可能会包含:
- 视频轨
- 音频轨
- 字幕轨
- 每个 packet 的时间戳
- 索引信息
- 编码参数描述
- 文件头 / 元信息
所以 MP4、FLV、TS 这些格式真正解决的是:
如何把媒体数据组织成一个可存储、可传输、可解析、可播放的整体。
4.3 容器并不决定“视频内容怎么压缩”
这点非常关键。
MP4 本身不负责定义:
- H.264 的预测结构
- H.265 的变换量化
- AAC 的编码算法
这些是编码标准的工作。
MP4 做的更像是:
- 你的视频已经压好了
- 你把压好的数据按 MP4 的规则装进来
- 再给它配上时间信息和索引信息
所以你不能把:
- MP4
n- H.264
看成同一层东西。
五、一张图看懂“码流”和“容器”为什么经常一起出现,却又根本不是一回事
flowchart TD
A[H.264 / H.265 / AAC] --> B[压缩码流层]
B --> C[MP4 / FLV / TS]
C --> D[文件或网络承载]
这张图最关键的意思就是:
- H.264/H.265/AAC 更像“里面装的内容类型”
- MP4/FLV/TS 更像“外层包装方式”
你以后看见:
- H.264 in MP4
- H.264 in FLV
- H.264 in TS
就不会再觉得矛盾了。
因为这本来就是:
同一种视频码流,被装进不同容器里。
六、什么叫裸流,裸到底是什么意思
6.1 裸流的本质
“裸流”这个词听起来很玄,其实意思很朴素。
它通常指:
没有再套完整容器层、而是更接近编码器直接输出结果的码流数据。
比如:
.h264.h265- 某些原始 AAC 数据
这些通常就比 MP4、FLV、TS 更“裸”。
6.2 它为什么叫裸
因为它通常少了很多容器层会帮你做的事,比如:
- 完整索引
- 多轨统一组织
- 拖动播放所需的结构信息
- 丰富元信息
- 文件级别头结构
也就是说,裸流更接近:
我这里只有被压好的媒体内容本体。
6.3 裸流为什么在工程里仍然很重要
因为它更接近编码器本来的输出形态。
比如你在研究:
- SPS / PPS
- NALU 切分
- Annex B / AVCC
- RTP 打包前的数据组织
这时你经常就会直接面对裸码流。
所以裸流虽然不总适合直接拿来做用户文件交付,但它在工程分析里非常重要。
七、MP4、FLV、TS 各自到底是什么定位
这一节很关键,因为很多人只知道这几个名词,却不知道它们各自擅长什么。
7.1 MP4
MP4 更偏:
- 文件存储
- 点播
- 索引和随机访问友好
- 结构化媒体文件
它通常适合:
- 本地文件
- 点播分发
- 需要清晰轨道组织的场景
7.2 FLV
FLV 更偏:
- 流式传输场景中的轻量封装
- 传统 RTMP 生态里的常见承载格式
它在很多直播推流链路里长期常见,尤其是:
- RTMP 推流时,媒体数据常按 FLV tag 方式组织
所以很多人会看到:
- H.264 + AAC + FLV + RTMP
这不是四个平级概念,而是不同层的组合。
7.3 TS
TS 更偏:
- 连续传输
- 广播 / 流媒体传输友好
- 对流式切片和容错更友好
它常出现在:
- MPEG-TS 文件
- 某些直播 / 切片 / 广播相关场景
所以你不能简单问“哪个更高级”,而要问:
当前这个场景,到底需要哪种封装能力。
八、第一段核心代码:最小 demux 流程到底在处理什么
现在上代码。
下面这个例子是一个非常典型的 FFmpeg 解封装骨架,重点是让你看见:
demux 处理的是容器,不是解码。
1 | |
这段代码最该看懂什么
最该看懂的是:
av_read_frame() 干的事不是“给你一帧画面”,而是:
从容器里拆出一个压缩 packet。
这就是 demux 的职责边界。
也就是说:
- MP4 / FLV / TS 这层先被 format 模块处理
- 取出来的是压缩数据包
- 后面要不要变成
AVFrame,是 decoder 的事
这条边界必须清楚。
九、第二段核心代码:最小 decode 流程为什么是“码流层”的工作
1 | |
这段代码说明什么
它说明解码器关心的是:
- 输入压缩 packet
- 输出原始 frame
这里处理的对象已经不再是“MP4 文件结构”,而是:
压缩码流内容。
所以你可以很清楚地看到两层分工:
- format 层处理容器
- codec 层处理码流
十、第三段核心代码:最小 mux 流程为什么是在“把码流重新装进容器”
这一层也很关键。
1 | |
这段代码最该看什么
最该看的是:
- mux 不负责重新编码内容本身
- mux 负责把已有压缩 packet 按某种容器规则写出去
也就是说,这一层干的是:
组织与封装。
它再次证明:
容器层和编码层不是一回事。
十一、第四段核心代码:裸 H.264 码流和 MP4 输入在程序入口上到底有什么差别
这段特别适合建立“裸流 vs 容器”的代码直觉。
11.1 处理 MP4 输入时
你通常可以直接:
- open input
- find stream info
- read packet
因为 MP4 已经帮你把轨道、时间、组织结构都整理好了。
11.2 处理裸 H.264 时,系统 often 需要更多帮助
裸流更接近原始压缩数据,所以在很多实现里,往往要更依赖:
- parser
- 手动送入 decoder
- 手动处理帧边界 / access unit
下面是一个简化版 parser 骨架:
1 | |
这段代码为什么有价值
因为它直接让你看到:
裸流没有容器层替你把边界和结构整理得那么舒服。
所以在工程里,裸流往往更接近码流本体,需要你更直接面对 parser / NALU / access unit 这些问题。
十二、一张流程图看懂“码流进容器”和“从容器取码流”是两件对称但不同的事
flowchart TD
A[原始Frame/PCM] --> B[编码器]
B --> C[压缩码流Packet]
C --> D[Mux封装]
D --> E[MP4/FLV/TS]
E --> F[Demux解封装]
F --> G[压缩码流Packet]
G --> H[解码器]
H --> I[原始Frame/PCM]
这张图其实就是整条媒体主线最核心的骨架之一。
如果这张图立住了,后面你再看:
- FFmpeg format
- FFmpeg codec
- MP4 / FLV / TS
- H.264 / AAC
就基本不会再乱。
十三、一张时序图看懂播放器打开 MP4 时系统到底在干嘛
sequenceDiagram
participant App as Player/App
participant Demux as Demuxer
participant Decoder as Decoder
participant Render as Renderer
App->>Demux: open MP4 file
Demux->>Demux: parse container metadata / tracks
loop read packets
Demux->>Decoder: compressed video/audio packet
Decoder->>Render: decoded frame / pcm
end
这张图特别适合纠正常见误解:
- 打开 MP4,不等于直接“读到画面”
- 中间一定有容器解析和 packet 提取
- decoder 处理的是 packet,不是整个 MP4 文件
十四、FLV、TS 为什么常常跟“传输”联系更紧,而 MP4 更像文件世界的常客
这个问题也值得单独说一下。
14.1 MP4 更偏结构化文件组织
MP4 对:
- 多轨组织
- 时间信息
- 索引
- 随机访问
这些事情做得很适合点播与文件。
14.2 FLV、TS 更常出现在流式场景里
比如:
- RTMP 常见搭配 FLV tag 组织媒体
- TS 常见于连续传输和切片相关场景
这不是说它们只能传输,或者 MP4 绝不能流式,而是说:
不同容器的设计取向和工程主战场不同。
所以你以后看到:
- RTMP + FLV
- HLS + TS
- 文件点播 + MP4
就会觉得这些组合很自然。
十五、工程里最容易混掉的 4 个错误认知
这一节我给你直接列出来。
错误 1:MP4 是视频编码格式
不是。
MP4 是容器。
错误 2:H.264 是文件格式
不是。
H.264 更准确说是视频编码标准 / 码流格式。
错误 3:FLV 和 TS 跟 H.264 是平级可替换关系
不是。
它们通常处于不同层:
- H.264 是里面的压缩视频内容
- FLV / TS 是外层承载组织
错误 4:demux 就是 decode
不是。
- demux:从容器里拆 packet
- decode:把压缩 packet 解成原始 frame
这两层不分清,FFmpeg 永远看着像一团雾。
十六、最后给一句更像工程师的话:别把“媒体内容怎么压缩”和“媒体数据怎么装起来”混成一件事
写到这里,最想落下的一句话其实就是:
音视频工程里,最基础但也最容易混的分层,就是“内容压缩层”和“数据承载层”。
如果你把这两层混掉,就会出现一连串认知问题:
- 看不懂 FFmpeg 模块分工
- 分不清 parser、demuxer、decoder 谁在干嘛
- 一看到 MP4、FLV、TS、RTP、Annex B 就容易全串
- 很难真正理解“转封装”和“转码”的区别
但如果你把这层分清,很多问题就会一下变简单:
- H.264 / H.265 / AAC 是压缩表达
- MP4 / FLV / TS 是承载组织
- demux / mux 处理容器
- decoder / encoder 处理码流与原始媒体的转换
到这一步,你的整个音视频知识体系会明显稳很多。
十七、总结
最后把这篇收成几句话。
1. 码流和容器不是一个层次的东西
- 码流:压缩后的媒体内容表达
- 容器:对媒体数据和时间信息的组织与承载
2. H.264/H.265/AAC 更偏码流层,MP4/FLV/TS 更偏容器层
它们经常组合出现,但不是平级概念。
3. 裸流更接近编码器原始输出,容器则提供更完整的组织能力
所以裸流更适合看码流本体,容器更适合文件与整体播放交付。
4. FFmpeg 里 demux/mux 和 decode/encode 分属不同层
这层边界清楚后,整个框架会好理解很多。
5. 真正该记住的不是几个名词,而是这条主线
原始媒体 -> 编码得到码流 -> mux 装进容器 -> demux 拆出 packet -> decode 还原原始媒体
如果你愿意,我下一篇建议直接接:
《音频系统基础入门:采样率、位宽、声道数与 PCM 到底怎么理解》
这篇补上之后,你这套专栏就不再是“视频特别强,音频还偏薄”,整个体系会更完整。