php 8 需通过 proc_open 捕获 ffmpeg 的 stderr 实时解析 time= 字段,结合 ffprobe 获取总时长计算进度百分比,并用 sse 或 websocket 推送;须加 -stats -v quiet 参数、设非阻塞流、用绝对路径并处理编码与权限。

PHP 8 本身无法直接“获取” FFmpeg 转码的实时进度,因为 FFmpeg 是外部命令行程序,它默认将进度信息输出到 stderr(标准错误流),且是动态刷新的(如覆盖同一行),不是结构化数据。PHP 要拿到这个进度,核心思路是:**启动 FFmpeg 进程,捕获其 stderr 输出,并实时解析其中的 time=xxx、speed=xxx 等字段,再结合输入文件时长估算百分比**。
1. 启动 FFmpeg 并捕获 stderr 流
不能用 exec() 或 shell_exec(),它们会阻塞直到命令结束,且拿不到实时输出。必须使用 proc_open() 创建进程并保持管道打开:
- 设置
stderr为管道(pipe),并设为非阻塞(stream_set_blocking($pipes[2], false)) - 避免 FFmpeg 因缓冲或交互模式卡住:加
-y -v quiet -stats参数(-stats确保输出进度行;-v quiet抑制其他杂讯,只留关键行) - 示例命令:
ffmpeg -i input.mp4 -c:v libx264 -c:a aac output.mp4 -y -v quiet -stats
2. 解析 FFmpeg 的实时进度行
FFmpeg 每秒左右会输出类似这样的行(写入 stderr):
你需要从这行中提取 time= 后的播放时间(如 00:00:52.12),再转换为秒数(52.12)。同时需提前用 ffprobe 获取输入文件总时长(单位:秒):
ffprobe -v quiet -show_entries format=duration -of default=nw=1 input.mp4- 进度百分比 =
(current_time / total_duration) * 100(注意:这是预估,非绝对精确,因编码帧顺序和 B 帧可能导致 time 暂停或跳变)
3. 在 PHP 中边转码边推送进度(如通过 SSE 或 WebSocket)
单纯解析还不够,要让前端“看到”进度,需配合服务端推送机制:
- 用
while ($procRunning)循环读取 stderr 管道,每次读到新行就解析 time 字段 - 计算百分比后,可通过
echo "data: {\"progress\": 42}\n\n"+ob_flush(); flush();实现 Server-Sent Events(SSE) - 若用 WebSocket(如 Ratchet),则在解析出新进度时调用
$conn->send(json_encode([...])) - 务必设置超时和最大运行时间,防止僵死进程(
proc_get_status()定期检查)
4. 注意事项与常见坑
实际部署时容易出问题,这些细节不能忽略:
- Linux 权限:PHP 运行用户(如 www-data)必须有权限执行 ffmpeg 和读写输入/输出文件
-
路径问题:用绝对路径调用 ffmpeg(
/usr/bin/ffmpeg),避免 PATH 环境差异 -
字符编码:FFmpeg 输出可能是 UTF-8 或 locale 编码,解析前可用
mb_convert_encoding()统一 -
不显示进度?检查是否漏了
-stats;或 FFmpeg 版本太低(建议 ≥ 4.0);或用了-v error把 stats 行也屏蔽了 -
进度卡住?可能是 stderr 缓冲未及时刷新,可在命令前加
stdbuf -eL(Linux)强制行缓存
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











