本文爲做者原創,轉載請註明出處:http://www.javashuo.com/article/p-hkmuzuvf-cb.htmlhtml
本文介紹FFmpeg最基礎的概念,瞭解FFmpeg的簡單使用,幫助理解FFmpeg工程源碼。內容主要節選和翻譯自書籍《FFmpeg Basics》及官網文檔「Documentation-ffmpeg」和「Documentation-ffmpeg-all」。服務器
FFmpeg是一款用於多媒體處理的自由軟件工程,基於GPL許可證發佈。FFmpeg提供的最核心的命令行工具是ffmpeg
,ffmpeg
命令行工具的主要特徵是輸出快速、高品質、文件尺寸小。「FFmpeg」中「FF」表示「Fast Forward」,「mpeg」表示「Moving Pictures Experts Group」。網絡
FFmpeg提供以下四個命令行工具:
-- ffmpeg 音視頻編碼器/解碼器
-- ffplay 媒體播放器
-- ffprobe 顯示媒體文件信息
-- ffserver 多媒體流廣播服務器,使用HTTP和RTSP協議。FFmpeg 4.1版本已經刪除ffserver,新的替代者還未添加進來。ide
FFmpeg提供以下軟件開發庫:
-- libavcodec 多媒體編解碼器庫
-- libavdevice 設備庫
-- libavfilter 濾鏡庫
-- libavformat 媒體格式庫
-- libavutil software library containing various utilities
-- libpostproc 後處理庫
-- libswresample 音頻重採樣庫
-- libswscale software library for media scaling工具
命令行基本格式爲:
ffmpeg [global_options] {[input_file_options] -i input_url} ... {[output_file_options] output_url} ...
格式分解以下:post
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」選項指定(後續「Stream selection」章節會深刻描述)。編碼
注:關於附加數據(attachment)和普通數據(data)的說明以下:url
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)做用於跟隨此選項後的第一個文件。所以,順序很重要,而且能夠在命令行中屢次指定同一選項。每一個選項僅做用於離此選項最近的下一輸入或輸出文件。全局選項不受此規則限制。
不要把輸入文件和輸出文件混在一塊兒———應該先將輸入文件寫完,再寫輸出文件。也不要把不一樣文件的選項混在一塊兒,各選項僅對其下一輸入或輸出文件有效,一個選項不能跨越一個文件傳遞到後續文件。
舉幾個命令行例子:
ffmpeg -i input.avi -b:v 64k -bufsize 64k output.avi
ffmpeg -r 1 -i input.m2v -r 24 output.avi
ffmpeg -y -i video.avi -s vga video.mp4
_______ ______________ | | | | | input | demuxer | encoded data | decoder | file | ---------> | packets | -----+ |_______| |______________| | v _________ | | | decoded | | frames | |_________| ________ ______________ | | | | | | | output | <-------- | encoded data | <----+ | file | muxer | packets | encoder |________| |______________|
ffmpeg
調用libavformat庫(包含解複用器demuxer),從輸入文件中讀取到包含編碼數據的包(packet)。若是有多個輸入文件,ffmpeg
嘗試追蹤多個有效輸入流的最小時間戳(timestamp),用這種方式實現多個輸入文件的同步。
而後編碼包(packet)被傳遞到解碼器(decoder),解碼器解碼後生成原始幀(frame),原始幀能夠被濾鏡(filter)處理(圖中未畫濾鏡),經濾鏡處理後的幀送給編碼器,編碼器將之編碼後輸出編碼包。最終,由複用器(muxex)將編碼包寫入特定封裝格式的輸出文件。
在多媒體處理中,術語濾鏡(filter)指的是修改未編碼的原始音視頻數據幀的一種軟件工具。濾鏡分爲音頻濾鏡和視頻濾鏡。FFmpeg提供了不少內置濾鏡,能夠用不少方式將這些濾鏡組合使用。經過一些複雜指令,能夠將解碼後的幀從一個濾鏡引向另外一個濾鏡。這簡化了媒體處理,由於有損編解碼器對媒體流進行屢次解碼和編碼操做會下降整體質量,而引入濾鏡後,不須要屢次解碼編碼操做,相關處理可使用多個濾鏡完成,而濾鏡處理的是原始數據,不會形成數據損傷。
FFmpeg的libavfilter庫提供了濾鏡API,支持多路輸入和多路輸出。
濾鏡(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
「-vf」(同「-filter:v」)選項表示使用視頻濾鏡,「transpose=1」是濾鏡,此行命令表示使用transpose視頻濾鏡產生一個順時針旋轉90度的測試圖案
示例2:
ffmpeg -i input.mp3 -af atempo=0.8 output.mp3
「-af」(同「-filter:a」)選項表示使用音頻濾鏡,「atempo=0.8」是濾鏡,此行命令表示使用atempo音頻濾鏡將輸入音頻速率下降到80%後寫入輸出文件
注意:有些濾鏡只會修改幀屬性而不會修改幀內容。例如,fps濾鏡,setpts濾鏡等。
濾鏡鏈(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=2iw」(將圖像寬度填充到輸入寬度的2倍)。此行命令表示,將輸入視頻經降噪處理後,再填充視頻寬度爲輸入寬度的2倍。
濾鏡圖(filtergraph)一般是以分號分隔的濾鏡鏈(filterchain)序列。濾鏡圖分爲簡單濾鏡圖和複雜濾鏡圖。
濾鏡圖(filtergraph)的語法以下:
filter1;fiter2;filter3;...;filterN-2;filterN-1;filterN
簡單濾鏡圖(filtergraph)只能處理單路輸入流和單路輸出流,並且要求輸入和輸出具備相同的流類型。
簡單濾鏡圖由-filter選項指定。簡單濾鏡圖示意圖以下:
_______ _____________________ ________ | | | | | | | input | ---> | simple filter graph | ---> | output | |_______| |_____________________| |________|
複雜濾鏡圖(filtergraph)用於簡單濾鏡圖處理不了的場合。好比,多路輸入流和(或)多路輸出流,或者輸出流與輸入流類型不一樣。
有些特殊的濾鏡(filter)自己就屬於複雜濾鏡圖,用-filter_complex選項或-lavfi選項指定,如overlay濾鏡和amix濾鏡就是複雜濾鏡圖。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 -------+
在濾鏡圖中可使用鏈接標號(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
濾鏡(廣義)一般以濾鏡鏈(filterchain, 以逗號分隔的濾鏡序列)和濾鏡圖(filtergraph, 以分號分隔的濾鏡序列)的形式使用。濾鏡鏈由濾鏡構成,濾鏡圖由濾鏡鏈構成,這樣能夠提供複雜多樣的組合方式以應對不一樣的應用場景。
濾鏡(狹義)是濾鏡鏈的簡單特例,濾鏡鏈是濾鏡圖的簡單特例。注意這裏濾鏡(狹義)、濾鏡鏈、濾鏡圖之間不是繼承的關係,而是組合的關係,好比,一個濾鏡圖能夠只包含一個濾鏡鏈,而一個濾鏡鏈也能夠只包含一個濾鏡,這種特例狀況下,一個濾鏡圖僅由單個濾鏡構成。FFmpeg的命令行中,濾鏡(廣義)的出現形式有濾鏡(狹義)、濾鏡鏈、濾鏡圖三種形式,但濾鏡(狹義)和濾鏡鏈能夠看做是特殊的濾鏡圖,所以,爲了簡便,FFmpeg的命令行中濾鏡相關選項,只針對濾鏡圖(filtergraph)概念,分爲以下兩類:
針對簡單濾鏡圖的選項:「-vf」等同「-filter:v」,「-af」等同「-filter:a」
針對複雜濾鏡圖的選項:「-lavfi」等價「-filter_complex」
「-codec copy」可以使能流拷貝(stream copy)模式。流拷貝直接將輸入流拷貝到輸出,僅涉及解複用和複用,不涉及解碼和編碼,所以也不支持濾鏡操做。流拷貝對於修改容器格式或容器級別元數據很是有用。由於不涉及編解碼操做,整個過程會很是快。示意圖以下所示:
_______ ______________ ________ | | | | | | | input | demuxer | encoded data | muxer | output | | file | ---------> | packets | -------> | file | |_______| |______________| |________|
有些容器,如AVI、MP4等,能夠包含多種不一樣類型的流。FFmpeg能夠識別5種流類型:音頻(audio, a),視頻(video, v),字幕(subtitle, s),附加數據(attachment, t)和普通數據(data, d)。
流選擇(stream selection)是從輸入文件中選定某些流進行處理。流選擇有兩種模式,1) 使用-map選項手動指定要選擇的流;2) 無-map選項時由FFmpeg根據相應規則自動選擇流。
自動選擇模式下,每種類型的流只選擇一路,規則以下:
音頻流:選擇具備最多通道的流,若多個音頻流通道數相同且通道數最多,則選第一個
視頻流:選擇具備最高分辨率的流,若多個視頻流分辨率相同且是最高分辨率,則選第一個
字幕流:選擇第一個字幕流。注意:字幕流有文本字幕流和圖形字幕流,輸出格式默認的字幕編碼器僅處理其支持的字幕類型
手動選擇模式下,要選定的流由-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 streams stream_specifier 1st video 0:v:0 2nd video 0:v:1 1st audio 0:a:0 2nd audio 0:a:1 1st subtitle 0:s:0 2nd subtitle 0:s:1 3rd subtitle 0:s:2
file2的流組成與對應的stream_specifier以下:
file streams stream_specifier 1st video 1:v:0 1st audio 1:a:0 1st subtitle 1: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中的全部流
有些選項(好比設置碼率、設置編解碼器)是針對流的。一個選項具體做用於哪些流,由stream_specifier指定。
stream_specifier附在選項後面,由「:」分隔。例如:-codec:a:1 ac3
中a:1
就是stream_specifier。
stream_specifier能夠匹配一路流或多路流,對應的選項可做用於stream_specifier匹配的這些流。一個空的stream_specifier將匹配全部的流。例如:-b:a 128k
匹配全部音頻流,而-codec copy
或-codec: copy
則匹配全部流。
除上一節所述-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
若是某個複雜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]' -an out1.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" \ -an out1.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]' -an out1.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」構成。
流處理(stream handling)和流選擇是互不影響的(字幕例外)。流處理經過-codec選項設置,-codec選項針對輸出文件中的流。FFmpeg對-codec選項的處理是在流選擇(stream selection)過程以後的,所以-codec選項(流處理)不會影響流選擇。若是某類型的流未指定-codec選項,將會使用輸出文件muxer註冊的默認編碼器。
上述規則不適用於字幕。若是一個輸出文件指定了字幕編碼器,那麼找到的第一個字幕流(文本字幕或圖形字幕)總會被包含進來。FFmpeg不會檢查編碼器是否能轉換選定的流或已轉換的流可否被輸出格式接受。這一般也適用:當用戶手動設置編碼器時,流選擇過程不能檢查編碼流是否能夠複用到輸出文件中。若是編碼流不能複用到輸出文件,FFmpeg會終止,全部的輸出文件處理會失敗。
[1] FFmpeg Basics
[2] ffmpeg.html,http://ffmpeg.org/ffmpeg.html
[3] ffmpeg-all.html, http://ffmpeg.org/ffmpeg-all.html
[4] What are the the data and attachment stream type?, https://ffmpeg.org/pipermail/ffmpeg-user/2015-June/027333.html
[5] Solutions to some ffmpeg errors and messages
2018-12-15 V1.0 首次整理 2019-02-15 V1.1 完善stream_specifier章節,補充選項章節與示例章節 2019-02-16 V1.1 增長視頻截圖命令行示例 2019-02-19 V1.2 整理完善濾鏡章節 2019-02-23 V1.3 命令行選項與命令行示例拆分出去單獨成文