搞视频处理的人,不管是做短视频运营、在线教育录课,还是自己折腾NAS里的家庭录像,迟早都会撞上两件事:给视频加水印,以及跟分辨率较劲。水印是为了打上自己的标记、防止素材被白嫖,分辨率则直接决定成片画质、文件大小,甚至推流时候会不会卡顿。
而这两件事,用 FFmpeg 一条命令就能一起解决。FFmpeg 这个命令行工具在音视频圈子里几乎是“神级”存在,它的强大之处不在于某个花哨界面,而在于你能用一行命令精确控制每一个像素和每一帧画面。这篇文章我打算把水印和分辨率这两块彻底讲透:文字水印怎么加、图片 Logo 怎么叠、位置怎么精确控制到“九宫格任意一格”,分辨率怎么查看、怎么指定、录音录像时怎么选,最后再给几个能直接抄作业的组合命令,顺便把我踩过的坑和已经验证过的排查技巧一起放出来。
全文以实操为主,命令会逐条拆解。没接触过 FFmpeg 的小白照着重现就行,有基础的也能在位置计算和滤镜组合上拿到一些新思路。
1. FFmpeg给视频加水印的两条路:drawtext文字水印与overlay图片叠加
1.1 文字水印drawtext:一条命令搞定“我的频道名”
文字水印是使用频率最高的一种,适合在视频角落放上频道名、账号ID、拍摄日期之类的内容。FFmpeg 里对应的滤镜叫 drawtext,工作流程可以简单理解为:解码视频帧 -> 在帧上把文字画上去 -> 再重新编码输出。
最基础的一条命令长这样:
ffmpeg -i input.mp4 -vf "drawtext=text='MyChannel':fontsize=48:fontcolor=white:x=10:y=10" -c:a copy output.mp4这里面的参数逐个拆开看:
text='MyChannel':要显示的文字内容。如果包含空格或特殊符号,一定要用引号包起来。fontsize=48:字号,单位是像素。做全屏水印建议小一点,角落水印可以设到36到64之间。fontcolor=white:文字颜色。常见的还有black、yellow,也可以写成十六进制颜色值,比如0xFF0000表示红色。x=10:y=10:文字左上角锚点的坐标,单位是像素。后面章节我会详细说这个坐标怎么算。
有一件事我必须放在最前面提醒:在 Windows 环境下,drawtext 的参数里冒号:和命令行参数分隔符冲突,很容易解析出错。建议在 Windows 下用双引号把 filter 串包住,并且尽量避免空格。Linux 和 macOS 下相对宽容,但养成统一写法的习惯能省掉很多麻烦。
再补充一个重点:如果需要显示中文,必须指定中文字体文件。否则即使系统里有中文字体,FFmpeg 也可能找不到,水印就会变成方块。指定字体的命令是这样的:
ffmpeg -i input.mp4 -vf "drawtext=text='测试水印':fontfile=C\:\\Windows\\Fonts\\msyh.ttc:fontsize=48:fontcolor=white:x=10:y=10" -c:a copy output.mp4Windows 下路径里的反斜杠需要转义成\\,冒号前面也要加\,这是不少新手第一次使用 drawtext 时会卡住半小时的细节。Linux 下则简单得多:
ffmpeg -i input.mp4 -vf "drawtext=text='测试水印':fontfile=/usr/share/fonts/truetype/droid/DroidSansFallbackFull.ttf:fontsize=48:fontcolor=white:x=10:y=10" -c:a copy output.mp41.2 图片水印overlay:给视频盖上Logo
如果不想用文字,而是想把一张带透明通道的 PNG Logo 叠加到视频上,那就用 overlay 滤镜。它的效率比 drawtext 高一些,因为不需要每次渲染文字轮廓,直接做图片合成。
ffmpeg -i input.mp4 -i logo.png -filter_complex "overlay=10:10" -c:a copy output.mp4这条命令的输入有两路:-i input.mp4是主视频,-i logo.png是水印图片。overlay=10:10表示把第二路输入叠加到第一路输入上,坐标是左上角(10, 10)。注意叠加滤镜放在-filter_complex里,而不是-vf,因为它同时操作两个输入流。
overlay 里有个很实用的坐标变量体系:
W和H:主视频的宽和高w和h:水印图片的宽和高
所以“右下角留 10 像素边距”就可以写成overlay=W-w-10:H-h-10,意思是水印的左上角放在“主视频宽度减去水印宽度再减 10”的位置上。这条表达式逻辑稍后在水印位置章节会反复用到,建议直接背下来。
对于准备长期使用的号主,我强烈建议把 Logo 做成一角透明的 PNG,尺寸控制在 200 到 300 像素范围内。PNG 的透明通道能让水印显得既不突兀又能一直存在,而 JPG 白底图叠上去会直接毁掉画面。
1.3 进阶:多行多列文字水印与动态时间戳
很多教程讲到这里就停了,但实际上 drawtext 还能做多行多列水印和动态水印。所谓多行多列,一般是重复绘制多个文字块,铺满画面或者做成栅格。比如要在画面里铺一个 3x3 的频道名水印栅格,可以叠加多个 drawtext:
ffmpeg -i input.mp4 -filter_complex \ "drawtext=text='MyChannel':fontsize=36:fontcolor=white@0.3:x=50:y=50,\ drawtext=text='MyChannel':fontsize=36:fontcolor=white@0.3:x=(w-tw)/2:y=50,\ drawtext=text='MyChannel':fontsize=36:fontcolor=white@0.3:x=w-tw-50:y=50" \ -c:a copy output.mp4这里的white@0.3是颜色透明度语法,0.3表示 30% 不透明度,适合做那种“存在但不打扰”的满屏水印。tw是文字实际渲染后的宽度,后面讲坐标时会详细展开。
动态时间戳的做法也很有意思。给录屏、监控录像打上拍摄时间,是 drawtext 的另一个高频场景:
ffmpeg -i input.mp4 -vf "drawtext=text='%{localtime\:%Y-%m-%d %H\\:%M\\:%S}':fontsize=24:fontcolor=yellow:x=w-tw-20:y=h-th-20" -c:a copy output.mp4%{localtime:...}是 drawtext 内建的时间展开语法,它会在每一帧刷新时重新渲染当前系统时间。注意里面的冒号也需要转义成\\:,不然会跟参数分隔符冲突。这条命令我用来给家里的摄像头录像做时间水印,跑了一年多没有出过问题。
2. 水印位置不求人:坐标规则、九宫格速查与跑马灯效果
2.1 FFmpeg水印坐标体系:原点、方向和常见坑
先明确坐标原点:FFmpeg 画布左上角是 (0, 0),x 轴向右增大,y 轴向下增大。这和你在学校学的平面直角坐标系在 y 轴方向上是反的,很多第一次接触的人会下意识把 y 往大写,结果水印直接跑出画面底部。
drawtext 滤镜里可用的变量如下:
w、h:主视频画面的宽和高。tw、th(等价写法text_w、text_h):实际渲染出来的文字宽度和高度,由字号和内容决定。
overlay 滤镜里可用的变量略有差异:
W、H:主视频画面的宽和高。w、h:水印图片的宽和高。
所以你会发现:drawtext 里靠tw、th计算文字自身尺寸,overlay 里靠w、h计算图片自身尺寸。这两个体系搞混是水印位置翻车的最常见原因。
举个例子,你要把文字水印放在右下角,离边距 20 像素。正确写法是:
drawtext=text='MyChannel':x=w-tw-20:y=h-th-20我第一次写的时候用了x=W-tw-20,结果 W 这个变量在 drawtext 里并不存在(准确说是主视频的另一个别名,但在部分版本中不会被解析),水印直接没有显示。正确理解变量所属的滤镜上下文,能少走很多弯路。
另外需要注意坐标的计算时机。FFmpeg 的滤镜表达式是逐帧求值的,也就是说,只要视频分辨率在流水线中发生变化,水印位置会自动跟着新分辨率重新计算。这算是 FFmpeg 的贴心之处,但前提是你用了w、h这样的相对变量,而不是写死一个数字。
2.2 九宫格水印定位速查表
为了方便日常使用,我把最常见的水印摆放位置整理成了一张速查表。文字水印套用drawtext,把tw/th换成文字自身宽高;图片水印套用overlay,把tw/th换成w/h。实际操作时,水印的定位可以用这几个公式直接套用:
| 位置 | drawtext 文字水印坐标 | overlay 图片水印坐标 |
|---|---|---|
| 左上角 | x=20:y=20 | x=20:y=20 |
| 上边居中 | x=(w-tw)/2:y=20 | x=(W-w)/2:y=20 |
| 右上角 | x=w-tw-20:y=20 | x=W-w-20:y=20 |
| 左边居中 | x=20:y=(h-th)/2 | x=20:y=(H-h)/2 |
| 画面正中 | x=(w-tw)/2:y=(h-th)/2 | x=(W-w)/2:y=(H-h)/2 |
| 右边居中 | x=w-tw-20:y=(h-th)/2 | x=W-w-20:y=(H-h)/2 |
| 左下角 | x=20:y=h-th-20 | x=20:y=H-h-20 |
| 下边居中 | x=(w-tw)/2:y=h-th-20 | x=(W-w)/2:y=H-h-20 |
| 右下角 | x=w-tw-20:y=h-th-20 | x=W-w-20:y=H-h-20 |
这张表里的20都可以换成任意边距值。你会发现规律非常统一:左右看w-tw还是(w-tw)/2,上下看h-th还是(h-th)/2,只要记住“自身尺寸放在表达式里”这个原则,任何分辨率都能自动居中或靠边。
2.3 动态水印:跑马灯效果与分辨率自适应
动态水印听起来高大上,其实也只是在坐标里放了一个随时间变化的表达式。drawtext 的坐标支持表达式求值,而表达式里有一个内置变量t,表示当前播放时间,单位是秒。
跑马灯效果,也就是水印从左上角缓慢移动到右下角,可以用这样一条命令:
ffmpeg -i input.mp4 -vf "drawtext=text='MyChannel':fontsize=36:fontcolor=white@0.5:x=mod(50*t\,w+tw):y=mod(40*t\,h+th)" -c:a copy output.mp4mod(50*t, w+tw)的意思是把随 t 线性增长的值映射到 0 到 总宽度的循环区间里,水印就会循环移动。50和40分别是水平和垂直方向的移动速度,单位是像素/秒。逗号在滤镜参数里是分隔符,出现在坐标表达式内部时必须转义成\,,否则 FFmpeg 解析会提前截断。
这种跑马灯水印适合防搬运场景,虽然稍微有点干扰观看,但盗视频的人很难通过简单裁剪把水印去掉。如果你担心影响观看体验,可以把移动速度调慢到 20 以下,或者像我上面写的那样把透明度降到 0.5 以下,观感会舒服很多。
3. 分辨率三件事:怎么查看、怎么指定、录音录像时怎么选
3.1 快速查看视频分辨率的三个方法
在加水印、转码、推流之前,第一步永远是搞清源视频的分辨率。FFmpeg 提供了几种查看方式,按使用场景选即可。
第一个方法最简单直接,用-i参数让 FFmpeg 输出媒体信息,报错也没关系,信息照样会打印出来:
ffmpeg -i input.mp4看输出里的这一行:
Stream #0:0: Video: h264 (High) (avc1 / 0x31637661), yuv420p, 1920x1080 [SAR 1:1 DAR 16:9]1920x1080就是分辨率。
第二个方法用 ffprobe,这是 FFmpeg 套件里专门查媒体信息的工具,输出干净,适合脚本化调用:
ffprobe -v error -select_streams v:0 -show_entries stream=width,height -of default=noprint_wrappers=1 input.mp4输出会是这样:
width=1920 height=1080第三个方法适合批量处理时用,直接将分辨率写入文件:
ffprobe -v error -select_streams v:0 -show_entries stream=width,height -of csv=s=x:p=0 input.mp4输出1920x1080。这个写法我经常放在 shell 脚本里,配合变量做下一步判断。
3.2 指定输出分辨率的姿势:-s、scale与等比例缩放
指定输出分辨率主要有两种写法:-s参数和scale滤镜。很多新手喜欢用-s,因为它短,但它的短板很明显:直接拉伸,不会保持宽高比。4:3 的视频被强行压到 16:9 的画面,人物会变成“矮胖子”,这个效果在录制讲座、PPT 视频时尤其明显。
不推荐的做法:
ffmpeg -i input.mp4 -s 1920x1080 -c:a copy output.mp4推荐的做法是用 scale 滤镜配合等比例控制:
ffmpeg -i input.mp4 -vf "scale=1280:720" -c:a copy output.mp4如果源视频是 1080p 想降到 720p,等比缩放一行代码搞定:
ffmpeg -i input.mp4 -vf "scale=1280:-1" -c:a copy output.mp4-1表示另一个维度按照源视频比例自动计算。这样既固定了宽度,高度也自动完成等比,不会变形。
如果想让输出的宽高严格等于目标值,同时又不裁剪画面内容,可以用force_original_aspect_ratio=decrease先等比缩放到目标尺寸以内:
ffmpeg -i input.mp4 -vf "scale=1280:720:force_original_aspect_ratio=decrease" -c:a copy output.mp4这个写法的效果是等比缩放并保证目标尺寸内完整显示,可能留出黑边。如果不想有黑边,就需要“放大 + 裁剪”组合拳,这个放在第四部分实操里展开。
3.3 录音录像时分辨率选择的实际考量
录音录像时怎么定分辨率,要看三样东西:摄像头支持什么分辨率、目标平台建议什么分辨率、你的编码性能能不能跑得动。
FFmpeg 调用本地摄像头采集画面,Windows 上典型命令是:
ffmpeg -f dshow -list_devices true -i dummy先列出可用的设备名,然后开始录制:
ffmpeg -f dshow -i video="USB Camera" -f dshow -i audio="麦克风 (USB Audio)" -c:v libx264 -s 1280x720 -c:a aac -t 60 output.mp4Linux 上通常走 v4l2,列出设备格式用这条命令:
ffmpeg -f v4l2 -list_formats all -i /dev/video0然后以 720p 30 帧录制:
ffmpeg -f v4l2 -framerate 30 -video_size 1280x720 -i /dev/video0 -f alsa -i default -c:v libx264 -preset veryfast -c:a aac output.mkv这里-video_size必须放在-i前面,因为它不是滤镜,而是摄像头采集参数。很多人在 Linux 下录了半天发现分辨率没生效,就是这个参数位置的问题。
关于分辨率选多大,我给一个参考表格,适用于大多数视频平台和录制场景:
| 目标场景 | 分辨率 | 建议码率 |
|---|---|---|
| 手机观看、短视频 | 1280x720 | 2-4 Mbps |
| 主流网站成片 | 1920x1080 | 4-8 Mbps |
| 大屏/高清素材 | 2560x1440 | 9-14 Mbps |
| 4K 播放/后期精剪 | 3840x2160 | 20-40 Mbps |
码率太低画面会出现明显的块状模糊,尤其在高动态场景下;码率太高文件体积直线上升,而且上传平台也会二次压缩。上面这个表格是基于我自己的录制经验整理出来的,不同场景可以微调,但不要跨档乱跳。
另一个需要重视的问题是电脑的编码性能。如果用的是软件编码 libx264,1080p 30fps 录制时 CPU 占用会直线飙升,风扇起飞是常态。此时有两个选择:一是降低分辨率到 720p,二是加上-preset veryfast或-preset ultrafast牺牲压缩率换编码速度。如果硬件支持,还可以考虑硬件编码。NVIDIA 显卡用-c:v h264_nvenc,Intel 核显用-c:v h264_qsv,AMD 用-c:v h264_amf。这些编码器同样有对应的-preset p1/p2之类的参数,具体在命令行里敲ffmpeg -h encoder=h264_nvenc就能看到。
4. 组合实操:一次搞定“分辨率转换+水印”的完整命令
4.1 从原始命令到可用命令的拆解
很多教程只教单独的水印命令或单独的分辨率命令,但实际处理视频时,两件事往往是同时发生的。比如你从手机导出一段 4K 素材,想压成 1080p,同时在右下角打上自己的频道名,这就是典型的“一次滤镜链、多重效果”的需求。
一条完整的组合命令如下:
ffmpeg -i input.mp4 -vf "scale=1920:1080:force_original_aspect_ratio=decrease,drawtext=text='MyChannel':fontsize=36:fontcolor=white@0.7:x=w-tw-30:y=h-th-30" -c:v libx264 -preset medium -crf 18 -c:a aac -b:a 192k output.mp4逐个拆解:
scale=1920:1080:force_original_aspect_ratio=decrease:先把视频等比缩放到不超过 1920x1080 的范围。如果源视频本身就是 4K 横屏视频,它会缩到 1920x1080 附近,画面上没有黑边,比例也正确。drawtext:在缩放后的画面上画水印。因为滤镜链是顺序执行的,水印的坐标参考是缩放后的画面,所以x=w-tw-30里的w已经是 1920,水印自动位于缩放后画面的右下角。-crf 18:libx264 的质量控制参数,数值越低质量越高,18 是视觉无损级别。日常网络发布不建议低于 18,否则文件会大得毫无意义。-preset medium:压缩速度和压缩率的平衡档。
这里我特别说明一下force_original_aspect_ratio=decrease的作用。它有两种模式:decrease是“等比缩小到不超出目标框”,适合保证裁剪安全;increase是“等比放大到完全盖住目标框”,适合配合crop做去黑边处理。需要铺满屏幕且不留黑边时,用下面这条“裁切版”:
ffmpeg -i input.mp4 -vf "scale=1920:1080:force_original_aspect_ratio=increase,crop=1920:1080,drawtext=text='MyChannel':fontsize=36:fontcolor=white@0.7:x=w-tw-30:y=h-th-30" -c:v libx264 -preset medium -crf 18 -c:a aac -b:a 192k output.mp4scale先把画面放大到超出 1920x1080,然后crop=1920:1080从画面正中心截取一块 1920x1080 的内容。最后画水印。这个流程就是各大视频平台“封面占满”效果的标准实现,区别只是平台后端用的编码器不同而已。
配合第 2 节的九宫格公式,水印位置可以随意切换。比如要放在正中间防止盗用,把x=w-tw-30:y=h-th-30换成x=(w-tw)/2:y=(h-th)/2即可。
4.2 竖屏短视频的处理思路
短视频平台的竖屏视频默认是 1080x1920,也就是宽 1080、高 1920。从横屏素材转竖屏时,如果你直接把scale=1080:1920套上去,画面会被严重拉伸变形。正确的处理思路跟上面类似,先等比放大再裁切:
ffmpeg -i input_horizontal.mp4 -vf "scale=1080:1920:force_original_aspect_ratio=increase,crop=1080:1920,drawtext=text='@MyChannel':fontsize=48:fontcolor=white:x=(w-tw)/2:y=h-th-80" -c:v libx264 -preset medium -crf 20 -c:a aac output_vertical.mp4这条命令会把横屏素材放大到完全覆盖 1080x1920,然后从中间裁出竖屏范围。水印放在竖屏画面中下方,贴近短视频的使用习惯。缺点是画面上下部分会被裁掉不少,如果原视频的关键信息在左右两侧,裁切会直接丢内容。这种场景更适合“背景模糊 + 居中完整画面”的方案,那个属于滤镜链的高级玩法,这里先不展开。
4.3 几个值得养成习惯的FFmpeg小参数
组合命令用得顺手之后,有几个参数我会建议你固定写进去,它们能避免很多莫名其妙的问题。
第一个是-y。FFmpeg 默认在输出文件已存在时会进入交互询问,脚本化执行时直接卡住。加上-y表示无条件覆盖,省心很多。
第二个是-c:a copy的判断。如果只处理视频画面、不动音频,直接-c:a copy拷贝音频流,速度飞快,而且不会引入二次音频压缩损失。只有需要改变音频采样率或者码率时才用-c:a aac重编码。
第三个是-movflags +faststart。这个参数只对 MP4 有效,作用是把播放需要的索引信息从文件末尾移到文件开头。没有它,从网页直接播放 MP4 时要等整个文件下载完才能拖动进度条;加上它,浏览器一打开就能秒播。我处理所有要上传到网站的视频都会加这个参数。
ffmpeg -i input.mp4 -vf "scale=1280:720" -c:v libx264 -crf 20 -preset medium -c:a aac -b:a 128k -movflags +faststart -y output.mp45. 常见问题与排查技巧实录
5.1 常见问题速查表
在使用 FFmpeg 过程中踩过不少坑,这里整理成一张速查表,都是能一两句话解决的典型问题:
| 现象 | 原因 | 解决方案 |
|---|---|---|
| 水印完全没有显示 | fontfile 路径错误或 drawtext 参数被转义截断 | 检查字体路径,Windows 下注意反斜杠和冒号转义,先输出一帧静帧测试 |
| 中文水印显示成方块 | 没有指定中文字体 | 用fontfile=指定系统中文字体文件,比如微软雅黑 msyh.ttc |
| 分辨率变了但水印位置偏了 | 坐标写死了具体像素 | 改用w/tw/h/th表达式变量,让水印随分辨率自适应 |
| 输出画面变形、人物被拉宽 | 直接用-s强制拉伸 | 改用scale滤镜加force_original_aspect_ratio |
| 输出时提示会覆盖文件 | FFmpeg 默认交互确认 | 命令开头加-y |
| 转码速度极慢 | 默认编码参数过于保守 | 加-preset veryfast,或用硬件编码器h264_nvenc/h264_qsv |
| 视频无法拖动进度条 | MP4 索引在文件尾部 | 加-movflags +faststart |
这张表里的问题,我几乎每一条都真实遇到过一次以上。尤其是第一条“水印完全没有显示”,我在给朋友远程排障时见过太多次,十有八九是 Windows 下滤镜参数里的冒号没有正确转义,或者是字体路径少了转义符号。
5.2 排查技巧:先测静帧再跑全片
这是我认为对新手最有价值的一条排查思路:不管你的命令最终要处理多长的视频,在正式跑全片之前,先把输出限制成一张静帧。这样做的好处是:一张图只要零点几秒就能出来,而跑全片可能要几分钟甚至几十分钟。命令写错了,跑全片就是浪费大把时间。
做法很简单,在滤镜链末尾加上-frames:v 1并输出成 PNG:
ffmpeg -i input.mp4 -vf "scale=1280:720,drawtext=text='MyChannel':fontsize=36:x=w-tw-30:y=h-th-30" -frames:v 1 test.png打开test.png看一眼水印位置对不对、字号大不大、颜色清不清楚。一切满意后再把-frames:v 1删掉,正式输出视频。这个习惯我坚持了很多年,它能让你在动手写一条复杂命令时不断试错,成本极低。
如果水印位置不对,改动的也只是坐标表达式,重新跑静帧验证再正式编码,效率会提高一个数量级。定位问题最忌讳的就是拿一个十分钟的视频反复试,所有时间都耗在等待上了。
5.3 关于去水印:合法场景与delogo滤镜用法
热词里大量出现“去水印”需求,这里必须补充说明。FFmpeg 确实提供了delogo滤镜,作用是去除视频画面中指定区域的 Logo 水印,原理是读取水印区域周围的像素值,用插值方式把水印区域重新填充。
命令长这样:
ffmpeg -i input.mp4 -vf "delogo=x=100:y=100:w=120:h=40" -c:a copy output.mp4x/y/w/h四个参数圈定水印所在区域。注意这个滤镜对动态水印无效,因为水印位置变化后区域就不准了;对透明半透明叠印在复杂纹理上的水印,效果也一般,插值补出来的画面会有较明显的涂抹感。它更适合那种位于纯色背景或静态画面上的固定水印。
但使用这个功能前,请务必确认素材的版权归属:只能处理自己拥有版权的视频,或者去除平台自动叠加的标记。拿别人的版权内容去水印再传播,既违反平台规则,也触碰法律红线。技术本身是中性的,使用边界得自己把握好。
结尾的一些个人经验
最后分享一个我自己的小习惯:凡是涉及水印和分辨率组合的命令,我都会把滤镜链的关键部分写成一个脚本文件,而不是每次敲一长串。比如处理 YouTube 和 B 站视频,两套分辨率预设和水印文案往往不同,我就会写两个脚本参数化处理,一行命令调用,既不担心手误,也能保证长期一致的输出风格。
多试几次之后你会发现,FFmpeg 的滤镜链就像搭积木,水印和分辨率只是最基础的两块。当你熟练了 drawtext 的坐标表达式,理解了 overlay 和 scale 的变量体系,再去碰剪切、拼接、转场、字幕、音画同步这些功能时会顺手很多。希望这篇内容能帮你在处理水印和分辨率问题时少走几段弯路,把那点折腾命令的时间省下来,用到更值得的地方去。