编码是解码的镜像:上面倒帧,下面接包,出包总比送帧晚一步。 gop_size 决定关键帧多久来一次,这是码率与拖动的兑换。
4.3 讲解码方向,这一节是它的镜像。 下一篇 4.5.1 把两个方向串成转码。
一、两个方向正好对称
把 4.3 那张表倒过来看,就是这一节。 解码 编码
上面倒 包 帧 下面接 帧 包 缓冲攒什么 参考帧 一整帧的量化结果 冲空包 取尾帧 取尾包
两边都要冲空包,这一点完全对称。 忘了冲,解码丢最后几帧,编码丢最后几个包。 但编码方向多了一个东西:参数要自己定。 解码时格式与尺寸由码流决定,编码时全靠调用者给。
二、编码器要你先说清四件事
少说一件,编码器就打不开或产出怪东西。 要给的 不给会怎样
尺寸 直接报错 像素格式 报错或颜色错乱 帧率 时间戳全错,播出来忽快忽慢 码率或质量参数 能出文件,但大小不可控
前三个是硬性要求,第四个是软性的。 不给码率参数,libx264 会用一个默认值编出来, 文件大得离谱但画质也就那样。 码率与质量二选一:给 crf 是按质量编(大小不定),给 b 是按大小编(质量不定)。 两个都给,crf 会失效。
三、gop_size:多久塞一个能独立解的帧
这是编码参数里最影响体验的一个。 gop 小 gop 大
拖进度条立刻出画面 要等下一个关键帧 码率高 码率低 文件大 文件小 适合点播、剪辑 适合长时间归档
实测把 gop_size 设成 10,关键帧正好落在第 0、10、20、30、40 个包上。 设定与实际完全一致, 说明它就是这个意思。 直播场景通常把 gop 定成帧率的整数倍。 25 帧配 50,两秒一个关键帧, 新观众最多等两秒就能出画面。 gop 设成 1 就是全 I 帧,每一帧都能独立解,代价是码率暴涨几倍。只在需要逐帧精确编辑时这么干。
四、编码比解码更贵
同一段内容,编码的耗时通常是解码的几倍到几十倍。 解码是「按码流说的做」,路径确定;编码是「找最优的压法」,要试各种分块与运动估计。 搜索空间大,所以贵。 preset 速度 压缩率
ultrafast 最快 最差 medium 中等 中等 veryslow 最慢 最好
preset 不动画质,只动「花多少时间去找更好的压法」。 想要画质好又不加时间, 该调 crf 而不是 preset。 这个区分很实用:压片子赶时间就调 preset,要画质就调 crf,两者互不替代。
五、动手 Demo:编码流程——出包总比送帧晚一步
送 50 帧进去,看包什么时候出来;再改 gop 看关键帧位置。
import av
with av.open("_media32/tiny.mp4") as ic:
with av.open("_media32/enc_probe.mp4", "w") as oc:
vs = oc.add_stream("libx264", rate=25)
vs.width, vs.height, vs.pix_fmt = 160, 120, "yuv420p"
vs.options = {"crf": "30", "preset": "ultrafast"}
seq = []
for frame in ic.decode(ic.streams.video[0]):
seq.append(len(vs.encode(frame)))
tail = len(vs.encode())
print(sum(seq), tail)
实跑输出:
--------------------------------------------------------------------
编码流程——出包总比送帧晚一步
--------------------------------------------------------------------
**① 编码方向与解码方向正好相反**
解码 编码
--------------------------------------------------
上面倒 包 帧
下面接 帧 包
缓冲 攒参考帧 攒一整帧的量化结果
收尾 冲空包取尾帧 冲空包取尾包
**两边都要冲空包,这一点对称。** 忘了冲,就会丢掉最后几帧或最后几个包。
**② 真跑一次:送 50 帧,看包什么时候出来**
项 数量
----------------------------
送进去的帧 50 帧
编码期间出的包 49 个
冲空包后补出的 1 个
合计 50 个
**第一帧送进去,出 0 个包**(表里第一个数是 0)。编码器要先看完整一帧才能决定怎么压。
**每帧出包数,前十六个:**
0、1、1、1、1、1、1、1、1、1、1、1、1、1、1、1
**③ gop_size 决定关键帧多久来一次**
把 gop_size 设成 10,一共出 **50** 个包,关键帧落在:**0、10、20、30、40**。
**每隔 10 个包一个关键帧,跟设定完全对上。** gop_size 就是「多久塞一个能独立解出来的帧」。
**④ 这个数不是白设的**
gop 小,随机访问快(拖进度条立刻出画面),但码率高;gop 大,码率低,拖进度条要等下一个关键帧。
**这是码率与响应速度的兑换。**
**直播场景通常把 gop 定成帧率的整数倍**(比如 25 帧率配 gop 50,两秒一个),
这样新观众最多等两秒就能出画面。
第一帧送进去出 0 个包,编码器要先看完整一帧才能决定怎么压。
tail 是冲空包补出来的,不冲就会少最后一个包。
30 秒实验
- 把
preset换成veryslow,看耗时怎么变、文件小多少。 - 把
crf从 30 改到 18,看文件大多少。 - 把
gop_size设成 1,数关键帧占比。
常见误区
- 忘了冲空包。 最后一个包丢掉,播出来结尾少一小段。
- 同时给 crf 与 b。 crf 会失效,实际按 b 走(或反之),结果不可预期。
- 以为 preset 能提画质。 preset 只影响搜索时间,画质由 crf 定。
自测三问(点开对答案)
1. 送第一帧进去为什么出 0 个包? —— 编码器要先看完整一帧,才能决定怎么量化、怎么预测。 2. gop_size 设成 10 意味着什么? —— 每 10 个包有一个关键帧,能独立解出来。 3. 画质不够,该调 preset 还是 crf? —— crf。preset 只改编码花的时间,不改目标质量。- ☐ (本节完,下一节 4.5.1 FFmpeg 转码流程简介)