FFmpeg动效实践与探索(文字方框的同步展开及收缩)

FFmpeg动效实践与探索(文字方框的同步展开及收缩)

当你看到这样的动图时,你会想到这是用FFmpeg做出来的吗?

FFmpeg,作为一款强大的视频编辑处理工具,在基础的视频处理上十分可靠,在音视频编解码上大放异彩,那么它在动态效果上的应用怎么样呢,笔者为此做了一定的实践,踩了不少坑,现将FFmpeg文字方框动效实现的工作总结如下。

下文将按照实现最终动效的探索顺序一步一步展示。

1、文字淡入淡出

首先要实现的第一个动效是:文字淡入淡出
经过查询,可以用fade函数来控制文字的淡入淡出,用enable来控制时间。

话不多说,直接上案例代码:

ffmpeg -i test.mp4 -lavfi "drawtext=text='Summer Video':fontsize=40:box=1:x=w/2:y=h/2:enable='between(t,8,15)',fade=t=in:start_time=8:d=1:alpha=1,fade=t=out:start_time=14.5:d=1:alpha=1[fg];[0][fg]overlay=format=auto,format=yuv420p" -c:a copy output2.mp4

tips及小坑:

  • “-lavfi” 等价于 “-filter_complex”
  • -y无条件覆盖之前的输出
  • -c:a copy对音频可以不用编码所以直接复制比较快
  • fade函数的alpha必须等于1,否则会黑屏

2、向上向下擦除效果

要实现向上向下擦除效果,目前ffmpeg并没有特定的滤镜实现,因此只能曲线救国,通过控制x,y与时间戳t参数的计算公式的方式去实现。具体代码如下:

ffmpeg -i test.mp4 -lavfi "drawtext=text='Summer Video':fontsize=50:box=1:boxcolor=white:alpha=0.5:x=(w-tw)/2:y='if(lte(t,9),(h-th)/2+(t-9)*30,if(gte(t,14),(h-th)/2+(t-14)*30,(h-th)/2))':enable='between(t,8,15)',fade=t=in:start_time=8:d=1:alpha=1,fade=t=out:start_time=14:d=1:alpha=1[fg];[0][fg]overlay,format=yuv420p" -c:a copy output2.mp4

通过写x,y与t的表达式,可以人工去制造x,y的动态变化,同时从此例可以看到,ffmpeg的if语法以及lte,gte函数的使用。

3、背景框+文字

如果想要背景框和文字一起出现,那么可以用以下代码实现:

ffmpeg -i test.mp4 -filter_complex "drawbox=(iw-w)/2:(ih-h)/2:360:100:pink@0.5:t=fill:enable='between(t,8,15)',drawtext=text='Summer Video':fontsize=50:x=(w-tw)/2:y='if(lte(t,9),(h-th)/2+(t-9)*30,if(gte(t,14),(h-th)/2+(t-14)*30,(h-th)/2))':enable='between(t,8,15)',fade=t=in:start_time=8:d=1:alpha=1,fade=t=out:start_time=14:d=1:alpha=1[fg];[0][fg]overlay=format=auto,format=yuv420p" -c:a copy output2.mp4
  • T是填充度,表示填充的像素个数 t=fill表示填充
  • Drawbox中iw即为视频输入画面的宽度,所以中心位置应该为:
  • x=(iw-w)/2:y=(ih-h)/2:w=360:100(视频尺寸为1280*720)
  • 注意:单独使用drawtext时要记得在drawtext=后加上双引号“”,特别是要定义x和y的表达式时

4、方框扩张

关于方框的扩张,笔者尝试了好几种方案,也踩了好几种坑,现按照尝试顺序进行记录。

首先,我们如何做到方框扩张呢?最易理解的一种思路就是改变方框宽度及左顶点与时间的表达式。我们可以一方面控制方框的宽度w,令他随时间扩张,另一方面我们控制x参数,令x随时间向左移动,并使方框w扩张的速度为x向左移动速度的两倍。这样不就实现了吗?
因此咱们就按照这个思路去实现吧~go~

drawbox版本

