PHP不能直接压制SRT硬字幕,必须通过proc_open安全调用ffmpeg执行重编码命令,需严格校验输入、处理BOM与时间码异常,并禁用-c:v copy以确保字幕渲染。

PHP 本身不直接支持视频编码或字幕压制,ffmpeg 才是实际干活的工具;PHP 只能调用它、组织参数、校验输入、处理失败路径。别指望 file_get_contents("sub.srt") 后直接“合成”出带字幕的 MP4——那不是 PHP 的活儿。
PHP 怎么安全调用 ffmpeg 压制 SRT 硬字幕
核心是拼对命令、避开 shell 注入、捕获错误输出。直接拼接用户上传的文件名进命令行等于开门揖盗。
- 用
escapeshellarg()包裹所有外部输入:视频路径、字幕路径、输出路径都必须过一遍 - 不要用
exec()或system(),改用proc_open(),能控制 stdin/stdout/stderr 并设超时 - 确保
ffmpeg在系统 PATH 中,或写死绝对路径(如/usr/bin/ffmpeg),避免被恶意软链接劫持 - 命令模板固定为:
ffmpeg -i {video} -vf "subtitles={srt}" -c:a copy -c:v libx264 -crf 23 {output};不加-c:v copy,硬字幕必须重编码视频流
PHP 解析 SRT 文件时最容易崩在哪几处
不是格式错,而是没预判真实世界的脏数据:Subtitle Edit 导出的 SRT 经常带 BOM、时间码缺前导零、序号跳空、甚至混入注释行。
- 读文件前先用
mb_detect_encoding($content, ['UTF-8', 'GBK'], true)判定编码,再mb_convert_encoding()统一转 UTF-8 - 跳过以
#开头或全空白的行,别假设每块都严格三行(序号+时间+内容) - 时间码解析不用正则硬匹配,改用
explode('-->', $line)拆开,再对左右两边分别preg_match('/(\d{2}):(\d{2}):(\d{2})[,.](\d{2,3})/', $part, $m)提取;毫秒位不足 3 位就补零 - 存进数组时只信时间戳,序号字段
$entry['index']直接丢弃,后续按start_ms排序
为什么不能用 copy 模式压制字幕
-c:v copy 表示不重编码视频帧,但 subtitles= 滤镜必须把字幕画到每一帧像素上,这和“复制”冲突。ffmpeg 会直接报错:Filtergraph 'subtitles' was defined for video output stream 0:0 but cannot be applied。
- 必须指定视频编码器,如
-c:v libx264或-c:v libx265 - 用
-crf 23控制质量(数值越小越清晰,18~28 是常用区间),别用-b:v固定码率,容易导致字幕边缘模糊 - 如果原始视频是 H.265,强行用
libx264转码会损失效率;应检测源流编码:ffprobe -v quiet -show_entries stream=codec_name -of csv=p=0 input.mp4 | grep -q h265 && echo "HEVC"
真正麻烦的不是命令怎么写,而是当用户上传一个 2GB 的 MKV + 乱码 SRT,PHP 进程卡住、临时目录爆满、ffmpeg 崩溃退出却没返回错误码——这些边界情况比语法本身更消耗调试时间。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











