Shell脚本命令行正常,PHP shell_exec调用生成损坏MP4
问题分析与解决方案
你碰到的这个问题,核心原因其实是命令行执行环境和web服务器执行环境的差异导致的,下面我帮你拆解具体原因,以及对应的解决办法:
一、最可能的元凶:权限与环境变量不匹配
1. Web服务器用户权限不够
当你通过网页调用PHP脚本时,代码是由web服务器的系统用户(比如www-data、apache这类)执行的,而你在命令行跑PHP时用的是自己的登录用户,两者的权限范围完全不一样:
- 先检查
videos目录的权限:得确保web用户能往里面写文件,执行这两条命令就行:chown -R www-data:www-data /usr/local/nginx/html/mydir/videos chmod -R 755 /usr/local/nginx/html/mydir/videos - 另外,
record.sh、NAMESTREAM、RECSTREAM这些文件,web用户也得有读取/写入的权限,不然脚本没法正常读取配置或者生成临时文件。
2. FFmpeg的路径找不到
命令行里你能直接用ffmpeg,是因为你的用户PATH环境变量包含了FFmpeg的安装路径,但web用户的PATH可能没加这个路径,导致脚本执行时找不到FFmpeg,自然生成的MP4就是损坏的空文件。解决办法很简单:
- 先在命令行里查FFmpeg的绝对路径:
比如输出是which ffmpeg/usr/bin/ffmpeg,然后把record.sh里的ffmpeg改成这个绝对路径,比如:/usr/bin/ffmpeg -i "rtmp://localhost/hls/$VAR" -crf 19 videos/"$VAR"_"$DATE".mp4
二、FFmpeg参数优化:确保生成能正常播放的MP4
直播流录制MP4时,默认设置有个坑:MP4的索引信息(moov atom)是存在文件末尾的,如果录制中途脚本意外停止(比如web进程被杀掉),这个索引没写完,播放器就识别不了文件。可以加几个参数解决这个问题:
推荐的直播录制参数
/usr/bin/ffmpeg -i "rtmp://localhost/hls/$VAR" \ -crf 19 \ -movflags +faststart \ -c:v copy -c:a copy \ videos/"$VAR"_"$DATE".mp4
给你解释下这些参数:
-movflags +faststart:录制完成后自动把moov atom移到文件开头,这样播放器不用读到文件末尾就能加载播放,就算录制中途中断,已经写入的完整帧也能被正常识别。-c:v copy -c:a copy:直接复制原流的视频和音频编码,不用重新编码,既省资源又保证兼容性(只要原流是H.264/AAC,MP4就完美支持)。如果原流编码不兼容MP4,再去掉这两个参数让FFmpeg转码。
兼容性拉满的测试参数
要是上面的参数还是不行,就强制转成MP4最通用的编码组合(H.264视频+AAC音频),几乎所有播放器都能认:
/usr/bin/ffmpeg -i "rtmp://localhost/hls/$VAR" \ -crf 19 \ -movflags +faststart \ -c:v libx264 -c:a aac \ videos/"$VAR"_"$DATE".mp4
三、调试小技巧:找出脚本的错误日志
你现在的PHP代码把脚本的输出和错误都扔到/dev/null了,根本看不到哪里出问题。可以暂时改成把日志存下来,方便排查:
$old_path = getcwd(); chdir('/usr/local/nginx/html/mydir'); // 把脚本的输出和错误都写到record.log里 $output = shell_exec('nohup ./record.sh > record.log 2>&1 &'); chdir($old_path);
之后查看record.log的内容,就能知道脚本执行时是不是有报错(比如找不到FFmpeg、权限不够、RTMP流连不上之类的)。
内容的提问来源于stack exchange,提问作者Eric Feillant
相关产品推荐
相关产品推荐

