-demuxers 有 368 行,比 muxer 多了 186 个。 多出来的全是「只能读」的:能解出来给人看,没人再往里写。
3.11.2 看了能写的那一侧,这一节看能读的这一侧。 两边一比,就是「读多写少」这条规律最直接的证据。
一、368 对 182,差在哪
先把两个数摆在一起:
项 值 说明
-demuxers 行数 368 只能读 232 + 读写都行 136 与 muxer 相差 186 多出来的全是只能读的
读的能力比写的能力多,这是一条普遍规律: 历史格式只需要能解出来给人看,没人会再往里写新内容。
186 这个差值正好是「只能读 232」减去「只能写 46」。 换句话说,两个方向的差全部来自那些单向的老格式,读写都行的那 136 个两边持平。
二、demuxer 不只认文件,还认流和设备
这是它比 muxer 复杂的地方:输入端的东西有四种形态。
类型 例子 怎么识别
文件容器 mov/matroska/avi 看文件头 裸流 h264/hevc/aac 看起始码 网络协议 rtmp/rtsp/hls 看 URL 前缀 采集设备 dshow/gdigrab 看 -f 参数
第二行值得单独说:裸流没有容器,ffmpeg 靠「起始码」猜它是什么。
猜错时要手动 -f h264 告诉它,否则会解出一堆垃圾数据。
第三四行的共同点是「没有文件头可看」,所以必须靠外部信息指定。
这就是 -f 在输入端比在输出端更常用的原因。
三、从文件名反推 demuxer 靠什么
每个 demuxer 都声明了一组扩展名,这就是「猜格式」的依据:
项 值
format.name mov,mp4,m4a,3gp,3g2,mj2 long_name QuickTime / MOV 扩展名集合 3g2、3gp、avif、f4v、heic、heif、ism、isma
第一行那一串名字说明一件容易忽略的事:这些后缀由同一个组件处理。
所以 .m4a 和 .mp4 在 ffmpeg 眼里是同一个 demuxer,参数、行为完全一致。
这也意味着「改后缀不改内容」是不会报错的——ffmpeg 照样读得出来。 但别的软件可能就认后缀,所以改后缀这种做法只在排障时临时用。
四、动手 Demo:能读的 368 个,与写的那一侧对比
把上一段的方向翻过来:只统计「能读」的那一批,再与「能写」的对比。 差出来的那个数,就是「只能读」的老格式有多少。
import av
rd, wr, both = 0, 0, 0
for n in av.formats_available:
try:
f = av.format.ContainerFormat(n)
except Exception:
continue
if f.is_input and f.is_output:
both += 1
elif f.is_input:
rd += 1
elif f.is_output:
wr += 1
print(u"能读合计 %d" % (rd + both))
print(u"能写合计 %d" % (wr + both))
print(u"差值 %d" % (rd - wr))
# 一个具体例子:mp4 那一族由哪些后缀触发
f = av.format.ContainerFormat(u"mov")
print(u"名字 %s" % f.name)
print(u"长名 %s" % f.long_name)
print(u"后缀 %s" % f.extensions)
实跑输出:
--------------------------------------------------------------------
3.11.3 -demuxers——能读的 368 个
--------------------------------------------------------------------
**① demuxer 是「解封装器」,比 muxer 多得多**
项 值 说明
---------------------------------------------------------
-demuxers 行数 368 只能读 232 + 读写都行 136
与 muxer 相差 186 多出来的全是只能读的
**读的能力比写的能力多,这是一条普遍规律:**
**历史格式只需要能解出来给人看,没人会再往里写新内容。**
**② demuxer 不只认文件,还认「流」和「设备」**
类型 例子 怎么识别
---------------------------------------------------
文件容器 mov/matroska/avi 看文件头
裸流 h264/hevc/aac 看起始码
网络协议 rtmp/rtsp/hls 看 URL 前缀
采集设备 dshow/gdigrab 看 -f 参数
**第二行值得注意:裸流没有容器,ffmpeg 靠「起始码」猜它是什么。**
**猜错的时候要手动 `-f h264` 告诉它,否则会解出一堆垃圾。**
**③ 本机能读的一个具体例子**
项 值
--------------------------------------------------------------
format.name mov,mp4,m4a,3gp,3g2,mj2
long_name QuickTime / MOV
流数 2
扩展名集合 3g2、3gp、avif、f4v、heic、heif、ism、isma
**扩展名那一列是 demuxer 自己声明的,所以它也是「猜格式」的依据。**
**同一个 demuxer 认十几个后缀,这就是为什么 .m4a 和 .mp4 由同一个组件处理。**
第三行的差值,正好等于「只能读」减去「只能写」——也就是那些单向的老格式。 这个数在别的构建上会变,但「读的一侧更大」这个方向几乎不会变。
最后三行是「按名字查一个 demuxer」的写法:mov 这个名字就能拿到整个族。
拿到后缀那一串,就明白了为什么 .m4a、.heic、.3gp 都被同一个组件接走。
注意 extensions 那一行打出来的是一串逗号分隔的后缀,不是列表。
所以想按后缀反查 demuxer,得自己拆字符串——这也是为什么很多人干脆用 grep。
30 秒实验
- 数能读的:
ffmpeg -demuxers | wc -l,与-muxers对比差值。 - 按后缀查:
ffmpeg -demuxers | grep -i m4a,看它归在哪个 demuxer 下。 - 裸流硬指定:
ffmpeg -f h264 -i 裸流.h264 -c copy 出.mp4,不加-f会解出垃圾。
常见误区
- 以为
.m4a和.mp4是两个组件。它们是同一个 demuxer 声明的两个后缀,参数完全一致。 - 读裸流时不写
-f。没有容器头可看,ffmpeg 只能靠起始码猜,猜错就解出垃圾。 - 以为读得比写得多是 ffmpeg 的特殊设计。这是格式演进的必然结果,任何解码库都这样。
自测三问(点开对答案)
1. `-demuxers` 比 `-muxers` 多出来的那 186 个是什么? —— 全是「只能读」的单向老格式,读得出来但没人再往里写,所以只出现在 demuxer 一侧。 2. 为什么读 `.m4a` 和读 `.mp4` 用的参数完全一样? —— 因为它们由同一个 demuxer(`mov,mp4,m4a,3gp,3g2,mj2`)处理,只是后缀不同。 3. 读裸流为什么要手动 `-f h264`? —— 裸流没有容器头,ffmpeg 只能按起始码猜格式,猜不出来就会解出一堆垃圾。- ☐ (本节完,下一节 3.11.4 -devices)