ffmpeg overlay滚动水印必须用x表达式(如x='mod(t*80,w+w)-w')并单引号包裹,依赖t/w/w等变量,注意大小写、alpha通道加format=yuv420、竖屏需先transpose归一化坐标系。

overlay 中 x 坐标必须用表达式,不能写死数值
静态坐标如 overlay=10:50 会让水印钉死在左上角某点,滚动效果根本出不来。FFmpeg 的 overlay 滤镜支持时间变量 t(当前秒数),所有动态位移都得靠它算。常见错误是直接拼数字,比如 x=10+t*20 —— 这在 shell 里会被提前展开成字符串,FFmpeg 实际收到的是无效值;必须用单引号包裹整个表达式,让 FFmpeg 自己解析。
关键点:
-
x表达式里可用t、W(主视频宽)、w(水印宽)等变量,大小写敏感 -
W和w别写反,W-w是右边界预留空间,w-W恒为负,水印直接消失 - 若水印图本身带 alpha 通道,加
format=yuv420防止半透明发灰或黑边
跑马灯效果:从左到右匀速滚动并循环
最典型的滚动是 logo 从画面左侧滑入、横穿、滑出,再立即从左侧重新进入。核心是用 mod(t, T) 把时间轴折成周期,再映射到像素位移。例如 5 秒走完一屏,命令这样写:
ffmpeg -i input.mp4 -i logo.png -filter_complex "[1:v]scale=180:-1[wm];[0:v][wm]overlay=x='mod(t*80, W+w)-w':y=H-h-20:format=yuv420" -c:a copy output.mp4
说明:
-
scale=180:-1先统一水印宽度为 180px,高度自适应,避免因原始尺寸不一致导致滚动卡顿 -
mod(t*80, W+w)-w:每秒移动 80px,周期长度 = 主视频宽 + 水印宽,确保无缝衔接 -
y=H-h-20固定在底部上方 20px,H和h必须大写/小写严格对应 - 没加
shortest=1,因为 logo 是单帧 PNG,不会延长输出时长
带延迟启动和区间控制的滚动水印
有些场景要求水印 3 秒后才开始滚动,且只持续 12 秒(即 t ∈ [3,15))。这时不能只靠 x 表达式,得用 enable 参数开关整个 overlay:
ffmpeg -i input.mp4 -i logo.png -filter_complex "[1:v]scale=120:-1[wm];[0:v][wm]overlay=x='if(gte(t,3), mod((t-3)*60, W+w)-w, NAN)':y=20:enable='between(t,3,15)'" -c:a copy output.mp4
注意点:
-
if(gte(t,3), ..., NAN)让水印在 3 秒前完全不可见(NAN表示“不绘制”) -
enable='between(t,3,15)'是双重保险,防止因 PTS 异常导致水印残留 -
(t-3)*60表示从第 3 秒起计时,速度仍为 60px/s,和总时长解耦 - 如果水印图是 GIF 或短视频,必须加
-ignore_loop 0或改用movie滤镜,否则只播一次就停
手机竖屏视频滚动水印偏移的修复
直接对手机拍的 MP4 跑上面命令,大概率发现水印斜着滚、位置飘忽——不是 FFmpeg bug,是旋转元数据(rotate=90)没处理。FFmpeg 默认按存储像素坐标系计算,但播放器按旋转后画面渲染,两者错位。
必须先归一化方向:
- 用
ffprobe -v quiet -show_entries stream_tags=rotate input.mp4查旋转角度 - 若返回
rotate=90,加-vf "transpose=1"(顺时针转90°)或transpose=2(逆时针)预处理 - 或者一步到位:
-vf "transpose=1,setsar=1:1",强制 SAR 为 1:1,避免宽高比拉伸干扰滚动轨迹 - 之后再接
overlay,所有W/H变量才真正对应你看到的画面尺寸
滚动水印最难调的从来不是表达式,而是坐标系是否和人眼一致。哪怕一个 setsar=1:1 漏掉,W 就可能比实际画面宽 33%,滚动路径全乱。











