PHP无法纯实现视频水印,必须依赖FFmpeg;exec()最可控,php-ffmpeg仅是封装;水印偏移主因是SAR不一致、旋转元数据未处理及W/H大小写混淆,需预检归一化。

PHP 本身不能做视频水印,必须走 FFmpeg;所谓“扩展”只是封装了 exec 调用,底层仍是外部命令。 不存在纯 PHP 实现的可能,所有方案最终都落在 ffmpeg 进程上。选原生 exec() 还是 php-ffmpeg 扩展,本质是封装程度和可控性的取舍,不是技术路线差异。
直接 exec() 调用 ffmpeg 命令最可控
你完全掌握每个参数、每个空格、每个引号的位置,出问题能立刻定位到哪一行命令错在哪。适合对水印位置、透明度、SAR 处理有明确要求的场景。
- 错误信息直接来自
ffmpeg,比如No such file or directory或Invalid argument in overlay expression,不用猜是封装层吞掉了什么 - 可以自由组合
movie+overlay+setsar+scale等 filter 链,比如先归一化 SAR 再贴水印,避免因旋转元数据导致偏移 - 中文路径/字体需手动处理编码:Windows 下 cmd 默认 GBK,
iconv('UTF-8', 'GBK', $fontPath)是绕不开的一步;Linux 一般 UTF-8,但也要确认终端 locale - 必须自己加超时控制:
proc_open()配合stream_set_timeout(),否则一个卡死的ffmpeg进程会拖垮整个 PHP 请求
php-ffmpeg 扩展只是语法糖,不解决底层问题
php-ffmpeg 的价值在于统一初始化、自动拼接 filtergraph 和封装常见操作(如 addWatermark()),但它不会帮你处理 SAR 不一致、旋转元数据、alpha 通道丢失这些真问题。
- 它默认用
overlay=W-w-10:H-h-10,但如果你的视频带rotate=90元数据,这个坐标就完全错位——扩展不会自动读ffprobe检查 rotation 并旋转水印图 - 它调用的仍是
exec(),只是把命令藏在FFMpeg\Media\Video类里;你依然要配置ffmpeg.binaries路径,依然会遇到command not found - 透明 PNG 水印若出现黑边,不是扩展的问题,而是没加
format=yuv420或alpha=straight参数——这些得自己 hack 进 filter 字符串 - 批量处理时,它默认串行执行;想并发跑多个
ffmpeg,还得自己管理进程池,扩展不提供原生支持
水印偏移的根因永远不在 PHP 层
无论你用 exec() 还是 php-ffmpeg,只要看到水印跑到画外、拉伸变形、或贴错角,99% 是这三个点没校准:
立即学习“PHP免费学习笔记(深入)”;
-
W/H和w/h大小写混用:比如写成x=w-w-10,结果恒为 -10,水印永远在左边界外 - 主视频和水印图的
SAR不一致:手机竖屏视频常带sar=640:360,而 PNG 水印默认sar=1:1,播放器缩放后视觉位置就飘了 - 忽略旋转元数据:用
ffprobe -v quiet -show_entries stream_tags=rotate input.mp4查出rotate=90,就得在 filter 里先transpose=1主视频,或把水印图预旋转 90°
真正难的从来不是“怎么调用”,而是“怎么让坐标在各种视频源下都算对”。哪怕命令写对了,只要没处理 SAR 和 rotation,换一部手机录的视频,水印就大概率偏移。这点,任何 PHP 封装都绕不过去,必须在构造命令前完成预检和归一化。



















