4、容器格式探秘:封装格式原理、常见格式对比(MP4/MKV/TS/FLV)、muxer与demuxer参数
说到封装格式,我经常被问到:「编码和封装到底有啥区别?」
打个比方你就明白了。编码是「怎么把肉做成牛排」——H.264、AAC 这些是烹饪方法。封装是「用什么盘子端上来」——MP4、MKV 就是不同的盘子。你煎了块顶级和牛,结果拿个漏勺端上来,那也白搭。
这一章,我们就来聊聊这些「盘子」的讲究。
4.1 封装格式的核心原理
封装格式,说白了就是一套打包规则。它把视频流、音频流、字幕、元数据(metadata)这些东西,按特定结构塞进一个文件里。
一个典型的封装文件包含三个部分:
- 容器头(Header):文件类型、时长、编码信息、索引表。
- 媒体数据(Media Data):实际的视频帧、音频采样,按时间顺序排列。
- 元数据(Metadata):封面、章节信息、标签(Title、Artist 等)。
为什么需要封装?因为播放器需要知道「第 10 秒的视频数据在哪」。没有索引表,播放器就得从头扫到尾,那拖动进度条就成噩梦了。
核心要点:封装格式决定了文件的兼容性、功能丰富度和流式传输能力。选错容器,轻则播放器不认,重则直播卡顿。
下面这张图,是我画的一个封装结构示意,帮你直观理解数据是怎么组织的:
4.2 常见格式对比:MP4 / MKV / TS / FLV
我这些年经手过的项目,基本就这四种格式打转。每个都有它的脾气。
| 特性 | MP4 | MKV | TS | FLV |
|---|---|---|---|---|
| 全称 | MPEG-4 Part 14 | Matroska | MPEG Transport Stream | Flash Video |
| 兼容性 | 极高(浏览器、手机、电视) | 中高(需播放器支持) | 中(广电、直播设备) | 中(Flash 已淘汰) |
| 流式传输 | 支持(需 moov 前置) | 不支持原生流式 | 原生支持(直播标准) | 支持(RTMP 协议) |
| 字幕支持 | 有限(仅 timed text) | 极强(ASS/SSA/SRT 等) | 有限(DVB 字幕) | 不支持 |
| 多音轨 | 支持 | 支持 | 支持 | 有限 |
| 容错性 | 低(损坏后难恢复) | 中(可部分恢复) | 极高(丢包仍可播放) | 低 |
| 典型场景 | 点播、移动端、Web | 高清收藏、多轨封装 | 数字电视、HLS 切片 | 老旧直播、RTMP 推流 |
我的经验:如果你做的是 Web 点播,无脑选 MP4。但记得把 moov box 挪到文件头,不然用户得等整个文件下载完才能播放。用 -movflags +faststart 就能搞定。
4.3 深入理解 MP4:moov box 与 faststart
MP4 的结构基于「box」的概念。每个 box 有类型、大小、数据。最关键的是 moov box——它存着整个视频的索引表。
默认情况下,FFmpeg 把 moov 放在文件末尾。这意味着播放器必须下载完整个文件,才能知道「第 5 分钟的视频块在哪」。你想想看,一个 2GB 的电影,用户得等多久?
解决方案就是 -movflags +faststart:
ffmpeg -i input.mp4 -c copy -movflags +faststart output.mp4
这个参数把 moov box 挪到文件开头。播放器拿到前几 KB 就能开始拖动了。
注意:如果你用 -c copy 做流式拷贝,记得先确认源文件的 moov 位置。我曾经遇到一个项目,直播录制的 MP4 没有 moov,结果播放器直接崩溃。后来加了个 -movflags +faststart 重新 remux 才解决。
4.4 MKV:为什么它适合做收藏格式
MKV 是开源社区的心头好。它的设计哲学就是「什么都能装」。H.264、H.265、VP9、AV1,甚至 VC-1 都能塞进去。音频更是通吃:AAC、MP3、FLAC、DTS、TrueHD。
我个人习惯用 MKV 做蓝光备份。因为蓝光原盘通常有多条音轨(英语 DTS-HD、日语 AAC、评论音轨),MKV 能完美保留,还能带上 ASS 特效字幕。
一个典型的 MKV 封装命令:
ffmpeg -i video.h264 -i audio.flac -i subtitles.ass \
-c copy -map 0 -map 1 -map 2 \
output.mkv
这里 -map 是流映射,0 是视频,1 是音频,2 是字幕。不加 map 的话,FFmpeg 会按默认规则选流,容易丢东西。
避坑指南:MKV 虽然强大,但浏览器原生不支持。如果你做 Web 播放,别用 MKV。我见过有人把 MKV 直接丢到 <video> 标签里,结果 Chrome 直接报错。
4.5 TS:直播场景的硬通货
TS(Transport Stream)是广电行业的老兵。它的设计目标是在有损信道(比如卫星信号、有线电视)上稳定传输数据。所以 TS 的包很小——188 字节一个包,每个包都有同步头。
为什么直播用 TS?因为容错。如果网络丢了一个包,TS 流不会整个崩溃,后面的包还能继续解析。MP4 就不行,丢一个 moov 就全废了。
HLS(HTTP Live Streaming)就是基于 TS 的。Apple 把 TS 切片成一个个小文件(.ts),然后用一个 m3u8 索引文件管理。FFmpeg 生成 HLS 的典型命令:
ffmpeg -i input.mp4 -c:v libx264 -c:a aac \
-f hls -hls_time 10 -hls_list_size 0 \
output.m3u8
-hls_time 10 表示每个切片 10 秒。-hls_list_size 0 表示保留所有切片(不限制列表长度)。
小技巧:如果你做低延迟直播,可以把切片时长降到 2-3 秒。但切片越小,服务器压力越大。我一般取 4 秒作为平衡点。
4.6 FLV:老古董但还没死
FLV 是 Flash 时代的产物。按理说 Flash 都淘汰了,FLV 也该进坟墓。但 RTMP 协议还在用,而 RTMP 的默认封装就是 FLV。
为什么?因为 FLV 结构简单,头部固定,非常适合流式写入。你想想看,直播推流时,编码器一边编码一边把数据塞进 FLV,播放器一边接收一边解码。这种「边写边读」的能力,MP4 做不到(除非用 fragmented MP4)。
FFmpeg 推 RTMP 流时,默认就是 FLV 封装:
ffmpeg -i input.mp4 -c copy -f flv rtmp://server/live/stream
注意 -f flv 必须指定。如果不加,FFmpeg 会尝试根据输出后缀推断格式,但 RTMP URL 没有后缀,所以得手动指定。
警告:FLV 不支持 HEVC(H.265)和 AAC 的某些高级配置。如果你推 HEVC 视频,FLV 会直接报错。这时候得用 fragmented MP4(fMP4)或者 TS 替代。
4.7 Muxer 与 Demuxer 参数详解
Muxer(复用器)负责把多个流合成一个容器。Demuxer(解复用器)负责从容器里拆出各个流。FFmpeg 里,muxer 和 demuxer 都有各自的参数。
4.7.1 常用 Muxer 参数
| 参数 | 适用格式 | 说明 |
|---|---|---|
-movflags +faststart |
MP4 | 将 moov box 移到文件头,支持流式播放 |
-movflags +frag_keyframe+empty_moov |
MP4 | 生成 fragmented MP4,适合直播录制 |
-hls_time |
HLS | 设置每个 TS 切片的时长(秒) |
-hls_list_size |
HLS | 限制 m3u8 索引中的切片数量(0 表示不限) |
-hls_segment_filename |
HLS | 自定义切片文件名模板 |
-metadata |
通用 | 添加元数据,如 -metadata title="My Video" |
-map_metadata |
通用 | 从源文件复制元数据到输出 |
4.7.2 常用 Demuxer 参数
| 参数 | 适用格式 | 说明 |
|---|---|---|
-ss |
通用 | 跳转到指定时间点开始解码(精确 seek 依赖关键帧) |
-t |
通用 | 限制解码时长 |
-to |
通用 | 解码到指定时间点结束 |
-analyzeduration |
通用 | 设置分析时长,用于探测流信息(默认 5 秒) |
-probesize |
通用 | 设置探测数据量(字节),影响流识别准确性 |
-correct_ts_overflow |
MP4/TS | 修正时间戳溢出(直播流常见问题) |
实战经验:有一次我处理一个损坏的 MP4 文件,用 -analyzeduration 100M -probesize 100M 强制 FFmpeg 多读一些数据,才把流信息完整探测出来。默认值太小,遇到大文件或损坏文件容易报错。
4.8 格式转换实战:MP4 转 MKV 保留多轨
假设你有一个 MP4 文件,里面有一条视频流、两条音频流(英语、日语)、一条字幕流。你想转成 MKV 保留所有轨道。
ffmpeg -i input.mp4 \
-map 0:v -c:v copy \
-map 0:a:0 -c:a:0 copy \
-map 0:a:1 -c:a:1 copy \
-map 0:s -c:s copy \
-metadata:s:a:0 language=eng \
-metadata:s:a:1 language=jpn \
-metadata:s:s:0 language=chi \
output.mkv
这里 -map 0:a:0 表示「源文件的第 0 个输入的第 0 条音频流」。不加这个,FFmpeg 默认只选一条音频。
建议:转换前先用 ffprobe input.mp4 看看流信息。我每次都会先跑一下 ffprobe,确认轨道数量和编码格式,避免转出来发现少东西。
4.9 本章小结
封装格式这东西,选对了事半功倍,选错了处处踩坑。我的建议是:
- 点播分发:用 MP4,加
+faststart。 - 高清收藏:用 MKV,保留多轨和字幕。
- 直播推流:用 TS 或 fragmented MP4。
- RTMP 推流:用 FLV,但注意编码限制。