passthru() 不该用于 ffmpeg,因其不捕获 stderr、不检查退出码、无法解析进度、不能二次验证,且放大路径/编码风险;应改用 proc_open() 手动接管三管道并逐行处理 stderr。

直接用 passthru() 调用 ffmpeg 是最危险的“看似能跑通、实则埋雷”的做法——它不捕获输出、不检查退出码、不处理错误流,一旦命令失败或路径含空格/中文,PHP 就静默吞掉所有线索,只留下一个损坏的视频文件。
为什么 passthru() 不该用于 ffmpeg
passthru() 的设计目标是“把子进程的 stdout 直接透传给 Web 服务器”,适合生成纯文本报告或简单状态输出;但 ffmpeg 的行为完全相反:它把进度写到 stderr,关键错误也混在其中,成功时甚至可能不输出任何 stdout。你看到浏览器里刷出一堆“frame= 1234”并不等于转码成功,更不代表文件可播放。
- 无法获取退出码:
passthru()第二个参数是「引用变量」,但它只接收命令的退出状态(int),而不会告诉你 stderr 里有没有Invalid data found when processing input - 无法解析进度:
stderr被直接刷到响应体,PHP 没法逐行读取、提取帧数或估算剩余时间 - 无法做二次验证:转码完不能立刻调用
ffprobe验证输出文件是否结构完整,因为不知道命令到底跑完了没 - 路径和编码陷阱被放大:Windows 下含中文路径、Linux 下空格未转义,
passthru()会直接报错退出,但你收不到任何提示
替代方案:proc_open() 是唯一靠谱的选择
必须用 proc_open() 手动接管 stdin/stdout/stderr 三根管道,才能真正掌控 ffmpeg 进程生命周期。下面是最小可行封装:
PHP 8.5.12 RC 1 候选版修复 BZ2、DOM、PDO、Opcache/JIT 和扩展内存安全问题,改进 Unicode 字素处理、URL 解析、TLS 流状态、信号分发及会话错误处理。
function runFfmpeg($cmd) {
$descriptors = [
0 => ['pipe', 'r'], // stdin
1 => ['pipe', 'w'], // stdout(ffmpeg 通常不用)
2 => ['pipe', 'w'], // stderr(进度和错误全在这)
];
$process = proc_open($cmd, $descriptors, $pipes);
if (!is_resource($process)) {
throw new RuntimeException('Failed to start ffmpeg process');
}
stream_set_blocking($pipes[2], false); // stderr 非阻塞读取
$output = '';
while (is_resource($process)) {
$line = fgets($pipes[2]);
if (false === $line) break;
$output .= $line;
// 可在此处匹配 "frame=\d+" 提取进度,或检测 "Error" 关键字
if (strpos($line, 'Error') !== false || strpos($line, 'failed') !== false) {
break;
}
}
$returnCode = proc_close($process);
if ($returnCode !== 0) {
throw new RuntimeException("FFmpeg failed with code {$returnCode}: {$output}");
}
return $output;
}
- 必须显式指定
2 => ['pipe', 'w'],否则 stderr 会丢进 Web 服务器日志,你根本看不到 -
stream_set_blocking($pipes[2], false)是关键:避免 fgets() 卡住,让 PHP 能边读边判断 - 不要依赖
$returnCode === 0就认为成功——有些 ffmpeg 错误(如音轨缺失)仍返回 0,得结合 stderr 内容判断 - 命令字符串本身必须用
escapeshellarg()包裹所有路径,尤其是用户上传的文件名
常见错误命令写法与修正
以下写法在 passthru() 下看似能跑,在 proc_open() 下立刻暴露问题:
立即学习“PHP免费学习笔记(深入)”;
- 错误:
ffmpeg -i input.mp4 -c:v libx264 output.mp4→ 缺少-y,遇到同名文件会卡在交互式确认
修正:ffmpeg -y -i 'input.mp4' -c:v libx264 'output.mp4' - 错误:
ffmpeg -i video.mov -vf subtitles=cn.srt out.mp4→ Windows 下cn.srt路径含空格或中文会崩
修正:ffmpeg -y -i 'video.mov' -vf subtitles='cn.srt' 'out.mp4',且确保cn.srt是 UTF-8 编码 - 错误:
ffmpeg -i a.mp4 -i b.mp4 -filter_complex "[0:v][1:v]hstack" out.mp4→ 没指定音频流,可能无声或报错
修正:加-c:a copy或-an明确音频策略
真正难的不是写对一条命令,而是让 PHP 在 ffmpeg 崩溃、卡死、输出损坏时能第一时间感知并中断——这要求你放弃 passthru() 的“省事幻觉”,老老实实处理管道、退出码和 stderr 的每一行输出。漏掉任意一环,线上就可能出现“转码完成但视频打不开”的静默故障。


















