本文来自OnVideo视频创作云平台联合创始人刘歧在LiveVideoStackCon的讲师热身分享,刘歧分享了FFmpeg的基本原理、使用方法及开发方法。在10月19-20日的LiveVideoStackCon 2018上,刘歧还将分享如何通过FFmpeg实现视频版权保护的方法。
文 / 刘歧
整理 / LiveVideoStack
直播回放:
https://www.baijiayun.com/web/playback/index?classid=18081550127330&session_id=201808160&token=UpmOXagOUwZt681rrJ0J_fi3uef2YslR9xPZowdmKaIVnKE7ZcXWwkkNnUrZm8So4_FWYYdehfAKp0fXMnVKLQ
大家好我是刘歧,目前和几个朋友一起创办了视频云创作平台OnVideo,主要负责做一些新功能预研和整体方向把控。
今天主要从四个方面来介绍一下FFmpeg的滤镜深度应用:
1. Filter基本原理
2. Filter使用方法
3. Filter开发基础
4. Filter开发
一. Filter基本原理
基本原理的理解可以从以下六点出发:
- 线性属性:滤镜是一个线性的属性,是一步接着一步的往下走的,当然也可以有多线程的,但是多线程的实现不是特别好,一般的实现都是通过单线程来线性处理的。
- 输入输出标签:滤镜都有输入和输出的标签。
- 滤镜名
- 滤镜参数:当我们在使用某一类滤镜时,需要知道对应的参数大小,如位置,宽高等。
- 内置变量:在使用滤镜时,可以用到它的很多内置变量,如帧率,PTS等,这些变量可以在命令行中使用,在调用API时也会用到。
- 压缩前、解压后的YUV/RGB/PCM数据:对数据进行滤镜处理时使用的都是原始数据,也就是未压缩的数据。
线性处理图解
如上图所示,从滤镜线性处理图中可以看出,对每一个的滤镜处理过程必须要有输入和输出,比如在最后滤镜8的位置没有输出1来承接它的结果,则会报错。另外,如从滤镜1到滤镜2的过程,滤镜1的输出Out可以作为滤镜2的输入In,从滤镜2再往后,滤镜2的输出Out可以分为两部分,分别作为其它滤镜处理的输入。
原始数据处理
滤镜的处理都是基于原始数据来进行的,比如视频图像的每一帧,当然也包括音频。如上图所示,每一帧可以有前后的多个层,一般情况下是两层,当有多层时,如Frame1是三层,则可以先处理前两层,处理之后合并成一层,再跟另一层一起去做处理。当处理完Frame1后送到编码器进行编码,再依次处理Frame2、Frame3、Frame4,处理完后均送到编码器进行压缩编码为另一个格式,比如H.264。
二. Filter使用方法
1. 常用滤镜
常用的滤镜中重点的是:scale、trim、overlay、yadif、rotate、movie
比如常用的scale 可以用来做缩放,trim可以做比较精确的帧级的剪切,overlay可以来实现视频混流,画中画或多画面等叠加处理,rotate来做旋转,movie可以加载第三方的视频等等,yadif可以在摄像机上做隔行扫描,在PC端的播放器可以做去交错。另外,还有delogo、crop、colorkey、chromakey、zoompan; delogo可以用来模糊化某一区域,crop用于图像区域剪切,colorkey和chromakey是用于抠图的,chromakey以YUV输入为主,colorkey以RGB输入为主,zoompan可以将图像放大。
2. 命令行
ffmpeg -filters
通过这一命令,可以列出所有的滤镜,但需要弄清楚每一个滤镜的作用,比如上图的adelay,它的作用是音频转音频,延缓一个或更多的音频通道。在滤镜参数里面还包括timeline support、slice threading、command support,等在后面也会有介绍。
ffmpeg -h filters=rotate
当某个滤镜不知道它里面都有什么参数或者参数怎么用时,那么可以通过上面这条help命令去查看滤镜的内容。比如在上图,查看旋转(rotate)滤镜,可以看出它支持slice threading,而在最下面可以看到还支持timeline操作,并且输入和输出都是Video格式,再下面就是rotate的一些参数,值得注意的是,在FFmpeg里面,尤其滤镜这部分,为了让大家在输入参数时候更简单,它会把很长的参数提供一个简化的名称。
命令行的使用开头都是:filter_complex,然后再在后面接一些字符串,如下:
下面列出几个例子的命令行使用:
1. 跑马灯
代码语言:javascript复制[0:v][1:v]overlay=x='if(gte(t,0), -w (mod(n, W w)) 5, NAN)':y=0[out]
应用场景如新闻播放时画面下方的小栏预告,从右跑到左。
2. 图像旋转
代码语言:javascript复制[1:v]rotate=a=60*PI/180:ow=180:oh=180[v1];[0:v][v1]overlay=x=-20:y=-20[o]
3. delogo(模糊化某一区域)
代码语言:javascript复制delogo=x=72:y=32:w=168:h=86:band=1:show=1
下图为以上三条命令行处理后的展示效果:
在FFmpeg的ffplay里可以通过dumpgraph对滤镜的处理过程增加更深的了解,可以看看下面这个例子:
代码语言:javascript复制./ffplay -dumpgraph 1 -f lavfi "movie=a.flv[l];movie=logo.png[r];[l][r]overlay=x=0:y=0"
在滤镜处理的过程中是先加载第一个电影,然后再加载一张PNG图片,为了叠加处理比较方便会先将它们转换成YUV,再进行叠加运算,最后输出到一个buffersink中。
3. API
滤镜Filter的API结构包括:
AVFilterContext:滤镜的上下文
AVFilterInOut:滤镜的输入、输出
AVFilterGraph:滤镜的图结构
AVFilter:滤镜的具体处理类
AVFilterPad:包括输入的Pad和输出的Pad
调用API的分为两个大的步骤:
第一步,滤镜的初始化
初始化部分首先要确定In的Buffer和Out的Buffersink,接下来就是创建一个滤镜的Graph,再就是做一个滤镜的解析,最后就是通用配置。
第二步,滤镜处理
滤镜处理过程是先解码,解码之后将帧加入到Buffer,再调用滤镜处理模块,处理之后输出到Buffersink。
滤镜API的使用方法跟之前命令行的使用方法是一样的,只是略微有一点差别。如果我们不会使用多输入多输出的方式,对于API的使用实际上它的输入也可以用字符串处理的方式,就是命令行里边的处理方式,如以下官方demo的代码:
跑马灯
代码语言:javascript复制[0:v][1:v]overlay=x='if(gte(t,0), -w (mod(n, W w)) 5, NAN)':y=0[out]
图像旋转
代码语言:javascript复制[1:v]rotate=a=60*PI/180:ow=180:oh=180[v1];[0:v][v1]overlay=x=-20:y=-20[o]
Delogo(模糊化某一区域)
代码语言:javascript复制delogo=x=72:y=32:w=168:h=86:band=1:show=1
从上面的介绍可以看出,命令行与API实质上并没有太大的区别。
三. Filter开发基础
1. Module结构
在上面的Module结构里面,可以很清晰的看出步骤流程,先要在滤镜的类里面注册它所对应的所有信息,如滤镜名,上下文等等;下一步就是对Input和Output的处理,对帧的滤镜处理正常都是在一个Input里面处理的;
再就是要看滤镜是否支持Timeline,比如做一些动态的变量,跑马灯之类的,也要有时间线的概念。在这个里面也有一些对应的内置函数,可以把他们理解为一些数字运算的接口。以及下面还有一些变量名的定义。
2. 加入到FFmpeg中
添加滤镜的时候,主要是往下面两个里面添加:
四. Filter开发
在这一部分,为大家做了FFMPEG添加新滤镜的过程的录像,录像地址为http://bbs.chinaffmpeg.com/add_filter.mp4,有兴趣的同学可以看一下
首先建立了一个zoomscale的滤镜文件,建立完这个文件之后,创建对应的context,然后把刚才讲到的那些接口都写进来,定义zoomscale公共的类,下面我们实现了一个filter _frame,它会把里面的input给释放掉,让它变成一个NULL,此时显示的前景图像就会变成一个纯绿色的图像,如果说这里有process commode,那么就可以在做zmq的时候用,如果没有的话,可以留空。关键是filter_frame,可以当作滤镜处理的一个入口,而get_video_buffer可以把它理解为我们看到的很多demo里面写到的get_video_buffer,即创建一个帧的对应Buffer,将其添加至Makefile之后,继续添加一个avfilter。这是FFmpeg4.0之前的版本,在后面新版本里面,采用的是数组的形式,也就没有列表那么麻烦,速度会更快一些,添加完之后,可以通过config进行查看配置,每次config完之后,会有整体的一个输出。添加完成后,进行编译,之后可以在avfilter库里直接查看滤镜是否已经加到FFmpeg的应用程序里,然后调用API部分,直接把字符串传进来就可以使用了。
Q&A:
Q1:直播实时应用滤镜怎么处理?
A:只要性能跟的上就可以,比如说通过滤镜里可以添加OpenCV,或者OpenGL的方式。如果原来是直接调用OpenGL,想更加方便,在FFmpeg里进行处理的话,可以把它添加到FFmpeg的滤镜里面,实现对应的OpenGL方法,再调方法即可。
Q2:在FFmpeg里面滤镜处理都是线性的吗?
A:介绍是这样的,通过代码的演示可以看到滤镜的处理是一步一步进行的,处理完一帧,再处理下一帧。接口的调用也是如此。