🎬 第五章 FFmpeg命令行实现音视频转码
📄 一句话概括

转封装几乎不改字节,重编码能改到原来的百分之十几。 差别全在「动没动画面」这一步。

📝 本节和前面几节的关系

第四章做过 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 秒实验

  1. 命令行对照:ffmpeg -i 片子.mp4 -c:v libx264 -crf 26 -c:a aac 出.flv。
  2. 只换盒子:-c copy,对比耗时与字节,体会两种做法的差距。
  3. 试奇数高度:-vf scale=80:45,看报什么错。

常见误区

  1. 以为转 FLV 一定要重编码。源已经是 H.264 加 AAC 时,-c copy 就够了。
  2. 缩放时用了奇数高宽。yuv420p 要求偶数,否则编码器拒绝。
  3. 以为小画面字节一定会小很多。像素少四分之三,字节可能只降两成。
自测三问(点开对答案) 1. 转 FLV 之前要先确认什么? —— 源里的编码 FLV 认不认。H.264 与 AAC 一定认,VP9、AV1 不认。 2. 为什么 80×45 会被拒? —— yuv420p 的色度是横竖各减半,宽高必须是偶数,45 是奇数。 3. 像素数降了 64%,字节为什么只降 21%? —— 小画面里细节相对更密,编码器要在每个像素上花更多比特才能保住清晰度。

相关笔记