[時間:2016-08] [狀態:Open]
[關鍵詞:FFmpeg, filter, filter graph,命令行]
1. 引言及示例
FFmpeg中的libavfilter提供了一整套的基於filter的機制。filter本身是一個插件的形式,可以快速的組裝需要的效果。
比如下面的filter,可以實現視頻的水平鏡像效果。
ffplay.exe sample.rmvb -vf hflip
FFmpeg為什么重新定義filter API?
FFmpeg定義的libavcodec接口已經成為在編解碼領域的事實上的行業標准。但音視頻filter並沒有類似的標准,多個不同的多媒體項目(比如MPlayer、Xine、GStreamer等)都實現了自定義的filter系統。為了統一filter庫API接口,FFmpeg提出了參考DirectDraw實現了高質量、高效、靈活的音視頻filter接口。詳細的文檔資料可以參考FFmpeg filter。
傳統概念上filter是什么?
本部分資料參考filter-def
filter可以翻譯成過濾器,濾波器。物理概念上,常見的過濾器跟凈化器概念重復,比如濾水器、空氣凈化器等。
- 在計算機程序中,filter是指一段代碼,可用於檢查輸入或者輸出,按照預定的規則處理並傳遞這些數據。換種說法,filter是一種傳遞(pass-through)代碼塊,將輸入數據做特定的變換並輸出。通常filter自身不做任何輸入/輸出。舉個例子,linux下的grep可以認為是一個filter,按照正則表達式匹配選擇,從輸入中選擇輸出數據。
- 在電信工程領域,filter通常指的用於信號處理的設備,比如音頻處理比較典型的低通濾波器、高通濾波器、帶通濾波器、去噪濾波器等。
filter的分類
按照處理數據的類型,通常多媒體的filter分為:
- 音頻filter
- 視頻filter
- 字幕filter
另一種按照處於編解碼器的位置划分:
- prefilters: used before encoding
- intrafilters: used while encoding (and are thus an integral part of a video codec)
- postfilters: used after decoding
FFmpeg中filter分為:
- source filter (只有輸出)
- audio filter
- video filter
- Multimedia filter
- sink filter (只有輸入)
除了source和sink filter,其他filter都至少有一個輸入、至少一個輸出。
介紹了這么多,下面也是一個例子,使用filter實現寬高減半顯示:
ffplay.exe sample.rmvb -vf scale=iw/2:ih/2
下面是使用mptestsrc的source filter作為ffplay輸入,直接顯示:
ffplay -f lavfi mptestsrc=t=dc_luma
ffplay -f lavfi life=s=300x200:mold=10:r=60:ratio=0.1:death_color=#C83232:life_color=#00ff00,scale=1200:800:flags=16
2. 基本原理
FFmpeg filter可以認為是一些預定義的范式,可以實現類似積木的多種功能的自由組合。每個filter都有固定數目的輸入和輸出,而且實際使用中不允許有空懸的輸入輸出端。使用文本描述時我們可以通過標識符指定輸入和輸出端口,將不同filter串聯起來,構成更復雜的filter。這就形成了嵌套的filter。當然每個filter可以通過ffmpeg/ffplay命令行實現,但通常filter更方便。
ffmpeg.exe、ffplay.exe能夠通過filter處理原始的音視頻數據。ffmpeg將filtergraph分為simple filtergraph和complex filtergraph。通常simple filtergraph只有一個輸入和輸出,ffmpeg命令行中使用-vf、-af識別,基本原理圖如下:
_________ ______________
| | | |
| decoded | | encoded data |
| frames |\ _ | packets |
|_________| \ /||______________|
\ __________ /
simple _\|| | / encoder
filtergraph | filtered |/
| frames |
|__________|
complex filtergraph,通常是具有多個輸入輸出文件,並有多條執行路徑;ffmpeg命令行中使用-lavfi、-filter_complex,基本原理圖如下:
_________
| |
| input 0 |\ __________
|_________| \ | |
\ _________ /| output 0 |
\ | | / |__________|
_________ \| complex | /
| | | |/
| input 1 |---->| filter |\
|_________| | | \ __________
/| graph | \ | |
/ | | \| output 1 |
_________ / |_________| |__________|
| | /
| input 2 |/
|_________|
第三部分會介紹filtergraph的基本語法和構成。
在libavfilter, 一個filter可以包含多個輸入、多個輸出。下圖是一個filtergraph的示例:
[main]
input --> split ---------------------> overlay --> output
| ^
|[tmp] [flip]|
+-----> crop --> vflip -------+
上圖中filtergraph將輸入流分成兩個流,其中一個通過crop filter和vflip filter,然后通過overlay filter將這兩個流合成一個流輸出。這個filtergraph可以用下面命令行表示:
ffmpeg -i INPUT -vf "split [main][tmp]; [tmp] crop=iw:ih/2:0:0, vflip [flip]; [main][flip] overlay=0:H/2" OUTPUT
3. 語法識別
FFmpeg中filter包含三個層次,filter->filterchain->filtergraph。具體可以參考下圖:

