php视频转码必须异步队列处理,禁用web请求中直接执行ffmpeg;需用redis或rabbitmq调度任务,worker独立运行、设set_time_limit(0),调用proc_open捕获stderr,加-y、-crf、线程限制及输出校验。

PHP上传后不直接调用FFmpeg,必须走队列
直接在Web请求里执行ffmpeg命令是危险操作:超时、内存溢出、阻塞后续请求,几乎必然发生。PHP的max_execution_time再调大也扛不住1080p视频转码动辄数分钟的耗时。
正确做法是上传完成即返回成功响应,把转码任务推入队列,由独立Worker进程消费执行。Redis List或RabbitMQ都可选,但Redis更轻量、部署简单:
- 上传接口收到
input.mp4后,存到对象存储(如MinIO),生成唯一task_id - 构造JSON消息:
{"task_id":"t_abc123","src_path":"s3://bucket/uploads/abc123.mp4","target_format":"mp4","resolution":"1280x720"} - 用
RPUSH写入transcode_queue,立刻返回202 Accepted
Worker进程必须脱离Web服务器生命周期
用php artisan queue:work(Laravel)或自建守护脚本跑Worker,不能依赖exec()在Web上下文中触发。否则Apache/Nginx超时后杀掉子进程,ffmpeg变成孤儿进程,文件句柄泄漏、CPU持续满载。
关键控制点:
- Worker启动时调用
set_time_limit(0),禁用PHP超时 - 用
proc_open()而非shell_exec(),便于捕获ffmpegstderr并实时记录进度 - 务必设置
-threads 2参数限制线程数,避免多任务并发时CPU争抢 - 转码前检查源文件完整性:
ffmpeg -v error -i input.mp4 -f null -,失败则跳过并标记invalid_file
FFmpeg命令必须带容错与标准输出控制
裸用ffmpeg -i input.avi output.mp4在生产环境等于埋雷。实际命令需兼顾兼容性、错误可追踪、资源可控:
- 强制指定编码器:
-c:v libx264 -c:a aac,避免某些源文件触发非H.264/AAC路径导致播放失败 - 用
-crf 23替代-b:v控制画质,比固定码率更适应内容复杂度 - 加
-y覆盖输出文件,防止因同名文件存在导致ffmpeg交互式等待输入 - 重定向stderr到日志:
2>&1 | tee /var/log/transcode/t_abc123.log,便于排查Invalid data found when processing input类错误
示例完整命令:
ffmpeg -y -i s3://bucket/uploads/abc123.mp4 \ -c:v libx264 -crf 23 -preset medium -vf "scale=1280:720:force_original_aspect_ratio=decrease,pad=1280:720:(ow-iw)/2:(oh-ih)/2" \ -c:a aac -b:a 128k \ -s3://bucket/outputs/abc123_720p.mp4 2>&1 | tee /var/log/transcode/t_abc123.log
状态更新和失败重试不能靠“一次成功”幻想
网络抖动、磁盘满、FFmpeg版本bug都会导致转码中途退出。Worker必须主动上报状态,且失败后自动进重试队列(最多3次):
- 每次启动时向数据库插入
status = 'processing'记录 - 转码成功后更新为
'completed',并写入output_url字段 - 捕获
proc_get_status()返回的exitcode !== 0,记录error_message并LPUSH回队列 - 前端轮询
/api/task-status?t=abc123,响应含progress百分比(需解析ffmpegstderr中的frame=...行)
真正容易被忽略的是:FFmpeg对某些损坏MP4的解复用会静默失败,只输出几帧就退出,但exitcode仍是0。必须额外校验输出文件是否可播放——用ffprobe -v error -show_entries format=duration -of default input.mp4确认时长有效。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











