🎬 第三章 FFmpeg三大常用工具及应用选项详解
📄 一句话概括

-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 秒实验

  1. 数能读的:ffmpeg -demuxers | wc -l,与 -muxers 对比差值。
  2. 按后缀查:ffmpeg -demuxers | grep -i m4a,看它归在哪个 demuxer 下。
  3. 裸流硬指定:ffmpeg -f h264 -i 裸流.h264 -c copy 出.mp4,不加 -f 会解出垃圾。

常见误区

  1. 以为 .m4a 和 .mp4 是两个组件。它们是同一个 demuxer 声明的两个后缀,参数完全一致。
  2. 读裸流时不写 -f。没有容器头可看,ffmpeg 只能靠起始码猜,猜错就解出垃圾。
  3. 以为读得比写得多是 ffmpeg 的特殊设计。这是格式演进的必然结果,任何解码库都这样。
自测三问(点开对答案) 1. `-demuxers` 比 `-muxers` 多出来的那 186 个是什么? —— 全是「只能读」的单向老格式,读得出来但没人再往里写,所以只出现在 demuxer 一侧。 2. 为什么读 `.m4a` 和读 `.mp4` 用的参数完全一样? —— 因为它们由同一个 demuxer(`mov,mp4,m4a,3gp,3g2,mj2`)处理,只是后缀不同。 3. 读裸流为什么要手动 `-f h264`? —— 裸流没有容器头,ffmpeg 只能按起始码猜格式,猜不出来就会解出一堆垃圾。

相关笔记