FFmpeg批量视频加水印:图片与文字水印自动处理实战指南
之前帮朋友处理过一批短视频素材数量大概几百条每条都要在右上角加同一个 logo 水印。一开始用剪辑软件手动操作拖素材、对位置、调大小、导出一条视频折腾两三分钟几百条下来一晚上都未必能做完而且重复劳动特别容易出错。后来换成 FFmpeg 批处理方案一条命令处理一个视频写个循环脚本后基本不用盯着屏幕。如果你的需求也类似比如有几百上千个视频要加同一张图片水印或文字水印还希望水印位置、大小都能自定义那接下来的这套方案可以直接照着做。本文会先讲清楚视频水印处理的背景和核心概念然后从环境准备、FFmpeg 参数拆解到批量处理脚本的完整编写再补充常见报错排查和工程化建议。整个方案免费、开源、跨平台适合新媒体运营、视频剪辑辅助、内容批处理开发等场景。1. 视频加水印的价值与批量处理痛点1.1 视频水印的作用视频水印是指在视频画面上叠加一段可见的标识常见形式有图片水印和文字水印。图片水印通常是品牌 Logo、频道头像、二维码等格式以 PNG 为主因为 PNG 支持透明通道叠加后不会遮挡太多画面内容。文字水印通常是账号昵称、版权声明、日期编号等适合快速防搬运和标记来源。水印的核心作用有两个一个是品牌曝光视频被转发、下载后水印能持续传递来源信息另一个是防盗与溯源别人搬运你的视频时水印很难完全去除即使裁掉部分画面也会影响观看体验。1.2 批量加水印的痛点当视频数量只有几条时用剪辑软件手动加水印并不麻烦。但数量一旦达到几十、几百甚至上千条手动处理就会出现明显问题重复劳动量大。每个视频都要导入、拖水印、调整、导出操作完全重复浪费时间。位置和大小难以统一。手动拖动容易出现偏移不同视频中水印位置不一致。拼接素材容易出错。批量剪辑时忘记换素材、忘记改文字是高频事故。占用设备和精力。长时间手动操作人很容易疲惫效率也会越来越低。所以批量加水印这个需求本质上是一个“重复性批处理任务”最好的解法是交给脚本自动完成而 FFmpeg 就是目前最常用的开源方案。1.3 FFmpeg 是什么FFmpeg 是一个跨平台的开源音视频处理工具提供录制、转换、编解码、滤镜、流媒体处理等能力。它的命令行工具支持非常丰富的滤镜参数其中 overlay 滤镜用于叠加图片水印drawtext 滤镜用于叠加文字水印。FFmpeg 采用命令行方式运行可以通过脚本循环处理大量视频所以非常适合批量加水印的场景。它支持 Windows、macOS、Linux几乎所有主流服务器环境都可以使用。2. 环境准备与版本说明2.1 操作系统与运行环境本文示例以常见的三大桌面操作系统为例Windows 10 / 11macOS 12 及以上LinuxUbuntu / CentOS 等FFmpeg 被称为命令行工具不需要复杂的运行环境只要安装好可执行文件并能在终端中调用即可。如果你使用的是 Windows建议配合 PowerShell 或 CMD 使用macOS 和 Linux 使用系统自带的终端即可。2.2 安装 FFmpeg不同系统的安装方式不同下面分开说明。Windows 安装推荐使用包管理器 winget也可以手动下载官方编译版本。winget install ffmpeg安装完成后重新打开终端输入下面的命令验证版本ffmpeg -version如果系统提示找不到命令可能是安装目录没有加入 PATH需要手动配置环境变量将 FFmpeg 可执行文件所在目录添加到 PATH 中。macOS 安装使用 Homebrew 安装brew install ffmpegLinux 安装Ubuntu/Debian 使用 aptsudo apt update sudo apt install ffmpegCentOS/RHEL 系列可以尝试sudo yum install ffmpeg如果系统自带源里没有 ffmpeg需要先添加 EPEL 和 RPM Fusion 源。具体版本需要根据你的系统实际情况调整本文不做强制限定。2.3 验证是否安装成功安装完成后在终端中输入ffmpeg -version能看到类似下面的输出说明安装成功ffmpeg version 5.x.x Copyright (c) 2000-2024 the FFmpeg developers built with gcc 12 (Ubuntu 12.x.x)版本差异会影响某些参数细节但本文用到的 overlay、drawtext、scale 都是非常稳定的基础滤镜主流版本都支持。2.4 准备测试文件为了验证效果建议先准备一个测试视频和一张 PNG 水印图片。下面是示例目录结构video-watermark-demo/ ├── input/ │ ├── video1.mp4 │ ├── video2.mp4 │ └── video3.mp4 ├── watermark/ │ └── logo.png └── output/输入视频放在 input 目录水印图片放在 watermark 目录处理结果输出到 output 目录。先保证单个视频加水印成功再扩展到批量处理。3. FFmpeg 水印参数拆解FFmpeg 给视频加水印的核心是滤镜系统。下面先讲解最常用的图片水印 overlay 滤镜再补充文字水印 drawtext 滤镜。3.1 图片水印基础用法先来看一条给单个视频叠加图片水印的完整命令ffmpeg -i input/video1.mp4 -i watermark/logo.png \ -filter_complex [1:v]scale120:120[logo];[0:v][logo]overlay20:20 \ -codec:a copy output/video1_watermarked.mp4这条命令的含义是-i input/video1.mp4指定输入视频。-i watermark/logo.png指定水印图片这里作为第二个输入文件。-filter_complex复杂滤镜图可以同时使用多个滤镜。[1:v]scale120:120[logo]将第二个输入水印图片缩放到 120x120 像素输出命名为logo。[0:v][logo]overlay20:20将第一个输入的原始视频与logo叠加水印左上角距离视频左上角的 x、y 坐标都是 20 像素。-codec:a copy音频流直接复制不重新编码速度更快。output/video1_watermarked.mp4输出文件路径。这条命令能跑通之后所有更复杂的操作都是在 overlay 的位置参数和 scale 尺寸上做文章。3.2 控制水印位置overlay 滤镜的坐标参数支持数字、变量和表达式。下面列出常用写法位置overlay 参数说明左上角overlay20:20距左边 20 像素距上边 20 像素右上角overlaymain_w-wm_w-20:20距右边 20 像素距上边 20 像素左下角overlay20:main_h-wm_h-20距左边 20 像素距下边 20 像素右下角overlaymain_w-wm_w-20:main_h-wm_h-20距右边 20 像素距下边 20 像素水平居中overlay(main_w-wm_w)/2:(main_h-wm_h)/2视频中央这些变量含义如下main_w主视频宽度。main_h主视频高度。wm_w水印宽度。wm_h水印高度。用变量而不是写死数字的好处是当视频分辨率不同时水印的相对位置仍然正确。比如视频是 1920x1080水印是 120x120右下角写法就会自动算成1920-120-20 1780和1080-120-20 940。3.3 调整水印大小水印大小通常在 scale 滤镜中控制。比如把水印缩放为视频宽度的 10%可以这样写ffmpeg -i input/video1.mp4 -i watermark/logo.png \ -filter_complex [1:v]scaleiw*0.1:-1[logo];[0:v][logo]overlaymain_w-wm_w-20:20 \ -codec:a copy output/video1_watermarked.mp4这里iw表示水印图片原始宽度iw*0.1表示取原始宽度的 10%-1表示高度按比例自动计算。注意在复杂表达式中*和/运算符会被 Shell 解释所以需要用单引号包裹。如果你的水印原图比较大比如 1000x1000 的 PNG直接叠加会覆盖大半个画面使用比例缩放更安全。3.4 设置水印透明度默认情况下PNG 图片的透明通道会被保留但如果你希望整张水印半透明呈现可以用 colorchannelmixer 滤镜调整透明度。ffmpeg -i input/video1.mp4 -i watermark/logo.png \ -filter_complex [1:v]formatrgba,colorchannelmixeraa0.5[logo];[0:v][logo]overlay20:20 \ -codec:a copy output/video1_watermarked.mp4aa0.5表示把 alpha 通道值改为原来的 50%。alpha 是透明度通道值越低越透明。使用半透明水印能减少对主体内容的遮挡同时保留防搬运效果。3.5 文字水印 drawtext 滤镜如果没有图片 Logo只想加一段文字可以用 drawtext 滤镜。ffmpeg -i input/video1.mp4 \ -vf drawtexttextcopyrightdemo:fontcolorwhite:fontsize36:x20:y20 \ -codec:a copy output/video1_textwatermark.mp4参数说明text水印文字内容。fontcolor字体颜色。fontsize字号。x和y文字左上角坐标。中文字体在部分系统上需要指定字体路径否则会显示乱码。可以在 macOS/Linux 上指定一个系统中文字体例如ffmpeg -i input/video1.mp4 \ -vf drawtexttext版权声明:fontfile/System/Library/Fonts/PingFang.ttc:fontcolorwhite:fontsize36:x20:y20 \ -codec:a copy output/video1_textwatermark.mp4Windows 环境下字体路径一般类似C\:/Windows/Fonts/msyh.ttc。注意 Windows 路径中的冒号需要转义写作C\:/。3.6 水印出现时间控制有些视频只需要在开头或结尾显示水印可以用enable参数控制显示时间段。ffmpeg -i input/video1.mp4 -i watermark/logo.png \ -filter_complex [1:v]scale120:120[logo];[0:v][logo]overlay20:20:enablebetween(t,0,10) \ -codec:a copy output/video1_watermarked.mp4这段表示水印只在 0 到 10 秒之间显示10 秒之后水印消失。enable也可以搭配lt(t,10)这样的表达式表示前面 10 秒内显示。4. 批量加水印完整实战理解了单条命令后批量加水印的核心思路就明确了用循环脚本把单条命令反复执行对 input 目录下的每个视频都执行一次 FFmpeg 命令。4.1 编写批量处理脚本前的准备先建立项目目录mkdir -p video-watermark-demo/input video-watermark-demo/output video-watermark-demo/watermark把待处理视频放入 input 目录水印图片放入 watermark 目录输出目录保持为空。为了保证脚本能顺利运行需要先确认以下两点FFmpeg 命令可以直接在终端中调用。水印图片路径中不要包含空格如果有空格建议改用相对路径时先进入项目目录。4.2 Windows 批处理脚本Windows 下可以直接使用 CMD 的 for 循环来批量处理。新建一个batch_add_watermark.bat文件内容如下echo off chcp 65001 nul setlocal enabledelayedexpansion set INPUT_DIRinput set OUTPUT_DIRoutput set WATERMARKwatermark\logo.png set WATER_SIZE120:120 set POS_Xmain_w-wm_w-20 set POS_Y20 if not exist %OUTPUT_DIR% mkdir %OUTPUT_DIR% for %%f in (%INPUT_DIR%\*.mp4) do ( echo Processing %%~nxf ... ffmpeg -i %%f -i %WATERMARK% ^ -filter_complex [1:v]scale%WATER_SIZE%[logo];[0:v][logo]overlay%POS_X%:%POS_Y% ^ -codec:a copy %OUTPUT_DIR%\%%~nf_watermarked.mp4 ) echo All done! pause脚本说明chcp 65001把终端编码切换为 UTF-8避免输出中文乱码。setlocal enabledelayedexpansion启用变量延迟扩展避免循环中的变量读取问题。for %%f in (%INPUT_DIR%\*.mp4)遍历 input 目录下所有 mp4 文件。%%~nxf取文件名和扩展名。%%~nf只取文件名不包含扩展名用于生成新的输出文件名。^CMD 中的换行连接符表示下一行继续执行。如果想同时处理 mp4、mov、avi 等格式可以在 for 循环中加一层for %%f in (%INPUT_DIR%\*.mp4 %INPUT_DIR%\*.mov %INPUT_DIR%\*.avi)。4.3 macOS/Linux Bash 脚本macOS 和 Linux 下推荐使用 bash 脚本。新建batch_add_watermark.sh内容如下#!/bin/bash INPUT_DIRinput OUTPUT_DIRoutput WATERMARKwatermark/logo.png WATER_SIZE120:120 POS_Xmain_w-wm_w-20 POS_Y20 mkdir -p $OUTPUT_DIR for file in $INPUT_DIR/*.mp4 $INPUT_DIR/*.mov $INPUT_DIR/*.avi; do if [ -f $file ]; then filename$(basename $file) name${filename%.*} echo Processing $filename ... ffmpeg -i $file -i $WATERMARK \ -filter_complex [1:v]scale${WATER_SIZE}[logo];[0:v][logo]overlay${POS_X}:${POS_Y} \ -codec:a copy $OUTPUT_DIR/${name}_watermarked.mp4 fi done echo All done!保存后给脚本添加执行权限chmod x batch_add_watermark.sh运行脚本./batch_add_watermark.sh这个脚本比 Windows 版本更通用同时支持 mp4、mov、avi 三种格式。没有匹配到文件时if [ -f $file ]判断会跳过 glob 字面量避免执行无效命令。4.4 自定义水印位置和大小上面脚本中水印位置和大小是通过变量控制的。你可以按需修改右上角POS_Xmain_w-wm_w-20POS_Y20。右下角POS_Xmain_w-wm_w-20POS_Ymain_h-wm_h-20。左上角POS_X20POS_Y20。左下角POS_X20POS_Ymain_h-wm_h-20。居中POS_X(main_w-wm_w)/2POS_Y(main_h-wm_h)/2。水印大小修改WATER_SIZE即可。比如希望水印宽度固定 150高度按比例可以写WATER_SIZE150:-1如果希望水印为视频宽度的 12%则无法直接写在变量里因为 scale 表达式需要考虑视频宽度需要写成ffmpeg -i $file -i $WATERMARK \ -filter_complex [1:v]scaleiw*0.12:-1[logo];[0:v][logo]overlay${POS_X}:${POS_Y} \ -codec:a copy $OUTPUT_DIR/${name}_watermarked.mp44.5 递归处理子目录视频当视频分散在不同子目录时可以配合find命令收集文件列表再逐个处理。以 macOS/Linux 为例#!/bin/bash INPUT_ROOTinput OUTPUT_DIRoutput WATERMARKwatermark/logo.png mkdir -p $OUTPUT_DIR find $INPUT_ROOT -type f \( -name *.mp4 -o -name *.mov -o -name *.avi \) | while read -r file; do filename$(basename $file) name${filename%.*} echo Processing $file ... ffmpeg -i $file -i $WATERMARK \ -filter_complex [1:v]scale120:120[logo];[0:v][logo]overlaymain_w-wm_w-20:20 \ -codec:a copy $OUTPUT_DIR/${name}_watermarked.mp4 done echo All done!需要注意如果子目录中多个文件同名会覆盖输出。生产场景建议在输出文件名中加上子目录前缀或顺序编号。4.6 批量处理后的质量与压缩问题视频加水印默认会重新编码视频流如果对质量有要求可以在命令中加入-crf参数控制质量。CRF 值越小质量越高文件越大。常用范围是 18 到 28。ffmpeg -i $file -i $WATERMARK \ -filter_complex [1:v]scale120:120[logo];[0:v][logo]overlaymain_w-wm_w-20:20 \ -c:v libx264 -crf 20 -preset medium \ -codec:a copy $OUTPUT_DIR/${name}_watermarked.mp4-c:v libx264表示使用 H.264 编码兼容性最好。-preset medium是编码速度与压缩率的平衡选择如果机器性能较强可以用slow获得更好的压缩效果。实际项目中一般建议先处理一条测试视频确认画质和文件大小后再全量跑。5. 进阶扩展多水印叠加与自适应尺寸5.1 同时叠加图片水印和文字水印有些场景需要同时显示 Logo 和文字信息比如“品牌 Logo 账号昵称”。这时可以组合 overlay 和 drawtext 两个滤镜。ffmpeg -i input/video1.mp4 -i watermark/logo.png \ -filter_complex \ [1:v]scale100:100[logo];\ [0:v][logo]overlay20:20[withlogo];\ [withlogo]drawtexttextdemo:fontcolorwhite:fontsize28:x130:y20 \ -codec:a copy output/video1_multi_watermark.mp4这段命令先叠加图片水印到原视频上然后把带水印的视频作为新输入再叠加文字水印。文字放在图片水印右侧坐标 x130 是因为图片宽度为 100再加上 20 像素左边距和 10 像素间距。5.2 位置大小自适应视频分辨率不同视频分辨率不一样时假设水印统一缩放为视频宽度的 8%并且固定在右上角可以这样写ffmpeg -i input/video1.mp4 -i watermark/logo.png \ -filter_complex [1:v]scaleiw*0.08:-1[logo];[0:v][logo]overlaymain_w-wm_w-20:20 \ -codec:a copy output/video1_adaptive.mp4这里scaleiw*0.08是相对于水印图片自身尺寸的实际水印最终显示大小还要受视频分辨率影响。如果你的水印原图分辨率较大叠加到小分辨率视频上时还是可能偏大需要先观察输出效果再微调比例。如果希望水印尺寸相对于视频分辨率计算需要用到原视频的宽度信息。例如先获取视频宽度再传入脚本width$(ffprobe -v error -select_streams v:0 -show_entries streamwidth -of csvp0 input/video1.mp4) watermark_size$((width * 8 / 100))然后把这个计算出的宽度传给 scale。这种方式适合处理分辨率差异特别大的视频集合。5.3 根据文件列表批量处理如果不想处理整个目录而是只处理一个清单中的文件可以将文件路径写入list.txt然后逐行读取#!/bin/bash OUTPUT_DIRoutput WATERMARKwatermark/logo.png mkdir -p $OUTPUT_DIR while IFS read -r file; do if [ -f $file ]; then filename$(basename $file) name${filename%.*} echo Processing $file ... ffmpeg -i $file -i $WATERMARK \ -filter_complex [1:v]scale120:120[logo];[0:v][logo]overlaymain_w-wm_w-20:20 \ -codec:a copy $OUTPUT_DIR/${name}_watermarked.mp4 else echo File not found: $file fi done list.txt这种方式在生产环境更可控因为你可以精确指定需要处理的文件避免误处理无关目录中的素材。6. 常见问题与排查思路批量加水印过程中比较容易遇到下面几类问题按错误现象、常见原因、解决思路整理如下问题现象常见原因解决思路提示No such file or directory输入视频或水印图片路径错误检查文件是否存在确认路径中是否有空格或中文水印没有出现在视频中overlay 的输入编号或 filter_complex 写错检查[0:v]、[1:v]是否正确对应视频和水印水印位置跟预期不一致坐标写死没考虑视频分辨率使用main_w-wm_w等相对变量水印太大或太小scale 尺寸设置不合适根据视频实际分辨率调整建议先测试一条中文水印显示乱码系统没有指定中文字体使用fontfile指定中文字体路径处理速度很慢没有控制编码参数使用-preset faster、-crf平衡质量和速度输出文件没有音频-codec:a copy失败检查原视频是否有音频或去掉-codec:a copy重新编码音频处理到一半报错退出某个源视频损坏或编码格式异常增加日志输出循环中单个失败不要中断整体下面展开几个高频问题。6.1 水印没有显示最常见的原因是filter_complex中的输入编号和文件顺序不匹配。比如输入顺序是先 water 后 video但写成了[0:v][1:v]overlay水印和视频就反了。建议在每个输入文件后面标注作用比如-i input.mp4是视频-i logo.png是水印然后统一写成[0:v][1:v]overlay。另外如果 PNG 文件本身就是全透明或者路径不对也会出现水印不显示的情况。先用图片查看器确认 PNG 不是空内容再确认路径执行没有问题。6.2 位置参数加减号报错在 Linux/macOS 的 bash 中main_w-wm_w-20里的减号会被命令行解析为参数如果直接写可能报错。建议把位置表达式用引号包裹或者把整条 filter 用单引号包起来-overlaymain_w-wm_w-20:20Windows CMD 下双引号的转义方式不同建议直接参考上面 4.2 节的写法。6.3 处理速度太慢批量处理视频时性能瓶颈主要体现在视频编码上。如果你只是给视频加水印不要求重新编码视频可以尝试用-c:v copy但前提是视频流格式和封装格式允许。实际情况是大部分滤镜操作都需要重新编码所以想完全复制视频流并不现实。更实用的优化手段是降低编码预设难度比如-preset faster。控制输出分辨率比如加上-vf scale1280:720。多任务并行但要注意 CPU 占用和磁盘 IO。6.4 输出文件比原文件大很多添加水印后因为视频被重新编码文件大小可能发生变化。如果发现输出文件比原文件大不少可以调整 CRF 或加上最大码率限制-c:v libx264 -crf 23 -maxrate 2M -bufsize 4MCRF 值越大压缩越强文件越小但画质会下降。建议先处理一个小视频对比画质和体积之后再做选择。7. 最佳实践与工程建议7.1 先测试再全量不管脚本写得多完整第一次批量跑之前都强烈建议先用两个视频做测试一个是比较小的视频验证速度一个是分辨率不同的视频验证水印位置和大小是否合理。测试通过后再把脚本放到全量视频目录中运行。7.2 输出目录与命名规范批量脚本中如果多个源视频文件名相同输出文件会互相覆盖。建议输出命名带上源目录结构或时间戳name${filename%.*} timestamp$(date %Y%m%d%H%M%S) output_file${OUTPUT_DIR}/${name}_${timestamp}_watermarked.mp4如果是在 Windows 批处理中可以用!random!或%date%生成编号set timestamp%date:~0,4%%date:~5,2%%date:~8,2% ffmpeg -i %%f ... %OUTPUT_DIR%\%%~nf_%timestamp%_watermarked.mp47.3 保留原始视频备份处理前建议保留原始视频不要在原目录上直接覆盖输出。批量处理过程中如果发现某个视频水印位置不合适还可以重新处理如果直接覆盖了原始文件再想调整就只能重新收集素材。7.4 日志记录与失败重试批量处理几百个视频时不可能全程盯着终端。建议把脚本输出写到日志文件./batch_add_watermark.sh watermark_log.txt 21处理完成后检查日志中是否存在error或Errorgrep -i error watermark_log.txt也可以让脚本在每个视频处理成功时写一行记录这样即使中途中断也能知道哪些文件处理成功、哪些还没处理。7.5 合法性提醒加水印这个技术本身是中性的。如果给别人的视频加水印再发布必须要获得视频版权所有者的授权。在公司内部项目中也要确认水印素材的版权归属尤其是字体和 Logo 图片避免因为使用了未经授权的商业字体而产生纠纷。7.6 权限与生产环境注意事项如果在服务端批量处理建议使用独立的工作目录并确保脚本运行账号只有该目录的读写权限。不要用 root 或管理员账号直接跑全目录脚本。涉及批量转换时先备份原视频再执行操作。任何动态生成的命令参数都应该经过校验防止文件名注入到命令中。7.7 性能与并发批量处理大量视频时不一定非得逐条串行执行。如果机器 CPU 核数较多可以参考以下并发思路用xargs -P指定并行任务数。每个任务使用独立的输出文件。控制并发数量避免 CPU 过载导致转码崩坏。例如在 Linux 下find input -type f -name *.mp4 | xargs -P 4 -I {} bash -c echo Processing {}; ffmpeg -i $1 -i watermark/logo.png -filter_complex [1:v]scale120:120[logo];[0:v][logo]overlaymain_w-wm_w-20:20 -codec:a copy output/$(basename $1 .mp4)_watermarked.mp4 _ {}并发处理能显著加快速度但要注意磁盘写入压力和 CPU 占用。建议先从-P 2开始测试再逐步增加。8. 总结与后续方向本文围绕“1000 个视频快速加水印”这个场景完整拆解了 FFmpeg 批量加水印的方案视频水印的价值在于品牌曝光和防搬运批量处理的核心是自动化脚本。FFmpeg 的 overlay 滤镜负责图片水印drawtext 滤镜负责文字水印。位置和大小可以通过变量表达式灵活控制支持左上、右上、左下、右下、居中也支持按视频分辨率比例缩放。批量处理可以通过 Windows 批处理脚本或 macOS/Linux bash 脚本实现处理单个视频的命令和批量脚本是同一套逻辑。遇到水印不显示、位置不对、中文乱码、速度慢等问题时可以按表格中的思路快速排查。接下来如果你想进一步深入可以从这几个方向继续学习FFmpeg 的更多滤镜比如文字滚动水印、片头片尾添加、视频裁剪拼接。使用 Python 调用 FFmpeg实现更复杂的批量任务调度和失败重试机制。使用 GPU 硬件编码加速比如 NVIDIA 的 NVENC 方案在大批量转码场景下提升效率。批量加水印只是 FFmpeg 能力中的一小部分但它覆盖了“视频处理 脚本自动化 工程化排错”的完整链路这个思路对你处理其他重复性视频任务同样适用。建议你拿起手边几个视频先试一条把命令跑通了再往批量脚本上迁移整个过程不会超过半小时。