[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(""))

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章