exec() 在 thinkphp 中调用 ffmpeg 失败主因是 cli 环境不继承系统 path,需用绝对路径(如 /usr/bin/ffmpeg);加 2>&1 捕获错误、检查 web 用户权限、避免用 console 门面封装。

为什么 exec() 在 ThinkPHP 中调用 FFmpeg 总是失败?
因为 ThinkPHP 的默认运行环境(尤其是 CLI 模式下)不继承系统 PATH,ffmpeg 命令找不到可执行文件。不是权限问题,也不是 PHP 配置禁用了函数——exec() 本身能跑,但找不到 ffmpeg。
实操建议:
- 用绝对路径调用:先在终端运行
which ffmpeg,拿到结果如/usr/bin/ffmpeg,代码里必须写全路径,不能只写ffmpeg - 检查 Web 用户权限:Nginx/Apache 运行用户(如
www-data或nginx)是否对 FFmpeg 二进制文件和输入/输出目录有读写权限 - 加
2>&1捕获错误:比如exec('/usr/bin/ffmpeg -i ... 2>&1', $output, $return_code),否则静默失败 - ThinkPHP 的
think\facade\Console不适合调外部命令,别试图用它封装 FFmpeg 调用
怎么用 FFmpeg 给 MP4 视频加右下角文字水印且不压帧?
核心是用 drawtext 滤镜 + -c:v libx264 -crf 23 保持编码质量,避免二次编码导致画质崩坏或耗时飙升。
实操建议:
- 关键参数必须带
-vf滤镜链:-vf "drawtext=fontfile=/path/to/msyh.ttf:fontsize=24:fontcolor=white:x=w-tw-10:y=h-th-10:text='©MySite'"(注意fontfile必须是服务器绝对路径,中文需指定支持 Unicode 的字体) - 强制保留原视频编码参数:加上
-c:a copy -c:v libx264 -crf 23 -preset medium;-c:a copy表示音频直接复制,不重编码 - 输入文件路径必须用单引号包裹,防止路径含空格或特殊字符被 shell 截断
- 不要用
-y直接覆盖,先生成临时文件,成功后再rename()替换,避免上传中途失败导致原文件丢失
上传大视频时进程卡住或超时怎么办?
ThinkPHP 默认的 max_execution_time 和 Nginx 的 client_max_body_size、proxy_read_timeout 全都会拦你。FFmpeg 处理 500MB 视频可能要 3–5 分钟,PHP 脚本早被 kill 了。
实操建议:
- 上传阶段用分片上传(如
plupload或webuploader),后端用 ThinkPHP 的think\File合并,避开 PHP 执行时间限制 - FFmpeg 处理必须丢到后台:用
nohup /usr/bin/ffmpeg ... > /dev/null 2>&1 &启动,再用数据库记录任务状态,前端轮询查进度 - Nginx 配置至少加三行:
client_max_body_size 2G;、proxy_read_timeout 600;、fastcgi_read_timeout 600; - 别在控制器里同步调 FFmpeg —— 即使设了
set_time_limit(0),Web 服务器仍会切断连接
怎么确保水印位置适配不同分辨率的视频?
drawtext 的 x 和 y 支持表达式,但必须用单引号包裹整个滤镜字符串,且内部用双引号或转义,否则 FFmpeg 解析报错。
实操建议:
- 动态计算右下角:用
x=w-tw-10(w 是视频宽,tw 是文字宽)和y=h-th-10(h 是视频高,th 是文字高),这是最稳的写法 - 如果要用比例定位(比如右下角 5% 处),写成
x='w-tw-0.05*w',注意单引号不能少,否则 shell 把$w当作变量提前展开 - 测试时先用
-t 5截取前 5 秒快速验证滤镜效果,别一上来就跑完整视频 - 某些老版本 FFmpeg 不支持
w/h变量,升级到 4.0+ 更稳妥
真正麻烦的是字体渲染兼容性:Linux 服务器没装中文字体、fontfile 路径权限不对、或者 FFmpeg 编译时没开 freetype 支持,都会让文字变方块或直接崩溃。这个点,比写对命令难查得多。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











