1.前言
使用ffmpeg添加和去除水印是常见的功能,两者的参数都跟在-vf选项后,本文主要介绍水印去除功能。
2.水印去除功能简介
常见的ffmpeg水印去除功能的命令行如下
ffmpeg -i xxx.mp4 -vf "delogo=x=1:y=1:w=800:h=100" -c:a copy delogo.mp4
意思是从左上角开始去除水印,水印的范围是长度为800,宽度为100的矩形。一个720P(1280x720)的视频,去除水印的效果如下
但是这样将水印坐标固定的写法会导致一些问题,比如当视频的分辨率更低时,很容易出现去除水印区域的长度非法的问题。
比如我们调整去除水印区域的长度超过视频画面的长度,比如1800对于1080P(1920x1080)的视频是合法的,但是对于720P(1280x720)的视频,就不合法了。
ffmpeg -i xxx.mp4 -vf "delogo=x=1:y=1:w=1800:h=100" -c:a copy delogo.mp4
所以我们很自然的会想到参考添加水印时采用的方式,使用相对长度和位置,以保证不会使水印去除区域过大。添加水印时,视频画面的长度记为W,视频画面的宽度为H,如下所示
ffmpeg -i xxx.mp4 -vf "drawtext=text='asdf':fontcolor=white:fontfile=jdsongti.ttf:fontsize=48:x=72:y=H-th-72" -c:a copy logo.mp4
但是如果我们直接套用到去除水印的功能里的话,就会出错
ffmpeg -i xxx.mp4 -vf "delogo=x=1:y=1:w=W/2:h=H/10" -c:a copy delogo.mp4
那么,这个问题应该如何解决呢?如果要修改ffmpeg,应该如何修改呢?
3.修改ffmpeg代码
由于添加水印的部分已经具备了相对长度,所以我们可以参考水印添加的代码,位于
libavfilter/vf_drawtext.c
,而我们需要修改的代码位于libavfilter/vf_delogo.c
,改动如下:首先增加变量名称
main_h
和main_w
来匹配ffmpeg命令行参数中的字符(如x=main_h/2)static const char * const var_names[] = {
"main_h", ///< height of the input video
"main_w", ///< width of the input video
"x",
"y",
"w",
"h",
"n", ///< number of frame
"t", ///< timestamp expressed in seconds
NULL
};
其次添加变量
VAR_MAIN_H
和VAR_MAIN_W
来记录视频的高和宽enum var_name {
VAR_MAIN_H,
VAR_MAIN_W,
VAR_X,
VAR_Y,
VAR_W,
VAR_H,
VAR_N,
VAR_T,
VAR_VARS_NB
};
最后在
config_input
处理每一帧时,计算出水印去除区域的坐标。config_input
保存了视频的长和宽,并解析表达式(如x=main_h/2
),得到计算后的值。static int config_input(AVFilterLink *inlink)
{
DelogoContext *s = inlink->dst->priv;
s->var_values[VAR_MAIN_W] = inlink->w;
s->var_values[VAR_MAIN_H] = inlink->h;
s->x = av_expr_eval(s->x_pexpr, s->var_values, s);
s->y = av_expr_eval(s->y_pexpr, s->var_values, s);
s->w = av_expr_eval(s->w_pexpr, s->var_values, s);
s->h = av_expr_eval(s->h_pexpr, s->var_values, s);
// printf("s->x:%d s->y:%d s->w:%d s->h:%d s->band:%d \n", s->x, s->y, s->w, s->y, s->band);
/* Check whether the logo area fits in the frame */
if (s->x + (s->band - 1) < 0 || s->x + s->w - (s->band*2 - 2) > inlink->w ||
s->y + (s->band - 1) < 0 || s->y + s->h - (s->band*2 - 2) > inlink->h) {
av_log(s, AV_LOG_ERROR, "Logo area is outside of the frame.\n");
return AVERROR(EINVAL);
}
return 0;
}
4.效果
重新编译修改后的ffmpeg,新生成的ffmpeg可以使用以下参数进行水印去除。并且随着视频分辨率的变化,去除水印的位置也会相对发生变化,做到了自适应。
ffmpeg -i xxx.mp4 -vf "delogo=x=1:y=1:w=main_w/2:h=main_h/10" -c:a copy delogo.mp4
可以看到水印去除的区域,就位于整个视频画面长度的一半。
5.总结
ffmpeg源码里有很多值得学习的地方,新功能可以基于已有的功能进行移植和裁剪来实现。