PHP-FFMpeg 1.4.0 的 addWatermark() 仅支持图片水印,不自动处理 SAR、旋转元数据及缩放前尺寸对齐,易致偏移、拉伸、错位;需手动获取视频与水印尺寸并计算坐标,文字水印须用 filters()->custom() 调 drawtext 实现。

PHP-FFMpeg 1.4.0 的 addWatermark() 不是万能接口
它只支持图片水印,且默认不处理 SAR、旋转元数据、缩放前尺寸对齐——直接调用 addWatermark() 很容易出现水印偏移、拉伸、错位。这不是 bug,是设计使然:该方法底层封装的是 overlay 滤镜,但把坐标计算交给了用户,没做自动适配。
常见错误现象:
- 水印贴在左上角却显示在画面外(
x=0,y=0时实际被旋转/缩放干扰) - 竖屏视频水印跑到右上角(手机录的
rotate=90元数据未清除) - PNG 水印变模糊或发灰(
format=yuv420缺失,alpha 通道未正确映射)
使用场景限制:
- 仅接受 PNG/JPEG 图片,不支持文字水印(得换
drawtext) - 不支持动态位置(如随时间移动),x/y 必须是固定数值或简单表达式
- PHP-FFMpeg 1.4.0 对
overlay表达式支持有限,W-w这类变量需手动计算传入,不能直接写字符串
用 addWatermark() 加右下角水印,必须先算坐标
PHP-FFMpeg 1.4.0 的 addWatermark() 第二个参数是 position,但它不是枚举值(如 'bottom-right'),而是数组:['x' => 1720, 'y' => 980]。你得自己算出这个坐标,不能依赖滤镜运行时计算。
立即学习“PHP免费学习笔记(深入)”;
关键步骤:
- 用
ffprobe获取原始视频宽高:ffprobe -v quiet -show_entries stream=width,height -of csv=p=0 input.mp4 - 用 GD 或
getimagesize()获取水印图宽高 - 手动计算:
$x = $videoWidth - $watermarkWidth - 10,$y = $videoHeight - $watermarkHeight - 10 - 若视频含
rotate元数据(常见于手机拍摄),先用-vf transpose=2或-noautorotate预处理,否则宽高关系会反
示例代码片段:
use FFMpegFFMpeg;
use FFMpegCoordinateDimension;
<p>$ffmpeg = FFMpeg::create(['ffmpeg.binaries' => '/usr/bin/ffmpeg']);
$video = $ffmpeg->open('input.mp4');</p><p>// 手动获取尺寸(不可省)
$probe = json_decode(shell_exec('ffprobe -v quiet -show_entries stream=width,height -of json input.mp4'), true);
$w = $probe['streams'][0]['width'];
$h = $probe['streams'][0]['height'];</p><p>list($wm_w, $wm_h) = getimagesize('logo.png');
$x = $w - $wm_w - 10;
$y = $h - $wm_h - 10;</p><p>$video->addWatermark('logo.png', ['x' => $x, 'y' => $y])
->save(new X264(), 'output.mp4');想加文字水印?别碰 addWatermark(),改用 filters()->custom()
addWatermark() 完全不支持文字,PHP-FFMpeg 1.4.0 中唯一可靠方式是绕过封装,直插 drawtext 滤镜。用 filters()->custom() 注入原始字符串,才能控制 fontfile、text_w、shadowy 等关键参数。
容易踩的坑:
- Windows 下字体路径含空格或中文,必须用双引号包裹并转义反斜杠:
"C:\Windows\Fonts\simhei.ttf" - Linux 下中文字体路径常为
/usr/share/fonts/truetype/wqy/wqy-zenhei.ttc,不指定则 fallback 到无中文的字体 -
text='My Text'中的单引号在 PHP 字符串里要小心嵌套,建议用双引号 + 变量拼接 - 中文乱码:PHP 文件编码为 UTF-8,但 ffmpeg 在 Windows cmd 下默认 GBK,需提前执行
chcp 65001或改用 UTF-8 字体(如 Noto Sans CJK)
实操示例(右下角文字水印):
$video->filters()->custom(
"drawtext=fontfile=/usr/share/fonts/truetype/wqy/wqy-zenhei.ttc:" .
"text='©2026':fontsize=24:fontcolor=white@0.8:" .
"x=w-tw-10:y=h-th-10:shadowx=2:shadowy=2"
)->save(new X264(), 'output.mp4');透明水印发灰、半透明失效?补 format=yuv420 和 alpha 处理
PNG 水印叠加后颜色发灰、边缘锯齿、透明度丢失,90% 是因为没强制统一像素格式。FFmpeg 默认输出 yuv422 或 yuv444,而大多数播放器和浏览器只认 yuv420;alpha 通道在非 yuv420 下无法正确映射。
解决方案分两步:
- 给水印输入加
format=yuv420:在addWatermark()后手动追加滤镜链,或改用filter_complex全控 - 确保水印图本身带完整 alpha(用
identify -verbose logo.png | grep Alpha验证) - 若仍发灰,加
alpha=1或enable='between(t,0,10)'显式启用 alpha 通道
推荐写法(兼容 PHP-FFMpeg 1.4.0):
$video->filters()->custom(
"movie=logo.png [wm]; [wm] format=yuv420, scale=120:-1 [wm_scaled];" .
"[in][wm_scaled] overlay=W-w-10:H-h-10:format=yuv420"
)->save(new X264(), 'output.mp4');注意:format=yuv420 要同时出现在水印流和 overlay 输出端,漏一个就会灰。
真正难的不是写对命令,而是每次都要判断视频有没有 rotate 元数据、SAR 是不是 1:1、水印图 alpha 是否干净——这些没法靠一个接口自动兜底,得在调用前做探测和预处理。



















