根据维基百科上的图表,后来的Intel CPU通过Quick Sync支持VP8编码,Kaby Lake甚至支持VP9编码。
是否有任何 FFmpeg 构建能够利用这一点?
当我运行ffmpeg -codecs
最新的 Zeranoe 版本时,我看到:
DEV.L. vp8 On2 VP8 (decoders: vp8 libvpx vp8_cuvid vp8_qsv ) (encoders: libvpx )
运行 FFmpeg-vcodec vp8_qsv
输出失败,因为显然没有编码器。
这是我的构建的问题吗,还是目前 FFmpeg 不支持?
答案1
现在可以实现,但是要在 Linux 上通过 VAAPI 实现。
这里是一个要点,向您展示如何在 Ubuntu 16.04LTS 上生成可运行的版本。
对于基于 QSV 的编码器,经测试开源英特尔媒体 SDK(在 Apollolake 测试平台上),只有 H.264/AVC 和 HEVC 编码器可用。较新的一代通常会提供更多的编码功能。
请注意,QuickSync 硬件加速需要媒体驱动程序系统上的软件包。某些 Linux 发行版,例如 Ubuntu 19.04LTS(及以上版本)将其打包到他们的存储库中。
更新: VP9 编码现已为 IceLake (ICL+) 启用通过 QuickSync 同步硬件,以及FFmpeg 可以利用它。
演示如何使用 FFmpegvp9_vaapi
和vp9_qsv
编码器包装器的示例:
您需要直接使用 FFmpeg,您可以选择添加vp9_超帧和vp9_raw_reorder 比特流过滤器如果在编码器中启用 B 帧,则在同一命令行中vp9_vaapi
。
例子:
ffmpeg -vaapi_device /dev/dri/renderD128 -hwaccel vaapi \
-i http://server:port -vf 'format=nv12|vaapi,hwupload' \
-c:v vp9_vaapi -global_quality 50 -bf 1 \
-bsf:v vp9_raw_reorder,vp9_superframe \
-f segment -segment_time 5 -segment_format_options movflags=+faststart output%03d.mp4
根据需要调整输入和输出路径/URL。
此命令的作用:
它将通过以下方式创建 5 秒长的 mp4 片段:段复用器。查看的用法movflags=+faststart
,以及它如何通过上面的标志作为格式选项传递给底层 mp4 复用器-segment_format_options
。
片段长度可能不正好是 5 秒,因为每个片段都以关键帧开始(剪切)。
不过,我不建议在该编码器中启用 B 帧,因为这些比特流过滤器有其他不良影响,例如摆弄编码器的速率控制和触发类似这样的错误。这在生产环境中是不可取的。这就是为什么下面的脚本没有启用该选项,而是直接在编码器选项中定义一组速率控制模式。
如果您需要利用 VAAPI 的 1:N 编码,请使用以下代码片段:
- 如果需要去隔行,请调用
deinterlace_vaapi
过滤器:
ffmpeg -loglevel debug -threads 1 \
-init_hw_device vaapi=va:/dev/dri/renderD128 -hwaccel vaapi -hwaccel_device va -filter_hw_device va -hwaccel_output_format vaapi \
-i 'http://server:port' \
-filter_complex "[0:v]format=nv12|vaapi,hwupload,deinterlace_vaapi,split=3[n0][n1][n2]; \
[n0]scale_vaapi=1152:648[v0]; \
[n1]scale_vaapi=848:480[v1];
[n2]scale_vaapi=640:360[v2]" \
-b:v:0 2250k -maxrate:v:0 2250k -bufsize:v:0 360k -c:v:0 vp9_vaapi -g:v:0 50 -r:v:0 25 -rc_mode:v:0 2 \
-b:v:1 1750k -maxrate:v:1 1750k -bufsize:v:1 280k -c:v:1 vp9_vaapi -g:v:1 50 -r:v:1 25 -rc_mode:v:1 2 \
-b:v:2 1000k -maxrate:v:2 1000k -bufsize:v:2 160k -c:v:2 vp9_vaapi -g:v:2 50 -r:v:2 25 -rc_mode:v:2 2 \
-c:a aac -b:a 128k -ar 48000 -ac 2 \
-flags -global_header -f tee -use_fifo 1 \
-map "[v0]" -map "[v1]" -map "[v2]" -map 0:a \
"[select=\'v:0,a\':f=segment:segment_time=5:segment_format_options=movflags=+faststart]$output_path0/output%03d.mp4| \
[select=\'v:1,a\':f=segment:segment_time=5:segment_format_options=movflags=+faststart]$output_path1/output%03d.mp4| \
[select=\'v:2,a\':f=segment:segment_time=5:segment_format_options=movflags=+faststart]$output_path2/output%03d.mp4"
- 不使用去隔行:
ffmpeg -loglevel debug -threads 1 \
-init_hw_device vaapi=va:/dev/dri/renderD128 -hwaccel vaapi -hwaccel_device va -filter_hw_device va -hwaccel_output_format vaapi \
-i 'http://server:port' \
-filter_complex "[0:v]format=nv12|vaapi,hwupload,split=3[n0][n1][n2]; \
[n0]scale_vaapi=1152:648[v0]; \
[n1]scale_vaapi=848:480[v1];
[n2]scale_vaapi=640:360[v2]" \
-b:v:0 2250k -maxrate:v:0 2250k -bufsize:v:0 2250k -c:v:0 vp9_vaapi -g:v:0 50 -r:v:0 25 -rc_mode:v:0 2 \
-b:v:1 1750k -maxrate:v:1 1750k -bufsize:v:1 1750k -c:v:1 vp9_vaapi -g:v:1 50 -r:v:1 25 -rc_mode:v:1 2 \
-b:v:2 1000k -maxrate:v:2 1000k -bufsize:v:2 1000k -c:v:2 vp9_vaapi -g:v:2 50 -r:v:2 25 -rc_mode:v:2 2 \
-c:a aac -b:a 128k -ar 48000 -ac 2 \
-flags -global_header -f tee -use_fifo 1 \
-map "[v0]" -map "[v1]" -map "[v2]" -map 0:a \
"[select=\'v:0,a\':f=segment:segment_time=5:segment_format_options=movflags=+faststart]$output_path0/output%03d.mp4| \
[select=\'v:1,a\':f=segment:segment_time=5:segment_format_options=movflags=+faststart]$output_path1/output%03d.mp4| \
[select=\'v:2,a\':f=segment:segment_time=5:segment_format_options=movflags=+faststart]$output_path2/output%03d.mp4"
- 使用英特尔的 QuickSync(在支持的平台上):
在 Intel Icelake 及更高版本上,您可以使用vp9_qsv
编码器包装器,但目前已知有以下限制:
(a). 您必须启用,因为目前驱动程序low_power mode
仅公开了 VDENC 解码路径。iHD
(b).MSDK不支持编码选项1和extra_data。
(c). MSDK 中会默认插入 IVF 头,但 FFmpeg 不需要该头,因此默认为禁用状态。
请参阅以下示例:
- 如果需要去隔行,请调用
vpp_qsv
过滤器:
ffmpeg -nostdin -y -fflags +genpts -init_hw_device qsv=hw \
-hwaccel qsv -hwaccel_output_format qsv \
-filter_hw_device hw \
-threads 1 -vsync 1 -async 1 -reinit_filter 1 \
-i 'http://server:port' \
-filter_complex "[0:v]format=nv12|qsv,hwupload=extra_hw_frames=64,vpp_qsv=deinterlace=2:async_depth=4,split[n0][n1][n2]; \
[n0]vpp_qsv=w=1152:h=648:async_depth=4[v0]; \
[n1]vpp_qsv=w=848:h=480:async_depth=4[v1];
[n2]vpp_qsv=w=640:h=360:async_depth=4[v2]" \
-b:v:0 2250k -maxrate:v:0 2250k -bufsize:v:0 360k -c:v:0 vp9_qsv -g:v:0 50 -r:v:0 25 -low_power:v:0 2 \
-b:v:1 1750k -maxrate:v:1 1750k -bufsize:v:1 280k -c:v:1 vp9_qsv -g:v:1 50 -r:v:1 25 -low_power:v:1 2 \
-b:v:2 1000k -maxrate:v:2 1000k -bufsize:v:2 160k -c:v:2 vp9_qsv -g:v:2 50 -r:v:2 25 -low_power:v:2 2 \
-c:a aac -b:a 128k -ar 48000 -ac 2 \
-flags -global_header -f tee -use_fifo 1 \
-map "[v0]" -map "[v1]" -map "[v2]" -map 0:a \
"[select=\'v:0,a\':f=segment:segment_time=5:segment_format_options=movflags=+faststart]$output_path0/output%03d.mp4| \
[select=\'v:1,a\':f=segment:segment_time=5:segment_format_options=movflags=+faststart]$output_path1/output%03d.mp4| \
[select=\'v:2,a\':f=segment:segment_time=5:segment_format_options=movflags=+faststart]$output_path2/output%03d.mp4"
- 不使用去隔行:
ffmpeg -nostdin -y -fflags +genpts -init_hw_device qsv=hw \
-hwaccel qsv -hwaccel_output_format qsv \
-filter_hw_device hw \
-threads 1 -vsync 1 -async 1 -reinit_filter 1 \
-i 'http://server:port' \
-filter_complex "[0:v]format=nv12|qsv,hwupload=extra_hw_frames=64,split=3[n0][n1][n2]; \
[n0]vpp_qsv=w=1152:h=648:async_depth=4[v0]; \
[n1]vpp_qsv=w=848:h=480:async_depth=4[v1];
[n2]vpp_qsv=w=640:h=360:async_depth=4[v2]" \
-b:v:0 2250k -maxrate:v:0 2250k -bufsize:v:0 2250k -c:v:0 vp9_qsv -g:v:0 50 -r:v:0 25 -low_power:v:0 2 \
-b:v:1 1750k -maxrate:v:1 1750k -bufsize:v:1 1750k -c:v:1 vp9_qsv -g:v:1 50 -r:v:1 25 -low_power:v:1 2 \
-b:v:2 1000k -maxrate:v:2 1000k -bufsize:v:2 1000k -c:v:2 vp9_qsv -g:v:2 50 -r:v:2 25 -low_power:v:2 2 \
-c:a aac -b:a 128k -ar 48000 -ac 2 \
-flags -global_header -f tee -use_fifo 1 \
-map "[v0]" -map "[v1]" -map "[v2]" -map 0:a \
"[select=\'v:0,a\':f=segment:segment_time=5:segment_format_options=movflags=+faststart]$output_path0/output%03d.mp4| \
[select=\'v:1,a\':f=segment:segment_time=5:segment_format_options=movflags=+faststart]$output_path1/output%03d.mp4| \
[select=\'v:2,a\':f=segment:segment_time=5:segment_format_options=movflags=+faststart]$output_path2/output%03d.mp4"
请注意,我们使用了vpp_qsv filter
选项设置为 4 的。与使用和async_depth
相比,这大大提高了转码性能。请参阅scale_qsv
deinterlace_qsv
这次提交在 FFmpeg 的 git 上。
笔记:如果您使用 QuickSync 路径,请注意,如果系统上的 Media SDK 库支持 MFE(多帧编码模式),则将默认启用它。
参考:
- 查看编码器选项,包括支持的速率控制方法:
ffmpeg -h encoder=vp9_vaapi
- 查看deinterlace_vaapi过滤器使用选项:
ffmpeg -h filter=deinterlace_vaapi
- 关于
vpp_qsv
过滤器的使用,请参见:
ffmpeg -h filter=vpp_qsv
例如,如果您想要从去隔行器获得场速率输出而不是帧速率输出,则可以将选项传递rate=field
给它:
-vf=vaapi_deinterlace=rate=field
例如,此功能与支持以下功能的编码器相关:巴马福. 其他的,比如 FFmpeg 中基于 NVENC 的,没有实现这个(截至撰写本文时)。
使用 FFmpeg 中基于硬件的编码器最大限度提高性能的技巧:
尽可能参考内置文档,如上例所示。它们可以发现潜在的陷阱,您可以通过了解过滤器链和编码器初始化的工作原理、不支持的功能等以及对性能的影响来避免这些陷阱。
其次,在可能的情况下,利用需要多个输出的 1:N 转码。这样,输入和过滤器链产品就可以重复使用。
例如,您将在上面的代码片段中看到,我们只调用一次去隔行器,然后通过split
过滤器将其输出拆分为单独的缩放器。这样做是为了降低如果我们多次调用去隔行器时产生的开销,否则会造成浪费。