Laravel原生不支持视频水印,必须用FFmpeg;推荐php-ffmpeg包,需系统安装ffmpeg并严格校验用户输入、隔离路径、异步处理,Docker/Serverless环境建议用云厂商API。

直接用 Laravel 原生能力无法给视频加水印——它压根不处理视频编解码,所谓“优雅”不是写得漂亮,而是选对链路、避开静默失败、守住安全边界。
别碰 Intervention\Image 处理视频
很多人一看到“水印”就下意识去查 Intervention Image,但它只支持图片(jpeg、png、gif),对 .mp4、.mov 等视频格式调用 make() 会直接抛出 InvalidArgumentException: Unsupported image type,且错误信息极其模糊。
- 它底层依赖 GD 或 Imagick,这两个扩展根本不认识视频容器和编码器
- 哪怕你用
file_get_contents()读取视频二进制再传进去,也会在解析阶段失败 - 试图用
text()或insert()强行操作,结果只有空文件或 0 字节输出
真正可行的路径只有两条:PHP 封装 FFmpeg 或 Shell 调用
视频水印本质是帧级叠加 + 编码重封装,必须交由 ffmpeg 完成。Laravel 生态里最成熟的选择是 php-ffmpeg/php-ffmpeg(即 Laravel-FFmpeg 封装包),它不是魔法,只是帮你拼好命令并管理进程。
- 安装:
composer require php-ffmpeg/php-ffmpeg,然后确保服务器已安装系统级ffmpeg(运行ffmpeg -version验证) - 水印图必须是本地可读路径,不能是 URL;推荐放在
storage/app/watermarks/,避免通过 Web 可直接访问 - 位置参数如
'bottom-right'是语义化简写,实际生成的 filter_complex 是类似overlay=x=W-w-10:y=H-h-10的表达式 - 若需动态位置(比如前 3 秒显示左上,之后移到右下),得手写
filter_complex字符串,不能依赖addWatermark()链式方法
安全红线:所有用户输入必须隔离、过滤、白名单校验
调用外部二进制程序是高危操作,shell_exec() 或 exec() 若未严格约束,可能被构造为任意命令执行漏洞。
- 用户上传的视频文件名必须用
escapeshellarg()包裹,否则input; rm -rf /这类输入会触发命令注入 - 禁止允许用户指定水印图 URL,禁用
allow_url_fopen,防止远程文件读取或 SSRF - 水印图路径必须硬编码为白名单目录(如
storage_path('app/watermarks/logo.png')),绝不能拼接用户传入的路径片段 - 建议在队列中异步执行水印任务,避免请求超时;同时限制 ffmpeg 进程最大内存与 CPU 时间(通过
-max_muxing_queue_size和ulimit)
透明度与性能容易被忽略的细节
视频水印的透明度不是靠 PNG alpha 通道自动生效——ffmpeg 默认把 PNG 当作不透明图层叠加,必须显式加 format=rgba 和 alpha=0.7 参数,否则水印边缘发黑或背景变灰。
- 文字水印更麻烦:得先用
drawtext滤镜,字体路径必须是服务器绝对路径,且fontfile参数不支持中文路径 - 4K 视频加水印极易 OOM,务必提前缩放原视频(
scale=1280:-2)或用-vf "scale=iw/2:ih/2"减半分辨率再处理 - 导出格式别硬写
X264,优先用new \FFMpeg\Format\Video\X264('libx264', 'aac')显式指定音频编码,否则老版本 ffmpeg 可能默认用 ac3 导致播放失败
最后提醒一句:如果你的部署环境是 Docker 或 Serverless(如 Laravel Vapor),ffmpeg 二进制很可能根本不存在,或者权限受限无法 fork 进程——这种情况下,与其折腾容器镜像,不如直接走阿里云媒体处理(MTS)或腾讯云 VOD 的 API,用 Storage::disk('oss')->put() 上传后调用 SDK 提交水印作业,反而更稳。


















