如果你搜过“FFmpeg调用NVIDIA显卡加速视频转码”,大概率已经见过一堆看起来可以直接复制的命令。但我建议你先别急着复制,因为同样一条命令,在你那台NVIDIA显卡上到底能不能跑起来,先取决于驱动、显卡架构、FFmpeg编译版本这三件事。Windows 10/11下用GPU转码,踩坑往往不是命令本身,而是前置检查没做好。这篇文章我想把这套流程完整说一遍:从驱动版本检查到FFmpeg版本选择,从一条最小可用命令到批量处理脚本,最后再把我踩过的几个典型坑摊开讲,希望对正在折腾这件事的人有点帮助。
1. 为什么放着好好的CPU不用,偏要折腾NVIDIA硬编码
1.1 从一次彻夜转码说起
前阵子帮朋友处理一批4K素材,机器是i7-10700加RTX 3060,他用x264 veryfast挂机压了一个通宵,才压完一半。我问他为什么不直接用显卡,他说“显卡不是用来玩游戏的吗?”这个想法很常见,但放到视频转码场景里,显卡反而是性价比极高的“专职工人”。
CPU转码本身不慢,x264、x265这些软件编码器质量也确实好,但它的代价是“时间换质量”。一颗10代i7在4K HEVC编码下,能跑个10到15帧每秒已经不错了,一个小时的素材,压四个小时很正常。而同一台机器里的RTX 3060,用NVENC引擎跑HEVC编码,速度能到200帧每秒以上,体验完全不同。
当然这不是说CPU编码就该被淘汰,而是你需要知道,NVIDIA显卡里藏着一块专门负责视频编解码的硬件单元,它不吃游戏性能,也不吃CUDA核心,转码时功耗很低。FFmpeg调用这块硬件,就是大家常说的硬件加速转码。
1.2 NVENC、NVDEC和CUDA:三兄弟别搞混
很多人在群里问“为什么我加了-c:v h264_nvenc速度还是上不去”,十有八九是把三个概念搞混了。
NVIDIA显卡的视频加速能力分成三块:
- NVDEC:负责视频解码,也就是把你电脑里的H.264、HEVC、AV1视频流“解包”成原始画面。
- NVENC:负责视频编码,把原始画面“打包”成H.264、HEVC或AV1码流。
- CUDA:通用计算单元,可以做滤镜、缩放、颜色转换等各种计算,也负责GPU显存上的数据搬运。
FFmpeg里对应的关系也很清楚:-hwaccel cuda让解码使用NVDEC,-c:v hevc_nvenc让编码使用NVENC,-vf scale_cuda让缩放滤镜跑在CUDA上。如果你只指定了-c:v h264_nvenc,但没有指定硬件解码,那FFmpeg会先用CPU软解,再把每一帧画面从系统内存传到显存,最后交给NVENC编码。结果就是视频编码确实走GPU了,但前面的解码瓶颈还在CPU,速度提升非常有限,这也是很多人说“我怎么感觉没快多少”的真正原因。
1.3 什么场景下值得走GPU
根据我这几年用的经验,硬件转码最值得的场景有这几个:
- 批量归档:手里有一堆手机拍的、相机导出的素材,想转成HEVC节省硬盘空间,GPU转码的吞吐量比CPU软编高出太多。
- 直播转码:线上直播时对延迟和实时性要求高,NVENC的低延迟模式非常合适,CPU软编根本扛不住多路流。
- 剪辑代理文件:把高分辨率素材转成低分辨率代理,方便剪辑软件流畅回放,这种中间产物对画质要求不高,GPU转码非常高效。
- 快速预审:自己拍完视频后想快速看一遍,或者给客户出小样,用GPU压一遍几秒钟就出来了。
如果你追求的是“一部电影压出来和蓝光原盘几乎无损”,那CPU软编仍然有优势,尤其是用x265的veryslow参数。但多数场景下,NVENC的质量已经足够用,速度优势又太明显,这盘账怎么算都划算。
2. “你的显卡到底能不能加速”:驱动、架构、FFmpeg三方体检
2.1 先读一遍nvidia-smi的输出
在Windows下打开CMD或PowerShell,输入:
nvidia-smi正常情况下会显示GPU型号、驱动版本、显存占用,最底下还会有一行CUDA Version。这里有个关键认知:这行CUDA Version表示当前驱动最高支持的CUDA运行时版本,不代表你系统里装了什么CUDA Toolkit。对FFmpeg调用NVENC来说,不需要单独安装CUDA Toolkit,因为NVIDIA的显卡驱动里已经包含了NVDEC和NVENC运行库,FFmpeg通过CUDA Video API直接调用驱动里的东西。
所以驱动版本在这里就很重要。NVENC API会随着NVIDIA Video Codec SDK迭代,老驱动带的API版本太旧,新版FFmpeg调用时可能直接报Driver does not support the required nvenc API version,甚至Could not load nvcuda.dll。我的建议是,如果你要用FFmpeg硬编,就别用半年前的老驱动,有条件直接装NVIDIA官网最新的Game Ready或Studio驱动。
还有一种更直观的检查方式:
nvidia-smi --query-gpu=name,driver_version,memory.total --format=csv输出类似:
NVIDIA GeForce RTX 3060, 551.86, 12288 MiB驱动版本号511.65、551.86这些后缀其实对应功能集,NVIDIA还分标准驱动和DCH驱动。现在新电脑预装的绝大多数是DCH驱动,对FFmpeg来说没有区别,控制面板可以通过Microsoft Store安装,这都不影响硬件加速。
2.2 显卡架构决定NVENC的上限
不是所有NVIDIA显卡都支持所有编码格式,这点特别容易踩坑。我自己见过有人拿一张GTX 750 Ti想压HEVC,折腾两个小时没成功,就是因为那块卡的NVENC单元不支持HEVC编码。
我用一个简化表格整理常见架构的支持情况,供参考:
| 架构/显卡系列 | H.264编码 | HEVC编码 | AV1编码 |
|---|---|---|---|
| Pascal(GTX 10系) | 支持 | 支持 | 不支持 |
| Turing(GTX 16系、RTX 20系) | 支持 | 支持(质量增强) | 不支持 |
| Ampere(RTX 30系) | 支持 | 支持(质量增强) | 不支持 |
| Ada Lovelace(RTX 40系) | 支持 | 支持 | 支持 |
Maxwell及更早的卡非常不建议折腾,一是NVENC单元太老,二是驱动和FFmpeg新版本对它支持优先级很低。Turing之后的卡在HEVC编码质量上有明显提升,我自己用RTX 3060压HEVC,-cq 22左右输出,正常观看很难看出和CPU软编的差距。AV1编码只有RTX 40系和部分专业卡支持,如果你有RTX 4090,那走av1_nvenc能压出更小体积的文件。
还有一点经常被忽略:消费级显卡的NVENC并发session数有限制。Pascal时代普遍限制3路并发,Turing之后放宽到8路。你平时单路转码感觉不到,但批量脚本如果多进程并行转码,可能会遇到初始化失败或报错,后面讲到脚本时再说。
2.3 FFmpeg这个Windows版本里到底有没有NVENC
从FFmpeg官网或者gyan.dev、BtbN这些第三方站点下载Windows版FFmpeg时,你会发现文件区分很多种,有full、essentials、release、latest之分。如果你下的是精简版,很可能没有编入NVENC支持,跑命令时就会遇到:
Unknown encoder 'hevc_nvenc'所以拿到FFmpeg后第一件事,不是转码,而是体检。在CMD里执行:
ffmpeg -hide_banner -encoders | findstr /I "nvenc"如果输出里有:
h264_nvenc hevc_nvenc av1_nvenc那就说明支持。再执行:
ffmpeg -hide_banner -hwaccels能看到cuda说明硬件解码接口也没问题。如果你想顺带确认FFmpeg的编译配置,可以用:
ffmpeg -version看输出里面有没有--enable-cuda-nvcc --enable-nvenc --enable-cuvid这些字段。如果发现不支持,最省事的办法是换一个全功能版FFmpeg,优先选BtbN或者gyan.dev最近更新的包,别用几年前的所谓“稳定版”。FFmpeg的视频编解码API更新很快,旧版本对新显卡的支持、新参数的支持都很弱,版本越新,NVENC的兼容性和质量越好。
3. 从零搭一条“真·GPU全管线”转码命令
3.1 只加-c:v h264_nvenc,可能还是CPU在解码
很多人会写出这样的命令:
ffmpeg -i input.mp4 -c:v hevc_nvenc -preset p5 output.mp4这条命令能跑,但未必足够快。原因是输入文件input.mp4默认是用CPU软解成YUV帧,然后每一帧经过系统内存复制到显存,再喂给NVENC编码。其中CPU解码和PCIe传输都可能成为瓶颈,尤其处理高分辨率高码率素材时,CPU占用会很高,GPU的Video Encode引擎却只跑60%。
想充分发挥NVIDIA硬件加速,要让解码、编码都待在GPU上。对应FFmpeg就是加两个参数:
-hwaccel cuda -hwaccel_output_format cuda第一个-hwaccel cuda指示FFmpeg用CUDA视频解码器,也就是NVDEC进行硬解;第二个-hwaccel_output_format cuda让解码后的帧保持在显存,而不是复制回系统内存。这样解码后的帧直接留在显存,NVENC再从显存里读取,中间少了很多次内存拷贝。
3.2 一条最常用的命令拆开看
下面是我在Windows下最常用的一条离线转码命令,以H.264转HEVC为例:
ffmpeg -hwaccel cuda -hwaccel_output_format cuda -i input.mkv -c:v hevc_nvenc -preset p5 -cq 22 -c:a copy -c:s copy -map 0 output.mkv逐个拆开看:
-i input.mkv:输入文件。-c:v hevc_nvenc:视频编码器用NVIDIA的HEVC硬件编码器,如果想输出H.264,改成h264_nvenc即可。-preset p5:NVENC预设,p5是速度和质量比较均衡的一档,旧版FFmpeg里叫medium,新版统一改成p1到p7。-cq 22:目标质量系数,数值越小质量越高,文件也越大。HEVC一般22到25比较合适。-c:a copy:音频直接复制,不做重编码,能省大量时间。如果源音频格式播放器不兼容,再考虑转成AAC。-c:s copy -map 0:把字幕轨道也复制出来,-map 0确保所有轨道都被保留,否则FFmpeg默认只选一条视频和一条音频。
如果你想顺便把画面缩放,可以加硬件缩放滤镜:
ffmpeg -hwaccel cuda -hwaccel_output_format cuda -i input.mkv -vf scale_cuda=1920:1080 -c:v hevc_nvenc -cq 22 -c:a copy output.mkvscale_cuda是在GPU上完成缩放的,不会像普通scale滤镜那样先把帧拉回CPU。处理4K转1080P时,这个操作几乎零成本,速度非常快。
3.3 怎么确认GPU真的在跑
命令启动后,你以为GPU在工作,但最好亲自确认一下。转码过程中另开一个CMD窗口执行:
nvidia-smi -l 1每隔1秒刷新一次,能看到GPU利用率、显存占用。重点看Video Encode这一项,如果编码引擎利用率跑起来,说明NVENC确实在干活。任务管理器里切到“性能”标签,选中GPU,也能看到“Video Encode”和“Video Decode”两个曲线。
还有一个很直观的判断方式:看FFmpeg的输出日志。转码开始时会打印“Stream mapping”,类似:
Stream #0:0 -> #0:0 (h264 (h264_cuvid) -> hevc (hevc_nvenc))这里解码头如果是h264_cuvid,说明硬解生效了;如果显示h264 (h264),那说明输入还是软解,需要检查-hwaccel参数或显卡驱动的NVDEC是否正常。转码结束时看speed=18x这类数字,如果超过1x,说明转码速度已经快于视频实时时长,压一个1小时视频只花几分钟,这基本就是正确的GPU加速体验。
4. 画质和码率怎么调:NVENC参数的取舍
4.1 三种码率控制模式
NVENC编码器的码率控制模式和CPU软编类似,但不完全一样。很多刚接触的朋友只看到别人用-b:v设置码率,就固定填了一个数字,结果画面复杂场景糊成一片,简单场景又浪费码率。其实NVENC的码率控制主要有三种思路:
- CBR(固定码率):通过
-b:v指定码率,再配合-maxrate和-bufsize。这种模式适合直播推流,因为输出码率稳定,不会因为画面复杂而突然上涨。 - VBR(可变码率):指定目标码率和最大码率,让编码器在复杂场景突破目标码率,适合文件存储。但单独用
-b:v时不够智能。 - ConstQP/CQ(恒定质量):通过
-cq指定质量系数,让编码器根据画面内容动态决定码率。这是我现在最常用的方式,简单、直观、基本不会出大错。
如果你无法判断该用哪种,文件转码优先选择-cq模式。以HEVC为例,-cq 20偏高质量,-cq 23比较均衡,-cq 26体积小但能看出轻微劣化。H.264的NVENC编码器质量相对弱一些,想接近无损的话,建议-cq 16到-cq 20。这里没有绝对正确,最好用一小段有复杂运动的视频做测试,观察输出文件大小和画质。
4.2 预设、延迟和B帧
新版FFmpeg的NVENC预设从p1到p7,p1最快,p7质量最好。线下转码我一般用p5,直播或实时处理用p3或p4。很多人以为用最高预设p7就一定最好,但NVENC的p7对画质提升有限,速度却会明显下降,反而失去了硬件加速的意义。如果你时间不敏感,想尽量榨干NVENC画质,可以试p6、p7,但我不建议每天都等那么久。
-tune参数也很重要。离线转码可以设hq,低延迟直播用ll或ull(ultra low latency)。如果直播用的是H.264编码,延迟要求高,可以这样设置:
ffmpeg -hwaccel cuda -i input.mp4 -c:v h264_nvenc -preset p4 -tune ll -b:v 6M -maxrate 6M -bufsize 12M -g 120 -r 30 -c:a copy output.ts这里的-g 120表示每120帧一个关键帧,对应30帧率下每4秒一个关键帧,便于直播流随机拖动和纠错。文件名存档则可以把这个值调大,比如250或300,压缩效率会更好。
B帧这块也得注意。HEVC NVENC默认会开B帧,用于提升画质,但B帧会引入延迟和更大的缓冲,直播场景可以设置-b_ref_mode none关闭参考B帧;文件转码时保留默认就好。如果你转出来的视频要进剪辑软件,太多B帧导致拖动卡顿,也可以适当限制GOP大小。
4.3 几个容易被忽略的进阶参数
NVENC里有几个参数我一开始完全没在意,后来发现对画质影响不小:
-spatial-aq 1:开启空间自适应量化,让编码器在画面纹理复杂区域多分配码率,减少块效应。-temporal-aq 1:开启时域自适应量化,减少运动场景下的拖影和模糊。-rc-lookahead 20:设置前瞻帧数,让编码器提前分析画面内容,码率分配更合理。数值越大越耗显存。-multipass qfull:如果FFmpeg版本支持,启用完整多趟编码,质量会好一点,但速度会慢一些。
我自己在RTX 3060上压HEVC的完整参数差不多是这样:
ffmpeg -hwaccel cuda -hwaccel_output_format cuda -i input.mkv -c:v hevc_nvenc -preset p5 -cq 22 -spatial-aq 1 -temporal-aq 1 -rc-lookahead 20 -c:a copy -c:s copy output.mkv这组参数的逻辑是:利用NVENC已经足够好的默认码率控制,再开启AQ和lookahead提升复杂场景表现,代价是显存占用增加、速度小幅度下降,但换来的是肉眼可见的画质稳定。如果你压出来的视频在暗部区域出现色块,优先尝试降低-cq而不是疯狂加码率。
5. Windows下的批量转码脚本:从单条命令到拖文件夹即用
5.1 一个能直接用的批处理骨架
单条命令再快,一次处理一整个文件夹也是灾难。Windows下做批量转码,我推荐优先用批处理或者PowerShell,逻辑简单,还能自动跳过已经转过的文件。
下面是一个可以直接保存成.bat的批处理,放在视频文件夹里双击就能用:
@echo off chcp 65001 >nul set /p SRC_FOLDER=请输入源文件夹路径: set /p OUT_FOLDER=请输入输出文件夹路径: if not exist "%OUT_FOLDER%" mkdir "%OUT_FOLDER%" for %%i in ("%SRC_FOLDER%\*.mp4" "%SRC_FOLDER%\*.mkv" "%SRC_FOLDER%\*.mov") do ( echo 正在处理: %%~nxi ffmpeg -hwaccel cuda -hwaccel_output_format cuda -i "%%i" -c:v hevc_nvenc -preset p5 -cq 22 -spatial-aq 1 -temporal-aq 1 -c:a copy -c:s copy "%%~ni_hevc.mkv" 2>>transcode_log.txt if errorlevel 1 ( echo 转码失败: %%~nxi ) ) echo 全部处理完成。 pause这段代码里几个细节:
chcp 65001把控制台代码页切到UTF-8,避免中文文件名乱码。for %%i in (...)遍历指定扩展名,注意批处理里for变量必须用两个百分号。2>>transcode_log.txt把FFmpeg的错误输出追加到日志文件,方便排查。errorlevel检测FFmpeg的退出码,非零说明这次转码失败。
5.2 中文文件名和空格怎么办
Windows批处理对中文路径和空格有点敏感,稍不注意就会报“系统找不到指定的文件”。最稳妥的写法是给所有路径都加上双引号,上面脚本里已经做了。但如果你经常处理带中文名、带空格的素材,我更推荐PowerShell,它对Unicode的支持更好,脚本可读性也更强。
一个PowerShell版本的批量转码可以参考:
$inputDir = "D:\Videos" $outputDir = "D:\Videos\Output" $files = Get-ChildItem -Path $inputDir -Include *.mp4,*.mkv,*.mov -Recurse foreach ($f in $files) { $outPath = Join-Path $outputDir ($f.BaseName + "_hevc.mkv") if (Test-Path $outPath) { Write-Host "跳过已存在: $($f.Name)" continue } Write-Host "转码: $($f.FullName)" ffmpeg -hwaccel cuda -hwaccel_output_format cuda -i $f.FullName ` -c:v hevc_nvenc -preset p5 -cq 22 -spatial-aq 1 -temporal-aq 1 ` -c:a copy -c:s copy $outPath 2>&1 | Out-File -Append -Encoding utf8 transcode_log.txt if ($LASTEXITCODE -ne 0) { Write-Host "转码失败: $($f.Name)" } }PowerShell里反引号是换行符,加不加都行,但建议加,否则命令一长串很难维护。Test-Path用来检测输出文件是否已存在,断点续跑的时候体验非常好。
5.3 要不要开多任务并行
GPU转码速度已经很快了,为什么还有人想并行?因为单路处理高分辨率长视频时,输入读取、音频处理、字幕解析这些环节仍然可能在CPU上排队,NVENC引擎有时候吃不满。开两个并行任务确实能提高整机吞吐量,但前提是显存要够。
我实测下来,1080P素材同时开三路NVENC转码,显存占用大约是2G到3G,RTX 3060 12G完全没压力;但如果你直接压4K HEVC,单路就可能吃掉6G到8G显存,再开第二路很容易OOM。
Windows下最简单的并行,是开多个CMD窗口手动执行,或者用批处理里的start:
start "" cmd /c ffmpeg -i 1.mkv -c:v hevc_nvenc ... start "" cmd /c ffmpeg -i 2.mkv -c:v hevc_nvenc ...但要注意控制并发路数。我的经验是,显存小于8G的卡老老实实跑单路;12G显存可以尝试两路4K或三路1080P。并行的收益不是线性的,当你发现GPU的Video Encode引擎利用率一直在90%以上,再加任务只会互相抢资源,转码总时间反而可能变长。
6. 实在绕不过去的坑:驱动失效、显存爆掉、花屏和CPU反升
6.1 nvidia-smi都跑不起来怎么救
有一种很典型的Windows故障:之前显卡还好好的,突然某个驱动更新后,打开CMD执行nvidia-smi报错:
NVIDIA-SMI has failed because it couldn't communicate with the NVIDIA driver.控制面板也可能跟着消失,任务管理器里看不到GPU。这种情况基本就是驱动层出了问题,不是FFmpeg能解决的。我自己遇到过一次,当时一怒之下在设备管理器里卸载显卡驱动,结果Windows自动更新又装了一个老版本,循环折腾。
后来我学到比较稳的处理方式是:下载Display Driver Uninstaller(DDU),进安全模式把旧驱动彻底清掉,重启回正常模式,再装一次最新驱动。装完先跑一遍nvidia-smi确认驱动通信正常,再回FFmpeg干活。如果只是NVIDIA控制面板不见了,不用重装整个驱动,去Microsoft Store搜“NVIDIA Control Panel”安装即可,这属于现代系统把控制面板和驱动分离后的正常现象,不影响硬件加速。
6.2 Out of Memory到底是谁的内存
FFmpeg转码时报错信息五花八门,其中最常见的是:
[hevc_nvenc @ ...] Insufficient device memory Cannot allocate memory这里的内存指的不是你电脑的内存条,而是显存。尤其是加了-hwaccel_output_format cuda之后,解码帧直接留在显存,如果同时开了-rc-lookahead 20、硬件缩放、多任务并行,显存很容易被打满。
解决办法按优先级排序:
- 减少并行任务数量。
- 降低
-rc-lookahead数值,比如从32降到12。 - 在编码前用
scale_cuda把画面尺寸缩小,显存占用会大幅下降。 - 检查一下是不是有旧的ffmpeg进程还占着显存,用
nvidia-smi看看有哪些进程在GPU上。
还有一个小技巧:可以给FFmpeg指定使用哪块GPU。如果你的机器里有核显和独显,或者有多个NVIDIA显卡,用:
-init_hw_device cuda=cu:0强制选择设备0。多卡用户在批量转码时可以用这个参数做负载均衡。
6.3 花屏、绿屏和驱动回滚
硬件加速转码出来的视频出现绿屏、花屏、色偏,大概率不是FFmpeg命令的问题,而是驱动和硬件解码器之间的兼容性问题。尤其是新出的显卡配上一个很新的驱动,FFmpeg老版本还没适配,就容易翻车。
我的排查顺序是:
- 先用
ffmpeg -hwaccels确认cuda可用,再做一次小片段测试,不要一上来就跑全片。 - 如果花屏,先去掉
-hwaccel_output_format cuda,改成默认格式,看是否还花屏。有些时候是NVENC编码器对某些输入颜色格式支持不好,强制转换成NV12能解决。 - 尝试升级FFmpeg到最新全功能版,老版本的硬件编码器适配问题在新版本里可能早就修了。
- 如果新驱动花屏,回退到上一个Studio驱动。
画面如果出现轻微色偏,还可以试试在滤镜里加上:
-vf scale_cuda=format=nv12强制像素格式,很多奇奇怪怪的绿色、紫色问题都是像素格式不匹配导致的。
6.4 为什么你的GPU转码CPU占用还是高
这是“假加速”最典型的现象:NVENC编码器明明在跑,CPU占用率依然居高不下。原因很简单,你只是把编码交给了显卡,但解码还是CPU软解,分辨率一高,CPU自然忙得要命。
之前讲过,加-hwaccel cuda -hwaccel_output_format cuda是让解码也走GPU的关键。但在某些特殊场景下,即使加了这两个参数,CPU占用率还是高,常见原因有:
- 输入编码格式是AV1或VP9:老显卡的NVDEC不支持解码,FFmpeg会回退到软解。这时候要么先转码成中间格式,要么干脆接受CPU解码。
- 音频重编码:
-c:a copy只是复制音频,如果是直接改成-c:a aac,FFmpeg会用CPU跑AAC编码,多线程负担不小。 - 字幕烧录:
-vf subtitles=xxx.srt这个滤镜完全是CPU计算,而且很慢。如果你不想在视频上永久烧字幕,建议用-c:s copy保留字幕轨道。 - 封装格式和流复制本身:像是
-map 0、音视频交错、时间戳处理,CPU会参与一些轻量工作,这是正常的。
所以排查CPU占用时,别一上来就怀疑显卡,先看FFmpeg日志里解码器的名字。显示h264_cuvid、hevc_cuvid或者cuviddec就说明硬解成功了;显示h264 (h264)、hevc (hevc)说明还在用CPU解码。前者就把箭头后边的h264_mf之类统统检查一遍,后者则优先怀疑FFmpeg版本或驱动兼容性。
我个人的习惯是,每次批量处理前先拿一个20秒的片段跑一遍,确认日志里解码和编码都指向硬件,再放开全量任务。这套方法帮我避开了不少显存爆炸和驱动抽风的问题,也省下了很多在凌晨盯着进度条的时间。