FFmpeg作为一个集录制、转换、音/视频编码解码功能为一体的开源框架,自然也需要考虑怎样去和当下流行的视频分析技术融合。本文来自英特尔网络平台部软件工程师谢林在LiveVideoStack线上分享中的演讲,详细解析了如何用FFmpeg搭建基于CNN的视频分析方案。
文 / 谢林
整理 / LiveVideoStack
回放链接https://www2.tutormeetplus.com/v2/render/playback?mode=playback&token=279cb665e4ac4cacb95ccf5afcf00ce2
大家好,我是来自英特尔网络平台部VEI组的谢林。本次分享希望与大家一起探索如何用FFmpeg搭建视频分析方案,内容主要分为以下几个方面。
![ee0086aa070a05d88ad426eb7d4d826c.png](https://i-blog.csdnimg.cn/blog_migrate/bf7b87b0bc9e4de4ae70bfcfe619207d.png)
1. 自我介绍与团队介绍
![f7ea1084ad66d4e1be01e5516dfdfd34.png](https://i-blog.csdnimg.cn/blog_migrate/37a79e16739d3beeb5f2e096a36bdce2.png)
我从事软件开发超过十一年,在嵌入式系统、多媒体等领域有丰富的开发经验,现在也开始尝试一些与深度学习相关的工作,主要是开发与视频分析相关的解决方案。
![4b4417fcd5627c22b2fb67661b612b8b.png](https://i-blog.csdnimg.cn/blog_migrate/6d724f7128030989895c8a4cbe55a641.png)
我所在的团队也基本专注于视频相关的业务,主要涵盖以上四个部分:第一部分是多媒体的使能与分发,主要就是传统的转码业务;第二部分是云游戏,目前我们在Android和 Windows平台上都有相应的解决方案;第三部分是沉浸式虚拟现实,包括360°全景摄影、VR/AR等端到端的解决方案;第四部分就是视频分析。我们在这四大板块都有商用化的解决方案与成熟完整的技术储备。
2. OpenVINO 深度学习开发工具套件介绍
![dee3c299a776b9401af046c9a218cd80.png](https://i-blog.csdnimg.cn/blog_migrate/8a577dc1312515afa3dd6408e5a12a0e.png)
OpenVINO是英特尔近几年推出的一个重要软件产品,主要用于深度学习的开发,由Model Optimizer与Inference Engine两个主要模块组成。Model Optimizer的主要功能是模型的优化,包括转换一些现有成熟的AI框架下的模型成为中间格式,再通过推理引擎把模型部署到英特尔的各种设备之上。
最新的OpenVINO版本又引入了Nervana的N-Graph,可以说在模型的支持方面又有了新的突破。你可以在Github上找到oepn model zoo,里面包含了许多训练好的模型与代码,能够帮助快速上手,OpenVINO基于此开发了多种应用。
除此之外,OpenVINO也可以用来开发许多与深度学习相关的工具,例如我们现在经常用到的是将精度FP32的模型转换成int-8,使得性能大大提升。OpenVINO中也包含许多已经成熟的视觉相关的开发套件,包括OpenCV、OpenVX,同样也支持英特尔的MediaSDK与主要用于GPU加速的OpenCL,当然也支持FPGA开发环境。
OpenVINO最成功的一点就是将英特尔所有的与AI相关的软硬件进行了整体的封装,通过OpenVINO的推理引擎将各个设备都利用起来,从而进一步提升开发效率。
![65fcab5f6f119cb72ed4ace6ff00f121.png](https://i-blog.csdnimg.cn/blog_migrate/e52615a5d7a68e527801113979231cf2.png)
一个典型的音视频处理流程,首先在流程开始时对输入码流解码,然后解出来的视频帧进行前处理,处理完毕后的数据会被传输至推理引擎当中进行推理,从引擎输出的结果会再经过后处理,在此之后如果需要编码成其它格式则进行encode工作。
图中展示的是OpenVINO实际工作流程。首先,我们需要有一个训练好的模型,可能来自TensorFlow、Caffe或者mxnet等。训练完毕的数据通过Model Optimizer被转换成IR格式,此时一部分文件格式为.xml,另一部分文件格式则为.bin。其中.xml文件主要包括一些网络拓扑结构,.bin文件则包含那些参数的权重。将这两种格式的文件同时放入推理引擎当中,文件加载后再根据用户的设置,采用CPU、GPU、VPU等设备进行推理。
![db8396818e35a688b5af14002aac282c.png](https://i-blog.csdnimg.cn/blog_migrate/fc80fcf1fda2cd49de6465d3e96da2e2.png)
通过OpenVINO开发套件我们可以实现很多基于深度学习的案例,比较常见的如图形分类、分割,物体的检测、追踪,人脸识别,也有一些与交通安全相关的用例,如行人、车牌识别,另外,还可以用它做一些语音识别相关的应用。但是这里存在一个问题,如果使用OpenVINO开发,需要根据特定需求写具体的应用,没有一个可以完全复用的框架从而高效便捷地搭建一个完整方案。由此我们产生一个想法:将FFmpeg与深度学习有机结合,让大家使用简单的FFmpeg命令行就能够搭建高质量的深度学习用例。
3. FFmpeg与DNN模型
![a904f0e07d378fb25106302abca966af.png](https://i-blog.csdnimg.cn/blog_migrate/f6809d7b9726f4dce6617352bb30823b.png)
图中展示的是当前FFmpeg 与DNN模型结构框架,FFmpeg包含一个DNN Interface,它由两部分组成:DNNModel与DNNModule,可以看到,基于DNNInterface已经实现了一个超分的SR Filter,以及一个用于去雨滴功能的Derain Filter。在后端则支持TensorFlow Backend与NativeBackend。
举一个实际的例子来说,其命令行如下所示:
ffmpeg -i derain_input.mp4 -vfderain=model=derain_RESCAN.pb:dnn_backend=1 derain_output.mp4
其中.mp4文件作为输入被解码,解码出的视频帧首先会被送到Derain Filter,对于Derain Filter指定了参数dnn_backend,如果dnn_backend=1则会选用TensorFlow来做Backend,另外一个参数model对应的是指定模型文件的路径。输出后的数据被编码打包成.mp4文件,整个流程逻辑清晰,使用起来与其它滤镜相比也没有太大差别。但就功能来说还较为单一,要想实现复杂应用如人脸识别、物体跟踪与检测等则较为困难。我们曾尝试在当前框架下进一步扩展,但是我们发现现有的DNN Interface还不够完整,目前还在继续完善与更新中,而且如果完全按照现有结构去实现一些较为复杂的用例,其性能表现并不出色。
4. FFmpeg与OpenVINO IE Integration
![bf11c1369cb64d9d1a549d2b0121465b.png](https://i-blog.csdnimg.cn/blog_migrate/f178871e00b42a96528609655c12a565.png)
因此我们自主研发了一套全新的架构,如上图所示:我们定义了一个Image Inference Backend模块,其与DNNInterface相互独立。这里我们利用了英特尔的OpenVINO推理引擎并将其作为一个Backend。通过推理引擎的CAPI,利用各种硬件设备实现推理加速。
上图列出了比较重要的两个Filter:检测与分类,可以通过ffmpeg命令行直接使用这些filter。相较于之前的DNN Interface,该架构拥有诸多新特性:首先该模型可部署到多种硬件设备平台,支持异步工作模式以及多个推理请求并行处理。其次,该模型支持Batch mode,可以一次送多个frame进行inference。此外,该模型也支持隔帧处理,支持不需要每一帧都去inference的情形。
5. FFmpeg Video Analytics PluginsList
![426a2a8bf74d65f33438b1cc676d5443.png](https://i-blog.csdnimg.cn/blog_migrate/1220ad62cca3cd3f7afff8f6b9c55676.png)
图中展示的是具体实现中的plugin,在FFmpegFilter方面我们实现了Detection、Classification与identification也就是检测、分类与识别。除此之外,还实现了Metadata Conversion,也就是将推理以后一些数据存储到FFmpeg的AVFrame SideData当中。
另外我们也实现了两个muxer,一个是可将这些Metadata进行发布,或者存储成一些文件。同时也支持Kafka协议进行分发,创建一个Kafka的Broker并发送到Kafka的远端服务器上。
6. FFmpeg视频分析案例
![918387bcda6e334c450c153769983a9c.png](https://i-blog.csdnimg.cn/blog_migrate/dff13c20e310128844b9cdec3e7ae6e2.png)
由上图所示,我们可以看到其中采用了我们实现的一些Filter以及Muxer:解码后系统获得视频帧,随后进入第一个Filter:Detect Filter,Detect Filter中第一项流程是预处理(包含颜色空间的转换与缩放)。大家知道深度学习的模型,其输入的数据与frame的layout不一样,因此这里的格式转换十分必要。目前大多只能接受RGB作为输入,同时模型对输入的尺寸也有固定要求,因此这里需要一个尺寸重新调整的过程。
预处理后的流程是Inference,利用OpenVINO推理引擎。在此之后,由于我们需要明确Detect Filter输出数据的含义,数据经过Detect Filter后还要进行后处理。后处理将输出结果转换成一些预先定义好的数据结构,这些结果与原始的Video帧一起,被存储在 Detect Side Data当中,送至下一个Classify Filter(分类滤镜)。其工作流程与之前的Filter差不多,唯一的差别是Classify Filter会根据前面检测出的结果获取一些ROI,并对这些ROI进行crop处理,取出所需一块之后再去做Scale与CSC。ClassifyFilter处理完成的数据会被存储在Classify Side Data里,并与原始视频一起送往下一个单元,在这里可以选择分发,也可以用overlay的方式呈现原始图像。
如果使用ffmpeg命令行加上我们新实现的component,该怎么去做呢?
![823f5781ac57fb17a7817f3f811d2750.png](https://i-blog.csdnimg.cn/blog_migrate/f2db51c791a3d09dad45ce6815d34629.png)
具体实践如上图所示,其中的高亮表示关键元素。前面的部分主要是指定一个输入,而后面对于Filter的描述则非常多,大致可以看到其中包含了4个Filter。经过detect的数据会被送至下一步classify,这里有可能做另外的分类。分类过后则是metadata的转换,最后通过metapublish分发出去。
我们再来看看其中的一些参数,例如有参数用来指定检测模型的检测路径,还有一个参数被称为model_proc,主要用于告知模型前处理与后处理的一些基本信息,这是一个Json格式的script。nireq参数可以告诉我们同时有多少推理请求正在工作,device参数可以告诉我们这个模型需要部署到哪一个硬件设备上,其他的classify参数与detect的基本一致。
metapublish参数则是告诉我们数据需要被转化成什么输出格式,现在我们支持用json格式来输出,最后通过一个kafka协议的url发送至kafka的远程服务器。
![dba7a278765286ce4782309a378c80c3.png](https://i-blog.csdnimg.cn/blog_migrate/24814c2df9cbdcfd2c78ab73d01d997a.png)
上图就是我们在GitHub上的主页(https://github.com/VCDP/FFmpeg-patch),包括具体的实现和一些教程,wiki里有教大家如何一步步搭建这样一个视频分析方案,欢迎大家前访问并提宝贵建议。这个repo并不是一个完整的FFmpeg source code。因为FFmpeg的架构无法将一个完全独立的工程作为一个插件。我们只是把现有的实现按照patch的方式来发布,而这些patch都是以FFmpeg 4.2 release作为基础。在实际过程中,首先需要下载FFmpeg4.2源代码,再安装一些可能需要依赖的三方库,然后编译整个FFmpeg工程,就可以通过ffmpeg命令行使用我们设计的这些Filter与Muxer。
![2ae0d8354e50c89b5ea65c4f0e4ca4b5.png](https://i-blog.csdnimg.cn/blog_migrate/bdc68d8f87a7c007a9f752f43f237617.png)
在开发的过程之中,我们发现FFmpeg可能存在一些不足。例如每个Filter不能单独运行在一个线程上,并且不能实现多个frame分发到不同线程,这对效率有比较严重的制约。另外目前的Filter chain是串行工作的模式,不能够做并行处理,这也会降低整体效率。
深度学习当中输入输出的格式与传统音视频输入输出的格式不同,目前FFmpeg当中也没有合适的数据结构或者一些预置定义好的接口用于支持DL Tensor,为此我们自定义了一些数据结构。Libavfilter当初设计的目标主要是针对一些轻量级的处理,并未考虑引入像深度学习这样复杂繁重的功能。如果我们不想被限制在Libavfilter这个框架下,也可以考虑重新设计一套框架来更好地支持深度学习。