如何利用FFmpeg给视频添加水印？

引言

最近工作中遇到一个需求，需要给大量的视频批量添加水印，试了试网上现有的各种添加水印的程序，有的需要付费，有的又不支持脚本。其实，众所周知，大部分的商业视频处理程序都或多或少利用到了 FFmpeg 来帮助实现功能，因此我决定直接使用 FFmpeg 来给视频添加水印，这样子也可以很方便地书写 bash 脚本来批处理。顺带一提，某些利用 FFmpeg 的商业程序并不遵循其开源协议，真是可恶啊！

下载与安装

macOS/Linux 直接使用包管理工具安装就可以
Windows 则在 FFmepg 下载 处下载就可以了
具体就不再赘述了

使用

FFmpeg 使用起来非常简单
-i 指定输入的文件
末尾指定输出的文件
ffmpeg -i input.mp4 output.avi
即意味着把 mp4 格式的视频转换为 avi 格式

还可以通过-c:v 参数来指定视频编码器的格式，默认为 h264 编码

这只是最常用的视频格式转换，但我们需要的是添加水印
FFmepg 提供了一个参数选项 -fliter
被称作过滤器或滤波器，通过这个参数可以实现裁剪、旋转、改变图像分辨率、添加滤镜等功能

在 FFmpeg 中有两种类型的 fliter，一种是简单型的，一种是复杂性的，简单型只支持单输入单输出的情形，而复杂性则因为其不是简单的线性处理而支持多输入多输出。
通常而言，简单型的处理速度会比较快，其解码相关的帧处理后重新编码。

Copy
_________ ______________
| | | |
| decoded | | encoded data |
| frames |\ _ | packets |
|_________| \ /||______________|
\ __________ /
simple _\|| | / encoder
filtergraph | filtered |/
| frames |
|__________|

同时，过滤器之间也可以线性叠加，一个过滤器的输出成为另一个过滤器的输入从而形成一个过滤器链

-vf 和 -af 分别代表着视频和音频过滤器的参数

例如

Copy
ffmpeg -i input.mp4 -c:v libx264 -vf "transpose=2" output.mp4

则代表着将主视频轨逆时针旋转 90 度

通过-vf 参数之间添加逗号即可形成过滤器链

类似 transpose 的过滤器还有很多，在 FFmepg fliters doc 中可以看到大约仅视频过滤器就有近 300 种

添加水印

添加水印的种类我大致分为两类

文字水印

图片水印

而这两种水印添加时又要考虑水印添加的内容、位置、水印大小、透明度等

接下来我一个一个说明

文字水印

文字水印的实现方式是通过 drawtext 过滤器实现的

Drawtext doc

我介绍几个有用的参数

fontsize （字体大小）

fontfile （字体）

text （文字）

x （x 坐标）

y （y 坐标）

fontcolor （文字颜色）

alpha （透明度）

我准备用以下指令测试一下

Copy
ffmpeg -i input.mp4 -vf "drawtext=fontsize=100:fontfile=Arial.ttf:text='水印':x=20:y=20:fontcolor=red:alpha=0.5" output.mp4

效果

可以看到颜色、位置、字体、透明度都达到了自定义

图片水印

图片水印的实现方式是通过 movie 与 overlay 两个参数来实现

以下举一个例子

Copy
ffmpeg -i input.mp4 -vf "movie=watermark.jpg[wm];[in][wm]overlay=20:20[out]" output.mp4

效果

照片似乎有点大，我们该怎么处理呢？
这时候就要应用到前面的过滤器链来处理
我们利用 scale 来缩放

Copy
ffmpeg -i input.mp4 -vf "movie=watermark.jpg,scale=50:50[wm];[in][wm]overlay=20:20[out]" output.mp4

好像照片又会遮挡到画面，那么该怎么办呢？
那这个时候就让它变透明吧！
首先利用 format 把图像转换到含有 alpha 通道的像素格式，再通过指定混合来实现

Copy
ffmpeg -i input.mp4 -vf "movie=watermark.jpg,scale=200:200,format=yuva444p,colorchannelmixer=aa=0.5[wm];[in][wm]overlay=20:20[out]" output.mp4

不过话说回来，为什么不一开始就使用透明的 png 图片来合成呢？哈哈，那样貌似更方便一些