PHP调度FFmpeg生成多码率HLS流时,每路输出必须成对指定-vf缩放、-c:v编码器、-b:v码率及-audio参数,避免滤镜复用错误;-hls_time设为6秒、-hls_list_size 0、-hls_playlist_type vod,并确保Nginx/Apache正确配置.ts/.m3u8 MIME类型及文件可访问性。

ffmpeg 生成多码率自适应 HLS 流,PHP 不负责编码,只调度和组装命令。关键不是“能不能做”,而是“命令怎么写才不出错、能播、不卡”。
多路输出时 -vf 和 -c:v 必须成对出现
常见错误是把所有 -vf 参数堆在一条命令开头,结果只有第一个分辨率生效,其余被忽略或报错 Option vf not found。
FFmpeg 的多路输出语法要求:每一路输出前必须重新指定输入流处理逻辑(含缩放、编码器、码率等),不能复用前面的滤镜设置。
-
-vf scale=1280:720后必须紧跟-c:v libx264 -b:v 2500k,再接-f hls ... output_720.m3u8 - 第二路要重复写
-vf scale=854:480 -c:v libx264 -b:v 1200k -f hls ... output_480.m3u8 - 音频不能漏:每路都建议加
-c:a aac -b:a 128k,否则部分播放器会静音 - 避免用
-an(禁用音频)——除非你确认所有终端都不需要声音,否则 iOS Safari 会直接拒播
PHP exec() 调用时必须处理超时与权限
一个 5 分钟的视频转多码率,ffmpeg 可能跑 3–8 分钟。PHP 默认 max_execution_time=30,直接中断,output.m3u8 文件残缺,播放器报 Invalid playlist。
- 调用前加
set_time_limit(0),但仅对 CLI 模式有效;Web 环境下必须用异步方式(如proc_open()+ 后台进程) -
exec()返回值为 0 不代表成功——要看ffmpeg日志里有没有error或failed字样,建议捕获 stderr:2>&1 - 确保 PHP 进程有写入
hls/目录的权限,且磁盘空间足够(TS 片段 + m3u8 文件总大小通常是源文件的 1.2–1.8 倍) - 不要用
shell_exec()直接拼接用户上传的文件名,防止命令注入;用escapeshellarg()包裹路径
HLS 切片参数影响首屏加载与切换流畅度
-hls_time 不是越小越好,也不是越大越稳。设为 2 秒,切片太多,m3u8 文件膨胀,iOS 上易触发 NSURLErrorTimedOut;设为 10 秒,码率切换延迟高,网络波动时卡顿明显。
立即学习“PHP免费学习笔记(深入)”;
- 推荐值:4–6 秒(
-hls_time 6),平衡首帧延迟与自适应响应速度 -
-hls_list_size 0必须加——否则 m3u8 只保留最近 N 个 TS 条目,VOD 场景下播放会中断 -
-hls_playlist_type vod告诉播放器这是点播,禁用实时更新逻辑,避免#EXT-X-ENDLIST缺失导致无限加载 - 加
-start_number 0统一索引起点,避免某些安卓播放器解析失败
生成的 m3u8 文件必须可被 Web 服务器直接访问
哪怕 ffmpeg 成功输出了 index.m3u8 和一堆 index0.ts,如果路径不在 Web 根目录下,或者 Nginx/Apache 没开 .ts MIME 类型,浏览器就只会显示黑屏+加载转圈。
- 输出路径建议为
/var/www/html/hls/{video_id}/index.m3u8,对应 URL 是https://example.com/hls/abc123/index.m3u8 - Nginx 需添加:
location ~ \.ts$ { add_header Content-Type application/vnd.apple.mpegurl; } - Apache 需确保
.htaccess或主配置中有:AddType application/vnd.apple.mpegurl .m3u8和AddType video/MP2T .ts - 检查 m3u8 文件里每个
.ts路径是否可 curl 直达;相对路径容易出错,优先用绝对路径或确保 base-url 一致
ffmpeg”,而是 m3u8 文件里某一行路径写错、Nginx 少配了一行 MIME、或者 iOS 设备上 AAC 音频没带 -profile:a aac_low 导致无声——这些细节不验证,前端永远播不出来。



















