换盒子不换内容:包原样过去,只有目录换了写法。 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 秒实验
- 直接转:
ffmpeg -i 片子.mp4 -c copy 出.flv,注意-c copy不能少。 - 看头:用十六进制工具看前 9 个字节,
46 4C 56 01 05很好认。 - 故意塞 VP9:
ffmpeg -i vp9.mp4 -c copy 出.flv,看它怎么拒绝。
常见误区
- 忘了
-c copy。不加就是重编码,字节与耗时都会变,还白掉一次画质。 - 以为
.flv后缀就能决定一切。推流到rtmp://时必须显式-f flv。 - 以为 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),不是命令写错。- ☐ (本节完,下一节 4.3.2 MP4 转换为 AVI)