ffmpeg-watermark-guide

FFmpeg 视频水印完全指南:文字水印、图片水印、动态时间戳与阿里云数字水印 API

目录

  1. 概述
  2. FFmpeg 文字水印(drawtext)
  3. FFmpeg 图片水印(overlay)
  4. FFmpeg 动态时间戳
  5. FFmpeg 高级水印技巧
  6. 阿里云 MPS 数字水印 API
  7. 方案对比与选型建议

概述

视频水印是内容保护与品牌宣传的基础能力。本文覆盖两大场景:

场景 技术方案 适用情况
可见水印(明水印) FFmpeg drawtext / overlay 滤镜 品牌 Logo、版权声明、时间戳
不可见水印(盲水印/数字水印) 阿里云 MPS API 版权保护、盗版溯源、隐秘追踪

一、FFmpeg 文字水印(drawtext)

1.1 前置条件

drawtext 滤镜依赖以下库,编译 FFmpeg 时需启用:

  • libfreetype —— 字体渲染引擎
  • libharfbuzz —— 文本整形布局
  • libfontconfig —— 字体匹配与配置
  • libfribidi —— 双向文本(阿拉伯语/希伯来语)

验证是否支持:

ffmpeg -version | findstr "freetype"
# Linux/macOS: ffmpeg -version | grep freetype

1.2 基本语法

ffmpeg -i input.mp4 -vf "drawtext=参数1=值1:参数2=值2:..." output.mp4

1.3 核心参数表

