虽然通过声明[x][y]avfilter=a=x:b=y;avfilter=xxx的方式可以创建一个可用的Filter调用链,并且在绝大多数场合下这种方式都是靠谱和实用的。 但如果想精细化的管理AVFilter调用链,例如根据某些条件来动态生成AVFilter Graph。这种声明方式就不太灵活(也可以通过if判断来动态组装字符串,如果你非常喜欢这种字符串声明方式,到此为止不在建议你往下阅读了)。

首先快速温习一下,如何创建一个AVFilter Graph

             +-------+             +---------------------+         +---------------+
|buffer | |Filter ..... Filter N| | buffersink |
----------> | |output|------>|input| |output|---> |input| |-------->
+-------+ +---------------------+ +---------------+

创建三部曲:

  1. 初始化bufferbuffersink
  2. 初始化其它filter
  3. 设定Filter Graph的Input和Output。

其中bufferbuffersink分别代表Graph的起始和结束。

然后快速封装args也就是movie=t.png[wm];[in][wm]overlay=10:20[out]这样的filter-complex命令。 而且通过avfilter_graph_parse_ptr完成中间filter的初始化,

最后指定各个filter的input和output,一个graph就算搞定了。

好,下面来看如何通过API精细化生成AVFilter Graph。 生成下面的Graph:

             +-------+             +---------------------+         +---------------+
|buffer | | Filter | | buffersink |
----------> | |output|------>|input| Fade |output|---> |input| |-------->
+-------+ +---------------------+ +---------------+

首先初始化各个AVFilter。所有的AVFilter的初始化都可以简化为两步操作:

  • 通过avfilter_get_by_name查找指定的AVFilter
  • 通过avfilter_graph_create_filter初始化AVFilterContext

AVcodecAVCodecContext的关系一样, 所有的AVFilter的执行都依靠对应的AVFilterContext(在ffmpeg开发中,每个组件都会对应一个上下文管理器,由这个上下文管理器封装各种参数然后调用组件执行)。

通过avfilter_get_by_name生成AVFilter实例之后,紧跟着就需要调用avfilter_graph_create_filter初始化上下文管理器。

按照下面的流程,依次初始化三个AVFilter:

buffer_src = avfilter_get_by_name("buffer");
ret = avfilter_graph_create_filter(&buffersrc_ctx, buffer_src, "in", args, NULL, filter_graph);

这里重点聊一下avfilter_graph_create_filter。 下面是函数原型:

int avfilter_graph_create_filter(AVFilterContext **filt_ctx, const AVFilter *filt,
const char *name, const char *args, void *opaque,
AVFilterGraph *graph_ctx);

filt_ctx表示这个AVFilter的上下文管理器。

name表明的是AVFilter在Graph中的名称,这个名称叫啥不重要但必须唯一。 例如Fade AVFilter就可以叫做fade1,fade2或者ifade等等。

args则是这个AVFilter的参数, 注意仅仅是这个AVFilter的参数,不是整个graph的参数。再拿Fade举例,args就可以是t=in:st=3:d=3

opaque一般给NULL就可以了。

初始完这三个AVFilter之后,就进入到本次文档的重点: avfilter_link.

int avfilter_link(	AVFilterContext * 	src,
unsigned srcpad,
AVFilterContext * dst,
unsigned dstpad
)

avfilter_link分别用来链接两个AVFilter(传说中的一手托两家)。 srcdst分别表示源Filter和目标Filter。 srcpad表示src第N个输出端, dstpad表示dst第N个输入端。 如果不好理解,直接看下面的图:


+-------------+ +-------------+
| src srcpad 1 -----> dstpad 3 dst |
| srcpad 2 -----> dstpad 2 |
| srcpad 3 -----> dstpad 1 |
+-------------+ +-------------+

上图假设srcdst分别有三个输出端和三个输入端(不是所有avfilter都有这么多的输入输出端,像fade只有一个,但overlay就有多个)。

srcpaddstpad表示的就是输出/输入端的序号。假如将buffer第一个输出端对应fade第一个输入端。 那么就应该这么写:

avfilter_link(buffersrc_ctx, 0, ifade_ctx, 0);

然后将fade的第一个输出端对应buffersink的输入端,就这么写:

avfilter_link(ifade_ctx, 0, buffersink_ctx, 0);

而所谓的精细化就是在这里体现的,通过代码的逻辑判断,可以动态的组合不同的AVFilter生成不同的Filter Graph。并且还可以组合不同的输入/输出端。

本次代码示例可以参考ifilter。同时也可以参考 ffmpeg-go-server(一个尝试为ffmpeg提供restful API的web server)。

