🎬 第四章 FFmpeg命令行实现音视频转封装
📄 一句话概括

换盒子不换内容:包原样过去,只有目录换了写法。 FLV 的整个文件头只有 9 个字节,前三字节就是 F、L、V。

📝 本节和前面几节的关系

4.2 讲的是「拆与装」,从这一节开始换盒子。 换盒子的成败只看一件事:新盒子认不认里面装的编码。

一、换盒子的一次实测

同一份 2 秒素材,只换盒子不重编码:

文件 字节 格式名


原 MP4 37982 mov,mp4,m4a,3gp,3g2,mj2 转成 FLV 37807 flv

差 175 字节,方向是变小——这点差距就是两个盒子记账方式的差别。 包没动,所以不会差出「几十个百分点」那种量级。

判断是不是真转封装,最省事的办法就是看字节与耗时: 字节几乎持平、耗时可忽略,那就是 copy;差出一截且慢,那就是重编码了。

二、FLV 的文件头极简

一共 9 个字节,把身份、版本、能力全说完了:

位置 内容 实测值


0~3 三个字母 FLV 46 4C 56 3 版本号 1 4 标志位 0x05 5~9 头长度 9

第三行那个 0x05 是「有音频」(0x04)加「有视频」(0x01)的和。 所以只读这一个字节,就知道这个文件里有没有声音、有没有画面。

对比 MP4:它的头是一个 ftyp 盒子,得按「长度+类型」的规矩解析才知道是什么。 FLV 这种「自报家门」的设计,在流式传输时省事得多——读三个字节就能分流。

三、FLV 装什么是有讲究的

能不能转成功,取决于里面装的编码,不取决于命令:

流类型 FLV 接不接 说明


H.264 视频 接 AVC 是标准配置 AAC 音频 接 — VP9/AV1 视频 不接 那是 webm 的地盘 FLAC 音频 不接 要无损得换盒子

所以「MP4 转 FLV」这句需求要拆成两步判断:盒子里装的是什么编码,FLV 接不接。 装的是 H.264 加 AAC 就一定成;装的是 VP9 就必须先重编码。

第三行那种情况有个更省事的选择:既然内容是 VP9,直接转 webm 就行, 不用为了迁就 FLV 而重压一遍——这也是「先看内容再选盒子」的实际用处。

四、动手 Demo:转成 FLV,再读它那 9 个字节的头

把 2 秒素材转成 FLV,对比字节,然后直接读文件头的前 13 个字节。 FLV 的头短到可以一个字段一个字段地念出来。

import av, os

c0 = av.open(u"m4.mp4")
c1 = av.open(u"o.flv", u"w", format=u"flv")
mp = {s.index: c1.add_stream_from_template(s) for s in c0.streams}
for pk in c0.demux():
    if not pk.size:
        continue
    pk.stream = mp[pk.stream.index]
    c1.mux(pk)
c1.close()
c0.close()

print(u"mp4 %d 字节  flv %d 字节"
      % (os.path.getsize(u"m4.mp4"), os.path.getsize(u"o.flv")))

with open(u"o.flv", u"rb") as f:
    h = f.read(13)
print(u"前三个字节 %s" % h[:3].decode(u"latin-1"))
print(u"版本 %d  标志 0x%02X" % (h[3], h[4]))
print(u"头长度 %d" % int.from_bytes(h[5:9], u"big"))

实跑输出:

--------------------------------------------------------------------
4.3.1 MP4 转 FLV——盒子换了,包没动
--------------------------------------------------------------------

**① 转一遍,看字节怎么变**

  文件         字节      格式名
  --------------------------------------------------
  原 MP4       37982     mov,mp4,m4a,3gp,3g2,mj2
  转成 FLV     37807     flv

**字节基本持平(差几十到几百),说明包原样过去了,只有目录换了写法。**

**② FLV 的文件头只有 9 个字节**

  项              值
  --------------------------------------------------
  前 3 个字节     46 4C 56
  转成字符        FLV
  版本号          1
  标志位          0x05(低两位:有音频/有视频)
  头长度          9 字节

**前三个字节拼起来就是 `FLV` 这三个字母——FLV 文件是「自报家门」的。**
**对比 MP4:它的头是 ftyp 盒子,要按盒子格式解析才知道是什么。**

**③ FLV 装什么是有讲究的**

  流类型            FLV 接不接
  ------------------------------------------
  H.264 视频        接(AVC 是标准配置)
  AAC 音频          接
  VP9/AV1 视频     不接
  FLAC 音频         不接

**所以「MP4 转 FLV」能不能成功,取决于里面装的是什么编码,不是取决于命令。**
**装的是 H.264 加 AAC 就一定能转;装的是 VP9 就必须先重编码。**

format=u"flv" 不能省:输出名如果写成 o.flv 也能靠后缀猜出来, 但推流到 rtmp:// 这类没有后缀的地方就必须显式给 -f。

标志位 0x05 是「0x04 有音频」加「0x01 有视频」,两个位同时亮。 只拆音频的文件会是 0x04,只有画面的会是 0x01。

最后一行读出来 9,说明文件头正好 9 个字节;紧接着是第一个 tag 的头部。 FLV 后面所有内容都是「tag」:一个 tag 装一个包,tag 头 11 个字节。

30 秒实验

  1. 直接转:ffmpeg -i 片子.mp4 -c copy 出.flv,注意 -c copy 不能少。
  2. 看头:用十六进制工具看前 9 个字节,46 4C 56 01 05 很好认。
  3. 故意塞 VP9:ffmpeg -i vp9.mp4 -c copy 出.flv,看它怎么拒绝。

常见误区

  1. 忘了 -c copy。不加就是重编码,字节与耗时都会变,还白掉一次画质。
  2. 以为 .flv 后缀就能决定一切。推流到 rtmp:// 时必须显式 -f flv。
  3. 以为 FLV 什么都能装。VP9、AV1、FLAC 都不接,得换盒子或重编码。
自测三问(点开对答案) 1. FLV 的文件头有几个字节?前三个是什么? —— 9 个字节。前三个是 `46 4C 56`,也就是字符 F、L、V。 2. 标志位 0x05 表示什么? —— 同时有音频和视频:0x04 表示有音,0x01 表示有视,加起来就是 0x05。 3. MP4 转 FLV 失败,最可能的原因是什么? —— 盒子里装的编码 FLV 不接(比如 VP9、AV1、FLAC),不是命令写错。

相关笔记