FFmpeg使用GPU转码如何更改分辨率

感谢zhi-hua.wang的研究,为防止文章地址变动,转载以记之。

ffmpeg 参数_使用FFMPEG进行视频转码_weixin_39673303的博客-CSDN博客

--------------------------------------------------------------------------------------------------------------------------

5c20c4e4864ec530d91c6d4e42481de5.png 

使用FFMPEG进行视频转码 - 日落孤城​www.zhi-hua.wang

FFMPEG 是一款开源的视频处理软件,再利用FFMPEG转码的过程中踩了很多坑,在这里列举一些常用的 FFMPEG 命令防止忘记。

以下所有的命令的输入都是《The.Mandalorian.S01E01.INTERNAL.HDR.2160p.WEB.H265-DEFLATE.mkv》这部影片,这部影片有很多特殊的地方,所以给我带来了不少困扰。它也驱使我学习了关于视频的很多知识,以下是它的一些基本参数:

 
  1. 视频

  2. 格式 : HEVC

  3. 格式配置 (Profile) : Main 10@L5@High

  4. 时长 : 38 分 46 秒

  5. 码率 : 15.9 Mb/s

  6. 宽度 : 3 840 像素

  7. 高度 : 1 608 像素

  8. 原始高度 : 2 160 像素

  9. 帧率模式 : 恒定帧率 (CFR)

  10. 帧率 : 23.976 (24000/1001) FPS

  11. 色彩空间 : YUV

  12. 色度抽样 : 4:2:0

  13. 位深 : 10 位

  14. 数据密度 [码率/(像素*帧率)] : 0.108

  15. 流大小 : 4.32 GiB (94%)

  16. 色彩范围 : Limited

  17. 色彩原色 : BT.2020

  18. 传输特性 : PQ

  19. 矩阵系数 : BT.2020 non-constant

这部影片是 4K HDR10 影片,编码方式为 HEVC,像素格式为 yuv420p10le,容器分辨率为 3840X2160,实际分辨率为3840X1608,色彩范围( color range )为TV( Limited ),色彩原色( color primaries )为 bt2020,传输特性为smpte2084( PQ ),矩阵系数(color matrix )为 bt2020nc 。为了节省篇幅,以下均以input.mkv代表这个视频源文件。而且由于配备了gtx1070显卡以及安装了最新的驱动,所有的操作都是尽量利用GPU进行硬件加速。

1. 转为720p(失败)

为了达到最好的性能,我准备使用hevc_cuvid进行解码,scale_npp进行视频缩放,hevc_nvenc进行编码。-hwaccel cuvid 告诉ffmpeg使用cuvid加速,并且把视频的数据保留在显存而不是内存上。由于版权问题ffmpeg不自带scale_npp滤镜,需要自己编译。为了方便我选择manjaro系统,直接安装AUR上的ffmpeg-full包。这个过程也很心酸,可以专门写一篇文章了,这里就不展开了。关于ffmpeg转码的文章在互联网上有很多,尤其是英伟达官网的《ffmpeg transcoding guide》建议好好读一读。

ffmpeg -hwaccel cuvid -c:v hevc_cuvid -i input.mkv -c:a copy -vf scale_npp=1280:720 -c:v hevc_nvenc -b:v 5M output.mkv

运行以上命令却报错了。原来scale_npp滤镜很娇气,无法处理10bit的视频,因此尝试失败。

2. 转为720p (format, scale_npp:48fps)

既然无法处理10bit,那我把它转成8bit呗。上format滤镜,将yuv420p10le转为yuv420p。由于format是软件滤镜,因此取消-hwaccel cuvid 。这样解码的视频会自动转存到内存。format处理好的内容使用hwupload_cuda上传到GPU,然后通过scale_npp缩放,之后通过hevc_nvenc编码。

ffmpeg -c:v hevc_cuvid -i input.mkv -c:a copy -vf "format=yuv420p,hwupload_cuda,scale_npp=1280:720" -c:v hevc_nvenc -b:v 5M output.mkv

输入以上命令,发现咋只有48fps呢?那岂不是要十几分钟?太慢了!

3. 转为720p (scale_cuda:227fps)

突然发现了scale_cuda这个宝藏,它能接受10bit输入。而且scale_cuda在ffmpeg的编译版中自带!这就好办了,赶紧敲下以下命令:

ffmpeg -hwaccel cuvid -c:v hevc_cuvid -i input.mkv -c:a copy -vf scale_cuda=1280:720 -c:v hevc_nvenc -b:v 5M output.mkv

所有任务都转到GPU上运行了,性能得到巨大提升。通过观察发现此时的瓶颈变为了GPU解码。GPU解码器的占用率高局100%,而编码器只占用了不到20%。

4. 转为720p 8bit (pix_fmt:失败)

ffmpeg -hwaccel cuvid -c:v hevc_cuvid -i input.mkv -c:a copy -vf scale_cuda=1280:720   -c:v hevc_nvenc -pix_fmt yuv420p -b:v 5M output.mkv

然而它报错:

Impossible to convert between the formats supported by the filter 'Parsed_scale_cuda_0' and the filter 'auto_scaler_0'
Error reinitializing filters!
Failed to inject frame into filter network: Function not implemented
Error while processing the decoded data for stream #0:0

5.转为720p 8bit (pix_fmt:223fps)