新手学习FFmpeg - 通过API实现可控的Filter调用链的更多相关文章

  1. 新手学习FFmpeg - 通过API完成filter-complex功能

    本篇尝试通过API实现Filter Graph功能. 源码请参看 https://andy-zhangtao.github.io/ffmpeg-examples/ FFmpeg提供了很多实用且强大的滤 ...

  2. 新手学习FFmpeg - 调用API完成录屏

    调用FFMPEG Device API完成Mac录屏功能. 调用FFMPEG提供的API来完成录屏功能,大致的思路是: 打开输入设备. 打开输出设备. 从输入设备读取视频流,然后经过解码->编码 ...

  3. 新手学习FFmpeg - 调用API编写实现多次淡入淡出效果的滤镜

    前面几篇文章聊了聊FFmpeg的基础知识,我也是接触FFmpeg不久,除了时间处理之外,很多高深(滤镜)操作都没接触到.在学习时间处理的时候,都是通过在ffmpeg目前提供的avfilter基础上面修 ...

  4. 新手学习FFmpeg - 调用API完成录屏并进行H.264编码

    Screen Record H.264 目前在网络传输视频/音频流都一般会采用H.264进行编码,所以尝试调用FFMPEG API完成Mac录屏功能,同时编码为H.264格式. 在上一篇文章中,通过调 ...

  5. 新手学习FFmpeg - 调用API完成视频的读取和输出

    在写了几个avfilter之后,原本以为对ffmpeg应该算是入门了. 结果今天想对一个视频文件进行转码操作,才发现基本的视频读取,输出都搞不定. 痛定思痛,仔细研究了一下ffmpeg提供的examp ...

  6. 新手学习FFmpeg - 调用API调整视频局部速率

    通过修改setpts代码实现调整视频部分的播放速率. 完整代码可参考: https://andy-zhangtao.github.io/ffmpeg-examples/ 在前面提到了PTS/DTS/T ...

  7. 新手学习FFmpeg - 调用API完成两个视频的任意合并

    本次尝试在视频A中的任意位置插入视频B. 在上一篇中,我们通过调整PTS可以实现视频的加减速.这只是对同一个视频的调转,本次我们尝试对多个视频进行合并处理. Concat如何运行 ffmpeg提供了一 ...

  8. 新手学习FFmpeg - 调用API计算关键帧渲染时间点

    通过简单的计算来,线上I帧在视频中出现的时间点. 完整代码请参考 https://andy-zhangtao.github.io/ffmpeg-examples/ 名词解释 首先需要明确以下名词概念: ...

  9. 新手学习FFmpeg - 如何编写Kubernetes资源文件

    Kubernetes API的使用方式 Kubernetes API属于声明式API编程, 它和常用的命令式编程有一些区别. 通俗的说,命令式编程是第一人称,我要做什么,我要怎么做. 操作系统最喜欢这 ...

随机推荐

  1. Javaweb Session机制(有待补充)

    Javaweb Session机制 一.前言 session,中文经常翻译为会话,其本来的含义是指有始有终的一系列动作/消息,比如打电话是从拿起电话拨号到挂断电话这中间的一系列过程可以称之为一个ses ...

  2. Salesforce LWC学习(五) LDS & Wire Service 实现和后台数据交互 & meta xml配置

    之前的几节都是基于前台变量进行相关的操作和学习,我们在项目中不可避免的需要获取数据以及进行DML操作.之前的内容中也有提到wire注解,今天就详细的介绍一下对数据进行查询以及DML操作以及Wire S ...

  3. PythonWeb框架Django:虚拟环境安装(virtualenv)

    虚拟环境的用处: 当我们有多个项目要使用不同的第三方类库的时候,就会发生冲突,因为Python的环境内只允许一个版本的第三方类库. 比如说 有A,B两个Web项目,但是A项目的Django的环境为2. ...

  4. volatile底层实现原理

    前言 当共享变量被声明为volatile后,对这个变量的读/写操作都会很特别,下面我们就揭开volatile的神秘面纱. 1.volatile的内存语义 1.1 volatile的特性 一个volat ...

  5. Linux的基本操作(一)

    一.Linux的常用命令 1.ls[参数] 目录或文件 参数 -a :显示指定目录下的所有子目录与文件,包括隐藏文件: -l 以长格式显示文件的详细信息 如图: 文件类型:“-“表示常规文件:”d&q ...

  6. Python基础学习笔记(一)python发展史与优缺点,岗位与薪资

    相信有好多朋友们都是第一次了解python吧,可能大家也听过或接触过这个编程语言.那么到底什么是python呢?它在什么机缘巧合下诞生的呢?又为什么在短短十几年时间内就流行开来呢?就请大家带着疑问,让 ...

  7. poj2528 Mayor's posters(线段树区间修改+特殊离散化)

    Description The citizens of Bytetown, AB, could not stand that the candidates in the mayoral electio ...

  8. Codeforces 735D Taxes(简单数论)

    题目链接 http://codeforces.com/problemset/problem/735/D 题意:一个人的收入为n他要交的税是n的最大除数,他为了少缴税将n分成k个数n1,n2,n2... ...

  9. 【笔记 Jvm-并发】

    概述 并发处理 是使得Amadahl定律代替摩尔定律成为计算机性能发展源动力的根本原因: Amdahl定律 通过系统中串行化与并行化的比重来描述多处理器系统所能获得到的运算加速能力: 摩尔定律 描述处 ...

  10. java 面试题 1-10

    1. Java 基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法, 线程的语法,集合的语法,io 的语法,虚拟机方面的语法 1.一个".java&q ...