PHP无法直接添加视频水印,必须依赖ffmpeg命令行工具,PHP仅负责安全拼接参数(如escapeshellarg)、调用exec()执行命令,并处理返回码与异常;所有水印逻辑由ffmpeg的overlay或drawtext滤镜完成。

PHP 本身不能给视频加水印,无论你用的是 PHP 8.5 还是 PHP 5.6 —— 它没有音视频编解码能力。所谓“PHP 给视频加水印”,实际只是调用 ffmpeg 命令行工具,PHP 充当一个参数拼接 + 执行调度的角色。
exec() 调用 ffmpeg 是唯一可行路径
PHP 没有内置视频处理扩展,GD、Imagick 都只处理单帧图像;FFmpeg 才是真正干活的。PHP 的任务就是:
- 校验输入路径是否合法
- 用
escapeshellarg()安全包裹所有用户可控参数(尤其是文字内容、字体路径、坐标表达式) - 构造带
overlay或drawtext滤镜的命令 - 设置超时(
set_time_limit(300))、捕获返回码($return_code)、清理临时文件
常见错误现象:
- 执行后无输出、视频没变化 →
ffmpeg命令被截断或路径含空格未转义 - 中文水印乱码 →
fontfile指向不支持 UTF-8 的字体(如 DejaVuSans.ttf 不行,得换NotoSansCJKsc-Regular.otf) - 水印位置错乱 → 错把
w/h当作视频宽高,实际是滤镜上下文变量,必须写在引号内:x='w-tw-10',不能写成x=w-tw-10
示例(安全调用文字水印):
立即学习“PHP免费学习笔记(深入)”;
$video = escapeshellarg('/var/www/uploads/input.mp4');
$font = escapeshellarg('/usr/share/fonts/opentype/noto/NotoSansCJKsc-Regular.otf');
$text = escapeshellarg('© 2026 MyChannel');
$output = escapeshellarg('/var/www/output/protected.mp4');
<p>$cmd = "ffmpeg -i $video -vf \"drawtext=fontfile=$font:fontsize=28:fontcolor=white@0.6:x='w-tw-15':y='h-th-15':text=$text\" -c:a copy $output 2>&1";
exec($cmd, $output_lines, $return_code);</p><p>if ($return_code !== 0) {
error_log("FFmpeg failed: " . implode("\n", $output_lines));
}</p>图片水印必须用 PNG + Alpha,且缩放要提前做
直接拿大尺寸 Logo 往视频上叠,ffmpeg 会默认拉伸变形。正确做法是:
- 水印图用透明背景
PNG,尺寸建议 ≤ 200×200 像素 - 若需等比缩放,用
scale=150:-1放在第二个输入前:-i "scale=150:-1:flags=lanczos" watermark.png - 位置计算别硬编码,用滤镜变量:
overlay=W-w-20:H-h-20表示右下角留 20px 边距
容易踩的坑:
- 用 JPG 当水印图 → 丢 alpha 通道,边缘发白或黑边
- 忘记
-c:a copy→ 音频重编码,耗时翻倍且可能失真 - 在 Windows 上用单引号包裹滤镜 → 失效,必须用双引号并转义内部双引号
动态防盗水印不能只靠固定文字
单纯右下角加一行“© MySite”防不住截图或裁剪。真要防盗,得组合策略:
- 文字水印叠加时间戳:
text='%{localtime:%Y-%m-%d %H\:%M\:%S}'(注意 Linux 下:要双反斜杠转义) - 多位置重复:用
split+overlay链,在左上、右上、左下、右下各打一个半透水印 - 透明度压到
@0.3~@0.5(如fontcolor=white@0.4),既不易察觉又难批量去除
但要注意:过度叠加或多滤镜链会显著拖慢处理速度,尤其对 4K 视频。PHP 层无法加速,只能靠 ffmpeg 启用硬件加速(如 -hwaccel cuda),前提是服务器有对应 GPU。
关键点就一句:PHP 8.5 和 PHP 7.4 在这事上毫无区别,能跑通 exec() + ffmpeg 就行。最常被忽略的是字体路径校验和 shell 参数转义——漏掉任何一个,不是乱码就是命令执行失败,还可能被注入。



















