[ffmpeg] ffmpeg filter模型介绍及开发指南

[ffmpeg] ffmpeg filter模型介绍及开发指南


FFmpeg filter简介

libavfilter是ffmpeg基本库之一,定义了许多音视频滤镜处理的功能,例如视频缩放、截取、翻转、叠加等功能。

这些filer都在avfilter库中实现,常用的一些filter如:
scale:视频/图像的缩放
overlay:视频/图像的叠加
rotate:以任意角度旋转视频

举个官方栗子:

在libavfilter中,一个滤镜可以有多个输入和多个输出。为了尽可能介绍清楚,我们假定有下面的滤镜链图。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-t7sxjyWK-1574405909770)(./1574391257397.png)]

在这个滤镜链图中,利用split滤镜把输入流分离成了两路流,其中一路通过crop滤镜和vfilp滤镜的同一路级联应用,再同另外一路一起通过overlay滤镜处理的流合成进行输出。则可以采用如下的命令行实现:

ffmpeg -i INPUT -vf “split [main][tmp]; [tmp] crop=iw:ih/2:0:0, vflip [flip]; [main][flip] overlay=0:H/2” OUTPUT
这样最终输出将是视频上部是原始,下部是上部的镜像。(倒影效果)

滤镜链图介绍

ffmpeg的滤镜能实现很多花里胡哨的效果,这几乎得意它的滤镜链图。

一张滤镜链图由如下组件组成:

滤镜链图(filtergraph)

滤镜链(filterchain)

滤镜垫(filterpad)

滤镜(filter)

1、基本滤镜

首先最基本一个滤镜应该包括这些:
基本滤镜(filter)

当然有些滤镜没有输入, 例如输出源类的滤镜 color-srcnullsrc

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-N6qqyBuL-1574405909772)(./1574394614884.png)]

2、 滤镜链

多个基本滤镜输入输出link起来就成了一条滤镜链:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9tDttRKt-1574405909775)(./1574394867542.png)]

3、滤镜链图

对于一些复杂的滤镜,通常会有多个输入输出,进行一些音视频合成类,这时候就需要定义一张滤镜链图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nWgeEaML-1574405909778)(./1574395721422.png)]

一个滤镜链图(filtergraph)是连接滤镜的有向图。它可以包含循环动作,也可以在多个滤镜间形成链路,每个链接都有一个连接到滤镜的输入和一个连接到滤镜的输出。

滤镜链图中的每个滤镜都是一个滤镜注册类应用程序的实例,它定义了滤镜的功能、输入接口和输出接口。

如果滤镜没有输入端(接口),则被称作“源”,如果滤镜没有输出端则被称作“槽”

开发API

当然啦,开发的时候不需要你写一整套滤镜链图出来,ffmpeg的api会提供接口给我们使用,只需要提供一串描述滤镜链图的字符串即可,ffmpeg会解析字符串并生成这张滤镜链图。

主要用了这么几个结构体:
AVFilter
AVFilterContext
AVFilterLink
AVFilterPad
AVFilterGraph
AVFilterInOut

当然上下文是这几个里面最重要的结构体啦,代表着一个filter实例,pad、link、graph则分别起着前面所描述的作用效果。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0Z4frVRZ-1574405909780)(./1574403759154.png)]

主要API

AvfilterGraphAlloc 分配一张graph空间

AvfilterGraphCreateFilter 在一张已存在的链图中创建一个filter的实例,主要用于创建buffersrc filter和buffersink filter,其他filter会在AvfilterGraphParse2后自动创建好,我们主需要配置好链图的输入源和输出槽,并link到graph的输入输出垫(pad)上。

AvFilterInOut 这是一条filter的连接链关于输入输出的,与前面图述的filter不是一个东西哦。在解析完用户描述滤波图的字符串后该链就会代表着一条输入/输出链。

AvfilterInoutAlloc 分配AvFilterInOut空间

AvfilterGetByName 通过名字获取一个filter实例

AvfilterGraphParse2 parser用户链图描述字符串

AvfilterLink 链接前后两个Pad

AvfilterGraphConfig 最后用于检查链图是否配置正确的

AvfilterGraphDump dump链图,会画出链图

