PHP调用FFmpeg Pipe推流:多标签页视频加载卡顿及响应延迟问题
多标签页访问视频监控页面时加载延迟/资源耗尽问题
问题现象
- 单页访问
index.php时4个视频可正常播放 - 多开标签页访问时,仅能显示2个视频,需等待较长时间才能加载全部;关闭一个标签页后所有视频恢复正常
- 服务器配置:Ubuntu 20.04、8GB内存,多开标签页可稳定复现
当前代码实现
index.php
<html> <video src="test.php?video_name=1.mp4" type="video/mp4" autoplay="autoplay" class="video"></video> <video src="test.php?video_name=2.mp4" type="video/mp4" autoplay="autoplay" class="video"></video> <video src="test.php?video_name=3.mp4" type="video/mp4" autoplay="autoplay" class="video"></video> <video src="test.php?video_name=4.mp4" type="video/mp4" autoplay="autoplay" class="video"></video> <html>
test.php
<? $video_name=$_GET['video_name']; header('Content-type: video/mp4'); passthru('ffmpeg -i '.$video_name.'.mp4 -c copy -movflags frag_keyframe+empty_moov+faststart -f mp4 pipe:'); exit(); ?>
核心原因
- 每个视频标签的请求都会触发PHP调用一次FFmpeg进程,多标签页/多用户访问时,FFmpeg进程数量指数级增长(比如2个标签页会启动8个FFmpeg进程)
- FFmpeg即使是
-c copy模式也会占用一定CPU和内存资源,大量并发进程直接耗尽服务器资源,导致请求阻塞 - PHP的
passthru会持续占用进程直到FFmpeg输出结束,未做任何资源限制或缓存处理
解决方案
1. 预转码视频文件,直接静态分发
既然使用-c copy仅做封装格式调整,提前一次性转码所有视频文件,无需每次请求都调用FFmpeg:
ffmpeg -i 1.mp4 -c copy -movflags frag_keyframe+empty_moov+faststart 1_transcoded.mp4 # 对2.mp4、3.mp4、4.mp4执行同样命令
修改index.php直接指向转码后的静态文件:
<video src="1_transcoded.mp4" type="video/mp4" autoplay="autoplay" class="video"></video> <!-- 其他视频同理修改 -->
这是最优解,彻底消除PHP和FFmpeg的进程开销,Web服务器静态文件分发效率远高于动态调用。
2. 启用视频请求缓存(针对实时流场景)
如果是实时摄像头流无法预转码,给FFmpeg输出加缓存:
- 可使用Nginx的
proxy_cache或fastcgi_cache缓存视频片段 - 或在PHP中实现文件缓存,第一次请求生成转码后的临时文件,后续请求直接返回该文件:
<? $video_name = $_GET['video_name']; $cache_file = "/tmp/{$video_name}_transcoded.mp4"; // 检查缓存是否存在且未过期 if (!file_exists($cache_file) || filemtime($cache_file) < filemtime("{$video_name}.mp4")) { exec("ffmpeg -i {$video_name}.mp4 -c copy -movflags frag_keyframe+empty_moov+faststart -f mp4 {$cache_file}"); } header('Content-type: video/mp4'); readfile($cache_file); exit(); ?>
3. 限制并发FFmpeg进程数
使用flock给每个视频加锁,确保同一视频同时只有一个FFmpeg进程在处理:
<? $video_name = $_GET['video_name']; $lock_file = "/tmp/{$video_name}.lock"; $fp = fopen($lock_file, 'w'); // 加排他锁,等待锁释放再执行 if (flock($fp, LOCK_EX)) { header('Content-type: video/mp4'); passthru("ffmpeg -i {$video_name}.mp4 -c copy -movflags frag_keyframe+empty_moov+faststart -f mp4 pipe:"); flock($fp, LOCK_UN); } else { header("HTTP/1.1 503 Service Unavailable"); exit("Too many requests"); } fclose($fp); unlink($lock_file); exit(); ?>
4. 优化Web服务器进程配置
- 调整Apache/Nginx的最大连接数、PHP-FPM的进程数,避免因进程耗尽导致请求阻塞
- 比如PHP-FPM的
pm.max_children可设置为服务器CPU核心数的2-4倍(8GB内存建议设置为16-32)
内容的提问来源于stack exchange,提问作者cihan ranx
相关产品推荐
相关产品推荐

