虽然通过声明[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. Unity进阶:用AssetBundle和Json做了一个玩家登陆界面

    版权申明: 本文原创首发于以下网站: 博客园『优梦创客』的空间:https://www.cnblogs.com/raymondking123 优梦创客的官方博客:https://91make.top ...

  2. 学习整理:用webpack4.x构建基本项目

    webpack4 在2018年就已经发布了, 相比webpack3,webpack4需要的配置减少了很多,对入口和出口配置都有默认设置可以不用手动设置,但还是要在webpack.config.js中配 ...

  3. 从入门到实践:创作一个自己的 Helm Chart

    前言 我们平时在日常生活中会经常在不同的平台上与各种各样的应用打交道,比如从苹果的 App Store 里下载的淘宝.高德.支付宝等应用,或者是在 PC 端安装的 Word.Photoshop.Ste ...

  4. 容器的进程与namespace、rootfs

    一:容器是什么 容器的本质是一种特殊的进程. 在linux容器中有三个重要的概念:Namespace.Cgroups.rootfs. Namespace做隔离,让进程只能看到Namespace中的世界 ...

  5. JavaWeb实现增删查改(图书信息管理)——之查询

     关于此次CRUD所需要的jar包,本人把文件放在了百度网盘,需要的自行去下载: 链接:https://pan.baidu.com/s/1Pqe88u6aPaeVjjOq1YFQ-w  提取码:pim ...

  6. 剑指Offer(二十八):数组中出现次数超过一半的数字

    剑指Offer(二十八):数组中出现次数超过一半的数字 搜索微信公众号:'AI-ming3526'或者'计算机视觉这件小事' 获取更多算法.机器学习干货 csdn:https://blog.csdn. ...

  7. 007 Linux系统优化进阶

    一.更改 ssh 服务远程登录的配置 windows:默认远程端口和管理员用户 管理员:administrator port :3389 Linux:远程连接默认端口和超级用户 管理员:root   ...

  8. SDU暑期集训排位(9)

    SDU暑期集训排位(9) G. Just Some Permutations 基础 DP 练习部分 定义 \(f(S)\),表示让 S 中的人全 happy 的方案数. \(dp[i][j]\) 表示 ...

  9. HZNU 2019 Summer training 6 -CodeForces - 622

    A - Infinite Sequence  CodeForces - 622A 题目大意:给你一个这样的数列1,1,2,1,2,3,1,2,3,4,1,2,3,4,5....就是从1~n排列(n++ ...

  10. CF 988E Divisibility by 25 思维 第十二

    Divisibility by 25 time limit per test 1 second memory limit per test 256 megabytes input standard i ...