ffmpeg的中滤波器是以帧为原料来进行滤波的,那么自然地就会对帧的格式有所要求,可以说如果滤波器不知道帧的格式,就无法对帧进行处理。在进行视频滤波时,滤波格式指的是视频的像素格式;在进行音频滤波时,滤波格式包括音频采样格式,采样率以及通道数目。

承担协商任务的结构体AVFilterLink

滤波器可能只支持某一种帧格式,也有可能对所有的帧格式都支持,因此在执行滤波操作之前,有必要对滤波过程中的各个滤波器所支持的格式进行协商。

相邻的两个滤波实例之间是由AVFilterLink来连接的,因此AVFilterLink也承担了协商相邻两个滤波实例的滤波格式的任务。AVFilterLink的结构体中与滤波格式相关如下的变量如下:

struct AVFilterLink {
/*format negotiation valuables*/
uint64_t channel_layout;
int sample_rate;
int format; AVFilterFormats *in_formats;
AVFilterFormats *out_formats;
AVFilterFormats *in_samplerates;
AVFilterFormats *out_samplerates;
struct AVFilterChannelLayouts *in_channel_layouts;
struct AVFilterChannelLayouts *out_channel_layouts;
}

其中,in_formats/in_samplerates/in_channel_layouts、out_formats/out_samplerates/out_channel_layouts分别为当前Link输入端滤波器支持的输出滤波格式以及输出端滤波器支持的输入滤波格式,以Link的视点来说,分别就是可能的输入(所以变量被命名为in)格式以及可能的输出格式(所以变量被命名为out)。而format/sample_rate/channel_layout就是经过协商后得出唯一的格式。

滤波格式协商

滤波格式的协商分为以下几个步骤:

  1. 设置所有AVFilterLink上的格式列表
  2. 如果AVFilterLink的输入输出格式列表中有相同的格式,则提取相同格式作为新的输入以及输出格式
  3. 否则表明需要进行格式转换
  4. 提取合并后的格式列表中的某个格式作为最终格式

设置格式列表

滤波格式的协商是在AVFilterLink上执行的,因此滤波器需要向AVFilterLink表明自己所支持的输入以及输出格式,即由AVFilter来设置它周围的AVFilterLink上的格式列表。这个设置的操作是由AVFilter内的query_formats函数来实现的。

query_formats是AVFilter中的一个回调函数,如果某个滤波器对输入以及输出格式有要求或者限制,则需要通过query_formats来设置输入link的out_formats以及输出link的in_formats。如果滤波器不实现query_formats,则表明该滤波器的输入输出默认支持所有格式。

    for (i = 0; i < graph->nb_filters; i++) {
AVFilterContext *f = graph->filters[i];
if (formats_declared(f))
continue;
if (f->filter->query_formats)
ret = filter_query_formats(f);
else
ret = ff_default_query_formats(f);
if (ret < 0 && ret != AVERROR(EAGAIN))
return ret;
/* note: EAGAIN could indicate a partial success, not counted yet */
count_queried += ret >= 0;
}

AVFilterLink的格式合并

在设置完graph上所有link的in_formats以及out_formats后,就需要提取每个link上in_formats与out_formats的共同formats,我们称这一步骤为合并(merge)。

在讨论怎么merge formats之前,我们需要先了解AVFilterFormats这一结构体。AVFilterFormats就是我们前文所说的格式列表,定义如下:

struct AVFilterFormats {
unsigned nb_formats; ///< number of formats
int *formats; ///< list of media formats unsigned refcount; ///< number of references to this list
struct AVFilterFormats ***refs; ///< references to this list
};

nb_formats是列表中format的数目;formats是指向format列表的指针;refcount代表本列表被引用的次数;refs指向一个列表,该列表中存放的是引用了本列表的地方的地址。

这里的合并,是要从AVFilterLink的in_formats以及out_formats中挑出相同的format,然后组合成新的一个AVFilterFormats。

在实际滤波过程中,我们需要通过名为buffersrc的滤波器输入帧,而buffersrc在进行初始化的时候需要指定唯一的帧格式,即位于整个滤波图头部的是一个format个数为1的AVFilterFormats。并且,对于很多滤波器来说,所支持的输入输出格式是一样的,因此位于滤波器输入端link的out_formats与输出端link的in_formats会指向同一个AVFilterFormats。出于这两个原因,在循环地对graph上的AVFilterLink进行merge的时候,很容易地就能使得整个graph上所有的link中的in_formats与out_formats都指向同一个AVFilterFormats,并且其中含有唯一一个format就是输入帧的format。