经过长时间的试错,发现之前的错误通过在scale_cuda滤镜后添加hwdownload,format=p010le就可以解决了,至于为啥我也不知道 。-hwaccel_output_format cuda目前可以不加,只不过会警告你以后不加就会报错了。

ffmpeg -hwaccel cuvid -hwaccel_output_format cuda  -c:v hevc_cuvid -i input.mkv -c:a copy -vf "scale_cuda=1280:720,hwdownload,format=p010le" -c:v hevc_nvenc  -pix_fmt yuv420p  -b:v 5M output.mkv

6.用resize缩放而不是scale_cuda或scale_npp (resize:225fps)

这一条与上一条没多大的区别,只是把scale_cuda改为了resize。resize这个参数是我从英伟达官网上的一篇pdf上了解到的,它说用resize性能好一些。至于好不好我不知道,我只知道resize更易读与打出来。

ffmpeg -hwaccel cuvid -hwaccel_output_format cuda  -c:v hevc_cuvid -resize 1280x720 -i input.mkv -c:a copy -vf "hwdownload,format=p010le" -c:v hevc_nvenc  -pix_fmt yuv420p  -b:v 5M output.mkv

7.固定质量模式

之前的转码都是恒定码率模式,很不科学。hevc_nvenc没有crf参数,这可咋办呢?好在找到了替代方案。通过将-rc:v 设置为vbr或者vbr_hq,从而启用VBR模式,然后设置-cq:v-qmin-qmax。这里最好把它们设置成一个数值,否则没有多大意义。这个数值接类似于crf参数。

ffmpeg -hwaccel cuvid -c:v hevc_cuvid -resize 1280x720  -i input.mkv -c:a copy  -vf "hwdownload,format=p010le"  -c:v hevc_nvenc -pix_fmt yuv420p -rc:v vbr -cq:v 20 -qmin 20 -qmax 20 output.mkv

8.颜色偏差--HDR转SDR(tonemap_opencl)

经过前面这么多的折腾,视频已经压制好了。可是越看越不对劲。为啥我压的视频看起来灰蒙蒙的呢?经过深入调查发现是由于HDR导致的。这个视频本身是HDR的,而转换过程中ffmpeg并没有保留HDR有关的元数据(这应该是个BUG),又没进行颜色的转换。所以播放器把这个HDR视频当作SDR来处理,就会导致画面发灰。解决办法有两种,一是将其转为SDR,这需要tonemap滤镜(似乎scale滤镜也可以)。另一种是保留元数据,让播放器能够正常处理。这里选择第一种。由于tonemap是运行在cpu上的,效率低,所以我改用它的硬件加速版:tonemap_opencl

http://ffmpeg.org/ffmpeg-filters.html#tonemap_005fopencl​ffmpeg.org

注意下面的命令我只在linux上测试成功过,win10会报错,我也不知道为什么。

ffmpeg -hwaccel cuvid -init_hw_device opencl=ocl -filter_hw_device ocl -extra_hw_frames 3  -c:v hevc_cuvid -resize 1280x720  -i input.mkv -c:a copy  -vf "hwupload,tonemap_opencl=tonemap=mobius:param=0.01:desat=0:r=tv:p=bt709:t=bt709:m=bt709:format=nv12,hwdownload,format=nv12"  -c:v hevc_nvenc  -rc:v vbr -cq:v 20 -qmin 20 -qmax 20 output.mkv

tonemap_opencl的这些参数中,tonemap指定了使用的算法,可选值为nonecliplineargammareinhardhablemobius。param是用来微调这些算法的。具体的内容看ffmpeg的文档。这里最重要的是将p、t以及m都设置为了bt709。所以这个滤镜会完成以下转换:色彩原色: bt2020->bt709、传输特性:smpte2084->bt709、矩阵系数:bt2020nc->bt709 (HDR有多种不同标准,smpte2084对应的是HDR10。 经过HDR->SDR的转换,颜色终于变得正常了,不过仔细看肯定和原片有差距。可以根据画面的特色选用不同的算法和参数来进行调整,以保证画面的准确度。

9. 颜色偏差--添加HDR元数据(失败)

上一节我们将HDR转为了SDR,画面的准确度有些许丢失。那该怎么办呢?之前说到还有一种方法,那就是保留元数据。我尝试过用MKVToolNix将色彩原色、传输特性以及矩阵系数分别设置为9、16和9。然后这个视频在linux上播放正常了,但是在windows上还是灰的。我真的是很无语!!!

YouTubeHDR/hdr_metadata​github.com

我在github上还找到了另外一个项目,叫做nv_hevc_hdr_patcher。但是这个项目看起来挺不好用的,我懒得折腾,所以HDR元数据添加宣告失败(怎么可能这么轻易放弃,下一节会用其它工具搞定这个问题(如果我还会继续写的话(会吗?)))

SK-Hardwired/nv_hevc_hdr_patcher​github.com

总结

使用FFMPEG,在尽量利用硬件加速的情况下完成了视频的压制。直接转码时会丧失HDR元数据,导致发灰(见下图中间一栏)。通过tonemap可以将HDR转为SDR视频,画面的颜色得到较为准确的还原,但还是有轻微的差别。最终也没能够完成HDR元数据的添加。

3f42ece4ae62432b057661df5df57898.png

原片

4aecaff032f44e91d53abab80644b113.png

直接压片,元数据丢失导致发灰

93f6d014d1a4bc3336cc5c75fce7ece3.png

tonemap转为SDR后颜色恢复

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值