前半部分(使用windows ffmpeg)写于2023年6月;后半部分(使用ubuntu ffmpeg)写于2023年12月
Table of Contents
2023年6月(windows ffmpeg)
当前命令(似乎是从某个supseruser.com问答里拿出来的,等后续找个时间补充下链接)
https://superuser.com/questions/1236275/how-can-i-use-crf-encoding-with-nvenc-in-ffmpeg
* windows
$ ffmpeg -i <input.mp4> -c:v hevc_nvenc -preset:v p7 -tune:v hq -rc:v vbr -cq:v 19 -b:v 0 -preset slow <output.mp4>
现在的问题在于:如何调整参数?(不使用bitrate控制质量)
似乎和 p7 有关。
又找到一个新的命令,看起来更详细一些:
https://gist.github.com/jungin500/d412986ad633889e5968531a138d82ca
先用上面链接的原始命令(去掉了几行不必要的,保留了关键的 p1 和 cq, vbr = 24 )
更新:后面又去掉了-hwaccel cuda和-hwaccel_output_format cuda,因为我的ffmpeg识别不出这个命令:
ffmpeg \
-i "input.mp4" \
-vcodec hevc_nvenc \
-preset p1 \
-rc vbr -cq 24 -qmin 24 -qmax 24 -b:v 0K \
"output1.mp4"
评价(原视频大小为990M的游戏录屏,Nvidia replay制作的,码率固定,时长固定,所以本身可能就存在较大优化空间<等待后续补充>):
990M->583M
转码后的视频质量很高
然后把-preset p1改成p7
ffmpeg \
-i "input.mp4" \
-vcodec hevc_nvenc \
-preset p7 \
-rc vbr -cq 24 -qmin 24 -qmax 24 -b:v 0K \
"output2.mp4"
990M->572M
但编码时间从原来的10秒不到变成了现在的1分钟左右。似乎不太值。
所以接下来默认使用p4,这样编码时间是25秒左右,可以接受。(speed在4.5x~4.7x浮动)
试试-preset p4,然后cq省略不写
ffmpeg \
-i "input.mp4" \
-vcodec hevc_nvenc \
-preset p4 \
"output3.mp4"
990M->36.3M
很糊,但依然能辨别游戏视频里的小字(比如队友ID,敌人ID,煎药配方等等)
试试-preset p4,然后cq=40
ffmpeg \
-i "input.mp4" \
-vcodec hevc_nvenc \
-preset p4 \
-rc vbr -cq 40 -qmin 40 -qmax 40 -b:v 0K \
"output4.mp4"
990M->56.7M
看来cq还是太大了
试试-preset p4,然后cq=30
ffmpeg \
-i "input.mp4" \
-vcodec hevc_nvenc \
-preset p4 \
-rc vbr -cq 30-qmin 30-qmax 30 -b:v 0K \
"output5.mp4"
990M->241M
如果截图快速对比,还是能发现cq=30的质量要下降一些的。有些很小的字,在cq=30的质量下还是会模糊。
听说crf和cq只是叫法不一样(一个是H264一个是HEVC),数字大小的用法是一样的。现在来试试cq的默认值是不是23:
试试-preset p4,然后cq=23
ffmpeg \
-i "input.mp4" \
-vcodec hevc_nvenc \
-preset p4 \
-rc vbr -cq 23-qmin 23-qmax 23 -b:v 0K \
"output6.mp4"
990M->660M
对比这个:
可以看出数字大小还是不能直接照搬的。cq的默认值应该比40还要大,大概是45?
再试试-preset p5和cq=26:
ffmpeg \
-i "input.mp4" \
-vcodec hevc_nvenc \
-preset p5 \
-rc vbr -cq 26-qmin 26-qmax 26 -b:v 0K \
"output7.mp4"
990M->425M
效果不错。虽然仔细放大也能看出小字体的区别,但已经无伤大雅了,非常适合拿来压缩那些不怎么重要的游戏视频。
再试试OBS gpu-h265的录屏
待补充
2023年12月(ubuntu ffmpeg)
现在换成了ubuntu 22,部分运行参数发生了改变
环境:ubuntu 22.04
首先,在ffmpeg.org下载的linux static build是不支持nvidia硬件加速的,要想开启hevc_nvenc,只能:
- 使用apt-get获取,但ubuntu 22.04系统上只支持到ffmpeg 4.4.x
- 自己编译ffmpeg 5.x甚至6.x甚至7.x
先试试自己编译,这里参考了:🔗 [Using FFmpeg with NVIDIA GPU Hardware Acceleration - NVIDIA Docs] https://docs.nvidia.com/video-technologies/video-codec-sdk/11.1/ffmpeg-with-nvidia-gpu/index.html
按照教程来确实可以编译,但ffmpeg执行的时候出了问题:
ffmpeg: error while loading shared libraries: libavdevice.so.60: cannot open shared object file
这个 libavdevice.so.60 来得非常诡异,网上查了一圈,参考🔗 [Ffmpeg error in linux - Stack Overflow] https://stackoverflow.com/questions/12901706/ffmpeg-error-in-linux,发现我的ubuntu系统里面只有 libavdevice.so.58 .
所以估计是ffmpeg的源代码太新了(一开始用的是ffmpeg 6.1),所以试一试ffmpeg 5.x,结果5.x也不支持我手里的 libavdevice.so.58 :
ffmpeg: error while loading shared libraries: libavdevice.so.59: cannot open shared object file
所以说ffmpeg 5.x也还是太新了,要再降级到4.x才行,但4.x版本不就是apt-get获取的版本吗...所以说似乎走到死胡同里去了
猜测:想要手动编译ffmpeg 6.x成功,得上一个自带 libavdevice.so.60 文件的系统才行,估计要ubuntu 23,但手里的ubuntu 22.04暂时不打算升级...
本来已经打算将就着用apt提供的ffmpeg 4.4.x了,但突然用教程🔗 [CompilationGuide/Ubuntu – FFmpeg] https://trac.ffmpeg.org/wiki/CompilationGuide/Ubuntu 成功编译了ffmpeg 6.1(去掉了 --enable-libsvtav1 的支持,因为懒得搞av1的依赖了,而且暂时也用不上,此外nvidia对av1_nvenc的支持仅限于新显卡:🔗 [Nvidia NVENC - Wikipedia] https://en.m.wikipedia.org/wiki/Nvidia_NVENC )
再次更新:
首先目前的显卡不支持av1_nvenc
其次hevc_nvenc编码出来的文件体积甚至比原始文件(h264)还大
以一个9.6M的h265视频为例:
使用libx265能做出5.6M的高清h265视频,质量和原本的h264相当
使用hevc_nvenc要么只能做出15M的视频(质量和原本h264相当),要么只能做出3.2M的垃圾质量视频(糊成一团)
🔗 [ffmpeg - Different outputs when encode to h265 using CPU only and GPU together - Super User] https://superuser.com/questions/1754003/different-outputs-when-encode-to-h265-using-cpu-only-and-gpu-together
再补充一些对hevc_nvenc的测试:
ffmpeg -i input.mp4 -vcodec hevc_nvenc -preset [xxxxxx] output.mp4
接下来的测试中仅改变-preset xxxxx的参数
(和cpu不同)hevc_nvenc环境下,-preset几乎完全决定了输出视频的质量
源文件9.6M的h264
没有slower和veryslow
-preset slow 输出15.2M的h265,质量和原h264文件相当,speed=0.966x
-preset medium 输出15.3M的h265,质量和原h264文件相当,speed=3.11x
-preset fast 输出3.3M的h265,糊成一团,speed=7.58x
不写-preset 输出15.3M的h265,质量和原h264文件相当,speed=3.11x,推测此时就是使用了-preset medium
看来slow, medium, fast这种preset参数不太行,试试-preset p5这样的写法
-preset p5 输出15.3M的h265,质量和原h264文件相当,speed=2.52x
-preset p4 输出15.3M的h265,质量和原h264文件相当,speed= 3.1x
-preset p3 输出15.3M的h265,质量和原h264文件相当,speed=4.49x
-preset p2 输出15.1M的h265,质量和原h264文件差不多相当,speed=5.04x
-preset p1 输出3.3M的h265,糊成一团,speed=7.61x
作为对比的cpu软解libx265:
ffmpeg -i input.mp4 -c:v libx265 -vtag hvc1 -preset [xxxxxxx] output.mp4
接下来的测试中仅改变-preset xxxxx的参数
-preset对视频质量的改变不大:
-preset slow 输出5.6M的h265,质量和原h264文件相当,speed=0.807x
-preset medium 输出5.4M的h265,质量和原h264文件相当,speed=1x
-preset fast 输出5.6M的h265,质量和原h264文件相当,speed=1.45x
(跳过了faster, veryfast, superfast)
-preset ultrafast 输出3.9M的h265,质量看得出下降了一点,但肯定要比hevc_nvenc的那个3.3M的糊成一团h265好得多,speed=2.68x
而且还有一个很重要的一点,就是使用hevc_nvenc的时候,gpu使用率并不高(普遍只有5%以下;只有-preset fast的时候才会占用15%的样子),要想彻底利用显卡,可能还是要写脚本同时处理多个视频能占满gpu. 写脚本也未必有用,因为我发现同时运行多个hevc_nvenc实例时,每个ffmpeg进程的gpu利用率更低了(比如开3个进程,就是3% + 1% + 4%)。看来hevc_nvenc这个东西可能天生就很难占满某些nvidia显卡,或者说是我的ffmpeg指令有问题。
目前的结论
整篇笔记的服务对象只有一个:对h264视频进行高效且高质量的h265编码。所以下面的结论仅仅适合我的使用需求。
原本的设想是:使用hevc_nvenc以后,gpu占用率可以达到100%(单文件),处理速度很快,而且h265的产出质量(指文件大小和视频质量)和cpu软解libx265差不太多。
但事实上是:使用hevc_nvenc以后,单文件ffmpeg命令的gpu占用率很低,处理速度虽然比cpu快,但h265的产出质量极难控制:想要视频质量好,视频文件体积往往大于cpu软解,而且编码速度也不快;如果仅通过-preset把视频文件体积降下来,视频体积大概率会一下子降得特别厉害(从-preset medium到fast),视频画质也随即变成了一团糊糊。
目前看来使用显卡进行高效且高质量的h265编码还是不太现实。