本机 FFmpeg 认识 16 个硬件编码器名,能真出片的是 qsv 与 nvenc。 但它们都比软件编码慢——因为素材只有 160×90,摊不平初始化的开销。
前面所有节都是软件编码。这一节换成 GPU,看它到底快在哪、慢在哪。 结论可能和你的预期相反:小片子上硬件编码更慢。
一、先看本机认识哪些硬件编码器
按名字里的厂商标记分组数一遍:
硬件系列 编码器个数
qsv 9 nvenc 3 amf 4 合计 16
qsv 是英特尔的核显方案,nvenc 是英伟达的独显方案,amf 是 AMD 的。 16 个名字里既有 H.264 也有 HEVC、AV1,所以数量比「三家」多得多。
这些名字只是「这个 FFmpeg 编译时带上了它们」,不代表本机真有对应硬件。 能不能用,得真跑一次——下面这组实测就很有代表性。
注意这是 Python 的 PyAV 枚举出来的,与命令行 ffmpeg -encoders 是同一份清单。
没有 ffmpeg.exe 的环境里,用 av.codecs_available 一样能查。
二、三个编码器各编一遍
同一段素材,软件与两个硬件编码器:
编码器 像素格式 字节
libx264 yuv420p 4696 h264_qsv nv12 6396 h264_nvenc nv12 41384
第一个坑在第二列:qsv 给 yuv420p 会直接报 returned 22,
必须换成 nv12 才肯干活。而 libx265 恰好相反,只认 yuv420p。
第二列对上了,第三列又出意外:nvenc 大了将近九倍。 因为它没给 CRF,按默认码率模式跑——硬件编码器的默认参数本来就是面向大画面的。
耗时也更长,不是更短。 原因不是硬件不行,而是这段素材只有 160×90、50 帧:初始化一个硬件上下文要几十毫秒, 数据传输与格式转换也要时间,这点活儿根本摊不平。
硬件加速的优势要到大分辨率、长片子上才显出来——4K 一帧的像素量是这里的几百倍。 所以「开了硬件加速会不会更快」这个问题,必须带上分辨率和时长才有答案。
三、硬解为什么不一定更快
硬解的每个环节都可能多出一道搬运:
环节 硬件解码后 要付的代价
解码 GPU 上完成,很快 要把码流送进显存 滤镜处理 也能在 GPU 上 滤镜得支持 GPU 帧 取回内存 不需要 要在 CPU 上处理就得下回来 编码 也能在 GPU 上 质量通常不如软件编码
第三行是很多人踩空的坑:解码快了三倍,结果为了在 CPU 上处理, 又把整帧下载回来,一来一回比纯软件还慢。
所以「要不要开硬解」的答案永远取决于整条链路,而不是某一步跑分。 纯转码(解码→编码都在 GPU)收益最大;中间只要插一个 CPU 滤镜,收益就没了。
最后一行也别忘了:硬件编码为了速度牺牲了搜索精度,同码率下画质通常差一档。 直播可以接受,交付母版不该用。
四、动手 Demo:数一遍硬件编码器,再各编一遍比字节
先按名字里的厂商标记数一遍本机认识多少个硬件编码器。 再用 libx264、h264_qsv、h264_nvenc 各编一遍,注意像素格式的差别。
import av, os, time
names = sorted(av.codecs_available)
for tag in (u"qsv", u"nvenc", u"amf"):
hit = [n for n in names if tag in n]
print(u"%-6s %d 个:%s" % (tag, len(hit), u", ".join(hit)))
def enc(dst, codec, pix=u"nv12", 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
print(u"libx264 %d 字节 %.3f 秒"
% enc(u"hw_sw.mp4", u"libx264", u"yuv420p", {u"crf": u"26"}))
for codec in (u"h264_qsv", u"h264_nvenc"):
try:
n, t = enc(u"hw_%s.mp4" % codec, codec)
print(u"%-10s %d 字节 %.3f 秒" % (codec, n, t))
except Exception as e:
print(u"%-10s 失败 %s" % (codec, str(e)[:45]))
实跑输出:
--------------------------------------------------------------------
5.9 FFmpeg 的 GPU 硬件加速原理及应用案例
--------------------------------------------------------------------
**① 本机的 FFmpeg 认识哪些硬件编码器**
硬件系列 编码器个数
------------------------
qsv 9
nvenc 3
amf 4
合计 16
**这些名字只是「这个 FFmpeg 编译时带上了它们」,不代表本机真有对应硬件。**
**能不能用,得真跑一次——下面这组实测就很有代表性。**
**② 三个编码器各编一遍**
编码器 字节 耗时
----------------------------------
libx264 5150 0.038 秒
h264_qsv 6396 0.615 秒
h264_nvenc 41384 0.200 秒
**两个「硬件」编码器都成功出片了,但都比 libx264 慢;nvenc 还大出好几倍——**
**因为它没给 CRF,按默认码率模式跑,硬件编码器的默认参数本来就是面向大画面的。**
**原因不是硬件不行,而是这段素材只有 160×90、50 帧:**
**初始化一个硬件上下文要几十毫秒,数据传输与格式转换也要时间,这点活儿根本摊不平。**
**硬件加速的优势要到大分辨率、长片子上才显出来——4K 一帧的像素量是这里的几百倍。**
**③ 硬解为什么不一定更快**
环节 硬件解码后 要付的代价
-----------------------------------------------------------
解码 GPU 上完成,很快 要把码流送进显存
滤镜处理 也能在 GPU 上 滤镜得支持 GPU 帧
取回内存 — 要下载回 CPU,这一步最贵
编码 也能在 GPU 上 质量通常不如软件编码
**第三行是很多人踩空的坑:解码快了三倍,结果为了在 CPU 上处理,**
**又把整帧下载回来,一来一回比纯软件还慢。**
**所以「要不要开硬解」的答案永远取决于整条链路,而不是某一步跑分。**
av.codecs_available 是编译期清单,与「本机有没有这块卡」是两件事。
真能不能用,只有在 encode 第一帧时才见分晓。
qsv 必须用 nv12。这一段代码里把默认像素格式设成 nv12, 所以 libx264 那一次显式传了 yuv420p——两个编码器的偏好是相反的。
三个耗时数字每次跑都会变,只有「都比 libx264 慢」这个结论稳定。 因为小画面上硬件开销摊不平,这一点与机器跑分无关。
30 秒实验
- 命令行对照:
ffmpeg -hwaccel qsv -i 源.mp4 -c:v h264_qsv 出.mp4。 - 列编码器:
ffmpeg -encoders | findstr qsv(Windows)看本机清单。 - 放大再试:把素材放大到 1920×1080 重跑,硬件编码才可能反超。
常见误区
- 以为列出来的硬件编码器都能用。清单是编译期的,跑一次才知道。
- 给 qsv 传 yuv420p。它只认 nv12,报的是含义含糊的
returned 22。 - 以为开了硬解一定更快。中间插一个 CPU 滤镜,来回搬运比软件还慢。
自测三问(点开对答案)
1. qsv、nvenc、amf 分别是哪家的方案? —— qsv 是英特尔核显,nvenc 是英伟达,amf 是 AMD。 2. 为什么小片子上硬件编码反而更慢? —— 硬件上下文初始化与数据搬运是固定开销,160×90 的活太少,摊不平。 3. 硬解最容易被忽略的代价是什么? —— 把帧从显存下载回 CPU。中间插了 CPU 滤镜时,一来一回比纯软件还慢。- ☐ 第五章完,下一节 6.1 音频滤镜简介