ffmpeg 9.0是误传,截至2026年10月官方最新稳定版为8.1;其版本演进遵循主版本号变更即允许api不兼容的规范,而实际工程中加水印需用w/w/h/h变量适配分辨率、确保png含alpha通道、避免-c:v copy跳过滤镜,并注意gpu加速下的坐标偏移问题。

ffmpeg 9.0 并不存在——截至 2026 年 10 月,FFmpeg 官方最新稳定版是 6.1(2023 年 11 月发布),后续为 7.0(2024 年 4 月)、7.1(2024 年 10 月)、8.0(2025 年 4 月)、8.1(2025 年 10 月)。所谓 “FFmpeg 9.0” 是误传或混淆了其他项目(如某些第三方 fork、包装器、或模型版本号)。你实际用的极大概率是 ffmpeg 8.1 或更早版本,命令语法与历史主流版本完全兼容。
下面直接说清「一条命令加水印」怎么写、为什么这么写、哪些地方一动就错:
overlay 坐标必须用 W/w/H/h 才能适配不同分辨率
硬写 overlay=100:50 看似简单,但视频从 720p 换成 4K 时,水印会缩成一个小点贴左上角——因为坐标单位是像素,不是百分比。真正在工程中能复用的写法必须依赖内置变量:
-
W表示主视频帧宽,H表示主视频帧高 -
w表示水印图宽,h表示水印图高 - 右下角距边 20px:
overlay=W-w-20:H-h-20 - 居中显示:
overlay=(W-w)/2:(H-h)/2
注意:W/H 在 filter_complex 中才可用;-vf 单滤镜链里不支持这些变量,强行用会报错 Undefined constant or missing '('。
水印 PNG 必须带 alpha 通道,且不能用 -c:v copy
很多用户加完水印发现水印边缘发白、有黑边、或者整个水印变不透明——根本原因是水印图本身没做预处理,或 FFmpeg 被强制跳过重编码:
- 用
ffplay -i watermark.png检查:如果看到背景是棋盘格,说明 alpha 正常;如果纯白/纯黑背景,alpha 已丢 - 用
identify -verbose watermark.png | grep -i alpha(ImageMagick)确认输出含Alpha: associate - 加水印时若加
-c:v copy,FFmpeg 会跳过滤镜,直接报错Filtergraph 'overlay' was defined, but no corresponding filter output was found——overlay 必须触发重编码,无法零拷贝
时间控制要用 if() + between(),别信网上抄的 lt(mod(t,20),10)
网上大量教程教用 if(lt(mod(t,20),10),X,NAN) 实现“每 20 秒显 10 秒”,但这在 VFR(可变帧率)视频里极易错位——t 是解码时间戳(DTS),不是播放时间轴(PTS),两者的差值在 B-frame 多的源片里可能达数百毫秒。
真正稳的写法是:
overlay=x='if(between(t,5,15),100,NAN)':y=100
它只在绝对时间 5–15 秒区间内启用 overlay,不依赖周期函数。若需循环,应先用 -vf setpts=PTS-STARTPTS 归一化时间轴,再套条件表达式。
GPU 加速(nvenc)下 overlay 位置会偏移 1–2 像素
用 -c:v h264_nvenc 或 hevc_nvenc 时,NVIDIA 驱动对 YUV420P 的 subsampling 处理和 CPU 编码器略有差异,叠加坐标常出现亚像素偏移。这不是 bug,是硬件实现差异。
- 临时修复:把
overlay=100:100改成overlay=100.5:100.5(FFmpeg 支持浮点坐标) - 长期方案:加
scale=ceil(iw/2)*2:ceil(ih/2)*2强制偶数分辨率,规避 NVENC 对奇数尺寸的 padding 行为 - 验证方法:用
ffprobe -v quiet -show_entries frame=pkt_pts_time,pkt_duration -of csv=p=0 output.mp4 | head -n 5检查 PTS 是否连续
最易被忽略的一点:overlay 的坐标原点是水印图像的左上角像素中心,不是左上角边缘。所以 overlay=0:0 实际会让水印最左上 0.5×0.5 像素落在画布外——这解释了为什么很多人调到边缘还留白。











