转封装几乎不改字节,重编码能改到原来的百分之十几。 差别全在「动没动画面」这一步。
第四章做过 MP4 转 FLV 的转封装,这一节改成重编码。 同一句需求,两种做法,代价差几十倍。
一、重编码之后的字节
同一份 2 秒素材,重编码成 FLV:
文件 字节 分辨率
源 MP4 38933 160×90 转 FLV 4927 160×90
只剩源的百分之十三。这不是画质变差了,而是这段素材本来就好压: CRF 26 在这么平的画面上花不了几个字节。换成实拍素材,比例会高得多。
对比第四章的转封装:那边字节几乎不变,因为一个像素都没动。 所以判断走哪条路,只要问一句「目标设备认不认现在的编码」。
二、顺手缩小,字节掉得更少
同一档参数,两个分辨率:
分辨率 字节 相对原尺寸
160×90 4927 1.00× 96×54 3884 0.79× 80×45 被拒 —
第三行是个高频坑:yuv420p 的宽高都必须是偶数,80×45 的高度是奇数。 libx264 会直接拒绝,命令行报错也差不多,只说「无效参数」。
第二行像素数只剩百分之三十六,字节却还有百分之七十九。 说明缩小之后每个像素花得更贵了——细节相对更密,编码器要多花比特保住它。
三、FLV 能装的编码
换盒子之前先查这张表,能省掉大半天:
流 FLV 接不接
H.264 视频 接,标准配置 AAC 音频 接 MP3 音频 接,老直播常见 VP9、AV1 视频 不接
所以「转 FLV」要拆成两步:先定 FLV 接什么编码,再把源转成那个编码。 跳过后一步的命令会失败,报错只说「不支持的编码」,不会告诉你怎么改。
这也解释了为什么直播推流几乎都是 H.264 加 AAC:不是因为它最好, 而是因为 RTMP 与 FLV 这条链路只保证认得这两个。
四、动手 Demo:重编码成 FLV,再缩小一半试一次
先按原分辨率重编码成 FLV,再缩到 96×54 重来一次。 顺便试一下 80×45,看 libx264 怎么拒绝奇数高度。
import av, os
def to_flv(dst, w, h):
c1 = av.open(dst, u"w", format=u"flv")
v1 = c1.add_stream(u"libx264", rate=25)
v1.width, v1.height, v1.pix_fmt = w, h, u"yuv420p"
v1.options = {u"crf": u"26"}
c0 = av.open(u"s5.mp4")
for f in c0.decode(video=0):
fr = f.reformat(width=w, height=h, format=u"yuv420p")
for o in v1.encode(fr):
c1.mux(o)
for o in v1.encode(None):
c1.mux(o)
c1.close()
c0.close()
return os.path.getsize(dst)
print(u"160x90 -> %d" % to_flv(u"a.flv", 160, 90))
print(u"96x54 -> %d" % to_flv(u"b.flv", 96, 54))
try:
to_flv(u"c.flv", 80, 45)
except Exception as e:
print(u"80x45 被拒:%s" % str(e)[:50])
实跑输出:
--------------------------------------------------------------------
5.4.2 MP4 格式转码为 FLV
--------------------------------------------------------------------
**① 重编码到 FLV,字节与源对比**
文件 字节 分辨率
----------------------------
源 MP4 38933 160×90
转 FLV 5058 160×90
**转出来的 FLV 只有源的 13%:不是画质变差了,而是这段素材本来就好压,**
**CRF 26 在这么平的画面上花不了几个字节。换成实拍素材,这个比例会高得多。**
**这一条和第四章的转封装完全不同——那边字节几乎不变,这边变多少全看 CRF。**
**② 顺手缩小一半,字节掉多少**
分辨率 字节 相对原尺寸
---------------------------------------------
160×90 5058 1.00×
96×54 4015 0.79×
80×45 被拒(宽高要偶数) —
**第三行是个很常见的坑:yuv420p 的宽高都必须是偶数,80×45 的高度是奇数,**
**libx264 直接拒绝。命令行的报错也差不多,只说「无效参数」。**
**第二行像素数只剩 36%,字节降到 79%——比像素数掉得少一点。**
**说明体积不只看像素数,还看每个像素花多少比特。缩小之后每像素的码率反而更高,**
**这样才能保住细节,不至于缩成一片糊。**
**③ FLV 能装什么**
流 FLV 接不接
---------------------------------
H.264 视频 接,标准配置
AAC 音频 接
VP9、AV1 视频 不接
MP3 音频 接,老直播常见
**所以「转 FLV」这个需求要拆成两步:先定 FLV 接什么,再把源转成那个编码。**
**跳过后一步的命令会失败,而且报错通常只说「不支持的编码」,不会告诉你怎么改。**
format=u"flv" 显式给上,比靠后缀猜更稳。
推流到 rtmp:// 这类没有后缀的地方时,这一步是必须的。
缩放用 reformat 一步到位,比先解码再 resize 少一次拷贝。
注意宽高都要偶数,这是 yuv420p 的硬要求。
两个文件写在同一目录里,靠文件名区分。 如果后面还要比画质,记得把参数字符串也记下来——不然过两天就分不清谁是谁。
30 秒实验
- 命令行对照:
ffmpeg -i 片子.mp4 -c:v libx264 -crf 26 -c:a aac 出.flv。 - 只换盒子:
-c copy,对比耗时与字节,体会两种做法的差距。 - 试奇数高度:
-vf scale=80:45,看报什么错。
常见误区
- 以为转 FLV 一定要重编码。源已经是 H.264 加 AAC 时,
-c copy就够了。 - 缩放时用了奇数高宽。yuv420p 要求偶数,否则编码器拒绝。
- 以为小画面字节一定会小很多。像素少四分之三,字节可能只降两成。
自测三问(点开对答案)
1. 转 FLV 之前要先确认什么? —— 源里的编码 FLV 认不认。H.264 与 AAC 一定认,VP9、AV1 不认。 2. 为什么 80×45 会被拒? —— yuv420p 的色度是横竖各减半,宽高必须是偶数,45 是奇数。 3. 像素数降了 64%,字节为什么只降 21%? —— 小画面里细节相对更密,编码器要在每个像素上花更多比特才能保住清晰度。- ☐ (本节完,下一节 5.4.3 MP4 格式转码为 AVI 格式)