You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 16:57:40