如何在PHP shell_exec()中用timeout正确实现FFmpeg转码超时控制
问题1:屏蔽FFmpeg日志并正确获取超时状态码
你之前写的if ((int)$result == 124)逻辑本身无法生效:shell_exec() 函数只返回命令打印的输出内容,拿不到进程的退出状态码。就算完全屏蔽FFmpeg的所有日志,$result只会是空字符串,强转成int后值为0,永远匹配不到124的超时状态。
正确实现步骤:
- 不用给FFmpeg加
-loglevel quiet,直接把命令的标准输出、标准错误全部重定向到系统空设备/dev/null,就能彻底丢弃所有FFmpeg日志,包括异常报错输出,比调整日志等级的方式更彻底。 - 换用
exec()函数执行命令,它支持通过第三个参数拿到命令的退出状态码,直接判断这个值就能识别是否超时。 - 拼接命令时所有文件路径参数必须用
escapeshellarg()转义,避免文件名带空格、特殊字符导致命令执行失败,同时防范命令注入风险。
参考代码:
// 拼接转码命令,所有输出重定向到空设备丢弃 $cmd = sprintf( 'timeout 30 ffmpeg -i %s -vcodec h264 -filter:v fps=30 -acodec aac %s > /dev/null 2>&1', escapeshellarg($filetmp), escapeshellarg($filetmp . '.mp4') ); $execOutput = []; $exitCode = 0; exec($cmd, $execOutput, $exitCode); // 直接判断退出码即可识别超时 if ($exitCode === 124) { // 此处写超时触发后的业务逻辑 }
问题2:是否需要添加
--kill-after=DURATION参数 要不要加这个参数看你的业务场景,先明确timeout的默认工作逻辑:
默认超时触发后,timeout会先给FFmpeg发SIGTERM信号,FFmpeg收到这个信号会正常收尾:写完MP4的文件索引、关闭文件句柄再退出,这种情况下生成的视频文件是可以正常播放的。但如果遇到源文件损坏、磁盘IO卡住这类异常情况,FFmpeg会卡住不响应SIGTERM,进程一直残留占用CPU、内存、IO资源。
选择建议:
- 如果你的视频源都是可控的、很少有损坏文件,优先保证转码出来的文件能正常播放,就不用加这个参数。
- 如果经常遇到异常视频导致FFmpeg进程卡死、占满服务器资源的情况,建议加上这个参数,宽限期设3~5秒就够,比如写成
timeout --kill-after=5 30 ffmpeg [...]。意思是发完SIGTERM等5秒,要是进程还没退,就直接发SIGKILL信号强制杀进程,避免留僵尸进程耗资源。
注意:被
SIGKILL强制杀掉的FFmpeg进程来不及做文件收尾,生成的MP4大概率是损坏的没法播放,加这个参数的话要配套做超时后坏文件清理、转码结果校验的逻辑。
内容的提问来源于stack exchange,提问作者Avatar
相关产品推荐
相关产品推荐

