用AVFilter加文字水印需正确配置drawtext滤镜:指定fontfile路径(如Linux用绝对路径/usr/share/fonts/.../DejaVuSans.ttf,Windows需转义)、设置text/x/y/fontsize/fontcolor等参数;多滤镜串联时注意格式兼容与时间基对齐,C++ API中须显式avfilter_link并统一time_base,避免花屏或初始化失败。

如何用 AVFilter 给视频加文字水印(drawtext)
直接用 drawtext 滤镜是最常用、最轻量的方式,不需要预渲染图片或额外帧处理。它在滤镜链里实时绘制,支持字体、位置、颜色、透明度等控制,但依赖系统有可用字体文件。
常见错误是路径写错或字体不被识别,比如在 Linux 上漏掉 /usr/share/fonts/truetype/dejavu/DejaVuSans.ttf 这类绝对路径;Windows 上若用相对路径,FFmpeg 往往找不到,必须用双反斜杠或正斜杠转义。
-
drawtext=fontfile='/path/to/font.ttf':text='Hello':x=(w-tw)/2:y=h-th-10:fontsize=24:fontcolor=white@0.8—— 居中底部文字,带半透明白色 - 如果报错
Cannot find a valid font for the drawtext filter,先用fc-list(Linux/macOS)或检查 Windows 的fonts目录确认字体存在 - 中文需确保字体含对应字形,
text参数要 UTF-8 编码;Windows 命令行默认 GBK,建议改用 PowerShell 或把命令写进 .txt 文件再用-filter_complex_script - 动态文本(如时间戳)可用
text='%{localtime:%H\\:%M\\:%S}',注意反斜杠要四重转义
怎么把 drawtext 和其它滤镜串成一条 AVFilter 链
单个滤镜好写,多个串联时容易出错的不是语法,而是数据流顺序和格式兼容性——比如 scale 改变分辨率后,drawtext 的 x/y 坐标就得按新尺寸算,否则文字跑偏。
典型链式结构是:输入 → 缩放/旋转 → 文字水印 → 其它效果(如模糊、色调)→ 输出。中间每步输出必须是下一滤镜能接受的像素格式(如 yuv420p),否则 FFmpeg 会静默插入 format 自动转换,但可能拖慢速度或引入色度抽样偏差。
- 完整命令示例:
-vf "scale=1280:720,drawtext=fontfile=...:x=(w-tw)/2:y=h-th-20,format=yuv420p" - 避免隐式转换:显式加
format=yuv420p在链尾,尤其当输出格式指定为-pix_fmt yuv420p时,两者一致可省去一次重采样 - 滤镜间用英文逗号分隔,整个字符串用双引号包裹;若命令中含空格或特殊字符(如
:),Shell 下需额外转义,推荐把复杂滤镜写进文件,用-filter_complex_script
如何在 C++ 中用 libavfilter 动态构建 drawtext 滤镜图
命令行方便调试,但生产环境要用 C++ API 手动连接滤镜上下文,关键在于:不能只调 avfilter_graph_create_filter,还必须用 avfilter_link 显式建立输入/输出端口连接,并确保所有滤镜的 AVBufferRef* 时间基、宽高、格式一致。
最容易忽略的是 drawtext 滤镜的 time_base 必须与主流(通常是解码器的 time_base)对齐,否则文字闪烁或卡顿。它内部用 PTS 计算动画帧,错配会导致时间戳跳变。
- 创建
drawtext实例后,必须调av_opt_set设置fontfile、text等字符串选项,且每个av_opt_set调用后要avfilter_init_str触发初始化 - 连接时,
drawtext的输入必须接前级滤镜(如buffer)的输出,输出必须连到下一级(如buffersink)的输入;漏掉任一avfilter_link,avfilter_graph_config会失败并返回负值 - 动态更新文字(如逐帧改内容)不能靠反复调
av_opt_set,得在filter_frame回调里用av_dict_set修改私有上下文字段,再触发重绘逻辑——这需要自己 patch 或用较新 FFmpeg(>=5.1)的reinit支持
为什么动态滤镜(如 zoompan + drawtext)容易花屏或不同步
不是滤镜本身有问题,而是 zoompan 这类运动滤镜会改变帧的时间戳和持续时间,而 drawtext 默认按输入帧 PTS 渲染。两者节奏不一致,就会出现文字“滞后”或“跳帧”。
根本解法是统一时间基,并让 drawtext 的动画逻辑绑定到缩放滤镜输出的时间线上。例如 zoompan 输出帧的 pts 已被重映射,此时 drawtext 若仍读原始 PTS,坐标和透明度动画就全乱了。
- 强制同步:在滤镜链中把
zoompan放在drawtext前,并确保drawtext的reload设为 1,使其每帧重新解析表达式 - 避免混用不同时间基的输入:不要把解码器原始
AVFrame和zoompan输出帧混进同一滤镜图;所有输入必须经buffer滤镜注入,且buffer的time_base必须与zoompan输出一致 - 调试技巧:加
showinfo滤镜查看每帧pts_time和duration_time,对比drawtext前后是否突变
动态滤镜组合真正难的不是写法,而是时间线对齐和内存生命周期管理——AVFilterContext、AVFilterGraph、AVFrame 三者释放顺序稍错,就可能 crash 或残留未释放的字体缓存。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











