php调用ffmpeg转码失败主因是环境不可靠或路径不一致;推荐docker封装实现环境隔离,需正确挂载宿主机目录、显式指定镜像版本、过滤用户输入防注入,并通过proc_open捕获错误及异步处理大文件。

php 调用转码失败,大概率不是 PHP 本身的问题,而是 FFmpeg 环境不可靠或调用路径不一致。直接在宿主机装 FFmpeg 容易污染系统、版本难统一、权限难收敛;用 Docker 封装后,PHP 只需稳定调用容器内命令,环境隔离、可复现、易迁移。
docker run 命令里必须加 -v 挂载宿主机文件目录
PHP 的 exec() 或 shell_exec() 启动的容器默认无法访问 PHP 进程所在目录的视频文件——因为容器是沙箱,没挂载就看不见宿主机路径。
- 错误写法:
docker run jrottenberg/ffmpeg ffmpeg -i input.mp4 output.mp4(input.mp4 在 PHP 当前目录,但容器里根本不存在) - 正确写法:
docker run -v $(pwd):/workspace jrottenberg/ffmpeg ffmpeg -i /workspace/input.mp4 /workspace/output.mp4 - PHP 中拼接命令时,务必把 PHP 获取到的绝对路径(如
/var/www/uploads/video.mp4)映射进容器,例如:-v /var/www/uploads:/mnt,然后命令里用/mnt/video.mp4 - 注意:挂载路径两端都得是绝对路径;Windows 下 Docker Desktop 需提前在 Settings → Resources → File Sharing 中添加对应盘符(如
C:\)
PHP 执行 docker 命令前要检查权限和超时
exec() 默认受 disable_functions 限制,且容器启动+FFmpeg执行耗时可能远超 Web 请求默认的 30 秒超时。
- 确认
docker命令在 PHP 运行用户(如www-data或nginx)下可用:sudo -u www-data which docker - 给 PHP 用户加
docker组权限:sudo usermod -aG docker www-data,然后重启 PHP-FPM 或 Nginx - PHP 脚本里显式设置执行超时:
set_time_limit(300);(5 分钟),避免大视频卡住进程 - 建议用
proc_open()替代exec(),能捕获 stderr 输出,便于定位 FFmpeg 报错(比如Unknown encoder 'libx264'表示镜像没编译该编码器)
别用 :latest 镜像,固定 jrottenberg/ffmpeg 版本号
latest 标签不保证向后兼容:某次更新可能删掉 libvpx,或升级 FFmpeg 导致 CRF 行为变化,PHP 侧参数全得跟着调。
- 查当前镜像支持的编码器:
docker run --rm jrottenberg/ffmpeg:6.1-alpine ffmpeg -encoders | grep x264 - 生产环境明确指定带版本和平台的 tag,例如:
jrottenberg/ffmpeg:6.1-alpine(轻量)、jrottenberg/ffmpeg:6.1-nvidia(需 GPU 加速) - 如果项目依赖
vaapi,必须选ubuntu或debian基础镜像(Alpine 不支持),例如:jrottenberg/ffmpeg:6.1-ubuntu2204 - Docker Hub 上
jrottenberg/ffmpeg的所有 tag 和构建配置都公开在 GitHub:https://www.php.cn/link/bfe175c77f43d0aee301e9487256b576
PHP 调用时要过滤输入路径,防止命令注入
用户上传的文件名若含空格、分号、$() 等字符,直接拼进 shell 命令会触发注入,后果可能是容器被逃逸或宿主机文件被删。
- 绝对不要这样写:
"docker run ... ffmpeg -i $input_path ..." - 必须用
escapeshellarg()包裹每个路径参数:escapeshellarg("/mnt/" . basename($input_file)) - 更安全的做法:先用
pathinfo()提取合法文件名,再拼路径;或改用临时目录 + 随机名,避免用户控制文件名 - 额外加固:Docker 运行时加
--read-only和--tmpfs /tmp:size=128m,限制容器写权限
exec() 启动的 docker run 是子进程,一旦 PHP 请求结束,它可能被 SIGPIPE 或孤儿化终止。大视频转码必须走异步——要么用队列(Redis + Worker),要么让容器自己回传结果到 COS/OSS,PHP 只负责发任务、查状态。php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











