php调用ffmpeg前必须确认三件事:ffmpeg在系统path中可执行、php执行用户对文件有读写权限、源视频编码被当前ffmpeg版本支持;需用shell_exec配合2>&1捕获错误,结合escapeshellarg、-y参数及file_exists二次校验,并通过异步队列避免web请求超时。

PHP调用ffmpeg命令前必须确认的三件事
PHP本身不支持视频格式转换,所有“PHP转MP4”本质都是调用系统级工具(主要是ffmpeg)。直接写exec('ffmpeg -i ...')却失败,90%是因为没查清这三点:
• ffmpeg是否在系统PATH中(运行which ffmpeg或ffmpeg -version验证)
• PHP执行用户(如www-data、apache)是否有权限读源文件、写目标目录、执行ffmpeg二进制
• 源视频编码是否被当前ffmpeg版本支持(尤其H.265/HEVC或带DRM的文件会静默失败)
用shell_exec安全执行转码并捕获错误
别用exec()忽略输出,也别用system()直接回显——出错时你根本不知道哪步挂了。正确做法是统一用shell_exec()配合2>&1捕获全部输出:
$input = '/path/to/uploaded/video.mov';
$output = '/var/www/archives/' . date('YmdHis') . '_' . uniqid() . '.mp4';
$cmd = "ffmpeg -i " . escapeshellarg($input) . " -c:v libx264 -c:a aac -strict experimental -y " . escapeshellarg($output) . " 2>&1";
$result = shell_exec($cmd);
if (strpos($result, 'error') !== false || !file_exists($output)) {
error_log("FFmpeg failed: " . $result);
throw new Exception("Video conversion failed");
}
关键点:
• escapeshellarg()必须包裹所有路径,防止空格或特殊字符破坏命令
• -y跳过确认提示,否则命令卡住
• -strict experimental让AAC编码器接受非标准参数(常见于老版ffmpeg)
• 转码后务必用file_exists()二次确认,因为ffmpeg有时返回0但实际没生成文件
重命名逻辑要避开文件名冲突和时间精度问题
单纯用date('Y-m-d-H-i-s')当文件名,在高并发上传场景下极易重复。更稳妥的方式是组合时间戳+随机串+原始扩展名(注意不是硬写.mp4):
$ext = pathinfo($input, PATHINFO_EXTENSION);
$safe_name = date('YmdHis') . '_' . substr(md5(uniqid()), 0, 8) . '.' . ($ext === 'mp4' ? 'mp4' : 'mp4');
// 实际归档路径:/archives/20240520153022_abc12345.mp4
常见陷阱:
• 不检查$input是否真有PATHINFO_EXTENSION(比如无扩展名文件会返回空)
• 把重命名逻辑放在转码前——万一转码失败,你已删/移动了原文件
• 归档目录没提前mkdir()且没设755权限,导致写入失败
为什么不要在Web请求里长时间执行转码
一个10分钟的视频转码可能耗时几十秒,PHP默认max_execution_time是30秒,超时直接中断,且阻塞整个Apache/Nginx worker进程。生产环境必须异步:
• 把转码任务写入队列(如Redis List或数据库任务表)
• 启一个常驻PHP脚本(php worker.php)轮询并执行shell_exec
• Web端只返回“已提交处理”,前端轮询状态接口
• 或用nohup ffmpeg ... >/dev/null 2>&1 &后台运行,但需额外机制监听完成信号
最易被忽略的是磁盘空间监控——临时转码过程可能占用2倍原文件体积,归档前不清理/tmp或源上传目录,几次大文件上传就撑爆磁盘。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











