php无法直接给视频加水印,必须用ffmpeg抽帧→php逐帧处理图像→ffmpeg合成;需动态适配分辨率、保持整数坐标防抖动、分批处理防内存溢出。

imagecopyresampled 不适合视频,PHP 本身不支持视频帧级处理。电商视频加水印必须转为逐帧图像处理,或调用外部工具。
PHP 8.5 不能直接处理视频水印
PHP 没有内置视频解码/编码能力,GD 和 Imagick 都只操作静态图像。所谓“PHP 给视频加水印”,本质是:用命令行工具(如 ffmpeg)抽帧 → PHP 批量给 PNG/JPG 帧图加水印 → 再用 ffmpeg 合成新视频。PHP 在其中只承担“中间图像处理”环节。
必须用 ffmpeg 抽帧 + 合成
跳过这步,所有 PHP 代码都跑不起来。确认服务器已安装并可执行:
-
ffmpeg -version能正常输出版本(建议 ≥ 6.0) - PHP 中能调用:
exec('ffmpeg -i input.mp4 -vframes 1 -f image2 tmp.jpg 2>&1', $out, $ret)返回 0 - 注意 web 用户(如 www-data)需有读写临时目录权限,且
disable_functions未禁用exec/shell_exec
PHP 批量处理帧图时,水印位置和缩放要动态适配
电商视频分辨率多变(竖屏 1080×1920、横屏 3840×2160),硬编码坐标会错位。关键点:
- 每帧加载后,用
imagesx($frame)和imagesy($frame)获取真实尺寸 - 水印 PNG 必须带 Alpha 通道;加载时用
imagecreatefrompng(),并立即调用:imagealphablending($watermark, false)和imagesavealpha($watermark, true) - 用
imagecopyresampled()而非imagecopy()—— 可等比缩放水印(例如固定为视频宽度的 15%):$logo_w = (int)($src_w * 0.15); $logo_h = (int)($logo_w * $orig_logo_h / $orig_logo_w); - 右下角安全边距设为
max(20, $src_w * 0.02),避免被手机 UI 遮挡
内存和超时是批量失败主因
一段 30 秒 30fps 视频产生 900 帧,全加载进内存必崩。必须:
- 单次循环只处理 10–20 帧,然后
imagedestroy()所有资源 - 调用
gc_collect_cycles()强制回收 - 设置
set_time_limit(0)和ini_set('memory_limit', '512M')(但别设太高,防失控) - 绝对不要用
glob('*.jpg')一次性读取全部帧 —— 改用scandir()+ 分页遍历,或直接用ffmpeg的-start_number/-vframes控制抽帧范围
真正难的不是加水印,而是帧对齐:水印在视频里不能“抖动”。这意味着所有帧必须用同一套缩放比例和坐标偏移计算逻辑,且不能依赖 imagesx() 返回值做浮点运算(GD 内部截断会导致像素级偏移)。最稳的方式是预计算好目标尺寸和锚点,全程用整数坐标。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