filter是ffmpeg的libavfilter提供的基礎單元。在同一個線性鏈中的filter使用逗號分隔,在不同線性鏈中的filter使用分號隔開,比如下面的例子:
ffmpeg -i INPUT -vf "split [main][tmp]; [tmp] crop=iw:ih/2:0:0, vflip [flip]; [main][flip] overlay=0:H/2" OUTPUT
這里crop、vflip處於同一個線性鏈,split、overlay位於另一個線性鏈。二者連接通過命名的label實現(位於中括號中的是label的名字)。在上例中split filter有兩個輸出,依次命名為[main]和[tmp];[tmp]作為crop filter輸入,之后通過vflip filter輸出[flip];overlay的輸入是[main]和[flilp]。如果filter需要輸入參數,多個參數使用冒號分割。
對於沒有音頻、視頻輸入的filter稱為source filter,沒有音頻、視頻輸出的filter稱為sink filter。
4. 經典的filter
FFmpeg支持的所有filter可以通過filters查看。
這里選幾個相對經典的filter。
音頻filter
adelayfilter
實現不同聲道的延時處理。使用參數如下adelay=1500|0|500,這個例子中實現第一個聲道的延遲1.5s,第三個聲道延遲0.5s,第二個聲道不做調整。aechofilter
實現回聲效果,具體參考 http://ffmpeg.org/ffmpeg-filters.html#aecho 。amergefilter
將多個音頻流合並成一個多聲道音頻流。具體參考 http://ffmpeg.org/ffmpeg-filters.html#amerge-1 。ashowinfofilter
顯示每一個audio frame的信息,比如時間戳、位置、采樣格式、采樣率、采樣點數等。具體參考 http://ffmpeg.org/ffmpeg-filters.html#ashowinfo 。panfilter
特定聲道處理,比如立體聲變為單聲道,或者通過特定參數修改聲道或交換聲道。主要有兩大類:
混音處理,比如下面的例子pan=1c|c0=0.9*c0+0.1*c1,實現立體聲到單聲道的變換;
聲道變換,比如5.1聲道順序調整,pan="5.1| c0=c1 | c1=c0 | c2=c2 | c3=c3 | c4=c4 | c5=c5"。silencedetect和silenceremovefilter
根據特定參數檢測靜音和移除靜音。volume和volumedetectfilter
這兩個filter分別實現音量調整和音量檢測。- audio source filter
aevalsrcfilter按照特定表達式生成音頻信號。
anullsrcfilter生成特定的原始音頻數據,用於模板或測試。
anoisesrcfilter生成噪聲音頻信號。
sinefilter生成正弦波音頻信號。 - audio sink filter
abuffersinkfilter和anullsinkfilter,這些filter只是用於特定情況下結束filter chain。
視頻filter
blend和tblendfilter
將兩幀視頻合並為一幀。具體參數參考 http://ffmpeg.org/ffmpeg-filters.html#blend 。cropfilter
按照特定分辨率裁剪輸入視頻,具體參數參考 http://ffmpeg.org/ffmpeg-filters.html#crop 。drawbox、drawgrid、drawtextfilter
繪制box(對話框)、grid(表格)、text(文本)。edgedetectfilter
邊緣檢測filter。fpsfilter
按照指定幀率輸出視頻幀(丟幀或者復制)。具體參考 http://ffmpeg.org/ffmpeg-filters.html#fps-1 。hflip、vflipfilter
水平和垂直鏡像。histogramfilter
生成每幀的各顏色分量的直方圖。noisefilter
在輸入視頻幀中添加白噪聲。overlayfilter
視頻疊加。具體參考 http://ffmpeg.org/ffmpeg-filters.html#overlay-1 。padfilter
視頻邊界填充。具體參考 http://ffmpeg.org/ffmpeg-filters.html#pad-1 。rotatefilter
視頻任意角度旋轉。具體參考 http://ffmpeg.org/ffmpeg-filters.html#rotate 。scalefilter
使用libswscale庫完成視頻縮放的filter。showinfofilter
顯示視頻幀的參數信息,比如時間戳、采樣格式、幀類型等。subtitlesfilter
使用libass庫繪制subtitle(字幕)。thumbnailfilter
提取縮略圖的filter。transposefilter
圖像轉置的filter。參數參考 http://ffmpeg.org/ffmpeg-filters.html#transpose 。
source filter
主要有buffer、cellatuo、coreimagesrc、mptestsrc、life等filter,具體效果建議參考ffmpeg用戶手冊。
source sink
主要有buffersink、nullsink兩個filter。
多媒體filter
ahistogramfilter
將音頻轉化為視頻輸出,並顯示為音量的直方圖。concatfilter
將音頻流、視頻流拼接成一個。具體參考 http://ffmpeg.org/ffmpeg-filters.html#concat 。metadata、ametadatafilter
操作metadata信息。setpts、asetptsfilter
改變輸入音頻幀或視頻幀的pts。showfreqs、showspectrum、showspertrumpic、showvolume、showwavesfilter
將輸入音頻轉換為視頻顯示,並顯示頻譜、音量等信息split、asplitfilter
將輸入切分為多個相同的輸出。- source filter
主要是movie、amoviefilter。從movie容器中讀取音頻或者視頻幀。
5. 實例demo
FFmpeg提供了很多有趣的filter實例,詳見Fancy Filtering Examples 。
我們這里先從幾個簡單的實例開始。
實例一:縮放scale
將輸入縮小寬度縮小一半,並保持寬高比。
ffmpeg -i input.jpg -vf scale=iw/2:-1 output.jpg
實例二:filter、filterchain和filtergraph的使用
先將輸入去交織,然后減半顯示。
以下三個命令是等價的。
# 2 chains form, one filter per chain, chains linked by the [middle] pad
ffmpeg -i input -vf [in]yadif=0:0:0[middle];[middle]scale=iw/2:-1[out] output# 1 chain form, with 2 filters in the chain, linking implied
ffmpeg -i input -vf [in]yadif=0:0:0,scale=iw/2:-1[out] output# the input and output are implied without ambiguity
ffmpeg -i input -vf yadif=0:0:0,scale=iw/2:-1 output
實例三:2x2布局畫面拼接
這個實例主要說明下filtergraph使用。命令行如下:
./ffmpeg -f lavfi -i testsrc -f lavfi -i testsrc -f lavfi -i testsrc -f lavfi -i testsrc -filter_complex \
"[0:v]pad=iw*2:ih*2[a]; \
[1:v]negate[b]; \
[2:v]hflip[c]; \
[3:v]edgedetect[d]; \
[a][b]overlay=w[x]; \
[x][c]overlay=0:h[y]; \
[y][d]overlay=w:h[out]" -map "[out]" -c:v ffv1 -t 5 multiple_input_grid.avi
