PHP视频加水印超时本质是ffmpeg子进程阻塞,需用timeout命令包裹并设超时,配合proc_open流控制与Web服务器超时配置三者协同。

PHP视频加水印脚本超时,本质不是“水印慢”,而是执行路径卡在系统命令阻塞
直接说结论:你用 exec()、shell_exec() 或 system() 调用 ffmpeg 加水印时没设超时,一旦视频大、编码复杂或磁盘/IO卡顿,进程就会挂住,直到 PHP 的 max_execution_time 触发(比如 30 秒),报错 “Maximum execution time exceeded”。这不是水印逻辑的问题,是子进程失控。
ffmpeg 命令必须加 timeout 包裹,否则 Linux 层不终止
PHP 本身对 exec() 这类函数没有内置超时机制——它会傻等 ffmpeg 自己退出。而 ffmpeg 在遇到坏帧、缺失 codec 或 I/O 阻塞时可能永远不返回。正确做法是让操作系统强制截断:
- Linux/macOS 下,用系统自带的
timeout命令包裹:timeout 120s ffmpeg -i input.mp4 -i logo.png -filter_complex "overlay=10:10" output.mp4 - 务必用
escapeshellcmd()处理所有用户输入的路径或参数,防止命令注入 - 检查
$retval返回值:124 表示被timeout杀掉,126/127 是命令不存在或权限问题,0 才是成功 - Windows 不原生支持
timeout,需改用start /wait /b cmd /c "timeout /t 120 & ffmpeg ..."(不推荐,兼容性差)
proc_open + stream_set_timeout 是更可控的替代方案
如果你需要捕获 ffmpeg 实时输出(比如进度、错误日志),或想在 PHP 层主动判断超时,proc_open() 比 exec() 更可靠:
- 用
proc_open()启动ffmpeg,拿到stdout和stderr流句柄 - 对每个流调用
stream_set_timeout($stream, 120),单位是秒(注意:不支持毫秒) - 用
stream_select()轮询流是否就绪;若超时,立即proc_terminate()杀进程 - 最后用
proc_close()获取真实退出码,避免僵尸进程
这个方式能避免“PHP 等着 ffmpeg,ffmpeg 等着磁盘”,但代码量略多,适合中高阶需求。
立即学习“PHP免费学习笔记(深入)”;
别只调 set_time_limit(0),Web 服务器和 ffmpeg 自身也会掐断你
即使你在脚本开头写了 set_time_limit(0),也拦不住三层超时叠加:
- PHP 层:
max_execution_time = 0在 php.ini 中才真正生效(set_time_limit(0)在某些 PHP-FPM 配置下会被忽略) - Web 服务器层:Nginx 的
fastcgi_read_timeout默认 60 秒,Apache 的Timeout指令也常设为 300 秒——它们看到 PHP 进程没回包,直接返回 504,PHP 根本收不到 - ffmpeg 层:加水印时若指定
-t或使用-ss随机访问,底层 seek 可能因关键帧缺失而卡死数分钟,此时 timeout 是唯一救命稻草
真正稳住视频加水印,得同时配好三处:系统级 timeout 命令、PHP-FPM pool 的 php_admin_value[max_execution_time]、Nginx 的 fastcgi_read_timeout。漏一层,就白调。



