参数 类型 说明 示例
text string 水印文字内容(UTF-8) text='Hello World'
textfile string 从文件读取文字内容 textfile='watermark.txt'
fontfile string 字体文件绝对路径(中文必须指定 fontfile='C\:/Windows/Fonts/simhei.ttf'
fontsize int 字体大小(像素) fontsize=24
fontcolor string 字体颜色(名称或十六进制) fontcolor=white / fontcolor=#FFFFFF
x / y int/expr 水印左上角坐标 x=10:y=10
alpha float 透明度,0.0 ~ 1.0 alpha=0.5
box int 是否绘制背景框(0/1) box=1
boxcolor string 背景框颜色,支持透明度 boxcolor=black@0.5
boxborderw int 背景框边框宽度 boxborderw=2
borderw int 文字描边宽度 borderw=2
bordercolor string 文字描边颜色 bordercolor=black
shadowcolor string 阴影颜色 shadowcolor=black@0.5
shadowx / shadowy int 阴影偏移量 shadowx=2:shadowy=2
enable expr 时间条件表达式,控制显示时段 enable='between(t,5,35)'

1.4 内置变量(用于 x / y / text 表达式)

变量 说明
w / h 当前视频帧的宽度 / 高度
tw / th 文字水印的宽度 / 高度
n 当前帧编号(从 0 开始)
t 当前时间戳(秒)
text_w / text_h 等同于 tw / th

1.5 示例:Windows 下添加中文水印

ffmpeg -i input.mp4 -vf ^
"drawtext=fontfile='C\:/Windows/Fonts/simhei.ttf':^
 text='版权所有 (C) 2026 MyCompany':^
 x=w-tw-10:y=h-th-10:^
 fontsize=24:fontcolor=white:^
 box=1:boxcolor=black@0.4:boxborderw=8:^
 borderw=1:bordercolor=white" ^
-c:v libx264 -crf 23 -c:a copy output.mp4

注意: Windows CMD 中使用 ^ 换行,: 前需要 \: 转义;PowerShell 中使用 ` 换行。路径分隔符建议使用正斜杠 /

1.6 分段显示水印

# 仅在第 5 秒到第 35 秒之间显示水印
ffmpeg -i input.mp4 -vf "drawtext=text='Preview':fontfile='C\:/Windows/Fonts/arial.ttf':x=10:y=10:fontsize=48:fontcolor=yellow:enable='between(t,5,35)'" output.mp4

# 每隔 3 秒显示 1 秒(闪烁效果)
ffmpeg -re -i input.mp4 -vf "drawtext=text='REC':fontfile='C\:/Windows/Fonts/arial.ttf':x=10:y=10:fontsize=36:fontcolor=red:enable='lt(mod(t,3),1)'" output.mp4

二、FFmpeg 图片水印(overlay)

2.1 基本语法

ffmpeg -i input.mp4 -i logo.png -filter_complex "overlay=x:y" output.mp4

overlay 滤镜将第二个输入(水印图片)叠加到第一个输入(视频)之上。

2.2 位置参数

overlay 的内置变量:

变量 含义
main_w / W 主视频宽度
main_h / H 主视频高度
overlay_w / w 水印图片宽度
overlay_h / h 水印图片高度
位置 x 表达式 y 表达式
左上角 0 0
右上角 main_w-overlay_w 0
左下角 0 main_h-overlay_h
右下角 main_w-overlay_w main_h-overlay_h
居中 (main_w-overlay_w)/2 (main_h-overlay_h)/2

建议留 10~20px 边距,示例:

# 右下角,留 20px 边距
ffmpeg -i video.mp4 -i logo.png -filter_complex "overlay=W-w-20:H-h-20" output.mp4

2.3 水印缩放

缩放必须在 overlay 之前完成。使用 -filter_complex 链式组合:

固定像素缩放

ffmpeg -i input.mp4 -i logo.png ^
  -filter_complex "[1:v]scale=180:-1[wm];[0:v][wm]overlay=W-w-20:H-h-20" ^
  -c:v libx264 -crf 23 -c:a copy output.mp4
  • scale=180:-1:宽度 180px,高度按比例自动计算
  • [1:v] 指第二个输入的视轨(水印图片),[0:v] 指第一个输入的视轨(视频)
  • [wm] 是自定义标签,传递缩放后的水印流给 overlay

相对主视频百分比缩放(scale2ref)

ffmpeg -i input.mp4 -i logo.png ^
  -filter_complex "[1:v][0:v]scale2ref=w=iw*0.15:h=ow/mdar[wm][vid];^
                   [vid][wm]overlay=W-w-20:H-h-20" ^
  -c:v libx264 -crf 23 -c:a copy output.mp4

scale2ref 将水印 [1:v] 相对于参考流 [0:v](主视频)进行缩放,iw*0.15 表示水印宽度 = 视频宽度的 15%。

2.4 透明度控制

前提:水印图片必须是带 Alpha 通道的 PNG。

# 方法1:colorchannelmixer 调整 Alpha 通道
ffmpeg -i input.mp4 -i logo.png ^
  -filter_complex "[1:v]colorchannelmixer=aa=0.5[wm];[0:v][wm]overlay=W-w-20:H-h-20" ^
  output.mp4

aa=0.5 将 Alpha 通道乘以 0.5(50% 透明度)。

2.5 分段显示

# 5 秒后出现,持续 30 秒(5~35 秒)
ffmpeg -i input.mp4 -i logo.png ^
  -filter_complex "[1:v]scale=150:-1[wm];^
                   [0:v][wm]overlay=W-w-10:10:enable='between(t,5,35)'" ^
  output.mp4

2.6 多个水印叠加

ffmpeg -i input.mp4 -i logo_top_left.png -i logo_bottom_right.png ^
  -filter_complex "[1:v]scale=80:-1[a];^
                   [2:v]scale=60:-1[b];^
                   [0:v][a]overlay=10:10[tmp];^
                   [tmp][b]overlay=W-w-10:H-h-10[out]" ^
  -map "[out]" -map 0:a -c:v libx264 -y output.mp4

提示: 使用 filter_complex 后,必须通过 -map 显式指定输出流。-map "[out]" 映射视频流,-map 0:a 映射原始音频流。

2.7 overlay 参数速查

参数 说明 示例
x / y 左上角坐标(支持表达式) W-w-20
enable 时间条件 'between(t,5,35)'
format 输出像素格式 yuv420
alpha 叠加透明度(0~1) 0.5
shortest 1=随短流结束 shortest=1
eof_action 前景流结束行为:pass/repeat/endall eof_action=pass

三、FFmpeg 动态时间戳

3.1 时间格式化占位符

占位符 含义
%Y 四位年份
%m 月份(01-12)
%d 日期(01-31)
%H 小时(00-23)
%M 分钟(00-59)
%S 秒(00-59)
%a 星期缩写
%b 月份缩写

3.2 当前系统时间(localtime)

ffmpeg -i input.mp4 -vf ^
"drawtext=fontfile='C\:/Windows/Fonts/simhei.ttf':^
 text='录制时间: %{localtime\:%Y-%m-%d %H\:%M\:%S}':^
 x=10:y=10:fontsize=24:fontcolor=white:^
 box=1:boxcolor=black@0.5:boxborderw=6" ^
-c:v libx264 -crf 23 -c:a copy output.mp4

转义说明: \: 用于转义 drawtext 内部的冒号,避免与滤镜参数分隔符冲突。

3.3 UTC 时间(gmtime)

ffmpeg -i input.mp4 -vf ^
"drawtext=text='UTC: %{gmtime\:%Y-%m-%d %H\:%M\:%S}':^
 x=10:y=10:fontsize=24:fontcolor=white" ^
output.mp4

3.4 双时间戳(UTC + 本地)

ffmpeg -i input.mp4 -vf ^
"drawtext=fontfile='C\:/Windows/Fonts/simhei.ttf':^
 text='%{gmtime\:%Y-%m-%d %H\:%M\:%S} UTC | %{localtime\:%Y-%m-%d %H\:%M\:%S} CST':^
 x=10:y=10:fontsize=20:fontcolor=white:^
 box=1:boxcolor=black@0.5" ^
output.mp4

3.5 视频内部时间戳(pts)

pts 函数从视频的显示时间戳(Presentation TimeStamp)渲染时间,适合为监控录像或回放叠加时间:

ffmpeg -i input.mp4 -vf ^
"drawtext=text='播放进度: %{pts\:gmtime\:0\:%H\:%M\:%S}':^
 fontfile='C\:/Windows/Fonts/consola.ttf':^
 x=10:y=10:fontsize=30:fontcolor=white" ^
output.mp4

pts:gmtime:0 的格式含义:pts 获取帧时间戳,gmtime 将其解释为 UTC 秒数,0 表示从第 0 秒开始偏移。

3.6 实时播放时叠加动态时间

通过 ffplay 预览带动态时间戳的视频:

ffplay -vf ^
"drawtext=fontfile='C\:/Windows/Fonts/simhei.ttf':^
 text='当前时间: %{localtime\:%Y-%m-%d %H\:%M\:%S}':^
 x=w-tw-10:y=10:fontsize=24:fontcolor=white:^
 shadowcolor=black:shadowx=2:shadowy=2" ^
input.mp4

四、FFmpeg 高级水印技巧

4.1 文字水印 + 图片水印叠加

ffmpeg -i input.mp4 -i logo.png ^
  -filter_complex "[0:v]drawtext=text='Copyright 2026':fontfile='C\:/Windows/Fonts/simhei.ttf':x=10:y=10:fontsize=20:fontcolor=white[text];^
                   [text][1:v]overlay=W-w-10:H-h-10[out]" ^
  -map "[out]" -map 0:a -c:v libx264 -y output.mp4

4.2 跑马灯/滚动水印

ffmpeg -re -i input.mp4 -vf ^
"drawtext=text='滚动通知: 这是一条重要的水印信息':^
 fontfile='C\:/Windows/Fonts/simhei.ttf':^
 x=mod(200*t\,w):y=h*0.05:^
 fontsize=28:fontcolor=yellow:^
 box=1:boxcolor=black@0.6" ^
output.mp4

x=mod(200*t,w) 使文字从右向左匀速滚动,mod 取模实现循环。

4.3 正弦浮动水印

ffmpeg -re -i input.mp4 -vf ^
"drawtext=text='Float':^
 x=w/2-tw/2:y=abs(sin(t))*h*0.5:^
 fontsize=36:fontcolor=green" ^
output.mp4

y=abs(sin(t))*h*0.5 使水印在垂直方向按正弦波上下浮动。

4.4 批处理脚本(PowerShell)

# 对目录下所有 mp4 文件批量加水印
$files = Get-ChildItem "E:\videos\" -Filter "*.mp4"
foreach ($f in $files) {
    $out = "E:\videos\watermarked\" + $f.BaseName + "_wm.mp4"
    ffmpeg -i $f.FullName -vf "drawtext=text='MyBrand':fontfile='C\:/Windows/Fonts/simhei.ttf':x=w-tw-10:y=h-th-10:fontsize=24:fontcolor=white@0.7" -c:v libx264 -crf 23 -c:a copy $out
    Write-Host "Done: $out"
}

4.5 常见问题排查

问题 原因 解决方案
中文显示乱码/方框 未指定中文字体 使用 fontfile 指定中文字体(如 simhei.ttf、simsun.ttc、NotoSansCJK)
fontcolor 无效 编译时未启用 libfreetype 重新编译 FFmpeg 或下载预编译全功能版本
时间不更新/静止 未使用 -re 参数 实时场景添加 -re;离线转码时 localtime 为转码时刻的时间
: 导致解析错误 冒号与滤镜分隔符冲突 使用 \: 转义
overlay 无输出 忘记 -map 映射 filter_complex 后需 -map "[out]" -map 0:a
PNG 水印有白底 水印图片无 Alpha 通道 使用带透明通道的 PNG 图片

五、阿里云 MPS 数字水印 API

本节内容基于阿里云官方文档:数字水印 - 媒体处理 MPS

5.1 数字水印概述

阿里云 MPS 提供两种数字水印类型:

类型 API 说明 适用场景
版权水印 SubmitCopyrightJob 将版权信息(文字)嵌入视频,肉眼不可见 版权声明、内容确权
溯源水印 SubmitTraceAbJob 生成 A/B 两个版本,分发给不同用户,用于追踪泄露源 防盗版、泄密溯源
溯源水印(M3U8) SubmitTraceM3u8Job 针对 M3U8 格式视频的溯源水印 HLS 流媒体溯源

配套查询/提取接口:

操作 API
查询版权水印作业 QueryCopyrightJob
提取版权水印 SubmitCopyrightExtractJob
查询版权水印提取结果 QueryCopyrightExtractJob
查询溯源 AB 作业 QueryTraceAbJob
提取溯源码 SubmitTraceExtractJob
查询溯源提取作业 QueryTraceExtractJob

VOD(视频点播)产品线也提供独立的数字水印接口:

操作 API
提取数字水印 SubmitDigitalWatermarkExtractJob
查询提取结果 GetDigitalWatermarkExtractResult

5.2 公共参数

所有 MPS API 均需携带以下公共请求参数:

参数 必填 说明
Format 返回格式,JSON 或 XML(默认 XML)
Version API 版本,固定 2014-06-18
AccessKeyId 阿里云 RAM 用户的 AccessKey ID
Signature 使用 HMAC-SHA1 算法生成的请求签名
SignatureMethod 签名算法,固定 HMAC-SHA1
SignatureVersion 签名版本,固定 1.0
SignatureNonce 唯一随机数(GUID),防重放攻击
Timestamp UTC 时间戳,格式 YYYY-MM-DDThh:mm:ssZ
Action 具体 API 接口名称(如 SubmitCopyrightJob

地域限制: 数字水印接口目前仅支持 华东2(上海)华北2(北京) 地域。

5.3 SubmitCopyrightJob —— 提交版权水印嵌入作业

接口信息

项目
Action SubmitCopyrightJob
API 版本 2014-06-18
调用方式 异步(提交后轮询或回调获取结果)
QPS 限制 200 次/秒
最小视频时长 默认 ≥ 3 分钟;短视频需传 Params={"algoType":"v2"}

请求参数

参数 类型 必填 描述
Input String 输入视频 OSS 地址,JSON 格式。与 Url 二选一
Output String 输出视频 OSS 地址,JSON 格式
Message String 待嵌入的版权信息文本
Description String 水印描述备注
StartTime Long 水印嵌入开始时间(秒),默认 0
TotalTime Long 水印嵌入总时长(秒),默认整个视频
Level Int 嵌入通道:0=U 通道,1=UV 通道,2=YUV 全通道(默认)
CallBack String 任务完成回调 URL(仅 HTTP)
Url String 公网可访问的视频 URL,优先级高于 Input
UserData String 用户自定义数据(≤ 1024 字节)
Params String 扩展参数,如 {"algoType":"v2"} 支持短视频

Input / Output JSON 格式

// Input
{
    "Bucket": "my-video-bucket",
    "Location": "oss-cn-shanghai",
    "Object": "input/video.mp4"
}

// Output
{
    "Bucket": "my-video-bucket",
    "Location": "oss-cn-shanghai",
    "Object": "output/watermarked_video.mp4"
}

调用示例(Python SDK)

from aliyunsdkmts.request.v20140618.SubmitCopyrightJobRequest import SubmitCopyrightJobRequest
from alibabacloud_mts20140618.client import Client as MtsClient
from alibabacloud_mts20140618 import models as mts_models

# 初始化客户端
client = MtsClient(
    config=mts_models.Config(
        access_key_id='<Your-AccessKeyId>',
        access_key_secret='<Your-AccessKeySecret>',
        region_id='cn-shanghai'
    )
)

req = SubmitCopyrightJobRequest()
req.set_accept_format('json')
req.set_Input('{"Bucket":"my-bucket","Location":"oss-cn-shanghai","Object":"input/video.mp4"}')
req.set_Output('{"Bucket":"my-bucket","Location":"oss-cn-shanghai","Object":"output/wm_video.mp4"}')
req.set_Message('(C) MyCompany 2026 All Rights Reserved')
req.set_Level(2)             # YUV 全通道嵌入
req.set_Description('版权保护水印')
req.set_Params('{"algoType":"v2"}')  # 短视频模式

resp = client.submit_copyright_job(req)
print(f"JobId: {resp.body.job_id}")
print(f"RequestId: {resp.body.request_id}")

响应参数

参数 类型 说明
JobId String 水印嵌入作业 ID,用于后续查询
RequestId String 请求唯一标识

查询作业结果

from aliyunsdkmts.request.v20140618.QueryCopyrightJobRequest import QueryCopyrightJobRequest

req = QueryCopyrightJobRequest()
req.set_accept_format('json')
req.set_JobId('<Job-Id>')

resp = client.query_copyright_job(req)
# resp.body 包含作业状态(Submitted / Transcoding / Success / Fail)、输出文件信息等

5.4 SubmitTraceAbJob —— 提交溯源水印 AB 流作业

接口信息

项目
Action SubmitTraceAbJob
API 版本 2014-06-18
调用方式 异步
QPS 限制 100 次/秒
最小视频时长 ≥ 3 分钟

请求参数

参数 类型 必填 描述
Input String 输入视频 OSS 地址,JSON 格式
Output String 输出 OSS 地址,需指定 Dir(目录)而非 Object(文件)
StartTime Long 嵌入开始时间(秒),默认 0
TotalTime Long 嵌入总时长(秒)
Level Int 嵌入通道:0U / 1UV / 2YUV(默认)
CallBack String 回调 URL(仅 HTTP)
Url String 公网视频 URL,优先级高于 Input
CipherBase64ed String Base64 编码的加密密钥
UserData String 用户自定义数据(≤ 1024 字节)

Output 格式(注意:Dir 而非 Object)

{
    "Bucket": "my-video-bucket",
    "Location": "oss-cn-shanghai",
    "Dir": "trace_output/"
}

关键区别: SubmitCopyrightJob 的 Output 指定具体的 Object(输出文件路径),而 SubmitTraceAbJob 的 Output 指定 Dir(输出目录),因为溯源水印会生成 两个文件(A 版本和 B 版本)。

调用示例(Python SDK)

from aliyunsdkmts.request.v20140618.SubmitTraceAbJobRequest import SubmitTraceAbJobRequest

req = SubmitTraceAbJobRequest()
req.set_accept_format('json')
req.set_Input('{"Bucket":"my-bucket","Location":"oss-cn-shanghai","Object":"input/movie.mp4"}')
req.set_Output('{"Bucket":"my-bucket","Location":"oss-cn-shanghai","Dir":"trace/"}')
req.set_Level(2)
req.set_UserData('user-12345')  # 可嵌入用户标识用于溯源

resp = client.submit_trace_ab_job(req)
print(f"JobId: {resp.body.job_id}")

5.5 版权水印 vs 溯源水印对比

维度 SubmitCopyrightJob SubmitTraceAbJob
用途 嵌入版权归属声明 生成不同版本追踪泄露源
水印信息 Message(明文版权文字) 加密溯源信息(可选 CipherBase64ed 密钥)
输出文件数 1 个 2 个(A 版本 + B 版本)
Output 格式 指定 Object(文件) 指定 Dir(目录)
短视频支持 支持(Params={"algoType":"v2"} 不支持,需 ≥ 3 分钟
信息描述 支持 Description 不支持
加密密钥 不支持 支持 CipherBase64ed

5.6 VOD 产品线数字水印 API

如果你使用的是 VOD(视频点播)产品线,提取水印时使用以下接口:

SubmitDigitalWatermarkExtractJob

项目
Action SubmitDigitalWatermarkExtractJob
API 版本 2017-03-21
Endpoint vod.aliyuncs.com
QPS 限制 30 次/秒
参数 类型 必填 说明
MediaId String 待提取水印的视频 ID(视频时长需 > 6 分钟
ExtractType String TraceMark(溯源水印)或 CopyrightMark(版权水印)

调用示例

// 请求
POST /?Action=SubmitDigitalWatermarkExtractJob
&MediaId=0222e203cf80f9c22870a4d2c****
&ExtractType=TraceMark
&Format=JSON
&Version=2017-03-21
...

// 响应
{
    "JobId": "ad90a501b1b9472374ad005046****",
    "RequestId": "04F0F334-1335-436C-****-6C044FE73368"
}

GetDigitalWatermarkExtractResult

参数 类型 必填 说明
MediaId String 视频 ID
ExtractType String 水印类型
JobId String 指定作业 ID(不传则返回该视频所有历史提取结果)

5.7 数字水印最佳实践

  1. 版权保护场景:使用 SubmitCopyrightJobMessage 中嵌入完整的版权声明文字,Level=2(YUV 全通道)以获得最强鲁棒性。
  2. 盗版溯源场景:使用 SubmitTraceAbJob,为每个下游用户分发不同的版本(A/B),当发现盗版时通过提取水印定位泄露源。
  3. 短视频处理:务必在 Params 中指定 {"algoType":"v2"},否则 < 3 分钟的视频将处理失败。
  4. 结果获取:数字水印为异步接口,建议配置 CallBack 回调地址接收任务完成通知,或使用查询接口轮询。
  5. 安全性:溯源水印场景建议使用 CipherBase64ed 加密密钥,防止溯源信息被反向破解。

六、方案对比与选型建议

维度 FFmpeg drawtext/overlay 阿里云 MPS 数字水印
水印可见性 可见(明水印) 不可见(盲水印)
抗攻击性 弱 —— 裁剪/遮挡即可去除 强 —— YUV 通道嵌入,裁剪/压缩后仍可提取
部署方式 本地命令行,无外部依赖 云 API 调用,需 OSS 存储 + RAM 授权
成本 免费(开源) 按处理时长计费
版权声明效果 直观可见,兼有品牌展示作用 隐蔽,不对观看体验造成干扰
盗版溯源能力 无 —— 水印可被轻易去除 强 —— A/B 版本分发 + 加密溯源
实时处理 支持(ffplay 预览) 仅支持异步作业
适用场景 品牌 Logo、角标、时间戳、公开视频 付费内容保护、内部机密视频分发、版权诉讼举证

选型建议

  • 对外宣传视频:使用 FFmpeg 明水印 —— 成本低、效果直观、且能提升品牌曝光。
  • 付费/内部分发视频:使用阿里云 MPS 数字水印 —— 每个用户收到隐蔽的唯一版本,泄露后可精准溯源。
  • 叠加使用:两者并不互斥,可在同一视频中同时使用明水印(品牌展示)和盲水印(泄露溯源),实现双重保护。

参考链接

posted @ 2026-05-05 15:59  20232408-李易骋  阅读(68)  评论(0)    收藏  举报