php不处理视频水印,仅调度ffmpeg执行;gd/imagick无法解析视频容器、保持音视频同步或复用编码参数,逐帧处理会导致cpu飙升、关键帧丢失、音频失真及无法硬件加速;需严格过滤用户输入、设置超时与错误处理;中文乱码源于ffmpeg未加载utf-8字体,须用noto sans cjk等字体并显式指定编码;并发时须用唯一哈希命名临时文件并确保清理。

PHP 本身不处理视频水印,必须交由 ffmpeg 执行;你写的不是“水印逻辑”,而是“调度逻辑”。所有位置计算、透明度、字体渲染、时间变量都由 ffmpeg 滤镜完成,PHP 只负责拼命令、传参、验返回码、清临时文件。
为什么不能用 GD 或 Imagick 直接加水印
GD 和 Imagick 只能解帧、处理单张图片,无法解析视频容器(如 MP4 的 moov、mdat)、无法保持音视频同步、无法复用原始编码参数。强行逐帧读取→加水印→重编码,会导致:
- CPU 占用飙升,1 分钟视频可能耗时 5 分钟以上
- H.264 关键帧丢失,播放卡顿或黑屏
- 音频流被意外丢弃或重采样失真
- 无法支持硬件加速(如
nvenc、qsv)
exec() 调用 ffmpeg 的安全红线
用户输入若直接拼进命令行,等于开放远程代码执行入口。必须守住三条线:
- 所有路径参数(
$videoPath、$watermarkPath、$fontPath)必须用escapeshellarg()包裹,哪怕你已校验过扩展名 - 禁止用户控制滤镜表达式中的
x=、y=、text=值——只提供预设位置(左上/右下/居中)和固定文案模板 - 必须设置超时:
set_time_limit(300),并捕获ffmpeg返回码:if ($returnCode !== 0) { /* 记录错误日志,删临时文件 */ }
中文水印乱码的根因与解法
乱码不是 PHP 编码问题,而是 ffmpeg 的 drawtext 滤镜默认不加载 UTF-8 字体。关键动作只有两个:
- 确认字体文件真实存在且可读:
file_exists($fontPath) && is_readable($fontPath),路径中不能含空格或中文(Linux 下尤其敏感) - 换用 Noto Sans CJK、Source Han Sans 等明确声明支持 UTF-8 的开源字体,避免用 Windows 自带的
simhei.ttf(无 Unicode 映射) - 命令中显式指定编码:
drawtext=fontfile=...:text='中文':x=...:y=...:fontcolor=white:shadowcolor=black@0.5:shadowx=2:shadowy=2
最容易被忽略的是并发场景下的资源竞争:多个请求同时写同一个输出路径,或共享一个未加锁的临时目录。务必用唯一哈希(如 md5($videoPath . time() . rand()))生成临时文件名,并在 finally 块里 unlink() 清理——否则磁盘会悄无声息地被填满。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











