FFmpeg滤镜AVFilter编程——简单滤镜

FFmpeg滤镜AVFilter编程——简单滤镜
⼀、认识滤镜
ffmpeg⾥⾯的滤镜(也叫过滤器)⼗分重要,功能也⼗分强⼤,除了我们所谓的滤镜,还有贴图、伸缩变形、拆解合并等功能,这⾥放2个、来证明他的强⼤。
ffmpeg⾥⾯⽤(AVFilterGraph)graph来作为管理⼀整套滤镜流程的容器,⾥⾯还有(AVFilter)filter作为过滤器和它的实例(AVFilterContext)filter_ctx,过滤器⾥还包含AVFilterPad作为过滤器的输⼊输出⼝,还有(AVFilterLink)link在过滤器之间做关联,这样前后串起来就是⼀个滤镜链了。
这⾥需要注意的是,滤镜链的前后需要名字专门叫做“buffer”的过滤器作为输⼊,也需要名字⼀定是“buffersink”的过滤器作为输出。
滤镜在FFmpeg⾥还分简单滤镜链和复杂滤镜链,⼀般的单⼊单出的为简单滤镜链,我们先讲简单的。
⼆、如何使⽤滤镜
使⽤滤镜有2种⽅式,我们就先讲很多demo⾥⾯都有的⼀种,就是使⽤avfilter_graph_parse_ptr。
FFmpeg的example⾥有个filtering_video.c,对照着滤镜部分看。
static int init_filters(const char *filters_descr)
{
char args[512];
int ret = 0;
// "buffer"滤镜:缓冲视频帧,作为滤镜图的输⼊
const AVFilter *buffersrc  = avfilter_get_by_name("buffer");
// "buffersink"滤镜:缓冲视频帧,作为滤镜图的输出
const AVFilter *buffersink = avfilter_get_by_name("buffersink");
AVFilterInOut *outputs = avfilter_inout_alloc();
AVFilterInOut *inputs  = avfilter_inout_alloc();
AVRational time_base = fmt_ctx->streams[video_stream_index]->time_base;
enum AVPixelFormat pix_fmts[] = { AV_PIX_FMT_GRAY8, AV_PIX_FMT_NONE };
// 分配⼀个滤镜图filter_graph
filter_graph = avfilter_graph_alloc();
if (!outputs || !inputs || !filter_graph) {
ret = AVERROR(ENOMEM);
goto end;
}
/* buffer video source: the decoded frames from the decoder will be inserted here. */
// args是buffersrc滤镜的参数
snprintf(args, sizeof(args),
"video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d",
dec_ctx->width, dec_ctx->height, dec_ctx->pix_fmt,
time_base.num, time_base.den,
dec_ctx->sample_aspect_ratio.num, dec_ctx->sample_aspect_ratio.den);
// 为buffersrc滤镜创建滤镜实例buffersrc_ctx,命名为"in"
// 将新创建的滤镜实例buffersrc_ctx添加到滤镜图filter_graph中
ret = avfilter_graph_create_filter(&buffersrc_ctx, buffersrc, "in",
args, NULL, filter_graph);
if (ret < 0) {
av_log(NULL, AV_LOG_ERROR, "Cannot create buffer source\n");
goto end;
}
/* buffer video sink: to terminate the filter chain. */
// 为buffersink滤镜创建滤镜实例buffersink_ctx,命名为"out"
// 将新创建的滤镜实例buffersink_ctx添加到滤镜图filter_graph中
ret = avfilter_graph_create_filter(&buffersink_ctx, buffersink, "out",
NULL, NULL, filter_graph);
if (ret < 0) {
av_log(NULL, AV_LOG_ERROR, "Cannot create buffer sink\n");
goto end;
}
// 设置输出像素格式为pix_fmts[]中指定的格式(如果要⽤SDL显⽰,则这些格式应是SDL⽀持格式)    ret = av_opt_set_int_list(buffersink_ctx, "pix_fmts", pix_fmts,
AV_PIX_FMT_NONE, AV_OPT_SEARCH_CHILDREN);
if (ret < 0) {
av_log(NULL, AV_LOG_ERROR, "Cannot set output pixel format\n");
goto end;
}
/*
* Set the endpoints for the filter graph. The filter_graph will
* be linked to the graph described by filters_descr.
*/
// 设置滤镜图的端点,包含此端点的滤镜图将会被连接到filters_descr
// 描述的滤镜图中
/*
* The buffer source output must be connected to the input pad of
* the first filter described by filters_descr; since the first
* filter input label is not specified, it is set to "in" by
* default.
*/
/
/ outputs变量意指buffersrc_ctx滤镜的输出引脚(output pad)
// src缓冲区(buffersrc_ctx滤镜)的输出必须连到filters_descr中第⼀个
// 滤镜的输⼊;filters_descr中第⼀个滤镜的输⼊标号未指定,故默认为
// "in",此处将buffersrc_ctx的输出标号也设为"in",就实现了同标号相连
outputs->name      = av_strdup("in");
outputs->filter_ctx = buffersrc_ctx;
outputs->pad_idx    = 0;
outputs->next      = NULL;
/*
* The buffer sink input must be connected to the output pad of
* the last filter described by filters_descr; since the last
* filter output label is not specified, it is set to "out" by
* default.
*/
// inputs变量意指buffersink_ctx滤镜的输⼊引脚(input pad)
// sink缓冲区(buffersink_ctx滤镜)的输⼊必须连到filters_descr中最后
// ⼀个滤镜的输出;filters_descr中最后⼀个滤镜的输出标号未指定,故
// 默认为"out",此处将buffersink_ctx的输出标号也设为"out",就实现了
// 同标号相连
inputs->name      = av_strdup("out");
inputs->filter_ctx = buffersink_ctx;
inputs->pad_idx    = 0;
inputs->next      = NULL;
// 将filters_descr描述的滤镜图添加到filter_graph滤镜图中
简易过滤器// 在filters_descr字符串中,如果第⼀个滤镜未指定输⼊标号,则假定为"in";如果最后⼀个滤镜
// 未指定输出标号,则假定为"out"。⼏个实参说明如下:
// @filter_graph  将字符串描述的滤镜图连接到此滤镜图中
// @filters_descr 描述滤镜的字符串,⽤于解析⽣成滤镜图
// @inputs        指向滤镜图中输⼊链表,调⽤返回时,更新为包含开放输⼊列表
// @outputs      指向滤镜图的输出链表,调⽤返回时,更新为包含开放输出列表
// 我的话:
// 调⽤前:filter_graph包含两个滤镜buffersrc_ctx和buffersink_ctx
// 调⽤后:filters_descr描述的滤镜图插⼊到filter_graph中,buffersrc_ctx连接到filters_descr
/
/        的输⼊,filters_descr的输出连接到buffersink_ctx,filters_descr只进⾏了解析⽽不
//        建⽴内部滤镜间的连接。filters_desc与filter_graph间的连接是利⽤AVFilterInOut inputs
//        和AVFilterInOut inputs连接起来的,AVFilterInOut是⼀个链表,最终可⽤的连在⼀起的
//        滤镜链/滤镜图就是通过这个链表串在⼀起的。
if ((ret = avfilter_graph_parse_ptr(filter_graph, filters_descr,
&inputs, &outputs, NULL)) < 0)
goto end;
// 验证有效性并配置filtergraph中所有连接和格式
if ((ret = avfilter_graph_config(filter_graph, NULL)) < 0)
goto end;
end:
avfilter_inout_free(&inputs);
avfilter_inout_free(&outputs);
return ret;
}
1、⽤avfilter_get_by_name创建⼀个AVFilter对象,如buffersrc
2、⽤avfilter_graph_alloc分配⼀个AVFilterGraph对象,如filter_graph
3、⽤avfilter_graph_create_filter实例化⼀个AVFilterContext对象,如buffersrc_ctx,期间对“buffer”的过滤器要设置⼀些args
4、然后对AVFilterInOut进⾏inputs和outputs的对应创建和设置
5、通过avfilter_graph_parse_ptr将filters_descr所描述的滤镜内容插⼊到前⾯的inputs、outputs之间,串起来设置到filter_graph中
6、通过avfilter_graph_config验证有效性并配置filter_graph中所有连接和格式
主要函数和流程就是这些,照着例⼦搬就⾏了,你只需要改动filters_descr,也就是你想要的滤镜效果即可
7、解码后的帧送⼊av_buffersrc_add_frame_flags(buffersrc_ctx, frame, 0);
8、过滤后的帧从⾥⾯取出av_buffersink_get_frame(buffersink_ctx, filt_frame);
三、⽤例⼦分析流程
我这⾥⽤命令⾏来分析⼀下流程
< -re -y -rtsp_transport tcp -i "rtsp://xxx" -an -vf "scale=352x288" -vcodec libx264 -f flv 1.flv
我们可以使⽤char *graph_str = avfilter_graph_dump(filter_graph, NULL);将整个滤镜链打印出来,打印出来如下图:
我们可以看到,ffmpeg⾥⾯给我们的scale过滤器命名为"Parsed_scale_0"。我们知道过滤器⼀定有个输⼊有个输出,可以看到输⼊的buffer过滤器命名为了"graph 0 input from stream 0:0",⾥⾯设置了如video_size的args。然后还看到了format,ffmpeg⾃动给我们加了⾊彩空间转换的过滤器。可以看到滤镜链是从  输⼊过滤器=》scale过滤器=》format过滤器=》输出过滤器
[Parsed_scale_0 @ 0x5555559178c0] Setting 'w' to value '352x288'
[Parsed_scale_0 @ 0x5555559178c0] Setting 'flags' to value 'bicubic'
[Parsed_scale_0 @ 0x5555559178c0] w:352 h:288 flags:'bicubic' interl:0
[graph 0 input from stream 0:0 @ 0x55555581b180] Setting 'video_size' to value '1920x1080'
[graph 0 input from stream 0:0 @ 0x55555581b180] Setting 'pix_fmt' to value '12'
[graph 0 input from stream 0:0 @ 0x55555581b180] Setting 'time_base' to value '1/90000'
[graph 0 input from stream 0:0 @ 0x55555581b180] Setting 'pixel_aspect' to value '0/1'
[graph 0 input from stream 0:0 @ 0x55555581b180] Setting 'sws_param' to value 'flags=2'
[graph 0 input from stream 0:0 @ 0x55555581b180] Setting 'frame_rate' to value '25/1'
[graph 0 input from stream 0:0 @ 0x55555581b180] w:1920 h:1080 pixfmt:yuvj420p tb:1/90000 fr:25/1 sar:0/1 sws_param:flags=2
[format @ 0x5555557f7940] Setting 'pix_fmts' to value 'yuv420p|yuvj420p|yuv422p|yuvj422p|yuv444p|yuvj444p|nv12|nv16|nv21|yuv420p10le|yuv422p10le|yuv44 avfilter_graph_dump:
+----------------+
graph 0 input from stream 0:0:default--[0x0 0:0 ?]--default| Parsed_scale_0 |default--[0x0 0:0 ?]--format:default
|    (scale)    |
+----------------+
+-------------------------------+
| graph 0 input from stream 0:0 |default--[0x0 0:0 ?]--Parsed_scale_0:default
|          (buffer)            |
+-------------------------------+
+--------------+
format:default--[0x0 0:0 ?]--default|  out_0_0    |
| (buffersink) |
+--------------+
+----------+
Parsed_scale_0:default--[0x0 0:0 ?]--default|  format  |default--[0x0 0:0 ?]--out_0_0:default
| (format) |
+----------+
经过上⾯代码流程的讲解和实例的展⽰,看下⾯的流程图应该也不会难了。
后续会讲关于第⼆种过滤器编程的⽅法和复杂过滤器的编程使⽤,记得关注哦~参考:

本文发布于:2024-09-23 21:30:41,感谢您对本站的认可!

本文链接:https://www.17tex.com/tex/4/350801.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:过滤器   输出   流程   作为
留言与评论(共有 0 条评论)
   
验证码:
Copyright ©2019-2024 Comsenz Inc.Powered by © 易纺专利技术学习网 豫ICP备2022007602号 豫公网安备41160202000603 站长QQ:729038198 关于我们 投诉建议