真正生效的视频水印必须调用ffmpeg命令行执行滤镜操作,PHP仅负责调度;水印图须为PNG格式,需校验ffmpeg可用性、设置足够执行时间、严格过滤用户输入,并用drawbox+overlay或drawtext实现边框阴影效果。

PHP 本身不处理视频水印,只调度 ffmpeg
直接用 GD 或 Imagick 给视频加水印是无效的——它们只能处理单帧图像,无法解析音视频流、保持时间轴同步、复用编码参数。所有真正生效的“视频水印”,本质都是调用 ffmpeg 命令行执行滤镜操作,PHP 只负责拼参数、跑命令、收返回码。
常见错误现象包括:生成文件无水印、音频丢失、视频卡顿、输出黑屏。多数源于未校验 ffmpeg 是否可用,或未用 -c:a copy 保留原音频流。
- 必须确认系统已安装
ffmpeg,且在PATH中可执行(运行which ffmpeg或ffmpeg -version验证) - 水印图必须为 PNG 格式(带 alpha 通道),否则透明区域会变黑
- PHP 脚本需有足够执行时间:
set_time_limit(600),避免大视频中途被中断 - 绝对不要拼接用户输入进命令字符串,必须用
escapeshellarg()包裹所有路径和文本
图片水印加边框阴影:用 ffmpeg overlay + drawbox 实现
ffmpeg 没有“一键加边框阴影”的滤镜,但可以用 drawbox 和 overlay 组合模拟:先画一个偏移的深色矩形作阴影,再把水印图叠在上方。关键在于控制两层坐标严格对齐。
示例命令(右下角带 2px 黑色阴影边框的 PNG 水印):
立即学习“PHP免费学习笔记(深入)”;
ffmpeg -i input.mp4 -i watermark.png -filter_complex "[1:v]scale=150:-1:flags=lanczos,drawbox=x=0:y=0:w=iw+4:h=ih+4:color=black@0.6:t=2[shadow]; [shadow][1:v]overlay=x=W-w-10:y=H-h-10[watermarked]; [0:v][watermarked]overlay=W-w-10:H-h-10" -c:a copy output.mp4
说明:
-
scale=150:-1先缩放水印图,避免过大压盖画面 -
drawbox在水印图上绘制外扩 4px 的半透明黑色框(t=2表示线宽 2px) - 两个
overlay:第一个把带框水印图合成到新视频流,第二个把该流叠到底层视频上 - 若要阴影向右下偏移(更自然),把
drawbox的x/y改为x=2:y=2,同时调整第二层overlay的坐标补偿
文字水印带阴影:用 drawtext 的 shadowx/shadowy 参数
比手动叠加 box 更简洁——drawtext 内置阴影支持,通过 shadowx 和 shadowy 控制偏移量,shadowcolor 设颜色与透明度。
中文必须指定支持 UTF-8 的字体路径,例如 /usr/share/fonts/truetype/noto/NotoSansCJK-Regular.ttc;Windows 下注意双反斜杠转义。
- 基础命令(右下角白字+黑影):
drawtext=fontfile=/path/to/font.ttf:text='©2026':fontsize=28:fontcolor=white:shadowcolor=black@0.8:shadowx=2:shadowy=2:x=w-tw-10:y=h-th-10 -
shadowcolor=black@0.8中的@0.8是 alpha 值,范围 0–1,值越小越透明 - 避免中文乱码:字体文件名不能含空格或中文路径;Linux 下可用
fc-list :lang=zh查看已安装中文字体 - 若需动态内容(如时间戳),用
text='%{localtime:%Y-%m-%d %H\:%M\:%S}',注意 Linux 下冒号要双转义
用 php-ffmpeg 库封装时,别绕过底层 filter_complex
很多人以为引入 php-ffmpeg/php-ffmpeg 就能“面向对象加水印”,结果发现它默认只支持简单 overlay,不暴露 drawbox 或复杂滤镜链。强行用高级 API 反而受限,不如直接调用 FFMpeg::fromBinary($ffmpegPath) 后手写 addFilter()。
容易踩的坑:
- 库默认会重设视频编码参数,导致输出体积暴增或画质崩坏;务必显式调用
$video->save(new FFMpegFormatVideoX264(), $output)并传入预设格式 -
addFilter()接收的是原始滤镜字符串,不是数组,例如:$video->filters()->custom('[v]drawbox=...[out]') - Windows 路径中的反斜杠需转义,
fontfile=C:\Windows\Fonts\simhei.ttf,否则ffmpeg报错No such file - 调试时先用
proc_open()捕获完整 stderr 输出,很多问题(如字体加载失败、alpha 不支持)只在 stderr 提示
最稳的路径始终是:确认 ffmpeg 命令行能跑通 → 再用 PHP 调度它 → 最后考虑是否值得封装成类。边框和阴影这类效果,滤镜表达式稍一写错,就全图花屏,没有捷径可抄。



