类似如下 :
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jRxuZFAh-1574405909783)(./1574405302994.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CbIOC7pg-1574405909788)(./1574405268851.png)]

示例

这是一张简单的滤波链图的配置过程,go写的,希望对你有帮助 :

其中[]args 和descrition是输入参数 ,分别表示对输入源的描述和链图描述字符串。

graph := avfilter.AvfilterGraphAlloc()
	if graph == nil {
		log.Fatal("AvfilterGraphAlloc Failed.")
		return nil
	}
	
	/*frame := avutil.AvFrameAlloc()
	if frame == nil {
		log.Fatal("AvFrameAlloc failed.")
	}*/

	inputs  := avfilter.AvfilterInoutAlloc()
	outputs := avfilter.AvfilterInoutAlloc()
	if inputs == nil || outputs == nil {
		log.Fatal("AvfilterInoutAlloc Failed.")
		return nil
	}

	defer avfilter.AvfilterInoutFree(inputs)
	defer avfilter.AvfilterInoutFree(outputs)

	var buffersrc *avfilter.Filter
	var buffersink *avfilter.Filter
	if description.AudioFilter {
		buffersrc  = avfilter.AvfilterGetByName("abuffer")
		buffersink = avfilter.AvfilterGetByName("abuffersink")

	} else {
		buffersrc  = avfilter.AvfilterGetByName("buffer")
		buffersink = avfilter.AvfilterGetByName("buffersink")
	}
	
	if buffersink == nil || buffersrc == nil {
		log.Fatal("AvfilterGetByName Failed.")
		return nil
	}

	ret := graph.AvfilterGraphParse2(description.Description, &inputs, &outputs)
	if ret < 0 {
		log.Fatal("AvfilterInoutAlloc Failed des : ", avutil.ErrorFromCode(ret))
		return nil
	}

	var ins    []*avfilter.Context
	var outs   []*avfilter.Context
	var frames []*avutil.Frame
	
	// inputs
	index := 0
	for cur := inputs; cur != nil; cur = cur.Next() {
		//log.Debug("index :", index)
		var in *avfilter.Context
		//var args = "video_size=1280x720:pix_fmt=0:time_base=1/30:pixel_aspect=1/1"
		inName := "in" + strconv.Itoa(index)
		ret = avfilter.AvfilterGraphCreateFilter(&in, buffersrc, inName, args[i], 0, graph)
		if ret < 0 {
			log.Fatal("AvfilterGraphCreateFilter Failed des : ", avutil.ErrorFromCode(ret))
			return nil
		}

		ins = append(ins, in)
		ret = avfilter.AvfilterLink(ins[index], 0, cur.FilterContext(), cur.PadIdx())
		if ret < 0 {
			log.Fatal("AvfilterLink Failed des : ", avutil.ErrorFromCode(ret))
			return nil
		}
		index++
	}

	// outputs
	index = 0
	for cur := outputs; cur != nil; cur = cur.Next() {
		var out *avfilter.Context
		outName := "out" + strconv.Itoa(index)
		ret = avfilter.AvfilterGraphCreateFilter(&out, buffersink, outName, "", 0, graph)
		if ret < 0 {
			log.Fatal("AvfilterGraphCreateFilter Failed des : ", avutil.ErrorFromCode(ret))
			return nil
		}

		outs = append(outs, out)
		ret = avfilter.AvfilterLink(cur.FilterContext(), cur.PadIdx(), outs[index], 0)
		if ret < 0 {
			log.Fatal("AvfilterLink Failed des : ", avutil.ErrorFromCode(ret))
			return nil
		}
		index++
	}

	ret = graph.AvfilterGraphConfig(0)
	if ret < 0 {
		log.Fatal("AvfilterGraphConfig Failed des : ", avutil.ErrorFromCode(ret))
		//return nil
	}

	return &Filter{
		ins         : ins,
		outs 	    : outs,
		graph       : graph,
	}
	//log.Trace("GraphDump : \n", graph.AvfilterGraphDump(""))

发布了123 篇原创文章 · 获赞 156 · 访问量 28万+

猜你喜欢

转载自blog.csdn.net/qq_17308321/article/details/103200968
今日推荐