overlay滤镜必须显式指定format=yuv420,否则PNG水印半透明失效;透明度需用colorchannelmixer=aa=0.6等前置滤镜调整;坐标计算须严格区分W/w、H/h大小写。

overlay滤镜必须用format=yuv420,否则半透明发灰或全黑
FFmpeg 9.0.2默认输出YUV444或RGB格式时,PNG水印的alpha通道不会被正确解释,结果是水印区域一片死黑、发灰,或边缘严重锯齿。这不是水印图问题,而是像素格式不匹配。
解决方法很简单:在overlay滤镜末尾强制加format=yuv420。这个参数必须显式写,不能依赖默认值。
-
overlay=x=W-w-10:y=H-h-10:format=yuv420✅ 正确 -
overlay=x=W-w-10:y=H-h-10❌ 半透明失效(尤其在H.264/H.265编码下) - 如果主视频本身是YUV422/444源,
format=yuv420会触发自动色度抽样转换,这是必要开销,不可跳过
透明度控制靠alpha滤镜前置,不是overlay参数
很多人以为overlay=alpha=0.7能调透明度——这是错的。overlay滤镜本身没有alpha参数。真正起作用的是提前对水印流做alpha处理。
正确链路是:[1:v](水印图)→ 先过alpha滤镜 → 再进overlay。FFmpeg 9.0.2中推荐用colorchannelmixer或fade,但最稳的是format=rgba,geq组合:
-
[1:v]format=rgba,geq=r='r(X,Y)':g='g(X,Y)':b='b(X,Y)':a='a(X,Y)*0.6'[wm]—— 将alpha通道统一乘以0.6 - 更简洁写法:
[1:v]format=rgba,colorchannelmixer=aa=0.6[wm](aa即alpha alpha) - 然后
[0:v][wm]overlay=x=W-w-10:y=H-h-10:format=yuv420
坐标计算必须区分W/w和H/h,大小写混用直接偏出画面
右下角10px留白是最常用需求,但x=W-w-10写成x=w-w-10就会恒为-10,水印完全消失在左边界外;写成x=W-W-10也一样错。
FFmpeg 9.0.2里变量大小写敏感且无报错提示,只静默失败。务必心算校验:
- 假设主视频
W=1280、H=720,水印w=240、h=80 -
x=W-w-10→1280-240-10 = 1030,合法范围是[0, 1040](因为W-w = 1040),✅ -
y=H-h-10→720-80-10 = 630,合法范围[0, 640],✅ - 手机竖屏视频带
rotate=90元数据?先用-vf "transpose=2"或rotate=0归一化方向,再算坐标
PHP调用时注意shell转义和路径空格
PHP用exec()或shell_exec()跑FFmpeg命令,最容易栽在两处:空格路径没加引号、括号和$符号被shell提前解析。
正确写法示例(PHP 8.1+):
ffmpeg -i "/path/to/video.mp4" -i "/path/to/logo.png" \ -filter_complex "[1:v]format=rgba,colorchannelmixer=aa=0.6[wm];[0:v][wm]overlay=x=W-w-10:y=H-h-10:format=yuv420" \ -c:a copy "/path/to/output.mp4"
- 所有含空格的路径必须用双引号包裹,且引号在PHP字符串里要转义:
"\"/path with space/video.mp4\"" -
$在双引号PHP字符串中会被解释,必须写成\$,或改用单引号包裹整个命令 -
W-w-10这类表达式不能拆到PHP变量里拼接,必须原样传给FFmpeg,否则PHP会尝试计算而报错
format=yuv420不能省、alpha得用前置滤镜调、W/w大小写一个都不能错。其它都是锦上添花。

















