📘m3u8云切片转码系统(PHP+FFmpeg)详细配置及排错指南

📌 1、环境要求与准备
环境 | 推荐版本 | 备注 |
---|---|---|
Linux系统 | Debian/CentOS 推荐 | 不建议Windows平台 |
Web环境 | Apache + PHP 7.1 + MySQL 5.6 | 宝塔面板推荐(易操作) |
执行权限 | 必须开启exec() 权限 | 宝塔面板的禁用函数 去掉exec 和shell_exec |
- 具体需求:
- Apache/Nginx 必须配置支持伪静态(后续播放和访问需要)。
- PHP需开启
exec()
,默认宝塔面板会禁用,需取消禁用。 - 数据库MySQL5.6版本以上。
- FFmpeg 可使用静态编译版本 (推荐版本 ≥3.0)。
📌 2、遇到的问题与逐步排查过程
(1)🔧 初步问题:转码任务一直不启动
原因:
- 最开始的
exec()
函数被PHP禁用,导致系统无法调用FFmpeg
进行转码。
解决方式:
- 宝塔面板 → 软件商店 → PHP管理 → 禁用函数,删除
exec
和shell_exec,保存并重启PHP。
新建一个php,验证exec是否可用,前端访问这个php文件。
然后到 logs/test_exec.log
看是否有输出:
- 有内容 ➜
exec()
没问题,问题出在转码命令; - 没内容或空文件 ➜
exec()
被彻底限制,宝塔可能还有残余设置没解开。
如果看到“执行结束”说明 PHP 正常运行了 exec()
<?php $cmd = './packs/ffmpeg -version'; exec($cmd, $out, $status); file_put_contents('logs/test_exec.log', implode("\n", $out)."\nSTATUS=$status\n", FILE_APPEND); echo "执行结束"; ?>
接下来检查这个test_exec.log文件 如果类似下面的并且最后是STATUS=0 说明一切正常
ffmpeg version N-47099-gdcbd89e000-static https://johnvansickle.com/ffmpeg/ built with gcc ... ... STATUS=0
完成之后可以在程序根目录下新建并运行下面的php页面验证FFmpeg:
<?php $output = []; $return_var = 0; exec('./packs/ffmpeg -version', $output, $return_var); echo "<pre>"; print_r($output); echo "返回状态码:$return_var"; echo "</pre>"; ?>
访问这个 PHP 页面,如果返回状态码不是 0
,或者没有输出,说明:
- 可能 PHP 禁用了
exec
函数 - 或者当前运行环境(例如 Open_basedir 或 safe_mode)不允许
正常情况下 在终端输入./packs/ffmpeg -version 如果ffmpeg正常 应该返回下面的信息
Array ( [0] => ffmpeg version N-47099-gdcbd89e000-static https://johnvansickle.com/ffmpeg/ Copyright (c) 2000-2018 the FFmpeg developers [1] => built with gcc 6.3.0 (Debian 6.3.0-18+deb9u1) 20170516 [2] => configuration: --enable-gpl --enable-version3 --enable-static --disable-debug --disable-ffplay --disable-indev=sndio --disable-outdev=sndio --cc=gcc-6 --enable-fontconfig --enable-frei0r --enable-gnutls --enable-gray --enable-libaom --enable-libfribidi --enable-libass --enable-libvmaf --enable-libfreetype --enable-libmp3lame --enable-libopencore-amrnb --enable-libopencore-amrwb --enable-libopenjpeg --enable-librubberband --enable-libsoxr --enable-libspeex --enable-libvorbis --enable-libopus --enable-libtheora --enable-libvidstab --enable-libvo-amrwbenc --enable-libvpx --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxml2 --enable-libxvid --enable-libzimg [3] => libavutil 56. 19.101 / 56. 19.101 [4] => libavcodec 58. 31.102 / 58. 31.102 [5] => libavformat 58. 18.104 / 58. 18.104 [6] => libavdevice 58. 4.105 / 58. 4.105 [7] => libavfilter 7. 33.100 / 7. 33.100 [8] => libswscale 5. 2.100 / 5. 2.100 [9] => libswresample 3. 2.100 / 3. 2.100 [10] => libpostproc 55. 2.100 / 55. 2.100 ) 返回状态码:0
(2)🔧 FFmpeg路径配置错误
问题表现:
- 转码页面显示
ffmpeg未找到
或提示ffmpeg error
。
排查方法:
- 后台配置路径:设置为绝对路径而非相对路径。
正确路径示例:
./packs/
(路径后必须有斜杠,且不要包含具体ffmpeg
文件名)
测试FFmpeg有效性PHP代码:
<?php exec("/www/wwwroot/你的网站/packs/ffmpeg -version", $output, $status); print_r($output); echo "STATUS=$status"; ?>
状态码为0且有输出表示成功。
(3)🔧 转码队列一直卡“等待转码”
这个问题可能出现在当前系统的转码调度器上
当前系统的“转码调度器”就是 Ting.php
控制器里的 transcode()
方法,它负责从数据库 queue
表里取任务,并用 ffmpeg
进行转码。
这种情况 你首先可以手动访问 http://你的域名/admin.php/ting/transcode
,查看返回的数字(视频编号)
假如返回的是数字 10
,说明 ID 为 10 的视频任务已经被调度器处理了!
但你观察后台状态仍然是“等待转码”,如果还未生成m3u8文件(你可以查看一下存不存在生成的m3u8和ts文件。我当时确实没有生成)说明问题已经明确是出在 FFmpeg 调用或执行失败阶段。
系统调用 FFmpeg 的地方其实是在 Ting::transcode()
的这段逻辑:
$this->load->library('xyz'); $format = $this->xyz->format($Video_Path.$uploadStr.$newname);
说明系统是通过一个叫 xyz
的类来封装 FFmpeg 的调用!
而这个Xyz.php的transcode()
方法中这句:
$result = exec($make_command,$arr,$log);
没有日志输出 无法定位
所以现在我们只要在 transcode()
方法中加上如下调试代码,就能完全清楚系统是否命令写错、路径不对,还是权限问题。
在下面这句
$result = exec($make_command,$arr,$log);
添加下面的代码:
file_put_contents('/tmp/ffmpeg_command.log', $make_command . "\n", FILE_APPEND); file_put_contents('/tmp/ffmpeg_output.log', implode("\n", $arr) . "\nEXIT CODE: $log\n", FILE_APPEND);
完成后大概长这样:
$result = exec($make_command,$arr,$log); file_put_contents('/tmp/ffmpeg_command.log', $make_command . "\n", FILE_APPEND); file_put_contents('/tmp/ffmpeg_output.log', implode("\n", $arr) . "\nEXIT CODE: $log\n", FILE_APPEND); if($log == 0){ return defined('KBPS') ? KBPS : 'm3u8ok'; }else{ return ''; } }
原因:
- 文件路径问题或队列状态被锁死(转码中断或进程假死)。
解决办法:
- 检查PHP代码
Ting.php
、Xyz.php
的路径正确性; - 确认
packs
目录中存在ffmpeg
和ffprobe
,并拥有755权限; - 杀死已有卡死进程:
ps aux | grep "ffmpeg" | awk '{print $2}' | xargs kill -9
(4)🔧 转码执行但未生成M3U8文件
错误表现:
- 转码队列状态已成功,但实际无
m3u8
文件生成。
排查方法:
- 手动执行FFmpeg命令,检查错误:
./packs/ffmpeg -i [源视频] -c:v libx264 -hls_time 10 -hls_list_size 0 -f hls [目标路径]/index.m3u8
- 错误提示如
Could not write header for output file...
表明目标文件夹未创建。 - 需要提前创建目标目录或确保程序的自动创建目录权限正常。
📌 最终成功的核心点:
- 文件夹必须有完整读写权限;
- PHP的
mkdir
函数有效(mkdirss
递归创建); - 手动创建测试文件夹确认FFmpeg正常执行后,转码即可顺利完成。
(5)🔧 HTTPS不支持问题
这套程序比较老旧,默认不支持https访问,你需要手动将硬编码部分的http改为https
表现:
- 代码默认生成
http://
链接,页面使用HTTPS访问会报错,视频无法播放。
排查发现:
- 核心函数
m3u8_link()
内硬编码了http://
协议。
修改方案:
找到定义 m3u8_link()
的文件(在 /application/helpers/common_helper.php
)
这部分已经写死了
$link = 'http://'.Web_Url.$webpath.'m3u8/'.sys_auth($path).'.'.$ac;
所以需要将http://改为https://
修改后应该长这样
function m3u8_link($path,$ac='m3u8',$xu=1){ $Mu_Path = substr(Mu_Path,0,2) == './' ? substr(Mu_Path,2) : Mu_Path; $webpath = CS_Web_Wjt == 1 ? Web_Path : Web_Path.'index.php/'; $protocol = 'https://'; // 强制使用 HTTPS //多图模式 if($ac != 'm3u8'){ if($xu > 1 && strpos($path, '[xu]') === false){ $name = end(explode('/', $path)); $path = str_replace($name, $xu.'_'.$name, $path); }else{ $path = str_replace('[xu]', $xu, $path); } if($ac == 'gif') $path = str_replace('.jpg', '.gif', $path); }else{ if(Mu_Type == 1 && $xu != '1'){ $ext = end(explode('/', $path)); $path = str_replace($ext, $xu.'/'.$ext, $path); } } if(Web_Play == ''){ if(Web_M3u8On == 1){ if($ac == 'm3u8'){ $link = $protocol.Web_Url.$webpath.'m3u8/'.sys_auth($path).'.'.$ac; }else{ $link = $protocol.Web_Url.$webpath.'m3u8/pic/'.sys_auth($path).'.'.$ac; } }else{ $link = $protocol.Web_Url.Web_Path.$Mu_Path.$path; } }else{ if(Web_M3u8On == 1){ if($ac == 'm3u8'){ $link = $protocol.Web_Play.$webpath.'m3u8/'.sys_auth($path).'.'.$ac; }else{ $link = $protocol.Web_Play.$webpath.'m3u8/pic/'.sys_auth($path).'.'.$ac; } }else{ $link = $protocol.Web_Play.'/'.$path; } } return $link; }
这样就可顺利兼容HTTPS,视频播放正常。
📌 3、配置参数完整说明
以下是涉及到的核心配置参数的完整注释说明(config.php
或application/config
中的配置):
参数 | 解释 | 推荐设置 |
---|---|---|
Mu_Path | M3U8输出路径 | ./video/m3u8/ |
Video_Path | 视频源路径 | ./video/data/ |
Mu_Ffpath | FFmpeg的安装路径 | /www/wwwroot/v.iob.lol/packs/ |
Mu_Type | 是否启用双码率 | 0 (否) 或 1 (是) |
Mu_Kbps | 默认码率(kbps) | 800 (根据需求调整) |
Mu_Kbps2 | 第二码率(kbps) | 400 (根据需求调整) |
Mu_Size | 视频转码分辨率 | 1280x720 (可自定) |
Mu_Time | 每个TS切片长度(秒) | 推荐10 |
Mp_On | 是否处理音频 | 1 (启用音频处理) |
Web_CrossOn | 防盗链功能 | 根据需求,默认关闭(0 ) |
Web_M3u8On | 是否启用M3U8加密 | 根据需求 |
📌 4、推荐的标准操作流程(未来使用时)
- 部署环境,安装PHP 7.1 + MySQL 5.6 + Apache/Nginx,开放
exec()
; - 上传完整代码,设置文件夹权限(755);
- 配置
ffmpeg
路径,使用绝对路径; - 测试
exec()
执行ffmpeg -version
; - 配置HTTPS,修改函数
m3u8_link()
协议为https://
; - 确认目标路径存在且可写,后台创建队列;
- 转码成功后,在前台播放,观察视频播放状态。
📌 5、排错核心要点(再出现问题时的核心步骤)
步骤 | 检测项 | 操作方法 |
---|---|---|
1 | PHP函数权限 | exec() 必须可用 |
2 | 路径权限 | 文件夹必须755以上权限,允许PHP创建 |
3 | FFmpeg命令测试 | 使用shell直接执行ffmpeg命令测试 |
4 | m3u8链接协议 | HTTPS需强制为https协议 |
5 | 日志与监控 | 查看logs/ 内生成的日志文件 |
Comments NOTHING