首先说到方框扩张,第一时间想到了用drawbox滤镜去实现。然而查阅了很多资料发现,如果单独使用drawbox是无法实现的,因为它并不接受t参数(在drawbox滤镜中,t有特殊的含义,为thickness,指box的厚度)
因此以下代码并不能产生一个扩张的方框:

ffmpeg -i test.mp4 -filter_complex "drawbox=x=460+(8-t)*10:y=310:w=100+(t-8)*10*2:h=100:color=pink:t=fill:enable='between(t,8,15)',fade=t=in:start_time=8:d=1:alpha=1,fade=t=out:start_time=14:d=1:alpha=1[fg];[0][fg]overlay=format=auto,format=yuv420p" -c:a copy kuang.mp4

drawbox+scale版本

既然drawbox本身尺寸无法动态变化,那么用其他的filter可以做到吗?经过一番检索,查到了有scale这个滤镜,并开始了尝试:

ffmpeg -y -i test.mp4 -filter_complex "drawbox=iw/2:ih/2:20:20:blue@0.5:t=fill:enable='between(t,3,8)',scale=eval=frame:w=100+t*10:h=20[vb]; [0][vb]overlay=x=W/2:y=H/2" -t 16 a.mp4

而结果是令人失望的,由于这种写法相当于是绘图盒滤镜的输入所以这样覆盖层包含整个视频本身,所以无法显示独立的色块,显示的是源视频的缩影,因此并不符合动效要求。

color版本

那么如果不用drawbox,可以用其他的滤镜去代替drawbox么?当然有,这就是color!color是非常便捷的展示色块的滤镜,于是兴高采烈的写下了以下代码:

ffmpeg -y -i test.mp4 -filter_complex  "color=red@0.5:s=100*tx200[c];[0][c]overlay='if(lte(-1024+1500*t,0),-1024+1500*t,0)':100" -t 15 out.mp4

不过事实再一次令人绝望——color无法改变size!!不管你如何变化,它的size并不会跟着t改变。so sad。

color+scale版本

那么让我们试试color+scale吧
bingo!终于可以了!通过这俩结合的方式,我们实现了方框随时间扩张!

ffmpeg -y -i test.mp4 -filter_complex  "color=c=blue@0.5:size=500x300,scale=eval=frame:w=100+t*200:h=10[cl];[0][cl]overlay=x=W/2-100*t:y=H/2" -t 15 out.mp4

可是,你如果使用这段代码生产视频则会发现,color这个框从一开始就存在了。color模块无法控制出现的时间,始终有size
那么如何控制color出现呢?
答案就是:在overlay中增加控制时间的enable!

ffmpeg -y -i test.mp4 -filter_complex  "color=c=blue@0.5:size=10x10,scale=eval=frame:w='if(lte(t,3),0,if(lte(t,6),(t-3)*300,if(lte(t,10),(6-3)*300,if(lte(t,13),(6-3)*300-(t-10)*300,0))))':h=10,fade=t=in:start_time=2.5:d=0.5:alpha=1,fade=t=out:start_time=12.5:d=0.5:alpha=1[cl];[0][cl]overlay=x='if(lte(t,6),W/2-150*(t-3),if(gte(t,10),W/2-150*(6-3)+150*(t-10),W/2-150*(6-3)))':y=H/2:enable='between(t,3,13)'" -c:a copy -t 15 outting.mp4

注意:

  • 对W的限制一定要明确前后是0,不能超过enable的时间区间
  • Color最好有size参数,这样更稳定

5、方框扩张+文字

现在,我们整理以下刚刚的代码,将方框扩张和文字淡入淡出放在一起,就得到了以下一长串代码:

ffmpeg -y -i test.mp4 -filter_complex  "color=c=blue@0.5:size=10x10,scale=eval=frame:w='if(lte(t,3),0,if(lte(t,4),(t-3)*600,if(lte(t,12),(4-3)*600,if(lte(t,13),(4-3)*600-(t-12)*600,0))))':h=10,fade=t=in:start_time=3:d=0.5:alpha=1,fade=t=out:start_time=12.5:d=0.5:alpha=1[cl1];color=c=blue@0.5:size=10x10,scale=eval=frame:w='if(lte(t,3),0,if(lte(t,4),(t-3)*600,if(lte(t,12),(4-3)*600,if(lte(t,13),(4-3)*600-(t-12)*600,0))))':h=10,fade=t=in:start_time=3:d=0.5:alpha=1,fade=t=out:start_time=12.5:d=0.5:alpha=1[cl2];[0][cl1]overlay=x='if(lte(t,4),W/2-300*(t-3),if(gte(t,12),W/2-300*(4-3)+300*(t-12),W/2-300*(4-3)))':y=H/4:enable='between(t,3,13)'[bkg];[bkg][cl2]overlay=x='if(lte(t,4),W/2-300*(t-3),if(gte(t,12),W/2-300*(4-3)+300*(t-12),W/2-300*(4-3)))':y=3*H/4:enable='between(t,3,13)',drawtext=text='summer video':fontsize=50:x=(w-tw)/2:y='if(lte(t,4),(h-th)/2+(t-4)*30,if(gte(t,12),(h-th)/2+(t-12)*30,(h-th)/2))':enable='between(t,3,13)',fade=t=in:start_time=2.5:d=0.5:alpha=1,fade=t=out:start_time=12.5:d=0.5:alpha=1" -c:a copy -t 15 outting.mp4

此时,问题又出现了,我们发现当两者一起时,文字淡入淡出消失了,fade功能不见了。
因此我们换种思路,还有什么参数可以控制文字淡入淡出呢?
是的,我们可以通过动态调整alpha参数,来实现文字淡入淡出:

ffmpeg -y -i test.mp4 -filter_complex  "color=c=blue@0.5:size=10x10,scale=eval=frame:w='if(lte(t,3),0,if(lte(t,4),(t-3)*600,if(lte(t,12),(4-3)*600,if(lte(t,13),(4-3)*600-(t-12)*600,0))))':h=10,fade=t=in:start_time=3:d=0.5:alpha=1,fade=t=out:start_time=12.5:d=0.5:alpha=1[cl1];color=c=blue@0.5:size=10x10,scale=eval=frame:w='if(lte(t,3),0,if(lte(t,4),(t-3)*600,if(lte(t,12),(4-3)*600,if(lte(t,13),(4-3)*600-(t-12)*600,0))))':h=10,fade=t=in:start_time=3:d=0.5:alpha=1,fade=t=out:start_time=12.5:d=0.5:alpha=1[cl2];[0][cl1]overlay=x='if(lte(t,4),W/2-300*(t-3),if(gte(t,12),W/2-300*(4-3)+300*(t-12),W/2-300*(4-3)))':y=H/4:enable='between(t,3,13)'[bkg];[bkg][cl2]overlay=x='if(lte(t,4),W/2-300*(t-3),if(gte(t,12),W/2-300*(4-3)+300*(t-12),W/2-300*(4-3)))':y=3*H/4:enable='between(t,3,13)',drawtext=text='summer video':fontsize=50:x=(w-tw)/2:y='if(lte(t,4),(h-th)/2+(t-4)*30,if(gte(t,12),(h-th)/2+(t-12)*30,(h-th)/2))':enable='between(t,3,13)':alpha='if(lt(t,3),0,if(lt(t,4),(t-3)/1,if(lt(t,12),1,if(lt(t,13),(1-(t-12))/1,0))))'" -c:a copy -t 15 outting.mp4

这样问题就被解决了。

6、添加多行文本(最终代码)

目前ffmpeg并没有直接添加多行文本的好命令,最多可以在命令行通过enter来实现文本换行,一般情况下,都是通过控制xy参数对齐,通过多个drawtext滤镜链实现多行文本。

实现办法:在Overlay后面可以直接连接一个drawtext(最多一个),并以此为一幕[te1],然后再以[te1]开头写draw text,可以多个用逗号分隔。

具体代码如下:

ffmpeg -y -i test.mp4 -filter_complex  "color=c=LightSteelBlue@0.5:size=10x10,scale=eval=frame:w='if(lte(t,3),0,if(lte(t,4),(t-3)*600,if(lte(t,12),(4-3)*600,if(lte(t,13),(4-3)*600-(t-12)*600,0))))':h=10,fade=t=in:start_time=3:d=0.5:alpha=1,fade=t=out:start_time=12.5:d=0.5:alpha=1[cl1];color=c=MediumSlateBlue@0.5:size=10x10,scale=eval=frame:w='if(lte(t,3),0,if(lte(t,4),(t-3)*600,if(lte(t,12),(4-3)*600,if(lte(t,13),(4-3)*600-(t-12)*600,0))))':h=10,fade=t=in:start_time=3:d=0.5:alpha=1,fade=t=out:start_time=12.5:d=0.5:alpha=1[cl2];[0][cl1]overlay=x='if(lte(t,4),W/2-300*(t-3),if(gte(t,12),W/2-300*(4-3)+300*(t-12),W/2-300*(4-3)))':y=H/4:enable='between(t,3,13)'[bkg];[bkg][cl2]overlay=x='if(lte(t,4),W/2-300*(t-3),if(gte(t,12),W/2-300*(4-3)+300*(t-12),W/2-300*(4-3)))':y=3*H/4:enable='between(t,3,13)'[at];[at]drawtext=text='summer video':fontsize=50:x=(w-tw)/2:y='if(lte(t,4),(h-th)/2+(t-4)*30,if(gte(t,12),(h-th)/2+(t-12)*30,(h-th)/2))':enable='between(t,3,13)':alpha='if(lt(t,3),0,if(lt(t,4),(t-3)/1,if(lt(t,12),1,if(lt(t,13),(1-(t-12))/1,0))))',drawtext=text='夏日视频':fontsize=50:fontcolor=Brown:x=(w-tw)/2:y='if(lte(t,4),(h-th)/2-100+(t-4)*30,if(gte(t,12),(h-th)/2-100+(t-12)*30,(h-th)/2-100))':enable='between(t,3,13)':alpha='if(lt(t,3),0,if(lt(t,4),(t-3)/1,if(lt(t,12),1,if(lt(t,13),(1-(t-12))/1,0))))',drawtext=text='really nice':fontsize=50:fontcolor=Brown:x=(w-tw)/2:y='if(lte(t,4),(h-th)/2+100+(t-4)*30,if(gte(t,12),(h-th)/2+100+(t-12)*30,(h-th)/2+100))':enable='between(t,3,13)':alpha='if(lt(t,3),0,if(lt(t,4),(t-3)/1,if(lt(t,12),1,if(lt(t,13),(1-(t-12))/1,0))))'" -c:a copy -t 15 output.mp4

而这段代码则是实现开头所展示的动效的最终代码了。

而实现这一个动效,用了多少代码呢?

下面这张图给大家感受一下:
FFmpeg动效实践与探索(文字方框的同步展开及收缩)
没错,看到这一段的时候,我当时的内心也是崩溃的。。

7、矩形框张缩+多行文本淡入淡出动效代码解读

FFmpeg动效实践与探索(文字方框的同步展开及收缩)
FFmpeg动效实践与探索(文字方框的同步展开及收缩)
FFmpeg动效实践与探索(文字方框的同步展开及收缩)
FFmpeg动效实践与探索(文字方框的同步展开及收缩)

8、总结

从以上艰难的实现历程和反复的踩坑上其实多多少少也能看出来,ffmpeg并不适合制作动效。

不仅这方面应用的资料难查,代码繁琐,而且实现成本也很高。

其实仔细看ffmpeg通过参数计算实现的动效也会发现,扩张的方框也存在着一定程度的抖动,其抖动原因,我猜想主要是因为方框左端在变化,右端也在变化,两边同时变化,而ffmpeg其内在的计算单元的精度可能无法保证两边变化的速度一定一致,因此出现抖动。

总之,ffmpeg是一个音视频好工具,但是是否适合制作动效,我本人的意见是否定的。不过以上工作总结也希望能给相关从业者提供一点参考和帮助。

当然我也期待日后会有大神开发出更多制作动效的filter,以便大家使用~期待~

上一篇:【V1.1】基于树莓派的OpenCV-Python摄像头人脸追踪系统(更新系统、含演示视频)


下一篇:JavaScript(JS) string.blink( )