PHP调用FFmpeg加文字水印失败主因是权限、路径、转义问题;需硬编码ffmpeg路径、用escapeshellarg()处理参数、检查Web用户读写权限、加-v error捕获错误;动态文字用drawtext的t变量配合floor(t)取整,中文须指定可读字体文件;动态内容应写入临时textfile而非拼接命令;转MP4须用libx264+aac编码并加-movflags +faststart。

PHP调用FFmpeg加文字水印为什么总失败?
直接用exec()跑ffmpeg命令却没生成文件,多半是权限、路径或转义问题。PHP默认不继承系统环境变量,ffmpeg可能根本找不到;另外shell_exec()在禁用函数列表里(查disable_functions),或safe_mode残留(虽已废弃,但某些旧环境仍生效)。
- 先确认
which ffmpeg输出路径,硬编码进PHP,别依赖PATH - 用
escapeshellarg()包裹所有用户输入参数,尤其文字内容含空格或单引号时 - 检查Web用户(如
www-data)对输入视频、输出目录、临时字体文件的读写权限 - 加
-v error参数捕获错误输出,用2>&1合并到返回值里再var_dump()看具体报错
动态文字怎么随时间变化?用drawtext滤镜的t变量
drawtext支持t(当前秒数)和n(帧序号)变量,实现“第X秒显示XX”的效果。但注意:FFmpeg内部时间戳精度为微秒级,t是浮点数,直接取整会抖动;需用floor(t)或trunc(t)截断。
- 示例命令片段:
drawtext=fontfile=/path/to/font.ttf:text='Time\: %{eif floor(t)}s':x=(w-tw)/2:y=h-th-20:fontsize=24:fontcolor=white - 若要每5秒换一句文案,用
text='expr\:%{eif floor(t/5)}配合数组索引逻辑(实际需在PHP中拼接字符串,FFmpeg不支持数组) - 中文必须指定
fontfile且确保字体文件可读,否则渲染为空白方块 - 避免在
text里写\:以外的冒号——它会被FFmpeg解析为选项分隔符,要用\:转义
PHP生成带时间戳的水印文字,别硬编码进命令
把动态文本(比如当前时间、用户ID、版权信息)塞进FFmpeg命令,不能靠字符串拼接完再exec()——特殊字符(如括号、&、$)会破坏shell语法。正确做法是先生成临时文本文件,再用textfile参数引用。
- PHP侧生成临时文件:
$tmpFile = tempnam(sys_get_temp_dir(), 'watermark_') . '.txt'; file_put_contents($tmpFile, date('Y-m-d H:i:s') . ' | UID:' . $uid); - FFmpeg命令中改用:
drawtext=fontfile=...:textfile='{$tmpFile}':... - 执行完记得
unlink($tmpFile),否则/tmp塞满 - 如果文字含换行,
textfile支持多行,但drawtext默认只渲染首行;需加y=h-th-20+(line_h+10)*n手动控制行高(n是行号)
转MP4封装和兼容性绕不开H.264+AAC
单纯转格式不等于能播——浏览器只认H.264视频流+ACC音频流的MP4。用-c:v libx264 -c:a aac强制重编码,别用-c copy省事,否则源流不兼容时网页播放器直接报错。
立即学习“PHP免费学习笔记(深入)”;
- 关键参数:
-movflags +faststart让MP4头部包含索引,支持边下边播 - 分辨率非2的幂次(如1920×1080)没问题,但
libx264默认预设medium速度慢;批量任务建议用-preset fast平衡速度与体积 - 音频采样率必须是44100或48000,否则Safari拒绝播放;加
-ar 48000保险 - 别忽略
-y参数,否则FFmpeg交互式等待覆盖确认,PHP进程就卡死
动态水印真正麻烦的不是命令拼写,而是字体加载失败时静默跳过、时间计算跨帧不准、以及并发转码时临时文件名冲突——这些得靠日志+唯一ID+重试机制兜底,而不是指望一条命令跑通。



















