必须确认三件事:ffmpeg是否已安装并可执行;水印图片路径为绝对路径且PHP进程有读取权限;视频输入与输出目录可写且磁盘空间充足。

PHP 用 ffmpeg 叠加水印前必须确认的三件事
直接调用 ffmpeg 命令叠加水印失败,大概率不是命令写错,而是环境或路径没对齐。先确认:
• 服务器上 ffmpeg 是否真能执行(运行 which ffmpeg 或 ffmpeg -version);
• 水印图片(如 logo.png)路径是绝对路径,且 PHP 进程有读取权限;
• 待处理视频和输出目录可写,且磁盘空间足够(1080p 视频临时转码可能吃掉 2–3 倍空间)。
exec() 调用 ffmpeg 命令时如何避免静默失败
PHP 的 exec() 默认不抛异常,错误全被吞掉。必须捕获输出并检查返回码:
• 用 exec($cmd, $output, $return_code),$return_code !== 0 就代表失败;
• 把 2>&1 加到命令末尾,把 stderr 合并进 stdout,否则你看不到 “No such file” 这类关键报错;
• 不要用 shell_exec(),它不返回状态码,出问题只能瞎猜。
exec('ffmpeg -i /tmp/upload.mp4 -i /var/www/logo.png -filter_complex "overlay=10:10" -y /tmp/output.mp4 2>&1', $output, $code);
if ($code !== 0) {
error_log('FFmpeg failed: ' . implode("\n", $output));
}
水印位置、缩放和透明度怎么用 -filter_complex 精确控制
硬编码 overlay=x:y 太死板,用户上传横竖屏混杂时水印会跑偏。更稳妥的做法:
• 用 overlay=W-w-10:H-h-10 放右下角(W/H 是视频宽高,w/h 是 logo 宽高);
• 先用 scale=120:-1 缩放 logo,保持宽高比;
• 加 format=rgba 和 colorchannelmixer=aa=0.7 控制透明度(aa 是 alpha 值,0.0–1.0)。
ffmpeg -i video.mp4 -i logo.png -filter_complex "[1:v]scale=120:-1,format=rgba,colorchannelmixer=aa=0.7[logo]; [0:v][logo]overlay=W-w-10:H-h-10" -y out.mp4
为什么不能在用户请求中直接执行 ffmpeg?
视频转码耗时长(几秒到几分钟),阻塞 PHP-FPM 进程,导致后续请求超时或 504。必须异步:
• 上传成功后只存原始文件,写一条任务记录到数据库(含 video_id、logo_path、status);
• 用守护进程(如 supervisord 管理的 PHP CLI 脚本)轮询待处理任务;
• 或用消息队列(Redis List / Beanstalkd),但小项目用数据库轮询更轻量;
• 完成后更新状态,并触发回调(如生成缩略图、通知前端)。
立即学习“PHP免费学习笔记(深入)”;
水印逻辑本身不复杂,真正卡住人的永远是路径权限、异步调度和错误反馈这三块。漏掉任意一个,上线后都会变成“上传成功但没水印”的玄学问题。



















