濾鏡
什么是濾鏡盏求?百度百科介紹說“濾鏡主要是用來實(shí)現(xiàn)圖像的各種特殊效果......”滚澜。
我們最早在ffmpeg是如何轉(zhuǎn)碼的一文中了解過濾鏡朽色,來回顧下當(dāng)時(shí)的轉(zhuǎn)碼流程圖胀瞪。
從圖中可以看到濾鏡前后畫的是虛線金矛,表示可有可無毙玻,在術(shù)語中秸抚,濾鏡指的是在編碼之前針對(duì)解碼器解碼出來的原始數(shù)據(jù)(即音視頻幀)進(jìn)行處理的動(dòng)作速和,我們還可以稱它為過濾器歹垫。
ffmpeg內(nèi)置了大概近400種濾鏡,我們可以用 ffmpeg -filters 命令查看所有的濾鏡颠放,也可以用命令 ffmpeg -h filter=xxx 或者查看官方文檔了解每一種濾鏡排惨。
實(shí)際在大部分音視頻的處理過程中都離不開濾鏡,所以你應(yīng)該能明白其重要性碰凶。
多個(gè)濾鏡可以結(jié)合在一起使用形成濾鏡鏈或者濾鏡圖暮芭,在每一個(gè)濾鏡中,不僅可以對(duì)輸入源進(jìn)行處理欲低,A濾鏡處理好的結(jié)果還可以作為B濾鏡的輸入?yún)?shù)辕宏,通過B濾鏡繼續(xù)處理。
針對(duì)濾鏡的處理砾莱,ffmpeg提供了兩種處理方式瑞筐,簡單濾鏡和復(fù)雜濾鏡。
簡單濾鏡
簡單濾鏡指的是只有一個(gè)輸入和輸出腊瑟,而且保證輸入和輸出的流類型相同聚假。
比如我們?cè)谏掀恼?a target="_blank">流的操作(二)如何選擇流?末尾提到的把原視頻 r3.mp4 等比例縮放一倍
ffmpeg -i r3.mp4 -vf scale=272:480 -y filter.mp4
-vf 是 -filter:v 的簡寫闰非,類似的我們還可以使用 -filter:a 或者 -af 針對(duì)音頻流做處理膘格。
-filter的語法規(guī)則:-filter[:stream_specifier] filtergraph (output,per-stream)
stream_specifier流的類型我們一般用a表示音頻,v表示視頻财松,filtergraph表示具體的濾鏡瘪贱,這里用的是scale濾鏡。
scale濾鏡用于調(diào)整視頻的大小辆毡,比如等比例縮放政敢、等比例放大,不做等比例操作輸出就變形了胚迫,變形結(jié)果我們一般不考慮喷户。
因?yàn)槲覀冎涝曨l r1ori.mp4 的分辨率是 544x960,所以等比例縮放一倍访锻,上面的命令直接指定了 272x480褪尝,scale濾鏡自帶很多參數(shù),我們介紹幾個(gè)常用的期犬。
in_w in_h 或者 iw ih 表示輸入視頻的寬高
out_w out_h 或者 ow oh 表示輸出視頻的寬高
當(dāng)然不一定是視頻河哑,輸入輸出也可以是圖片。
所以原視頻縮放一倍我們還可以這樣寫:
ffmpeg -i r3.mp4 -vf scale=iw/2:ih/2 -y filter.mp4
問題一:如果我們要把原視頻的寬度調(diào)整為300且保持原分辨率龟虎,怎么辦璃谨?
列一個(gè)方程 544/960 = 300/x ,x=300x960/540,很麻煩佳吞,結(jié)果還不一定能整除拱雏,為此我們可以直接指定高度等于-1,它會(huì)自動(dòng)做等比例處理底扳。
ffmpeg -i r1ori.mp4 -vf scale=300:-1 -y filter.mp4
結(jié)果發(fā)現(xiàn)轉(zhuǎn)碼失敗了铸抑,提示
[libx264 @ 0x7ff509053a00] height not divisible by 2 (300x529)
Error initializing output stream 0:0 --
Error while opening encoder for output stream #0:0 -
maybe incorrect parameters such as bit_rate, rate, width or height [aac @ 0x7ff50904e200]
Qavg: 28010.410 [aac @ 0x7ff50904e200] 2 frames left in the queue on closing
提示我們 height not divisible by 2 (300x529)即高度529不能被2整除。這是因?yàn)橐恍┚幗獯a器要求很多視頻的寬高必須是n的倍數(shù)(這里n是2)衷模,所以我們寫腳本處理視頻或者圖片寬高的時(shí)候鹊汛,切記不要使用-1,正確的用法是使用-2阱冶。
ffmpeg -i r1ori.mp4 -vf scale=300:-2 -y filter.mp4 輸出結(jié)果視頻的分辨率是 300 × 530
問題二:老板為了刁難你刁憋,提出了一個(gè)新的要求:“我想要所有輸出視頻的分辨率是 300x500且不能變形”,怎么辦木蹬?
我們知道3:5的寬高比是很少見的职祷,現(xiàn)在常見的分辨率是16:9、4:3届囚,也就是說原視頻我們必須要經(jīng)過一番處理才可以滿足老板的變態(tài)需求有梆。
針對(duì)原視頻 r1ori.mp4,如果保證寬度是300意系,等比例縮放后高度是530泥耀,強(qiáng)制設(shè)置高度為500就會(huì)變形,也就是說我們只能讓高度等于500蛔添,盡量縮小寬度試試痰催。
ffmpeg -i r1ori.mp4 -vf scale=-2:500 -y filter.mp4 輸出的結(jié)果視頻的分辨率是284x500
如上圖,藍(lán)色框表示視頻的真實(shí)寬高迎瞧,紅色框表示目標(biāo)寬高夸溶,有些像html中的css一樣,可以給空出來的部分填充顏色即內(nèi)邊距不就可以了凶硅?
查閱了文檔我們發(fā)現(xiàn)pad濾鏡可以解決我們的問題缝裁。
pad濾鏡的語法規(guī)則:-pad=width[:height[:x[:y[:color]]]]
1、ffmpeg -i r1ori.mp4 -vf "scale=-2:500,pad=300:500:(300-iw)/2:0" -y filter2.mp4
2足绅、ffmpeg -i r1ori.mp4 -vf scale=-2:500,pad=300:500:-1:0 -y filter.mp4
3捷绑、ffmpeg -i r1ori.mp4 -vf scale=-2:500,pad=300:500:-1:0:black -y filter.mp4
4、ffmpeg -i r1ori.mp4 -vf "scale=-2:500,pad=300:ih:(ow-iw)/2:0:green" -y filter.mp4
上面提供4中寫法氢妈,我們以方法4做個(gè)簡單介紹粹污。
scale=-2:500,指原視頻按照等比例縮放首量,高度等于500壮吩,就是上面大家看到的284x500进苍。
pad=300:ih:(ow-iw)/2:0:green,300:ih即300:500就是紅色框的寬高(ow-iw)/2鸭叙,指的是紅色框和藍(lán)色框差值的一半觉啊,即兩邊各需要填充的范圍;最后一個(gè)參數(shù)表示需要填充的顏色递雀,默認(rèn)是黑色 black,為了調(diào)試方便我們把顏色設(shè)為green蚀浆。
現(xiàn)在我們保證了當(dāng)前視頻一定會(huì)按照300x500的比例輸出且不會(huì)變形缀程,但是請(qǐng)注意老板說的“所有輸出視頻”,也就是說輸入視頻的分辨率可能是200x300市俊、544x960杨凑、500x400、200x800等等各種比例都要保證按照300x500輸出摆昧,很顯然撩满,上面的寫法不完全通用,怎么辦绅你?
現(xiàn)在我們已知原輸入視頻的寬高和想要的寬高伺帘,針對(duì)這種情況,我們制定一套處理規(guī)則即可解決:
- 寬高都偏小忌锯,不拉伸伪嫁,不縮放
- 寬高都偏大,等比例縮小偶垮,以高度為準(zhǔn)
- 寬超出范圍张咳,等比例縮小,以寬為準(zhǔn)
- 高超出范圍似舵,等比例縮小脚猾,以高為準(zhǔn)
在實(shí)際的開發(fā)過程中,我們要跟代碼打交道砚哗,平時(shí)在命令行中的實(shí)現(xiàn)都是練習(xí)龙助,所以基于該規(guī)則,我們有了下面一段代碼
<?php
declare(strict_types=1);
class CalculatorService
{
/**
* 用戶視頻分辨率轉(zhuǎn)換
* 規(guī)則:
* 寬高都偏小蛛芥,不拉伸泌参,不縮放
* 寬高都偏大,等比例縮小常空,以高度為準(zhǔn)
* 寬超出范圍沽一,等比例縮小,以寬為準(zhǔn)
* 高超出范圍漓糙,等比例縮小铣缠,以高為準(zhǔn)
* @param int $inputWidth 輸入視頻的寬度
* @param int $inputHeight 輸入視頻的高度
* @param int $outWidth 輸出視頻的寬高
* @param int $outHeight 輸出視頻的高度
* @return string scale
*/
public function getSize(int $inputWidth, int $inputHeight, int $outWidth, int $outHeight): string
{
$scale = "";
if ($inputWidth <= $outWidth && $inputHeight <= $outHeight) {
$scale = "scale={$inputWidth}:{$inputHeight},pad={$outWidth}:{$outHeight}:-1:-1:green";
} elseif (($inputWidth > $outWidth && $inputHeight > $outHeight)
|| ($inputHeight > $outHeight)
) {
$scale = "scale=-2:{$outHeight},pad={$outWidth}:{$outHeight}:-1:0:green";
} elseif ($inputWidth > $outWidth) {
$scale = "scale={$outWidth}:-2,pad={$outWidth}:{$outHeight}:0:-1:green";
}
return $scale;
}
}
$calculatorService = new CalculatorService();
var_dump($calculatorService->getSize(200, 300, 300, 500));
var_dump($calculatorService->getSize(544, 960, 300, 500));
var_dump($calculatorService->getSize(500, 400, 300, 500));
var_dump($calculatorService->getSize(200, 600, 300, 500));
// 結(jié)果
string(37) "scale=200:300,pad=300:500:-1:-1:green"
string(35) "scale=-2:500,pad=300:500:-1:0:green"
string(35) "scale=300:-2,pad=300:500:0:-1:green"
string(35) "scale=-2:500,pad=300:500:-1:0:green"
為了方便理解,大家可以參考下面的圖一一對(duì)應(yīng)。
復(fù)雜濾鏡
相對(duì)于簡單濾鏡蝗蛙,復(fù)雜濾鏡是可以處理任意數(shù)量輸入和輸出效果的濾鏡圖蝇庭,它幾乎無所不能。
復(fù)雜濾鏡用命令 -filter_complex 表示捡硅,它還有一個(gè)別名 -lavfi哮内。
上篇文章介紹到流和濾鏡結(jié)合是一種最重要、最常用的方法壮韭。依然是將輸入視頻 r3.mp4 等比例縮放一倍北发,我們以手動(dòng)選擇流的方式為例。
ffmpeg -i r3.mp4 -filter_complex "[0]scale=272:480[out]" -map 0:a -map "[out]" -y filter.mp4
簡單分析如下:
- 命令 "[0]scale=272:480[out]" 中的[0]表示第一個(gè)輸入的視頻喷屋,因?yàn)橐獙?duì)視頻做處理琳拨,所以也可以用[0:v]表示,如果要對(duì)音頻單獨(dú)處理屯曹,就需要用 [0:a] 了狱庇;
- [0] 結(jié)合scale濾鏡,表示的就是把第一個(gè)輸入的視頻作為scale濾鏡的參數(shù)輸入恶耽;
- [out] 中括號(hào)是必須要的密任,out是自定義的一個(gè)別名,結(jié)合scale濾鏡偷俭,表示的是把scale濾鏡輸出的結(jié)果命名為[out]批什,但并非是最終輸出的結(jié)果,只能作為中間過程輸出的一個(gè)結(jié)果社搅;
- -map "[out]" 就是直接選擇[out] 流作為輸出
我們說過驻债,一個(gè)濾鏡的輸出作為另一個(gè)濾鏡的輸入,這樣就極大的避免了寫多條命令反復(fù)編解碼操作形葬,我們的原則只有一個(gè)合呐,能用一條命令處理的絕不用兩條命令。
有損編解碼器反復(fù)編解碼操作會(huì)降低原視頻質(zhì)量笙以。
比如現(xiàn)在要把原視頻 r1ori.mp4 的中間部分裁剪出來淌实,但仍保持原視頻的分辨率544x960,如何做呢猖腕?
ffmpeg -i r1ori.mp4 -filter_complex "nullsrc=s=544x960[background]; \
crop=iw:(ih/2 - 110):0:250[middle]; \
[background][middle]overlay=shortest=1:x=(main_w-overlay_w)/2:y=(main_h-overlay_h)/2[out]" \
-map "[out]"
-map 0:a
-movflags +faststart
-y fc.mp4
這個(gè)命令就顯得稍微長了一些拆祈,在這條命令中使用了nullsrc、crop倘感、overlay三種常見濾鏡放坏。
nullsrc濾鏡用于創(chuàng)建一個(gè)空的視頻,簡單的說就是一個(gè)空的畫布或者說是綠布老玛,因?yàn)槟J(rèn)創(chuàng)建的顏色是綠色的淤年。s用于指定畫布的大小钧敞,默認(rèn)是320x240,這里表示我們創(chuàng)建一個(gè)544x960的畫布麸粮,并命名為background溉苛;
關(guān)于nullsrc還有很多種不同的用戶,比如使用nullsrc和CIQRCodeGenerator創(chuàng)建一個(gè)“白狼椗澹”首頁的二維碼
ffmpeg -f lavfi -i nullsrc=s=200x200,coreimage=filter=CIQRCodeGenerator@inputMessage=\
http\\\\\://manks.top/@inputCorrectionLevel=H -frames:v 1 manks.png
crop濾鏡用于裁剪視頻愚战,也就是說視頻的任意區(qū)域任意大小,我們都可以裁剪出來齐遵。crop=iw:(ih/2 - 110):0:250[middle]; 這里我們裁剪原視頻的中間部分并命名為middle寂玲;
overlay濾鏡表示兩個(gè)視頻相互疊加,shortest官網(wǎng)是這么介紹的:“If set to 1, force the output to terminate when the shortest input terminates. Default value is 0.”洛搀,因?yàn)槲覀兪褂胣ullsrc創(chuàng)建了一個(gè)沒有時(shí)間軸的畫布敢茁,所以這里需要以middle的視頻時(shí)間為最終時(shí)間佑淀,故設(shè)置為1留美。main_w和main_h表示主視頻的寬高,overlay_w和overlay_h表示疊加視頻的寬高伸刃。如果要把A視頻疊加到B視頻上谎砾,則main_w和main_h表示B視頻的寬高,overlay_w和overlay_h表示A視頻的寬高捧颅。合起來便是把middle疊加到background之上且置于background的中間(相當(dāng)于有個(gè)疊加層的概念)景图;
最后一個(gè)參數(shù)是-movflags,它跟mp4的元數(shù)據(jù)有關(guān)碉哑,設(shè)為faststart表示會(huì)將moov移動(dòng)到mdat的前面挚币,在線播放的時(shí)候會(huì)稍微快一些。
作業(yè):我們?cè)?a target="_blank">音視頻合成案例一文中介紹了兩個(gè)案例扣典,快去試試你能不能一條命令解決妆毕?
關(guān)于濾鏡的基本介紹我們就介紹到這里,有任何問題可以下方留言贮尖。