FFmpeg基礎概念
錯誤難免,逐漸完善。
本文介紹FFmpeg最基礎的概念,瞭解FFmpeg的簡單使用,幫助理解FFmpeg工程原始碼。內容主要節選和翻譯自書籍《FFmpeg Basics》及官網文件“Documentation-ffmpeg ”
1. 概述
FFmpeg是一款用於多媒體處理的自由軟體工程,基於GPL許可證釋出。FFmpeg提供的最核心的命令列工具是ffmpeg
,ffmpeg
命令列工具的主要特徵是輸出快速、高品質、檔案尺寸小。“FFmpeg”中“FF”表示“Fast Forward”,“mpeg”表示“Moving Pictures Experts Group”。
FFmpeg提供如下四個命令列工具:
--ffmpeg 音視訊編碼器/解碼器
--ffplay 媒體播放器
-ffprobe 顯示媒體檔案資訊
--ffserver 多媒體流廣播伺服器,使用HTTP和RTSP協議
FFmpeg提供如下軟體開發庫:
--libavcodec software library for various multimedia codecs
--libavdevice software library for devices
--libavfilter software library containing filters
--libavformat software library for media formats
--libavutil software library containing various utilities
--libpostproc software library for post processing
--libswresample software library for audio resampling
--libswscale software library for media scaling
2. 命令列格式
命令列基本格式為:
ffmpeg [global_options] {[input_file_options] -i input_url} ... {[output_file_options] output_url} ...
格式分解如下:
ffmpeg global_options input1_options -i input1 input2_options -i input2 ... output1_options output1 output2_options output2 ...
ffmpeg
讀取任意數量的輸入“檔案”(可以是常規檔案、管道、網路流、錄製裝置等,由“-i”選項指定),寫入任意數量的輸出“檔案”。命令列中無法被解釋為選項(option)的任何元素都會被當作輸出檔案。
每個輸入或輸出檔案,原則上都可以包含任意數量的流。FFmpeg中流的型別有五種:視訊(video)、音訊(audio)、字幕(subtitle)、附加資料(attachment)、普通資料(data)。檔案中流的數量和(或)流型別種數的極限值由檔案封裝格式決定。選擇哪路輸入的哪路流輸出到哪一路輸出,這個選擇過程既可以由FFmpeg自動完成,也可以通過“-map”選項指定(後續“Steam selection”章節會深入描述)。
注:關於附加資料(attachment)和普通資料(data)的說明如下:
Attachments could be liner notes, related images, metadata files, fonts, etc.
Data tracks would be for things like timecode, navigation items, cmml, streaming tracks.
參考資料[3] “What are the the data and attachment stream type? ”
命令列中的輸入檔案及輸入檔案中的流都可以通過對應的索引引用(檔案、流的索引都是從0開始)。例如,2:3表示第3個輸入檔案中的第4個流。(後續“Stream specifiers”章節會詳細描述)。
一個通用規則是:輸入/輸出選項(options)作用於跟隨此選項後的第一個檔案。因此,順序很重要,並且可以在命令列中多次指定同一選項。每個選項僅作用於離此選項最近的下一輸入或輸出檔案。全域性選項不受此規則限制。
不要把輸入檔案和輸出檔案混在一起———應該先將輸入檔案寫完,再寫輸出檔案。也不要把不同檔案的選項混在一起,各選項僅對其下一輸入或輸出檔案有效,一個選項不能跨越一個檔案傳遞到後續檔案。
舉幾個命令列例子:
-
設定輸出檔案位元速率為64kbit/s:
ffmpeg -i input.avi -b:v 64k -bufsize 64k output.avi
其中“-b:v 64k”和“-bufsize 64k”是輸出選項。 -
強制輸入檔案幀率(僅對raw格式有效)是1fps,輸出檔案幀率為24fps:
ffmpeg -r 1 -i input.m2v -r 24 output.avi
其中“-r 1”是輸入選項,“-r 24”是輸出選項。 -
ffmpeg -y -i video.avi -s vga video.mp4
其中“-y”是全域性選項,“-s vga”是輸出選項。
3. 轉碼過程
_____________________ |||| | input |demuxer| encoded data |decoder | file| ---------> | packets| -----+ |_______||______________|| v _________ || | decoded | | frames| |_________| ______________________| ||||| | output | <-------- | encoded data | <----+ | file|muxer| packets|encoder |________||______________|
ffmpeg
呼叫libavformat庫(包含解複用器demuxer),從輸入檔案中讀取到包含編碼資料的包(packet)。如果有多個輸入檔案,ffmpeg
嘗試追蹤多個有效輸入流的最小時間戳(timestamp),用這種方式實現多個輸入檔案的同步。
然後編碼包被傳遞到解碼器(decoder),解碼器解碼後生成原始幀(frame),原始幀可以被濾鏡(filter)處理(圖中未畫濾鏡),經濾鏡處理後的幀送給編碼器,編碼器將之編碼後輸出編碼包。最終,由複用器(muxex)將編碼碼寫入特定封裝格式的輸出檔案。
4. 濾鏡(filter)
在多媒體處理中,術語濾鏡(filter) 指的是修改未編碼的原始音視訊資料幀的一種軟體工具。濾鏡分為音訊濾鏡和視訊濾鏡。FFmpeg提供了很多內建濾鏡,可以用很多方式將這些濾鏡組合使用。通過一些複雜指令,可以將解碼後的幀從一個濾鏡引向另一個濾鏡。這簡化了媒體處理,因為有損編解碼器對媒體流進行多次解碼和編碼操作會降低總體質量,而引入濾鏡後,不需要多次解碼編碼操作,相關處理可以使用多個濾鏡完成,而濾鏡處理的是原始資料,不會造成資料損傷。
濾鏡通常以濾鏡鏈(filterchain, 以逗號分隔的濾鏡序列)和濾鏡圖(filtergraph, 以分號分隔的濾鏡序列)的形式使用。
濾鏡鏈由濾鏡構成,濾鏡圖由濾鏡鏈構成,這樣可以提供複雜多樣的組合方式以應對不同的應用場景。
在濾鏡圖中可以使用連線標號(link lable),連線標號表示特定濾鏡/濾鏡鏈的輸入或輸出,參4.1節。
例如,我們想要把一個經過降噪處理後的輸出檔案與輸入原檔案進行比較,如果不使用濾鏡圖,我們需要至少兩條命令:
ffmpeg -i input.mpg -vf hqdn3d,pad=2*iw output.mp4
ffmpeg -i output.mp4 -i input.mpg -filter_complex overlay=w compare.mp4
如果使用帶有連線標號的濾鏡圖,則一條命令就可以了:
ffplay -i i.mpg -vf split[a][b];[a]pad=2*iw[A];[b]hqdn3d[B];[A][B]overlay=w
4.1 濾鏡(filter)的使用
FFmpeg的libavfilter庫提供了濾鏡API,支援多路輸入和多路輸出。
命令列中,“-filter”選項指定濾鏡,“-vf”(同“-filter:v”)選項表示使用視訊濾鏡,“-af”(同“-filter:a”)選項表示使用音訊濾鏡。
filter的語法為:
[input_link_lable1][input_link_lable2]... filter_name=parameters [output_link_lable1][output_link_lable12]...
上述語法中,輸入輸出都有連線標號(link lable) ,連線符號是可選項,輸入連線標號表示濾鏡的輸入,輸出連線標號表示濾鏡的輸出。連線標號通常用在濾鏡圖中,通常前一個濾鏡的輸出標號會作為後一個濾鏡的輸入標號,通過同名的標號將濾鏡及濾鏡鏈連線起來。連線標號的用法參考4.3.2節示例。
示例1:
ffplay -f lavfi -i testsrc -vf transpose=1
“transpose=1”是filter,此行命令表示使用transpose視訊濾鏡產生一個順時針旋轉90度的測試圖案
示例2:
ffmpeg -i input.mp3 -af atempo=0.8 output.mp3
“atempo=0.8”是filter,此行命令表示使用atempo音訊濾鏡將輸入音訊速率降低到80%後寫入輸出檔案
注意:有些濾鏡只會修改幀屬性而不會修改幀內容。例如,fps濾鏡,setpts濾鏡等。
4.2 濾鏡鏈(filterchain)的使用
濾鏡鏈filterchain是以逗號分隔的filter序列,語法如下:
filter1,fiter2,filter3,...,filterN-2,filterN-1,filterN
濾鏡鏈中如果有空格,需要將濾鏡鏈用雙引號括起來,因為命令列中空格是分隔引數用的。
示例1:
ffmpeg -i input.mpg -vf hqdn3d,pad=2*iw output.mp4
“hqdn3d,pad=2iw”是filterchain,第一個filter是“hqdn3d”(降噪);第二個filter是“pad=2 iw”(將影象寬度填充到輸入寬度的2倍)。此行命令表示,將輸入視訊經降噪處理後,再填充視訊寬度為輸入寬度的2倍。
4.3 濾鏡圖(filtergraph)的使用
濾鏡圖filtergraph通常是以分號分隔的filterchain序列。filtergraph分為簡單filtergraph和複雜filtergraph。
filtergraph的語法如下:
filter1;fiter2;filter3;...;filterN-2;filterN-1;filterN
4.3.1 簡單濾鏡圖(simple filtergraph)
簡單filtergraph只能處理單路輸入流和單路輸出流,而且要求輸入和輸出具有相同的流型別。
簡單filtergraph由-filter選項指定。簡單filtergraph示意圖如下:
____________________________________ |||||| | input | ---> | simple filter graph | ---> | output | |_______||_____________________||________|
4.3.2 複雜濾鏡圖(complex filtergraph)
複雜filtergraph用於簡單filtergraph處理不了的場合。比如,多路輸入流和(或)多路輸出流,或者輸出流與輸入流型別不同。
有些特殊的filter本身就屬於複雜filtergraph,用-filter_complex選項或-lavfi選項指定,如overlay濾鏡和amix濾鏡就是複雜filtergraph。overlay濾鏡有兩個視訊輸入和一個視訊輸出,將兩個輸入視訊混合在一起。而amix濾鏡則是將兩個輸入音訊混合在一起。
複雜filtergraph示意圖如下:
_________ || | input 0 |\__________ |_________| \|| \_________/| output 0 | \ ||/ |__________| _________\| complex | / ||||/ | input 1 |---->| filter|\ |_________||| \__________ /| graph|\ || / ||\| output 1 | _________/|_________||__________| || / | input 2 |/ |_________|
示例1:
ffmpeg -i INPUT -vf "split [main][tmp]; [tmp] crop=iw:ih/2:0:0, vflip [flip]; [main][flip] overlay=0:H/2" OUTPUT
上例中"split [main][tmp]; [tmp] crop=iw:ih/2:0:0, vflip [flip]; [main][flip] overlay=0:H/2"是複雜濾鏡圖,由三個濾鏡鏈構成(分號分隔),第二個濾鏡鏈“[tmp] crop=iw:ih/2:0:0, vflip [flip]”由兩個濾鏡構成(逗號分隔)。第一個濾鏡鏈中:濾鏡split產生兩個輸出[main]和[tmp];第二個濾鏡鏈中:[tmp]作為crop濾鏡的輸入,[flip]作為vflip濾鏡的輸出,crop濾鏡輸出連線到vflip濾鏡的輸入;第三個濾鏡鏈中:[main]和[flip]作為overlay濾鏡的輸入。整行命令實現的功能是:將輸入分隔為兩路,其中一路經過裁剪和垂直翻轉後,再與另一路混合,生成輸出檔案。示意圖如下所示:
[main] input --> split ---------------------> overlay --> output |^ |[tmp][flip]| +-----> crop --> vflip -------+
5. 流拷貝(stream copy)
“-codec copy”可使能流拷貝模式。流拷貝直接將輸入流拷貝到輸出,僅涉及解複用和複用,不涉及解碼和編碼,因此也不支援濾鏡操作。流拷貝對於修改容器格式或容器級別元資料非常有用。因為不涉及編解碼操作,整個過程會非常快。示意圖如下所示:
_____________________________ |||||| | input |demuxer| encoded data |muxer| output | | file| ---------> | packets| -------> | file| |_______||______________||________|
6. 流選擇(stream selection)
有些容器,如AVI、MP4等,可以包含多種不同型別的流。FFmpeg可以識別5種流型別:音訊(audio, a),視訊(video, v),字幕(subtitle, s),附加資料(attachment, t)和普通資料(data, d)。
流選擇是從輸入檔案中選定某些流進行處理。流選擇有兩種模式,1) 使用-map選項手動指定要選擇的流;2) 無-map選項時由FFmpeg根據相應規則自動選擇流。
6.1 流選擇自動模式
自動選擇模式下,每種型別的流只選擇一路,規則如下:
音訊流:選擇具有最多通道的流,若多個音訊流通道數相同且通道數最多,則選第一個
視訊流:選擇具有最高解析度的流,若多個視訊流解析度相同且是最高解析度,則選第一個
字幕流:選擇第一個字幕流。注意:字幕流有文字字幕流和圖形字幕流,輸出格式預設的字幕編碼器僅處理其支援的字幕型別
6.2 流選擇手動模式
手動選擇模式下,要選定的流由-map選項後的流指定符(stream specifer)指定。stream_specifier語法如下:
[-]file_index:stream_type[:stream_index]
帶-
表示排除此流,不帶-
表示選中此流。檔案序號file_index和流序號stream_index都是從0開始計數。
幾個特殊的stream_specifier如下:
--map 0 選擇所有型別的所有流。
--map i:v 選擇檔案i中所有的視訊流,i:a、i:s等同理。
--map -vn 排除所有視訊流,-an、-sn等同理。
示例:
假設ffmpeg命令列如下:
ffmpeg -i file1 -i file2 select_streams output
其中有兩個輸入檔案file1和file2,選擇的流位於select_streams
file1的流組成與對應的stream_specifier如下:
file streamsstream_specifier 1st video0:v:0 2nd video0:v:1 1st audio0:a:0 2nd audio0:a:1 1st subtitle0:s:0 2nd subtitle0:s:1 3rd subtitle0:s:2
file2的流組成與對應的stream_specifier如下:
file streamsstream_specifier 1st video1:v:0 1st audio1:a:0 1st subtitle1:s:0
select_streams各種示例說明如下:
-map 0 -map 1
選擇兩個檔案的所有流
-map 0:s:2 -map 1:v:0 -map 1:a:0
選擇file1的3rd字幕流,file2的1st視訊流和file2的1st音訊流
-map 0 -map 1:s:0 -an
選擇file1除音訊外的所有流和file2的1st字幕流
-map 0 -map 1 -map -0:v:0 -map -0:a:1
選擇除file1的1st視訊流和2nd音訊流外的所有流,選擇file2中的所有流
6.3 擴充套件1:stream_specifier的各種形式
除-map選項外,stream_specifier還可用在很多其他選項中,形式有如下幾種:
specifer形式描述 stream_index選擇索引為stream_index的流 stream_type[:stream_index]選擇型別為stream_type索引為stream_index的流 p:program_id[:stream_index] 選擇節目program_id中索引為stream_index的流 stream_id選擇指定ID的流
例如,使用-b選項設定音訊流和視訊流的位元速率:
ffmpeg -i input.mpg -b:a 128k -b:v 1500k output.mp4
其中“-b:a”和“-b:v”就是stream_specifier。
6.4 擴充套件2:複雜filtergraph中的流選擇
如果某個複雜filtergraph中的輸出流未攜帶標號,則這些流將被新增到第一個輸出檔案中。如果封裝器格式不支援某種流型別,將會導致致命錯誤。
如果未使用-map選項,包含這些複雜filtergraph輸出流將導致不會對這些流型別啟用自動選擇。
如果使用了-map選項,除-map選定的流之外,這些filtergraph輸出流也會被包含進來。
複雜filtergraph的輸出流若帶標號,則標號必須被對映一次,且只能被對映一次。
假設有三個輸入檔案用於示例,其流組成成分如下:
input file 'A.avi' stream 0: video 640x360 stream 1: audio 2 channels input file 'B.mp4' stream 0: video 1920x1080 stream 1: audio 2 channels stream 2: subtitles (text) stream 3: audio 5.1 channels stream 4: subtitles (text) input file 'C.mkv' stream 0: video 1280x720 stream 1: audio 2 channels stream 2: subtitles (image)
示例1:無標號filtergraph的流選擇
ffmpeg -i A.avi -i C.mkv -i B.mp4 -filter_complex "overlay" out1.mp4 out2.srt
-filter_complex選項指定了一個複雜filtergraph,此filtergraph由單個視訊濾鏡overlay構成。overlay濾鏡需要兩個視訊輸入,但此處並未為overlay濾鏡指定輸入,因此A.avi和C.mkv中頭兩個有效視訊流會被作為overlay濾鏡的輸入。overlay濾鏡輸出無標號,因此overlay濾鏡的輸出會被寫入第一個輸出檔案out1.mp4中。
本來自動選擇模式會選中B.mp4中的“stream 0”視訊流(最高解析度真)和B.mp4中的“stream 3”音訊流(最多通道數)。但overlay濾鏡輸出流是視訊流,因此,不會對視訊流進行自動選擇,即不會選擇B.mp4中的“stream 0”。
不會選中任何字幕流,因為MP4封裝格式未註冊預設字幕編碼器,使用者也未指定字幕編碼器,無編碼器可用所以不會選擇字幕流。
第二個輸出檔案out2.srt,僅接受文字型別的字幕流。所以,就算C.mkv中的“stream 2"是第一個被找到的字幕流,也會因型別不符合被忽略掉。B.mp4中的“stream 2”會被選中,因為它才是第一個文字字幕流。
示例2:帶標號filtergraph的流選擇
ffmpeg -i A.avi -i B.mp4 -i C.mkv -filter_complex "[1:v]hue=s=0[outv];overlay;aresample" \ -map '[outv]' -anout1.mp4 \ out2.mkv \ -map '[outv]' -map 1:a:0 out3.mkv
上述命令會執行失敗,因為filtergraph的輸出標號[outv]被映射了兩次。此命令不會生成任何輸出檔案。
ffmpeg -i A.avi -i B.mp4 -i C.mkv -filter_complex "[1:v]hue=s=0[outv];overlay;aresample" \ -anout1.mp4 \ out2.mkv \ -map 1:a:0 out3.mkv
上述命令也會執行失敗,因為hue濾鏡有一個輸出標號[outv],但此標號未作任何對映。
正確的命令應該寫成下面這樣:
ffmpeg -i A.avi -i B.mp4 -i C.mkv -filter_complex "[1:v]hue=s=0,split=2[outv1][outv2];overlay;aresample" \ -map '[outv1]' -anout1.mp4 \ out2.mkv \ -map '[outv2]' -map 1:a:0 out3.mkv
“[1:v]”表示B.mp4中的視訊流,B.mp4中的視訊流被髮送到hub濾鏡,hub濾鏡的輸出被split濾鏡拷貝了一份,生成兩份輸出,兩份輸出用標號[outv1]和[outv2]表示。
overlay濾鏡需要兩個視訊輸入,使用頭兩個未使用的視訊流作輸入,即A.avi和C.mkv中的視訊流。overlay濾鏡輸出未帶標號,所以overlay濾鏡輸出被髮送到第一個輸出檔案out1.mp4,有沒有-map選項對此無影響。
aresample濾鏡使用第一個未使用的音訊流(A.avi中的“stream 1”)作為輸入。aresample濾鏡輸出也未帶標號,所以avresample濾鏡輸出也被對映到第一個輸出檔案out1.mp4。-an選項僅僅抑制了音訊流的自動或手動流選擇,而不會抑制filtergraph的輸出。所以,out1.mp4有三個輸入流:1)overlay濾鏡輸出、2)aresample濾鏡輸出和3)標號outv1,B.mp4中1)2)排序應在3)之前。
對映到out2.mkv的視訊、音訊和字幕流由自動選擇模式選定。
out3.mkv由hue濾鏡輸出和B.mp4中的“stream 1”構成。
6.5 擴充套件3:流處理(stream handling)
流處理和流選擇是互不影響的(字幕例外)。流處理通過-codec選項設定,-codec選項針對輸出檔案中的流。FFmpeg對-codec選項的處理是在流選擇(stream selection)過程之後的,因此-codec選項(流處理)不會影響流選擇。如果某型別的流未指定-codec選項,將會使用輸出檔案muxer註冊的預設編碼器。
上述規則不適用於字幕。如果一個輸出檔案指定了字幕編碼器,那麼找到的第一個字幕流(文字字幕或圖形字幕)總會被包含進來。FFmpeg不會檢查編碼器是否能轉換選定的流或已轉換的流能否被輸出格式接受。這通常也適用:當用戶手動設定編碼器時,流選擇過程不能檢查編碼流是否可以複用到輸出檔案中。如果編碼流不能複用到輸出檔案,FFmpeg會終止,所有的輸出檔案處理會失敗。
7. 選項
待補充
8. 示例
待補充
9. 參考資料
[1] FFmpeg Basics
[2]ffmpeg.html ,http://ffmpeg.org/ffmpeg.html
[3]What are the the data and attachment stream type? ,https://ffmpeg.org/pipermail/ffmpeg-user/2015-June/027333.html
10. 修改記錄
2018-12-15 V1.0 首次整理