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

format 段回答「这个文件整体是什么」。 duration 为 N/A、码率离谱地小,这两个异常最值得盯。

📝 本节和前面几节的关系

3.3.1 讲的是容器怎么切包,这一节跳到最外层看整个文件。 3.3.3 再往下钻到最细的帧。

一、五个关键字段

format 段的字段不多,但每一个异常都有明确指向:

字段 含义 异常值意味着


format_name 容器名,可能一串别名 空或 unknown duration 时长(秒) N/A,索引坏了 size 文件字节数 比实际文件小 bit_rate 平均码率 离谱地小 nb_streams 流条数 比你预期少

第二行是最常用的诊断点:duration 是 N/A,几乎总是索引坏了。 那种文件能播开头,但进度条拖不动,ffmpeg 转它会在结尾出错。

第五行的「比预期少」也很典型:你以为转出来带音频,结果 nb_streams 是 1。 原因通常是忘了 -map——3.9 会专门讲这个。

二、format_name 那一串逗号是什么

第一次看到 mov,mp4,m4a,3gp,3g2,mj2 会以为有六个格式,其实是一个:

字段 值 怎么读


format_name 一串逗号分隔的名字 同一个组件的所有别名 long_name QuickTime / MOV 人看的正式名 extensions mp4/m4a/mov 等 它认哪些后缀

所以判断「这是什么容器」要看 long_name,别看 format_name 那一串。 那一串是给程序匹配用的,不是给人读的。

这也解释了为什么 .m4a 和 .mp4 由同一个组件处理:它们本来就是同一个容器, 只是约定用不同后缀区分「里面装的是音频还是视频」。

三、三个码率不是一回事

「码率」这个词在不同位置含义不同,混用会算错带宽:

说法 算的是什么 什么时候看


文件码率 总字节 ×8 ÷ 总时长 估传输要多久 视频码率 视频字节 ×8 ÷ 总时长 估画质 音频码率 音频字节 ×8 ÷ 总时长 估音质

注意单位:ffprobe 的 bit_rate 是「比特每秒」,而文件大小按字节算,差 8 倍。 这个 8 倍是算带宽时最常见的错误来源——报了个字节数当带宽,差了整整八倍。

第一行也不等于第二三行之和:容器本身还有索引、头、元数据的开销。 文件越小这个差值占比越大,几百 KB 的片子容器开销能占好几个百分点。

四、动手 Demo:一个文件最有用的几个数

把一份素材的 format 段原样打出来,并亲手算一遍文件码率。 重点是看 format_name 那一串别名,以及码率的单位换算。

import os
import av

c = av.open(u"src.mp4")
f = c.format
dur = float(c.duration) / 1000000
nb = len(c.streams)
c.close()

sz = os.path.getsize(u"src.mp4")
print(u"format_name  %s" % f.name)
print(u"long_name    %s" % f.long_name)
print(u"duration     %.2f 秒" % dur)
print(u"size         %d 字节" % sz)
print(u"bit_rate     %.0f bit/秒" % (sz * 8 / dur))
print(u"nb_streams   %d" % nb)

# 别名与扩展名
ext = getattr(f, u"extensions", None)
print(u"扩展名 %d 个:%s"
      % (len(ext) if ext else 0,
         u"、".join(sorted(ext)[:6]) if ext else u"无"))

实跑输出:

--------------------------------------------------------------------
ffprobe -show_format——一个文件最有用的几个数
--------------------------------------------------------------------

**① format 段里的关键字段**

  字段            含义                     常见异常值
  ----------------------------------------------------------
  format_name     容器名,可能一串别名     空或 unknown
  duration        时长(秒)               N/A,索引坏了
  size            文件字节数               比实际小
  bit_rate        平均码率                 离谱地小
  nb_streams      流条数                   比你预期少

**第二列的异常值不是随便举的:duration 为 N/A 是 mp4 索引损坏最典型的表现。**
**那种文件能播开头,但进度条拖不动,ffmpeg 转它也会在结尾出错。**

**② 本机这份素材的 format 段**

  字段            值
  -------------------------------------------
  format_name     mov,mp4,m4a,3gp,3g2,mj2
  long_name       QuickTime / MOV
  duration        5.00 秒
  size            9606 字节
  bit_rate        15370 bit/秒
  nb_streams      1

**`format_name` 那一串逗号是别名,不是多个格式——mp4、m4a、mov 都由同一个组件处理。**
**所以判断「这是什么容器」要看 long_name,别看 format_name 那一串。**

**③ 码率这个数最容易误会**

  说法         算的是什么            什么时候看
  ---------------------------------------------------
  文件码率     总字节 ÷ 总时长       估传输要多久
  视频码率     视频字节 ÷ 总时长     估画质
  音频码率     音频字节 ÷ 总时长     估音质

**三个数加起来不等于第一个:容器本身还有索引、头、元数据的开销。**
**文件越小,这个差值占比越大——几百 KB 的片子,容器开销能占好几个百分点。**

**④ duration 是从哪来的**

**mp4 的时长记在文件尾的索引里,所以能直接读出来,不用解码。**
**而有些格式(比如裸 H.264 流)根本没有时长这个字段,ffprobe 只能报 N/A。**
**那种情况下想算时长,只能把整片解一遍数帧数——这就是它慢的原因。**

format_name 那一串逗号是别名,六个名字对应一个组件。 所以判断容器类型要看 long_name,那一串是给程序做匹配用的。

bit_rate 那一行是「字节数 × 8 ÷ 秒数」,单位是比特每秒。 忘了乘 8 就会把带宽算小八倍——这是报「传输要多久」时最常见的错。

扩展名那一行说明了「猜格式」的依据:demuxer 自己声明认得哪些后缀。 所以改扩展名不影响识别,因为真正的判断靠文件头,不靠后缀。

30 秒实验

  1. 看一次:ffprobe -show_format 片子.mp4,重点确认 duration 不是 N/A。
  2. 算一次:拿文件大小乘 8 除时长,和 ffprobe 报的 bit_rate 对一下,看差多少。
  3. 造一个坏文件:truncate -s 一半大小 片子.mp4,再看 duration 是不是变成 N/A。

常见误区

  1. 把 bit_rate 当字节每秒。它是比特每秒,差 8 倍。报带宽时算错这一项最要命。
  2. 以为 format_name 那串逗号是多个格式。那是同一个组件的多个别名。
  3. duration 是 N/A 就以为文件废了。能用 -c copy 重封装救回来,索引会重建。
自测三问(点开对答案) 1. `duration` 显示 N/A 通常说明什么? —— 容器的索引坏了或缺失。这种文件能播开头但拖不动进度条,用 `-c copy` 重封装通常能修好。 2. 为什么 format_name 是一串逗号分隔的名字? —— 那是同一个组件的多个别名。mp4、m4a、mov 都由这个组件处理,所以列在一起。 3. 文件码率怎么算?单位是什么? —— 文件字节数 × 8 ÷ 时长,单位是比特每秒。乘 8 是因为字节和比特差八倍。

相关笔记