作者: 葉餘 來源: https://www.cnblogs.com/leisure_chn/p/10297002.html 本文介紹 FFmpeg 最基礎的概念,了解 FFmpeg 的使用方法。本文内容主要節選和翻譯自書籍《FFmpeg Basics》及官網文檔 “ Documentation-ffmpeg ” 和 “ Documentation-ffmpeg-all ”。
1. 概述
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,新的替代者還未添加進來。
FFmpeg提供如下軟體開發庫:
-- libavcodec 多媒體編解碼器庫
-- libavdevice 裝置庫
-- libavfilter 濾鏡庫
-- libavformat 媒體格式庫
-- libavutil 實用工具庫
-- libpostproc 後處理庫
-- libswresample 音頻重采樣庫
-- libswscale 視訊縮放庫
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” 選項手動指定(後面第 6 節 “流選擇” 章節會深入描述)。
注:關于附加資料(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:4 表示第 3 個輸入檔案中的第 5 個流。(後面 6.3 節 “stream specifier” 章節會詳細介紹)。
一個通用規則是:輸入/輸出選項(options)作用于跟随此選項後的第一個檔案。是以,順序很重要,并且可以在指令行中多次指定同一選項。每個選項僅作用于離此選項最近的下一輸入或輸出檔案。全局選項不受此規則限制。
不要把輸入檔案和輸出檔案混在一起———應該先将輸入檔案寫完,再寫輸出檔案。也不要把不同檔案的選項混在一起,各選項僅對其下一輸入或輸出檔案有效,一個選項不能跨越一個檔案傳遞到後續檔案。
舉幾個指令行例子:
■ 設定輸出檔案碼率為 64 kbit/s:
ffmpeg -i input.avi -b:v 64k -bufsize 64k output.avi
其中 “-b:v 64k” 和 “-bufsize 64k” 是輸出選項。
■ 強制輸入檔案幀率(僅對 raw 格式有效)是 1 fps,輸出檔案幀率為 24 fps:
ffmpeg -r 1 -i input.m2v -r 24 output.avi
其中 “-r 1” 是輸入選項,“-r 24” 是輸出選項。
■ 轉封裝:将 avi 格式轉為 mp4 格式,并将視訊縮放為 vga 分辨率:
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),用這種方式實作多個輸入檔案的同步。
然後編碼包(packet)被傳遞到解碼器(decoder),解碼器解碼後生成原始幀(frame),原始幀可以被濾鏡(filter)處理(圖中未畫濾鏡),經濾鏡處理後的幀送給編碼器,編碼器将之編碼後輸出編碼包。最終,由複用器(muxex)将編碼包寫入特定封裝格式的輸出檔案。
4. 濾鏡
在多媒體進行中,術語 濾鏡(filter) 指的是修改未編碼的原始音視訊資料幀的一種軟體工具。濾鏡分為音頻濾鏡和視訊濾鏡。FFmpeg 提供了很多内置濾鏡,可以用很多方式将這些濾鏡組合使用。通過一些複雜指令,可以将解碼後的幀從一個濾鏡引向另一個濾鏡。這簡化了媒體處理,因為有損編解碼器對媒體流進行多次解碼和編碼操作會降低總體品質,而引入濾鏡後,不需要多次解碼編碼操作,相關處理可以使用多個濾鏡完成,而濾鏡處理的是原始資料,不會造成資料損傷。
4.1 濾鏡的使用
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 濾鏡等。
4.2 濾鏡鍊的使用
濾鏡鍊(filterchain) 是以逗号分隔的濾鏡(filter)序列,文法如下:
filter1,fiter2,filter3,...,filterN-2,filterN-1,filterN
濾鏡鍊中如果有空格,需要将濾鏡鍊用雙引号括起來,因為指令行中空格用于分隔參數。
ffmpeg -i input.mpg -vf hqdn3d,pad=2*iw output.mp4
“hqdn3d,pad=2*iw” 是 filterchain,此 filterchain 中第一個 filter 是 “hqdn3d”(降噪);第二個 filter 是 “pad=2*iw”(将圖像寬度填充到輸入寬度的 2 倍)。此行指令表示,将輸入視訊經降噪處理後,再填充視訊寬度為輸入寬度的 2 倍。
4.3 濾鏡圖的使用
濾鏡圖(filtergraph) 通常是以分号分隔的濾鏡鍊(filterchain)序列。濾鏡圖分為簡單濾鏡圖和複雜濾鏡圖。
濾鏡圖(filtergraph)的文法如下:
filter1;fiter2;filter3;...;filterN-2;filterN-1;filterN
4.3.1 簡單濾鏡圖
簡單濾鏡圖隻能處理單路輸入流和單路輸出流,而且要求輸入和輸出具有相同的流類型。
簡單濾鏡圖由 “-filter” 選項指定。簡單濾鏡圖示意圖如下:
_______ _____________________ ________
| | | | | |
| input | ---> | simple filter graph | ---> | output |
|_______| |_____________________| |________|
4.3.2 複雜濾鏡圖
複雜濾鏡圖用于簡單濾鏡圖處理不了的場合。比如,多路輸入流和(或)多路輸出流,或者輸出流與輸入流類型不同。
有些特殊的濾鏡(filter)本身就屬于複雜濾鏡圖,用 “-filter_complex” 選項或 “-lavfi” 選項指定,如 overlay 濾鏡和 amix 濾鏡就是複雜濾鏡圖。overlay 濾鏡有兩個視訊輸入和一個視訊輸出,将兩個輸入視訊混合在一起。而 amix 濾鏡則是将兩個輸入音頻混合在一起。
複雜濾鏡圖示意圖如下:
_________
| |
| 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 -------+
4.3.3 濾鏡圖中的連接配接标号
在濾鏡圖中可以使用連接配接标号(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.4 濾鏡使用總結
濾鏡(廣義)通常以濾鏡鍊(filterchain, 以逗号分隔的濾鏡序列)和濾鏡圖(filtergraph, 以分号分隔的濾鏡序列)的形式使用。濾鏡鍊由濾鏡構成,濾鏡圖由濾鏡鍊構成,這樣可以提供複雜多樣的組合方式以應對不同的應用場景。
濾鏡(狹義)是濾鏡鍊的簡單特例,濾鏡鍊是濾鏡圖的簡單特例。比如,一個濾鏡圖可以隻包含一個濾鏡鍊,而一個濾鏡鍊也可以隻包含一個濾鏡,這種特例情況下,一個濾鏡圖僅由單個濾鏡構成。
FFmpeg 的指令行中,濾鏡(廣義)的出現形式有濾鏡(狹義)、濾鏡鍊、濾鏡圖三種形式,但濾鏡(狹義)和濾鏡鍊可以看作是特殊的濾鏡圖,是以,為了簡便,FFmpeg 的指令行中濾鏡相關選項,隻針對濾鏡圖(filtergraph)概念,分為如下兩類:
- 針對簡單濾鏡圖的選項:“-vf” 等同 “-filter:v”,“-af” 等同 “-filter:a”
- 針對複雜濾鏡圖的選項:“-lavfi” 等價于 “-filter_complex”
5. 流拷貝
“-codec copy” 選項可以使能流拷貝(stream copy)模式。流拷貝直接将輸入流拷貝到輸出,僅涉及解複用和複用,不涉及解碼和編碼,是以也不支援濾鏡操作。流拷貝對于修改容器格式或容器級别中繼資料非常有用。因為不涉及編解碼操作,整個過程會非常快。
流拷貝示意圖如下所示:
_______ ______________ ________
| | | | | |
| input | demuxer | encoded data | muxer | output |
| file | ---------> | packets | -------> | file |
|_______| |______________| |________|
6. 流選擇
有些容器,如 AVI、MP4 等,可以包含多種不同類型的流。FFmpeg 可以識别 5 種流類型:音頻(audio, a),視訊(video, v),字幕(subtitle, s),附加資料(attachment, t)和普通資料(data, d)。
流選擇(stream selection) 是從輸入檔案中標明某些流進行處理。流選擇有兩種模式,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 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 中的所有流
6.3 stream_specifier
有些選項(比如設定碼率、設定編解碼器)是針對流的。一個選項具體作用于哪些流,由選項後跟随的 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
■ 示例1:-codec 選項和 -map 選項聯合使用中的 stream_specifier
先看一下源檔案格式:
> ffprobe -hide_banner .\16dvbsubtitles.trp
Input #0, mpegts, from '.\16dvbsubtitles.trp':
Duration: 00:01:00.70, start: 3374.994533, bitrate: 21680 kb/s
Program 2
Stream #0:0[0x21]: Video: mpeg2video (Main) ([2][0][0][0] / 0x0002), yuv420p(tv, progressive), 1920x1080 [SAR 1:1 DAR 16:9], 18000 kb/s, 30 fps, 30 tbr, 90k tbn, 60 tbc
Side data:
cpb: bitrate max/min/avg: 18000000/0/0 buffer size: 13008896 vbv_delay: N/A
Stream #0:1[0x24]: Audio: ac3 ([129][0][0][0] / 0x0081), 48000 Hz, stereo, fltp, 384 kb/s
Stream #0:2[0x2d](eng): Subtitle: dvb_subtitle ([6][0][0][0] / 0x0006)
Stream #0:3[0x2e](eng): Subtitle: dvb_subtitle ([6][0][0][0] / 0x0006)
Stream #0:4[0x2f](gre): Subtitle: dvb_subtitle ([6][0][0][0] / 0x0006)
Stream #0:5[0x1920](heb): Subtitle: dvb_subtitle ([6][0][0][0] / 0x0006)
Stream #0:6[0x1921](nob): Subtitle: dvb_subtitle ([6][0][0][0] / 0x0006)
Stream #0:7[0x1922](hrv): Subtitle: dvb_subtitle ([6][0][0][0] / 0x0006)
Stream #0:8[0x1923](swe): Subtitle: dvb_subtitle ([6][0][0][0] / 0x0006)
Stream #0:9[0x1924](tur): Subtitle: dvb_subtitle ([6][0][0][0] / 0x0006)
Stream #0:10[0x1925](ger): Subtitle: dvb_subtitle ([6][0][0][0] / 0x0006)
Stream #0:11[0x1926](ara): Subtitle: dvb_subtitle ([6][0][0][0] / 0x0006)
Stream #0:12[0x1927](fra): Subtitle: dvb_subtitle ([6][0][0][0] / 0x0006)
Stream #0:13[0x1928](dan): Subtitle: dvb_subtitle ([6][0][0][0] / 0x0006)
Stream #0:14[0x1929](dut): Subtitle: dvb_subtitle ([6][0][0][0] / 0x0006)
Stream #0:15[0x192a](fas): Subtitle: dvb_subtitle ([6][0][0][0] / 0x0006)
Stream #0:16[0x192b](spa): Subtitle: dvb_subtitle ([6][0][0][0] / 0x0006)
Stream #0:17[0x192c](slv): Subtitle: dvb_subtitle ([6][0][0][0] / 0x0006)
源檔案中有 1 路視訊流、1 路音頻流、16 路字幕流。
下述指令實作:将輸入檔案中所有視訊流、所有音頻流、第 1 路字幕流和第 4 路字幕流拷貝到輸出檔案中。
ffmpeg -i 16dvbsubtitles.trp -c:v copy -map 0:v -c:a copy -map 0:a -c:s:0 copy -map 0:s:0 -c:s:1 copy -map 0:s:3 16dvbsubtitles.ts
-c:v 等同于 -codec:v 或 -vcodec,-c:a 等同于 -codec:a 或 -acodec,-c:s 等同于 -codec:s 或 -scodec。-c 選項将編碼器指定為 copy 時,表示将輸入直接拷貝到輸出,不會經過解碼再編碼的步驟。
注意:字幕流選項 “-c:s:0 copy -map 0:s:0 -c:s:1 copy -map 0:s:3” 中 -map 選項的參數值(如 0:s:0)針對的是源檔案中的流(輸入流),而 -c 選項中的 stream_specifier(如 -c:s:0 中的 s:0)針對的則是由 -map 選項選中的流(輸出流),因為 -map 選項總共選了兩路字幕流,是以 -c 選項隻能是 -c:s:0 和/或 -c:s:1。另外,-map 選項和 -c 選項的在指令行中的位置無先後關系要求。
如下兩條指令都會導緻執行出錯:
ffmpeg -i 16dvbsubtitles.trp -c:v copy -map 0:v -c:a copy -map 0:a -c:s:0 copy -map 0:s:0 -c:s:2 copy -map 0:s:3 16dvbsubtitles.ts
ffmpeg -i 16dvbsubtitles.trp -c:v copy -map 0:v -c:a copy -map 0:a -c:s:0 copy -map 0:s:0 -map 0:s:3 16dvbsubtitles.ts
第一條指令中隻有兩路輸出字幕流,-c:s:2 卻指定第三路字幕流,-c:s:2 會被 ffmpeg 忽略掉,但是第二路輸出字幕流卻未指定字幕編碼器,ffmpeg 嘗試為第二路字幕自動選擇編碼器但是失敗。這條指令執行報錯如下:
Automatic encoder selection failed for output stream #0:3. Default encoder for f
ormat mpegts (codec none) is probably disabled. Please choose an encoder manuall
y.
Error selecting an encoder for stream 0:3
如果是非 ts 的某些封裝格式,封裝格式指定了預設編碼器,可能自動選擇編碼器會成功,那麼這條指令就會執行成功。
将上面第一條指令改成下面這樣,會執行成功。
ffmpeg -i 16dvbsubtitles.trp -c:v copy -map 0:v -c:a copy -map 0:a -c:s:0 copy -map 0:s:0 -c:s:2 copy -map 0:s:3 -c:s:1 copy 16dvbsubtitles.ts
“-map 0:s:0”作為第一路輸出流,被 “-c:s:0 copy”指定編碼器為 copy,“-map 0:s:3”作為第二路輸出流,被 “-c:s:1 copy”指定編碼器為 copy。“-c:s:2 copy”這個選項找不到指定的流,會被 ffmpeg 忽略掉。
上面第二條指令中存在同樣的錯誤,未對第二路輸出字幕流指定字幕編碼器,ffmpeg 預設選擇編碼器失敗。
如下指令都是正确的:
ffmpeg -i 16dvbsubtitles.trp -c:v copy -map 0:v -c:a copy -map 0:a -c:s copy -map 0:s:0 -map 0:s:3 16dvbsubtitles.ts
ffmpeg -i 16dvbsubtitles.trp -map 0:v -map 0:a -map 0:s:0 -map 0:s:3 -c:v copy -c:a copy -c:s copy 16dvbsubtitles.ts
ffmpeg -i 16dvbsubtitles.trp -c:v copy -map 0:v -c:a copy -map 0:a -c:s:1 copy -map 0:s:0 -c:s:0 copy -map 0:s:3 16dvbsubtitles.ts
ffmpeg -i 16dvbsubtitles.trp -map 0:v -c:v copy -map 0:a -c:a copy -map 0:s:0 -c:s:1 copy -map 0:s:3 -c:s:0 copy 16dvbsubtitles.ts
第一條指令和第二條指令是一樣的,第三條指令和第四條指令是一樣的,說明 -map 和 -c 選項沒有位置先後關系的要求。“-c:s copy”為所有輸出字幕流指定編碼器為 copy。
6.4 擴充1:複雜濾鏡圖中的流選擇
如果某個複雜 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 中的 stream 0 和 C.mkv 中的 stream0)會被作為 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 中的視訊流(B.mp4 中的視訊流已被 hub 濾鏡使用)。overlay 濾鏡輸出未帶标号,是以 overlay 濾鏡輸出被發送到第一個輸出檔案 out1.mp4,有沒有 “-map” 選項對此無影響。
aresample 濾鏡需要一個音頻輸入,但濾鏡輸入未帶标号,是以使用第一個未使用的音頻流(A.avi中的 “stream 1”)作為輸入。aresample 濾鏡輸出也未帶标号,是以 avresample 濾鏡輸出也被映射到第一個輸出檔案 out1.mp4。“-an” 選項僅僅抑制了音頻流的自動或手動流選擇,而不會抑制 filtergraph 的輸出,是以 avresample 濾鏡輸出的音頻流會成功輸出到 out1.mp4 中。
是以,out1.mp4 有三個輸入流:1) overlay 濾鏡輸出、2) aresample 濾鏡輸出 和 3) 标号
outv1,out1.mp4 中 1) 2) 排序應在 3) 之前。
映射到 out2.mkv 的視訊、音頻和字幕流由自動選擇模式標明。
out3.mkv 由 hue 濾鏡輸出和 B.mp4 中的 “stream 1” 構成。
6.5 擴充2:流處理
流處理(stream handling)和流選擇是互不影響的(字幕例外)。流處理通過 “-codec” 選項設定,“-codec” 選項針對輸出檔案中的流。FFmpeg 對 “-codec” 選項的處理是在流選擇(stream selection)過程之後的,是以 -codec 選項(流處理)不會影響流選擇。如果某類型的流未指定 “-codec”選項,将會使用輸出檔案 muxer 注冊的預設編碼器。
上述規則不适用于字幕。如果一個輸出檔案指定了字幕編碼器,那麼找到的第一個字幕流(文本字幕或圖形字幕)總會被包含進來。FFmpeg 不會檢查編碼器是否能轉換標明的流或已轉換的流能否被輸出格式接受。這通常也适用:當使用者手動設定編碼器時,流選擇過程不能檢查編碼流是否可以複用到輸出檔案中。如果編碼流不能複用到輸出檔案,FFmpeg 會終止,所有的輸出檔案處理會失敗。
7. 參考資料
[1] FFmpeg Basics
[2]
ffmpeg.html,
http://ffmpeg.org/ffmpeg.html[3]
ffmpeg-all.html,
http://ffmpeg.org/ffmpeg-all.html[4]
https://ffmpeg.org/pipermail/ffmpeg-user/2015-June/027333.html[5]
Solutions to some ffmpeg errors and messages8. 修改記錄
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 指令行選項與指令行示例拆分出去單獨成文
2020-03-26 V1.3 修改文字錯誤
2020-04-17 V1.4 6.3 節增加示例:-codec 選項和 -map 選項聯合使用中的 stream_specifier
2021-02-23 V1.4 完善 6.3 節示例說明
「視訊雲技術」你最值得關注的音視訊技術公衆号,每周推送來自阿裡雲一線的實踐技術文章,在這裡與音視訊領域一流工程師交流切磋。