PHP无法纯靠自身实现视频加水印,必须依赖ffmpeg等外部工具,因GD扩展仅支持图像处理,file_get_contents仅读取二进制流,而视频处理需解复用、解码、帧处理、编码、复用全流程。

PHP 本身不直接处理视频帧,所谓“网页版视频加水印”必须依赖外部命令行工具(主要是 ffmpeg),PHP 只负责接收文件、拼接命令、调用执行并返回结果。想纯靠 PHP 函数实现水印叠加,行不通。
为什么不能只用 PHP 的 file_get_contents 或 GD 扩展
GD 扩展只能处理图像(PNG/JPG),对视频文件只是读取二进制流,无法解码帧、叠加文字/图片、再编码回视频;file_get_contents 更只是搬运字节。视频是时序压缩数据,加水印本质是:解复用 → 解码 → 帧处理 → 编码 → 复用。这个链条里每一步都超出 PHP 标准库能力范围。
- 试图用
imagecreatefromstring()加载 MP4 文件会直接报Unsupported image type - 用
exec('ffmpeg -version')返回空或报错?说明服务器没装ffmpeg或 PHP 进程无执行权限 - 上传大视频(如 500MB)时页面超时?不是代码问题,是 PHP 的
max_execution_time和 Nginx/Apache 的请求体限制在拦你
必须配置的三个服务层依赖
缺一不可,少一个就卡在第一步:
-
ffmpeg必须已编译安装(推荐 static build),且 PHP 能调用:exec('which ffmpeg')应返回路径(如/usr/local/bin/ffmpeg) - Web 服务器要放宽上传限制:Nginx 需设
client_max_body_size 2g;PHP-FPM 需调upload_max_filesize = 2G、post_max_size = 2G、max_execution_time = 600 - PHP 进程需有写入临时目录和输出目录的权限(比如
/var/www/html/uploads/和/var/www/html/output/),否则exec()调用会因“Permission denied”静默失败
核心命令模板与参数避坑点
最简可用的加文字水印命令长这样:
立即学习“PHP免费学习笔记(深入)”;
ffmpeg -i <input.mp4> -vf "drawtext=fontfile=/path/to/font.ttf:fontsize=24:fontcolor=white:x=10:y=10:text='© MySite'" -c:a copy <output.mp4>
但实际部署时这些参数极易出错:
-
fontfile路径必须是绝对路径,且 PHP 进程用户(如www-data)有读权限;相对路径或中文路径基本失败 - 不加
-c:a copy会导致音频重编码,耗时翻倍;但若源视频音频格式特殊(如 AAC LC 不兼容),可能黑屏,此时得改用-c:a aac - 用
drawtext时若提示Unable to parse option value "© MySite",是单引号嵌套冲突,应改为双引号包裹整个 filter,并对内部双引号转义:drawtext=...:text=\"© MySite\" - 加图片水印更复杂:要用
overlay滤镜,且需确保水印图尺寸不大于视频宽高,否则报Invalid too big
PHP 调用时必须做的三件事
别只写 exec($cmd) 就完事,生产环境至少要:
- 校验上传文件 MIME 类型(
$_FILES['video']['type']可伪造,必须用finfo_file()二次确认是video/mp4或video/quicktime) - 生成唯一输出文件名(用
uniqid() . '_' . time()),避免并发请求覆盖同一output.mp4,导致下载错乱 - 捕获
ffmpeg错误输出:exec($cmd . ' 2>&1', $output, $return_code),若$return_code !== 0,就把$output日志存文件,否则用户只看到空白页
水印位置、字体大小、是否保留原音轨……这些都不是 PHP 逻辑决定的,而是 ffmpeg 命令行参数的排列组合。真正难的从来不是写几行 PHP,而是让那条命令在各种服务器环境下稳定跑通。



















