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

这一代换得不划算:同样 CRF,x265 的字节是 x264 的 127%,耗时还要多两三倍。 HEVC 的收益要靠大块划分与大分辨率,小画面上摊不开。

📝 本节和前面几节的关系

x264 用了二十多年,x265 是它的下一代。 「新一代更省」这句话在这个尺寸上不成立,这一节用实测把这件事讲清楚。

一、同 CRF 下两代对比

同一段素材、同一档 CRF 26:

编码器 字节 相对 x264


libx264 4696 1.00× libx265 5941 1.27×

这一组数字是反直觉的:x265 的字节是 x264 的 127%,耗时还要多两三倍。 也就是说在这个尺寸上,HEVC 既没省下空间,还多花了时间。

原因有两层:一是 HEVC 的收益来自大块划分与更多参考帧,160×90 这点像素摊不开; 二是 CRF 在两代编码器里不是同一把尺子,同数值也不等于同画质。

换 HEVC 的正确姿势是:在真实的素材与分辨率上测一遍,而不是照「新一代更省」这句话走。 在 1080p 与 4K 上,x265 通常能省三到五成——但那是另一组实验。

二、x265 挑像素格式

同样给 libx265,两种像素格式的结果不同:

像素格式 libx265 的结果


yuv420p 成功,5941 字节 nv12 被拒(returned 22)

nv12 其实就是「半平面」的 yuv420p:亮度一整块,色度交错成一整块。 两者数据量一样,只是排法不同;x265 只认 yuv420p,转一下格式就能过。

反过来,硬件编码器普遍偏爱 nv12——这一点在 5.9 会看到实例: qsv 给 yuv420p 直接拒绝,给 nv12 才成功。

所以「同一个 yuv420p 家族」内部还要再分一次排法,这是转码里常被忽略的一层开销。 多一次格式转换,就多一次全帧遍历。

三、该不该换 HEVC

按场合决定,不按「新不新」决定:

场合 建议


网页、要最大兼容 用 H.264 4K、存储吃紧 用 H.265 要硬件解码保底 先查设备支持 老手机、老电视 别用

第三行是实际操作里最该先做的一步:HEVC 的专利池比 H.264 更麻烦, 不少设备厂商干脆不解码,用户看到的就是黑屏。

所以「省了多少字节」永远不能单独当决策依据,得先确认播放端认不认。 这一条在 5.4.2 讲 FLV 时说过一次,换 HEVC 时同样成立。

四、动手 Demo:两个编码器各编一遍,再换像素格式试一次

同一档 CRF 26,分别用 libx264 与 libx265 编一遍,比字节与耗时。 再给 libx265 换成 nv12 像素格式,看它接不接。

import av, os, time

def enc(dst, codec, pix=u"yuv420p", opts=None):
    c1 = av.open(dst, u"w")
    s1 = c1.add_stream(codec, rate=25)
    s1.width, s1.height, s1.pix_fmt = 160, 90, pix
    if opts:
        s1.options = opts
    c0 = av.open(u"s5.mp4")
    t0 = time.time()
    for f in c0.decode(video=0):
        for o in s1.encode(f.reformat(width=160, height=90, format=pix)):
            c1.mux(o)
    for o in s1.encode(None):
        c1.mux(o)
    c1.close()
    c0.close()
    return os.path.getsize(dst), time.time() - t0

x4 = enc(u"c264.mp4", u"libx264", opts={u"crf": u"26"})
x5 = enc(u"c265.mp4", u"libx265", opts={u"crf": u"26"})
print(u"libx264 %d 字节 %.3f 秒" % x4)
print(u"libx265 %d 字节 %.3f 秒  是 x264 的 %.0f%%"
      % (x5[0], x5[1], x5[0] * 100.0 / x4[0]))

for pix in (u"yuv420p", u"nv12"):
    try:
        n = enc(u"px_%s.mp4" % pix, u"libx265", pix, {u"crf": u"26"})
        print(u"%-8s 成功 %d 字节" % (pix, n[0]))
    except Exception as e:
        print(u"%-8s 被拒 %s" % (pix, str(e)[:45]))

实跑输出:

--------------------------------------------------------------------
5.8 libx265 的常用编码选项及应用案例
--------------------------------------------------------------------

**① 同 CRF 下 libx264 与 libx265 对比**

  编码器      字节     耗时
  ------------------------------
  libx264     4696     0.039 秒
  libx265     5941     0.119 秒

**这一组数字是反直觉的:x265 的字节是 x264 的 127%,耗时却是 3.0 倍。**
**也就是说在这个尺寸上,HEVC 既没省下空间,还多花了时间。**
**原因是 HEVC 的收益来自「大块划分与更多参考帧」,160×90 这点像素根本摊不开它那些工具;**
**再加上 CRF 在两代编码器里不是同一把尺子,同数值也不等于同画质。**
**换 HEVC 的正确姿势是:在【真实的】素材与分辨率上测一遍,而不是照着「新一代更省」这句话走。**

**② x265 挑像素格式,nv12 会被拒**

  像素格式     libx265 的结果
  ---------------------------------
  yuv420p      成功
  nv12         被拒(returned 22)

**nv12 其实就是「半平面」的 yuv420p:亮度一整块,色度交错成一整块。**
**两者数据量一样,只是排法不同;x265 只认 yuv420p,转一下格式就能过。**
**反过来,硬件编码器普遍偏爱 nv12——显卡处理交错排列更省事,这也是一处常见的转格式开销。**

**③ 该不该换 HEVC**

  场合                 建议
  ----------------------------------
  网页、要最大兼容     用 H.264
  4K、存储吃紧         用 H.265
  要硬件解码保底       先查设备支持
  老手机、老电视       别用

**第三行是实际操作里最该先做的一步:HEVC 的专利池比 H.264 更麻烦,**
**不少设备厂商干脆不解码,用户看到的就是黑屏。**

libx265 必须给 crf,不给会报 returned 22。 它不像 libx264 有默认值兜着,这一条很容易忘。

x265 会在 stderr 打一大段 [info] 日志,那是它自己的统计报告。 用 PyAV 时这些日志走的是 C 层的输出,不影响结果。

比两代编码器时,crf 只当「大致同档」看,不能当「同画质」。 真要公平比较得先做主观或客观画质评估,那是另一门功课。

30 秒实验

  1. 命令行对照:ffmpeg -i 源.mp4 -c:v libx265 -crf 26 出.mp4。
  2. 换容器:x265 常配 -tag:v hvc1 才能在苹果设备上播。
  3. 大分辨率再试:把素材放大到 1280×720,两代的差距会明显变大。

常见误区

  1. 以为新一代一定更省。小画面上 x265 反而更大,而且慢三倍。
  2. 忘了给 crf。libx265 没有默认值,报的是含义含糊的 returned 22。
  3. 给 libx265 传 nv12。它只认 yuv420p,得先转格式。
自测三问(点开对答案) 1. 为什么 x265 在这个尺寸上不省? —— HEVC 的收益来自大块划分与更多参考帧,160×90 这点像素摊不开它的工具。 2. libx265 对像素格式有什么要求? —— 只认 yuv420p,nv12 会被拒。硬件编码器正相反,偏爱 nv12。 3. 什么时候该用 HEVC? —— 4K、存储吃紧、且播放端确认支持。网页交付仍该用 H.264。

相关笔记