如上图就是filter的输入与输出端支持同样格式,因此会把输入link的out_formats以及输出link的in_formats指向同一AVFilterFormats。不过如果filter两端支持不同的格式,则表示该filter内可能对帧的格式进行了转换。

自动格式转换

如果AVFilterLink的in_formats与out_formats中不含有相同format,就表明需要进行格式转换。出现这种情况的时候ffmpeg会在该link上插入一个用于格式转换的滤波器,进行视频像素格式转换的滤波器名为scale,进行音频采样格式转换的滤波器名为aresample。

if (convert_needed) {
switch (link->type) {
case AVMEDIA_TYPE_VIDEO:
filter = avfilter_get_by_name("scale");
avfilter_graph_create_filter(&convert, filter,
inst_name, graph->scale_sws_opts, NULL,
graph);
break;
case AVMEDIA_TYPE_AUDIO:
filter = avfilter_get_by_name("aresample");
avfilter_graph_create_filter(&convert, filter,
inst_name, graph->aresample_swr_opts,
NULL, graph);
break;
default:
return AVERROR(EINVAL);
} if ((ret = avfilter_insert_filter(link, convert, 0, 0)) < 0)
return ret; if ((ret = filter_query_formats(convert)) < 0)
return ret;
if (!ff_merge_formats( inlink->in_formats, inlink->out_formats, inlink->type) ||
!ff_merge_formats(outlink->in_formats, outlink->out_formats, outlink->type))
ret = AVERROR(ENOSYS);

插入格式转换滤波器包含以下步骤:

  1. 插入格式转换滤波器的函数avfilter_insert_filter会把原来的link的出口连接到格式转换滤波器的入口上,然后用新的link连接格式转换滤波器的出口以及源link原来的目标端口。最后还会把源link的out_formats移给新link的out_formats。
  2. filter_query_formats则会调用格式转换滤波器的query_formats函数来设置其两端的out_formats以及in_formats,由于此时我们一般不会为其设置任何参数,因此此时格式转换滤波器两端的out_formats以及in_formats会支持所有格式。
  3. 最后的ff_merge_formats把涉及到的这两个link上的格式进行merge,这样就使得格式转换滤波器两端的out_formats以及in_formats设定完毕。

在得到格式转换滤波器两边的两个link的最终格式后,会通过调用格式转换滤波器的config_props函数来进行转换的初始化,初始化时的输入参数就是这两个link的最终格式。

static int config_output(AVFilterLink *outlink)
{
AVFilterContext *ctx = outlink->src;
AVFilterLink *inlink = ctx->inputs[0]; aresample->swr = swr_alloc_set_opts(aresample->swr,
outlink->channel_layout, outlink->format, outlink->sample_rate,
inlink->channel_layout, inlink->format, inlink->sample_rate,
0, ctx);
}

提取最终格式

经过前面的流程,已经能保证link上的in_formats与out_formats是merge过的了,下面处理同一个滤波器的输入以及输出link之间的格式问题。

尽管我们前面说过,对于内部不会进行格式转换的滤波器,通常其输入输出会支持相同的格式,因此输入link的out_formats与输出link的in_formats一般来说都是指向同一个AVFilterFormats,不过滤波器多种多样,也有可能会出现明明可以不用格式转换,输入link的out_formats却与输出link的in_formats却指向不同AVFilterFormats。为了防止滤波器做不必要的格式转换,有以下处理方式:一旦发现输入link的out_formats当中只有唯一一个格式,并且输出link的in_formats当中包含该格式,则会把该格式移动到in_formats[0],并把格式数目设置为1。

在选取link的最终格式时,一般来说link的格式列表中只有一个格式,因此会直接选则这一个格式。

static int pick_format(AVFilterLink *link, AVFilterLink *ref)
{
link->in_formats->nb_formats = 1;
link->format = link->in_formats->formats[0];
}

不过也存在特殊情况:如果滤波器支持不同于源格式的多种输出格式,但是用户并没有指定具体的输出格式,那么应该根据源格式从当前列表中选择最优的输出格式。

[ffmpeg] 滤波格式协商的更多相关文章

  1. FFmpeg封装格式处理4-转封装例程

    本文为作者原创,转载请注明出处:https://www.cnblogs.com/leisure_chn/p/10506662.html FFmpeg封装格式处理相关内容分为如下几篇文章: [1]. F ...

  2. FFmpeg封装格式处理3-复用例程

    本文为作者原创,转载请注明出处:https://www.cnblogs.com/leisure_chn/p/10506653.html FFmpeg封装格式处理相关内容分为如下几篇文章: [1]. F ...

  3. FFmpeg封装格式处理2-解复用例程

    本文为作者原创,转载请注明出处:https://www.cnblogs.com/leisure_chn/p/10506642.html FFmpeg封装格式处理相关内容分为如下几篇文章: [1]. F ...

  4. FFmpeg封装格式处理

    本文为作者原创,转载请注明出处:https://www.cnblogs.com/leisure_chn/p/10506636.html FFmpeg封装格式处理相关内容分为如下几篇文章: [1]. F ...

  5. [ffmpeg] 滤波

    ffmpeg中有很多已经实现好的滤波器,这些滤波器的实现位于libavfilter目录之下,用户需要进行滤波时,就是是调用这些滤波器来实现的.ffmpeg对于调用滤波器有一整套的调用机制. 基本结构 ...

  6. ffmpeg视频格式转换(Java)

    命令: 高品质: ffmpeg -i E:\input\a.wmv -ab 128 -acodec libmp3lame -ac 1 -ar 22050 -r 29.97 -qscale 4 -y E ...

  7. 音视频处理之FFmpeg封装格式20180510

    一.FFMPEG的封装格式转换器(无编解码) 1.封装格式转换 所谓的封装格式转换,就是在AVI,FLV,MKV,MP4这些格式之间转换(对应.avi,.flv,.mkv,.mp4文件). 需要注意的 ...

  8. ffmpeg视频格式转换中关键帧的设置

    在用ffmpeg转换视频到flv过程中,需要设置关键帧的间隔,以便在播放过程中实现精确定位.在网上查找了不少,最后发现这个指令有效: -g 1 -keyint_min 2 . http://blog. ...

  9. [ffmpeg] 定制滤波器

    如果有定制ffmpeg滤波器的需求,有两个结构体是必须要了解的:AVFilter.AVFilterPad,所定制的滤波器主要就是通过填充这两个结构体来实现的.我们下面将详细解析这两个结构体,并通过对滤 ...

随机推荐

  1. java jdk 8反编译工具JD-GUI、procyon-decompiler、luyten、crf下载使用简介

    本文对常用的反编译工具进行简单介绍 JD-GUI.procyon-decompiler.luyten.crf   反编译工具分类 JD-GUI JDK7以及之前可以使用   JD-GUI,如果版本&g ...

  2. SLAM+语音机器人DIY系列:(三)感知与大脑——4.音响麦克风与摄像头

    摘要 在我的想象中机器人首先应该能自由的走来走去,然后应该能流利的与主人对话.朝着这个理想,我准备设计一个能自由行走,并且可以与人语音对话的机器人.实现的关键是让机器人能通过传感器感知周围环境,并通过 ...

  3. WebForm+一般处理程序+Ajax聊天

    #### 很容易理解 插入数据 到数据库 在使用 setInterval() 读取数据 显示在 页面中 好了 不废话 直接上代码 不会的 可以加我 微信 Jth11163## 效果图片 ![在这里插入 ...

  4. Java 设置PDF文档背景色

    一般生成的PDF文档默认的文档底色为白色,我们可以通过一定方法来更改文档的背景色,以达到文档美化以及保护双眼的作用. 以下内容提供了Java编程来设置PDF背景色的方法.包括: 设置纯色背景色 设置图 ...

  5. spring2.0 mybatis JDBC配置

    mybatis 搭建 <!--连接池--> <dependency> <groupId>org.springframework.boot</groupId&g ...

  6. 修改phpcms中的评论样式

    phpcms中自带的评论插件很好用!但是样式个人感觉丑的狠,百度一下也没能找到解决方式,也许是自己的搜索方式不对,于是自己就研究了研究,这里可以使用两种方法进行修改 方法一: 使用PHPCMS中的ge ...

  7. PHP遍历文件夹下所有文件

    不论是面试还是正常工作需要都会用到遍历文件夹下的所有文件,今天就记录一下笔记.废话不多说直接上代码: <?php /** * 遍历当前文件夹展示所有的文件和目录 */ function dirL ...

  8. Nginx系列

    包括nginx的入门和进阶学习. 目录 nginx系列1:认识nginx nginx系列2:搭建nginx环境 nginx系列3:搭建一个静态资源web服务器 nginx系列4:日志管理 nginx系 ...

  9. Spring MVC(四)文件上传

    文件上传步骤 1.写一个文件上传的页面 2.写一个文件上传的控制器 注意: 1.method="post" 2.enctype="multipart/form-data& ...

  10. 用Jenkins搭建自动构建服务

    Jenkins是BS跨平台构建工具,之前名为Hundson.wiki [chs  en]  最新windows安装包:下载 下文以1.593版本为例,讲述Jenkins的Windows版本的一些要注意 ...