ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

FFmpeg实战:分辨率查看与文字/图片水印添加全攻略

FFmpeg实战:分辨率查看与文字/图片水印添加全攻略 直接开工不整虚的。网上搜FFmpeg教程十篇有八篇在互相抄抄来抄去连滤镜参数都写错。我这些年用FFmpeg处理过上万条视频从监控录像到短视频素材都碰过这次就把水印和分辨率这两块最常用的操作一次性讲透——查看分辨率、指定分辨率、加文字水印、加图片水印、控制水印位置全都有现成命令和实测参数照着抄就能用。1. 动手之前先把FFmpeg的命令骨架搞明白1.1 FFmpeg不是一条命令而是一组工具链很多人第一次接触FFmpeg以为它就是个能转格式的命令行工具其实它是一整套多媒体处理套件。日常用得最多的是三个成员ffmpeg负责转码和处理ffprobe负责查看媒体文件信息ffplay负责快速预览播放。查看分辨率和媒体信息优先用ffprobe它输出的信息干净、结构化好解析ffmpeg也能看但信息混在转码日志里不如ffprobe清爽。安装这一步反而最容易被忽略。Windows用户别去官网下那个几百MB的完整版直接搜gyan.dev的release构建下载ffmpeg-release-full.7z解压把bin目录加进系统PATH就行。macOS用户用Homebrew跑brew install ffmpeg一条命令搞定。Linux用户一般apt install ffmpeg或yum install ffmpeg但有些发行版的源里版本太老转码性能和新滤镜支持都跟不上建议用官方静态构建。版本这事别马虎FFmpeg每年都在加新滤镜新参数网上很多老教程的命令在新版本上跑不通就是因为版本太旧。1.2 命令的通用骨架输入、滤镜、输出FFmpeg命令看着长拆开就三部分输入参数、滤镜链、输出参数。拿水印命令举例ffmpeg -i input.mp4 -vf drawtexttextHELLO:fontsize48 output.mp4-i input.mp4是输入-vf后面跟的是视频滤镜链output.mp4是输出。所有复杂的视频处理比如缩放、裁剪、加水印、调色本质上都是在-vf这根滤镜链上做文章。滤镜之间用逗号串联按顺序执行前一个滤镜的输出是后一个滤镜的输入。音频处理则是-af参数比如调音量、降噪、变速不变调。录音场景里经常要同时处理视频和音频可以分开写两条滤镜链。理解了这个骨架后面所有命令都只是往里面填不同的滤镜参数而已。我见过不少新手把滤镜参数写到-i前面结果FFmpeg完全忽略输出文件没任何变化这就是没搞清楚输入输出顺序的典型错误。2. 查看分辨率别猜直接用ffprobe拿准确数据2.1 ffprobe一条命令看全部媒体信息想知道一个视频是多大分辨率、什么编码、多少帧率最靠谱的方式就是ffprobeffprobe -show_streams -show_format input.mp4输出内容会比较长包含视频流、音频流、封装格式三类信息。实际操作中一般不用看完整输出加个-select_streams v只选视频流ffprobe -v error -select_streams v:0 -show_entries streamwidth,height,avg_frame_rate,codec_name,bit_rate -of defaultnoprint_wrappers1 input.mp4返回结果大概是codec_nameh264 width1920 height1080 avg_frame_rate30000/1001 bit_rateN/A这里有个细节容易看懵avg_frame_rate返回的是分数形式30000/1001不是乱码是NTSC标准的29.97fps。直接除一下就知道真实帧率。bit_rate显示N/A也别慌很多封装格式里码率信息不在流级别里要到容器级别查。想看容器信息就用-show_entries formatbit_rate,duration。2.2 从ffmpeg输出日志里辨认分辨率有些时候你只有ffmpeg -i的日志输出比如在一台没装ffprobe的机器上临时排查。FFmpeg在转码前会输出媒体文件的基础信息里面有一行长这样Stream #0:0(und): Video: h264 (High) (avc1 / 0x31637661), yuv420p(progressive), 1920x1080 [SAR 1:1 DAR 16:9], 30000/1001 fps, 30000/1001 tbr, 12800 tbn中间的1920x1080就是分辨率括号里的DAR 16:9是显示宽高比。这里有个小坑有些视频的存储分辨率是1920x1080但实际显示宽高比不是16:9比如某些DV录像或老视频会带像素宽高比SAR修正。后续做缩放处理时这个SAR信息很关键处理不对画面就会变形。2.3 批量查看多文件分辨率的实用脚本单个文件用命令看没问题几十个文件排着队就麻烦了。我习惯写个小循环脚本几行代码把文件夹里所有视频的分辨率一次列出来for f in *.mp4; do dims$(ffprobe -v error -select_streams v:0 -show_entries streamwidth,height -of csvsx:p0 $f) echo $f: $dims done-of csvsx:p0的意思是输出格式为CSV分隔符用x不打印条目名所以结果就是1920x1080这种直接的字符串。Windows用户没有bash循环可以写个批处理或者直接用PowerShell的ForEach-Object逻辑一样。批量场景里还有一个更高效的办法用ffprobe输出JSON格式然后交给Python或jq解析数据量大时这样更好处理。3. 指定分辨率缩放滤镜的进阶用法3.1 scale滤镜基础直接写死宽高最朴素的指定分辨率方式ffmpeg -i input.mp4 -vf scale1280:720 output.mp4也可以不写数字让FFmpeg自己算一边ffmpeg -i input.mp4 -vf scale1280:-1 output.mp4-1表示按原视频宽高比自动计算另一边。比如源文件是1920x1080scale1280:-1会算出高度720成片就是1280x720。这个参数很常用但也容易出问题如果源视频是竖屏1080x1920scale1280:-1算出的是1280x2276跑出来分辨率奇奇怪怪不是你想的效果。所以动脑子想想再决定哪边写死哪边自适应。另一个常见需求是固定倍数缩放两倍放大用ffmpeg -i input.mp4 -vf scaleiw*2:ih*2 output.mp4iw是输入宽度ih是输入高度这是滤镜里的变量。缩到50%就是scaleiw/2:ih/2。这里的变量体系是整个滤镜链的通用能力不只在scale里用overlay、drawtext这些滤镜也能引用后面讲水印位置时会反复用到。3.2 保持宽高比与防止画面变形新手最常犯的错误是直接写两个固定值把画面拉伸变形。比如把1920x1080压到640x480视频流分辨率确实变成640x480了但画面内容被横向压扁人脸拉宽字幕变色观感极差。真正稳妥的做法有三条路用-1自动计算一边ffmpeg -i input.mp4 -vf scale640:-1 output.mp4先裁剪补边再缩放适合不同宽高比转换的场景ffmpeg -i input.mp4 -vf scale640:480:force_original_aspect_ratiodecrease,pad640:480:(ow-iw)/2:(oh-ih)/2 output.mp4只改分辨率宽高比交给播放器自适应ffmpeg -i input.mp4 -vf scale640:480:force_original_aspect_ratiodecrease output.mp4最后这个命令输出会是640x360对16:9源但容器里记录了640x480的目标显示尺寸部分播放器能正确处理但很多播放器会糊。做后期的话千万别用这种方案压片的人拿到你这文件得骂街。force_original_aspect_ratiodecrease这个参数要重点说它的作用是让画面保持原始宽高比缩放不足的部分用背景填充配合pad滤镜补边。我在短视频平台的封面制作里经常用这招把横屏视频转成竖屏封面主体内容不变形上下黑边自然填充。3.3 录音录像场景采集时直接指定采集分辨率用FFmpeg做屏幕录制或摄像头采集时指定分辨率的方式和转码略有不同。屏幕录制命令ffmpeg -f gdigrab -framerate 30 -video_size 1920x1080 -i desktop output.mp4这里-video_size是采集参数不是滤镜参数写在-i前面。摄像头采集Linux下UVC设备常见ffmpeg -f v4l2 -input_format mjpeg -video_size 1280x720 -framerate 30 -i /dev/video0 output.mp4macOS下摄像头采集用的是-f avfoundationWindows用的是-f dshow# Windows设备名要先列出来 ffmpeg -f dshow -list_devices true -i dummy # 然后按设备名采集 ffmpeg -f dshow -video_size 1280x720 -framerate 30 -i videoUSB Camera output.mp4这种推流前先查分辨率的思路和后面讲水印时的先查视频参数再定水印位置是一脉相承的。采集分辨率这个东西高了浪费CPU因为采集完可能还要缩放低了糊最好是按最终输出需求来。我实测过采集1920x1080再缩到1280x720画质比直接采1280x720好不了多少但CPU占用会高8%~12%实时场景里完全不值得。录音也有类似的逻辑。指定采样率ffmpeg -f pulse -i default -ar 44100 -ac 2 output.wav-ar是音频采样率-ac是声道数。录像时要同步指定音视频参数我常用的实战命令ffmpeg -f gdigrab -framerate 30 -video_size 1920x1080 -i desktop -f dshow -i audioMicrophone -c:v libx264 -preset ultrafast -crf 28 -c:a aac -b:a 128k output.mp43.4 高效缩放的性能考量与参数微调缩放不是免费的1280x720升级到4K对性能要求不低。FFmpeg默认的swscale软件缩放器虽然兼容性好但速度一般。有两个办法提速指定更快的缩放算法ffmpeg -i input.mp4 -vf scale1920:1080:flagsfast_bilinear output.mp4flags可以选fast_bilinear速度快、bicubic质量好、lanczos质量最好但慢。日常处理用bicubic就够了做放大这类质量敏感的操作用lanczos。硬件缩放。Intel平台的QSV、NVIDIA的CUDA都能参与缩放命令写法不同但思路都是在scale前指定硬件上下文。比如NVIDIA硬解硬编的例子ffmpeg -hwaccel cuda -hwaccel_output_format cuda -i input.mp4 -vf scale_cuda1920:1080 -c:v h264_nvenc output.mp4硬件加速这条路能聊一整篇这里先提个醒RTX 30系以后显卡的NVENC编码器质量已经很好了但NVIDIA的缩放滤镜scale_cuda在部分老驱动或老FFmpeg版本上兼容性一般遇到黑屏或花屏先检查驱动和FFmpeg版本。CPU多核性能够用的话优先软件方案显卡留给渲染或者游戏稳字当头。4. 添加水印文字、图片、位置控制一次搞定4.1 文字水印drawtext滤镜初体验文字水印用drawtext滤镜最简单的命令ffmpeg -i input.mp4 -vf drawtexttextHello World:fontsize48:fontcolorwhite output.mp4默认文字出现在左上角白字黑边实际上默认带一点阴影。真实场景里一般要指定字体和位置ffmpeg -i input.mp4 -vf drawtextfontfile/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf:textHELLO:fontsize48:fontcolorwhite:x50:y50 output.mp4Windows用户注意fontfile路径里的冒号和反斜杠都要转义这个坑我踩得刻骨铭心。Windows下写路径要用正斜杠或者双反斜杠。macOS下常见字体路径是/System/Library/Fonts/Helvetica.ttc。x和y是文字左上角的坐标。但实际控制位置时几乎不会用死值因为水印要跟着视频尺寸走比如居中、靠右下角留出边距这些都需要用到动态变量。4.2 图片水印overlay滤镜实战图片水印用overlay滤镜逻辑是把一张PNG叠在视频上面ffmpeg -i input.mp4 -i logo.png -filter_complex overlayW-w-10:10 output.mp4注意这里用的是-filter_complex而不是-vf因为涉及两个输入视频和图片必须用复杂滤镜图。W是视频宽度w是图片宽度W-w-10就是图片离视频右边界10像素。初始坐标是图片左上角的落点。多个水印叠加就写多个overlay滤镜用逗号串联ffmpeg -i input.mp4 -i logo1.png -i logo2.png -filter_complex overlayW-w-10:10,overlay10:H-h-10 output.mp4每个overlay作用的对象是前一步的结果所以第二个overlay里的W和H已经是第一个overlay处理过的视频尺寸了。这个逻辑初学者容易绕晕我打个比方滤镜链就是流水线每个工位处理完的东西会传给下一个工位所以后面工位看到的视频宽高已经是加工过的了。想确认某个滤镜需要的变量值可以加-debugverbose看日志里的滤波器图信息。4.3 水印位置控制坐标变量的进阶理解水印定位是滤镜里最灵活也最容易出错的点。最基本的定位需求就五种左上、右上、左下、右下、居中。位置x坐标y坐标说明左上角1010直接写死就行右上角W-w-1010靠右留边左下角10H-h-10靠下留边右下角W-w-10H-h-10最常用居中(W-w)/2(H-h)/2注意除法结果自动截断取整这几个公式里W、H是视频宽高w、h是水印图片宽高。做图片水印时如果你不确定PNG的实际尺寸可以先跑一下ffprobe logo.png看宽高免得算出来的位置偏得离谱。上面这些公式在drawtext里同样适用只是变量含义略有区别drawtext里没有w和h那是文字尺寸文字宽度要用text_w表示文字高度用text_h。所以文字水印右下角定位是drawtext...:xW-tw-20:yH-th-20tw是文本宽度th是文本高度。这个变量名很多人不知道翻官方文档也只写了text_w和text_h的别名tw、th。我在脚本里习惯全用短变量命令能短一截是一截。4.4 多行多列水印平铺马赛克式水印的做法多行多列文字水印这个需求最近很热很多人想给自己的素材打满屏水印防搬运。做法不复杂思路是用多个drawtext滤镜平铺或者用一个drawtext加换行符。先看多行文字ffmpeg -i input.mp4 -vf drawtextfontfile...:text版权声明 请勿搬运:x10:y10,drawtextfontfile...:text2025-03-01:x10:y80 output.mp4多列就继续加坐标手动写了N个drawtext也行但我觉得丑且难维护。更好的方案是生成一张文字水印PNG然后用overlay平铺。生成PNG可以用FFmpeg自己来做下面这行命令会生成一个透明背景的PNG但中文字体支持很麻烦我一般用ImageMagick或Photoshop搞定不过更干净的办法是准备好一张水印图然后用overlay铺ffmpeg -i input.mp4 -i watermark.png -filter_complex overlayW-w-10:H-h-10,overlayW-w-260:H-h-10,overlayW-w-10:H-h-180 output.mp4手动铺三个已经有点嫌弃了真正平铺整屏的做法是配合tile滤镜。先复制输入帧然后做多宫格拼贴这个命令稍微反直觉我这里给一个实用的平铺方案ffmpeg -i input.mp4 -i wm.png -filter_complex [0:v]scale1920:1080[bg];[1:v]scale200:-1[wm];[bg][wm]overlayW-w-20:H-h-20[o1];[o1][wm]overlay20:H-h-20[o2];[o2][wm]overlayW-w-20:20[o3];[o3][wm]overlay20:20 output.mp4这个命令先把视频缩到统一尺寸然后把水印图缩到200宽四个角落各放一个。做九宫格水印满屏网格水印的核心也就是这个思路的扩展。4.5 半透明水印与动态水印半透明水印的做法是用formatrgba,colorchannelmixeraa0.4先把PNG转成半透明再overlay。不过更常见的做法是PNG素材本身就是半透明的像很多设计网站出的透明底logo透明度在PS里已经控制好了滤镜端不用动。如果硬要在FFmpeg里调透明度ffmpeg -i input.mp4 -i logo.png -filter_complex [1:v]formatrgba,colorchannelmixeraa0.5[logo];[0:v][logo]overlayW-w-10:H-h-10 output.mp4动态水印比如跑马灯效果就是让x或y坐标随时间变化。dx和dy参数可以让水印移动ffmpeg -i input.mp4 -i logo.png -filter_complex overlayxmod(t*50, W):y10 output.mp4这个效果是水印从左边进入从右边消失。t是当前时间秒数t*50控制速度mod是取模运算保证x坐标不会超过视频宽度。5. 水印实战中的坑与解决思路5.1 中文字体缺失与编码drawtext默认字体往往不支持中文加上fontfile参数指定中文字体是必须的。Windows下一般指定C盘字体目录比如C\:/Windows/Fonts/msyh.ttc微软雅黑。Linux下常见的中文字体路径是/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc。另外中文字符串建议单独抽出来先用文本编辑器确认编码然后直接贴在参数里。如果你在Windows的命令行里敲中文参数编码经常不是UTF-8FFmpeg会报错或者显示乱码。稳妥的办法是把文字内容先写入一个文本文件然后让drawtext读取ffmpeg -i input.mp4 -vf drawtextfontfile...:textfiletitle.txt:fontsize48 output.mp45.2 水印清晰度与视频压缩的关系水印被压糊的根本原因是编码码率不够。加了水印的视频尤其是文字水印区域细节变多编码器会分配更多码率如果总码率固定画质必然下降。两个应对办法用CRF模式而不是固定码率ffmpeg -i input.mp4 -vf drawtext... -c:v libx264 -crf 23 output.mp4CRF恒定质量因子的数值越小质量越高23是Web发布的推荐值18以下属于无损级做存档用。水印做成半透明减少高频细节。5.3 批量给整个目录加图片水印文本处理和批处理是shell的拿手戏。给一个文件夹里所有MP4加同一个水印for f in *.mp4; do ffmpeg -i $f -i logo.png -filter_complex overlayW-w-10:H-h-10 -c:a copy watermarked_$f done这个命令有一个不起眼但很重要的参数-c:a copy表示音频直接复制不重新编码能够让处理速度快很多。视频流做了滤镜处理必须重新编码但音频流如果不需要滤镜操作直接copy能省下不少CPU。6. 常见问题排查与命令速查6.1 滤镜未找到或参数报错[Parsed_drawtext_0 0x...] Error: Invalid text or expression这类问题八成是字体文件路径有问题或者text参数里的特殊字符没转义。排查方法先把命令拆到只保留drawtext其他滤镜全部去掉然后逐步加参数定位是哪个参数导致的报错。[AVFilterGraph 0x...] No such filter: drawtext这是FFmpeg编译时没启用libfreetype静态构建一般不会出这问题但某些精简版FFmpeg会。解决办法就是去下载完整版。6.2 分辨率指定了但没生效最典型的情况是把滤镜参数放在了输出文件名后面# 错误示例 ffmpeg -i input.mp4 output.mp4 -vf scale1280:720FFmpeg对参数顺序有严格要求-vf必须出现在输出文件之前。第二个坑是把滤镜写到了-i前面某些场景下会被解释成输入选项然后被忽略。6.3 水印位置参数速查表需求overlay (图片)drawtext (文字)右下角留边20pxxW-w-20:yH-h-20xW-tw-20:yH-th-20右上角留边20pxxW-w-20:y20xW-tw-20:y20水平居中垂直顶部x(W-w)/2:y20x(W-tw)/2:y20水平居中垂直居中x(W-w)/2:y(H-h)/2x(W-tw)/2:y(H-th)/26.4 去水印的正确姿势与边界看到去水印需求我的态度很明确只聊技术原理不鼓励盗用他人素材。视频去水印在FFmpeg里没有魔法只有delogo滤镜做法是在水印区域模糊或插值ffmpeg -i input.mp4 -vf delogox100:y100:w200:h80 output.mp4delogo的原理是取水印区域周围的像素做内插把水印盖住。实效上静态背景上效果好复杂运动场景会有明显涂抹感。如果你要处理的是自己拍的视频不小心录入了厂商logo比如摄像头时间戳、相机水印delogo是能救急的处理别人的视频素材用于商用这就不只是技术问题了是版权问题我不给任何建议。真要给自己的素材做去水印更优雅的做法是用crop直接裁掉水印区域比如去掉底部时间条ffmpeg -i input.mp4 -vf cropin_w:in_h-100:0:0 output.mp4这是把画面底部100像素高的区域整个裁掉效果干净利落没有任何涂抹感。7. 我自己攒的一套水印分辨率工作流最后分享一个我长期在生产环境里用的脚本框架本质上是把分辨率检测、缩放、加水印三个环节串成了一个流程。这个脚本做的事情是读取输入视频的真实分辨率检查是否超过目标尺寸超标就缩放然后把水印加上最后统一输出。#!/bin/bash # usage: process_video.sh input.mp4 output.mp4 input$1 output$2 width$(ffprobe -v error -select_streams v:0 -show_entries streamwidth -of csvp0 $input) height$(ffprobe -v error -select_streams v:0 -show_entries streamheight -of csvp0 $input) echo Source: ${width}x${height} # 超过1920宽就按比例缩 if [ $width -gt 1920 ]; then VF-vf scale1920:-2 else VF fi # 加右下角图片水印如果VF已有内容就追加 VF$VF -i logo.png -filter_complex overlayW-w-20:H-h-20 ffmpeg -i $input $VF -c:a copy $output这里有个容易忽略的细节scale1920:-2中的-2和-1略有区别-1可能生成奇数宽高某些编码器不兼容-2保证是偶数所以限制只能2的倍数且正负号表示保持宽高比时偏向放大/缩小。编码时高度必须是偶数不然硬编会报错这是个很小但很真实的坑——我在第一次接触时完全没想到。这套流程我用了很久实际效果在有水印的情况下1080p视频用-crf 23输出码率会涨8%到15%这是水印内容增加细节导致的正常现象。如果你是给平台批量传视频建议统一CRF别锁死码率。在水印尺寸上我的经验是水印宽度一般控制在视频宽度的1/4以内。比如1920宽的1080p视频水印图片宽度在300到480像素之间观感最合适再大就喧宾夺主了。文字水印的字体大小也类似1080p下fontsize36是基础档位封面或推广视频可以放到48到64。最后再分享一个小技巧加水印时如果视频很长几十GB的那种建议先截取10秒片段测试命令确认水印位置、大小、透明度都满意了再跑全片。截取命令ffmpeg -i input.mp4 -t 10 -c copy sample.mp4这10秒钟的等待能帮你省下几个小时的重复转码我在处理批量素材时永远先跑这个流程。
返回列表