刘歧:FFmpeg Filter深度应用

2021-09-01 11:10:58 浏览数 (1)

本文来自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基本原理

基本原理的理解可以从以下六点出发:

  1. 线性属性:滤镜是一个线性的属性,是一步接着一步的往下走的,当然也可以有多线程的,但是多线程的实现不是特别好,一般的实现都是通过单线程来线性处理的。
  2. 输入输出标签:滤镜都有输入和输出的标签。
  3. 滤镜名
  4. 滤镜参数:当我们在使用某一类滤镜时,需要知道对应的参数大小,如位置,宽高等。
  5. 内置变量:在使用滤镜时,可以用到它的很多内置变量,如帧率,PTS等,这些变量可以在命令行中使用,在调用API时也会用到。
  6. 压缩前、解压后的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:介绍是这样的,通过代码的演示可以看到滤镜的处理是一步一步进行的,处理完一帧,再处理下一帧。接口的调用也是如此。

0 人点赞